sessions: move Create Pull Request bar to the editor tabs title (#325122)

* sessions: move Create Pull Request bar to the editor tabs title

Host the Changes 'Create Pull Request' button bar (ChangesActionsBar) in
the single-pane editor tabs title (the editor-actions area of the docked
tab bar, Menus.SessionsEditorTitle) instead of the session title bar.

- Contribute the CHANGES_HEADER_ACTIONS_ID anchor action to
  Menus.SessionsEditorTitle, gated on the Changes editor being active in
  single-pane (singlePaneChangesEditorTitle) + SessionHasChangesContext.
- Provide the custom action view item (ChangesActionsBarActionViewItem)
  from SessionChangesEditor.getActionViewItem so the group's editor-actions
  toolbar renders it.
- Collapse the button labels to icon-only when the tabs title is narrow: a
  ResizeObserver on the tab-bar ancestor drives a compact observable that
  the button-bar widgets read to strip labels, freeing room for tabs.
- Adjust the ChangesActionsBar CSS comments/scoping for the editor tabs
  title host.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Make the button responsive

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com>
This commit is contained in:
Sandeep Somavarapu
2026-07-10 17:56:54 +00:00
committed by GitHub
co-authored by Copilot Ladislau Szomoru
parent 02955b5aae
commit 45d9cf02a2
7 changed files with 86 additions and 30 deletions
File diff suppressed because one or more lines are too long
@@ -31,6 +31,17 @@
padding-right: 0;
}
.agent-sessions-workbench .part.editor > .content .editor-group-container > .title > .tabs-and-actions-container.tabs-border-bottom > .editor-actions {
flex: 0 999 auto;
min-width: 70px;
overflow: hidden;
}
.agent-sessions-workbench .part.editor > .content .editor-group-container > .title > .tabs-and-actions-container.tabs-border-bottom > .monaco-scrollable-element {
flex: 1 1 auto;
min-width: 0;
}
.agent-sessions-workbench .part.editor > .content .editor-group-container > .title .editor-actions-separator {
display: block;
align-self: center;
@@ -446,18 +446,21 @@ export class ChangesActionsBar extends Disposable {
updateVisibility();
}));
}
}
// --- Editor header menus (single-pane): the Changes editor declares
// Menus.SessionsEditorHeaderPrimary (Branch Changes picker + diff stats, left) and
// Menus.SessionsEditorHeaderSecondary (diff/code-review/view-mode actions, right), and
// the editor group renders them. The Create Pull Request bar (ChangesActionsBar) is
// hosted in the sessions title bar (Menus.TitleBarSessionMenu). The custom action view
// items below are registered globally by menu id so the toolbars render them.
// hosted in the editor tabs title (Menus.SessionsEditorTitle); its custom action view
// item is provided by the Changes editor pane (SessionChangesEditor.getActionViewItem).
// The custom action view items below are registered globally by menu id so the
// header toolbars render them.
export const CHANGES_HEADER_ACTIONS_ID = 'workbench.changesView.headerActions';
/** Renders the {@link ChangesActionsBar} widget as the Create Pull Request title-bar action item. */
/** Renders the {@link ChangesActionsBar} widget as the Create Pull Request editor tabs title action item. */
export class ChangesActionsBarActionViewItem extends BaseActionViewItem {
constructor(
action: IAction,
@@ -509,11 +512,6 @@ class ChangesEditorHeaderContribution extends Disposable implements IWorkbenchCo
return instantiationService.createInstance(ChangesDiffStatsActionItem, action, options);
}, onDidRegister.event));
// Create Pull Request button bar, hosted in the title bar (right, session actions area).
this._register(actionViewItemService.register(Menus.TitleBarSessionMenu, CHANGES_HEADER_ACTIONS_ID, (action, options, instantiationService) => {
return instantiationService.createInstance(ChangesActionsBarActionViewItem, action, options);
}, onDidRegister.event));
onDidRegister.fire();
}
}
@@ -142,9 +142,11 @@ const singlePaneChangesEditorTitleVisible = ContextKeyExpr.and(
/**
* Anchor action hosting the Create Pull Request button bar ({@link ChangesActionsBar})
* in the single-pane title bar (right side the session actions area). The custom action
* view item is registered for {@link Menus.TitleBarSessionMenu} via IActionViewItemService
* in changesView.ts. The bar hides itself when its underlying menu has no actions.
* in the single-pane editor tabs title (the editor-actions area of the docked tab bar).
* The custom action view item is provided by the Changes editor pane
* ({@link SessionChangesEditor.getActionViewItem}) when the Changes editor is active,
* so the anchor is gated on the same. The bar hides itself when its underlying menu has
* no actions.
*/
class ChangesHeaderActionsAction extends Action2 {
constructor() {
@@ -153,13 +155,11 @@ class ChangesHeaderActionsAction extends Action2 {
title: localize2('changesView.headerActions', "Changes Actions"),
f1: false,
menu: {
id: Menus.TitleBarSessionMenu,
id: Menus.SessionsEditorTitle,
group: 'navigation',
order: 5,
when: ContextKeyExpr.and(
IsSessionsWindowContext,
IsAuxiliaryWindowContext.toNegated(),
SinglePaneLayoutEnabledContext,
singlePaneChangesEditorTitle,
SessionHasChangesContext
)
},
@@ -250,7 +250,7 @@
/* Compact the ChangesActionsBar buttons to 24px (2px shorter than the classic
* 26px header buttons) so the changes-editor header stays compact without extra
* header padding. Keyed off the bar's marker class. In the sessions title bar the
* header padding. Keyed off the bar's marker class. In the editor tabs title the
* 20px override below (scoped to the toolbar action-item context) wins on
* specificity, so this 24px baseline effectively applies to the classic internal
* changes-editor header. */
@@ -261,8 +261,8 @@
}
/* The Create Pull Request bar is rendered as a toolbar action item
* (`.action-item.changes-actions-bar` inside a `.monaco-action-bar`) in the sessions
* title bar (session actions area), where the classic
* (`.action-item.changes-actions-bar` inside a `.monaco-action-bar`) in the editor
* tabs title (the editor-actions area of the docked tab bar), where the classic
* `.session-changes-editor-header-right` normalization does not reach. Scope these to
* the action-item context so the classic internal changes-editor header and the aux-bar
* Changes view are left untouched. */
@@ -270,23 +270,49 @@
display: flex;
align-items: center;
gap: var(--vscode-spacing-size40);
min-width: 0;
/* Small breathing room so the button isn't flush against neighbouring toolbar icons. */
margin-left: var(--vscode-spacing-size20);
margin-right: var(--vscode-spacing-size20);
}
/* Render the button bar thinner so it sits comfortably in the title bar. Overrides the
* default 24px ChangesActionsBar button height without affecting the classic internal
/* Normalize the button bar height so it sits comfortably in the editor tabs title. Overrides the
* default ChangesActionsBar button height without affecting the classic internal
* changes-editor header (which renders the bar on a plain div, not inside a
* `.monaco-action-bar .action-item`). */
.monaco-action-bar .action-item.changes-actions-bar .monaco-button,
.monaco-action-bar .action-item.changes-actions-bar .monaco-button-dropdown {
height: 20px;
min-height: 20px;
height: 26px;
min-height: 26px;
padding-top: 0;
padding-bottom: 0;
}
.monaco-action-bar .action-item.changes-actions-bar .monaco-button-dropdown {
display: flex;
min-width: 0;
max-width: 100%;
}
.monaco-action-bar .action-item.changes-actions-bar .monaco-button-dropdown > .monaco-button:not(.monaco-dropdown-button) {
flex: 1 1 auto;
flex-wrap: wrap;
min-width: 0;
overflow: hidden;
}
.monaco-action-bar .action-item.changes-actions-bar .monaco-button-dropdown > .monaco-button-dropdown-separator,
.monaco-action-bar .action-item.changes-actions-bar .monaco-button-dropdown > .monaco-button.monaco-dropdown-button {
flex: 0 0 auto;
}
.monaco-action-bar .action-item.changes-actions-bar .monaco-button > span:not(.codicon) {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* The generic actionbar rule `.monaco-action-bar .action-item .codicon { width:
* 16px; height: 16px }` clamps every codicon including the button elements
* themselves (which carry the `codicon` class), squashing the secondary icon
@@ -294,7 +320,8 @@
* specificity than the base rule) so the buttons size to their own content. */
.monaco-action-bar .action-item.changes-actions-bar .codicon {
width: auto;
height: auto;
height: 24px;
flex-shrink: 0;
color: var(--vscode-button-foreground) !important;
}
@@ -37,8 +37,11 @@ import { Menus } from '../../../browser/menus.js';
import { IAgentWorkbenchLayoutService } from '../../../browser/workbench.js';
import { ActiveSessionContextKeys } from '../common/changes.js';
import { IChangesViewService } from '../common/changesViewService.js';
import { ChangesActionsBar } from './changesView.js';
import { ChangesActionsBar, ChangesActionsBarActionViewItem, CHANGES_HEADER_ACTIONS_ID } from './changesView.js';
import { SessionChangesEditorInput } from './sessionChangesEditorInput.js';
import { IAction } from '../../../../base/common/actions.js';
import { IBaseActionViewItemOptions } from '../../../../base/browser/ui/actionbar/actionViewItems.js';
import { IActionViewItem } from '../../../../base/browser/ui/actionbar/actionbar.js';
const HEADER_HEIGHT = 35;
@@ -210,6 +213,17 @@ export class SessionChangesEditor extends AbstractEditorWithViewState<IMultiDiff
return { instantiationService: this._scopedInstantiationService };
}
/**
* In single-pane, render the Create Pull Request button bar ({@link ChangesActionsBar})
* as the editor tabs title anchor action ({@link CHANGES_HEADER_ACTIONS_ID}).
*/
override getActionViewItem(action: IAction, options: IBaseActionViewItemOptions): IActionViewItem | undefined {
if (this._singlePane && action.id === CHANGES_HEADER_ACTIONS_ID) {
return this.instantiationService.createInstance(ChangesActionsBarActionViewItem, action, options);
}
return super.getActionViewItem(action, options);
}
override async setInput(input: SessionChangesEditorInput, options: IMultiDiffEditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise<void> {
await super.setInput(input, options, context, token);
const viewModel = await input.getViewModel();
@@ -10,7 +10,7 @@ import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/tes
import { isIMenuItem, MenuId, MenuRegistry } from '../../../../../platform/actions/common/actions.js';
import { isICommandActionToggleInfo } from '../../../../../platform/action/common/action.js';
import { EditorContextKeys } from '../../../../../editor/common/editorContextKeys.js';
import { ActiveEditorContext, AuxiliaryBarVisibleContext, IsSessionsWindowContext, MainEditorAreaVisibleContext } from '../../../../../workbench/common/contextkeys.js';
import { ActiveEditorContext, AuxiliaryBarVisibleContext, IsAuxiliaryWindowContext, IsSessionsWindowContext, IsTopRightEditorGroupContext, MainEditorAreaVisibleContext } from '../../../../../workbench/common/contextkeys.js';
import { Menus } from '../../../../browser/menus.js';
import { ChangesContextKeys } from '../../common/changes.js';
import { SessionHasChangesContext, SinglePaneLayoutEnabledContext } from '../../../../common/contextkeys.js';
@@ -184,24 +184,30 @@ suite('Changes View Actions', () => {
}]);
});
test('Create Pull Request anchor is contributed to the title bar session menu', () => {
const item = MenuRegistry.getMenuItems(Menus.TitleBarSessionMenu)
test('Create Pull Request anchor is contributed to the editor tabs title menu', () => {
const item = MenuRegistry.getMenuItems(Menus.SessionsEditorTitle)
.filter(isIMenuItem)
.find(item => item.command.id === CHANGES_HEADER_ACTIONS_ID);
assert.ok(item, 'expected the changes header actions anchor on the title bar session menu');
assert.ok(item, 'expected the changes header actions anchor on the editor tabs title menu');
const when = item.when?.serialize() ?? '';
assert.deepStrictEqual({
group: item.group,
order: item.order,
hasSessionsWindowGate: when.includes(IsSessionsWindowContext.key),
hasActiveEditorGate: when.includes(ActiveEditorContext.key) && when.includes(SessionChangesEditor.ID),
hasSinglePaneConfigGate: when.includes(SinglePaneLayoutEnabledContext.key),
hasAuxiliaryWindowGate: when.includes(IsAuxiliaryWindowContext.key),
hasTopRightEditorGroupGate: when.includes(IsTopRightEditorGroupContext.key),
hasChangesGate: when.includes(SessionHasChangesContext.key),
}, {
group: 'navigation',
order: 5,
hasSessionsWindowGate: true,
hasActiveEditorGate: true,
hasSinglePaneConfigGate: true,
hasAuxiliaryWindowGate: true,
hasTopRightEditorGroupGate: true,
hasChangesGate: true,
});
});