From 9e65f7c654c6c5677d15a17cafe9bd49418ca447 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 11 Aug 2026 12:26:10 -0400 Subject: [PATCH 1/2] Hide voice placeholder while typing Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../chat/browser/widgetHosts/viewPane/chatViewPane.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/vs/workbench/contrib/chat/browser/widgetHosts/viewPane/chatViewPane.ts b/src/vs/workbench/contrib/chat/browser/widgetHosts/viewPane/chatViewPane.ts index 99f56fa072513..87ea2bc226039 100644 --- a/src/vs/workbench/contrib/chat/browser/widgetHosts/viewPane/chatViewPane.ts +++ b/src/vs/workbench/contrib/chat/browser/widgetHosts/viewPane/chatViewPane.ts @@ -512,6 +512,11 @@ export class ChatViewPane extends ViewPane implements IViewWelcomeDelegate { Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('agents.voice.liveTranscript')), () => this.configurationService.getValue('agents.voice.liveTranscript') !== false ); + const inputValue = observableFromEvent( + this, + this._widget.inputEditor.onDidChangeModelContent, + () => this._widget.getInput() + ); const transcriptOverlay = $('.voice-transcript-overlay'); const transcriptScrollable = this._register(new DomScrollableElement(transcriptOverlay, { horizontal: ScrollbarVisibility.Hidden, @@ -667,6 +672,7 @@ export class ChatViewPane extends ViewPane implements IViewWelcomeDelegate { const currentSession = this._currentSessionResource.read(reader); const showTranscript = showTranscriptSetting.read(reader); const showLiveTranscript = showLiveTranscriptSetting.read(reader); + const hasInput = inputValue.read(reader).length > 0; const visible = turns.filter(t => t.text.length > 0 || (t.speaker === 'user' && t.isPartial)); const showListeningPlaceholder = voiceState === 'listening' && (!showTranscript || !showLiveTranscript); @@ -726,6 +732,11 @@ export class ChatViewPane extends ViewPane implements IViewWelcomeDelegate { // Show hint when connected but no transcript yet if (visible.length === 0 || !showTranscript || showListeningPlaceholder) { + if (hasInput) { + transcriptOverlayNode.style.display = 'none'; + transcriptOverlayNode.classList.remove('has-transcript'); + return; + } const handsFree = this.configurationService.getValue('agents.voice.handsFree') === true; if (showListeningPlaceholder) { transcriptOverlayNode.style.display = ''; From f58cc8ca3effc350bc0ea416e01c2de0f0844957 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 11 Aug 2026 12:52:21 -0400 Subject: [PATCH 2/2] Hide omni voice placeholder while typing Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../browser/chatInputWindow/chatInputWindowService.ts | 4 +++- .../chat/browser/voiceClient/voiceInputDecorations.ts | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatInputWindow/chatInputWindowService.ts b/src/vs/workbench/contrib/chat/browser/chatInputWindow/chatInputWindowService.ts index afa0a3ad75971..1fbfa88dda705 100644 --- a/src/vs/workbench/contrib/chat/browser/chatInputWindow/chatInputWindowService.ts +++ b/src/vs/workbench/contrib/chat/browser/chatInputWindow/chatInputWindowService.ts @@ -44,7 +44,7 @@ import { ChatContextKeys } from '../../common/actions/chatContextKeys.js'; import { ChatSessionRoutingController, IChatSessionRoutingHost } from '../sessionRouter/chatSessionRoutingController.js'; import { combineVoiceInput } from '../voiceClient/voiceInputUtils.js'; import { IChatInputWindowService, ChatInputWindowStorageKeys, CHAT_INPUT_WINDOW_DEFAULT_HEIGHT, CHAT_INPUT_WINDOW_SET_VOICE_TARGET_COMMAND_ID } from '../../common/chatInputWindow.js'; -import { autorun, IReader } from '../../../../../base/common/observable.js'; +import { autorun, IReader, observableFromEvent } from '../../../../../base/common/observable.js'; import { AgentSessionStatus } from '../agentSessions/agentSessionsModel.js'; import { IAgentSessionsService } from '../agentSessions/agentSessionsService.js'; import { IVoiceSessionController } from '../voiceClient/voiceSessionController.js'; @@ -450,6 +450,7 @@ export class ChatInputWindowService extends Disposable implements IChatInputWind const inputContainer = widget.input.inputContainerElement; if (inputContainer) { try { + const inputValue = observableFromEvent(this, widget.inputEditor.onDidChangeModelContent, () => widget.getInput()); this._windowDisposables.add(setupVoiceInputDecorations({ voiceSessionController: this.voiceSessionController, ttsPlaybackService: this.ttsPlaybackService, @@ -462,6 +463,7 @@ export class ChatInputWindowService extends Disposable implements IChatInputWind inputContainer, glowContainer: surface, isActive: this.voiceSessionController.omniInputActive, + inputValue, isOwner: this.voiceSessionController.omniInputActive, })); } catch (error) { diff --git a/src/vs/workbench/contrib/chat/browser/voiceClient/voiceInputDecorations.ts b/src/vs/workbench/contrib/chat/browser/voiceClient/voiceInputDecorations.ts index 6ea331c1bbd99..5ceeba8113a99 100644 --- a/src/vs/workbench/contrib/chat/browser/voiceClient/voiceInputDecorations.ts +++ b/src/vs/workbench/contrib/chat/browser/voiceClient/voiceInputDecorations.ts @@ -41,6 +41,8 @@ export interface IVoiceInputDecorationsOptions { readonly glowContainer?: HTMLElement; /** Whether this surface is active/visible. */ readonly isActive: IObservable; + /** Current text in the input. Voice placeholders are hidden while it is non-empty. */ + readonly inputValue?: IObservable; /** Explicit ownership for surfaces such as omni that do not yet have a resource. */ readonly isOwner?: IObservable; /** Surface resource, compared with the voice target to avoid misrouting. */ @@ -151,6 +153,7 @@ export function setupVoiceInputDecorations(services: IVoiceInputDecorationsServi const connected = voiceSessionController.isConnected.read(reader); const voiceState = voiceSessionController.voiceState.read(reader); const active = isActive.read(reader); + const hasInput = (options.inputValue?.read(reader).length ?? 0) > 0; const showTranscript = configurationService.getValue('agents.voice.showTranscript') !== false; const visible = turns.filter(t => t.text.length > 0 || (t.speaker === 'user' && t.isPartial)); @@ -161,6 +164,11 @@ export function setupVoiceInputDecorations(services: IVoiceInputDecorationsServi } if (visible.length === 0 || !showTranscript) { + if (hasInput) { + transcriptOverlayNode.style.display = 'none'; + transcriptOverlayNode.classList.remove('has-transcript'); + return; + } const handsFree = configurationService.getValue('agents.voice.handsFree') === true; if (!showTranscript && voiceState === 'listening') { // Transcript is disabled: surface a minimal "Listening..." overlay