# Card Runtime

The app shell is responsible for layout, navigation, sessions, feed ordering, and workbench placement.
Cards are responsible for their own UI and behavior through a small dynamic runtime contract.

## Source Of Truth

- Live card templates live under `~/.nanobot/cards/templates`.
- Repo examples under `examples/cards/templates` are mirrors for development/reference.
- New cards must be added as `manifest.json + template.html + card.js`.
- `template.html` is markup and styles only. Do not put executable `<script>` tags in it.

## Module Contract

Each template must export:

```js
export function mount({ root, state, host }) {
  // render and wire up DOM
  return {
    update?.({ root, item, state, host }) {},
    destroy?.() {},
  };
}
```

`root`
- The card root element already contains the rendered `template.html`.

`state`
- The current `template_state` object for this card/workbench item.

`host`
- The runtime host API described below.

## Lifecycle

- `mount()` runs once when the card is attached.
- `update()` runs when the card item or `template_state` changes.
- `destroy()` must clean up timers, listeners, observers, and in-flight UI handlers.

Cards must not rely on:
- `document.currentScript`
- global `window.__nanobot*` helpers
- inline script re-execution

## Host API

State
- `host.getState()`
- `host.replaceState(nextState)`
- `host.patchState(patch)`

Card context
- `host.setLiveContent(snapshot)`
- `host.getLiveContent()`
- `host.setSelection(selection)`
- `host.getSelection()`
- `host.clearSelection()`

Refresh
- `host.setRefreshHandler(handler)`
- `host.runRefresh()`
- `host.requestFeedRefresh()`

Tools
- `host.callTool(name, args?)`
- `host.startToolCall(name, args?)`
- `host.getToolJob(jobId)`
- `host.callToolAsync(name, args?, options?)`
- `host.listTools()`

Utilities
- `host.renderMarkdown(markdown, { inline? })`
- `host.copyText(text)`
- `host.getThemeName()`
- `host.getThemeValue("--theme-card-neutral-text")`

## Theme Tokens

Cards should inherit theme from shared CSS variables instead of hardcoding app-level colors.

Use these families first:
- `--theme-card-neutral-*` for standard data cards
- `--theme-card-warm-*` for timeline/planning cards
- `--theme-card-success-*` for inbox/positive cards
- `--card-*` for feed shell cards
- `--helper-card-*` for helper cards
- `--theme-text*`, `--theme-border*`, `--theme-accent*`, `--theme-status-*` for generic UI

Examples:

```html
<div style="background:var(--theme-card-neutral-bg); color:var(--theme-card-neutral-text); border:1px solid var(--theme-card-neutral-border)">
```

```js
statusEl.style.color = "var(--theme-status-live)";
const accent = host.getThemeValue("--theme-accent");
```

## Rules

- Persist all card-local durable state through `replaceState()` / `patchState()`.
- Use `setLiveContent()` for transient model-readable card context.
- Register at most one refresh handler with `setRefreshHandler()`.
- Always clear timers/intervals in `destroy()`.
- Prefer standard DOM APIs and small helper functions over framework-specific assumptions.
- Prefer shared theme tokens over hardcoded shell/surface colors.

## Validation

Run:

```bash
python3 scripts/check_card_runtime.py
node scripts/check_card_runtime_fixture.mjs
python3 scripts/sync_card_templates.py --check
```

The runtime check enforces:
- `manifest.json`, `template.html`, and `card.js` exist
- `template.html` has no inline script
- `card.js` parses cleanly
- no legacy runtime globals remain
- the lifecycle fixture contract can mount, update, and destroy cleanly
