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:
@@ -70,9 +70,10 @@ public final class HistoryStore {
|
||||
/// dismissing the form clears it ("dismissing the sheet dismisses the stale error"). The other
|
||||
/// half is `reportFailure`, which posts the banner when the answer arrives to an empty room.
|
||||
///
|
||||
/// The form is the popover's git section today and the board settings sheet once that exists —
|
||||
/// the ruling's container moved in the 2026-07-31 popover/sheet split, its substance did not, and
|
||||
/// `noteFormVisible(_:)` is the one line the sheet will re-point.
|
||||
/// The form is the **board settings sheet's Git section** (`BoardSettingsSheet`), which is where
|
||||
/// the 2026-07-31 popover/sheet split put add-git; it was the popover's git section until that
|
||||
/// sheet was built, and `noteFormVisible(_:)` is the one line the move re-pointed — the ruling's
|
||||
/// container changed, its substance did not.
|
||||
public private(set) var lastFailure: GitOperationFailure?
|
||||
|
||||
/// Whether the form add-git was asked from is on screen right now (`noteFormVisible(_:)`).
|
||||
@@ -114,15 +115,15 @@ public final class HistoryStore {
|
||||
|
||||
// MARK: - Commit identity
|
||||
|
||||
/// **What repo-local `.git/config` says right now** — the popover's two fields, as values rather
|
||||
/// than as a resolved identity (06 ▸ Interaction with external writers: "The board popover's git
|
||||
/// section exposes name/email fields that write that repo-local config — the setting *is* the
|
||||
/// file").
|
||||
/// **What repo-local `.git/config` says right now** — the settings sheet's two fields, as values
|
||||
/// rather than as a resolved identity (06 ▸ Interaction with external writers: "The board settings
|
||||
/// sheet's identity section … exposes name/email fields that write that repo-local config — the
|
||||
/// setting *is* the file").
|
||||
///
|
||||
/// Empty means the file names no such key, which is what an empty field means: the derived default
|
||||
/// applies, shown as the field's *placeholder*. Filling the field in with the derived value would
|
||||
/// be the app writing its own guess into the user's repository the first time they edited anything
|
||||
/// else in the popover — the exact thing 06 rules out.
|
||||
/// else on the sheet — the exact thing 06 rules out.
|
||||
public private(set) var identityName = ""
|
||||
|
||||
public private(set) var identityEmail = ""
|
||||
@@ -132,12 +133,12 @@ public final class HistoryStore {
|
||||
/// Deliberately not computed at composition: `GitIdentity.derivedDefault()` reads
|
||||
/// `ProcessInfo.hostName`, which can block on a machine whose name resolution is slow, and the
|
||||
/// board-open path is where 02-architecture.md's hang-avoidance doctrine is strictest. It is read
|
||||
/// off the main actor with the config, when the popover asks.
|
||||
/// off the main actor with the config, when the sheet asks.
|
||||
public private(set) var derivedIdentity: GitIdentity?
|
||||
|
||||
/// The last identity-write failure, surfaced as an inline caption in the popover beside the fields
|
||||
/// — 06's popover-anchored posture ("the user asked from a form still under their eye"), which is
|
||||
/// exactly where `lastFailure` above already puts add-git's.
|
||||
/// The last identity-write failure, surfaced as an inline caption on the settings sheet beside the
|
||||
/// fields — 06's form-anchored posture ("the user asked from a form still under their eye"), which
|
||||
/// is exactly where `lastFailure` above already puts add-git's.
|
||||
public private(set) var identityFailure: GitOperationFailure?
|
||||
|
||||
/// The board's write-provenance ledger, held so an add-git flip can build a committer over the
|
||||
@@ -173,8 +174,8 @@ public final class HistoryStore {
|
||||
/// **The form appeared or was dismissed.** Dismissal clears the stale inline error, which is the
|
||||
/// ruling's own sentence ("dismissing the sheet dismisses the stale error, retry is right there").
|
||||
///
|
||||
/// A `Bool` rather than a count because there is one such form per board at a time: the popover is
|
||||
/// built fresh on each open and the settings sheet is modal to its board window.
|
||||
/// A `Bool` rather than a count because there is one such form per board at a time: the settings
|
||||
/// sheet is modal to its board window, and opening it dismisses the popover.
|
||||
public func noteFormVisible(_ visible: Bool) {
|
||||
isFormVisible = visible
|
||||
if !visible { lastFailure = nil }
|
||||
@@ -258,14 +259,18 @@ public final class HistoryStore {
|
||||
/// **Opt-in init** (06-history-undo.md ▸ Rules): initializes a repository at the board root and
|
||||
/// immediately commits the whole tree as "Initial board state".
|
||||
///
|
||||
/// Reachable from one place — the board popover's git section under Pro — and from nowhere else:
|
||||
/// Reachable from one place — the board settings sheet's Git section under Pro — and from nowhere
|
||||
/// else:
|
||||
/// "No silent auto-init, ever", a deliberate pivot from the pathfinder, which initialized a repo
|
||||
/// under every board it opened.
|
||||
///
|
||||
/// **It flips the open board's mode immediately**, which is the design's one sanctioned
|
||||
/// mid-session transition: "clicking it flips the open board into git mode immediately — the
|
||||
/// popover flows straight into the git controls". The flip is commanded, not discovered, which
|
||||
/// is what distinguishes it from the `git init` a user runs in a terminal under an open board.
|
||||
/// popover flows straight into the git controls". Since the 2026-07-31 split that flow is one
|
||||
/// surface further along — the sheet's Git section becomes its Branch and Commit Identity
|
||||
/// sections, and the popover behind it gains the branch line — but the immediacy is the same. The
|
||||
/// flip is commanded, not discovered, which is what distinguishes it from the `git init` a user
|
||||
/// runs in a terminal under an open board.
|
||||
///
|
||||
/// Only mode `none` can be added to. Mode `git` has nothing to add, and a repo-nested board is
|
||||
/// one the app "leaves strictly alone" — no nested repo, ever.
|
||||
@@ -279,7 +284,7 @@ public final class HistoryStore {
|
||||
|
||||
let root = boardRoot
|
||||
// Off the main actor: `git_repository_init` plus a whole-tree stage and commit is real
|
||||
// filesystem work, and the popover it was clicked in stays live while it runs.
|
||||
// filesystem work, and the sheet it was clicked in stays live while it runs.
|
||||
let outcome = await Task.detached(priority: .userInitiated) {
|
||||
GitRepository.create(at: root)
|
||||
}.value
|
||||
@@ -326,7 +331,8 @@ public final class HistoryStore {
|
||||
}
|
||||
|
||||
/// Reads the current branch name into `branch` — the popover's read-only display line, refreshed
|
||||
/// when the popover opens. A no-op outside git mode.
|
||||
/// when the popover opens (and when the settings sheet does, whose create control reads the same
|
||||
/// surface). A no-op outside git mode.
|
||||
public func refreshBranch() async {
|
||||
guard mode == .git else { return }
|
||||
let root = boardRoot
|
||||
@@ -337,8 +343,8 @@ public final class HistoryStore {
|
||||
|
||||
// MARK: - Commit identity
|
||||
|
||||
/// Reads repo-local config and the derived default into the popover's fields. A no-op outside git
|
||||
/// mode, `refreshBranch()`'s rule.
|
||||
/// Reads repo-local config and the derived default into the settings sheet's fields. A no-op
|
||||
/// outside git mode, `refreshBranch()`'s rule.
|
||||
///
|
||||
/// Both reads run off the main actor: one opens a repository, the other asks the system for the
|
||||
/// account and host names.
|
||||
|
||||
Reference in New Issue
Block a user