cmux Layout System Optimization

Pane zoom (⌘⇧↩ maximize / minimize) benchmark & fix — July 10, 2026

Goal: benchmark and eliminate the lag when maximizing a pane and then minimizing it with multiple panes open.

6.3×
faster minimize in the real app (825 ms → 130 ms median, 11 panes)
2.7×
faster maximize in the real app (315 ms → 115 ms median, 11 panes)
~395×
faster minimize in the isolated layout benchmark (1303 ms → 3.3 ms)
0
view rebuilds & terminal portal detach/attach per toggle (was: full tree + 18–20 portal events)

Root cause

Toggling zoom rebuilt the entire split layout, twice over, on every keypress:

The fix: collapse-in-place zoom

Zoom no longer swaps or re-keys anything. The split tree stays mounted; zoom is applied by collapsing the side of each split that doesn't contain the zoomed pane (hidden arranged subviews are excluded from NSSplitView tiling, so the zoom path expands to the full container). Un-zoom unhides and re-applies the stored divider ratios.

Benchmark 1 — isolated layout engine (bonsplit package)

New reproducible benchmark: Tests/BonsplitTests/ZoomTogglePerformanceTests.swift hosts the real BonsplitView in an NSWindow, builds an 8-pane layout, and measures wall-clock settle latency per toggle over 12 cycles plus AppKit content-view churn. Run with swift test --filter ZoomTogglePerformanceTests. Identical harness for before/after.

Metric (debug, 8 panes, median)BeforeAfterChange
Maximize settle443.8 ms166–190 ms *≈2.5× faster
Minimize settle1303.4 ms3.3 ms≈395× faster
Pane content NSViews recreated per maximize10eliminated
Pane content NSViews recreated per minimize80eliminated

Release mode before: maximize 146.7 ms / minimize 406.8 ms median → after: minimize 2.5–3.3 ms; churn 0.

* The residual maximize time in this harness is offscreen-window SwiftUI update scheduling, not layout work: a probe confirmed the AppKit frames (which drive cmux's portal-hosted terminals) are final within the toggle call, and a single forced layout pass immediately produces the final full-width frame. A real, visible window performs that pass on the next display frame — confirmed by the in-app numbers below.

Benchmark 2 — real app A/B (end-to-end)

Two identical Debug builds of cmux (tags layout-zoom-before / layout-zoom-bench), differing only in the fix. Same machine, same 11-pane workspace, same scripted driver: 10 maximize/minimize cycles via real ⌘⇧↩ key events, timings extracted from the timestamped debug event log with the same parser and settle definition (toggle begin → last layout/portal activity).

Metric (11 panes, median of 10)BeforeAfterChange
Maximize settle315 ms115 ms2.7× faster
Minimize settle825 ms130 ms6.3× faster
Keypress → all split frames applied (direct metric)n/a (rebuild)~31–34 msone event-loop turn
Terminal portal detach/attach per minimize18–200eliminated
NSSplitView instances rebuilt per togglefull tree0same view pointers across all 20 toggles

The remaining ~115–130 ms “settle” tail is the trailing SwiftUI pass applying visibility flags and chrome updates; the layout itself (what the user sees move) lands in ~31–34 ms from the keypress — within about two display frames, with zero Metal-surface rebinds.

Visual verification

11-pane split layout before zoom
11-pane workspace before zoom (optimized build).
Zoomed pane fills the window with zoom indicator on tab
⌘⇧↩ maximize: the focused pane fills the window (note the zoom indicator on the tab). No ghost content from hidden panes.
Layout restored exactly after minimize
⌘⇧↩ minimize: all 11 panes restored pixel-perfect — frames were preserved through the zoom, so nothing had to be rebuilt.

Bugs found & fixed along the way

Validation

Changes

Reproduce

# Isolated layout benchmark (before/after by stashing the bonsplit fix)
cd vendor/bonsplit && swift test --filter ZoomTogglePerformanceTests

# Real-app A/B
./scripts/reload.sh --tag layout-zoom-bench        # fixed build
# create 10 splits, then drive ⌘⇧↩ cycles and parse /tmp/cmux-debug-<tag>.log
# for zoom.toggle.begin → last split/portal activity per toggle

Report generated by Claude (cmux layout benchmark session). Localization audit: no user-facing strings, shortcuts, or settings were added or changed — rendering-internal changes plus DEBUG-only log lines only.