Path: blob/main/src/vs/workbench/contrib/chat/browser/actions/chatMoveActions.ts
5310 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { Codicon } from '../../../../../base/common/codicons.js';6import { ThemeIcon } from '../../../../../base/common/themables.js';7import { URI } from '../../../../../base/common/uri.js';8import { localize, localize2 } from '../../../../../nls.js';9import { Action2, MenuId, MenuRegistry, registerAction2 } from '../../../../../platform/actions/common/actions.js';10import { ContextKeyExpr, ContextKeyExpression } from '../../../../../platform/contextkey/common/contextkey.js';11import { ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js';12import { ActiveEditorContext } from '../../../../common/contextkeys.js';13import { ViewContainerLocation } from '../../../../common/views.js';14import { IEditorGroupsService } from '../../../../services/editor/common/editorGroupsService.js';15import { ACTIVE_GROUP, AUX_WINDOW_GROUP, IEditorService } from '../../../../services/editor/common/editorService.js';16import { IViewsService } from '../../../../services/views/common/viewsService.js';17import { isChatViewTitleActionContext } from '../../common/actions/chatActions.js';18import { ChatContextKeys } from '../../common/actions/chatContextKeys.js';19import { ChatAgentLocation } from '../../common/constants.js';20import { ChatViewId, IChatWidgetService } from '../chat.js';21import { ChatEditor, IChatEditorOptions } from '../widgetHosts/editor/chatEditor.js';22import { ChatEditorInput } from '../widgetHosts/editor/chatEditorInput.js';23import { ChatViewPane } from '../widgetHosts/viewPane/chatViewPane.js';24import { CHAT_CATEGORY } from './chatActions.js';2526enum MoveToNewLocation {27Editor = 'Editor',28Window = 'Window'29}3031export function registerMoveActions() {32registerAction2(class GlobalMoveToEditorAction extends Action2 {33constructor() {34super({35id: 'workbench.action.chat.openInEditor',36title: localize2('chat.openInEditor.label', "Move Chat into Editor Area"),37category: CHAT_CATEGORY,38precondition: ChatContextKeys.enabled,39f1: true,40menu: {41id: MenuId.ViewTitle,42when: ContextKeyExpr.equals('view', ChatViewId),43order: 0,44group: '1_open'45},46});47}4849async run(accessor: ServicesAccessor, ...args: unknown[]) {50const context = args[0];51executeMoveToAction(accessor, MoveToNewLocation.Editor, isChatViewTitleActionContext(context) ? context.sessionResource : undefined);52}53});5455registerAction2(class GlobalMoveToNewWindowAction extends Action2 {56constructor() {57super({58id: 'workbench.action.chat.openInNewWindow',59title: localize2('chat.openInNewWindow.label', "Move Chat into New Window"),60category: CHAT_CATEGORY,61precondition: ChatContextKeys.enabled,62f1: true,63menu: {64id: MenuId.ViewTitle,65when: ContextKeyExpr.equals('view', ChatViewId),66order: 0,67group: '1_open'68},69});70}7172async run(accessor: ServicesAccessor, ...args: unknown[]) {73const context = args[0];74executeMoveToAction(accessor, MoveToNewLocation.Window, isChatViewTitleActionContext(context) ? context.sessionResource : undefined);75}76});7778registerAction2(class GlobalMoveToSidebarAction extends Action2 {79constructor() {80super({81id: 'workbench.action.chat.openInSidebar',82title: localize2('interactiveSession.openInSidebar.label', "Move Chat into Side Bar"),83category: CHAT_CATEGORY,84precondition: ChatContextKeys.enabled,85f1: true86});87}8889async run(accessor: ServicesAccessor, ...args: unknown[]) {90return moveToSidebar(accessor);91}92});9394function appendOpenChatInViewMenuItem(menuId: MenuId, title: string, icon: ThemeIcon, locationContextKey: ContextKeyExpression) {95MenuRegistry.appendMenuItem(menuId, {96command: { id: 'workbench.action.chat.openInSidebar', title, icon },97when: ContextKeyExpr.and(98ActiveEditorContext.isEqualTo(ChatEditorInput.EditorID),99locationContextKey100),101group: menuId === MenuId.CompactWindowEditorTitle ? 'navigation' : undefined,102order: 0103});104}105106[MenuId.EditorTitle, MenuId.CompactWindowEditorTitle].forEach(id => {107appendOpenChatInViewMenuItem(id, localize('interactiveSession.openInSecondarySidebar.label', "Move Chat into Secondary Side Bar"), Codicon.layoutSidebarRightDock, ChatContextKeys.panelLocation.isEqualTo(ViewContainerLocation.AuxiliaryBar));108appendOpenChatInViewMenuItem(id, localize('interactiveSession.openInPrimarySidebar.label', "Move Chat into Primary Side Bar"), Codicon.layoutSidebarLeftDock, ChatContextKeys.panelLocation.isEqualTo(ViewContainerLocation.Sidebar));109appendOpenChatInViewMenuItem(id, localize('interactiveSession.openInPanel.label', "Move Chat into Panel"), Codicon.layoutPanelDock, ChatContextKeys.panelLocation.isEqualTo(ViewContainerLocation.Panel));110});111}112113async function executeMoveToAction(accessor: ServicesAccessor, moveTo: MoveToNewLocation, sessionResource?: URI) {114const widgetService = accessor.get(IChatWidgetService);115116const auxiliary = { compact: true, bounds: { width: 800, height: 640 } };117118const widget = (sessionResource ? widgetService.getWidgetBySessionResource(sessionResource) : undefined)119?? widgetService.lastFocusedWidget;120if (!widget || !widget.viewModel || widget.location !== ChatAgentLocation.Chat) {121await widgetService.openSession(ChatEditorInput.getNewEditorUri(), moveTo === MoveToNewLocation.Window ? AUX_WINDOW_GROUP : ACTIVE_GROUP, { pinned: true, auxiliary });122return;123}124125const existingWidget = widgetService.getWidgetBySessionResource(widget.viewModel.sessionResource);126if (!existingWidget) {127// Do NOT attempt to open a session that isn't already open since we cannot guarantee its state.128await widgetService.openSession(ChatEditorInput.getNewEditorUri(), moveTo === MoveToNewLocation.Window ? AUX_WINDOW_GROUP : ACTIVE_GROUP, { pinned: true, auxiliary });129return;130}131132// Save off the session resource before clearing133const resourceToOpen = widget.viewModel.sessionResource;134135// Todo: can possibly go away with https://github.com/microsoft/vscode/pull/278476136const modelInputState = existingWidget.getViewState();137138await widget.clear();139140const options: IChatEditorOptions = { pinned: true, modelInputState, auxiliary };141await widgetService.openSession(resourceToOpen, moveTo === MoveToNewLocation.Window ? AUX_WINDOW_GROUP : ACTIVE_GROUP, options);142}143144async function moveToSidebar(accessor: ServicesAccessor): Promise<void> {145const viewsService = accessor.get(IViewsService);146const editorService = accessor.get(IEditorService);147const editorGroupService = accessor.get(IEditorGroupsService);148149const chatEditor = editorService.activeEditorPane;150const chatEditorInput = chatEditor?.input;151let view: ChatViewPane;152if (chatEditor instanceof ChatEditor && chatEditorInput instanceof ChatEditorInput && chatEditorInput.sessionResource) {153const previousViewState = chatEditor.widget.getViewState();154await editorService.closeEditor({ editor: chatEditor.input, groupId: editorGroupService.activeGroup.id });155view = await viewsService.openView(ChatViewId) as ChatViewPane;156157// Todo: can possibly go away with https://github.com/microsoft/vscode/pull/278476158const newModel = await view.loadSession(chatEditorInput.sessionResource);159if (previousViewState && newModel && !newModel.inputModel.state.get()) {160newModel.inputModel.setState(previousViewState);161}162} else {163view = await viewsService.openView(ChatViewId) as ChatViewPane;164}165166view.focus();167}168169170