mirror of
https://github.com/microsoft/vscode.git
synced 2026-09-08 09:23:38 +01:00
Consolidate the two "Select Microphone" commands into one (#328835)
* Initial plan * Consolidate microphone selection into a single command Co-authored-by: meganrogge <29464607+meganrogge@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: meganrogge <29464607+meganrogge@users.noreply.github.com>
This commit is contained in:
@@ -37,7 +37,6 @@ import { IStorageService, StorageScope, StorageTarget } from '../../../../platfo
|
||||
|
||||
import { AgentsVoiceSettingId, AgentsVoiceStorageKeys, AGENTS_VOICE_CONNECTED, AGENTS_VOICE_CONNECTING, AGENTS_VOICE_LISTENING } from '../common/agentsVoice.js';
|
||||
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
|
||||
import { IQuickInputService } from '../../../../platform/quickinput/common/quickInput.js';
|
||||
import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js';
|
||||
import { INotificationService } from '../../../../platform/notification/common/notification.js';
|
||||
import {
|
||||
@@ -541,77 +540,9 @@ registerAction2(class extends Action2 {
|
||||
}
|
||||
});
|
||||
|
||||
// --- Select Microphone Command ---
|
||||
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'agentsVoice.selectMicrophone',
|
||||
title: nls.localize2('agentsVoice.selectMicrophone', "Voice: Select Microphone"),
|
||||
f1: true,
|
||||
precondition: ContextKeyExpr.equals('config.agents.voice.enabled', true),
|
||||
});
|
||||
}
|
||||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const quickInputService = accessor.get(IQuickInputService);
|
||||
const storageService = accessor.get(IStorageService);
|
||||
|
||||
const devices = await navigator.mediaDevices.enumerateDevices();
|
||||
|
||||
// Filter out the virtual "default"/"communications" entries (which duplicate a real
|
||||
// device) and de-duplicate by deviceId so a single microphone shows up only once.
|
||||
const seenDeviceIds = new Set<string>();
|
||||
const audioInputs = devices.filter(d => {
|
||||
if (d.kind !== 'audioinput' || d.deviceId === 'default' || d.deviceId === 'communications') {
|
||||
return false;
|
||||
}
|
||||
if (seenDeviceIds.has(d.deviceId)) {
|
||||
return false;
|
||||
}
|
||||
seenDeviceIds.add(d.deviceId);
|
||||
return true;
|
||||
});
|
||||
|
||||
if (audioInputs.length === 0) {
|
||||
quickInputService.pick([{ label: nls.localize('noMicrophones', "No microphones found") }]);
|
||||
return;
|
||||
}
|
||||
|
||||
const currentDeviceId = storageService.get(AgentsVoiceStorageKeys.MicrophoneDevice, StorageScope.APPLICATION, '');
|
||||
|
||||
type DevicePickItem = { label: string; description?: string; deviceId: string };
|
||||
const items: DevicePickItem[] = [];
|
||||
|
||||
// "System Default" entry — clears the stored device so the OS default is always used
|
||||
items.push({
|
||||
label: nls.localize('systemDefault', "System Default"),
|
||||
description: currentDeviceId === '' ? nls.localize('current', "(current)") : undefined,
|
||||
deviceId: '',
|
||||
});
|
||||
|
||||
for (const d of audioInputs) {
|
||||
const label = d.label || nls.localize('unknownDevice', "Unknown Device ({0})", d.deviceId.slice(0, 8));
|
||||
items.push({
|
||||
label,
|
||||
description: d.deviceId === currentDeviceId ? nls.localize('current', "(current)") : undefined,
|
||||
deviceId: d.deviceId,
|
||||
});
|
||||
}
|
||||
|
||||
const picked = await quickInputService.pick(items, {
|
||||
placeHolder: nls.localize('selectMic', "Select a microphone for Voice Mode"),
|
||||
});
|
||||
|
||||
if (picked) {
|
||||
const selection = picked as DevicePickItem;
|
||||
if (selection.deviceId) {
|
||||
storageService.store(AgentsVoiceStorageKeys.MicrophoneDevice, selection.deviceId, StorageScope.APPLICATION, StorageTarget.MACHINE);
|
||||
} else {
|
||||
storageService.remove(AgentsVoiceStorageKeys.MicrophoneDevice, StorageScope.APPLICATION);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
// Microphone selection is shared with dictation via the single
|
||||
// `workbench.action.chat.selectSpeechToTextMicrophone` command (see
|
||||
// chatSpeechToTextActions.ts), so Voice Mode no longer registers its own.
|
||||
|
||||
// --- Settings ---
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ import { cancelDictation, isDictating, startDictation, stopDictation } from '../
|
||||
|
||||
// Gate on `ChatContextKeys.enabled` so the dictation UI and its commands are
|
||||
// hidden (not just disabled) when the user has turned AI features off; without
|
||||
// it the F1 "Dictate: Select Microphone" command stays discoverable.
|
||||
// it the F1 "Select Microphone" command stays discoverable.
|
||||
export const ChatSpeechToTextConfigured = ContextKeyExpr.and(ChatContextKeys.enabled, ContextKeyExpr.has(ChatContextKeys.speechToTextConfigured.key));
|
||||
/** True while the selected dictation backend is preparing. */
|
||||
export const ChatSpeechToTextPreparing = ContextKeyExpr.has(ChatContextKeys.speechToTextPreparing.key);
|
||||
@@ -299,10 +299,12 @@ class SelectSpeechToTextMicrophoneAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: SelectSpeechToTextMicrophoneAction.ID,
|
||||
title: localize2('chat.speechToText.selectMicrophone', "Dictate: Select Microphone"),
|
||||
title: localize2('chat.speechToText.selectMicrophone', "Select Microphone"),
|
||||
category: CHAT_CATEGORY,
|
||||
f1: true,
|
||||
precondition: ChatSpeechToTextConfigured,
|
||||
// Shared by dictation and Voice Mode (both persist to the same
|
||||
// device), so stay available whenever either feature is enabled.
|
||||
precondition: ContextKeyExpr.or(ChatSpeechToTextConfigured, ContextKeyExpr.equals('config.agents.voice.enabled', true)),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user