# Contextual UI Toolkit

A suite of UI primitives rendered inline in the conversation via `render-component`. Use these when structured interaction improves clarity, reduces ambiguity, or prevents errors — not for every exchange.

## Component Catalogue

| Name | Purpose | Data key fields |
|------|---------|-----------------|
| `single-select` | Choose one option from a list | `options[].{value, label, description?}`, `submitMessage?` |
| `multi-select` | Choose multiple options with checkboxes | `options[].{value, label, description?, badge?, defaultSelected?}`, `submitMessage?`, `skipLabel?` |
| `confirm` | Approve or reject a consequential action | `title`, `description?`, `items?[].{label, value}`, `confirmMessage?`, `rejectMessage?` |
| `info-card` | Display structured entity data (read-only) | `title`, `subtitle?`, `fields[].{label, value, type?}`, `actions?[].{label, message, variant}` |
| `action-list` | List of items with per-item actions | `items[].{id, label, description?, status?, actions[]}`, `doneMessage?` |
| `form` | Collect 2+ pieces of structured input | `fields[].{name, label, type, description?, placeholder?, required?, options?[].{value, label, description?}}`, `submitMessage?` |
| `progress` | Multi-step progress tracker (read-only) | `steps[].{label, status, description?}`, `completedMessage?` |
| `browser-viewer` | VNC iframe for browser automation | `title` |
| `document-editor` | Inline markdown editor — prose content | `title`, `content` (markdown), `filePath?`, `readOnly?` |
| `rich-content-editor` | Rich HTML editor — visual/branded content with images | `title`, `content` (HTML fragment), `filePath?`, `readOnly?`, `brandName?`, `brandLogo?` |
| `grid-editor` | Spreadsheet grid — tabular data editing | `title`, `columns[]`, `rows[][]`, `filePath?`, `brandName?`, `brandLogo?` |

Editors that accept `filePath` display an **Approve** button in the header bar. Omitting `filePath` renders the editor without an approval action — the user can view and edit but cannot submit content for writing. Always include `filePath` when the content requires user approval before being saved to disk.

## Editor Selection

Three editor modes serve different content types. Choose based on what the user is working with:

| Content type | Editor | Examples |
|---|---|---|
| Prose — headings, paragraphs, lists | `document-editor` | Knowledge base articles, FAQs, procedures, meeting notes, SOUL files |
| Visual/branded — HTML with images and layout | `rich-content-editor` | Product cards, blog posts, invoices with logos, marketing materials |
| Tabular — columns and rows of values | `grid-editor` | Price lists, contact details, inventory, service catalogues |

**Decision rule:** If the content has images or needs branded visual layout → `rich-content-editor`. If the content is a table of values → `grid-editor`. Everything else → `document-editor`.

All three editors support PDF export. Include `brandName` and `brandLogo` (from the business profile in the graph) when the output may be exported as a branded PDF.

## When to Use UI vs. Text

**Use a component when:**
- The user is choosing from 2+ discrete options → `single-select` or `multi-select`
- You need approval before a consequential or irreversible action → `confirm`
- You're presenting a structured entity (contact, task, invoice, status) → `info-card`
- A list has distinct per-item actions → `action-list`
- You need 2+ pieces of information at once → `form`
- A multi-step process benefits from visible progress → `progress`

**Use an editor when:**
- The user asks to see, review, or edit a document → `document-editor`
- The user needs visual/branded content (product card, invoice, brochure) → `rich-content-editor`
- The user asks about tabular data (prices, contacts, inventory) → `grid-editor`
- "Show me the FAQ" → `document-editor` with the FAQ content
- "Create a product card for the new range" → `rich-content-editor` with generated HTML
- "Show me the price list" → `grid-editor` with columns and rows

**Use plain text when:**
- Simple yes/no — ask in text
- Open-ended input — ask in text
- Informational response with no structure — just respond
- Single piece of information needed — ask in text
- The interaction is fast and unambiguous without UI

Components add value when they reduce ambiguity, prevent errors, or present structure that text cannot. Not every interaction needs a widget.

## How to Use

Call `render-component` with `name` (component name) and `data` (structured object matching the component's schema). Then wait for the user's response before continuing — the response arrives as a regular message.

### Form field types

The `form` component supports these field types:

| Type | Renders as | Value type | Notes |
|------|-----------|------------|-------|
| `text` | Text input | string | Default for short text |
| `textarea` | Multi-line text | string | For longer input |
| `select` | Dropdown | string | Requires `options[]` with `{value, label, description?}` — descriptions are appended to the option label |
| `checkbox` | Toggle checkbox | boolean | Uses the shared Checkbox component. Label appears inline next to the checkbox, not above it |
| `number` | Number input | string | |
| `date` | Date input | string | |

All field types accept optional `description` (help text below the label) and `defaultValue` (pre-populated value — used if the user doesn't interact with the field) properties.

For `checkbox` fields, the submitted JSON value is `true` or `false` (boolean), not a string. For all other types, the value is a string.

### Submit message templates

Most components accept a `submitMessage` field that controls the format of the message sent back:
- `single-select`: `"Selected: {{value}}"` — `{{value}}` and `{{label}}` are replaced
- `multi-select`: `"Selected: {{values}}"` — `{{values}}` is comma-separated selected values
- `form`: `"Form submitted: {{json}}"` — `{{json}}` is a JSON string of `{name: value}` pairs. Per-field placeholders `{{fieldName}}` are also supported (e.g. `"model={{model}}, live={{liveMemory}}"`). `{{values}}` is an alias for `{{json}}`
- `confirm`: `confirmMessage` / `rejectMessage` — literal strings
- `action-list`: each item action has its own `message` field
- `info-card`: each action has its own `message` field

When no template is provided, sensible defaults are used.

### Backwards-compatible aliases

These legacy names still work: `output-style` → `single-select`, `thinking-view` → `single-select`, `plugin-selector` → `multi-select`.

## Anti-patterns

- Rendering a `single-select` for a yes/no question — just ask in text
- Rendering a `form` for a single field — just ask in text
- Chaining multiple components in one response — render one, wait for the response, then decide if another is needed
- Using `info-card` for data the user already knows — only show structured display when it adds value
- Rendering `progress` once and never updating it — progress is for multi-step processes where you re-render with updated step statuses
