'use client' import * as React from 'react' import { cn } from '../../lib/utils' import { ChatThread } from '../chat/ChatThread' import { ChatComposer } from '../chat/ChatComposer' import type { ChatMessageData } from '../chat/types' export interface ConversationTimelineProps { /** The agent-shaped timeline entries (already chat-shaped: status/kind/toolName). */ entries: ChatMessageData[] /** Header title. */ title?: string /** 0..100 progress bar shown under the header (omit to hide). */ progressPercent?: number /** Short status label shown in the header. */ statusLabel?: string /** Called with the composed message text. Sending state is tracked internally. */ onSend?: (text: string) => void | Promise /** The agent is working / entries are loading — shows the working indicator. */ isLoading?: boolean /** Load older entries (renders a "Load more" control when `hasMore`). */ onLoadMore?: () => void hasMore?: boolean composerPlaceholder?: string /** Rendered when there are no entries. */ emptyState?: React.ReactNode /** Custom message-body renderer passed through to ChatThread (e.g. markdown). */ renderContent?: (content: string, message: ChatMessageData) => React.ReactNode className?: string } /** * The "steer the agent" surface: a scrolling timeline of agent events plus a * composer that sends a message back to the agent. Built on the existing * ChatThread / ChatComposer, so it inherits their auto-scroll, streaming * indicators, tool/status rows, and keyboard handling. Fully generic — it takes * `ChatMessageData[]` and an `onSend` callback; it knows nothing about the * transport or any app model. */ export function ConversationTimeline({ entries, title, progressPercent, statusLabel, onSend, isLoading = false, onLoadMore, hasMore = false, composerPlaceholder = 'Message the agent…', emptyState, renderContent, className, }: ConversationTimelineProps) { const [sending, setSending] = React.useState(false) const handleSubmit = React.useCallback( (text: string) => { if (!onSend) return const result = onSend(text) if (result && typeof (result as Promise).then === 'function') { setSending(true) void (result as Promise).finally(() => setSending(false)) } }, [onSend], ) const showHeader = !!title || !!statusLabel || progressPercent != null return (
{showHeader && (
{title &&

{title}

} {statusLabel && {statusLabel}}
{progressPercent != null && (
Progress {Math.round(progressPercent)}%
)}
)}
{hasMore && onLoadMore && (
)}
{onSend && ( )}
) }