From bfbff69eae7067f5eddb08f985690d40a0a17135 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 8 Apr 2026 20:16:42 +0000 Subject: [PATCH 1/2] Replace GeometryReader with .onGeometryChange(for:) in ChatView MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GeometryReader at ChatView.swift:124 wrapped the entire chat view hierarchy as a layout container, forcing full O(n × depth) measurement cascades through LazyVStack on every attribute graph dirty (hover, window focus, animation tick), causing 48-83 second hangs. Replace with @State containerWidth + .onGeometryChange(for:) modifier, which observes geometry after layout completes without creating a layout node that participates in measurement. The action closure only fires when the extracted width value actually changes (built-in deduplication via Equatable), so hover/focus events cause zero state writes. Data flow is unchanged: containerWidth flows through mainContentStack(containerWidth:) -> activeConversationContent -> MessageListView -> bubbleMaxWidth environment key. Co-Authored-By: ashlee@vellum.ai --- .../Features/Chat/ChatView.swift | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/clients/macos/vellum-assistant/Features/Chat/ChatView.swift b/clients/macos/vellum-assistant/Features/Chat/ChatView.swift index 8f9e9d4f626..f2f5b8eceda 100644 --- a/clients/macos/vellum-assistant/Features/Chat/ChatView.swift +++ b/clients/macos/vellum-assistant/Features/Chat/ChatView.swift @@ -101,6 +101,7 @@ struct ChatView: View { @State private var currentMatchIndex = 0 @State private var showSkeleton = false @State private var skeletonDebounceTask: Task? = nil + @State private var containerWidth: CGFloat = 0 private var isEmptyState: Bool { viewModel.paginatedVisibleMessages.isEmpty && viewModel.isHistoryLoaded @@ -121,21 +122,23 @@ struct ChatView: View { #if DEBUG let _ = os_signpost(.event, log: PerfSignposts.log, name: "ChatView.body") #endif - GeometryReader { proxy in - let containerWidth = proxy.size.width - ZStack { - mainContentStack(containerWidth: containerWidth) - .background(alignment: .bottom) { - chatBackground - } - .background(VColor.surfaceBase) - .overlay(alignment: .bottom) { - btwOverlay - } - .animation(VAnimation.fast, value: viewModel.btwResponse != nil) + ZStack { + mainContentStack(containerWidth: containerWidth) + .background(alignment: .bottom) { + chatBackground + } + .background(VColor.surfaceBase) + .overlay(alignment: .bottom) { + btwOverlay + } + .animation(VAnimation.fast, value: viewModel.btwResponse != nil) - dropTargetOverlay - } + dropTargetOverlay + } + .onGeometryChange(for: CGFloat.self) { proxy in + proxy.size.width + } action: { newWidth in + containerWidth = newWidth } .environment(\.dropActions, currentDropActions) .onDrop(of: [.fileURL, .image, .png, .tiff], isTargeted: $isDropTargeted) { providers in From 559248a78c7b1efcaacd5c81f44ee182ac9b4e3a Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 8 Apr 2026 21:10:03 +0000 Subject: [PATCH 2/2] =?UTF-8?q?Absorb=20initial=200=E2=86=92real=20contain?= =?UTF-8?q?erWidth=20transition=20as=20first=20measurement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With @State containerWidth starting at 0, the first .onGeometryChange callback triggers handleContainerWidthChanged() which would start a spurious 100ms resize stabilization during initial load. This never happened with GeometryReader because containerWidth was always the real width from the first render. Add an early return when lastHandledContainerWidth is 0, recording the width without triggering stabilization. Subsequent real resizes still trigger normally. Co-Authored-By: ashlee@vellum.ai --- .../Features/Chat/MessageListView+Lifecycle.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/clients/macos/vellum-assistant/Features/Chat/MessageListView+Lifecycle.swift b/clients/macos/vellum-assistant/Features/Chat/MessageListView+Lifecycle.swift index 0d6922a8c08..998d9d06280 100644 --- a/clients/macos/vellum-assistant/Features/Chat/MessageListView+Lifecycle.swift +++ b/clients/macos/vellum-assistant/Features/Chat/MessageListView+Lifecycle.swift @@ -219,6 +219,14 @@ extension MessageListView { func handleContainerWidthChanged() { guard containerWidth > 0, abs(containerWidth - scrollState.lastHandledContainerWidth) > 2 else { return } + // First real measurement (0 → actual width) is not a resize — just + // record the width so subsequent changes are detected as real resizes. + // This avoids spurious resize stabilization during initial load when + // containerWidth starts at 0 (e.g. from @State + .onGeometryChange). + guard scrollState.lastHandledContainerWidth > 0 else { + scrollState.lastHandledContainerWidth = containerWidth + return + } scrollState.lastHandledContainerWidth = containerWidth // Route through coordinator for policy decision. let intents = scrollCoordinator.handle(.containerWidthChanged)