diff --git a/src/vs/sessions/browser/layoutActions.ts b/src/vs/sessions/browser/layoutActions.ts index cd1f5880504..c9bf983b7c9 100644 --- a/src/vs/sessions/browser/layoutActions.ts +++ b/src/vs/sessions/browser/layoutActions.ts @@ -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"), diff --git a/src/vs/sessions/browser/menus.ts b/src/vs/sessions/browser/menus.ts index 74ebe982e7d..c322fba968b 100644 --- a/src/vs/sessions/browser/menus.ts +++ b/src/vs/sessions/browser/menus.ts @@ -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; diff --git a/src/vs/sessions/browser/parts/media/titlebarpart.css b/src/vs/sessions/browser/parts/media/titlebarpart.css index 6aab26d2631..f2f5d1ff68f 100644 --- a/src/vs/sessions/browser/parts/media/titlebarpart.css +++ b/src/vs/sessions/browser/parts/media/titlebarpart.css @@ -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; -} diff --git a/src/vs/sessions/browser/parts/sidebarPart.ts b/src/vs/sessions/browser/parts/sidebarPart.ts index b9132d4d13e..75eb74869dd 100644 --- a/src/vs/sessions/browser/parts/sidebarPart.ts +++ b/src/vs/sessions/browser/parts/sidebarPart.ts @@ -120,7 +120,7 @@ export class SidebarPart extends AbstractPaneCompositePart { ViewContainerLocation.Sidebar, Extensions.Viewlets, Menus.SidebarTitle, - Menus.TitleBarLeft, + Menus.TitleBarLeftLayout, notificationService, storageService, contextMenuService, diff --git a/src/vs/sessions/browser/parts/titlebarPart.ts b/src/vs/sessions/browser/parts/titlebarPart.ts index fa99c2e21e3..18c2d2867f0 100644 --- a/src/vs/sessions/browser/parts/titlebarPart.ts +++ b/src/vs/sessions/browser/parts/titlebarPart.ts @@ -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); diff --git a/src/vs/sessions/contrib/changesView/browser/changesViewActions.ts b/src/vs/sessions/contrib/changesView/browser/changesViewActions.ts index 1a06ef09fb5..4b348230ee0 100644 --- a/src/vs/sessions/contrib/changesView/browser/changesViewActions.ts +++ b/src/vs/sessions/contrib/changesView/browser/changesViewActions.ts @@ -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); })); diff --git a/src/vs/sessions/contrib/chat/browser/chat.contribution.ts b/src/vs/sessions/contrib/chat/browser/chat.contribution.ts index 74b536c6947..c10d4b4cdc0 100644 --- a/src/vs/sessions/contrib/chat/browser/chat.contribution.ts +++ b/src/vs/sessions/contrib/chat/browser/chat.contribution.ts @@ -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() { diff --git a/src/vs/sessions/contrib/chat/browser/runScriptAction.ts b/src/vs/sessions/contrib/chat/browser/runScriptAction.ts index e5c024cb2ba..fcb730a7b56 100644 --- a/src/vs/sessions/contrib/chat/browser/runScriptAction.ts +++ b/src/vs/sessions/contrib/chat/browser/runScriptAction.ts @@ -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()) diff --git a/src/vs/sessions/contrib/sessions/browser/media/sessionsTitleBarWidget.css b/src/vs/sessions/contrib/sessions/browser/media/sessionsTitleBarWidget.css index 577c5a482f6..26b50d594d9 100644 --- a/src/vs/sessions/contrib/sessions/browser/media/sessionsTitleBarWidget.css +++ b/src/vs/sessions/contrib/sessions/browser/media/sessionsTitleBarWidget.css @@ -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; diff --git a/src/vs/sessions/contrib/sessions/browser/sessionsTitleBarWidget.ts b/src/vs/sessions/contrib/sessions/browser/sessionsTitleBarWidget.ts index c5d61de4e50..6ff448786dd 100644 --- a/src/vs/sessions/contrib/sessions/browser/sessionsTitleBarWidget.ts +++ b/src/vs/sessions/contrib/sessions/browser/sessionsTitleBarWidget.ts @@ -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; } diff --git a/src/vs/sessions/contrib/terminal/browser/sessionsTerminalContribution.ts b/src/vs/sessions/contrib/terminal/browser/sessionsTerminalContribution.ts index 269d3a63a23..4696c11f97d 100644 --- a/src/vs/sessions/contrib/terminal/browser/sessionsTerminalContribution.ts +++ b/src/vs/sessions/contrib/terminal/browser/sessionsTerminalContribution.ts @@ -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()) }] }); diff --git a/src/vs/sessions/test/browser/layoutActions.test.ts b/src/vs/sessions/test/browser/layoutActions.test.ts index 786236ec970..f8362f7d654 100644 --- a/src/vs/sessions/test/browser/layoutActions.test.ts +++ b/src/vs/sessions/test/browser/layoutActions.test.ts @@ -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');