Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/chat/browser/actions/chatNewActions.ts
4780 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { Codicon } from '../../../../../base/common/codicons.js';
7
import { KeyCode, KeyMod } from '../../../../../base/common/keyCodes.js';
8
import { ServicesAccessor } from '../../../../../editor/browser/editorExtensions.js';
9
import { localize, localize2 } from '../../../../../nls.js';
10
import { IAccessibilityService } from '../../../../../platform/accessibility/common/accessibility.js';
11
import { Action2, MenuId, MenuRegistry, registerAction2 } from '../../../../../platform/actions/common/actions.js';
12
import { CommandsRegistry } from '../../../../../platform/commands/common/commands.js';
13
import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js';
14
import { IDialogService } from '../../../../../platform/dialogs/common/dialogs.js';
15
import { KeybindingWeight } from '../../../../../platform/keybinding/common/keybindingsRegistry.js';
16
import { ActiveEditorContext } from '../../../../common/contextkeys.js';
17
import { ChatContextKeys } from '../../common/actions/chatContextKeys.js';
18
import { IChatEditingSession } from '../../common/editing/chatEditingService.js';
19
import { IChatService } from '../../common/chatService/chatService.js';
20
import { ChatAgentLocation, ChatModeKind } from '../../common/constants.js';
21
import { ChatViewId, IChatWidgetService } from '../chat.js';
22
import { EditingSessionAction, getEditingSessionContext } from '../chatEditing/chatEditingActions.js';
23
import { ChatEditorInput } from '../widgetHosts/editor/chatEditorInput.js';
24
import { ACTION_ID_NEW_CHAT, ACTION_ID_NEW_EDIT_SESSION, CHAT_CATEGORY, handleCurrentEditingSession } from './chatActions.js';
25
import { clearChatEditor } from './chatClear.js';
26
import { AgentSessionsViewerOrientation } from '../agentSessions/agentSessions.js';
27
28
export interface INewEditSessionActionContext {
29
30
/**
31
* An initial prompt to write to the chat.
32
*/
33
inputValue?: string;
34
35
/**
36
* Selects opening in agent mode or not. If not set, the current mode is used.
37
* This is ignored when coming from a chat view title context.
38
*/
39
agentMode?: boolean;
40
41
/**
42
* Whether the inputValue is partial and should wait for further user input.
43
* If false or not set, the prompt is sent immediately.
44
*/
45
isPartialQuery?: boolean;
46
}
47
48
export function registerNewChatActions() {
49
50
// Add "New Chat" submenu to Chat view menu
51
MenuRegistry.appendMenuItem(MenuId.ViewTitle, {
52
submenu: MenuId.ChatNewMenu,
53
title: localize2('chat.newEdits.label', "New Chat"),
54
icon: Codicon.plus,
55
when: ContextKeyExpr.equals('view', ChatViewId),
56
group: 'navigation',
57
order: -1,
58
isSplitButton: true
59
});
60
61
registerAction2(class NewChatEditorAction extends Action2 {
62
constructor() {
63
super({
64
id: 'workbench.action.chatEditor.newChat',
65
title: localize2('chat.newChat.label', "New Chat"),
66
icon: Codicon.plus,
67
f1: false,
68
precondition: ChatContextKeys.enabled,
69
});
70
}
71
async run(accessor: ServicesAccessor, ...args: unknown[]) {
72
await clearChatEditor(accessor);
73
}
74
});
75
76
registerAction2(class NewChatAction extends Action2 {
77
constructor() {
78
super({
79
id: ACTION_ID_NEW_CHAT,
80
title: localize2('chat.newEdits.label', "New Chat"),
81
category: CHAT_CATEGORY,
82
icon: Codicon.plus,
83
precondition: ContextKeyExpr.and(ChatContextKeys.enabled, ChatContextKeys.location.isEqualTo(ChatAgentLocation.Chat)),
84
f1: true,
85
menu: [
86
{
87
id: MenuId.ChatContext,
88
group: 'z_clear'
89
},
90
{
91
id: MenuId.ChatNewMenu,
92
group: '1_open',
93
order: 1,
94
},
95
{
96
id: MenuId.CompactWindowEditorTitle,
97
group: 'navigation',
98
when: ContextKeyExpr.and(ActiveEditorContext.isEqualTo(ChatEditorInput.EditorID), ChatContextKeys.lockedToCodingAgent.negate()),
99
order: 1
100
}
101
],
102
keybinding: {
103
weight: KeybindingWeight.WorkbenchContrib + 1,
104
primary: KeyMod.CtrlCmd | KeyCode.KeyN,
105
secondary: [KeyMod.CtrlCmd | KeyCode.KeyL],
106
mac: {
107
primary: KeyMod.CtrlCmd | KeyCode.KeyN,
108
secondary: [KeyMod.WinCtrl | KeyCode.KeyL]
109
},
110
when: ChatContextKeys.inChatSession
111
}
112
});
113
}
114
115
async run(accessor: ServicesAccessor, ...args: unknown[]) {
116
const accessibilityService = accessor.get(IAccessibilityService);
117
118
const executeCommandContext = args[0] as INewEditSessionActionContext | undefined;
119
120
// Context from toolbar or lastFocusedWidget
121
const context = getEditingSessionContext(accessor, args);
122
const { editingSession, chatWidget: widget } = context ?? {};
123
if (!widget) {
124
return;
125
}
126
127
const dialogService = accessor.get(IDialogService);
128
129
const model = widget.viewModel?.model;
130
if (model && !(await handleCurrentEditingSession(model, undefined, dialogService))) {
131
return;
132
}
133
134
await editingSession?.stop();
135
await widget.clear();
136
widget.attachmentModel.clear(true);
137
widget.input.relatedFiles?.clear();
138
widget.focusInput();
139
140
accessibilityService.alert(localize('newChat', "New chat"));
141
142
if (!executeCommandContext) {
143
return;
144
}
145
146
if (typeof executeCommandContext.agentMode === 'boolean') {
147
widget.input.setChatMode(executeCommandContext.agentMode ? ChatModeKind.Agent : ChatModeKind.Edit);
148
}
149
150
if (executeCommandContext.inputValue) {
151
if (executeCommandContext.isPartialQuery) {
152
widget.setInput(executeCommandContext.inputValue);
153
} else {
154
widget.acceptInput(executeCommandContext.inputValue);
155
}
156
}
157
}
158
});
159
CommandsRegistry.registerCommandAlias(ACTION_ID_NEW_EDIT_SESSION, ACTION_ID_NEW_CHAT);
160
161
MenuRegistry.appendMenuItem(MenuId.ChatViewSessionTitleNavigationToolbar, {
162
command: {
163
id: ACTION_ID_NEW_CHAT,
164
title: localize2('chat.goBack', "Go Back"),
165
icon: Codicon.arrowLeft,
166
},
167
when: ChatContextKeys.agentSessionsViewerOrientation.notEqualsTo(AgentSessionsViewerOrientation.SideBySide), // when sessions show side by side, no need for a back button
168
group: 'navigation',
169
order: 1
170
});
171
172
registerAction2(class UndoChatEditInteractionAction extends EditingSessionAction {
173
constructor() {
174
super({
175
id: 'workbench.action.chat.undoEdit',
176
title: localize2('chat.undoEdit.label', "Undo Last Edit"),
177
category: CHAT_CATEGORY,
178
icon: Codicon.discard,
179
precondition: ContextKeyExpr.and(ChatContextKeys.chatEditingCanUndo, ChatContextKeys.enabled),
180
f1: true,
181
menu: [{
182
id: MenuId.ViewTitle,
183
when: ContextKeyExpr.equals('view', ChatViewId),
184
group: 'navigation',
185
order: -3,
186
isHiddenByDefault: true
187
}]
188
});
189
}
190
191
async runEditingSessionAction(accessor: ServicesAccessor, editingSession: IChatEditingSession) {
192
await editingSession.undoInteraction();
193
}
194
});
195
196
registerAction2(class RedoChatEditInteractionAction extends EditingSessionAction {
197
constructor() {
198
super({
199
id: 'workbench.action.chat.redoEdit',
200
title: localize2('chat.redoEdit.label', "Redo Last Edit"),
201
category: CHAT_CATEGORY,
202
icon: Codicon.redo,
203
precondition: ContextKeyExpr.and(ChatContextKeys.chatEditingCanRedo, ChatContextKeys.enabled),
204
f1: true,
205
menu: [
206
{
207
id: MenuId.ViewTitle,
208
when: ContextKeyExpr.equals('view', ChatViewId),
209
group: 'navigation',
210
order: -2,
211
isHiddenByDefault: true
212
}
213
]
214
});
215
}
216
217
async runEditingSessionAction(accessor: ServicesAccessor, editingSession: IChatEditingSession) {
218
const chatService = accessor.get(IChatService);
219
await editingSession.redoInteraction();
220
chatService.getSession(editingSession.chatSessionResource)?.setCheckpoint(undefined);
221
}
222
});
223
224
registerAction2(class RedoChatCheckpoints extends EditingSessionAction {
225
constructor() {
226
super({
227
id: 'workbench.action.chat.redoEdit2',
228
title: localize2('chat.redoEdit.label2', "Redo"),
229
tooltip: localize2('chat.redoEdit.tooltip', "Reapply discarded workspace changes and chat"),
230
category: CHAT_CATEGORY,
231
precondition: ContextKeyExpr.and(ChatContextKeys.chatEditingCanRedo, ChatContextKeys.enabled),
232
f1: true,
233
menu: [{
234
id: MenuId.ChatMessageRestoreCheckpoint,
235
when: ChatContextKeys.lockedToCodingAgent.negate(),
236
group: 'navigation',
237
order: -1
238
}]
239
});
240
}
241
242
async runEditingSessionAction(accessor: ServicesAccessor, editingSession: IChatEditingSession) {
243
const widget = accessor.get(IChatWidgetService);
244
245
while (editingSession.canRedo.get()) {
246
await editingSession.redoInteraction();
247
}
248
249
const currentWidget = widget.getWidgetBySessionResource(editingSession.chatSessionResource);
250
const requestText = currentWidget?.viewModel?.model.checkpoint?.message.text;
251
252
// if the input has the same text that we just restored, clear it.
253
if (currentWidget?.inputEditor.getValue() === requestText) {
254
currentWidget?.input.setValue('', false);
255
}
256
257
currentWidget?.viewModel?.model.setCheckpoint(undefined);
258
currentWidget?.focusInput();
259
}
260
});
261
}
262
263