Commit 655e8c0
fix(spec): the form option-value refusal and the options describe name the derive path for enum members that cannot be spelled (#19906)
Fixes #19678
Fixes #19907
Clause-②: no
Executes ruling comment `5805845085` on #19907 (batch #218 item 3,
letter 乙, maintainer 「其他同意」). It narrows item 1 of ruling `5793380467`
on #19678 (batch #217 item 5, letter 不动 + 声明), which the first round of
this PR executed to the letter:
> 1. The rule as recorded on `FormFieldSchema.options`' describe and in
`defineForm`'s refusal: an enum-typed metadata-form row MAY carry an
inline `options` list (human labels, a deliberate subset); a row whose
members cannot be spelled as option values (a hyphen, a capital) OMITS
`options`, the control derives the members from the served JSON Schema,
and meanings go in `helpText`. The refusal names that path as the
remedy.
> 2. The 27 existing rows stay; #19331's labels stay.
> 3. PR #19906 lands with its describe and remedy sentence narrowed to
that wording.
From ruling `5793380467`, the parts 乙 does not narrow still hold:
`FormSelectOptionSchema.value` keeps the system-identifier bound, and
`newTab` vs `new-tab` stays a recorded boundary, untouched here. No
value bound, no schema shape, no key and no export moves.
## What changed
- **The describe.** `FormFieldSchema.options`
(`packages/spec/src/ui/view.zod.ts`, the `FormFieldBaseSchema` row)
keeps its per-option `default` sentence and now adds: *On a metadata
form (schema-bound, built by `defineForm`), an enum-typed row may list
its members here, to give them human labels or to offer a deliberate
subset. An option `value` is a lowercase system identifier, so a row
whose members cannot be spelled as option values (a hyphen, a capital)
omits `options`: the control derives the members from the served JSON
Schema, and their meanings go in `helpText`.* The TSDoc above the row
says the same thing and names both rulings.
- **The wall.** `defineForm` calls `FormViewSchema.safeParse`. When the
parse fails, it throws a `ZodRealError` built from the parse's own
issues. That is the class `FormViewSchema.parse` threw before this PR:
an `Error` whose `name` is `ZodError`. Its stack is captured at the
`defineForm` call, so an uncaught module-load throw prints the issues,
the remedy and the author's call site (round 3, below). Only one thing
changes in the issues: a grammar refusal (`invalid_format` or
`too_small`) at an inline option's `value` (path ending
`options.INDEX.value`, also when nested inside the field-row union's
`errors`) keeps its message and gets this sentence after it: *An enum
member carrying a hyphen, a capital or a single character cannot be a
form option `value`, which is a lowercase system identifier. When this
row edits a spec enum whose members cannot be spelled as option values,
omit `options`: the control derives the members from the served JSON
Schema, and their meanings go in `helpText`.* No issue is added, removed
or re-coded.
- **Generated:** `content/docs/references/ui/view.mdx`, regenerated by
`pnpm --filter @objectstack/spec gen:docs` after a spec build. Two table
rows changed (the `options` row of the two FormField tables).
`check:generated` then reported all 15 artifacts up to date.
- **Changeset:** `.changeset/19678-form-option-enum-derive-remedy.md`,
`@objectstack/spec: patch`, rewritten to state ruling 乙's rule.
### Round 2 (ruling 乙): what moved from the first round
- The describe no longer says *a row whose key is a spec enum omits
`options`*. It now permits an inline list on an enum-typed row and
scopes the derive path to a row whose members cannot be spelled.
- The remedy no longer says *When this row edits a spec enum, omit
`options`*. It now conditions the same derive path on members that
cannot be spelled as option values.
- The verdict did not move. The same values are refused and the same
values are accepted as on the first round's head `ebd7fc2fa8`.
- The branch is merged with `origin/main` at `c8399867b8` (merge commit
`61ff3aebe6`, through `scripts/pm/os-regen-merge.sh`). The wording
commit is `182ed4c154` and the regeneration commit is `74ea5dbba3`.
### Round 3 (at-tier record `5818584341`: FAIL): the refusal is an
`Error` with a stack again
- **What the record found.** Round 2 threw `new z.ZodError(…)`. In zod
v4 classic (`zod@4.6.1` here) that constructor has no `Error` parent, so
the thrown object was not an `Error` and had no `stack`. An uncaught
module-load throw printed only `ZodError { name: 'ZodError', message:
[Getter/Setter] }`. That hid the issues and the remedy, the very wall
both rulings require. `refusal()` asserted only
`toBeInstanceOf(z.ZodError)`, and both shapes pass that.
- **The fix** (`76a053e9d0`). `defineForm` now throws `new
z.ZodRealError(withOptionValueDeriveRemedy(parsed.error.issues))` and
captures its trace with `z.core.util.captureStackTrace(refusal,
defineForm)`. `ZodRealError` is the class `FormViewSchema.parse` threw
before this PR: its `name` is `ZodError`, it is an `Error`, and it
passes `instanceof z.ZodError`. The walker is unchanged. It returns
copies, it grows only `invalid_format` and `too_small` at a
`…options.INDEX.value` path, it still walks `invalid_union`, and an
unrelated refusal is still answered without the remedy. The verdict did
not move.
- **Why this route, and not either spelling in the record as written.**
Both were measured on `zod@4.6.1`, each thrown uncaught from a scratch
form module that parses with the source `FormViewSchema` (run by `tsx`).
1. **Neither spelling has a frame.** zod builds every `ZodRealError`
with `Error.stackTraceLimit = 0` (`newError` in `zod/v4/core/core.js`).
It captures a trace only inside `parse` (`util.captureStackTrace(e,
callee)`), and `safeParse` never does. So `throw parsed.error` and a
bare `throw new z.ZodRealError(…)` both print the issues as `[ZodError:
…]`, with 0 `at` frames and no source line. Read directly in `node`:
`new z.ZodError([])` is not an `Error` and its `stack` is `undefined`,
`new z.ZodRealError([])` and a `safeParse` error are `Error`s whose
stacks hold 0 frames, and the error `parse` throws holds 8. That `stack`
is still a string, so the record's two assertions pass on both
spellings. The fix captures the trace the way zod's own `parse` does.
The callee is `defineForm`, so the first frame is the author's
`defineForm(…)` call.
2. **A copy, not a mutation in place.** A mutation in place would not
leak into another caller. Two parses of the same input share 0 issue
objects, because zod's `finalizeIssue` builds each issue fresh and
`lazySchema` caches the schema, never a result. A mutation of the first
parse's issues showed up 0 times in the second parse. The hazard is
order. zod 4.6.1 computes an error's `message` on its first read and
caches it (`_zod.message`), and V8 formats the stack header on the first
read of `.stack`. So a message grown in place reaches the printout only
if nothing read `.message` or `.stack` before the mutation. Measured on
`FormViewSchema.parse`'s own error, which has its frames. Grown with no
earlier read, the remedy is in the issues, the `message` and the `stack`
once each, and the uncaught printout carries it. After one earlier read
of `.message`, the issues still carry it once, but the `message`, the
`stack` and the printout carry it 0 times. An error built from issues
that already carry the remedy does not depend on that order.
- **The wall, proved with a real uncaught throw.** A scratch form
module, shaped like `packages/spec/src/**/*.form.ts`, imports
`defineForm` from the built `packages/spec/dist/ui/index.mjs` and calls
it at module scope with `{ field: 'openIn', options: [{ label: 'New
tab', value: 'new-tab' }] }`. A second module imports it, and nothing
catches. Both ran under `node` 22.22.2, and stderr was captured:
| read on stderr | the fix (`76a053e9d0`, `dist` built) | negative
control: round 2's `new z.ZodError(…)` line, `dist` rebuilt |
|:--|:--|:--|
| node exit | 1 | 1 |
| what it printed | `ZodError: [` followed by the issue list as JSON |
`ZodError { name: 'ZodError', message: [Getter/Setter] }` and nothing
else |
| the issue path (`sections.0.fields.0`, then `options.0.value` in the
union's branch) | present | absent |
| the grammar message (`System identifier must be lowercase…`) | 1 | 0 |
| the remedy sentence (omit `options`, the members come from the served
JSON Schema) | 1 | 0 |
| the remedy's scope (`cannot be spelled as option values`) | 1 | 0 |
| stack frames | 4. The first is `action-behavior.form.mjs:5:35`, and
node's caret points at `defineForm({` in that module | 0 |
For the negative control, `view.zod.ts` was byte-identical to round 2's
blob `d6471f538d06`, and `ablation-dist-preflight` found the old line in
11 built files. After the restore the `dist` was rebuilt. The old line
is absent from all 216 built files, the tree is clean, and the fix's
wall reads the same as before (stderr sha256 `7d118336d597` both times).
- **The pin.** On every refusal, `refusal()` now asserts
`toBeInstanceOf(Error)` and a string `stack`, the record's two. It also
asserts that the stack names this test file, the module that called
`defineForm`. The third assertion is the one that tells a trace-less
`ZodRealError` apart. A new case reads the wall itself: the head of the
stack (`ZodError: ` and the message) carries today's grammar message,
read live off the object face and JSON-escaped, and the derive path with
its scope.
**Ablation, round 3.** One-shot, at `76a053e9d0`, through
`scripts/ablation-replace.mjs` under the verify lock, one leg at a time.
The test imports `./view.zod` as source, so no `dist` is in its path.
| leg | mutation | anchor | blob | result |
|:--|:--|:--|:--|:--|
| 1 | the throw put back to round 2's `throw new
z.ZodError(withOptionValueDeriveRemedy(parsed.error.issues));` | x1 → x0
| `e2feed0106e6` → `d6471f538d06` (round 2's blob, byte for byte) |
`Tests 19 failed \| 14 passed (33)`, every one at
`expect(thrown).toBeInstanceOf(Error)` |
| 2 | only the trace capture deleted: a `ZodRealError` with no frame,
the shape both spellings in the record give | x1 → x0 | `e2feed0106e6` →
`11a3b9cfae81` | `Tests 19 failed \| 14 passed (33)`, every one at *the
stack names no frame in the module that called defineForm*. The record's
two assertions passed on this shape |
The 19 red cases are the ones that go through `refusal()`. The 14 green
ones build a form, parse a schema or read the describe, and never reach
`refusal()`. Both legs were restored: after each, the blob was
`e2feed0106e6`, equal to HEAD, `git diff HEAD` was empty, and `git
status --porcelain` read 0 lines.
- **The changeset is not reworded.** Its sentence "`defineForm` still
throws a `ZodError` at module load with the same issues and codes" is
literally true at this head. The thrown object is a `ZodRealError`, the
class `FormViewSchema.parse` threw before this PR. Its issues are the
parse's own, copied, with the same codes, and only the matching messages
grow.
- **No base merge.** `origin/main` moved 23 commits past the round-2
merge base `c8399867b8`, to `e8f163fc3a`. None of them touches this PR's
four paths, `identifiers.zod.ts` or `field.zod.ts` (`git diff
--name-only`: 0 hits). Derived on a probe tree at `e8f163fc3a` with this
PR's four files, the gate list is the same 107 commands as in this
worktree (the two sorted lists do not differ). No generated artifact
moved: `check:generated` reports all 15 generated artifacts up to date
at `76a053e9d0`, and `view.mdx` is unchanged from round 2, so nothing
was regenerated.
### Where the refusal lives (found by content), and why the remedy is
attached at `defineForm`
- **The text** is `SystemIdentifierSchema`'s regex message, declared in
`packages/spec/src/shared/identifiers.zod.ts` (lines 104 and 107 on the
first round's base). It reaches the form face through
`SelectOptionSchema.value` (`data/field.zod.ts`).
`FormSelectOptionSchema` reuses that value **by reference**, and the
`property schemas are shared BY REFERENCE` pin in
`form-select-option.test.ts` holds it there.
- **The thrower at module load** is `defineForm` (`ui/view.zod.ts`). On
the base it threw through `FormViewSchema.parse`; since the first round
it runs `safeParse` and throws the refusal itself. All 17
`packages/spec/src/**/*.form.ts` modules call it at module scope.
- **The remedy cannot go where the text is declared.** The same grammar
also bounds object-field options (`Field.select.options`) and three
object-storage names. For those, "omit `options`, derive from the served
JSON Schema" is the wrong advice. A form-face-only message would need a
second `value` schema, and that breaks the by-reference derivation the
ruling cites. A zod error map on a parent object cannot rewrite the
issue either, because the regex check's own `error` resolves first.
`defineForm` is the one door where the remedy is true: it stamps
`data.provider: 'schema'` on every form it builds. So the sentence is
appended there, and only there.
## Measured first, on `origin/main` @ `dabf8d795e` (first round)
1. **Today's refusal** for the card's own example, `defineForm({
schemaId: 'action', type: 'simple', sections: [{ label: 'X', fields: [{
field: 'openIn', options: [{ label: 'New tab', value: 'new-tab' }] }] }]
})`: a `ZodError` from `defineForm`, with one `invalid_union` issue at
`sections.0.fields.0`. Its object branch carries `{ code:
'invalid_format', format: 'regex', pattern: '/^[a-z][a-z0-9_.]*$/',
path: ['options', 0, 'value'] }` with this message, verbatim:
`System identifier must be lowercase, starting with a letter, and may
contain letters, numbers, underscores, or dots (e.g., "user_profile" or
"order.created")`
`perRecord` and `system-data` gave the same issue shape and the same
text. A one-character value gives `too_small` with `System identifier
must be at least 2 characters`.
2. **The describe authors read** (`view.zod.ts:3235` on that base):
`Options for select/multiselect/radio/checkboxes fields (per-option
\`default\` is not accepted here — declare the pre-selected choice on
the object definition)`. It does not name a JSON Schema, `helpText` or
omitting `options`.
3. **Census of hand-listed enum members**: see Acceptance notes. None of
the 27 rows is broken by this change, and under ruling 乙 every one of
them is the permitted shape.
## Tests
`packages/spec/src/ui/form-option-enum-derive.test.ts` (33 tests). Its
assertions name subjects (omitting `options`, the JSON Schema,
`helpText`, members that cannot be spelled) rather than whole sentences.
- **The thrown class, and the printed wall (round 3).** Every refusal
the file reads goes through `refusal()`, which asserts a `z.ZodError`,
an `Error`, a string `stack`, and a stack that names this test file, the
module that called `defineForm`. A new case reads the head of the stack,
which is what an uncaught throw prints: `ZodError: `, today's grammar
message JSON-escaped, and the derive path with its scope.
- **Refusal.** The refusal for `new-tab`, `perRecord`, `system-data`
(`invalid_format`) and `x` (`too_small`) names the derive path and
scopes it to members that cannot be spelled. The grammar message is kept
verbatim ahead of the remedy, read live off the object face. A nested
row (composite `fields`) gets the same remedy.
- **Firing controls for the predicates.** Both predicates are RED on
today's message: the object face raises the grammar issue through the
very property schema the form face shares, with no remedy. The
blanket-rule predicate is LIT on the two spellings the first round
shipped, so its "states no blanket rule" assertions cannot be vacuous.
- **Ruling 乙 item 1, on real spec enums.** Each case has a firing and a
dark control. Every enum is read off the served JSON Schema
(`z.toJSONSchema(getMetadataTypeSchema(type))`, input side), so
"unspellable", "spellable" and "subset" are measured, not assumed.
- `object.managedBy` (members that cannot be spelled): with inline
`options` it is REFUSED. Every unspellable member is refused with the
remedy, and no spellable one is. The same row without `options`,
meanings in `helpText`, is GREEN.
- `object.sharingModel` with a labelled full list (the #19331 shape):
GREEN, labels kept. The same list with one member re-spelled with a
hyphen is REFUSED at that member.
- `field.deleteBehavior` master_detail subset (`cascade`, `restrict`, no
`set_null`): GREEN, not widened. The lit precondition shows `set_null`
is a served member. The same subset with one member capitalised is
REFUSED at that member.
- **The verdict did not move.** The same values are refused, `new_tab`
is still accepted, and a spellable inline option still builds.
- **The remedy is scoped.** An unknown key on the option, and an
unrelated refusal on the same form, are both answered without it.
- **The describe states ruling 乙's rule** in the served JSON Schema
(`z.toJSONSchema(FormFieldSchema)`). It permits an inline list (human
labels, a deliberate subset). It names the derive path, scoped to
members that cannot be spelled. It no longer states the blanket rule. It
keeps the per-option `default` sentence.
**Old-wording pins, reversed rather than deleted.** A `git grep` for the
old describe, the old remedy and the old ruling's 「never hand-listed」
found one assertion pinning the old wording: the describe test's
`toContain('spec enum')`. It became the assertions above: the permission
and the scoped derive path are present, and the blanket rule is absent.
The file header's restatement of the old rule is rewritten to ruling 乙.
The other 「never hand-listed」 hits in the repository (nine test and
source comments) describe unrelated derived vocabularies. `../objectui`
has no hit for either old sentence.
**Ablation, round 2** (one-shot, at `74ea5dbba3`, through
`scripts/ablation-replace.mjs` under the verify lock, one leg at a time,
with the old wording put back). The test imports `./view.zod` as source,
so no `dist` is in the path.
| leg | mutation | anchor | blob | result |
|:--|:--|:--|:--|:--|
| 1 | remedy constant back to *When this row edits a spec enum, omit
`options`* | x1 → x0 | `d6471f538d06` → `c91e601551c2` | `Tests 6 failed
\| 26 passed (32)`: the four scoped-remedy cases, the nested row, the
`managedBy` FIRING case |
| 2 | describe back to *a row whose key is a spec enum omits `options`*
| x1 → x0 | `d6471f538d06` → `1e03c6756624` | `Tests 3 failed \| 29
passed (32)`: the permission, scoped-derive and no-blanket-rule describe
cases |
Both legs went red in the expected direction. Both restored: blob after
restore `d6471f538d06` == HEAD, and `git diff HEAD` was empty. The first
round's ablation, at `2aa26de218`, removed the remedy altogether (`throw
parsed.error;`) and gave `Tests 9 failed | 7 passed (16)`, which showed
the remedy itself is load-bearing.
Suite runs, all at `76a053e9d0` (the PR head):
| run | result |
|:--|:--|
| `@objectstack/spec` `vitest run --project local` | `Test Files 532
passed (532)` · `Tests 15688 passed \| 2 todo (15690)` |
| `@objectstack/spec` `vitest run --project repo` | `Test Files 35
passed (35)` · `Tests 602 passed (602)` |
| `@objectstack/spec` `typecheck` (tsc + scripts + test layer) | exit 0.
The test file is in `tsconfig.test.json`'s program, and `view.zod.ts` in
`tsconfig.json`'s (`--listFilesOnly`: 1 hit each) |
| `@objectstack/spec` `check:generated` | `All 15 generated artifacts
are up to date`, against a `dist` built at this head |
| `@objectstack/spec` `check:docs` | `225 generated files in sync with
packages/spec` |
| eslint, narrowed to the two changed `.ts` files | `--no-inline-config
--format json`: 2 files, 0 errors, 0 warnings. Both are in eslint's
population (`--print-config` resolves a config for each). The config is
not type-aware (no `parserOptions.project` / `projectService`), so this
diff cannot move a verdict on an untouched file. The changeset and
`view.mdx` resolve no eslint config |
**The regenerated page against `main`.** Against the merged `main` tip
`c8399867b8`, `view.mdx` differs in exactly the two `options` rows. The
six PRs that last moved that page on `main` are `95fb417ec8`,
`48c91e9e46`, `9dcdb775a0`, `2b52a5b013`, `b01bdbc4d9` and `1ff3a8f210`.
Every line they added that is still on `main`, 51 in all, was grepped
quoted-exact (`git grep -F -c`). Each has the same count on `c8399867b8`
as on this branch, with 0 mismatches. In round 3 neither side moved the
page: `git diff --quiet` exits 0 for `view.mdx` from `c8399867b8` to
`origin/main` `e8f163fc3a`, and from `74ea5dbba3` to `76a053e9d0`.
**Gates:** `node scripts/pm/dispatch-gates.mjs --repo
objectstack-ai/objectstack --commands` at `76a053e9d0` derived 107
commands. The count matches round 2's 107 at `74ea5dbba3`, and the list
is identical to the one derived on a probe tree at `origin/main`
`e8f163fc3a` with this PR's four files. The `--ran` reconciliation
reports `107 derived famil(ies) accounted for — 107 run, 0
NOT-MEASURED`. All 107 exited 0 on the first run. Their prerequisites
were built before it: a spec build, then a turbo build of every package
except docs (`73 successful, 73 total`). In round 2, seven of them first
exited 3 and went green once those prerequisites were built:
- `check:doc-formula-expressions`, `check:doc-security-posture`,
`check:docs-transcript-drift`: after the `@objectstack/lint...` closure.
- `check:lean-entry-closure`: after the `@objectstack/objectql...`
closure.
- `check:skill-examples`, `check:dual-build-cjs-loads`,
`check:type-check-debt`: after a turbo build of every package except
docs (73 tasks).
**The `Test Core (1/6)` walker race.** On the first round's head, `Test
Core (1/6)` was red on `scripts/check-error-status-conformance.mjs`'s
`walk()`: an ENOENT from a transient `tsup.config.bundled_*.mjs` (the
open finding #19667; #19916 is closed). This base merge re-measured it.
On `74ea5dbba3` every check run completed `success`, including `Test
Core (1/6)` and all seven required contexts. That script is not edited
here.
## Changeset: `patch`
Runtime text in a released package changes. The `defineForm` refusal
ships in `@objectstack/spec`'s `dist`, and the describe is served in the
JSON Schema. That is a released-package change, so there is a changeset.
Round 3 changes no word of it: the thrown class is `ZodRealError` again,
so its sentence "`defineForm` still throws a `ZodError` at module load
with the same issues and codes" is literally true. It is `Clause-②: no`:
every value accepted or refused before is accepted or refused now, and
nothing an author can write is added or removed. So it takes the
checklist's `patch`, not `minor`.
## Sibling PRs
- #19861's region (`checkViewFilterRuleValueShape` /
`ViewFilterRuleSchema`) has landed on `main` and came in with the base
merge. It merged without a conflict, and this PR does not touch it.
- #19809 is the one open PR that also edits `view.zod.ts` and
`view.mdx`. That was re-derived from the file lists of all 34 open PRs
on 2026-09-24. Its regions (`PaginationConfigSchema`, the per-kind
Gallery / Timeline / Kanban / AddRecord configs, `rowLimitKey`, the
`CalendarConfig` type exports) do not overlap the
`FormFieldBaseSchema.options` row or `defineForm`. Its `view.mdx` hunks
do not touch the two `options` rows. If the two collide, the page is
regenerated, never hand-merged.
## Acceptance notes
- **Census: 27 inline `options` rows in 9 of the 17
`packages/spec/src/**/*.form.ts` modules** (`git grep` at `74ea5dbba3`:
object 12, field 3, hook 3, action 3, page 2, and agent, skill,
permission and email_template 1 each). The first round's census grouped
them as 11 of 17 metadata forms. This round did not re-derive that
grouping. Each row's key was resolved in the served JSON Schema at
`dabf8d795e`.
- All 27 keys are spec enums. None lists a non-member. **None contains
an unspellable member.** So under ruling 乙 every row is the permitted
shape, and item 2 keeps all of them. None is converted.
- Lit control: the same instrument, run on the three option-less
reference rows, reports the unspellable members it should:
`object.managedBy` (4: `system-data`, `engine-owned`, `append-only`,
`better-auth`), `action.execution` (`perRecord`) and `action.openIn`
(`new-tab`).
- **24 rows list every member with human labels**: object
`fields.valueDomain`, `fields.deleteBehavior` (lookup row),
`fields.returnType`, `fields.summaryOperations.function`, `ownership`,
`sharingModel`, `editMode`, `lifecycle.class`,
`lifecycle.storage.strategy`, `lifecycle.storage.unit`; field
`returnType`, `summaryOperations.function`; hook `body.language`,
`onError`, `runAs`; action `mode`, `body.language`, `operation`; page
`type`, `interfaceConfig.recordAction`; agent `surface`; skill
`surface`; permission `managedBy`; email_template `category`.
- **3 rows are deliberate subsets**: object `fields.type` omits `secret`
and `user`, and the two master_detail `deleteBehavior` rows (object
`fields.deleteBehavior`, field `deleteBehavior`) omit `set_null`.
- The #19331 comment in `object.form.ts` ("Each enum gets an explicit
`options` list because the bare member reads as a word…") and the served
describe now agree. The first round's contradiction between them is what
#19907 decided.
- **Boundary:** a schema-bound form view authored outside `defineForm`
(a stack's `view` metadata with `data: { provider: 'schema' }`, parsed
at compose or publish) still gets the bare grammar message. The ruling
names the module-load refusal. The object-field option face is unchanged
by design.
---
_Generated by [Claude Code](https://claude.ai/code)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 5581d30 commit 655e8c0
4 files changed
Lines changed: 533 additions & 4 deletions
File tree
- .changeset
- content/docs/references/ui
- packages/spec/src/ui
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
181 | 181 | | |
182 | 182 | | |
183 | 183 | | |
184 | | - | |
| 184 | + | |
185 | 185 | | |
186 | 186 | | |
187 | 187 | | |
| |||
346 | 346 | | |
347 | 347 | | |
348 | 348 | | |
349 | | - | |
| 349 | + | |
350 | 350 | | |
351 | 351 | | |
352 | 352 | | |
| |||
0 commit comments