import HistoricalMessage from "./HistoricalMessage"; import PromptReply from "./PromptReply"; import { useEffect, useRef, useState } from "react"; import { ArrowDown, CircleNotch } from "@phosphor-icons/react"; import debounce from "lodash.debounce"; export default function ChatHistory({ settings = {}, history = [] }) { const replyRef = useRef(null); const [isAtBottom, setIsAtBottom] = useState(true); const chatHistoryRef = useRef(null); useEffect(() => { scrollToBottom(); }, [history]); const handleScroll = () => { if (!chatHistoryRef.current) return; const diff = chatHistoryRef.current.scrollHeight - chatHistoryRef.current.scrollTop - chatHistoryRef.current.clientHeight; // Fuzzy margin for what qualifies as "bottom". Stronger than straight comparison since that may change over time. const isBottom = diff <= 40; setIsAtBottom(isBottom); }; const debouncedScroll = debounce(handleScroll, 100); useEffect(() => { function watchScrollEvent() { if (!chatHistoryRef.current) return null; const chatHistoryElement = chatHistoryRef.current; if (!chatHistoryElement) return null; chatHistoryElement.addEventListener("scroll", debouncedScroll); } watchScrollEvent(); }, []); const scrollToBottom = () => { if (chatHistoryRef.current) { chatHistoryRef.current.scrollTo({ top: chatHistoryRef.current.scrollHeight, behavior: "smooth", }); } }; if (history.length === 0) { return (

{settings?.greeting ?? "Send a chat to get started!"}

); } return (
{history.map((props, index) => { const isLastMessage = index === history.length - 1; const isLastBotReply = index === history.length - 1 && props.role === "assistant"; if (isLastBotReply && props.animate) { return ( ); } return ( ); })} {!isAtBottom && (
)}
); } export function ChatHistoryLoading() { return (
); }