feat(runtime): preserve native column resizing
Merged
QE
5h ago+780+780+012345678901234567890123456789-87-87-01234567890123456789
Summary
Dragging a tiled window's side edge sets its column width, and the width stays. The overlay stretches the window and its neighbours with the pointer. Hidden windows wait at the side edge again. Closes #94.
on(left mouse down on a tiled window)
+ record the press location (side-edge band: 3 pt inside, 8 pt outside)
on(drag)
- wait for a snapshot to show the resize, then carry the window
+ stretch the overlay on the first 2 pt along a side edge, or the first WindowServer resize
+ clamp the stretch to the window's min/max width, on the ribbon slot
+ pause desktop sync while stretching (no CGWindowList lock contention)
on(release)
read the released frame from the WindowServer
learn width = released frame + gaps, ignoring the window's own bounds
- drop the window's width bounds
+ drop only bounds the released frame contradicts, clear learned rounding
+ a dragged left edge stays where it was released (the ribbon before it scrolls)
+ a late frame matching a pending Defi write is not learned
parkedFrame(hidden window)
- bottom-right corner, then other corners and edges
+ nearest side edge at the window's own height, then the other side edge,
+ then corners and the bottom edge, first spot clear of other displays
Evidence
- Before: while dragging an edge, 5 to 10 freezes of about 500 ms over 12 slow drags. Neighbours sometimes stood still. Windows from other workspaces crossed the screen on release. Hidden windows came in diagonally from the bottom-right corner.
After: 0 freezes over 12 slow drags. Neighbours follow from the first event. Other workspaces stay parked on release (anotherWorkspacesParkedWindowStaysPutWhenTheRibbonMoves). Installed build: hidden windows at x=1511 and x=-375, y=37, withparkingRepairs=0. python3 script/verify.py localpasses on b7c582c (build, Swift tests, workflow tests).- New pure tests, among them:
Stacked window resized past its maximum to the column width drops the maximum: fails before b7c582c, passes after.Mouse resize forgets the learned width rounding,A request just under the window's minimum is a clamp, not rounding.ParkingTests: side-edge table, right-half window, displays on both sides fall back to the bottom edge.
- Not covered: the two-display lane and the animations-off release path are tested only by pure tests. Desktop E2E did not run on the final head.
Merge Danger
Door: two-way
Revertible commits, with no stored data or config changes. The parking change also reverses #152's corner parking. AGENTS.md is updated to match.
Blast Radius: wide
Every hidden window moves to a new parking spot once, after the upgrade. A regression would show as a resize that snaps back, a stretch that lags or starts during a reorder drag, or a parked window showing on a side edge or on another display.
[!NOTE]
Preserve native column resizing through gesture release, width learning, and parking changes
- Lets the daemon detect a mouse release even when the native resize observation is missing, by tracking that the held gesture changed the window frame in
Daemon.mouseGestureChangedWindowFrame and settling against the latest WindowServer frame.
- Skips desktop sync while a live border gesture stretches the proxy, and caps the sync timer at min(display refresh, 120 Hz).
- Reworks width learning in
learnTiledWindowWidth to keep valid min/max bounds, drop only bounds the observed frame violates, clear stale width rounding, and adjust workspace scroll offset from the released window's left edge. mouseGestureWidthLearningFrame now prefers a release frame and ignores frames matching recent internal Defi writes.
- Adds
AXFrameCoordinator.frameMatchesPendingInternalWrite to identify frames that match an in-flight internal write, and changes acceptTiledWindowFrame to not count minimum-width clamps as width-rounding overshoot.
- Moves parking to side-edge candidates at the window's current height, and lets
cycleWidth step from off-preset pixel widths when a viewport width is supplied.
- Behavioral Change:
parkedFrame in Parking.swift returns side-edge positions instead of bottom/corner fallbacks; nearestPresetIndex in WorkspaceScrolling.swift is removed. Adds a manual verification guide at column-resizing.md.
Macroscope summarized b7c582c.