Repository navigation
Chat sometimes doesn't scroll all the way to bottom on new message #594
Description
Summary
The Cortex chat panel sometimes fails to scroll all the way to the bottom when a new message arrives, leaving the latest content partially or fully hidden below the fold. Manual scroll is required to see it.
Root cause
interface/src/components/CortexChatPanel.tsx:402-404:
useEffect(() => {
messagesEndRef.current?.scrollIntoView({behavior: "smooth"});
}, [messages.length, isStreaming, toolActivity.length]);The dependency array misses several height-changing events:
-
Tool result expansion is invisible to the effect. In
interface/src/hooks/useCortexChat.ts:122-133, thetool_completedSSE event updates an existingtoolActivity[i].statusfrom"running"→"done"and attaches aresult_preview. The array length is unchanged, so the effect doesn't re-fire — but the rendered tool card grows in height. If the result preview is tall, it pushes the assistant message below the fold. -
ThinkingIndicatortoggle doesn't fire it either. AtCortexChatPanel.tsx:505, the indicator is shown based on!toolActivity.some(t => t.status === "running"). That's derived state, not a length change, so its appear/disappear doesn't re-scroll. -
Async Markdown layout shifts.
<Markdown>(CortexChatPanel.tsx:493) renders synchronously, but code-block syntax highlighting, font loading, and embedded image loads reflow heights after the scroll has already finished. Nothing re-scrolls. -
behavior: "smooth"races layout. The animation locks onto the target's pixel position when it starts. Between the message arriving (messages.length++),isStreaming → falseremoving the streaming UI, andtoolActivityclearing in thefinallyblock (useCortexChat.ts:167-168), several layout shifts can happen during the in-flight smooth scroll, so it lands short.
Note: messages are not streamed token-by-token — the assistant content arrives whole on the done SSE event (useCortexChat.ts:142-151). So the bug is layout-shift-driven, not streaming-text-driven.
Steps to reproduce (intermittent)
Most reliable when:
- An assistant turn includes one or more tool calls whose
result_previewis tall. - Or the assistant response contains code blocks / images that reflow after first paint.
Expected
After any new message or tool result, the chat scrolls fully so the bottom of the latest content is visible.
Actual
Scroll stops short of the bottom; user must manually scroll down.
Suggested fix
Either of:
- Replace the effect with a
ResizeObserveron the messages container that keeps it pinned to bottom wheneverscrollHeightgrows and the user was already near the bottom (preserve scroll-up intent). - Or include tool-status in deps (e.g.
toolActivity.map(t => t.status).join(",")), switch tobehavior: "auto", and follow up with arequestAnimationFrame(or shortsetTimeout) re-scroll after layout settles — to catch async markdown reflow.
Activity
fix(chat): apply useStickToBottom to PortalTimeline