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:
2026-05-08 18:16:09 +00:00
committed by Marek Jaisński
parent 775fb73fa9
commit 55cc3ae10d
38 changed files with 442 additions and 179 deletions
+8 -6
View File
@@ -22,8 +22,8 @@
protected override async Task OnInitializedAsync()
{
QuizState.OnQuizRequested += HandleQuizRequested;
FocusMode.OnFocusModeChanged += StateHasChanged;
QuizState.OnQuizRequested += HandleQuizRequestedAsync;
FocusMode.OnFocusModeChanged += HandleUpdate;
await FocusMode.InitializeAsync();
}
@@ -54,16 +54,18 @@
}
}
private void HandleQuizRequested(string blockId)
private async Task HandleQuizRequestedAsync(string blockId)
{
_activeQuizBlockId = blockId;
StateHasChanged();
await InvokeAsync(StateHasChanged);
}
private Task HandleUpdate() => InvokeAsync(StateHasChanged);
public async ValueTask DisposeAsync()
{
QuizState.OnQuizRequested -= HandleQuizRequested;
FocusMode.OnFocusModeChanged -= StateHasChanged;
QuizState.OnQuizRequested -= HandleQuizRequestedAsync;
FocusMode.OnFocusModeChanged -= HandleUpdate;
if (_interopModule != null && _keydownHandler != null)
{
@@ -1,8 +1,8 @@
.home-reader-container {
width: 100%;
height: 100%;
max-width: 800px;
margin: 0 auto;
padding: 2rem;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
+10 -5
View File
@@ -1,5 +1,10 @@
@page "/not-found"
@layout MainLayout
<h3>Not Found</h3>
<p>Sorry, the content you are looking for does not exist.</p>
@page "/not-found"
@layout Layout.MainLayout
<div class="not-found-preloader">
<div class="preloader-robot">
<NexusIcon Name="robot" Size="64" class="neon-pulse" />
<div class="scan-line"></div>
</div>
<NexusTypography Variant="NexusTypography.TypographyVariant.UI">Synchronizowanie przestrzeni Nexus...</NexusTypography>
</div>
@@ -0,0 +1,42 @@
.not-found-preloader {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 60vh;
gap: 2rem;
}
.preloader-robot {
position: relative;
padding: 1rem;
}
.scan-line {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 2px;
background: var(--nexus-neon);
box-shadow: 0 0 10px var(--nexus-neon);
animation: scan 2s linear infinite;
}
@keyframes scan {
0% { top: 0; }
50% { top: 100%; }
100% { top: 0; }
}
.neon-pulse {
color: var(--nexus-neon);
filter: drop-shadow(0 0 5px var(--nexus-neon));
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.1); opacity: 0.8; }
100% { transform: scale(1); opacity: 1; }
}