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 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)