import { memo, type MutableRefObject, type Ref } from "react"; import { useStyletron } from "baseui"; import { LabelSmall, LabelXSmall } from "baseui/typography"; import { Copy } from "lucide-react"; import { HistoryMinimap } from "./history-minimap"; import { SkeletonBlock, SkeletonLine } from "./skeleton"; import { SpinnerDot } from "./ui"; import { buildDisplayMessages, formatMessageDuration, formatMessageTimestamp, type AgentTab, type HistoryEvent, type Message } from "./view-model"; export const MessageList = memo(function MessageList({ tab, scrollRef, messageRefs, historyEvents, onSelectHistoryEvent, copiedMessageId, onCopyMessage, thinkingTimerLabel, }: { tab: AgentTab | null | undefined; scrollRef: Ref; messageRefs: MutableRefObject>; historyEvents: HistoryEvent[]; onSelectHistoryEvent: (event: HistoryEvent) => void; copiedMessageId: string | null; onCopyMessage: (message: Message) => void; thinkingTimerLabel: string | null; }) { const [css, theme] = useStyletron(); const messages = buildDisplayMessages(tab); return ( <> {historyEvents.length > 0 ? : null}
{tab && messages.length === 0 ? ( tab.created && tab.status === "running" ? ( /* New tab that's loading — show message skeleton */
) : (
{!tab.created ? "Choose an agent and model, then send your first message" : "No messages yet in this session"}
) ) : null} {messages.map((message) => { const isUser = message.sender === "client"; const isCopied = copiedMessageId === message.id; const messageTimestamp = formatMessageTimestamp(message.createdAtMs); const displayFooter = isUser ? messageTimestamp : message.durationMs ? `${messageTimestamp} • Took ${formatMessageDuration(message.durationMs)}` : null; return (
{ if (node) { messageRefs.current.set(message.id, node); } else { messageRefs.current.delete(message.id); } }} className={css({ display: "flex", justifyContent: isUser ? "flex-end" : "flex-start" })} >
{message.text}
{displayFooter ? ( {displayFooter} ) : null}
); })} {tab && tab.status === "running" && messages.length > 0 ? (
Agent is thinking {thinkingTimerLabel ? ( {thinkingTimerLabel} ) : null}
) : null}
); });