Voice transcription routes to main input instead of active dialog/reply box

Resolved 💬 3 comments Opened Mar 5, 2026 by kimbucha Closed Apr 9, 2026

Bug Description

When voice mode is enabled and a secondary input field is focused (e.g., plan mode reply box, AskUserQuestion dialog), holding Space triggers voice recording correctly — but the transcribed text appears in the main prompt input instead of the dialog's reply box.

After pressing Escape to dismiss the dialog, the transcribed text is visible in the main input underneath.

Steps to Reproduce

  1. Start Claude Code with voice mode enabled (/voice)
  2. Trigger plan mode or any action that shows an AskUserQuestion dialog (the reply box with accept/reject/clear context/reply options)
  3. With the dialog reply box focused, hold Space to record voice
  4. Speak a message, release Space
  5. Observe: The transcribed text does NOT appear in the dialog's reply box
  6. Press Escape to dismiss the dialog
  7. Observe: The transcribed text appears in the main prompt input below

Expected Behavior

When a dialog/reply box is focused and the user holds Space for voice input, the transcribed text should be inserted into the currently focused input (the dialog's reply box), not the main prompt.

Root Cause Analysis

Traced through the minified bundle (cli.js v2.1.69) across the full code path:

The complete chain:

  1. Space held → detected at process/module level (NOT via Ink's useInput with isActive guards)
  2. Cvq() starts recording (line ~5895) — checks only the Ak6 boolean flag, has zero modal/focus checks
  3. Audio captured via sox/native module
  4. Space releasedIvq() stops recording, audio sent for transcription
  5. onTranscript callback fires (line ~12904) → calls H.current(text)
  6. H.current is a stale ref — initialized once via useRef, always points to the main prompt's onInput handler, never updated when focus moves to a dialog
  7. wrappedOnInput (line ~3620) receives text → calls q(D, X) → updates main prompt input state
  8. Dialog's input handler (registered via separate useInput with its own inputValue state) never receives the transcription

Three independent failures:

| # | Failure Point | Detail |
|---|---------------|--------|
| 1 | No focus/modal check in Cvq() | Recording starts unconditionally — no check for questionState.isActive or dialog focus |
| 2 | Stale H.current ref | Voice hook stores the input handler ref once at init, never updates when focus moves to dialog input |
| 3 | No questionState awareness | Voice system has no concept of dialog/modal input contexts |

Key code references (minified names):

  • Cvq() — startRecording function (~line 5895)
  • Ak6 — global recording state boolean
  • H.current — stale useRef holding main prompt's onInput (~line 12904)
  • wrappedOnInput in Gwq — input routing function (~line 3620)
  • questionState — dialog active state (11 occurrences, not checked by voice)

Suggested Fix

Either:

  1. Update H.current whenever input focus changes (so it always points to the active input's handler), OR
  2. Add focus/modal gating in the voice handler — check questionState.isActive before recording and route transcription to the dialog's input handler when active, OR
  3. Use Ink's isActive parameter on the voice useInput hook so it's automatically disabled when a dialog has focus priority

Environment

  • Claude Code version: 2.1.68
  • OS: macOS (Darwin 25.3.0, arm64)
  • Installation: Homebrew

Related

  • #30968 (voice mode doesn't auto-activate on session start)

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗