The board search item becomes the stock NSSearchToolbarItem — the field grows on focus

The toolbar's search field now lives in AppKit's own NSSearchToolbarItem:
em(17) is the focused width (preferredWidthForSearchField), the resting
width is the item's own, and ⌘F becomes beginSearchInteraction — one call
that expands and focuses. Escape's empty-field exit collapses the field as
the keyboard leaves. resignsFirstResponderWithCancel is off so the staged
Escape survives the cancel button. The item ships its own live overflow row
and an above-.high visibility priority, so both custom-view workarounds
retire. The palette copy stays inert through an enablement round-trip the
item's searchField setter would otherwise overwrite.

The centering half of the card is blocked on a Design ruling (03 settles
"trailing") and is not built; the two-homes width question is filed.

Claude-Session: https://claude.ai/code/session_01CqjXB7ASoWtbyoGod68k97
This commit is contained in:
2026-08-01 11:16:58 -04:00
parent 31fee00c73
commit 5e6417e749
4 changed files with 354 additions and 53 deletions
+23 -13
View File
@@ -101,20 +101,30 @@ enum BoardToolbar {
"Search",
identifier: .boardSearch,
symbol: nil,
// The field's width in *characters* rather than points (10-accessibility.md Text
// scaling: "no fixed point sizes") the same 17 ems the transient bar's field takes
// (`BoardSearchBar`), so F's two homes are one width whichever the user is in.
behavior: .control(width: BoardMetrics.em(17, bodyPointSize: BoardMetrics.bodyPointSize)) {
[weak store] willBeInserted in
guard willBeInserted, let store else {
return BoardSearchFieldController.makePaletteField()
// The width in *characters* rather than points (10-accessibility.md Text scaling:
// "no fixed point sizes") the same 17 ems the transient bar's field takes
// (`BoardSearchBar`). It is the **focused** width here: `NSSearchToolbarItem` grows
// the field to its preferred width when the keyboard arrives and lets it settle
// back to the item's natural width when the keyboard leaves, so the two homes match
// once the user is typing rather than at rest.
behavior: .searchField(
focusedWidth: BoardMetrics.em(17, bodyPointSize: BoardMetrics.bodyPointSize),
make: { [weak store] willBeInserted in
guard willBeInserted, let store else {
return BoardSearchFieldController.makePaletteField()
}
return BoardSearchFieldController.makeField(
store: store,
presentation: search,
home: .toolbar
)
},
// The toolbar half of the field's home that only the *item* can answer: F's
// expand-and-focus, and the collapse Escape's second step asks for.
install: { item in
BoardSearchFieldController.adopt(item, presentation: search)
}
return BoardSearchFieldController.makeField(
store: store,
presentation: search,
home: .toolbar
)
}
)
),
]
}