fix(next): the sidebar's content hold changes the content's frame once; per-frame trace of both modes

Merged
TE
6d ago
+208-51

Toggling the sidebar no longer shifts the content and snaps it back in either sidebar mode. This PR fixes the opt-in content hold so it changes the content's frame once and leaves it still. It also adds a per-frame trace that proves both modes reach their landing with no bounce.

Leo:

when i toggle the sidebar on and off I STILL SEE THE ENTIRE OTHER PART OF THE APP SHIFT LAYOUT FOR NO REASON AND SNAP BACK

Cause

The slide's content hold (MainPaneCardView.contentHold) was added by the GPU audit of 2026-10-21 so panes lay out once per toggle, not on every frame. It kept the content at the widest width of the slide and pinned it to the sidebar's moving edge:

  • Show: the old wide layout rode the sidebar's edge across the window, its far side clipped, then laid out narrower when the slide landed. Centred and trailing content slid right and jumped back.
  • Hide: the content was laid out at its final width at once, but at the card's old edge, then rode back.

#29894 (a carry of #28894), which landed while this was in flight, made the default sidebar.slideHoldsContent: false. The content now follows the card on every frame, so the default no longer snaps. This PR keeps that default.

Fix

The hold, still in Debug Settings, now takes the card's landing width when the slide starts. It pins the content to the card's far edge, which doesn't move (ContentHold.pinsTrailingEdge: the trailing edge for a left sidebar, the leading edge for a right one). The content is laid out once, on the first frame, at its final size and place, and clearing the hold at the landing changes nothing. That's still one layout per toggle, so the GPU audit's saving holds when it's on. A reversal mid-flight lays out once more, at the new landing width. The rail style, the sidebar's own animation and the default path are unchanged.

Performance

Per-frame trace from SidebarToggleContentTraceTests: real display ticks, a 1100x700 window, a 300 pt left sidebar. Each sample is the content's frame in window coordinates on one display frame. Steps: red 8ee6ecb4d5e2d4e91b046149 at 0a4c827a92f (base plus tests), green d5b6044761f6125b5e255ae5 at c9f93278319.

Metric (one toggle)BeforeAfterDeltaHarness
Hold on, show: content frame changes15 (rides the edge 6 → 47 → 79 → … → 306 at 1088 wide, then snaps to 788)1 (306+788 from frame 1)−14trace, steps above
Hold on, hide: content frame changes11 (241 → 204 → … → 6 at 1088 wide)1 (6+1088 from frame 1)−10same
Hold on: content layouts per toggle110same
Default (follow), show: frames that turn back toward the start0 (13 steps 6+1088 → 71+1023 → … → 306+788)0 (14 steps, 42+1052 → … → 306+788)0same
Default (follow), hide: frames that turn back toward the start0 (11 steps)0 (11 steps, 249+845 → … → 6+1088)0same
Default (follow): changes after the slide lands000same

The default is #29894's behaviour on both sides. The trace pins it: the content's minX, maxX and width only move toward where they land, and nothing changes after the landing. It does resize the content on every frame, the cost the GPU audit measured at 6.7–7.4 ms of main thread a frame. The hold avoids that.

Tests

Red first: 0a4c827a92f is the base's production code plus the tests; the fix is c9f93278319.

cmux-ci run --class light --script scripts/ci/package-test-lane.sh --display --env CMUX_TEST_REQUIRE_GUI=1 --arg=suite --arg=Packages/macOS/CmuxNext --arg=SidebarSlideHoldsContentTests,SidebarToggleContentTraceTests,SidebarToggleSettlementTests

  • Red at 0a4c827a92f: SidebarSlideHoldsContentTests fails 3 of 9:

    • hide: the content moved 208 pt on screen;
    • show: the content stayed 988 wide instead of its 780 landing width, and the landing changed its frame;
    • turnaround: also fails.

    SidebarToggleContentTraceTests fails for the hold, with 15 and 11 frame changes; its default cases pass.

  • Green at c9f93278319: SidebarSlideHoldsContentTests passes 9 of 9, SidebarToggleContentTraceTests passes all 4 cases (hold and default, show and hide), and SidebarToggleSettlementTests (#29894) passes.

Observed / Not run

  • Observed: the runs above, on fleet display workers (Xcode 26.6, macOS SDK 26.5). The pre-push gate passes. An independent Opus review at c9f93278319 returned SHIP.
  • Not run: a full app session with a live transcript, terminal and Chromium page. The trace uses a plain content view. Only a left sidebar was traced; the right sidebar is covered by unit tests.

No capture: the change is motion and opt-in; the trace is the evidence.

Prior art: #29894 / #28894 (the default), #24342 (introduced the hold), #18840 (classic sidebar), #21125 (right-sidebar pane widths). Owner note in plans/cmux-next/sidebar-owner.md.

Changelog

Fixed: With "Hold content size in sidebar slide" on (Debug Settings), showing or hiding the sidebar no longer shifts the rest of the window and snaps it back.

🤖 Generated with Claude Code

Agent session: agent session