@@ -351,6 +351,7 @@ export default function SessionView(props: SessionViewProps) {
351351 let pendingScrollBehavior : ScrollBehavior = "auto" ;
352352 let lastAutoScrollAt = 0 ;
353353 let lastKnownScrollTop = 0 ;
354+ let userScrolledAwayAt = 0 ;
354355 let streamRenderBatchTimer : number | undefined ;
355356 let streamRenderBatchQueuedAt = 0 ;
356357 let streamRenderBatchReschedules = 0 ;
@@ -2292,6 +2293,9 @@ export default function SessionView(props: SessionViewProps) {
22922293 runProgressSignature ( ) ;
22932294 if ( initialAnchorPending ( ) ) return ;
22942295 if ( ! isViewingLatest ( ) ) return ;
2296+ // Suppress auto-scroll for 800ms after user scrolled away to prevent fighting manual scroll
2297+ const timeSinceUserScroll = Date . now ( ) - userScrolledAwayAt ;
2298+ if ( timeSinceUserScroll < 800 ) return ;
22952299 scheduleScrollToLatest ( "auto" ) ;
22962300 } ) ;
22972301
@@ -4224,10 +4228,14 @@ export default function SessionView(props: SessionViewProps) {
42244228 const bottomGap =
42254229 container . scrollHeight -
42264230 ( container . scrollTop + container . clientHeight ) ;
4227- if ( bottomGap <= 96 ) {
4231+ // Use a small threshold (24px) to only pin when truly at bottom
4232+ // This prevents auto-scroll from fighting manual scroll during streaming
4233+ if ( bottomGap <= 24 ) {
42284234 setIsViewingLatest ( true ) ;
42294235 } else if ( container . scrollTop < lastKnownScrollTop - 1 ) {
4236+ // User scrolled up - respect their intent
42304237 setIsViewingLatest ( false ) ;
4238+ userScrolledAwayAt = Date . now ( ) ;
42314239 }
42324240 lastKnownScrollTop = container . scrollTop ;
42334241 refreshTopClippedMessage ( ) ;
0 commit comments