feat: add Codex-style voice dictation to web and mobile - #6625
feat: add Codex-style voice dictation to web and mobile#6625KachurPro wants to merge 20 commits into
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
ApprovabilityVerdict: Needs human review This PR introduces a complete new voice dictation feature across web, mobile, and server, including new external API integrations (OpenAI/Groq), new server endpoints, audio recording infrastructure, and API key handling. New features of this scope and complexity require human review. You can customize Macroscope's approvability policy. Learn more. |
| apiKey: voiceTranscriptionSettings.apiKey, | ||
| onTranscriptInsert: appendVoiceTranscriptToDraft, | ||
| onTranscriptSend: (transcript) => { | ||
| if (appendVoiceTranscriptToDraft(transcript)) void sendCurrentDraft(); |
There was a problem hiding this comment.
🟠 High threads/ThreadComposer.tsx:585
The voice “send” action sends the previous draftMessage snapshot instead of the transcript, so the transcript remains in the composer (or nothing is sent). appendVoiceTranscriptToDraft only schedules onChangeDraftMessage, while sendCurrentDraft() immediately calls onSendMessage() without the new text; pass the updated draft through the send path or wait for the state update before sending.
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/mobile/src/features/threads/ThreadComposer.tsx around line 585:
The voice “send” action sends the previous `draftMessage` snapshot instead of the transcript, so the transcript remains in the composer (or nothing is sent). `appendVoiceTranscriptToDraft` only schedules `onChangeDraftMessage`, while `sendCurrentDraft()` immediately calls `onSendMessage()` without the new text; pass the updated draft through the send path or wait for the state update before sending.
There was a problem hiding this comment.
Verified as a false positive. onChangeDraftMessage does not schedule React state: useThreadComposerState calls setComposerDraftText, which synchronously calls appAtomRegistry.set. onSendMessage then calls getComposerDraftSnapshot from that same registry, so the just-appended transcript is present before enqueue. The existing mobile composer-drafts test also covers synchronous latest-draft reads for send.
There was a problem hiding this comment.
Sorry, I'm unable to act on this request because you do not have permissions within this repository.
196ba3a to
5d1e3e3
Compare
|
Really looking forward to this — mobile dictation is exactly what I've been missing on iPhone. One request: the web/desktop implementation from #5213 supports both OpenAI and Groq with model selection, but the mobile path hardcodes the OpenAI endpoint and |
59ba2fc to
495d058
Compare
|
@brvale97 Implemented in 495d058. Mobile now supports both OpenAI and Groq with fixed official transcription endpoints, separate securely stored keys and model choices per provider, a model picker, and a custom model ID field so future compatible models do not require an app update. OpenAI now defaults to |
There was a problem hiding this comment.
UI consistency review of the web changes (apps/web/src/**). Three findings, all in the new dictation UI: a ticking live region that conflicts with the documented pattern in Sidebar.tsx, and two call sites that rebuild the existing ghost-muted button variant with a class override.
Posted via Macroscope — UI Consistency
There was a problem hiding this comment.
Reviewed the web-scope changes (ChatComposer.tsx, VoiceTranscriptionPanel.tsx, SettingsPanels.tsx, settingsSearch.ts, hooks/useVoiceTranscription.ts, lib/voiceTranscription.ts). The earlier findings on the panel (muted-ghost variant, live-region timer) are resolved. Two remaining consistency issues in the composer.
Posted via Macroscope — UI Consistency
There was a problem hiding this comment.
UI consistency review: one finding on the web composer dictation panel. Previous findings (muted-ghost variant usage, live-region timer, responsive error inset, mic pointer-focus) are resolved in this head.
Posted via Macroscope — UI Consistency
There was a problem hiding this comment.
One finding on the dictation panel's pointer-focus contract in the composer footer. Prior comments (mic variant/pointer focus, error-row inset, ghost-muted, aria-hidden timer) are addressed in this revision.
Posted via Macroscope — UI Consistency
There was a problem hiding this comment.
Reviewed the new Effect code (apps/server/src/transcription.ts, the new route layers in apps/server/src/http.ts, and the contracts/shared additions) against the service conventions. Error classes use Schema.TaggedErrorClass with structural attributes and preserved cause, failures are recovered with Effect.catchTags, HttpClient is acquired from the environment, and Effect modules are imported from their subpaths — all consistent with the conventions. One finding on the web client below.
Posted via Macroscope — Effect Service Conventions
9f92517 to
4732d3a
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 4732d3a. Configure here.
There was a problem hiding this comment.
One finding: the dictation send action bypasses the composer's themeable send tokens. Everything else (pointer-focus contract, ghost-muted variants, aria-hidden timer, responsive error inset) looks consistent with the composer's existing patterns.
Posted via Macroscope — UI Consistency

Problem
The existing voice dictation PR (#5213) only covered web/desktop and had become stale. Native mobile dictation was still missing, and the first mobile version was limited to a hard-coded OpenAI endpoint and model.
Solution
mainexpo-audioexpo-secure-store, with migration of the legacy OpenAI keygpt-4o-transcribeand Groq towhisper-large-v3-turboThis supersedes #5213 and carries its web/desktop behavior forward.
Interaction
X: discard the recordingVerification
expo-audio, the microphone permission, and Secure StoreThe current development machine does not have Xcode or an iOS Simulator installed, so a local simulator screenshot is not included. The native flow still needs the EAS preview/device pass produced by CI.
Built with GPT-5.6 Sol in Codex Desktop.
Note
Medium Risk
New authenticated transcription proxy accepts client-supplied API keys in custom headers and forwards audio to third parties; composer send/stop paths gain async recording state, and mobile sends audio and keys directly to providers.
Overview
Adds Codex-style voice dictation across web/desktop chat, native mobile thread composer, and server-backed transcription.
Web/desktop: New Voice dictation settings (OpenAI or Groq, client API key or server
OPENAI_API_KEY/GROQ_API_KEY, model picker). The composer gets a mic, a recording UI (waveform, cancel / stop / transcribe-and-send), and send behavior that waits on transcription when recording. Audio goes through authenticated/api/transcriptionand/api/transcription/modelson the connected T3 server (25 MB cap). SharedappendVoiceTranscriptand terminal-action rules live in@t3tools/shared. Desktop gains macOS microphone usage text.Mobile:
expo-audiorecording, per-provider keys/models in Secure Store (with legacy OpenAI key migration), a Voice Dictation settings screen, and direct uploads to OpenAI/Groq (not the server proxy). Composer integration mirrors web (thread-scoped append, cancel on navigation).Contracts: Client settings add
voiceTranscription*fields; reset-to-defaults includes voice dictation when dirty.Reviewed by Cursor Bugbot for commit ff3706c. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Add Codex-style voice dictation to web and mobile chat composers
ChatComposer) and mobile (ThreadComposer) chat composers, replacing the toolbar with a live waveform panel during recording/transcribing and appending the transcript to the draft on stop.GET/POST /api/transcription,GET /api/transcription/models) forward audio to the selected provider with a 25 MiB size limit and 2-minute timeout, supporting both environment-level and user-supplied API keys.@t3tools/sharedhandle transcript appending (whitespace-aware) and terminal action resolution (abort > send > insert).NSMicrophoneUsageDescriptionin Info.plist; the mobile Expo app adds theexpo-audioplugin with microphone permission.voiceTranscriptionEnabled=false); users must configure a model to activate the mic button. Existing client settings are migrated from a legacy OpenAI key format on first load.Macroscope summarized ff3706c.