# `` — schema-driven runtime walker _(no submodules)_ ## Purpose `` is the runtime that turns a validated `ControlSurface` document into a live React DOM tree. It performs a depth-first traversal of the schema, renders every `Control` node via the appropriate atomic component for its `kind`, and lays out every `Group` node via one of the three grouping primitives (`` / `` / ``) chosen by the group's `layout.kind`. Every control's `onChange` is automatically wired to `bindings.dispatch(control.binding, value)` so the consumer never plumbs handlers manually. This is the entry point for every consumer of the package. The atomic components are exported separately for cases where the consumer wants to compose a static UI, but the typical pattern is to author the surface as JSON, validate it with `@suaveplan/control-surface-schema`, populate a `BindingRegistry`, and let `` do the rest. ## Features - DFS-walk via `walkSurface` from `@suaveplan/control-surface-schema` - 18 atomic controls dispatched by `kind` - Three grouping primitives chosen by `layout.kind` (`grid` → ``, `stack` → ``, `absolute` / `free3d` → ``) - `bindings.dispatch` invoked on every control change - Unbound controls render but emit nothing (the no-op handler is a closure, not a per-render allocation) - Unknown control kinds throw `REACT_CONTROL_SURFACE.UNKNOWN_KIND` - `buildSurfaceTree(schema, bindings)` exported for custom shells ## Usage: Basic ```tsx import { Surface, BindingRegistry, } from "@suaveplan/react-control-surface"; import drumKitSurface from "./drum-kit.surface.json"; const registry = new BindingRegistry(); registry.register("pluginParam", ({ pluginId, paramPath, value }) => { engine.setParameter(pluginId, paramPath, value); }); ; ``` ## Usage: Advanced ```tsx import { buildSurfaceTree, BindingRegistry, } from "@suaveplan/react-control-surface"; function SkinnedSurface({ schema, registry, skinId }: Props) { const tree = useMemo( () => buildSurfaceTree(schema, registry), [schema, registry] ); return ( }>{tree} ); } ``` ## API Reference | Prop | Type | Description | | ---------- | ----------------- | ---------------------------------------------------- | | `schema` | `ControlSurface` | Pre-validated surface document. | | `bindings` | `BindingRegistry` | Registry whose `dispatch` is wired to each control. | The exported `buildSurfaceTree(schema, bindings)` returns the rendered `ReactNode` without the `` memoisation wrapper — use it when you need to mount the tree under a custom provider tree without an extra render layer. ## Implementation Notes The walker runs `walkSurface` once to collect every node in pre-order DFS, then iterates the collection in reverse to render leaves first and bubble each rendered element up into the parent group's `children` prop. This single-pass approach is `O(n)` and avoids recursive React re-renders during traversal — every component is constructed once and React reconciles them all in a single commit. Each control's `onChange` is wrapped in a closure that calls `bindings.dispatch(binding, value)`. When `binding` is `undefined` we return a no-op closure so the control still receives a stable handler identity (avoiding spurious re-renders inside the atomic components). Unknown layout kinds and control kinds fall through to TypeScript's exhaustiveness guard and throw `REACT_CONTROL_SURFACE.UNKNOWN_KIND` with the offending value embedded in the message.