Skip to content

Commit 7d6225b

Browse files
authored
Merge pull request microsoft#180427 from microsoft/aiday/improvingInteractiveEditor
Improving the accessibility of interactive editor
2 parents b292560 + 638b02a commit 7d6225b

File tree

6 files changed

+106
-98
lines changed

6 files changed

+106
-98
lines changed

src/vs/workbench/contrib/interactiveEditor/browser/interactiveEditor.contribution.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ registerAction2(interactiveEditorActions.ArrowOutDownAction);
2424
registerAction2(interactiveEditorActions.FocusInteractiveEditor);
2525
registerAction2(interactiveEditorActions.PreviousFromHistory);
2626
registerAction2(interactiveEditorActions.NextFromHistory);
27+
registerAction2(interactiveEditorActions.ViewInChatAction);
2728

2829
registerAction2(interactiveEditorActions.UndoToClipboard);
2930
registerAction2(interactiveEditorActions.UndoToNewFile);

src/vs/workbench/contrib/interactiveEditor/browser/interactiveEditor.css

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -100,36 +100,34 @@
100100
display: none;
101101
}
102102

103-
.monaco-editor .interactive-editor .status .link {
104-
color: var(--vscode-textLink-foreground);
105-
cursor: pointer;
106-
font-size: 12px;
107-
white-space: nowrap;
108-
/* margin-top: auto; */
109-
}
110-
111103
.monaco-editor .interactive-editor .status .label {
112104
overflow: hidden;
113-
font-size: 12px;
105+
padding-left: 10px;
114106
margin-left: auto;
115107
}
116108

117-
.monaco-editor .interactive-editor .status .label.message>span>p {
118-
margin: 0px;
109+
.monaco-editor .interactive-editor .markdownMessage {
110+
padding-top: 10px;
111+
}
112+
113+
.monaco-editor .interactive-editor .markdownMessage.hidden {
114+
display: none;
115+
}
116+
117+
.monaco-editor .interactive-editor .markdownMessage .message * {
118+
margin: unset;
119+
}
120+
121+
.monaco-editor .interactive-editor .markdownMessage .message {
122+
margin-left: 5px;
119123
-webkit-line-clamp: 3;
120124
-webkit-box-orient: vertical;
121125
overflow: hidden;
122126
display: -webkit-box;
123127
}
124128

125-
.monaco-editor .interactive-editor .status .link .status-link {
126-
padding-left: 10px;
127-
}
128-
129-
.monaco-editor .interactive-editor .status .link .status-link .codicon {
130-
line-height: unset;
131-
font-size: 12px;
132-
padding-right: 4px;
129+
.monaco-editor .interactive-editor .markdownMessage .messageActions {
130+
direction: rtl;
133131
}
134132

135133
.monaco-editor .interactive-editor .status .label A {

src/vs/workbench/contrib/interactiveEditor/browser/interactiveEditorActions.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { EditorAction2 } from 'vs/editor/browser/editorExtensions';
1010
import { EmbeddedCodeEditorWidget, EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget';
1111
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
1212
import { InteractiveEditorController, InteractiveEditorRunOptions, Recording } from 'vs/workbench/contrib/interactiveEditor/browser/interactiveEditorController';
13-
import { CTX_INTERACTIVE_EDITOR_FOCUSED, CTX_INTERACTIVE_EDITOR_HAS_ACTIVE_REQUEST, CTX_INTERACTIVE_EDITOR_HAS_PROVIDER, CTX_INTERACTIVE_EDITOR_INNER_CURSOR_FIRST, CTX_INTERACTIVE_EDITOR_INNER_CURSOR_LAST, CTX_INTERACTIVE_EDITOR_EMPTY, CTX_INTERACTIVE_EDITOR_OUTER_CURSOR_POSITION, CTX_INTERACTIVE_EDITOR_VISIBLE, MENU_INTERACTIVE_EDITOR_WIDGET, CTX_INTERACTIVE_EDITOR_LAST_EDIT_TYPE, MENU_INTERACTIVE_EDITOR_WIDGET_UNDO, MENU_INTERACTIVE_EDITOR_WIDGET_STATUS, CTX_INTERACTIVE_EDITOR_LAST_FEEDBACK, CTX_INTERACTIVE_EDITOR_INLNE_DIFF, CTX_INTERACTIVE_EDITOR_EDIT_MODE, EditMode, CTX_INTERACTIVE_EDITOR_LAST_RESPONSE_TYPE } from 'vs/workbench/contrib/interactiveEditor/common/interactiveEditor';
13+
import { CTX_INTERACTIVE_EDITOR_FOCUSED, CTX_INTERACTIVE_EDITOR_HAS_ACTIVE_REQUEST, CTX_INTERACTIVE_EDITOR_HAS_PROVIDER, CTX_INTERACTIVE_EDITOR_INNER_CURSOR_FIRST, CTX_INTERACTIVE_EDITOR_INNER_CURSOR_LAST, CTX_INTERACTIVE_EDITOR_EMPTY, CTX_INTERACTIVE_EDITOR_OUTER_CURSOR_POSITION, CTX_INTERACTIVE_EDITOR_VISIBLE, MENU_INTERACTIVE_EDITOR_WIDGET, CTX_INTERACTIVE_EDITOR_LAST_EDIT_TYPE, MENU_INTERACTIVE_EDITOR_WIDGET_UNDO, MENU_INTERACTIVE_EDITOR_WIDGET_STATUS, CTX_INTERACTIVE_EDITOR_LAST_FEEDBACK, CTX_INTERACTIVE_EDITOR_INLNE_DIFF, CTX_INTERACTIVE_EDITOR_EDIT_MODE, EditMode, CTX_INTERACTIVE_EDITOR_LAST_RESPONSE_TYPE, MENU_INTERACTIVE_EDITOR_WIDGET_MARKDOWN_MESSAGE } from 'vs/workbench/contrib/interactiveEditor/common/interactiveEditor';
1414
import { localize } from 'vs/nls';
1515
import { IAction2Options } from 'vs/platform/actions/common/actions';
1616
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
@@ -413,7 +413,6 @@ export class ApplyPreviewEdits extends AbstractInteractiveEditorAction {
413413
},
414414
menu: {
415415
id: MENU_INTERACTIVE_EDITOR_WIDGET_STATUS,
416-
when: CTX_INTERACTIVE_EDITOR_LAST_RESPONSE_TYPE.notEqualsTo('message'),
417416
group: '0_main',
418417
order: 0
419418
}
@@ -449,7 +448,6 @@ export class CancelSessionAction extends AbstractInteractiveEditorAction {
449448
},
450449
menu: {
451450
id: MENU_INTERACTIVE_EDITOR_WIDGET_STATUS,
452-
when: CTX_INTERACTIVE_EDITOR_LAST_RESPONSE_TYPE.notEqualsTo('message'),
453451
group: '0_main',
454452
order: 1
455453
}
@@ -501,3 +499,23 @@ export class CopyRecordings extends AbstractInteractiveEditorAction {
501499
}
502500
}
503501
}
502+
503+
export class ViewInChatAction extends AbstractInteractiveEditorAction {
504+
constructor() {
505+
super({
506+
id: 'interactiveEditor.viewInChat',
507+
title: localize('viewInChat', 'View in Chat'),
508+
icon: Codicon.commentDiscussion,
509+
precondition: CTX_INTERACTIVE_EDITOR_VISIBLE,
510+
menu: {
511+
id: MENU_INTERACTIVE_EDITOR_WIDGET_MARKDOWN_MESSAGE,
512+
when: CTX_INTERACTIVE_EDITOR_LAST_RESPONSE_TYPE.isEqualTo('message'),
513+
group: 'viewInChat',
514+
order: 1
515+
}
516+
});
517+
}
518+
override runInteractiveEditorCommand(_accessor: ServicesAccessor, ctrl: InteractiveEditorController, _editor: ICodeEditor, ..._args: any[]): void {
519+
ctrl.viewInChat();
520+
}
521+
}

src/vs/workbench/contrib/interactiveEditor/browser/interactiveEditorController.ts

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storag
4141
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
4242
import { InteractiveEditorDiffWidget } from 'vs/workbench/contrib/interactiveEditor/browser/interactiveEditorDiffWidget';
4343
import { InteractiveEditorZoneWidget } from 'vs/workbench/contrib/interactiveEditor/browser/interactiveEditorWidget';
44-
import { CTX_INTERACTIVE_EDITOR_HAS_ACTIVE_REQUEST, CTX_INTERACTIVE_EDITOR_INLNE_DIFF, CTX_INTERACTIVE_EDITOR_LAST_EDIT_TYPE as CTX_INTERACTIVE_EDITOR_LAST_EDIT_KIND, CTX_INTERACTIVE_EDITOR_LAST_FEEDBACK as CTX_INTERACTIVE_EDITOR_LAST_FEEDBACK_KIND, IInteractiveEditorBulkEditResponse, IInteractiveEditorEditResponse, IInteractiveEditorRequest, IInteractiveEditorResponse, IInteractiveEditorService, IInteractiveEditorSession, IInteractiveEditorSessionProvider, IInteractiveEditorSlashCommand, INTERACTIVE_EDITOR_ID, EditMode, InteractiveEditorResponseFeedbackKind, CTX_INTERACTIVE_EDITOR_LAST_RESPONSE_TYPE, InteractiveEditorResponseType } from 'vs/workbench/contrib/interactiveEditor/common/interactiveEditor';
44+
import { CTX_INTERACTIVE_EDITOR_HAS_ACTIVE_REQUEST, CTX_INTERACTIVE_EDITOR_INLNE_DIFF, CTX_INTERACTIVE_EDITOR_LAST_EDIT_TYPE as CTX_INTERACTIVE_EDITOR_LAST_EDIT_KIND, CTX_INTERACTIVE_EDITOR_LAST_FEEDBACK as CTX_INTERACTIVE_EDITOR_LAST_FEEDBACK_KIND, IInteractiveEditorBulkEditResponse, IInteractiveEditorEditResponse, IInteractiveEditorRequest, IInteractiveEditorResponse, IInteractiveEditorService, IInteractiveEditorSession, IInteractiveEditorSessionProvider, IInteractiveEditorSlashCommand, INTERACTIVE_EDITOR_ID, EditMode, InteractiveEditorResponseFeedbackKind, CTX_INTERACTIVE_EDITOR_LAST_RESPONSE_TYPE, InteractiveEditorResponseType, IInteractiveEditorMessageResponse } from 'vs/workbench/contrib/interactiveEditor/common/interactiveEditor';
4545
import { IInteractiveSessionWidgetService } from 'vs/workbench/contrib/interactiveSession/browser/interactiveSessionWidget';
4646
import { IInteractiveSessionService } from 'vs/workbench/contrib/interactiveSession/common/interactiveSessionService';
4747
import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService';
@@ -217,7 +217,7 @@ export class EditResponse {
217217

218218
class Session {
219219

220-
private readonly _responses: EditResponse[] = [];
220+
private readonly _responses: (EditResponse | IInteractiveEditorMessageResponse)[] = [];
221221

222222
readonly teldata: TelemetryData;
223223

@@ -240,12 +240,12 @@ class Session {
240240
};
241241
}
242242

243-
addResponse(response: EditResponse): void {
243+
addResponse(response: EditResponse | IInteractiveEditorMessageResponse): void {
244244
const newLen = this._responses.push(response);
245245
this.teldata.rounds += `${newLen}|`;
246246
}
247247

248-
get lastResponse(): EditResponse | undefined {
248+
get lastResponse(): EditResponse | IInteractiveEditorMessageResponse | undefined {
249249
return this._responses[this._responses.length - 1];
250250
}
251251
}
@@ -297,6 +297,9 @@ export class InteractiveEditorController implements IEditorContribution {
297297
private _ctsSession: CancellationTokenSource = new CancellationTokenSource();
298298
private _ctsRequest?: CancellationTokenSource;
299299

300+
private _requestPrompt: string | undefined;
301+
private _messageReply: string | undefined;
302+
300303
constructor(
301304
private readonly _editor: ICodeEditor,
302305
@IInstantiationService private readonly _instaService: IInstantiationService,
@@ -333,6 +336,12 @@ export class InteractiveEditorController implements IEditorContribution {
333336
return this._configurationService.getValue('interactiveEditor.editMode');
334337
}
335338

339+
viewInChat() {
340+
if (this._messageReply && this._requestPrompt) {
341+
this._instaService.invokeFunction(showMessageResponse, this._requestPrompt, this._messageReply);
342+
}
343+
}
344+
336345
async run(options: InteractiveEditorRunOptions | undefined): Promise<void> {
337346

338347
// hide/cancel inline completions when invoking IE
@@ -403,7 +412,7 @@ export class InteractiveEditorController implements IEditorContribution {
403412
store.add(this._instaService.invokeFunction(installSlashCommandSupport, this._zone.widget.inputEditor as IActiveCodeEditor, session.slashCommands));
404413
}
405414

406-
this._zone.widget.updateMessage(session.message ?? localize('welcome.1', "AI-generated code may be incorrect."));
415+
this._zone.widget.updateStatus(session.message ?? localize('welcome.1', "AI-generated code may be incorrect."));
407416

408417
// CANCEL when input changes
409418
this._editor.onDidChangeModel(this.cancelSession, this, store);
@@ -535,7 +544,7 @@ export class InteractiveEditorController implements IEditorContribution {
535544
if (!isCancellationError(e)) {
536545
this._logService.error('[IE] ERROR during request', provider.debugName);
537546
this._logService.error(e);
538-
this._zone.widget.updateMessage(toErrorMessage(e), { classes: ['error'] });
547+
this._zone.widget.updateStatus(toErrorMessage(e), { classes: ['error'] });
539548
// statusWidget
540549
continue;
541550
}
@@ -553,7 +562,7 @@ export class InteractiveEditorController implements IEditorContribution {
553562

554563
if (!reply) {
555564
this._logService.trace('[IE] NO reply or edits', provider.debugName);
556-
this._zone.widget.updateMessage(localize('empty', "No results, please refine your input and try again."), { classes: ['warn'] });
565+
this._zone.widget.updateStatus(localize('empty', "No results, please refine your input and try again."), { classes: ['warn'] });
557566
continue;
558567
}
559568

@@ -563,9 +572,11 @@ export class InteractiveEditorController implements IEditorContribution {
563572

564573
if (reply.type === 'message') {
565574
this._logService.info('[IE] received a MESSAGE, continuing outside editor', provider.debugName);
566-
const messageReply = reply.message;
567-
const renderedMarkdown = renderMarkdown(messageReply, { inline: true });
568-
this._zone.widget.updateMessage(renderedMarkdown.element, { linkListener: () => this._instaService.invokeFunction(showMessageResponse, request.prompt, messageReply.value), isMessageReply: true });
575+
this._messageReply = reply.message.value;
576+
this._requestPrompt = request.prompt;
577+
const renderedMarkdown = renderMarkdown(reply.message, { inline: true });
578+
this._zone.widget.updateMarkdownMessage(renderedMarkdown.element);
579+
this._currentSession.addResponse(reply);
569580
continue;
570581
}
571582

@@ -655,7 +666,7 @@ export class InteractiveEditorController implements IEditorContribution {
655666
} else {
656667
message = localize('lines.N', "Generated reply and changed {0} lines.", linesChanged);
657668
}
658-
this._zone.widget.updateMessage(message);
669+
this._zone.widget.updateStatus(message);
659670
}
660671

661672

@@ -777,7 +788,7 @@ export class InteractiveEditorController implements IEditorContribution {
777788
}
778789

779790
undoLast(): string | void {
780-
if (this._currentSession?.lastResponse) {
791+
if (this._currentSession?.lastResponse instanceof EditResponse) {
781792
this._currentSession.modelN.undo();
782793
return this._currentSession.lastResponse.localEdits[0].text;
783794
}
@@ -786,14 +797,14 @@ export class InteractiveEditorController implements IEditorContribution {
786797
feedbackLast(helpful: boolean) {
787798
if (this._currentSession?.lastResponse) {
788799
const kind = helpful ? InteractiveEditorResponseFeedbackKind.Helpful : InteractiveEditorResponseFeedbackKind.Unhelpful;
789-
this._currentSession.provider.handleInteractiveEditorResponseFeedback?.(this._currentSession.session, this._currentSession.lastResponse.raw, kind);
800+
this._currentSession.provider.handleInteractiveEditorResponseFeedback?.(this._currentSession.session, this._currentSession.lastResponse instanceof EditResponse ? this._currentSession.lastResponse.raw : this._currentSession.lastResponse, kind);
790801
this._ctxLastFeedbackKind.set(helpful ? 'helpful' : 'unhelpful');
791-
this._zone.widget.updateMessage('Thank you for your feedback!', { resetAfter: 1250 });
802+
this._zone.widget.updateStatus('Thank you for your feedback!', { resetAfter: 1250 });
792803
}
793804
}
794805

795806
async applyChanges(): Promise<EditResponse | void> {
796-
if (this._currentSession?.lastResponse) {
807+
if (this._currentSession?.lastResponse instanceof EditResponse) {
797808
const { lastResponse } = this._currentSession;
798809
await this._strategy?.apply();
799810
this._ctsSession.cancel();
@@ -845,7 +856,7 @@ class PreviewStrategy extends EditModeStrategy {
845856
async apply() {
846857

847858
const response = this._session.lastResponse;
848-
if (!response) {
859+
if (!(response instanceof EditResponse)) {
849860
return;
850861
}
851862

@@ -1007,7 +1018,8 @@ async function showMessageResponse(accessor: ServicesAccessor, query: string, re
10071018
const interactiveSessionWidgetService = accessor.get(IInteractiveSessionWidgetService);
10081019
const widget = await interactiveSessionWidgetService.revealViewForProvider(providerId);
10091020
if (widget && widget.viewModel) {
1010-
interactiveSessionService.addCompleteRequest(widget.viewModel.sessionId, query, { message: response });
1021+
await interactiveSessionService.addCompleteRequest(widget.viewModel.sessionId, query, { message: response });
1022+
widget.focusLastMessage();
10111023
}
10121024
}
10131025

0 commit comments

Comments
 (0)