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:
2026-07-27 16:50:39 -04:00
parent 5ebf9fb90c
commit 4b97ecf3f0
18 changed files with 1904 additions and 73 deletions
+15
View File
@@ -426,6 +426,21 @@ public final class BoardRegistry {
save()
}
/// Drops every record File Open Recent Clear Menu (11-command-nexus.md).
///
/// **Finder's Clear Menu clears the menu; this registry *is* the menu**, so the equivalence is
/// exact: there is no separate recents list that could be emptied while the records stayed, and
/// a record whose board never appears anywhere is a setting nothing can reach. It is therefore
/// `forget(id:)` applied to every row, and the one test worth writing says exactly that.
///
/// One save rather than one per record: the file is rewritten wholesale anyway, and forgetting
/// twenty boards should not be twenty writes.
public func forgetAll() {
guard !records.isEmpty else { return }
records.removeAll()
save()
}
// MARK: - Matching
/// The index of the record whose bookmark resolves to the same file as `url`, if any.