AkurAI Build
Menu

Public repository / master

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

Latest change d49e9bb8beb5dc669fe029e034a13342d358a4f3 - feat(ai): tool-ui Progress + Confirmation components (AB4-B) by Ólafur Búi Ólafsson

# AI Progress & Confirmation components

Part of the **tool-ui** component set (Batch AB4-B). Four vanilla ES modules in
`site/frontend/ai/components/progress/` that render tool-call payloads as rich UI
inside an AI thread. Each exports a `def` object the tool-call runtime uses to match
and render payloads.

> **Component contract.** Every file exports `const def = { name, category, title,
> match(payload), render(payload, ctx), example }`. `match` returns `true` when the
> payload belongs to this component. `render` returns a DOM `Element`. `ctx` is
> feature-detected (may be `undefined` when previewing outside a runtime).

---

## Progress components

Progress components are read-only — they display state but do not send receipts.

### Plan

**File:** `site/frontend/ai/components/progress/plan.js`
**Name:** `plan` | **Category:** Progress

Renders a labelled checklist with status icons for each step.

**Payload**

```json
{
  "title": "string (optional)",
  "steps": [
    { "label": "string", "status": "pending | active | done | failed" }
  ]
}
```

| Field | Type | Required | Notes |
|---|---|---|---|
| `title` | string | no | Card heading |
| `steps` | array | yes | At least one element |
| `steps[].label` | string | yes | Step description |
| `steps[].status` | `"pending"` \| `"active"` \| `"done"` \| `"failed"` | yes | Drives icon + colour |

**Status icons:** `✓` done · `▶` active · `○` pending · `✗` failed

**Receipt:** none (read-only)

**Example**

```json
{
  "title": "Deploy pipeline",
  "steps": [
    { "label": "Build binary",    "status": "done"    },
    { "label": "Run tests",       "status": "done"    },
    { "label": "Upload artifact", "status": "active"  },
    { "label": "Restart service", "status": "pending" },
    { "label": "Smoke check",     "status": "pending" }
  ]
}
```

---

### Progress Tracker

**File:** `site/frontend/ai/components/progress/progress_tracker.js`
**Name:** `progress_tracker` | **Category:** Progress

Renders a labelled progress bar. Accepts a fractional value (`value / total`) or a
direct percentage. Optionally renders step labels below the bar.

**Payload — fractional form**

```json
{ "label": "string (optional)", "value": 3, "total": 10 }
```

**Payload — percent form**

```json
{ "label": "string (optional)", "percent": 30 }
```

**Payload — with step labels**

```json
{
  "label": "Processing files",
  "value": 3,
  "total": 10,
  "steps": ["Parse", "Validate", "Transform", "Upload"]
}
```

| Field | Type | Required | Notes |
|---|---|---|---|
| `label` | string | no | Displayed above the bar |
| `value` | number | one of | Used with `total` for fraction |
| `total` | number | no | Denominator; shows `value / total` counter |
| `percent` | number | one of | 0–100; takes precedence when present |
| `steps` | string[] | no | Labels shown beneath bar as a stepper |

When neither `value` nor `total` can yield a percent, the bar renders as
indeterminate (animated).

**Receipt:** none (read-only)

**Example**

```json
{
  "label": "Processing files",
  "value": 3,
  "total": 10,
  "steps": ["Parse", "Validate", "Transform", "Upload"]
}
```

---

## Confirmation components

Confirmation components are interactive. On the user's decision they call
`ctx.sendReceipt(obj)` (feature-detected) and dispatch a
`CustomEvent('toolui:confirm', { detail, bubbles: true })` on the root element.
After a decision the buttons are disabled and a badge shows the outcome.

---

### Approval Card

**File:** `site/frontend/ai/components/progress/approval_card.js`
**Name:** `approval_card` | **Category:** Confirmation

Renders a titled card with Approve / Reject buttons. Set `danger: true` for
destructive actions — the card border turns red and the Approve button renders in
danger styling.

**Payload**

```json
{
  "title": "Deploy to production?",
  "summary": "This will restart the server and apply the pending migration.",
  "details": "Estimated downtime: ~30 seconds. 3 migrations will run.",
  "danger": true
}
```

| Field | Type | Required | Notes |
|---|---|---|---|
| `title` | string | yes | Card heading |
| `summary` | string | yes | Short description of the action |
| `details` | string | no | Secondary detail text; shown below a divider |
| `danger` | boolean | no | Enables danger styling; defaults to `false` |

**Receipt**

```json
{ "approved": true }
```

or

```json
{ "approved": false }
```

Sent via `ctx.sendReceipt` and `CustomEvent('toolui:confirm')`.

**Example**

```json
{
  "title": "Deploy to production?",
  "summary": "This will restart the server and apply the pending migration.",
  "details": "Estimated downtime: ~30 seconds. 3 migrations will run.",
  "danger": true
}
```

---

### Order Summary

**File:** `site/frontend/ai/components/progress/order_summary.js`
**Name:** `order_summary` | **Category:** Confirmation

Renders an itemized order table with subtotal, tax, and total rows, followed by a
Confirm button. Amounts are formatted with `Intl.NumberFormat` for the page locale.

**Payload**

```json
{
  "items": [
    { "name": "Widget Pro",  "qty": 2, "price": 9.99 },
    { "name": "Gadget Plus", "qty": 1, "price": 24.50 }
  ],
  "subtotal": 44.48,
  "tax": 4.00,
  "total": 48.48,
  "currency": "USD"
}
```

| Field | Type | Required | Notes |
|---|---|---|---|
| `items` | array | yes | At least one item |
| `items[].name` | string | yes | Item name |
| `items[].qty` | number | yes | Quantity |
| `items[].price` | number | yes | Unit price |
| `subtotal` | number | no | Shown above tax row when present |
| `tax` | number | no | Shown above total when present |
| `total` | number | yes | Bold total row |
| `currency` | string | no | ISO 4217 code; defaults to `"USD"` |

**Receipt**

```json
{ "confirmed": true }
```

Sent via `ctx.sendReceipt` and `CustomEvent('toolui:confirm')`.

**Example**

```json
{
  "items": [
    { "name": "Widget Pro",  "qty": 2, "price": 9.99 },
    { "name": "Gadget Plus", "qty": 1, "price": 24.50 }
  ],
  "subtotal": 44.48,
  "tax": 4.00,
  "total": 48.48,
  "currency": "USD"
}
```

---

## Registering components

Import the `def` object and register it with the tool-call runtime:

```js
import { def as plan }            from '/ai/components/progress/plan.js';
import { def as progressTracker } from '/ai/components/progress/progress_tracker.js';
import { def as approvalCard }    from '/ai/components/progress/approval_card.js';
import { def as orderSummary }    from '/ai/components/progress/order_summary.js';

runtime.register(plan, progressTracker, approvalCard, orderSummary);
```

The runtime calls `def.match(payload)` in registration order; the first match wins.
On no match the runtime falls back to a JSON code view.