Skip to content

feat(dictation): recording deck for voice dictation (timer, waveform, pause, retry, send modes) - #109

Merged
kahme247 merged 14 commits into
kahme247:mainfrom
andrebrait:feat/dictation-recording-deck
Sep 19, 2026
Merged

kahme247 merged 14 commits into
kahme247:mainfrom
andrebrait:feat/dictation-recording-deck

Conversation

@andrebrait

Copy link
Copy Markdown
Contributor

Summary

Replaces the plain mic toggle with a full recording deck while a dictation session is active, bringing the flow to parity with modern chat apps (ChatGPT/WhatsApp-style):

  • Recording deck replaces the composer textarea: pulsing-free status dot, elapsed timer (active time only), live waveform (AnalyserNode over the existing MediaStream), and a progress bar toward the existing 5-minute cap (which auto-converts on hit).
  • Explicit controls: pause/resume (MediaRecorder pause — timer and waveform freeze, waveform rendered gray while paused), stop-to-convert (transcribe into the composer), cancel/discard.
  • Composer Send becomes transcribe-and-send while recording: one press records-stop, transcribes, and dispatches — safe to pocket the phone. While the agent is streaming the same button takes the Queue state, mirroring the behavior of typed text (steer/follow-up per the user's submit-during-run preference).
  • Toolbar mic becomes ✕ cancel during the session and returns to mic outside it. Esc cancels, Enter converts/retries (window-level listener since the textarea is unmounted).
  • Resilience: transcription failures and timeouts keep the recorded audio for one-press retry instead of losing it; timeouts are surfaced as a distinct error (previously swallowed). A zero-length capture surfaces "No speech detected".
  • i18n: new keys added to en, ja and zh-CN.

Implementation notes

  • hooks/useDictation.ts gains pause/resume (native MediaRecorder), elapsed-time refs (no per-second re-render of consumers), an AudioContext/AnalyserNode capture for the waveform (torn down when capture finishes), and retained-audio retry.
  • components/RecordingDeck.tsx (new) owns the 100 ms timer tick and the requestAnimationFrame waveform loop; the canvas stretches with the composer (dynamic bar count, DPR-aware, newest samples hug the buttons) and freezes gray while paused.
  • Audio colors use the existing --status-error theme token (WCAG-verified) rather than new custom properties.

Testing

  • node --test: 848/848 (includes new tests for pause accounting, analyser setup, blob retention/retry, timeout surfacing, and the send/queue wiring; also makes the systemd-install test hermetic against ambient OMP_WEB_OMP_BIN).
  • tsc --noEmit and eslint clean.
  • Manually verified in Chromium with --use-fake-device-for-media-stream: deck swap, timer/progress, pause freeze, convert-to-text, transcribe-and-send, error→retry, cancel via button and Esc.

Companion PR for automated review bots: andrebrait/ompweb (same head, base pinned to upstream/main).

…anscribes+queues, deck keeps only pause and stop
…, attachment staleness, dynamic waveform width

- Use the theme's --status-error token instead of undefined --danger
  custom properties so dictation colors follow light/dark themes.
- Close the AudioContext and release the analyser when capture finishes,
  not only on cleanup, so transcription no longer holds the mic graph.
- Read attachments through refs inside handleSend/sendQueued so files
  attached mid-recording are included when the transcript dispatches.
- Surface 'No speech detected' when a capture yields no audio instead of
  failing silently.
- Stretch the waveform canvas to the free composer width (dynamic bar
  count, newest samples at the buttons) and freeze it gray while paused.
- Make the transcribe-and-send wiring test assert the contiguous branch.
- Drop the orphaned chatInput.stopDictation i18n key.
Stop enters review with playback instead of sending immediately.
Paused capture keeps a left-side preview play button.
@andrebrait

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@kahme247
kahme247 merged commit a44946d into kahme247:main Sep 19, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants