Skip to content

Commit b010f70

Browse files
committed
enhance conversation mechanics
1 parent d9592d3 commit b010f70

File tree

4 files changed

+55
-27
lines changed

4 files changed

+55
-27
lines changed

src/client/components/ChatV2/ChatBox.tsx

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Send } from '@mui/icons-material'
2+
import StopIcon from '@mui/icons-material/Stop';
23
import { Box, IconButton, TextField, Typography } from '@mui/material'
3-
import { useEffect, useState } from 'react'
4+
import { useState, useRef } from 'react'
45
import useUserStatus from '../../hooks/useUserStatus'
56
import { useParams } from 'react-router-dom'
67
import { useTranslation } from 'react-i18next'
@@ -10,14 +11,25 @@ export const ChatBox = ({ disabled, onSubmit }: { disabled: boolean; onSubmit: (
1011
const { userStatus, isLoading: statusLoading, refetch: refetchStatus } = useUserStatus(courseId)
1112
const [message, setMessage] = useState<string>('')
1213

14+
const textFieldRef = useRef<HTMLInputElement>(null)
15+
1316
const { t, i18n } = useTranslation()
1417
const handleSubmit = (e: React.FormEvent) => {
1518
e.preventDefault()
19+
20+
// This is here to prevent the form from submitting but because disabling it in the component breaks re-focusing on the text field
21+
if (disabled) return
22+
23+
1624
if (message.trim()) {
1725
onSubmit(message)
1826
setMessage('')
1927
refetchStatus()
2028
}
29+
30+
if (textFieldRef.current) {
31+
textFieldRef.current.focus()
32+
}
2133
}
2234

2335
if (statusLoading) {
@@ -44,19 +56,25 @@ export const ChatBox = ({ disabled, onSubmit }: { disabled: boolean; onSubmit: (
4456
<Box sx={{ border: '1px solid rgba(0,0,0,0.4)', borderRadius: '0.2rem' }}>
4557

4658
<TextField
59+
ref={textFieldRef}
60+
autoFocus
4761
value={message}
4862
onChange={(e) => setMessage(e.target.value)}
4963
placeholder="Kirjoita viestisi tähän..."
5064
fullWidth
5165
multiline
5266
maxRows={8}
53-
disabled={disabled}
5467
variant="standard"
5568
sx={{ padding: '0.5rem 1rem' }}
5669
slotProps={{
5770
input: {
5871
disableUnderline: true,
59-
endAdornment: (
72+
endAdornment: disabled ? (
73+
// TODO: finish the stop signal API
74+
<IconButton disabled={!disabled}>
75+
<StopIcon />
76+
</IconButton>
77+
) : (
6078
<IconButton disabled={disabled} type="submit">
6179
<Send />
6280
</IconButton>

src/client/components/ChatV2/ChatV2.tsx

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -208,19 +208,33 @@ export const ChatV2 = () => {
208208
}
209209

210210
useEffect(() => {
211-
// Keep this useEffect for autoscroll effect
211+
// Scrolls to bottom on initial load only
212+
if (!appContainerRef.current || !conversationRef.current || !settingsRef.current || messages.length === 0) return
213+
if (isCompletionDone) {
214+
const container = appContainerRef.current
215+
if (container) {
216+
container.scrollTo({
217+
top: container.scrollHeight,
218+
behavior: 'instant',
219+
})
220+
}
221+
}
222+
}, [])
223+
224+
useEffect(() => {
225+
// Scrolls to last assistant message on text generation
212226
if (!appContainerRef.current || !conversationRef.current || !settingsRef.current || messages.length === 0) return
213227

214228
const lastNode = conversationRef.current.lastElementChild as HTMLElement
215229

216-
if (lastNode.classList.contains('message-role-assistant')) {
230+
if (lastNode.classList.contains('message-role-assistant') && !isCompletionDone) {
217231
const container = appContainerRef.current
218232
const settingsHeight = settingsRef.current.clientHeight
219233

220234
const containerRect = container.getBoundingClientRect()
221235
const lastNodeRect = lastNode.getBoundingClientRect()
222236

223-
const scrollTopPadding = 180
237+
const scrollTopPadding = 200
224238
const scrollOffset = lastNodeRect.top - containerRect.top + container.scrollTop - settingsHeight - scrollTopPadding
225239

226240
container.scrollTo({
@@ -276,14 +290,10 @@ export const ChatV2 = () => {
276290
gap: '1.2rem',
277291
backgroundColor: 'white',
278292
padding: '1.8rem 1rem 0.8rem 1rem',
293+
zIndex: 10,
279294
}}
280295
>
281296
{/* {disclaimerInfo && <Disclaimer disclaimer={disclaimerInfo} />}
282-
<SystemPrompt content={system.content} setContent={(content) => setSystem({ content })} />
283-
<Button onClick={handleReset}>Reset</Button>
284-
<IconButton onClick={() => setSettingsModalOpen(true)} title="Settings">
285-
<Settings></Settings>
286-
</IconButton> */}
287297
{/* <SettingsButton startIcon={<AddCommentIcon />}>Alustus</SettingsButton> */}
288298
<SettingsButton startIcon={<SettingsIcon />} onClick={() => setSettingsModalOpen(true)}>
289299
Keskustelun asetukset
@@ -298,18 +308,18 @@ export const ChatV2 = () => {
298308

299309
<Box
300310
sx={{
301-
height: '100%',
302311
display: 'flex',
303312
flexDirection: 'column',
313+
height: '100%',
304314
width: '70%',
305315
margin: 'auto',
306-
paddingBottom: '5rem',
307316
paddingTop: '1rem',
317+
paddingBottom: '8rem',
308318
}}
309319
>
310320
<Conversation
311321
conversationRef={conversationRef}
312-
lastNodeHeight={window.innerHeight - settingsRef.current?.clientHeight - inputFieldRef.current?.clientHeight}
322+
expandedNodeHeight={window.innerHeight - settingsRef.current?.clientHeight - inputFieldRef.current?.clientHeight}
313323
messages={messages}
314324
completion={completion}
315325
isCompletionDone={isCompletionDone}
@@ -319,7 +329,7 @@ export const ChatV2 = () => {
319329

320330
<Box ref={inputFieldRef} sx={{ position: 'sticky', bottom: 0, backgroundColor: 'white', paddingBottom: '1.5rem' }}>
321331
<ChatBox
322-
disabled={false}
332+
disabled={!isCompletionDone}
323333
onSubmit={(message) => {
324334
if (message.trim()) {
325335
handleSubmit(message)

src/client/components/ChatV2/Conversation.tsx

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ const dotStyle = (delay: number) => ({
1515
animationDelay: `${delay}s`,
1616
})
1717

18-
export const MessageLoading = ({ lastNodeHeight }: { lastNodeHeight: number }) => (
18+
export const LoadingMessage = ({ expandedNodeHeight }: { expandedNodeHeight: number }) => (
1919
<div
2020
className="message-role-assistant"
2121
style={{
22-
height: lastNodeHeight,
22+
height: expandedNodeHeight,
2323
display: 'flex',
2424
padding: '2rem',
2525
}}
@@ -42,15 +42,16 @@ export const MessageLoading = ({ lastNodeHeight }: { lastNodeHeight: number }) =
4242
</div>
4343
)
4444

45-
const MessageItem = ({ message, isLastAssistantNode, lastNodeHeight }: { message: Message; isLastAssistantNode: boolean; lastNodeHeight: number }) => (
45+
const MessageItem = ({ message, isLastAssistantNode, expandedNodeHeight }: { message: Message; isLastAssistantNode: boolean; expandedNodeHeight: number }) => (
4646
<Box
4747
className={`message-role-${message.role}`}
4848
sx={{
4949
alignSelf: message.role === 'assistant' ? 'flex-start' : 'flex-end',
5050
backgroundColor: message.role === 'assistant' ? 'transparent' : '#efefef',
5151
padding: '0 1.5rem',
5252
borderRadius: '0.6rem',
53-
height: isLastAssistantNode ? lastNodeHeight : 'auto',
53+
minHeight: isLastAssistantNode ? expandedNodeHeight : 'auto',
54+
boxShadow: message.role === 'assistant' ? 'none' : '0px 2px 2px rgba(0, 0, 0, 0.2)',
5455
}}
5556
>
5657
<ReactMarkdown remarkPlugins={[remarkGfm]}>{message.content}</ReactMarkdown>
@@ -59,14 +60,14 @@ const MessageItem = ({ message, isLastAssistantNode, lastNodeHeight }: { message
5960

6061
export const Conversation = ({
6162
conversationRef,
62-
lastNodeHeight,
63+
expandedNodeHeight,
6364
messages,
6465
completion,
6566
isCompletionDone,
6667
fileSearchResult,
6768
}: {
6869
conversationRef: React.RefObject<HTMLElement>
69-
lastNodeHeight: number
70+
expandedNodeHeight: number
7071
messages: Message[]
7172
completion: string
7273
isCompletionDone: boolean
@@ -77,14 +78,14 @@ export const Conversation = ({
7778
{messages.map((message, idx) => {
7879
const isLastAssistantNode = idx === messages.length - 1 && message.role === 'assistant'
7980

80-
return <MessageItem key={idx} message={message} isLastAssistantNode={isLastAssistantNode} lastNodeHeight={lastNodeHeight} />
81+
return <MessageItem key={idx} message={message} isLastAssistantNode={isLastAssistantNode} expandedNodeHeight={expandedNodeHeight} />
8182
})}
8283
{!isCompletionDone &&
8384
messages.length > 0 &&
8485
(completion.length > 0 ? (
85-
<MessageItem message={{ role: 'assistant', content: completion, fileSearchResult }} isLastAssistantNode={true} lastNodeHeight={lastNodeHeight} />
86+
<MessageItem message={{ role: 'assistant', content: completion, fileSearchResult }} isLastAssistantNode={true} expandedNodeHeight={expandedNodeHeight} />
8687
) : (
87-
<MessageLoading lastNodeHeight={lastNodeHeight} />
88+
<LoadingMessage expandedNodeHeight={expandedNodeHeight} />
8889
))}
8990
</Box>
9091
)

src/client/components/ChatV2/generics/ConversationSplash.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
import { Box, Typography } from '@mui/material'
22
import hyLogo from '../../../assets/hy_logo.svg'
33

4-
54
export const ConversationSplash = () => (
65
<Box
76
sx={{
7+
height: '100%',
88
display: 'flex',
99
flexDirection: 'column',
1010
justifyContent: 'center',
1111
alignItems: 'center',
12-
height: '100%',
1312
transition: 'opacity 0.6s, transform 0.6s',
1413
opacity: 1,
1514
transform: 'scale(1)',
@@ -25,4 +24,4 @@ export const ConversationSplash = () => (
2524
Aloita keskustelu läettämällä viesti...
2625
</Typography>
2726
</Box>
28-
)
27+
)

0 commit comments

Comments
 (0)