Skip to content

Commit 27cb8ae

Browse files
committed
fix(webview-ui): finalize sticky follow pin; disable on expand; satisfy lint
1 parent 4a096e1 commit 27cb8ae

File tree

1 file changed

+20
-30
lines changed

1 file changed

+20
-30
lines changed

webview-ui/src/components/chat/ChatView.tsx

Lines changed: 20 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
187187
const [expandedRows, setExpandedRows] = useState<Record<number, boolean>>({})
188188
const prevExpandedRowsRef = useRef<Record<number, boolean>>()
189189
const scrollContainerRef = useRef<HTMLDivElement>(null)
190-
const disableAutoScrollRef = useRef(false)
190+
const stickyFollowRef = useRef<boolean>(false)
191191
const [showScrollToBottom, setShowScrollToBottom] = useState(false)
192192
const [isAtBottom, setIsAtBottom] = useState(false)
193193
const lastTtsRef = useRef<string>("")
@@ -508,9 +508,11 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
508508
}
509509
}
510510

511+
// Expanding a row indicates the user is browsing; disable sticky follow
511512
if (wasAnyRowExpandedByUser) {
512-
disableAutoScrollRef.current = true
513+
stickyFollowRef.current = false
513514
}
515+
514516
prevExpandedRowsRef.current = expandedRows // Store current state for next comparison
515517
}, [expandedRows])
516518

@@ -583,7 +585,6 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
583585
// Do not reset mode here as it should persist.
584586
// setPrimaryButtonText(undefined)
585587
// setSecondaryButtonText(undefined)
586-
disableAutoScrollRef.current = false
587588
}, [])
588589

589590
/**
@@ -1385,41 +1386,25 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
13851386

13861387
const handleRowHeightChange = useCallback(
13871388
(isTaller: boolean) => {
1388-
if (!disableAutoScrollRef.current) {
1389+
if (isAtBottom) {
13891390
if (isTaller) {
13901391
scrollToBottomSmooth()
13911392
} else {
13921393
setTimeout(() => scrollToBottomAuto(), 0)
13931394
}
13941395
}
13951396
},
1396-
[scrollToBottomSmooth, scrollToBottomAuto],
1397+
[scrollToBottomSmooth, scrollToBottomAuto, isAtBottom],
13971398
)
13981399

1399-
useEffect(() => {
1400-
let timer: ReturnType<typeof setTimeout> | undefined
1401-
if (!disableAutoScrollRef.current) {
1402-
timer = setTimeout(() => scrollToBottomSmooth(), 50)
1403-
}
1404-
return () => {
1405-
if (timer) {
1406-
clearTimeout(timer)
1407-
}
1408-
}
1409-
}, [groupedMessages.length, scrollToBottomSmooth])
1410-
1400+
// Disable sticky follow when user scrolls up inside the chat container
14111401
const handleWheel = useCallback((event: Event) => {
14121402
const wheelEvent = event as WheelEvent
1413-
1414-
if (wheelEvent.deltaY && wheelEvent.deltaY < 0) {
1415-
if (scrollContainerRef.current?.contains(wheelEvent.target as Node)) {
1416-
// User scrolled up
1417-
disableAutoScrollRef.current = true
1418-
}
1403+
if (wheelEvent.deltaY < 0 && scrollContainerRef.current?.contains(wheelEvent.target as Node)) {
1404+
stickyFollowRef.current = false
14191405
}
14201406
}, [])
1421-
1422-
useEvent("wheel", handleWheel, window, { passive: true }) // passive improves scrolling performance
1407+
useEvent("wheel", handleWheel, window, { passive: true })
14231408

14241409
// Effect to clear checkpoint warning when messages appear or task changes
14251410
useEffect(() => {
@@ -1867,12 +1852,15 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
18671852
increaseViewportBy={{ top: 3_000, bottom: 1000 }}
18681853
data={groupedMessages}
18691854
itemContent={itemContent}
1855+
followOutput="smooth"
18701856
atBottomStateChange={(isAtBottom: boolean) => {
18711857
setIsAtBottom(isAtBottom)
1872-
if (isAtBottom) {
1873-
disableAutoScrollRef.current = false
1858+
if (!isAtBottom && stickyFollowRef.current) {
1859+
// While in sticky mode, force-pin as streaming increases height
1860+
scrollToBottomAuto()
1861+
return
18741862
}
1875-
setShowScrollToBottom(disableAutoScrollRef.current && !isAtBottom)
1863+
setShowScrollToBottom(!isAtBottom)
18761864
}}
18771865
atBottomThreshold={10}
18781866
initialTopMostItemIndex={groupedMessages.length - 1}
@@ -1893,8 +1881,10 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
18931881
appearance="secondary"
18941882
className="flex-[2]"
18951883
onClick={() => {
1896-
scrollToBottomSmooth()
1897-
disableAutoScrollRef.current = false
1884+
// Engage sticky follow until user scrolls up
1885+
stickyFollowRef.current = true
1886+
// Pin immediately to avoid lag during fast streaming
1887+
scrollToBottomAuto()
18981888
}}>
18991889
<span className="codicon codicon-chevron-down"></span>
19001890
</VSCodeButton>

0 commit comments

Comments
 (0)