import type { ModelMessage } from 'ai'; import { Streamdown } from 'streamdown'; import { DataInspector } from '../ui/data-inspector'; interface ConversationViewProps { messages: ModelMessage[]; } export function ConversationView({ messages }: ConversationViewProps) { if (messages.length === 0) { return (
No messages
); } return (
{messages.map((message, index) => ( ))}
); } function MessageBubble({ message }: { message: ModelMessage }) { const role = message.role; const parts = parseContent(message.content); const style = getRoleStyle(role); return (
{/* Role header */}
{role}
{/* Content */}
{parts.map((part, i) => ( ))}
); } interface ParsedPart { type: 'text' | 'tool-call' | 'tool-result'; text?: string; toolName?: string; input?: unknown; output?: unknown; } function ContentPart({ part, role }: { part: ParsedPart; role: string }) { if (part.type === 'text') { if (!part.text) return null; // Use Streamdown for assistant messages (they often contain markdown) if (role === 'assistant') { return (
{part.text}
); } return (
{part.text}
); } if (part.type === 'tool-call') { return (
🔧 {part.toolName}
{part.input != null && (
{typeof part.input === 'string' ? (
                {part.input}
              
) : ( )}
)}
); } if (part.type === 'tool-result') { return (
✓ {part.toolName} result
{part.output != null && (
{typeof part.output === 'string' ? (
                {part.output}
              
) : ( )}
)}
); } return null; } function getRoleStyle(role: string) { switch (role) { case 'user': return { bg: 'var(--ds-blue-100)', border: 'var(--ds-blue-300)', label: 'var(--ds-blue-700)', }; case 'assistant': return { bg: 'var(--ds-gray-100)', border: 'var(--ds-gray-300)', label: 'var(--ds-gray-700)', }; case 'system': return { bg: 'var(--ds-amber-100)', border: 'var(--ds-amber-300)', label: 'var(--ds-amber-700)', }; case 'tool': return { bg: 'var(--ds-green-50)', border: 'var(--ds-green-300)', label: 'var(--ds-green-700)', }; default: return { bg: 'var(--ds-gray-100)', border: 'var(--ds-gray-300)', label: 'var(--ds-gray-700)', }; } } function parseContent(content: unknown): ParsedPart[] { if (typeof content === 'string') { return [{ type: 'text', text: content }]; } if (Array.isArray(content)) { return content.map((part): ParsedPart => { if (typeof part === 'string') { return { type: 'text', text: part }; } if (part?.type === 'text') { return { type: 'text', text: String(part.text ?? '') }; } if (part?.type === 'tool-call') { return { type: 'tool-call', toolName: part.toolName, input: part.input, }; } if (part?.type === 'tool-result') { return { type: 'tool-result', toolName: part.toolName, output: part.output, }; } return { type: 'text', text: '' }; }); } return []; }