Skip to content

[bug] Hands-free says "Listening…" and shows no words — the composer still binds voice.interim, which stopped carrying speech in #281 #364

Description

@serge-ivo

Reported

"In hands-free mode, when it has just started and says Listening, it is not transcribing or showing
any text/dictation. Only after I spoke for a while."

The composer reads a variable that stopped carrying speech

packages/sdk/src/voice/use-voice.ts:151:

// `interim` is now ONLY the composer's transient NOTICE line (mic errors, the wrong-language
// …
const [interim, setInterim] = useState("");

#281 promoted the live utterance to a first-class Dictation object (voice.dictation — text +
dictating/transcribing/failed status, words never blanked by a status change) and repurposed
interim as the notice line. The console was not updated with it. InstanceDetail.tsx:946:

value={voice.interim || input}

and :953 still styles the box for live speech when it is set:

${voice.interim ? "border-accent text-accent italic" : voice.micOn ? "border-green" : "border-line"}

So the accent-italic "you are speaking" treatment now fires only on a mic error, and the input can
never display speech — because speech no longer goes there.

What the user sees

In hands-free the top box shows its placeholder ("Listening…", :951) and stays empty for the whole
utterance. The words are being captured correctly and rendered correctly — as the voice.dictation
bubble at :1162, which is the last child of the message thread, i.e. below the fold, at the
opposite end of the screen from the input the user is watching.

Nothing is lost and nothing is broken in the voice pipeline. The live transcript is simply drawn
somewhere the reporter was not looking, while the place they were looking asserts "Listening…" and
shows nothing.

Two surfaces, one of them dead

This is the part worth fixing rather than papering over: there are currently two live-transcript
surfaces in the chat tab, and one of them has been disconnected since #281 without being removed. A
reader of InstanceDetail.tsx cannot tell which is authoritative, and the styling actively implies
the dead one is.

Fix

Stop binding the composer to voice.interim. voice.dictation is the single live-speech surface and
should stay that way.

interim still needs a home — it is the only thing that surfaces mic errors and the wrong-language
warning today, so it cannot simply be dropped. A small banner near the controls, not the input's
value (which also makes the input readOnly while a notice shows, :952).

Open, not proven

The dead binding fully explains "the box never shows my words". It does not necessarily explain
"only after I spoke for a while" — that half may additionally be the Web Speech gate's start-up
latency, since the gate is constructed and start()ed per turn (use-voice.ts:275-343) and cannot
emit onInterim until the engine boots. Worth measuring once the binding is fixed and the words are
somewhere visible; it may disappear entirely as a reporting artefact.

Related: #281 (the dictation object), #319 (the stored live capture), #365 (the layout change that
supersedes this).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingfrontendFrontend / UI workvoiceVoice / STT / TTS

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions