2024-12-03 17:49:38 +03:00
|
|
|
import { PreviewMessage, ThinkingMessage } from './message';
|
2025-04-03 01:05:07 -07:00
|
|
|
import { Greeting } from './greeting';
|
2025-09-07 00:04:51 +01:00
|
|
|
import { memo, useEffect } from 'react';
|
2025-04-03 01:05:07 -07:00
|
|
|
import type { Vote } from '@/lib/db/schema';
|
2024-12-10 17:54:10 +05:30
|
|
|
import equal from 'fast-deep-equal';
|
2025-04-03 01:05:07 -07:00
|
|
|
import type { UseChatHelpers } from '@ai-sdk/react';
|
2025-05-01 02:28:24 -07:00
|
|
|
import { useMessages } from '@/hooks/use-messages';
|
2025-07-03 02:26:34 -07:00
|
|
|
import type { ChatMessage } from '@/lib/types';
|
|
|
|
|
import { useDataStream } from './data-stream-provider';
|
2025-09-07 00:04:51 +01:00
|
|
|
import { Conversation, ConversationContent } from './elements/conversation';
|
|
|
|
|
import { ArrowDownIcon } from 'lucide-react';
|
2024-12-03 17:49:38 +03:00
|
|
|
|
|
|
|
|
interface MessagesProps {
|
|
|
|
|
chatId: string;
|
2025-07-03 02:26:34 -07:00
|
|
|
status: UseChatHelpers<ChatMessage>['status'];
|
2024-12-03 17:49:38 +03:00
|
|
|
votes: Array<Vote> | undefined;
|
2025-07-03 02:26:34 -07:00
|
|
|
messages: ChatMessage[];
|
|
|
|
|
setMessages: UseChatHelpers<ChatMessage>['setMessages'];
|
|
|
|
|
regenerate: UseChatHelpers<ChatMessage>['regenerate'];
|
2024-12-06 13:36:56 +03:00
|
|
|
isReadonly: boolean;
|
2025-02-13 08:25:57 -08:00
|
|
|
isArtifactVisible: boolean;
|
2025-09-07 00:04:51 +01:00
|
|
|
selectedModelId: string;
|
2024-12-03 17:49:38 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function PureMessages({
|
|
|
|
|
chatId,
|
2025-03-11 15:33:18 -07:00
|
|
|
status,
|
2024-12-03 17:49:38 +03:00
|
|
|
votes,
|
|
|
|
|
messages,
|
2024-12-05 15:29:33 +03:00
|
|
|
setMessages,
|
2025-07-03 02:26:34 -07:00
|
|
|
regenerate,
|
2024-12-06 13:36:56 +03:00
|
|
|
isReadonly,
|
2025-09-01 11:07:07 +01:00
|
|
|
isArtifactVisible,
|
2025-09-07 00:04:51 +01:00
|
|
|
selectedModelId,
|
2024-12-03 17:49:38 +03:00
|
|
|
}: MessagesProps) {
|
2025-05-01 02:28:24 -07:00
|
|
|
const {
|
|
|
|
|
containerRef: messagesContainerRef,
|
|
|
|
|
endRef: messagesEndRef,
|
2025-09-07 00:04:51 +01:00
|
|
|
isAtBottom,
|
|
|
|
|
scrollToBottom,
|
2025-05-01 02:28:24 -07:00
|
|
|
hasSentMessage,
|
|
|
|
|
} = useMessages({
|
|
|
|
|
chatId,
|
|
|
|
|
status,
|
|
|
|
|
});
|
2024-12-03 17:49:38 +03:00
|
|
|
|
2025-07-03 02:26:34 -07:00
|
|
|
useDataStream();
|
|
|
|
|
|
2025-09-07 00:04:51 +01:00
|
|
|
useEffect(() => {
|
|
|
|
|
if (status === 'submitted') {
|
|
|
|
|
requestAnimationFrame(() => {
|
|
|
|
|
const container = messagesContainerRef.current;
|
|
|
|
|
if (container) {
|
|
|
|
|
container.scrollTo({
|
|
|
|
|
top: container.scrollHeight,
|
|
|
|
|
behavior: 'smooth'
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
2025-09-08 21:02:06 +01:00
|
|
|
}, [status, messagesContainerRef]);
|
2025-09-07 00:04:51 +01:00
|
|
|
|
2024-12-03 17:49:38 +03:00
|
|
|
return (
|
2025-09-07 00:04:51 +01:00
|
|
|
<div
|
|
|
|
|
ref={messagesContainerRef}
|
|
|
|
|
className="overflow-y-scroll flex-1 touch-pan-y overscroll-behavior-contain -webkit-overflow-scrolling-touch"
|
|
|
|
|
style={{ overflowAnchor: 'none' }}
|
|
|
|
|
>
|
2025-09-08 21:02:06 +01:00
|
|
|
<Conversation className="flex flex-col gap-4 px-2 py-4 mx-auto min-w-0 max-w-4xl md:gap-6 md:px-4">
|
2025-09-07 00:04:51 +01:00
|
|
|
<ConversationContent className="flex flex-col gap-4 md:gap-6">
|
2025-08-28 14:15:36 +01:00
|
|
|
{messages.length === 0 && <Greeting />}
|
2024-12-03 17:49:38 +03:00
|
|
|
|
2025-08-28 14:15:36 +01:00
|
|
|
{messages.map((message, index) => (
|
|
|
|
|
<PreviewMessage
|
|
|
|
|
key={message.id}
|
|
|
|
|
chatId={chatId}
|
|
|
|
|
message={message}
|
2025-09-07 00:04:51 +01:00
|
|
|
isLoading={
|
|
|
|
|
status === 'streaming' && messages.length - 1 === index
|
|
|
|
|
}
|
2025-08-28 14:15:36 +01:00
|
|
|
vote={
|
|
|
|
|
votes
|
|
|
|
|
? votes.find((vote) => vote.messageId === message.id)
|
|
|
|
|
: undefined
|
|
|
|
|
}
|
|
|
|
|
setMessages={setMessages}
|
|
|
|
|
regenerate={regenerate}
|
|
|
|
|
isReadonly={isReadonly}
|
|
|
|
|
requiresScrollPadding={
|
|
|
|
|
hasSentMessage && index === messages.length - 1
|
|
|
|
|
}
|
2025-09-01 11:07:07 +01:00
|
|
|
isArtifactVisible={isArtifactVisible}
|
2025-08-28 14:15:36 +01:00
|
|
|
/>
|
|
|
|
|
))}
|
2024-12-03 17:49:38 +03:00
|
|
|
|
2025-08-28 14:15:36 +01:00
|
|
|
{status === 'submitted' &&
|
|
|
|
|
messages.length > 0 &&
|
2025-09-07 00:04:51 +01:00
|
|
|
messages[messages.length - 1].role === 'user' &&
|
|
|
|
|
selectedModelId !== 'chat-model-reasoning' && (
|
|
|
|
|
<ThinkingMessage />
|
|
|
|
|
)}
|
2024-12-03 17:49:38 +03:00
|
|
|
|
2025-09-07 00:04:51 +01:00
|
|
|
<div
|
2025-08-28 14:15:36 +01:00
|
|
|
ref={messagesEndRef}
|
|
|
|
|
className="shrink-0 min-w-[24px] min-h-[24px]"
|
|
|
|
|
/>
|
|
|
|
|
</ConversationContent>
|
|
|
|
|
</Conversation>
|
2025-09-07 00:04:51 +01:00
|
|
|
|
|
|
|
|
{!isAtBottom && (
|
|
|
|
|
<button
|
|
|
|
|
className="absolute bottom-40 left-1/2 z-10 p-2 rounded-full border shadow-lg transition-colors -translate-x-1/2 bg-background hover:bg-muted"
|
|
|
|
|
onClick={() => scrollToBottom('smooth')}
|
|
|
|
|
type="button"
|
|
|
|
|
aria-label="Scroll to bottom"
|
|
|
|
|
>
|
|
|
|
|
<ArrowDownIcon className="size-4" />
|
|
|
|
|
</button>
|
|
|
|
|
)}
|
2024-12-03 17:49:38 +03:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-10 17:54:10 +05:30
|
|
|
export const Messages = memo(PureMessages, (prevProps, nextProps) => {
|
2025-02-13 08:25:57 -08:00
|
|
|
if (prevProps.isArtifactVisible && nextProps.isArtifactVisible) return true;
|
2024-12-19 17:05:04 +05:30
|
|
|
|
2025-03-11 15:33:18 -07:00
|
|
|
if (prevProps.status !== nextProps.status) return false;
|
2025-09-07 00:04:51 +01:00
|
|
|
if (prevProps.selectedModelId !== nextProps.selectedModelId) return false;
|
2024-12-10 17:54:10 +05:30
|
|
|
if (prevProps.messages.length !== nextProps.messages.length) return false;
|
2025-01-13 14:34:51 +05:30
|
|
|
if (!equal(prevProps.messages, nextProps.messages)) return false;
|
2024-12-10 17:54:10 +05:30
|
|
|
if (!equal(prevProps.votes, nextProps.votes)) return false;
|
2024-12-03 17:49:38 +03:00
|
|
|
|
2025-07-03 02:26:34 -07:00
|
|
|
return false;
|
2024-12-10 17:54:10 +05:30
|
|
|
});
|