📚 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.
Some tables extend beyond the page width. Swipe to see the remaining columns, or focus a table and use the arrow keys.
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. |
check.set | checked*:boolean (true ticks the box, false clears it) | check | light | destructive, idempotent | Tick or clear a registered tick box (checkbox or radio) — the human reviews and submits; the AI never submits. |
block.remove | no args | block | light | destructive, idempotent | Drop one block from the composed page. The page keeps every other block's id, so a later verb still names what it means. |
block.span | span*:string (one of: full, half, third) | block | light | idempotent | Set how wide one block sits: full, half or third. The whole layout vocabulary, and there is no fourth word. |
block.move | direction*:string (one of: up, down) | block | light | — | Move one block one place earlier or later in the page. Clamped at the ends rather than wrapped. |
Every kind at least one verb above accepts.
| kind |
|---|
block |
chat-log |
check |
field |
item |
notepad |
reflection |
say-stream |
screen |
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) |
tick | set a registered tick box's checked state — persists for human review (a radio may only be ticked, never cleared) |
span | set one block's width word on a composed page — full, half or third, and there is no fourth |
move | shift one block one place on a composed page — up or down, clamped at the ends rather than wrapped |
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 |
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 | #65635B |
--ink-faint | #ABA89F |
--hairline | #1C1B17 |
--line-soft | rgba(28, 27, 23, .14) |
--scrim | rgba(20, 19, 15, .72) |
--on-scrim | #F4F2EA |
--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 |
--editor-header-h | 2.9rem |
--z-presentation | 8000 |
--z-ai-veil | 9000 |
--z-ai-spot | 9001 |
--z-ai-surface | 9002 |
--paper-grain-opacity | 0.09 |