import { useState } from 'react' import { Streamdown } from 'streamdown' import type { ChatToolCall } from '@/app/chat/store' import './AssistantMessage.css' interface AssistantMessageProps { children: string /** Reasoning summary streamed alongside the visible answer. Renders * as a collapsible block above the answer when present. */ thinking?: string /** Tool invocations the assistant made during this turn. Rendered as * collapsible cards under the visible answer. */ tools?: ChatToolCall[] /** True while the message is still being streamed in — disables hard * parsing of unfinished code fences / tables / etc. */ streaming?: boolean } function ChevronRight({ open }: { open: boolean }) { return ( ) } export function AssistantMessage({ children, thinking, tools, streaming }: AssistantMessageProps) { // Default-open while streaming so users see reasoning land live; once // the answer is final, the user can collapse it to focus on the answer. const [open, setOpen] = useState(true) return (
{thinking && thinking.length > 0 && (
{open && ( {thinking} )}
)} {children.length > 0 && ( {children} )} {tools?.map((t) => )}
) } function ToolCall({ tool }: { tool: ChatToolCall }) { const [open, setOpen] = useState(false) const pending = tool.result === undefined return (
{open && (
Input
{prettyJson(tool.inputJson)}
{tool.result !== undefined && ( <>
Output
{tool.result}
)}
)}
) } /** Render a one-line preview of the tool's input, falling back to the * raw JSON if it doesn't parse (it's still streaming, partial JSON). */ function summarizeInput(json: string): string { if (!json) return '' try { const parsed = JSON.parse(json) as Record // Pick the most useful single field if obvious. for (const k of ['command', 'file_path', 'path', 'pattern', 'url', 'query']) { if (typeof parsed[k] === 'string') return String(parsed[k]) } return JSON.stringify(parsed) } catch { return json.length > 80 ? json.slice(0, 80) + '…' : json } } function prettyJson(json: string): string { if (!json) return '' try { return JSON.stringify(JSON.parse(json), null, 2) } catch { return json } }