The undo command surface rebuilds — app-owned rows and explicit toolbar targets over FocusedValues

Edit ▸ Undo/Redo become the app's own replaced rows and the board toolbar
pair takes explicit targets, both reading the focused session's
BoardUndoManager through FocusedValues.undoStack (board windows publish the
session's manager, card windows their own) — the nil-target route died with
the SwiftUI window latch, 13-native-undo.md ▸ Rules ▸ command surface,
re-ruled 2026-08-08. The rows enact the routing predicate themselves: text
focus routes ⌘Z to the first responder's own manager, title and enablement
included, re-derived at fire time with a beep for the stale window.
NativeHistoryProvider turns @Observable so both surfaces re-derive on stack
changes; a checkpoint-notification ticker covers plain text managers.
.responderAction leaves ToolbarItemSpec with its only user;
windowWillReturnUndoManager stays wired for AppKit's own asks.

Live-probed on the fixture board (21/21): the row retitles to "Undo Add
Lane" and crosses via real ⌘Z key events, ⇧⌘Z redoes via a window-server
chord, the toolbar pair validates and fires, search-field and body-editor
⌘Z stay text undo with board stacks untouched, and a card window crosses
its own stack with no fall-through. 2698 unit tests green.

Claude-Session: https://claude.ai/code/session_014PtZdPwqZuqEDLc6wZMtEy
This commit is contained in:
2026-08-08 18:55:34 -04:00
parent 78c32776d4
commit 1fd19dfb12
13 changed files with 926 additions and 201 deletions
+11 -4
View File
@@ -221,11 +221,18 @@ struct KanbanApp: App {
TrashCommands()
}
// Edit Undo/Redo, replacing the system's own pair **the command surface is the app's**
// (13-native-undo.md Rules, re-ruled 2026-08-08): the platform's nil-target rows resolve
// through `NSWindow.undoManager`, which a SwiftUI window latches empty before any delegate of
// ours can vend the board's, so the rows below read the focused session's stack themselves
// (`UndoCommands.swift`).
UndoRedoCommands()
// The Edit menu: Find (F), then its card-window stepping twins, placed after the standard
// Cut/Copy/Paste/Select All group, which is where macOS puts Find. Undo/Redo and the
// clipboard items are the system's and the board answers them as a responder
// (`ClipboardCommands.swift`) a second item sharing one of those titles is what
// titles-are-API forbids.
// Cut/Copy/Paste/Select All group, which is where macOS puts Find. The clipboard items above
// them stay the system's, answered by the board as a responder (`ClipboardCommands.swift`)
// a second item sharing one of those titles is what titles-are-API forbids. Undo and Redo
// were the system's too until the latch (the group just above).
CommandGroup(after: .pasteboard) {
FindCommand()
FindSteppingCommands()