11"use client" ;
22
3- import { useEffect , useRef } from "react" ;
4-
53interface Message {
64 role : string ;
75 content : string ;
@@ -17,49 +15,6 @@ export default function MessageList({
1715 messages,
1816 loading = false ,
1917} : MessageListProps ) {
20- const messagesEndRef = useRef < HTMLDivElement > ( null ) ;
21- const prevMessagesLengthRef = useRef < number > ( 0 ) ;
22- const prevLoadingRef = useRef < boolean > ( false ) ;
23-
24- // Enhanced scrolling behavior to handle:
25- // 1. New messages being added
26- // 2. Loading indicator appearing/disappearing
27- // 3. New user messages (to ensure they're always visible)
28- useEffect ( ( ) => {
29- const lastMessage = messages [ messages . length - 1 ] ;
30- const isNewUserMessage =
31- messages . length > prevMessagesLengthRef . current &&
32- lastMessage ?. role === "user" ;
33-
34- const loadingChanged = loading !== prevLoadingRef . current ;
35-
36- // Store current scroll position and total scroll height
37- const messageContainer = messagesEndRef . current ?. parentElement ;
38- if ( messagesEndRef . current && messageContainer ) {
39- // Determine if we should force scroll
40- const shouldForceScroll =
41- isNewUserMessage || // New user message added
42- loading || // Loading indicator is active
43- loadingChanged ; // Loading state changed
44-
45- // Check if we're already near the bottom
46- const isNearBottom =
47- messageContainer . scrollHeight -
48- messageContainer . scrollTop -
49- messageContainer . clientHeight <
50- 100 ;
51-
52- // Scroll if we're forced to or if we're already near the bottom
53- if ( shouldForceScroll || isNearBottom ) {
54- messagesEndRef . current . scrollIntoView ( { behavior : "smooth" } ) ;
55- }
56- }
57-
58- // Update references for next comparison
59- prevMessagesLengthRef . current = messages . length ;
60- prevLoadingRef . current = loading ;
61- } , [ messages , loading ] ) ;
62-
6318 // If no messages, show a placeholder
6419 if ( messages . length === 0 ) {
6520 return (
@@ -89,7 +44,32 @@ export default function MessageList({
8944 ) ;
9045
9146 return (
92- < div className = "overflow-y-auto" >
47+ < div
48+ className = "overflow-y-auto"
49+ ref = { ( scrollAreaRef ) => {
50+ if ( ! scrollAreaRef ) {
51+ return ;
52+ }
53+
54+ scrollAreaRef . scrollTo ( {
55+ top : scrollAreaRef . scrollHeight ,
56+ } ) ;
57+
58+ const callback : MutationCallback = ( mutationsList ) => {
59+ for ( const mutation of mutationsList ) {
60+ if ( mutation . type === "childList" ) {
61+ scrollAreaRef . scrollTo ( {
62+ top : scrollAreaRef . scrollHeight ,
63+ behavior : "smooth" ,
64+ } ) ;
65+ }
66+ }
67+ } ;
68+
69+ const observer = new MutationObserver ( callback ) ;
70+ observer . observe ( scrollAreaRef , { childList : true , subtree : false } ) ;
71+ } }
72+ >
9373 < div className = "p-4 flex flex-col gap-4 max-w-4xl mx-auto" >
9474 { messages . map ( ( message ) => (
9575 < div
@@ -124,8 +104,6 @@ export default function MessageList({
124104 < LoadingDots />
125105 </ div >
126106 ) }
127-
128- < div ref = { messagesEndRef } />
129107 </ div >
130108 </ div >
131109 ) ;
0 commit comments