import { Loader2, PauseCircle, RefreshCw } from 'lucide-react' import { createContext, useContext, useEffect, useRef } from 'react' import { cn } from '../lib/utils' import { Button } from '../ui/button' import ChatMessage from './chat-message' import { useChatUI } from './chat.context' import type { Message } from './chat.interface' interface ChatMessagesProps extends React.PropsWithChildren { className?: string } interface ChatMessagesListProps extends React.PropsWithChildren { className?: string } interface ChatMessagesLoadingProps extends React.PropsWithChildren { className?: string } interface ChatMessagesEmptyProps extends React.PropsWithChildren { className?: string heading?: string subheading?: string } interface ChatActionsProps extends React.PropsWithChildren { className?: string } interface ChatMessagesContext { isPending: boolean showReload?: boolean showStop?: boolean messageLength: number lastMessage: Message } const chatMessagesContext = createContext(null) const ChatMessagesProvider = chatMessagesContext.Provider export const useChatMessages = () => { const context = useContext(chatMessagesContext) if (!context) { throw new Error( 'useChatMessages must be used within a ChatMessagesProvider' ) } return context } function ChatMessages(props: ChatMessagesProps) { const { messages, regenerate, stop, isLoading } = useChatUI() const messageLength = messages.length const lastMessage = messages[messageLength - 1] const isLastMessageFromAssistant = messageLength > 0 && lastMessage?.role !== 'user' const showReload = regenerate && !isLoading && isLastMessageFromAssistant const showStop = stop && isLoading // `isPending` indicate // that stream response is not yet received from the server, // so we show a loading indicator to give a better UX. const isPending = isLoading && !isLastMessageFromAssistant const children = props.children ?? return (
{children}
) } function ChatMessagesList(props: ChatMessagesListProps) { const scrollableChatContainerRef = useRef(null) const { messages } = useChatUI() const { lastMessage, messageLength } = useChatMessages() const scrollToBottom = () => { if (scrollableChatContainerRef.current) { scrollableChatContainerRef.current.scrollTop = scrollableChatContainerRef.current.scrollHeight } } useEffect(() => { scrollToBottom() }, [messageLength, lastMessage]) const children = props.children ?? ( <> {messages.map((message, index) => { return ( ) })} ) return (
{children}
) } function ChatMessagesEmpty(props: ChatMessagesEmptyProps) { const { messages } = useChatUI() if (messages.length > 0) return null if (props.children) { return (
{props.children}
) } return (

{props.heading ?? 'Hello there!'}

{props.subheading ?? "I'm here to help you with your questions."}

) } function ChatMessagesLoading(props: ChatMessagesLoadingProps) { const { isPending } = useChatMessages() if (!isPending) return null const children = props.children ?? ( ) return (
{children}
) } function ChatActions(props: ChatActionsProps) { const { regenerate, stop, requestData } = useChatUI() const { showReload, showStop } = useChatMessages() if (!showStop && !showReload) return null const children = props.children ?? ( <> {showStop && ( )} {showReload && ( )} ) return (
{children}
) } ChatMessages.List = ChatMessagesList ChatMessages.Loading = ChatMessagesLoading ChatMessages.Empty = ChatMessagesEmpty ChatMessages.Actions = ChatActions export default ChatMessages