# wity-rkf -- Full Documentation Version: rkf-core v2.0.0 --- ## QUICK REFERENCE -- read this first ### Core model RKF is a declarative format spec. It defines element types and evaluation semantics, not a wire encoding. The reference implementation serializes as XML, but the format is equally expressible as JSON, YAML, or binary. An RKF document models **documents as state machines**, grounded in three vector spaces: ``` f(document: RkfDocument, state: Object) -> ComputedFrame ``` - `document` is parsed from a serialized representation (immutable) - `state` is a plain object of variable values - `ComputedFrame` is the complete render state: resolved marks, controls, envs, gates, outputs, elastics, section tree No DOM. No framework. No side effects. Call `evaluate(doc, state)` at any point, in any order. ### Three vector spaces - **Feature-space** (`rkf-var`) -- dimensions of the machine's state - **Observation-space** (`rkf-gate`) -- named boolean conditions over features - **Transformation-space** (`rkf-transform`) -- state transitions triggered by gates State is emergent, not declared -- it arises from regions in feature-space recognised by gates. ### Install ```bash npm install @wity-ai/rkf-core # Node.js only (optional XML peer): npm install @xmldom/xmldom ``` ### Import ```js import { parse, evaluate, initState, applyUpdate, applyUpdates, serialize, validate, setGraphProvider, setXmlParser, rkfToKnowledge, RkfEdit, splitBlocksByHeading } from '@wity-ai/rkf-core'; ``` ### Setup The stored form of an RKF document is a `` graph. `parse()` and `serialize()` delegate to graph-headless, injected once: ```js import * as graph from '@wity/graph-headless'; setGraphProvider(graph); // required for parse() / serialize() setXmlParser(new DOMParser()); // Node.js only (@xmldom/xmldom); forwarded to graph-headless ``` `evaluate()`, `validate()`, `initState()` and `applyUpdate()` work on a parsed document and need no injection. The `` XML format is retired: nothing reads or writes it except `rkfToKnowledge(xml)`, the one-time upgrade for old documents. The schema section below still writes elements in that notation as a compact way to list each element type and its attributes — in the document model an element is an object with that `tag` and those attributes as fields; in the stored form it is a graph node of that type with them as data. ### Minimal example ```js // An RKF document is a plain object: the model parse() returns and serialize() takes. const doc = { version: '2.0', id: 'demo', title: 'Hello RKF', vars: [{ tag: 'rkf-var', id: 'x', type: 'number', init: 0.5 }], sections: [{ tag: 'rkf-section', id: 'intro', children: [ { tag: 'rkf-env', type: 'prose', content: 'Move the slider to change the value.' }, { tag: 'rkf-machine', id: 'viz', vars: [], transforms: [], machines: [], gates: [{ tag: 'rkf-gate', id: 'moving', expr: 'x > 0.1' }], derives: [{ tag: 'rkf-derive', id: 'y', expr: 'x * x' }], ports: [{ tag: 'rkf-port', dir: 'in', var: 'x', type: 'continuous', min: 0, max: 1, step: 0.01, label: 'x' }], children: [{ tag: 'rkf-canvas', width: 400, height: 300, children: [{ tag: 'rkf-circle', cx: 'x', cy: 'y', r: 8, fill: '#3b82f6' }], }], }, ], }], }; const state = initState(doc); const frame = evaluate(doc, state); // frame.marks -> one rkf-circle with cx: 0.5, cy: 0.25, r: 8 // frame.gates -> { moving: true } const xml = serialize(doc); // ; needs setGraphProvider(graph) — see Setup ``` --- ## SCHEMA v2.0 ### Document structure ``` RkfDocument ├── RkfVar[] (document-level feature variables) └── RkfSection[] (top-level sections; optional gate) ├── RkfEnv (opaque environments: prose, math, image, video, model, audio) └── RkfMachine (state machine blocks) ├── RkfVar[] (feature-space: state dimensions) ├── RkfGate[] (observation-space: named conditions) ├── RkfDerive[] (derived features) ├── RkfTransform[] (transformation-space: state transitions) │ └── RkfSet[] (variable assignments) ├── RkfPort[] (typed I/O interfaces, dir=in|out) ├── RkfMachine[] (nested sub-machines, recursive) ├── RkfCanvas (visual output area) │ ├── RkfScale[] (data-to-pixel mapping) │ ├── RkfAxis[] (axis descriptors) │ └── Marks[] (scatter, line, arrow, rect, circle, arc, text, path, area) └── RkfEnv[] (opaque environments with optional ports) ``` ### Root element: Retired as a format (see Setup); shown as notation only. In the document model these are fields of the document object; in the stored form, data of the `rkf-document` node. | Attribute | Type | Required | Description | |-----------|--------|----------|-------------| | `version` | string | yes | Must be `"2.0"` | | `id` | string | no | Document identifier | | `title` | string | no | Document title | ### rkf-section | Attribute | Type | Description | |-----------|--------|-------------| | `id` | string | Unique section identifier | | `label` | string | Display label | | `gate` | string | Visibility condition (gate id or expression) | ### rkf-env (opaque environment) Unified element replacing v1.0 rkf-prose, rkf-math, rkf-media. `type` selects variant. Can contain child `rkf-port` elements. | Attribute | Type | Description | |-----------|--------|-------------| | `type` | enum | `prose`, `math`, `image`, `video`, `model`, `audio` | | `display` | enum | `block` or `inline` (for `math`) | | `src` | string | Source URL (for image, video, model, audio) | | `alt` | string | Alt text | | `gate` | string | Visibility condition | | `blocks` | array | Heterogeneous content blocks (v2.0 multi-block model) | | `pos` | object | Spatial position `{ x, y, w, h }` for whiteboard placement | ### Content blocks (v2.0 multi-block model) An rkf-env can carry a `blocks[]` array with heterogeneous content: | Kind | Description | Key fields | |------|-------------|------------| | `text` | Rich text | `content`, `heading`, `list` | | `image` | Static image | `src`, `alt` | | `math` | LaTeX expression | `content` | | `machine` | Inline rkf-machine | `id`, `vars`, `gates`, `derives`, `transforms`, `ports` | | `embed` | Embedded `` representation | `id`, `application`, `snapshot` | | `mermaid` | Mermaid diagram source | `content` | | `row` | Grid container | `columns`, `cells` (each `{ blocks }`) | Every block has a stable `id`, unique within its card (assigned on create and by `RkfEdit.normalizeDoc`; an embed's or machine's id is never changed). A row is `{ kind: 'row', id, columns, cells: [{ blocks: [...] }] }`; a cell holds any kind except `row`, `embed`, `machine`. The earlier shape (`blocks` directly on the row, one per cell) is read and upgraded without loss. Embed blocks host another domain representation inline: `{ kind: 'embed', application: 'workbook', snapshot: { ... } }`. Supported applications: `workbook`, `system`, `decision-matrix`, `swot`, `eisenhower`, `ooda`. The snapshot is opaque to rkf-core — the renderer mounts the appropriate domain editor. ### rkf-var (feature-space) | Attribute | Default | Description | |-------------------|------------|-------------| | `id` | required | Variable name | | `type` | `number` | `number`, `string`, `boolean`, `array` | | `init` | `0` | Initial value | | `elasticity` | none | Self-reset delay in ticks | | `elastic-default` | none | Value to reset to | ### rkf-gate (observation-space) | Attribute | Description | |-----------|-------------| | `id` | Gate name (referenced by gate/trigger attributes) | | `expr` | Boolean expression over state | ### rkf-derive | Attribute | Description | |-----------|-------------| | `id` | Derived variable name | | `expr` | Expression evaluated against state | ### rkf-transform (transformation-space) | Attribute | Description | |-----------|-------------| | `trigger` | Gate id or boolean expression | | `on` | Optional event filter: `tick`, `click`, or custom | Contains `` children. ### rkf-set | Attribute | Description | |-----------|-------------| | `var` | Variable to update | | `expr` | New value expression | | `when` | Per-set guard | ### rkf-machine (state machine container) | Attribute | Description | |-----------|-------------| | `id` | Unique block identifier | | `gate` | Visibility condition | Contains vars, gates, derives, transforms, ports, canvas, nested machines, envs. Supports recursive nesting. Inner vars accessible via dot notation: `sub.varName`. ### rkf-port (typed I/O) Replaces v1.0 rkf-input. `dir` specifies input or output. | Attribute | Description | |-----------|-------------| | `dir` | `in` or `out` | | `var` | State variable to bind | | `type` | Constraint type (for dir="in") | | `label` | Display label | | `hint` | Non-normative renderer hint | | `gate` | Visibility condition | **type="continuous"**: bounded numeric range. Attrs: `min`, `max`, `step`. Hint: `slider`, `knob`, etc. **type="choice"**: one of enumerated values. Attrs: `options` (comma-separated), `labels`. **type="boolean"**: true/false. **type="clock"**: boolean + tick timer. Attrs: `interval` (ms, default 120). **type="increment"**: step action on numeric. Attrs: `by` (default 1). **type="event"**: spatial event region. Attrs: `on` (action expression). Contains child marks. ### rkf-canvas | Attribute | Default | Description | |-----------|---------|-------------| | `width` | `560` | Canvas width px | | `height` | `360` | Canvas height px | | `padding` | `40` | Uniform padding px | ### rkf-scale | Attribute | Default | Description | |-----------|----------|-------------| | `id` | required | Scale name | | `type` | `linear` | `linear`, `log`, `ordinal` | | `domain` | required | Data domain (e.g. `"0, 1"`) | | `range` | required | Pixel range. Tokens: `pad`, `w-pad`, `h-pad`, `w`, `h` | ### rkf-axis | Attribute | Description | |-----------|-------------| | `scale` | Scale id | | `orient` | `bottom`, `left`, `top`, `right` | | `label` | Axis label | ### rkf-group | Attribute | Description | |-----------|-------------| | `id` | Group identifier | | `gate` | Group visibility condition | ### Mark elements All marks support `gate`. Expression-valued attributes are evaluated against state. **rkf-scatter**: `data`, `x`, `y`, `color-by`, `radius` **rkf-line**: `x1`, `y1`, `x2`, `y2`, `stroke`, `stroke-width`, `dash` **rkf-arrow**: `x1`, `y1`, `x2`, `y2`, `color`, `width`, `label` **rkf-rect**: `x`, `y`, `width`, `height`, `fill`, `stroke`, `rx` **rkf-circle**: `cx`, `cy`, `r`, `fill`, `stroke` **rkf-arc**: `cx`, `cy`, `r`, `startAngle`, `endAngle`, `fill`, `stroke` **rkf-text**: `x`, `y`, `text-anchor` (start|middle|end), `font-size`, `color`, `font-weight`. Content inside tag. **rkf-image**: `src`, `x`, `y`, `width`, `height` **rkf-path**: `d` (SVG path data), `fill`, `stroke`, `stroke-width` **rkf-area**: `data`, `x`, `y0`, `y1`, `fill` --- ## API REFERENCE -- @wity-ai/rkf-core ### parse(xml) -> RkfDocument Parse a `` document into an `RkfDocument`. Requires the graph module (see Setup); throws if the root is not ``. ### setGraphProvider(graph) Inject graph-headless (`{ GraphAbstract, serialize, parse, setXmlParser }`). Required for `parse()` / `serialize()`. ### rkfToKnowledge(rkfXml) -> string Convert a document in the retired `` format to ``. ### setXmlParser(parserInstance) Inject DOMParser for Node.js (forwarded to graph-headless). Browser uses native DOMParser automatically. ### initState(doc) -> Object Build initial state from all `rkf-var` declarations (document-level and nested). ### evaluate(doc, state) -> ComputedFrame Evaluate document against state. Returns `{ state, marks, controls, envs, gates, outputs, elastics, sections }`. Pure function. An evaluated machine's `ports` each carry the current value of their var (derives included). ### applyUpdate(update, state, event, gateValues?) -> Object Apply one transform rule. Checks `on` match, evaluates `trigger` (gate id or expression), runs sets. Pure. Accepts optional `gateValues` for pre-computed gate evaluations. ### applyUpdates(updates, state, event, gateValues?) -> Object Apply all transforms sequentially for a given event. Accepts optional `gateValues`. ### serialize(doc) -> string Serialize a document to ``. Requires the graph module. Round-trips with parse(). ### validate(doc) -> { valid, errors, warnings } Validate parsed document. Checks required attributes, duplicate var IDs, structural constraints, and illegal combinational cycles among derives/gates. ### evalExpr(expr, state) -> any Safe expression evaluator. Hand-rolled recursive descent. No eval(), no Function(). Supports: `+`, `-`, `*`, `/`, `%`, `==`, `!=`, `<`, `>`, `<=`, `>=`, `&&`, `||`, `!`, `? :`, `()`, `Math.*`, dot access. ### evalGate(gate, state) -> boolean Evaluate gate condition. Returns true if gate is null/undefined. ### Schema constants `SCHEMA_VERSION` = `'2.0'` `ELEMENT_TAGS`, `ENV_TAGS`, `STRUCTURE_TAGS`, `STATE_TAGS`, `LAYOUT_TAGS`, `MARK_TAGS`, `PORT_TAGS` `PORT_TYPES`, `PORT_DIRECTIONS`, `ENV_TYPES`, `BLOCK_KINDS` `TEXT_ANCHOR_VALUES`, `SCALE_TYPE_VALUES`, `AXIS_ORIENT_VALUES`, `MATH_DISPLAY_VALUES` --- ## EXPRESSION EVALUATOR Safe, hand-rolled recursive descent parser. No eval(), no Function(). ### Supported operations - Arithmetic: `+`, `-`, `*`, `/`, `%` - Comparison: `==`, `!=`, `<`, `>`, `<=`, `>=` - Logical: `&&`, `||`, `!` (same values as JavaScript's; both sides are always parsed) - Ternary: `condition ? valueIfTrue : valueIfFalse` - Parentheses: `(expr)` - Literals: `42`, `3.14`, `true`, `false`, `'string'` - Variables: `varName` (resolved from state) - Dot access: `obj.field` (including nested machine vars: `sub.varName`) - Math functions: `Math.abs`, `Math.floor`, `Math.ceil`, `Math.round`, `Math.min`, `Math.max`, `Math.sqrt`, `Math.sin`, `Math.cos`, `Math.tan`, `Math.PI` ### Examples ```js evalExpr('w1 * x + bias', { w1: 0.5, x: 1.0, bias: 0.1 }) // 0.6 evalExpr('-w1 / w2', { w1: 0.5, w2: -0.3 }) // 1.6667 evalExpr('epoch > 10 ? "done" : "training"', { epoch: 15 }) // "done" evalExpr('Math.sin(angle) * radius', { angle: 1.57, radius: 100 }) // ~100 ``` --- ## RENDERING wity-rkf is rendering-target-agnostic. evaluate() returns ComputedFrame. Render to any target: | Target | How | |---|---| | SVG | Map marks to SVG elements | | Canvas 2D | Draw with fillRect, arc, moveTo/lineTo, fillText | | DOM/HTML | Position with CSS, render envs as HTML | | WebGL | Map marks to 3D scene objects | | Server | Evaluate for testing, validation, static rendering | ### ComputedFrame shape ```js { state: { x: 0.5, y: 0.25 }, marks: [ { tag: 'rkf-circle', cx: 200, cy: 150, ... } ], controls: [ { tag: 'rkf-port', dir: 'in', type: 'continuous', ... } ], envs: [ { tag: 'rkf-env', type: 'prose', content: '...', ... } ], gates: { isRunning: true, converged: false }, outputs: [ { tag: 'rkf-port', dir: 'out', var: 'energy', value: 42 } ], elastics: [ { var: 'pulse', resetTo: 0, delayMs: 240 } ], sections: [ { tag: 'rkf-section', id: 'intro', children: [...] } ], } ``` ### Reactive loop pattern ```js let state = initState(doc); function onChange(varName, value) { state = { ...state, [varName]: value }; render(evaluate(doc, state)); } function onTick() { state = applyUpdates(transforms, state, 'tick'); render(evaluate(doc, state)); } render(evaluate(doc, state)); // initial ``` --- ## RELATION TO WITY-SCENE | | wity-scene | wity-rkf | |---|---|---| | Primary axis | Time (t in seconds) | State (variable values) | | Core function | f(scene, t) -> ComputedFrame | f(document, state) -> ComputedFrame | | Content model | Layers with positioned visual elements | Sections with envs, machines, ports | | Use case | Motion graphics, title cards, video overlays | Interactive notebooks, explorable explanations | | Shared patterns | Declarative format, headless evaluation, isomorphic, zero deps | Same |