The clear title bar is said in SwiftUI — a configuration pass stops painting the bar back over the board's picture

`titlebarAppearsTransparent` is not the app's to hold on a scene window: it is
the AppKit face of the tree's resolved `toolbarBackgroundVisibility`, and
SwiftUI writes it on every pass that re-applies a window's configuration. The
board stated nothing, so `.automatic` resolved and each pass put `false` back
over what `HostedWindowController` had set out of band — correct on open,
because the walk lands after the pass that follows window creation, and opaque
at the next one, with nothing to re-assert it since the board's own reading had
not changed. The board now declares the posture where SwiftUI will keep
asserting it; the AppKit write stays as the same value one turn earlier.

Claude-Session: https://claude.ai/code/session_014PtZdPwqZuqEDLc6wZMtEy
This commit is contained in:
2026-08-09 09:44:30 -04:00
parent ece33bbf78
commit a429a7ec4d
3 changed files with 146 additions and 1 deletions
+45 -1
View File
@@ -171,6 +171,11 @@ struct BoardWindowHost: View {
// Nothing to render and nothing worth animating: this window is dismissing itself.
Color.clear
case let .open(store):
// **Whether this board paints a background of its own**, read once and spent twice
// below the AppKit half of the chrome decision (`setExtendsContentUnderTitlebar`) and
// the SwiftUI half (`toolbarBackgroundVisibility`) are one answer, not two, and reading
// it twice would be the first step towards their disagreeing.
let custom = BoardBackdrop.isCustom(store.snapshot, root: store.rootURL)
VStack(spacing: 0) {
BannerStripView(rows: store.bannerRows) { store.banners.dismiss($0) }
// **F's fallback**, and only that: the search field's home is the toolbar item
@@ -221,9 +226,31 @@ struct BoardWindowHost: View {
// first render is already a level, not a change this is the board's first statement
// about its chrome, and the loading half deliberately made none
// (`HostedWindowController.extendsUnderTitlebar`).
.onChange(of: BoardBackdrop.isCustom(store.snapshot, root: store.rootURL), initial: true) { _, custom in
.onChange(of: custom, initial: true) { _, custom in
windowController.setExtendsContentUnderTitlebar(custom)
}
// **And the same sentence said to SwiftUI, which is the half that lasts.**
//
// `NSWindow.titlebarAppearsTransparent` is not the app's property to hold on a SwiftUI
// scene window: it is the AppKit face of *this* modifier, and SwiftUI writes it the
// resolved value, whether or not it changed every time it re-applies a window's
// configuration. A tree that says nothing resolves `.automatic`, so every such pass
// wrote `false` over the `true` the line above had just set out of band. The board
// opened correct, because the load lands after the pass that follows window creation,
// and turned opaque at the next one a title bar painting its own material and its
// `titlebarSeparatorStyle` hairline across a picture that is still being drawn full
// height underneath, which is exactly what the bug's screenshots show (nothing below
// the bar moves, because `.fullSizeContentView` was never the half that was lost).
//
// Stating it here makes SwiftUI's own passes assert the posture the board asked for
// instead of undoing it. Verified as an A/B against a real configuration pass: the same
// AppKit write survives with this modifier declared and is reverted within ~15 ms
// without it.
//
// The AppKit write stays, and is not a second owner of the same truth it is the same
// value one turn earlier, so the frame in which a `background:` first reads is already
// drawn under a clear bar. What it cannot do is *stay* said; that is this line's job.
.toolbarBackgroundVisibility(Self.toolbarBackground(overCustomBackground: custom), for: .windowToolbar)
// (**The board settings sheet was presented here** between 2026-07-31 and 2026-08-07,
// when the popover/sheet split was reversed: the sheet retired, its contents rehomed
// into the popover's Git tab itself retired 2026-08-08 with the git excision
@@ -288,6 +315,23 @@ struct BoardWindowHost: View {
record?.displayName ?? AppModel.folderDisplayName(of: url)
}
/// **What the window toolbar's background does over this board** gone when the board paints
/// one of its own, the system's own answer when it does not (03-board-ui.md § Styling
/// Capabilities: the board's colour or image "paints the full window").
///
/// `.automatic` rather than `.visible` for the ordinary board, deliberately: a board with no
/// background of its own must keep "the standard chrome exactly as it has always looked", and
/// only `.automatic` means *whatever the system would have done* including the scroll-driven
/// appearance AppKit manages for itself. `.visible` would pin a bar on permanently and would be
/// this modifier's own version of the bug it exists to fix.
///
/// Static and pure for `loadingTitle`'s reason: this one line is the whole of the rule that a
/// board's chrome follows its background, and an inversion here is invisible in review and
/// obvious on screen.
static func toolbarBackground(overCustomBackground custom: Bool) -> Visibility {
custom ? .hidden : .automatic
}
// MARK: - Opening
/// Starts the open as a task of its own, so something can hold it.