commit ad9f97d129ac761c0c5f5537e519caf358a7b142 Author: Theo Weijmer Date: Thu Oct 1 09:29:49 2026 +0200 Add push-to-talk dictation design spec Co-Authored-By: Claude Sonnet 5.5 diff --git a/docs/superpowers/specs/2026-10-01-push-to-talk-dictation-design.md b/docs/superpowers/specs/2026-10-01-push-to-talk-dictation-design.md new file mode 100644 index 0000000..4869425 --- /dev/null +++ b/docs/superpowers/specs/2026-10-01-push-to-talk-dictation-design.md @@ -0,0 +1,82 @@ +# Push-to-talk dictatie-app voor macOS — ontwerp + +Datum: 2026-10-01 · Status: concept ter review · Werktitel: `Sst` + +## Doel + +Een systeembrede dictatie-app, vergelijkbaar met Wispr Flow. De gebruiker houdt **Fn** ingedrukt, spreekt, laat los, en de transcriptie verschijnt op de cursor in elke app. Alles draait lokaal op de Mac. Nederlands en Engels mogen door elkaar gebruikt worden. + +## Buiten scope (v1) + +- AI-cleanup (vulwoorden, zelfcorrecties, toon per app). Alleen de naad (`Cleaner`) wordt voorbereid. +- Streaming-transcriptie tijdens het praten. +- Mac App Store-distributie, accounts, sync, cloud. +- Branding. Die volgt als aparte stap na de werkende kern. + +## Doelomgeving + +Apple M4 Pro, 24 GB RAM, macOS 27, Xcode 27, Swift 6.4. Alleen Apple Silicon. + +## Aannames + +- Persoonlijk gebruik, niet-gesandboxed. De Mac App Store-sandbox verbiedt globale key-taps en gesimuleerd plakken. +- Niets verlaat de Mac. Audio wordt niet bewaard. +- Spraakmodel: Whisper `large-v3-turbo` via WhisperKit, met automatische taalherkenning. + +## Architectuur + +Een macOS-menubalk-app (SwiftUI `MenuBarExtra`). De logica staat in een Swift Package (testbaar met `swift test`). De Xcode-app is een dunne schil eromheen. + +| Unit | Taak | Afhankelijk van | +|---|---|---| +| `HotkeyMonitor` | Detecteert Fn omlaag/omhoog via `CGEventTap`; stuurt `pressed`/`released` events | Input Monitoring | +| `AudioRecorder` | `AVAudioEngine` neemt op naar een 16 kHz mono Float-buffer; start bij `pressed`, stopt bij `released` | Microfoon | +| `Transcriber` (protocol) | `transcribe(samples) async throws -> String`; eerste implementatie `WhisperKitTranscriber` | WhisperKit | +| `Cleaner` (protocol) | `clean(text, context) async throws -> String`; v1 `PassthroughCleaner` | n.v.t. | +| `TextInserter` | Zet tekst op het klembord, simuleert ⌘V, herstelt het klembord | Accessibility | +| `DictationController` | Statemachine `idle → recording → transcribing → inserting → idle`; enige component dat de andere kent | bovenstaande | +| `OverlayPanel` | Floating `NSPanel` met pilletje: opname, golfvorm, bezig | SwiftUI | +| `MenuBarExtra` | Status, instellingen, model-download, rechten-check | n.v.t. | + +Ontwerpprincipes: elke unit heeft één taak en een duidelijke interface. `Transcriber` en `Cleaner` zijn protocollen, zodat modellen (bijvoorbeeld Parakeet of een LLM) later verwisselbaar zijn. Tests gebruiken fakes voor de protocollen. + +## Dataflow + +1. Fn omlaag: opname start, overlay verschijnt. +2. Fn omhoog: opname stopt. +3. Samples gaan naar `Transcriber`, daarna naar `Cleaner`. +4. `TextInserter` voegt de tekst in; overlay verdwijnt. + +Het model wordt bij het opstarten geladen en blijft warm in het geheugen. + +## Foutafhandeling + +- Opname korter dan ~0,3 s: negeren (per ongeluk aangetikt). +- Rechten ontbreken: menubalk toont wat ontbreekt, met knop naar de juiste Systeeminstellingen. +- Model nog niet gedownload: eerste start toont voortgang; dictatie is dan uitgeschakeld. +- Invoegen mislukt: tekst blijft op het klembord, overlay meldt dit. +- Stilte of lege transcriptie: niets invoegen. +- Transcriptiefout: overlay toont een korte foutmelding; de controller keert terug naar `idle`. + +## Vereiste rechten + +Microfoon, Accessibility (plakken simuleren), Input Monitoring (Fn-toets). + +## Testen + +- Unit-tests voor `DictationController` met fake `Transcriber`, `Cleaner`, `TextInserter` (toestandsovergangen, korte opnames, foutpaden). +- Unit-test voor het klembord-herstel in `TextInserter` (injecteerbare pasteboard). +- Handmatige rooktest van de hele keten in TextEdit, Notes en een browser: Nederlands, Engels en gemengd. + +## Bouwvolgorde (skeleton eerst) + +1. Package plus menubalk-app; hotkey logt alleen "recording…". +2. Opnemen naar een buffer. +3. WhisperKit transcriberen. +4. Tekst invoegen. +5. Overlay-pill. +6. Cleanup, instellingen en branding (aparte specs). + +## Open punten + +Geen voor het skeleton. De definitieve naam en branding worden in een latere stap bepaald.