diff --git a/web/src/hooks/useAutoScroll.stories.tsx b/web/src/hooks/useAutoScroll.stories.tsx index 710f4dc9f..f50675c5b 100644 --- a/web/src/hooks/useAutoScroll.stories.tsx +++ b/web/src/hooks/useAutoScroll.stories.tsx @@ -14,9 +14,10 @@ const AutoScrollDemo = () => { const containerRef = useRef(null); const [messages, setMessages] = useState([]); const [isAutoAddEnabled, setIsAutoAddEnabled] = useState(false); + const [enabled, setEnabled] = useState(false); const intervalRef = useRef(); const { isAutoScrollEnabled, scrollToBottom, scrollToTop, enableAutoScroll, disableAutoScroll } = - useAutoScroll(containerRef); + useAutoScroll(containerRef, { enabled, observeSubTree: true }); const addMessage = () => { const newMessage: Message = { @@ -80,6 +81,13 @@ const AutoScrollDemo = () => { }`}> Auto Add {isAutoAddEnabled ? 'ON' : 'OFF'} +
@@ -210,7 +218,7 @@ export const ScrollAreaComponentWithAutoScroll: Story = { scrollToTop, enableAutoScroll, disableAutoScroll - } = useAutoScroll(containerRef, { observeDeepChanges: true }); + } = useAutoScroll(containerRef, {}); const addCard = useCallback(() => { setCards((prev) => [...prev, generateCard(prev.length + 1)]); @@ -309,7 +317,7 @@ export const RapidTextAppend: Story = { const intervalRef = useRef(); const { isAutoScrollEnabled, enableAutoScroll, disableAutoScroll } = useAutoScroll( containerRef, - { observeDeepChanges: true } + { observeSubTree: true, observeCharacterData: true, observeAttributes: false } ); const addWord = useCallback(() => { diff --git a/web/src/hooks/useAutoScroll.ts b/web/src/hooks/useAutoScroll.ts index 2847008c3..a1b78b08a 100644 --- a/web/src/hooks/useAutoScroll.ts +++ b/web/src/hooks/useAutoScroll.ts @@ -20,7 +20,9 @@ interface UseAutoScrollOptions { * If true, the hook will observe changes to the container's content and scroll position. * If false, the hook will only observe changes to the container's scroll position. */ - observeDeepChanges?: boolean; + observeSubTree?: boolean; + observeCharacterData?: boolean; + observeAttributes?: boolean; /** * Duration in milliseconds to continue animations after a mutation. @@ -72,7 +74,9 @@ export const useAutoScroll = ( enabled = true, bottomThreshold = 50, chaseEasing = 0.2, - observeDeepChanges = true, + observeSubTree = true, + observeCharacterData = false, + observeAttributes = false, animationCooldown = 500 } = options; @@ -150,7 +154,8 @@ export const useAutoScroll = ( // Set up the mutation observer useEffect(() => { const container = containerRef.current; - if (!container || !isAutoScrollEnabled || !enabled) return; + + if (!container || !isAutoScrollEnabled) return; // Clean up previous observer if it exists if (observerRef.current) { @@ -169,9 +174,9 @@ export const useAutoScroll = ( // Configure observer to watch for changes const observerConfig = { childList: true, - subtree: observeDeepChanges, - characterData: observeDeepChanges, - attributes: observeDeepChanges + subtree: observeSubTree, + characterData: observeCharacterData, + attributes: observeAttributes }; // Start observing @@ -201,19 +206,22 @@ export const useAutoScroll = ( isAutoScrollEnabled, containerRef, startScrollAnimation, - observeDeepChanges, + observeSubTree, + observeCharacterData, + observeAttributes, animationCooldown ]); // Listen for user–initiated events. Only disable auto–scroll if the container isn't near the bottom. useEffect(() => { const container = containerRef.current; - if (!container || !isAutoScrollEnabled || !enabled) return; + if (!container || !isAutoScrollEnabled) return; const disableAutoScrollHandler = () => { // Only disable auto–scroll if we're not near the bottom. if (!isAtBottom(container, bottomThreshold)) { setIsAutoScrollEnabled(false); + console.log('disableAutoScrollHandler', isAutoScrollEnabled, enabled); // Stop any ongoing animations if (rAFIdRef.current) { @@ -235,7 +243,7 @@ export const useAutoScroll = ( container.removeEventListener('touchstart', disableAutoScrollHandler); container.removeEventListener('mousedown', disableAutoScrollHandler); }; - }, [containerRef, bottomThreshold, enabled]); + }, [containerRef, bottomThreshold, isAutoScrollEnabled]); // Listen for scroll events. If the user scrolls back close to the bottom, re-enable auto–scroll. useEffect(() => { @@ -252,11 +260,7 @@ export const useAutoScroll = ( return () => { container.removeEventListener('scroll', onScroll); }; - }, [containerRef, bottomThreshold]); - - useEffect(() => { - setIsAutoScrollEnabled(enabled); - }, [enabled]); + }, [containerRef, isAutoScrollEnabled, bottomThreshold]); // Exposed functions. diff --git a/web/src/layouts/ChatLayout/ChatContainer/ChatContent/ChatContent.tsx b/web/src/layouts/ChatLayout/ChatContainer/ChatContent/ChatContent.tsx index 578bdaa1c..cd877610d 100644 --- a/web/src/layouts/ChatLayout/ChatContainer/ChatContent/ChatContent.tsx +++ b/web/src/layouts/ChatLayout/ChatContainer/ChatContent/ChatContent.tsx @@ -13,26 +13,20 @@ const autoClass = 'mx-auto max-w-[600px] w-full'; export const ChatContent: React.FC<{}> = React.memo(() => { const chatId = useChatIndividualContextSelector((state) => state.chatId); const chatMessageIds = useChatIndividualContextSelector((state) => state.chatMessageIds); - const containerRef = useRef(null); - const [autoMessages, setAutoMessages] = useState([]); - const { isAutoScrollEnabled, scrollToBottom } = useAutoScroll(containerRef, { - observeDeepChanges: true + + const { isAutoScrollEnabled, scrollToBottom, enableAutoScroll } = useAutoScroll(containerRef, { + observeSubTree: true, + enabled: false }); useEffect(() => { const container = document.getElementById(CHAT_CONTENT_CONTAINER_ID); if (!container) return; - console.log('ADD IN A TODO ABOUT IS COMPLETED STREAM'); containerRef.current = container; - - setInterval(() => { - setAutoMessages((prev) => [...prev, 'This is a test ' + prev.length]); - }, 22220); + enableAutoScroll(); }, []); - console.log('isAutoScrollEnabled', isAutoScrollEnabled); - return ( <>
@@ -41,14 +35,6 @@ export const ChatContent: React.FC<{}> = React.memo(() => {
))} - -
- {autoMessages.map((message, index) => ( - - {message} - - ))} -
diff --git a/web/src/layouts/ChatLayout/ChatContainer/ChatContent/ChatScrollToBottom.tsx b/web/src/layouts/ChatLayout/ChatContainer/ChatContent/ChatScrollToBottom.tsx index d045e19a7..e9521649c 100644 --- a/web/src/layouts/ChatLayout/ChatContainer/ChatContent/ChatScrollToBottom.tsx +++ b/web/src/layouts/ChatLayout/ChatContainer/ChatContent/ChatScrollToBottom.tsx @@ -9,18 +9,23 @@ export const ChatScrollToBottom: React.FC<{ scrollToBottom: ReturnType['scrollToBottom']; }> = React.memo(({ isAutoScrollEnabled, scrollToBottom }) => { return ( - - - +
+ + + +
); });