feat: implement interactive citation markers with metadata and optimize knowledge caching with concurrent collision handling

This commit is contained in:
2026-05-26 13:43:05 +02:00
parent 824b4366e0
commit 75c7b2f279
7 changed files with 830 additions and 314 deletions
@@ -67,6 +67,7 @@
private bool _isJsInitialized;
private ElementReference _containerRef;
private bool _isInteractive;
private string? _currentActiveBlockId;
protected override async Task OnInitializedAsync()
{
@@ -143,6 +144,7 @@
[JSInvokable]
public async Task HandleBlockReached(string blockId, string content)
{
_currentActiveBlockId = blockId;
await Coordinator.OnBlockReachedAsync(blockId, content);
if (ViewModel != null)
@@ -160,8 +162,15 @@
private async Task HandleSyncProgressReceived(string blockId, DateTime timestamp)
{
if (string.IsNullOrEmpty(blockId) || blockId == _currentActiveBlockId)
{
Logger.LogDebug("[Sync] Received progress {BlockId} is empty or matches active block. Ignoring scroll.", blockId);
return;
}
Logger.LogInformation("[Sync] Received progress from another device: block {BlockId} at {Timestamp}", blockId, timestamp);
_currentActiveBlockId = blockId;
await ScrollToNodeAsync(blockId);
await InvokeAsync(StateHasChanged);
}
@@ -212,6 +221,7 @@
private async Task LoadChapterAsync(int index)
{
await Coordinator.ClearAsync();
_isJsInitialized = false; // Reset JS initialization to re-bind the scroll observer to new DOM elements!
_isLoadingChapter = true;
StatusMessage = "Wczytywanie treści...";
StateHasChanged();
@@ -253,6 +263,7 @@
{
var targetBlockId = NavigationService.PendingScrollBlockId;
NavigationService.PendingScrollBlockId = null; // Clear it to prevent multiple scrolls
_currentActiveBlockId = targetBlockId;
// Give the browser slightly more than one frame to render the loaded blocks
await Task.Delay(150);