Artifact Panel

Pro

Conversation and generated document side by side, with a revision history that records why each version says what it says. 3 variants: split, focus and history.

</>sv-artifact-panel aiartifactcanvasversions
import { SvArtifactPanelComponent } from '@styloviz-pro/artifact-panel';

Demo & usage

Basic usage

Pro

Usage example — open the gallery for the full interactive demo.

Pro

An interactive demo for this component lives in the gallery.

Open live demo

More examples

Focus & history

Pro

When to use

  • An assistant that produces a document rather than a reply — a policy, a spec, a component, a query.
  • Any surface where somebody will later ask "why does it say this", which is every document an assistant edits more than once.
  • For a plain chat with no artifact, use the AI Chat Interface.

Async states

  • loading
  • error
  • retry

Inputs

NameTypeDefaultDescription
loadingbooleanfalseTrue while the artifact is being fetched; shows a skeleton in place of the panel.
errorSvDataError | string | nullnullLoad failure, shown as an error panel in place of the content. Use `toSvDataError()` from `@styloviz/core` to normalize an `HttpErrorResponse`.
variantArtifactVariant'split'Layout: side by side, artifact only, or the revision history.
artifactArtifact | nullnullThe artifact and every revision of it.
messagesreadonly ArtifactMessage[][]The conversation beside it.
selectedVersionIdstring | null (two-way)nullWhich revision is on screen. Two-way and defaulting to the newest — but NOT silently re-pinned when a new revision arrives. A pane that swaps under the reader mid-sentence is the complaint every canvas generates.
showDiffboolean (two-way)falseShow the diff against the previous revision rather than the document.
saveStateArtifactSaveState'saved'Whether the store has this copy of the artifact. THE HOST OWNS THIS, AND THE COMPONENT REFUSES TO GUESS IT. Only the host knows whether its write landed, and a canvas that infers "saved" from "the content input changed" reports success for an edit that never left the browser. Left at `'saved'` the indicator is a quiet confirmation; a host that never sets it is making the same claim every other canvas makes silently. `conflict` is not resolved here either — the component says so, loudly and persistently, and the merge is the host's.

Outputs

EventPayload typeDescription
retryvoidEmitted when the user presses the error panel's "Try again" button (retryable errors only); the host should re-run the failed load.
messageSelectArtifactMessageThe reader activated a message.
versionSelectArtifactVersionThe reader activated a revision.
copiedArtifactVersionThe revision on screen reached the clipboard. Emitted only after the browser confirmed the write — never on a click alone.
copyFailedArtifactVersionCopying the revision on screen failed — the clipboard is absent over plain HTTP and on a server render, and `writeText` rejects when permission is denied. The component says so and selects the text; a host can offer more.
exportRequestedArtifactExportThe reader exported the revision on screen. Emitted AND downloaded: the button works in a host that never wired it, and a host that wants to upload the file instead gets the content, file name and MIME type without re-deriving them.
selectedVersionIdChangestring | nullEmits the new `selectedVersionId` whenever it changes — the output half of `[(selectedVersionId)]`.
showDiffChangebooleanEmits the new `showDiff` whenever it changes — the output half of `[(showDiff)]`.

Wire it to your API

This component is headless about transport: it renders what you give it and reports what the user asks for. Nothing below requires @styloviz/data — drive it from whatever you already use.

Template

Pro

Bound to the names this component actually exposes.

Host class

Pro

Drive the three async inputs from your own fetch.

  • This component does not emit a query, so it renders what you give it. Fetch on your own trigger and bind `messages`, `loading` and `error`.
  • Normalize failures with `toSvDataError()` from `@styloviz/core`. It derives `retryable` from the status, so a 404 does not get a "Try again" button that can only fail again.

Full walkthrough: Connecting to a backend.

Customization & theming

  • `artifact` and `messages` are inputs; import `DEMO_ARTIFACT` / `DEMO_MESSAGES` for a demo.
  • Three layouts via `variant`: `split` (conversation and document), `focus` (document alone) and `history` (every revision with what it changed and why).
  • `selectedVersionId` and `showDiff` are two-way. Activating a conversation turn moves the document to the revision that turn caused.
  • A `kind: 'code'` artifact renders through `sv-code-block` with its language; prose renders as text. The diff renders by line in both cases.
  • `SV_DIFF_LINE_CAP` (2000) is the point past which an exact LCS diff is not computed — the counts still are, from a line-multiset comparison, and the result is labelled `approximate` rather than silently degraded.
  • Past that cap the diff surface itself carries the reason it is empty: pressing 'What changed' shows a notice naming the cap, the counts it does know, and the fact that the positions are not known — never a blank box, which reads as 'nothing changed' on the largest documents.
  • `saveState` is the host's: `saved` / `saving` / `unsaved` / `conflict`, each with its own sentence, and the last two styled as needing attention. The component never infers it from a changed input — only the host knows whether its write landed — and it does not resolve a conflict, it states one.
  • A staleness banner appears when the reader is behind, and offers the jump rather than taking it.
  • Copy and Export act on the revision **on screen**, not silently the newest. Copy confirms only after the browser accepted the write: `(copied)` then, `(copyFailed)` and a select-the-text fallback when the clipboard is unavailable. Export downloads `<title>-v<n>.<ext>` (extension from `kind` and `language`) and emits `(exportRequested)` with `{ filename, content, mimeType, version }`, so a host can upload it instead. `svArtifactExport()` builds the same payload outside the component. Both controls are withdrawn when the AI privacy policy sets `copy: false` (`provideSvAiPrivacy`).

Best practices

  • Do: Record `causedBy` on every version. Without it the document remembers its content and forgets its reason, and six turns later nobody can answer for it.
  • Do: Read the region count before accepting a revision. One instruction should produce one region; five regions means something was rewritten that nobody asked about.
  • Don't: Do not silently re-pin the newest version. Either the pane swaps under the reader mid-sentence, or they review a document two revisions out of date believing it is current.
  • Tip: The diff is by line, via longest common subsequence — a line-by-line walk reports the whole document as changed the moment a sentence is inserted at the top.

Accessibility

  • Each revision row carries its change summary, its cause and the scope warning in its accessible name.
  • The diff marks additions and removals with a sign character as well as colour.
  • The diff toggle is hidden on the first revision rather than present and dead — there is nothing to compare it against.
  • The copy outcome is announced through a `role="status"` region that is always present, and the Copy and Export buttons name the version (and the file) they act on.

Feedback on Artifact Panel

Spotted a gap in this page, or something that didn't work as described? Comments are GitHub Discussions in the public styloviz/community repo, so posting needs a GitHub account.

Loads giscus.app and GitHub — nothing is fetched until you click.