UI Builder
The builder keeps the design document independent of rendered Svelte components. A BuilderSpec is plain serialisable data; a block registry maps each node type to metadata and an implementation; a reactive store owns mutation history and messages; and a recursive renderer turns the node forest into the preview.
The relay protocol remains version 1 with the same ten public members. Its only relay-visible changes in this refinement are additive block metadata and the larger 55-entry catalog; all 22 original block keys remain registered.
Source boundaries
| Boundary | Repository surface | Contract |
|---|---|---|
| Data model | src/lib/builder/spec.ts | versioned node tree, chat and activity state, serialisable block metadata, and mutation vocabulary |
| Internal block contract | src/lib/builder/block-def.ts | BlockProps plus internal BlockDef, including optional createDefaults() factories that never cross the relay boundary |
| Prop normalisation | src/lib/builder/props.ts | single prop-normalisation boundary for preview and export, including the one shared unsafe-URL policy used by LinkButton, LinkPreview, and Navigation Menu |
| Date/time values | src/lib/builder/date-values.ts | ISO-only persisted date/time conversion boundary between BuilderSpec strings and @internationalized/date values |
| Contributed definitions | src/lib/builder/blocks/defs/*.ts | grouped disclosure, overlay, menu, form, date, and utility definitions composed by the registry |
| Block registry | src/lib/builder/blocks.ts | 55 addable block types = 46 showcase mappings + 9 builder-only definitions; composition validation, fresh defaults, registry ordering, and detached relay metadata |
| State | src/lib/builder/store.svelte.ts | apply, select, undo, redo, persistence, messages, activity, and export |
| Renderer | src/lib/builder/Render.svelte | recursive node rendering, editable mouse/keyboard selection, and unknown-type fallback |
| Field editor | src/lib/builder-ui/FieldEditor.svelte | all eleven field kinds, with ISO temporal values, plain-object record arrays, and filtered node references |
| Agent drawer | src/lib/builder-ui/ChatPanel.svelte | always-mounted Conversation and Activity panels, draft/send behavior, status display, and pinned-scroll-aware logs |
| Layers drawer | src/lib/builder-ui/LayersPanel.svelte | spec-derived flattened tree, local disclosures, selection, and sibling reordering through existing operations |
| Relay | src/lib/builder/relay.ts | unchanged version 1 window.bitsBuilder surface, cloned state boundaries, and lifecycle markers |
| Export coordinator | src/lib/builder/export.ts | preview-aligned Svelte projection, legacy dispatch, and grouped emitter dispatch |
| Source emitters | src/lib/builder/emit/context.ts and src/lib/builder/emit/*.ts | SourceContext spec/node lookup, Bits UI/date/icon import sets, top-level declarations, collision-free identifier allocation, and per-group source emitters |
| Persistence | src/routes/api/builder/session/+server.ts | validated GET and bounded POST of { spec, messages } |
| Workbench | src/routes/builder and src/routes/relay | canvas-first editor surface and relay-oriented workbench |
The model contract is detailed in Spec model. The concrete block shape is in Block registry. Browser-agent integration is in Relay protocol.
Canvas-first IDE
/builder uses canvas-first adaptive drawers. A 3rem activity rail switches one left drawer between Agent and Layers; the flexible canvas remains the only growing middle region; and the right drawer contains the complete Inspector plus the editable JSON contract. A full-width, two-row bottom dock spans beneath the workbench and carries all 55 add controls: 46 showcase mappings in showcase order, then nine builder-only definitions.
Both drawers start closed. Their single-mounted DOM nodes collapse through size or visibility classes plus inert, rather than component teardown. ChatPanel and LayersPanel also remain mounted while the active left view changes, and the Agent Conversation and Activity panels remain mounted behind their tab state. Chat drafts, tab and scroll positions, the JSON draft, and layer disclosures therefore survive drawer and view switches.
Drawer visibility, the active left view, and layer disclosures are ephemeral UI state. They never enter localStorage, BuilderSpec, undo history, or relay state.
Editable JSON contract
The right drawer’s JSON tab edits the complete current specification. Apply parses the draft, then submits the existing set-spec operation through BuilderStore.apply; a successful replacement therefore uses the normal validation path, clears selection, enters undo history, schedules persistence, and records observable activity.
A parse or schema failure retains the draft and leaves the specification, history, and selection unchanged. Once edited, the draft survives tab and drawer switches. If the canvas changes after editing begins, the tab reports that the draft is stale and that Apply will replace the current specification; Reset discards it and loads current state.
Layers contract
LayersPanel.svelte derives a flattened presentation from builder.spec.root on every update; it does not cache a second document tree. Each row carries its node, parent id, pre-removal sibling index and count, and depth. Row activation submits the existing select operation.
Sibling reordering submits the existing move operation. For a row at index i, Move up sends i - 1; Move down sends i + 2 because the store computes the destination against the pre-removal sibling list. Disclosure state remains component-local and resets on specification replacement.
State flow
- Scalar, structured, temporal, editable-JSON, Layers, or relay controls submit one or more
BuilderOpvalues. BuilderStore.applyvalidates and applies the batch, returning{ ok, errors }.- Valid operations apply in order. An error does not roll back earlier mutations in the batch. If at least one operation changes the spec, one pre-batch spec/selection snapshot enters undo history; a selection-only batch does not.
- New nodes receive deep-cloned defaults from
createBlockProps(), including materialisedcreateDefaults()values. Render.svelterecursively resolves eachtypethrough the block registry.- The UI and
window.bitsBuildershare the same store rather than mutate parallel documents. save()andload()exchange the spec and messages through the builder session endpoint.exportCode()creates aSourceContext, dispatches legacy and per-group emitters, collects Bits UI, date, and icon imports plus top-level declarations, allocates collision-free identifiers, and renders the finished Svelte source.
Trust boundary
The relay is an in-browser integration point for an OMP agent operating through the browser relay. The application dependency set contains no server-side LLM SDK, and the relay contract does not accept or store an LLM provider key. This is a transport choice, not a claim that arbitrary browser scripts are trusted: an agent must still inspect applyOps failures before reporting success.
Verification state
- Current source gate:
npm run checkcompleted with 0svelte-checkerrors and 0 warnings. - Dated relay, persistence, validation, history, canvas, export, responsive, and deployment receipts are recorded in private page (sign-in required).
loca.zone · bits.loca.zone · Svelte · Bits UI