Every board root keys once at its store — the hover path stops walking the filesystem
`DragLocality.isSameBoard` resolved symlinks on both URLs, which stats every path component. It ran five-plus times per `dropUpdated` and again per lane per body evaluation through `renderedCards` → `hiddenMembers` — order of 50–100 stat calls per mouse-move, and worse on iCloud-backed paths. A pickup stall sample had it on ~47 of 943 stacks. `BoardRootKey` mints that canonical spelling once, where the root is, and every locality comparison downstream is an `==` on two strings. The drag carriers hold keys rather than URLs, so re-deriving under the cursor is no longer expressible. `rootURL` keeps the user's spelling — the folder name is the display-name fallback, and canonicalizing there would visibly rename a board opened through a pin. The key follows the folder through `relocate(to:)`: a key frozen at open would collide with a new board opened at the vacated path, and two boards comparing as one is a cross-board drag silently behaving as a move. Claude-Session: https://claude.ai/code/session_01CqjXB7ASoWtbyoGod68k97
This commit is contained in:
@@ -147,7 +147,7 @@ struct BoardDropContext {
|
||||
/// disagree.
|
||||
func revalidateProposal() {
|
||||
guard let proposal = session.proposal,
|
||||
DragLocality.isSameBoard(proposal.boardRoot, store.rootURL),
|
||||
proposal.boardRoot == store.rootKey,
|
||||
let laneID = proposal.laneID
|
||||
else { return }
|
||||
guard !store.snapshot.lanes.contains(where: { $0.id == laneID }) else { return }
|
||||
@@ -179,7 +179,7 @@ struct BoardDropContext {
|
||||
/// `receiveLanes` keep the index space they always had.
|
||||
func retargetLanes() {
|
||||
guard session.isDraggingLanes, let cursor = stripCursor() else { return }
|
||||
let hidden = session.hiddenMembers(onBoardRooted: store.rootURL)
|
||||
let hidden = session.hiddenMembers(onBoardRooted: store.rootKey)
|
||||
let resting = store.snapshot.lanes.filter { !hidden.contains($0.id) }
|
||||
let restingUnits = resting.map { LaneLayoutMath.displayUnits(of: $0) }
|
||||
let slot = DropSlotMath.laneSlot(
|
||||
@@ -188,11 +188,11 @@ struct BoardDropContext {
|
||||
draggedUnits: session.laneUnits,
|
||||
standard: standard(),
|
||||
gap: gap,
|
||||
current: session.stripProposal(onBoardRooted: store.rootURL)
|
||||
current: session.stripProposal(onBoardRooted: store.rootKey)
|
||||
)
|
||||
guard let slot else { return } // a dead region: hold the current proposal
|
||||
let index = min(max(0, slot), restingUnits.count)
|
||||
session.propose(DropTarget(boardRoot: store.rootURL, container: .strip, index: index))
|
||||
session.propose(DropTarget(boardRoot: store.rootKey, container: .strip, index: index))
|
||||
}
|
||||
|
||||
/// Where a **card** session would land in `laneID`'s masonry.
|
||||
@@ -215,7 +215,7 @@ struct BoardDropContext {
|
||||
}
|
||||
guard let grid = registry.grids[laneID] else { return }
|
||||
|
||||
let hidden = session.hiddenMembers(onBoardRooted: store.rootURL)
|
||||
let hidden = session.hiddenMembers(onBoardRooted: store.rootKey)
|
||||
let rendered = lane.cards.filter { !hidden.contains($0.id) }
|
||||
let heights = rendered.map { registry.heights[$0.id] ?? LaneDropRegistry.nominalCardHeight }
|
||||
let placement = MasonryPlacement(
|
||||
@@ -232,10 +232,10 @@ struct BoardDropContext {
|
||||
// The run's footprint at the landing spot: the first dragged card's frozen height, which
|
||||
// is the trigger rect the cursor is over (the rest stack below it).
|
||||
draggedHeight: session.cardHeights.first ?? LaneDropRegistry.nominalCardHeight,
|
||||
current: session.laneProposal(onBoardRooted: store.rootURL, laneID: laneID)
|
||||
current: session.laneProposal(onBoardRooted: store.rootKey, laneID: laneID)
|
||||
)
|
||||
guard let slot else { return } // a dead region: hold
|
||||
session.propose(DropTarget(boardRoot: store.rootURL, container: .lane(laneID), index: slot))
|
||||
session.propose(DropTarget(boardRoot: store.rootKey, container: .lane(laneID), index: slot))
|
||||
}
|
||||
|
||||
/// The strip's fall-through for card sessions: which lane is under the cursor, analytically.
|
||||
@@ -280,8 +280,8 @@ struct BoardDropContext {
|
||||
return TrashDrop.accepts(
|
||||
kind: session.kind,
|
||||
container: session.container,
|
||||
isWithinBoard: DragLocality.isSameBoard(sourceRoot, store.rootURL),
|
||||
operation: session.resolveOperation(destinationRoot: store.rootURL),
|
||||
isWithinBoard: sourceRoot == store.rootKey,
|
||||
operation: session.resolveOperation(destinationRoot: store.rootKey),
|
||||
isTrashShown: store.transient.isTrashVisible,
|
||||
acceptsMutations: store.acceptsBoardMutations
|
||||
)
|
||||
@@ -304,7 +304,7 @@ struct BoardDropContext {
|
||||
return
|
||||
}
|
||||
session.propose(DropTarget(
|
||||
boardRoot: store.rootURL,
|
||||
boardRoot: store.rootKey,
|
||||
container: .trash,
|
||||
index: TrashDrop.landingIndex
|
||||
))
|
||||
@@ -390,7 +390,7 @@ struct BoardDropContext {
|
||||
placement: placement,
|
||||
heights: heights,
|
||||
nominalHeight: LaneDropRegistry.nominalCardHeight,
|
||||
current: session.fileLaneProposal(onBoardRooted: store.rootURL, laneID: laneID)?.index
|
||||
current: session.fileLaneProposal(onBoardRooted: store.rootKey, laneID: laneID)?.index
|
||||
)
|
||||
|
||||
switch landing {
|
||||
@@ -399,13 +399,13 @@ struct BoardDropContext {
|
||||
case let .attach(index):
|
||||
guard rendered.indices.contains(index) else { return }
|
||||
session.proposeFile(FileDropTarget(
|
||||
boardRoot: store.rootURL,
|
||||
boardRoot: store.rootKey,
|
||||
landing: .attach(cardID: rendered[index].id),
|
||||
fileCount: count
|
||||
))
|
||||
case let .create(index):
|
||||
session.proposeFile(FileDropTarget(
|
||||
boardRoot: store.rootURL,
|
||||
boardRoot: store.rootKey,
|
||||
landing: .create(laneID: laneID, index: index),
|
||||
fileCount: count
|
||||
))
|
||||
@@ -463,7 +463,7 @@ struct BoardDropContext {
|
||||
func commitFileDrop(_ info: DropInfo) -> Bool {
|
||||
guard acceptsFileDrops,
|
||||
let target = session.fileTarget,
|
||||
DragLocality.isSameBoard(target.boardRoot, store.rootURL)
|
||||
target.boardRoot == store.rootKey
|
||||
else {
|
||||
session.proposeFile(nil)
|
||||
return false
|
||||
@@ -503,9 +503,9 @@ struct BoardDropContext {
|
||||
/// live as the cursor crosses a board boundary (04-interactions.md ▸ Drag and drop).
|
||||
func dropProposal() -> DropProposal {
|
||||
guard let proposal = session.proposal,
|
||||
DragLocality.isSameBoard(proposal.boardRoot, store.rootURL)
|
||||
proposal.boardRoot == store.rootKey
|
||||
else { return DropProposal(operation: .cancel) }
|
||||
let operation = session.resolveOperation(destinationRoot: store.rootURL)
|
||||
let operation = session.resolveOperation(destinationRoot: store.rootKey)
|
||||
return DropProposal(operation: operation == .copy ? .copy : .move)
|
||||
}
|
||||
|
||||
@@ -541,7 +541,7 @@ struct BoardDropContext {
|
||||
}
|
||||
revalidateProposal()
|
||||
guard let target = session.proposal,
|
||||
DragLocality.isSameBoard(target.boardRoot, store.rootURL)
|
||||
target.boardRoot == store.rootKey
|
||||
else {
|
||||
cancelDrop()
|
||||
return false
|
||||
@@ -570,8 +570,8 @@ struct BoardDropContext {
|
||||
}
|
||||
let ids = survivors.map { session.members[$0] }
|
||||
let folders = survivors.map { session.folders[$0] }
|
||||
let within = DragLocality.isSameBoard(sourceRoot, store.rootURL)
|
||||
let operation = session.resolveOperation(destinationRoot: store.rootURL)
|
||||
let within = sourceRoot == store.rootKey
|
||||
let operation = session.resolveOperation(destinationRoot: store.rootKey)
|
||||
|
||||
switch kind {
|
||||
case .lanes:
|
||||
|
||||
Reference in New Issue
Block a user