Files
Lumotia/src/routes/+layout.svelte
Jake a9733544c0 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>
2026-05-15 08:51:55 +01:00

37 lines
1.2 KiB
Svelte

<script lang="ts">
// @ts-nocheck
// v0.2 Phase 6 — thin composition. The three split-out shells own
// their respective responsibilities; this file just decides whether
// to render the chrome (main window) or pass children straight
// through (secondary windows like /float, /viewer, /preview, which
// also have their own +layout@.svelte to break out of this layout).
import "../app.css";
import { page as sveltePage } from "$app/stores";
import AppRuntime from "$lib/shell/AppRuntime.svelte";
import AppChrome from "$lib/shell/AppChrome.svelte";
import AppOverlays from "$lib/shell/AppOverlays.svelte";
let { children } = $props();
// Defensive: secondary windows use +layout@.svelte to escape this
// root layout, but if any /float, /viewer, /preview load ever
// reaches here we still drop the chrome.
let isSecondaryWindow = $derived(
$sveltePage.url.pathname.startsWith("/float") ||
$sveltePage.url.pathname.startsWith("/viewer") ||
$sveltePage.url.pathname.startsWith("/preview")
);
</script>
<AppRuntime />
{#if isSecondaryWindow}
{@render children()}
{:else}
<AppChrome>
{@render children()}
</AppChrome>
{/if}
<AppOverlays />