Skip to content

fix: add top gap for Modern UI floating panels when title bar is hidden - #328688

Merged
Lee Murray (mrleemurray) merged 6 commits into
microsoft:mainfrom
Bosphoramus:fix/modern-ui-fullscreen-top-gap
Aug 5, 2026
Merged

fix: add top gap for Modern UI floating panels when title bar is hidden#328688
Lee Murray (mrleemurray) merged 6 commits into
microsoft:mainfrom
Bosphoramus:fix/modern-ui-fullscreen-top-gap

Conversation

@Bosphoramus

Copy link
Copy Markdown
Contributor

Fixes #328681

Problem

When Modern UI (workbench.experimental.modernUI) is enabled and the title bar is hidden (e.g. native fullscreen on macOS), the floating-card content island is flush against the screen top while left, right, and bottom gaps are preserved.

Fix

  • Add a notitlebar layout class (mirroring nostatusbar) toggled on mainContainer based on isVisible(Parts.TITLEBAR_PART) — whether the title bar is visible in fullscreen depends on configuration (window.commandCenter, activity bar position, etc.), not just platform, so the fix is gated on actual visibility rather than fullscreen state
  • CSS: doubled outer gutter (8px) on all floating parts when .notitlebar is present
  • JS: deduct top margin from content dimensions in paneCompositePart.ts and editorPart.ts

All changes are gated behind .floating-panels / isFloatingPanelsEnabled() — no effect when Modern UI is disabled.

Test plan

  • macOS fullscreen (default config): all four gaps consistent
  • macOS windowed: no change
  • Modern UI OFF (windowed + fullscreen): no change
  • Fullscreen with window.commandCenter: false: title bar hides, top gap appears
  • Fullscreen with window.commandCenter: true (default): title bar visible, no extra gap
  • Panel in all 4 positions, fullscreen + windowed
  • Status bar hidden + title bar hidden: both gaps doubled
  • Zen mode: gaps consistent
…en (microsoft#328681)

When the Modern UI experiment (`workbench.experimental.modernUI`) is enabled,
the floating-card layout relies on the title bar to provide visual spacing at
the top. When the title bar is hidden (e.g. native fullscreen on macOS) the
content island becomes flush against the screen edge while left, right, and
bottom gaps are preserved.

Add a `notitlebar` layout class (mirroring the existing `nostatusbar` pattern
for the bottom edge) that is toggled on `mainContainer` whenever the title bar
grid view is not visible. This class is set based on the actual title bar
visibility (`isVisible(Parts.TITLEBAR_PART)`), not the fullscreen state, so it
correctly handles platforms where the title bar remains visible in fullscreen
(e.g. Windows with its window controls and hamburger menu).

CSS: add a doubled outer gutter (`--vscode-spacing-size40 * 2`, 8px) to the
top of all floating parts (sidebar, auxiliary bar, editor, panel, activity bar)
when `.notitlebar` is present. Also inset vertical sash highlights to match.

JS: update the layout insets in `paneCompositePart.ts` and `editorPart.ts` to
deduct the top margin from content dimensions when `isVisible(Parts.TITLEBAR_PART)`
returns false.

All new CSS selectors require `.floating-panels` (only set when Modern UI is
enabled) and the JS changes are guarded by `isFloatingPanelsEnabled()`, so
there is no effect when the experiment is disabled.

Fixes microsoft#328681
Copilot AI review requested due to automatic review settings August 3, 2026 10:47
@Bosphoramus

Copy link
Copy Markdown
Contributor Author

@microsoft-github-policy-service agree

@vs-code-engineering

vs-code-engineering Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

📬 CODENOTIFY

The following users are being notified based on files changed in this PR:

Benjamin Christopher Simmonds (@benibenj)

Matched files:

  • src/vs/workbench/browser/parts/activitybar/activitybarPart.ts
  • src/vs/workbench/browser/parts/editor/editorPart.ts
  • src/vs/workbench/browser/parts/paneCompositePart.ts
  • src/vs/workbench/services/layout/browser/layoutService.ts
  • src/vs/workbench/services/layout/test/browser/layoutService.test.ts

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Not ready to approve

Activity bar sizing, banner visibility, and top-panel sash alignment currently produce incorrect layouts.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Pull request overview

Adds a top outer gutter to Modern UI floating panels when the title bar is hidden.

Changes:

  • Tracks hidden title bar state with notitlebar.
  • Adds top margins and matching content insets.
  • Adjusts sash highlights for the new gutter.
File summaries
File Description
paneCompositePart.ts Reserves top space for floating pane composites.
editorPart.ts Reserves the editor’s top gutter.
floatingPanels.css Styles top gutters and sash insets.
layout.ts Maintains the notitlebar layout class.
Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 4
  • Review effort level: Balanced

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Comment on lines +187 to +189
.monaco-workbench.floating-panels.notitlebar .part.activitybar {
margin-top: calc(var(--vscode-spacing-size40) * 2);
}
Comment on lines +70 to +75
/* When the title bar is hidden (.notitlebar, e.g. native fullscreen on macOS) the top
* becomes a window edge; double the top margin to match the outer gutter on other
* window edges. Mirrors the .nostatusbar pattern for the bottom edge. */
.monaco-workbench.floating-panels.notitlebar .part.sidebar,
.monaco-workbench.floating-panels.notitlebar .part.auxiliarybar {
margin-top: calc(var(--vscode-spacing-size40) * 2);
Comment on lines +356 to +358
.monaco-workbench.floating-panels.notitlebar.panel-position-top:not(.nopanel) .monaco-sash.vertical:not(.part .monaco-sash)::before {
top: calc(var(--vscode-spacing-size40) * 2);
}
Comment on lines +1484 to +1489
// When the title bar is hidden (fullscreen), the editor top faces the window edge;
// use a doubled outer gutter. When a top panel is visible the editor faces the panel
// card instead (inter-card gap).
const titleBarHidden = !this.layoutService.isVisible(Parts.TITLEBAR_PART, mainWindow);
const topMargin = panelAtTop ? FLOATING_PANEL_MARGIN
: titleBarHidden ? FLOATING_PANEL_MARGIN * 2 : 0;
@mrleemurray

Copy link
Copy Markdown
Contributor

Please add automated regression coverage for this layout change. The CSS margins and TypeScript content insets must remain synchronized across title-bar visibility, banner visibility, panel positions, and the maximized-panel case. In particular, the existing activity bar tests in src/vs/workbench/test/browser/parts/activitybar/activitybarPart.test.ts currently cover only width constraints and would not catch the new top-margin/content-height mismatch. A focused layout test for the hidden-title-bar transition would protect the behavior this PR is fixing.

The activity bar's layout() only reserved the bottom floating-panels
gutter. With the new doubled top margin when the title bar is hidden,
its content ended up 8px too tall, so it could overlap or clip at the
bottom. It now reserves both gutters.

The notitlebar class (and everything that reads it — layout.ts,
EditorPart, AbstractPaneCompositePart, ActivitybarPart) only checked
title bar visibility. A visible banner still gives the floating cards
a top edge to sit against, same as a visible title bar, so it
shouldn't get the doubled gutter either. Added a shared
isFloatingTopEdgeHidden(layoutService, targetWindow) in
layoutService.ts that checks both, and made setBannerHidden() update
the class when banner visibility changes — it didn't before.

Also dropped a redundant sash selector in floatingPanels.css
(.notitlebar.panel-position-top:not(.nopanel)) that doubled the sash
inset to 8px even though the editor/sidebar margin stays at the
normal 4px when a top panel is visible. CSS specificity alone
resolves this correctly once the override is gone.

Shortened an inline comment in editorPart.ts down to one line to
match the inline-comment guideline.

Added tests: a suite for isFloatingTopEdgeHidden across title-bar/
banner combinations in layoutService.test.ts, and focused layout()
tests in activitybarPart.test.ts covering the content-height
reservation across title-bar visibility, banner visibility, and
floating-panels-disabled.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Not ready to approve

The activity bar still under-reserves its doubled bottom gutter when the status bar is hidden.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details

Suppressed comments (4)

src/vs/workbench/test/browser/parts/activitybar/activitybarPart.test.ts:356

  • This disables the lint rule to install a fake through an any cast, which the repository's test guidance explicitly prohibits. Please provide an injectable/test-visible seam for the composite-bar layout dependency or exercise a real PaneCompositeBar instead of mutating the private disposable.
		// eslint-disable-next-line local/code-no-any-casts
		(part as any).compositeBar.value = {

src/vs/workbench/browser/parts/activitybar/activitybarPart.ts:277

  • When the status bar is hidden, the CSS gives the activity bar a doubled bottom gutter (floatingPanels.css:292-293), but this still subtracts only one gutter. The composite bar is therefore laid out 4px too tall and can clip at the bottom—especially in the test-plan case where both top and bottom gutters are doubled. Compute a status-aware bottom gutter here and update the new tests, which currently encode the single-bottom-gutter result.
		const contentHeight = Math.max(0, height - gutter - topGutter);

src/vs/workbench/test/browser/parts/activitybar/activitybarPart.test.ts:351

  • This four-line inline comment narrates the test and exceeds the repository rule that inline comments stay to one line. The test name and a short note are sufficient.

This issue also appears on line 355 of the same file.

	// Regression coverage for the hidden-title-bar transition: `.floating-panels.notitlebar
	// .part.activitybar` (floatingPanels.css) adds a doubled top margin when the title bar
	// (and banner) are hidden, so `layout()` must reserve that same space in `contentHeight`
	// or the composite bar is laid out too tall and its content clips/overlaps at the bottom.

src/vs/workbench/services/layout/browser/layoutService.ts:132

  • This JSDoc exceeds the repository's 1–2 short-sentence limit and enumerates implementation consumers. Keep the helper's contract concise; callers are already discoverable from references.
/**
 * Whether the top of the floating cards faces the window edge directly and should
 * receive the doubled outer gutter (mirrors the doubled bottom gutter used when the
 * status bar is hidden). The title bar and the banner are the two grid rows that can
 * sit above the middle section, so the cards are only flush with the window edge when
  • Files reviewed: 8/8 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

@mrleemurray

Copy link
Copy Markdown
Contributor

Tony (@Bosphoramus) can you address the Copilot comments then I'll re-review

…est the margins

The class was called notitlebar, but it only gets set when the title
bar and the banner are both hidden. Anyone reading the CSS would guess
wrong. It is now top-window-edge, which says what actually happens: the
floating cards are sitting against the top of the window. The helper is
isFloatingTopEdgeExposed to match.

Six places were updating that class by hand. layout.ts already has an
event for exactly this, and it covers both the title bar and the
banner, so one subscription does the job. Seven places flip those two
views today, and doing it by hand works right up until someone adds an
eighth and forgets.

The activity bar's layout() now checks this.content instead of the
composite bar, and only calls the bar if it exists. Both this.content
and partLayout are set in Part.create(), so nothing breaks, and the
content area gets sized whether or not the bar is there. That means a
test can read the height back instead of poking at private fields.

The margin formulas for the pane composites and the editor had no tests
at all, which left the panel positions and the maximized panel
unprotected. They are plain functions of the layout service, so they
moved to layoutService.ts next to the other floating helpers, the same
way isSidebarSiblingToEditor already delegated there. paneCompositePart
and editorPart lose about ninety lines between them, and the logic is
unchanged.

Tests now cover the panel at top, bottom and left, centered and
justified alignment, the panel maximized with and without something
above it, the status bar hidden, and the experiment switched off. The
activity bar ones drive layout() for real and read the height back. No
casts, no lint suppressions.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Ready to approve

The CSS and layout calculations are consistently gated and covered across the relevant visibility and panel configurations.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details
  • Files reviewed: 8/8 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

@Bosphoramus

Copy link
Copy Markdown
Contributor Author
@Bosphoramus

Copy link
Copy Markdown
Contributor Author

I'll resolve merge conflicts asap Lee Murray (@mrleemurray)

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Ready to approve

The behavior is consistently implemented and tested; remaining feedback is non-blocking comment cleanup.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details

Suppressed comments (4)

src/vs/workbench/services/layout/browser/layoutService.ts:292

  • Repository guidance limits method-body comments to one line. Please condense this explanation while retaining the non-obvious edge relationship.
	// A top panel faces the editor below it, and a sibling bar faces a bottom panel card,
	// so in neither case does the bottom reach the window edge.

src/vs/workbench/browser/parts/activitybar/activitybarPart.ts:270

  • This comment only narrates the immediately preceding guard, which the repository guidance explicitly asks method-body comments to avoid. Removing it keeps the control flow self-explanatory.
			return; // not created yet

src/vs/workbench/services/layout/browser/layoutService.ts:281

  • Repository guidance limits method-body comments to one line. This constraint can be stated concisely without splitting the explanation across two lines.

This issue also appears on line 291 of the same file.

		// A visible editor sits above the panel, so the gap is between two cards. When the
		// panel is maximized the editor is gone and the panel takes over that row instead.

src/vs/workbench/browser/layout.ts:1704

  • Repository guidance limits method-body comments to one line. This explanation can be reduced to a single concise statement.
		// The floating cards abut the top window edge only while neither of the two grid rows
		// above the middle section is showing, so track both rather than the title bar alone.
  • Files reviewed: 8/8 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

@Bosphoramus

Copy link
Copy Markdown
Contributor Author

@mrleemurray Lee Murray (mrleemurray) left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@Bosphoramus

Copy link
Copy Markdown
Contributor Author

Lee Murray (@mrleemurray) There is a failing test:

Agent Host E2E — Claude
reopening a session keeps sub-agent messages out of the parent transcript (replay path)

First it failed on Linux, now its failing on macOS

But i think its unrelated to my changes

@mrleemurray
Lee Murray (mrleemurray) merged commit b9ca85e into microsoft:main Aug 5, 2026
46 of 47 checks passed
@vs-code-engineering vs-code-engineering Bot added this to the 1.133.0 milestone Aug 5, 2026
@Bosphoramus
Tony (Bosphoramus) deleted the fix/modern-ui-fullscreen-top-gap branch August 6, 2026 06:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

5 participants