Land Finder file drops positionally, header release topmost

04's settled clauses were mostly shipped already — the create landing
resolved through DropSlotMath.cardSlot with one nominal shadow per
importable file — but a release on the lane header fell through to the
card zones, which clamp inward, so a scrolled lane could propose behind
the header stripe. FileDropZones now folds header, attach hit-test, and
card-slot resolution into one pure seam asked in that order, the header
answering topmost per the ruling; lane headers register their frames
for it. FinderDrop.shadowCount names the floor-at-one rule. New tests
pin the header boundary, a differential against cardSlot's own zones
(same zones, not similar), and a store-level differential proving a
file landing takes the very ranks a card move there takes.

Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
2026-07-28 08:28:28 -04:00
parent 7be9bb2345
commit 524488122f
7 changed files with 392 additions and 72 deletions
+9
View File
@@ -132,6 +132,15 @@ struct LaneView: View {
)
}
.onDrag(startLaneDrag, preview: { dragReplica })
// **The header is the file drop's topmost position** (04-interactions.md Drag and drop,
// settled 2026-07-28: "a release on the lane header resolves to the topmost position
// forgiving beats a dead stripe"). Its frame is registered rather than derived from the
// grid's because the grid is scroll-view *content*: scrolled down, its top edge climbs
// past the header and would stop being a boundary at all. The bar itself never moves.
.onGeometryChange(for: CGRect.self) { $0.frame(in: .global) } action: { frame in
drops.registry.update(header: frame, for: lane.id)
}
.onDisappear { drops.registry.removeHeader(lane.id) }
.overlay(alignment: .trailing) { newCardButton }
.contextMenu { laneMenu }
// The lane's half of the Style popover. Anchored on the header because that is the