Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@
"react-hot-toast": "^2.4.1",
"react-i18next": "12.0.0",
"react-leaflet": "^4.2.1",
"react-virtuoso": "^4.12.7",
"standardized-audio-context": "25.3.34",
"three": "0.144.0",
"tiny-invariant": "^1.3.3"
Expand Down
1 change: 1 addition & 0 deletions src/components/Chat/Chat.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ WithExpandable.args = {
history: historyWithExpandable,
dialogState,
layout: 'DEFAULT',
mode: 'log',
simulateUserPrompt: () => {},
sendMessage: (msg: string) => console.log(msg),
stopListening: () => {},
Expand Down
57 changes: 46 additions & 11 deletions src/components/Chat/Chat.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, memo } from 'react';
import React, { useEffect, memo, useState } from 'react';
import cx from 'classnames';
import {
DialogState,
Expand Down Expand Up @@ -78,9 +78,11 @@ export interface Props {
user?: User;
experts?: ExpertReference[];
useMathFormatting?: boolean;
showFunctionCache?: boolean;
mode?: 'chat' | 'log';
}

const Chat: React.FC<Props> = ({
const OptimizedChat: React.FC<Props> = ({
memori,
tenant,
sessionID,
Expand Down Expand Up @@ -124,20 +126,34 @@ const Chat: React.FC<Props> = ({
userAvatar,
showUpload = false,
experts,
showFunctionCache = false,
useMathFormatting = false,
mode = 'chat',
}) => {
// Determine if the chat has a lot of messages (for optimization)
const hasLargeHistory = history.length > 50;

// Track if we're currently in a loading state (for large messages)
const [isLoadingMessage, setIsLoadingMessage] = useState(false);

// Scroll to the bottom of the chat
const scrollToBottom = () => {
setTimeout(() => {
let userMsgs = document.querySelectorAll(
'.memori-chat--bubble-container.memori-chat--bubble-from-user'
);
userMsgs[userMsgs.length - 1]?.scrollIntoView?.();
}, 200);
// Only attempt to scroll if we're not in a loading state
if (!isLoadingMessage) {
setTimeout(() => {
let userMsgs = document.querySelectorAll(
'.memori-chat--bubble-container.memori-chat--bubble-from-user'
);
userMsgs[userMsgs.length - 1]?.scrollIntoView?.();
}, 200);
}
};

useEffect(() => {
!preview && scrollToBottom();
}, [history, preview]);

}, [history, preview, isLoadingMessage]);

// Text input focus/blur handlers
const onTextareaFocus = () => {
stopListening();
const hasTouch = hasTouchscreen();
Expand All @@ -155,6 +171,7 @@ const Chat: React.FC<Props> = ({
}, 300);
}
};

const onTextareaBlur = () => {
if (
document
Expand All @@ -169,6 +186,11 @@ const Chat: React.FC<Props> = ({
}
};

// Callback when a message starts/finishes formatting
const handleMessageLoadingState = (isLoading: boolean) => {
setIsLoadingMessage(isLoading);
};

return (
<div
className={cx('memori-chat--wrapper', {
Expand All @@ -188,6 +210,7 @@ const Chat: React.FC<Props> = ({
'memori-chat--content-touch': hasTouchscreen(),
})}
>
{/* Cover image */}
<div
className={cx('memori-chat--cover')}
style={{
Expand All @@ -206,8 +229,10 @@ const Chat: React.FC<Props> = ({
}}
/>

{/* Message history */}
{history.map((message, index) => (
<React.Fragment key={index}>
{/* Use optimized chat bubble */}
<ChatBubble
isFirst={index === 0}
message={message}
Expand All @@ -230,8 +255,11 @@ const Chat: React.FC<Props> = ({
experts={experts}
showCopyButton={showCopyButton}
useMathFormatting={useMathFormatting}
showFunctionCache={showFunctionCache}
mode={mode}
/>

{/* Timestamps */}
{showDates && !!message.timestamp && (
<small
className={`memori-chat--timestamp ${
Expand All @@ -252,6 +280,7 @@ const Chat: React.FC<Props> = ({
</small>
)}

{/* Context variables */}
{showContextPerLine &&
!!Object.keys(message.contextVars ?? {}).length && (
<div className="memori-chat--context-vars">
Expand Down Expand Up @@ -282,6 +311,7 @@ const Chat: React.FC<Props> = ({
</div>
)}

{/* Media attachments */}
<MediaWidget
simulateUserPrompt={simulateUserPrompt}
media={message?.media?.filter(
Expand All @@ -297,6 +327,7 @@ const Chat: React.FC<Props> = ({
</React.Fragment>
))}

{/* Hints display */}
{dialogState?.hints &&
dialogState.hints.length > 0 &&
!memoriTyping && (
Expand All @@ -313,6 +344,7 @@ const Chat: React.FC<Props> = ({
/>
)}

{/* Typing indicator */}
{!!memoriTyping && (
<Typing
useDefaultSentences={showTypingText}
Expand All @@ -334,10 +366,13 @@ const Chat: React.FC<Props> = ({
key={typingText}
/>
)}

{/* Reference element for scrolling */}
<div id="end-messages-ref" />
</div>
</div>

{/* Chat input area */}
{showInputs && (
<ChatInputs
resetTranscript={resetTranscript}
Expand Down Expand Up @@ -367,4 +402,4 @@ const Chat: React.FC<Props> = ({
);
};

export default memo(Chat);
export default memo(OptimizedChat);
Loading