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

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+};"
}