Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion webview-ui/src/components/chat/ChatView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ export interface ChatViewRef {

export const MAX_IMAGES_PER_MESSAGE = 20 // Anthropic limits to 20 images

// Viewport buffer constants
const VIEWPORT_BUFFER_AT_BOTTOM = 10_000 // Maintains scroll lock when at bottom
const VIEWPORT_BUFFER_SCROLLED_UP = 1_000 // Reduces memory usage when scrolled up
const VIEWPORT_BUFFER_TOP = 3_000

const isMac = navigator.platform.toUpperCase().indexOf("MAC") >= 0

const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewProps> = (
Expand Down Expand Up @@ -174,6 +179,8 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
const disableAutoScrollRef = useRef(false)
const [showScrollToBottom, setShowScrollToBottom] = useState(false)
const [isAtBottom, setIsAtBottom] = useState(false)
// Debounced version of isAtBottom to prevent rapid viewport buffer changes
const [debouncedIsAtBottom, setDebouncedIsAtBottom] = useState(false)
const lastTtsRef = useRef<string>("")
const [wasStreaming, setWasStreaming] = useState<boolean>(false)
const [showCheckpointWarning, setShowCheckpointWarning] = useState<boolean>(false)
Expand Down Expand Up @@ -1430,6 +1437,15 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro

useEvent("wheel", handleWheel, window, { passive: true }) // passive improves scrolling performance

// Debounce isAtBottom to prevent rapid viewport buffer changes
useEffect(() => {
const timer = setTimeout(() => {
setDebouncedIsAtBottom(isAtBottom)
}, 300)

return () => clearTimeout(timer)
}, [isAtBottom])

// Effect to handle showing the checkpoint warning after a delay
useEffect(() => {
// Only show the warning when there's a task but no visible messages yet
Expand Down Expand Up @@ -1887,7 +1903,12 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
ref={virtuosoRef}
key={task.ts}
className="scrollable grow overflow-y-scroll mb-1"
increaseViewportBy={{ top: 3_000, bottom: 1000 }}
increaseViewportBy={{
top: VIEWPORT_BUFFER_TOP,
// Dynamic bottom buffer: larger when at bottom for scroll lock,
// smaller when scrolled up for memory efficiency
bottom: debouncedIsAtBottom ? VIEWPORT_BUFFER_AT_BOTTOM : VIEWPORT_BUFFER_SCROLLED_UP,
}}
data={groupedMessages}
itemContent={itemContent}
atBottomStateChange={(isAtBottom: boolean) => {
Expand All @@ -1899,6 +1920,8 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
}}
atBottomThreshold={10}
initialTopMostItemIndex={groupedMessages.length - 1}
// Smooth scrolling when new content arrives
followOutput="smooth"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good addition! The smooth scrolling should improve UX. Could you add a comment explaining how this relates to the scroll lock fix? This would help future maintainers understand the complete solution.

/>
</div>
<div className={`flex-initial min-h-0 ${!areButtonsVisible ? "mb-1" : ""}`}>
Expand Down
Loading