AkurAI Build
Menu

Public repository / 439f0dcabf5cdf2b120c7724be895cf1df30507f

AkurAI-Framework / site/content/docs/ai-artifacts.md

Latest change 8847e6f4a315fb27eba8cb69b87397dd08a068eb - feat(ai/artifacts): add Chart, DataTable, CodeBlock, CodeDiff ES modules by Ólafur Búi Ólafsson

# 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:

```js
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

```ts
{
  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) or `300×200` (pie); scales to container width via `width: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 using `M 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

```json
{ "type": "bar", "labels": ["Jan","Feb","Mar"], "series": [12,19,8] }
```

```json
{
  "type": "line",
  "labels": ["Q1","Q2","Q3","Q4"],
  "series": [
    { "label": "Revenue", "data": [40,55,72,90] },
    { "label": "Costs",   "data": [30,38,44,51] }
  ]
}
```

```json
{
  "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

```ts
{
  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

```json
{
  "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

```ts
{
  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

```json
{
  "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:

```ts
{
  filename?: string;
  hunks: {
    old: string[];   // removed lines
    new: string[];   // added lines
  }[];
}
```

Or a unified diff string (output of `git diff` / `diff -u`):

```ts
{
  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)

```json
{
  "filename": "src/greet.js",
  "hunks": [
    {
      "old": ["function greet(name) {", "  return 'Hello ' + name;", "}"],
      "new": ["const greet = (name) => {", "  return `Hello, ${name}!`;", "};"]
    }
  ]
}
```

### Example (unified diff)

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