@@ -23,10 +23,17 @@ export const Messages = React.memo(({
23
23
const { id } = useParams ( ) ;
24
24
const viewportRef = React . useRef < HTMLDivElement > ( null ) ;
25
25
const [ showScrollToBottom , setShowScrollToBottom ] = React . useState ( false ) ;
26
+ const initialLoadRef = React . useRef ( true ) ;
26
27
27
28
const handleScroll = React . useCallback ( ( event : Event ) => {
28
29
const viewport = event . target as HTMLDivElement ;
29
30
const isNotAtBottom = viewport . scrollHeight - viewport . scrollTop - viewport . clientHeight > 10 ;
31
+ console . log ( 'Scroll position:' , {
32
+ scrollHeight : viewport . scrollHeight ,
33
+ scrollTop : viewport . scrollTop ,
34
+ clientHeight : viewport . clientHeight ,
35
+ isNotAtBottom
36
+ } ) ;
30
37
setShowScrollToBottom ( isNotAtBottom ) ;
31
38
} , [ ] ) ;
32
39
@@ -39,15 +46,84 @@ export const Messages = React.memo(({
39
46
} ) ;
40
47
} , [ ] ) ;
41
48
49
+ // Effect for handling scroll events
42
50
React . useEffect ( ( ) => {
43
51
const viewport = viewportRef . current ;
44
52
if ( ! viewport ) return ;
45
53
46
54
viewport . addEventListener ( 'scroll' , handleScroll ) ;
47
55
// Initial check for scroll position
48
56
handleScroll ( { target : viewport } as unknown as Event ) ;
49
- return ( ) => viewport . removeEventListener ( 'scroll' , handleScroll ) ;
50
- } , [ handleScroll ] ) ;
57
+
58
+ // Check scroll position after a short delay to account for content rendering
59
+ const checkTimeout = setTimeout ( ( ) => {
60
+ handleScroll ( { target : viewport } as unknown as Event ) ;
61
+ } , 100 ) ;
62
+
63
+ return ( ) => {
64
+ viewport . removeEventListener ( 'scroll' , handleScroll ) ;
65
+ clearTimeout ( checkTimeout ) ;
66
+ } ;
67
+ } , [ handleScroll , messages , streamingAIMessageChunks ] ) ;
68
+
69
+ // Effect for initial scroll to bottom on page load
70
+ React . useEffect ( ( ) => {
71
+ if ( initialLoadRef . current && messages && messages . length > 0 && viewportRef . current ) {
72
+ // Use a timeout to ensure content is rendered before scrolling
73
+ const initialScrollTimeout = setTimeout ( ( ) => {
74
+ if ( viewportRef . current ) {
75
+ viewportRef . current . scrollTo ( {
76
+ top : viewportRef . current . scrollHeight ,
77
+ behavior : 'smooth'
78
+ } ) ;
79
+ initialLoadRef . current = false ;
80
+ }
81
+ } , 100 ) ;
82
+
83
+ return ( ) => clearTimeout ( initialScrollTimeout ) ;
84
+ }
85
+ } , [ messages ] ) ;
86
+
87
+ // Reset initialLoadRef when chat ID changes
88
+ React . useEffect ( ( ) => {
89
+ // Reset the initial load flag when the chat ID changes
90
+ initialLoadRef . current = true ;
91
+
92
+ // Attempt to scroll to bottom after a short delay
93
+ if ( id && id !== "new" ) {
94
+ const resetScrollTimeout = setTimeout ( ( ) => {
95
+ if ( viewportRef . current && messages && messages . length > 0 ) {
96
+ viewportRef . current . scrollTo ( {
97
+ top : viewportRef . current . scrollHeight ,
98
+ behavior : 'smooth'
99
+ } ) ;
100
+ }
101
+ } , 200 ) ;
102
+
103
+ return ( ) => clearTimeout ( resetScrollTimeout ) ;
104
+ }
105
+ } , [ id ] ) ;
106
+
107
+ // Scroll to bottom when streaming messages change
108
+ React . useEffect ( ( ) => {
109
+ // Only auto-scroll if we're already near the bottom or if this is the first message chunk
110
+ if ( viewportRef . current && streamingAIMessageChunks . length > 0 ) {
111
+ const viewport = viewportRef . current ;
112
+ const isNearBottom = viewport . scrollHeight - viewport . scrollTop - viewport . clientHeight < 100 ;
113
+
114
+ if ( isNearBottom || streamingAIMessageChunks . length === 1 ) {
115
+ // Use requestAnimationFrame to ensure smooth scrolling during streaming
116
+ requestAnimationFrame ( ( ) => {
117
+ if ( viewportRef . current ) {
118
+ viewportRef . current . scrollTo ( {
119
+ top : viewportRef . current . scrollHeight ,
120
+ behavior : streamingAIMessageChunks . length === 1 ? 'smooth' : 'auto'
121
+ } ) ;
122
+ }
123
+ } ) ;
124
+ }
125
+ }
126
+ } , [ streamingAIMessageChunks ] ) ;
51
127
52
128
if ( id === "new" || ! messages ) {
53
129
return < div className = "flex-1 min-h-0" > < ScrollArea className = "h-full" /> </ div > ;
@@ -99,7 +175,7 @@ export const Messages = React.memo(({
99
175
< Button
100
176
variant = "secondary"
101
177
size = "icon"
102
- className = "absolute left-1/2 -translate-x-1/2 z-50 bottom-4 rounded-full shadow-md hover:bg-accent bg-background/80 backdrop-blur-sm"
178
+ className = "absolute z-50 bottom-4 left-1/2 -translate-x-1/2 rounded-full shadow-md hover:bg-accent bg-background/80 backdrop-blur-sm"
103
179
onClick = { scrollToBottom }
104
180
>
105
181
< ArrowDown className = "h-4 w-4" />
0 commit comments