Commit Graph
4 Commits
Author SHA1 Message Date
amosblomqvist 2d3f0e0412 feat: dictation round 2 — deferred transcript, adaptive meter
Owner's second live test:

- DEFERRED transcript: finals accumulate in a buffer during the run and
  flush into the primary input once, when dictation ends (Stop →
  CloseStream → Deepgram's last finals → socket close → one joined
  append; the 3s safety net covers a missing close). Nothing textual
  renders while recording — the meter is the only live feedback. Live
  output may return as a toggleable setting at the settings milestone;
  deferred is the default behavior.
- ADAPTIVE meter: cell count derives from the measured strip width (4px
  pitch, 8-48 cells, ResizeObserver; cells drop their minimum width) so
  narrow rows beside the knowledge-context slider fit — verified in the
  dev shell: draft 20 cells/83px, chat 48 cells/273px, no overlap.
2026-09-17 20:06:07 +03:00
amosblomqvist 4dbbfa6fe7 feat: dictation round 1 — primary-input routing, no interims, optimistic meter, wide button
Owner's first live mic test (2026-09-17) drove four changes:

- ONE primary transcription input at all times: while a quiz/ask
  question waits, finals land in its field (text mode → 'Your answer',
  select modes → 'Your thinking') via a module-level claim registry
  (useDictationTarget in dictation.tsx); the composer is the fallback.
  Esc in a question field also stops dictation (threaded through
  DockOverlay → QuestionDock).
- No interim results: live words were distracting and shifted the meter.
  Params now match the pi dictate extension exactly (no interim_results,
  endpointing=300, no utterance_end_ms); the meter's width stays fixed.
- Optimistic meter: the click enters the meter view immediately (Stop
  pill + meter within one frame, verified at 150ms in the dev shell);
  token mint and mic acquisition run concurrently behind it.
- Wide idle button: the stats-bar middle is one full-width affordance —
  a quiet bordered 'Dictate' button when idle (failure text replaces the
  label), Stop pill + fixed meter while live.

Live-verified in the Electron dev shell: wide button renders on home
(167px) and chat (358px), meter + Stop appear at 150ms after click,
flat 2px bars in silence, clean return to 'Dictate' on stop.
2026-09-17 19:59:14 +03:00
amosblomqvist 573e369db5 fix: dictation cloud path — bearer subprotocol, access_token grant field
Two live-verified Deepgram facts from the first real mic-test round:
- /v1/auth/grant returns { access_token }, not { token } — the route now
  parses the right field.
- Minted JWTs authenticate over the WebSocket via the "bearer"
  subprotocol; ["token", jwt] is rejected at the handshake (token is
  for raw API keys only, as in the pi dictate extension).

Also documents the env-precedence gotcha in docs/research/stt.md: a
DEEPGRAM_API_KEY exported in the shell (e.g. .zshrc) silently beats
.env.local in next dev and the desktop authEnv() seam.
2026-09-17 19:44:35 +03:00
amosblomqvist 6e08d77ec2 docs: STT research — verified Deepgram + whisper.cpp findings and decisions
Two researcher passes (2026-09-17), cross-checked against our codebase:
Deepgram streaming (WebSocket, Nova-3, SDK v5, $0.0048/min streaming
promo, $200 credit), AudioWorklet linear16 capture (MediaRecorder webm
has unresolved empty-transcript reports), endpointing/utterance_end/keep-
alive gotchas, 127.0.0.1 secure context, server-minted short-lived access
tokens (renderer never holds the real key); whisper.cpp CLI child process
(Metal RTF base 0.04/small 0.08, base.en 142MB/small.en 466MB, MIT) with
per-utterance batch flow and LH_DATA_DIR model cache. Decisions: toggle
mic, cloud-first wave scope, renderer->wss direct.
2026-09-17 17:18:41 +03:00