@@ -60,30 +60,54 @@ export default function Recipe({ endpoint, model, pathname }: RecipeProps) {
6060 const [ followStream , setFollowStream ] = useState ( true )
6161 const viewportRef = useRef < HTMLDivElement > ( null )
6262 const bottomRef = useRef < HTMLDivElement > ( null )
63+ const isAutoScrolling = useRef ( false )
6364
6465 // Whenever a new message arrives, keep the latest tokens in view unless
6566 // we've intentionally scrolled upward to review earlier context.
6667 useEffect ( ( ) => {
6768 if ( ! followStream ) return
69+
70+ // Mark that we're about to programmatically scroll
71+ isAutoScrolling . current = true
6872 bottomRef . current ?. scrollIntoView ( { behavior : 'smooth' , block : 'end' } )
73+
74+ // Clear the flag after scroll animation starts (~100ms is enough)
75+ const timer = setTimeout ( ( ) => {
76+ isAutoScrolling . current = false
77+ } , 100 )
78+
79+ return ( ) => clearTimeout ( timer )
6980 } , [ messages , followStream ] )
7081
7182 return (
7283 < >
7384 < Box style = { { flex : 1 , minHeight : 0 } } >
7485 < ScrollArea
75- // Mantine exposes scroll info through a forwarded ref so we can detect manual scrolling.
86+ // Mantine exposes scroll info through a forwarded ref
87+ // so we can detect manual scrolling.
7688 h = "100%"
7789 type = "auto"
7890 viewportRef = { viewportRef }
7991 onScrollPositionChange = { ( ) => {
8092 const el = viewportRef . current
8193 if ( ! el ) return
94+
8295 const distanceToBottom =
8396 el . scrollHeight - el . scrollTop - el . clientHeight
84- const nearBottomThreshold = 8 // px
97+ const nearBottomThreshold = 20 // px
8598 const nearBottom = distanceToBottom <= nearBottomThreshold
8699
100+ // If user has clearly scrolled away (>50px from bottom),
101+ // they want to stop following - even during auto-scroll
102+ if ( isAutoScrolling . current && distanceToBottom > 50 ) {
103+ isAutoScrolling . current = false
104+ setFollowStream ( false )
105+ return
106+ }
107+
108+ // Don't interfere with programmatic auto-scroll when close to bottom
109+ if ( isAutoScrolling . current ) return
110+
87111 // Pause auto-follow when the user scrolls up to read older content.
88112 setFollowStream ( nearBottom )
89113 } }
@@ -104,7 +128,7 @@ export default function Recipe({ endpoint, model, pathname }: RecipeProps) {
104128}
105129
106130// ============================================================================
107- // Message panel types and components
131+ // Message panel
108132// ============================================================================
109133
110134/*
0 commit comments