From 9243a001aa6f69a090f12ea85f04deea0edcf46d Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 21 Nov 2025 12:52:26 -0800 Subject: [PATCH] chat: cleanup most flagrant usages of IChatWidgetService.lastFocusedWidget (#278861) Closes https://github.com/microsoft/vscode/issues/278638 mostly --- .../actions/chatAccessibilityActions.ts | 8 ++++---- .../chat/browser/actions/chatCopyActions.ts | 10 +++++----- .../chat/browser/actions/chatNewActions.ts | 7 ++++--- .../attachments/implicitContextAttachment.ts | 6 +++--- .../contrib/chat/browser/chat.contribution.ts | 6 +++--- src/vs/workbench/contrib/chat/browser/chat.ts | 4 ++++ .../contrib/chat/browser/chatDragAndDrop.ts | 15 ++++++++------- .../browser/chatEditing/chatEditingActions.ts | 12 ++++++------ .../contrib/chat/browser/chatInputPart.ts | 6 +++--- .../contrib/chat/browser/chatListRenderer.ts | 2 +- .../browser/chatMarkdownDecorationsRenderer.ts | 18 ++++++++++-------- .../browser/contrib/chatInputCompletions.ts | 2 +- .../contrib/chat/common/chatServiceImpl.ts | 2 +- .../contrib/chat/common/chatSlashCommands.ts | 9 +++++---- .../chat/notebook.chat.contribution.ts | 13 +++---------- 15 files changed, 61 insertions(+), 59 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityActions.ts index e57dee9ddcc2..e5c4173750b7 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityActions.ts @@ -34,14 +34,14 @@ class AnnounceChatConfirmationAction extends Action2 { async run(accessor: ServicesAccessor): Promise { const chatWidgetService = accessor.get(IChatWidgetService); - const lastFocusedWidget = chatWidgetService.lastFocusedWidget; + const pendingWidget = chatWidgetService.getAllWidgets().find(widget => widget.viewModel?.model.requestNeedsInput.get()); - if (!lastFocusedWidget) { + if (!pendingWidget) { alert(localize('noChatSession', 'No active chat session found.')); return; } - const viewModel = lastFocusedWidget.viewModel; + const viewModel = pendingWidget.viewModel; if (!viewModel) { alert(localize('chatNotReady', 'Chat interface not ready.')); return; @@ -53,7 +53,7 @@ class AnnounceChatConfirmationAction extends Action2 { const lastResponse = viewModel.getItems()[viewModel.getItems().length - 1]; if (isResponseVM(lastResponse)) { // eslint-disable-next-line no-restricted-syntax - const confirmationWidgets = lastFocusedWidget.domNode.querySelectorAll('.chat-confirmation-widget-container'); + const confirmationWidgets = pendingWidget.domNode.querySelectorAll('.chat-confirmation-widget-container'); if (confirmationWidgets.length > 0) { firstConfirmationElement = confirmationWidgets[0] as HTMLElement; } diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatCopyActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatCopyActions.ts index 21d3388ad92d..6df226b57309 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatCopyActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatCopyActions.ts @@ -8,11 +8,11 @@ import { ServicesAccessor } from '../../../../../editor/browser/editorExtensions import { localize2 } from '../../../../../nls.js'; import { Action2, MenuId, registerAction2 } from '../../../../../platform/actions/common/actions.js'; import { IClipboardService } from '../../../../../platform/clipboard/common/clipboardService.js'; -import { CHAT_CATEGORY, stringifyItem } from './chatActions.js'; -import { ChatTreeItem, IChatWidgetService } from '../chat.js'; +import { katexContainerClassName, katexContainerLatexAttributeName } from '../../../markdown/common/markedKatexExtension.js'; import { ChatContextKeys } from '../../common/chatContextKeys.js'; import { IChatRequestViewModel, IChatResponseViewModel, isChatTreeItem, isRequestVM, isResponseVM } from '../../common/chatViewModel.js'; -import { katexContainerClassName, katexContainerLatexAttributeName } from '../../../markdown/common/markedKatexExtension.js'; +import { ChatTreeItem, IChatWidgetService } from '../chat.js'; +import { CHAT_CATEGORY, stringifyItem } from './chatActions.js'; export function registerChatCopyActions() { registerAction2(class CopyAllAction extends Action2 { @@ -30,10 +30,10 @@ export function registerChatCopyActions() { }); } - run(accessor: ServicesAccessor, ...args: unknown[]) { + run(accessor: ServicesAccessor, context?: ChatTreeItem) { const clipboardService = accessor.get(IClipboardService); const chatWidgetService = accessor.get(IChatWidgetService); - const widget = chatWidgetService.lastFocusedWidget; + const widget = (context?.sessionResource && chatWidgetService.getWidgetBySessionResource(context.sessionResource)) || chatWidgetService.lastFocusedWidget; if (widget) { const viewModel = widget.viewModel; const sessionAsText = viewModel?.getItems() diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatNewActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatNewActions.ts index 41999fffa969..77eb8da1ab53 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatNewActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatNewActions.ts @@ -16,6 +16,7 @@ import { KeybindingWeight } from '../../../../../platform/keybinding/common/keyb import { ActiveEditorContext } from '../../../../common/contextkeys.js'; import { ChatContextKeys } from '../../common/chatContextKeys.js'; import { IChatEditingSession } from '../../common/chatEditingService.js'; +import { IChatService } from '../../common/chatService.js'; import { ChatAgentLocation, ChatModeKind } from '../../common/constants.js'; import { ChatViewId, IChatWidgetService } from '../chat.js'; import { EditingSessionAction, getEditingSessionContext } from '../chatEditing/chatEditingActions.js'; @@ -204,9 +205,9 @@ export function registerNewChatActions() { } async runEditingSessionAction(accessor: ServicesAccessor, editingSession: IChatEditingSession) { - const widget = accessor.get(IChatWidgetService); + const chatService = accessor.get(IChatService); await editingSession.redoInteraction(); - widget.lastFocusedWidget?.viewModel?.model.setCheckpoint(undefined); + chatService.getSession(editingSession.chatSessionResource)?.setCheckpoint(undefined); } }); @@ -235,7 +236,7 @@ export function registerNewChatActions() { await editingSession.redoInteraction(); } - const currentWidget = widget.lastFocusedWidget; + const currentWidget = widget.getWidgetBySessionResource(editingSession.chatSessionResource); const requestText = currentWidget?.viewModel?.model.checkpoint?.message.text; // if the input has the same text that we just restored, clear it. diff --git a/src/vs/workbench/contrib/chat/browser/attachments/implicitContextAttachment.ts b/src/vs/workbench/contrib/chat/browser/attachments/implicitContextAttachment.ts index dd8ffee4d1ad..58c42bbaec6b 100644 --- a/src/vs/workbench/contrib/chat/browser/attachments/implicitContextAttachment.ts +++ b/src/vs/workbench/contrib/chat/browser/attachments/implicitContextAttachment.ts @@ -29,7 +29,7 @@ import { ILabelService } from '../../../../../platform/label/common/label.js'; import { IResourceLabel, ResourceLabels } from '../../../../browser/labels.js'; import { ResourceContextKey } from '../../../../common/contextkeys.js'; import { IChatRequestImplicitVariableEntry, IChatRequestStringVariableEntry, isStringImplicitContextValue } from '../../common/chatVariableEntries.js'; -import { IChatWidgetService } from '../chat.js'; +import { IChatWidget } from '../chat.js'; import { ChatAttachmentModel } from '../chatAttachmentModel.js'; import { IChatContextService } from '../chatContextService.js'; @@ -39,6 +39,7 @@ export class ImplicitContextAttachmentWidget extends Disposable { private readonly renderDisposables = this._register(new DisposableStore()); constructor( + private readonly widgetRef: () => IChatWidget | undefined, private readonly attachment: IChatRequestImplicitVariableEntry, private readonly resourceLabels: ResourceLabels, private readonly attachmentModel: ChatAttachmentModel, @@ -50,7 +51,6 @@ export class ImplicitContextAttachmentWidget extends Disposable { @ILanguageService private readonly languageService: ILanguageService, @IModelService private readonly modelService: IModelService, @IHoverService private readonly hoverService: IHoverService, - @IChatWidgetService private readonly chatWidgetService: IChatWidgetService, @IConfigurationService private readonly configService: IConfigurationService, @IChatContextService private readonly chatContextService: IChatContextService, ) { @@ -205,6 +205,6 @@ export class ImplicitContextAttachmentWidget extends Disposable { const file = URI.isUri(this.attachment.value) ? this.attachment.value : this.attachment.value.uri; this.attachmentModel.addFile(file); } - this.chatWidgetService.lastFocusedWidget?.focusInput(); + this.widgetRef()?.focusInput(); } } diff --git a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts index 589f2bbcd9c4..aa4493f46a10 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts @@ -1065,7 +1065,7 @@ class ChatSlashStaticSlashCommandsContribution extends Disposable { executeImmediately: true, locations: [ChatAgentLocation.Chat], modes: [ChatModeKind.Ask] - }, async (prompt, progress) => { + }, async (prompt, progress, _history, _location, sessionResource) => { const defaultAgent = chatAgentService.getDefaultAgent(ChatAgentLocation.Chat); const agents = chatAgentService.getAgents(); @@ -1085,11 +1085,11 @@ class ChatSlashStaticSlashCommandsContribution extends Disposable { .filter(a => a.locations.includes(ChatAgentLocation.Chat)) .map(async a => { const description = a.description ? `- ${a.description}` : ''; - const agentMarkdown = instantiationService.invokeFunction(accessor => agentToMarkdown(a, true, accessor)); + const agentMarkdown = instantiationService.invokeFunction(accessor => agentToMarkdown(a, sessionResource, true, accessor)); const agentLine = `- ${agentMarkdown} ${description}`; const commandText = a.slashCommands.map(c => { const description = c.description ? `- ${c.description}` : ''; - return `\t* ${agentSlashCommandToMarkdown(a, c)} ${description}`; + return `\t* ${agentSlashCommandToMarkdown(a, c, sessionResource)} ${description}`; }).join('\n'); return (agentLine + '\n' + commandText).trim(); diff --git a/src/vs/workbench/contrib/chat/browser/chat.ts b/src/vs/workbench/contrib/chat/browser/chat.ts index d1936d9a2325..c9f806fa7395 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.ts @@ -36,6 +36,10 @@ export interface IChatWidgetService { /** * Returns the most recently focused widget if any. + * + * ⚠️ Consider carefully if this is appropriate for your use case. If you + * can know what session you're interacting with, prefer {@link getWidgetBySessionResource} + * or similar methods to work nicely with multiple chat widgets. */ readonly lastFocusedWidget: IChatWidget | undefined; diff --git a/src/vs/workbench/contrib/chat/browser/chatDragAndDrop.ts b/src/vs/workbench/contrib/chat/browser/chatDragAndDrop.ts index dc847f8e8ab3..986f7f8e0f5f 100644 --- a/src/vs/workbench/contrib/chat/browser/chatDragAndDrop.ts +++ b/src/vs/workbench/contrib/chat/browser/chatDragAndDrop.ts @@ -19,13 +19,13 @@ import { ILogService } from '../../../../platform/log/common/log.js'; import { IThemeService, Themable } from '../../../../platform/theme/common/themeService.js'; import { ISharedWebContentExtractorService } from '../../../../platform/webContentExtractor/common/webContentExtractor.js'; import { IExtensionService, isProposedApiEnabled } from '../../../services/extensions/common/extensions.js'; +import { extractSCMHistoryItemDropData } from '../../scm/browser/scmHistoryChatContext.js'; import { IChatRequestVariableEntry } from '../common/chatVariableEntries.js'; -import { IChatWidgetService } from './chat.js'; -import { IChatAttachmentResolveService, ImageTransferData } from './chatAttachmentResolveService.js'; +import { IChatWidget } from './chat.js'; import { ChatAttachmentModel } from './chatAttachmentModel.js'; +import { IChatAttachmentResolveService, ImageTransferData } from './chatAttachmentResolveService.js'; import { IChatInputStyles } from './chatInputPart.js'; import { convertStringToUInt8Array } from './imageUtils.js'; -import { extractSCMHistoryItemDropData } from '../../scm/browser/scmHistoryChatContext.js'; enum ChatDragAndDropType { FILE_INTERNAL, @@ -50,12 +50,12 @@ export class ChatDragAndDrop extends Themable { private disableOverlay: boolean = false; constructor( + private readonly widgetRef: () => IChatWidget | undefined, private readonly attachmentModel: ChatAttachmentModel, private readonly styles: IChatInputStyles, @IThemeService themeService: IThemeService, @IExtensionService private readonly extensionService: IExtensionService, @ISharedWebContentExtractorService private readonly webContentExtractorService: ISharedWebContentExtractorService, - @IChatWidgetService private readonly chatWidgetService: IChatWidgetService, @ILogService private readonly logService: ILogService, @IChatAttachmentResolveService private readonly chatAttachmentResolveService: IChatAttachmentResolveService ) { @@ -299,9 +299,10 @@ export class ChatDragAndDrop extends Themable { } // TODO: use dnd provider to insert text @justschen - const selection = this.chatWidgetService.lastFocusedWidget?.inputEditor.getSelection(); - if (selection && this.chatWidgetService.lastFocusedWidget) { - this.chatWidgetService.lastFocusedWidget.inputEditor.executeEdits('chatInsertUrl', [{ range: selection, text: url }]); + const widget = this.widgetRef(); + const selection = widget?.inputEditor.getSelection(); + if (selection && widget) { + widget.inputEditor.executeEdits('chatInsertUrl', [{ range: selection, text: url }]); } this.logService.warn(`Image URLs must end in .jpg, .png, .gif, .webp, or .bmp. Failed to fetch image from this URL: ${url}`); diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingActions.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingActions.ts index 015bec3ada53..4fbced0bb698 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingActions.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingActions.ts @@ -30,7 +30,7 @@ import { isChatViewTitleActionContext } from '../../common/chatActions.js'; import { ChatContextKeys } from '../../common/chatContextKeys.js'; import { applyingChatEditsFailedContextKey, CHAT_EDITING_MULTI_DIFF_SOURCE_RESOLVER_SCHEME, chatEditingResourceContextKey, chatEditingWidgetFileStateContextKey, decidedChatEditingResourceContextKey, hasAppliedChatEditsContextKey, hasUndecidedChatEditingResourceContextKey, IChatEditingService, IChatEditingSession, ModifiedFileEntryState } from '../../common/chatEditingService.js'; import { IChatService } from '../../common/chatService.js'; -import { isRequestVM, isResponseVM } from '../../common/chatViewModel.js'; +import { isChatTreeItem, isRequestVM, isResponseVM } from '../../common/chatViewModel.js'; import { ChatAgentLocation, ChatConfiguration, ChatModeKind } from '../../common/constants.js'; import { CHAT_CATEGORY } from '../actions/chatActions.js'; import { ChatTreeItem, IChatWidget, IChatWidgetService } from '../chat.js'; @@ -305,7 +305,7 @@ async function restoreSnapshotWithConfirmation(accessor: ServicesAccessor, item: const configurationService = accessor.get(IConfigurationService); const dialogService = accessor.get(IDialogService); const chatWidgetService = accessor.get(IChatWidgetService); - const widget = chatWidgetService.lastFocusedWidget; + const widget = chatWidgetService.getWidgetBySessionResource(item.sessionResource); const chatService = accessor.get(IChatService); const chatModel = chatService.getSession(item.sessionResource); if (!chatModel) { @@ -403,7 +403,7 @@ registerAction2(class RemoveAction extends Action2 { let item = args[0] as ChatTreeItem | undefined; const chatWidgetService = accessor.get(IChatWidgetService); const configurationService = accessor.get(IConfigurationService); - const widget = chatWidgetService.lastFocusedWidget; + const widget = (isChatTreeItem(item) && chatWidgetService.getWidgetBySessionResource(item.sessionResource)) || chatWidgetService.lastFocusedWidget; if (!isResponseVM(item) && !isRequestVM(item)) { item = widget?.getFocus(); } @@ -451,7 +451,7 @@ registerAction2(class RestoreCheckpointAction extends Action2 { async run(accessor: ServicesAccessor, ...args: unknown[]) { let item = args[0] as ChatTreeItem | undefined; const chatWidgetService = accessor.get(IChatWidgetService); - const widget = chatWidgetService.lastFocusedWidget; + const widget = (isChatTreeItem(item) && chatWidgetService.getWidgetBySessionResource(item.sessionResource)) || chatWidgetService.lastFocusedWidget; if (!isResponseVM(item) && !isRequestVM(item)) { item = widget?.getFocus(); } @@ -493,7 +493,7 @@ registerAction2(class RestoreLastCheckpoint extends Action2 { let item = args[0] as ChatTreeItem | undefined; const chatWidgetService = accessor.get(IChatWidgetService); const chatService = accessor.get(IChatService); - const widget = chatWidgetService.lastFocusedWidget; + const widget = (isChatTreeItem(item) && chatWidgetService.getWidgetBySessionResource(item.sessionResource)) || chatWidgetService.lastFocusedWidget; if (!isResponseVM(item) && !isRequestVM(item)) { item = widget?.getFocus(); } @@ -552,7 +552,7 @@ registerAction2(class EditAction extends Action2 { async run(accessor: ServicesAccessor, ...args: unknown[]) { let item = args[0] as ChatTreeItem | undefined; const chatWidgetService = accessor.get(IChatWidgetService); - const widget = chatWidgetService.lastFocusedWidget; + const widget = (isChatTreeItem(item) && chatWidgetService.getWidgetBySessionResource(item.sessionResource)) || chatWidgetService.lastFocusedWidget; if (!isResponseVM(item) && !isRequestVM(item)) { item = widget?.getFocus(); } diff --git a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts index 417b8b083531..787e4659fa02 100644 --- a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts @@ -434,7 +434,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge this._attachmentModel = this._register(this.instantiationService.createInstance(ChatAttachmentModel)); this._register(this._attachmentModel.onDidChange(() => this._syncInputStateToModel())); this.selectedToolsModel = this._register(this.instantiationService.createInstance(ChatSelectedTools, this.currentModeObs)); - this.dnd = this._register(this.instantiationService.createInstance(ChatDragAndDrop, this._attachmentModel, styles)); + this.dnd = this._register(this.instantiationService.createInstance(ChatDragAndDrop, () => this._widget, this._attachmentModel, styles)); this.inputEditorMaxHeight = this.options.renderStyle === 'compact' ? INPUT_EDITOR_MAX_HEIGHT / 3 : INPUT_EDITOR_MAX_HEIGHT; @@ -1675,7 +1675,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge const isSuggestedEnabled = this.configurationService.getValue('chat.implicitContext.suggestedContext'); if (this.implicitContext?.value && !isSuggestedEnabled) { - const implicitPart = store.add(this.instantiationService.createInstance(ImplicitContextAttachmentWidget, this.implicitContext, this._contextResourceLabels, this.attachmentModel)); + const implicitPart = store.add(this.instantiationService.createInstance(ImplicitContextAttachmentWidget, () => this._widget, this.implicitContext, this._contextResourceLabels, this.attachmentModel)); container.appendChild(implicitPart.domNode); } @@ -1749,7 +1749,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge const shouldShowImplicit = !isLocation(implicitValue) ? !currentlyAttached : implicitValue.range; if (shouldShowImplicit) { - const implicitPart = store.add(this.instantiationService.createInstance(ImplicitContextAttachmentWidget, this.implicitContext, this._contextResourceLabels, this._attachmentModel)); + const implicitPart = store.add(this.instantiationService.createInstance(ImplicitContextAttachmentWidget, () => this._widget, this.implicitContext, this._contextResourceLabels, this._attachmentModel)); container.appendChild(implicitPart.domNode); } } diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 4131f6804a17..1285c05ab880 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -840,7 +840,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer agentToMarkdown(part.agent, false, accessor)); + result += this.instantiationService.invokeFunction(accessor => agentToMarkdown(part.agent, sessionResource, false, accessor)); } else { result += this.genericDecorationToMarkdown(part); } @@ -180,7 +182,7 @@ export class ChatMarkdownDecorationsRenderer { button.label = nameWithLeader; store.add(button.onDidClick(() => { const agent = this.chatAgentService.getAgent(args.agentId); - const widget = this.chatWidgetService.lastFocusedWidget; + const widget = this.chatWidgetService.getWidgetBySessionResource(args.sessionResource) || this.chatWidgetService.lastFocusedWidget; if (!widget || !agent) { return; } @@ -216,7 +218,7 @@ export class ChatMarkdownDecorationsRenderer { })); button.label = name; store.add(button.onDidClick(() => { - const widget = this.chatWidgetService.lastFocusedWidget; + const widget = this.chatWidgetService.getWidgetBySessionResource(args.sessionResource) || this.chatWidgetService.lastFocusedWidget; if (!widget || !agent) { return; } diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts index e2b4ec8b73e0..ba6a0b1cbe41 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts @@ -605,7 +605,7 @@ class StartParameterizedPromptAction extends Action2 { const widgetService = accessor.get(IChatWidgetService); const fileService = accessor.get(IFileService); - const chatWidget = widgetService.lastFocusedWidget; + const chatWidget = await widgetService.revealWidget(true); if (!chatWidget) { return; } diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index 686d2417a6bd..52e6a7030cfa 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -1135,7 +1135,7 @@ export class ChatService extends Disposable implements IChatService { const message = parsedRequest.text; const commandResult = await this.chatSlashCommandService.executeCommand(commandPart.slashCommand.command, message.substring(commandPart.slashCommand.command.length + 1).trimStart(), new Progress(p => { progressCallback([p]); - }), history, location, token); + }), history, location, model.sessionResource, token); agentOrCommandFollowups = Promise.resolve(commandResult?.followUp); rawResult = {}; diff --git a/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts b/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts index 050873243d9d..6f10eac66fa9 100644 --- a/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts +++ b/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts @@ -12,6 +12,7 @@ import { IChatMessage } from './languageModels.js'; import { IChatFollowup, IChatProgress, IChatResponseProgressFileTreeData } from './chatService.js'; import { IExtensionService } from '../../../services/extensions/common/extensions.js'; import { ChatAgentLocation, ChatModeKind } from './constants.js'; +import { URI } from '../../../../base/common/uri.js'; //#region slash service, commands etc @@ -41,7 +42,7 @@ export interface IChatSlashData { export interface IChatSlashFragment { content: string | { treeData: IChatResponseProgressFileTreeData }; } -export type IChatSlashCallback = { (prompt: string, progress: IProgress, history: IChatMessage[], location: ChatAgentLocation, token: CancellationToken): Promise<{ followUp: IChatFollowup[] } | void> }; +export type IChatSlashCallback = { (prompt: string, progress: IProgress, history: IChatMessage[], location: ChatAgentLocation, sessionResource: URI, token: CancellationToken): Promise<{ followUp: IChatFollowup[] } | void> }; export const IChatSlashCommandService = createDecorator('chatSlashCommandService'); @@ -52,7 +53,7 @@ export interface IChatSlashCommandService { _serviceBrand: undefined; readonly onDidChangeCommands: Event; registerSlashCommand(data: IChatSlashData, command: IChatSlashCallback): IDisposable; - executeCommand(id: string, prompt: string, progress: IProgress, history: IChatMessage[], location: ChatAgentLocation, token: CancellationToken): Promise<{ followUp: IChatFollowup[] } | void>; + executeCommand(id: string, prompt: string, progress: IProgress, history: IChatMessage[], location: ChatAgentLocation, sessionResource: URI, token: CancellationToken): Promise<{ followUp: IChatFollowup[] } | void>; getCommands(location: ChatAgentLocation, mode: ChatModeKind): Array; hasCommand(id: string): boolean; } @@ -102,7 +103,7 @@ export class ChatSlashCommandService extends Disposable implements IChatSlashCom return this._commands.has(id); } - async executeCommand(id: string, prompt: string, progress: IProgress, history: IChatMessage[], location: ChatAgentLocation, token: CancellationToken): Promise<{ followUp: IChatFollowup[] } | void> { + async executeCommand(id: string, prompt: string, progress: IProgress, history: IChatMessage[], location: ChatAgentLocation, sessionResource: URI, token: CancellationToken): Promise<{ followUp: IChatFollowup[] } | void> { const data = this._commands.get(id); if (!data) { throw new Error('No command with id ${id} NOT registered'); @@ -114,6 +115,6 @@ export class ChatSlashCommandService extends Disposable implements IChatSlashCom throw new Error(`No command with id ${id} NOT resolved`); } - return await data.command(prompt, progress, history, location, token); + return await data.command(prompt, progress, history, location, sessionResource, token); } } diff --git a/src/vs/workbench/contrib/notebook/browser/controller/chat/notebook.chat.contribution.ts b/src/vs/workbench/contrib/notebook/browser/controller/chat/notebook.chat.contribution.ts index d3089f250320..771bffc7bb57 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/chat/notebook.chat.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/chat/notebook.chat.contribution.ts @@ -310,7 +310,7 @@ class KernelVariableContextPicker implements IChatContextPickerItem { } -registerAction2(class CopyCellOutputAction extends Action2 { +registerAction2(class AddCellOutputToChatAction extends Action2 { constructor() { super({ id: 'notebook.cellOutput.addToChat', @@ -371,15 +371,8 @@ registerAction2(class CopyCellOutputAction extends Action2 { const mimeType = outputViewModel.pickedMimeType?.mimeType; const chatWidgetService = accessor.get(IChatWidgetService); - let widget = chatWidgetService.lastFocusedWidget; - if (!widget) { - const widgets = chatWidgetService.getWidgetsByLocations(ChatAgentLocation.Chat); - if (widgets.length === 0) { - return; - } - widget = widgets[0]; - } - if (mimeType && NOTEBOOK_CELL_OUTPUT_MIME_TYPE_LIST_FOR_CHAT_CONST.includes(mimeType)) { + const widget = await chatWidgetService.revealWidget(); + if (widget && mimeType && NOTEBOOK_CELL_OUTPUT_MIME_TYPE_LIST_FOR_CHAT_CONST.includes(mimeType)) { const entry = createNotebookOutputVariableEntry(outputViewModel, mimeType, notebookEditor); if (!entry) {