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.