--- name: Windows and SvelteKit routes type: architecture-map-page slice: 01-frontend last_verified: 2026/05/09 --- # Windows and routes > **Where you are:** [Architecture map](../README.md) → [Frontend](README.md) → Windows and routes **Plain English summary.** Lumotia is a single SvelteKit build that runs as multiple desktop windows. The Rust side opens four webviews (main, tasks float, transcript viewer, transcription preview) and points each one at a different SvelteKit route. The frontend uses SvelteKit's `+layout@.svelte` "break" trick so secondary windows skip the main shell (sidebar, titlebar, toast viewport) and render a focused, single purpose UI. ## At a glance - **Path:** `src/routes/` - **LOC:** 2 740 across the route tree. - **Key files:** - `src/routes/+layout.js:5`. Disables SSR. Adapter is `adapter-static` with `index.html` fallback (`svelte.config.js:6`). The whole tree is SPA only. - `src/routes/+layout.svelte`. The shell. Sidebar, custom titlebar (non Linux only), toast viewport, focus timer overlay, morning triage modal, resize handles, global hotkey wiring, OS detection, profile load, first run gate, error capture. - `src/routes/+page.svelte`. The main window's page switch (`page.current` → 7 page modules). - `src/routes/float/+layout@.svelte`. "Break" layout for the tasks float window. - `src/routes/float/+page.svelte`. Tasks float window UI. - `src/routes/viewer/+layout@.svelte`. Break layout for the transcript editor window. - `src/routes/viewer/+page.svelte`. Transcript editor with audio playback. - `src/routes/preview/+layout@.svelte`. Break layout for the live transcription preview overlay. - `src/routes/preview/+page.svelte`. Preview state machine: listening → live → cleanup → final. - **Imports:** - Internal: every store, plus shell components (`Sidebar`, `TaskSidebar`, `Titlebar`, `ToastViewport`, `ResizeHandles`, `FocusTimer`, `MorningTriageModal`). - External: `@tauri-apps/api/core` (invoke, Channel, convertFileSrc), `@tauri-apps/api/event` (listen, emit), `@tauri-apps/api/window` (getCurrentWindow), `@tauri-apps/plugin-global-shortcut`, `@tauri-apps/plugin-dialog`, `lucide-svelte`. - **Used by:** Tauri (slice 02). Window labels are `main`, `tasks-float`, `transcript-viewer`, `transcription-preview`. Each is created with the matching route URL. ## What's in here ### `src/routes/+layout.js` (5 LOC) Single line: `export const ssr = false;`. Tauri has no Node server, so SvelteKit must run as a SPA. ### `src/routes/+layout.svelte` (493 LOC) The shell. Mounts on every window (the secondary windows then render only `{@render children()}` and suppress the chrome). Responsibilities: - Initialise svelte-i18n (`initI18n()` is idempotent across windows, `+layout.svelte:38`). - Detect Tauri runtime (`hasTauriRuntime`) and OS (`loadOsInfo`). Linux uses native KWin/Mutter decorations; macOS and Windows render the custom `Titlebar` plus invisible `ResizeHandles`. Default `useCustomChrome = false` to avoid a flash on Linux (`+layout.svelte:49`). - Hotkey backend selection. On Wayland, attempt the evdev backend (`check_hotkey_access`). Otherwise fall back to `tauri-plugin-global-shortcut`. Falls back to "unavailable" if neither path works (`+layout.svelte:76-102`). - Hotkey registration. Only the `main` window owns the global shortcut. The function debounces evdev autorepeat at 120 ms (Handy issue #1143 referenced in comments, `+layout.svelte:171-186`). - Cross window listeners: `lumotia:hotkey-pressed` (evdev path), `lumotia:open-wind-down` (tray menu), `lumotia:preferences-changed` (sync prefs across windows). Apply external preferences if the source label differs from our own. - Theme migration `$effect`. Reads `settings.theme` (legacy "Light" / "Dark" / "System") and writes `preferences.theme` ("light" / "dark" / "system"). See README debt note 2. - Font size CSS variable `--font-size-transcript` set on `` from `settings.fontSize`. - Global error capture. `window.onerror` and `unhandledrejection` forward to `log_frontend_error` Rust command. Best effort, swallow throws. - First run check on mount. If `list_models` and `list_parakeet_models` both return empty arrays, switch `page.current` to `"first-run"`. - Background update check via `check_for_update`. Toast on result. - Pre warm default model on mount if `settings.prewarmModelOnStartup` (calls `prewarm_default_model_cmd`). - Meeting auto capture poller (`$effect`). When `settings.meetingAutoCapture` is true, polls `detect_meeting_processes` every 15 s with the configured patterns (default `["zoom", "teams"]`). Edge triggered (toast only on first match per session). Tear down on effect re run. - Sidebar collapse heuristic. `[` toggles, narrow viewport collapses automatically. - Render branches: `isSecondaryWindow` (URL starts with `/float` or `/viewer`) renders only children. Otherwise: optional Titlebar, sidebar (hidden on first run), main slot, optional task sidebar (when `page.taskSidebarOpen`). - Always rendered alongside: ``, ``, ``, `` (custom chrome only). ### `src/routes/+page.svelte` (33 LOC) Pure dispatcher. Switches the seven page modules from `page.current`: - `first-run` → `FirstRunPage` - `dictation` → `DictationPage` - `files` → `FilesPage` - `tasks` → `TasksPage` - `history` → `HistoryPage` - `settings` → `SettingsPage` - `shutdown` → `ShutdownRitualPage` Also redirects the legacy `page.current === "profiles"` to `"settings"` via `$effect` (debt note 5). ### `src/routes/float/+layout@.svelte` (99 LOC) The `@.svelte` suffix tells SvelteKit to skip parent layouts. Reimports `app.css`, mounts `Titlebar` (non Linux) and `FocusTimer`, applies the same theme migration `$effect`, listens on the browser `storage` event (not Tauri events) to sync `settings` from main window writes, and handles the `task-window-focus` Tauri event to glow and auto focus the quick add input. ### `src/routes/float/+page.svelte` (481 LOC) Tasks float window. Self contained quick add, list filter, sort menu, completed toggle, list management (rename, delete, reorder, move to profile), drag and drop reordering. Reads tasks straight from the `page.svelte.ts` store helpers. No `invoke()` calls of its own; mutations go via `addTask`, `completeTask`, etc, which then call Rust. ### `src/routes/viewer/+layout@.svelte` (77 LOC) Same break layout pattern. Mounts `Titlebar`, `ToastViewport`, applies preferences sync via `lumotia:preferences-changed`, applies theme. ### `src/routes/viewer/+page.svelte` (606 LOC) Transcript editor. Hydrates from a transcript ID handed off via `localStorage` (`lumotia:viewer-handoff` style key, see file). Fetches the full row from SQLite via `get_transcript`. Renders an `