--- ## ChatMessageList Scrollable conversation log with a stick-to-bottom engine: follows streaming content while the reader is at the bottom, breaks off on upward scroll, and offers a floating jump-back button with a new-message counter. Anchors the scroll position when older history is prepended. Announces generation start and the completed answer once to screen readers instead of every token. Renders ChatMessage per entry by default; override per message via the `message` snippet. **Import:** `import { ChatMessageList } from '@urbicon-ui/blocks';` ### Examples ```svelte regenerate(m.id)} onStickChange={(stuck) => (following = stuck)} /> ``` ### Api | Prop | Type | Required | Default | Description | | --- | --- | :---: | --- | --- | | messages | `ChatMessageData[]` | yes | | The conversation, oldest first. The component never mutates it. Note: rest attributes (incl. a raw `onscroll`) land on the non-scrolling root — observe follow-state via `onStickChange` instead. | | ...HTMLAttributes | `HTMLAttributes` | no | | HTML attributes (excluding: 'children') | | abortedLabel | `string` | no | | Screen-reader announcement when a stream ends in `aborted` without text. | | class | `string` | no | | Extra classes merged onto the root element. | | density | `ChatMessageProps['density']` | no | | Message density handed to every default ChatMessage. | | empty | `Snippet` | no | | Empty-state content. Default: an EmptyState with `emptyTitle`/`emptyDescription`. | | emptyDescription | `string` | no | | Empty-state description. | | emptyTitle | `string` | no | | Empty-state heading. | | errorLabel | `string` | no | | Screen-reader announcement when a stream ends in `error` without text. | | generatingLabel | `string` | no | | Screen-reader announcement when an assistant message starts streaming. | | layout | `ChatMessageProps['layout']` | no | | Message layout handed to every default ChatMessage. | | listLabel | `string` | no | | Accessible name of the scrollable conversation region. | | message | `Snippet<[ChatMessageListItemContext]>` | no | | Per-message renderer override. Default: `` wired with the props below. | | newMessagesLabel | `string` | no | | Label suffix of the jump button while new messages are pending (prefixed with the count). | | onRegenerate | `(message: ChatMessageData) => void` | no | | Regenerate handler — wired only to the last message when it is an assistant message. | | onRetry | `(message: ChatMessageData) => void` | no | | Retry handler for messages in `error`/`aborted` status. | | onStickChange | `(stuck: boolean) => void` | no | | Fires when the list starts (`true`) or stops (`false`) following new content. | | partRenderers | `ChatMessageProps['partRenderers']` | no | | Part-renderer overrides handed to every default ChatMessage. | | preset | `string` | no | | Apply a named preset registered via ``. Prefer this over `class` overrides when the requested look falls outside the semantic intent palette. | | scrollToBottomLabel | `string` | no | | Accessible label of the jump button when there are no new messages. | | slotClasses | `Partial>` | no | | Per-slot class overrides. Slots: root | viewport | content | empty | newButton | | unstyled | `boolean` | no | | Remove all default tv classes. | | urlPolicy | `MarkdownUrlPolicy` | no | | URL policy handed to every default ChatMessage (links, citations, attachments). | Inherited from: - Omit, 'children'> (omit-pattern) ### Types ```ts interface ChatMessageListItemContext { message: ChatMessageData; index: number; isLast: boolean; } ``` ```ts interface ChatMessageData { /** Stable unique id — keyed `{#each}` identity, never an array index. */ id: string; role: ChatRole; /** Ordered renderable segments. A plain text answer is `[{ type: 'text', text }]`. */ parts: ChatMessagePart[]; createdAt?: Date; /** Omitted counts as `complete`. */ status?: ChatMessageStatus; /** Consumer-defined extras (model name, token counts, …). Not rendered by defaults. */ metadata?: Record; } ``` ```ts interface MarkdownUrlPolicy { /** * Allowed link protocols (with trailing colon). Relative URLs are always * allowed for links. @default ['http:', 'https:', 'mailto:', 'tel:'] */ allowedLinkProtocols?: string[]; /** * Prefix allowlist for image sources, matched against the normalized * absolute URL. Empty (the default) blocks every external image; relative * sources are allowed. Prefer deep, specific prefixes — broad ones are * open-redirect bait. `data:` images are blocked unless a `data:` prefix * is listed explicitly. * @default [] */ allowedImagePrefixes?: string[]; /** Observability hook — fires once per blocked URL occurrence. */ onBlocked?: (kind: 'link' | 'image', url: string) => void; } ``` ```ts type ChatRole = 'user' | 'assistant' | 'system' ``` ```ts type ChatPartRenderers = { [K in Exclude]?: Snippet< [Extract] >; } ``` ```ts type ChatMessagePart = | { type: 'text'; text: string } /** Model reasoning. Collapsed, tertiary presentation; `durationMs` feeds the "Thought for Xs" label. */ | { type: 'reasoning'; text: string; durationMs?: number } /** A tool invocation with its lifecycle state. Rendered as a ToolCallCard by default; override via `partRenderers`. */ | { type: 'tool-call'; id: string; name: string; state: 'pending' | 'running' | 'complete' | 'error'; input?: unknown; output?: unknown; errorMessage?: string; } /** A cited source. Same shape as CitationSource — also feeds StreamingMarkdown's `sources` for `[id]` markers. */ | ({ type: 'source' } & CitationSource) /** * A file attached to the message. `url` is only ever rendered as a * policy-checked download link (never as an inline image/iframe) — LLM- or * server-supplied URLs are untrusted input. */ | { type: 'attachment'; name: string; mimeType: string; size?: number; url?: string } /** Declarative A2UI payload (P4). Validated fail-loud by A2UIView; ignored by default renderers until then. */ | { type: 'a2ui'; payload: unknown } ``` ```ts type ChatMessageStatus = 'streaming' | 'complete' | 'error' | 'aborted' ``` ```ts type SlotNames = keyof ReturnType & string ``` ```ts type VariantProps = Omit< Exclude[0], undefined>, 'class' > ``` ```ts interface CitationSource { /** Stable id — matches the `[id]` marker in the streamed markdown. */ id: string; /** Human-readable source title, always shown in the popover (and as the chip label under `citationStyle="label"`). */ title: string; /** Source URL. Rendered as an outbound link only when it passes the `urlPolicy`; a blocked or absent URL shows title/snippet with no link. */ url?: string; /** Short excerpt shown under the title, clamped to ~3 lines. */ snippet?: string; } ``` ### Slots (slotClasses keys) `root`, `viewport`, `content`, `empty`, `newButton`