@@ -17,12 +17,7 @@ import {
17
17
} from "react" ;
18
18
import { usePopup } from "@/components/admin/connectors/Popup" ;
19
19
import { SEARCH_PARAM_NAMES } from "../services/searchParams" ;
20
- import {
21
- LlmDescriptor ,
22
- useFederatedConnectors ,
23
- useFilters ,
24
- useLlmManager ,
25
- } from "@/lib/hooks" ;
20
+ import { useFederatedConnectors , useFilters , useLlmManager } from "@/lib/hooks" ;
26
21
import { FeedbackType } from "@/app/chat/interfaces" ;
27
22
import { OnyxInitializingLoader } from "@/components/OnyxInitializingLoader" ;
28
23
import { FeedbackModal } from "./modal/FeedbackModal" ;
@@ -86,11 +81,10 @@ import {
86
81
useChatSessionSharedStatus ,
87
82
useHasSentLocalUserMessage ,
88
83
} from "../stores/useChatSessionStore" ;
89
- import { AIMessage } from "../message/messageComponents/AIMessage" ;
90
84
import { FederatedOAuthModal } from "@/components/chat/FederatedOAuthModal" ;
91
- import { HumanMessage } from "../message/HumanMessage" ;
92
85
import { AssistantIcon } from "@/components/assistants/AssistantIcon" ;
93
86
import { StarterMessageDisplay } from "./starterMessages/StarterMessageDisplay" ;
87
+ import { MessagesDisplay } from "./MessagesDisplay" ;
94
88
95
89
export function ChatPage ( {
96
90
toggle,
@@ -360,15 +354,15 @@ export function ChatPage({
360
354
} , 100 ) ;
361
355
} ;
362
356
363
- const resetInputBar = ( ) => {
357
+ const resetInputBar = useCallback ( ( ) => {
364
358
setMessage ( "" ) ;
365
359
setCurrentMessageFiles ( [ ] ) ;
366
360
if ( endPaddingRef . current ) {
367
361
endPaddingRef . current . style . height = `95px` ;
368
362
}
369
- } ;
363
+ } , [ setMessage , setCurrentMessageFiles ] ) ;
370
364
371
- const clientScrollToBottom = ( fast ?: boolean ) => {
365
+ const clientScrollToBottom = useCallback ( ( fast ?: boolean ) => {
372
366
waitForScrollRef . current = true ;
373
367
374
368
setTimeout ( ( ) => {
@@ -389,15 +383,17 @@ export function ChatPage({
389
383
} ) ;
390
384
391
385
if ( chatSessionIdRef . current ) {
392
- updateHasPerformedInitialScroll ( chatSessionIdRef . current , true ) ;
386
+ useChatSessionStore
387
+ . getState ( )
388
+ . updateHasPerformedInitialScroll ( chatSessionIdRef . current , true ) ;
393
389
}
394
390
} , 50 ) ;
395
391
396
392
// Reset waitForScrollRef after 1.5 seconds
397
393
setTimeout ( ( ) => {
398
394
waitForScrollRef . current = false ;
399
395
} , 1500 ) ;
400
- } ;
396
+ } , [ ] ) ;
401
397
402
398
const debounceNumber = 100 ; // time for debouncing
403
399
@@ -470,7 +466,6 @@ export function ChatPage({
470
466
) ;
471
467
472
468
// Access chat state directly from the store
473
- const beforeZustandTime = performance . now ( ) ;
474
469
const currentChatState = useCurrentChatState ( ) ;
475
470
const chatSessionId = useChatSessionStore ( ( state ) => state . currentSessionId ) ;
476
471
const submittedMessage = useSubmittedMessage ( ) ;
@@ -690,28 +685,6 @@ export function ChatPage({
690
685
[ setIsChatSearchModalOpen ]
691
686
) ;
692
687
693
- interface RegenerationRequest {
694
- messageId : number ;
695
- parentMessage : Message ;
696
- forceSearch ?: boolean ;
697
- }
698
-
699
- function createRegenerator ( regenerationRequest : RegenerationRequest ) {
700
- // Returns new function that only needs `modelOveride` to be specified when called
701
- return async function ( modelOverride : LlmDescriptor ) {
702
- return await onSubmit ( {
703
- message : message ,
704
- selectedFiles : selectedFiles ,
705
- selectedFolders : selectedFolders ,
706
- currentMessageFiles : currentMessageFiles ,
707
- useAgentSearch : deepResearchEnabled ,
708
- modelOverride,
709
- messageIdToResend : regenerationRequest . parentMessage . messageId ,
710
- regenerationRequest,
711
- forceSearch : regenerationRequest . forceSearch ,
712
- } ) ;
713
- } ;
714
- }
715
688
if ( ! user ) {
716
689
redirect ( "/auth/login" ) ;
717
690
}
@@ -853,6 +826,9 @@ export function ChatPage({
853
826
onOutsideClick = { ( ) => updateCurrentDocumentSidebarVisible ( false ) }
854
827
title = "Sources"
855
828
>
829
+ { /* IMPORTANT: this is a memoized component, and it's very important
830
+ for performance reasons that this stays true. MAKE SURE that all function
831
+ props are wrapped in useCallback. */ }
856
832
< DocumentResults
857
833
setPresentingDocument = { setPresentingDocument }
858
834
modal = { true }
@@ -1008,6 +984,9 @@ export function ChatPage({
1008
984
}
1009
985
` }
1010
986
>
987
+ { /* IMPORTANT: this is a memoized component, and it's very important
988
+ for performance reasons that this stays true. MAKE SURE that all function
989
+ props are wrapped in useCallback. */ }
1011
990
< DocumentResults
1012
991
setPresentingDocument = { setPresentingDocument }
1013
992
modal = { false }
@@ -1114,171 +1093,38 @@ export function ChatPage({
1114
1093
</ div >
1115
1094
) }
1116
1095
1117
- { messageHistory . length === 0 &&
1118
- ! isFetchingChatMessages &&
1119
- ! loadingError &&
1120
- ! submittedMessage &&
1121
- null }
1122
- < div
1123
- style = { { overflowAnchor : "none" } }
1124
- key = { chatSessionId }
1125
- className = {
1126
- ( hasPerformedInitialScroll ? "" : " hidden " ) +
1127
- "desktop:-ml-4 w-full mx-auto " +
1128
- "absolute mobile:top-0 desktop:top-0 left-0 " +
1129
- ( settings ?. enterpriseSettings
1130
- ?. two_lines_for_chat_header
1131
- ? "pt-20 "
1132
- : "pt-4 " )
1096
+ < MessagesDisplay
1097
+ messageHistory = { messageHistory }
1098
+ completeMessageTree = { completeMessageTree }
1099
+ liveAssistant = { liveAssistant }
1100
+ llmManager = { llmManager }
1101
+ deepResearchEnabled = { deepResearchEnabled }
1102
+ selectedFiles = { selectedFiles }
1103
+ selectedFolders = { selectedFolders }
1104
+ currentMessageFiles = { currentMessageFiles }
1105
+ setPresentingDocument = { setPresentingDocument }
1106
+ setCurrentFeedback = { setCurrentFeedback }
1107
+ onSubmit = { onSubmit }
1108
+ onMessageSelection = { onMessageSelection }
1109
+ stopGenerating = { stopGenerating }
1110
+ uncaughtError = { uncaughtError }
1111
+ loadingError = { loadingError }
1112
+ handleResubmitLastMessage = {
1113
+ handleResubmitLastMessage
1133
1114
}
1134
- // NOTE: temporarily removing this to fix the scroll bug
1135
- // (hasPerformedInitialScroll ? "" : "invisible")
1136
- >
1137
- { messageHistory . map ( ( message , i ) => {
1138
- const messageTree = completeMessageTree ;
1139
-
1140
- const messageReactComponentKey = `message-${ message . nodeId } ` ;
1141
- const parentMessage = message . parentNodeId
1142
- ? messageTree ?. get ( message . parentNodeId )
1143
- : null ;
1144
- if ( message . type === "user" ) {
1145
- const nextMessage =
1146
- messageHistory . length > i + 1
1147
- ? messageHistory [ i + 1 ]
1148
- : null ;
1149
-
1150
- return (
1151
- < div
1152
- id = { messageReactComponentKey }
1153
- key = { messageReactComponentKey }
1154
- >
1155
- < HumanMessage
1156
- setPresentingDocument = {
1157
- setPresentingDocument
1158
- }
1159
- disableSwitchingForStreaming = {
1160
- ( nextMessage &&
1161
- nextMessage . is_generating ) ||
1162
- false
1163
- }
1164
- stopGenerating = { stopGenerating }
1165
- content = { message . message }
1166
- files = { message . files }
1167
- messageId = { message . messageId }
1168
- onEdit = { ( editedContent ) => {
1169
- onSubmit ( {
1170
- message : editedContent ,
1171
- messageIdToResend :
1172
- message . messageId || undefined ,
1173
- // TODO: fix
1174
- selectedFiles : [ ] ,
1175
- selectedFolders : [ ] ,
1176
- currentMessageFiles : [ ] ,
1177
- useAgentSearch : deepResearchEnabled ,
1178
- } ) ;
1179
- } }
1180
- otherMessagesCanSwitchTo = {
1181
- parentMessage ?. childrenNodeIds || [ ]
1182
- }
1183
- onMessageSelection = { onMessageSelection }
1184
- />
1185
- </ div >
1186
- ) ;
1187
- } else if ( message . type === "assistant" ) {
1188
- const previousMessage =
1189
- i !== 0 ? messageHistory [ i - 1 ] : null ;
1190
-
1191
- if (
1192
- ( uncaughtError || loadingError ) &&
1193
- i === messageHistory . length - 1
1194
- ) {
1195
- return (
1196
- < div
1197
- key = { `error-${ message . nodeId } ` }
1198
- className = "max-w-message-max mx-auto"
1199
- >
1200
- < ErrorBanner
1201
- resubmit = { handleResubmitLastMessage }
1202
- error = {
1203
- uncaughtError || loadingError || ""
1204
- }
1205
- />
1206
- </ div >
1207
- ) ;
1208
- }
1209
-
1210
- return (
1211
- < div
1212
- className = "text-text"
1213
- id = { `message-${ message . nodeId } ` }
1214
- key = { messageReactComponentKey }
1215
- ref = {
1216
- i == messageHistory . length - 1
1217
- ? lastMessageRef
1218
- : null
1219
- }
1220
- >
1221
- < AIMessage
1222
- rawPackets = { message . packets }
1223
- chatState = { {
1224
- handleFeedback : ( feedback ) =>
1225
- setCurrentFeedback ( [
1226
- feedback ,
1227
- message . messageId ! ,
1228
- ] ) ,
1229
- assistant : liveAssistant ,
1230
- docs : message . documents ,
1231
- userFiles : [ ] , // TODO: Extract user files from message context
1232
- citations : message . citations ,
1233
- setPresentingDocument :
1234
- setPresentingDocument ,
1235
- regenerate : createRegenerator ( {
1236
- messageId : message . messageId ! ,
1237
- parentMessage : previousMessage ! ,
1238
- } ) ,
1239
- overriddenModel :
1240
- llmManager . currentLlm ?. modelName ,
1241
- } }
1242
- nodeId = { message . nodeId }
1243
- otherMessagesCanSwitchTo = {
1244
- parentMessage ?. childrenNodeIds || [ ]
1245
- }
1246
- onMessageSelection = { onMessageSelection }
1247
- />
1248
- </ div >
1249
- ) ;
1250
- }
1251
- } ) }
1252
-
1253
- { ( ( uncaughtError || loadingError ) &&
1254
- messageHistory [ messageHistory . length - 1 ]
1255
- ?. type === "user" ) ||
1256
- ( messageHistory [ messageHistory . length - 1 ]
1257
- ?. type === "error" && (
1258
- < div className = "max-w-message-max mx-auto" >
1259
- < ErrorBanner
1260
- resubmit = { handleResubmitLastMessage }
1261
- error = { uncaughtError || loadingError || "" }
1262
- />
1263
- </ div >
1264
- ) ) }
1265
-
1266
- { messageHistory . length > 0 && (
1267
- < div
1268
- style = { {
1269
- height : ! autoScrollEnabled
1270
- ? getContainerHeight ( )
1271
- : undefined ,
1272
- } }
1273
- />
1274
- ) }
1275
-
1276
- { /* Some padding at the bottom so the search bar has space at the bottom to not cover the last message*/ }
1277
- < div ref = { endPaddingRef } className = "h-[95px]" />
1278
-
1279
- < div ref = { endDivRef } />
1280
- </ div >
1115
+ autoScrollEnabled = { autoScrollEnabled }
1116
+ getContainerHeight = { getContainerHeight }
1117
+ lastMessageRef = { lastMessageRef }
1118
+ endPaddingRef = { endPaddingRef }
1119
+ endDivRef = { endDivRef }
1120
+ hasPerformedInitialScroll = {
1121
+ hasPerformedInitialScroll
1122
+ }
1123
+ chatSessionId = { chatSessionId }
1124
+ enterpriseSettings = { enterpriseSettings }
1125
+ />
1281
1126
</ div >
1127
+
1282
1128
< div
1283
1129
ref = { inputRef }
1284
1130
className = { `absolute pointer-events-none z-10 w-full ${
0 commit comments