feat: unify card runtime and event-driven web ui
This commit is contained in:
parent
0edf8c3fef
commit
4dfb7ca3cc
105 changed files with 17382 additions and 8505 deletions
126
CARD_RUNTIME.md
Normal file
126
CARD_RUNTIME.md
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
# 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue