The lane-resize release gets its own hold — the strip stops two-stepping through the stale snapshot

LaneWidthHold: after the release the session's frozen standard and the
WRITTEN unit count keep governing the strip until a landed snapshot
carries that width back — dividing the already-grown window by the
stale unit total in between was the visible two-step. A deliberately
separate type from CommittedHold: that hold stands in for an
arrangement and any landing retires it; this one stands in for a value
and only a landing that carries it will do. Echo reads through
LaneLayoutMath.displayUnits so the width-1 key-removal case compares
right; the watch rides landedReloads so a value-equal echo still
answers; width writes now return whether bytes reached disk so a
refused or no-op write dissolves the hold instead of arming it; the
1500ms timeout family covers the rest.

Drag-perf card 231e3693.

Claude-Session: https://claude.ai/code/session_01CqjXB7ASoWtbyoGod68k97
This commit is contained in:
2026-08-01 19:33:33 -04:00
parent f6105d4389
commit 409f430813
6 changed files with 591 additions and 32 deletions
+2
View File
@@ -191,3 +191,5 @@ The board strip itself has nothing to autoscroll: every lane is always visible (
## Adjacent interaction: the lane resize drag (not a drag session)
Dragging a lane's trailing edge resizes it between whole unit counts — see `LaneResizeSession.swift` and `LaneLayoutMath`. It deliberately lives OUTSIDE the drag-session machinery above: the handle is a plain `DragGesture`, carries no drop target, and refuses to start while a card/lane session is in flight. Its layout trick inverts this document's premise: instead of reflowing siblings around a shadow, the session freezes the strip's standard width and resizes the *window* by one standard-plus-gap per snap tick, so every other lane keeps its exact pixels and the release settles the dragged lane into a slot that's already in place. Snapping is asymmetric ("shadow leads"): tick up the instant the live edge clears the inter-lane gap; tick down only after retreating 10pt back into it — the 10pt re-entry band is the only hysteresis, cousin to the dead-region hold above.
It does borrow one thing from the machinery it lives outside: **the release gets its own hold** (`LaneWidthHold`). The release writes the new unit count, and that write takes the one-way flow's round trip, so a session that cleared there would hand the strip back to a standard divided from the already-grown window by the snapshot's still-stale unit total — a visible two-step, once into the wrong arrangement and again when the echo lands. So the frozen standard and the *written* unit count keep governing until the snapshot carries that width, with the same timeout-and-dissolve guarantee as the committed-overlay hold. The condition is the only real difference, and it follows from the subject: that hold stands in for an arrangement and any landing on the board retires it, while this one stands in for a value and only a landing that actually carries it will do.