Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

voice - polish UX when recording #196544

Merged
merged 1 commit into from Oct 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions src/vs/workbench/contrib/chat/browser/chat.ts
Expand Up @@ -112,6 +112,7 @@ export interface IChatWidget {
moveFocus(item: ChatTreeItem, type: 'next' | 'previous'): void;
getFocus(): ChatTreeItem | undefined;
updateInput(query?: string): void;
getInput(): string;
acceptInput(query?: string): void;
acceptInputWithPrefix(prefix: string): void;
setInputPlaceholder(placeholder: string): void;
Expand Down
8 changes: 6 additions & 2 deletions src/vs/workbench/contrib/chat/browser/chatWidget.ts
Expand Up @@ -514,6 +514,10 @@ export class ChatWidget extends Disposable implements IChatWidget {
this.inputPart.setValue(value);
}

getInput(): string {
return this.inputPart.inputEditor.getValue();
}

async acceptInput(query?: string): Promise<void> {
this._acceptInput(query ? { query } : undefined);
}
Expand All @@ -526,7 +530,7 @@ export class ChatWidget extends Disposable implements IChatWidget {
if (this.viewModel) {
this._onDidAcceptInput.fire();

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

getViewState(): IViewState {
this.inputPart.saveState();
return { inputValue: this.inputPart.inputEditor.getValue() };
return { inputValue: this.getInput() };
}
}

Expand Down
Expand Up @@ -19,7 +19,7 @@ import { CHAT_CATEGORY } from 'vs/workbench/contrib/chat/browser/actions/chatAct
import { IChatWidget, IChatWidgetService, IQuickChatService } from 'vs/workbench/contrib/chat/browser/chat';
import { IChatService } from 'vs/workbench/contrib/chat/common/chatService';
import { MENU_INLINE_CHAT_WIDGET } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
import { CONTEXT_PROVIDER_EXISTS } from 'vs/workbench/contrib/chat/common/chatContextKeys';
import { CONTEXT_CHAT_REQUEST_IN_PROGRESS, CONTEXT_PROVIDER_EXISTS } from 'vs/workbench/contrib/chat/common/chatContextKeys';
import { InlineChatController } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { getCodeEditor } from 'vs/editor/browser/editorBrowser';
Expand Down Expand Up @@ -59,6 +59,7 @@ interface IVoiceChatSessionController {
focusInput(): void;
acceptInput(): void;
updateInput(text: string): void;
getInput(): string;

setInputPlaceholder(text: string): void;
clearInputPlaceholder(): void;
Expand Down Expand Up @@ -166,6 +167,7 @@ class VoiceChatSessionControllerFactory {
focusInput: () => chatView.focusInput(),
acceptInput: () => chatView.acceptInput(),
updateInput: text => chatView.updateInput(text),
getInput: () => chatView.getInput(),
setInputPlaceholder: text => chatView.setInputPlaceholder(text),
clearInputPlaceholder: () => chatView.resetInputPlaceholder()
};
Expand All @@ -179,13 +181,14 @@ class VoiceChatSessionControllerFactory {
focusInput: () => quickChat.focusInput(),
acceptInput: () => quickChat.acceptInput(),
updateInput: text => quickChat.updateInput(text),
getInput: () => quickChat.getInput(),
setInputPlaceholder: text => quickChat.setInputPlaceholder(text),
clearInputPlaceholder: () => quickChat.resetInputPlaceholder()
};
}

private static doCreateForInlineChat(inlineChat: InlineChatController,): IVoiceChatSessionController {
const inlineChatSession = inlineChat.run();
private static doCreateForInlineChat(inlineChat: InlineChatController): IVoiceChatSessionController {
const inlineChatSession = inlineChat.joinCurrentRun() ?? inlineChat.run();

return {
context: 'inline',
Expand All @@ -196,7 +199,8 @@ class VoiceChatSessionControllerFactory {
),
focusInput: () => inlineChat.focus(),
acceptInput: () => inlineChat.acceptInput(),
updateInput: text => inlineChat.updateInput(text),
updateInput: text => inlineChat.updateInput(text, false),
getInput: () => inlineChat.getInput(),
setInputPlaceholder: text => inlineChat.setPlaceholder(text),
clearInputPlaceholder: () => inlineChat.resetPlaceholder()
};
Expand Down Expand Up @@ -252,14 +256,13 @@ class VoiceChatSessions {
session.disposables.add(controller.onDidAcceptInput(() => this.stop(sessionId, controller.context)));
session.disposables.add(controller.onDidCancelInput(() => this.stop(sessionId, controller.context)));

controller.updateInput('');
controller.focusInput();

this.voiceChatGettingReadyKey.set(true);

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

let transcription: string = '';
let inputValue = controller.getInput();
const acceptTranscriptionScheduler = session.disposables.add(new RunOnceScheduler(() => session.controller.acceptInput(), 1200));
session.disposables.add(speechToTextSession.onDidChange(({ status, text }) => {
if (cts.token.isCancellationRequested) {
Expand All @@ -272,14 +275,14 @@ class VoiceChatSessions {
break;
case SpeechToTextStatus.Recognizing:
if (text) {
session.controller.updateInput([transcription, text].join(' '));
session.controller.updateInput([inputValue, text].join(' '));
acceptTranscriptionScheduler.cancel();
}
break;
case SpeechToTextStatus.Recognized:
if (text) {
transcription = [transcription, text].join(' ');
session.controller.updateInput(transcription);
inputValue = [inputValue, text].join(' ');
session.controller.updateInput(inputValue);
acceptTranscriptionScheduler.schedule();
}
break;
Expand Down Expand Up @@ -368,7 +371,7 @@ export class VoiceChatInChatViewAction extends Action2 {
original: 'Voice Chat in Chat View'
},
category: CHAT_CATEGORY,
precondition: ContextKeyExpr.and(HasSpeechProvider, CONTEXT_PROVIDER_EXISTS),
precondition: ContextKeyExpr.and(HasSpeechProvider, CONTEXT_PROVIDER_EXISTS, CONTEXT_CHAT_REQUEST_IN_PROGRESS.negate()),
f1: true
});
}
Expand All @@ -395,7 +398,7 @@ export class InlineVoiceChatAction extends Action2 {
original: 'Inline Voice Chat'
},
category: CHAT_CATEGORY,
precondition: ContextKeyExpr.and(HasSpeechProvider, CONTEXT_PROVIDER_EXISTS, ActiveEditorContext),
precondition: ContextKeyExpr.and(HasSpeechProvider, CONTEXT_PROVIDER_EXISTS, ActiveEditorContext, CONTEXT_CHAT_REQUEST_IN_PROGRESS.negate()),
f1: true
});
}
Expand All @@ -422,7 +425,7 @@ export class QuickVoiceChatAction extends Action2 {
original: 'Quick Voice Chat'
},
category: CHAT_CATEGORY,
precondition: ContextKeyExpr.and(HasSpeechProvider, CONTEXT_PROVIDER_EXISTS),
precondition: ContextKeyExpr.and(HasSpeechProvider, CONTEXT_PROVIDER_EXISTS, CONTEXT_CHAT_REQUEST_IN_PROGRESS.negate()),
f1: true
});
}
Expand Down Expand Up @@ -450,7 +453,7 @@ export class StartVoiceChatAction extends Action2 {
},
category: CHAT_CATEGORY,
icon: Codicon.mic,
precondition: ContextKeyExpr.and(HasSpeechProvider, CONTEXT_VOICE_CHAT_GETTING_READY.negate()),
precondition: ContextKeyExpr.and(HasSpeechProvider, CONTEXT_VOICE_CHAT_GETTING_READY.negate(), CONTEXT_CHAT_REQUEST_IN_PROGRESS.negate()),
menu: [{
id: MenuId.ChatExecute,
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()),
Expand Down
30 changes: 20 additions & 10 deletions src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts
Expand Up @@ -230,6 +230,10 @@ export class InlineChatController implements IEditorContribution {
}
}

joinCurrentRun(): Promise<void> | undefined {
return this._currentRun;
}

// ---- state machine

private _showWidget(initialRender: boolean = false, position?: IPosition) {
Expand Down Expand Up @@ -405,14 +409,14 @@ export class InlineChatController implements IEditorContribution {
}
}

private _placeholder: string | undefined = undefined;
private _forcedPlaceholder: string | undefined = undefined;
setPlaceholder(text: string): void {
this._placeholder = text;
this._forcedPlaceholder = text;
this._updatePlaceholder();
}

resetPlaceholder(): void {
this._placeholder = undefined;
this._forcedPlaceholder = undefined;
this._updatePlaceholder();
}

Expand All @@ -421,8 +425,8 @@ export class InlineChatController implements IEditorContribution {
}

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

Expand Down Expand Up @@ -490,11 +494,11 @@ export class InlineChatController implements IEditorContribution {
return State.MAKE_REQUEST;
}

if (!this._zone.value.widget.value) {
if (!this.getInput()) {
return State.WAIT_FOR_INPUT;
}

const input = this._zone.value.widget.value;
const input = this.getInput();

if (!InlineChatController._promptHistory.includes(input)) {
InlineChatController._promptHistory.unshift(input);
Expand Down Expand Up @@ -565,7 +569,7 @@ export class InlineChatController implements IEditorContribution {
this._zone.value.widget.updateInfo(data.message);
}
if (data.slashCommand) {
const valueNow = this._zone.value.widget.value;
const valueNow = this.getInput();
if (!valueNow.startsWith('/')) {
this._zone.value.widget.updateSlashCommandUsed(data.slashCommand);
}
Expand Down Expand Up @@ -848,9 +852,15 @@ export class InlineChatController implements IEditorContribution {
this._messages.fire(Message.ACCEPT_INPUT);
}

updateInput(text: string): void {
updateInput(text: string, selectAll = true): void {
this._zone.value.widget.value = text;
this._zone.value.widget.selectAll();
if (selectAll) {
this._zone.value.widget.selectAll();
}
}

getInput(): string {
return this._zone.value.widget.value;
}

regenerate(): void {
Expand Down