cmux NEXT: a divider held in a clamp keeps the pane at its minimum while the neighbouring column still grows (FRAME OWNER S2B rule D follow-up)

Merged
LA
1d ago
+188-21

FRAME OWNER REFACTOR S2B, a follow-up to rule D (#35775): when a drag is held in a clamp while the anchor still springs toward the line, the pane between them no longer shows under its minimum.

This builds on #35775 (merged 2026-11-04) and takes nothing back: it adds the clamp's push and the plain-mode hold to SharedEdgeClamp, and keeps every rule #35775 added.

Why

  • pixel-perfect's clamp film of #35775's final build (round ab-s2bdc2-ddf0ac4, ON only): b1 showed down to 196.0 pt against its 200 pt minimum, from 40 ms to about 900 ms after the press at x0.125 (about 0.11 s at x1). OFF never went under 200 pt. #35775's body states this as a known limit, with this fix as its follow-up.
  • Cause: the press holds the line where it is drawn while the anchor (the edge across the pane, here A's edge) keeps its spring toward it. The line springs to its clamp (the anchor's rest place plus the minimum) more slowly than the anchor comes, so the pane between them shrinks under its minimum until the spring catches up.

Fix

  • SharedEdgeClamp records the anchor at the press and the floor (how far the line's leftover may come behind the anchor's: 0, or the press's value when the pane already showed under its minimum there).
    • The anchor is a pane edge that does not move with the line, of a pane whose other edge does, at the anchor's rest place when the line moves away from it, or at a split's far edge when the line moves toward its anchor.
    • A column edge at its widest share (nothing behind the line) gets no anchor.
  • push(on:) runs once per frame, after the springs step and before the frame shows (ScreenContentView.step, one line). Where the anchor came closer than the floor allows, every edge that moves with the line takes the difference (each by its share, as the clamp's other moves do), and the line takes the anchor's speed. From then on the pane stays at its minimum while the anchor pushes, and both land together.
  • The push never takes the line past its clamp (a bouncy anchor that overshoots its target, ui.animations.bounce subtle). At the clamp the line keeps its own speed.
  • The press measures each edge's share in the plain mode, so a clamp hold now exists in the plain mode only: a press with Option or Command held records none, and pressing either during the drag ends it (before follow or push can use those shares). The clamp's remaining spring then lands as an ordinary leftover.
  • ScreenContentView does not grow (it is over its type budget on the base): the frame hook is one line, and the place() doc comment is one line shorter.

Changelog

  • Fixed: holding a divider still while a neighbouring column grows into the pane's minimum no longer shows the pane narrower than its minimum for a moment.

Frames

  • Frames: divider, column edge and panes. Before, in a clamp the pane between the anchor and the held line showed under its minimum (196 pt for 200 pt) for about 0.9 s at x0.125. After, the pane shows at its minimum or more in every frame: once the anchor reaches it, the anchor pushes the line, and the two move as one to the clamp. In every other drag nothing changes: the push runs only while a clamp hold exists.
  • No content stops following its pane.

Tests

On the fleet: cmux-ci run --class light --script scripts/ci/run-swift-testing-suites.sh --arg=Packages/macOS/CmuxNext --env CMUX_SWIFT_TEST_SUITES=SharedEdgeDuringSpringTests,SharedEdgeClampPushTests

  • SharedEdgeDuringSpringTests: the rig's new minimum watch keeps a span (from one pane's left edge to another's right edge, as the screen shows it) at its minimum or more in every frame. The four clamp cases set it: b1 in anchorPushesSplitToMinimum, pointerPassesTheClampBeforeItLands and pointerChasesTheClamp; column B in pointerChasesTheColumnEdgeClamp.
  • SharedEdgeClampPushTests (new, springs set by hand on a clamp made from its parts):
    • an anchor 2 pt past its target may take the line to its clamp, never past it, and the line keeps its own speed there;
    • an anchor short of its target pushes the line to the floor with the anchor's speed (b1 then has no leftover);
    • a line ahead of its anchor is not pushed.

Commits are red first.

RunRefStepResult
red: the minimum watch, tests only on featd87a719d2c4cd5a0cc7899c1841b370964eanchorPushesSplitToMinimum fails, 13 misses: "still frame 1: b1...b1 shows 198.00 pt wide, under its minimum 200.00 pt", then 197.00 pt for frames 2-5 and 198 to 199 pt to frame 12. The other 17 cases pass.
green: the pushcdc5850c284f43ef7127f5494c1f25277d129 suites pass, 18/18 cases with 0 misses
red: the push bound (Codex round 1), tests only7991530faced53f2f3d22288d1fa23bb297anAnchorPastItsTargetDoesNotCarryTheLinePastTheClamp fails: the line 2.0 pt past the clamp at 30.0 pt/s (the anchor's speed); the other 2 push tests pass
green: the bound and the plain-mode holde40ebc5f267bbe64d66853f4f069f044f6830 suites pass (SharedEdgeClampPushTests 3/3, SharedEdgeDuringSpringTests 18/18 cases with 0 misses, SharedEdgeCatchFallbackTests, SharedEdgeCameraLimitTests, DividerDragFollowsPointerTests and the 26 S2 suites; PaneMotionWorkTests skips, a bench)
32 more layout suitese40ebc5f26709b8af2924c630a9673b213431 pass; OutOfSightFramesTests skips (it needs a GUI)
display fleet run (gui-session-v1 --display)d8ac2f4010b72e13ec087e99d60d6adb186ci-step s2b-clampmin-display-d8ac2f4010b-v1 exited 0: 16/16 suites pass (SharedEdgeClampPushTests, the 3 SharedEdge suites, DividerDragFollowsPointerTests and the 11 divider-drag and split-motion suites), 59 tests, 0 skipped; 18/18 cases, 0 misses
final head (feat 042fcc83b15 merged): the push and SharedEdge suites + the 26 S2 suites588c9c0192943736230090993ffbd83df5730 suites pass, 18/18 cases with 0 misses; PaneMotionWorkTests skips (a bench)
final head: 32 more layout suites588c9c01929dc837c6e0f2ad0f44113ac2a31 pass; OutOfSightFramesTests skips (it needs a GUI)
final head, display fleet run (gui-session-v1 --display)588c9c019297a9d724a4e040e95b2477721ci-step s2b-clampmin-display-588c9c01929-v2 exited 0: 16/16 suites pass (SharedEdgeClampPushTests, the 3 SharedEdge suites, DividerDragFollowsPointerTests and the 11 divider-drag and split-motion suites), 59 tests, 0 skipped; 18/18 cases, 0 misses. The first display run at this head (2ff367f37c3d6070185f8309) failed one timing test outside this change: SplitSpringMotionTests.fiveRapidSplitsAreOneMotion, slowest frame 11.66 ms against its 8.33 ms bound (no clamp, no drag in that test).

d8ac2f4010b is an empty commit that corrects 7991530face's message (it guessed 20 pt/s; the fleet showed 30.0 pt/s).

Typing

Nothing runs per key or per layout pass outside a drag. push(on:) runs once per frame tick only while a drag holds a clamp (two dictionary reads and a compare; it writes frames only when the anchor pushes). Without a clamp hold, the hook is one optional check.

Review

  • Codex round 1 at cdc5850c284: FIX, two should-fixes: the push was not bounded by the line's leftover, and a modifier change could reuse the press's shares. Both are fixed in e40ebc5f267.
  • Codex round 2 at d8ac2f4010b: SHIP (receipt below).

Films

pixel-perfect, round ab-s2bmin-22 on build dc5e8866 (head d8ac2f4010b), cmux22, x0.125, OFF and ON (STATUS 13487): PASS.

  • clamp-2x ON: b1 is 200.00 pt or more in every frame, the press moves nothing, and x ends exactly at the clamp.
  • clamp-chase-2x ON: b1 is 200.00 pt or more, and x ends at the pointer.
  • Against ef400020f122 ON (feat before this fix), the m14 judge: clamp-2x fails 173 against 180 frames, scaled 0 against 12, worst 4 against 16 px; the chase 72 against 71 (the same class). No new red.
  • The scaled frames this removes were real: b1's terminal at its 200 pt minimum was wider than its 195.5 pt pane and covered A's edge.
  • The films count for the final head 588c9c01929: that commit only merges feat, and in the clamp path feat adds only the switch delete (#35908), which pixel-perfect filmed at d6e39f42a8b (pixel-perfect's decision: no extra film).

Observed / Not run

  • Observed: the fleet runs above (green at the final head 588c9c01929), the prepush gate, check-no-godfiles and check-motion, two Codex rounds (SHIP at d8ac2f4010b, carried to 588c9c01929), and pixel-perfect's films.
  • Not tested: the push with the line moving left (free = -1) and a modifier change during a clamp. Codex round 2 named these as gaps; it found the code right for both signs.

No capture: frame timing during motion; the clamp film from the refactor owner is the evidence.