@@ -36,6 +36,11 @@ export function useScrollAnchor(isStreaming: boolean, content?: string) {
3636 const stickyRef = useRef ( false )
3737 // Tracks the user's last intentional position; updated only on genuine user events, never programmatic ones.
3838 const intendedScrollTopRef = useRef ( 0 )
39+ // Mirrors the spacer's current minHeight so onScroll can check it without a layout read.
40+ // Re-engagement is blocked while the spacer is active: the spacer inflates scrollHeight to
41+ // exactly targetScrollTop + clientHeight, so distanceFromBottom = 0 after restoration —
42+ // which would otherwise falsely trigger the re-engage branch and clear the spacer.
43+ const spacerHeightRef = useRef ( 0 )
3944
4045 const scrollToBottom = useCallback ( ( ) => {
4146 const el = containerRef . current
@@ -59,7 +64,10 @@ export function useScrollAnchor(isStreaming: boolean, content?: string) {
5964 if ( hasUserScrolledRef . current ) {
6065 intendedScrollTopRef . current = el . scrollTop
6166 const distanceFromBottom = el . scrollHeight - el . scrollTop - el . clientHeight
62- if ( distanceFromBottom <= NEAR_BOTTOM_THRESHOLD ) {
67+ // Only re-engage when the spacer is not active. While the spacer is inflated,
68+ // distanceFromBottom reads 0 after programmatic scroll restoration — that is
69+ // artificial proximity, not the user genuinely reaching the document bottom.
70+ if ( distanceFromBottom <= NEAR_BOTTOM_THRESHOLD && spacerHeightRef . current === 0 ) {
6371 hasUserScrolledRef . current = false
6472 stickyRef . current = true
6573 }
@@ -132,6 +140,7 @@ export function useScrollAnchor(isStreaming: boolean, content?: string) {
132140 if ( ! el ) return
133141
134142 if ( ! hasUserScrolledRef . current || ! isStreaming ) {
143+ spacerHeightRef . current = 0
135144 if ( spacer ) spacer . style . minHeight = '0'
136145 return
137146 }
@@ -148,6 +157,7 @@ export function useScrollAnchor(isStreaming: boolean, content?: string) {
148157 prevSpacerHeight
149158 )
150159
160+ spacerHeightRef . current = shortage
151161 if ( spacer ) spacer . style . minHeight = `${ shortage } px`
152162 if ( el . scrollTop < targetScrollTop ) el . scrollTop = targetScrollTop
153163 } , [ content , isStreaming ] )
0 commit comments