From d9ad7755da6c8f8c18ed4101349898a42f523bab Mon Sep 17 00:00:00 2001 From: Cyrus Gray <144336577+graycyrus@users.noreply.github.com> Date: Wed, 10 Jun 2026 19:08:53 +0530 Subject: [PATCH] feat(brain): ambient-glow loading overlay (#3586) --- app/src/pages/Brain.tsx | 77 ++++++++++++++++++++++---- app/src/pages/__tests__/Brain.test.tsx | 17 ++++++ 2 files changed, 84 insertions(+), 10 deletions(-) diff --git a/app/src/pages/Brain.tsx b/app/src/pages/Brain.tsx index b111057e1..dd3b2b574 100644 --- a/app/src/pages/Brain.tsx +++ b/app/src/pages/Brain.tsx @@ -200,21 +200,46 @@ export default function Brain() { ) : null} - {/* Loading overlay — the "brain collecting information" flourish. Opaque - and viewport-filling so the whole page (header, toolbar, graph, and the - panels below) stays hidden until the graph is ready. `fixed` keeps it - centered in the viewport regardless of page height; z-40 sits below the - bottom nav bar (z-50) so navigation stays available. */} + {/* Loading overlay — the "brain collecting information" flourish, dressed + as an ambient ocean-glow scene: a radial gradient wash, a soft pulsing + halo, and the brain floating above it. Opaque and viewport-filling so + the whole page (header, toolbar, graph, and the panels below) stays + hidden until the graph is ready. `fixed` keeps it centered regardless + of page height; z-40 sits below the bottom nav bar (z-50) so navigation + stays available. All motion layers are dropped under reduced motion — + which falls back to a static knowledge-node glyph instead of the + looping Lottie. */} {!overlayDone && (
- {!reduceMotion.current && ( - - )} -

+ {/* Ambient ocean-glow wash filling the viewport behind everything. */} +

+ + {/* Brain over a soft pulsing halo. The halo breathes (glow-pulse) and + the brain floats; both are stilled under reduced motion. */} +
+
+ {reduceMotion.current ? ( + + ) : ( +
+ +
+ )} +
+ +

{t('brain.loading')}

@@ -224,3 +249,35 @@ export default function Brain() {
); } + +/** + * Static knowledge-node glyph shown in the loading overlay under reduced + * motion — a central hub linked to satellite nodes, echoing the graph the + * page is about to reveal, with no animation. + */ +function BrainGlyph() { + return ( + + + + + + + + + + + + + + + + ); +} diff --git a/app/src/pages/__tests__/Brain.test.tsx b/app/src/pages/__tests__/Brain.test.tsx index 771163a5c..d9302e23b 100644 --- a/app/src/pages/__tests__/Brain.test.tsx +++ b/app/src/pages/__tests__/Brain.test.tsx @@ -102,6 +102,23 @@ describe('Brain page', () => { expect(screen.getByTestId('memory-graph')).toHaveTextContent('nodes:0'); }); + it('uses the static knowledge-node glyph (no Lottie) under reduced motion', async () => { + // Reduced motion → the floating Lottie is replaced by the still glyph. + window.matchMedia = vi + .fn() + .mockReturnValue({ + matches: true, + addEventListener: vi.fn(), + removeEventListener: vi.fn(), + }) as unknown as typeof window.matchMedia; + graphExportMock.mockResolvedValue(makeGraph(2)); + render(); + + expect(screen.getByTestId('brain-loading')).toBeInTheDocument(); + expect(screen.getByTestId('brain-glyph')).toBeInTheDocument(); + expect(screen.queryByTestId('brain-lottie')).toBeNull(); + }); + it('surfaces an error and dismisses the overlay when the fetch fails', async () => { graphExportMock.mockRejectedValue(new Error('boom')); render();