--- ## ReasoningDisclosure Default renderer for `reasoning` parts in ChatMessage. Wraps a model's thinking trace in a collapsed, muted disclosure: the header shows a state label ("Thinking" while streaming, "Thought for Xs" once settled with a duration, otherwise "Reasoning"), the body renders the reasoning text through StreamingMarkdown in a damped tertiary tone. Stays collapsed by default — including while streaming — so a growing reasoning trace never steals the answer's space; the caller drives the `streaming` flag (the part carries no status). **Import:** `import { ReasoningDisclosure } from '@urbicon-ui/blocks';` ### Examples ```svelte ``` ### Api | Prop | Type | Required | Default | Description | | --- | --- | :---: | --- | --- | | reasoning | `ChatReasoningPart` | yes | | The reasoning part to render (`{ type: 'reasoning'; text; durationMs? }`). Required. | | ...HTMLAttributes | `HTMLAttributes` | no | | HTML attributes (excluding: 'class' | 'children') | | class | `string` | no | | Extra classes merged onto the root (the underlying Collapsible base). | | defaultOpen | `boolean` | no | false | Initial expanded state for uncontrolled usage. Reasoning stays collapsed by default, even while streaming. | | formatDuration | `(seconds: number) => string` | no | (s) => `Thought for ${s}s` | Header label once settled with a `durationMs`, receiving whole seconds (`Math.round(durationMs / 1000)`). | | onOpenChange | `(open: boolean) => void` | no | | Fires once per trigger-driven open transition, after the state is applied. | | open | `boolean` | no | | Whether the disclosure is expanded. Supports `bind:open`. Passing `open` without `bind:` requires mirroring every `onOpenChange` back into your state. | | preset | `string` | no | | Apply a named preset registered via ``. Prefer this over `class` overrides when the requested look falls outside the semantic intent palette. | | reasoningLabel | `string` | no | 'Reasoning' | Header label once settled without a `durationMs`. | | slotClasses | `Partial>` | no | | Per-slot class overrides. Slots: `trigger` (header button), `label` (state label), `chevron`, `content` (markdown wrapper). | | streaming | `boolean` | no | false | The reasoning trace is currently growing. Drives the "Thinking" label and its pulse; supplied by the caller (the part itself carries no status). | | thinkingLabel | `string` | no | 'Thinking' | Header label while `streaming`. | | unstyled | `boolean` | no | | Strip ReasoningDisclosure's own tv() classes (trigger/label/chevron/content); the collapse mechanics stay. | | urlPolicy | `MarkdownUrlPolicy` | no | | URL policy passed through to the inner StreamingMarkdown for any links in the reasoning text (same strict default — untrusted model output). | Inherited from: - Omit, 'class' | 'children'> (omit-pattern) ### Types ```ts type ChatReasoningPart = Extract ``` ```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 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 SlotNames = keyof ReturnType & string ``` ```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) `trigger`, `label`, `chevron`, `content`