The pickup seeds its own slot — the lift and the shadow become one transaction

"At drag start it replaces the item's original space" is now true from the
very first frame: begin takes the run's own resting slot as a seed, set in
the same transaction that lifts the run out. Without it the vacated gap
closed un-animated and sprang back open at the first dropUpdated — a
shuffle carrying no information. The seed bypasses propose deliberately (a
pickup is not a new landing spot, so no alignment tick), and the first real
sample's re-propose of the same slot is the early-out's ordinary silence. A
⌥-pickup seeds nothing — a copy's resting layout keeps the originals in
place, so there is no vacated space to hold. Card pickups compute the seed
through the same LaneView.rendered rules the retargets count in; lane
pickups seed their strip slot the same way. DRAG-REORDER.md's own-slot
paragraph records the rule, and SeededPickupTests pins all four corners.

Claude-Session: https://claude.ai/code/session_014PtZdPwqZuqEDLc6wZMtEy
This commit is contained in:
2026-08-07 11:22:26 -04:00
parent eef0a4539f
commit f126614b56
5 changed files with 176 additions and 9 deletions
+11 -1
View File
@@ -587,7 +587,17 @@ struct LaneView: View, Equatable {
// The dragged items' own sizes, frozen at drag start the one thing that is
// (03-board-ui.md § Motion).
units: members.map { LaneLayoutMath.displayUnits(of: $0) },
source: store
source: store,
// The strip's own-slot seed (`DragSession.begin`): the grabbed lane's position among
// the lanes with the dragged run removed the strip proposal's own index space. A
// within-board lane drag never resolves to `.copy` ( is ignored there), so unlike the
// card seed this one carries no modifier clause.
seed: DropTarget(
boardRoot: store.rootKey,
container: .strip,
index: store.snapshot.lanes.prefix { $0.id != lane.id }
.count { !ids.contains($0.id) }
)
)
return payload.itemProvider()
}