The popover/sheet split reverses — settings fold into the Git tab, the widget stacks name over branch

The titlebar widget becomes a two-line identity block: the board glyph at
22pt spanning both lines, the title over the branch (git-mode only, smaller
and secondary), the em-dash retired. New Branch… returns to the switch menu
behind a divider, revealing an inline name field — the pre-split shape. The
board settings sheet retires whole: add-git and commit identity render
inline in the Git tab's postures (BoardGitSetup.swift), the availability
rule collapses into BoardGitSetupSection.resolve, and Board ▸ Board
Settings… leaves the menu bar. Where 07's remote/credential setup surfaces
land is deliberately left open — filed on the Redesign board.

Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
2026-08-07 21:48:02 -04:00
parent 99ebb69a1d
commit 7414fc8400
22 changed files with 884 additions and 1259 deletions
+155
View File
@@ -0,0 +1,155 @@
import Foundation
import Testing
@testable import Kanban
/// **What the popover's Git tab sets up, board by board** (03-board-ui.md Board popover Git tab;
/// 06-history-undo.md Rules) `BoardGitSetupSection.resolve`, the tab's setup inventory, pinned
/// for `BoardGitSection.resolve`'s reason one seam over: the *contents* are the decision worth
/// asserting and the SwiftUI that renders them is not. Every case below is a plain value no board
/// on disk, no window, no session.
///
/// ### This suite is the settings sheet's, inherited
///
/// It was `BoardSettingsSectionTests` and `BoardSettingsAvailabilityTests` against the sheet's own
/// inventory (2026-07-31 2026-08-07). The **2026-08-07 reversal** retired that sheet and rehomed
/// its controls into the tab, so the *subject* moved and most of the rules did not: an unreadable
/// repository still hosts no setup, repo-nested and unverifiable still host none, and mode `none`
/// still means add-git and nothing else.
///
/// Two things did change, and both are asserted below rather than described:
///
/// - **Branch creation is not in this inventory.** It went back into the switch menu as New Branch
/// with an inline reveal (`BoardGitControls`), which is a daily control rather than a standing
/// form so the git-mode posture's setup half is the commit identity alone.
/// - **Emptiness no longer means "no surface".** The sheet's `BoardSettingsAvailability` existed to
/// answer "does this surface exist at all", because a sheet with no sections had no honest empty
/// state and two doors had to validate before opening it. The tab always exists (every board
/// carries all three tabs since 12-editions.md PIVOT 2026-08-07) and always has a posture to
/// render, so an empty inventory now means only "this posture hosts no setup block" there is no
/// door left to disable, and the availability seam retired with the doors.
@Suite("Board popover ▸ Git tab ▸ the setup inventory")
struct BoardGitSetupSectionTests {
@Test("Mode none: add-git and nothing else")
func modeNoneHoldsAddGit() {
// "add-git (mode none; opt-in init 06)", rendered under the no-repository note since the
// reversal put it back where the pre-split popover had it. Every tier's since
// 12-editions.md PIVOT 2026-08-07, and still an offer rather than an auto-init.
#expect(BoardGitSetupSection.resolve(mode: .none) == [.addGit])
}
@Test("Git mode: the commit identity — creation is the switch menu's again")
func gitModeHoldsTheIdentity() {
// "commit identity name/email (06 the visibility-scoped 2 s config re-read rides with the
// fields)". Branch creation was this posture's other section while the sheet existed; the
// reversal moved it back into the menu, so it is deliberately absent here.
#expect(BoardGitSetupSection.resolve(mode: .git) == [.commitIdentity])
}
@Test("Every section is reachable from some posture, and no posture invents one")
func theInventoryIsTotal() {
let offered = Set(BoardGitMode.allCases.flatMap { BoardGitSetupSection.resolve(mode: $0) })
#expect(offered == Set(BoardGitSetupSection.allCases))
}
@Test("Repo-nested: nothing setup-shaped applies — the posture is its explanation")
func repoNestedHoldsNothing() {
// "not a hidden 'add git' but a short explanation the option is absent because it *can't*
// apply" (06 Rules). The posture still renders its note is the whole of it but there
// is no setup block under it.
#expect(BoardGitSetupSection.resolve(mode: .repoNested) == [])
}
@Test("Unverifiable: the same emptiness, for the denial-not-absence reason")
func unverifiableHoldsNothing() {
// "Denial is not absence" (06 Rules Detection, ruled 2026-07-31): a denied ancestor check
// can never be told apart from a repository actually being there, so add-git stays as
// unreachable here as it is on a genuinely nested board.
#expect(BoardGitSetupSection.resolve(mode: .unverifiable) == [])
}
@Test("Git mode with an unreadable repository: no setup block, but still the branch posture")
func anUnreadableRepositoryHoldsNoSetup() {
// **The corrupt-`.git` loud failure** (06 Rules, ruled 2026-07-31): writing an identity is
// a *write* into the repository's own config, and there is no repository the app can open to
// write it into.
#expect(BoardGitSetupSection.resolve(mode: .git, isRepositoryUnreadable: true) == [])
// and the mode is still `git` throughout: this is emptiness *within* git mode, never the
// fall to mode none that would let add-git be offered against an existing `.git`.
#expect(BoardGitSetupSection.resolve(mode: .git) == [.commitIdentity])
// The difference the tab can express and the sheet could not: the *posture* is unchanged, so
// the board still gets its branch surface, holding and explaining itself
// (`BoardGitBranchSurface.unreadableNote`). Only the setup block goes.
#expect(BoardGitSection.resolve(mode: .git) == .branch)
}
@Test("Only the identity block carries a header")
func headersAreNamed() {
// 10-accessibility.md's navigable-header rule, carried over from the retired sheet's
// sections: a heading the VoiceOver rotor jumps to. Add-git has none it renders directly
// under the note that states the posture, and a "Git" header inside the Git tab would be the
// surface naming itself twice.
#expect(BoardGitSetupSection.commitIdentity.title == "Commit Identity")
#expect(BoardGitSetupSection.addGit.title == nil)
}
}
/// The same inventory asked about **a board on disk** the two ends of the range a real
/// `HistoryStore` puts into it, so the seam's inputs are shown to be the ones a composed session
/// actually produces rather than values a test invented.
///
/// `@MainActor` because composing a `HistoryStore` is (the inventory itself is `nonisolated`, which
/// is exactly what the suite above exercises with no actor in sight).
@MainActor
@Suite("Board popover ▸ Git tab ▸ the setup inventory, against a composed session")
struct BoardGitSetupCompositionTests {
@Test("A board carrying an unopenable `.git` is a git board with no setup")
func anInertGitIsAnUnreadableGit() throws {
let fixture = try WriterFixture()
defer { fixture.tearDown() }
try fixture.item("", Item.board)
try fixture.file(".git/HEAD", Data("ref: refs/heads/main\n".utf8))
// The retired posture, asserted where it used to bite: a `.git` at a board root was **inert**
// off Pro never read, never written, the popover's one-line Pro pointer that board's whole
// story. Detection runs at every board open on every tier now (12-editions.md PIVOT
// 2026-08-07), so this composes in git mode with no tier asked for.
let git = HistoryStore.compose(boardRoot: fixture.root)
#expect(git.mode == .git)
// And what these three bytes actually are is a repository libgit2 will not open, so the board
// takes **06's corrupt-`.git` posture, not the retired inert one** (06 Rules, ruled
// 2026-07-31): git mode throughout never a fall to mode none that would offer add-git
// against an existing `.git` with no setup block, because every control there would write
// into something the app cannot open.
#expect(git.isRepositoryUnreadable)
#expect(
BoardGitSetupSection.resolve(
mode: git.mode,
isRepositoryUnreadable: git.isRepositoryUnreadable
) == []
)
}
@Test("A fresh mode-none board resolves to the add-git offer")
func aFreshBoardOffersAddGit() throws {
let fixture = try WriterFixture()
defer { fixture.tearDown() }
try fixture.item("", Item.board)
// Composed with no tier in sight: git is tier-independent since 12-editions.md PIVOT
// 2026-08-07, so this is every session's git state, not Pro's.
let git = HistoryStore.compose(boardRoot: fixture.root)
#expect(git.mode == .none)
#expect(
BoardGitSetupSection.resolve(
mode: git.mode,
isRepositoryUnreadable: git.isRepositoryUnreadable
) == [.addGit]
)
}
}
-209
View File
@@ -1,209 +0,0 @@
import Foundation
import Testing
@testable import Kanban
/// **The board settings sheet's two pure seams** (03-board-ui.md Board settings sheet, ruled
/// 2026-07-31 the popover/sheet split; 04-interactions.md The map's configuration carve-out):
/// what the sheet holds for a board, and therefore whether the sheet exists for that board at all.
///
/// They are pinned here for `BoardGitSection.resolve`'s reason one surface over: the *inventory* is
/// the decision worth asserting and the SwiftUI that renders it is not. Every case below is a plain
/// value no board on disk, no window, no session.
///
/// **The tier axis came out at 12-editions.md PIVOT 2026-08-07**: this inventory resolved `guard
/// tier == .pro` first and the mode second until git left the paywall. What survives is the mode
/// reading, which was always the part carrying the design's reasoning.
@Suite("Board settings sheet ▸ the sections")
struct BoardSettingsSectionTests {
@Test("Mode none: the sheet is add-git and nothing else")
func modeNoneHoldsAddGit() {
// "add-git (mode none; opt-in init 06)" 03's own first entry for this surface, and the
// control that moved here out of the popover with the split. Every tier's since
// 12-editions.md PIVOT 2026-08-07, and still an offer rather than an auto-init.
#expect(BoardSettingsSection.resolve(mode: .none) == [.git])
}
@Test("Git mode: branch creation and the commit identity, in that order")
func gitModeHoldsCreationAndIdentity() {
// "branch creation (switching stays in the popover)" and "commit identity name/email (06
// the visibility-scoped 2 s config re-read rides with the fields)".
#expect(BoardSettingsSection.resolve(mode: .git) == [.branch, .commitIdentity])
}
@Test("Every section is reachable from some posture, and no posture invents one")
func theInventoryIsTotal() {
let offered = Set(BoardGitMode.allCases.flatMap { BoardSettingsSection.resolve(mode: $0) })
#expect(offered == Set(BoardSettingsSection.allCases))
}
@Test("Unverifiable: the sheet has nothing to show — structurally like repo-nested")
func unverifiableHoldsNothing() {
// "Denial is not absence" (06 Rules Detection, ruled 2026-07-31): a denied ancestor check
// can never be told apart from a repository actually being there, so add-git stays as
// unreachable here as it is on a genuinely nested board.
#expect(BoardSettingsSection.resolve(mode: .unverifiable) == [])
}
@Test("Git mode with an unreadable repository: nothing setup-shaped applies either")
func anUnreadableRepositoryHoldsNothing() {
// **The corrupt-`.git` loud failure** (06 Rules, ruled 2026-07-31): both sections here are
// *writes* to a repository a branch created in it, an identity written into its config
// and there is no repository the app can open to write either into. Repo-nested's emptiness,
// reached one step further along.
#expect(BoardSettingsSection.resolve(mode: .git, isRepositoryUnreadable: true) == [])
// and the mode is still `git` throughout: this is emptiness *within* git mode, never the
// fall to mode none that would let add-git be offered against an existing `.git`.
#expect(BoardSettingsSection.resolve(mode: .git) == [.branch, .commitIdentity])
}
@Test("The sections carry the headers VoiceOver navigates by")
func headersAreNamed() {
// 10-accessibility.md Board settings sheet: "titled and sectioned with headers VoiceOver
// can navigate by". The strings are the surface's spoken structure, so they are stated once
// and pinned once.
#expect(BoardSettingsSection.git.title == "Git")
#expect(BoardSettingsSection.branch.title == "Branch")
#expect(BoardSettingsSection.commitIdentity.title == "Commit Identity")
}
}
/// **Where the sheet can be opened from, mode by mode** the answer both doors validate on: Board
/// Board Settings's `disabled` state, and whether the popover's git section renders its Board
/// Settings row at all.
///
/// **The Pro clause is gone** (12-editions.md PIVOT 2026-08-07): every board can reach the setup
/// its own mode leaves it, on any tier.
@Suite("Board settings sheet ▸ availability")
struct BoardSettingsAvailabilityTests {
@Test("The whole matrix: a none-or-git board, and nowhere else")
func theMatrix() {
// A board whose mode leaves something to set up.
#expect(BoardSettingsAvailability.resolve(mode: .none))
#expect(BoardSettingsAvailability.resolve(mode: .git))
// **Repo-nested**: "nothing setup-shaped can apply" no add-git (06's prose, not a
// disabled button), no branch of ours to create, no repo-local config of ours to write. The
// popover's explanation stands and no door opens.
#expect(!BoardSettingsAvailability.resolve(mode: .repoNested))
// **Unverifiable**: the same unreachability, for the denial-not-absence reason a denied
// ancestor check is never distinguishable from a repository actually being there.
#expect(!BoardSettingsAvailability.resolve(mode: .unverifiable))
// **Git mode over a repository that will not open**: no door either, so neither the
// popover's Board Settings row nor the menu command offers a surface with nothing on it
// (06 Rules, the corrupt-`.git` loud failure).
#expect(!BoardSettingsAvailability.resolve(mode: .git, isRepositoryUnreadable: true))
}
@Test("Reachable means exactly 'has something to show'")
func reachabilityIsTheInventory() {
// The derivation, not a coincidence: a surface whose whole job is hosting setup controls has
// no honest empty state, so the two answers are one answer. pro-m2's sections join the
// inventory and this identity keeps holding.
for mode in BoardGitMode.allCases {
#expect(
BoardSettingsAvailability.resolve(mode: mode)
== !BoardSettingsSection.resolve(mode: mode).isEmpty
)
}
}
}
/// **The window's sheet flag** (`BoardSettingsPresentation`) `BoardInfoPresentation`'s sibling,
/// with the one thing the popover flag does not carry: the session posture both doors validate on.
@MainActor
@Suite("Board settings sheet ▸ the window's presentation")
struct BoardSettingsPresentationTests {
@Test("It starts closed, unadopted, and unreachable")
func startsClosed() {
let presentation = BoardSettingsPresentation()
#expect(presentation.isPresented == false)
#expect(presentation.git == nil)
// No session adopted yet, so no sheet: every control this surface hosts writes *through* the
// git state, and a sheet of bare headers is the empty state the design forbids. (It was the
// free tier's default that kept this shut before 12-editions.md PIVOT 2026-08-07; the
// absence of a session is what keeps it shut now.)
#expect(presentation.isReachable == false)
#expect(presentation.sections.isEmpty)
}
@Test("An unreachable board's sheet refuses to present")
func presentRefusesWhereUnreachable() {
let presentation = BoardSettingsPresentation()
presentation.present()
#expect(presentation.isPresented == false, "an unadopted window has no sheet to open")
}
@Test("Two windows hold their own flags")
func perWindow() {
// `BoardInfoPresentation`'s rule, restated for the sheet: "two board windows each hold their
// own and can never toggle each other's".
let first = BoardSettingsPresentation()
let second = BoardSettingsPresentation()
first.isPresented = true
#expect(second.isPresented == false)
}
@Test("Adopting a session makes the sheet reachable, and dismissal is idempotent")
func adoptingASession() throws {
let fixture = try WriterFixture()
defer { fixture.tearDown() }
try fixture.item("", Item.board)
// Mode `none` the add-git posture, which is the sheet's whole job on a board with no
// repository yet. Composed with no tier in sight: git is tier-independent since
// 12-editions.md PIVOT 2026-08-07, so this is every session's git state, not Pro's.
let git = HistoryStore.compose(boardRoot: fixture.root)
#expect(git.mode == .none)
let presentation = BoardSettingsPresentation()
presentation.adopt(git: git)
#expect(presentation.isReachable)
#expect(presentation.sections == [.git])
presentation.present()
#expect(presentation.isPresented)
presentation.dismiss()
presentation.dismiss()
#expect(presentation.isPresented == false)
}
@Test("A board carrying a `.git` is a git board — the inert posture is retired")
func anInertGitIsNoLongerInert() throws {
let fixture = try WriterFixture()
defer { fixture.tearDown() }
try fixture.item("", Item.board)
try fixture.file(".git/HEAD", Data("ref: refs/heads/main\n".utf8))
// The retired posture, asserted where it used to bite: a `.git` at a board root was **inert**
// off Pro never read, never written, the popover's one-line Pro pointer that board's whole
// story. Detection runs at every board open on every tier now (12-editions.md PIVOT
// 2026-08-07), so this composes in git mode with no tier asked for.
let git = HistoryStore.compose(boardRoot: fixture.root)
#expect(git.mode == .git)
// And what these three bytes actually are is a repository libgit2 will not open, so the board
// takes **06's corrupt-`.git` posture, not the retired inert one** (06 Rules, ruled
// 2026-07-31): git mode throughout never a fall to mode none that would offer add-git
// against an existing `.git` with both doors shut because every section here is a write
// into something the app cannot open.
#expect(git.isRepositoryUnreadable)
let presentation = BoardSettingsPresentation()
presentation.adopt(git: git)
#expect(presentation.isReachable == false)
#expect(presentation.sections.isEmpty)
}
}
+26 -64
View File
@@ -1,16 +1,21 @@
import Testing
@testable import Kanban
/// `caretChordsYield(boardInfo:boardSettings:search:)` 04-interactions.md Grammar's caret-chords
/// `caretChordsYield(boardInfo:search:)` 04-interactions.md Grammar's caret-chords
/// rule as one expression, and `BoardCommands.swift`'s single seam for it: Board Move Left/Move
/// Right / and the lane-width pair / disable via menu validation whenever *any* text
/// control has keyboard focus, because / are the standard line-start/end caret chords and an
/// enabled key equivalent fires before a field ever sees the key.
///
/// The function reads exactly three flags and nothing else, so every test here constructs
/// `BoardInfoPresentation`, `BoardSettingsPresentation` and `BoardSearchPresentation` directly rather
/// than through a `BoardStore` a fixture that stood up a board would be exercising machinery this
/// seam never touches.
/// The function reads exactly two flags and nothing else, so every test here constructs
/// `BoardInfoPresentation` and `BoardSearchPresentation` directly rather than through a `BoardStore`
/// a fixture that stood up a board would be exercising machinery this seam never touches.
///
/// **It read three until 2026-08-07.** The 2026-07-31 popover/sheet split gave the board settings
/// sheet its own flag here, because the branch-name and commit-identity fields had moved onto it; the
/// reversal retired that sheet and brought those fields back into the popover's Git tab, so the
/// popover's own open-at-all clause covers every configuration field again and the third disjunct
/// went with the surface it described.
@MainActor
@Suite("caretChordsYield ▸ the caret-chords rule")
struct CaretChordTests {
@@ -22,7 +27,7 @@ struct CaretChordTests {
// "Card-window fields need nothing: those windows never publish a boardStore, so both items
// are already scopeless there" (caretChordsYield's doc comment) nil/nil is that window's
// steady state, not a corner case.
#expect(caretChordsYield(boardInfo: nil, boardSettings: nil, search: nil) == false)
#expect(caretChordsYield(boardInfo: nil, search: nil) == false)
}
// MARK: The board popover, alone
@@ -30,46 +35,13 @@ struct CaretChordTests {
@Test("The board popover open yields; closed, it does not")
func popoverPresence() {
let boardInfo = BoardInfoPresentation()
#expect(caretChordsYield(boardInfo: boardInfo, boardSettings: nil, search: nil) == false, "closed by default")
#expect(caretChordsYield(boardInfo: boardInfo, search: nil) == false, "closed by default")
boardInfo.isPresented = true
#expect(caretChordsYield(boardInfo: boardInfo, boardSettings: nil, search: nil) == true)
#expect(caretChordsYield(boardInfo: boardInfo, search: nil) == true)
boardInfo.isPresented = false
#expect(caretChordsYield(boardInfo: boardInfo, boardSettings: nil, search: nil) == false, "closing re-enables the chords")
}
// MARK: The settings sheet, alone
@Test("The settings sheet open yields; closed, it does not")
func settingsSheetPresence() {
// The sheet joined this seam with the 2026-07-31 popover/sheet split: it is where the branch
// name field and the commit-identity fields live now, so the surface that used to be covered
// by "the popover is open" has to be covered by "the sheet is up" as well.
let settings = BoardSettingsPresentation()
#expect(caretChordsYield(boardInfo: nil, boardSettings: settings, search: nil) == false, "closed by default")
settings.isPresented = true
#expect(caretChordsYield(boardInfo: nil, boardSettings: settings, search: nil) == true)
settings.isPresented = false
#expect(
caretChordsYield(boardInfo: nil, boardSettings: settings, search: nil) == false,
"dismissing re-enables the chords"
)
}
@Test("The sheet's own reachability is not this seam's question — a presented sheet yields either way")
func presentationRatherThanReachability() {
// `isReachable` gates the two *doors* (`BoardSettingsAvailability`); this function reads the
// flag that says a sheet is on screen. A presentation left unadopted reads free-tier and
// therefore unreachable, and if something ever put its flag up anyway the chords must still
// yield what a caret chord competes with is a field that exists, not a tier.
let settings = BoardSettingsPresentation()
#expect(settings.isReachable == false, "unadopted reads as the free tier")
settings.isPresented = true
#expect(caretChordsYield(boardInfo: nil, boardSettings: settings, search: nil) == true)
#expect(caretChordsYield(boardInfo: boardInfo, search: nil) == false, "closing re-enables the chords")
}
// MARK: The search field, alone
@@ -77,13 +49,13 @@ struct CaretChordTests {
@Test("The search field focused yields; unfocused, it does not")
func searchFocus() {
let search = BoardSearchPresentation()
#expect(caretChordsYield(boardInfo: nil, boardSettings: nil, search: search) == false, "unfocused by default")
#expect(caretChordsYield(boardInfo: nil, search: search) == false, "unfocused by default")
search.isFocused = true
#expect(caretChordsYield(boardInfo: nil, boardSettings: nil, search: search) == true)
#expect(caretChordsYield(boardInfo: nil, search: search) == true)
search.isFocused = false
#expect(caretChordsYield(boardInfo: nil, boardSettings: nil, search: search) == false, "losing focus re-enables the chords")
#expect(caretChordsYield(boardInfo: nil, search: search) == false, "losing focus re-enables the chords")
}
// MARK: Both surfaces together
@@ -98,32 +70,22 @@ struct CaretChordTests {
focusedSearch.isFocused = true
// One side published and inert, the other absent (the still-loading-window shape): false.
#expect(caretChordsYield(boardInfo: closedInfo, boardSettings: nil, search: nil) == false)
#expect(caretChordsYield(boardInfo: nil, boardSettings: nil, search: unfocusedSearch) == false)
#expect(caretChordsYield(boardInfo: closedInfo, search: nil) == false)
#expect(caretChordsYield(boardInfo: nil, search: unfocusedSearch) == false)
// One side published and active, the other absent: true.
#expect(caretChordsYield(boardInfo: openInfo, boardSettings: nil, search: nil) == true)
#expect(caretChordsYield(boardInfo: nil, boardSettings: nil, search: focusedSearch) == true)
#expect(caretChordsYield(boardInfo: openInfo, search: nil) == true)
#expect(caretChordsYield(boardInfo: nil, search: focusedSearch) == true)
// Both published, both inert: false the popover being open at all and the field holding
// focus are each read independently, so neither's mere presence counts on its own.
#expect(caretChordsYield(boardInfo: closedInfo, boardSettings: nil, search: unfocusedSearch) == false)
#expect(caretChordsYield(boardInfo: closedInfo, search: unfocusedSearch) == false)
// Both published, one or both active: true. This is an `||`, not an `&&` one live text
// surface is enough to send the chords to it, whatever the other surface is doing.
#expect(caretChordsYield(boardInfo: openInfo, boardSettings: nil, search: unfocusedSearch) == true)
#expect(caretChordsYield(boardInfo: closedInfo, boardSettings: nil, search: focusedSearch) == true)
#expect(caretChordsYield(boardInfo: openInfo, boardSettings: nil, search: focusedSearch) == true)
// And the third surface composes the same way inert beside two inert siblings, sufficient
// beside them when it is up.
let closedSheet = BoardSettingsPresentation()
let openSheet = BoardSettingsPresentation()
openSheet.isPresented = true
#expect(caretChordsYield(boardInfo: closedInfo, boardSettings: closedSheet, search: unfocusedSearch) == false)
#expect(caretChordsYield(boardInfo: closedInfo, boardSettings: openSheet, search: unfocusedSearch) == true)
#expect(caretChordsYield(boardInfo: openInfo, boardSettings: openSheet, search: focusedSearch) == true)
#expect(caretChordsYield(boardInfo: openInfo, search: unfocusedSearch) == true)
#expect(caretChordsYield(boardInfo: closedInfo, search: focusedSearch) == true)
#expect(caretChordsYield(boardInfo: openInfo, search: focusedSearch) == true)
}
// MARK: Inline editors are a different seam
@@ -142,6 +104,6 @@ struct CaretChordTests {
// No board popover, no search focus caretChordsYield answers false regardless of the open
// editor above, because it never reads isEditingInline at all.
#expect(caretChordsYield(boardInfo: nil, boardSettings: nil, search: nil) == false)
#expect(caretChordsYield(boardInfo: nil, search: nil) == false)
}
}