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>
8.3 KiB
name, type, slice, last_verified
| name | type | slice | last_verified |
|---|---|---|---|
| Stores | architecture-map-page | 01-frontend | 2026/05/09 |
Stores
Where you are: Architecture map → Frontend → Stores
Plain English summary. Reactive state. Lumotia uses Svelte 5 runes ($state, $derived, $effect) instead of legacy stores. Each file under src/lib/stores/ exports one (or more) $state objects plus the helper functions that read or mutate them. Components import the state directly and Svelte tracks reactivity automatically. Two persistence channels: localStorage for settings, profiles, task lists, templates; Tauri (save_preferences) for accessibility preferences.
At a glance
- Path:
src/lib/stores/ - Files: 10 (4 085 LOC across stores + utils + types).
- Cross window sync:
localStoragestorageevent in the float window for settings.- Tauri
emit("lumotia:preferences-changed")for preferences (handled by every layout).
- Persistence keys:
lumotia_settings,lumotia_profiles,lumotia_task_lists,lumotia_templates,lumotia_locale, plus a viewer handoff key.
The stores
page.svelte.ts (697 LOC). The big one.
Owns:
page(PageState):current,status,statusColor,activeProfile,recording,timerText,handedness,taskSidebarOpen. Initial:current = "dictation".settings(SettingsState). Persisted tolocalStorage["lumotia_settings"]viautils/settingsMigrations.ts. Defaults at line 60 onward. Includes engine choice, model size, language, device, format mode, fillers, anti hallucination, British English, auto copy/paste, transcription preview, meeting auto capture (+ apps list), sound cues + volume, timestamps, theme, font size, AI tier, LLM model id and prompt preset, GPU concurrency, prewarm flag, save audio, output folder, global hotkey, sidebar collapsed, microphone device, energy state, match my energy, TTS voice + rate, rituals (morning/evening + time), launch at login, ritualsPromptSeen, nudges (enabled/muted/speakAloud), showMomentumSparkline.profiles,templates,taskListsarrays.tasksandhistoryarrays.
Helpers (selected, with line refs):
addToHistory→add_transcript(page.svelte.ts:234).mapTranscriptRow,saveTranscriptMeta→update_transcript(page.svelte.ts:272).deleteFromHistoryById→delete_transcript(page.svelte.ts:320).addTask,completeTask,uncompleteTask,deleteTask,setTaskEnergy,updateTask→ matching*_task_cmdRust commands.completeTaskand friends emitlumotia:task-completed | -uncompleted | -deletedwindow events (line 470 onward).- Task lists:
addTaskList,renameTaskList,deleteTaskList,moveTaskList,sortTaskLists,moveTaskToList,moveTaskListToProfile. - Profile mutators:
addProfileTaskList,removeProfileTaskList. saveSettings,saveProfiles,saveTemplateswrite tolocalStorage.
preferences.svelte.ts (172 LOC).
Owns the Preferences shape: theme ("light" | "dark" | "system"), zone ("default" | ...), accessibility (fontFamily, fontSize, letterSpacing, lineHeight, transcriptSize, bionicReading, reduceMotion).
- DOM is the source of truth at runtime:
readFromDOM()reads<html>data attributes and CSS variables;applyToDOM(prefs)writes them. - Persists via
invoke("save_preferences", { preferences: JSON.stringify(prefs) })with a debounce. Failure shows a single toast per process (preferences.svelte.ts:101-120). - Cross window:
broadcastPreferencescallsemit("lumotia:preferences-changed", { source, prefs }). Receivers in+layout.svelteand the secondary+layout@.sveltefiles apply external prefs after a label check. - Exposes
getPreferences,updatePreferences,updateAccessibility,applyExternalPreferences,PREFERENCES_CHANGED_EVENTconstant. - Font families resolved from a constant map (Lexend, Atkinson, OpenDyslexic).
profiles.svelte.ts (123 LOC).
The vocabulary profile store (separate from the profiles array on page.svelte.ts). Holds:
activeProfileId, list of profiles, vocabulary terms.
Tauri commands: load_profiles_cmd (implicit on load), update_profile_cmd, delete_profile_cmd, delete_profile_term_cmd, plus add term commands.
Exposes DEFAULT_PROFILE_ID.
toasts.svelte.ts (99 LOC).
Toast notifications. State is an array of {id, severity, title, body, duration}.
Helpers: toasts.info(title, body?), .warn, .error, .success, plus dismiss(id). Auto dismiss via setTimeout. Read by ToastViewport.svelte.
llmStatus.svelte.ts (64 LOC).
Tracks the LLM lifecycle pill. State is one of idle | loading | generating | downloading | unavailable.
refreshLlmStatus(aiTier)callsget_llm_statusand updates the chip.markGenerating(),markGenerationDone()are called aroundcleanup_transcript_text_cmdinDictationPage.svelte.
completionStats.svelte.ts (59 LOC).
Phase 8 gamification. Owns recentCompletions (DailyCompletionCount[]) and a todayCount derivation.
Refresh triggers (no polling): module load, lumotia:task-completed, lumotia:step-completed, lumotia:task-uncompleted, lumotia:task-deleted, window focus (for midnight rollover).
focusTimer.svelte.ts (238 LOC).
Owns the floating focus timer. State: target ms, started at, label, taskId, paused.
setIntervalat 250 ms (TICK_INTERVAL_MS).- Triggered by
lumotia:start-timerwindow events. Emitslumotia:focus-timer-cancelledandlumotia:focus-timer-completeon transitions.
implementationIntentions.svelte.ts (260 LOC).
"When X happens, do Y" rules engine. Owns the rules array.
- 30 second
setInterval(TIME_RULE_POLL_MS) checks time based rules. - On match, can route
page.current = "tasks"(lines 125, 136, 142) and calltts_speakif speak aloud is enabled (line 170). - Uses
delete_implementation_ruleto remove a rule (line 82). - Emits
lumotia:implementation-rules-changedafter writes. - Lifecycle:
startImplementationIntentions,stopImplementationIntentionsfrom+layout.svelte.
nudgeBus.svelte.ts (292 LOC).
Owns the nudge engine. Two setInterval handles:
blurCheckHandlefor window blur detection.triagePollHandleat 5 minute cadence checking morning triage triggers.- Plus a
microStepTimersMap for per task delayed notifications.
Key commands: deliver_nudge (line 112), tts_speak (line 119).
Emits lumotia:morning-triage-finished after the modal closes.
Lifecycle: startNudgeBus, stopNudgeBus from +layout.svelte.
speaker.svelte.ts (10 LOC).
Tiny. Holds speakingId so SpeakerButton instances can debounce / cancel each other. Candidate for collapsing into a util (README debt note 9).
Cross store traffic
DictationPage.completeRecording()→addToHistory(page) →add_transcriptRust → push to history.MicroSteps"start timer" button →lumotia:start-timer→focusTimerstore transitions →FocusTimercomponent renders ring.TasksPage.addTask→tasksstore → emitslumotia:task-completed/...→completionStatsrefreshes →CompletionSparklinere renders.SettingsPageaccessibility toggle →updatePreferences→ DOM write +save_preferencesinvoke +lumotia:preferences-changedemit → secondary windows mirror.- Float window settings sync uses
localStoragestorageevent, not the Tauri preference event. Drift candidate.
Watch outs
page.svelte.tsis doing a lot. Splitting transcripts, tasks, task lists, settings, profiles into separate stores would help but is outside this map's job.settingsandpreferencesoverlap (theme, font size,transcriptSize). Settings are localStorage only; preferences are Tauri persisted. Race possible during the migration$effect.nudgeBusandimplementationIntentionsboth run timers. They are torn down in+layout.svelteonDestroy. Only the main window starts them.completionStatslistens on the DOMwindowfor events; it does not subscribe totasksdirectly. Adding a new task mutation that does not emit one of thelumotia:task-*events will silently break the sparkline.
See also
- Components. Consumers.
- Frontend ↔ Tauri bridge. The
invokeandemitcalls per store. - Actions, utils, types.
settingsMigrations,errors,storage,time,osInfo,runtimehelpers used by stores.