The board settings sheet — setup leaves the popover for a home a stray click can't dismiss

The popover/sheet split lands: a board-scoped, titled, sectioned sheet on
the board window hosts everything setup-shaped, opened from the popover's
Board Settings… row and the new Board ▸ Board Settings… menu row. The three
existing setup controls relocate — add-git (whose noteFormVisible lines now
make the sheet the form-anchored failure surface), branch creation (a
standing field; create-and-switch runs the identical settle sequence), and
the commit-identity fields (the 2s visibility-scoped poll rides with them).
The popover keeps the daily face and its postures; its Pro/mode-none
section becomes header + door (.addGit renamed .noRepository). Availability
is derived from the section inventory (Pro + mode none or git), so pro-m2's
sections can't drift from the doors; the sheet's fields join the
caret-chord disable set. The audit suite pins what the free fixture can
reach; the sheet's own audit is manual until a tier override is ruled.

Claude-Session: https://claude.ai/code/session_01CqjXB7ASoWtbyoGod68k97
This commit is contained in:
2026-08-01 12:07:12 -04:00
parent 988a7245a3
commit 8345378972
12 changed files with 1168 additions and 321 deletions
+27 -1
View File
@@ -57,6 +57,12 @@ struct BoardWindowHost: View {
/// menu item through the focus system, like the store.
@State private var boardInfo = BoardInfoPresentation()
/// This window's board settings sheet, open or not (03-board-ui.md § Board settings sheet).
/// `@State` for `boardInfo`'s reason one per window and published the same way, because
/// Board Board Settings is a menu-bar item that has to reach the frontmost board window, and
/// because the sheet is presented *on this window* and modal to it.
@State private var boardSettings = BoardSettingsPresentation()
/// This window's purge alert, open or not (03-board-ui.md § Trash). `@State` for `boardInfo`'s
/// reason and reaching the menu bar the same way: File Delete (landing on a trash selection)
/// and Empty Trash are menu-bar items, and a menu item cannot present anything of its own.
@@ -197,6 +203,17 @@ struct BoardWindowHost: View {
.onChange(of: boardSearch.isFocused) { _, _ in
boardSearch.dismissTransientIfCleared(query: store.searchQuery)
}
// **The board settings sheet** (03-board-ui.md Board settings sheet) presented from
// the board window's own content, which is what makes it modal to *this* board rather
// than to the app: "a board-scoped, titled, sectioned sheet on the board window".
//
// It hangs here rather than inside `BoardView` for the reason the popover's flag is a
// window's: the two doors that open it are the titlebar popover's row and a menu-bar
// item, neither of which is inside the board, and a sheet has to be presented by
// something that outlives the surface that asked for it.
.sheet(isPresented: $boardSettings.isPresented) {
BoardSettingsSheet(store: store, presentation: boardSettings)
}
// "The board in front", for the menu items that act on it (`LaneWidthCommands`), and
// beside it the window's own popover flag, which is what File Board Info toggles, its
// purge-alert host, which the trash's two confirmed commands raise, and its search
@@ -207,6 +224,7 @@ struct BoardWindowHost: View {
// is keyed on the window rather than on the board it is showing.
.focusedSceneValue(\.boardWindowRef, ref)
.focusedSceneValue(\.boardInfo, boardInfo)
.focusedSceneValue(\.boardSettings, boardSettings)
.focusedSceneValue(\.trashConfirmations, trashConfirmations)
// Board Open Card's second half the same closure `BoardView` gets, so the menu item
// and the double-click open one window per card by construction.
@@ -694,13 +712,21 @@ struct BoardWindowHost: View {
// happen on this path `beginSession` precedes `configureWindow` and reads as the free
// tier's posture, which is the harmless direction.
let session = appModel.session(for: ref)
// The settings sheet's two doors validate on the same pair, so they are adopted here rather
// than read again somewhere else: the popover's Board Settings row and Board Board
// Settings must never disagree about whether this board has setup to show
// (`BoardSettingsAvailability`). The mode *inside* the git state stays live add-git flipping
// it re-resolves the sheet's sections and both doors, which is the one mid-session transition
// 06 sanctions.
boardSettings.adopt(tier: session?.tier ?? .free, git: session?.git)
windowController.installTitlebarAccessory(
boardInfoTitlebarAccessory(
store: store,
recents: appModel.styleRecents,
tier: session?.tier ?? .free,
git: session?.git,
presentation: boardInfo
presentation: boardInfo,
settings: boardSettings
)
)
// The widget above now says the board's name (and, on a git-mode Pro board, its branch)