1
1
import RestartAltIcon from '@mui/icons-material/RestartAlt'
2
2
import HelpIcon from '@mui/icons-material/Help'
3
- import { Alert , Box , Drawer , Fab , FormControlLabel , Paper , Switch , Typography , useMediaQuery , useTheme } from '@mui/material'
4
- import { useEffect , useRef , useState } from 'react'
3
+ import { Alert , Box , Drawer , Fab , FormControlLabel , Paper , Switch , SxProps , Typography , useMediaQuery , useTheme } from '@mui/material'
4
+ import { useCallback , useEffect , useLayoutEffect , useRef , useState } from 'react'
5
5
import { useTranslation } from 'react-i18next'
6
6
import { useParams , useSearchParams } from 'react-router-dom'
7
7
import { DEFAULT_ASSISTANT_INSTRUCTIONS , DEFAULT_MODEL , DEFAULT_MODEL_TEMPERATURE , FREE_MODEL , inProduction , validModels } from '../../../config'
@@ -12,7 +12,7 @@ import useLocalStorageState from '../../hooks/useLocalStorageState'
12
12
import { useCourseRagIndices } from '../../hooks/useRagIndices'
13
13
import useRetryTimeout from '../../hooks/useRetryTimeout'
14
14
import useUserStatus from '../../hooks/useUserStatus'
15
- import type { Message , Prompt } from '../../types'
15
+ import type { Course , Message , Prompt } from '../../types'
16
16
import { ChatBox } from './ChatBox'
17
17
import { Conversation } from './Conversation'
18
18
import { DisclaimerModal } from './Disclaimer'
@@ -29,11 +29,14 @@ import { useIsEmbedded } from '../../contexts/EmbeddedContext'
29
29
import { enqueueSnackbar } from 'notistack'
30
30
import { useAnalyticsDispatch } from '../../stores/analytics'
31
31
import EmailButton from './EmailButton'
32
- import { ArrowDownward , MenuBookTwoTone , Tune } from '@mui/icons-material'
32
+ import { ArrowDownward , ChevronLeft , Close , MenuBookTwoTone , Tune } from '@mui/icons-material'
33
33
import { useChatScroll } from '../../hooks/useChatScroll'
34
34
import { TestUseInfoV2 } from './TestUseInfo'
35
35
import Footer from '../Footer'
36
36
import type { ToolCallResultEvent } from '../../../shared/chat'
37
+ import { ChatToolResult } from '../../../shared/tools'
38
+ import { TFunction } from 'i18next'
39
+ import { RagIndexAttributes } from '../../../shared/types'
37
40
38
41
function useLocalStorageStateWithURLDefault ( key : string , defaultValue : string , urlKey : string ) {
39
42
const [ value , setValue ] = useLocalStorageState ( key , defaultValue )
@@ -91,7 +94,7 @@ export const ChatV2 = () => {
91
94
const [ chatLeftSidePanelOpen , setChatLeftSidePanelOpen ] = useState < boolean > ( false )
92
95
// RAG states
93
96
const [ ragIndexId , setRagIndexId ] = useState < number | undefined > ( )
94
- const [ activeToolResult , setActiveToolResult ] = useState < ToolCallResultEvent | undefined > ( )
97
+ const [ activeToolResult , setActiveToolResult0 ] = useState < ToolCallResultEvent | undefined > ( )
95
98
const ragIndex = ragIndices ?. find ( ( index ) => index . id === ragIndexId )
96
99
97
100
// Analytics
@@ -286,6 +289,33 @@ export const ChatV2 = () => {
286
289
}
287
290
} , [ userStatus , course ] )
288
291
292
+ const showRagSelector = ( ragIndices ?. length ?? 0 ) > 0
293
+ const rightMenuOpen = ! ! activeToolResult
294
+ const rightMenuWidth = rightMenuOpen ? '300px' : '0px'
295
+
296
+ // Handle layout shift when right menu opens (tool result becomes visible)
297
+ const prevScrollYProportional = useRef ( 0 )
298
+ const handleLayoutShift = useCallback ( ( ) => {
299
+ // Save the current proportional scroll position
300
+ prevScrollYProportional . current = window . scrollY / document . body . scrollHeight
301
+
302
+ console . log ( 'New scroll position:' , window . scrollY , document . body . scrollHeight )
303
+
304
+ // Set timeout to restore after layout change
305
+ setTimeout ( ( ) => {
306
+ const scrollY = prevScrollYProportional . current * document . body . scrollHeight
307
+ window . scrollTo ( 0 , scrollY )
308
+ console . log ( 'Restored scroll position:' , scrollY , document . body . scrollHeight )
309
+ } , 0 )
310
+ } , [ ] )
311
+ const setActiveToolResult = useCallback (
312
+ ( toolResult : ToolCallResultEvent | undefined ) => {
313
+ handleLayoutShift ( )
314
+ setActiveToolResult0 ( toolResult )
315
+ } ,
316
+ [ handleLayoutShift ] ,
317
+ )
318
+
289
319
if ( course && course . usageLimit === 0 ) {
290
320
return (
291
321
< Box >
@@ -326,10 +356,6 @@ export const ChatV2 = () => {
326
356
}
327
357
}
328
358
329
- const showRagSelector = ( ragIndices ?. length ?? 0 ) > 0
330
- const rightMenuOpen = ! ! activeToolResult
331
- const rightMenuWidth = rightMenuOpen ? '300px' : '0px'
332
-
333
359
if ( statusLoading ) return null
334
360
335
361
return (
@@ -352,10 +378,11 @@ export const ChatV2 = () => {
352
378
} }
353
379
>
354
380
< LeftMenu
355
- sx = { { } }
356
381
course = { course }
357
382
handleReset = { handleReset }
358
- user = { user }
383
+ onClose = { ( ) => {
384
+ setChatLeftSidePanelOpen ( false )
385
+ } }
359
386
t = { t }
360
387
setSettingsModalOpen = { setSettingsModalOpen }
361
388
setDisclaimerStatus = { setDisclaimerStatus }
@@ -371,7 +398,6 @@ export const ChatV2 = () => {
371
398
sx = { { display : { sm : 'none' , md : 'flex' } , position : 'fixed' , top : 0 } }
372
399
course = { course }
373
400
handleReset = { handleReset }
374
- user = { user }
375
401
t = { t }
376
402
setSettingsModalOpen = { setSettingsModalOpen }
377
403
setDisclaimerStatus = { setDisclaimerStatus }
@@ -550,10 +576,10 @@ export const ChatV2 = () => {
550
576
}
551
577
552
578
const LeftMenu = ( {
553
- sx,
579
+ sx = { } ,
554
580
course,
555
581
handleReset,
556
- user ,
582
+ onClose ,
557
583
t,
558
584
setSettingsModalOpen,
559
585
setDisclaimerStatus,
@@ -562,6 +588,19 @@ const LeftMenu = ({
562
588
setRagIndexId,
563
589
ragIndices,
564
590
messages,
591
+ } : {
592
+ sx ?: object
593
+ course ?: Course
594
+ handleReset : ( ) => void
595
+ onClose ?: ( ) => void
596
+ t : TFunction
597
+ setSettingsModalOpen : React . Dispatch < React . SetStateAction < boolean > >
598
+ setDisclaimerStatus : React . Dispatch < React . SetStateAction < boolean > >
599
+ showRagSelector : boolean
600
+ ragIndex ?: RagIndexAttributes
601
+ setRagIndexId : React . Dispatch < React . SetStateAction < number | undefined > >
602
+ ragIndices ?: RagIndexAttributes [ ]
603
+ messages : Message [ ]
565
604
} ) => {
566
605
return (
567
606
< Box
@@ -580,7 +619,7 @@ const LeftMenu = ({
580
619
>
581
620
< Box p = "1rem" >
582
621
{ course && < ChatInfo course = { course } /> }
583
- < Box sx = { { display : 'flex' , flexDirection : 'column' , gap : '0.6rem' , mb : '2rem' } } >
622
+ < Box sx = { { display : 'flex' , flexDirection : 'column' , gap : '0.6rem' } } >
584
623
< OutlineButtonBlack startIcon = { < RestartAltIcon /> } onClick = { handleReset } id = "empty-conversation-button" >
585
624
{ t ( 'chat:emptyConversation' ) }
586
625
</ OutlineButtonBlack >
@@ -606,6 +645,11 @@ const LeftMenu = ({
606
645
) }
607
646
</ Box >
608
647
</ Box >
648
+ { onClose && (
649
+ < OutlineButtonBlack sx = { { m : '1rem' , mt : 'auto' } } onClick = { onClose } startIcon = { < ChevronLeft /> } >
650
+ { t ( 'common:close' ) }
651
+ </ OutlineButtonBlack >
652
+ ) }
609
653
< Footer />
610
654
</ Box >
611
655
)
0 commit comments