Card windows drop the title from the title bar — the body already says it

The card's name renders as part of the card body, so the chrome said it
twice. HostedWindowController gains a titleVisibility slot on the same
retained-slot pattern as the accessory and toolbar (reapplied at attach,
so the macOS 26 provisional-window swap can't lose it); CardWindowHost
opts in beside installToolbar, board windows keep AppKit's default.
window.title itself is untouched — navigationTitle still feeds the
Window menu, Exposé, VoiceOver and restoration.

Known consequence, probed empirically: under .hidden AppKit collapses
title and subtitle into one hidden field, so the "board › lane"
navigationSubtitle disappears with it — and the details sidebar carries
no placement rows, so a card window currently shows its home nowhere.
Filed on the Redesign board to bless or relocate; 05-card-window.md's
subtitle sentence needs reconciling either way.

Claude-Session: https://claude.ai/code/session_01CqjXB7ASoWtbyoGod68k97
This commit is contained in:
2026-08-01 08:10:31 -04:00
parent 8206a78ecc
commit 6f2e0d15da
3 changed files with 124 additions and 0 deletions
+77
View File
@@ -973,3 +973,80 @@ struct BoardInfoAccessoryTests {
controller.detach()
}
}
// MARK: - Card window chrome hidden title
/// **Card windows show no title in the title bar** the card's name is shown as part of the card's
/// body instead (05-card-window.md Window: "card title is shown as part of card's body"), while the
/// toolbar renders exactly as it always has. `HostedWindowController.hideTitle()` is the seam
/// `CardWindowHost` reaches for; board windows call no such thing and keep AppKit's own default. Like
/// `BoardInfoAccessoryTests` above, this cannot see what a user sees on screen only that the
/// mechanism this app owns (`NSWindow.titleVisibility`) lands correctly, survives the macOS 26
/// provisional-window swap (`HostedWindowController.detach`'s doc comment chrome held by the
/// controller reapplies to whichever window actually sticks around), and leaves `window.title` itself
/// alone, since that string is what the Window menu, Mission Control/Exposé, VoiceOver and state
/// restoration all read.
@MainActor
@Suite("Card window chrome ▸ hidden title")
struct CardWindowTitleVisibilityTests {
private func makeWindow(title: String = "Fix login") -> NSWindow {
let window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 400, height: 300),
styleMask: [.titled, .closable, .resizable],
backing: .buffered,
defer: false
)
window.title = title
return window
}
@Test("hideTitle() hides the title bar's title but leaves window.title alone")
func hidesTitleLeavesTitleStringAlone() {
let window = makeWindow()
let controller = HostedWindowController()
controller.attach(to: window)
controller.hideTitle()
#expect(window.titleVisibility == .hidden)
#expect(window.title == "Fix login", "the Window menu, Exposé and restoration still need the string")
}
@Test("A window nobody calls hideTitle() on keeps AppKit's default — the board window's posture")
func boardWindowsKeepTheDefault() {
let window = makeWindow()
let controller = HostedWindowController()
controller.attach(to: window)
#expect(window.titleVisibility == .visible)
}
@Test("Hiding before the window exists still lands once one attaches")
func hidesBeforeAttach() {
let controller = HostedWindowController()
controller.hideTitle()
let window = makeWindow()
controller.attach(to: window)
#expect(window.titleVisibility == .hidden)
}
@Test("The setting survives the provisional-window swap: detach, then attach to the real window")
func survivesDetachAttachCycle() {
let provisional = makeWindow()
let controller = HostedWindowController()
controller.attach(to: provisional)
controller.hideTitle()
#expect(provisional.titleVisibility == .hidden)
controller.detach()
let real = makeWindow(title: "Fix login")
controller.attach(to: real)
#expect(real.titleVisibility == .hidden, "reapplied on the window that actually sticks around")
#expect(real.title == "Fix login", "window.title still flows independently of the chrome swap")
}
}