Implement toolbar customization for both windows
NSToolbar through the existing HostedWindowController rather than SwiftUI's toolbar — for reasons that are contract, not taste: 03's transient-search clause is a decision over the toolbar's current contents, which NSToolbar publishes and SwiftUI's API cannot answer; Undo/Redo are the system's nil-target responder-chain actions so the toolbar items validate exactly as the menu rows do (disabled on base boards, alive in m8 unchanged); and the search item hosts the real NSSearchField with explicit first-responder control. Customization is all system furniture — Customize sheet, drag rearrange, display-mode popup, overflow, autosaved per window kind. Board default: the search field alone, trailing; catalog adds New Card, New Lane, Undo, Redo, Show Trash, every action extracted from its menu command so no second predicate exists. Card default: the Edit Body / Raw Source toggles and Add Attachment, mirroring their commands' own predicates live via observation tracking. Removing the search item keeps the promise — ⌘F surfaces the same field as a transient strip under the title bar, persisting until the query clears, and an overflowed item that cannot take the keyboard falls through to the strip too. Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
@@ -132,12 +132,15 @@ enum Motion {
|
||||
enum Appearance: Equatable {
|
||||
/// Scales up from `from` while fading in (and the reverse on the way out).
|
||||
case scaleAndFade(from: CGFloat)
|
||||
/// Slides in from `edge` while fading in, and leaves the way it came.
|
||||
case slideAndFade(from: Edge)
|
||||
/// Opacity only — 10-accessibility.md's "crossfade" variant.
|
||||
case crossfade
|
||||
|
||||
var transition: AnyTransition {
|
||||
switch self {
|
||||
case let .scaleAndFade(scale): .scale(scale: scale).combined(with: .opacity)
|
||||
case let .slideAndFade(edge): .move(edge: edge).combined(with: .opacity)
|
||||
case .crossfade: .opacity
|
||||
}
|
||||
}
|
||||
@@ -165,6 +168,20 @@ enum Motion {
|
||||
laneAppearance(reduced: reduced).transition
|
||||
}
|
||||
|
||||
/// The transient search bar arriving and leaving — ⌘F's fallback when the search field has been
|
||||
/// removed from the toolbar (03-board-ui.md ▸ Toolbar: "with the field removed from the toolbar,
|
||||
/// invoking it surfaces the field transiently until the search clears").
|
||||
///
|
||||
/// It comes from the top edge because that is where the field lives when it is installed: the
|
||||
/// bar is the toolbar's item arriving one row lower, not a new kind of surface.
|
||||
static func transientSearchAppearance(reduced: Bool) -> Appearance {
|
||||
reduced ? .crossfade : .slideAndFade(from: .top)
|
||||
}
|
||||
|
||||
static func transientSearchTransition(reduced: Bool) -> AnyTransition {
|
||||
transientSearchAppearance(reduced: reduced).transition
|
||||
}
|
||||
|
||||
// MARK: - The AppKit face
|
||||
|
||||
/// The lane-resize window animation's duration, for `NSAnimationContext` — which takes a number
|
||||
|
||||
Reference in New Issue
Block a user