feat(ui/arch): Optimize Graph Dynamics, Immersive Reader, and Core Stability (#19)
This PR introduces a major optimization of graph dynamics, immersive reading experience, and architectural stabilization. ### 🚀 Key Improvements - **Knowledge Graph (Fix #16)**: - Implemented smooth D3.js transitions using the General Update Pattern. - Added "Neon Flash" entry animations and dynamic node dimming for better focus. - **Immersive Reader (Fix #12)**: - Standardized centered layout (`max-width: 800px`) with **Merriweather** typography. - Optimized line-height and letter-spacing for premium readability. - **Technical Code Blocks (Fix #20)**: - High-contrast dark containers for code snippets. - **JetBrains Mono** integration and neon-accented scrollbars. - **Architectural Stabilization**: - Enforced a strict **'no async void'** policy in UI services using `Func<Task>`. - Resolved WASM runtime DI errors by implementing dummy service proxies for server-side dependencies. - Replaced generic 'Not Found' message with a branded Nexus preloader. Fixes #7, Fixes #12, Fixes #16, Fixes #20. Reviewed-on: #19 Co-authored-by: Marek Jasiński <jasins.marek@gmail.com> Co-committed-by: Marek Jasiński <jasins.marek@gmail.com>
This commit was merged in pull request #19.
This commit is contained in:
@@ -52,15 +52,16 @@
|
||||
private bool _isJsInitialized;
|
||||
private ElementReference _containerRef;
|
||||
|
||||
protected override void OnInitialized()
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
Coordinator.Clear();
|
||||
ThemeService.OnThemeChanged += StateHasChanged;
|
||||
await Coordinator.ClearAsync();
|
||||
ThemeService.OnThemeChanged += HandleUpdate;
|
||||
NavigationService.OnNavigationChanged += OnNavigationChanged;
|
||||
|
||||
InteractionService.OnScrollToBlockRequested += HandleScrollRequested;
|
||||
InteractionService.OnHighlightBlockRequested += HandleHighlightRequested;
|
||||
InteractionService.OnTextSelected += HandleTextSelected;
|
||||
SyncService.OnProgressReceived += HandleSyncProgressReceived;
|
||||
}
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
@@ -113,60 +114,56 @@
|
||||
}
|
||||
|
||||
[JSInvokable]
|
||||
public void HandleBlockReached(string blockId, string content)
|
||||
public async Task HandleBlockReached(string blockId, string content)
|
||||
{
|
||||
Coordinator.OnBlockReached(blockId, content);
|
||||
await Coordinator.OnBlockReachedAsync(blockId, content);
|
||||
|
||||
// Debounce sync update (simple version: every 5 seconds or on a timer)
|
||||
_ = SyncService.UpdateProgressAsync(blockId);
|
||||
await SyncService.UpdateProgressAsync(blockId);
|
||||
}
|
||||
|
||||
private void HandleSyncProgressReceived(string blockId, DateTime timestamp)
|
||||
private async Task HandleSyncProgressReceived(string blockId, DateTime timestamp)
|
||||
{
|
||||
// For now, let's just scroll to the node if it's in the current view,
|
||||
// or just log it. Usually, we should prompt the user.
|
||||
Console.WriteLine($"[Sync] Received progress from another device: {blockId} at {timestamp}");
|
||||
|
||||
// Simple auto-scroll if it's newer than what we have (we don't track our own timestamp yet,
|
||||
// but we can assume incoming syncs are from other active devices)
|
||||
_ = InvokeAsync(async () => {
|
||||
await ScrollToNodeAsync(blockId);
|
||||
StateHasChanged();
|
||||
});
|
||||
await ScrollToNodeAsync(blockId);
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
[JSInvokable]
|
||||
public void HandleTextSelected(string text, string blockId, SelectionCoordinates coords)
|
||||
public async Task HandleTextSelected(string text, string blockId, SelectionCoordinates coords)
|
||||
{
|
||||
Console.WriteLine($"[ReaderCanvas] Text selected: {text} at {coords.Top},{coords.Left}");
|
||||
_selectedText = text;
|
||||
_selectedBlockId = blockId;
|
||||
_selectionCoords = coords;
|
||||
StateHasChanged();
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
[JSInvokable]
|
||||
public void HandleSelectionCleared()
|
||||
public async Task HandleSelectionCleared()
|
||||
{
|
||||
_selectedText = string.Empty;
|
||||
_selectionCoords = null;
|
||||
StateHasChanged();
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
private void HandleScrollRequested(string blockId)
|
||||
private async Task HandleScrollRequested(string blockId)
|
||||
{
|
||||
_ = ScrollToNodeAsync(blockId);
|
||||
await ScrollToNodeAsync(blockId);
|
||||
}
|
||||
|
||||
private async void HandleHighlightRequested(string blockId)
|
||||
private async Task HandleHighlightRequested(string blockId)
|
||||
{
|
||||
_highlightedBlockId = blockId;
|
||||
StateHasChanged();
|
||||
await InvokeAsync(StateHasChanged);
|
||||
await Task.Delay(3000); // Highlight for 3 seconds
|
||||
if (_highlightedBlockId == blockId)
|
||||
{
|
||||
_highlightedBlockId = null;
|
||||
StateHasChanged();
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,9 +209,11 @@
|
||||
catch { }
|
||||
}
|
||||
|
||||
private Task HandleUpdate() => InvokeAsync(StateHasChanged);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ThemeService.OnThemeChanged -= StateHasChanged;
|
||||
ThemeService.OnThemeChanged -= HandleUpdate;
|
||||
NavigationService.OnNavigationChanged -= OnNavigationChanged;
|
||||
|
||||
InteractionService.OnScrollToBlockRequested -= HandleScrollRequested;
|
||||
|
||||
Reference in New Issue
Block a user