Build the style, details, and actions sidebar sections
The sidebar completes: the shared style editor gains a second anchor — StyleEditorLayout carries the geometry (the popover keeps its settled 268/14/7/8 untouched as the default; the sidebar packs columns to its width with no inner scroller) while every well, the batch display, the arrow grammar, and the one applyStyle bracket stay the shared component's. The card anchor is fixed, not tracking: the target is this card, and the fate walk retires the window when the card goes. Details renders every unknown frontmatter key read-only in file order — Card.document already carried them — showing the author's own bytes where the raw span is a value and the engine's rendering for block scalars and empties; reserved enhanced-schema keys are ordinary unknowns, and no keys means no section. Actions: Delete rides the same tombstone bytes as Backspace and drop-on-trash through a one-line seam, says nothing about selection, and lets the fate walk dismiss; Reveal in Finder resolves through the attachment scope so the two paths cannot disagree. History reserves its m7 slot without drawing a header no base board can honor. Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
@@ -138,6 +138,50 @@ struct TrashDeleteTests {
|
||||
#expect(store.selection.isEmpty)
|
||||
}
|
||||
|
||||
@Test("The card window's Delete is the same tombstone, and says nothing about the selection")
|
||||
func cardWindowDeleteIsTheSameWrite() throws {
|
||||
let fixture = try makeBoard()
|
||||
defer { fixture.tearDown() }
|
||||
let store = try BoardStore(rootURL: fixture.root)
|
||||
let before = try fixture.indexText("\(Ident.lane1)/\(Ident.card1)")
|
||||
// Something *else* is selected on the board, which is the case the rule is about: the card
|
||||
// window's card need not be the board's selection at all.
|
||||
store.select([card3], liveness: .live, anchor: card3, head: card3)
|
||||
|
||||
store.deleteCard(card1)
|
||||
|
||||
// Byte-indistinguishable from the ⌫ tombstone above — same write op, same stamps, same
|
||||
// minimal touch (05-card-window.md ▸ Actions: "Delete — tombstones the card").
|
||||
let after = try fixture.indexText("\(Ident.lane1)/\(Ident.card1)")
|
||||
#expect(try FrontmatterDocument.parse(after).deleted.value != nil)
|
||||
#expect(!after.contains("modified-by"))
|
||||
#expect(untouchedLines(after) == untouchedLines(before))
|
||||
// ⌫ moves the selection to the successor sibling so a repeated keystroke walks down a lane.
|
||||
// A button in another window has no such continuation, and re-pointing a selection that never
|
||||
// lost anything would be the drag's mistake (`deleteByDrag`'s rule, shared).
|
||||
#expect(store.selection.ids == [card3])
|
||||
}
|
||||
|
||||
@Test("The card window's Delete writes nothing for a card that is already gone")
|
||||
func cardWindowDeleteIsLiveOnly() throws {
|
||||
let fixture = try makeBoard()
|
||||
defer { fixture.tearDown() }
|
||||
let store = try BoardStore(rootURL: fixture.root)
|
||||
let trashed = try stat(fixture, "\(Ident.lane1)/\(Ident.card2)")
|
||||
let underTombstonedLane = try stat(fixture, "\(Ident.lane3)/\(Ident.card4)")
|
||||
|
||||
// Its own tombstone, its lane's tombstone (effective liveness is ancestor-walked), and a card
|
||||
// this board has never heard of. All three are windows already dismissing — nothing is ever
|
||||
// written into a vanished folder.
|
||||
store.deleteCard(card2)
|
||||
store.deleteCard(card4)
|
||||
store.deleteCard(ItemID(rawValue: "00000000-0000-4000-8000-000000000000"))
|
||||
|
||||
#expect(try stat(fixture, "\(Ident.lane1)/\(Ident.card2)").modified == trashed.modified)
|
||||
#expect(try stat(fixture, "\(Ident.lane3)/\(Ident.card4)").modified == underTombstonedLane.modified)
|
||||
#expect(store.banners.oneShots.isEmpty)
|
||||
}
|
||||
|
||||
@Test("Already-tombstoned ids are skipped rather than re-stamped, and an empty set writes nothing")
|
||||
func liveOnly() throws {
|
||||
let fixture = try makeBoard()
|
||||
|
||||
Reference in New Issue
Block a user