@@ -242,20 +242,16 @@ const AIViewInner: React.FC<AIViewProps> = ({ workspaceId, projectName, branch,
242242 // Get the aggregator for this workspace
243243 const aggregator = getAggregator ( workspaceId ) ;
244244
245+ // Clear stale streaming state before subscribing - backend replay is source of truth
246+ aggregator . clearActiveStreams ( ) ;
247+
245248 // Load existing messages for this workspace
246249 setDisplayedMessages ( aggregator . getDisplayedMessages ( ) ) ;
247- setCanInterrupt ( aggregator . getActiveStreams ( ) . length > 0 ) ;
248-
249- // Enable auto-scroll when switching workspaces
250- setAutoScroll ( true ) ;
250+ setCanInterrupt ( false ) ; // Will be set correctly by replay if stream is active
251251
252252 // Set loading state based on whether we have messages
253253 // This preserves streaming state when switching workspaces
254- if ( aggregator . hasMessages ( ) ) {
255- setLoading ( false ) ; // Clear loading if we have messages
256- } else {
257- setLoading ( true ) ; // Show loading only if empty
258- }
254+ setLoading ( ! aggregator . hasMessages ( ) ) ;
259255
260256 // Subscribe to workspace-specific chat channel
261257 // This will automatically send historical messages then stream new ones
@@ -266,14 +262,20 @@ const AIViewInner: React.FC<AIViewProps> = ({ workspaceId, projectName, branch,
266262 // Batch-load all historical messages at once for efficiency
267263 if ( historicalMessages . length > 0 ) {
268264 aggregator . loadHistoricalMessages ( historicalMessages ) ;
269- updateUIAndScroll ( ) ;
270265 }
271266 isCaughtUp = true ;
272267 setLoading ( false ) ;
273- // Scroll to bottom once caught up
268+ updateUIAndScroll ( ) ;
269+
270+ // After rendering, sync autoScroll with actual scroll position
274271 requestAnimationFrame ( ( ) => {
275272 if ( contentRef . current ) {
276- contentRef . current . scrollTop = contentRef . current . scrollHeight ;
273+ const isAtBottom =
274+ contentRef . current . scrollHeight -
275+ contentRef . current . scrollTop -
276+ contentRef . current . clientHeight <
277+ 100 ;
278+ setAutoScroll ( isAtBottom ) ;
277279 }
278280 } ) ;
279281 return ;
@@ -371,11 +373,6 @@ const AIViewInner: React.FC<AIViewProps> = ({ workspaceId, projectName, branch,
371373 // After caught-up: handle messages normally
372374 aggregator . handleMessage ( data ) ;
373375 updateUIAndScroll ( ) ;
374-
375- // Auto-scroll for new messages after caught up
376- if ( contentRef . current ) {
377- contentRef . current . scrollTop = contentRef . current . scrollHeight ;
378- }
379376 }
380377 }
381378 ) ;
0 commit comments