* fix(control-plane): stop next-intl parsing 'type:<x>' tag hint as an unclosed tag The Tags hint used 'type:<x>', which next-intl reads as an unclosed rich-text tag (INVALID_MESSAGE: UNCLOSED_TAG), crashing the create/edit page dialog. Replace the angle-bracket placeholder with 'type:…' across all locales. * refactor(control-plane): replace loading-emoji hourglasses with a shared Spinner The UI used a spinning ⏳ emoji for loading in several places — inconsistent and not accessible. Add a shared <Spinner> (wrapping lucide Loader2, sized xs–xl, role=status) and use it for all 7 loading states: the large centered 'loading…' placeholders (documents / document / chunks / chunk-modal) and the inline save-button spinners (tags / content / edit-memory). * feat(control-plane): branded tumbling-logo spinner for loading states Add a LogoSpinner that renders the Hindsight mark doing a looping 2D 'tumble' (crouch → hop + 360° flip → land squash), ported from the motion lab. The hop is expressed as a % of the element so one keyframe scales across sizes, and reduced-motion falls back to an opacity pulse. Use it for the prominent centered loading states (documents list, document/ chunk modal); the compact lucide Spinner stays for tiny inline spots. * feat(control-plane): make the tumbling logo the shared spinner everywhere Fold LogoSpinner into the shared Spinner so every loading state renders the Hindsight mark, and swap all remaining ad-hoc spinners (lucide Loader2, the hand-rolled ring-<div> spinners, the sonner toast loader, and the two RefreshCw-as-loader stand-ins) over to it — ~65 sites across 22 files. Spinner now has two motions: - variant "flip" (default): an in-place 360° flip + squash, safe inline and in buttons (only a tiny vertical bob, stays on the text baseline). - variant "jump": the full tumble (crouch + hop + flip), for prominent centered loaders that have vertical room. RefreshCw icons that spin only while refreshing are left as-is — a spinning refresh arrow reads as "refreshing", which is distinct from a generic loader. * fix(control-plane): show the loader (not empty state) on Documents, and finish the spinner sweep Documents: the mount fetch is debounced, so the empty state ("No documents found" / "0 documents") flashed before the spinner. Track a `loaded` flag and gate the empty state + count on it, so the loader shows until the first fetch resolves. Also swap the 📄/📊 emoji empty states for lucide icons. Finish converting the loaders the first sweep missed (they used non-spinner patterns, so grepping for Loader2/animate-spin didn't find them): - pulsing Clock full-view loaders → jump Spinner (stats page, bank profile) - emoji + text-only loaders (entities graph/list/linked-memories, llm-requests and audit-logs chart loaders) → Spinner - emoji empty states (no chunks / no entities / no data) → lucide icons * fix(control-plane): render the Documents loader on first paint (hard refresh) On a hard refresh, bank-context starts currentBank=null and only resolves it from the URL in an effect (after the first paint, before hydration + theme), so the previous `!!currentBank` guard made the empty state win the very first render — you'd see "No documents found" flash before anything else. Gate the loader on `!loaded` alone. currentBank always resolves on a /banks/[id] route and the fetch's finally flips `loaded` (even on an invalid bank whose fetch errors), so it can't get stuck. Verified: the SSR HTML now renders the loading Spinner, not the empty state. * fix(control-plane): apply theme before first paint (no light flash on hard refresh) ThemeProvider only reads the saved/system theme in a useEffect (after paint), so a dark-mode user saw a light flash on every hard refresh. Add a tiny blocking inline script as the first <body> child that sets the .dark class synchronously before the content paints — the standard anti-FOUC pattern (what next-themes does internally). <html> already has suppressHydrationWarning for the class mutation. Logic mirrors lib/theme-context.tsx exactly (saved || system). * feat(control-plane): add the tumbling mascot to the no-bank welcome screen Loop the jump Spinner above 'Welcome to Hindsight' as a friendly greeting on the dashboard shown when no bank is selected. * feat(control-plane): spin the header logo on sidebar navigation Clicking a sidebar item now gives the header Hindsight logo a one-shot 'spin round' — a little playful nav feedback. The sidebar dispatches a 'hindsight:logo-spin' window event (decoupled, like DOCUMENTS_REFRESH_EVENT) and the header listens and toggles a one-shot animation, cleared on animationEnd. The logo is a wide lockup, so a 2D rotate would swing it vertical and overflow the header — use a rotateY card-flip that stays within its footprint instead. * feat(control-plane): spin only the logo mark (not the wordmark) on navigation Split the header lockup into two pieces: the octopus mark (favicon.png, a standalone image so it can rotate freely) and the 'Hindsight' wordmark (the right slice of the full logo.png, shown via a cropped background). Their widths sum to the full logo so they butt together seamlessly. Only the mark carries the one-shot spin, so the wordmark stays put. Because the mark alone is ~square, the spin is now a clean 2D rotate (reverted the rotateY card-flip workaround that the wide full lockup had required). * feat(control-plane): soften logo nav feedback from a spin to a subtle wiggle A full 360 rotate was too much. Replace it with a small tilt that springs back (logo-wiggle, ~450ms) on the mark — less impactful but still a bit of life. * chore(control-plane): drop the dead logo-spin-once reduced-motion block Leftover from the rename to logo-wiggle. * style(control-plane): apply prettier formatting to the spinner-sweep files Ran scripts/hooks/lint.sh — the earlier commits were eslint-clean but not prettier-formatted, which tripped verify-generated-files.
153 KiB
1268x767px
153 KiB
1268x767px