From 205bb2c56e33c1528a036531b0fbbbe6ed2ba7fb Mon Sep 17 00:00:00 2001 From: Alan Wizemann Date: Fri, 24 Apr 2026 00:17:08 +0200 Subject: [PATCH] fix(window): pin detail column's reported frame so Chat/Sessions stop resizing window MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prior fixes (4baa3d4, 9aad905, d968878) narrowed the root cause but didn't fully close the loop. Both the Chat section and the v2.3 per-project Sessions tab were still growing the window past the screen — the chat input bar ended up below the visible desktop edge, unreachable. Why the previous fixes weren't enough: - Adding `.frame(maxHeight: .infinity)` on ChatView / ProjectSessionsView / dashboardArea told each view to FILL the space they were offered, but didn't cap what they reported UP the tree as their intrinsic ideal. - `.windowResizability(.contentMinSize)` at the WindowGroup level used the content's minimum size as the window's min floor — and with VStack-based layouts (RichChatMessageList materialises every message in a plain VStack to avoid LazyVStack's whitespace bug), the minimum bubbles up as ~messages-total-height, which exceeds the screen on long sessions. This commit pins the NavigationSplitView.detail slot's reported frame explicitly. The detail column now reports: - minWidth/minHeight: 500×300 — big enough for toolbars + chat input to always fit, small enough to work on any Mac screen - idealWidth/idealHeight: 900×600 — reasonable first-launch size that fits under `.contentMinSize`'s floor without pushing past the screen - maxWidth/maxHeight: infinity — user-resizable, no ceiling With this bound intercepting the size-reporting chain, NavigationSplitView's ideal becomes 500×300 ± idealWidth/Height regardless of what ChatView or ProjectSessionsView's children want internally. The window's content-derived minimum stays bounded to a sensible value. Views still fill the offered space because their `.frame(maxHeight: .infinity)` modifiers continue to claim whatever the detail column hands them. This is a window-layout-level fix that sits above the per-view clamps in earlier commits — those stay in as defensive intra- view layout, and the new frame here handles the outer coupling to the window. 80/80 Swift tests still pass. Co-Authored-By: Claude Opus 4.7 (1M context) --- scarf/scarf/ContentView.swift | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/scarf/scarf/ContentView.swift b/scarf/scarf/ContentView.swift index a67c211..20ee087 100644 --- a/scarf/scarf/ContentView.swift +++ b/scarf/scarf/ContentView.swift @@ -17,6 +17,32 @@ struct ContentView: View { .navigationSplitViewColumnWidth(min: 180, ideal: 240, max: 360) } detail: { detailView + // The detail column's size is what NavigationSplitView + // reports up to the window. Without a bound here, the + // reported ideal is derived from the currently-rendered + // section's natural intrinsic size — and some sections + // (Chat with a fully-materialized message list, the + // v2.3 per-project Sessions tab) have intrinsic heights + // that exceed the screen. With `.windowResizability + // (.contentMinSize)` in scarfApp, the window is forced + // at least that tall, pushing its bottom edge past the + // visible desktop and hiding the input bar. + // + // This frame pins the detail's reported ideal at a + // modest 900×600 — small enough to fit any reasonable + // screen — while allowing it to expand freely to + // whatever the user drags the window to. `minHeight: 0` + // is load-bearing: it overrides the "my child's min is + // huge" chain so NavigationSplitView doesn't carry a + // massive min up to the window. + .frame( + minWidth: 500, + idealWidth: 900, + maxWidth: .infinity, + minHeight: 300, + idealHeight: 600, + maxHeight: .infinity + ) .toolbar { ToolbarItem(placement: .navigation) { ServerSwitcherToolbar()