/** * ChatThread renders the conversation: user prompts, streamed agent answers * (markdown) and inline errors, auto-scrolled to the latest entry. * * @example * */ import { useEffect, useRef } from "react"; import type { ThreadItem } from "../reducer"; import { Markdown } from "./Markdown"; import { ToolCallCard } from "./ToolCallCard"; interface ChatThreadProps { thread: ThreadItem[]; turnActive: boolean; } export function ChatThread({ thread, turnActive }: ChatThreadProps) { const endRef = useRef(null); // biome-ignore lint/correctness/useExhaustiveDependencies: thread and turnActive intentionally retrigger the scroll-to-bottom on every conversation change. useEffect(() => { // Optional call: test DOMs (jsdom) do not implement scrollIntoView. endRef.current?.scrollIntoView?.({ behavior: "smooth" }); }, [thread, turnActive]); return (
{thread.length === 0 && (

Send a prompt to start working with the agent.

)} {thread.map((item, index) => ( // biome-ignore lint/suspicious/noArrayIndexKey: the thread is append-only (never reordered or spliced), so the index is a stable identity. ))} {turnActive && ( the agent is working… )}
); } function ThreadEntry({ item }: { item: ThreadItem }) { switch (item.kind) { case "user": return
{item.text}
; case "agent": return (
); case "thought": return (
đź’­ Thinking
); case "tool": return ; case "error": return (
{item.text}
); } }