# plastron — guide for LLMs & agents This is the one canonical guide. It is served verbatim as plain text at **https://plastron.ca/llms.txt**, mirrored in the repo `README.md`, and shown (condensed) in the app's own readme. Paste it into a system prompt to author plastron formulas without access to the source. plastron is a **reactive spreadsheet made of cels**, shipped as one self-contained `index.html`. A cell doesn't just compute — its formula can render DOM, spawn grids, call an LLM, query SQLite, or build a whole app. **A formula IS the app, and a formula fits in a URL**, so a whole running plastron is shareable as a link. ──────────────────────────────────────────────────────────────────────────── FIRST — RESEARCH, DON'T GUESS (plastron describes itself) ──────────────────────────────────────────────────────────────────────────── This guide lists the high-frequency verbs, but it is NOT the full set. Plastron ships MANY more (charts, graphs, music/MIDI, peer/net, files, …). If you need a capability that isn't spelled out below, DISCOVER it with a formula instead of guessing a verb that may not exist: =vocab() every verb + its one-line description, grouped by segment =vocab("charts") just one segment's verbs =inspect("name") one verb's full doc — signature, about, source =members("seg") the cels in a segment =segments() every loaded segment Put one of these in a cell, read the result, THEN write the real formula. A wrong guess surfaces as `#NAME?`; `=inspect("")` tells you the true signature. (Full plain-text guide + complete catalog: https://plastron.ca/llms.txt) ──────────────────────────────────────────────────────────────────────────── THE TWO FORMULA LANGUAGES (pick one per cell; the leading char selects it) ──────────────────────────────────────────────────────────────────────────── - **infix** — Excel/function-call style, starts with `=`: `=1+1` `=cels(8,5,"todo")` `=dom("h1","hello")` `=g!A1*2` `=SUM(A1:A10)` - **S-expression** — Lisp prefix, starts with `(`: `(+ 1 1)` `(cels 8 5 "todo")` `(dom "h1" "hello")` They are equivalent in power. Infix is the Excel-compatible surface; S-expr is the homoiconic one. Don't mix them inside one formula. Cell references are `A1`, ranges are `A1:B3` or `seg!A1:B3`. Cel keys in formulas are ASCII `[\w.-]+`. FORMAT BIG FORMULAS MULTI-LINE: newlines and tabs are IGNORED by the parser, so indent a large nested formula and put one argument per line — a single long line is the #1 cause of `expected ")"` (a miscounted paren). Indentation makes every `(` line up with its `)`. STRINGS: use `"…"` or `'…'`. A second delimiter means a nested formula needs **no escaping**, one level deep: `at("A1", '=dom("h1", "Tasks")')` ✅ (not `at("A1","=dom(\"h1\",\"Tasks\")")`) A cell whose content is itself a `=formula` MUST be quoted with the alternate delimiter — `at("B2", '=SCAN(0,A2:A6,LAMBDA(a,v,a+v))')` ✅; a BARE `at("B2", =SCAN(…))` is a parse error ("unexpected token ="). Only TWO string levels exist (`"` outside, `'` inside) — do NOT go a third level deep and NEVER use backticks. If you need depth (e.g. a chart inside a menu-spawned sheet), build that sheet as its OWN top-level `=cels(…)` and reference it, rather than inlining a third nested formula. ──────────────────────────────────────────────────────────────────────────── CORE FORMULAS (the high-frequency ones — full catalog at the bottom) ──────────────────────────────────────────────────────────────────────────── GRIDS / SHEETS =cels(rows, cols) one worksheet of editable cels =cels(rows, cols, "name") a named worksheet =cels("in",4,3, "out",4,3) a workbook of named sheets =cels(8,5,"todo", at("A1","Task"), at("B1","Status")) seed cell contents at(addr, content) one cell's initial content (value or =formula) VIEWS (DOM + canvas) =dom("h1", "hello") an element rendered in the cell =dom("div", style("color","red"), "hi") style() = key/value attribute pairs =dom("button", on("click","origin.tone", 440), "♪") on(event, verb, arg) =canvas(420,260, barchart(t!A2:A8, t!B2:B8)) a drawn chart DATA + CHART TOGETHER (the right way) `cels()` is a GENESIS — it builds a sheet. It is **top-level**, NOT a child of `dom()`. Never write `dom("div", cels(...), ...)` — the grid won't materialize (it stringifies to "[object Object]") and any chart that reads it shows "no data". Instead, put the chart in a cell OF the same sheet so its range read is local: =cels("sales", 5, 2, at("A1","Item"), at("B1","Value"), at("A2","Apples"),at("B2",10), at("A3","Pears"), at("B3",20), at("A4","Plums"), at("B4",15), at("A5", '=canvas(360,200, barchart(sales!A2:A4, sales!B2:B4))')) (Note the `'…'` delimiter on the nested chart formula — no escaping needed.) LOGIC / DATA =1+1 =A1*2 =SUM(A1:A10) =IF(A1>0,"yes","no") =LET(x, A1*2, x + x*x) name a sub-expression, reuse it (readability) =SUM(MAP(A1:A9, LAMBDA(v, v*v))) LAMBDA + MAP/REDUCE/SCAN/BYROW over a range NO ARRAY SPILL INTO CELLS: MAP/FILTER/SCAN/BYROW return ONE array VALUE. Put it in a single cell and it shows as a JSON dump ([100,250,…]) — it does NOT fill the cells below/beside it. (EXCEPTION — a DOM container flattens an array child, so MAP→dom IS how you render a list; see RENDER A LIST. The rule below is only about a raw array sitting in a sheet cell.) So: • collapse it to one value with an aggregate: =SUM(MAP(A2:A9, LAMBDA(v, v*v))) ✅ • to DISPLAY a per-row column (running totals, row totals) write an EXPLICIT formula in EACH target cell with its own range — there is no fill-down and no `$`: running total: at("B2",'=SUM(A2:A2)'), at("B3",'=SUM(A2:A3)'), at("B4",'=SUM(A2:A4)') row totals: at("D2",'=SUM(A2:C2)'), at("D3",'=SUM(A3:C3)'), at("D4",'=SUM(A4:C4)') • if a task REQUIRES SCAN/BYROW for that column, each cell must STILL resolve to ONE number — extract it with INDEX(array, position); never leave the raw array in a cell: at("C2",'=INDEX(SCAN(0,B2:B2,LAMBDA(a,v,a+v)),1)'), at("C3",'=INDEX(SCAN(0,B2:B3,LAMBDA(a,v,a+v)),2)') … at("D2",'=INDEX(BYROW(A2:C4,LAMBDA(r,SUM(r))),1)'), at("D3",'=INDEX(BYROW(A2:C4,LAMBDA(r,SUM(r))),2)') … A lone =SCAN(…)/=BYROW(…) sitting in one cell is the #1 way these tasks render wrong — don't. SEED YOUR INPUTS + LABEL THE OUTPUT: a formula over empty cells shows 0/blank, and a bare scalar answer is nothing to look at. If a task says "price in A1, qty in A2", build a LABELED sheet that seeds the inputs so the result is visible: =cels("inv",4,2, at("A1","Price"), at("B1",12), at("A2","Qty"), at("B2",3), at("A3","Subtotal"), at("B3",'=B1*B2'), at("A4","Grand total"), at("B4",'=LET(s, B1*B2, s + s*0.13)')) RENDER A LIST (a collection → repeated DOM — this is how data becomes UI) dom() FLATTENS an array child, so turn a range into repeated elements by MAPping it: =dom("ul", MAP(todo!A1:A5, LAMBDA(t, dom("li", t)))) one
  • per row FILTER(range, LAMBDA(x, pred)) keeps only matching values — render a SUBSET: =dom("ul", MAP(FILTER(nums!A1:A9, LAMBDA(x, x>0)), LAMBDA(x, dom("li", x)))) null/""/false items in the array are dropped (so a MAP that yields "" omits that child). To carry per-row IDENTITY (so a click/drag knows WHICH row it is), MAP over an ID column (seed col A = 1..N) and use the id to BUILD the cel key + READ the fields with INDEX: =dom("div", MAP(todo!A1:A5, LAMBDA(id, dom("div.row", on("click", "some.handler", CONCAT("todo.B", id)), ← payload NAMES this row's cel INDEX(todo!B1:B5, id))))) ← INDEX reads this row's field MAP is for iterating a DATA RANGE. For a FIXED, small set of sections (e.g. a few columns or tabs), there is NO inline list literal — do NOT invent ARRAY(…)/[…]. Instead define the section once as a LAMBDA (via LET) and CALL it per section: =LET(section, LAMBDA(name, dom("div.col", dom("h3", name), …use name…)), dom("div", section("Left"), section("Middle"), section("Right"))) A LET-bound LAMBDA can call EARLIER LET names and see the calling LAMBDA's params, so a section can reuse a shared "card"/"row" LAMBDA and the section's own name together. DRAG & DROP — "a drop writes a cel" (reusable: boards, buckets, file moves, assignment) Make an item DRAGGABLE and have it NAME a cel; make a drop ZONE carry a VALUE: item: =dom("div", attr("draggable","true"), on("dragstart","drag.grab","todo.B2"), "a task") zone: =dom("div", on("dragover","drag.over"), on("drop","drag.drop","Done"), "Done") Dropping the item runs drag.drop → sets todo.B2 := "Done". (on("dragover","drag.over") is REQUIRED on the zone — without it the browser fires no drop.) Movement is REACTIVE: if a view FILTERs/reads that cel, the item RE-RENDERS in its new place automatically — you never move it by hand. drag.active remembers the grabbed cel between grab and drop. DATABASE (browser SQLite — persistent, runs in a Worker; the db NAME is the handle) =sqlitedemo() one formula: makes a table, seeds it, opens the client =sqlclient("mydb") a SQL client WINDOW — query editor, tables sidebar, results grid =sql("mydb", "create table t(a,b)") run SQL on a db; writes persist. The first arg is the db name =sql("mydb", "select * from t") …or a handle from =db(); SELECT returns rows =dbseed("mydb", rows, "t") bulk-load a JSON array of row-objects (or a range) into table t =schema("mydb") =tables("mydb") introspect tables/columns/PK·FK · list table names Results from =sqlclient land in real `sqlres.*` cels you can chart or reference. FILES (OPFS, in-browser) =write("/a.txt","hi") =cat("/a.txt") =ls("/") =mkdir("/d") =upload("/") WINDOWS / LAYOUT =win(key,"title","body") =jail(seed) =doom() (Doom in a wasm window, consent-gated) =cels("app",5,2, geom(40,40,520,360)) geom(x,y,w,h[,minW,minH]) sizes a sheet's own window APP = DATA SHEET + WINDOW (one pasteable, shareable formula) =winapp(id, "Title", '=') a draggable WINDOW whose body is a REACTIVE formula =segment(part, part, …) compose cels()/winapp()/def() parts into ONE formula The idiomatic app is a data sheet + a window that reads it — one formula in, one link out: =segment( cels("todo", 4, 2, at("A1","Task"), at("B1","Status"), at("A2","Email Bob"), at("B2","To Do"), at("A3","Ship"), at("B3","Done")), winapp("todo", "To Do", "=dom('div', MAP(todo!A2:A3, LAMBDA(t, dom('div.card', t))))")) The window body reads the sheet by GLOBAL ref (todo!A2:A3) even though it is a different segment. Two string levels: top-level args are "…", the nested window body is '…'. SEGMENTS — every workbook / window / app you make is a SEGMENT (a named layer) =segments() list the loaded segments =members("seg") the cels in a segment =nav(viewport.mobile, item(…)) a navbar that switches between your segments/windows (see NAVBAR below) Each =cels/=winapp/=doom/=jail MINTS a document segment (kind workbook/winapp/ wasm/jail); the substrate (net/dom/origin/…) is reserved and not exported. NAVBAR / MENUS (a pasteable menu; one formula, mobile + desktop) item(label, action, ...children) one menu node. label = emoji+text; action = a window KEY (click focuses it) or a '=formula' (click spawns a window); omit action for a submenu parent. children = nested item()s (WordPress-style). =nav(viewport.mobile, item(…), …) renders the menu — pass viewport.mobile FIRST so it auto-switches: a collapsible ☰ left sidebar on mobile, desktop launcher icons otherwise. Example: =nav(viewport.mobile, item("📁 Files", "files"), item("📊 Charts", item("🥧 Pie", '=cels("pie",5,2, at("A1","Item"), at("B1","Value"), at("A2","Apples"), at("B2",10), at("A3","Pears"), at("B3",20), at("A4","Plums"), at("B4",15), at("A5", "=canvas(360,200, piechart(pie!A2:A4, pie!B2:B4))"))'), item("📈 Bar", '=cels("bar",5,2, at("A1","Item"), at("B1","Value"), at("A2","Q1"), at("B2",30), at("A3","Q2"), at("B3",45), at("A4","Q3"), at("B4",25), at("A5", "=canvas(360,200, barchart(bar!A2:A4, bar!B2:B4))"))')), item("元 Origin", "元")) A chart INSIDE a submenu sheet works WITHOUT a third quote level: a chart formula has no inner string quotes, so wrap the sheet action in '…' and the chart in "…" — `at("A5", "=canvas(360,200, barchart(bar!A2:A4, bar!B2:B4))")`. NEVER reach for backticks to get depth (`infix: unexpected character` — a hard parse error). RESPONSIVE LAYOUT (the page is the product — use the whole viewport, don't cram) The result is a full plastron.ca PAGE, not just a cell. An app-like formula should claim space: open a sized window, or size DOM to the viewport. - viewport.w / viewport.h / viewport.mobile / viewport.orient — reactive cels; a formula that references them RE-RUNS on resize. =viewport() = a one-shot {…}. - =win("app", "App", body, viewport.w, viewport.h - 40) fill the screen - =IF(viewport.mobile, dom("h1","phone view"), bigDesktopUi) branch on device Prefer a window or a sized canvas/dom over a widget squeezed into one tiny cell. ──────────────────────────────────────────────────────────────────────────── SHARE LINKS — how to make one (IMPORTANT: don't hand-encode) ──────────────────────────────────────────────────────────────────────────── A formula fits in a URL. To make a shareable link, **write the formula and let the app encode it** — never hand-roll the compressed form. - `=link()` → encode THIS whole sheet → https://plastron.ca/#f= - `=link("元")` → encode one cell's source - `#raw=` → the simplest link YOU can build by hand: https://plastron.ca/#raw=%3Dcels(8%2C5%2C%22todo%22) (just URL-encode a valid `=formula`) SELF-SERVICE URLs (open one → it answers in PLAIN TEXT; no app runs): - `https://plastron.ca/#check=` → "✅ VALID" or "❌ INVALID + error". - `https://plastron.ca/#encode=` → the compressed `#f=` link for it. Example: to share `=cels(3,3)` → open `https://plastron.ca/#encode=%3Dcels(3%2C3)`. THE #f= CODEC — build a link by hand, no app needed. `#f=` where payload is: tag "0" + base64url(utf8(formula)) ← plain tag "1" + base64url(deflateRaw(utf8(formula))) ← compressed (raw DEFLATE) Emit whichever is SHORTER; the leading tag char tells the decoder which. base64url = base64 with "+"→"-", "/"→"_", trailing "=" stripped. **No JSON wrapper.** Worked (plain): =vocab() → utf8 3D 76 6F 63 61 62 28 29 → base64url PXZvY2FiKCk → #f=0PXZvY2FiKCk ENCRYPTED links — the URL param NAMES the method (a self-describing booter): - `=encrypt(pass)` → `#aes256gcm=` (passphrase prompt on open) payload = base64url( salt[16] ‖ iv[12] ‖ AES-256-GCM(deflateRaw(formula)) ) key = PBKDF2-HMAC-SHA256(pass, salt, 600k) → AES-256. Compress-THEN-encrypt. - `=otpEncrypt()` → `#otp=.` (one-time **pad** = Vernam, NOT a password) payload = base64url( (formula XOR pad) ‖ oneTimeMAC[16] ). NO compression (would leak length). A one-time Carter–Wegman MAC over GF(2^127-1) makes integrity unconditional too. ONE pad = ONE message; never reuse a pad. `=otpDecrypt(url)` decodes it (file picker for the pad). Opening any `#f= / #raw= / #aes256gcm= / #otp=` link is safe: it boots locked — every dangerous fn (net/storage/db/code/secrets) is blocked until a human Allows it. SEGMENT ARCHIVES — the lossless complement to formula-share (=link/=seed): - `=export("seg")` → a 甲骨 archive json string of one document segment - `=export("seg","formula")` → its re-minting =cels(…)/=def(…) formula - `=export("seg","encrypt",p) → an `aes256gcm:` (encrypted archive) - `=export()` → the whole document stack (substrate excluded) - `=import('{…}')` / `=import(blob, pass)` → load it back: ADD or wholesale-REPLACE a same-named segment (refuses a reserved substrate name). Mix in, either form out. ──────────────────────────────────────────────────────────────────────────── WORKED ONE-TIME-PAD DEMO (the pad is PUBLIC → a codec demo, not a secret message) ──────────────────────────────────────────────────────────────────────────── formula =dom("h1", "🐢 turtles all the way down") pad https://plastron.ca/card.png (237084 bytes — download it, load it in the picker) link https://plastron.ca/#otp=card.png.tDQhKiUocjsiLCAvudfU8CB0ccJ0bGcFKGNsbCC0Sokrd2FpIC0rNjpatQy315CfU1qkRszr88szruI ──────────────────────────────────────────────────────────────────────────── FULL VERB CATALOG (baked from =vocab() at build time — current as of this deploy) ──────────────────────────────────────────────────────────────────────────── functions (call as (name …) or =name(…)), grouped by segment: [builtins] * — Variadic product. Coerces args via Number(); 0 args → 1. + — Variadic sum. Coerces args via Number(); 0 args → 0. - — Variadic subtract. 0 args → 0; 1 arg → negation; ≥2 args → left fold. / — Variadic divide. 0 args → NaN; 1 arg → reciprocal; ≥2 args → left fold. json — (value) — turn any value into a pretty-printed JSON string (JSON.stringify(value, null, 2)). Use it to read an object/array cel as text instead of "[object Object]": =json(clients.C1) renders the chat message list as readable JSON. parseRange — Parse range notation ("Seg!A1:B3", "1,1:2,2") → Range { at, shape }. Passes a Range struct through; null when unparseable. rangeToKeys — Enumerate a Range (struct or notation string) into row-major member cel keys — the Key[] shape inputMap accepts. [charts] barchart — (labels, values [, x] [, y] [, w] [, h]) - bar-chart ops for canvas(): one bar per row, value above, label below. labels/values are column ranges: =canvas(420, 260, barchart(turtles!A2:A8, turtles!B2:B8)). Default box 0,0,420,260; pass x/y/w/h to place several charts on one canvas. Edits to the range re-render the chart. linechart — (labels, values [, x] [, y] [, w] [, h]) - line-chart ops for canvas(): one series as a polyline with point dots, gridlines with value ticks, labels along the x axis. =canvas(420, 260, linechart(turtles!A2:A8, turtles!C2:C8)). piechart — (labels, values [, x] [, y] [, w] [, h]) - pie-chart ops for canvas(): wedges from 12 o'clock + a legend with each row's value and share. Non-positive values drop out. =canvas(420, 260, piechart(turtles!A2:A8, turtles!D2:D8)). [checkpoint] checkpoint — (name) - vocabulary head: value is a checkpoint request; the channel drain takes the snapshot. [cli-segment-export] exportToDir — CLI-only. Async. (state, targetDir, opts?) -> { exportedSegments, targetDir }. Copies the plastron/ store (mirroring its layout) to targetDir/plastron/. opts.onlySegments?: Key[] (default: everything except the kernel closure); opts.includeTransitiveDeps?: boolean (default true when onlySegments is set); opts.includeKernel?: boolean (default false); opts.overwrite?: boolean (default false — refuses if targetDir/plastron exists). Rebuilds targetDir/plastron/index.json from the copied set. Pure file op; does not touch state.cels. Installed only when file-store.backend === 'node-fs'. importFromDir — CLI-only. Async. (state, sourceDir, opts?) -> { importedSegments }. Reads sourceDir/plastron/segments///{manifest,segment}.json and writes each into the local segment-store. Refuses role:kernel pairs (kernel comes from the local bundle). Throws on name@version collision unless opts.overwrite is true. Installed only when file-store.backend === 'node-fs'. [docgraph] wiki — (name?) - a button that opens the wiki window on `name` (a cel key, function, segment, or win. layer). =wiki("runCycle"). The article shows summary/doc metadata, the formula source, links to the functions it calls and its input cels (from inputMap), backlinks, a force-directed neighborhood graph, and an editable note saved to metadata.note. Navigation: click any link or graph node. wikidoc — (article) - passthrough for the wiki window's content cel: renders the article vnode wiki.open assembled, or a hint when none is open. wikisrc — (doc) - the source window's content: a node's LIVE source (formula f, or the bound native's toString - always the running code) + its github link. GitHub cannot be iframed (X-Frame-Options deny); the live source is strictly better anyway. [dom] attr — (name value …) - HTML attributes (href, target, type, id, …); pass as a child of dom(). dom — (tag, ...children) - a presentation vnode value. tag.class is emmet sugar; string children → text, nested dom(…) → elements, (style …)/(attr …)/(on …) → that element's style/attributes/events. An ARRAY child is FLATTENED in place, so a collection from MAP/FILTER renders as sibling children: (dom 'div' (MAP tasks!A1:A6 (LAMBDA t (dom 'div.card' t)))); null/''/false items are dropped. Children must be vnodes/strings/arrays/style/attr/on — NOT a genesis: cels()/segment() build sheets and don't nest here (they'd stringify to "[object Object]"); make the grid top-level instead. img — (src... , style()/attr() children) - an image element. String args form a src fallback chain (first non-empty wins). A "/path" src references a file in OPFS: the painter hydrates it to an objectURL via file-store after paint, so formulas reference images by filesystem path - (img desktop.A2 windows.wallpaper (style ...)). data:/http(s)/blob srcs pass straight through. on — (event handlerKey [payload]) - bind a dom event to a handler cel; pass as a child of dom(). =dom("button", on("click", "vault.lock"), "lock"). The handler runs (state, payload, event). style — (prop value …) - inline styles for a dom element; pass as a child of dom(). [doom] doom — () - open a DOOM window: a wasm-window (canvas) running Doom, lazy-fetching doom.wasm + the WAD from plastron.ca (consent-gated). Arrow keys / Ctrl (fire) / Space (use) drive the player when the window is focused. =doom() [file-explorer] download — (path) - a button that downloads an OPFS file to your disk. explorer — (cwd, preview, listing) - a PURE OPFS file-explorer vnode: renders the folders (📁, click→explorer.nav to descend) and files (📄, click→explorer.open to preview) from `listing` ({entries,previewText}) at cwd, plus a '..' row and an upload input. The async OPFS read lives in the nav/open handlers (which write explorer.listing); this fn is pure so the window content formula (explorer explorer.cwd explorer.preview explorer.listing) re-fires reactively. upload — (dir?) - a file picker; the chosen file is written into OPFS (default /). [file-store] file-binary_isChanged — isChanged protocol for file-binary. (oldV, newV) → boolean. Byte-by-byte compare; short-circuits on length mismatch. file-binary_mime — mime protocol for file-binary. (v) → string. Returns 'application/octet-stream' — specialized schemas (file-image, file-doc-*) in Phase B override per type. file-binary_size — size protocol for file-binary. (v) → number. Returns v.length when v is Uint8Array, 0 otherwise. [forcegraph] fgview — (id, spec, pos, zoom) - render a force-directed graph: canvas edges under draggable node chips (drag to move, scroll to zoom, click to act). Use IN A FORMULA with the instance cels as args so interactions re-render reactively: (fgview "g1" fg.g1.spec fg.g1.pos fg.g1.zoom). spec = { nodes: [{key,label?,size?}], edges: [[a,b]...], pin?, onNode?: {dispatch} }. Set up an instance with fg.set. [formula-compiler] formula — kind "formula" — a defn whose body is a parameterized plastron formula, not JS. Header form (p1, p2, …) => body; the body is compiled via the kernel's compileFormula and called positionally with params bound to the call args, non-param symbols (verbs, cel refs) resolved from the registry live. Lets you define reusable verbs (layout, note, glue macros) from the formula surface; the definition lives on cel.f so it is editable and wiki-visible. A formula verb is a function of its parameters — body cel refs are read live but do NOT wire reactivity into callers (pass reactive data as a parameter). Interpreted per call: cheap for UI/glue, not for hot inner loops. [html-template-parser] html-template — Inline HTML-template parser. A FormulaCel whose `f` is the template source and `parser` is "html-template" compiles to a render-spec ({vnode, mount, listeners}). Interpolation deps auto-wire into inputMap. html-template-ref — Live-editable HTML-template parser. The template source is read from the reserved input name "template" at render time and reparsed on string change. Deps are author-declared in inputMap. render-spec_isChanged string-list_isChanged vnode_isChanged [io-touch] touchcapable — () - true on a touch device (maxTouchPoints>0 / ontouchstart). Mount the overlay only when capable. touchpad — (keys) - an on-screen overlay of hold-buttons; keys=[{label,key}] write keys.pressed via touch.press/release. Doom cannot tell a thumb from a keyboard. [js-common-schema] array_dehydrate array_hydrate array_isChanged bigint_dehydrate bigint_hydrate bigint_isChanged boolean_dehydrate boolean_hydrate boolean_isChanged date_dehydrate date_hydrate date_isChanged map_dehydrate map_hydrate map_isChanged null_dehydrate null_hydrate null_isChanged number_dehydrate number_hydrate number_isChanged object_dehydrate object_hydrate object_isChanged regexp_dehydrate regexp_hydrate regexp_isChanged set_dehydrate set_hydrate set_isChanged string_dehydrate string_hydrate string_isChanged uint8array_dehydrate uint8array_hydrate uint8array_isChanged [js-compiler] js — kind "js" — JS source (last expression must be a callable) → runtime Fn via QuickJS-emscripten (a JS-in-wasm sandbox). new Function removed (wasm-only-functions): no DOM, no eval, no host APIs unless granted via the host segment. Lazy-loads the ~1MB runtime on first compile. [kernel] cel-error_dehydrate cel-error_hydrate cel-error_isChanged clearErrors — Reset the kernel errors log. consume — Drain one channel's queue into the caller. dehydrate — Serialize non-kernel cels and manifests to JSON-shaped 甲骨 + 冊 records. dehydrate函 — Warm boot: emit the application as ONE 函 artifact — awake segments deflated inline, dormant payloads passed through, bundled/native as codeSeed, boot.wake = the currently-awake root cover. drain — Drain a channel to empty, invoking its drain fn. ensureSegments — Load every pending segment in the 冊.dependencies closure of the given names, concurrently, with a single precompute for the batch. f — Default formula parser: S-expression source → CompiledLambda. Replace to swap formula languages (provide matching extractDeps). findDependents — Walk 冊.dependencies to find every manifest that transitively depends on a given segment. flush — Tear down a segment and its dependents; fires _dispose, removes cels, precomputes. forget — Remove a segment from state: dormant ⇒ optional persist-to-sink, delete, precompute; awake ⇒ sleep-then-forget (delegates to flush). Kernel-closure guard stays. getCel — The one read — return the live Cel at key (take .v from it). Undefined when missing. getSegmentManifest — Read a 冊 manifest from state.segments by name. hydrate — Inflate segments + manifests + fn maps into state.cels; lock-aware. hydrate函 — Boot an application from ONE 函 artifact (version + per-segment payload sources + boot record): install entries dormant/codeSeed, wake the boot roots, apply boot.set. codeSeed missing from the host bundle fails here, before anything runs. ilk — Short form of interlinked, for formulas: (ilk). Cels of an ilk, interlinked. isSegmentPending — True when a segment's manifest is registered but its cels await loadSegment/ensureSegments. listSegments — Return every 冊 currently loaded into state.segments. loadScript — (url) - load an external script from a URL (a CDN) at runtime; resolves once it loads. Browser-only (injects a