1
- import React , { useCallback , useEffect , useContext } from 'react' ;
1
+ import React , { useCallback , useEffect , useContext , useRef } from 'react' ;
2
2
import type { AssistantMessage } from '../compass-assistant-provider' ;
3
3
import { AssistantActionsContext } from '../compass-assistant-provider' ;
4
4
import type { Chat } from '../@ai-sdk/react/chat-react' ;
@@ -211,6 +211,10 @@ export const AssistantChat: React.FunctionComponent<AssistantChatProps> = ({
211
211
} ) => {
212
212
const track = useTelemetry ( ) ;
213
213
const darkMode = useDarkMode ( ) ;
214
+ const messagesContainerRef = useRef < HTMLDivElement > ( null ) ;
215
+ const previousLastMessageId = useRef < string | undefined > ( undefined ) ;
216
+ const { id : lastMessageId , role : lastMessageRole } =
217
+ chat . messages [ chat . messages . length - 1 ] ?? { } ;
214
218
215
219
const { ensureOptInAndSend } = useContext ( AssistantActionsContext ) ;
216
220
const { messages, status, error, clearError, setMessages } = useChat ( {
@@ -222,6 +226,26 @@ export const AssistantChat: React.FunctionComponent<AssistantChatProps> = ({
222
226
} ,
223
227
} ) ;
224
228
229
+ const scrollToBottom = useCallback ( ( ) => {
230
+ if ( messagesContainerRef . current ) {
231
+ // Since the container uses flexDirection: 'column-reverse',
232
+ // scrolling to the bottom means setting scrollTop to 0
233
+ messagesContainerRef . current . scrollTop = 0 ;
234
+ }
235
+ } , [ ] ) ;
236
+
237
+ useEffect ( ( ) => {
238
+ if (
239
+ lastMessageId &&
240
+ previousLastMessageId . current !== undefined &&
241
+ lastMessageId !== previousLastMessageId . current &&
242
+ lastMessageRole === 'user'
243
+ ) {
244
+ scrollToBottom ( ) ;
245
+ }
246
+ previousLastMessageId . current = lastMessageId ;
247
+ } , [ lastMessageId , lastMessageRole , scrollToBottom ] ) ;
248
+
225
249
useEffect ( ( ) => {
226
250
const hasExistingNonGenuineWarning = chat . messages . some (
227
251
( message ) => message . id === 'non-genuine-warning'
@@ -240,17 +264,18 @@ export const AssistantChat: React.FunctionComponent<AssistantChatProps> = ({
240
264
} , [ hasNonGenuineConnections , chat , setMessages ] ) ;
241
265
242
266
const handleMessageSend = useCallback (
243
- ( messageBody : string ) => {
267
+ async ( messageBody : string ) => {
244
268
const trimmedMessageBody = messageBody . trim ( ) ;
245
269
if ( trimmedMessageBody ) {
270
+ await chat . stop ( ) ;
246
271
void ensureOptInAndSend ?.( { text : trimmedMessageBody } , { } , ( ) => {
247
272
track ( 'Assistant Prompt Submitted' , {
248
273
user_input_length : trimmedMessageBody . length ,
249
274
} ) ;
250
275
} ) ;
251
276
}
252
277
} ,
253
- [ track , ensureOptInAndSend ]
278
+ [ track , ensureOptInAndSend , chat ]
254
279
) ;
255
280
256
281
const handleFeedback = useCallback (
@@ -351,6 +376,7 @@ export const AssistantChat: React.FunctionComponent<AssistantChatProps> = ({
351
376
< div
352
377
data-testid = "assistant-chat-messages"
353
378
className = { messageFeedFixesStyles }
379
+ ref = { messagesContainerRef }
354
380
>
355
381
< div className = { messagesWrapStyles } >
356
382
{ messages . map ( ( message , index ) => {
@@ -450,7 +476,9 @@ export const AssistantChat: React.FunctionComponent<AssistantChatProps> = ({
450
476
< div className = { inputBarStyleFixes } >
451
477
< InputBar
452
478
data-testid = "assistant-chat-input"
453
- onMessageSend = { handleMessageSend }
479
+ onMessageSend = { ( messageBody ) =>
480
+ void handleMessageSend ( messageBody )
481
+ }
454
482
state = { status === 'submitted' ? 'loading' : undefined }
455
483
textareaProps = { {
456
484
placeholder : 'Ask a question' ,
0 commit comments