Files
Lumotia/docs/architecture-map/01-frontend/windows-and-routes.md
Jake 26c7307607
Some checks failed
check / cargo check (macos-latest) (push) Has been cancelled
check / cargo check (ubuntu-22.04) (push) Has been cancelled
check / cargo check (windows-latest) (push) Has been cancelled
check / svelte build + lint (push) Has been cancelled
agent: lumotia-rebrand — docs, scripts, root config, residuals
Phase 9 of the rebrand cascade. Sweep covers everything the Phase 8
frontend pass deliberately skipped: docs/, root markdown, scripts,
Cargo.toml descriptions, code comments that survived earlier
word-boundary sed, plus a handful of identifiers caught on the final
verify pass.

transcription-app changes:
- README.md, HANDOVER.md, KNOWN-ISSUES.md, run.sh — magnotia/Magnotia
  -> lumotia/Lumotia.
- docs/ — sweep across all subdirs except docs/handovers/ (preserved
  as immutable audit trail). Includes architecture-map references
  to magnotia_core::*, magnotia_storage::*, etc. now pointing at
  lumotia_*; dev-setup.md tracing output examples (lumotia_startup
  target); brief/ + superpowers/ + issues/ + whisper-ecosystem/ +
  audit/.
- Cargo.toml descriptions on 9 crates (core, audio, cloud-providers,
  hotkey, llm, mcp, plus referenced others).
- crates/core/src/{error,hardware,recommendation,paths}.rs +
  crates/audio/src/wav.rs + crates/llm/src/model_manager.rs +
  crates/cloud-providers/src/keystore.rs + crates/mcp/src/lib.rs —
  doc comments and a model-manager user-agent string.
- Caught on final pass: BroadcastChannel("magnotia_task_sync") -> ...
  ("lumotia_task_sync"); magnotia_locale i18n localStorage key
  renamed + migration shim added; CSS keyframe names
  magnotiaPulse / magnotiaBar / magnotiaFade renamed in the design-
  system kit; magnotia_viewer_item / magnotia_viewer_mode handoff
  keys renamed in HistoryPage + viewer/+page.svelte; src/assets/
  wordmark.svg text.
- src-tauri/src/lib.rs comment cleanup ("magnotia era" was sed'd
  to "lumotia era" earlier — restored).

Preserved (intentional):
- crates/core/src/paths.rs — keeps "magnotia" / "Magnotia" / ".magnotia"
  legacy detection strings in legacy_and_target_paths() so the
  migration shim can still find user data from the magnotia era.
- src/lib/stores/{page,focusTimer}.svelte.ts + src/lib/i18n/index.ts
  — migration call sites reference the legacy magnotia keys
  deliberately.
- docs/handovers/ — historical audit trail.

cargo build --workspace passes. npm run check: 0 errors / 0 warnings
(3958 files). cargo test --workspace: 339 pass / 0 fail.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 12:38:03 +01:00

9.6 KiB

name, type, slice, last_verified
name type slice last_verified
Windows and SvelteKit routes architecture-map-page 01-frontend 2026/05/09

Windows and routes

Where you are: Architecture mapFrontend → 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 <body> 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: <ToastViewport />, <FocusTimer />, <MorningTriageModal />, <ResizeHandles /> (custom chrome only).

src/routes/+page.svelte (33 LOC)

Pure dispatcher. Switches the seven page modules from page.current:

  • first-runFirstRunPage
  • dictationDictationPage
  • filesFilesPage
  • tasksTasksPage
  • historyHistoryPage
  • settingsSettingsPage
  • shutdownShutdownRitualPage

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 <audio> element using convertFileSrc() to map the saved audio path to a webview URL. Provides per segment editing, debounced autosave through saveTranscriptMeta, virtual scrolling via VirtualSegmentList, playback speed control (PLAYBACK_SPEEDS), starring and tagging.

src/routes/preview/+layout@.svelte (68 LOC)

Mounts only what the overlay needs: theme, preferences sync. No sidebar, no titlebar, no toast viewport.

src/routes/preview/+page.svelte (274 LOC)

Live transcription overlay. Listens for preview-listening, preview-cleanup, preview-hide (and reads partial text via the same Channel pattern as Dictation, in some flows). State machine: listening → live → cleanup → final → auto hide. Provides a copy button (uses navigator.clipboard.writeText first, falls back to copy_to_clipboard invoke) and a revert to raw button. Auto hide timer constants live at the top of the file.

Data flow

  • Window creation: Rust opens four webviews and routes them to /, /float, /viewer, /preview. The same JS bundle loads in each.
  • Cross window state:
    • localStorage carries lumotia_settings and the viewer handoff payload. Float window listens on the browser storage event to mirror settings.
    • Tauri events carry preferences (lumotia:preferences-changed), tray driven navigation (lumotia:open-wind-down), and float window focus (task-window-focus).
  • Hotkey: only the main window registers, but every window's layout includes the migration $effect. The label guard at +layout.svelte:115-121 prevents secondary windows from re registering.
  • First run gating: only the main window evaluates and sets page.current = "first-run". The float/viewer/preview windows skip the shell altogether.

Watch outs

  • The useCustomChrome flash is mitigated by defaulting to false on Linux. If you ever flip the default, expect a one frame flash of custom chrome before loadOsInfo() resolves.
  • The break layouts duplicate the theme migration $effect. Touch one, touch all. Same for applyExternalPreferences listeners.
  • transcription-preview is a borderless overlay with WindowTypeHint = Utility. Layout assumptions about chrome height do not apply.
  • +layout.svelte calls applyExternalPreferences only after a payload source check. If you ever add a fifth window, its label must be propagated correctly or the window will echo its own preference writes.
  • Drag drop on FilesPage.svelte listens on tauri://drag-drop events, which require the window to declare drag drop in tauri.conf.json. Slice 02 owns that surface.

See also