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

# `lr-artifact-panel`

- **Import** `import '@aceshooting/lyra-ui/components/lr-artifact-panel.js';` (stable tag alias; registers the tag)
- **Class** `LyraArtifactPanel`, also available unregistered from `@aceshooting/lyra-ui/components/agent-tools/artifact-panel/artifact-panel.class.js`
- **Family** `components/agent-tools/` — see `llms/index.md` for its siblings
- **Status** `stable` since `4.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** none
- **Themeable via** 19 parts, 2 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-artifact-panel`

Shell around one agent-generated artifact: a title/kind header, a preview↔code toggle, version
navigation with restore, a streaming indicator, and built-in copy/download actions. Renders none of
the artifact itself — content is slotted. No content rendering of its own (slots own it), no
dialog/dock chrome (compose `lr-dialog`/`lr-dock-panel`/`lr-multi-split`), no version storage or
diffing (host state; diffs via `lr-diff-view`), no code editing (`lr-code-editor`).

**Properties:** `label?: string` — the artifact's title, shown in the header; omitting it reads back
`undefined` and localizes the `artifactPanelLabel` default for the view-toggle group's accessible name
(with no visible title), while an explicit empty string renders no visible or accessible label.
`kind: string = ''`
— a short kind label (e.g. `document`, `code`), shown as a badge next to `label`. `view: 'preview' |
'code' = 'preview'` (reflected) — which slot is currently visible. `versions: ArtifactVersion[] = []`
(attribute: false, each `{ id, label? }`) — the artifact's version history, oldest first; the last
entry is the latest version. Empty/blank ids and later duplicate ids are omitted before navigation, active lookup,
position counts, and restore events. The active entry's optional `label` renders beside its localized
position. `activeVersionId: string | null = null` (attribute `active-version-id`) — the currently
viewed version's id, or `null` for "the latest version." Removing the named version reconciles the
property to `null` without a user-action event, so reinserting that id cannot unexpectedly repin it.
`streaming: boolean = false` (reflected) — whether the artifact is still being generated; sets
`aria-busy` on the body and shows a text indicator (not animated, so it stays legible under reduced
motion). `copyText: string = ''` (attribute `copy-text`) — the text copied to the clipboard by the
copy button; empty hides the button. `downloadSrc: string = ''` (attribute `download-src`) — the
download URL, sanitized through `safeDownloadHref()` (`http:`/`https:`/`blob:` only — narrower than
the media/resource allowlist, which also permits `data:`); an empty value hides the button. The
sanitizer runs at click time, not render time, so a _non-empty but rejected_ URL still renders the
button and simply emits nothing when pressed. The component never navigates on its own: it emits
`lr-download` with the sanitized `src` and leaves the actual download to the host.
`downloadName: string = ''` (attribute `download-name`) — the suggested filename reported in the
`lr-download` event detail.

**Slots:** default — preview-view content (markdown/html-viewer/browser-frame/image). `code` —
code-view content (typically a `lr-code-block`); the preview/code toggle only renders once this
slot has assigned content. Assigning `view='code'` without assigned code content normalizes back to
`preview`, including after mount. `actions` — extra header controls, rendered between the version
navigation and the built-in copy/download buttons.

**Events:** `lr-view-change` (`detail: { view }`), `lr-version-change` (`detail: { versionId }`,
fired when the previous/next navigation moves to a different version), `lr-restore` (`detail: {
versionId }`, fired by the restore-this-version button; mutates nothing itself), `lr-copy`
(`detail: { ok: true, text }`, after the clipboard write fulfills), `lr-error` plus
`lr-copy-error` (`detail: { ok: false, text, reason, error }`) on a localized failure, and
`lr-download` (`detail: { filename, src }`, with the required sanitized download URL).

**CSS parts:** `base`, `header`, `label`, `kind`, `view-toggle` (rendered only once the `code` slot
has content), `view-button` (carries `data-view="preview"` or `data-view="code"`), `version-nav`
(rendered only once `versions` is non-empty), `version-previous`, `version-previous-glyph` (the `‹`
chevron inside `version-previous`, mirrored via `scaleX(-1)` under `:dir(rtl)`), `version-next`,
`version-next-glyph` (the `›` chevron inside `version-next`, mirrored the same way), `version-position`
(the "Version N of M" text), `version-label` (the active version's optional caller-supplied label),
`restore-button` (rendered only while the active version isn't the latest), `actions`, `copy-button`
(rendered only while `copyText` is non-empty), `download-button`
(rendered only while `downloadSrc` is non-empty), `body`, `streaming-indicator` (rendered only while
`streaming`).

**Themeable custom properties:** `--lr-artifact-panel-view-active-bg` (default
`var(--lr-color-brand-quiet)`) and `--lr-artifact-panel-view-active-color` (default
`var(--lr-color-brand)`) — the background and text color of the pressed (active) preview/code toggle
button. Both follow the state-scoped-property convention described under `lr-span-waterfall`: inline
`var()` fallbacks rather than `:host` declarations, so either can be set on the element or on any
ancestor. They exist because `::part(view-button)[aria-pressed='true']` is invalid CSS, leaving the
library-wide brand tokens as the only prior lever.

```html
<lr-artifact-panel label="report.md" kind="document">
  <lr-markdown id="preview"></lr-markdown>
  <lr-code-block slot="code" id="code" language="markdown"></lr-code-block>
</lr-artifact-panel>
<script type="module">
  const panel = document.querySelector("lr-artifact-panel");
  panel.versions = versions;
  panel.querySelector("#preview").content = markdown;
  panel.querySelector("#code").code = markdown;
  panel.addEventListener("lr-restore", (e) => restoreVersion(e.detail.versionId));
</script>
```
