File size: 1,854 Bytes
f59fbe2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | import ScrollToBottom, { useScrollToBottom, useSticky } from "react-scroll-to-bottom";
import { IconArrowDown } from "@tabler/icons-react";
import { ChatMessage } from "./ChatMessage";
import { WelcomeScreen } from "./WelcomeScreen";
import { useChatStore } from "@/stores";
import { cn } from "@/lib/utils";
import { getForkTurnIndex } from "shared/fork-turn-index";
function ScrollButton() {
const scrollToBottom = useScrollToBottom();
const [sticky] = useSticky();
if (sticky) return null;
return (
<button
onClick={() => scrollToBottom()}
className={cn("absolute bottom-4 right-4 p-2 rounded-full z-10", "bg-blue-400 text-white shadow-lg", "hover:bg-blue-600 transition-all")}
>
<IconArrowDown className="size-4" />
</button>
);
}
function MessageList() {
const messages = useChatStore((s) => s.messages);
const isStreaming = useChatStore((s) => s.isStreaming);
return (
<>
<div className="">
{messages.map((message, idx) => (
<ChatMessage
key={message.id}
message={message}
turnIndex={getForkTurnIndex(messages, idx)}
isStreaming={isStreaming && idx === messages.length - 1 && message.role === "assistant"}
/>
))}
</div>
<ScrollButton />
</>
);
}
export function ChatArea() {
const messageCount = useChatStore((s) => s.messages.length);
if (messageCount === 0) {
return (
<div className="h-full flex items-center justify-center relative">
<WelcomeScreen />
</div>
);
}
return (
<div className="h-full relative">
<ScrollToBottom className="h-full" scrollViewClassName="h-full overflow-y-auto overflow-x-hidden" followButtonClassName="hidden" initialScrollBehavior="auto">
<MessageList />
</ScrollToBottom>
</div>
);
}
|