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

# Dictation page

> **Where you are:** [Architecture map](../../README.md) → [Frontend](../README.md) → [Pages overview](../pages-overview.md) → Dictation

**Plain English summary.** This is the recording surface. Press the hotkey or the mic button, watch live partial text stream into a textarea, then on stop run the AI cleanup, extract tasks, copy to clipboard or paste into the focused application. The page handles model loading, the live transcription session, the preview overlay, optional template insertion, and task extraction.

## At a glance

- **Path:** `src/lib/pages/DictationPage.svelte`
- **LOC:** 1 100
- **Imports (selected):**
  - Tauri: `Channel`, `invoke` from `@tauri-apps/api/core`. `emit` from `@tauri-apps/api/event`. `getCurrentWindow` from `@tauri-apps/api/window`.
  - Stores: `page`, `settings`, `templates`, `profiles`, `addToHistory`, `addTask`, `tasks` from `page.svelte.ts`. `markGenerating`, `markGenerationDone` from `llmStatus.svelte.ts`. `profilesStore` from `profiles.svelte.ts`. `toasts`. `getPreferences` from `preferences.svelte.ts`.
  - Components: `Card`, `ModelDownloader`, `EmptyState`, `SpeakerButton`.
  - Utils: `exportTranscript`, `extractTasks`, `pad`, `FEEDBACK_TIMEOUT_MS`, `bionicReading` action, `measurePreWrap`, `transcriptPretextFont`, `transcriptPretextLineHeight`, `playStartCue`, `playStopCue`, `playCompleteCue`.
  - External: `lucide-svelte` icons (`Mic`, `Loader2`, `SquareCheck`, `AlertTriangle`).
- **Used by:** `src/routes/+page.svelte:20` when `page.current === "dictation"`. Also forced by global hotkey and first run path.

## What's in here

### Local state (selected)

- `transcript` (string), `segments` (array of `{start, end, text}`).
- `recording` is on the global `page` store; this page mirrors it.
- `timerInterval`, `startTime`, `timerText` (mm:ss).
- Model lifecycle flags: `modelReady`, `modelLoading`, `needsDownload`.
- AI flags: `aiProcessing`, `aiStatus`, `extractedCount`.
- Live session: `sessionId`, `drainingSessionId`, `lastResultAt`, `lastLiveActivityAt`, `liveWarning`.
- Tauri channels (typed): `resultChannel`, `statusChannel` opened by `new Channel(...)`.
- UI: `showExportMenu`, `saved`, `insertPos` (cursor-based insertion), `activeTemplate`.
- `runtimeCapabilities` (from `get_runtime_capabilities`, used to decide engine availability and CUDA presence).

### Lifecycle

- `onMount`. Loads `runtimeCapabilities` (`DictationPage.svelte:134`). Sets up the global hotkey custom event listener (`lumotia:toggle-recording`, dispatched from the `+layout.svelte` hotkey path).
- `onDestroy`. Tears down listeners and timers.

### Recording flow (high level)

1. Toggle from the mic button or the `lumotia:toggle-recording` window event.
2. If model not ready, ensure model: check `check_engine`, `check_parakeet_engine`, `check_llm_model`, then load via `load_model` / `load_parakeet_model` / `load_llm_model` as required (`DictationPage.svelte:241-272`).
3. Open two `Channel<message>` instances (`DictationPage.svelte:341-342`) and call `start_live_transcription_session` with the channel handles.
4. As the backend pushes status and partial result messages, append text to `transcript`, update `segments`, and broadcast `preview-append` to the preview overlay window (`DictationPage.svelte:165, 381, 385`). If the preview is enabled and not already open, `open_preview_window` is invoked.
5. On stop, call `stop_live_transcription_session`, then run AI cleanup (`cleanup_transcript_text_cmd`) gated on `get_llm_status` (`DictationPage.svelte:464-472`).
6. Optionally extract tasks via `extract_tasks_from_transcript_cmd` (`DictationPage.svelte:487-491`) and add them via the `addTask` store helper.
7. Push to history with `addToHistory` (which calls `add_transcript`).
8. Auto copy and/or auto paste based on `settings.autoCopy` / `settings.autoPaste`. Paste path uses `paste_text` (`DictationPage.svelte:544-554`); fallback to `copy_to_clipboard`.
9. Emit `preview-cleanup`, `preview-final`, `preview-hide` to drive the overlay state machine (`DictationPage.svelte:531, 539, 606`).

## Tauri command surface

`get_runtime_capabilities`, `check_llm_model`, `load_llm_model`, `load_parakeet_model`, `load_model`, `start_live_transcription_session`, `stop_live_transcription_session`, `open_preview_window`, `get_llm_status`, `cleanup_transcript_text_cmd`, `extract_tasks_from_transcript_cmd`, `paste_text`, `copy_to_clipboard`.

Plus `emit("preview-append" | "preview-listening" | "preview-cleanup" | "preview-final" | "preview-hide")`.

## Watch outs

- The page uses `// @ts-nocheck`. Adding type safety here would catch a class of regressions, especially around the `Channel<T>` payload shape.
- The cursor based insertion (`insertPos`) is fragile. Editing the textarea while a live session is running can corrupt the segment offset map. Tests around `extractTasks` only cover the post stop path.
- Multiple paths can flip `recording` (button, hotkey custom event, error early-out). Treat the page as a state machine with a single source of truth and you will save yourself.
- `extractedCount` is purely cosmetic. It is reset on recording start.
- The "live activity" stalled detector is timer based (`lastLiveActivityAt`). On a stalled session the user sees `liveWarning`. The recovery path is "stop and start again".

## See also

- [Stores](../stores.md). `page`, `settings`, `llmStatus` reactivity.
- [Frontend ↔ Tauri bridge](../frontend-tauri-bridge.md). All commands and events.
- [Components](../components.md). `Card`, `ModelDownloader`, `SpeakerButton`, `EmptyState`.
- [../03-audio-transcription/README.md](../../03-audio-transcription/README.md). Live session plumbing on the Rust side.
- [../04-llm-formatting-mcp/README.md](../../04-llm-formatting-mcp/README.md). Cleanup and task extraction commands.
