--- 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: `