Artifact Panel
ProConversation 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.
import { SvArtifactPanelComponent } from '@styloviz-pro/artifact-panel';Demo & usage
Basic usage
ProUsage example — open the gallery for the full interactive demo.
An interactive demo for this component lives in the gallery.
Open live demoMore examples
Focus & history
ProWhen 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
| Name | Type | Default | Description |
|---|---|---|---|
| loading | boolean | false | True while the artifact is being fetched; shows a skeleton in place of the panel. |
| error | SvDataError | string | null | null | Load failure, shown as an error panel in place of the content. Use `toSvDataError()` from `@styloviz/core` to normalize an `HttpErrorResponse`. |
| variant | ArtifactVariant | 'split' | Layout: side by side, artifact only, or the revision history. |
| artifact | Artifact | null | null | The artifact and every revision of it. |
| messages | readonly ArtifactMessage[] | [] | The conversation beside it. |
| selectedVersionId | string | null (two-way) | null | Which 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. |
| showDiff | boolean (two-way) | false | Show the diff against the previous revision rather than the document. |
| saveState | ArtifactSaveState | '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
| Event | Payload type | Description |
|---|---|---|
| retry | void | Emitted when the user presses the error panel's "Try again" button (retryable errors only); the host should re-run the failed load. |
| messageSelect | ArtifactMessage | The reader activated a message. |
| versionSelect | ArtifactVersion | The reader activated a revision. |
| copied | ArtifactVersion | The revision on screen reached the clipboard. Emitted only after the browser confirmed the write — never on a click alone. |
| copyFailed | ArtifactVersion | Copying 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. |
| exportRequested | ArtifactExport | The 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. |
| selectedVersionIdChange | string | null | Emits the new `selectedVersionId` whenever it changes — the output half of `[(selectedVersionId)]`. |
| showDiffChange | boolean | Emits 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
ProBound to the names this component actually exposes.
Host class
ProDrive 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.