---
## 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`