Materialize the trash — faces, menus, and grammar

Phase 3 finishes the pivot at the surface. One card face serves two
containers: CardFaceView extracted with a role — board or trash — so
stripe, tint, chip, selection stroke, cut dim, marquee registration,
and drag are shared by construction, the trash side differing only in
its absences: no Open, no rename, no Style, no file-hover highlight,
and a Delete that goes through the confirmation host. The column
rewrote around the lanes' own single-column masonry so drag reflow
reads as positional slides; chrome stays the hatched header, symbol,
and count — 11 gives Empty Trash to the File menu alone. Two real
grammar bugs die here: plain Backspace on a trash selection purged
without the confirmation the menu raises, and the context menu's
Delete resolved against the standing selection, so right-clicking a
trash card under a board selection silently did nothing — it now
stages the clicked set explicitly. Open, Rename, Style, and Empty
Trash validation became testable store seams; the column is one named
accessibility container of ordinary card elements. The tombstone era
is swept: deleteItem, restoreItem, stripTombstonedChildren — dead
since lane copies stopped nesting trash — the restore verb, the
unreachable put-back banner row, and every quasi-lane doc comment.

Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
2026-07-28 18:18:39 -04:00
parent 53bc71f7fb
commit 797d020d01
34 changed files with 1272 additions and 1422 deletions
+3 -6
View File
@@ -631,9 +631,8 @@ public final class BannerCenter {
/// does not: the enum knows an item's title, never its *kind*, so an untitled failure says
/// "the item" rather than guessing "card" and being wrong about a lane.
///
/// The trash verbs match the commands the user pressed **Delete**, Put Back, Delete
/// Immediately which is 03-board-ui.md § Trash's naming constraint, settled with the trash UI
/// copy: "Finder's 'Move to Trash' phrasing is reserved for the system Trash; board deletion says
/// The trash verbs match the commands the user pressed **Delete**, Delete Immediately, Empty
/// Trash which is 03-board-ui.md § Trash's naming constraint, settled with the trash UI copy: "Finder's 'Move to Trash' phrasing is reserved for the system Trash; board deletion says
/// 'Delete'". A banner saying a card could not be *moved to the trash* would name the wrong one
/// of the app's two trashes (the card window's attachment Remove is the other).
private nonisolated static func actionPhrase(for operation: WriteOperation) -> String {
@@ -652,8 +651,6 @@ public final class BannerCenter {
if let title { "Couldn't copy '\(title)'" } else { "Couldn't copy the item" }
case let .delete(title):
if let title { "Couldn't delete '\(title)'" } else { "Couldn't delete the item" }
case let .restore(title):
if let title { "Couldn't put '\(title)' back" } else { "Couldn't put the item back" }
case let .purge(title):
if let title { "Couldn't permanently delete '\(title)'" } else { "Couldn't permanently delete the item" }
case let .migrateTombstone(title):
@@ -683,7 +680,7 @@ public final class BannerCenter {
// it. The naming constraint above reserves "move to the Trash" for the *system* Trash,
// and this is the operation that uses it: the file really did go (or fail to go) where
// Finder's own sends things, so saying anything else "remove", "delete" would
// describe the board's quasi-lane instead and promise the wrong recovery.
// describe the board's own trash instead and promise the wrong recovery.
"Couldn't move '\(filename)' to the Trash"
case .renumberChildren:
"Couldn't renumber cards"