@@ -54,9 +54,12 @@ export function ThinkingBlock({
5454} : ThinkingBlockProps ) {
5555 const [ isExpanded , setIsExpanded ] = useState ( false )
5656 const [ duration , setDuration ] = useState ( 0 )
57+ const [ userHasScrolledAway , setUserHasScrolledAway ] = useState ( false )
5758 const userCollapsedRef = useRef < boolean > ( false )
5859 const scrollContainerRef = useRef < HTMLDivElement > ( null )
5960 const startTimeRef = useRef < number > ( Date . now ( ) )
61+ const lastScrollTopRef = useRef ( 0 )
62+ const programmaticScrollRef = useRef ( false )
6063
6164 /**
6265 * Auto-expands block when streaming with content
@@ -67,6 +70,7 @@ export function ThinkingBlock({
6770 if ( ! isStreaming || hasFollowingContent ) {
6871 setIsExpanded ( false )
6972 userCollapsedRef . current = false
73+ setUserHasScrolledAway ( false )
7074 return
7175 }
7276
@@ -80,6 +84,7 @@ export function ThinkingBlock({
8084 if ( isStreaming && ! hasFollowingContent ) {
8185 startTimeRef . current = Date . now ( )
8286 setDuration ( 0 )
87+ setUserHasScrolledAway ( false )
8388 }
8489 } , [ isStreaming , hasFollowingContent ] )
8590
@@ -95,22 +100,65 @@ export function ThinkingBlock({
95100 return ( ) => clearInterval ( interval )
96101 } , [ isStreaming , hasFollowingContent ] )
97102
98- // Auto- scroll to bottom during streaming using interval (same as copilot chat)
103+ // Handle scroll events to detect user scrolling away
99104 useEffect ( ( ) => {
100- if ( ! isStreaming || ! isExpanded ) return
105+ const container = scrollContainerRef . current
106+ if ( ! container || ! isExpanded ) return
107+
108+ const handleScroll = ( ) => {
109+ if ( programmaticScrollRef . current ) return
110+
111+ const { scrollTop, scrollHeight, clientHeight } = container
112+ const distanceFromBottom = scrollHeight - scrollTop - clientHeight
113+ const isNearBottom = distanceFromBottom <= 20
114+
115+ const delta = scrollTop - lastScrollTopRef . current
116+ const movedUp = delta < - 2
117+
118+ if ( movedUp && ! isNearBottom ) {
119+ setUserHasScrolledAway ( true )
120+ }
121+
122+ // Re-stick if user scrolls back to bottom
123+ if ( userHasScrolledAway && isNearBottom ) {
124+ setUserHasScrolledAway ( false )
125+ }
126+
127+ lastScrollTopRef . current = scrollTop
128+ }
129+
130+ container . addEventListener ( 'scroll' , handleScroll , { passive : true } )
131+ lastScrollTopRef . current = container . scrollTop
132+
133+ return ( ) => container . removeEventListener ( 'scroll' , handleScroll )
134+ } , [ isExpanded , userHasScrolledAway ] )
135+
136+ // Smart auto-scroll: only scroll if user hasn't scrolled away
137+ useEffect ( ( ) => {
138+ if ( ! isStreaming || ! isExpanded || userHasScrolledAway ) return
101139
102140 const intervalId = window . setInterval ( ( ) => {
103141 const container = scrollContainerRef . current
104142 if ( ! container ) return
105143
106- container . scrollTo ( {
107- top : container . scrollHeight ,
108- behavior : 'smooth' ,
109- } )
144+ const { scrollTop, scrollHeight, clientHeight } = container
145+ const distanceFromBottom = scrollHeight - scrollTop - clientHeight
146+ const isNearBottom = distanceFromBottom <= 50
147+
148+ if ( isNearBottom ) {
149+ programmaticScrollRef . current = true
150+ container . scrollTo ( {
151+ top : container . scrollHeight ,
152+ behavior : 'smooth' ,
153+ } )
154+ window . setTimeout ( ( ) => {
155+ programmaticScrollRef . current = false
156+ } , 150 )
157+ }
110158 } , SCROLL_INTERVAL )
111159
112160 return ( ) => window . clearInterval ( intervalId )
113- } , [ isStreaming , isExpanded ] )
161+ } , [ isStreaming , isExpanded , userHasScrolledAway ] )
114162
115163 /**
116164 * Formats duration in milliseconds to seconds
@@ -137,6 +185,14 @@ export function ThinkingBlock({
137185 if ( ! isThinkingDone ) {
138186 return (
139187 < div className = 'mt-1 mb-0' >
188+ { /* Define shimmer keyframes */ }
189+ < style > { `
190+ @keyframes thinking-shimmer {
191+ 0% { background-position: 150% 0; }
192+ 50% { background-position: 0% 0; }
193+ 100% { background-position: -150% 0; }
194+ }
195+ ` } </ style >
140196 < button
141197 onClick = { ( ) => {
142198 setIsExpanded ( ( v ) => {
0 commit comments