Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/vs/workbench/api/common/extHostChatSessions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions src/vs/workbench/contrib/chat/browser/actions/chatActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
});

Expand Down
75 changes: 70 additions & 5 deletions src/vs/workbench/contrib/chat/browser/chatSessions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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';
Expand All @@ -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;
Expand Down Expand Up @@ -546,6 +559,7 @@ interface ISessionTemplateData {
container: HTMLElement;
resourceLabel: IResourceLabel;
actionBar: ActionBar;
elementDisposable: DisposableStore;
}

// Renderer for session items in the tree
Expand All @@ -557,6 +571,8 @@ class SessionsRenderer extends Disposable implements ITreeRenderer<IChatSessionI
private readonly labels: ResourceLabels,
@IThemeService private readonly themeService: IThemeService,
@ILogService private readonly logService: ILogService,
@IMenuService private readonly menuService: IMenuService,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
) {
super();

Expand Down Expand Up @@ -615,17 +631,24 @@ class SessionsRenderer extends Disposable implements ITreeRenderer<IChatSessionI
renderTemplate(container: HTMLElement): ISessionTemplateData {
const element = append(container, $('.chat-session-item'));
const resourceLabel = this.labels.create(element, { supportHighlights: true });
const actionBar = new ActionBar(container);
const actionsContainer = append(resourceLabel.element, $('.actions'));
const actionBar = new ActionBar(actionsContainer);
const elementDisposable = new DisposableStore();

return {
container: element,
resourceLabel,
actionBar
actionBar,
elementDisposable
};
}

renderElement(element: ITreeNode<IChatSessionItem, FuzzyScore>, 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;
Expand Down Expand Up @@ -661,9 +684,51 @@ class SessionsRenderer extends Disposable implements ITreeRenderer<IChatSessionI
fileKind: undefined,
icon: iconTheme || iconUri
});

// Create context overlay for this specific session item
const contextOverlay = getSessionItemContextOverlay(session, sessionWithProvider.provider);

const contextKeyService = this.contextKeyService.createOverlay(contextOverlay);

// Create menu for this session item
const menu = templateData.elementDisposable.add(
this.menuService.createMenu(MenuId.ChatSessionsMenu, contextKeyService)
);

// Setup action bar with contributed actions
const setupActionBar = () => {
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<IChatSessionItem, FuzzyScore>, _index: number, templateData: ISessionTemplateData): void {
templateData.elementDisposable.clear();
}

disposeTemplate(templateData: ISessionTemplateData): void {
templateData.elementDisposable.dispose();
templateData.resourceLabel.dispose();
templateData.actionBar.dispose();
}
Expand Down Expand Up @@ -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(
Expand Down
12 changes: 12 additions & 0 deletions src/vs/workbench/contrib/chat/browser/media/chatSessions.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
2 changes: 2 additions & 0 deletions src/vs/workbench/contrib/chat/common/chatContextKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ export namespace ChatContextKeys {
};

export const panelLocation = new RawContextKey<ViewContainerLocation>('chatPanelLocation', undefined, { type: 'number', description: localize('chatPanelLocation', "The location of the chat panel.") });

export const sessionType = new RawContextKey<string>('chatSessionType', '', { type: 'string', description: localize('chatSessionType', "The type of the current chat session item.") });
}

export namespace ChatContextKeyExprs {
Expand Down
Loading