Skip to content

Inverted glass layout: Aside-style backdrop, opaque content cards - #303

Merged
arzafran merged 14 commits into
mainfrom
feat/glass-inverted
Aug 14, 2026
Merged

Inverted glass layout: Aside-style backdrop, opaque content cards#303
arzafran merged 14 commits into
mainfrom
feat/glass-inverted

Conversation

@arzafran

Copy link
Copy Markdown
Member

What this does

Flips the window's layering to match how native Tahoe apps (and Aside) treat a sidebar: the window backdrop is now a Liquid Glass surface sampling the desktop and following the system appearance, and the terminal/browser panes float on it as opaque rounded cards. This fixes the forced-dark sidebar in light mode at the root, and deletes the custom corner-mask machinery that caused every transparent-corner bug — the standard AppKit frame owns the window shape again.

Summary

  • Backdrop: full-window NSGlassEffectView underlay in the theme frame below the contentView; standard transparent-window compositing, no contentView replacement, no masks. Stock on macOS 26 (cmuxShouldApplyWindowGlass returns true when available; pre-26 keeps the legacy opt-in).
  • Content cards: panes are opaque, 12pt continuous radius, 8pt inset; clipped at both the SwiftUI pane background and the AppKit surface containers. The 0.82 terminal-opacity glass clamp is removed.
  • Strip: single row — per-pane pills + bare "+" (new tab) left, split/new-workspace capsules right, one shared surface tone, 8pt grid; pills reserve the capsules' width.
  • Safe-area leaks fixed in three independently-created hosting views (main window, sidebar host, bonsplit panes) — full-size-content windows inset every NSHostingView by the titlebar height by default.
  • Chrome tracks live resize: pane chrome syncs immediately during inLiveResize, traffic lights re-seat per resize tick.
  • Settings pass: "Match Terminal Background" removed (it fought the system-appearance sidebar); sidebar spacing on a 4pt grid.

Review order

  1. Sources/WindowChrome.swift — the new backdrop architecture (large deletion)
  2. Sources/GhosttyTerminalSupport.swift + Sources/AppDelegate.swift — the gate/predicate changes
  3. Sources/WindowPaneChromePortal.swift — strip layout, "+" button, live-resize sync
  4. vendor/bonsplit — pane safe area, bar height
  5. The rest is spacing/settings fallout

Test plan

  • CI green (unit + socket)
  • Manual: light system mode → light glass sidebar; dark mode → dark
  • Manual: resize continuously — pills, capsules, and traffic lights track the edges
  • Manual: 4-pane split, close panes, restore session — cards and pills stay coherent

The window backdrop is now the sidebar's Liquid Glass surface: a full-window
NSGlassEffectView underlay in the theme frame below the contentView, sampling
the desktop through the standard transparent-window compositing path (no
contentView replacement, no custom corner masks — AppKit owns the frame,
which removes the transparent-corner class of bugs at the source).

Terminal and browser panes are opaque elevated cards (12pt continuous
radius, 8pt inset from edges and sidebar). The 0.82 glass opacity clamp is
gone — the card is fully opaque by design. The sidebar renders flat on the
backdrop with the system appearance, fixing the forced-dark sidebar in
light mode; only pane pills keep terminal-derived contrast.

Pane chrome portal now searches the theme frame for its host, minimal mode
no longer pulls content under the titlebar in card layout, and a
debug.viewtree socket command dumps the window's AppKit tree (frames,
layer colors, effect-view state) for chrome-layering diagnosis.
The square corners were bonsplit's pane background painting the full pane
rect; it is now clipped to the card shape. Sidebar paddings land on a 4pt
grid (edges on 8): workspace rows, footer, quota rows; the off-grid scroll
spacer is gone. Control clusters use 8pt top offset and gaps.
…right

Both capsules share the tab strip's midline instead of stacking below the
window top; the tab bar reserves their width so pills compress rather than
slide under; capsules take the resting-pill surface so the strip's three
backgrounds read as one material family.
…apsules

Strip is 44pt (28pt pill + 8/8); selected pills and control capsules share
labelColor@0.12 at full presence, quiet pills stay untinted glass.
…live resize

A Safari-style + capsule follows the last pill (scrolls with tabs, pills
compress around its reserved seat) wired to the pane's new-terminal-tab
action, with EN/JA strings. Chrome geometry syncs immediately during live
resize instead of coalescing — the one-turn lag read as icons trailing the
window edge. Pill gap joins the 8pt grid.
The + is a plain 28pt ghost button, not a capsule peer of the pills. With
the sidebar visible the card layout no longer reserves the legacy 32pt
titlebar band (and skips the invisible titlebar overlay) — the strip owns
the card top. Sidebar-hidden and fullscreen keep the band for the titlebar
accessory controls.
Remove the Match Terminal Background toggle and its scheme-forcing modifier
— the sidebar follows the system appearance by design, and the modifier was
the last path that could force terminal-derived contrast onto it. Drop the
dead sidebarBlendMode parameter from cmuxShouldApplyWindowGlass and update
its test to the new contract (glass is stock when available; the legacy
opt-in only gates the pre-26 fallback). Legacy key readers in fallback and
debug paths stay untouched.
Each bonsplit pane is hosted by its own NSHostingController, which was
still honoring the window's titlebar safe area — ~24pt of dead space above
the tab strip in full-size-content windows. The pane owns its own top, so
the hosting controller's safeAreaRegions are cleared.
Titlebar relayout snaps the buttons back to the stock corner on every
resize tick; re-seat them synchronously per tick instead of only at
didEndLiveResize.
Bare rows (no card surface) align to card-edge + row-inset (16pt), not the
8pt card-edge line; 8pt to the window bottom.
…coherence

Update the two tests still asserting pre-inversion contracts (0.82 opacity
clamp; contentView-replacement glass hierarchy) to the new ones: opaque
cards stay opaque, and the backdrop installs as a theme-frame sibling below
an untouched contentView and removes cleanly. Card corner rounding now
shares the card-layout predicate (skipped under Reduce Transparency, which
also disables the insets). The fallback sidebar restores terminal-derived
label contrast when Match Terminal Background is on.
Resolves WindowPaneChromePortal: keep the single-row strip layout and adopt
main's ensureAboveBars z-order helper from the portal-churn fix (#302).
…ss contract

On macOS 26 the transparent window + glass backdrop are stock, so the
transparency predicate returns true regardless of the legacy opt-in; the
test now branches on availability. The backdrop test tracks the exact view
apply() adds instead of type-matching, which also caught the theme frame's
own titlebar material.
@arzafran
arzafran merged commit 97c537b into main Aug 14, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant