3.5 KiB
3.5 KiB
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/templatesare mirrors for development/reference. - New cards must be added as
manifest.json + template.html + card.js. template.htmlis markup and styles only. Do not put executable<script>tags in it.
Module Contract
Each template must export:
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_stateobject 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 ortemplate_statechanges.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:
<div style="background:var(--theme-card-neutral-bg); color:var(--theme-card-neutral-text); border:1px solid var(--theme-card-neutral-border)">
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:
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, andcard.jsexisttemplate.htmlhas no inline scriptcard.jsparses cleanly- no legacy runtime globals remain
- the lifecycle fixture contract can mount, update, and destroy cleanly