Give the registry live display-state write-through and record-before-load

BoardRecord carries icon/iconColor; recordOpen/recordClose stamp them with the
display name, and a displayStateDelegate on the store (wired in BoardWindowHost
beside onFrameChanged) syncs all three through BoardRegistry.syncDisplayState on
every successful reload — welcome rows now wear the board's own icon and follow
in-app renames live. recordOpen now runs before the load with the folder name as
a brand-new record's provisional display name, so a first open that fails
fail-fast still lands in recents carrying the failure row-level (02's rule); an
existing record's cached name survives a failing retry, and the welcome fallback
list remains only for failures naming no record at all.

Claude-Session: https://claude.ai/code/session_01CqjXB7ASoWtbyoGod68k97
This commit is contained in:
2026-07-27 20:44:30 -04:00
parent cacd48cb0f
commit f2d9f3ad07
10 changed files with 608 additions and 41 deletions
+20 -2
View File
@@ -531,6 +531,17 @@ public final class AppModel {
return (lanes, cards)
}
/// The folder name, extension stripped (01-storage-format.md § Board naming) `displayName`'s
/// own fallback, and (02-architecture.md § Per-board app state) the registry record's
/// *provisional* display name for a board recorded before its load has run: "fail-fast means the
/// frontmatter can't be trusted, and the folder name is the Finder document name the user just
/// picked". A first successful load replaces it with the cached title through the ordinary
/// `displayName(of:)` path there is no separate provisional vocabulary, just this one fallback
/// used a moment earlier than usual.
public static func folderDisplayName(of url: URL) -> String {
url.deletingPathExtension().lastPathComponent
}
/// A board's display name: its `title`, falling back to the folder name sans extension
/// (01-storage-format.md § Board naming).
///
@@ -540,7 +551,7 @@ public final class AppModel {
if let title = store.snapshot.title.value, !title.isEmpty {
return title
}
return store.rootURL.deletingPathExtension().lastPathComponent
return folderDisplayName(of: store.rootURL)
}
// MARK: - Closing
@@ -617,7 +628,14 @@ public final class AppModel {
recordClose: { [weak self] in
guard let self, let session = sessions[ref] else { return }
let counts = Self.liveCounts(of: session.store.snapshot)
boardRegistry.recordClose(id: session.recordID, laneCount: counts.lanes, cardCount: counts.cards)
boardRegistry.recordClose(
id: session.recordID,
displayName: Self.displayName(of: session.store),
laneCount: counts.lanes,
cardCount: counts.cards,
icon: session.store.snapshot.icon.value,
iconColor: session.store.snapshot.iconColor.value
)
},
clearOpenNow: { [weak self] in
guard let self, let session = sessions[ref] else { return }