| name | rendering-artifacts |
| description | When the user wants to SEE, render, visualize, preview, or "show me" a chart, diagram, mock-up, table, or interactive widget — render it with the show_artifact tool instead of writing files to disk. |
Rendering artifacts (generative UI)
The console has an Artifact panel powered by the show_artifact tool. Use it whenever the
user wants to look at something rendered rather than get source files.
When to use show_artifact (NOT the filesystem)
- "show me…", "render…", "visualize…", "draw…", "make a chart/diagram/flowchart of…",
"build a little widget/demo to see…", "preview…"
- → call
show_artifact(kind, code); it renders sandboxed and the user sees it immediately.
Reach for show_artifact before writing files. Writing .jsx/.html to the workspace gives
the user files to wire up themselves — not what they asked for when they want to see it.
Kinds
mermaid — flowcharts, sequence/ER/gantt diagrams. code is the Mermaid definition.
markdown — a Markdown document (notes, a README, a write-up). Rendered with design-system
prose styling; GitHub-style tables/lists/code work, and a ```mermaid fence becomes a live
diagram. Reach for this over html when you just want formatted text.
html — a full or partial HTML document (with inline <style>/<script> as needed).
svg — inline SVG markup (icons, simple charts).
react — a self-contained component script that renders into #root; React, ReactDOM, and
Babel are provided. Easiest: name your top-level component App and it auto-mounts — you
don't have to write the mount call. (You still can: ReactDOM.createRoot(...).render(...); an
explicit render always wins.) React artifacts can also import from a curated offline set
(see below).
Richer React: charts, icons, and design-system components
react artifacts may import (ES modules) from this curated, fully-offline set — no network:
-
@pl/ui — protoLabs design-system component wrappers that match the console theme.
Enough to prototype real layouts and components, not just widgets:
- Layout / structure —
AppShell ({header, sidebar, aside}) · Header ({name, org, actions})
· SideNav + SideNavItem ({icon, active}) · Container · Section · Panel · Grid
({cols, gap, auto}) · Row ({label, desc, status}) · Hero ({cta}) · Divider
- Navigation —
Tabs + Tab ({active, icon}) · Segmented + SegmentedButton ·
Menu + MenuItem ({icon, destructive}) + MenuSeparator
- Data / content —
Table · Board · Stats + Stat ({value, label}) · Steps + Step
({num, title}) · Progress ({value, max, variant}) · Accordion + AccordionItem
({title, open}) · Avatar ({src}) · Empty ({icon, title, desc, action})
- Overlays —
Dialog ({title, onClose, footer}) · Drawer ({side, title, footer}) ·
Callout ({variant, title}) · Tip
- Forms —
Field ({label, hint}) · Input · Textarea · Select · Switch ({label}) ·
Checkbox ({label})
- Primitives / type —
Button ({variant, size}) · IconButton · Card · Badge · Tag ·
Kbd · Link · Dot ({variant, pulse}) · Spinner · Skeleton · Alert · Heading
({as}) · Eyebrow · Lead · Prose · Icon (a lucide icon by
name, e.g. <Icon name="rocket" />)
Prefer these over hand-rolled markup so a prototype matches the live theme; drop to a raw
element with className="pl-…" for anything not wrapped here.
-
chart.js — import { Chart } from 'chart.js' (controllers pre-registered) for quick
bar/line/pie/etc. charts onto a <canvas>.
-
d3 — import * as d3 from 'd3' for bespoke/data-driven SVG visualisations.
-
lucide — the raw icon library, if you're not using @pl/ui's Icon.
-
react / react-dom/client — also importable (import { createRoot } from 'react-dom/client'); they resolve to the same React the globals use.
import { createRoot } from 'react-dom/client';
import { Card, Stat, Button, Icon } from '@pl/ui';
import { Chart } from 'chart.js';
function App() {
const ref = React.useRef(null);
React.useEffect(() => {
new Chart(ref.current, { type: 'bar',
data: { labels: ['A','B','C'], datasets: [{ label: 'n', data: [3,7,5] }] } });
}, []);
return (
<Card>
<Stat value="7" label="peak" /> <Icon name="trending-up" />
<canvas ref={ref} width={320} height={160} />
<Button variant="primary">OK</Button>
</Card>
);
}
createRoot(document.getElementById('root')).render(<App />);
You can also style plain elements with the design system's .pl-* classes (e.g.
className="pl-btn pl-btn--primary") and --pl-* CSS tokens in any html/react/markdown
artifact — they're injected so artifacts match the console's live theme. Only these libraries are
available; for anything else, write the code inline (the sandbox has no other network access).
Editing an artifact (don't re-create it)
When the user asks to change something you already rendered, iterate the same artifact — don't
call show_artifact again (that makes a near-duplicate and clutters the panel). Use:
update_artifact(old_string, new_string) — a targeted edit. old_string must appear in the
current source exactly once (copy it verbatim, whitespace included; add surrounding context
to make it unique). This is the fast path — prefer it for small changes. Creates a new version.
rewrite_artifact(code, title?) — replace the whole source. Use for large changes where a
targeted edit would be awkward. Creates a new version; the kind is kept.
Each edit is a version the user can step back through in the panel, so iterate freely — you're
never destroying the previous version. Both default to the most-recent artifact; pass
artifact_id to target another.
Did it render? ALWAYS verify (closing the loop)
A React artifact can throw at render time (a bad import, an undefined component, an export
mismatch) even when the code looks right. Verifying the render is a standard step, not an
optional one — confirm it worked before you tell the user it's done:
- When the panel is open,
show_artifact / update_artifact / rewrite_artifact wait briefly and
append the render verdict to their reply — e.g. "⚠ But it FAILED to render: Icon is not
defined" or "It rendered cleanly." Read it.
- If the reply didn't carry a clean verdict (it came back before the render finished, or said "no
result yet"), call
check_artifact to confirm — it waits briefly for the verdict. Make this
your default after creating or editing an artifact.
- On a failure, fix it with
update_artifact / rewrite_artifact — the artifact still exists,
so iterate on it (don't start over), then verify again. Loop until it renders cleanly.
Treat a render error as the signal to iterate — that's the code→render→fix loop. Don't apologise and
guess; read the error and make the targeted edit.
Managing artifacts
list_artifacts() — see the ids/kinds/titles/version counts (to target an edit or delete).
check_artifact(artifact_id?) — the latest render verdict (see above).
delete_artifact(artifact_id) — remove one for cleanup. (The user can also delete from the
panel's trash button.)
Interactive artifacts (calling back to you)
html and react artifacts can call window.protoArtifact.ask(prompt) — it returns a
Promise resolving to your answer — so an artifact can be a live mini-app (a game NPC, a tutor,
a generator). Use it when the user asks for something that needs intelligence inside the widget:
const line = await window.protoArtifact.ask("Greet the player as a grumpy dwarf, one line.");
It only works if the operator set ARTIFACT_ASK_ENABLED — if it's off, ask() rejects with a
message telling them how to enable it, so write artifacts that degrade gracefully.
When to still write files
Only when the user explicitly wants a project / files ("scaffold a repo", "write the component
to a file", "create a Vite app"). For "show me a counter widget" → show_artifact("react", …).