Build the trash quasi-lane

Deletion becomes a two-stage, Finder-style story. File > Delete and
plain Backspace tombstone the live selection; View > Show Trash (no
chord — Shift-Cmd-T stays with the system's tab bar) reveals the
quasi-lane: trailing, one fixed width unit consumed only while shown,
hatched dimmed header, count badge, no new-card button, exempt from
resize and reorder alike. Its contents are a pure view over the
snapshot — the deterministic sort (deleted newest first, folder-name
ties, unparseable stamps oldest) interleaves card rows with a
tombstoned lane's single entry, whose count names what Put Back
returns; the ancestor walk is absolute, so an own-flag card beneath a
tombstoned lane has no row and recovery is deliberately two steps.
Put Back twins Delete on Cmd-Backspace with validation enabling
exactly one; restore fidelity is byte-perfect because nothing ever
moved. Delete Immediately confirms exactly where loss is real (every
board is mode-none today; the predicate names the git carve-out for
m7), Empty Trash always confirms with the true whole-board count,
and dragging a tombstoned card onto a live lane restores it there —
positional drops and cross-board locality arrive with m5's machinery.
The banner's delete phrasing drops "move to the trash" per the naming
constraint: board deletion says Delete, "Move to Trash" stays
reserved for the system Trash. 47 new tests.

Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
2026-07-27 15:38:34 -04:00
parent aa6aaf2a11
commit 2090d742b9
14 changed files with 2428 additions and 18 deletions
+9 -1
View File
@@ -39,6 +39,11 @@ struct BoardWindowHost: View {
/// menu item through the focus system, like the store.
@State private var boardInfo = BoardInfoPresentation()
/// This window's purge alert, open or not (03-board-ui.md § Trash). `@State` for `boardInfo`'s
/// reason and reaching the menu bar the same way: Delete Immediately and Empty Trash are
/// menu-bar items, and a menu item cannot present anything of its own.
@State private var trashConfirmations = TrashConfirmations()
@State private var phase: Phase = .opening
private enum Phase {
@@ -80,15 +85,18 @@ struct BoardWindowHost: View {
BoardView(
store: store,
window: { windowController.window },
confirmations: trashConfirmations,
openCard: { cardID in
openWindow(id: WindowID.card, value: CardWindowRef(board: ref, cardID: cardID))
}
)
}
// "The board in front", for the menu items that act on it (`LaneWidthCommands`), and
// beside it the window's own popover flag, which is what File Board Info toggles.
// beside it the window's own popover flag, which is what File Board Info toggles, and
// its purge-alert host, which the trash's two confirmed commands raise.
.focusedSceneValue(\.boardStore, store)
.focusedSceneValue(\.boardInfo, boardInfo)
.focusedSceneValue(\.trashConfirmations, trashConfirmations)
}
}