From 6e675f2d8845678a31ad653cae7b6a50a48629e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=91=98=E9=98=BF=E6=B1=9F=28Relakkes?= =?UTF-8?q?=29?= Date: Sun, 10 May 2026 12:15:27 +0800 Subject: [PATCH] fix(desktop): prevent jump-to-latest button from flickering during streaming Programmatic scroll calls (scrollIntoView) trigger onScroll events mid-animation, causing updateAutoScrollState to momentarily see a non-bottom position and flash the button on/off. Guard against this with isProgrammaticScrollingRef. Co-Authored-By: Claude Sonnet 4.6 --- desktop/src/components/chat/MessageList.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/desktop/src/components/chat/MessageList.tsx b/desktop/src/components/chat/MessageList.tsx index 7c4aceb5..914e8b27 100644 --- a/desktop/src/components/chat/MessageList.tsx +++ b/desktop/src/components/chat/MessageList.tsx @@ -316,6 +316,7 @@ export function MessageList({ sessionId, compact = false }: MessageListProps = { const scrollContainerRef = useRef(null) const bottomRef = useRef(null) const shouldAutoScrollRef = useRef(true) + const isProgrammaticScrollingRef = useRef(false) const lastSessionIdRef = useRef(resolvedSessionId) const t = useTranslation() const [turnChangeCards, setTurnChangeCards] = useState([]) @@ -328,6 +329,7 @@ export function MessageList({ sessionId, compact = false }: MessageListProps = { const scrollToBottom = useCallback((behavior: ScrollBehavior) => { shouldAutoScrollRef.current = true + isProgrammaticScrollingRef.current = true bottomRef.current?.scrollIntoView?.({ behavior, block: 'end' }) const container = scrollContainerRef.current if (container && resolvedSessionId) { @@ -337,9 +339,16 @@ export function MessageList({ sessionId, compact = false }: MessageListProps = { }) } setShowJumpToLatest(false) + // Reset flag after the scroll event(s) from scrollIntoView have fired + requestAnimationFrame(() => { + isProgrammaticScrollingRef.current = false + }) }, [resolvedSessionId]) const updateAutoScrollState = useCallback(() => { + // Ignore scroll events triggered by our own programmatic scrolling to + // prevent the jump-to-latest button from flickering during auto-scroll. + if (isProgrammaticScrollingRef.current) return const container = scrollContainerRef.current if (!container) return const isAtBottom = isNearScrollBottom(container)