A card window toolbar control shows and hides the trailing sidebar — View ▸ Show Sidebar joins Edit Body, Raw Source and Add Attachment
The card window's trailing attributes sidebar has always been unconditional since m6; this adds View ▸ Show Sidebar (a checkmark toggle, ShowComments' shape) and a matching toolbar item on the customizable card toolbar (NSToolbar/WindowToolbarController), a fourth default beside Edit Body, Raw Source and Add Attachment. sidebar.right for the trailing pane; one shared animated write path (AppPreferences.setShowCardSidebar) both faces call, structural-voice reflow with a trailing slide-and-fade transition (Motion.cardSidebarTransition), Reduce Motion respected throughout. CardWindowMetrics.minimumSize gains a sidebar: Bool = true parameter so a hidden sidebar shrinks the window's floor, composing with the existing commentsColumn parameter. The toolbar item's read/write are injectable closures (defaulted to the real UserDefaults-backed pair) so its plumbing is testable without touching the developer's own preferences domain. WindowToolbarController's observation tracking only sees @Observable reads, so a small HostedWindowController.revalidateToolbar() plus an onChange nudge keeps the toolbar button's on-state in step with the View-menu row's write. Scope held narrowly to visibility, per the card: no sidebar section reordering, no action-moving. Claude-Session: https://claude.ai/code/session_014PtZdPwqZuqEDLc6wZMtEy
This commit is contained in:
@@ -598,16 +598,16 @@ struct CardToolbarTests {
|
||||
return (body, raw, attachments)
|
||||
}
|
||||
|
||||
@Test("The default set is the whole catalog — the trio, in 03's order")
|
||||
@Test("The default set is the whole catalog — the trio plus Show Sidebar, in this file's order")
|
||||
func defaultsAreTheCatalog() {
|
||||
let (body, raw, attachments) = makeHandles()
|
||||
let specs = CardToolbar.specs(body: body, rawSource: raw, attachments: attachments)
|
||||
|
||||
// "Card window default: Edit Body · Raw Source · Add Attachment … the catalog is the same
|
||||
// trio."
|
||||
#expect(CardToolbar.defaultItems == [.cardEditBody, .cardRawSource, .cardAddAttachment])
|
||||
// trio" — joined by Show Sidebar, the fourth default item the toolbar-toggle card added.
|
||||
#expect(CardToolbar.defaultItems == [.cardEditBody, .cardRawSource, .cardAddAttachment, .cardShowSidebar])
|
||||
#expect(specs.map(\.identifier) == CardToolbar.defaultItems)
|
||||
#expect(specs.map(\.label) == ["Edit Body", "Raw Source", "Add Attachment"])
|
||||
#expect(specs.map(\.label) == ["Edit Body", "Raw Source", "Add Attachment", "Show Sidebar"])
|
||||
}
|
||||
|
||||
@Test("Every item's symbol resolves on this system")
|
||||
@@ -711,6 +711,45 @@ struct CardToolbarTests {
|
||||
#expect(!addAttachment.isEnabled)
|
||||
#expect(addAttachment.isEnabled == AddAttachmentCommand.isEnabled(attachments))
|
||||
}
|
||||
|
||||
/// **Show Sidebar is always enabled and drives its own injected write path** — the two closures
|
||||
/// `CardToolbar.specs` defaults to `AppPreferences.showCardSidebar` / `.setShowCardSidebar`, held
|
||||
/// here over a local `Bool` instead so this test never touches the developer's own
|
||||
/// `UserDefaults.standard` domain (`CardSessionUndoTests`' quick-style caution, applied to the one
|
||||
/// item here with no window-scoped handle to hold a scratch value instead).
|
||||
@Test("Show Sidebar toggles its own bit, always enabled, on-state matching the read")
|
||||
func showSidebarTogglesItsOwnBit() {
|
||||
let (body, raw, attachments) = makeHandles()
|
||||
var shown = true
|
||||
let specs = CardToolbar.specs(
|
||||
body: body,
|
||||
rawSource: raw,
|
||||
attachments: attachments,
|
||||
isSidebarShown: { shown },
|
||||
setSidebarShown: { shown = $0 }
|
||||
)
|
||||
guard let showSidebar = specs.spec(.cardShowSidebar) else {
|
||||
Issue.record("no Show Sidebar item")
|
||||
return
|
||||
}
|
||||
|
||||
#expect(showSidebar.isOn == true)
|
||||
#expect(showSidebar.isEnabled, "showing or hiding a pane is not a mutation the read-only lock gates")
|
||||
|
||||
showSidebar.activate()
|
||||
#expect(shown == false, "the item drives the same bit the injected closures read")
|
||||
#expect(showSidebar.isOn == false)
|
||||
|
||||
showSidebar.activate()
|
||||
#expect(shown == true)
|
||||
#expect(showSidebar.isOn == true)
|
||||
|
||||
// Raw source and the read-only lock are both the *other* items' predicates — Show Sidebar
|
||||
// reads neither.
|
||||
raw.enter()
|
||||
attachments.isEditable = false
|
||||
#expect(showSidebar.isEnabled)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ⌘F and the search field's two homes
|
||||
|
||||
Reference in New Issue
Block a user