diff --git a/frontend/src/components/features/chat/chat-interface.tsx b/frontend/src/components/features/chat/chat-interface.tsx index 1eb8c44867..86acca7982 100644 --- a/frontend/src/components/features/chat/chat-interface.tsx +++ b/frontend/src/components/features/chat/chat-interface.tsx @@ -127,7 +127,7 @@ export function ChatInterface() {
onChatBodyScroll(e.currentTarget)} - className="flex flex-col grow overflow-y-auto overflow-x-hidden px-4 pt-4 gap-2" + className="flex flex-col grow overflow-y-auto overflow-x-hidden px-4 pt-4 gap-2 fast-smooth-scroll" > {isLoadingMessages && (
diff --git a/frontend/src/components/features/jupyter/jupyter.tsx b/frontend/src/components/features/jupyter/jupyter.tsx index bae651f9f4..0ce1480c94 100644 --- a/frontend/src/components/features/jupyter/jupyter.tsx +++ b/frontend/src/components/features/jupyter/jupyter.tsx @@ -20,7 +20,7 @@ export function JupyterEditor({ maxWidth }: JupyterEditorProps) {
onChatBodyScroll(e.currentTarget)} > diff --git a/frontend/src/components/shared/modals/security/invariant/invariant.tsx b/frontend/src/components/shared/modals/security/invariant/invariant.tsx index 545386e3bd..471264f851 100644 --- a/frontend/src/components/shared/modals/security/invariant/invariant.tsx +++ b/frontend/src/components/shared/modals/security/invariant/invariant.tsx @@ -131,7 +131,10 @@ function SecurityInvariant() { {t(I18nKey.INVARIANT$EXPORT_TRACE_LABEL)}
-
+
{logs.map((log: SecurityAnalyzerLog, index: number) => (
) { - // for auto-scroll + // Track whether we should auto-scroll to the bottom when content changes + const [shouldScrollToBottom, setShouldScrollToBottom] = useState(true); - const [autoScroll, setAutoScroll] = useState(true); + // Track whether the user is currently at the bottom of the scroll area const [hitBottom, setHitBottom] = useState(true); - const onChatBodyScroll = (e: HTMLElement) => { - const bottomHeight = e.scrollTop + e.clientHeight; + // Check if the scroll position is at the bottom + const isAtBottom = useCallback((element: HTMLElement): boolean => { + const bottomThreshold = 10; // Pixels from bottom to consider "at bottom" + const bottomPosition = element.scrollTop + element.clientHeight; + return bottomPosition >= element.scrollHeight - bottomThreshold; + }, []); - const isHitBottom = bottomHeight >= e.scrollHeight - 10; + // Handle scroll events + const onChatBodyScroll = useCallback( + (e: HTMLElement) => { + const isCurrentlyAtBottom = isAtBottom(e); + setHitBottom(isCurrentlyAtBottom); - setHitBottom(isHitBottom); - setAutoScroll(isHitBottom); - }; + // Only update shouldScrollToBottom when user manually scrolls + // This prevents content changes from affecting our scroll behavior decision + setShouldScrollToBottom(isCurrentlyAtBottom); + }, + [isAtBottom], + ); - function scrollDomToBottom() { + // Scroll to bottom function with animation + const scrollDomToBottom = useCallback(() => { const dom = scrollRef.current; if (dom) { requestAnimationFrame(() => { - setAutoScroll(true); - dom.scrollTo({ top: dom.scrollHeight, behavior: "auto" }); + // Set shouldScrollToBottom to true when manually scrolling to bottom + setShouldScrollToBottom(true); + setHitBottom(true); + + // Use smooth scrolling but with a fast duration + dom.scrollTo({ + top: dom.scrollHeight, + behavior: "smooth", + }); }); } - } + }, [scrollRef]); - // auto scroll + // Auto-scroll effect that runs when content changes useEffect(() => { - if (autoScroll) { - scrollDomToBottom(); + // Only auto-scroll if the user was already at the bottom + if (shouldScrollToBottom) { + const dom = scrollRef.current; + if (dom) { + requestAnimationFrame(() => { + dom.scrollTo({ + top: dom.scrollHeight, + behavior: "smooth", + }); + }); + } } }); return { scrollRef, - autoScroll, - setAutoScroll, + autoScroll: shouldScrollToBottom, + setAutoScroll: setShouldScrollToBottom, scrollDomToBottom, hitBottom, setHitBottom, diff --git a/frontend/src/index.css b/frontend/src/index.css index 00a234baa4..c69bd60392 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -57,3 +57,9 @@ code { .markdown-body td { padding: 0.1rem 1rem; } + +/* Fast smooth scrolling for chat interface */ +.fast-smooth-scroll { + scroll-behavior: smooth; + scroll-timeline: 100ms; +}