Skip to content

Commit b320559

Browse files
authored
voice - polish UX when recording (microsoft#196544)
1 parent 8fca86d commit b320559

File tree

4 files changed

+43
-25
lines changed

4 files changed

+43
-25
lines changed

src/vs/workbench/contrib/chat/browser/chat.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ export interface IChatWidget {
112112
moveFocus(item: ChatTreeItem, type: 'next' | 'previous'): void;
113113
getFocus(): ChatTreeItem | undefined;
114114
updateInput(query?: string): void;
115+
getInput(): string;
115116
acceptInput(query?: string): void;
116117
acceptInputWithPrefix(prefix: string): void;
117118
setInputPlaceholder(placeholder: string): void;

src/vs/workbench/contrib/chat/browser/chatWidget.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,10 @@ export class ChatWidget extends Disposable implements IChatWidget {
514514
this.inputPart.setValue(value);
515515
}
516516

517+
getInput(): string {
518+
return this.inputPart.inputEditor.getValue();
519+
}
520+
517521
async acceptInput(query?: string): Promise<void> {
518522
this._acceptInput(query ? { query } : undefined);
519523
}
@@ -526,7 +530,7 @@ export class ChatWidget extends Disposable implements IChatWidget {
526530
if (this.viewModel) {
527531
this._onDidAcceptInput.fire();
528532

529-
const editorValue = this.inputPart.inputEditor.getValue();
533+
const editorValue = this.getInput();
530534
this._chatAccessibilityService.acceptRequest();
531535
const input = !opts ? editorValue :
532536
'query' in opts ? opts.query :
@@ -708,7 +712,7 @@ export class ChatWidget extends Disposable implements IChatWidget {
708712

709713
getViewState(): IViewState {
710714
this.inputPart.saveState();
711-
return { inputValue: this.inputPart.inputEditor.getValue() };
715+
return { inputValue: this.getInput() };
712716
}
713717
}
714718

src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { CHAT_CATEGORY } from 'vs/workbench/contrib/chat/browser/actions/chatAct
1919
import { IChatWidget, IChatWidgetService, IQuickChatService } from 'vs/workbench/contrib/chat/browser/chat';
2020
import { IChatService } from 'vs/workbench/contrib/chat/common/chatService';
2121
import { MENU_INLINE_CHAT_WIDGET } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
22-
import { CONTEXT_PROVIDER_EXISTS } from 'vs/workbench/contrib/chat/common/chatContextKeys';
22+
import { CONTEXT_CHAT_REQUEST_IN_PROGRESS, CONTEXT_PROVIDER_EXISTS } from 'vs/workbench/contrib/chat/common/chatContextKeys';
2323
import { InlineChatController } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController';
2424
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
2525
import { getCodeEditor } from 'vs/editor/browser/editorBrowser';
@@ -59,6 +59,7 @@ interface IVoiceChatSessionController {
5959
focusInput(): void;
6060
acceptInput(): void;
6161
updateInput(text: string): void;
62+
getInput(): string;
6263

6364
setInputPlaceholder(text: string): void;
6465
clearInputPlaceholder(): void;
@@ -166,6 +167,7 @@ class VoiceChatSessionControllerFactory {
166167
focusInput: () => chatView.focusInput(),
167168
acceptInput: () => chatView.acceptInput(),
168169
updateInput: text => chatView.updateInput(text),
170+
getInput: () => chatView.getInput(),
169171
setInputPlaceholder: text => chatView.setInputPlaceholder(text),
170172
clearInputPlaceholder: () => chatView.resetInputPlaceholder()
171173
};
@@ -179,13 +181,14 @@ class VoiceChatSessionControllerFactory {
179181
focusInput: () => quickChat.focusInput(),
180182
acceptInput: () => quickChat.acceptInput(),
181183
updateInput: text => quickChat.updateInput(text),
184+
getInput: () => quickChat.getInput(),
182185
setInputPlaceholder: text => quickChat.setInputPlaceholder(text),
183186
clearInputPlaceholder: () => quickChat.resetInputPlaceholder()
184187
};
185188
}
186189

187-
private static doCreateForInlineChat(inlineChat: InlineChatController,): IVoiceChatSessionController {
188-
const inlineChatSession = inlineChat.run();
190+
private static doCreateForInlineChat(inlineChat: InlineChatController): IVoiceChatSessionController {
191+
const inlineChatSession = inlineChat.joinCurrentRun() ?? inlineChat.run();
189192

190193
return {
191194
context: 'inline',
@@ -196,7 +199,8 @@ class VoiceChatSessionControllerFactory {
196199
),
197200
focusInput: () => inlineChat.focus(),
198201
acceptInput: () => inlineChat.acceptInput(),
199-
updateInput: text => inlineChat.updateInput(text),
202+
updateInput: text => inlineChat.updateInput(text, false),
203+
getInput: () => inlineChat.getInput(),
200204
setInputPlaceholder: text => inlineChat.setPlaceholder(text),
201205
clearInputPlaceholder: () => inlineChat.resetPlaceholder()
202206
};
@@ -252,14 +256,13 @@ class VoiceChatSessions {
252256
session.disposables.add(controller.onDidAcceptInput(() => this.stop(sessionId, controller.context)));
253257
session.disposables.add(controller.onDidCancelInput(() => this.stop(sessionId, controller.context)));
254258

255-
controller.updateInput('');
256259
controller.focusInput();
257260

258261
this.voiceChatGettingReadyKey.set(true);
259262

260263
const speechToTextSession = session.disposables.add(this.speechService.createSpeechToTextSession(cts.token));
261264

262-
let transcription: string = '';
265+
let inputValue = controller.getInput();
263266
const acceptTranscriptionScheduler = session.disposables.add(new RunOnceScheduler(() => session.controller.acceptInput(), 1200));
264267
session.disposables.add(speechToTextSession.onDidChange(({ status, text }) => {
265268
if (cts.token.isCancellationRequested) {
@@ -272,14 +275,14 @@ class VoiceChatSessions {
272275
break;
273276
case SpeechToTextStatus.Recognizing:
274277
if (text) {
275-
session.controller.updateInput([transcription, text].join(' '));
278+
session.controller.updateInput([inputValue, text].join(' '));
276279
acceptTranscriptionScheduler.cancel();
277280
}
278281
break;
279282
case SpeechToTextStatus.Recognized:
280283
if (text) {
281-
transcription = [transcription, text].join(' ');
282-
session.controller.updateInput(transcription);
284+
inputValue = [inputValue, text].join(' ');
285+
session.controller.updateInput(inputValue);
283286
acceptTranscriptionScheduler.schedule();
284287
}
285288
break;
@@ -368,7 +371,7 @@ export class VoiceChatInChatViewAction extends Action2 {
368371
original: 'Voice Chat in Chat View'
369372
},
370373
category: CHAT_CATEGORY,
371-
precondition: ContextKeyExpr.and(HasSpeechProvider, CONTEXT_PROVIDER_EXISTS),
374+
precondition: ContextKeyExpr.and(HasSpeechProvider, CONTEXT_PROVIDER_EXISTS, CONTEXT_CHAT_REQUEST_IN_PROGRESS.negate()),
372375
f1: true
373376
});
374377
}
@@ -395,7 +398,7 @@ export class InlineVoiceChatAction extends Action2 {
395398
original: 'Inline Voice Chat'
396399
},
397400
category: CHAT_CATEGORY,
398-
precondition: ContextKeyExpr.and(HasSpeechProvider, CONTEXT_PROVIDER_EXISTS, ActiveEditorContext),
401+
precondition: ContextKeyExpr.and(HasSpeechProvider, CONTEXT_PROVIDER_EXISTS, ActiveEditorContext, CONTEXT_CHAT_REQUEST_IN_PROGRESS.negate()),
399402
f1: true
400403
});
401404
}
@@ -422,7 +425,7 @@ export class QuickVoiceChatAction extends Action2 {
422425
original: 'Quick Voice Chat'
423426
},
424427
category: CHAT_CATEGORY,
425-
precondition: ContextKeyExpr.and(HasSpeechProvider, CONTEXT_PROVIDER_EXISTS),
428+
precondition: ContextKeyExpr.and(HasSpeechProvider, CONTEXT_PROVIDER_EXISTS, CONTEXT_CHAT_REQUEST_IN_PROGRESS.negate()),
426429
f1: true
427430
});
428431
}
@@ -450,7 +453,7 @@ export class StartVoiceChatAction extends Action2 {
450453
},
451454
category: CHAT_CATEGORY,
452455
icon: Codicon.mic,
453-
precondition: ContextKeyExpr.and(HasSpeechProvider, CONTEXT_VOICE_CHAT_GETTING_READY.negate()),
456+
precondition: ContextKeyExpr.and(HasSpeechProvider, CONTEXT_VOICE_CHAT_GETTING_READY.negate(), CONTEXT_CHAT_REQUEST_IN_PROGRESS.negate()),
454457
menu: [{
455458
id: MenuId.ChatExecute,
456459
when: ContextKeyExpr.and(HasSpeechProvider, CONTEXT_VOICE_CHAT_IN_VIEW_IN_PROGRESS.negate(), CONTEXT_QUICK_VOICE_CHAT_IN_PROGRESS.negate(), CONTEXT_VOICE_CHAT_IN_EDITOR_IN_PROGRESS.negate()),

src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,10 @@ export class InlineChatController implements IEditorContribution {
230230
}
231231
}
232232

233+
joinCurrentRun(): Promise<void> | undefined {
234+
return this._currentRun;
235+
}
236+
233237
// ---- state machine
234238

235239
private _showWidget(initialRender: boolean = false, position?: IPosition) {
@@ -405,14 +409,14 @@ export class InlineChatController implements IEditorContribution {
405409
}
406410
}
407411

408-
private _placeholder: string | undefined = undefined;
412+
private _forcedPlaceholder: string | undefined = undefined;
409413
setPlaceholder(text: string): void {
410-
this._placeholder = text;
414+
this._forcedPlaceholder = text;
411415
this._updatePlaceholder();
412416
}
413417

414418
resetPlaceholder(): void {
415-
this._placeholder = undefined;
419+
this._forcedPlaceholder = undefined;
416420
this._updatePlaceholder();
417421
}
418422

@@ -421,8 +425,8 @@ export class InlineChatController implements IEditorContribution {
421425
}
422426

423427
private _getPlaceholderText(): string {
424-
let result = this._placeholder ?? this._activeSession?.session.placeholder ?? localize('default.placeholder', "Ask a question");
425-
if (InlineChatController._promptHistory.length > 0) {
428+
let result = this._forcedPlaceholder ?? this._activeSession?.session.placeholder ?? localize('default.placeholder', "Ask a question");
429+
if (typeof this._forcedPlaceholder === 'undefined' && InlineChatController._promptHistory.length > 0) {
426430
const kb1 = this._keybindingService.lookupKeybinding('inlineChat.previousFromHistory')?.getLabel();
427431
const kb2 = this._keybindingService.lookupKeybinding('inlineChat.nextFromHistory')?.getLabel();
428432

@@ -490,11 +494,11 @@ export class InlineChatController implements IEditorContribution {
490494
return State.MAKE_REQUEST;
491495
}
492496

493-
if (!this._zone.value.widget.value) {
497+
if (!this.getInput()) {
494498
return State.WAIT_FOR_INPUT;
495499
}
496500

497-
const input = this._zone.value.widget.value;
501+
const input = this.getInput();
498502

499503
if (!InlineChatController._promptHistory.includes(input)) {
500504
InlineChatController._promptHistory.unshift(input);
@@ -565,7 +569,7 @@ export class InlineChatController implements IEditorContribution {
565569
this._zone.value.widget.updateInfo(data.message);
566570
}
567571
if (data.slashCommand) {
568-
const valueNow = this._zone.value.widget.value;
572+
const valueNow = this.getInput();
569573
if (!valueNow.startsWith('/')) {
570574
this._zone.value.widget.updateSlashCommandUsed(data.slashCommand);
571575
}
@@ -848,9 +852,15 @@ export class InlineChatController implements IEditorContribution {
848852
this._messages.fire(Message.ACCEPT_INPUT);
849853
}
850854

851-
updateInput(text: string): void {
855+
updateInput(text: string, selectAll = true): void {
852856
this._zone.value.widget.value = text;
853-
this._zone.value.widget.selectAll();
857+
if (selectAll) {
858+
this._zone.value.widget.selectAll();
859+
}
860+
}
861+
862+
getInput(): string {
863+
return this._zone.value.widget.value;
854864
}
855865

856866
regenerate(): void {

0 commit comments

Comments
 (0)