Realign search, clipboard, and lane-hover code with the second batch

Creation now clears the search by mechanism, not gesture: one seam
(noteUserCreation) states 04's rule once, called from the placeholder
funnel, paste — cards and lanes, after the staleness guard so a stale
paste clears nothing — and Finder file-drop creation; the attach path
deliberately doesn't clear, and cross-board arrivals and New Lane stay
outside the seam (a transfer isn't creation; a lane can't be born
invisible). An open inline rename now survives the filter hiding its
card: the model already kept the editor, but the field renders in the
card's slot, so renderedCards keeps the renaming card's slot exactly as
long as the editor is open — the query stands throughout, and commit or
Escape lets the predicate apply in the same pass. Verified conformant
and newly pinned: query-emptied lanes keep their slot with a 0 badge,
pasteboard staleness (takeover before paste and mid-staging both no-op),
out-transition reachability and the strip pre-divide hold by
construction with comments citing their rulings.

Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
2026-07-28 08:45:12 -04:00
parent 524488122f
commit 5c0c0e5619
9 changed files with 423 additions and 21 deletions
+18 -1
View File
@@ -505,7 +505,8 @@ struct BoardView: View {
/// simply divides across one more unit and every lane compresses.
/// - a **cross-board lane arrival**'s units while its shadow hovers here, by the same rule read
/// from the destination's side: the shadow occupies its units, and the strip has to make room
/// for them or the shadow would be drawn at a width the lane will not have.
/// for them or the shadow would be drawn at a width the lane will not have
/// (`arrivingLaneUnits`).
private func standardWidth(stripWidth: CGFloat) -> CGFloat {
if resize.isActive { return resize.standard }
var units = LaneLayoutMath.totalUnits(of: liveLanes, trashUnits: isTrashVisible ? 1 : 0)
@@ -519,6 +520,22 @@ struct BoardView: View {
/// The units a cross-board lane run would add to this strip while its shadow is proposed here;
/// zero for a within-board drag, whose lanes are already counted.
///
/// **A cross-board lane arrival pre-divides the destination strip during hover**
/// (04-interactions.md Drag and drop, settled): "while a foreign lane drag proposes into a
/// board, the destination's standard width is computed with the arriving run's units included, so
/// the shadow draws at the width the lane will actually take without this it overflows the
/// strip (the pathfinder's `stripWidthUnits`)". The units are the run's own, frozen at pickup by
/// the *source* board (`DragSession.laneUnits`) both boards are open in this app, so nothing
/// has to cross the pasteboard for the destination to know how wide its visitor is.
///
/// Gated on a live proposal *on this board* rather than on hover alone, which is the same bullet's
/// accepted residue: "the first entry samples the un-widened standard for one frame before
/// hysteresis settles accepted, imperceptible".
///
/// One number, two readers: body evaluation draws the strip and the shadow with it
/// (`standardWidth`), and the drop delegates retarget against it through `dropContext.standard`,
/// so the zones can never disagree with what is on screen.
private var arrivingLaneUnits: Int {
let session = appModel.dragSession
guard session.isDraggingLanes,