@@ -1112,15 +1112,15 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess
1112
1112
const isNearBottom = useCallback ( ( ) => {
1113
1113
if ( ! scrollContainerRef . current ) return false ;
1114
1114
const { scrollTop, scrollHeight, clientHeight } = scrollContainerRef . current ;
1115
- // Consider "near bottom" if within 100px of the bottom
1116
- return scrollHeight - scrollTop - clientHeight < 100 ;
1115
+ // Consider "near bottom" if within 50px of the bottom
1116
+ return scrollHeight - scrollTop - clientHeight < 50 ;
1117
1117
} , [ ] ) ;
1118
1118
1119
1119
// Handle scroll events to detect when user manually scrolls up
1120
1120
const handleScroll = useCallback ( ( ) => {
1121
1121
if ( scrollContainerRef . current ) {
1122
- const wasNearBottom = isNearBottom ( ) ;
1123
- setIsUserScrolledUp ( ! wasNearBottom ) ;
1122
+ const nearBottom = isNearBottom ( ) ;
1123
+ setIsUserScrolledUp ( ! nearBottom ) ;
1124
1124
}
1125
1125
} , [ isNearBottom ] ) ;
1126
1126
@@ -1540,13 +1540,12 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess
1540
1540
} ) ;
1541
1541
1542
1542
useEffect ( ( ) => {
1543
- // Only auto-scroll to bottom when new messages arrive if:
1544
- // 1. Auto-scroll is enabled in settings
1545
- // 2. User hasn't manually scrolled up
1543
+ // Auto-scroll to bottom when new messages arrive
1546
1544
if ( scrollContainerRef . current && chatMessages . length > 0 ) {
1547
1545
if ( autoScrollToBottom ) {
1546
+ // If auto-scroll is enabled, always scroll to bottom unless user has manually scrolled up
1548
1547
if ( ! isUserScrolledUp ) {
1549
- setTimeout ( ( ) => scrollToBottom ( ) , 0 ) ;
1548
+ setTimeout ( ( ) => scrollToBottom ( ) , 50 ) ; // Small delay to ensure DOM is updated
1550
1549
}
1551
1550
} else {
1552
1551
// When auto-scroll is disabled, preserve the visual position
@@ -1564,12 +1563,15 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess
1564
1563
}
1565
1564
} , [ chatMessages . length , isUserScrolledUp , scrollToBottom , autoScrollToBottom ] ) ;
1566
1565
1567
- // Scroll to bottom when component mounts with existing messages
1566
+ // Scroll to bottom when component mounts with existing messages or when messages first load
1568
1567
useEffect ( ( ) => {
1569
- if ( scrollContainerRef . current && chatMessages . length > 0 && autoScrollToBottom ) {
1570
- setTimeout ( ( ) => scrollToBottom ( ) , 100 ) ; // Small delay to ensure rendering
1568
+ if ( scrollContainerRef . current && chatMessages . length > 0 ) {
1569
+ // Always scroll to bottom when messages first load (user expects to see latest)
1570
+ // Also reset scroll state
1571
+ setIsUserScrolledUp ( false ) ;
1572
+ setTimeout ( ( ) => scrollToBottom ( ) , 200 ) ; // Longer delay to ensure full rendering
1571
1573
}
1572
- } , [ scrollToBottom , autoScrollToBottom ] ) ;
1574
+ } , [ chatMessages . length > 0 , scrollToBottom ] ) ; // Trigger when messages first appear
1573
1575
1574
1576
// Add scroll event listener to detect user scrolling
1575
1577
useEffect ( ( ) => {
@@ -1636,8 +1638,9 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess
1636
1638
can_interrupt : true
1637
1639
} ) ;
1638
1640
1639
- // Always scroll to bottom when user sends a message (they're actively participating)
1640
- setTimeout ( ( ) => scrollToBottom ( ) , 0 ) ;
1641
+ // Always scroll to bottom when user sends a message and reset scroll state
1642
+ setIsUserScrolledUp ( false ) ; // Reset scroll state so auto-scroll works for Claude's response
1643
+ setTimeout ( ( ) => scrollToBottom ( ) , 100 ) ; // Longer delay to ensure message is rendered
1641
1644
1642
1645
// Session Protection: Mark session as active to prevent automatic project updates during conversation
1643
1646
// This is crucial for maintaining chat state integrity. We handle two cases:
@@ -1882,21 +1885,21 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess
1882
1885
) }
1883
1886
1884
1887
< div ref = { messagesEndRef } />
1885
-
1886
- { /* Floating scroll to bottom button */ }
1887
- { isUserScrolledUp && chatMessages . length > 0 && (
1888
- < button
1889
- onClick = { scrollToBottom }
1890
- className = "absolute bottom-4 right-4 w-10 h-10 bg-blue-600 hover:bg-blue-700 text-white rounded-full shadow-lg flex items-center justify-center transition-all duration-200 hover:scale-105 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:ring-offset-gray-800 z-10"
1891
- title = "Scroll to bottom"
1892
- >
1893
- < svg className = "w-5 h-5" fill = "none" stroke = "currentColor" viewBox = "0 0 24 24" >
1894
- < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 2 } d = "M19 14l-7 7m0 0l-7-7m7 7V3" />
1895
- </ svg >
1896
- </ button >
1897
- ) }
1898
1888
</ div >
1899
1889
1890
+ { /* Floating scroll to bottom button - positioned outside scrollable container */ }
1891
+ { isUserScrolledUp && chatMessages . length > 0 && (
1892
+ < button
1893
+ onClick = { scrollToBottom }
1894
+ className = "fixed bottom-20 sm:bottom-24 right-4 sm:right-6 w-12 h-12 bg-blue-600 hover:bg-blue-700 text-white rounded-full shadow-lg flex items-center justify-center transition-all duration-200 hover:scale-105 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:ring-offset-gray-800 z-50"
1895
+ title = "Scroll to bottom"
1896
+ >
1897
+ < svg className = "w-5 h-5" fill = "none" stroke = "currentColor" viewBox = "0 0 24 24" >
1898
+ < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 2 } d = "M19 14l-7 7m0 0l-7-7m7 7V3" />
1899
+ </ svg >
1900
+ </ button >
1901
+ ) }
1902
+
1900
1903
{ /* Input Area - Fixed Bottom */ }
1901
1904
< div className = { `p-2 sm:p-4 md:p-6 flex-shrink-0 ${
1902
1905
isInputFocused ? 'pb-2 sm:pb-4 md:pb-6' : 'pb-16 sm:pb-4 md:pb-6'
@@ -1977,8 +1980,8 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess
1977
1980
</ svg >
1978
1981
</ button >
1979
1982
) }
1980
- { /* Mic button */ }
1981
- < div className = "absolute right-16 sm:right-16 top-1/2 transform -translate-y-1/2" >
1983
+ { /* Mic button - HIDDEN */ }
1984
+ < div className = "absolute right-16 sm:right-16 top-1/2 transform -translate-y-1/2" style = { { display : 'none' } } >
1982
1985
< MicButton
1983
1986
onTranscript = { handleTranscript }
1984
1987
className = "w-10 h-10 sm:w-10 sm:h-10"
0 commit comments