Repository navigation
Conversation
Identity, contract, behaviour guidelines, models and examples, variants as renders, collections and code targets. Theming beyond modes is recorded as a future task. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A .uidx file may carry ## Contract, ## Behavior, ## Models and ## Examples after the visual contract, and a <Styles> table beside the root. They are lowered into doc.spec as declarations, never scene nodes; the tree the viewer draws is unchanged. <Repeat slot count> multiplies one instance for the design tools. The emitter prints the regions back verbatim so uidx fmt keeps them. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…system audit, uidx contract
A component with a <Styles> table is drawn as the variant set the table
derives, one synthetic tree per combination, unlinked from the file. A
contract prop with a model resolves {item.field} to the model's samples, and
a <Repeat> multiplies its instance at successive sample indexes. The audit
checks parts, slots, rows, axes, models, bindings and repeats against each
other, and uidx check runs it. uidx contract prints the spec as JSON.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…system over @hwc/components @uidx/codegen renders each component identity to code (ADR 0017): a stylesheet keyed by the headless root's attributes and states, a markup fragment with sample content, a typed React component with props and events from the contract — a plain slot as a ReactNode prop, a repeating slot as items plus renderItem generic over the model, text parts as compound sub-components, compositions as the instance they hold — plus models.ts, runtime.ts, elements.d.ts and the contract as JSON. With a custom-elements.json manifest, each contract is checked against its element. `uidx codegen` drives it; `--check` fails on drift. examples/design-system carries Checkbox, Field, CheckboxField, Button, ContactOption and ContactList as .uidx identities over the vendored @hwc/components build, with generated/ committed and tested for drift, a Vite page mounting the HTML fragments beside the React components, and the React output type-checked against React 19. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…gh props
Canvas fixes found by rendering the example end to end:
- A whole-attribute stroke alias (`strokes="{border#default}"`) skipped the
stroke composition, so the stroke kept its colour and lost its weight and
drew as nothing. Both paths now share `wholePaintAlias`.
- The derived component set no longer carries the component's own paint and
layout; those live on each variant's root (ADR 0005 §5).
- A `<Slot>` that says nothing about its size hugs its placeholder instead of
the engine's 100x100 box.
- `<Prop sample>`: a demonstration value for the canvas and generated markup
(ADR 0013 amended); a text bound to a prop with neither sample nor default
renders empty.
- An instance's `props={{ label: '{label}' }}` resolves in the consuming
component's scope before entering the definition's, and a component
declared by its contract alone accepts passed values. The HTML target does
the same for compositions.
- `layoutAlign` maps to `align-self` in the CSS target.
Example: hug sizing declared on the hugging components, per-variant hover
rows for the button, samples on text props, light background for the demo.
Agent's insertable-element test now lists `Repeat`.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ess library The inspector gains a Contract tab beside Design (ADR 0013 §3 amended). Figma's precedent: bindings live in the properties panel, declared on the component and applied from the layer, with choices from a curated list. - A component chooses the element it implements from the library's roots; a text field only when the document names no library. - Each declared part is a row: bound rows name their layer and select it, unbound rows are a picker over the layers that could draw it. Binding from a new layer unbinds the old one in the same patch. A layer's own row offers the same parts, naming who holds the taken ones. - A <Repeat> picks a declared repeating slot (reselecting itself at its new address) and its count. Slots show whether the tree provides them. - The file's contract (props, events) is shown beside the bindings; the tab badge counts parts still to bind. uidx.json gains "headless": the library's custom-elements.json, served on /__uidx/headless and read on every document:opened; uidx codegen uses it when --manifest is absent. Roots and parts follow the <root>-<part> naming convention (plus cssParts), with a leaf test so hwc-text-input stays a root beside hwc-text. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The manifest's own vocabulary for parts is `cssParts`: parts inside the root's shadow tree, styled from outside through `::part()`. Until now every part had to be a `<root>-<part>` element, which is one library's convention. - The component model records each part's kind. A shadow part is styled as `root::part(name)` in the CSS target, emits no element in HTML or React, and gets no compound sub-component. - Conformance accepts either kind. A shadow part with design content under it warns rather than fails: the library draws the part, so that content is styled but not rendered. - The Contract tab marks shadow parts with a pill on the component's list and in the layer's picker, so an author knows it can be styled, not filled. ADR 0017 §3 records the rule. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The conformance warning for design content under a shadow part is gone. The library drawing the part, and the design drawing its own for the canvas and Figma, is how a shadow-part library works; a warning nobody can act on is noise. Conformance accepts a cssParts entry exactly as it accepts an element. ADR 0017 §3 reworded. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The contract API, simplified to what each renderer actually reads:
- `<States structural styling>` is gone. A boolean prop marked `visual` is a
state; `hover`, `focus` and `active` are the browser's and need no
declaration; `<States><State name>` remains only for states the element
produces itself, rendered as `:state(name)`. The state axis is default,
the visual booleans in prop order, the interaction states in table order,
then the declared ones.
- `<Parts>` is optional and written like every other list, `<Part name>`
with a description. The tree's `part=` bindings are the declaration.
- A model is named by a prop's type — `Contact`, `Contact[]` — and shared
across pages: declared once, on the page of the component that shows it.
`model="{models#…}"` is gone; a repeating slot names the list prop it
iterates, `of="items"`, so the model is written once. The example no
longer declares Contact twice.
- The parser names each old spelling and says what replaced it.
The contract-only helpers (axes, state kinds) move to @uidx/format so the
server's symbol lint can read them: `componentProps` now includes contract
props, a `{label}` passed through an instance is not judged at the use, a
`{item.name}` binding resolves through the model prop, and a derived set
draws every combination. `uidx check` passes on the whole example for the
first time; the audit runs after every page is parsed with a shared model
index. The Contract tab marks visual booleans as states and lists declared
element states.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The identity never spells a library. This makes that true end to end: - uidx.json's `headless` may be an object: `manifest`, a `profile` (how the library reflects props, its own states and parts — attributes, data attributes, classes, `::part()` or `data-part`) and `bindings` (its tag, part, event and attribute names per component). The code target and conformance read both, so the checkbox renders as `sl-checkbox`, `sl-checkbox[checked]::part(control)` and `sl-change` from the same file that renders over @hwc/components. Defaults are the example's conventions. - When nothing is named, the server discovers libraries the project's dependencies ship (the `customElements` field of package.json) and the Contract tab offers them, or any path; the choice is written into uidx.json through the headless route. The tab says which tag a binding renders a component as. - `uidx codegen` passes the profile and bindings through from uidx.json. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…b that edits the contract Phase 3 — a state is designed the Figma way. Derived variants are addressable, so selecting the hover checkbox on the canvas shows its root frame in the inspector with a "state=hover of Checkbox" chip, and a change to it becomes a cell of the `state="hover"` row through the new `style` patch op (format), rather than a patch to a node with no source. An edit to the default combination lands on the base tree; a position, a name or structure is refused with the reason. Undo inverts the cell. The canvas' own scene-write path resolves against the derived document too. The toolbar gains a Repeat tool that wraps the selected instance in a `<Repeat>` on the first open repeating slot; the patcher leaves a Repeat unnamed, as it does a Variant. Phase 4 — the Contract tab edits the contract. The new `contract` patch op writes one `<Prop>`, `<Event>`, `<Slot>`, `<State>` or `<Part>` in canonical form, creating its list and the `## Contract` region when absent and removing a list its last item leaves; it inverts to the previous declaration. In the tab, each declaration opens into a form (description, type, default, sample, visual and controllable; a slot's repeats, `of` and `accepts`), a row adds one, and "Fill from library" declares what the implemented element exposes and the contract lacks, with the manifest's descriptions or placeholders the tab marks. The viewer's library model carries the manifest's types and descriptions for that. Docs: ADR 0013 §3, ADR 0016 §4, ADR 0017 §2, a design-system walkthrough in the README, the example README, the changelog. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ate button uidx.json gains an optional `codegen` — `out` (relative to it) and `targets` — that `uidx codegen` reads when `--out` is absent and that the server's /__uidx/codegen route runs for the Contract tab's Generate button: the same generator over the same pages with the same library, so the panel and the command line write the same files. A page that does not parse or a contract the library contradicts stops the run before anything is written. The example names ../generated and the button reproduces its 36 committed files byte for byte. Issue #37 and the PR description now record everything delivered. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
CI typechecks before it builds, and the example's tsconfig did not carry the base config's `customConditions: ["development"]`, so `@uidx/format` and friends resolved to a dist that did not exist yet. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…sories Advisories published after main's last green run; every fix is a patch release inside the existing ranges. The release audit passes again. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`repeat="{items}"` on any layer of a component draws it once per item of a
list — a list prop of the contract, or a list field of an enclosing item
(`{person.tags}`), which nested makes a tree. `as` names the item, `count`
the canvas's rows (the model's samples decide otherwise). A repeat on a
`<Slot>` is the consumer-fillable one: a render prop named after the slot in
React, with the placeholder as the default. `<Repeat>` and the contract
slot's `repeats`/`of` are gone; `accepts` stays.
The canvas draws the first row as the layer itself and the rows after it as
generated echoes; a repeat edit rebuilds the scene. The scene and the
Contract tab resolve a list's model across pages, as `uidx check` does, and
the audit tells a shared model from no list at all. A base layer of a
derived component selects its default-state twin on the canvas, and the
default state on the canvas is the base layer in the inspector.
The viewer's Contract tab shows Over / As / Count on any part, slot or
instance; the Repeat tool writes the attribute in place. Example, ADRs
0013/0017, changelog and READMEs follow.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A Models face beside Tokens and Fonts gathers every model of the document with the page that declares it and the components that receive it, and edits it in place — description, and a table of fields with type, key, optional, sample and words. A new model goes on the page the author picks, a page called `models` offered as the shared one; models a contract names and nobody declares are listed and declared in one click. Two patch ops carry the writes: `model` and `field` reprint one `<Model>` canonically, create the region before `## Examples` or at the end, remove it with the last model, and invert — a removed model comes back with its fields. The model index the canvas draws from is rebuilt only when some page's models change, and the canvas redraws its repeats when it does, so a sample changed on the face redraws every row that shows it, wherever the model lives. A repeat's row in the Contract tab links to its model on the face. Closes #38. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`count` only overrode how many sample rows the canvas drew; code never
read it. The repeat API is now `repeat="{list}"` and, when the item needs a
name, `as`. The canvas draws one row per sample of the model — its longest
sample list, three when it has none — so the rows follow the model and
there is nothing to keep in step with it.
Parser, scene, HTML target, Contract tab, example, tests and docs follow.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
An instance inside a repeat shows what each contract prop of its definition
receives (ADR 0017 §2): the enclosing item whose model matches the prop's
type, nearest first, else the innermost item for the first model prop —
the rule the code target follows — and the use may say otherwise with
`props={{ node: '{child}' }}`, an item or a field of one, which the code
target now passes as written.
From the walkthrough that built a Tree over a TreeNode model: a Bind picker
beside a text's content offers the enclosing items' fields, a model prop's
fields and the component's props, and a `{…}` typed there is written as the
binding it is rather than resolved away; a slot the tree has and the
contract lacks is listed with Declare; a repeat on a container holding one
row offers to move onto the row; Make component warns when the page already
has a component; a new slot hugs; placing an instance with a slot selected
fills it; the picker leaves out the enclosing component; an emptied
`## Contract` heading goes; `uidx codegen` takes directories.
Fixed: the incremental patch path rebuilt a component without its spec after
a structural edit, so the Contract tab lost the lists until a restart; and
the symbol checker reported `repeat="{items}"` and `{item.field}` as unknown
component properties, in the shipped example too. A CLI test now checks the
example.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A visual prop draws its state set as soon as it is declared, rows or no
rows, so the first style row is written by selecting the state on the
canvas rather than by hand (ADR 0016 §4). Rotation is a look a state may
set — a chevron turns when a row opens — and the CSS target already renders
it as a transform. A use's bound value chooses the state on the canvas
(`props={{ open: '{item.expanded}' }}` inside a repeat), resolved in the
consumer's scope and mapped onto the state axis, so rows repeat open or
closed as their samples say; the code target passed this already.
The canvas now receives the shared model index it was meant to — the
binding had landed on the layers rail — so a list's model on another page
draws its samples on the canvas, not only in thumbnails. A text bound to a
boolean or a number is spelled out rather than crashing the text layout.
Pen-drawn vectors stay stroked in both code targets. The Repeat tool offers
the enclosing item's lists first and names a nested item after its list
(`child` for `{item.children}`). A sized component's layout reads Free, so
choosing Column writes it. An instance's bound prop is no longer called
mistyped.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A page that cannot be drawn keeps its last frame and says why in a
banner instead of going blank. A number field commits on Enter (by key,
so the numpad's too). Declaring a prop offers its type, and a boolean
comes with default={false}. A checkbox, a number and the visibility pill
bind to an enclosing item's field or a prop from a picker beside the
control, the way a text already did; any {…} committed from the
inspector is written as the binding it is rather than as a value.
The visibility pill's picker previously listed only declared BOOLEAN
props, so a tree's children frame had no way to follow item.expanded
from the inspector.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ebflow, Figma and Penpot Text is typed where it is drawn: the Text tool opens the new node for typing as soon as the file echoes it, a double-click on a text reopens it, and Escape or ⌘Enter keeps the words as one characters write. A right-click selects what is under the pointer and opens the toolbar's actions with their shortcuts, disabled where the selection cannot do them. The status strip says "Saving…" while edits are in flight and "All changes saved" after. An empty page says how to start. The "Inside …" breadcrumb names the container by its current name rather than the id it was entered by. The Contract tab leads with properties and slots; the code binding and the code library fold under "optional · for developers". The overview counts pages "with content" and "problems" instead of "drawable" and "parse"; the file-error overlay leads with plain words and keeps the diagnostic beneath; the Models face explains itself without jargon. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A token referenced only from a <Styles> row showed 0 uses, a rename rewrote only its declaration, and `uidx check` stayed green while codegen emitted an undefined CSS variable, so a state's look broke silently. The dependents index, the refactor plans (a `style` patch per cell) and the unresolved-reference check now read doc.spec.styles beside the tree. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W7AN4RSMLMWKACuggBMYEH
…d ops
The agent edit surface (uidx apply, MCP uidx_apply, eval) could only
edit the scene tree, and the skills `init` installs taught the model ADR
0016 replaced: props={{…}} and one <Variant> tree per combination. Now:
- ops set_style, declare, set_model and set_field compile to the format's
style, contract, model and field patches, gated like the rest; eval
gains style/declare/undeclare/model/field builders;
- uidx-authoring teaches identities (contract, <Styles>, models,
repeat, behaviour), eval-api shows a contract-first example, and a
new uidx-design-system skill gives the order of work;
- UIDX153 warns when <Variant> trees share one anatomy and differ only
in values, pointing at a styles table (ADR 0016 §5).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W7AN4RSMLMWKACuggBMYEH
…code ContactList's "shown instead of the options" lived only in Behavior prose, so the canvas drew "No contacts yet" under the rows and the generated React rendered it unconditionally. `empty` is now a built-in state of a component with a list prop, joining the state axis once a <Style> row names it (as hover does). The canvas draws it with no rows for the component's own lists; CSS keys it on [data-empty], which the React wrapper sets from its lists. A state row that targets a <Slot> now selects [data-slot] and keeps display: contents. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W7AN4RSMLMWKACuggBMYEH
The viewer showed none of a component's prose: the intent, ## Behavior and ## Examples were parsed and never drawn. The Docs face (beside Elements, ?view=docs) is the component's documentation page read from its identity: status and headless element, the intent, examples drawn by the canvas's own renderer as one-instance pages (a root state or a prop value per <Set>; counts and per-row sets are listed as not drawn), properties, events, slots, parts and states, accessibility, form and composition, behaviour rules with anchors, and every page that uses it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W7AN4RSMLMWKACuggBMYEH
ADR 0015 §3 says an example is exactly a Storybook story; nothing emitted one. `"stories"` (with `react`) writes <Component>.stories.tsx beside each React wrapper: the default story with every prop given its sample, its default or its model's sample rows; one story per value of each visual prop; and one per ## Examples scene the args can express (a visual boolean state, a prop value, a count trimming a repeating slot's list). Verified to type-check against @storybook/react 9 and React 19. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W7AN4RSMLMWKACuggBMYEH
A component can now get an enum prop ("choice": two or more values,
visual, defaulting to the first) and turn on hover, focus, pressed and
empty states from the tab; before, no UI could write the first row that
makes an interaction state exist, so hover could not be designed.
The style op takes an empty target and prop to address a whole row:
written with its cells ({} seeds an empty state row) or removed with all
of them, and inverted either way.
Fixes: a prop's Default or Sample typed as false no longer deletes it
(only visual and controllable are on/off flags), and clearing a
description puts the placeholder back instead of being ignored.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W7AN4RSMLMWKACuggBMYEH
…s, new files What a user could not do from the editor before, and now can: - choose a new token's type (Color, Number, Text, Toggle); a new collection could only ever hold numbers - add, rename and remove a collection's modes, so dark mode can be made in the editor; values start from an existing mode and node overrides follow a rename (schema setCollectionModes) - rename a collection, with every reference and override repointed (schema renameCollection); a new collection offers its name at once - set a collection's tier - edit a token's description and the pickers it is offered in (scopes) - create a component identity (with a starter contract) or a tokens file from the New file dialog, and a tokens file from the empty Tokens tab Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W7AN4RSMLMWKACuggBMYEH
…e, Go to main component - An instance offers a picker for a contract prop typed as a union of words, and refuses any other value; before it was a free text field. - A component's status is a draft/stable/deprecated picker in the inspector header instead of a read-only chip. - The canvas menu gains Duplicate (also Cmd/Ctrl+D; the copy goes after the original under a free name, beside it when the canvas places it), Rename (the layers rail's inline rename) and Go to main component (opens the page that declares an instance's component and selects it). The component an identity file describes is not duplicated, since a copy would have no contract. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W7AN4RSMLMWKACuggBMYEH
The prose half of an identity had no write path: Behavior, Examples and
the intent could only be edited in the file. Two format ops add one:
- region {name: Behavior | Examples, body?} writes a region whole, in
canonical order when it is new, and removes it without a body;
- intent {text} rewrites the prose above the visual contract.
Both are checked by the parser like any write and invert exactly.
The Docs face edits them as drafts saved as one op (one undo step):
rules as id + sentence rows, examples as a name plus property rows with
a picker for choice props, a toggle for booleans and the state axis
offered; mistakes are named in the editor before anything is sent. Empty
sections now invite their first entry instead of hiding.
Fix: behaviour rule text kept its backticks only in the file; the parser
dropped them, so the Docs page never showed code and a re-save would have
lost them. It now reads the rule from the source.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W7AN4RSMLMWKACuggBMYEH
<Accessibility>, <Form> and <Composes> were shown in the Docs face and nowhere editable. A contract-element op writes, replaces or removes each one in ## Contract, in ADR 0013's order (so undo puts it back where it was), creating the region when needed; it inverts exactly. The Contract tab gains an "Accessibility and forms" section: role (with the common ARIA roles offered), keyboard, accessible name, whether the component takes part in forms and what it submits, and the components it is built from. Emptying every field of an element removes it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W7AN4RSMLMWKACuggBMYEH
Pages could only be created. The server's pages route now also renames a page (the file moves beside where it was, its frontmatter id follows; nothing refers to a page by file, so no other file changes) and deletes one (never the last). The rail offers both on hover: rename inline, delete after a confirmation that counts the uses of the page's components on other pages, which a delete would break. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W7AN4RSMLMWKACuggBMYEH
A prop could not be renamed; deleting and re-adding lost its place and
broke every reader. The contract op takes a rename (the declaration is
rewritten under the new name in place, and inverts), and a rename plan
carries the {prop} bindings in the component, the style rows keyed by it,
the examples that set it and the props of every instance on every page,
one batch per file.
The Design face no longer offers the legacy props={{}} editor for a
component with a contract: it points to the Contract tab, so there is
one place to declare a property.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W7AN4RSMLMWKACuggBMYEH
Three faults made multi-file edits hard to take back: - Each file of one action (a collection, token or prop rename, a delete that inlines values elsewhere) was its own undo entry. Batches now carry a group and join one entry, so Cmd/Ctrl+Z undoes the action. - A write to a page other than the open one came back from the server looking like somebody else's revision and was recorded twice. The viewer now remembers every batch it recorded until it is confirmed. - The inverse of a rename (set name) named the old address, which no longer resolves once the rename lands, so undoing any rename failed. It now names the node where the rename put it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W7AN4RSMLMWKACuggBMYEH
- Detach instance (menu, Cmd/Ctrl+Alt+B) writes the layers an instance
draws into the page as a plain frame: the variant its props select,
{prop} bindings replaced by the values it showed, overrides applied,
slots filled with what it put in them, token bindings kept.
- Deleting a component that instances still use now says how many and
where, and what to do instead, rather than failing on the server.
- Canvas: a node that keeps its address but changes element (an instance
detached into a frame) now rebuilds; the incremental path kept the old
instance's generated subtree on screen until a reload.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W7AN4RSMLMWKACuggBMYEH
Only SVG could be dropped; a photo or bitmap logo was ignored without a word. The server now takes an upload into the document's first assets folder (declared the moment it lands, identical bytes reused, names kept unique), and the canvas places it as a rectangle with an image fill at its own size, capped at 480px. Anything else dropped is named in a notice instead of ignored. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W7AN4RSMLMWKACuggBMYEH
A slot could only be filled by dragging an existing layer onto its hole on the canvas. The instance panel now lists the component's slots: an empty one offers text or the components its contract accepts, a filled one names its content (click to select it) and can be emptied back to the component's default. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W7AN4RSMLMWKACuggBMYEH
…readers
- The Contract tab lists the styles table by row (base, hover,
tone=danger + focus…) with how many changes each makes; a row opens to
its cells, and a cell or a whole row can be removed.
- A model can be renamed from the Models face: the model op takes a
rename (fields and place kept, inverts), and the plan retypes every
prop and field that names it on every page.
- Renaming a field keeps its place (the field op takes a rename) and
carries the {item.field} / {prop.field} bindings inside the components
that receive the model; it used to move the field to the end and leave
those bindings pointing at nothing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W7AN4RSMLMWKACuggBMYEH
…theme - One top bar on every view: the logo and document › page breadcrumb, the faces (Design, Docs, Tokens, Fonts, Models) as icon tabs, undo and redo, save and connection state, a light/dark switch, Ask AI when the assistant is reachable, and a primary Get code button (explains how to configure codegen.out when it is not set). The old stacked nav and the status footer in the left rail are gone. - The left panel on a page has Layers and Insert tabs. Insert gathers the drawing tools, an image from disk, ready-made blocks (Stack, Row, Card, Button, Input, Heading) that insert into the selected frame or beside existing content, and every component, searchable. - A light theme: the tokens in theme.css redefined, chosen in the bar and remembered per browser, defaulting to the system preference. Rulers, page thumbnails and doc examples follow it. - The overview page is centred and roomier: larger title, cards with rounded corners and shadows, section headings with counts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W7AN4RSMLMWKACuggBMYEH
- Dark is the default again; light stays one click away in the top bar. - Left panel: the pages list is always on top on every face, so moving between files never hides behind a tab; Layers and Assets (blocks, images, components) sit below it. The page name in the top bar opens a menu of every file, with New file…. - Right panel: Design | Contract | Code. Code shows the selected component (or the one an instance uses, or the one a layer sits in) as the code it becomes — React, HTML, CSS, the contract JSON — rendered by a new GET /__uidx/code route in memory and written nowhere, refreshing as the design changes, with Copy and Write to <codegen.out>. - Bottom bar: a + button opens the Insert panel floating above it; it closes on Escape or after inserting. Menus and popovers in the top bar close on Escape or a click outside. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W7AN4RSMLMWKACuggBMYEH
The link from identity to code had a picker for the library and the implements tag, and nothing for the rest of what uidx.json holds. The right panel gains a Connect tab (Design | Contract | Connect | Code): - a status card: Button → <acme-button>, the React component it renders onto, and how many props, events, slots and bound parts it has; - Headless element (saved in the component's .uidx file): the web component or headless element it implements, from the library; - Names in the library (uidx.json headless.bindings): tag, attribute and event names only where the library differs, suggested from the library's own attributes and events; - Existing React component (uidx.json codegen.react): module, export, prop and callback renames, omitted props, what becomes children; - Project (uidx.json): the library (from dependencies or a path), how it spells boolean props, custom states and parts, the output folder and the code targets. Each field says which file it is saved in. A new /__uidx/config route edits only these fields, re-validates the whole manifest and restores the file when a change would make it invalid. The Code tab re-renders whenever the connection changes, so mapping a prop shows up in the code straight away. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W7AN4RSMLMWKACuggBMYEH
examples/shoelace: four identities (Button, Input, Checkbox, Switch) that implement Shoelace's sl-* elements and style them through ::part(), with light/dark tokens, connected in uidx.json through the Connect tab. A demo page mounts the generated HTML beside the generated React components, logs their events, and flips the token mode with the generated Switch. Its test audits every page, checks conformance against Shoelace's manifest, and asserts generated/ is current. What a general-purpose library surfaced, fixed with tests: - adopt: a part and a slot sharing a name (sl-input prefix) no longer clash - profile coverage 'subset' checks only what the contract declares; native DOM events count as dispatched - shadow parts pass slot content through with slot="name"; empty library slots print nothing; parts resolve to the root's own cssParts first - text an element renders from an attribute is written as that attribute (HTML) and typed string (React) - React booleans reach custom elements as true/false (Lit keeps a reflected attribute set to undefined) - focus on a shadow host is :focus-within, which it matches - an outlined vector's stroke is its colour, not a border - tokens.css carries every mode as [data-<collection>="<mode>"] blocks Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W7AN4RSMLMWKACuggBMYEH
Built a Shoelace Switch from an empty component using only the canvas and panels (connect, fill from library, draw, nest, bind parts, design each state on its variant, Get code), then ran the result on real Shoelace. Each fix carries a test. - format: an attribute edit or a re-lowered node now moves the spans of the regions after the tree, so a contract op right after connecting an element no longer splices into the middle of a tag (Fill from library was rejected with UIDX003) - viewer: a click on a panel number now puts the caret in its edit box; typed digits used to reach the layer tree and rename the layer - viewer: Design-tab edits on a component with states land on the default-state twin, for its layers and for the component itself; they were silently dropped when the layer was chosen in the Layers panel - viewer: a token picked for a state variant writes that state's style row instead of doing nothing - codegen: an axis that hugs is not given the width or height measured when it was drawn; per-side stroke weights win over the uniform one Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W7AN4RSMLMWKACuggBMYEH
Fixes everything the designer-style Switch build ran into, then rebuilt the Switch from scratch with the new gestures and ran it on Shoelace. - Frame selection (⌘/ctrl+alt+G, menu, toolbar): wraps sibling layers in a hugging auto-layout frame; shift/ctrl-click gathers rows in the Layers panel - Drawing or dragging onto a component with states nests into the frame under the pointer (the default-state twin stands for its base layer) - A layer drawn or moved into an auto layout carries no x/y - Fill from library opens a checklist: identity names (helpText, change), the usual members ticked, the default slot included, and the library's spellings saved as bindings in uidx.json - Bind by name binds every unbound part to the layer named after it; the Connect tab counts parts as the Contract tab does - The Hug/Fixed control shows what the canvas draws when no mode is written - Letting go of a look on a state clears its cells (weights with strokes) and keeps the state's row; stroke weights on a state write one weight - Token pickers: list keys (arrows, Enter picks the first match), the colour picker's Libraries search takes focus, and collections that fit the property come first (type for a font size, not space) - Shift+Enter / Enter select the parent / children, variants included - The canvas lays the page out again once the bundled fonts load, so a component's variants no longer collapse with clipped labels Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W7AN4RSMLMWKACuggBMYEH
Design a List whose repeated item slot accepts any item component, and
inject PersonRow, PersonCard and PersonChip into List instances without
touching List itself. Fix the gaps the flow exposed:
- Contract: model types (Person, Person[]) in "Type to add" and the
prop type input; undeclared slot message offers "Declare slot".
- Properties: text pills offer item fields ({item.name}); injected
content gets the layout controls of the slot it fills.
- Canvas: repeated slot fills render per-item data for every row, with
only the first copy editable; rebinding a prop to/from the contract
rebuilds the scene instead of leaving it stale; cross-page models
resolve for instance defaults.
- Codegen: wrapperless repeated slots emit row rules on the real
children instead of a dead [data-slot] rule; ellipses get a round
border-radius.
- Insert panel focuses its search field.
Shoelace example gains the list, item components, a Team page and a
"List with injected items" demo section.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W7AN4RSMLMWKACuggBMYEH
Rework the instance inspector around what a designer does with a list:
- Component card: thumbnail, description and a "go to component" button.
- Data group: where each contract prop comes from here - the list's
sample rows, "each item of list-2" for injected content, a receives
picker inside components, or a preview stepper for a lone item.
- Slot cards: say when a slot repeats, show what fills it (Default tag
for the component's own content) and open a content picker with real
component thumbnails, components that receive the row's item first,
then other components, Text, Nothing and Use default. A swap is one
undoable edit; a choice that cannot receive the item is called out.
- Injected content says which instance's slot it fills.
- A selected fill slot offers the same card instead of two buttons.
Fix what the earlier validation left open:
- Empty slots are outlined on the canvas overlay (not in the scene, so
layout and export are unchanged) and clicking one selects it. ADR 0007
amended.
- Item components preview any sample row ("Preview data" stepper), with
the canvas redrawing; editor state only, nothing written.
- "failed to open <0> as a font": the canvas asked for Inter before the
host font loader was installed, and the SDK cached a 404 body as the
face. The loader is now installed before the first await.
- Contract descriptions keep code spans and spacing ("`item`;" read
back as "item ;"), which also fixes the design-system example's docs.
- Real descriptions for the list example's components.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W7AN4RSMLMWKACuggBMYEH
Selecting a slot inside a component now shows one Slot panel, on the Design tab and the Contract tab alike, in the order a designer decides: - Description, or "Declare slot" when the contract lacks it. - Content: "Filled once" or "For each item". For each item picks the list prop (shown with its type, e.g. items · Person[]), or creates a new list property of a chosen model and repeats over it in one edit. Item name, and the model as a chip that opens it, with the canvas's sample row count. - What each filling receives (item: Person, index: number) and the React prop a use fills it through (renderItem?: (item, index) => ...). - Default content chosen from the same picker uses get, with a check that the default receives the item or a warning that it cannot. - Allowed content: "Any component" or the headless element fillings must implement, listing which components fit and flagging a default that does not. Fix: the instance picker treated `accepts` as component names, while ADR 0017 and the audit read it as the element a filling implements, so a slot with `accepts` offered nothing. It now matches `implements`. Also fix stray spaces in the Contract tab's repeat hint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W7AN4RSMLMWKACuggBMYEH
Repeat is per layer (ADR 0017 §2): a frame, text, instance or slot inside
a component can draw itself once per item, and everything inside it is
the template. The Design tab now has a Repeat section for every such
layer, and the Slot panel and Contract tab use the same section:
- Once / For each item. The list picker groups the item's own lists
first ("From the item it is inside": item.members · Person[]) for
nested repeats, then the component's list props; "+ New list
property…" declares a list of a chosen model and repeats over it.
- A first repeat names its item so it hides nothing: `tag` inside a
row of `item`s, `person` in a component whose prop is already `item`.
The toolbar's Repeat tool uses the same rule, now also avoiding props.
- Item name and model (opens the model), with the canvas's row count.
- "Part of group, drawn for each item (Group) of groups": the repeats a
layer already belongs to, each selectable.
- The template: the item's fields a layer inside can bind to, list
fields marked as nestable, and what already reads the item — bound
texts, nested components that receive it, nested repeats — each
selectable. "Repeat the row instead" when a repeat wraps one row.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W7AN4RSMLMWKACuggBMYEH
Repeat comes down to the two choices a designer makes, and nothing is
passed or guessed behind their back:
- Models face: each model has Items (one row per item, one column per
field; add, edit, remove) and Fields (the shape). Items are stored as
each field's sample list, so files, canvas and code are unchanged.
- Repeat section on any layer inside a component: a switch, then a
model picked from a token-style list ("Person · 5 items"). The list
prop is chosen silently: a list of that model the layer can reach, or
a new `people: Person[]` declared in the same edit; the item is named
so it hides no prop or outer item. "Edit items" opens the model. The
slot panel uses the same section; its receives box, render-prop line,
accepts control and inference checks are gone (accepts stays on the
Contract tab).
- Binding: inside a repeat, a component's properties and a text's
content bind to the item or its fields from a bind button, like
tokens ("From the item": item, item.name...). Bound values show as
pills with unbind.
- Explicit only: a nested component gets the row's item only when a
prop is bound to it (`props={{ item: '{item}' }}`). Removed the type
inference from the canvas, codegen and the Contract tab; the canvas
draws an unbound item component with the preview sample. The examples
bind explicitly and generate byte-identical code. ADR 0017 amended.
- `uidx check` accepts a contract prop passed through `props={{…}}`
(it only knew the older props declarations).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W7AN4RSMLMWKACuggBMYEH
- Labels: the panel says "This Person" and "This Person › name" for a
repeat's row and its fields, in the bind popups, the text binding
popup ("From each row") and on bound pills. The file keeps `{item}`
and `{item.name}`; the row's code name stays out of the designer's way.
A component's own prop reads by its name ("person › name").
- Bind on placement, explicitly: a component that takes the row's model
is bound to it when it is placed — from the Insert panel into a
repeated row, from a repeated slot's picker (uses and defaults), or
when a layer holding it starts repeating. Written into the file as
`props={{ person: '{item}' }}` and shown as a removable pill; nothing
is inferred afterwards.
- Example components take `person: Person` instead of `item`, so a use
reads `person ← This Person`; generated React follows
(`<PersonRow person={item} />`, `PersonChip({ person })`).
- Test: a component's text property bound to `{item.name}` draws each
row's own value.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W7AN4RSMLMWKACuggBMYEH
A coach points at the real control to use next and moves on when the document shows the step done, however the designer did it. Tutorials are offered on the Overview (with a first-visit welcome) and from a Learn button in the top bar; a run survives a reload. Fixes found while walking the tours in the editor: - a component gaining its first state redraws live instead of blank - a repeated text is offered its own item's fields to bind - Data rows no longer repeat props Properties already edits - the first field of a model keeps the card on Fields - the coach card steps aside from popups it would cover Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W7AN4RSMLMWKACuggBMYEH
Instances: - An instance that states a width or height is Fixed on that axis; one that states neither follows its component (hug, fixed or fill). An authored sizing mode, stretch or grow on the instance still wins on its own axis. Before, the size was written to the file but the component's hug redrew it at its content size. - A component that wraps one frame (a styles table, or a bare Component around a Frame) hands the instance's size to that frame, so the visible pill resizes with the instance, not just its invisible wrapper. - Same-file edits land where a rebuild would: the update path recomputes the wrapped frame, and a sizing flip or a parent layout change that moves which axis a stretch fills rebuilds. - The canvas drag no longer snaps back. Side handles write only the dimension they move; resizing a stretched or growing instance makes that axis Fixed, as Figma does. Width and Height offer Fixed/Hug for an instance; Hug removes the stated size. The file only ever gets x/y/width/height, never sizing modes on an Instance. Tutorials: - A step done while a popup it opened is still open (the color picker) ticks and says to close it, and moves on only once it closes. - The ring and the scroll follow what actually shows: the target is cut by the window, every scrolling panel and any sticky header over it, so Appearance is scrolled out from under the inspector header instead of ringed behind it. The coach retries while the layout moves it, stops once the designer scrolls by hand, and scrolls again after Back. The ring hides while a popup covers its target. - No step passes on an editor that has not loaded its pages yet, so a reload mid-tour no longer skips steps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The canvas preview only moved the instance's own node. A component with a styles table draws inside a variant wrapper, on a frame that carries the instance's size, and only a build or the file's echo put the size there. So a drag moved the selection box on every pointer move while the visible pill stayed at its old size, sliding with the wrapper, and jumped on pointer-up. - The frame half of reconcile's update-instance-root is now wrappedFrameUpdate, the one rule for what the wrapped frame takes from its instance; instanceDefinition finds the definition the build expands. The echo and the preview both ask them, so they cannot disagree. - The viewer draws that frame for every preview of an instance's size: a drag on any handle, a width or height typed into the panel, a stretch or grow let go of. A pointer cancel puts it back, and a document landing mid-drag is applied to the scene the file drew, then the preview again. - The file is unchanged: still only x/y/width/height on the Instance. - A journey drags a Button instance's handle and checks the drawn pill before pointer-up; it fails with the preview reverted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A placed component is a black box with a styleable outer box, the way an HTML element is styled from outside. An <Instance> may state its outer box's fills, strokes and their weights, alignment and dashes, corner radii, opacity, effects and padding, plus a textFills colour that every text inside takes. The component's own layout, gap, alignment and child layers stay locked. The use sits above the component's resting look and beneath its state rows, so a restyled button still shows its hover. - Schema: a role table (instance-box.ts) says what an instance may state. State rows stamp what they write, so the use layers beneath them. The box lands on the node that draws the look (a wrapped frame, a composition's one instance, on through bare wrappers), the same node a stated size pins. The text colour reaches the nearest use's texts; slot text that states its own fills keeps them. - Incremental updates land where a rebuild would, canvas edits never echo box attributes back into the file, and detach carries the box onto the frame that drew it. - Editor: for an instance the Design panel shows the component's values dimmed, an override dot and reset per row and section, Reset all in one undo step, a Text color row, padding unlocked and the inner layout as a read-only line. Previews draw live through the wrapped-frame path. - Code: resting rules read --uidx-* hooks with the component's values as fallbacks, state rules stay literal, and every root resets the hooks. React hands a held instance its hooks and a stated size through style. Contracts list the box. Example goldens regenerated. - Checks: uidx check warns on a locked attribute (UIDX154) and a box value that does nothing (UIDX155); the agent's edit tools follow the same roles. - ADR 0018 (amending 0012), an ADR index, and the property, panel and authoring docs. - A scene snapshot of every example page guards that only restyled instances change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The three code tabs each opened differently, padded their content twice, and printed raw server errors in full: a missing library file showed an ENOENT message with an absolute path that ran past the pane, red at the top of Connect and amber at the bottom of Contract. They now share the Design tab's grammar and say each problem once, in one place. - Shell: the tab strip always fits, with a small dot only on a tab that has a real problem (unbound parts are progress, not a problem). The identity row stays on one line; read-only is a lock chip there. One subject resolver decides what the three tabs are about, so an inner layer or an instance reads the same on Connect and Code. - Shared parts: InspectorSection (the Design tab's section head, with an (i) for the teaching copy), InspectorStatus (one status line: a short title, a count, details on demand, the raw error behind Details) and InspectorEmpty. inspector-messages.ts words every fault and empty state once, so the tabs cannot disagree. - Ownership: Contract declares and binds; Connect wires the library, names, React component and output folder; Code previews the output and owns Write code. Contract's library footer, chooser and Generate button are gone, with links to where they now live. - Code: the file switcher never wraps, Copy and Write are disabled when there is nothing to copy or write, diagnostics read as "Unknown part “spinner-track”" with the file and line, and a wrap toggle. - Library and config: uidx.json still loads when the library cannot, and the server adds a code and the configured path to its library error (additive; the message is unchanged). - Docs: ADR 0013 and 0017, inspector-design, README and CHANGELOG follow the new ownership. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DTPs41geuetWYjWSKmsBRu
An adversarial review of the shared frame for Contract, Connect and Code (each finding reproduced by a second reader before it was fixed) turned up these, among smaller copy and layout nits: - An (i) on a collapsed section now shows its tip; it used to press and show nothing, hiding the teaching copy on Names, React and Project. - A refused library choice no longer marks the working library as failed on every tab; the error stays under the path field, which stays open with what was typed. - Long component names no longer push section actions, chips or the footnote past the pane, and two tab dots no longer cut every tab label at the narrowest inspector. - Open contract, Bind parts and Show select the component before opening its Code binding. The Code dot follows the code whether or not the tab is open, and a stale "Wrote N files" no longer sits over a render that broke. - Status rows wrap their diagnostic instead of cutting it, and the title keeps priority over the count. - Contract groups its declarations by kind, the slot view and an undeclared slot use the shared row and wording, and a slot fill inside an instance names the slot it fills. - Hints use one grey that passes contrast in the light theme, the Write code footer stays on the pane's bottom edge, and switching tabs starts at the top. - properties-pane.test.ts unmounts every pane after each test; leaked panes re-rendered on each shared-state write and timed tests out. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DTPs41geuetWYjWSKmsBRu
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #37
Closes #38
Summary
A
.uidxfile becomes the identity of a component: intent, anatomy, layout and styles, plus a written contract, behaviour guidelines and view models. Components are renders of that identity — the viewer canvas, HTML/CSS and React here; Figma unchanged. Nothing in the file computes.Documented in six ADRs, implemented end to end, with an example package rendering six identities over the headless
@hwc/componentslibrary, and an inspector that edits all of it.Decisions (docs/decisions)
## Contract: props, events, slots, form, accessibility, composes.implementsbinds a headless root,partbinds its parts.<Prop sample>for demonstration values.<State>only for states the element produces itself;<Part>descriptions optional. The identity never spells a library:uidx.jsonmay carry aprofileand per-componentbindings.## Behavior: short- id: sentencebullets. Nowhen, no code.## Modelsand## Examples: view models declared once, shared across pages, named by a prop's type; every field explained; samples for demonstration only.<Styles>table keyed by visual enum props and states derives the set. Enum props are axes, visual booleans are states,hover/focus/activeneed no declaration. Derived variants are addressable: a state is designed on the canvas and its row is written.repeat="{items}"on any layer of a component, withas, nested for trees; a repeat on a<Slot>is the consumer-fillable one and a render prop in React; slots and render props in React, compositions render the instance they hold, shadow parts through::part(),codegenconfig shared by CLI and viewer.Packages
<Styles>table, therepeat/asattributes, spec types, diagnostics UIDX140–152, thestyleandcontractpatch ops with inverses; contract-only helpers (axes, state kinds).derivedTarget,contractJson,auditDesignSystemwith a shared model index; pass-through instance props resolve in the consumer's scope; slots hug; whole-attribute paint aliases compose strokes.custom-elements.json, shadow parts, library profiles and bindings.uidx.jsongainsheadless(path or{ manifest, profile, bindings }) andcodegen(out,targets); routes serve the library, discover libraries from dependencies'customElements, write the choice, and generate code; the symbol lint reads contract props.modelspage as the shared one), linked from a repeat's row; the newmodelandfieldpatch ops write one<Model>back canonically.props={{ node: '{child}' }}(the code target passes the same); a Bind picker beside a text's content, and a typed{…}written as a binding; undeclared slots listed with Declare; "repeat the row instead" when a repeat wraps one row; Make component warns when the page already has a component; new slots hug; Place instance fills the selected slot; the picker leaves out the enclosing component; an emptied## Contractheading goes;uidx codegentakes directories. Fixed: the incremental patch path dropped a component's spec after structural edits, anduidx checkflaggedrepeat="{items}"as an unknown property (now covered by a CLI test on the example).props={{ open: '{item.expanded}' }}) chooses the row's state on the canvas as it does in code; the canvas now actually receives the shared model index (the binding had landed on the rail); a text bound to a boolean is spelled out instead of crashing the layout; stroked vectors stay stroked in codegen; the Repeat tool prefers the enclosing item's lists and names nested items (child); a sized component reads Free; bound instance props are not called mistyped.default={false}; a checkbox, a number and the visibility pill bind to an enclosing item's field (item.expanded) or a prop from a picker beside the control, as a text already did, and any{…}committed from the inspector is written as the binding it is. Verified live: binding a tree'schildrenframe toitem.expandedfrom the Appearance pill wrotevisible="{item.expanded}"and hid the collapsed row's children on the canvas.uidx checkaudits the whole document;uidx contract;uidx codegenreadsheadlessandcodegenfromuidx.json.@hwc/components, generated output committed with a drift test, a Vite demo page;uidx checkpasses on the whole document.Verification
root:opacityinto its style row; editing a prop's description from the Contract tab rewrote the<Prop>; the Generate button wrote the same 36 files the CLI writes.🤖 Generated with Claude Code