@@ -20,7 +20,9 @@ import {
2020 Ref ,
2121 useCallback ,
2222 useEffect ,
23+ useLayoutEffect ,
2324 useMemo ,
25+ useRef ,
2426 useState ,
2527} from 'react' ;
2628import {
@@ -128,6 +130,27 @@ const useStyles = makeStyles(theme => ({
128130 padding : 0 ,
129131 margin : 0 ,
130132 } ,
133+ // Outer content wrapper (library may override overflow; we rely on inner scroll wrapper).
134+ chatbotContent : {
135+ minHeight : 0 ,
136+ display : 'flex' ,
137+ flexDirection : 'column' ,
138+ flex : 1 ,
139+ } ,
140+ // Inner scroll container we control: always scrollable so zoomed-in users see full content.
141+ chatbotContentScroll : {
142+ minHeight : 0 ,
143+ flex : 1 ,
144+ display : 'flex' ,
145+ flexDirection : 'column' ,
146+ overflowY : 'auto' ,
147+ WebkitOverflowScrolling : 'touch' ,
148+ } ,
149+ // When present, pushes welcome content to bottom (zoom out). Scroll up to see important box (zoom in).
150+ chatbotContentSpacer : {
151+ flex : 1 ,
152+ minHeight : 0 ,
153+ } ,
131154} ) ) ;
132155
133156type LightspeedChatProps = {
@@ -165,6 +188,8 @@ export const LightspeedChat = ({
165188 const [ isDeleteModalOpen , setIsDeleteModalOpen ] = useState < boolean > ( false ) ;
166189 const [ isRenameModalOpen , setIsRenameModalOpen ] = useState < boolean > ( false ) ;
167190 const [ isSortSelectOpen , setIsSortSelectOpen ] = useState < boolean > ( false ) ;
191+ const contentScrollRef = useRef < HTMLDivElement > ( null ) ;
192+ const bottomSentinelRef = useRef < HTMLDivElement > ( null ) ;
168193 const { isReady, lastOpenedId, setLastOpenedId, clearLastOpenedId } =
169194 useLastOpenedConversation ( user ) ;
170195 const {
@@ -342,6 +367,10 @@ export const LightspeedChat = ({
342367 if ( ! isFullscreenMode ) {
343368 setIsChatHistoryDrawerOpen ( false ) ;
344369 }
370+ } else {
371+ // Already on new chat: reset so scroll/layout works (e.g. after opening new chat again from another convo then back).
372+ setMessages ( [ ] ) ;
373+ setNewChatCreated ( true ) ;
345374 }
346375 } ) ( ) ;
347376 } , [
@@ -572,6 +601,40 @@ export const LightspeedChat = ({
572601 } )
573602 : [ ] ;
574603
604+ // Scroll to bottom when welcome content appears (sentinel + useLayoutEffect/RAF + ResizeObserver).
605+ useLayoutEffect ( ( ) => {
606+ if ( welcomePrompts . length === 0 ) return undefined ;
607+ const el = contentScrollRef . current ;
608+ const sentinel = bottomSentinelRef . current ;
609+ if ( ! el ) return undefined ;
610+
611+ const scrollToBottom = ( ) => {
612+ if ( sentinel && typeof sentinel . scrollIntoView === 'function' ) {
613+ sentinel . scrollIntoView ( { block : 'end' , behavior : 'auto' } ) ;
614+ } else {
615+ el . scrollTop = el . scrollHeight ;
616+ }
617+ } ;
618+
619+ const rafId =
620+ typeof requestAnimationFrame !== 'undefined'
621+ ? requestAnimationFrame ( ( ) => scrollToBottom ( ) )
622+ : null ;
623+ if ( rafId === null ) scrollToBottom ( ) ;
624+ const resizeObserver =
625+ typeof ResizeObserver !== 'undefined'
626+ ? new ResizeObserver ( ( ) => scrollToBottom ( ) )
627+ : undefined ;
628+ resizeObserver ?. observe ( el ) ;
629+
630+ return ( ) => {
631+ if ( rafId !== null && typeof cancelAnimationFrame !== 'undefined' ) {
632+ cancelAnimationFrame ( rafId ) ;
633+ }
634+ resizeObserver ?. disconnect ( ) ;
635+ } ;
636+ } , [ welcomePrompts . length ] ) ;
637+
575638 const handleFilter = useCallback ( ( value : string ) => {
576639 setFilterValue ( value ) ;
577640 } , [ ] ) ;
@@ -752,7 +815,7 @@ export const LightspeedChat = ({
752815 style : isFullscreenMode ? undefined : { zIndex : 1300 } ,
753816 } }
754817 reverseButtonOrder
755- displayMode = { ChatbotDisplayMode . embedded }
818+ displayMode = { displayMode }
756819 onDrawerToggle = { onChatHistoryDrawerToggle }
757820 title = ""
758821 navTitleIcon = { null }
@@ -811,19 +874,34 @@ export const LightspeedChat = ({
811874 </ div >
812875 ) }
813876
814- < ChatbotContent >
815- < LightspeedChatBox
816- userName = { userName }
817- messages = { messages }
818- profileLoading = { profileLoading }
819- announcement = { announcement }
820- ref = { scrollToBottomRef }
821- welcomePrompts = { welcomePrompts }
822- conversationId = { conversationId }
823- isStreaming = { isSendButtonDisabled }
824- topicRestrictionEnabled = { topicRestrictionEnabled }
825- displayMode = { displayMode }
826- />
877+ < ChatbotContent className = { classes . chatbotContent } >
878+ < div
879+ ref = { contentScrollRef }
880+ className = { classes . chatbotContentScroll }
881+ >
882+ { welcomePrompts . length > 0 && (
883+ < div className = { classes . chatbotContentSpacer } aria-hidden />
884+ ) }
885+ < LightspeedChatBox
886+ userName = { userName }
887+ messages = { messages }
888+ profileLoading = { profileLoading }
889+ announcement = { announcement }
890+ ref = { scrollToBottomRef }
891+ welcomePrompts = { welcomePrompts }
892+ conversationId = { conversationId }
893+ isStreaming = { isSendButtonDisabled }
894+ topicRestrictionEnabled = { topicRestrictionEnabled }
895+ displayMode = { displayMode }
896+ />
897+ { welcomePrompts . length > 0 && (
898+ < div
899+ ref = { bottomSentinelRef }
900+ aria-hidden
901+ style = { { height : 0 , flexShrink : 0 } }
902+ />
903+ ) }
904+ </ div >
827905 </ ChatbotContent >
828906 < ChatbotFooter className = { classes . footer } >
829907 < FilePreview />
0 commit comments