import type { ChatBlock } from '@/chat/types' import type { MessageStatus } from '@/types/api' import type { ApiClient } from '@/api/client' import type { SessionMetadataSummary } from '@/types/api' import { MarkdownRenderer } from '@/components/MarkdownRenderer' import { ToolCard } from '@/components/ToolCard/ToolCard' function ErrorIcon() { return ( ) } function MessageStatusIndicator(props: { status?: MessageStatus onRetry?: () => void }) { if (props.status !== 'failed') { return null } return ( {props.onRetry ? ( ) : null} ) } function formatUnixTimestamp(value: number): string { const ms = value < 1_000_000_000_000 ? value * 1000 : value const date = new Date(ms) if (Number.isNaN(date.getTime())) return String(value) return date.toLocaleString() } function renderEventLabel(event: ChatBlock & { kind: 'agent-event' }): string { const data = event.event as { type: string; [key: string]: unknown } if (data.type === 'switch') { const mode = data.mode === 'local' ? 'local' : 'remote' return `🔄 Switched to ${mode}` } if (data.type === 'title-changed') { const title = typeof data.title === 'string' ? data.title : '' return title ? `Title changed to "${title}"` : 'Title changed' } if (data.type === 'permission-mode-changed') { const mode = typeof data.mode === 'string' ? data.mode : 'default' return `🔐 Permission mode: ${mode}` } if (data.type === 'limit-reached') { const endsAt = typeof data.endsAt === 'number' ? data.endsAt : null return endsAt ? `⏳ Usage limit reached until ${formatUnixTimestamp(endsAt)}` : '⏳ Usage limit reached' } if (data.type === 'message') { return typeof data.message === 'string' ? data.message : 'Message' } try { return JSON.stringify(data) } catch { return 'Event' } } export function ChatBlockList(props: { api: ApiClient sessionId: string metadata: SessionMetadataSummary | null disabled: boolean onRefresh: () => void blocks: ChatBlock[] onRetryMessage?: (localId: string) => void }) { return (
{props.blocks.map((block) => { if (block.kind === 'user-text') { const userBubbleClass = 'w-fit max-w-[92%] ml-auto rounded-xl border border-[var(--app-border)] bg-[var(--app-secondary-bg)] px-3 py-2 text-[var(--app-fg)] shadow-sm' const status = block.status const onRetry = block.localId && status === 'failed' && props.onRetryMessage ? () => props.onRetryMessage!(block.localId!) : undefined return (
{status ? (
) : null}
) } if (block.kind === 'agent-text') { return (
) } if (block.kind === 'agent-event') { return (
{renderEventLabel(block)}
) } if (block.kind === 'tool-call') { const isTask = block.tool.name === 'Task' return (
{block.children.length > 0 ? ( isTask ? (
Task details ({block.children.length})
) : (
) ) : null}
) } return null })}
) }