AI Artifacts
Tool-result components in the Artifacts category render rich, interactive content directly inside an assistant message — no bundler, no library.
Each component is a vanilla ES module at site/frontend/ai/components/artifacts/<name>.js exporting a single def object:
export const def = {
name, // machine identifier
category, // "Artifacts"
title, // human label
match(payload), // returns true when this component owns the payload
render(payload, ctx), // returns an HTMLElement — pure DOM, no innerHTML on user data
example, // sample payload for preview/docs
};
The runtime calls match on every registered component in order. The first match wins; on no match the payload falls back to a JSON code view.
Chart
File: site/frontend/ai/components/artifacts/chart.js Name: chart
Renders a bar, line, or pie chart as native inline SVG — no charting library. Axis labels, gridlines, and brand accent colours come from CSS custom properties.
Payload
{
type: 'bar' | 'line' | 'pie';
labels: string[]; // one label per data point
series: number[] // single series shorthand
| { label: string; data: number[] }[]; // multi-series
}
Chart types
| type | Renders | |--------|---------| | bar | Grouped vertical bars, one colour per series | | line | Connected polyline with dots, one colour per series | | pie | Filled segments + percentage legend (first series only) |
SVG approach
- ViewBox
360×220(bar/line) or300×200(pie); scales to container width viawidth:100%. - Margins: L 50 px (Y-axis labels), B 40 px (X-axis labels), T 16 px, R 16 px.
- Y axis: 4 even gridlines; labels from
Math.round(max×1.1 × i/4). - Bar width is 72 % of the per-group slot, shared across series.
- Line:
<polyline>+<circle>dots per data point. - Pie:
<path>arc segments usingM cx,cy L x1,y1 A r,r,0,large,1,x2,y2 Z; angles start at 12 o'clock. - Palette:
['#5b8cff','#3ddc84','#f5b945','#8a6bff','#58b2ff','#ff6b6b']— wraps on overflow.
Examples
{ "type": "bar", "labels": ["Jan","Feb","Mar"], "series": [12,19,8] }
{
"type": "line",
"labels": ["Q1","Q2","Q3","Q4"],
"series": [
{ "label": "Revenue", "data": [40,55,72,90] },
{ "label": "Costs", "data": [30,38,44,51] }
]
}
{
"type": "pie",
"labels": ["Direct","Organic","Referral"],
"series": [45, 30, 25]
}
Data Table
File: site/frontend/ai/components/artifacts/data_table.js Name: data_table
Renders tabular data using the framework's .table class. Click any column header to sort ascending; click again to reverse. Numeric columns are right-aligned with tabular figures.
Payload
{
columns: string[]; // header labels
rows: any[][]; // each row is an array aligned to columns
}
Column type is inferred: a column is numeric when every non-empty cell converts to a finite number.
Example
{
"columns": ["Name", "Score", "Rank"],
"rows": [
["Alice", 92, 1],
["Bob", 87, 2],
["Carol", 78, 3]
]
}
Code Block
File: site/frontend/ai/components/artifacts/code_block.js Name: code_block
Renders a code card with a language badge, optional filename, a Copy button, and lightweight syntax highlighting.
Payload
{
language: string; // e.g. "javascript", "rust", "sql"
code: string;
filename?: string; // optional — shown next to the badge
}
Highlighting
HTML-escape runs first; token colouring is then layered on top with <span style="color:…"> wrappers. This guarantees XSS safety: user-supplied code is always escaped before any string manipulation.
Token order (first match wins, no overlaps):
| Token | Colour | |-------|--------| | Strings ("…", '…', … ) | #3ddc84 | | Comments (// … , /* … */, # …) | #5c647a | | Keywords (const, fn, return, …) | #8a6bff | | Numbers (42, 3.14, 1e6) | #f5b945 |
The Copy button writes payload.code to the clipboard via the Clipboard API.
Example
{
"language": "javascript",
"filename": "greet.js",
"code": "const greet = async (name) => {\n return `Hello, ${name}!`;\n};"
}
Code Diff
File: site/frontend/ai/components/artifacts/code_diff.js Name: code_diff
Renders a red/green line-level diff. Accepts either a unified diff string or a structured hunks array.
Payload
Structured hunks:
{
filename?: string;
hunks: {
old: string[]; // removed lines
new: string[]; // added lines
}[];
}
Or a unified diff string (output of git diff / diff -u):
{
filename?: string;
diff: string; // unified diff; --- / +++ headers are skipped
}
Rendering
| Line type | Background | Gutter | Text colour | |-----------|-----------|--------|-------------| | Deleted (-) | red tint | − red | #ffb3b3 | | Added (+) | green tint | + green | #b3f0d0 | | Hunk header (@@) | blue tint | — | #5c647a italic | | Context | transparent | — | #8a93a8 |
All line text is set via textContent — XSS-safe.
Example (hunks)
{
"filename": "src/greet.js",
"hunks": [
{
"old": ["function greet(name) {", " return 'Hello ' + name;", "}"],
"new": ["const greet = (name) => {", " return `Hello, ${name}!`;", "};"]
}
]
}
Example (unified diff)
{
"filename": "src/greet.js",
"diff": "@@ -1,3 +1,3 @@\n-function greet(name) {\n- return 'Hello ' + name;\n-}\n+const greet = (name) => {\n+ return `Hello, ${name}!`;\n+};"
}