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)
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
SharedEdgeClamprecords 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.bouncesubtle). 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
followorpushcan use those shares). The clamp's remaining spring then lands as an ordinary leftover. ScreenContentViewdoes not grow (it is over its type budget on the base): the frame hook is one line, and theplace()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 inanchorPushesSplitToMinimum,pointerPassesTheClampBeforeItLandsandpointerChasesTheClamp; column B inpointerChasesTheColumnEdgeClamp.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.
| Run | Ref | Step | Result |
|---|---|---|---|
| red: the minimum watch, tests only on feat | d87a719d2c4 | cd5a0cc7899c1841b370964e | anchorPushesSplitToMinimum 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 push | cdc5850c284 | f43ef7127f5494c1f25277d1 | 29 suites pass, 18/18 cases with 0 misses |
| red: the push bound (Codex round 1), tests only | 7991530face | d53f2f3d22288d1fa23bb297 | anAnchorPastItsTargetDoesNotCarryTheLinePastTheClamp 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 hold | e40ebc5f267 | bbe64d66853f4f069f044f68 | 30 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 suites | e40ebc5f267 | 09b8af2924c630a9673b2134 | 31 pass; OutOfSightFramesTests skips (it needs a GUI) |
display fleet run (gui-session-v1 --display) | d8ac2f4010b | 72e13ec087e99d60d6adb186 | ci-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 suites | 588c9c01929 | 43736230090993ffbd83df57 | 30 suites pass, 18/18 cases with 0 misses; PaneMotionWorkTests skips (a bench) |
| final head: 32 more layout suites | 588c9c01929 | dc837c6e0f2ad0f44113ac2a | 31 pass; OutOfSightFramesTests skips (it needs a GUI) |
final head, display fleet run (gui-session-v1 --display) | 588c9c01929 | 7a9d724a4e040e95b2477721 | ci-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 ine40ebc5f267. - 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
ef400020f122ON (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 atd6e39f42a8b(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 atd8ac2f4010b, carried to588c9c01929), 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.