@object-ui/layout 17.3.0 → 17.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +691 -0
- package/README.md +148 -32
- package/dist/AppSchemaRenderer.d.ts +23 -5
- package/dist/AppShell.d.ts +0 -2
- package/dist/NavigationRenderer.d.ts +32 -25
- package/dist/PageHeader.d.ts +12 -5
- package/dist/ResponsiveGrid.d.ts +19 -22
- package/dist/index.d.ts +7 -7
- package/dist/index.js +369 -350
- package/dist/index.umd.cjs +1 -1
- package/package.json +16 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,687 @@
|
|
|
1
1
|
# @object-ui/layout
|
|
2
2
|
|
|
3
|
+
## 17.6.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 88085e3: Consume the declared nav `runAction` slot; retire the private `?runAction=` string convention
|
|
8
|
+
|
|
9
|
+
An `object` navigation item can now declare `runAction: '<actionName>'` and the shell will run that action once on arrival at the object's list surface, through the ordinary execute path — so param dialogs, confirms and entitlement gates all still apply. The slot is `@objectstack/spec`'s `ObjectNavItemSchema.runAction`; objectui now reads it instead of a private convention.
|
|
10
|
+
|
|
11
|
+
- `NavigationItem` declares `runAction` (derived from the spec's object-nav variant), and objectui's own nav schema stops stripping it — `objectui validate` previously discarded the key, so an entry carrying a deep link validated clean with the deep link thrown away.
|
|
12
|
+
- `resolveHref` encodes it onto the list href as the reserved `?runAction=` param, on the list landings only. A `recordId` entry resolves to a record page, which has no list toolbar to answer it, so the slot is not encoded there.
|
|
13
|
+
- The deep link is honoured on **every** object list, not just the environments list. The action is armed only when it is actually present at `list_toolbar`; a name no action answers to runs nothing and deliberately leaves the URL untouched, so a later mount with fresher metadata can still honour it. Undefined references are rejected upstream at authoring time by `defineStack`.
|
|
14
|
+
- The param name now has exactly one definition (`NAV_RUN_ACTION_PARAM`, exported from `@object-ui/layout`) and is registered in the console's reserved-param collision check. It was previously a bare literal hand-written at both the producing and consuming ends, declared by no schema and listed in no registry.
|
|
15
|
+
- `CloudOnboardingNext` takes an optional `properties.createAction` (defaulting to `create_environment`) instead of hand-concatenating its deep link.
|
|
16
|
+
- 86f633f: Remove the published optional key `logo` from `AppShellBranding` (`@object-ui/layout`).
|
|
17
|
+
|
|
18
|
+
The key was declared but never read. `useAppShellBranding` applies only
|
|
19
|
+
`primaryColor`, `accentColor`, `favicon` and `title`, and `AppShell` installs no
|
|
20
|
+
context provider at all — so its doc comment, "Logo URL — passed to sidebar/navbar
|
|
21
|
+
via context", described a mechanism that did not exist. Three call sites were
|
|
22
|
+
feeding the key a real value that was silently discarded, and all three are removed
|
|
23
|
+
with it: `AppSchemaRenderer`, `ConsoleLayout` and the console's `useBranding` hook.
|
|
24
|
+
|
|
25
|
+
The real logo entry point is unchanged and is where it always was: the app schema's
|
|
26
|
+
own `branding.logo`, read directly by `AppSidebar` in `@object-ui/app-shell`, plus
|
|
27
|
+
the app schema's top-level `logo`, rendered directly by `AppSchemaRenderer`'s
|
|
28
|
+
default sidebar header. Neither path went through `AppShellBranding`, so nothing
|
|
29
|
+
rendering-visible changes.
|
|
30
|
+
|
|
31
|
+
Migration: a consumer that passes `logo` inside an `AppShellBranding` object literal
|
|
32
|
+
now gets a compile error. Delete the key — it never reached a renderer. To show a
|
|
33
|
+
logo, set it on the app schema's `branding.logo` instead.
|
|
34
|
+
- 29167d5: **Breaking (shipped as `minor`, following the `page-header` `description` retirement):**
|
|
35
|
+
`app-shell` is no longer a component key. `registerLayout()` registered `AppShell` under
|
|
36
|
+
that key with no `inputs`; the registration is gone (objectui#4841, ADR-0049
|
|
37
|
+
enforce-or-remove, remove side, maintainer ruling 2026-08-16).
|
|
38
|
+
|
|
39
|
+
**What it could never do.** Four of `AppShellProps`' seven keys are `React.ReactNode`
|
|
40
|
+
slots — `sidebar`, `navbar`, `children`, `rightRail` — and a JSON document can fill none
|
|
41
|
+
of them, so `{ "type": "app-shell" }` resolved to a component that had exactly two
|
|
42
|
+
outcomes, neither of them a shell. `children` was dropped in silence: `SchemaRenderer`
|
|
43
|
+
strips `children` (and `body`) before spreading a node's keys as props, and `AppShell`
|
|
44
|
+
reads its `children` prop, never `schema.children`, so the `<main>` element rendered
|
|
45
|
+
empty with nothing logged. A schema written into `sidebar` / `navbar` / `rightRail`
|
|
46
|
+
arrived as a plain object React refuses to render, replacing the node with an error box.
|
|
47
|
+
Only `className`, `defaultOpen` and `branding` ever survived the JSON path, i.e. the best
|
|
48
|
+
result JSON could reach was a shell with no navigation, no top bar and an empty content
|
|
49
|
+
area. With no `inputs` declared, `sdui-parser` had no declaration face to compare a node
|
|
50
|
+
against either, so neither outcome was diagnosed.
|
|
51
|
+
|
|
52
|
+
**What changes for an author.** The middle state — parses, resolves, renders nothing — is
|
|
53
|
+
replaced by a named refusal. `SchemaRenderer` now shows its `Unknown component type:
|
|
54
|
+
app-shell` panel (`OBJUI-001`) and `sdui-parser` reports an `error`-severity
|
|
55
|
+
`unknown-component` diagnostic before render.
|
|
56
|
+
|
|
57
|
+
**FROM → TO.** There is no in-place rewrite, because the node never produced a shell.
|
|
58
|
+
Schema authors who want the whole shell from metadata use `app-schema-renderer`
|
|
59
|
+
(`AppSchemaRenderer`), which declares its `inputs` and builds branding and sidebar
|
|
60
|
+
navigation from an `AppSchema` document: `{ "type": "app-shell", … }` →
|
|
61
|
+
`{ "type": "app-schema-renderer", "schema": { … } }`. Everyone else composes in React —
|
|
62
|
+
`AppShell` is **unchanged and still exported** from `@object-ui/layout`, and remains the
|
|
63
|
+
way to build a shell and render JSON pages inside it.
|
|
64
|
+
|
|
65
|
+
Repo-wide scan before removal found no `"type": "app-shell"` node anywhere in
|
|
66
|
+
`objectstack-ai/objectui` or `objectstack-ai/objectstack` at `origin/main` — no example,
|
|
67
|
+
catalog schema, fixture or template authored one. `content/docs/guide/layout.md` is
|
|
68
|
+
updated to state the new fact, and
|
|
69
|
+
`packages/layout/src/__tests__/app-shell-not-a-component-key.test.tsx` pins it on both
|
|
70
|
+
faces (source and live registry) plus the rendered diagnostic.
|
|
71
|
+
- 45f7dcc: **Breaking (shipped as `minor`, following the `app-shell` component-key deregistration):**
|
|
72
|
+
`app-schema-renderer`'s `mobileNavMode` is now declared as the vocabulary the renderer
|
|
73
|
+
implements — `enum: ['drawer', 'bottom_nav']` — instead of free-text `string`, and the
|
|
74
|
+
third member of `MobileNavMode` is retired (objectui#3985, ADR-0049 enforce-or-remove,
|
|
75
|
+
maintainer ruling 2026-08-10).
|
|
76
|
+
|
|
77
|
+
**What used to happen.** The registration declared `{ name: 'mobileNavMode', type:
|
|
78
|
+
'string' }`, so `sdui-parser`'s `checkType` asked only whether the value was a string.
|
|
79
|
+
`mobileNavMode="bottom-nav"` — the hyphenated spelling of the underscored value, and the
|
|
80
|
+
likeliest typo on this key — passed the manifest gate, passed the parser, reached the
|
|
81
|
+
renderer, missed its one equality test, and rendered the drawer. Nothing reported
|
|
82
|
+
anything, at any layer. The declaration was WIDER than the implementation, which is why
|
|
83
|
+
the `invalid-enum` that should have fired never could.
|
|
84
|
+
|
|
85
|
+
**What happens now.** A value outside the vocabulary is an **error**-level `invalid-enum`
|
|
86
|
+
from `validateTree` / `compile`, so a schema-driven author — very often an AI author — is
|
|
87
|
+
stopped at the typo instead of debugging a mode that silently did nothing. The generated
|
|
88
|
+
`sdui-intrinsics.d.ts` narrows from `mobileNavMode?: string` to the two-value union for
|
|
89
|
+
the same reason.
|
|
90
|
+
|
|
91
|
+
**Retirement.** `MobileNavMode` had a third member documented as "collapsed sidebar" with
|
|
92
|
+
**zero read points** in `AppSchemaRenderer`: the only two reads are the `drawer` default
|
|
93
|
+
and the `=== 'bottom_nav'` comparison that gates the bottom bar, so the value was
|
|
94
|
+
behaviourally identical to the default while the union, the JSDoc and `ROADMAP.md`
|
|
95
|
+
presented it as a capability. It is gone from the union and from the published
|
|
96
|
+
vocabulary. Making it real behaviour is an implementation card first — the value comes
|
|
97
|
+
back together with a renderer read point, never as a declaration on its own.
|
|
98
|
+
|
|
99
|
+
**Migration.** Both implemented modes are unchanged; no runtime behaviour moves. A
|
|
100
|
+
TypeScript caller passing the retired literal now fails to compile (it previously
|
|
101
|
+
compiled and rendered the drawer), and a JSON/JSX author writing it — or any other
|
|
102
|
+
out-of-vocabulary value — now gets a validation error where they previously got silence.
|
|
103
|
+
In both cases the value that reproduces the old behaviour exactly is `drawer`.
|
|
104
|
+
- f923b7c: **Breaking (shipped as `minor`, see below):** `@object-ui/layout`'s `<PageHeader>` retires the legacy `description` prop. `subtitle` is now the only spelling for the secondary line.
|
|
105
|
+
|
|
106
|
+
`PageHeader` read `subtitle ?? description`. The alias was not tolerance for a sloppy author — it was the only thing standing between an externally authored `description` and a page that renders its title and silently drops its second line, which is why objectui#3226 refused to delete it: "no in-repo author writes `description`" (true, verified) says nothing about the stored metadata of out-of-repo consumers.
|
|
107
|
+
|
|
108
|
+
**FROM → TO.** JSX call sites: `<PageHeader title="X" description="Y" />` → `<PageHeader title="X" subtitle="Y" />`. Schema authors: `{ "type": "page-header", "description": "Y" }` → `{ "type": "page-header", "subtitle": "Y" }`. Stored stack metadata needs no hand edit — `os migrate meta` rewrites it, and a protocol 17 loader rewrites it on the way in.
|
|
109
|
+
|
|
110
|
+
**Why the read could go now.** The normalization moved upstream, where a contract belongs: protocol 17's ADR-0087 D2 conversion `page-header-subtitle-alias` rewrites a header node's `properties.description` to `properties.subtitle` as the stack loads. The gate on this side was a measurement, not a date — every position a `page-header` node can occupy had to be shown to pass through that rewrite. It did not, at first: measured 2026-08-08 against `@objectstack/spec@17.0.0-rc.5`, the conversion walk reached `pages[].regions[].components[]` and stopped, leaving seven spec-valid header positions unconverted — including `slots.header` on a `kind: 'slotted'` record page, the shape this repo's own slotted-pages guide prescribes (objectstack#6775). With the walk widened by objectstack#6776 (slots) and objectstack PR #7034 (containers nested to any depth: `properties.children`, `items[].children`, `body`, `footer`), all seven convert. Re-measured against `@objectstack/spec@17.0.0-rc.6`, which this package depends on, before the read was deleted.
|
|
111
|
+
|
|
112
|
+
The measurement is now a standing pin rather than a one-off: `packages/layout/src/__tests__/page-header-subtitle-conversion-coverage.test.ts` re-runs all seven positions against the resolved spec build on every test run. If a future spec narrows that reach — or a dependency is pinned back below `17.0.0-rc.6` — it fails there, at the seam, instead of turning into a second line that quietly stops rendering in a consumer's app. The two tests in `page-header-authorable-keys.test.tsx` that pinned the fallback's continued existence were deleted in this same change, as their own SEQUENCING note instructed, and replaced with the opposite assertion: a lone `description` now renders no secondary line.
|
|
113
|
+
|
|
114
|
+
**Not affected, despite the shared word:** the `page` renderer's own `description` prop (the page's prose under the page title) is a live declared key of a different node, untouched here. `@object-ui/app-shell`'s separate `<PageHeader>` uses `description` as its only secondary-line prop and is likewise unchanged.
|
|
115
|
+
|
|
116
|
+
`minor` rather than `major` is deliberate and follows the repo's retirement precedent (PR #3793): all 39 publishable packages sit in one `fixed` group, so a `major` here would carry the whole family to 18.0.0 against an `@objectstack` still on 17 and break the "same major ⇒ compatible" pin between the two repos (AGENTS.md §版本号策略, enforced by `scripts/check-changeset-no-major.mjs`). objectui's own breaking changes ship as `minor` with the break spelled out in the body — which is what the FROM → TO above is.
|
|
117
|
+
- 9c60144: **Breaking (published API):** `NavigationRenderer` no longer accepts `resolveGroupLabel` or `resolveItemLabel`. If your build just broke on one of these props, delete the prop — it never did anything.
|
|
118
|
+
|
|
119
|
+
Both were id-keyed label resolvers on `NavigationRendererProps` (`@object-ui/layout`), typically wired to `useObjectLabel().navGroupLabel` from `@object-ui/i18n` to translate sidebar group and leaf labels from a client translation pack keyed `{ns}.apps.{appName}.navigation.{nodeId}.label`.
|
|
120
|
+
|
|
121
|
+
**They could never fire.** The renderer guards convention-based resolution with `isCustomized` — a case-insensitive "the authored label differs from this branch's comparison target" test. On the `object` / `dashboard` branches the target is the object or dashboard name, and the test is meaningful: it protects an author's custom label (`Projects`) from being overwritten by an `objects.project.label` translation. On the two id-keyed branches the target was the nav node's own **`id`** — `grp_workspace`, `group_sales` — while the label was its text — `Workspace`, `Sales`. Those never compare equal, so the guard was true for every real navigation entry and the resolver beneath it was unreachable. The only node that could have reached it is one whose label is literally its own id.
|
|
122
|
+
|
|
123
|
+
Nothing regresses when they go, because nothing was using them to begin with: **app-navigation localization is owned solely by the server-side `/meta` boundary.** `translateApp` (`@objectstack/spec`, `src/system/i18n-resolver.ts`) rewrites every navigation node's `label` by id, and `@objectstack/rest` applies it before the metadata reaches the client — so nav labels arrive already localized and the client-side path was never the one answering. One owner, not two.
|
|
124
|
+
|
|
125
|
+
**If you wired these hooks to localize navigation, your labels were never being localized by them.** Move the translation to the server side: add it to the app's i18n bundle that `translateApp` reads, keyed by navigation node id. A client-side pack keyed `apps.*.navigation.*.label` changes nothing in the sidebar.
|
|
126
|
+
|
|
127
|
+
Also in this change:
|
|
128
|
+
|
|
129
|
+
- `useObjectLabel().navGroupLabel` (`@object-ui/i18n`) is **kept**, but its docstring no longer promises `"Sales" → "销售"` for sidebar groups — that promise was false, and a live docstring describing an unreachable path is how an agent or a developer ends up wiring a translation pack and receiving silent non-localization. It is now documented for what it is: a plain reader for `{ns}.apps.{appName}.navigation.{groupId}.label` with no first-party caller, pointing at the server boundary for anything nav-related.
|
|
130
|
+
- `UnifiedSidebar` (`@object-ui/app-shell`) drops the two prop wirings, including a `studio` carve-out that existed only to stop `resolveItemLabel` from re-translating an already-translated "Package management" label.
|
|
131
|
+
- The `object` / `dashboard` / `viewName` branches — `resolveObjectLabel`, `resolveDashboardLabel`, `resolveViewLabel` — are untouched and keep both their resolvers and the `isCustomized` guard.
|
|
132
|
+
|
|
133
|
+
### Patch Changes
|
|
134
|
+
|
|
135
|
+
- d442795: AppShell: drop the unused `Sidebar` import. `AppShell` renders the node the caller passes
|
|
136
|
+
in the `sidebar` prop and never constructs a `Sidebar` itself, so the import was dead
|
|
137
|
+
(tree-shaken out of every bundle) and only suggested otherwise to readers. No runtime
|
|
138
|
+
behaviour changes.
|
|
139
|
+
- 8a9dece: `SidebarNav`'s README example teaches the shape the component actually reads.
|
|
140
|
+
|
|
141
|
+
`packages/layout/README.md` — the package's npm landing page, shipped in `files` —
|
|
142
|
+
spelled every nav item `{ label, path, icon: 'home' }`. `NavItem` declares none of
|
|
143
|
+
those three keys: the label is `title`, the target is `href`, and `icon` is a
|
|
144
|
+
`React.ComponentType` rendered as `<item.icon />`, not an icon name. Copied
|
|
145
|
+
verbatim the example produced a sidebar whose every row was unlabelled
|
|
146
|
+
(`<span>{item.title}</span>` reading `undefined`), a `NavLink` with
|
|
147
|
+
`to={undefined}`, and the string `'home'` handed to React as an unknown lowercase
|
|
148
|
+
tag. Both README examples now use `title` / `href` / imported Lucide components,
|
|
149
|
+
and annotate the array as `NavItem[]` so the same class of typo becomes a compile
|
|
150
|
+
error where it is written instead of a blank sidebar at runtime.
|
|
151
|
+
|
|
152
|
+
Adds the props tables the README never carried — `SidebarNavProps`, `NavItem`
|
|
153
|
+
(`badge`, `badgeVariant` and `children` included) and `NavGroup`, plus a grouped
|
|
154
|
+
and nested example — and pins all of it rather than leaving a second surface free
|
|
155
|
+
to rot the same way: the examples are real, type-checked code in
|
|
156
|
+
`readme-sidebar-nav-example.test.ts` asserted to appear verbatim in the README, and
|
|
157
|
+
the tables are compared against the interface keys read out of `SidebarNav.tsx` on
|
|
158
|
+
every run. Also corrects a comment in `side-effects-manifest.test.ts` that named
|
|
159
|
+
`SidebarNav` as the component behind the `navigation-renderer` registration; that
|
|
160
|
+
key belongs to a different component, and `SidebarNav` is registered under no key
|
|
161
|
+
at all.
|
|
162
|
+
|
|
163
|
+
No runtime change — `SidebarNav.tsx` is untouched. This is `patch` rather than a
|
|
164
|
+
no-release declaration because the corrected landing page only reaches npm through
|
|
165
|
+
a publish.
|
|
166
|
+
- 183d09b: Studio 页面设计器不再为 canonical `page:header` 提供 `icon` 编辑框(objectui#3829)
|
|
167
|
+
|
|
168
|
+
`PageHeaderProps.icon` 已在 `@objectstack/spec` 17.0.0 随 ADR-0087 D2 退役
|
|
169
|
+
(objectstack#6946 / PR objectstack#7115):canonical `page:header` 渲染器从未读过它,
|
|
170
|
+
表头的身份由 record chrome(`recordChrome`)与每个 action 自带的 `icon` 承担。退役前
|
|
171
|
+
作者填入的值被静默丢弃,退役后平台按名拒绝整个节点 —— 而设计器仍在提供那个输入框,
|
|
172
|
+
等于教作者写出解析失败的元数据。本次移除该字段与它此时已成孤儿的两条 i18n 键
|
|
173
|
+
(en / zh 同一次改动,两张表的键集保持一致),并把「不得回潮」钉在
|
|
174
|
+
`previews/__tests__/block-config.test.ts`。
|
|
175
|
+
|
|
176
|
+
`@object-ui/layout` 的 `page-header` / `layout:page-header` 别名**保留** `icon` 输入,
|
|
177
|
+
行为不变:那是另一个渲染器,它真读真画(`PageHeader.tsx`),文档与本仓唯一的活 demo
|
|
178
|
+
都写它。变的只是这条声明的**依据** —— 从 spec parity 改述为 renderer-read 事实,并让
|
|
179
|
+
`page-header-authorable-keys` 守卫在派生 spec 键集时跳过墓碑成员,不再因为
|
|
180
|
+
`Object.keys(shape)` 仍列着已退役的 `icon` 而假绿。
|
|
181
|
+
- Updated dependencies [88085e3]
|
|
182
|
+
- Updated dependencies [516663d]
|
|
183
|
+
- Updated dependencies [460c4d0]
|
|
184
|
+
- Updated dependencies [0ae27f7]
|
|
185
|
+
- Updated dependencies [2533ec5]
|
|
186
|
+
- Updated dependencies [78c0f9a]
|
|
187
|
+
- Updated dependencies [bbe8b86]
|
|
188
|
+
- Updated dependencies [8477be5]
|
|
189
|
+
- Updated dependencies [279fb13]
|
|
190
|
+
- Updated dependencies [2e82ab2]
|
|
191
|
+
- Updated dependencies [ad07b65]
|
|
192
|
+
- Updated dependencies [41f498b]
|
|
193
|
+
- Updated dependencies [e1d4251]
|
|
194
|
+
- Updated dependencies [40d3a33]
|
|
195
|
+
- Updated dependencies [8b9dc62]
|
|
196
|
+
- Updated dependencies [1184192]
|
|
197
|
+
- Updated dependencies [a2a9747]
|
|
198
|
+
- Updated dependencies [a1609a6]
|
|
199
|
+
- Updated dependencies [53f23bc]
|
|
200
|
+
- Updated dependencies [c4533dc]
|
|
201
|
+
- Updated dependencies [be60815]
|
|
202
|
+
- Updated dependencies [37f6844]
|
|
203
|
+
- Updated dependencies [93de4f6]
|
|
204
|
+
- Updated dependencies [2b50261]
|
|
205
|
+
- Updated dependencies [384f30d]
|
|
206
|
+
- Updated dependencies [ac600e5]
|
|
207
|
+
- Updated dependencies [97fba31]
|
|
208
|
+
- Updated dependencies [232f61a]
|
|
209
|
+
- Updated dependencies [d374caf]
|
|
210
|
+
- Updated dependencies [5673576]
|
|
211
|
+
- Updated dependencies [c1ef923]
|
|
212
|
+
- Updated dependencies [911ceaa]
|
|
213
|
+
- Updated dependencies [98eab36]
|
|
214
|
+
- Updated dependencies [af5e292]
|
|
215
|
+
- Updated dependencies [3fbbea1]
|
|
216
|
+
- Updated dependencies [7f96b10]
|
|
217
|
+
- Updated dependencies [167ec42]
|
|
218
|
+
- Updated dependencies [616a2a5]
|
|
219
|
+
- Updated dependencies [0046d8f]
|
|
220
|
+
- Updated dependencies [f1d4748]
|
|
221
|
+
- Updated dependencies [b1119ec]
|
|
222
|
+
- Updated dependencies [9f23d2b]
|
|
223
|
+
- Updated dependencies [578e025]
|
|
224
|
+
- Updated dependencies [af025ee]
|
|
225
|
+
- Updated dependencies [598c89a]
|
|
226
|
+
- Updated dependencies [4a0bd17]
|
|
227
|
+
- Updated dependencies [b8b9af4]
|
|
228
|
+
- Updated dependencies [31676be]
|
|
229
|
+
- Updated dependencies [8c0d52e]
|
|
230
|
+
- Updated dependencies [aff10e2]
|
|
231
|
+
- Updated dependencies [70a774b]
|
|
232
|
+
- Updated dependencies [9ce096f]
|
|
233
|
+
- Updated dependencies [e05db88]
|
|
234
|
+
- Updated dependencies [7458a41]
|
|
235
|
+
- Updated dependencies [5ffcc14]
|
|
236
|
+
- Updated dependencies [d971e51]
|
|
237
|
+
- Updated dependencies [97abb24]
|
|
238
|
+
- Updated dependencies [deb157a]
|
|
239
|
+
- Updated dependencies [d2ce342]
|
|
240
|
+
- Updated dependencies [9695da7]
|
|
241
|
+
- Updated dependencies [75444e3]
|
|
242
|
+
- Updated dependencies [58b8346]
|
|
243
|
+
- Updated dependencies [2d0bd16]
|
|
244
|
+
- Updated dependencies [dad51e5]
|
|
245
|
+
- Updated dependencies [1c9c342]
|
|
246
|
+
- Updated dependencies [787c738]
|
|
247
|
+
- Updated dependencies [8396656]
|
|
248
|
+
- Updated dependencies [dbbd38a]
|
|
249
|
+
- Updated dependencies [93fe362]
|
|
250
|
+
- Updated dependencies [dfc6975]
|
|
251
|
+
- Updated dependencies [3cf4de0]
|
|
252
|
+
- Updated dependencies [c9dc811]
|
|
253
|
+
- Updated dependencies [144ef9b]
|
|
254
|
+
- Updated dependencies [138ab04]
|
|
255
|
+
- Updated dependencies [a0b9e91]
|
|
256
|
+
- Updated dependencies [99bd015]
|
|
257
|
+
- @object-ui/types@17.6.0
|
|
258
|
+
- @object-ui/react@17.6.0
|
|
259
|
+
- @object-ui/components@17.6.0
|
|
260
|
+
- @object-ui/core@17.6.0
|
|
261
|
+
|
|
262
|
+
## 17.5.0
|
|
263
|
+
|
|
264
|
+
### Minor Changes
|
|
265
|
+
|
|
266
|
+
- bb68488: Stop declaring 14 symbols under names `@objectstack/spec` owns at `17.0.0-rc.6`
|
|
267
|
+
(objectui#4167, objectstack#4115).
|
|
268
|
+
|
|
269
|
+
The rc.6 bump published nine names this repo already declared locally, on top of
|
|
270
|
+
four that predate it — `check:spec-symbols` reported all thirteen at once, and a
|
|
271
|
+
fourteenth (`GlobalFilterSchema`) appeared during the bump itself. Each was
|
|
272
|
+
triaged on its own rather than blanket-renamed, because the right answer differs
|
|
273
|
+
per symbol: five bind to the spec, three are renamed because the spec's
|
|
274
|
+
same-named export means something else, five arrive by derivation, and one is a
|
|
275
|
+
declared dialect with a written reason.
|
|
276
|
+
|
|
277
|
+
**Breaking for importers of `@object-ui/react`, `@object-ui/app-shell` and
|
|
278
|
+
`@object-ui/types`** — three exported names changed, because the spec exports the
|
|
279
|
+
same name for a _different_ thing:
|
|
280
|
+
|
|
281
|
+
| package | was | now | what the spec's same-named export actually is |
|
|
282
|
+
| :-------------------- | :----------------- | :----------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
283
|
+
| `react` / `app-shell` | `MetadataState` | `MetadataCacheState` | a metadata item's LIFECYCLE state — `'draft' \| 'active' \| 'deprecated' \| 'archived'` (`MetadataStateSchema`, `@objectstack/spec/system`) |
|
|
284
|
+
| `react` / `app-shell` | `resolveI18nLabel` | `resolveKeyedI18nLabel` | a resolver for the INLINE per-locale map (`{ en: 'Owner', 'zh-CN': '负责人' }`) against a BCP-47 locale |
|
|
285
|
+
| `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 |
|
|
286
|
+
|
|
287
|
+
`resolveI18nLabel` is the one where the collision had already started costing
|
|
288
|
+
something. rc.6 widened `I18nLabel` from `string` to
|
|
289
|
+
`string | Record< string, string >`, so the same authored value now reaches
|
|
290
|
+
either resolver — and each answers wrongly, silently, for the other's input: the
|
|
291
|
+
keyed one returns `undefined` for `{ en: 'Owner' }` (no `key`, no
|
|
292
|
+
`defaultValue`), and the spec's reads `key` / `defaultValue` / `params` as locale
|
|
293
|
+
tags. The rc.6 bump PR met this and aliased the spec's import as
|
|
294
|
+
`resolveInlineI18nLabel` in five files, with hand-written comments at two of
|
|
295
|
+
them. That is a review convention, which is what objectstack#4115 exists to
|
|
296
|
+
replace with a rule — so `Keyed` is now the counterpart of that `Inline`, and the
|
|
297
|
+
name says which vocabulary it resolves at every call site.
|
|
298
|
+
|
|
299
|
+
**Eleven keep their names and are now imported or derived from the spec** instead
|
|
300
|
+
of re-declared: `DATE_RANGE_PRESETS`, `NavigationMode`, `AddressValue`,
|
|
301
|
+
`BreakpointColumnMap`, `BreakpointOrderMap`, `KanbanConfig`, `CalendarConfig`,
|
|
302
|
+
`GanttConfig`, plus the three renamed above at their new names.
|
|
303
|
+
|
|
304
|
+
**Four of the copies were losing information, not just duplicating it.**
|
|
305
|
+
|
|
306
|
+
- **`GanttConfig` declared six keys and called itself canonical; rc.6's
|
|
307
|
+
`GanttConfigSchema` declares seventeen.** The eleven it never mentioned —
|
|
308
|
+
`parentField`, `typeField`, `baselineStartField`, `baselineEndField`,
|
|
309
|
+
`groupByField`, `resourceView`, `assigneeField`, `effortField`, `capacity`,
|
|
310
|
+
`quickFilters`, `autoZoomToFilter` — are all read by
|
|
311
|
+
`plugin-gantt/src/ObjectGantt.tsx`, through a local `GanttConfigEx`
|
|
312
|
+
intersection that existed only because this type did not carry them. It now
|
|
313
|
+
derives from the spec, with `timeSegments` (shift segmentation) as the one
|
|
314
|
+
genuinely local extension; the schema is `$loose` upstream, so that key is
|
|
315
|
+
legal metadata rather than a second dialect.
|
|
316
|
+
- **`GanttConfig.tooltipFields` carried the comment "not part of the upstream
|
|
317
|
+
GanttConfigSchema".** It is, as of rc.6, so the key now arrives from the spec.
|
|
318
|
+
- **`AddressValue` declared five of the spec's seven parts** — `countryCode` and
|
|
319
|
+
`formatted` were missing, under a comment already claiming to be "the part
|
|
320
|
+
names of `AddressSchema`". The widget still renders five inputs; binding the
|
|
321
|
+
type stops it from asserting the platform cannot store the other two, and makes
|
|
322
|
+
the `{ ...address }` write-through say so.
|
|
323
|
+
- **`DATE_RANGE_PRESETS` was `Object.keys(PRESET_RANGES)`,** a third copy of a
|
|
324
|
+
vocabulary the spec extracted in objectstack#4614 precisely to collapse — its
|
|
325
|
+
own doc comment names this module as one of the three. It is now the spec's
|
|
326
|
+
array by reference, and the local date-macro bounds table is pinned complete
|
|
327
|
+
against it with `satisfies`, so a preset the schema gains without bounds here
|
|
328
|
+
is a compile error rather than a filter that validates clean and then selects
|
|
329
|
+
nothing.
|
|
330
|
+
|
|
331
|
+
`NavigationMode` was one hop from the spec already (`NavigationConfig['mode']`);
|
|
332
|
+
it is bound directly, with a both-directions type pin that it stays the same type
|
|
333
|
+
as the config's own `mode`. `KanbanConfig` / `CalendarConfig` /
|
|
334
|
+
`BreakpointColumnMap` / `BreakpointOrderMap` were exact hand copies of `$strict`
|
|
335
|
+
schemas and are now re-exports — "still exact" is the argument for binding them,
|
|
336
|
+
since a copy with nothing to protect can only drift.
|
|
337
|
+
|
|
338
|
+
`GlobalFilterSchema` is the one ALLOW entry. It is the same spread-composition
|
|
339
|
+
dialect as `SelectOptionSchema` next to it, and it collided only because rc.6's
|
|
340
|
+
new refinement forced `.extend()` to be respelled as a `.shape` spread — which
|
|
341
|
+
moved a derivation the guard could see into an object literal it deliberately
|
|
342
|
+
does not descend into. The dialect is unchanged and its three divergences are
|
|
343
|
+
pinned; which side moves on the refinement itself is objectui#4165.
|
|
344
|
+
|
|
345
|
+
`@objectstack/spec` moves from `devDependencies` to `dependencies` in
|
|
346
|
+
`@object-ui/layout`: its public type surface now references the spec.
|
|
347
|
+
|
|
348
|
+
### Patch Changes
|
|
349
|
+
|
|
350
|
+
- f650253: `BaseSchema.ariaLabel` declares the keyed i18n vocabulary the renderer actually
|
|
351
|
+
resolves, `.disabled` accepts the predicate string it actually evaluates, and the
|
|
352
|
+
keyed shape finally has a name (objectui#4581)
|
|
353
|
+
|
|
354
|
+
Three slots on one base type had drifted from what the renderer does with them.
|
|
355
|
+
PR #4593 fixed `visible` and measured the rest; these are the rest.
|
|
356
|
+
|
|
357
|
+
`ariaLabel` was `string`, but `SchemaRenderer.tsx:111` resolves it with
|
|
358
|
+
`resolveKeyedI18nLabel`, whose input is the KEYED form
|
|
359
|
+
`{ key, defaultValue?, params? }` — a reference into a translation bundle. It is
|
|
360
|
+
now `string | KeyedI18nLabel`, and `KeyedI18nLabel` is a new exported type in
|
|
361
|
+
`@object-ui/types` rather than a fourth inline copy of one object literal: the
|
|
362
|
+
three that existed (`@object-ui/react`'s resolver, `@object-ui/layout`'s
|
|
363
|
+
`resolveLabel`, `@object-ui/app-shell`'s `t`-taking twin) were verified identical
|
|
364
|
+
in their object half first, and two of them now import the name.
|
|
365
|
+
|
|
366
|
+
The vocabulary matters more than the widening. `#4581` originally asked for
|
|
367
|
+
`string | I18nLabel`, and that spelling was withdrawn as measured-wrong: the
|
|
368
|
+
spec's `I18nLabel` is the INLINE LOCALE MAP (`string | Record<string, string>`),
|
|
369
|
+
a different vocabulary resolved against a BCP-47 locale by a different function
|
|
370
|
+
of a confusingly similar name. Under it the shipped keyed fixture type-checked
|
|
371
|
+
only vacuously — as a locale map whose "locales" are named `key` and
|
|
372
|
+
`defaultValue` — the same label carrying `params` was rejected outright, and a
|
|
373
|
+
genuine `{ en: 'Owner' }` compiled while rendering an EMPTY `aria-label`. Naming
|
|
374
|
+
the keyed shape is the declaration half of the fix objectui#4167 started on the
|
|
375
|
+
naming side; `@object-ui/app-shell`'s copy keeps its inline spelling for now
|
|
376
|
+
because an open PR has a pending change to that file, and the comment there says
|
|
377
|
+
so.
|
|
378
|
+
|
|
379
|
+
`disabled` was `boolean` on a key the renderer never reads as one:
|
|
380
|
+
`SchemaRenderer.tsx:466` evaluates it through the same `evaluateCondition` as
|
|
381
|
+
`visible`, and a `disabledOn?: string` sibling exists for the same reason. It is
|
|
382
|
+
now `boolean | string`. The asymmetry with `visible` was accidental rather than
|
|
383
|
+
deliberate.
|
|
384
|
+
|
|
385
|
+
Both are widenings on authored-input-dominant properties: authors gain a
|
|
386
|
+
spelling, nothing that type-checked before stops doing so, and readers already
|
|
387
|
+
coped with `any` through `BaseSchema`'s index signature. Three test fixtures that
|
|
388
|
+
had been casting past these declarations with `as unknown as BaseSchema` state
|
|
389
|
+
their values directly now, and the declared unions are pinned invariantly so
|
|
390
|
+
neither a missing widening nor an overshoot to `any` can pass unnoticed.
|
|
391
|
+
|
|
392
|
+
Declaring the vocabulary honestly also surfaced a real one: the `toggle`
|
|
393
|
+
renderer writes `aria-label` itself instead of going through SchemaRenderer's
|
|
394
|
+
resolver, and it forwarded the raw value. Invoked directly it emitted
|
|
395
|
+
`aria-label="[object Object]"` for a keyed label — announced verbatim by a
|
|
396
|
+
screen reader. It resolves now. Through `SchemaRenderer` the bug was invisible,
|
|
397
|
+
because SchemaRenderer injects its own resolved `aria-label` afterwards; a
|
|
398
|
+
downstream type-check sweep found it, not a test.
|
|
399
|
+
|
|
400
|
+
`BaseSchema.label` and `.description` are deliberately unchanged and pinned that
|
|
401
|
+
way. They receive the spec's inline `I18nLabel` from the view bridges, which is a
|
|
402
|
+
real defect, but resolving it belongs at the spec-to-schema boundary rather than
|
|
403
|
+
in this declaration — and that work is still blocked on a design question about
|
|
404
|
+
where the display locale enters, so it is not in this release.
|
|
405
|
+
|
|
406
|
+
- bb68488: 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
|
|
407
|
+
|
|
408
|
+
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.
|
|
409
|
+
|
|
410
|
+
| package | what an author can now write and see |
|
|
411
|
+
| ----------------------------- | --------------------------------------------------------------------------------------- |
|
|
412
|
+
| `@object-ui/layout` | `NavigationArea.label` — the sidebar area switcher's button and its tooltip |
|
|
413
|
+
| `@object-ui/plugin-list` | `ViewTab.label` — the inline pill row, and the mobile dropdown's trigger and menu items |
|
|
414
|
+
| `@object-ui/plugin-dashboard` | `DashboardWidget.title` — the widget card heading and its `title` attribute |
|
|
415
|
+
| `@object-ui/plugin-designer` | `DashboardWidget.title` — the widget card and the preview tile |
|
|
416
|
+
| `@object-ui/app-shell` | `ActionParam.label` **and** each `ActionParam.options[].label` |
|
|
417
|
+
|
|
418
|
+
**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.
|
|
419
|
+
|
|
420
|
+
Two consequences worth knowing:
|
|
421
|
+
|
|
422
|
+
- **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.
|
|
423
|
+
- **`@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.
|
|
424
|
+
|
|
425
|
+
- Updated dependencies [ceccdcf]
|
|
426
|
+
- Updated dependencies [d6e5124]
|
|
427
|
+
- Updated dependencies [debad27]
|
|
428
|
+
- Updated dependencies [dc2aa3e]
|
|
429
|
+
- Updated dependencies [ee66e2e]
|
|
430
|
+
- Updated dependencies [ee26e65]
|
|
431
|
+
- Updated dependencies [5900ac5]
|
|
432
|
+
- Updated dependencies [f650253]
|
|
433
|
+
- Updated dependencies [3d9769a]
|
|
434
|
+
- Updated dependencies [8f85f8b]
|
|
435
|
+
- Updated dependencies [d0c3b26]
|
|
436
|
+
- Updated dependencies [3fc2971]
|
|
437
|
+
- Updated dependencies [aca27fa]
|
|
438
|
+
- Updated dependencies [dde7283]
|
|
439
|
+
- Updated dependencies [4dadf0d]
|
|
440
|
+
- Updated dependencies [ae10a01]
|
|
441
|
+
- Updated dependencies [92876f0]
|
|
442
|
+
- Updated dependencies [f279deb]
|
|
443
|
+
- Updated dependencies [4b70d28]
|
|
444
|
+
- Updated dependencies [eb7f586]
|
|
445
|
+
- Updated dependencies [e901131]
|
|
446
|
+
- Updated dependencies [d9d3463]
|
|
447
|
+
- Updated dependencies [2a40f69]
|
|
448
|
+
- Updated dependencies [bec3e14]
|
|
449
|
+
- Updated dependencies [613b167]
|
|
450
|
+
- Updated dependencies [b4d3c22]
|
|
451
|
+
- Updated dependencies [1f9b905]
|
|
452
|
+
- Updated dependencies [cb13400]
|
|
453
|
+
- Updated dependencies [bc64bfe]
|
|
454
|
+
- Updated dependencies [abb0f81]
|
|
455
|
+
- Updated dependencies [38ab505]
|
|
456
|
+
- Updated dependencies [3e19fe7]
|
|
457
|
+
- Updated dependencies [b953a97]
|
|
458
|
+
- Updated dependencies [d7f3e30]
|
|
459
|
+
- Updated dependencies [7e4f0e5]
|
|
460
|
+
- Updated dependencies [a84385b]
|
|
461
|
+
- Updated dependencies [45e1949]
|
|
462
|
+
- Updated dependencies [92250d6]
|
|
463
|
+
- Updated dependencies [c1d939f]
|
|
464
|
+
- Updated dependencies [49ae9f4]
|
|
465
|
+
- Updated dependencies [a3ae404]
|
|
466
|
+
- Updated dependencies [bfdf3d4]
|
|
467
|
+
- Updated dependencies [bb68488]
|
|
468
|
+
- Updated dependencies [b1e42d0]
|
|
469
|
+
- Updated dependencies [2459a3e]
|
|
470
|
+
- Updated dependencies [d6aa172]
|
|
471
|
+
- Updated dependencies [fe52a04]
|
|
472
|
+
- Updated dependencies [3f5f87c]
|
|
473
|
+
- Updated dependencies [f5e1143]
|
|
474
|
+
- Updated dependencies [f148a64]
|
|
475
|
+
- Updated dependencies [bb68488]
|
|
476
|
+
- Updated dependencies [9461dd3]
|
|
477
|
+
- Updated dependencies [47f551b]
|
|
478
|
+
- Updated dependencies [ab04728]
|
|
479
|
+
- Updated dependencies [5bf09fd]
|
|
480
|
+
- @object-ui/react@17.5.0
|
|
481
|
+
- @object-ui/components@17.5.0
|
|
482
|
+
- @object-ui/core@17.5.0
|
|
483
|
+
- @object-ui/types@17.5.0
|
|
484
|
+
|
|
485
|
+
## 17.4.0
|
|
486
|
+
|
|
487
|
+
### Patch Changes
|
|
488
|
+
|
|
489
|
+
- 6bd6a4d: `registerLayout()` 的 `inputs` 声明面与渲染器实现对齐 —— 校验器不再对正确写法报假诊断
|
|
490
|
+
|
|
491
|
+
`inputs` 是**作者面**:`sdui-parser/src/validate.ts` 拿一个节点的顶层属性逐个比对
|
|
492
|
+
`comp.inputs`,不在其中的报 `unknown-prop`,类型不符的报 `type-mismatch`;设计器面板、
|
|
493
|
+
`sdui.manifest.json`、生成的 JSX 类型也都由它派生。所以声明面写错的代价不是"文档不全",
|
|
494
|
+
而是**校验器对着能正常渲染的 schema 说谎**(objectui#3972,与 objectui#3900 同族、换到属性面)。
|
|
495
|
+
|
|
496
|
+
两处修正,都是按渲染器**实际读点**审计出来的:
|
|
497
|
+
|
|
498
|
+
- **`page-header` 补声明 `icon` 与 `actions`。** 两个键三面齐全:渲染器真读
|
|
499
|
+
(`PageHeader.tsx:117` 取参 `:224-226` 渲染 icon;`:119` 取参 `:192-196` 把 actions
|
|
500
|
+
委派给 `record:quick_actions`)、`@objectstack/spec/ui` 的 `PageHeaderProps` 声明、
|
|
501
|
+
`ManifestInputType` 表达得了(`string` / `array`)。`content/docs/layout/page-header.mdx`
|
|
502
|
+
的 Component Props 段落也把两者写成公开契约,而该文档页**唯一**的 live demo
|
|
503
|
+
(`layout-page-header/pageheader-with-actions`)就写着 `"icon": "users"` —— 于是仓内
|
|
504
|
+
自己的文档示例每次过 manifest 门都收到一条 `unknown-prop: page-header has no prop "icon"`。
|
|
505
|
+
`actions` 的类型与 canonical 的 `page:header` 逐字一致(`type: 'array'`),一个概念一个键
|
|
506
|
+
一种类型。
|
|
507
|
+
- **`navigation-renderer` 的 `items` 由 `type: 'object'` 改为 `'array'`。**
|
|
508
|
+
`NavigationRendererProps.items` 是 `NavigationItem[]`(`NavigationRenderer.tsx:108`),
|
|
509
|
+
而 `checkType` 对 `'object'` 判 `typeof value === 'object' && !Array.isArray(value)`、
|
|
510
|
+
对 `'array'` 判 `Array.isArray`(`validate.ts:124-129`)—— 两者互斥。所以旧声明对这个
|
|
511
|
+
渲染器**唯一能渲染的形状**报 `type-mismatch ... expected an object`,而对真会让它崩的
|
|
512
|
+
对象形状一言不发。这不是 objectui#3832 的表达力问题:`ManifestInputType` 本来就有
|
|
513
|
+
`'array'`,只是声明写错了一个表达得了的类型。
|
|
514
|
+
|
|
515
|
+
**刻意不声明**的键同样被钉住,因为"照抄 spec 的 shape"是这条修复最容易滑进去的反向缺陷:
|
|
516
|
+
`breadcrumb`(spec 有、这个渲染器零读点 —— 声明它就是 objectui#3829 的缺陷方向)、
|
|
517
|
+
`showBack` / `action` / `description`(渲染器读、spec 无此键 —— 声明任一个就是在开第二套
|
|
518
|
+
方言,正是 objectui#3226 收窄要防的事)、`aria`(每个 block 都因同一理由省略的可访问性逃逸口)。
|
|
519
|
+
|
|
520
|
+
渲染输出逐字节不变:渲染路径从不读 `inputs`。变化只在校验/设计器/清单这一侧,且两个方向
|
|
521
|
+
都钉了 —— 正确写法放行的同时,`description` 这类刻意不声明的键仍报 `unknown-prop`、
|
|
522
|
+
`items` 写成对象仍报 `type-mismatch`(诊断没有被弄哑)。
|
|
523
|
+
|
|
524
|
+
- 876e3f7: `@object-ui/layout` no longer tells bundlers it has no side effects while registering components at load time (objectui#3899)
|
|
525
|
+
|
|
526
|
+
The published manifest declared `"sideEffects": false` — a promise that no module
|
|
527
|
+
in the package does anything on evaluation, so any module whose exports go unused
|
|
528
|
+
may be dropped whole. But `src/index.ts` ends with a bare
|
|
529
|
+
`try { registerLayout(); } catch {}`, and that call is the only thing that puts
|
|
530
|
+
`page-header`, `page:card`, `app-shell`, `responsive-grid`,
|
|
531
|
+
`navigation-renderer` and `app-schema-renderer` into the `ComponentRegistry`.
|
|
532
|
+
|
|
533
|
+
Both statements cannot be true, and a bundler that believes the manifest is
|
|
534
|
+
right to delete the registration. Measured with the repo's own bundler by
|
|
535
|
+
building `import '@object-ui/layout';` — the side-effect-only import, i.e. the
|
|
536
|
+
documented "import it to register" pattern:
|
|
537
|
+
|
|
538
|
+
- `sideEffects: false` — the bundle is **0 bytes**. Zero registrations, exit
|
|
539
|
+
code 0, no warning.
|
|
540
|
+
- after this change — the bundle keeps all six `ComponentRegistry.register`
|
|
541
|
+
calls.
|
|
542
|
+
|
|
543
|
+
A dropped registration does not fail where it happened. It surfaces later as a
|
|
544
|
+
red `Unknown component type` panel (OBJUI-001) on a fully green build, with
|
|
545
|
+
nothing in the build log to connect the two. Nobody had been bitten yet only
|
|
546
|
+
because every consumer today ALSO imports a named export, which forces the module
|
|
547
|
+
to be evaluated regardless — coincidence, not design. objectui#3787 met the
|
|
548
|
+
hazard and routed around it by calling `registerLayout()` explicitly.
|
|
549
|
+
|
|
550
|
+
`sideEffects` is now the narrowest honest answer: an array naming the modules
|
|
551
|
+
that actually register, rather than `true`, which would be honest but would hand
|
|
552
|
+
the whole package to every bundler as unshakeable.
|
|
553
|
+
|
|
554
|
+
```json
|
|
555
|
+
"sideEffects": ["./dist/index.js", "./dist/index.umd.cjs", "./src/index.ts"]
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
All three are load-bearing, and the set is derived from the manifest rather than
|
|
559
|
+
guessed:
|
|
560
|
+
|
|
561
|
+
- `./dist/index.js` and `./dist/index.umd.cjs` are every JS file the manifest's
|
|
562
|
+
own entry fields point at (`module` / `main` / `exports` import+require). The
|
|
563
|
+
library build inlines everything into those two files, so there is no third
|
|
564
|
+
chunk to name.
|
|
565
|
+
- `./src/index.ts` is not published (`files` ships `dist` only) but is bundled
|
|
566
|
+
for real: `apps/console` and `examples/console-starter` both alias the
|
|
567
|
+
specifier straight at `packages/layout/src`, and a bundler reads this same
|
|
568
|
+
manifest for those files. With only the published paths declared, the console's
|
|
569
|
+
alias shape still produced a 0-byte bundle.
|
|
570
|
+
|
|
571
|
+
What deliberately did NOT change: the load-time `registerLayout()` itself.
|
|
572
|
+
Replacing it with an explicit registration API is the opposite direction — it
|
|
573
|
+
eliminates the side effect instead of declaring it, and it is breaking for any
|
|
574
|
+
consumer relying on automatic registration. objectui#3899 leaves that call to the
|
|
575
|
+
maintainer, and the two steps do not conflict: once the manifest tells the truth,
|
|
576
|
+
the migration to explicit registration can happen whenever it is wanted.
|
|
577
|
+
|
|
578
|
+
A new pin (`packages/layout/src/__tests__/side-effects-manifest.test.ts`) runs a
|
|
579
|
+
real bundler build per entry form and asserts the registrations survive a
|
|
580
|
+
side-effect-only import, with a `sideEffects: false` control per form asserting
|
|
581
|
+
they are dropped — so the pin cannot pass because the bundler stopped shaking
|
|
582
|
+
anything. The required set is derived from the manifest's own entry fields, so a
|
|
583
|
+
renamed build output or a new `exports` subpath fails as a missing declaration
|
|
584
|
+
instead of drifting silently.
|
|
585
|
+
|
|
586
|
+
- f3b2874: `navigation-renderer` 的 `items` 声明为 `required: true` —— 校验器不再放过必崩的节点
|
|
587
|
+
|
|
588
|
+
`items` 在组件侧是**非可选**的 `NavigationItem[]`(`NavigationRendererProps.items`,无 `?`),
|
|
589
|
+
渲染器也不给默认值,而注册声明一直没写 `required`。`sdui-parser` 只在 `input.required` 为真时
|
|
590
|
+
报 `missing-required-prop`(`validate.ts:55-64`),于是 `{ "type": "navigation-renderer" }`
|
|
591
|
+
这个节点**校验零诊断、渲染直接抛**:第一处无守卫的读点是 `pinnedItems` memo 里的
|
|
592
|
+
`collectPinnedItems(filteredItems)`(`NavigationRenderer.tsx:1242` → `:1410` 的
|
|
593
|
+
`for (const item of items)`),实测 `TypeError: items is not iterable`。
|
|
594
|
+
(`resolveActiveNavItem` memo 挡得住 —— 它的 `visit` 首行是 `if (!nodes) return`;
|
|
595
|
+
`:1247` 的 `filteredItems.slice()` 同样会抛,但根本走不到。)
|
|
596
|
+
|
|
597
|
+
这是 objectui#3972(键的**存在**与**类型**三面对齐)的第四面:**可选性**。#3972 与
|
|
598
|
+
objectui#3900 都是删除假诊断,这一条相反——它是**收紧**。
|
|
599
|
+
|
|
600
|
+
**blast radius:** 今天省略 `items` 写 `navigation-renderer` 的 schema,会新增一条
|
|
601
|
+
**error** 级 `missing-required-prop`。受影响面是仓外按 `inputs` / `packages/layout/README.md`
|
|
602
|
+
做 schema 驱动的消费者(仓内没有任何 JSON 元数据把它当 schema 节点写,React 调用侧的必填由
|
|
603
|
+
TS 兜住;`examples/schema-catalog` 的 `not-a-container` 对照节点补了 `items: []`,使它只剩
|
|
604
|
+
那一处故意植入的缺陷)。而这条诊断新拦下的形状,**恰好等于渲染必然崩溃的形状** —— 让作者
|
|
605
|
+
(尤其是 AI 作者)在发布期就听到运行期注定要发生的失败,正是 `missing-required-prop` 存在
|
|
606
|
+
的理由。若某个消费者确实想要"缺 items 就渲染空导航",那是给组件加 `= []` 默认值的另一条路
|
|
607
|
+
(objectui#3987 里记了,与本改动不互斥),而不是让校验器继续沉默。
|
|
608
|
+
|
|
609
|
+
`basePath` **保持可选**并被钉成对照:渲染器真的给了它默认值(`basePath = ''`)。`required`
|
|
610
|
+
是逐个属性从组件读出的事实,不是一刀切——否则这道门会开始拒绝完全能渲染的 schema,作者就会
|
|
611
|
+
学着无视 `missing-required-prop`,正如 #3972 里他们被教着无视 `type-mismatch`。
|
|
612
|
+
|
|
613
|
+
- 82f8dff: `page-header` 注册补 `isContainer: true` —— 校验器不再对文档承诺的 children 写法报 `not-a-container`
|
|
614
|
+
|
|
615
|
+
`PageHeader` 一直**有意**把 `schema.children` 渲染进右侧动作槽(`PageHeader.tsx:182`,
|
|
616
|
+
`record:quick_actions` 嵌在 `page:header.children` 下就是靠它),
|
|
617
|
+
`content/docs/layout/page-header.mdx` 把该槽的优先级(`action` → React `children` →
|
|
618
|
+
`actions` → schema children)写成公开契约,该文档页唯一的 live demo
|
|
619
|
+
(`layout-page-header/pageheader-with-actions`)正是这个形状且实测正常渲染。但
|
|
620
|
+
`packages/layout/src/index.ts` 的注册漏了 `isContainer: true`。
|
|
621
|
+
|
|
622
|
+
漏这个 flag 从来没有挡住任何渲染 —— 渲染路径根本不读它(`SchemaRenderer` 把
|
|
623
|
+
`children` 从 React props 里剥掉当元数据,而始终把整个节点作为 `schema` 传下去,
|
|
624
|
+
`PageHeader` 自己再把 `schema.children` 放回槽里)。它的消费者在别处:`sdui-parser`
|
|
625
|
+
的 `not-a-container` 诊断、Studio 调色板元数据、react-page 标签表。所以真正的后果是
|
|
626
|
+
**校验器在说谎**:作者照文档写出能正常渲染的 schema,却拿到一条
|
|
627
|
+
"`page-header` does not accept children" 的 warning;信了这条 warning 去掉 children,
|
|
628
|
+
右槽就空掉。而会说谎的 warning 比缺一条 warning 更贵 —— 它训练作者(尤其 AI 作者)
|
|
629
|
+
连真实的 `not-a-container`(那些确实不收子节点的组件)一起无视。
|
|
630
|
+
|
|
631
|
+
这不是在 spec 之外新开作者面:`children` 是 objectui JSON 协议里**每个节点**的基础属性
|
|
632
|
+
(`sdui-parser/src/validate.ts` 的 `BASE_PROPS` 把它和 `type`/`id`/`className` 并列),
|
|
633
|
+
不是 `PageHeaderProps` 的键。所以这个 flag 回答的是协议层面的"该节点是否接受子节点列表",
|
|
634
|
+
而对这个组件,答案一直是"是"。维护者 2026-08-09 就 objectui#3900 的 A/B 分叉裁定 A 案,
|
|
635
|
+
理由同上。
|
|
636
|
+
|
|
637
|
+
行为面变化极窄:注册元数据一个布尔位。渲染输出逐字节不变(渲染路径不读该 flag);
|
|
638
|
+
`sdui-parser` 对带 children 的 `page-header` 少报一条 warning;设计器把它当容器对待
|
|
639
|
+
(即它本来的样子)。canonical 的 `page:header`(`@object-ui/components`)不在此列且刻意不动
|
|
640
|
+
—— 那个渲染器完全不读 `schema.children`,所以它没有 `isContainer` 是正确的。
|
|
641
|
+
|
|
642
|
+
两个方向都已钉住:文档 demo 走应用真实构建的 manifest 后不再产生 `not-a-container`,
|
|
643
|
+
而一个真正不收子节点的组件(`navigation-renderer`)带 children 时诊断照旧触发 ——
|
|
644
|
+
后者是前者的对照,保证这条修复不是把诊断弄哑了。
|
|
645
|
+
|
|
646
|
+
- Updated dependencies [794c497]
|
|
647
|
+
- Updated dependencies [993336f]
|
|
648
|
+
- Updated dependencies [f0a625a]
|
|
649
|
+
- Updated dependencies [b5980f4]
|
|
650
|
+
- Updated dependencies [8aad9fd]
|
|
651
|
+
- Updated dependencies [6719877]
|
|
652
|
+
- Updated dependencies [56ff091]
|
|
653
|
+
- Updated dependencies [0cbdca8]
|
|
654
|
+
- Updated dependencies [d229dfa]
|
|
655
|
+
- Updated dependencies [ecae400]
|
|
656
|
+
- Updated dependencies [4bc6c23]
|
|
657
|
+
- Updated dependencies [d3e738a]
|
|
658
|
+
- Updated dependencies [c3b01a7]
|
|
659
|
+
- Updated dependencies [7ed3360]
|
|
660
|
+
- Updated dependencies [0fa5e4d]
|
|
661
|
+
- Updated dependencies [5bfaabd]
|
|
662
|
+
- Updated dependencies [e06810e]
|
|
663
|
+
- Updated dependencies [ab3ad4f]
|
|
664
|
+
- Updated dependencies [c2fd122]
|
|
665
|
+
- Updated dependencies [e24d767]
|
|
666
|
+
- Updated dependencies [aca561a]
|
|
667
|
+
- Updated dependencies [48132f7]
|
|
668
|
+
- Updated dependencies [0ef9dfd]
|
|
669
|
+
- Updated dependencies [1d723e3]
|
|
670
|
+
- Updated dependencies [0109f54]
|
|
671
|
+
- Updated dependencies [7e5bb5d]
|
|
672
|
+
- Updated dependencies [fbc23e0]
|
|
673
|
+
- Updated dependencies [e6fdbdc]
|
|
674
|
+
- Updated dependencies [54233b1]
|
|
675
|
+
- Updated dependencies [97b63d7]
|
|
676
|
+
- Updated dependencies [6bb454a]
|
|
677
|
+
- Updated dependencies [523be48]
|
|
678
|
+
- Updated dependencies [7e2b7e9]
|
|
679
|
+
- Updated dependencies [c1e1e6b]
|
|
680
|
+
- @object-ui/components@17.4.0
|
|
681
|
+
- @object-ui/react@17.4.0
|
|
682
|
+
- @object-ui/core@17.4.0
|
|
683
|
+
- @object-ui/types@17.4.0
|
|
684
|
+
|
|
3
685
|
## 17.3.0
|
|
4
686
|
|
|
5
687
|
### Minor Changes
|
|
@@ -17,6 +699,7 @@
|
|
|
17
699
|
the switcher and instead rendered as a selectable, empty area.
|
|
18
700
|
|
|
19
701
|
## What changed
|
|
702
|
+
|
|
20
703
|
- **Area visibility is now derived, not authored.** An area appears in the
|
|
21
704
|
switcher iff at least one of its navigation items survives the exact
|
|
22
705
|
item-level guards `NavigationRenderer` applies: the `visible` expression,
|
|
@@ -1012,6 +1695,7 @@
|
|
|
1012
1695
|
- b703480: feat(layout): smoother sidebar transitions
|
|
1013
1696
|
|
|
1014
1697
|
`SidebarNav` now animates the previously-instant state changes:
|
|
1698
|
+
|
|
1015
1699
|
- Active-state colour swap on `SidebarMenuButton` /
|
|
1016
1700
|
`SidebarMenuSubButton` is wrapped in `transition-colors duration-150`
|
|
1017
1701
|
so navigating between rows glides rather than snaps.
|
|
@@ -1091,6 +1775,7 @@
|
|
|
1091
1775
|
record detail page (lead, opportunity, future account/contact/case).
|
|
1092
1776
|
Before this change it had two problems that bled into every custom
|
|
1093
1777
|
page across the product:
|
|
1778
|
+
|
|
1094
1779
|
1. **Quadruple registration**: `@object-ui/layout` registered both
|
|
1095
1780
|
`page-header` and `page:header`, and `@object-ui/components`
|
|
1096
1781
|
independently registered `page:header` (and `page:section`).
|
|
@@ -1101,6 +1786,7 @@
|
|
|
1101
1786
|
than the default detail view it was meant to supersede.
|
|
1102
1787
|
|
|
1103
1788
|
This commit:
|
|
1789
|
+
|
|
1104
1790
|
- Removes the `@object-ui/layout` `page:header` registration. The
|
|
1105
1791
|
layout package keeps the legacy kebab-cased `page-header` alias only.
|
|
1106
1792
|
The canonical renderer now lives in `@object-ui/components` and is
|
|
@@ -1179,6 +1865,7 @@
|
|
|
1179
1865
|
- d714e85: Lookup display-name resolution now falls back through a Salesforce-style chain
|
|
1180
1866
|
when an `$expand`'d reference object lacks a top-level `name`/`label`/
|
|
1181
1867
|
`display_name`/`title` field:
|
|
1868
|
+
|
|
1182
1869
|
1. Standard display fields (existing behaviour)
|
|
1183
1870
|
2. `salutation first_name last_name` composite — handles person records that
|
|
1184
1871
|
only carry first/last name parts
|
|
@@ -1237,6 +1924,7 @@ Lin` and no `name` field now renders as `Bob Lin` everywhere — instead of
|
|
|
1237
1924
|
pages without a record context are unaffected.
|
|
1238
1925
|
|
|
1239
1926
|
- 2bd45af: feat(shell): main becomes the scroll container; record tabs are sticky
|
|
1927
|
+
|
|
1240
1928
|
- `AppShell`'s SidebarProvider wrapper is now constrained to viewport
|
|
1241
1929
|
height (`h-svh overflow-hidden`) instead of expanding with content via
|
|
1242
1930
|
the default `min-h-svh`. This makes the inner `<main>` (which is
|
|
@@ -1259,6 +1947,7 @@ Lin` and no `name` field now renders as `Bob Lin` everywhere — instead of
|
|
|
1259
1947
|
### Patch Changes
|
|
1260
1948
|
|
|
1261
1949
|
- 6b683c8: fix(detail): clean up record page rendering
|
|
1950
|
+
|
|
1262
1951
|
- Drop `ai:chat_window` from the protocol-component placeholder list. The
|
|
1263
1952
|
floating chat overlay (plugin-chatbot) is the canonical AI entry point;
|
|
1264
1953
|
inline page schemas that still reference `ai:chat_window` now surface
|
|
@@ -1272,6 +1961,7 @@ Lin` and no `name` field now renders as `Bob Lin` everywhere — instead of
|
|
|
1272
1961
|
as a fallback so both inlined and raw-bag schema shapes are supported.
|
|
1273
1962
|
|
|
1274
1963
|
- 0d8eb98: feat(detail): Salesforce-style record header + section field grid
|
|
1964
|
+
|
|
1275
1965
|
- `page:header` now renders an icon chip (resolves Lucide names via
|
|
1276
1966
|
`LazyIcon`) plus subtitle, so detail pages can show
|
|
1277
1967
|
"Name / Company" without an extra component.
|
|
@@ -1430,6 +2120,7 @@ Lin` and no `name` field now renders as `Bob Lin` everywhere — instead of
|
|
|
1430
2120
|
Library builds (vite lib mode) now externalize every non-relative import instead of bundling third-party CJS dependencies into the published dist. This avoids inlined `require("react")` / `require("react-dom")` calls that cause `Calling \`require\` for "react" in an environment that doesn't expose the \`require\` function` runtime errors when consumer apps re-bundle the published dist.
|
|
1431
2121
|
|
|
1432
2122
|
Specifically fixes:
|
|
2123
|
+
|
|
1433
2124
|
- `@object-ui/plugin-dashboard` no longer inlines `react-grid-layout` (and its transitive `react-draggable` / `react-resizable` CJS bundles). `react-grid-layout` is now declared as a peer dependency so consumers install a single ESM-friendly copy.
|
|
1434
2125
|
- `@object-ui/components`, `@object-ui/plugin-calendar`, `@object-ui/plugin-charts`, `@object-ui/plugin-designer` no longer inline `react-i18next` / `i18next` / `use-sync-external-store` CJS shims.
|
|
1435
2126
|
- All plugin packages now use a unified `external: (id) => !/^[./]/.test(id) && !id.startsWith(__dirname)` rule, ensuring future additions of CJS deps are automatically externalized.
|