From ebec7ccbae462c8a351cad85fb04f12a5897bac1 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 1 Oct 2026 22:44:13 +0200 Subject: [PATCH 1/2] sessions: Show close actions in side-by-side headers Expose the existing close commands beside the overflow menu for multi-session and split-chat layouts, while preserving the compact close glyph on tabs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../sessions/browser/parts/chatGroupView.ts | 8 ++- .../sessions/browser/parts/sessionHeader.ts | 6 +++ src/vs/sessions/common/contextkeys.ts | 1 + .../sessions/browser/sessionsActions.ts | 21 +++++++- .../test/browser/sessionsActions.test.ts | 53 +++++++++++++------ .../test/browser/chatGroupsView.test.ts | 16 ++++-- 6 files changed, 83 insertions(+), 22 deletions(-) diff --git a/src/vs/sessions/browser/parts/chatGroupView.ts b/src/vs/sessions/browser/parts/chatGroupView.ts index 253fdb631e1603..74c85119ac0055 100644 --- a/src/vs/sessions/browser/parts/chatGroupView.ts +++ b/src/vs/sessions/browser/parts/chatGroupView.ts @@ -19,7 +19,7 @@ import { getChatSessionArchiveActionPresentation, getChatSessionArchiveActionWor import { ChatInteractivity, IChat, isSideChatOf, SessionStatus } from '../../services/sessions/common/session.js'; import { IActiveSession } from '../../services/sessions/common/sessionsManagement.js'; import { UNARCHIVE_SESSION_COMMAND_ID } from '../../common/sessionCommands.js'; -import { SessionActiveChatHasSideChatsContext, SessionActiveChatIsClosableContext, SessionActiveChatResourceContext, SessionFocusedChatIsRenameTargetContext, SessionHeaderActiveChatIsPinnedContext, SessionHeaderShowsChatContext } from '../../common/contextkeys.js'; +import { SessionActiveChatHasSideChatsContext, SessionActiveChatIsClosableContext, SessionActiveChatResourceContext, SessionFocusedChatIsRenameTargetContext, SessionHeaderActiveChatIsPinnedContext, SessionHeaderShowsChatContext, SessionToolbarShowsSessionContext } from '../../common/contextkeys.js'; import { IChatViewFactory } from '../../services/chatView/browser/chatViewFactory.js'; import { ChatCompositeBar, IChatCompositeBarDelegate } from './chatCompositeBar.js'; import { type IRemoteHostUnavailableEmptyStateContent, RemoteHostUnavailableEmptyState } from './remoteHostUnavailableEmptyState.js'; @@ -117,6 +117,7 @@ export class ChatGroupView extends Disposable implements ISerializableView { private readonly _activeChatResourceKey: IContextKey; private readonly _activeChatHasSideChatsKey: IContextKey; private readonly _headerShowsChatKey: IContextKey; + private readonly _toolbarShowsSessionKey: IContextKey; private readonly _focusedChatIsRenameTargetKey: IContextKey; private readonly _connection: SessionRemoteConnection; @@ -153,6 +154,7 @@ export class ChatGroupView extends Disposable implements ISerializableView { this._activeChatResourceKey = SessionActiveChatResourceContext.bindTo(scopedContextKeyService); this._activeChatHasSideChatsKey = SessionActiveChatHasSideChatsContext.bindTo(scopedContextKeyService); this._headerShowsChatKey = SessionHeaderShowsChatContext.bindTo(scopedContextKeyService); + this._toolbarShowsSessionKey = SessionToolbarShowsSessionContext.bindTo(scopedContextKeyService); this._focusedChatIsRenameTargetKey = SessionFocusedChatIsRenameTargetContext.bindTo(scopedContextKeyService); // Assigned here rather than as a field initializer: `_instantiationService` @@ -233,6 +235,7 @@ export class ChatGroupView extends Disposable implements ISerializableView { this._activeChatResourceKey.reset(); this._activeChatHasSideChatsKey.reset(); this._headerShowsChatKey.reset(); + this._toolbarShowsSessionKey.reset(); this._focusedChatIsRenameTargetKey.reset(); this._chatHeader.setChat(undefined); this._compositeBar.setGroup(undefined); @@ -256,6 +259,9 @@ export class ChatGroupView extends Disposable implements ISerializableView { this._chatHeader.setVisible(visible); this._headerShowsChatKey.set(visible); })); + this._contextDisposables.add(autorun(reader => { + this._toolbarShowsSessionKey.set(context.showSessionActions.read(reader)); + })); const delegate: IChatCompositeBarDelegate = { session: context.session, diff --git a/src/vs/sessions/browser/parts/sessionHeader.ts b/src/vs/sessions/browser/parts/sessionHeader.ts index acdfad2f8337d4..f873d565970c6b 100644 --- a/src/vs/sessions/browser/parts/sessionHeader.ts +++ b/src/vs/sessions/browser/parts/sessionHeader.ts @@ -5,7 +5,9 @@ import { Event } from '../../../base/common/event.js'; import { Disposable } from '../../../base/common/lifecycle.js'; +import { IContextKey, IContextKeyService } from '../../../platform/contextkey/common/contextkey.js'; import { IInstantiationService } from '../../../platform/instantiation/common/instantiation.js'; +import { SessionToolbarShowsSessionContext } from '../../common/contextkeys.js'; import { IActiveSession } from '../../services/sessions/common/sessionsManagement.js'; import { SessionHeaderBar } from './sessionHeaderBar.js'; @@ -15,6 +17,7 @@ import { SessionHeaderBar } from './sessionHeaderBar.js'; export class SessionHeader extends Disposable { private readonly _bar: SessionHeaderBar; + private readonly _toolbarShowsSessionKey: IContextKey; get element(): HTMLElement { return this._bar.element; } get visible(): boolean { return this._bar.visible; } @@ -24,12 +27,15 @@ export class SessionHeader extends Disposable { constructor( @IInstantiationService instantiationService: IInstantiationService, + @IContextKeyService contextKeyService: IContextKeyService, ) { super(); + this._toolbarShowsSessionKey = SessionToolbarShowsSessionContext.bindTo(contextKeyService); this._bar = this._register(instantiationService.createInstance(SessionHeaderBar)); } setSession(session: IActiveSession | undefined): void { + this._toolbarShowsSessionKey.set(!!session); this._bar.setContext(session ? { session, chat: session.activeChat } : undefined); } diff --git a/src/vs/sessions/common/contextkeys.ts b/src/vs/sessions/common/contextkeys.ts index ac698ccdb84e82..7585632590b6d0 100644 --- a/src/vs/sessions/common/contextkeys.ts +++ b/src/vs/sessions/common/contextkeys.ts @@ -38,6 +38,7 @@ export const SessionActiveChatHasSideChatsContext = new RawContextKey(' export const SessionShouldShowChatTabsContext = new RawContextKey('sessionShouldShowChatTabs', false, localize('sessionShouldShowChatTabs', "Whether the session view's chat tab strip is shown, i.e. the session has more than one chat actually showing as a tab. A single visible tab always hides the strip")); export const SessionHasMultipleOpenChatsContext = new RawContextKey('sessionHasMultipleOpenChats', false, localize('sessionHasMultipleOpenChats', "Whether the session view's session has more than one open chat (the tabs shown in the strip, including in-composer drafts). Used to scope chat-to-chat navigation (next/previous chat, the Ctrl+Tab chat switcher)")); export const SessionActiveChatIsClosableContext = new RawContextKey('sessionActiveChatIsClosable', false, localize('sessionActiveChatIsClosable', "Whether the active chat surface can be closed, either by hiding a non-main chat or by removing a side-by-side chat group")); +export const SessionToolbarShowsSessionContext = new RawContextKey('sessionToolbarShowsSession', false, localize('sessionToolbarShowsSession', "Whether the session toolbar represents the whole session rather than a side-by-side chat group")); export const SessionHeaderShowsChatContext = new RawContextKey('sessionHeaderShowsChat', false, localize('sessionHeaderShowsChat', "Whether the session-style header represents one chat in a side-by-side chat group rather than the whole session")); export const SessionHeaderActiveChatIsPinnedContext = new RawContextKey('sessionHeaderActiveChatIsPinned', false, localize('sessionHeaderActiveChatIsPinned', "Whether the chat represented by a side-by-side chat group header is pinned")); export const SessionFocusedChatIsRenameTargetContext = new RawContextKey('sessionFocusedChatIsRenameTarget', false, localize('sessionFocusedChatIsRenameTarget', "Whether the focused chat group's visible chat is a non-main chat that should receive the chat-specific rename command instead of the session rename command")); diff --git a/src/vs/sessions/contrib/sessions/browser/sessionsActions.ts b/src/vs/sessions/contrib/sessions/browser/sessionsActions.ts index 37ae453628f3b4..3e42ebbb795b09 100644 --- a/src/vs/sessions/contrib/sessions/browser/sessionsActions.ts +++ b/src/vs/sessions/contrib/sessions/browser/sessionsActions.ts @@ -36,7 +36,7 @@ import { IViewsService } from '../../../../workbench/services/views/common/views import { getQuickNavigateHandler, inQuickPickContext } from '../../../../workbench/browser/quickaccess.js'; import { Menus } from '../../../browser/menus.js'; import { SessionsCategories } from '../../../common/categories.js'; -import { CanGoBackContext, CanGoForwardContext, SessionProviderIdContext, MultipleSessionsVisibleContext, SessionIsArchivedContext, SessionIsCreatedContext, SessionIsMaximizedContext, SessionIsStickyContext, SessionsFocusContext, SessionSupportsMultipleChatsContext, SessionSupportsRenameContext, SessionsWelcomeVisibleContext, SessionIdContext, SessionHasMultipleCommittedChatsContext, SessionHasMultipleOpenChatsContext, SessionsPickerVisibleContext, SessionActiveChatIsClosableContext, SessionFocusedChatIsRenameTargetContext, SessionActiveChatIsDeletableContext, SessionChatsPickerVisibleContext, SessionActiveChatHasSideChatsContext, SessionActiveChatResourceContext, SessionsTitleBarNewSessionEnabledContext, SessionsEditorScopeContext, SessionsHasClosedItemContext, IsNewChatSessionContext, IsQuickChatSessionContext, SessionsListPromoteNewChatActionContext, SessionHeaderActiveChatIsPinnedContext, SessionHeaderShowsChatContext, SessionItemIsMultiSelectionContext, IsPhoneLayoutContext } from '../../../common/contextkeys.js'; +import { CanGoBackContext, CanGoForwardContext, SessionProviderIdContext, MultipleSessionsVisibleContext, SessionIsArchivedContext, SessionIsCreatedContext, SessionIsMaximizedContext, SessionIsStickyContext, SessionsFocusContext, SessionSupportsMultipleChatsContext, SessionSupportsRenameContext, SessionsWelcomeVisibleContext, SessionIdContext, SessionHasMultipleCommittedChatsContext, SessionHasMultipleOpenChatsContext, SessionsPickerVisibleContext, SessionActiveChatIsClosableContext, SessionFocusedChatIsRenameTargetContext, SessionActiveChatIsDeletableContext, SessionChatsPickerVisibleContext, SessionActiveChatHasSideChatsContext, SessionActiveChatResourceContext, SessionsTitleBarNewSessionEnabledContext, SessionsEditorScopeContext, SessionsHasClosedItemContext, IsNewChatSessionContext, IsQuickChatSessionContext, SessionsListPromoteNewChatActionContext, SessionHeaderActiveChatIsPinnedContext, SessionHeaderShowsChatContext, SessionItemIsMultiSelectionContext, IsPhoneLayoutContext, SessionToolbarShowsSessionContext } from '../../../common/contextkeys.js'; import { ANY_AGENT_HOST_PROVIDER_RE } from '../../../common/agentHostSessionsProvider.js'; import { ARRANGE_SESSIONS_COMMAND_ID, CLOSE_CHAT_COMMAND_ID, CLOSE_SESSION_COMMAND_ID, FOCUS_ACTIVE_SESSION_COMMAND_ID, FOCUS_NEXT_CHAT_GROUP_COMMAND_ID, FOCUS_PREVIOUS_CHAT_GROUP_COMMAND_ID, MOVE_CHAT_TO_NEXT_GROUP_COMMAND_ID, MOVE_CHAT_TO_PREVIOUS_GROUP_COMMAND_ID, RENAME_CHAT_COMMAND_ID, RENAME_SESSION_COMMAND_ID, SESSION_GRID_FOCUS_COMMANDS, SPLIT_CHAT_GROUP_DOWN_COMMAND_ID, SPLIT_CHAT_GROUP_RIGHT_COMMAND_ID, TOGGLE_PIN_CHAT_COMMAND_ID, TOGGLE_PIN_SESSION_COMMAND_ID } from '../../../common/sessionCommands.js'; import { IActiveSession, ISessionsManagementService } from '../../../services/sessions/common/sessionsManagement.js'; @@ -919,6 +919,17 @@ registerAction2(class CloseChatAction extends Action2 { } }); +MenuRegistry.appendMenuItem(Menus.SessionBarToolbar, { + command: { + id: CLOSE_CHAT_COMMAND_ID, + title: localize('closeChatGroup', "Close"), + icon: Codicon.close, + }, + when: ContextKeyExpr.and(SessionHeaderShowsChatContext, SessionActiveChatIsClosableContext), + group: 'navigation', + order: 30, +}); + registerAction2(class CloseAllChatsAction extends Action2 { constructor() { super({ @@ -1907,6 +1918,14 @@ registerAction2(class CloseSessionAction extends Action2 { win: { primary: KeyMod.CtrlCmd | KeyCode.F4, secondary: [KeyMod.CtrlCmd | KeyCode.KeyW] }, }, menu: [{ + id: Menus.SessionBarToolbar, + when: ContextKeyExpr.and( + MultipleSessionsVisibleContext, + SessionToolbarShowsSessionContext, + ), + group: 'navigation', + order: 30, + }, { id: Menus.SessionBarToolbar, when: ContextKeyExpr.and( ContextKeyExpr.or(SessionIsCreatedContext, MultipleSessionsVisibleContext), diff --git a/src/vs/sessions/contrib/sessions/test/browser/sessionsActions.test.ts b/src/vs/sessions/contrib/sessions/test/browser/sessionsActions.test.ts index 9304fd3db3d576..e4ec742877f79f 100644 --- a/src/vs/sessions/contrib/sessions/test/browser/sessionsActions.test.ts +++ b/src/vs/sessions/contrib/sessions/test/browser/sessionsActions.test.ts @@ -356,6 +356,7 @@ suite('Sessions - Actions', () => { assert.deepStrictEqual(actions, [ { id: 'sessions.chatCompositeBar.togglePin', group: 'navigation' }, + { id: 'sessions.chatCompositeBar.close', group: 'navigation' }, { id: 'sessions.sessionHeader.rename', group: 'secondary/1_session' }, { id: 'sessions.chatCompositeBar.addChat', group: 'secondary/3_newChat' }, { id: 'sessions.chatCompositeBar.togglePin', group: 'secondary/4_pin' }, @@ -586,50 +587,72 @@ suite('Sessions - Actions', () => { { id: TOGGLE_PIN_CHAT_COMMAND_ID, title: 'Pin', group: 'navigation' }, { id: TOGGLE_PIN_CHAT_COMMAND_ID, title: 'Pin', group: 'secondary/4_pin' }, { id: 'sessions.chatCompositeBar.toggleMaximize', title: 'Maximize', group: 'secondary/4_pin' }, + { id: CLOSE_SESSION_COMMAND_ID, title: 'Close', group: 'navigation' }, { id: CLOSE_SESSION_COMMAND_ID, title: 'Close', group: 'secondary/4_pin' }, + { id: CLOSE_CHAT_COMMAND_ID, title: 'Close', group: 'navigation' }, { id: CLOSE_CHAT_COMMAND_ID, title: 'Close', group: 'secondary/4_pin' }, ]); }); - test('uses the same small close icon for chat and side-panel tabs', () => { - const chatClose = MenuRegistry.getMenuItems(Menus.SessionChatTab) + test('uses a compact close icon for tabs and a regular close icon for headers', () => { + const chatTabClose = MenuRegistry.getMenuItems(Menus.SessionChatTab) .filter(isIMenuItem) .find(item => item.command.id === CLOSE_CHAT_COMMAND_ID); + const chatHeaderClose = MenuRegistry.getMenuItems(Menus.SessionBarToolbar) + .filter(isIMenuItem) + .find(item => item.command.id === CLOSE_CHAT_COMMAND_ID && item.group === 'navigation'); + const sessionHeaderClose = MenuRegistry.getMenuItems(Menus.SessionBarToolbar) + .filter(isIMenuItem) + .find(item => item.command.id === CLOSE_SESSION_COMMAND_ID && item.group === 'navigation'); const instantiationService = workbenchInstantiationService(undefined, disposables); const editorClose = disposables.add(instantiationService.createInstance(CloseEditorTabAction, CloseEditorTabAction.ID, CloseEditorTabAction.LABEL)); assert.deepStrictEqual({ - chatIcon: chatClose?.command.icon, + chatTabIcon: chatTabClose?.command.icon, + chatHeaderIcon: chatHeaderClose?.command.icon, + sessionHeaderIcon: sessionHeaderClose?.command.icon, editorClass: editorClose.class, }, { - chatIcon: Codicon.closeSmall, + chatTabIcon: Codicon.closeSmall, + chatHeaderIcon: Codicon.close, + sessionHeaderIcon: Codicon.close, editorClass: 'codicon codicon-close-small', }); }); test('uses mutually exclusive close actions for session and chat group headers', () => { - const getCloseWhen = (menu: MenuId, commandId: string) => MenuRegistry.getMenuItems(menu) + const getCloseItems = (menu: MenuId, commandId: string) => MenuRegistry.getMenuItems(menu) .filter(isIMenuItem) - .find(item => item.command.id === commandId) - ?.when?.serialize(); + .filter(item => item.command.id === commandId) + .map(item => ({ group: item.group, when: item.when?.serialize() })); assert.deepStrictEqual({ toolbar: { - chat: getCloseWhen(Menus.SessionBarToolbar, CLOSE_CHAT_COMMAND_ID), - session: getCloseWhen(Menus.SessionBarToolbar, CLOSE_SESSION_COMMAND_ID), + chat: getCloseItems(Menus.SessionBarToolbar, CLOSE_CHAT_COMMAND_ID), + session: getCloseItems(Menus.SessionBarToolbar, CLOSE_SESSION_COMMAND_ID), }, contextMenu: { - chat: getCloseWhen(Menus.SessionHeaderContext, CLOSE_CHAT_COMMAND_ID), - session: getCloseWhen(Menus.SessionHeaderContext, CLOSE_SESSION_COMMAND_ID), + chat: getCloseItems(Menus.SessionHeaderContext, CLOSE_CHAT_COMMAND_ID), + session: getCloseItems(Menus.SessionHeaderContext, CLOSE_SESSION_COMMAND_ID), }, }, { toolbar: { - chat: 'sessionActiveChatIsClosable && sessionHeaderShowsChat', - session: 'multipleSessionsVisible && !sessionHeaderShowsChat || sessionIsCreated && !sessionHeaderShowsChat', + chat: [ + { group: 'navigation', when: 'sessionActiveChatIsClosable && sessionHeaderShowsChat' }, + { group: 'secondary/4_pin', when: 'sessionActiveChatIsClosable && sessionHeaderShowsChat' }, + ], + session: [ + { group: 'navigation', when: 'multipleSessionsVisible && sessionToolbarShowsSession' }, + { group: 'secondary/4_pin', when: 'multipleSessionsVisible && !sessionHeaderShowsChat || sessionIsCreated && !sessionHeaderShowsChat' }, + ], }, contextMenu: { - chat: 'sessionActiveChatIsClosable && sessionHeaderShowsChat', - session: 'multipleSessionsVisible && !sessionHeaderShowsChat || sessionIsCreated && !sessionHeaderShowsChat', + chat: [ + { group: '1_view', when: 'sessionActiveChatIsClosable && sessionHeaderShowsChat' }, + ], + session: [ + { group: '1_view', when: 'multipleSessionsVisible && !sessionHeaderShowsChat || sessionIsCreated && !sessionHeaderShowsChat' }, + ], }, }); diff --git a/src/vs/sessions/test/browser/chatGroupsView.test.ts b/src/vs/sessions/test/browser/chatGroupsView.test.ts index 69a9489a268192..7a982ff57fbc29 100644 --- a/src/vs/sessions/test/browser/chatGroupsView.test.ts +++ b/src/vs/sessions/test/browser/chatGroupsView.test.ts @@ -29,7 +29,7 @@ import { AbstractChatView, ChatViewKind, IChatViewOptions } from '../../browser/ import { ChatGroupsView } from '../../browser/parts/chatGroupsView.js'; import { SessionDropTarget } from '../../browser/parts/sessionDropTarget.js'; import { DraggedSessionIdentifier, SessionsDataTransfers } from '../../browser/dnd.js'; -import { SessionActiveChatHasSideChatsContext, SessionActiveChatIsClosableContext, SessionActiveChatResourceContext, SessionFocusedChatIsRenameTargetContext, SessionHeaderActiveChatIsPinnedContext, SessionHeaderShowsChatContext } from '../../common/contextkeys.js'; +import { SessionActiveChatHasSideChatsContext, SessionActiveChatIsClosableContext, SessionActiveChatResourceContext, SessionFocusedChatIsRenameTargetContext, SessionHeaderActiveChatIsPinnedContext, SessionHeaderShowsChatContext, SessionToolbarShowsSessionContext } from '../../common/contextkeys.js'; import { SESSIONS_CHAT_TABS_SETTING, SessionsChatTabsMode } from '../../common/sessionConfig.js'; import { type IAgentHostAutoConnect, type IAgentHostConnectProgress, type IAgentHostConnectionLabels, IAgentHostSessionsProvider } from '../../common/agentHostSessionsProvider.js'; import { IChatViewFactory } from '../../services/chatView/browser/chatViewFactory.js'; @@ -1547,21 +1547,27 @@ suite('Sessions - ChatGroupsView', () => { const contextKeyService = instantiationService.get(IContextKeyService); const singleGroup = view.element.querySelector('.chat-group-view')!; const singleGroupHeaderShowsChat = contextKeyService.getContext(singleGroup).getValue(SessionHeaderShowsChatContext.key); + const singleGroupToolbarShowsSession = contextKeyService.getContext(singleGroup).getValue(SessionToolbarShowsSessionContext.key); view.splitChatToSide(secondary.resource); - const splitGroupActions = Array.from(view.element.querySelectorAll('.session-chat-tabs-actions')); - const splitGroupHeaderShowsChat = Array.from(view.element.querySelectorAll('.chat-group-view')) - .map(group => contextKeyService.getContext(group).getValue(SessionHeaderShowsChatContext.key)); + const splitGroups = Array.from(view.element.querySelectorAll('.chat-group-view')); + const splitGroupActions = splitGroups.map(group => group.querySelector('.session-chat-tabs-actions')); + const splitGroupHeaderShowsChat = splitGroups.map(group => contextKeyService.getContext(group).getValue(SessionHeaderShowsChatContext.key)); + const splitGroupToolbarShowsSession = splitGroups.map(group => contextKeyService.getContext(group).getValue(SessionToolbarShowsSessionContext.key)); assert.deepStrictEqual({ singleGroupHidden, singleGroupHeaderShowsChat, - splitGroupsHidden: splitGroupActions.map(actions => actions.classList.contains('hidden')), + singleGroupToolbarShowsSession, + splitGroupsHidden: splitGroupActions.map(actions => actions?.classList.contains('hidden')), splitGroupHeaderShowsChat, + splitGroupToolbarShowsSession, }, { singleGroupHidden: false, singleGroupHeaderShowsChat: false, + singleGroupToolbarShowsSession: true, splitGroupsHidden: [true, true], splitGroupHeaderShowsChat: [false, false], + splitGroupToolbarShowsSession: [false, false], }); }); From afbf3f8d0540db04cd7ca955ea199dd6e9a410d9 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 1 Oct 2026 22:55:07 +0200 Subject: [PATCH 2/2] sessions: Clarify header close actions Give icon-only close actions surface-specific accessible names and make menu tests deterministic across Browser and Electron-Unit runners. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../sessions/contrib/sessions/browser/sessionsActions.ts | 2 ++ .../sessions/test/browser/sessionsActions.test.ts | 9 +++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/vs/sessions/contrib/sessions/browser/sessionsActions.ts b/src/vs/sessions/contrib/sessions/browser/sessionsActions.ts index 3e42ebbb795b09..cf74584534837b 100644 --- a/src/vs/sessions/contrib/sessions/browser/sessionsActions.ts +++ b/src/vs/sessions/contrib/sessions/browser/sessionsActions.ts @@ -923,6 +923,7 @@ MenuRegistry.appendMenuItem(Menus.SessionBarToolbar, { command: { id: CLOSE_CHAT_COMMAND_ID, title: localize('closeChatGroup', "Close"), + tooltip: localize('closeChatGroupTooltip', "Close Chat Group"), icon: Codicon.close, }, when: ContextKeyExpr.and(SessionHeaderShowsChatContext, SessionActiveChatIsClosableContext), @@ -1905,6 +1906,7 @@ registerAction2(class CloseSessionAction extends Action2 { super({ id: CLOSE_SESSION_COMMAND_ID, title: localize2('chatCompositeBar.close', "Close"), + tooltip: localize2('chatCompositeBar.closeTooltip', "Close Session"), icon: Codicon.close, keybinding: { weight: KeybindingWeight.SessionsContrib, diff --git a/src/vs/sessions/contrib/sessions/test/browser/sessionsActions.test.ts b/src/vs/sessions/contrib/sessions/test/browser/sessionsActions.test.ts index e4ec742877f79f..739f581b652307 100644 --- a/src/vs/sessions/contrib/sessions/test/browser/sessionsActions.test.ts +++ b/src/vs/sessions/contrib/sessions/test/browser/sessionsActions.test.ts @@ -574,7 +574,7 @@ suite('Sessions - Actions', () => { const actions = MenuRegistry.getMenuItems(Menus.SessionBarToolbar) .filter(isIMenuItem) .filter(item => [TOGGLE_PIN_SESSION_COMMAND_ID, TOGGLE_PIN_CHAT_COMMAND_ID, 'sessions.chatCompositeBar.toggleMaximize', CLOSE_SESSION_COMMAND_ID, CLOSE_CHAT_COMMAND_ID].includes(item.command.id)) - .sort((a, b) => (a.order ?? 0) - (b.order ?? 0) || a.command.id.localeCompare(b.command.id)) + .sort((a, b) => (a.order ?? 0) - (b.order ?? 0) || a.command.id.localeCompare(b.command.id) || (a.group ?? '').localeCompare(b.group ?? '')) .map(item => ({ id: item.command.id, title: typeof item.command.title === 'string' ? item.command.title : item.command.title.value, @@ -610,12 +610,16 @@ suite('Sessions - Actions', () => { assert.deepStrictEqual({ chatTabIcon: chatTabClose?.command.icon, chatHeaderIcon: chatHeaderClose?.command.icon, + chatHeaderTooltip: chatHeaderClose?.command.tooltip, sessionHeaderIcon: sessionHeaderClose?.command.icon, + sessionHeaderTooltip: typeof sessionHeaderClose?.command.tooltip === 'string' ? sessionHeaderClose.command.tooltip : sessionHeaderClose?.command.tooltip?.value, editorClass: editorClose.class, }, { chatTabIcon: Codicon.closeSmall, chatHeaderIcon: Codicon.close, + chatHeaderTooltip: 'Close Chat Group', sessionHeaderIcon: Codicon.close, + sessionHeaderTooltip: 'Close Session', editorClass: 'codicon codicon-close-small', }); }); @@ -624,7 +628,8 @@ suite('Sessions - Actions', () => { const getCloseItems = (menu: MenuId, commandId: string) => MenuRegistry.getMenuItems(menu) .filter(isIMenuItem) .filter(item => item.command.id === commandId) - .map(item => ({ group: item.group, when: item.when?.serialize() })); + .map(item => ({ group: item.group, when: item.when?.serialize() })) + .sort((a, b) => (a.group ?? '').localeCompare(b.group ?? '')); assert.deepStrictEqual({ toolbar: {