From 0a49bd6d57801f90357f427975f4512a2d83508b Mon Sep 17 00:00:00 2001 From: Louis LUO Date: Wed, 24 Jun 2026 10:42:43 +0800 Subject: [PATCH 1/3] fix: smart scroll for capsule reasoning block Only auto-scroll to bottom when user is at/near the bottom of the capsule scroll area. If user has scrolled up to read earlier content, new streaming content won't force-scroll them back down. --- src/features/message/parts/ReasoningPartView.tsx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/features/message/parts/ReasoningPartView.tsx b/src/features/message/parts/ReasoningPartView.tsx index 932735c9..6a5b0876 100644 --- a/src/features/message/parts/ReasoningPartView.tsx +++ b/src/features/message/parts/ReasoningPartView.tsx @@ -30,6 +30,7 @@ export const ReasoningPartView = memo(function ReasoningPartView({ part, isStrea const summaryContainerRef = useRef(null) const summaryMeasureRef = useRef(null) const [summaryOverflow, setSummaryOverflow] = useState(false) + const [isAtBottom, setIsAtBottom] = useState(true) const collapsedPreview = useMemo(() => (displayText || '').replace(/\s+/g, ' ').trim(), [displayText]) const thoughtDurationLabel = useMemo(() => { @@ -71,12 +72,19 @@ export const ReasoningPartView = memo(function ReasoningPartView({ part, isStrea } }, [isPartStreaming, hasContent]) + const handleCapsuleScroll = useCallback(() => { + const el = scrollAreaRef.current + if (!el) return + const atBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 32 + setIsAtBottom(atBottom) + }, []) + useEffect(() => { if (reasoningDisplayMode !== 'capsule') return - if (isPartStreaming && expanded && scrollAreaRef.current) { + if (isPartStreaming && expanded && isAtBottom && scrollAreaRef.current) { scrollAreaRef.current.scrollTop = scrollAreaRef.current.scrollHeight } - }, [displayText, isPartStreaming, expanded, reasoningDisplayMode]) + }, [displayText, isPartStreaming, expanded, isAtBottom, reasoningDisplayMode]) useEffect(() => { if (reasoningDisplayMode !== 'italic' && reasoningDisplayMode !== 'markdown') return @@ -246,7 +254,7 @@ export const ReasoningPartView = memo(function ReasoningPartView({ part, isStrea >
{shouldRenderBody && ( - +
{displayText}
From d326eb37157151d9da8e60c5c3896bdaf5cf9076 Mon Sep 17 00:00:00 2001 From: Louis LUO Date: Wed, 24 Jun 2026 10:58:20 +0800 Subject: [PATCH 2/3] fix: smart scroll for subagent task view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow the same pattern as the reasoning block fix — track whether user is at the bottom (32px threshold) and only auto-scroll when they are. --- .../message/tools/renderers/TaskRenderer.tsx | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/features/message/tools/renderers/TaskRenderer.tsx b/src/features/message/tools/renderers/TaskRenderer.tsx index fa3a1821..31e48bb6 100644 --- a/src/features/message/tools/renderers/TaskRenderer.tsx +++ b/src/features/message/tools/renderers/TaskRenderer.tsx @@ -271,6 +271,7 @@ interface SubSessionViewProps { const SubSessionView = memo(function SubSessionView({ sessionId }: SubSessionViewProps) { const { t } = useTranslation('message') const scrollRef = useRef(null) + const isAtBottomRef = useRef(true) const loadedRef = useRef(false) const subSessionMaxHeight = useResponsiveMaxHeight(0.25, 120, 240) @@ -310,11 +311,18 @@ const SubSessionView = memo(function SubSessionView({ sessionId }: SubSessionVie }) }, [sessionId]) - // 自动滚动 + const handleScroll = useCallback(() => { + const el = scrollRef.current + if (!el) return + isAtBottomRef.current = el.scrollHeight - el.scrollTop - el.clientHeight < 32 + }, []) + + // 自动滚动(仅在用户已在底部时跟随) useEffect(() => { - if (scrollRef.current && isStreaming) { - scrollRef.current.scrollTop = scrollRef.current.scrollHeight - } + if (!isStreaming) return + const el = scrollRef.current + if (!el || !isAtBottomRef.current) return + el.scrollTop = el.scrollHeight }, [messages, isStreaming]) // 过滤有内容的消息 @@ -340,6 +348,7 @@ const SubSessionView = memo(function SubSessionView({ sessionId }: SubSessionVie {/* Messages */}
From 0c729b0259b10584aa656fd8e89f530a7d112426 Mon Sep 17 00:00:00 2001 From: Louis LUO Date: Wed, 24 Jun 2026 11:11:01 +0800 Subject: [PATCH 3/3] fix: unify smart scroll threshold to 60px Align all auto-scroll thresholds (ReasoningPartView, SubSessionView) with the existing BashRenderer threshold of 60px for consistency. --- src/features/message/parts/ReasoningPartView.tsx | 2 +- src/features/message/tools/renderers/TaskRenderer.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features/message/parts/ReasoningPartView.tsx b/src/features/message/parts/ReasoningPartView.tsx index 6a5b0876..fc62c2fa 100644 --- a/src/features/message/parts/ReasoningPartView.tsx +++ b/src/features/message/parts/ReasoningPartView.tsx @@ -75,7 +75,7 @@ export const ReasoningPartView = memo(function ReasoningPartView({ part, isStrea const handleCapsuleScroll = useCallback(() => { const el = scrollAreaRef.current if (!el) return - const atBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 32 + const atBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 60 setIsAtBottom(atBottom) }, []) diff --git a/src/features/message/tools/renderers/TaskRenderer.tsx b/src/features/message/tools/renderers/TaskRenderer.tsx index 31e48bb6..3b64f7ef 100644 --- a/src/features/message/tools/renderers/TaskRenderer.tsx +++ b/src/features/message/tools/renderers/TaskRenderer.tsx @@ -314,7 +314,7 @@ const SubSessionView = memo(function SubSessionView({ sessionId }: SubSessionVie const handleScroll = useCallback(() => { const el = scrollRef.current if (!el) return - isAtBottomRef.current = el.scrollHeight - el.scrollTop - el.clientHeight < 32 + isAtBottomRef.current = el.scrollHeight - el.scrollTop - el.clientHeight < 60 }, []) // 自动滚动(仅在用户已在底部时跟随)