The empty provider was never load-bearing — the dragless layer frees the rubber band

Measured on real events 2026-08-07, correcting the 2026-08-06 hosted
finding: a bare count-1 tap on LaneView's empty-space layer fires in
~1-3 ms with no drag source at all — the hold that made the empty
.onDrag look necessary was the sterile NSApp.postEvent stream
over-disambiguating. And the provider was actively harmful: even an
empty drag source claims the mouse-drag at threshold, starving the
marquee's simultaneous DragGesture after one sample — the band froze
and the mouseUp never arrived. The layer goes dragless; drags from
empty space belong wholly to MarqueeControl. PointerClick's and the
layer's comments retell the corrected story. Alongside: openCard is
typed @MainActor throughout, which makes the closure Sendable and
lets CardFaceRole carry it under CardFaceView's nonisolated ==.

Claude-Session: https://claude.ai/code/session_014PtZdPwqZuqEDLc6wZMtEy
This commit is contained in:
2026-08-07 12:55:54 -04:00
parent b0ffff1aa1
commit 8aefaf23ce
9 changed files with 42 additions and 36 deletions
+12 -9
View File
@@ -31,15 +31,18 @@ extension ClickModifier {
/// SwiftUI's `TapGesture` hands its handler nothing about the event.
///
/// **This is how a surface without a drag source gets a double-click meaning** (the 2026-08-06
/// click-latency fix). A second tap recogniser is never the way: a sequential
/// `.onTapGesture(count: 2)` makes every single click on its subtree wait out the system
/// double-click interval and on macOS even a *simultaneous* two-tap recogniser holds primary
/// clicks on views that carry no `.onDrag`. (A drag source forces immediate event delivery, which
/// is why the card faces, the lane header and the trash rows `CardFaceView`'s simultaneous
/// arrangement stay instant; `LaneView`'s empty-space layer measurably does not.) A single
/// `.onTapGesture` fires once per click of a run, so branching on this count expresses
/// "first click selects, second creates" Finder's cadence with exactly one recogniser and
/// nothing to disambiguate.
/// click-latency fix). A second tap recogniser is never the way: a *multi-click* recogniser on a
/// dragless subtree a sequential `.onTapGesture(count: 2)` or even a simultaneous two-tap
/// makes macOS hold every primary click on that subtree pending disambiguation for the system
/// double-click interval. (A real `.onDrag` forces immediate delivery, which is why the card
/// faces, the lane header and the trash rows `CardFaceView`'s simultaneous arrangement can
/// carry one and stay instant. A lone count-1 tap needs no such help: measured on real events
/// 2026-08-07, `LaneView`'s dragless empty-space layer fires in ~13 ms there is nothing to
/// disambiguate. And an `.onDrag` must never be added there *for* delivery: even an
/// empty-provider drag source claims drags outright and kills the rubber band's simultaneous
/// `DragGesture`.) A single `.onTapGesture` fires once per click of a run, so branching on this
/// count expresses "first click selects, second creates" Finder's cadence with exactly one
/// recogniser and nothing to disambiguate.
enum PointerClick {
/// The `clickCount` of the click being handled: 1 for a lone click or a run's first, 2 for