/** * Render-tree assembly for the A2UI renderer — the bridge between the pure-TS * processor (`a2ui-validate.ts`, plain Maps) and the recursive Svelte dispatcher * (`A2UINode.svelte`). Pure TS, no Svelte, so `A2UINode` and `A2UIView` share * one typed contract (the house pattern of `md-context.ts`, deliberately not a * Svelte context — the tree is private and explicit prop flow keeps it * inspectable). * * `buildRenderTree` walks the component graph from `root` into a fully-expanded, * BOUNDED node tree: child/children references resolved, list templates expanded * over the data model, dangling references marked, and the depth/node/cycle * limits enforced in ONE place so the Svelte layer can never recurse without * bound regardless of payload. Graph-level ISSUES (the reportable faults) are * still produced by `collectGraphIssues` in `a2ui-validate.ts`; this walk only * shapes what renders. */ import type { Component, Snippet } from 'svelte'; import type { IconComponent } from '../../../icons/index.js'; import type { MarkdownUrlPolicy } from '../markdown/types.js'; import type { A2uiActionEvent, A2uiValidationIssue } from './a2ui.types.js'; import type { A2uiComponentInstance, A2uiSurfaceState } from './a2ui-validate.js'; /** Default traversal bounds — mirror `collectGraphIssues` so the tree and the issue list agree. */ export declare const A2UI_MAX_DEPTH = 32; export declare const A2UI_MAX_NODES = 512; /** * One node of the assembled render tree. `instance === null` marks a dangling * reference (a child id that was never defined) — rendered as a streaming * placeholder or, once settled, a fault chip. `children` are pre-expanded and * ordered; `key` is stable across incremental rebuilds so keyed `{#each}` keeps * component identity (and input focus) through a keystroke-triggered rebuild. */ export interface A2uiRenderNode { key: string; id: string; instance: A2uiComponentInstance | null; /** Template-item scope for relative-path resolution and two-way write-back. */ scopePrefix: string | undefined; children: A2uiRenderNode[]; /** flex-grow, present only when this node is a direct Row/Column child with a finite `weight`. */ weight?: number; /** * The parent prop this child came from (e.g. Card `header`/`footer`/`child`). * Lets a dispatcher with several named childId slots pick the right child by * name instead of position; single-slot / list components ignore it. */ slot?: string; } /** Static text labels threaded to the node dispatcher (i18n comes from A2UIView props). */ export interface A2uiRenderLabels { unsupported: string; blockedImage: string; pending: string; } /** * The single context object each `A2UINode` receives. Carries resolved slot * classes, the icon map, the data-binding resolver, the two-way write-back * callbacks, the action sink and rendering flags. Rebuilt per version bump so a * live data-model edit propagates fresh resolved values without remounting. */ export interface A2uiRenderContext { /** Resolved class string per slot (tv() + slotClasses + unstyled already applied). */ classes: Readonly>; urlPolicy: MarkdownUrlPolicy | undefined; /** Dangling refs render placeholders while true; fault chips once false. */ streaming: boolean; /** True inside a Button label (Text renders as an inline plain span). */ inline: boolean; surfaceId: string; onAction: ((event: A2uiActionEvent) => void) | undefined; /** * The data model to attach to a dispatched action, or `undefined` when the * surface did not ask for it (`createSurface.sendDataModel`). Read as a * function so the action carries the state at CLICK time, not at render time. */ actionDataModel: (() => unknown) | undefined; /** Resolve a Dynamic value (literal | { path } | function-call) against the model in `scope`. */ resolve: (value: unknown, scopePrefix: string | undefined) => { value: unknown; issue?: A2uiValidationIssue; }; /** Write a value at an absolute JSON Pointer into the surface model, then bump. */ write: (pointer: string, value: unknown) => void; /** Delete the key at an absolute JSON Pointer from the surface model, then bump. */ remove: (pointer: string) => void; /** A2UI icon-name → resolved icon component (IconProvider override honoured, direct import fallback). */ icons: Readonly>; /** Fallback glyph for an unmapped icon name. */ fallbackIcon: IconComponent; labels: A2uiRenderLabels; /** * The catalog's recursive node dispatcher (Basic → `A2UINode`; a custom * catalog → its own dispatcher). A2UIView's `renderNode` snippet renders * THIS rather than importing one dispatcher directly, so each surface renders * through the component that matches its catalog. */ nodeComponent: Component; } /** * Props of a catalog node dispatcher. Every dispatcher (Basic/Urbicon/…) takes * the same triple: the node, its render context, and the self-referencing * `renderChild` snippet A2UIView threads down for bounded recursion. */ export interface A2uiNodeProps { node: A2uiRenderNode; context: A2uiRenderContext; renderChild: Snippet<[A2uiRenderNode, A2uiRenderContext]>; } /** * Resolve a `{ path }` data binding to the ABSOLUTE JSON Pointer it addresses, * honouring the template scope for relative paths. Returns `undefined` for * literals and function-call bindings (nothing to write back into). */ export declare function bindingPointer(value: unknown, scopePrefix: string | undefined): string | undefined; /** Coerce an unknown model value to a string array of stable option values (for ChoicePicker). */ export declare function toStringArray(value: unknown): string[]; /** Coerce a scalar model value to the string an `` displays. */ export declare function toInputString(value: unknown): string; /** * Assemble the bounded render tree for a surface, starting at `root`. Returns * `null` when no `root` component exists yet (the caller renders a placeholder * while streaming, or a fault chip once settled). */ export declare function buildRenderTree(surface: A2uiSurfaceState, options?: { maxDepth?: number; maxNodes?: number; }): A2uiRenderNode | null;