600 lines
42 KiB
TypeScript
600 lines
42 KiB
TypeScript
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||
import type { JsonValue } from "./serde_json/JsonValue";
|
||
|
||
/**
|
||
* A single Action is a 1-key object: `{ "<action_name>": <body> }`.
|
||
*
|
||
* Examples (all are valid JSON `Action`s):
|
||
* - `{ "set": { "$state.count": "$state.count + 1" } }`
|
||
* - `{ "fetch": { "url": "/api/x", "into": "$state.u" } }`
|
||
* - `{ "push": "/detail/42" }`
|
||
*
|
||
* The body shape per action is NOT validated here — see `jian-core::action`.
|
||
*/
|
||
export type Action = { [key in string]?: JsonValue };
|
||
|
||
export type AlignItems = "start" | "center" | "end" | "stretch";
|
||
|
||
export type AppConfig = { name: string, version: string, id: string, entry: string | null, capabilities: Array<Capability> | null, theme: string | null, orientation: Orientation | null, backgroundColor: string | null, icon: string | null,
|
||
/**
|
||
* C19 splash-frame config.
|
||
*/
|
||
splash: SplashConfig | null,
|
||
/**
|
||
* C18 ASP web handshake postMessage origin allowlist.
|
||
* Only consulted by the web host; strict match; no wildcards.
|
||
*/
|
||
aspAllowedOrigins: Array<string> | null,
|
||
/**
|
||
* Auto-update backend descriptor — same idea as `app.icon`:
|
||
* the schema declares the source of truth (which release feed
|
||
* to consult), the host crate translates it into a concrete
|
||
* `Updater` impl. `None` (the default) means the host's own
|
||
* fallback wins (typically `NullUpdater`). Only consulted by
|
||
* hosts that compile with their respective updater feature
|
||
* (`jian-host-desktop/updater`).
|
||
*
|
||
* TS-side, the type widens to a generic discriminated record so
|
||
* third-party hosts can declare their own kinds without forking
|
||
* `ops.ts`; the Rust side is the typed `UpdaterConfig` struct
|
||
* below.
|
||
*/
|
||
updater: { kind: string; [key: string]: unknown } | null, };
|
||
|
||
export type AppLifecycleHooks = { onLaunch: Array<Action> | null, onResume: Array<Action> | null, onBackground: Array<Action> | null, onTerminate: Array<Action> | null, };
|
||
|
||
export type BlendMode = "normal" | "darken" | "multiply" | "screen" | "overlay" | "lighten" | "difference" | "hue" | "saturation" | "color" | "luminosity";
|
||
|
||
export type BlurBody = { radius: number, };
|
||
|
||
/**
|
||
* Boolean that may also be an expression.
|
||
*/
|
||
export type BoolOrExpression = boolean | string;
|
||
|
||
export type Capability = "storage" | "network" | "camera" | "microphone" | "location" | "notifications" | "clipboard" | "biometric" | "file_system" | "haptic";
|
||
|
||
/**
|
||
* Checkbox with an optional adjacent `label`. `checked` two-way binds
|
||
* via `bindings.bind:value`.
|
||
*/
|
||
export type CheckboxNode = { width: SizingBehavior | null, height: SizingBehavior | null, checked: BoolOrExpression | null, label: string | null, fill: Array<PenFill> | null, stroke: PenStroke | null, effects: Array<PenEffect> | null, cornerRadius: CornerRadius | null, states: WidgetStates | null, state: { [key in string]?: StateEntry } | null, bindings: { [key in string]?: Expression } | null, events: EventHandlers | null, lifecycle: NodeLifecycleHooks | null, semantics: SemanticsMeta | null, gestures: GestureOverrides | null, route: NavigationRoute | null, id: string, name: string | null, role: string | null, explain: string | null, x: number | null, y: number | null, rotation: number | null, opacity: NumberOrExpression | null, enabled: BoolOrExpression | null, visible: boolean | null, locked: boolean | null, flipX: boolean | null, flipY: boolean | null, theme: { [key in string]?: string } | null, };
|
||
|
||
export type ConversionEntry = { kind: ConversionKind,
|
||
/**
|
||
* Caller-stable key, e.g. "src/Button.tsx#Button" or "route:/settings".
|
||
*/
|
||
key: string, sourcePath: string | null,
|
||
/**
|
||
* Content fingerprint of the source unit; used for incremental conversion.
|
||
*/
|
||
sourceHash: string | null,
|
||
/**
|
||
* Master frame id (component) or screen frame id. None for token entries.
|
||
*/
|
||
nodeId: string | null,
|
||
/**
|
||
* Source-node id to document-node id mapping for idempotent reruns.
|
||
*/
|
||
nodeIds: { [key in string]?: string } | null, };
|
||
|
||
export type ConversionKind = "token" | "component" | "screen";
|
||
|
||
/**
|
||
* Ledger of code-to-design conversion units.
|
||
*/
|
||
export type ConversionSpec = { entries: Array<ConversionEntry>, };
|
||
|
||
export type CornerRadius = number | [number, number, number, number];
|
||
|
||
/**
|
||
* One named colour from the design-md colour palette.
|
||
*/
|
||
export type DesignMdColor = {
|
||
/**
|
||
* Human label, e.g. "Primary".
|
||
*/
|
||
name: string,
|
||
/**
|
||
* `#RRGGBB` hex value.
|
||
*/
|
||
hex: string,
|
||
/**
|
||
* How the colour is used, e.g. "buttons and links".
|
||
*/
|
||
role: string, };
|
||
|
||
/**
|
||
* A structured design-system brief attached to a [`PenDocument`].
|
||
*
|
||
* [`PenDocument`]: crate::document::PenDocument
|
||
*/
|
||
export type DesignMdSpec = {
|
||
/**
|
||
* Original markdown source — kept verbatim for round-trip fidelity.
|
||
*/
|
||
raw: string, projectName: string | null, visualTheme: string | null, colorPalette: Array<DesignMdColor> | null, typography: DesignMdTypography | null, componentStyles: string | null, layoutPrinciples: string | null, generationNotes: string | null, };
|
||
|
||
/**
|
||
* Typography guidance from the design-md typography section.
|
||
*/
|
||
export type DesignMdTypography = { fontFamily: string | null, headings: string | null, body: string | null,
|
||
/**
|
||
* Free-form scale description — usually the whole section text.
|
||
*/
|
||
scale: string | null, };
|
||
|
||
export type EllipseNode = { width: SizingBehavior | null, height: SizingBehavior | null, cornerRadius: number | null, innerRadius: number | null, startAngle: number | null, sweepAngle: number | null, fill: Array<PenFill> | null, stroke: PenStroke | null, effects: Array<PenEffect> | null, state: { [key in string]?: StateEntry } | null, bindings: { [key in string]?: Expression } | null, events: EventHandlers | null, lifecycle: NodeLifecycleHooks | null, semantics: SemanticsMeta | null, gestures: GestureOverrides | null, route: NavigationRoute | null, id: string, name: string | null, role: string | null, explain: string | null, x: number | null, y: number | null, rotation: number | null, opacity: NumberOrExpression | null, enabled: BoolOrExpression | null, visible: boolean | null, locked: boolean | null, flipX: boolean | null, flipY: boolean | null, theme: { [key in string]?: string } | null, };
|
||
|
||
/**
|
||
* All supported event hook keys. Note: input events (`onChange`, `onSubmit`, `onFocus`,
|
||
* `onBlur`) apply only to input-kind nodes. `on_key` is keyboard, `on_reach_end`
|
||
* is list-scroll-end, etc.
|
||
*/
|
||
export type EventHandlers = { onTap: Array<Action> | null, onDoubleTap: Array<Action> | null, onLongPress: Array<Action> | null, onPanStart: Array<Action> | null, onPanUpdate: Array<Action> | null, onPanEnd: Array<Action> | null, onScaleStart: Array<Action> | null, onScaleUpdate: Array<Action> | null, onScaleEnd: Array<Action> | null, onRotateStart: Array<Action> | null, onRotateUpdate: Array<Action> | null, onRotateEnd: Array<Action> | null, onHoverEnter: Array<Action> | null, onHoverLeave: Array<Action> | null, onChange: Array<Action> | null, onSubmit: Array<Action> | null, onFocus: Array<Action> | null, onBlur: Array<Action> | null, onKey: Array<Action> | null, onScroll: Array<Action> | null, onReachEnd: Array<Action> | null, };
|
||
|
||
/**
|
||
* A Tier 1 expression source — represented as a raw string.
|
||
*
|
||
* Parsing and validation are the responsibility of `jian-core::expression::parser`
|
||
* (Plan 2). The schema crate only guarantees the string is present; content-level
|
||
* correctness is deferred.
|
||
*/
|
||
export type Expression = string;
|
||
|
||
export type FontStyleKind = "normal" | "italic";
|
||
|
||
export type FontWeight = number | string;
|
||
|
||
/**
|
||
* Forward declaration of PenNode union — defined in `node/mod.rs`.
|
||
* We accept `Vec<super::PenNode>` as children.
|
||
*/
|
||
export type FrameNode = { children: Array<PenNode> | null, imageSearchQuery: string | null, reusable: boolean | null, slot: Array<string> | null, state: { [key in string]?: StateEntry } | null, bindings: { [key in string]?: Expression } | null, events: EventHandlers | null, lifecycle: NodeLifecycleHooks | null, semantics: SemanticsMeta | null, gestures: GestureOverrides | null, route: NavigationRoute | null, id: string, name: string | null, role: string | null, explain: string | null, x: number | null, y: number | null, rotation: number | null, opacity: NumberOrExpression | null, enabled: BoolOrExpression | null, visible: boolean | null, locked: boolean | null, flipX: boolean | null, flipY: boolean | null, theme: { [key in string]?: string } | null, width: SizingBehavior | null, height: SizingBehavior | null, layout: LayoutMode | null, gap: NumberOrExpression | null, padding: Padding | null, justifyContent: JustifyContent | null, alignItems: AlignItems | null, clipContent: boolean | null, cornerRadius: CornerRadius | null, fill: Array<PenFill> | null, stroke: PenStroke | null, effects: Array<PenEffect> | null, };
|
||
|
||
export type GestureOverrides = {
|
||
/**
|
||
* When true, this node and its subtree bypass the Arena and receive raw pointer events.
|
||
*/
|
||
rawPointer: boolean | null, disabled: Expression | null, scrollBehavior: ScrollBehavior | null,
|
||
/**
|
||
* Override drag threshold in logical pixels (default 8).
|
||
*/
|
||
dragThreshold: number | null,
|
||
/**
|
||
* Override long-press duration in ms (default 500).
|
||
*/
|
||
longPressDuration: number | null,
|
||
/**
|
||
* Author-explicit Tab-traversal opt-in.
|
||
*
|
||
* `Some(true)` — node enters the focus chain regardless of its
|
||
* semantic role.
|
||
* `Some(false)` — node is excluded even if its `semantics.role`
|
||
* would otherwise auto-include it (e.g. a decorative `Input`).
|
||
* `None` — falls back to the role heuristic (`Button` / `Link`
|
||
* / `Input` are auto-included; everything else is opt-in).
|
||
*/
|
||
focusable: boolean | null, };
|
||
|
||
export type GradientStop = { offset: number, color: string, };
|
||
|
||
export type GroupNode = { children: Array<PenNode> | null, state: { [key in string]?: StateEntry } | null, bindings: { [key in string]?: Expression } | null, events: EventHandlers | null, lifecycle: NodeLifecycleHooks | null, semantics: SemanticsMeta | null, gestures: GestureOverrides | null, route: NavigationRoute | null, id: string, name: string | null, role: string | null, explain: string | null, x: number | null, y: number | null, rotation: number | null, opacity: NumberOrExpression | null, enabled: BoolOrExpression | null, visible: boolean | null, locked: boolean | null, flipX: boolean | null, flipY: boolean | null, theme: { [key in string]?: string } | null, width: SizingBehavior | null, height: SizingBehavior | null, layout: LayoutMode | null, gap: NumberOrExpression | null, padding: Padding | null, justifyContent: JustifyContent | null, alignItems: AlignItems | null, clipContent: boolean | null, cornerRadius: CornerRadius | null, fill: Array<PenFill> | null, stroke: PenStroke | null, effects: Array<PenEffect> | null, };
|
||
|
||
export type IconFontNode = { iconFontName: string, iconFontFamily: string | null, width: SizingBehavior | null, height: SizingBehavior | null, fill: Array<PenFill> | null, stroke: PenStroke | null, state: { [key in string]?: StateEntry } | null, bindings: { [key in string]?: Expression } | null, events: EventHandlers | null, lifecycle: NodeLifecycleHooks | null, semantics: SemanticsMeta | null, gestures: GestureOverrides | null, route: NavigationRoute | null, id: string, name: string | null, role: string | null, explain: string | null, x: number | null, y: number | null, rotation: number | null, opacity: NumberOrExpression | null, enabled: BoolOrExpression | null, visible: boolean | null, locked: boolean | null, flipX: boolean | null, flipY: boolean | null, theme: { [key in string]?: string } | null, };
|
||
|
||
export type ImageFillBody = { url: string, mode: ImageFillMode | null, originalSize: ImageOriginalSize | null, transform: ImageTransform | null, explain: string | null, opacity: number | null, exposure: number | null, contrast: number | null, saturation: number | null, temperature: number | null, tint: number | null, highlights: number | null, shadows: number | null, };
|
||
|
||
export type ImageFillMode = "fill" | "fit" | "crop" | "tile" | "stretch";
|
||
|
||
export type ImageFitMode = "fill" | "fit" | "crop" | "tile";
|
||
|
||
export type ImageNode = { src: string, objectFit: ImageFitMode | null, width: SizingBehavior | null, height: SizingBehavior | null, cornerRadius: CornerRadius | null, effects: Array<PenEffect> | null, exposure: number | null, contrast: number | null, saturation: number | null, temperature: number | null, tint: number | null, highlights: number | null, shadows: number | null, imagePrompt: string | null, imageSearchQuery: string | null, state: { [key in string]?: StateEntry } | null, bindings: { [key in string]?: Expression } | null, events: EventHandlers | null, lifecycle: NodeLifecycleHooks | null, semantics: SemanticsMeta | null, gestures: GestureOverrides | null, route: NavigationRoute | null, id: string, name: string | null, role: string | null, explain: string | null, x: number | null, y: number | null, rotation: number | null, opacity: NumberOrExpression | null, enabled: BoolOrExpression | null, visible: boolean | null, locked: boolean | null, flipX: boolean | null, flipY: boolean | null, theme: { [key in string]?: string } | null, };
|
||
|
||
export type ImageOriginalSize = { width: number, height: number, };
|
||
|
||
export type ImageTransform = { m00: number, m01: number, m02: number, m10: number, m11: number, m12: number, };
|
||
|
||
export type JustifyContent = "start" | "center" | "end" | "space_between" | "space_around";
|
||
|
||
export type LayoutMode = "none" | "vertical" | "horizontal";
|
||
|
||
export type LineNode = { x2: number | null, y2: number | null, stroke: PenStroke | null, effects: Array<PenEffect> | null, state: { [key in string]?: StateEntry } | null, bindings: { [key in string]?: Expression } | null, events: EventHandlers | null, lifecycle: NodeLifecycleHooks | null, semantics: SemanticsMeta | null, gestures: GestureOverrides | null, route: NavigationRoute | null, id: string, name: string | null, role: string | null, explain: string | null, x: number | null, y: number | null, rotation: number | null, opacity: NumberOrExpression | null, enabled: BoolOrExpression | null, visible: boolean | null, locked: boolean | null, flipX: boolean | null, flipY: boolean | null, theme: { [key in string]?: string } | null, };
|
||
|
||
export type LinearGradientBody = { angle: number | null, stops: Array<GradientStop>, explain: string | null, opacity: number | null, blendMode: BlendMode | null, };
|
||
|
||
export type LiveRegion = "off" | "polite" | "assertive";
|
||
|
||
/**
|
||
* An ABI version string for the Tier 3 WASM module. The only recognised
|
||
* value today is `jian.wasm.v1`; Jian rejects unknown ABI at load time.
|
||
*/
|
||
export type LogicAbi = string;
|
||
|
||
export type LogicModuleRef = { id: string, source: string, integrity: string | null, abi: LogicAbi, capabilities: Array<Capability> | null, };
|
||
|
||
/**
|
||
* Uniform-grid mesh gradient (v1). A `rows`×`cols` lattice of
|
||
* `MeshVertexStop`s is Gouraud-interpolated across the node's
|
||
* round-rect. Mirrors the sibling gradient bodies for the shared
|
||
* `explain`/`opacity`/`blend_mode` tail.
|
||
*/
|
||
export type MeshGradientBody = { rows: number, cols: number, stops: Array<MeshVertexStop>, explain: string | null, opacity: number | null, blendMode: BlendMode | null, };
|
||
|
||
/**
|
||
* A single vertex of a `MeshGradient` grid. `row`/`col` are 0-based
|
||
* indices into a `rows`×`cols` lattice; `color` is the Gouraud-shaded
|
||
* colour anchored at that vertex.
|
||
*/
|
||
export type MeshVertexStop = { row: number, col: number, color: string, };
|
||
|
||
/**
|
||
* Declarative per-node navigation: clicking the node pushes/replaces/pops a route.
|
||
* Equivalent to `events.on_tap = [{"push": "..."}]` but more editor-discoverable.
|
||
*/
|
||
export type NavigationRoute = { "push": string } | { "replace": string } | { "pop": null };
|
||
|
||
export type NodeLifecycleHooks = { onMount: Array<Action> | null, onUnmount: Array<Action> | null, };
|
||
|
||
/**
|
||
* Numeric input with optional +/- steppers. Precise complement to
|
||
* `slider`; `value` two-way binds via `bindings.bind:value`. When
|
||
* omitted, `min`/`max`/`step` default to none/none/1 at runtime.
|
||
*/
|
||
export type NumberInputNode = { width: SizingBehavior | null, height: SizingBehavior | null,
|
||
/**
|
||
* Placeholder shown when `value` is empty.
|
||
*/
|
||
placeholder: string | null, value: NumberOrExpression | null,
|
||
/**
|
||
* Lucide glyph drawn at the left content edge. See `TextInputNode`.
|
||
*/
|
||
leadingIcon: string | null,
|
||
/**
|
||
* Lucide glyph drawn at the right content edge. See `TextInputNode`.
|
||
*/
|
||
trailingIcon: string | null, min: number | null, max: number | null, step: number | null, fill: Array<PenFill> | null, stroke: PenStroke | null, effects: Array<PenEffect> | null, cornerRadius: CornerRadius | null, states: WidgetStates | null, state: { [key in string]?: StateEntry } | null, bindings: { [key in string]?: Expression } | null, events: EventHandlers | null, lifecycle: NodeLifecycleHooks | null, semantics: SemanticsMeta | null, gestures: GestureOverrides | null, route: NavigationRoute | null, id: string, name: string | null, role: string | null, explain: string | null, x: number | null, y: number | null, rotation: number | null, opacity: NumberOrExpression | null, enabled: BoolOrExpression | null, visible: boolean | null, locked: boolean | null, flipX: boolean | null, flipY: boolean | null, theme: { [key in string]?: string } | null, };
|
||
|
||
/**
|
||
* Opacity can be a number or a `$variable` reference string.
|
||
*/
|
||
export type NumberOrExpression = number | string;
|
||
|
||
export type Orientation = "portrait" | "landscape" | "auto";
|
||
|
||
export type Padding = number | [number, number] | [number, number, number, number] | string;
|
||
|
||
export type PageLifecycleHooks = { onEnter: Array<Action> | null, onLeave: Array<Action> | null, onForeground: Array<Action> | null, onBackground: Array<Action> | null, };
|
||
|
||
export type PathNode = { iconId: string | null, d: string | null, anchors: Array<PenPathAnchor> | null, closed: boolean | null, width: SizingBehavior | null, height: SizingBehavior | null, fill: Array<PenFill> | null, stroke: PenStroke | null, effects: Array<PenEffect> | null, state: { [key in string]?: StateEntry } | null, bindings: { [key in string]?: Expression } | null, events: EventHandlers | null, lifecycle: NodeLifecycleHooks | null, semantics: SemanticsMeta | null, gestures: GestureOverrides | null, route: NavigationRoute | null, id: string, name: string | null, role: string | null, explain: string | null, x: number | null, y: number | null, rotation: number | null, opacity: NumberOrExpression | null, enabled: BoolOrExpression | null, visible: boolean | null, locked: boolean | null, flipX: boolean | null, flipY: boolean | null, theme: { [key in string]?: string } | null, };
|
||
|
||
export type PenDocument = {
|
||
/**
|
||
* Document format version stored in files since v0.x. Always present.
|
||
*/
|
||
version: string, name: string | null,
|
||
/**
|
||
* Wire shape: axis-name → ordered theme names. Frozen from v0.x.
|
||
*/
|
||
themes: { [key in string]?: Array<string> } | null, variables: { [key in string]?: VariableDefinition } | null, pages: Array<PenPage> | null,
|
||
/**
|
||
* Default-on-deserialize so a multi-page document that carries only
|
||
* `pages` (no top-level `children`) still loads — the TS web app's
|
||
* whole-document sync (`document.post.ts`) accepts `{version, pages}`
|
||
* without a `children` array. Always serialized (even empty `[]`).
|
||
*/
|
||
children: Array<PenNode>,
|
||
/**
|
||
* "1.0" when any v1 extension is present; undefined ⇒ legacy v0.x.
|
||
*/
|
||
formatVersion: string | null,
|
||
/**
|
||
* App id (reverse-DNS). Required when `app` is set; otherwise optional.
|
||
*/
|
||
id: string | null, app: AppConfig | null, routes: RoutesConfig | null, state: { [key in string]?: StateEntry } | null, lifecycle: AppLifecycleHooks | null, logicModules: Array<LogicModuleRef> | null,
|
||
/**
|
||
* Per-document design-system brief (the "design.md"). Optional —
|
||
* absent on documents that never authored one.
|
||
*/
|
||
designMd: DesignMdSpec | null,
|
||
/**
|
||
* Code-to-design conversion ledger.
|
||
*/
|
||
conversion: ConversionSpec | null, };
|
||
|
||
export type PenEffect = { "type": "blur" } & BlurBody | { "type": "background_blur" } & BlurBody | { "type": "shadow" } & ShadowBody;
|
||
|
||
export type PenFill = { "type": "solid" } & SolidFillBody | { "type": "linear_gradient" } & LinearGradientBody | { "type": "radial_gradient" } & RadialGradientBody | { "type": "mesh_gradient" } & MeshGradientBody | { "type": "shader" } & ShaderFillBody | { "type": "image" } & ImageFillBody;
|
||
|
||
/**
|
||
* Union of all concrete node types.
|
||
* Tag is the JSON `"type"` field.
|
||
*/
|
||
export type PenNode = { "type": "frame" } & FrameNode | { "type": "group" } & GroupNode | { "type": "rectangle" } & RectangleNode | { "type": "ellipse" } & EllipseNode | { "type": "line" } & LineNode | { "type": "polygon" } & PolygonNode | { "type": "path" } & PathNode | { "type": "text" } & TextNode | { "type": "text_input" } & TextInputNode | { "type": "image" } & ImageNode | { "type": "icon_font" } & IconFontNode | { "type": "text_area" } & TextAreaNode | { "type": "select" } & SelectNode | { "type": "switch" } & SwitchNode | { "type": "checkbox" } & CheckboxNode | { "type": "slider" } & SliderNode | { "type": "radio_group" } & RadioGroupNode | { "type": "number_input" } & NumberInputNode | { "type": "progress" } & ProgressNode | { "type": "tabs" } & TabsNode | { "type": "ref" } & RefNode;
|
||
|
||
export type PenPage = { id: string, name: string, children: Array<PenNode>, state: { [key in string]?: StateEntry } | null, lifecycle: PageLifecycleHooks | null, };
|
||
|
||
export type PenPathAnchor = { x: number, y: number, handleIn: PenPathHandle | null, handleOut: PenPathHandle | null, pointType: PenPathPointType | null, };
|
||
|
||
export type PenPathHandle = { x: number, y: number, };
|
||
|
||
export type PenPathPointType = "corner" | "mirrored" | "independent";
|
||
|
||
export type PenStroke = { thickness: StrokeThickness, align: StrokeAlign | null, join: StrokeJoin | null, cap: StrokeCap | null, dashPattern: Array<number> | null, dashOffset: number | null, fill: Array<PenFill> | null, };
|
||
|
||
export type PolygonNode = { polygonCount: number, width: SizingBehavior | null, height: SizingBehavior | null, cornerRadius: number | null, fill: Array<PenFill> | null, stroke: PenStroke | null, effects: Array<PenEffect> | null, state: { [key in string]?: StateEntry } | null, bindings: { [key in string]?: Expression } | null, events: EventHandlers | null, lifecycle: NodeLifecycleHooks | null, semantics: SemanticsMeta | null, gestures: GestureOverrides | null, route: NavigationRoute | null, id: string, name: string | null, role: string | null, explain: string | null, x: number | null, y: number | null, rotation: number | null, opacity: NumberOrExpression | null, enabled: BoolOrExpression | null, visible: boolean | null, locked: boolean | null, flipX: boolean | null, flipY: boolean | null, theme: { [key in string]?: string } | null, };
|
||
|
||
export type PrimitiveType = "int" | "float" | "number" | "string" | "bool" | "array" | "object" | "date";
|
||
|
||
/**
|
||
* Progress indicator. Display-only (not focusable/keyboard-driven):
|
||
* `value` is read from the state graph via `bindings.value`. `max`
|
||
* defaults to 100; `indeterminate` shows an animated unknown-progress
|
||
* state and ignores `value`.
|
||
*/
|
||
export type ProgressNode = { width: SizingBehavior | null, height: SizingBehavior | null, value: NumberOrExpression | null, max: number | null, indeterminate: boolean | null, fill: Array<PenFill> | null, stroke: PenStroke | null, effects: Array<PenEffect> | null, cornerRadius: CornerRadius | null, states: WidgetStates | null, state: { [key in string]?: StateEntry } | null, bindings: { [key in string]?: Expression } | null, events: EventHandlers | null, lifecycle: NodeLifecycleHooks | null, semantics: SemanticsMeta | null, gestures: GestureOverrides | null, route: NavigationRoute | null, id: string, name: string | null, role: string | null, explain: string | null, x: number | null, y: number | null, rotation: number | null, opacity: NumberOrExpression | null, enabled: BoolOrExpression | null, visible: boolean | null, locked: boolean | null, flipX: boolean | null, flipY: boolean | null, theme: { [key in string]?: string } | null, };
|
||
|
||
export type RadialGradientBody = { cx: number | null, cy: number | null, radius: number | null, stops: Array<GradientStop>, explain: string | null, opacity: number | null, blendMode: BlendMode | null, };
|
||
|
||
/**
|
||
* Single-choice radio group. Renders one radio per `option`; the
|
||
* selected option `value` two-way binds via `bindings.bind:value`.
|
||
*/
|
||
export type RadioGroupNode = { width: SizingBehavior | null, height: SizingBehavior | null,
|
||
/**
|
||
* Currently selected option `value`.
|
||
*/
|
||
value: string | null, options: Array<SelectOption> | null, fill: Array<PenFill> | null, stroke: PenStroke | null, effects: Array<PenEffect> | null, cornerRadius: CornerRadius | null, states: WidgetStates | null, state: { [key in string]?: StateEntry } | null, bindings: { [key in string]?: Expression } | null, events: EventHandlers | null, lifecycle: NodeLifecycleHooks | null, semantics: SemanticsMeta | null, gestures: GestureOverrides | null, route: NavigationRoute | null, id: string, name: string | null, role: string | null, explain: string | null, x: number | null, y: number | null, rotation: number | null, opacity: NumberOrExpression | null, enabled: BoolOrExpression | null, visible: boolean | null, locked: boolean | null, flipX: boolean | null, flipY: boolean | null, theme: { [key in string]?: string } | null, };
|
||
|
||
export type RectangleNode = { children: Array<PenNode> | null, state: { [key in string]?: StateEntry } | null, bindings: { [key in string]?: Expression } | null, events: EventHandlers | null, lifecycle: NodeLifecycleHooks | null, semantics: SemanticsMeta | null, gestures: GestureOverrides | null, route: NavigationRoute | null, id: string, name: string | null, role: string | null, explain: string | null, x: number | null, y: number | null, rotation: number | null, opacity: NumberOrExpression | null, enabled: BoolOrExpression | null, visible: boolean | null, locked: boolean | null, flipX: boolean | null, flipY: boolean | null, theme: { [key in string]?: string } | null, width: SizingBehavior | null, height: SizingBehavior | null, layout: LayoutMode | null, gap: NumberOrExpression | null, padding: Padding | null, justifyContent: JustifyContent | null, alignItems: AlignItems | null, clipContent: boolean | null, cornerRadius: CornerRadius | null, fill: Array<PenFill> | null, stroke: PenStroke | null, effects: Array<PenEffect> | null, };
|
||
|
||
export type RefNode = { ref: string, descendants: { [key in string]?: JsonValue } | null, children: Array<PenNode> | null, state: { [key in string]?: StateEntry } | null, bindings: { [key in string]?: Expression } | null, events: EventHandlers | null, lifecycle: NodeLifecycleHooks | null, semantics: SemanticsMeta | null, gestures: GestureOverrides | null, route: NavigationRoute | null, id: string, name: string | null, role: string | null, explain: string | null, x: number | null, y: number | null, rotation: number | null, opacity: NumberOrExpression | null, enabled: BoolOrExpression | null, visible: boolean | null, locked: boolean | null, flipX: boolean | null, flipY: boolean | null, theme: { [key in string]?: string } | null, };
|
||
|
||
export type RouteSpec = { pageId: string, preload: boolean | null, guards: Array<Action> | null,
|
||
/**
|
||
* Path-parameter type declarations (v1.0 additive — 2026-04-24).
|
||
* Keys correspond to `:param` placeholders in the route path
|
||
* (e.g. path `/detail/:id` → key `id`). The AI Action Surface
|
||
* uses these types when synthesising the JsonSchema for the
|
||
* derived `open_*(p)` action; runtime does strict type-checking
|
||
* on incoming values rather than silent coercion.
|
||
*/
|
||
params: { [key in string]?: StateType } | null, };
|
||
|
||
export type RoutesConfig = { entry: string, routes: { [key in string]?: RouteSpec }, transitions: { [key in string]?: Transition } | null, };
|
||
|
||
export type ScrollBehavior = "auto" | "contain" | "none";
|
||
|
||
/**
|
||
* Dropdown select. The runtime pops an option list; the selected
|
||
* option `value` two-way binds via `bindings.bind:value`.
|
||
*/
|
||
export type SelectNode = { width: SizingBehavior | null, height: SizingBehavior | null,
|
||
/**
|
||
* Shown when no option is selected.
|
||
*/
|
||
placeholder: string | null,
|
||
/**
|
||
* Currently selected option `value`.
|
||
*/
|
||
value: string | null, options: Array<SelectOption> | null, fill: Array<PenFill> | null, stroke: PenStroke | null, effects: Array<PenEffect> | null, cornerRadius: CornerRadius | null, states: WidgetStates | null, state: { [key in string]?: StateEntry } | null, bindings: { [key in string]?: Expression } | null, events: EventHandlers | null, lifecycle: NodeLifecycleHooks | null, semantics: SemanticsMeta | null, gestures: GestureOverrides | null, route: NavigationRoute | null, id: string, name: string | null, role: string | null, explain: string | null, x: number | null, y: number | null, rotation: number | null, opacity: NumberOrExpression | null, enabled: BoolOrExpression | null, visible: boolean | null, locked: boolean | null, flipX: boolean | null, flipY: boolean | null, theme: { [key in string]?: string } | null, };
|
||
|
||
/**
|
||
* A single dropdown option: the persisted `value` and its display `label`.
|
||
*/
|
||
export type SelectOption = { value: string, label: string, };
|
||
|
||
export type SemanticAction = { name: string, label: string, handler: Array<Action>, };
|
||
|
||
export type SemanticRole = "button" | "link" | "image" | "text" | "heading" | "input" | "list" | "list_item" | "header" | "nav" | "main" | "dialog" | "alert";
|
||
|
||
export type SemanticsMeta = { role: SemanticRole | null, label: string | null, hint: string | null, liveRegion: LiveRegion | null, disabled: Expression | null, actions: Array<SemanticAction> | null,
|
||
/**
|
||
* Author-stable override for the auto-derived AI action name.
|
||
* When set, the resulting action name is `<scope>.<aiName>`
|
||
* without the auto `_<hash4>` suffix and survives slug recomputes
|
||
* across builds. See `2026-04-24-ai-action-surface.md` §3.3-3.4.
|
||
*/
|
||
aiName: string | null,
|
||
/**
|
||
* Tool description shown to external AI agents. Overrides the
|
||
* auto-generated default; lets authors steer what a model "sees"
|
||
* without changing visible UI text.
|
||
*/
|
||
aiDescription: string | null,
|
||
/**
|
||
* `true` permanently hides the node's derived action from the AI
|
||
* surface (StaticHidden). Defaults to `false`. ConfirmGated /
|
||
* StateGated availability are decided dynamically and do **not**
|
||
* require this flag — see ai-action-surface.md §4.
|
||
*/
|
||
aiHidden: boolean | null,
|
||
/**
|
||
* Historical `aiName` values still accepted by `execute_action`
|
||
* for transparent migration after a rename. Aliases are honoured
|
||
* at execute time (with `audit reason_code: "alias_used"`) but
|
||
* not surfaced by `list_available_actions`. See §9.
|
||
*/
|
||
aiAliases: Array<string> | null, };
|
||
|
||
/**
|
||
* Native SkSL shader fill (v1). The `sksl` source is stored RAW and is
|
||
* treated as untrusted: the renderer entrypoint is the SkSL signature
|
||
* `half4 main(float2 fragCoord)`. On compile failure the backend
|
||
* degrades to a visible solid fill (the first `color` uniform, else
|
||
* mid-gray) and never panics. Mirrors the sibling gradient bodies for
|
||
* the shared `opacity`/`blend_mode` tail.
|
||
*
|
||
* Pencil-flavoured WebGL-GLSL import is an explicit follow-up, NOT v1;
|
||
* v1 expects SkSL (Skia's GLSL dialect) verbatim.
|
||
*/
|
||
export type ShaderFillBody = {
|
||
/**
|
||
* RAW SkSL source. Entrypoint: `half4 main(float2 fragCoord)`.
|
||
*/
|
||
sksl: string,
|
||
/**
|
||
* Optional named-uniform map (`float` / `vec*` / `color`). A
|
||
* shader may declare none; absent or empty both mean "no uniforms".
|
||
*/
|
||
uniforms: { [key in string]?: ShaderUniformValue } | null, explain: string | null, opacity: number | null, blendMode: BlendMode | null, };
|
||
|
||
/**
|
||
* One named uniform value bound into an SkSL shader fill via
|
||
* `RuntimeShaderBuilder` at paint time. `untagged` so the wire form is
|
||
* the bare JSON scalar/array the author wrote — a number is a `float`,
|
||
* a number array is a `vec2`/`vec3`/`vec4`, and a hex string is a
|
||
* `color` mapped to a `vec4` (premultiplied RGBA). The named-uniforms
|
||
* map on `ShaderFillBody` is optional; a shader may take none.
|
||
*/
|
||
export type ShaderUniformValue = number | Array<number> | string;
|
||
|
||
export type ShadowBody = { inner: boolean | null, offsetX: number, offsetY: number, blur: number, spread: number, color: string, };
|
||
|
||
export type SidedThickness = { top: number | null, right: number | null, bottom: number | null, left: number | null, };
|
||
|
||
/**
|
||
* Sizing value: a number, a fixed keyword, or an arbitrary string (typically `$variable` ref).
|
||
*/
|
||
export type SizingBehavior = number | SizingKeyword | string;
|
||
|
||
export type SizingKeyword = "fit_content" | "fill_container";
|
||
|
||
/**
|
||
* Range slider. `value` two-way binds via `bindings.bind:value`;
|
||
* `min`/`max`/`step` default to 0/100/1 at runtime when omitted.
|
||
*/
|
||
export type SliderNode = { width: SizingBehavior | null, height: SizingBehavior | null, min: number | null, max: number | null, step: number | null, value: NumberOrExpression | null, fill: Array<PenFill> | null, stroke: PenStroke | null, effects: Array<PenEffect> | null, cornerRadius: CornerRadius | null, states: WidgetStates | null, state: { [key in string]?: StateEntry } | null, bindings: { [key in string]?: Expression } | null, events: EventHandlers | null, lifecycle: NodeLifecycleHooks | null, semantics: SemanticsMeta | null, gestures: GestureOverrides | null, route: NavigationRoute | null, id: string, name: string | null, role: string | null, explain: string | null, x: number | null, y: number | null, rotation: number | null, opacity: NumberOrExpression | null, enabled: BoolOrExpression | null, visible: boolean | null, locked: boolean | null, flipX: boolean | null, flipY: boolean | null, theme: { [key in string]?: string } | null, };
|
||
|
||
export type SolidFillBody = { color: string, explain: string | null, opacity: number | null, blendMode: BlendMode | null, };
|
||
|
||
export type SplashConfig = { background: string | null, image: string | null, text: string | null, minDurationMs: number | null, };
|
||
|
||
export type StateEntry = { type: StateType, default: JsonValue | null, description: string | null, persist: boolean | null, };
|
||
|
||
/**
|
||
* Recursive state type description.
|
||
*/
|
||
export type StateType = PrimitiveType | { oneOf: Array<StateType>, } | { array: StateType, } | { object: { [key in string]?: StateType }, };
|
||
|
||
export type StrokeAlign = "inside" | "center" | "outside";
|
||
|
||
export type StrokeCap = "none" | "round" | "square";
|
||
|
||
export type StrokeJoin = "miter" | "bevel" | "round";
|
||
|
||
export type StrokeThickness = number | [number, number, number, number] | SidedThickness;
|
||
|
||
export type StyleOverride = { fill: Array<PenFill> | null, stroke: PenStroke | null, effects: Array<PenEffect> | null, opacity: number | null, };
|
||
|
||
export type StyledTextSegment = { text: string, fontFamily: string | null, fontSize: number | null, fontWeight: number | null, fontStyle: FontStyleKind | null, fill: string | null, underline: boolean | null, strikethrough: boolean | null, href: string | null, };
|
||
|
||
/**
|
||
* On/off toggle. `checked` two-way binds via `bindings.bind:value`.
|
||
*/
|
||
export type SwitchNode = { width: SizingBehavior | null, height: SizingBehavior | null, checked: BoolOrExpression | null, fill: Array<PenFill> | null, stroke: PenStroke | null, effects: Array<PenEffect> | null, cornerRadius: CornerRadius | null, states: WidgetStates | null, state: { [key in string]?: StateEntry } | null, bindings: { [key in string]?: Expression } | null, events: EventHandlers | null, lifecycle: NodeLifecycleHooks | null, semantics: SemanticsMeta | null, gestures: GestureOverrides | null, route: NavigationRoute | null, id: string, name: string | null, role: string | null, explain: string | null, x: number | null, y: number | null, rotation: number | null, opacity: NumberOrExpression | null, enabled: BoolOrExpression | null, visible: boolean | null, locked: boolean | null, flipX: boolean | null, flipY: boolean | null, theme: { [key in string]?: string } | null, };
|
||
|
||
/**
|
||
* Tabbed panel switcher. Unlike the leaf widgets this is a CONTAINER:
|
||
* `children[i]` is the panel for `tabs[i]`. The active tab `value`
|
||
* two-way binds via `bindings.bind:value`; only the active panel is
|
||
* painted at runtime.
|
||
*/
|
||
export type TabsNode = { width: SizingBehavior | null, height: SizingBehavior | null,
|
||
/**
|
||
* Tab bar entries; `value` keys the active tab, `label` is shown.
|
||
*/
|
||
tabs: Array<SelectOption> | null,
|
||
/**
|
||
* Currently active tab `value`.
|
||
*/
|
||
value: string | null,
|
||
/**
|
||
* Panel subtrees, one per tab (parallel to `tabs` by index).
|
||
*/
|
||
children: Array<PenNode> | null, fill: Array<PenFill> | null, stroke: PenStroke | null, effects: Array<PenEffect> | null, cornerRadius: CornerRadius | null, states: WidgetStates | null, state: { [key in string]?: StateEntry } | null, bindings: { [key in string]?: Expression } | null, events: EventHandlers | null, lifecycle: NodeLifecycleHooks | null, semantics: SemanticsMeta | null, gestures: GestureOverrides | null, route: NavigationRoute | null, id: string, name: string | null, role: string | null, explain: string | null, x: number | null, y: number | null, rotation: number | null, opacity: NumberOrExpression | null, enabled: BoolOrExpression | null, visible: boolean | null, locked: boolean | null, flipX: boolean | null, flipY: boolean | null, theme: { [key in string]?: string } | null, };
|
||
|
||
export type TextAlign = "left" | "center" | "right" | "justify";
|
||
|
||
export type TextAlignVertical = "top" | "middle" | "bottom";
|
||
|
||
/**
|
||
* Multi-line writable text input. Like `text_input` but wraps and
|
||
* scrolls vertically; two-way binds via `bindings.bind:value`.
|
||
*/
|
||
export type TextAreaNode = { width: SizingBehavior | null, height: SizingBehavior | null,
|
||
/**
|
||
* Placeholder shown when `value` is empty.
|
||
*/
|
||
placeholder: string | null,
|
||
/**
|
||
* Initial value. Two-way binding lives on `bindings.bind:value`.
|
||
*/
|
||
value: string | null,
|
||
/**
|
||
* Lucide glyph drawn at the left content edge. See `TextInputNode`.
|
||
*/
|
||
leadingIcon: string | null,
|
||
/**
|
||
* Lucide glyph drawn at the right content edge. See `TextInputNode`.
|
||
*/
|
||
trailingIcon: string | null,
|
||
/**
|
||
* Visible-line window before the content scrolls (chat-style).
|
||
*/
|
||
maxVisibleLines: number | null, fill: Array<PenFill> | null, stroke: PenStroke | null, effects: Array<PenEffect> | null, cornerRadius: CornerRadius | null, states: WidgetStates | null, state: { [key in string]?: StateEntry } | null, bindings: { [key in string]?: Expression } | null, events: EventHandlers | null, lifecycle: NodeLifecycleHooks | null, semantics: SemanticsMeta | null, gestures: GestureOverrides | null, route: NavigationRoute | null, id: string, name: string | null, role: string | null, explain: string | null, x: number | null, y: number | null, rotation: number | null, opacity: NumberOrExpression | null, enabled: BoolOrExpression | null, visible: boolean | null, locked: boolean | null, flipX: boolean | null, flipY: boolean | null, theme: { [key in string]?: string } | null, };
|
||
|
||
export type TextContent = string | Array<StyledTextSegment>;
|
||
|
||
export type TextGrowth = "auto" | "fixed-width" | "fixed-width-height";
|
||
|
||
/**
|
||
* Single-line text input. Forms / counters need a writable input
|
||
* surface that two-way binds via `bindings.bind:value`. The walker
|
||
* renders a styled rectangle + caret placeholder; full IME and
|
||
* selection-painter wiring lands in the desktop host (Plan 8) once
|
||
* the gesture arena gains `Focus` recognizers.
|
||
*/
|
||
export type TextInputNode = { width: SizingBehavior | null, height: SizingBehavior | null,
|
||
/**
|
||
* Placeholder shown when `value` is empty. Static text — author
|
||
* `bindings.placeholder` if it needs to react to state.
|
||
*/
|
||
placeholder: string | null,
|
||
/**
|
||
* Initial value. Two-way binding lives on `bindings.bind:value`,
|
||
* which derive lifts into a `set_*` action and the runtime keeps
|
||
* in sync with the state graph.
|
||
*/
|
||
value: string | null,
|
||
/**
|
||
* Lucide glyph drawn at the left content edge (e.g. `mail`, `lock`).
|
||
* The painter insets the text/caret past it so the whole box stays
|
||
* one interactive node. `None` = no leading icon.
|
||
*/
|
||
leadingIcon: string | null,
|
||
/**
|
||
* Lucide glyph drawn at the right content edge (e.g. `eye` for a
|
||
* password reveal). Decorative in Phase 1 (no toggle behaviour).
|
||
*/
|
||
trailingIcon: string | null, fill: Array<PenFill> | null, stroke: PenStroke | null, effects: Array<PenEffect> | null, cornerRadius: CornerRadius | null, states: WidgetStates | null, state: { [key in string]?: StateEntry } | null, bindings: { [key in string]?: Expression } | null, events: EventHandlers | null, lifecycle: NodeLifecycleHooks | null, semantics: SemanticsMeta | null, gestures: GestureOverrides | null, route: NavigationRoute | null, id: string, name: string | null, role: string | null, explain: string | null, x: number | null, y: number | null, rotation: number | null, opacity: NumberOrExpression | null, enabled: BoolOrExpression | null, visible: boolean | null, locked: boolean | null, flipX: boolean | null, flipY: boolean | null, theme: { [key in string]?: string } | null, };
|
||
|
||
export type TextNode = { width: SizingBehavior | null, height: SizingBehavior | null, content: TextContent, fontFamily: string | null, fontSize: number | null, fontWeight: FontWeight | null, fontStyle: FontStyleKind | null, letterSpacing: number | null, lineHeight: number | null, textAlign: TextAlign | null, textAlignVertical: TextAlignVertical | null, textGrowth: TextGrowth | null, underline: boolean | null, strikethrough: boolean | null, fill: Array<PenFill> | null, effects: Array<PenEffect> | null, state: { [key in string]?: StateEntry } | null, bindings: { [key in string]?: Expression } | null, events: EventHandlers | null, lifecycle: NodeLifecycleHooks | null, semantics: SemanticsMeta | null, gestures: GestureOverrides | null, route: NavigationRoute | null, id: string, name: string | null, role: string | null, explain: string | null, x: number | null, y: number | null, rotation: number | null, opacity: NumberOrExpression | null, enabled: BoolOrExpression | null, visible: boolean | null, locked: boolean | null, flipX: boolean | null, flipY: boolean | null, theme: { [key in string]?: string } | null, };
|
||
|
||
export type ThemedValue = { value: VariableScalar, theme: { [key in string]?: string } | null, };
|
||
|
||
export type Transition = "push" | "fade" | "modal" | "none";
|
||
|
||
export type VariableDefinition = { type: VariableKind, value: VariableValue, };
|
||
|
||
export type VariableKind = "color" | "number" | "boolean" | "string";
|
||
|
||
export type VariableScalar = boolean | number | string;
|
||
|
||
export type VariableValue = VariableScalar | Array<ThemedValue>;
|
||
|
||
/**
|
||
* Authored overrides for the four auto-derived interaction states.
|
||
*/
|
||
export type WidgetStates = { hover: StyleOverride | null, pressed: StyleOverride | null, focused: StyleOverride | null, disabled: StyleOverride | null, };
|