📚 Reference
Generated, not hand-copied.
Everything below is read from the real source at request time — the AI vocabulary contract and GRAIN's token slots. Change the source, this page changes with it.
Actions
Generated from grain/ai/contract.ts's ACTIONS registry — the single source of truth. Payload fields marked * are required; hints are MCP-style behaviour flags.
| verb | payload | accepts (surface kinds) | depth | hints | what it does |
|---|---|---|---|---|---|
item.archive | no args | item | light | idempotent | Archive an item (stands in for task.complete on the optimistic light path). |
say.set | text*:string | reflection | light | idempotent | Write a noted line back into a reflection surface. |
say.stream | no args | say-stream | light | idempotent | Stream a reflection line out token by token. |
demo.run | no args | screen | heavy | idempotent | Play a scripted AI-acting demo on the current screen. |
desk.stop | no args | screen | light | readOnly, idempotent | Ask the AI to halt the current run (mediated — never a force-kill). |
chat.send | text*:string | chat-log | light | — | Send a chat message; the AI's reply streams back over SSE. |
note.append | text*:string (markdown) | notepad | light | — | Append one markdown entry to the notepad. |
note.replace | text*:string (markdown) | notepad | light | destructive, idempotent | Rewrite the whole notepad from one markdown body. |
navigate | href*:string (root-relative path, e.g. /notes) | screen | light | readOnly, idempotent | Change screens — same-origin, root-relative href only (validated at the door). |
field.set | value*:string (plain text; replaces the field's current value) | field | light | destructive, idempotent | Prefill a registered form field with drafted text — the human reviews and submits; the AI never submits. |
Surface kinds
Every kind at least one verb above accepts.
| kind |
|---|
chat-log |
field |
item |
notepad |
reflection |
say-stream |
screen |
Render ops
The effect kinds the client dispatcher applies (see AI-INTERFACE.md).
| op | means |
|---|---|
replace | swap the target's HTML — the confirmed/committed fragment |
append | add HTML to the end of the target (a chat log, a plan list, the console) |
remove | delete the target element |
flash | a transient note; also rolls back an optimistic pending state |
type | stream one text token into the target, or (done) settle it |
spotlight | show the AI as actor — dim everything, light (and optionally pulse) the target |
log | append one provenance-tagged entry to the interaction timeline (the unified human+AI log) |
navigate | change the browser's location (href) — same-origin, root-relative only |
choices | the AI asks the human to pick — a chat bubble with a row of choice buttons (each a chat.send) |
fill | prefill a registered form field's value — persists for human review (the AI never submits) |
The one door
The HTTP surface every human/AI interaction enters through.
| endpoint | means |
|---|---|
POST /intent | the one door — every human/AI interaction enters here |
GET /stream | the per-session SSE channel render ops are pushed back over |
GET /ai/manifest | the machine-readable index of what's operable right now |
Token slots
Every custom property GRAIN's default theme sets on :root — override these to re-skin (see RE-SKIN-VIA-TOKENS.md), never a component's own CSS.
| token | default value |
|---|---|
--paper | #E2E0D8 |
--paper-2 | #E8E6DF |
--panel | #E8E6DF |
--ink | #1C1B17 |
--ink-muted | #6E6C64 |
--ink-faint | #ABA89F |
--hairline | #1C1B17 |
--line-soft | rgba(28, 27, 23, .14) |
--font-smooth | "Redaction", "Times New Roman", Georgia, serif |
--font-grain | "Redaction 50", "Redaction 35", "Times New Roman", Georgia, serif |
--font-accent | "Redaction 70", "Times New Roman", Georgia, serif |
--font-grain-fine | "Redaction 35", "Times New Roman", Georgia, serif |
--font-mono | ui-monospace, "SF Mono", "SFMono-Regular", Menlo, Consolas, "Liberation Mono", monospace |
--font-weight-regular | 400 |
--font-weight-medium | 500 |
--font-weight-semibold | 600 |
--font-weight-bold | 700 |
--text-xs | 0.8125rem |
--text-sm | 0.875rem |
--text-base | 0.9375rem |
--text-lg | 1.05rem |
--text-xl | 1.3rem |
--text-2xl | 1.6rem |
--text-3xl | 2.25rem |
--display | clamp(2rem, 6vw, 2.75rem) |
--leading-tight | 1.02 |
--leading-normal | 1.5 |
--leading-relaxed | 1.55 |
--space-1 | 0.25rem |
--space-2 | 0.5rem |
--space-3 | 0.75rem |
--space-4 | 1rem |
--space-5 | 1.25rem |
--space-6 | 1.5rem |
--space-8 | 2rem |
--radius-sm | 4px |
--radius-md | 4px |
--radius-pill | 2px |
--rule | 1.5px solid var(--hairline) |
--border | 1px solid var(--hairline) |
--content-max | 768px |
--page-pad | clamp(1.25rem, 5vw, 2.5rem) |
--color-bg | var(--paper) |
--color-surface | var(--panel) |
--color-fg | var(--ink) |
--color-muted | var(--ink-muted) |
--color-line | var(--line-soft) |
--color-accent | var(--ink) |
--color-accent-hover | var(--ink) |
--color-accent-contrast | var(--paper) |
--color-accent-soft | var(--paper-2) |
--color-primary | var(--color-accent) |
--color-primary-hover | var(--color-accent-hover) |
--color-primary-contrast | var(--color-accent-contrast) |
--color-primary-soft | var(--color-accent-soft) |
--color-success | var(--ink) |
--color-success-hover | var(--ink) |
--color-success-contrast | var(--paper) |
--color-success-soft | var(--paper-2) |
--color-danger | var(--ink) |
--color-danger-hover | var(--ink) |
--color-danger-contrast | var(--paper) |
--color-danger-soft | var(--paper-2) |
--color-ok-bg | transparent |
--color-ok-fg | var(--ink) |
--color-archived-bg | transparent |
--color-archived-fg | var(--ink-muted) |
--type-font | var(--font-smooth) |
--font-base | var(--text-base)/var(--leading-relaxed) var(--type-font) |
--ai-veil-alpha | 22% |
--ai-veil | color-mix(in srgb, var(--ink) var(--ai-veil-alpha), transparent) |
--ai-veil | color-mix(in srgb, var(--paper) 70%, transparent) |
--ai-veil | transparent |
--ai-focus-move | 0.65s |
--ai-focus-ease | ease-in-out |
--z-ai-veil | 9000 |
--z-ai-spot | 9001 |
--z-ai-surface | 9002 |
--paper-grain-opacity | 0.09 |