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
26 changes: 26 additions & 0 deletions .changeset/i18nlabel-render-sites-4163.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
'@object-ui/layout': patch
'@object-ui/plugin-list': patch
'@object-ui/plugin-dashboard': patch
'@object-ui/plugin-designer': patch
'@object-ui/app-shell': patch
---

An inline per-locale label now renders its locale's string at the thirteen read sites the `@objectstack/spec` 17.0.0-rc.6 bump exposed

rc.6 widened `I18nLabel` from `string` to `string | Record<string, string>`, so an author may write `label: { en: 'Owner', 'zh-CN': '负责人' }` anywhere the spec accepts a display label. PR #4169 repaired eight such sites; these thirteen were invisible to it because the five packages involved build through vite/rolldown, so `turbo run build` never type-checks their sources — only `turbo run type-check` does. All thirteen are now resolved through a shared resolver against a real locale, and `turbo run type-check` is 78/78 with zero errors.

| package | what an author can now write and see |
| --- | --- |
| `@object-ui/layout` | `NavigationArea.label` — the sidebar area switcher's button and its tooltip |
| `@object-ui/plugin-list` | `ViewTab.label` — the inline pill row, and the mobile dropdown's trigger and menu items |
| `@object-ui/plugin-dashboard` | `DashboardWidget.title` — the widget card heading and its `title` attribute |
| `@object-ui/plugin-designer` | `DashboardWidget.title` — the widget card and the preview tile |
| `@object-ui/app-shell` | `ActionParam.label` **and** each `ActionParam.options[].label` |

**Patch, not minor, in every case: no public surface changes meaning.** Every entry above is a read site that previously could only be reached with a value the type system rejected, so no caller's working code changes behaviour. `@object-ui/app-shell` is the only package with an exported-type change and it is purely additive on the authoring side — `RawActionParam.label` and `RawActionParam.options[].label` widen to `I18nLabel` (they accept strictly more), `ResolveActionParamsContext` gains an optional `locale`, and the new `RawActionParamOption` names the authoring shape that was previously spelled with the resolved one. What `resolveActionParams` **emits** is unchanged: `ActionParamDef.label` and its options' labels are still plain `string`s.

Two consequences worth knowing:

- **The dashboard designer's title input is deliberately read-only for a map-valued title.** Resolving a per-locale map into a single-line input and writing `e.target.value` back would collapse every other locale on the first keystroke, so the write is guarded and an inline map survives an unrelated edit-and-save round trip untouched — the same conservative branch #4169 took for `DashboardWidgetInspector`. What Studio should actually offer for authoring a per-locale label is objectui#4163 part 2, which is unclaimed and pending design.
- **`@object-ui/layout` resolves at the spec's `en` default, not the viewer's language.** That package carries no i18n dependency by design (its whole i18n story is injection), and `AppSchemaRendererProps` exposes no locale to thread. The choice and what would change it are documented at the call site.
46 changes: 46 additions & 0 deletions .changeset/record-picker-sort-limit-empty-text-4167.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
"@object-ui/components": minor
---

`element:record_picker` publishes `sort`, `limit` and `emptyText` as authoring
inputs (objectui#4167).

All three were already READ by the renderer and declared by the contract — the
renderer has passed `sort` into `$orderby` and `limit` into `$top` since the
block existed, and `emptyText` decides the no-rows message — but none of them
appeared in `inputs`, so every layer that reads a manifest said they did not
exist. `packages/components/src/renderers/layout/page.tsx` builds the JSX-page
compiler's prop whitelist from `getKnownTypes()` plus these `inputs`, so writing
any of the three on a JSX page drew an `unknown-prop` warning from
`sdui-parser/src/validate.ts` on a key the renderer then went on to honour.

That is objectui#3407's shape — honoured, undiscoverable — and this is the same
repair objectui#3808 made for `record:details.hideFields` and objectui#3830 made
for `element:record_picker.filter`. `@objectstack/spec` 17.0.0-rc.6 is what made
it actionable: objectstack#5775 declared the three upstream, and the reverse
direction of the console's registry parity gate went red demanding them the
moment the pin moved — a red the previous exemption had predicted in writing and
called "correct and wanted".

Each description documents the renderer's real behaviour rather than restating
the schema, because that is the half an author cannot read off the contract:

- **`sort`** and **`limit`** are both overridden OUTRIGHT by a node-level
`dataSource` binding (`dataSource.sort ?? sort`), not merged with it — so a
node that carries a `dataSource` silently ignores them.
- **`limit`** defaults to 50 in the renderer, not in the schema, and a record
outside the limit cannot be picked at all with nothing in the control to say
more exist.
- **`emptyText`** is published as `string` against a contract of
`string | Record< string, string >`: rc.6 widened it to `I18nLabel`, and this
renderer passes the value straight into a text node with no locale resolution,
so only the plain-string form renders today. The description says so rather
than advertising a shape the renderer drops — the narrowed-type treatment
objectui#3832 describes, with the render-site gap tracked in objectui#4163.

The console's `registry-inputs-spec-parity` suite also drops all twelve of its
off-spec exemptions, which rc.6 obsoleted at once (objectstack#6776 declared
`page:header.recordChrome` / `showStar` / `showCopyId`, `page:accordion.variant`
and `page:tabs.tabStyle`; objectstack#5775 declared the `element:record_picker`
trio and `children` on the four page containers). The forward direction of that
gate now runs with no cover of any kind.
92 changes: 92 additions & 0 deletions .changeset/spec-symbol-collisions-rc6-4167.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
---
"@object-ui/types": minor
"@object-ui/core": minor
"@object-ui/react": minor
"@object-ui/app-shell": minor
"@object-ui/layout": minor
"@object-ui/fields": minor
"@object-ui/components": minor
"@object-ui/plugin-designer": patch
---

Stop declaring 14 symbols under names `@objectstack/spec` owns at `17.0.0-rc.6`
(objectui#4167, objectstack#4115).

The rc.6 bump published nine names this repo already declared locally, on top of
four that predate it — `check:spec-symbols` reported all thirteen at once, and a
fourteenth (`GlobalFilterSchema`) appeared during the bump itself. Each was
triaged on its own rather than blanket-renamed, because the right answer differs
per symbol: five bind to the spec, three are renamed because the spec's
same-named export means something else, five arrive by derivation, and one is a
declared dialect with a written reason.

**Breaking for importers of `@object-ui/react`, `@object-ui/app-shell` and
`@object-ui/types`** — three exported names changed, because the spec exports the
same name for a *different* thing:

| package | was | now | what the spec's same-named export actually is |
|:--|:--|:--|:--|
| `react` / `app-shell` | `MetadataState` | `MetadataCacheState` | a metadata item's LIFECYCLE state — `'draft' \| 'active' \| 'deprecated' \| 'archived'` (`MetadataStateSchema`, `@objectstack/spec/system`) |
| `react` / `app-shell` | `resolveI18nLabel` | `resolveKeyedI18nLabel` | a resolver for the INLINE per-locale map (`{ en: 'Owner', 'zh-CN': '负责人' }`) against a BCP-47 locale |
| `types` | `DateRangePreset` | `FilterBuilderDateRangePreset` | the thirteen HISTORICAL dashboard filter-bar presets; this one is the filter-builder set, which adds eight FUTURE windows the dashboard schema rejects |

`resolveI18nLabel` is the one where the collision had already started costing
something. rc.6 widened `I18nLabel` from `string` to
`string | Record< string, string >`, so the same authored value now reaches
either resolver — and each answers wrongly, silently, for the other's input: the
keyed one returns `undefined` for `{ en: 'Owner' }` (no `key`, no
`defaultValue`), and the spec's reads `key` / `defaultValue` / `params` as locale
tags. The rc.6 bump PR met this and aliased the spec's import as
`resolveInlineI18nLabel` in five files, with hand-written comments at two of
them. That is a review convention, which is what objectstack#4115 exists to
replace with a rule — so `Keyed` is now the counterpart of that `Inline`, and the
name says which vocabulary it resolves at every call site.

**Eleven keep their names and are now imported or derived from the spec** instead
of re-declared: `DATE_RANGE_PRESETS`, `NavigationMode`, `AddressValue`,
`BreakpointColumnMap`, `BreakpointOrderMap`, `KanbanConfig`, `CalendarConfig`,
`GanttConfig`, plus the three renamed above at their new names.

**Four of the copies were losing information, not just duplicating it.**

- **`GanttConfig` declared six keys and called itself canonical; rc.6's
`GanttConfigSchema` declares seventeen.** The eleven it never mentioned —
`parentField`, `typeField`, `baselineStartField`, `baselineEndField`,
`groupByField`, `resourceView`, `assigneeField`, `effortField`, `capacity`,
`quickFilters`, `autoZoomToFilter` — are all read by
`plugin-gantt/src/ObjectGantt.tsx`, through a local `GanttConfigEx`
intersection that existed only because this type did not carry them. It now
derives from the spec, with `timeSegments` (shift segmentation) as the one
genuinely local extension; the schema is `$loose` upstream, so that key is
legal metadata rather than a second dialect.
- **`GanttConfig.tooltipFields` carried the comment "not part of the upstream
GanttConfigSchema".** It is, as of rc.6, so the key now arrives from the spec.
- **`AddressValue` declared five of the spec's seven parts** — `countryCode` and
`formatted` were missing, under a comment already claiming to be "the part
names of `AddressSchema`". The widget still renders five inputs; binding the
type stops it from asserting the platform cannot store the other two, and makes
the `{ ...address }` write-through say so.
- **`DATE_RANGE_PRESETS` was `Object.keys(PRESET_RANGES)`,** a third copy of a
vocabulary the spec extracted in objectstack#4614 precisely to collapse — its
own doc comment names this module as one of the three. It is now the spec's
array by reference, and the local date-macro bounds table is pinned complete
against it with `satisfies`, so a preset the schema gains without bounds here
is a compile error rather than a filter that validates clean and then selects
nothing.

`NavigationMode` was one hop from the spec already (`NavigationConfig['mode']`);
it is bound directly, with a both-directions type pin that it stays the same type
as the config's own `mode`. `KanbanConfig` / `CalendarConfig` /
`BreakpointColumnMap` / `BreakpointOrderMap` were exact hand copies of `$strict`
schemas and are now re-exports — "still exact" is the argument for binding them,
since a copy with nothing to protect can only drift.

`GlobalFilterSchema` is the one ALLOW entry. It is the same spread-composition
dialect as `SelectOptionSchema` next to it, and it collided only because rc.6's
new refinement forced `.extend()` to be respelled as a `.shape` spread — which
moved a derivation the guard could see into an object literal it deliberately
does not descend into. The dialect is unchanged and its three divergences are
pinned; which side moves on the refinement itself is objectui#4165.

`@objectstack/spec` moves from `devDependencies` to `dependencies` in
`@object-ui/layout`: its public type surface now references the spec.
4 changes: 2 additions & 2 deletions QUICK_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,9 @@ that test tells you to edit this block. The one exception is called out on its r

- **Version:** 17.4.0 (the version every `@object-ui/*` manifest carries — they are one
`fixed` group in `.changeset/config.json`, so a release moves all of them together)
- **Spec:** `@objectstack/spec` ^17.0.0-rc.5 (declared by the root `package.json` and by
- **Spec:** `@objectstack/spec` ^17.0.0-rc.6 (declared by the root `package.json` and by
`apps/console/package.json`)
- **Client:** `@objectstack/client` ^17.0.0-rc.5 (declared by `apps/console/package.json`
- **Client:** `@objectstack/client` ^17.0.0-rc.6 (declared by `apps/console/package.json`
and `packages/data-objectstack/package.json`)
- **Node.js:** ≥ 22 (see root `engines.node`)
- **pnpm:** ≥ 9 (the workspace pins `pnpm@10.31.0` via `packageManager`)
Expand Down
Loading