v0.2 Phase 6: shell split — AppRuntime / AppChrome / AppOverlays

src/routes/+layout.svelte was 537 LOC of mixed runtime, chrome and
overlay concerns. Split into three single-purpose shells under
src/lib/shell/, with +layout.svelte reduced to ~28 LOC of pure
composition.

AppRuntime (no DOM beyond <svelte:window>):
  - Global hotkey dual backend (evdev / tauri-plugin-global-shortcut)
  - 120ms hotkey debounce (sacred behaviour §5 #2)
  - PREFERENCES_CHANGED_EVENT listener (sacred §5 #4)
  - KI-05 one-shot legacy-theme migration
  - Sidebar hotkeys: [ toggle, Ctrl+K, Ctrl+, (sacred §5 #10)
  - Wind-down tray listener
  - Meeting auto-capture poller
  - Global frontend error capture
  - Nudge bus + implementation intentions lifecycle
  - Font-size CSS var $effect
  - Window resize → sidebar auto-collapse
  - Onboarding/first-run check + update check + LLM status warm-up

AppChrome (the visual shell):
  - Titlebar (OS-aware via customChrome helper)
  - Sidebar (recording-state-aware — sacred §5 #1 stays in
    Sidebar.svelte verbatim)
  - Main slot
  - TaskSidebar conditional rail

AppOverlays (mounted-once globals):
  - ToastViewport
  - FocusTimer
  - MorningTriageModal
  - ResizeHandles (OS-gated)

src/lib/utils/customChrome.svelte.ts holds the single source of truth
for useCustomChrome, a module-level $state both AppChrome and
AppOverlays subscribe to. Each only ever sees one loadOsInfo() call
between them.

Secondary windows still escape via their own +layout@.svelte; the
defensive isSecondaryWindow check in +layout.svelte stays so a
direct /float, /viewer, /preview navigation through the root layout
also drops the chrome.

Phase 6 per-page gate green: npm run check (0/0/5704 files),
npm test, npm run test:browser (3/3), npm run test:e2e (16/16).
No regressions in the Phase 1 smoke baseline.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-15 08:51:55 +01:00
parent c60f0aa5a5
commit a9733544c0
6 changed files with 530 additions and 538 deletions

View File

@@ -203,7 +203,7 @@ Filled during execution. Each entry: phase #, date, what landed, what's next.
| 3 | ✅ complete | New tokens `--color-caution`, `--color-info`, `--color-accent-environment` (dark + light), `--color-warning` aliased to `var(--color-caution)`. KI-05 resolved: `theme` dropped from SettingsState type + defaults, all four route-layout migration `$effect`s deleted, two SettingsPage SegmentedButton bindings repointed to `preferences.theme` via Svelte 5 function bindings, one-shot legacy-theme migration on first mount strips the field after copying. Gate green (check 0/0, vitest 13/13, e2e 16/16). |
| 4 | ✅ complete | Six wrapper aliases under `src/lib/ui/`: Lumotia{Card,StatusPill,Toggle,SettingsGroup,EmptyState,PostCaptureCard}. Same prop APIs, $bindable forwarded for Toggle. Underlying `src/lib/components/*.svelte` untouched. |
| 5 | ✅ complete | 11 primitives shipped under `src/lib/ui/`. `design-system-v2` preview route gated behind `VITE_LUMOTIA_DESIGN_SYSTEM_V2=1` (route-level 404 via `+page.ts` load, not nav-hidden). Browser-mode component test (LumotiaButton): 3/3 passing in Chromium. Gate green (check 0/0/5700 files, vitest 0/0, test:browser 3/3, e2e 16/16). |
| 6 | pending | Shell split |
| 6 | ✅ complete | `src/routes/+layout.svelte` (537 LOC) split into `$lib/shell/AppRuntime.svelte` (runtime listeners + hotkey + debounce + KI-05 migration + meeting poller + error capture), `$lib/shell/AppChrome.svelte` (titlebar + sidebar + task rail), `$lib/shell/AppOverlays.svelte` (toasts + focus timer + triage modal + resize handles). Shared `useCustomChrome` flag moved into `src/lib/utils/customChrome.svelte.ts` so both AppChrome and AppOverlays subscribe to the same reactive value. +layout.svelte is now ~28 LOC of pure composition. Gate green. |
| 7.1 ShutdownRitualPage | pending | |
| 7.2 FilesPage | pending | |
| 7.3 FirstRunPage | pending | |