Build the welcome screen
The welcome window becomes the real thing: Xcode-style, hidden title bar with background drag, branding and actions left, recents right — rows carrying the board symbol, name, location, and the registry's cached lane/card counts (stamped at close, never a scan at welcome time), sorted by last opened. Launch failures surface row-level per 02: a failure joins its recents row as a warning caption, an unresolvable bookmark renders unavailable with Forget its one affordance, and only a failure with no row to carry it falls back to a compact list; a board opening again heals its row. New Board (Opt-Cmd-N) opens the Pages-style template chooser — shipped with the single Basic template and the m9 seams marked — flowing through the save panel into createBoard/createLane and straight into a board window. Open Recent gains its submenu with Clear Menu (byte-identical to forgetting every row, pinned by test), and File > Duplicate forks the frontmost board to a Finder-style copy sibling: pending work flushes first through the close flush's step two alone (sessions stay open — 09's stated exception), every GUID and tombstone carries (the whole-board carve-out from copies-remint), and the copy opens in its own window while the original stays put. 36 new tests. Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
@@ -226,6 +226,76 @@ struct BoardRegistryTests {
|
||||
#expect(registry.recents().isEmpty)
|
||||
}
|
||||
|
||||
// MARK: Clear Menu
|
||||
|
||||
@Test("Clear Menu empties the registry, and persists")
|
||||
func forgetAllEmptiesTheRegistry() async throws {
|
||||
let storage = try RegistryStorage()
|
||||
defer { storage.tearDown() }
|
||||
let first = try makeBoard()
|
||||
defer { first.tearDown() }
|
||||
let second = try makeBoard()
|
||||
defer { second.tearDown() }
|
||||
|
||||
let registry = BoardRegistry(storageURL: storage.url)
|
||||
registry.recordOpen(of: first.root, displayName: "First")
|
||||
registry.recordOpen(of: second.root, displayName: "Second")
|
||||
#expect(registry.recents().count == 2)
|
||||
|
||||
registry.forgetAll()
|
||||
|
||||
#expect(registry.recents().isEmpty)
|
||||
#expect(BoardRegistry(storageURL: storage.url).recents().isEmpty, "Clear Menu persists")
|
||||
}
|
||||
|
||||
/// The equivalence 11-command-nexus.md's Clear Menu rests on: Finder clears a *menu*, and here
|
||||
/// the registry **is** the menu — so clearing it can only mean forgetting every record, and must
|
||||
/// leave the file in precisely the state that forgetting them one at a time would.
|
||||
@Test("Clear Menu is Forget applied to every row — same result, same file")
|
||||
func forgetAllMatchesForgettingEachRow() async throws {
|
||||
let wholesale = try RegistryStorage()
|
||||
defer { wholesale.tearDown() }
|
||||
let piecemeal = try RegistryStorage()
|
||||
defer { piecemeal.tearDown() }
|
||||
let first = try makeBoard()
|
||||
defer { first.tearDown() }
|
||||
let second = try makeBoard()
|
||||
defer { second.tearDown() }
|
||||
|
||||
func populate(_ storage: RegistryStorage) -> BoardRegistry {
|
||||
let registry = BoardRegistry(storageURL: storage.url)
|
||||
registry.recordOpen(of: first.root, displayName: "First")
|
||||
registry.recordOpen(of: second.root, displayName: "Second")
|
||||
return registry
|
||||
}
|
||||
|
||||
let bulk = populate(wholesale)
|
||||
bulk.forgetAll()
|
||||
|
||||
let oneByOne = populate(piecemeal)
|
||||
for row in oneByOne.recents() {
|
||||
oneByOne.forget(id: row.record.id)
|
||||
}
|
||||
|
||||
#expect(bulk.recents().isEmpty)
|
||||
#expect(oneByOne.recents().isEmpty)
|
||||
#expect(
|
||||
try Data(contentsOf: wholesale.url) == Data(contentsOf: piecemeal.url),
|
||||
"the two paths leave byte-identical files — there is no state Clear Menu skips"
|
||||
)
|
||||
}
|
||||
|
||||
@Test("Clear Menu on an empty registry writes nothing")
|
||||
func forgetAllOnEmptyRegistryIsANoOp() async throws {
|
||||
let storage = try RegistryStorage()
|
||||
defer { storage.tearDown() }
|
||||
|
||||
let registry = BoardRegistry(storageURL: storage.url)
|
||||
registry.forgetAll()
|
||||
|
||||
#expect(try storage.entryNames().isEmpty, "an empty registry has nothing to clear and no file to write")
|
||||
}
|
||||
|
||||
// MARK: Persistence
|
||||
|
||||
@Test("Every mutation survives a reload of the file, dates included")
|
||||
|
||||
Reference in New Issue
Block a user