fix sessions title bar (#298932)

* fix title alignment

* fix sessions title bar
This commit is contained in:
Sandeep Somavarapu
2026-03-03 12:29:27 +01:00
committed by GitHub
parent 858363ee8d
commit f7d4cc7365
12 changed files with 104 additions and 90 deletions

View File

@@ -52,7 +52,7 @@ class ToggleSidebarVisibilityAction extends Action2 {
},
menu: [
{
id: Menus.TitleBarLeft,
id: Menus.TitleBarLeftLayout,
group: 'navigation',
order: 0,
when: ContextKeyExpr.and(IsAuxiliaryWindowContext.toNegated(), SessionsWelcomeVisibleContext.toNegated())
@@ -104,7 +104,7 @@ class ToggleSecondarySidebarVisibilityAction extends Action2 {
f1: true,
menu: [
{
id: Menus.TitleBarRight,
id: Menus.TitleBarRightLayout,
group: 'navigation',
order: 10,
when: ContextKeyExpr.and(IsAuxiliaryWindowContext.toNegated(), SessionsWelcomeVisibleContext.toNegated())
@@ -165,7 +165,7 @@ registerAction2(ToggleSecondarySidebarVisibilityAction);
registerAction2(TogglePanelVisibilityAction);
// Floating window controls: always-on-top
MenuRegistry.appendMenuItem(Menus.TitleBarRight, {
MenuRegistry.appendMenuItem(Menus.TitleBarRightLayout, {
command: {
id: 'workbench.action.toggleWindowAlwaysOnTop',
title: localize('toggleWindowAlwaysOnTop', "Toggle Always on Top"),

View File

@@ -13,11 +13,10 @@ export const Menus = {
CommandCenter: new MenuId('SessionsCommandCenter'),
CommandCenterCenter: new MenuId('SessionsCommandCenterCenter'),
TitleBarContext: new MenuId('SessionsTitleBarContext'),
TitleBarControlMenu: new MenuId('SessionsTitleBarControlMenu'),
TitleBarLeft: new MenuId('SessionsTitleBarLeft'),
TitleBarCenter: new MenuId('SessionsTitleBarCenter'),
TitleBarRight: new MenuId('SessionsTitleBarRight'),
OpenSubMenu: new MenuId('SessionsOpenSubMenu'),
TitleBarLeftLayout: new MenuId('SessionsTitleBarLeftLayout'),
TitleBarSessionTitle: new MenuId('SessionsTitleBarSessionTitle'),
TitleBarSessionMenu: new MenuId('SessionsTitleBarSessionMenu'),
TitleBarRightLayout: new MenuId('SessionsTitleBarRightLayout'),
PanelTitle: new MenuId('SessionsPanelTitle'),
SidebarTitle: new MenuId('SessionsSidebarTitle'),
AuxiliaryBarTitle: new MenuId('SessionsAuxiliaryBarTitle'),
@@ -25,5 +24,4 @@ export const Menus = {
SidebarFooter: new MenuId('SessionsSidebarFooter'),
SidebarCustomizations: new MenuId('SessionsSidebarCustomizations'),
AgentFeedbackEditorContent: new MenuId('AgentFeedbackEditorContent'),
SessionTitleActions: new MenuId('SessionTitleActions'),
} as const;

View File

@@ -8,10 +8,73 @@
height: 100%;
align-items: center;
order: 0;
flex-grow: 2;
flex-grow: 0;
flex-shrink: 0;
width: auto;
justify-content: flex-start;
}
.monaco-workbench .part.titlebar > .sessions-titlebar-container.has-center > .titlebar-center {
order: 1;
width: auto;
flex-grow: 0;
flex-shrink: 1;
min-width: 0px;
margin: 0;
justify-content: flex-start;
}
.agent-sessions-workbench.monaco-workbench .part.titlebar > .sessions-titlebar-container > .titlebar-left {
width: fit-content;
flex-grow: 0;
}
.agent-sessions-workbench.monaco-workbench .part.titlebar > .sessions-titlebar-container > .titlebar-center {
flex: 1;
max-width: none;
}
.agent-sessions-workbench.monaco-workbench .part.titlebar > .sessions-titlebar-container > .titlebar-center .window-title {
margin: unset;
}
.agent-sessions-workbench.monaco-workbench .part.titlebar > .sessions-titlebar-container > .titlebar-right {
order: 2;
width: fit-content;
flex-grow: 0;
justify-content: flex-end;
margin-right: 10px;
}
/* Session Title Actions Container (before right toolbar) */
.monaco-workbench .part.titlebar > .sessions-titlebar-container > .titlebar-right > .titlebar-actions-container {
display: none;
flex-shrink: 0;
-webkit-app-region: no-drag;
height: 100%;
}
.monaco-workbench .part.titlebar > .sessions-titlebar-container > .titlebar-right > .titlebar-actions-container:not(.has-no-actions) {
display: flex;
align-items: center;
}
.monaco-workbench .part.titlebar > .sessions-titlebar-container > .titlebar-right > .titlebar-actions-container:not(.has-no-actions):not(:last-child)::after {
content: '';
width: 1px;
height: 16px;
margin: 0 4px;
background-color: var(--vscode-disabledForeground);
}
.monaco-workbench .part.titlebar > .sessions-titlebar-container > .titlebar-right > .titlebar-actions-container .codicon {
color: inherit;
}
.monaco-workbench .part.titlebar > .sessions-titlebar-container > .titlebar-right > .titlebar-actions-container .monaco-action-bar .action-item {
display: flex;
}
/* Left Tool Bar Container */
.monaco-workbench .part.titlebar > .sessions-titlebar-container > .titlebar-left > .left-toolbar-container {
display: none;
@@ -40,11 +103,8 @@
display: flex;
}
/* TODO: Hack to avoid flicker when sidebar becomes visible.
* The contribution swaps the menu item synchronously, but the toolbar
* re-render is async, causing a brief flash. Hide the container via
* CSS when sidebar is visible (nosidebar class is removed synchronously). */
.agent-sessions-workbench:not(.nosidebar) .part.titlebar > .sessions-titlebar-container > .titlebar-left > .left-toolbar-container {
/* Hide the entire titlebar left when the sidebar is visible */
.agent-sessions-workbench:not(.nosidebar) .part.titlebar > .sessions-titlebar-container > .titlebar-left {
display: none !important;
}
@@ -52,7 +112,3 @@
.agent-sessions-workbench.mac .part.titlebar .window-controls-container {
-webkit-app-region: drag;
}
.agent-sessions-workbench:not(.nosidebar) .part.titlebar > .sessions-titlebar-container > .titlebar-left .window-controls-container {
display: none !important;
}

View File

@@ -120,7 +120,7 @@ export class SidebarPart extends AbstractPaneCompositePart {
ViewContainerLocation.Sidebar,
Extensions.Viewlets,
Menus.SidebarTitle,
Menus.TitleBarLeft,
Menus.TitleBarLeftLayout,
notificationService,
storageService,
contextMenuService,

View File

@@ -185,7 +185,7 @@ export class TitlebarPart extends Part implements ITitlebarPart {
// Left toolbar (driven by Menus.TitleBarLeft, rendered after window controls via CSS order)
const leftToolbarContainer = append(this.leftContent, $('div.left-toolbar-container'));
this._register(this.instantiationService.createInstance(MenuWorkbenchToolBar, leftToolbarContainer, Menus.TitleBarLeft, {
this._register(this.instantiationService.createInstance(MenuWorkbenchToolBar, leftToolbarContainer, Menus.TitleBarLeftLayout, {
contextMenu: Menus.TitleBarContext,
telemetrySource: 'titlePart.left',
hiddenItemStrategy: HiddenItemStrategy.NoHide,
@@ -204,13 +204,22 @@ export class TitlebarPart extends Part implements ITitlebarPart {
}));
// Right toolbar (driven by Menus.TitleBarRight - includes account submenu)
const rightToolbarContainer = prepend(this.rightContent, $('div.action-toolbar-container'));
this._register(this.instantiationService.createInstance(MenuWorkbenchToolBar, rightToolbarContainer, Menus.TitleBarRight, {
const rightToolbarContainer = prepend(this.rightContent, $('div.titlebar-actions-container.titlebar-layout-actions-container'));
this._register(this.instantiationService.createInstance(MenuWorkbenchToolBar, rightToolbarContainer, Menus.TitleBarRightLayout, {
contextMenu: Menus.TitleBarContext,
telemetrySource: 'titlePart.right',
toolbarOptions: { primaryGroup: () => true },
}));
// Session title actions toolbar (before right toolbar)
const sessionActionsContainer = prepend(this.rightContent, $('div.titlebar-actions-container.titlebar-session-actions-container'));
this._register(this.instantiationService.createInstance(MenuWorkbenchToolBar, sessionActionsContainer, Menus.TitleBarSessionMenu, {
contextMenu: Menus.TitleBarContext,
hiddenItemStrategy: HiddenItemStrategy.NoHide,
telemetrySource: 'titlePart.sessionActions',
toolbarOptions: { primaryGroup: () => true },
}));
// Context menu on the titlebar
this._register(addDisposableListener(this.rootContainer, EventType.CONTEXT_MENU, e => {
EventHelper.stop(e);

View File

@@ -35,7 +35,8 @@ const openChangesViewActionOptions: IAction2Options = {
icon: Codicon.diffMultiple,
f1: false,
menu: {
id: Menus.SessionTitleActions,
id: Menus.TitleBarSessionMenu,
group: 'navigation',
order: 1,
when: ContextKeyExpr.equals(activeSessionHasChangesContextKey.key, true),
},
@@ -158,7 +159,7 @@ class ChangesViewActionsContribution extends Disposable implements IWorkbenchCon
) {
super();
this._register(actionViewItemService.register(Menus.SessionTitleActions, OpenChangesViewAction.ID, (action, options) => {
this._register(actionViewItemService.register(Menus.TitleBarSessionMenu, OpenChangesViewAction.ID, (action, options) => {
return instantiationService.createInstance(ChangesActionViewItem, action, options);
}));

View File

@@ -47,11 +47,11 @@ export class OpenSessionWorktreeInVSCodeAction extends Action2 {
id: OpenSessionWorktreeInVSCodeAction.ID,
title: localize2('openInVSCode', 'Open in VS Code'),
icon: Codicon.vscodeInsiders,
precondition: ContextKeyExpr.and(IsAuxiliaryWindowContext.toNegated(), SessionsWelcomeVisibleContext.toNegated(), IsActiveSessionBackgroundProviderContext),
menu: [{
id: Menus.TitleBarRight,
id: Menus.TitleBarSessionMenu,
group: 'navigation',
order: 10,
when: ContextKeyExpr.and(IsAuxiliaryWindowContext.toNegated(), SessionsWelcomeVisibleContext.toNegated(), IsActiveSessionBackgroundProviderContext)
}]
});
}
@@ -92,29 +92,6 @@ export class OpenSessionWorktreeInVSCodeAction extends Action2 {
}
registerAction2(OpenSessionWorktreeInVSCodeAction);
// Disabled placeholder shown in the titlebar when the active session does not support opening in VS Code
class OpenSessionWorktreeInVSCodeNotAvailableAction extends Action2 {
constructor() {
super({
id: 'chat.openSessionWorktreeInVSCode.notAvailable',
title: localize2('openInVSCode', 'Open in VS Code'),
tooltip: localize('openInVSCodeNotAvailableTooltip', "Open in VS Code is not available for this session type"),
icon: Codicon.vscodeInsiders,
precondition: ContextKeyExpr.false(),
menu: [{
id: Menus.TitleBarRight,
group: 'navigation',
order: 10,
when: ContextKeyExpr.and(IsAuxiliaryWindowContext.toNegated(), SessionsWelcomeVisibleContext.toNegated(), IsActiveSessionBackgroundProviderContext.toNegated())
}]
});
}
override run(): void { }
}
registerAction2(OpenSessionWorktreeInVSCodeNotAvailableAction);
class NewChatInSessionsWindowAction extends Action2 {
constructor() {

View File

@@ -285,7 +285,7 @@ export class RunScriptContribution extends Disposable implements IWorkbenchContr
}
// Register the Run split button submenu on the workbench title bar (background sessions only)
MenuRegistry.appendMenuItem(Menus.TitleBarRight, {
MenuRegistry.appendMenuItem(Menus.TitleBarSessionMenu, {
submenu: RunScriptDropdownMenuId,
isSplitButton: true,
title: localize2('run', "Run"),
@@ -305,7 +305,7 @@ class RunScriptNotAvailableAction extends Action2 {
icon: Codicon.play,
precondition: ContextKeyExpr.false(),
menu: [{
id: Menus.TitleBarRight,
id: Menus.TitleBarSessionMenu,
group: 'navigation',
order: 8,
when: ContextKeyExpr.and(IsAuxiliaryWindowContext.toNegated(), SessionsWelcomeVisibleContext.toNegated(), IsActiveSessionBackgroundProviderContext.toNegated())

View File

@@ -6,13 +6,11 @@
/* Container - button style hover */
.command-center .agent-sessions-titlebar-container {
display: flex;
width: 38vw;
max-width: 600px;
display: flex;
width: 100%;
flex-direction: row;
flex-wrap: nowrap;
align-items: center;
justify-content: center;
justify-content: flex-start;
padding: 0 10px;
height: 22px;
border-radius: 4px;
@@ -30,28 +28,13 @@
padding: 0 4px;
border-radius: 4px;
min-width: 0;
max-width: 600px;
}
.command-center .agent-sessions-titlebar-container .agent-sessions-titlebar-pill:hover {
background-color: var(--vscode-toolbar-hoverBackground);
}
/* Session title actions toolbar */
.command-center .agent-sessions-titlebar-container .agent-sessions-titlebar-actions {
display: flex;
align-items: center;
flex-shrink: 0;
}
.command-center .agent-sessions-titlebar-container .agent-sessions-titlebar-actions .actions-container {
height: auto;
}
.command-center .agent-sessions-titlebar-container .agent-sessions-titlebar-actions .action-item {
display: flex;
align-items: center;
}
.command-center .agent-sessions-titlebar-container:focus {
outline: 1px solid var(--vscode-focusBorder);
outline-offset: -1px;
@@ -63,11 +46,9 @@
align-items: center;
gap: 6px;
min-width: 0;
justify-content: center;
justify-content: flex-start;
cursor: pointer;
}
/* Kind icon */
.command-center .agent-sessions-titlebar-container .agent-sessions-titlebar-icon {
display: flex;
align-items: center;

View File

@@ -14,7 +14,7 @@ import { BaseActionViewItem, IBaseActionViewItemOptions } from '../../../../base
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
import { MenuRegistry, SubmenuItemAction } from '../../../../platform/actions/common/actions.js';
import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js';
import { HiddenItemStrategy, MenuWorkbenchToolBar } from '../../../../platform/actions/browser/toolbar.js';
import { Menus } from '../../../browser/menus.js';
import { IWorkbenchContribution } from '../../../../workbench/common/contributions.js';
import { IActionViewItemService } from '../../../../platform/actions/browser/actionViewItemService.js';
@@ -179,14 +179,6 @@ export class SessionsTitleBarWidget extends BaseActionViewItem {
this._container.appendChild(sessionPill);
// Session title actions toolbar (rendered next to the session title)
const actionsContainer = $('span.agent-sessions-titlebar-actions');
this._dynamicDisposables.add(this.instantiationService.createInstance(MenuWorkbenchToolBar, actionsContainer, Menus.SessionTitleActions, {
hiddenItemStrategy: HiddenItemStrategy.NoHide,
toolbarOptions: { primaryGroup: () => true },
}));
this._container.appendChild(actionsContainer);
// Hover
this._dynamicDisposables.add(this.hoverService.setupManagedHover(
getDefaultHoverDelegate('mouse'),
@@ -317,14 +309,14 @@ export class SessionsTitleBarContribution extends Disposable implements IWorkben
// Register the submenu item in the Agent Sessions command center
this._register(MenuRegistry.appendMenuItem(Menus.CommandCenter, {
submenu: Menus.TitleBarControlMenu,
submenu: Menus.TitleBarSessionTitle,
title: localize('agentSessionsControl', "Agent Sessions"),
order: 101,
when: ContextKeyExpr.and(IsAuxiliaryWindowContext.negate(), SessionsWelcomeVisibleContext.negate())
}));
// Register a placeholder action so the submenu appears
this._register(MenuRegistry.appendMenuItem(Menus.TitleBarControlMenu, {
this._register(MenuRegistry.appendMenuItem(Menus.TitleBarSessionTitle, {
command: {
id: FocusAgentSessionsAction.id,
title: localize('showSessions', "Show Sessions"),
@@ -334,7 +326,7 @@ export class SessionsTitleBarContribution extends Disposable implements IWorkben
when: IsAuxiliaryWindowContext.negate()
}));
this._register(actionViewItemService.register(Menus.CommandCenter, Menus.TitleBarControlMenu, (action, options) => {
this._register(actionViewItemService.register(Menus.CommandCenter, Menus.TitleBarSessionTitle, (action, options) => {
if (!(action instanceof SubmenuItemAction)) {
return undefined;
}

View File

@@ -149,9 +149,9 @@ class OpenSessionInTerminalAction extends Action2 {
title: localize2('openInTerminal', "Open Terminal"),
icon: Codicon.terminal,
menu: [{
id: Menus.TitleBarRight,
id: Menus.TitleBarSessionMenu,
group: 'navigation',
order: 11,
order: 9,
when: ContextKeyExpr.and(IsAuxiliaryWindowContext.toNegated(), SessionsWelcomeVisibleContext.toNegated())
}]
});

View File

@@ -16,7 +16,7 @@ suite('Sessions - Layout Actions', () => {
ensureNoDisposablesAreLeakedInTestSuite();
test('always-on-top toggle action is contributed to TitleBarRight', () => {
const items = MenuRegistry.getMenuItems(Menus.TitleBarRight);
const items = MenuRegistry.getMenuItems(Menus.TitleBarRightLayout);
const menuItems = items.filter(isIMenuItem);
const toggleAlwaysOnTop = menuItems.find(item => item.command.id === 'workbench.action.toggleWindowAlwaysOnTop');