The titlebar stand-in stops reappearing on a window that attaches after the board has loaded
`configureWindow` removed the loading stand-in from whatever window it happened to be running against, but left `onAttach` installed exactly as `configureLoadingWindow` set it — a closure that builds a fresh stand-in on every attach, deliberately, so it survives the provisional-window swap while the board is still loading. SwiftUI's dismantle-then- make swap has no deadline, though: when it lands after the store has already loaded, the stale closure reinstalls a stand-in on the real window a moment before `attach`'s own `addTitlebarAccessoryIfPossible()` installs the real widget beside it — both in the titlebar, stand-in leading, and nothing left holding a reference to remove it a second time. `onAttach` is now replaced in `configureWindow` too, keeping only the frame- placement half a re-attached window still needs. New suite `BoardLoadingTitlebarSwapRaceTests` drives `HostedWindowController` through the same attach sequence and pins both the fixed and (as a negative control) the unfixed shape. Claude-Session: https://claude.ai/code/session_014PtZdPwqZuqEDLc6wZMtEy
This commit is contained in:
@@ -808,6 +808,24 @@ struct BoardWindowHost: View {
|
||||
}
|
||||
self.loadingAccessory = nil
|
||||
|
||||
// **`onAttach` itself has to be replaced here, not just the stand-in it already installed**
|
||||
// (Pipeline card a73bad86, reopened). SwiftUI's dismantle-then-make swap of the provisional
|
||||
// window for the real one is not guaranteed to land before the load does
|
||||
// (`HostedWindowController.detach`'s own doc comment) — a fast load and a slow swap means the
|
||||
// real window's `attach` fires *after* this method has already run. Left alone,
|
||||
// `configureLoadingWindow`'s closure is still installed at that point, and it builds a fresh
|
||||
// stand-in and installs it on whatever window attaches next; `HostedWindowController.attach`
|
||||
// then runs `addTitlebarAccessoryIfPossible()` right after `onAttach`, installing the real
|
||||
// widget beside it — both in the titlebar, stand-in leading, and nothing left to remove it a
|
||||
// second time. "Occasionally" was exactly this ordering. The frame placement half survives,
|
||||
// because a late-swapped window is still a window whose saved frame has not been applied yet
|
||||
// — only the stand-in-building half goes.
|
||||
windowController.onAttach = { window in
|
||||
if let saved = appModel.boardRegistry.record(id: recordID)?.windowFrame {
|
||||
window.setFrame(HostedWindowController.placementOnCurrentScreens(for: saved), display: true)
|
||||
}
|
||||
}
|
||||
|
||||
// The window-title widget (03-board-ui.md § Board popover) — **board windows only**, which
|
||||
// is why it is installed here rather than in `WindowAccessor`: welcome, the bootstrap and
|
||||
// card windows share that machinery and have no board to describe. It goes in after the
|
||||
|
||||
Reference in New Issue
Block a user