diff --git a/src/vs/workbench/api/common/extHostChatSessions.ts b/src/vs/workbench/api/common/extHostChatSessions.ts index 35b43eceef17e..b56178412a2ec 100644 --- a/src/vs/workbench/api/common/extHostChatSessions.ts +++ b/src/vs/workbench/api/common/extHostChatSessions.ts @@ -75,7 +75,7 @@ export class ExtHostChatSessions extends Disposable implements ExtHostChatSessio commands.registerArgumentProcessor({ processArgument: (arg) => { if (arg && arg.$mid === MarshalledId.ChatSessionContext) { - const id = arg.id; + const id = arg.session.id; const sessionContent = this._sessionMap.get(id); if (sessionContent) { return sessionContent; diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts index b3530ff79ab00..4bd9c19cd3429 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts @@ -580,7 +580,7 @@ export function registerChatActions() { const ckey = contextKeyService.createKey('chatSessionType', session.providerType); const actions = menuService.getMenuActions(MenuId.ChatSessionsMenu, contextKeyService); - const menuActions = getContextMenuActions(actions, 'navigation'); + const menuActions = getContextMenuActions(actions, 'inline'); ckey.reset(); // Use primary actions if available, otherwise fall back to secondary actions @@ -756,7 +756,7 @@ export function registerChatActions() { const contextItem = context.item as ICodingAgentPickerItem; commandService.executeCommand(buttonItem.id, { uri: contextItem.uri, - session: contextItem.session, + session: contextItem.session?.session, $mid: MarshalledId.ChatSessionContext }); diff --git a/src/vs/workbench/contrib/chat/browser/chatSessions.ts b/src/vs/workbench/contrib/chat/browser/chatSessions.ts index aad0a7fa7733a..4fe7799caa73e 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSessions.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSessions.ts @@ -20,20 +20,24 @@ import { IContextKeyService, ContextKeyExpr } from '../../../../platform/context import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; import { IOpenerService } from '../../../../platform/opener/common/opener.js'; import { IHoverService } from '../../../../platform/hover/browser/hover.js'; +import { IMenuService, MenuId, MenuRegistry } from '../../../../platform/actions/common/actions.js'; +import { getActionBarActions } from '../../../../platform/actions/browser/menuEntryActionViewItem.js'; +import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; import { ViewPaneContainer } from '../../../browser/parts/views/viewPaneContainer.js'; +import { MarshalledId } from '../../../../base/common/marshallingIds.js'; import { ViewPane, IViewPaneOptions } from '../../../browser/parts/views/viewPane.js'; import { Extensions, IViewContainersRegistry, IViewDescriptorService, ViewContainerLocation, IViewsRegistry, IViewDescriptor } from '../../../common/views.js'; import { IExtensionService } from '../../../services/extensions/common/extensions.js'; import { IWorkbenchLayoutService } from '../../../services/layout/browser/layoutService.js'; import { WorkbenchAsyncDataTree } from '../../../../platform/list/browser/listService.js'; import { IChatSessionItem, IChatSessionItemProvider, IChatSessionsExtensionPoint, IChatSessionsService } from '../common/chatSessionsService.js'; +import { ChatContextKeys } from '../common/chatContextKeys.js'; import { IAsyncDataSource, ITreeRenderer, ITreeNode } from '../../../../base/browser/ui/tree/tree.js'; import { IListVirtualDelegate } from '../../../../base/browser/ui/list/list.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { FuzzyScore } from '../../../../base/common/filters.js'; import { ResourceLabels, IResourceLabel } from '../../../browser/labels.js'; import { ActionBar } from '../../../../base/browser/ui/actionbar/actionbar.js'; -import { Disposable } from '../../../../base/common/lifecycle.js'; import { append, $, getActiveWindow, clearNode } from '../../../../base/browser/dom.js'; import { URI } from '../../../../base/common/uri.js'; import { IEditorGroupsService, IEditorGroup } from '../../../services/editor/common/editorGroupsService.js'; @@ -45,7 +49,6 @@ import { EditorInput } from '../../../common/editor/editorInput.js'; import { ChatEditorInput } from './chatEditorInput.js'; import { IChatWidgetService, IChatWidget } from './chat.js'; import { ChatAgentLocation, ChatConfiguration } from '../common/constants.js'; -import { MenuId, MenuRegistry } from '../../../../platform/actions/common/actions.js'; import { registerIcon } from '../../../../platform/theme/common/iconRegistry.js'; import { IWorkbenchContribution } from '../../../common/contributions.js'; import { IViewsService } from '../../../services/views/common/viewsService.js'; @@ -56,6 +59,16 @@ import { coalesce } from '../../../../base/common/arrays.js'; export const VIEWLET_ID = 'workbench.view.chat.sessions'; +// Helper function to create context overlay for session items +function getSessionItemContextOverlay(session: IChatSessionItem, provider?: IChatSessionItemProvider): [string, any][] { + const overlay: [string, any][] = []; + if (provider) { + overlay.push([ChatContextKeys.sessionType.key, provider.chatSessionType]); + } + + return overlay; +} + // Extended interface for local chat session items that includes editor information or widget information interface ILocalChatSessionItem extends IChatSessionItem { editor?: EditorInput; @@ -546,6 +559,7 @@ interface ISessionTemplateData { container: HTMLElement; resourceLabel: IResourceLabel; actionBar: ActionBar; + elementDisposable: DisposableStore; } // Renderer for session items in the tree @@ -557,6 +571,8 @@ class SessionsRenderer extends Disposable implements ITreeRenderer, index: number, templateData: ISessionTemplateData): void { const session = element.element; + const sessionWithProvider = session as IChatSessionItem & { provider: IChatSessionItemProvider }; + + // Clear previous element disposables + templateData.elementDisposable.clear(); // Handle different icon types let iconResource: URI | undefined; @@ -661,9 +684,51 @@ class SessionsRenderer extends Disposable implements ITreeRenderer { + templateData.actionBar.clear(); + + // Create marshalled context for command execution + const marshalledSession = { + session: session, + $mid: MarshalledId.ChatSessionContext + }; + + const actions = menu.getActions({ arg: marshalledSession, shouldForwardArgs: true }); + + const { primary } = getActionBarActions( + actions, + 'inline', + ); + + templateData.actionBar.push(primary, { icon: true, label: false }); + + // Set context for the action bar + templateData.actionBar.context = session; + }; + + // Setup initial action bar and listen for menu changes + templateData.elementDisposable.add(menu.onDidChange(() => setupActionBar())); + setupActionBar(); + } + + disposeElement(_element: ITreeNode, _index: number, templateData: ISessionTemplateData): void { + templateData.elementDisposable.clear(); } disposeTemplate(templateData: ISessionTemplateData): void { + templateData.elementDisposable.dispose(); templateData.resourceLabel.dispose(); templateData.actionBar.dispose(); } @@ -867,7 +932,7 @@ class SessionsViewPane extends ViewPane { this.dataSource = new SessionsDataSource(this.provider); const delegate = new SessionsDelegate(); - const renderer = new SessionsRenderer(this.labels, this.themeService, this.logService); + const renderer = this.instantiationService.createInstance(SessionsRenderer, this.labels); this._register(renderer); this.tree = this.instantiationService.createInstance( diff --git a/src/vs/workbench/contrib/chat/browser/media/chatSessions.css b/src/vs/workbench/contrib/chat/browser/media/chatSessions.css index 358d8d158fcbb..8dc856511fb4d 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chatSessions.css +++ b/src/vs/workbench/contrib/chat/browser/media/chatSessions.css @@ -40,3 +40,15 @@ .chat-sessions-message .no-sessions-message .codicon { opacity: 0.7; } + +/* Hide action bar actions by default */ +.chat-sessions-tree .chat-session-item .actions .action-label { + display: none; +} + +/* Show action bar actions on hover and focus */ +.chat-sessions-tree .chat-session-item:hover .actions .action-label, +.chat-sessions-tree .monaco-list-row.focused .chat-session-item .actions .action-label, +.chat-sessions-tree .monaco-list-row.selected .chat-session-item .actions .action-label { + display: block; +} diff --git a/src/vs/workbench/contrib/chat/common/chatContextKeys.ts b/src/vs/workbench/contrib/chat/common/chatContextKeys.ts index e0f5a4e53dc3d..7751b56a70bec 100644 --- a/src/vs/workbench/contrib/chat/common/chatContextKeys.ts +++ b/src/vs/workbench/contrib/chat/common/chatContextKeys.ts @@ -99,6 +99,8 @@ export namespace ChatContextKeys { }; export const panelLocation = new RawContextKey('chatPanelLocation', undefined, { type: 'number', description: localize('chatPanelLocation', "The location of the chat panel.") }); + + export const sessionType = new RawContextKey('chatSessionType', '', { type: 'string', description: localize('chatSessionType', "The type of the current chat session item.") }); } export namespace ChatContextKeyExprs {