import type { Snippet } from 'svelte'; import type { HTMLAttributes } from 'svelte/elements'; import type { ChatMessageData, ChatMessagePart, ChatRole } from '../chat.types.js'; import type { MarkdownUrlPolicy } from '../markdown/types.js'; import type { ChatMessageSlots, ChatMessageVariants } from './chat-message.variants.js'; /** * Per-part snippet overrides keyed by part `type`. When a renderer exists for a * part's type it replaces the built-in rendering for that part (one level up * from StreamingMarkdown's node `renderers`) — this is how P3 swaps in * ToolCallCard / ReasoningDisclosure and P4 adds A2UIView without touching this * component. `source` is intentionally not overridable here: sources are * collected into the citation footer, not rendered inline. */ export type ChatPartRenderers = { [K in Exclude]?: Snippet<[ Extract ]>; }; /** * @summary One message in a conversation, with its text, reasoning, tool calls and attachments. * @description Renders one `ChatMessageData` — its ordered parts (markdown text via * StreamingMarkdown, reasoning, tool-call status lines, policy-checked attachment * chips) plus a citation footer, streaming placeholder, error/aborted Alert, a * hover-revealed copy/regenerate action bar and a timestamp. `layout="bubble"` * (default) tints and aligns per role; `layout="plain"` is a document-like * full-width Claude-style column. Untrusted URLs never render as inline media — * only as `urlPolicy`-checked download links. Compose many of these under * ChatMessageList; the consumer owns the `ChatMessageData[]`. * * @tag ai * @tag display * @related ChatMessageList * @related StreamingMarkdown * @stability experimental * * @example * ```svelte * regenerate(msg.id)} /> * ``` */ export interface ChatMessageProps extends Omit, Omit, 'children' | 'class'> { /** The message to render. The component never mutates it. */ message: ChatMessageData; /** * URL policy applied to attachment links and forwarded to StreamingMarkdown / * CitationChip. Strict by default (untrusted LLM/server output). Keep it * referentially stable to avoid re-parsing streamed markdown. */ urlPolicy?: MarkdownUrlPolicy; /** Provide to render a "Regenerate" action. Called on click. */ onRegenerate?: () => void; /** Provide to render a "Retry" button in the error/aborted Alert. Called on click. */ onRetry?: () => void; /** Avatar override. Receives the message role; default is the Avatar primitive with a role icon. */ avatar?: Snippet<[{ role: ChatRole; }]>; /** Action-bar override. Receives the message; default is copy (+ regenerate when `onRegenerate` is set). */ actions?: Snippet<[{ message: ChatMessageData; }]>; /** Metadata override. Receives the message; default is `createdAt` as a `