Fix dictation "Listening…" placeholder and shimmer interim text - #326211
Merged
Megan Rogge (meganrogge) merged 8 commits intoJul 16, 2026
Conversation
The chat input renders its placeholder as an editor decoration while dictation sets the editor's placeholder option (rendered by PlaceholderTextContribution), so both showed at once and overlapped. Suppress the decoration placeholder while the editor's placeholder option is set so only one renders. Also only show "Listening…" once the session is connected — the Recording state with the on-device model finished preparing — instead of while the model is still downloading/loading and transcription cannot happen yet. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
While dictation is streaming interim (not yet finalized) transcripts, decorate the inserted text with a shimmer animation so the user can tell which portion is still being transcribed. The shimmer is cleared once the final transcript is applied (or on cancel / editor disposal / a service-side error), leaving solid text. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The transcription service can fire a trailing interim transcript as it shuts down, after stopAndTranscribe resolves and the final (solid) text has been applied. That late interim update overwrote the final text and re-applied the shimmer, so the finalized text never appeared. Ignore interim updates once a final update has been applied. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Clear the shimmer and lock out interim updates the moment the user stops talking (beginFinalize), before the async final transcription resolves, so a trailing interim transcript can neither re-apply the shimmer nor overwrite the final text. Also clear the shimmer when there is no final transcript to apply. Thread ILogService through startDictation and add trace logging around the transcript/state events and the stop lifecycle to diagnose why finalized text was not replacing the shimmer. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR improves the chat speech-to-text (voice dictation) UX by preventing placeholder overlap, delaying the “Listening…” placeholder until the dictation session is actually ready, and visually distinguishing interim (streaming) transcript text via a shimmer decoration.
Changes:
- Suppress the chat input’s decoration-based placeholder while the editor’s
placeholderoption is active (e.g. dictation “Listening…”), avoiding double placeholders. - Add interim-transcript shimmering via inline editor decorations, plus a finalize lock to avoid late interim updates overriding final text.
- Thread
ILogServiceinto dictation startup and add trace logging across the dictation lifecycle.
Show a summary per file
| File | Description |
|---|---|
| src/vs/workbench/contrib/chat/browser/widget/input/editor/chatInputEditorContrib.ts | Skips decoration placeholder when EditorOption.placeholder is set, avoiding placeholder overlap. |
| src/vs/workbench/contrib/chat/browser/speechToText/media/dictationSession.css | Adds shimmer animation styling for interim dictation text. |
| src/vs/workbench/contrib/chat/browser/speechToText/dictationSession.ts | Implements placeholder gating, interim shimmer decorations, finalize guard, and trace logging. |
| src/vs/workbench/contrib/chat/browser/actions/chatSpeechToTextActions.ts | Passes ILogService into startDictation. |
| src/vs/sessions/contrib/chat/browser/newChatInput.ts | Passes ILogService into startDictation from the Sessions UI. |
Review details
- Files reviewed: 5/5 changed files
- Comments generated: 2
- Review effort level: Low
Interim (not-yet-finalized) dictation text now renders in the input's placeholder color instead of a single shimmer across the whole transcript: - The settled leading portion — the part that has not changed since the previous interim transcript, trimmed back to a word boundary — is shown statically in the placeholder color, so words stop shimmering once the recognizer stops revising them. - Only the still-processing trailing portion shimmers. - If the recognizer revises an earlier word, the common prefix shrinks and that word re-enters the shimmering region. Finalizing clears both decorations, leaving solid text. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…word Two dictation finalization fixes: - Park the caret at the start of the dictated region (a blinking cursor at the beginning) while transcription is in progress, instead of chasing the growing/revised interim text. The caret moves to the end only once the final transcript is applied. - Append a short trailing silence to the audio before the final transcription pass. Whisper frequently drops the last word when the recording ends abruptly right after it (no trailing silence to mark the utterance end); the pad gives the model the context it needs to emit the final word. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Passing the desired caret as executeEdits' endCursorState (instead of a setPosition() call afterward) guarantees the caret stays parked at the start of the dictated region during interim updates. The prior approach let the editor first place the caret at the end of the applied edit, causing it to visibly chase the streaming text before being corrected. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The settle boundary always trimmed the common prefix back to the previous word boundary, which left the final word shimmering forever once interim updates stabilized (the recognizer keeps re-emitting the same text, so the tail never "changed" but never settled either). Now the word-boundary trim is only applied when the tail actually diverges from the previous interim; when the common prefix already covers the whole current transcript, everything settles. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
Screenshot ChangesBase: Added (92) |
Paul (pwang347)
approved these changes
Jul 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Screen.Recording.2026-07-16.at.3.49.39.PM.mov
Summary
Fixes two bugs with the built-in chat voice dictation placeholder and adds a shimmer animation to not-yet-finalized (streaming) dictation text.
Placeholder fixes
EditorOption.placeholder) overlapped with the chat input's own placeholder (rendered via editor decorations). The decoration placeholder is now suppressed whileEditorOption.placeholderis set, so only one shows at a time.Shimmer for interim text
chat-thinking-shimmerpattern.beginFinalize()lock at the start ofstopDictationplus a_finalizedguard so a trailing interim event can't re-apply the shimmer after finalization (fixes the "final text never replaces the shimmer" race).[chat-stt-dictation]trace logging across the transcript/state lifecycle to aid diagnosis.Changes
dictationSession.ts: placeholder gating, shimmer decoration,beginFinalize()/_finalizedguard, trace logging.chatInputEditorContrib.ts: suppress decoration placeholder whenEditorOption.placeholderis active.speechToText/media/dictationSession.css: new file with shimmer keyframes/class.chatSpeechToTextActions.ts/newChatInput.ts: threadILogServiceintostartDictation.Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com