diff --git a/build/gulpfile.vscode.ts b/build/gulpfile.vscode.ts index 973ce01837a26..73ae459ea153f 100644 --- a/build/gulpfile.vscode.ts +++ b/build/gulpfile.vscode.ts @@ -94,6 +94,7 @@ const vscodeResourceIncludes = [ // Accessibility Signals 'out-build/vs/platform/accessibilitySignal/browser/media/*.mp3', + 'out-build/vs/workbench/contrib/agentsVoice/browser/media/*.mp3', // Welcome 'out-build/vs/workbench/contrib/welcomeGettingStarted/common/media/**/*.{svg,png}', diff --git a/build/gulpfile.vscode.web.ts b/build/gulpfile.vscode.web.ts index 87372f5c9aa28..18b85c9142adc 100644 --- a/build/gulpfile.vscode.web.ts +++ b/build/gulpfile.vscode.web.ts @@ -68,6 +68,7 @@ export const vscodeWebResourceIncludes = [ // Accessibility Signals 'out-build/vs/platform/accessibilitySignal/browser/media/*.mp3', + 'out-build/vs/workbench/contrib/agentsVoice/browser/media/*.mp3', // Welcome 'out-build/vs/workbench/contrib/welcomeGettingStarted/common/media/**/*.{svg,png}', diff --git a/build/next/index.ts b/build/next/index.ts index c012d9cd5a7bb..aa172458ce989 100644 --- a/build/next/index.ts +++ b/build/next/index.ts @@ -284,6 +284,7 @@ const desktopResourcePatterns = [ // Media - audio 'vs/platform/accessibilitySignal/browser/media/*.mp3', + 'vs/workbench/contrib/agentsVoice/browser/media/*.mp3', // Media - images 'vs/workbench/contrib/welcomeGettingStarted/common/media/**/*.svg', @@ -344,6 +345,7 @@ const serverWebResourcePatterns = [ // Media - audio 'vs/platform/accessibilitySignal/browser/media/*.mp3', + 'vs/workbench/contrib/agentsVoice/browser/media/*.mp3', // Media - images 'vs/workbench/contrib/welcomeGettingStarted/common/media/**/*.svg', @@ -371,6 +373,7 @@ const webResourcePatterns = [ // Media - audio 'vs/platform/accessibilitySignal/browser/media/*.mp3', + 'vs/workbench/contrib/agentsVoice/browser/media/*.mp3', // Media - images 'vs/workbench/contrib/welcomeGettingStarted/common/media/**/*.svg', diff --git a/src/vs/workbench/contrib/agentsVoice/browser/agentsVoice.contribution.ts b/src/vs/workbench/contrib/agentsVoice/browser/agentsVoice.contribution.ts index 376c92adbaed3..10df6369f2094 100644 --- a/src/vs/workbench/contrib/agentsVoice/browser/agentsVoice.contribution.ts +++ b/src/vs/workbench/contrib/agentsVoice/browser/agentsVoice.contribution.ts @@ -17,9 +17,11 @@ import '../common/voiceTranscriptStore.js'; // Register the Voice Transcripts view + show-command + chat-menu entry import './transcriptsView/voiceTranscripts.contribution.js'; -import { Disposable } from '../../../../base/common/lifecycle.js'; +import { addDisposableListener } from '../../../../base/browser/dom.js'; +import { Disposable, IDisposable } from '../../../../base/common/lifecycle.js'; import { autorun } from '../../../../base/common/observable.js'; import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; +import { FileAccess } from '../../../../base/common/network.js'; import * as nls from '../../../../nls.js'; import { Action2, MenuId, registerAction2 } from '../../../../platform/actions/common/actions.js'; import { Extensions as ConfigurationExtensions, ConfigurationScope, IConfigurationRegistry } from '../../../../platform/configuration/common/configurationRegistry.js'; @@ -33,13 +35,14 @@ import { IWorkbenchContribution, WorkbenchPhase, registerWorkbenchContribution2 import { ConfigurationKeyValuePairs, IConfigurationMigrationRegistry, Extensions as WorkbenchConfigurationExtensions } from '../../../common/configuration.js'; import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; -import { AgentsVoiceStorageKeys, AGENTS_VOICE_CONNECTED, AGENTS_VOICE_CONNECTING, AGENTS_VOICE_LISTENING } from '../common/agentsVoice.js'; +import { AgentsVoiceStorageKeys, AGENTS_VOICE_CONFIGURE_COMMAND_ID, AGENTS_VOICE_CONNECTED, AGENTS_VOICE_CONNECTING, AGENTS_VOICE_DISABLE_COMMAND_ID, AGENTS_VOICE_LISTENING, AGENTS_VOICE_PREVIEW_COMMAND_ID } from '../common/agentsVoice.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; -import { IQuickInputService } from '../../../../platform/quickinput/common/quickInput.js'; +import { IQuickInputService, IQuickPickItem } from '../../../../platform/quickinput/common/quickInput.js'; import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { VoiceEnabledClassification, VoiceEnabledEvent, VoiceDisabledClassification, VoiceDisabledEvent, + VoiceOnboardingCompletedClassification, VoiceOnboardingCompletedEvent, } from '../../chat/browser/voiceClient/voiceTelemetry.js'; import { mainWindow } from '../../../../base/browser/window.js'; import { Codicon } from '../../../../base/common/codicons.js'; @@ -47,6 +50,9 @@ import { ChatContextKeys } from '../../chat/common/actions/chatContextKeys.js'; import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js'; import { ChatAgentLocation } from '../../chat/common/constants.js'; import { ICommandService } from '../../../../platform/commands/common/commands.js'; +import { IBannerService } from '../../../services/banner/browser/bannerService.js'; +import { IChatEntitlementService } from '../../../services/chat/common/chatEntitlementService.js'; +import { INotificationService } from '../../../../platform/notification/common/notification.js'; // --- Context Keys --- @@ -112,6 +118,81 @@ class AgentsVoiceTelemetryContribution extends Disposable implements IWorkbenchC registerWorkbenchContribution2(AgentsVoiceTelemetryContribution.ID, AgentsVoiceTelemetryContribution, WorkbenchPhase.AfterRestored); +// --- First-use banner --- + +const AGENTS_VOICE_ONBOARDING_BANNER_ID = 'agentsVoice.onboarding'; + +class AgentsVoiceOnboardingBannerContribution extends Disposable implements IWorkbenchContribution { + static readonly ID = 'workbench.contrib.agentsVoiceOnboardingBanner'; + + constructor( + @IConfigurationService private readonly configurationService: IConfigurationService, + @IStorageService private readonly storageService: IStorageService, + @IBannerService private readonly bannerService: IBannerService, + @IChatEntitlementService private readonly chatEntitlementService: IChatEntitlementService, + @ITelemetryService private readonly telemetryService: ITelemetryService, + ) { + super(); + + this._register(this.configurationService.onDidChangeConfiguration(event => { + if (event.affectsConfiguration('agents.voice.enabled')) { + this.updateBanner(); + } + })); + this._register(this.storageService.onDidChangeValue(StorageScope.PROFILE, AgentsVoiceStorageKeys.OnboardingCompleted, this._store)(() => this.updateBanner())); + this._register(this.chatEntitlementService.onDidChangeSentiment(() => this.updateBanner())); + this.updateBanner(); + } + + private updateBanner(): void { + const sentiment = this.chatEntitlementService.sentiment; + const shouldShow = this.configurationService.getValue('agents.voice.enabled') === true + && !this.storageService.getBoolean(AgentsVoiceStorageKeys.OnboardingCompleted, StorageScope.PROFILE, false) + && !sentiment.hidden + && !sentiment.disabled + && !sentiment.disabledInWorkspace + && !sentiment.untrusted; + + if (!shouldShow) { + this.bannerService.hide(AGENTS_VOICE_ONBOARDING_BANNER_ID); + return; + } + + const message = nls.localize('agentsVoice.onboardingBanner.message', "Voice Mode is ready. Configure it, preview the selected voice, or turn it off."); + this.bannerService.show({ + id: AGENTS_VOICE_ONBOARDING_BANNER_ID, + icon: Codicon.voiceMode, + message, + ariaLabel: nls.localize('agentsVoice.onboardingBanner.ariaLabel', "{0} Use the banner actions to configure settings, preview or choose the voice, or disable Voice Mode.", message), + actions: [ + { + label: nls.localize('agentsVoice.onboardingBanner.configure', "Configure Settings"), + href: 'command:agentsVoice.openSettings', + }, + { + label: nls.localize('agentsVoice.onboardingBanner.preview', "Preview Voice"), + href: `command:${AGENTS_VOICE_PREVIEW_COMMAND_ID}`, + }, + { + label: nls.localize('agentsVoice.onboardingBanner.chooseVoice', "Choose Voice"), + href: `command:${AGENTS_VOICE_CONFIGURE_COMMAND_ID}`, + }, + { + label: nls.localize('agentsVoice.onboardingBanner.disable', "Disable Voice Mode"), + href: `command:${AGENTS_VOICE_DISABLE_COMMAND_ID}`, + }, + ], + closeLabel: nls.localize('agentsVoice.onboardingBanner.dismiss', "Dismiss Voice Mode Setup"), + onClose: () => { + this.storageService.store(AgentsVoiceStorageKeys.OnboardingCompleted, true, StorageScope.PROFILE, StorageTarget.USER); + this.telemetryService.publicLog2('voiceOnboardingCompleted', {}); + }, + }); + } +} + +registerWorkbenchContribution2(AgentsVoiceOnboardingBannerContribution.ID, AgentsVoiceOnboardingBannerContribution, WorkbenchPhase.AfterRestored); + // --- Voice mode button in Chat toolbar --- // Shows the voice mode icon in both idle and active states. // Click to connect if disconnected, or toggle PTT if connected. @@ -378,6 +459,140 @@ registerAction2(class extends Action2 { } }); +const voicePreviewFiles = { + victoria_neutral: 'victoria_neutral.mp3', + kevin_neutral: 'kevin_neutral.mp3', + maya_neutral: 'maya_neutral.mp3', + daniel_neutral: 'daniel_neutral.mp3', +} as const; + +type VoicePreview = keyof typeof voicePreviewFiles; + +function isVoicePreview(voice: string): voice is VoicePreview { + return Object.prototype.hasOwnProperty.call(voicePreviewFiles, voice); +} + +let currentVoicePreview: { audio: HTMLAudioElement; endedListener: IDisposable } | undefined; + +function stopVoicePreview(): void { + if (!currentVoicePreview) { + return; + } + + const { audio, endedListener } = currentVoicePreview; + currentVoicePreview = undefined; + audio.pause(); + endedListener.dispose(); +} + +async function playVoicePreview(voice: VoicePreview, notificationService: INotificationService): Promise { + stopVoicePreview(); + + const audio = new mainWindow.Audio(FileAccess.asBrowserUri(`vs/workbench/contrib/agentsVoice/browser/media/${voicePreviewFiles[voice]}`).toString(true)); + const endedListener = addDisposableListener(audio, 'ended', () => { + if (currentVoicePreview?.audio === audio) { + stopVoicePreview(); + } + }); + currentVoicePreview = { audio, endedListener }; + + try { + await audio.play(); + } catch { + if (currentVoicePreview?.audio === audio) { + stopVoicePreview(); + notificationService.error(nls.localize('agentsVoice.previewVoiceFailed', "Unable to preview the selected Voice Mode voice.")); + } + } +} + +class PreviewVoiceAction extends Action2 { + + constructor() { + super({ + id: AGENTS_VOICE_PREVIEW_COMMAND_ID, + title: nls.localize2('agentsVoice.previewVoice', "Voice Mode: Preview Voice"), + f1: true, + precondition: ContextKeyExpr.equals('config.agents.voice.enabled', true), + }); + } + + async run(accessor: ServicesAccessor): Promise { + const configurationService = accessor.get(IConfigurationService); + const notificationService = accessor.get(INotificationService); + const configuredVoice = configurationService.getValue('agents.voice.voice') ?? 'maya_neutral'; + const voice = isVoicePreview(configuredVoice) ? configuredVoice : 'maya_neutral'; + + await playVoicePreview(voice, notificationService); + } +} + +registerAction2(PreviewVoiceAction); + +interface VoiceQuickPickItem extends IQuickPickItem { + readonly voice: VoicePreview; +} + +registerAction2(class extends Action2 { + constructor() { + super({ + id: AGENTS_VOICE_CONFIGURE_COMMAND_ID, + title: nls.localize2('agentsVoice.configureVoice', "Voice Mode: Configure Voice"), + f1: true, + }); + } + + async run(accessor: ServicesAccessor): Promise { + const configurationService = accessor.get(IConfigurationService); + const notificationService = accessor.get(INotificationService); + const quickInputService = accessor.get(IQuickInputService); + const configuredVoice = configurationService.getValue('agents.voice.voice') ?? 'maya_neutral'; + const items: VoiceQuickPickItem[] = [ + { label: nls.localize('agentsVoice.voice.victoria', "Victoria"), voice: 'victoria_neutral' }, + { label: nls.localize('agentsVoice.voice.kevin', "Kevin"), voice: 'kevin_neutral' }, + { label: nls.localize('agentsVoice.voice.maya', "Maya"), voice: 'maya_neutral' }, + { label: nls.localize('agentsVoice.voice.daniel', "Daniel"), voice: 'daniel_neutral' }, + ]; + const activeItem = items.find(item => item.voice === configuredVoice) ?? items[2]; + let selectedItem: VoiceQuickPickItem | undefined; + + try { + selectedItem = await quickInputService.pick(items, { + title: nls.localize('agentsVoice.configureVoice.title', "Select Voice Mode Voice"), + placeHolder: nls.localize('agentsVoice.configureVoice.placeholder', "Focus a voice to preview it"), + activeItem, + onDidFocus: item => { + void playVoicePreview(item.voice, notificationService); + }, + }); + } finally { + stopVoicePreview(); + } + + if (selectedItem) { + await configurationService.updateValue('agents.voice.voice', selectedItem.voice); + } + } +}); + +registerAction2(class extends Action2 { + constructor() { + super({ + id: AGENTS_VOICE_DISABLE_COMMAND_ID, + title: nls.localize2('agentsVoice.disable', "Voice Mode: Disable"), + f1: true, + precondition: ContextKeyExpr.equals('config.agents.voice.enabled', true), + }); + } + + async run(accessor: ServicesAccessor): Promise { + const commandService = accessor.get(ICommandService); + const configurationService = accessor.get(IConfigurationService); + await commandService.executeCommand('agentsVoice.disconnect'); + await configurationService.updateValue('agents.voice.enabled', false); + } +}); + // --- Simulate Voice Connection (dev utility, backend down) --- registerAction2(class extends Action2 { @@ -556,7 +771,7 @@ configurationRegistry.registerConfiguration({ nls.localize('agents.voice.voice.maya', "Maya."), nls.localize('agents.voice.voice.daniel', "Daniel."), ], - description: nls.localize('agents.voice.voice', "The voice used when the assistant reads responses aloud. Changing this while voice mode is connected takes effect immediately."), + markdownDescription: nls.localize('agents.voice.voice', "The voice used when the assistant reads responses aloud. [Choose a voice and preview samples](command:{0}). Changing this while voice mode is connected takes effect immediately.", AGENTS_VOICE_CONFIGURE_COMMAND_ID), default: 'maya_neutral', scope: ConfigurationScope.APPLICATION, }, diff --git a/src/vs/workbench/contrib/agentsVoice/browser/agentsVoiceWindowService.ts b/src/vs/workbench/contrib/agentsVoice/browser/agentsVoiceWindowService.ts index 4534922c33448..d6595826e9096 100644 --- a/src/vs/workbench/contrib/agentsVoice/browser/agentsVoiceWindowService.ts +++ b/src/vs/workbench/contrib/agentsVoice/browser/agentsVoiceWindowService.ts @@ -214,7 +214,7 @@ export class AgentsVoiceWindowService extends Disposable implements IAgentsVoice const anchor = new StandardMouseEvent(getWindow(e.target as Node ?? auxiliaryWindow.container), e); this.contextMenuService.showContextMenu({ getAnchor: () => anchor, - getActions: () => getVoiceModeContextMenuActions(this.commandService, this.configurationService, this.keybindingService, 'agentsVoice.pushToTalk'), + getActions: () => getVoiceModeContextMenuActions(this.commandService, this.keybindingService, 'agentsVoice.pushToTalk'), }); }, submitFeedback: (text) => this.voiceSessionController.submitFeedback(text), diff --git a/src/vs/workbench/contrib/agentsVoice/common/agentsVoice.ts b/src/vs/workbench/contrib/agentsVoice/common/agentsVoice.ts index 6cc3aad25c350..c54c05437c5bf 100644 --- a/src/vs/workbench/contrib/agentsVoice/common/agentsVoice.ts +++ b/src/vs/workbench/contrib/agentsVoice/common/agentsVoice.ts @@ -17,6 +17,9 @@ import './agentsVoiceColors.js'; // Register custom voice theme colors export const AGENTS_VOICE_CONNECTED = new RawContextKey('agentsVoiceConnected', false); export const AGENTS_VOICE_CONNECTING = new RawContextKey('agentsVoiceConnecting', false); export const AGENTS_VOICE_LISTENING = new RawContextKey('agentsVoiceListening', false); +export const AGENTS_VOICE_CONFIGURE_COMMAND_ID = 'agentsVoice.configureVoice'; +export const AGENTS_VOICE_DISABLE_COMMAND_ID = 'agentsVoice.disable'; +export const AGENTS_VOICE_PREVIEW_COMMAND_ID = 'agentsVoice.previewVoice'; /** * Default dimensions for the Agents Voice floating window. diff --git a/src/vs/workbench/contrib/chat/browser/speechToText/micButtonMenuActions.ts b/src/vs/workbench/contrib/chat/browser/speechToText/micButtonMenuActions.ts index 581d5c8b7bd8f..e5598e4846a43 100644 --- a/src/vs/workbench/contrib/chat/browser/speechToText/micButtonMenuActions.ts +++ b/src/vs/workbench/contrib/chat/browser/speechToText/micButtonMenuActions.ts @@ -13,19 +13,16 @@ import { ICommandService } from '../../../../../platform/commands/common/command import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { IContextMenuService } from '../../../../../platform/contextview/browser/contextView.js'; import { IKeybindingService } from '../../../../../platform/keybinding/common/keybinding.js'; +import { AGENTS_VOICE_DISABLE_COMMAND_ID } from '../../../agentsVoice/common/agentsVoice.js'; /** Command that opens the microphone picker shared by dictation and Voice Mode. */ export const SELECT_MICROPHONE_COMMAND = 'workbench.action.chat.selectSpeechToTextMicrophone'; /** Command that cancels the active/preparing dictation session. */ const CANCEL_DICTATION_COMMAND = 'workbench.action.chat.cancelSpeechToText'; -/** Command that tears down an active Voice Mode session. */ -const VOICE_DISCONNECT_COMMAND = 'agentsVoice.disconnect'; /** Command that opens the Voice Mode settings; the affordance that used to live behind the toolbar gear. */ const VOICE_OPEN_SETTINGS_COMMAND = 'agentsVoice.openSettings'; /** Setting that enables dictation; toggled off by "Disable Dictation". */ const DICTATION_ENABLED_SETTING = 'dictation.enabled'; -/** Setting that enables Voice Mode; toggled off by "Disable Voice Mode". */ -const VOICE_ENABLED_SETTING = 'agents.voice.enabled'; /** * "Select Microphone" entry shared by every dictation / Voice Mode mic button @@ -60,14 +57,11 @@ function createDisableDictationAction(commandService: ICommandService, configura * the setting doesn't leave the microphone capturing while the toolbar * affordance disappears, then turns off the feature setting. */ -function createDisableVoiceModeAction(commandService: ICommandService, configurationService: IConfigurationService): IAction { +function createDisableVoiceModeAction(commandService: ICommandService): IAction { return toAction({ id: 'chat.voiceMode.disable', label: localize('voiceMode.disable', "Disable Voice Mode"), - run: async () => { - await commandService.executeCommand(VOICE_DISCONNECT_COMMAND); - await configurationService.updateValue(VOICE_ENABLED_SETTING, false); - }, + run: () => commandService.executeCommand(AGENTS_VOICE_DISABLE_COMMAND_ID), }); } @@ -105,12 +99,12 @@ function createVoiceModeSettingsAction(commandService: ICommandService): IAction * affordances that used to live behind the toolbar gear button. * `keybindingCommandId` is the stable command the keybinding entry targets. */ -export function getVoiceModeContextMenuActions(commandService: ICommandService, configurationService: IConfigurationService, keybindingService: IKeybindingService, keybindingCommandId: string): IAction[] { +export function getVoiceModeContextMenuActions(commandService: ICommandService, keybindingService: IKeybindingService, keybindingCommandId: string): IAction[] { return [ createConfigureKeybindingAction(commandService, keybindingService, keybindingCommandId), createVoiceModeSettingsAction(commandService), createSelectMicrophoneAction(commandService), - createDisableVoiceModeAction(commandService, configurationService), + createDisableVoiceModeAction(commandService), ]; } diff --git a/src/vs/workbench/contrib/chat/browser/voiceClient/voiceModeActionViewItem.ts b/src/vs/workbench/contrib/chat/browser/voiceClient/voiceModeActionViewItem.ts index 99ff7e47e898c..2fe47c0f724f1 100644 --- a/src/vs/workbench/contrib/chat/browser/voiceClient/voiceModeActionViewItem.ts +++ b/src/vs/workbench/contrib/chat/browser/voiceClient/voiceModeActionViewItem.ts @@ -7,7 +7,6 @@ import { IAccessibilityService } from '../../../../../platform/accessibility/com import { MenuItemAction } from '../../../../../platform/actions/common/actions.js'; import { IMenuEntryActionViewItemOptions, MenuEntryActionViewItem } from '../../../../../platform/actions/browser/menuEntryActionViewItem.js'; import { ICommandService } from '../../../../../platform/commands/common/commands.js'; -import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; import { IContextMenuService } from '../../../../../platform/contextview/browser/contextView.js'; import { IKeybindingService } from '../../../../../platform/keybinding/common/keybinding.js'; @@ -35,7 +34,6 @@ export class VoiceModeActionViewItem extends MenuEntryActionViewItem { action: MenuItemAction, options: IMenuEntryActionViewItemOptions | undefined, @ICommandService private readonly _commandService: ICommandService, - @IConfigurationService private readonly _configurationService: IConfigurationService, @IKeybindingService keybindingService: IKeybindingService, @INotificationService notificationService: INotificationService, @IContextKeyService contextKeyService: IContextKeyService, @@ -51,7 +49,7 @@ export class VoiceModeActionViewItem extends MenuEntryActionViewItem { this._register(addMicButtonContextMenuListener( container, - () => getVoiceModeContextMenuActions(this._commandService, this._configurationService, this._keybindingService, VOICE_START_COMMAND), + () => getVoiceModeContextMenuActions(this._commandService, this._keybindingService, VOICE_START_COMMAND), this._contextMenuService, )); } diff --git a/src/vs/workbench/contrib/chat/browser/voiceInputMode/voiceInputModeActionViewItem.ts b/src/vs/workbench/contrib/chat/browser/voiceInputMode/voiceInputModeActionViewItem.ts index 034165a427eb5..02a476f471620 100644 --- a/src/vs/workbench/contrib/chat/browser/voiceInputMode/voiceInputModeActionViewItem.ts +++ b/src/vs/workbench/contrib/chat/browser/voiceInputMode/voiceInputModeActionViewItem.ts @@ -373,7 +373,7 @@ export class VoiceInputModeActionViewItem extends BaseActionViewItem { })); this._register(addMicButtonContextMenuListener( this._voiceCell, - () => getVoiceModeContextMenuActions(this.commandService, this.configurationService, this.keybindingService, VOICE_START_COMMAND_ID), + () => getVoiceModeContextMenuActions(this.commandService, this.keybindingService, VOICE_START_COMMAND_ID), this.contextMenuService, )); // Pause the audio-reactive bars while hovering so the CSS "silent" preview shows. @@ -394,7 +394,7 @@ export class VoiceInputModeActionViewItem extends BaseActionViewItem { this._listenIcon = dom.append(this._listenCell, dom.$('span.chat-voice-input-mode-icon')); this._register(addMicButtonContextMenuListener( this._listenCell, - () => getVoiceModeContextMenuActions(this.commandService, this.configurationService, this.keybindingService, VOICE_START_COMMAND_ID), + () => getVoiceModeContextMenuActions(this.commandService, this.keybindingService, VOICE_START_COMMAND_ID), this.contextMenuService, )); this._register(this.hoverService.setupManagedHover(getDefaultHoverDelegate('element'), this._listenCell, diff --git a/src/vs/workbench/services/chat/common/chatEntitlementService.ts b/src/vs/workbench/services/chat/common/chatEntitlementService.ts index 156058a96dcf9..3d9398be93cfe 100644 --- a/src/vs/workbench/services/chat/common/chatEntitlementService.ts +++ b/src/vs/workbench/services/chat/common/chatEntitlementService.ts @@ -440,6 +440,7 @@ export class ChatEntitlementService extends Disposable implements IChatEntitleme ChatEntitlementContextKeys.Setup.completed.key, ChatEntitlementContextKeys.Setup.hidden.key, ChatEntitlementContextKeys.Setup.disabled.key, + ChatEntitlementContextKeys.Setup.disabledInWorkspace.key, ChatEntitlementContextKeys.Setup.untrusted.key, ChatEntitlementContextKeys.Setup.installed.key, ChatEntitlementContextKeys.Setup.later.key,