Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 146 additions & 0 deletions .changeset/console-00d3f09c500c.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion .objectui-sha
Original file line number Diff line number Diff line change
@@ -1 +1 @@
67dadd602a3a891666ea1513c5de677140784b6a
00d3f09c500c4a45b5f27aca8af80349412faaf1
6 changes: 3 additions & 3 deletions packages/sdui-parser/objectui-lockstep.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
],
"objectui": {
"repo": "https://github.com/objectstack-ai/objectui.git",
"rev": "67dadd602a3a891666ea1513c5de677140784b6a",
"revDate": "2026-09-01T16:00:05Z",
"rev": "00d3f09c500c4a45b5f27aca8af80349412faaf1",
"revDate": "2026-09-04T04:22:27Z",
"source": "packages/sdui-parser/src",
"files": [
"packages/sdui-parser/src/codegen.ts",
Expand All @@ -20,7 +20,7 @@
"packages/sdui-parser/src/validate.ts"
]
},
"recordedAgainstPin": "67dadd602a3a891666ea1513c5de677140784b6a",
"recordedAgainstPin": "00d3f09c500c4a45b5f27aca8af80349412faaf1",
"grammarRegion": {
"file": "packages/sdui-parser/src/parse.ts",
"delimiter": "/* ---------------------- the JS literal subset (#6614) ---------------------- */",
Expand Down
16 changes: 8 additions & 8 deletions packages/spec/src/data/api-methods-batch-conformance.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,14 @@ const SINGLE_RECORD_WRITE_ONLY: Record<string, string> = {
// `revoked` on ONE key. The multi-select surface this rule protects does not
// exist for API keys, and the shape a future one would take does not need
// `bulk` either — both read off the console build this release pins
// (`.objectui-sha` = `67dadd602`, `packages/plugin-grid`; re-measured at
// that pin, 2026-09-02 — previously measured at `d8ec8d6d4`, before that at
// `9602dc820`, `190fbd01d`, `9a3daf8d3`, originally at `6314e87f2`.
// `ObjectGrid.tsx` DID change across the move off `d8ec8d6d4` (seven
// commits, 461 lines), so both claims below were re-derived rather than
// carried over: `ObjectGrid.tsx:3436-3451`, whose block is byte-identical
// to the one cited at `d8ec8d6d4:3187-3202` and shifted only by insertions
// above it, and
// (`.objectui-sha` = `00d3f09c5`, `packages/plugin-grid`; re-measured at
// that pin, 2026-09-04 — previously measured at `67dadd602`, before that at
// `d8ec8d6d4`, `9602dc820`, `190fbd01d`, `9a3daf8d3`, originally at
// `6314e87f2`. `ObjectGrid.tsx` DID change across the move off `67dadd602`
// (171 insertions, 81 deletions), so both claims below were re-derived
// rather than carried over: `ObjectGrid.tsx:3790-3805`, whose block is
// byte-identical to the one cited at `67dadd602:3436-3451` and shifted only
// by insertions above it, and
// `hooks/useBulkExecutor.ts:284-289`, in a file byte-identical at both pins
// and re-READ there rather than carried on that identity — it still ends on
// `label = 'bulk delete'`, the line the `284-288` span cited three pins ago
Expand Down
64 changes: 33 additions & 31 deletions packages/spec/src/ui/component.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,19 +274,17 @@ describe('PageAccordionProps variant (#6776)', () => {
// sweep once read as declared-but-unenforced. It has a live cross-repo consumer:
// objectui's `PageAccordionRenderer` renders `{item.icon && <LazyIcon
// name={item.icon} …/>}` inside the `AccordionTrigger`
// (`packages/components/src/renderers/layout/containers.tsx:918-924`), and the
// (`packages/components/src/renderers/layout/containers.tsx:919-925`), and the
// same file's `ComponentRegistry.register('accordion', …)` publishes the key to
// the Studio block designer at `:965` (the `items` input, documented as
// the Studio block designer at `:966` (the `items` input, documented as
// `[{ label, icon?, collapsed?, children }]`). Measured at the pin this repo
// builds against — `.objectui-sha` = `67dadd602`. Re-derived at that pin
// 2026-09-02: `containers.tsx` DID change across the move off `d8ec8d6d4`
// (164 lines), but that change lands from `:995` on, below both anchors (its
// only edit above them rewrites one import line in place), so neither moved —
// the icon block is still `918-924` and the registration input still `965`,
// each re-READ there with the cited text unchanged. The earlier hop off
// `9602dc820` is the one that moved them, `851-857` → `918-924` and `898` →
// `965`. Every anchor was re-READ at the new pin, never inferred, because
// identity preserves a wrong anchor as faithfully as a right one (#10274).
// builds against — `.objectui-sha` = `00d3f09c5`. Re-derived at that pin
// 2026-09-04: `containers.tsx` DID change across the move off `67dadd602`
// (117 insertions, 63 deletions), and this time both anchors moved by exactly
// one line — the icon block `918-924` to `919-925` and the registration input
// `965` to `966` — each re-READ there with the cited text unchanged. Every
// anchor was re-READ at the new pin, never inferred, because identity
// preserves a wrong anchor as faithfully as a right one (#10274).
//
// #9397 spent a full dispatch cycle re-deriving that read point from scratch
// after the sweep proposed retiring the key. This block plus the `.describe()`
Expand Down Expand Up @@ -365,18 +363,16 @@ describe('PageTabsProps items[].value / items[].count (#5775)', () => {
// same bare declaration a liveness sweep reads as declared-but-unenforced.
// objectui's `PageTabsRenderer` renders `{item.icon && <LazyIcon
// name={item.icon} …/>}` inside the `TabsTrigger`
// (`packages/components/src/renderers/layout/containers.tsx:729-735`), and the
// (`packages/components/src/renderers/layout/containers.tsx:730-736`), and the
// same file's `ComponentRegistry.register('tabs', …)` publishes the key to the
// Studio block designer at `:788` (the `items` input, documented as
// Studio block designer at `:789` (the `items` input, documented as
// `[{ label, value?, icon?, count?, visibleWhen?, children }]`). Measured at
// the pin this repo builds against — `.objectui-sha` = `67dadd602`. Re-derived
// at that pin 2026-09-02: `containers.tsx` DID change across the move off
// `d8ec8d6d4` (164 lines), but that change lands from `:995` on, below both
// anchors, so neither moved — the icon block is still `729-735` and the
// registration input still `788`, each re-READ there with the cited text
// unchanged. The earlier hop off `9602dc820` is the one that moved them,
// `662-668` → `729-735` and `721` → `788`. Both were re-READ at the new pin,
// never inferred (#10274).
// the pin this repo builds against — `.objectui-sha` = `00d3f09c5`. Re-derived
// at that pin 2026-09-04: `containers.tsx` DID change across the move off
// `67dadd602` (117 insertions, 63 deletions), and both anchors moved by
// exactly one line — the icon block `729-735` to `730-736` and the
// registration input `788` to `789` — each re-READ there with the cited text
// unchanged. Both were re-READ at the new pin, never inferred (#10274).
//
// #9397 spent a full dispatch cycle re-deriving the accordion's read point
// after the sweep proposed retiring it. This block plus the `.describe()` it
Expand Down Expand Up @@ -2673,12 +2669,16 @@ describe('#7751 — object-* block props schemas', () => {
// #9881 and #9972 recorded the accordion and tab items; these two close the set.
//
// The button record re-measured at the pin this repo builds against —
// `.objectui-sha` = `67dadd602`, re-derived there 2026-09-02: `button.tsx` and
// `resolve-icon.ts` are both byte-identical to the ones at `d8ec8d6d4` (and at
// `9602dc820` before it), and every anchor below was still re-READ at the new
// pin rather than carried on that identity (#10274). Neither the move off
// `d8ec8d6d4` nor the one off `9602dc820` changed the read point or a single
// line number here.
// `.objectui-sha` = `00d3f09c5`, re-derived there 2026-09-04. This hop is the
// first that changed the record's SUBSTANCE and not merely its line numbers:
// `resolve-icon.ts` was restructured (110 insertions), so `resolveIcon` no
// longer PascalCases and maps inline — it delegates to the new
// `describeIconLookup` seam (`:117-120`), and the tokeniser now splits on
// hyphen, underscore AND whitespace (`/[-_\s]+/`), where this record used to
// say "splits on `-` only". That sentence was true when written and is false
// now, which is exactly why a citation refresh re-READS instead of moving
// numbers (#10274). `button.tsx` also changed (18 insertions): its anchors
// moved rather than died.
// The one move that changed the button READ POINT and not merely its line
// numbers was the one onto `9602dc820`: objectui#5993 deleted `button.tsx`'s
// file-local `toPascalCase` + `iconNameMap` + `icons` index and routed the
Expand All @@ -2694,11 +2694,13 @@ describe('ElementButtonPropsSchema icon liveness (#10053)', () => {
const button = ComponentPropsMap['element:button'];

it('accepts an icon on a button — the value objectui resolves through the lucide `icons` map', () => {
// objectui `packages/components/src/renderers/form/button.tsx:36` hands the
// objectui `packages/components/src/renderers/form/button.tsx:43` hands the
// name to the shared `resolveIcon`
// (`packages/components/src/renderers/action/resolve-icon.ts:30-35`), which
// PascalCases it and applies the one-entry rename map at `:14-24` before
// looking it up in `icons` from `lucide-react`; `button.tsx:57` / `:59`
// (`packages/components/src/renderers/action/resolve-icon.ts:129-132`),
// which delegates to `describeIconLookup` (`:117-120`): that PascalCases
// through `toPascalCase` (`:100-105`, splitting on hyphen, underscore or
// whitespace) and applies the one-entry rename map (`:90-92`) before the
// lookup in `icons` from `lucide-react`; `button.tsx:72` / `:74`
// draw it either side of the label per `iconPosition`.
const result = button.safeParse({ label: 'Save', icon: 'arrow-right' });
expect(result.success).toBe(true);
Expand Down
71 changes: 39 additions & 32 deletions packages/spec/src/ui/component.zod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -675,18 +675,17 @@ export const PageTabsProps = strictObject({
* false candidate a component over).
*
* The key is LIVE at the objectui pin this repo builds against
* (`.objectui-sha` = `67dadd602`; re-derived at that pin 2026-09-02 —
* `containers.tsx` DID change across the move off `d8ec8d6d4`, but that
* change lands from `:995` on, below both anchors, so neither moved: the
* icon block is still `729-735` and the registration input still `788`,
* each re-READ there with the cited text unchanged, never inferred; the
* earlier hop off `9602dc820` is the one that moved them, `662-668` →
* `729-735` and `721` → `788`): `containers.tsx:729-735`
* (`.objectui-sha` = `00d3f09c5`; re-derived at that pin 2026-09-04 —
* `containers.tsx` DID change across the move off `67dadd602` (117
* insertions, 63 deletions) and both anchors moved by exactly one line:
* the icon block from `729-735` and the registration input from `788`,
* each re-READ at the new pin with the cited text unchanged, never
* inferred): `containers.tsx:730-736`
* renders
* `{item.icon && <LazyIcon name={item.icon} …/>}` inside the
* `TabsTrigger`, left of the label span (`mr-1.5 h-3.5 w-3.5 shrink-0
* opacity-70`, `aria-hidden`), and the renderer's registration publishes
* the key to the Studio block designer at `:788` (the `items` input,
* the key to the Studio block designer at `:789` (the `items` input,
* documented as `[{ label, value?, icon?, count?, visibleWhen?, children
* }]`).
*
Expand Down Expand Up @@ -1735,18 +1734,17 @@ export const PageAccordionProps = strictObject({
* re-derive the same false candidate).
*
* The key is LIVE at the objectui pin this repo builds against
* (`.objectui-sha` = `67dadd602`; re-derived at that pin 2026-09-02 —
* `containers.tsx` DID change across the move off `d8ec8d6d4`, but that
* change lands from `:995` on, below both anchors, so neither moved: the
* icon block is still `918-924` and the registration input still `965`,
* each re-READ there with the cited text unchanged, never inferred; the
* earlier hop off `9602dc820` is the one that moved them, `851-857` →
* `918-924` and `898` → `965`): `containers.tsx:918-924`
* (`.objectui-sha` = `00d3f09c5`; re-derived at that pin 2026-09-04 —
* `containers.tsx` DID change across the move off `67dadd602` (117
* insertions, 63 deletions) and both anchors moved by exactly one line:
* the icon block from `918-924` and the registration input from `965`,
* each re-READ at the new pin with the cited text unchanged, never
* inferred): `containers.tsx:919-925`
* renders
* `{item.icon && <LazyIcon name={item.icon} …/>}` inside the
* `AccordionTrigger`, grouped with the label in the trigger's one wrapping
* span, and the renderer's registration publishes the key to the Studio
* block designer at `:965` (the `items` input, documented as
* block designer at `:966` (the `items` input, documented as
* `[{ label, icon?, collapsed?, children }]`).
*
* Vocabulary is Lucide, resolved through objectui's `LazyIcon`
Expand Down Expand Up @@ -1943,27 +1941,36 @@ export const ElementButtonPropsSchema = lazySchema(() => strictObject({
* the button.
*
* The key is LIVE at the objectui pin this repo builds against
* (`.objectui-sha` = `67dadd602`; re-derived at that pin 2026-09-02 —
* `button.tsx` and `resolve-icon.ts` are both byte-identical to the ones at
* `d8ec8d6d4` (and at `9602dc820` before it) and every anchor below is
* unmoved, each one re-READ at the new pin rather than carried on that
* identity (#10274). The read point MOVED
* rather than died on the earlier hop onto `9602dc820`, which is why the
* anchors below span a second file): `components/src/renderers/form/
* button.tsx:36` resolves `schema.icon` through the shared `resolveIcon`,
* and `:57` / `:59` render it on either side of the label per
* (`.objectui-sha` = `00d3f09c5`; re-derived at that pin 2026-09-04 — BOTH
* files changed across the move off `67dadd602`, and this is the first hop
* that moved the record's SUBSTANCE rather than only its line numbers:
* `resolve-icon.ts` was restructured (110 insertions) so the resolution now
* runs through a named `describeIconLookup` seam, and its tokeniser accepts
* more spellings than this record used to claim — see the corrected
* breakdown below. `button.tsx` changed by 18 insertions and its anchors
* moved. Every anchor below was re-READ at the new pin, never carried
* (#10274); the read point first MOVED rather than died on the earlier hop
* onto `9602dc820`, which is why the anchors span a second file):
* `components/src/renderers/form/
* button.tsx:43` resolves `schema.icon` through the shared `resolveIcon`,
* and `:72` / `:74` render it on either side of the label per
* `iconPosition` (`mr-2 h-4 w-4` left, `ml-2 h-4 w-4` right), both
* suppressed while `loading`.
*
* ⚠️ The resolution path is NOT `LazyIcon`, the slot the container icons on
* this surface use — it is the `action:*` resolver, and the two accept
* different spellings:
* - here: `resolveIcon`
* (`components/src/renderers/action/resolve-icon.ts:30-35`) →
* `toPascalCase`, which splits on `-` only, then a one-entry rename map
* (`Home` → `House`), both at `:14-24` → `icons[name]` from
* `lucide-react`. An unknown name resolves to `null` and the button
* renders with NO icon and no diagnostic anywhere.
* (`components/src/renderers/action/resolve-icon.ts:129-132`) delegates
* to `describeIconLookup` (`:117-120`), which PascalCases through
* `toPascalCase` (`:100-105`) and then applies a one-entry rename map
* (`Home` becomes `House`, `:90-92`) before the `icons[key]` lookup from
* `lucide-react`. ⚠️ The tokeniser splits on hyphen, underscore OR
* whitespace (`/[-_\s]+/`) as of this pin; this record previously said
* "splits on `-` only", which was true when written and is not now — a
* re-READ caught it, a line-number refresh would not have. An unknown
* name still resolves to `null` and the button renders with NO icon and
* no diagnostic anywhere.
* - `LazyIcon` / `getLazyIcon` (`components/src/lib/lazy-icon.tsx:66-92`):
* normalises to kebab-case, checks the name against Lucide's own name
* list, and degrades an unknown name to the `Database` glyph.
Expand Down Expand Up @@ -2528,10 +2535,10 @@ export const ObjectMetricPropsSchema = lazySchema(() => strictObject({
* same record for the metric tile.
*
* The key is LIVE at the objectui pin this repo builds against
* (`.objectui-sha` = `67dadd602`; re-derived at that pin 2026-09-02 — all
* (`.objectui-sha` = `00d3f09c5`; re-derived at that pin 2026-09-04 — all
* five files in the chain below, `index.tsx`, `ObjectMetricWidget.tsx`,
* `MetricWidget.tsx`, `MetricCard.tsx` and `lazy-icon.tsx`, are
* byte-identical to the ones at `d8ec8d6d4`, so NO anchor moved — the
* byte-identical to the ones at `67dadd602`, so NO anchor moved — the
* `object-metric` registration still begins at `:194` and the icon input
* still lands on `:204`. Every anchor below was re-READ at the new pin
* rather than inferred from that identity), and the chain runs
Expand Down
4 changes: 2 additions & 2 deletions scripts/sdui-manifest.record.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
"a pin bump without regeneration goes RED there — that is the anti-rot half of the freshness gate.",
"Regenerate + re-record: node scripts/gen-sdui-manifest-node.mjs (see its header for the offline mode)."
],
"objectuiSha": "67dadd602a3a891666ea1513c5de677140784b6a",
"objectuiSha": "00d3f09c500c4a45b5f27aca8af80349412faaf1",
"objectuiPackagesVersion": "17.6.0",
"generator": "scripts/gen-sdui-manifest-node.mjs",
"generatedAt": "2026-09-02",
"generatedAt": "2026-09-04",
"sha256": "49211fee7792cf51174930dc2c1be2169d5175f77a83491f68f90b3f0c19e69d",
"components": 57
}
Loading