---
name: Stores
type: architecture-map-page
slice: 01-frontend
last_verified: 2026/05/09
---

# Stores

> **Where you are:** [Architecture map](../README.md) → [Frontend](README.md) → 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:**
  - `localStorage` `storage` event 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 to `localStorage["lumotia_settings"]` via `utils/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`, `taskLists` arrays.
- `tasks` and `history` arrays.

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_cmd` Rust commands. `completeTask` and friends emit `lumotia:task-completed | -uncompleted | -deleted` window events (line 470 onward).
- Task lists: `addTaskList`, `renameTaskList`, `deleteTaskList`, `moveTaskList`, `sortTaskLists`, `moveTaskToList`, `moveTaskListToProfile`.
- Profile mutators: `addProfileTaskList`, `removeProfileTaskList`.
- `saveSettings`, `saveProfiles`, `saveTemplates` write to `localStorage`.

### `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: `broadcastPreferences` calls `emit("lumotia:preferences-changed", { source, prefs })`. Receivers in `+layout.svelte` and the secondary `+layout@.svelte` files apply external prefs after a label check.
- Exposes `getPreferences`, `updatePreferences`, `updateAccessibility`, `applyExternalPreferences`, `PREFERENCES_CHANGED_EVENT` constant.
- 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)` calls `get_llm_status` and updates the chip.
- `markGenerating()`, `markGenerationDone()` are called around `cleanup_transcript_text_cmd` in `DictationPage.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.

- `setInterval` at 250 ms (`TICK_INTERVAL_MS`).
- Triggered by `lumotia:start-timer` window events. Emits `lumotia:focus-timer-cancelled` and `lumotia:focus-timer-complete` on 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 call `tts_speak` if speak aloud is enabled (line 170).
- Uses `delete_implementation_rule` to remove a rule (line 82).
- Emits `lumotia:implementation-rules-changed` after writes.
- Lifecycle: `startImplementationIntentions`, `stopImplementationIntentions` from `+layout.svelte`.

### `nudgeBus.svelte.ts` (292 LOC).

Owns the nudge engine. Two `setInterval` handles:
- `blurCheckHandle` for window blur detection.
- `triagePollHandle` at 5 minute cadence checking morning triage triggers.
- Plus a `microStepTimers` Map 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_transcript` Rust → push to history.
- `MicroSteps` "start timer" button → `lumotia:start-timer` → `focusTimer` store transitions → `FocusTimer` component renders ring.
- `TasksPage.addTask` → `tasks` store → emits `lumotia:task-completed/...` → `completionStats` refreshes → `CompletionSparkline` re renders.
- `SettingsPage` accessibility toggle → `updatePreferences` → DOM write + `save_preferences` invoke + `lumotia:preferences-changed` emit → secondary windows mirror.
- Float window settings sync uses `localStorage` `storage` event, not the Tauri preference event. Drift candidate.

## Watch outs

- `page.svelte.ts` is doing a lot. Splitting transcripts, tasks, task lists, settings, profiles into separate stores would help but is outside this map's job.
- `settings` and `preferences` overlap (theme, font size, `transcriptSize`). Settings are localStorage only; preferences are Tauri persisted. Race possible during the migration `$effect`.
- `nudgeBus` and `implementationIntentions` both run timers. They are torn down in `+layout.svelte` `onDestroy`. Only the main window starts them.
- `completionStats` listens on the DOM `window` for events; it does not subscribe to `tasks` directly. Adding a new task mutation that does not emit one of the `lumotia:task-*` events will silently break the sparkline.

## See also

- [Components](components.md). Consumers.
- [Frontend ↔ Tauri bridge](frontend-tauri-bridge.md). The `invoke` and `emit` calls per store.
- [Actions, utils, types](actions-utils-types.md). `settingsMigrations`, `errors`, `storage`, `time`, `osInfo`, `runtime` helpers used by stores.
