<!-- GENERATED by scripts/build-llms.mjs from llms/conversation.md — do not edit this file. -->

# `lr-message-parts`

- **Import** `import '@aceshooting/lyra-ui/components/lr-message-parts.js';` (stable tag alias; registers the tag)
- **Class** `LyraMessageParts`, also available unregistered from `@aceshooting/lyra-ui/components/conversation/message-parts/message-parts.class.js`
- **Family** `components/conversation/` — see `llms/index.md` for its siblings
- **Status** `stable` since `7.0.0` — see the maturity and deprecation policy in `llms/shared.md`
- **Release history** [CHANGELOG.md](../../CHANGELOG.md); family-wide breaking-change summaries: [llms-full.txt](../../llms-full.txt)
- **Deprecations** none
- **Optional peers** `dompurify`, `katex`, `marked`, `shiki` — see `llms/peers.md`
- **Themeable via** 16 parts, 5 custom properties — see this component's own `@csspart`/`@cssprop` list below
- **Library-wide behavior** (events, form association, `locale`/`strings`, tokens, TS types): `llms/shared.md`

---

## `lr-message-parts`

Ordered renderer for provider-neutral `MessagePart[]`: text, reasoning, tool call/result, citation,
attachment, data/widget, audio, and error parts can interleave without flattening stream order.
Built-in text and reasoning Markdown receives each part's `state === 'streaming'` hint and displays
accumulated plain text without parsing or highlighting. Replacing that same-id part with
`state: 'complete'` parses and renders the final content.
Citation badge ranks are precomputed in one linear pass per render, rather than rescanning and
allocating every preceding part for each citation in a citation-heavy or growing message.

**Properties:** `parts: MessagePart[] = []` (attribute: false); `contentMode: MessagePartsContentMode =
'markdown'` (attribute `content-mode`, reflected) and `showReasoning: boolean = true` (attribute
`show-reasoning`, reflected, with string-aware true-default conversion);
`maxRenderedParts: number = 0` (attribute `max-rendered-parts`) — `0` (the default) renders every
part, unbounded, matching every prior release; a positive value windows rendering to the newest N
parts without touching the host's `parts` data. Citation ranks are unaffected by the window: they
are still derived from the full sequence first, in the same linear prepass, so a badge's number
stays stable even once an earlier citation rolls out of the rendered window. Opt in for a message
that can grow an unusually large number of interleaved parts (e.g. a long agentic run with many
tool-call/tool-result pairs), where unbounded live DOM can visibly stall the main thread.
`renderPart?: MessagePartRenderer` (attribute: false), where returning `undefined` delegates that
part to the built-in renderer; `accessibleLabel: string | null = null` (attribute `aria-label`).

Unsupported direct or `content-mode` attribute values normalize and reflect as `markdown`.

`MessagePartRenderer = (part: MessagePart, index: number) => unknown`; `MessagePart` and its
discriminated part shapes come from the `@aceshooting/lyra-ui/ai` subpath. Tool results are a strict
success/error union: a success has `result` and cannot have `error`; an error has `error` and may
retain partial `result`. Audio is a single `{ type: 'audio'; src?; transcript?; mimeType? }` part,
and data parts carry exactly one of `data` or `widget`. Empty ids and later duplicate occurrences
are ignored so each rendered identity and announcement remains unambiguous.

**Events:** `lr-citation-select` (`{ citation }`), `lr-part-retry` (`{ part }`). Composed child
events pass through unchanged: `lr-anchor-result`, `lr-citation-open`, `lr-copy`,
`lr-highlight-activate`, `lr-link-click`, `lr-preview-request`, `lr-remove`, `lr-render-error`, `lr-retry`,
`lr-search-change`, `lr-text-select`, `lr-toggle`, `lr-tool-call-chip-select`, `lr-widget-action`,
and `lr-widget-state-change`. The `lr-tool-chip-select` alias passthrough was removed in 9.0.0.

**CSS parts:** `base`, `part`, `part-streaming`, `text`, `reasoning`, `tool-call`, `tool-result`,
`tool-result-error`, `citation`, `attachment`, `data`, `audio`, `audio-control`,
`audio-transcript`, `error`, `retry`.

**Themeable custom properties:** `--lr-message-parts-streaming-color` (default
`var(--lr-color-text-quiet)`) controls a streaming wrapper's inherited text color.
`--lr-message-parts-audio-transcript-color` (default `var(--lr-color-text-quiet)`) controls an
audio transcript's text color. Error parts have separate
`--lr-message-parts-error-border-color` (default `var(--lr-color-danger)`),
`--lr-message-parts-error-background` (default `var(--lr-color-danger-quiet)`), and
`--lr-message-parts-error-color` (default `var(--lr-color-danger)`) hooks. All five are inline
fallbacks, so setting one on an ancestor rethemes only that state longhand.

**Slots:** none. **Optional peer deps:** those of composed content only: Markdown can use
`marked`/`dompurify`, and code content can use `shiki`; every composed primitive retains its own
fallback.

Error parts remain ordinary visible content. After the initial baseline, each newly added error-part
`id` is also appended through the shared assertive light-DOM announcement sink, using the caller's
message or the localized fallback. Existing history and reconnect renders stay silent; removing an
error id and later adding it again creates a new announcement.

```ts
import "@aceshooting/lyra-ui/components/conversation/message-parts/message-parts.js";
```

**Additional API surface:**

- `lr-anchor-result` event — Passthrough from rendered Markdown.
- `lr-citation-open` event — Passthrough from a rendered citation's full-preview action.
- `lr-copy` event — Passthrough from rendered JSON content.
- `lr-highlight-activate` event — Passthrough from rendered Markdown.
- `lr-link-click` event — Passthrough from rendered Markdown.
- `lr-preview-request` event — Passthrough from a rendered attachment. Not cancelable as of 10.0.0:
  `<lr-attachment-chip>` dropped the flag, since it owns no preview default action to veto.
- `lr-remove` event — Passthrough from a rendered attachment.
- `lr-render-error` event — Passthrough from rendered Markdown, tool-result, or widget content.
- `lr-retry` event — Passthrough from a rendered attachment.
- `lr-search-change` event — Passthrough from rendered JSON content.
- `lr-text-select` event — Passthrough from rendered Markdown.
- `lr-toggle` event — Passthrough from a rendered reasoning panel.
- `lr-tool-call-chip-select` event — Passthrough from a rendered tool-call chip. The
  `lr-tool-chip-select` alias it replaced was removed in 9.0.0.
- `lr-widget-action` event — Passthrough from a rendered declarative widget.
- `lr-widget-state-change` event — Passthrough from a rendered controlled widget.
