Menu
Public repository / fee5b6c2cde89fa6bf8dc51068af7336b56a88b8
AkurAI-Framework / site/content/docs/ai-input.md
Latest change 63ae119f1bdd5ddffd3f9a2b3b3216aa9d21449b - feat(ai): Input component batch — option_list, parameter_slider, preferences_panel, question_flow by Ólafur Búi Ólafsson
# AI Input components
Four interactive **tool-UI input components** in the `Input` category. Each is a
vanilla ES module at `site/frontend/ai/components/input/<name>.js`. The runtime
matches a tool-call payload against `def.match`, renders `def.render(payload, ctx)`,
and the user's interaction flows back to the assistant as a receipt via
`ctx.sendReceipt(receipt)`. A `CustomEvent('toolui:input', { detail: receipt })`
is also emitted on the root element as a fallback for listeners that don't use `ctx`.
All components are:
- **Pure vanilla DOM** — no imports, no framework, no build step.
- **XSS-safe** — user-supplied strings are always written via `.textContent`.
- **Reusable** — they use only existing `styles.css` design-system classes.
---
## Option List
**File:** `site/frontend/ai/components/input/option_list.js`
**`def.name`:** `option_list`
Presents a list of labelled options for the user to pick from. Supports both
single-select (radio) and multi-select (checkbox) modes. A **Confirm** button sends
the receipt.
### Payload
```json
{
"prompt": "Which deployment target?",
"options": [
{ "id": "bare", "label": "Bare metal", "description": "Full control, no overhead" },
{ "id": "docker", "label": "Docker", "description": "Containerised, portable" },
{ "id": "k8s", "label": "Kubernetes", "description": "Orchestrated at scale" }
],
"multi": false
}
```
| Field | Type | Required | Notes |
|---|---|---|---|
| `prompt` | `string` | no | Displayed as a heading above the list. |
| `options` | `{id, label, description?}[]` | yes | At least one option required (`match` gate). |
| `multi` | `boolean` | no | `false` → radio (single); `true` → checkboxes. Default `false`. |
### Receipt
```json
{ "component": "option_list", "selected": "docker" }
```
| Field | Value when `multi: false` | Value when `multi: true` |
|---|---|---|
| `selected` | `string \| null` — the chosen `id`, or `null` if none | `string[]` — all chosen `id`s (may be empty) |
---
## Parameter Slider
**File:** `site/frontend/ai/components/input/parameter_slider.js`
**`def.name`:** `parameter_slider`
A labelled range slider with a live badge showing the current value. An **Apply**
button sends the receipt.
### Payload
```json
{
"label": "Temperature",
"min": 0,
"max": 2,
"step": 0.1,
"value": 0.7,
"unit": ""
}
```
| Field | Type | Required | Notes |
|---|---|---|---|
| `label` | `string` | no | Displayed to the left of the value badge. |
| `min` | `number` | yes | Slider minimum. |
| `max` | `number` | yes | Slider maximum. |
| `step` | `number` | no | Slider step size. Default `1`. |
| `value` | `number` | yes | Initial value (must be between `min` and `max`). |
| `unit` | `string` | no | Appended to the displayed value, e.g. `"ms"`, `"°C"`. Default `""`. |
### Receipt
```json
{ "component": "parameter_slider", "value": 1.2 }
```
| Field | Type | Notes |
|---|---|---|
| `value` | `number` | The slider's value at the moment the user clicked Apply. |
---
## Preferences Panel
**File:** `site/frontend/ai/components/input/preferences_panel.js`
**`def.name`:** `preferences_panel`
A settings form grouped by section. Each field can be a **toggle** (switch),
**select** (dropdown), or **text** (free-text input). A **Save preferences** button
sends all field values at once.
### Payload
```json
{
"groups": [
{
"label": "Appearance",
"fields": [
{ "key": "dark_mode", "type": "toggle", "label": "Dark mode", "value": true },
{ "key": "compact", "type": "toggle", "label": "Compact layout", "value": false }
]
},
{
"label": "Locale",
"fields": [
{
"key": "language", "type": "select", "label": "Language", "value": "en",
"options": [{ "id": "en", "label": "English" }, { "id": "is", "label": "Íslenska" }]
},
{ "key": "display_name", "type": "text", "label": "Display name", "value": "" }
]
}
]
}
```
| Field | Type | Required | Notes |
|---|---|---|---|
| `groups` | `Group[]` | yes | At least one group required (`match` gate). |
| `groups[].label` | `string` | no | Section heading. |
| `groups[].fields` | `Field[]` | yes | One or more fields per group. |
| `field.key` | `string` | yes | Receipt key; must be unique across all groups. |
| `field.type` | `'toggle' \| 'select' \| 'text'` | yes | Controls rendered widget. |
| `field.label` | `string` | no | Human-readable label. Falls back to `key`. |
| `field.value` | any | no | Initial value. Toggles default `false`; others default `""`. |
| `field.options` | `{id,label}[] \| string[]` | select only | Dropdown choices. |
| `field.placeholder` | `string` | no | Hint text for `type: 'text'` inputs. |
### Receipt
```json
{
"component": "preferences_panel",
"values": {
"dark_mode": true,
"compact": false,
"language": "is",
"display_name": "Ólafur"
}
}
```
| Field | Type | Notes |
|---|---|---|
| `values` | `{ [key]: boolean \| string }` | Flat map of all field keys to their current values at save time. Toggles are `boolean`; select and text are `string`. |
---
## Question Flow
**File:** `site/frontend/ai/components/input/question_flow.js`
**`def.name`:** `question_flow`
A step-by-step wizard. Renders one question at a time with **Back / Next** navigation
and a progress bar. The user steps through all questions; pressing **Finish** on the
last step sends the receipt.
### Payload
```json
{
"questions": [
{
"id": "role",
"prompt": "What is your primary role?",
"type": "single",
"options": [
{ "id": "dev", "label": "Developer" },
{ "id": "design", "label": "Designer" },
{ "id": "pm", "label": "Product manager" }
]
},
{
"id": "tools",
"prompt": "Which tools do you use regularly?",
"type": "multi",
"options": [
{ "id": "vscode", "label": "VS Code" },
{ "id": "figma", "label": "Figma" },
{ "id": "linear", "label": "Linear" }
]
},
{
"id": "goal",
"prompt": "What are you trying to build?",
"type": "text"
}
]
}
```
| Field | Type | Required | Notes |
|---|---|---|---|
| `questions` | `Question[]` | yes | At least one question required (`match` gate). |
| `question.id` | `string` | yes | Used as the key in the receipt `answers` object. Must be unique. |
| `question.prompt` | `string` | yes | The question text displayed to the user. |
| `question.type` | `'single' \| 'multi' \| 'text'` | yes | `single` → radio buttons; `multi` → checkboxes; `text` → text input. |
| `question.options` | `{id,label}[] \| string[]` | single/multi only | The choices to display. |
Answers are preserved when navigating back, so the user can revise without starting
over.
### Receipt
```json
{
"component": "question_flow",
"answers": {
"role": "dev",
"tools": ["vscode", "figma"],
"goal": "A self-hosted AI assistant for my team."
}
}
```
| Field | Type | Notes |
|---|---|---|
| `answers` | `{ [id]: value }` | Keyed by `question.id`. `single` and `text` answers are `string \| null`; `multi` answers are `string[]`. |