diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatNewActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatNewActions.ts index d662fd086eb..648d7fd1329 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatNewActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatNewActions.ts @@ -24,12 +24,12 @@ import { localChatSessionType } from '../../common/chatSessionsService.js'; import { ChatAgentLocation, ChatModeKind } from '../../common/constants.js'; import { getChatSessionType, LocalChatSessionUri } from '../../common/model/chatUri.js'; import { ChatViewId, IChatWidgetService, isIChatViewViewContext } from '../chat.js'; -import { EditingSessionAction, getEditingSessionContext } from '../chatEditing/chatEditingActions.js'; +import { EditingSessionAction, EditingSessionActionContext, getEditingSessionContext } from '../chatEditing/chatEditingActions.js'; import { ChatEditorInput } from '../widgetHosts/editor/chatEditorInput.js'; import { ChatViewPane } from '../widgetHosts/viewPane/chatViewPane.js'; import { ACTION_ID_NEW_CHAT, ACTION_ID_NEW_EDIT_SESSION, CHAT_CATEGORY, handleCurrentEditingSession } from './chatActions.js'; import { clearChatEditor } from './chatClear.js'; -import { AgentSessionsViewerOrientation } from '../agentSessions/agentSessions.js'; +import { AgentSessionProviders, AgentSessionsViewerOrientation } from '../agentSessions/agentSessions.js'; export interface INewEditSessionActionContext { @@ -51,6 +51,23 @@ export interface INewEditSessionActionContext { isPartialQuery?: boolean; } +function isNewEditSessionActionContext(arg: unknown): arg is INewEditSessionActionContext { + if (arg && typeof arg === 'object') { + const obj = arg as Record; + if (obj.inputValue !== undefined && typeof obj.inputValue !== 'string') { + return false; + } + if (obj.agentMode !== undefined && typeof obj.agentMode !== 'boolean') { + return false; + } + if (obj.isPartialQuery !== undefined && typeof obj.isPartialQuery !== 'boolean') { + return false; + } + return true; + } + return false; +} + export function registerNewChatActions() { // Add "New Chat" submenu to Chat view menu @@ -119,64 +136,36 @@ export function registerNewChatActions() { } async run(accessor: ServicesAccessor, ...args: unknown[]) { - const accessibilityService = accessor.get(IAccessibilityService); - const viewsService = accessor.get(IViewsService); - - const executeCommandContext = args[0] as INewEditSessionActionContext | undefined; + const executeCommandContext = isNewEditSessionActionContext(args[0]) ? args[0] : undefined; // Context from toolbar or lastFocusedWidget const context = getEditingSessionContext(accessor, args); - const { editingSession, chatWidget: widget } = context ?? {}; - if (!widget) { - return; - } + await runNewChatAction(accessor, context, executeCommandContext); + } + } + ); + CommandsRegistry.registerCommandAlias(ACTION_ID_NEW_EDIT_SESSION, ACTION_ID_NEW_CHAT); - const dialogService = accessor.get(IDialogService); + registerAction2(class NewLocalChatAction extends Action2 { + constructor() { + super({ + id: 'workbench.action.chat.newLocalChat', + title: localize2('chat.newLocalChat.label', "New Local Chat"), + category: CHAT_CATEGORY, + icon: Codicon.plus, + precondition: ContextKeyExpr.and(ChatContextKeys.enabled, ChatContextKeys.location.isEqualTo(ChatAgentLocation.Chat)), + f1: false, + }); + } - const model = widget.viewModel?.model; - if (model && !(await handleCurrentEditingSession(model, undefined, dialogService))) { - return; - } + async run(accessor: ServicesAccessor, ...args: unknown[]) { + const executeCommandContext = isNewEditSessionActionContext(args[0]) ? args[0] : undefined; - await editingSession?.stop(); - - // Create a new session with the same type as the current session - const currentResource = widget.viewModel?.model.sessionResource; - const sessionType = currentResource ? getChatSessionType(currentResource) : localChatSessionType; - if (isIChatViewViewContext(widget.viewContext) && sessionType !== localChatSessionType) { - // For the sidebar, we need to explicitly load a session with the same type - const newResource = getResourceForNewChatSession(sessionType); - const view = await viewsService.openView(ChatViewId) as ChatViewPane; - await view.loadSession(newResource); - } else { - // For the editor, widget.clear() already preserves the session type via clearChatEditor - await widget.clear(); - } - - widget.attachmentModel.clear(true); - widget.input.relatedFiles?.clear(); - widget.focusInput(); - - accessibilityService.alert(localize('newChat', "New chat")); - - if (!executeCommandContext) { - return; - } - - if (typeof executeCommandContext.agentMode === 'boolean') { - widget.input.setChatMode(executeCommandContext.agentMode ? ChatModeKind.Agent : ChatModeKind.Edit); - } - - if (executeCommandContext.inputValue) { - if (executeCommandContext.isPartialQuery) { - widget.setInput(executeCommandContext.inputValue); - } else { - widget.acceptInput(executeCommandContext.inputValue); - } - } + // Context from toolbar or lastFocusedWidget + const context = getEditingSessionContext(accessor, args); + await runNewChatAction(accessor, context, executeCommandContext, AgentSessionProviders.Local); } }); - CommandsRegistry.registerCommandAlias(ACTION_ID_NEW_EDIT_SESSION, ACTION_ID_NEW_CHAT); MenuRegistry.appendMenuItem(MenuId.ChatViewSessionTitleNavigationToolbar, { command: { @@ -296,3 +285,62 @@ function getResourceForNewChatSession(sessionType: string): URI { return LocalChatSessionUri.forSession(generateUuid()); } + +async function runNewChatAction( + accessor: ServicesAccessor, + context: EditingSessionActionContext | undefined, + executeCommandContext?: INewEditSessionActionContext, + sessionType?: AgentSessionProviders +) { + const accessibilityService = accessor.get(IAccessibilityService); + const viewsService = accessor.get(IViewsService); + + const { editingSession, chatWidget: widget } = context ?? {}; + if (!widget) { + return; + } + + const dialogService = accessor.get(IDialogService); + + const model = widget.viewModel?.model; + if (model && !(await handleCurrentEditingSession(model, undefined, dialogService))) { + return; + } + + await editingSession?.stop(); + + // Create a new session with the same type as the current session + const currentResource = widget.viewModel?.model.sessionResource; + const newSessionType = sessionType ?? (currentResource ? getChatSessionType(currentResource) : localChatSessionType); + if (isIChatViewViewContext(widget.viewContext) && newSessionType !== localChatSessionType) { + // For the sidebar, we need to explicitly load a session with the same type + const newResource = getResourceForNewChatSession(newSessionType); + const view = await viewsService.openView(ChatViewId) as ChatViewPane; + await view.loadSession(newResource); + } else { + // For the editor, widget.clear() already preserves the session type via clearChatEditor + await widget.clear(); + } + + widget.attachmentModel.clear(true); + widget.input.relatedFiles?.clear(); + widget.focusInput(); + + accessibilityService.alert(localize('newChat', "New chat")); + + if (!executeCommandContext) { + return; + } + + if (typeof executeCommandContext.agentMode === 'boolean') { + widget.input.setChatMode(executeCommandContext.agentMode ? ChatModeKind.Agent : ChatModeKind.Edit); + } + + if (executeCommandContext.inputValue) { + if (executeCommandContext.isPartialQuery) { + widget.setInput(executeCommandContext.inputValue); + } else { + widget.acceptInput(executeCommandContext.inputValue); + } + } +} diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingActions.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingActions.ts index a2cde761222..30404affd13 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingActions.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingActions.ts @@ -58,11 +58,13 @@ export abstract class EditingSessionAction extends Action2 { abstract runEditingSessionAction(accessor: ServicesAccessor, editingSession: IChatEditingSession, chatWidget: IChatWidget, ...args: unknown[]): any; } +export type EditingSessionActionContext = { editingSession?: IChatEditingSession; chatWidget: IChatWidget }; + /** * Resolve view title toolbar context. If none, return context from the lastFocusedWidget. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any -export function getEditingSessionContext(accessor: ServicesAccessor, args: any[]): { editingSession?: IChatEditingSession; chatWidget: IChatWidget } | undefined { +export function getEditingSessionContext(accessor: ServicesAccessor, args: any[]): EditingSessionActionContext | undefined { const arg0 = args.at(0); const context = isChatViewTitleActionContext(arg0) ? arg0 : undefined;