@object-ui/app-shell 11.3.0 → 11.5.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.
Files changed (90) hide show
  1. package/CHANGELOG.md +700 -0
  2. package/README.md +32 -0
  3. package/dist/chrome/KeyboardShortcutsDialog.js +2 -1
  4. package/dist/console/AppContent.js +145 -26
  5. package/dist/console/ConsoleShell.js +25 -3
  6. package/dist/console/home/CloudOnboardingNext.d.ts +9 -0
  7. package/dist/console/home/CloudOnboardingNext.js +14 -4
  8. package/dist/console/home/HomePage.js +34 -7
  9. package/dist/console/organizations/CreateWorkspaceDialog.js +33 -3
  10. package/dist/console/organizations/OrganizationsPage.js +16 -7
  11. package/dist/context/CommandPaletteProvider.js +2 -1
  12. package/dist/hooks/useConsoleActionRuntime.js +32 -3
  13. package/dist/hooks/useObjectActions.js +16 -4
  14. package/dist/index.d.ts +1 -0
  15. package/dist/index.js +3 -0
  16. package/dist/layout/AppHeader.js +13 -5
  17. package/dist/layout/AppSidebar.js +10 -4
  18. package/dist/observability/sentry.d.ts +5 -0
  19. package/dist/observability/sentry.js +6 -1
  20. package/dist/preview/DraftChangesPanel.d.ts +31 -1
  21. package/dist/preview/DraftChangesPanel.js +146 -18
  22. package/dist/urlParams.d.ts +68 -0
  23. package/dist/urlParams.js +76 -0
  24. package/dist/utils/appRoute.d.ts +15 -0
  25. package/dist/utils/appRoute.js +22 -0
  26. package/dist/utils/deriveRelatedLists.d.ts +20 -5
  27. package/dist/utils/deriveRelatedLists.js +31 -13
  28. package/dist/utils/index.d.ts +1 -1
  29. package/dist/utils/index.js +1 -1
  30. package/dist/utils/pageTabsUrlSync.d.ts +32 -0
  31. package/dist/utils/pageTabsUrlSync.js +43 -0
  32. package/dist/utils/recordFormNavigation.d.ts +40 -0
  33. package/dist/utils/recordFormNavigation.js +30 -0
  34. package/dist/utils/resolveViewId.d.ts +23 -0
  35. package/dist/utils/resolveViewId.js +37 -0
  36. package/dist/utils/warnSuppressedListNav.d.ts +10 -0
  37. package/dist/utils/warnSuppressedListNav.js +40 -0
  38. package/dist/views/InterfaceListPage.d.ts +1 -0
  39. package/dist/views/InterfaceListPage.js +7 -5
  40. package/dist/views/ObjectDataPage.d.ts +29 -0
  41. package/dist/views/ObjectDataPage.js +227 -0
  42. package/dist/views/ObjectView.js +65 -13
  43. package/dist/views/RecordDetailView.js +191 -123
  44. package/dist/views/RecordFormPage.js +7 -1
  45. package/dist/views/RelatedRecordActionsBridge.d.ts +33 -0
  46. package/dist/views/RelatedRecordActionsBridge.js +147 -0
  47. package/dist/views/metadata-admin/PackagesPage.js +18 -7
  48. package/dist/views/metadata-admin/PermissionMatrixEditor.d.ts +18 -1
  49. package/dist/views/metadata-admin/PermissionMatrixEditor.js +73 -14
  50. package/dist/views/metadata-admin/ResourceEditPage.js +39 -0
  51. package/dist/views/metadata-admin/i18n.d.ts +12 -21
  52. package/dist/views/metadata-admin/i18n.js +557 -6
  53. package/dist/views/metadata-admin/inspectors/AppNavInspector.d.ts +11 -4
  54. package/dist/views/metadata-admin/inspectors/AppNavInspector.js +141 -7
  55. package/dist/views/metadata-admin/inspectors/FlowReferenceField.d.ts +14 -0
  56. package/dist/views/metadata-admin/inspectors/FlowReferenceField.js +76 -5
  57. package/dist/views/metadata-admin/inspectors/ObjectFieldInspector.js +60 -30
  58. package/dist/views/metadata-admin/inspectors/flow-node-config.d.ts +8 -1
  59. package/dist/views/metadata-admin/inspectors/flow-node-config.js +3 -2
  60. package/dist/views/metadata-admin/inspectors/nav-target.d.ts +52 -0
  61. package/dist/views/metadata-admin/inspectors/nav-target.js +149 -0
  62. package/dist/views/metadata-admin/nav-selection.d.ts +20 -0
  63. package/dist/views/metadata-admin/nav-selection.js +81 -0
  64. package/dist/views/metadata-admin/permission-slice.d.ts +66 -0
  65. package/dist/views/metadata-admin/permission-slice.js +70 -0
  66. package/dist/views/metadata-admin/previews/AppNavCanvas.js +19 -7
  67. package/dist/views/metadata-admin/previews/AppPreview.js +4 -2
  68. package/dist/views/metadata-admin/previews/FlowRunsPanel.d.ts +16 -7
  69. package/dist/views/metadata-admin/previews/FlowRunsPanel.js +18 -2
  70. package/dist/views/studio-design/BuilderLanding.d.ts +15 -0
  71. package/dist/views/studio-design/BuilderLanding.js +126 -0
  72. package/dist/views/studio-design/ObjectFormDesigner.d.ts +33 -0
  73. package/dist/views/studio-design/ObjectFormDesigner.js +231 -0
  74. package/dist/views/studio-design/ObjectSettingsPanel.d.ts +30 -0
  75. package/dist/views/studio-design/ObjectSettingsPanel.js +46 -0
  76. package/dist/views/studio-design/ObjectValidationsPanel.d.ts +30 -0
  77. package/dist/views/studio-design/ObjectValidationsPanel.js +80 -0
  78. package/dist/views/studio-design/PackageIdInput.d.ts +31 -0
  79. package/dist/views/studio-design/PackageIdInput.js +40 -0
  80. package/dist/views/studio-design/StudioDesignSurface.d.ts +13 -0
  81. package/dist/views/studio-design/StudioDesignSurface.js +962 -145
  82. package/dist/views/studio-design/metadataError.d.ts +23 -0
  83. package/dist/views/studio-design/metadataError.js +44 -0
  84. package/dist/views/studio-design/packageSurfaces.d.ts +49 -0
  85. package/dist/views/studio-design/packageSurfaces.js +34 -0
  86. package/dist/views/studio-design/packages-io.d.ts +38 -0
  87. package/dist/views/studio-design/packages-io.js +73 -0
  88. package/dist/views/studio-design/skeletons.d.ts +16 -0
  89. package/dist/views/studio-design/skeletons.js +51 -0
  90. package/package.json +42 -39
package/CHANGELOG.md CHANGED
@@ -1,5 +1,705 @@
1
1
  # @object-ui/app-shell — Changelog
2
2
 
3
+ ## 11.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 544d8eb: Add the app → Studio reverse bridge (ADR-0080): workspace admins see a "Design in Studio" entry in the app top bar that deep-links to the running app's owning package on the Studio design surface (`/studio/:packageId/data`). Hidden for non-admins and for apps with no owning package; package writability stays server-side (read-only packages open as browse-only).
8
+ - 6fffd3d: Client-side data-invalidation bus — refresh data, don't rebuild UI (objectui#2269 P1).
9
+
10
+ - `@object-ui/react` gains the bus: `notifyDataChanged({objectName, recordId?})`, `useDataInvalidation(objectName, recordId?)` (reader nonce), `subscribeDataChanges`, and `useMutationInvalidationBridge(dataSource)` which fans every dataSource write (`MutationEvent`) onto the bus. The bus also dispatches the legacy `objectui:related-changed` window event, so pre-bus listeners keep working.
11
+ - The `key={refreshKey}` remount of `RecordDetailView` (AppContent) and the `key={actionRefreshKey}` remount of `DetailView` (RecordDetailView) are GONE: record data now refetches in place via the bus — scroll, collapsed sections, tabs and in-progress inline edits survive every save/action/undo. All nine action-success bumps became precisely-scoped `notifyDataChanged` calls; undo/redo use the operation's own `objectName`/`recordId`.
12
+ - `RelatedCountStore` is wired to the bus (tab count badges refetch after any change to their object) and its `useSyncExternalStore` snapshot is now a monotonic version — previously it returned the same `Map` reference, so `emit()` never re-rendered subscribers and invalidations left badges stale; `useRelatedCountVersion()` is exported and drives the probe effect's re-fetch.
13
+ - app-shell also gains the reserved URL-param registry (`urlParams.ts` — `form`/`formObject`/`formLink`/`tab`/`recordId`/`palette`/`shortcuts` constants replace scattered string literals) and AGENTS.md Commandment #8 (UI-state classification: state that must survive a data refresh may never live only in an uncontrolled component).
14
+
15
+ - 9255686: Record detail tabs are URL-addressable (`?tab=`) and survive subtree remounts (objectui#2257, ADR-0054 C3).
16
+
17
+ - `buildDefaultTabs` emits STABLE semantic tab values (`details` / `related:<child>` / `related` / `activity` / `history`) instead of leaving the renderer to synthesize index-derived ones.
18
+ - `PageTabsRenderer` honors `item.value`, a host-provided `schema.defaultTab` (validated against actual tabs) and `schema.onTabChange`; index fallback kept for authored schemas without values.
19
+ - app-shell `RecordDetailView` restores the active tab from `?tab=` and writes it back with `replace` (tab switches never stack history), via the pure `withPageTabsUrlSync` page-tree injector (never mutates authored/memoized page schemas). Legacy `DetailView.autoTabs` wired to the same contract (`defaultTab`/`onTabChange`).
20
+ - Fixes the tab strip resetting to Details after save-refresh remounts (`refreshKey`-style) and dev-StrictMode URL churn; enables `?tab=` deep links; invalid values fall back to Details.
21
+
22
+ - 6c1ad9e: Record task flows open as derived overlays with lossless return (framework#2604, extends framework#2578).
23
+
24
+ - **Create/Edit never route** — the global record form is URL-driven (`?form=new` / `?form=<id>`): browser Back closes the overlay with the origin (list scroll/filters, detail state) intact; field-heavy objects derive a full-screen modal (`modalSize:'full'`) via the new `deriveRecordFlowSurface` mirror in plugin-view, light ones keep the auto-sized modal. `editMode:'page'` opt-in unchanged.
25
+ - **Save invariant** — _edit never moves you_ (origin refetches in place); _create lands on the new record's detail_ on its derived surface (drawer over the still-intact list for light objects, detail route for heavy), with `replace:true` so Back skips the transient form entry.
26
+ - **Subtable child create/edit = overlay over the parent detail, never a route** — related-list New/Edit push `?form=…&formObject=<child>&formLink=<fk>:<parentId>`; the one global overlay pre-links the parent (refresh-safe), sizes to the CHILD object, and on save stays on the parent while only the child's related lists refetch. ModalForm now forwards `initialValues` into its master-detail (subforms) branch so pre-links survive for children with inline line items.
27
+
28
+ - fbec4e1: feat(studio): pick a connector action from the chosen connector (no more hand-typed action ids)
29
+
30
+ In a flow's **Connector Action** node, the `actionId` field was a free-text box
31
+ (`sendMessage · send` placeholder) — a typo silently produced a node that fails
32
+ at run time. It was left as text because a connector's actions have "no flat
33
+ catalog"; but each connector already advertises its actions in the runtime
34
+ descriptors (`GET /api/v1/automation/connectors` → `{ name, actions:[{key,label}] }`).
35
+
36
+ `actionId` is now a **picker of the chosen connector's actions**, resolved from
37
+ the sibling `connectorId` (mirroring how `object-field` lists the fields of its
38
+ resolved object). New reference kind `connector-action` + `connectorSource` on
39
+ `FlowReferenceSpec`; `useConnectorActionOptions` fetches the descriptors and
40
+ `resolveConnectorName` reads the connector from the node's `connectorConfig`. Like
41
+ every reference in the designer it stays an **editable combobox** — with no
42
+ connector chosen (or none installed) it degrades to free text with a hint
43
+ ("Choose a Connector above to list its actions" / "Actions of <connector>.").
44
+
45
+ Closes the last critical hand-typed-identifier gap in flow-node config (the
46
+ object / field / flow / role / connector / template references were already
47
+ pickers). Unit-tested (`resolveConnectorName`, `connectorActionsToOptions`).
48
+
49
+ - 7a6837c: Studio package-create dogfood follow-ups (objectstack-ai/framework#2615):
50
+
51
+ - Read-only packages now gate authoring affordances client-side (Add field, New object/flow/permission set, nav Edit, Save draft, Publish, Create app) with a "switch to a writable package" hint, instead of letting doomed edits pile up until the server 422s (objectui#2259). Records stay fully usable; the field inspector opens read-only.
52
+ - New fields auto-derive their API name from the label while still auto-named — now also for the Data pillar's generic `field_N` names, so relabeling "New field" to "Status" yields a `status` column instead of `field_2` forever (objectui#2260).
53
+ - Publish is review-then-confirm: the header button opens the pending-changes panel, whose footer "Publish N change(s)" fires the atomic package publish; panel entries expand to a per-item field/property diff against the live version (objectui#2261).
54
+ - Create app can scaffold navigation from the package's objects (checkbox, on by default): one spec-valid object menu item per object, closing the "fresh app has zero nav" dead-end (objectui#2262).
55
+
56
+ - 5ed8d2d: feat(studio): automation enable/disable switch + live status in the Automations rail
57
+
58
+ The Automations pillar showed only an icon + label per flow, and no way to turn a
59
+ flow on or off — so an author couldn't tell whether an automation was live, or
60
+ stop one without deleting it (the header even said "Off by default · review before
61
+ enabling", but nothing reflected or controlled it). UX eval #6.
62
+
63
+ - **Live status dot** on every flow in the rail — a green "On" / gray "Off",
64
+ fetched from the engine's `GET /api/v1/automation/_status` (persisted `status`
65
+ is intent; this is what's actually enabled + bound to its trigger). Refetched
66
+ after a publish; degrades silently on an older backend. A flow the engine
67
+ doesn't know yet (never published) shows no dot — the amber "unpublished draft"
68
+ chip already covers that.
69
+ - **Enable/Disable switch** in the flow header. It flips the flow's deployment
70
+ `status` (active ↔ obsolete) and saves the draft immediately; the change goes
71
+ live when the package is published (so "review before enabling" is preserved).
72
+ Pairs with framework's engine-side gate (`obsolete`/`invalid` → not bound).
73
+
74
+ New `engine.studio.auto.*` i18n keys (en + zh). Unit-tested (`FlowStatusDot`:
75
+ enabled→On, disabled→Off, no-state→nothing, bound-vs-unbound tooltip). Verified in
76
+ a live browser: the rail shows a green "On" against every showcase flow and the
77
+ header switch reads "Enabled".
78
+
79
+ - 70c4a3f: Studio package-create dogfood follow-ups (framework#2615 — P2 wizard + P3 polish):
80
+
81
+ - **Package-id wizard feedback.** The three package wizards (switcher create,
82
+ landing create, landing duplicate) share a new `PackageIdInput`: illegal
83
+ characters are still normalized away, but no longer silently — a notice
84
+ says what was removed — a reverse-domain format hint shows while the id
85
+ doesn't parse, and a CJK-only name that yields no id suggestion is told to
86
+ type one manually instead of leaving the id box mysteriously empty.
87
+ - **Records-grid duplicate "Actions" column.** A field literally named
88
+ `actions` is now dropped from the Studio grid's data columns, so it no
89
+ longer collides with the always-pinned row-actions column (it stays
90
+ editable in the form designer).
91
+ - **Record-create verb consistency.** The `ObjectView` toolbar create button
92
+ resolved a hardcoded English "Create"; it now uses the same
93
+ `console.objectView.new` ("New" / 新建) key as the runtime object pages so
94
+ Studio and the running app agree.
95
+ - **Branded cold-load splash.** The console's pre-auth loading gate rendered a
96
+ bare "Loading…"; it now shows the branded, boot-safe `LoadingScreen`.
97
+ - **Picklist option editor.** Value/label inputs and CJK option labels no
98
+ longer truncate — the six controls that shared one cramped row are split
99
+ into a two-row layout so the inputs get the full panel width.
100
+ - **Draft-save confirmation.** The Data pillar's "Save draft" now shows a
101
+ success toast and a "last saved HH:MM" indicator, matching the App and
102
+ Automations pillars.
103
+
104
+ ### Patch Changes
105
+
106
+ - ec6bb16: Studio Automations rail now shows authored-but-unpublished (draft) flows.
107
+
108
+ The Automations pillar loaded its rail with `client.list('flow', …)` only, which
109
+ returns published/active metadata — so a flow authored (saved as a draft) but not
110
+ yet published was invisible in the rail, even while the "Changes · N" counter
111
+ showed a pending draft existed. Every sibling pillar (Data / Interfaces / Access)
112
+ already merged `client.listDrafts`; Automations was the sole outlier.
113
+
114
+ The published ∪ draft merge is extracted into a shared, unit-tested
115
+ `loadPackageSurfaces` helper and adopted by the Automations pillar, which also now
116
+ re-reads on `publishNonce` so drafts that go live collapse back into the published
117
+ rail after a package publish. A draft-only flow now appears in its rail (badged
118
+ "Unpublished draft"), is selectable, and loads its draft body for editing —
119
+ matching the other pillars. Fixes the empty-rail report for writable-base packages
120
+ whose flows are all still drafts.
121
+
122
+ - 4fbf910: Stop double-firing action toasts on record-detail script actions and the delete handler.
123
+
124
+ `ActionRunner.handlePostExecution` already surfaces a result's `error` as a toast
125
+ (and a success toast unless `silent`). Two handlers ALSO toasted themselves while
126
+ returning `{success:false, error}` (or a non-`silent` success), so on a runner
127
+ seeded with `onToast` the same message fired twice:
128
+
129
+ - **`RecordDetailView` `serverActionHandler`** (script actions): the HTTP/inner-fail
130
+ branch and the catch branch each called `toast.error` before returning the error.
131
+ #2177 fixed the twin in `useConsoleActionRuntime` (interface pages) but not this
132
+ copy, so record-detail script-action failures (e.g. a `RECORD_LOCKED` from an
133
+ approval-locked record) still showed the error twice for everyone on the published
134
+ console bundle. Both branches now return the error and let the runner toast it once.
135
+
136
+ - **`useObjectActions` `delete` handler** (ObjectView list/detail deletes): kept its
137
+ richer localized toast (label + description, or the bulk succeeded/failed summary)
138
+ and now returns WITHOUT `error` on failure so the runner doesn't re-toast it, and
139
+ marks successful deletes `silent` so the runner doesn't append a second generic
140
+ "Action completed successfully" toast.
141
+
142
+ Adds `useObjectActions.test.tsx` asserting exactly one toast on delete
143
+ success / failure / partial-bulk-failure.
144
+
145
+ - 6f15e43: test(studio): extend the create-conformance gate to the inline pillar creators
146
+
147
+ `createConformance.test.ts` guards that every authorable type's default
148
+ create-form output passes spec validation — catching the recurring "the designer
149
+ emits a minimal shape the spec rejects, so create→save 422s" dead-end family. But
150
+ it read only the metadata-admin registry, so the Studio's **inline** "New X"
151
+ creators (Data → object, Automations → flow, Interfaces → app, Access →
152
+ permission) — which build their skeletons directly in `StudioDesignSurface.tsx`,
153
+ bypassing the registry — were **uncovered**. A future edit to one of those shapes
154
+ could turn its "New" button into a silent dead-end with nothing to catch it.
155
+
156
+ Extracted the four inline skeletons into pure, exported builders
157
+ (`studio-design/skeletons.ts`) consumed by BOTH the pillars and a new gate block,
158
+ so the test can't drift from what the "New" button actually emits. No behavior
159
+ change — the builders return the byte-identical skeletons. The gate now covers all
160
+ create paths (registry + inline); the four inline skeletons validate clean.
161
+
162
+ - Updated dependencies [544d8eb]
163
+ - Updated dependencies [6fffd3d]
164
+ - Updated dependencies [9255686]
165
+ - Updated dependencies [fae75e2]
166
+ - Updated dependencies [1072701]
167
+ - @object-ui/i18n@11.5.0
168
+ - @object-ui/react@11.5.0
169
+ - @object-ui/components@11.5.0
170
+ - @object-ui/types@11.5.0
171
+ - @object-ui/data-objectstack@11.5.0
172
+ - @object-ui/fields@11.5.0
173
+ - @object-ui/layout@11.5.0
174
+ - @object-ui/plugin-editor@11.5.0
175
+ - @object-ui/auth@11.5.0
176
+ - @object-ui/collaboration@11.5.0
177
+ - @object-ui/core@11.5.0
178
+ - @object-ui/permissions@11.5.0
179
+ - @object-ui/providers@11.5.0
180
+
181
+ ## 11.4.0
182
+
183
+ ### Minor Changes
184
+
185
+ - 8bf6295: feat: adaptive record surface + semantic field span + responsive columns (framework#2578)
186
+
187
+ Field-heavy objects (all metadata is AI-authored) now present themselves without
188
+ any authored presentation config:
189
+
190
+ - **Adaptive surface** — a record's create/edit/detail opens as a full page when
191
+ the object is field-heavy, or a drawer when it is light. Derived from field
192
+ count (`deriveRecordSurface`), not authored; mobile always pages. Wired into the
193
+ app-shell ObjectView detail navigation (an authored view/object `navigation`
194
+ still wins).
195
+ - **Semantic field span** — `FormField.span` (`auto`/`full`) is a width primitive
196
+ decoupled from the (per-surface derived) column count; legacy `colSpan` is
197
+ clamped so it never overflows. `ObjectForm` now honours per-section `columns`
198
+ and carries `span`/`colSpan` from section defs — fixes the bug where
199
+ `type:'simple'` ignored `section.columns` and grouped fields rendered single
200
+ column.
201
+ - **Responsive columns** — `inferColumns` scales the column CAP with field count
202
+ (≤3→1, ≤8→2, ≤15→3, 16+→4); the ACTUAL column count follows the form's real
203
+ width via CSS container queries, so the same form goes 1→2→3→4 columns as a
204
+ drawer widens or becomes a page.
205
+ - **Runtime overlay width** — `NavigationConfig.size` bucket is resolved to a
206
+ viewport-clamped width at runtime (`overlayWidthFor`); a pixel width is never
207
+ authored (the author cannot know the client viewport).
208
+
209
+ - 144ab55: Consume the ADR-0085 object semantic roles from `@objectstack/spec@11.7.0`, retiring the per-surface hint dialects:
210
+
211
+ - **Single-source fieldGroups derivation**: `plugin-form`'s `deriveFieldGroupSections` and `plugin-detail`'s `deriveFieldGroupDetailSections` are now thin adapters over the spec's `deriveFieldGroupLayout` (ADR-0085 §5) — forms, modals and detail pages render the SAME grouping from one implementation. The canonical `collapse: 'none' | 'expanded' | 'collapsed'` enum is honoured everywhere (deprecated `collapsible`/`collapsed` and `defaultExpanded` spellings still read for pre-11.7 metadata).
212
+ - **`stageField` semantic role**: the detail stepper reads the top-level `stageField`; `stageField: false` now actually suppresses stage detection (previously the `false` handling was wired to the removed `detail.stageField` key, so spec-authored `false` fell through to the name heuristic).
213
+ - **`highlightFields` rename**: default grid columns, card compact views, the detail highlight strip, child-record preview fields and interface-page default columns read the object's `highlightFields` (deprecated `compactLayout` spelling read as fallback for pre-11.7 metadata).
214
+ - **Removed dead reads**: the never-spec-writable `objectDef.views.*` UI hints and the ADR-0085-removed `detail.*` block (`sections`, `sectionGroups`, `highlightFields`, `stageField`, `useFieldGroups`, `showReferenceRail`, `hideReferenceRail`, `hideRelatedTab`, `relatedLayout`) are no longer consulted. Per-page customization goes through an assigned Page schema (`record:reference_rail` remains available there as a renderer capability). `detail.renderViaSchema` survives only as the legacy-renderer kill-switch and is removed together with that path.
215
+
216
+ - d9f5ccd: feat(studio): package Access door is draft/published, not live (ADR-0086 P2 · D6/D7)
217
+
218
+ The package **Access** pillar edited permission sets **live** — it wrote the
219
+ active record directly, unlike the Data and Interfaces pillars which stage a
220
+ draft and publish with the rest of the package. That contradicted ADR-0086 D6
221
+ ("a package's own access is metadata → draft/publish") and left the two doors
222
+ sharing one live write path.
223
+
224
+ Now the **package door** (`/studio/:packageId/access`) writes **drafts**:
225
+
226
+ - The permission editor's Save (`PermissionMatrixEditPage`, package scope) and
227
+ the "new set" creator both call `client.save(..., { mode: 'draft', packageId })`
228
+ — the framework stamps the draft with the package, and the top-bar **Publish**
229
+ promotes it atomically (materialized into `sys_permission_set` by the framework
230
+ side, ADR-0086 P2 块1). The **environment-admin** door (no `packageId`) is
231
+ unchanged: it stays **live** (config), per D7.
232
+ - Reads are draft-aware: the editor loads any pending draft over the published
233
+ baseline, and the pillar rail merges published ∪ draft sets — so a set created
234
+ or edited as a draft stays visible before publish (matching Data/Interfaces).
235
+ Saving bumps the surface's pending-changes counter; a publish reloads the
236
+ published baseline.
237
+ - The pillar banner no longer claims "saved = live" (it said Publish didn't apply
238
+ here) — it now states edits save as package drafts and go live on Publish.
239
+
240
+ - 19f2533: Detail-page related lists: `relatedList: 'primary'` → own tab, multi-FK & self-referential related lists, unified picker columns (framework #2579).
241
+
242
+ - **plugin-detail** (`buildDefaultTabs`): the default related-list layout is now
243
+ the ADR-0085 prominence rule — lists whose FK declares `relatedList: 'primary'`
244
+ each get their OWN tab; every other related list collapses into a single
245
+ "Related" tab. With no primary lists this is byte-for-byte the previous stacked
246
+ default, so it is opt-in per relationship. `relatedLayout: 'tabs' | 'stack'`
247
+ remain app-level overrides (force all-own-tabs / all-stacked).
248
+ - **app-shell** (`deriveRelatedLists`): emits one related list per eligible FK —
249
+ a child referencing the parent through several relationships (e.g.
250
+ `primary_account` + `partner_account`) now surfaces each, disambiguated by the
251
+ FK label; includes self-referential relationships (hierarchies → a "child"
252
+ list); and carries the `isPrimary` prominence flag through. `RecordDetailView`
253
+ threads `isPrimary` into the synthesized page.
254
+ - **fields** (`deriveLookupColumns`): the lookup-picker default columns now
255
+ prefer the object's ADR-0085 `highlightFields` (then legacy `displayFields`,
256
+ then the field walk) — the same "how to list this object" source the related
257
+ list uses, so a picker and a related list of the same object agree with zero
258
+ per-surface config.
259
+
260
+ Pairs with the `@objectstack/spec` change that makes `relatedList` a tri-state
261
+ (`boolean | 'primary'`) and `record:related_list` `columns` optional.
262
+
263
+ - 17374ce: Studio Data pillar Phase B — Validations & Settings views complete the Data v1 surface (builder-ui pillars):
264
+
265
+ - **Validations view**: no-code editing of `ObjectSchema.validations` `script` rules (name / message / CEL fail-condition via the metadata-admin ConditionBuilder, fed the DRAFT field list / severity / active / delete). Non-script rule types (state_machine, format, …) stay visible read-only so the list remains a truthful inventory. New rules default to a VALID never-firing `condition: 'false'` — an empty condition 422s the whole draft save and dead-ends the create flow.
266
+ - **Settings view**: object basics via the shared metadata-admin default inspector (one implementation for both surfaces) plus direct editors for the ADR-0085 semantic roles — `nameField`, `stageField` (incl. the `false` "not a linear flow" state) and ordered `highlightFields` chips.
267
+ - **Draft-only packages fixed in the rail**: the object list now merges `listDrafts()` headers, so a freshly-created writable base shows its draft objects instead of hanging on "加载中…"; the empty state now says the package has no objects yet.
268
+
269
+ ### Patch Changes
270
+
271
+ - 4f77044: fix(studio): scope the Access matrix by package + slice-merge on save (ADR-0086 P0)
272
+
273
+ The Access pillar embedded the permission matrix at **environment scope**: it
274
+ listed every object in the environment (the "84-object leak"), and Save
275
+ overwrote the whole permission set — silently dropping authorization rows other
276
+ packages had contributed.
277
+
278
+ Opened inside a package, the matrix now:
279
+
280
+ - lists **only the objects that package declares** (`list('object', { packageId })`),
281
+ so a package's Access panel no longer exposes unrelated objects; and
282
+ - saves via **slice-merge** — it re-reads the record and writes back only this
283
+ package's slice, leaving every row contributed by other packages
284
+ byte-for-byte intact.
285
+
286
+ The Access rail also hides environment-owned platform-default sets
287
+ (`admin_full_access`, `member_default`, …) from a package's panel once the
288
+ backend tags sets with a record-level `package_id` (framework ADR-0086 P1), with
289
+ a mid-migration guard that shows all sets until that provenance axis is live so
290
+ the rail never goes empty. Behavior is unchanged when the editor is used outside
291
+ a package (no `packageId`): full object list, whole-record save.
292
+
293
+ - 1813544: feat(studio): Access pillar — the fourth content pillar (permission matrix)
294
+
295
+ The pillar builder gains **Access** (builder-ui §7 / ADR-0084's fourth pillar):
296
+ left rail lists the environment's permission sets / profiles (search + inline
297
+ create), and the main zone embeds the existing Salesforce-style
298
+ `PermissionMatrixEditPage` unchanged — objects × CRUD/VAMA/lifecycle plus
299
+ per-object field-level R/W, with its own save and destructive-change guard.
300
+
301
+ Deliberate v1 semantics, said out loud in the banner: permissions are
302
+ platform-level authorization objects, not package content — the matrix saves
303
+ the ACTIVE item directly, so the shell's package draft/publish does not apply.
304
+
305
+ - 2318ea2: fix(studio): scope the Access rail server-side by package (ADR-0086 P1)
306
+
307
+ The Access pillar's permission-set rail filtered client-side on a `package_id`
308
+ field read from `client.list('permission')` rows. But the metadata list endpoint
309
+ does not echo the record-level provenance columns — every row comes back with
310
+ `package_id` unset — so the filter's "any set tagged?" guard never fired and the
311
+ rail showed **all** sets, including environment-owned platform defaults
312
+ (`admin_full_access`, `member_default`, …), in a package's Access panel.
313
+
314
+ The rail now scopes server-side via `client.list('permission', { packageId })`:
315
+ the metadata API filters `permission` by the `package_id` provenance seeded in
316
+ framework ADR-0086 P1, returning only the sets this package owns. Verified
317
+ against a live showcase backend — the panel lists exactly `showcase_contributor`
318
+ and `showcase_member_default`, and the four platform defaults are excluded.
319
+
320
+ Removes the now-unused `scopePermissionSetList` client-side helper. Object-matrix
321
+ scoping and Save slice-merge (ADR-0086 P0) are unchanged.
322
+
323
+ - 9aec681: fix(app-shell): stop double-toasting failed script/modal action errors
324
+
325
+ `serverActionHandler` toasted the action error itself **and** returned
326
+ `{ success: false, error }`, which `ActionRunner.handlePostExecution` also
327
+ surfaces as a toast — so a failed script action (e.g. a validation throw)
328
+ showed two identical red toasts.
329
+
330
+ `apiHandler` and `flowHandler` already only return the error and let the
331
+ runner own the toast; `serverActionHandler` now does the same, so a failed
332
+ action toasts exactly once.
333
+
334
+ - 2edcaff: Drop the `compactLayout` fallback reads (6 sites: ObjectGrid default columns, deriveHighlightFields, RecordDetailView highlight strip + child preview, ObjectView ×2, InterfaceListPage). The deprecated spelling was retired from the spec by framework#2539 (framework#2536) — served metadata carries `highlightFields` only, so the fallbacks could never fire again; keeping them would teach the retired key to the next reader.
335
+ - 31f96f7: feat(studio): 复制 (duplicate base) on writable packages in the builder landing
336
+
337
+ Writable base cards on the builder landing gain **复制** — a name/id inline form
338
+ that calls `POST /packages/:id/duplicate` (ADR-0070 D4: re-namespaced clone with
339
+ rewritten references) and drops the user straight into the copy's builder — the
340
+ Airtable "duplicate base" gesture. Read-only code packages stay browse-only:
341
+ duplication copies `sys_metadata` rows, which code packages don't have; their
342
+ customization path is template/marketplace install.
343
+
344
+ - 34b92ac: fix(studio): show a failed flow run's reason in the Runs panel (string errors)
345
+
346
+ The Studio flow **Runs** panel (`FlowRunsPanel`) rendered a run-level error as
347
+ `run.error?.message`, but the automation engine sends `ExecutionLog.error` as a
348
+ plain **string** — so `.message` was always `undefined` and the failure reason,
349
+ the single most useful thing about a failed run, was silently dropped. This grew
350
+ important now that runs are durable (framework #2581): a failed run persists with
351
+ its reason, but the panel showed only a red "Failed" badge and no "why".
352
+
353
+ The panel now normalizes an error through a small `errorText()` helper that
354
+ accepts **either** a string (the run-level shape) **or** a `{ code, message }`
355
+ object (the step-level shape), and uses it for both the run summary and each
356
+ step row. Verified with a jsdom render test (a failed run's string reason reaches
357
+ the DOM) and live in the browser against a real failed run (`showcase_resilient_sync`):
358
+ the reason now displays where it previously showed nothing.
359
+
360
+ - 346e78e: feat(home,studio): builder cover on Home + builder→app bridge
361
+
362
+ Two entries that wire the application builder into the platform journey the
363
+ Airtable way — Home is the cover, the app is the published front-end:
364
+
365
+ - **Home builder cover** (admins/builders only): two guided cards above "Your
366
+ apps" — **Build an app** (start from scratch → `/studio`, pick/create a
367
+ writable package) and **Start with a template** (→ marketplace). End users
368
+ see their apps as before.
369
+ - **打开应用 bridge** in the `/studio` top bar: when the package ships an app,
370
+ one click opens its published front-end (`/apps/<name>`) in a new tab —
371
+ the builder edits the 设计界面, the app is what end users see (Airtable's
372
+ Data ↔ published-Interfaces relationship, our draft→publish included).
373
+
374
+ - c38d107: Fix view-level `FormField.visibleOn` (CEL) never taking effect (#2212).
375
+
376
+ The spec ships `visibleOn` as an Expression object `{ dialect: 'cel', source }`
377
+ (what the `P` template emits) or a bare string, but the whole chain dropped it:
378
+
379
+ - `sectionFields.ts` / `ObjectForm.tsx` only accepted the bare-string shape and
380
+ attached a dead `visible()` closure no renderer ever called — the Expression
381
+ object shape was silently discarded.
382
+ - The form renderer destructured `visibleOn` out of the field config and never
383
+ evaluated it.
384
+ - `RecordFormPage` dropped a `simple` form view's `sections` entirely, so
385
+ page-mode create/edit fell back to the raw schema (every field, no authored
386
+ selection/grouping) while the modal path honored the same view.
387
+ - `ObjectForm`'s grouped-sections path matched section fields by name only,
388
+ dropping per-field `visibleOn` overrides.
389
+
390
+ `visibleOn` now flows through normalization verbatim (both wire shapes) and is
391
+ evaluated reactively by the form renderer with the canonical expression engine
392
+ (`evalFieldPredicate` — same engine, record scope, and fail-open semantics as
393
+ field-level `visibleWhen`; both predicates must allow a field for it to show).
394
+ Sectioned/flat normalization also copies field-level `visibleWhen` /
395
+ `readonlyWhen` / `requiredWhen` rules it previously lost.
396
+
397
+ - 98c9855: fix(studio): lookup target picker can see the package's own draft objects
398
+
399
+ When designing a set of related objects in one authoring pass, the field
400
+ inspector's lookup "related object" picker only listed **published** objects
401
+ (`list('object')`), so sibling objects still in draft — the ones you're most
402
+ likely to point a new lookup at — were invisible and had to be typed as a raw
403
+ API name, blind. The picker now also merges unpublished object drafts
404
+ (`listDrafts({ type: 'object' })`, labelled "(草稿)"), so a lookup can target a
405
+ sibling object before the package's first publish.
406
+
407
+ - 363e8b7: Resolve short view names in `/view/<name>` routes instead of silently falling
408
+ back to the default view (#2217).
409
+
410
+ Nav items emit their `viewName` verbatim — usually the short form
411
+ (`tabular`) — while canonical view ids are fully qualified
412
+ (`showcase_task.tabular`), so nav-generated view links always rendered the
413
+ default view with no hint anything was wrong. `ObjectView` now resolves the
414
+ requested name in both directions (short → `<object>.<name>`, and qualified →
415
+ bare key for legacy embedded listViews), and logs a warning listing the known
416
+ view ids when nothing matches instead of swallowing the miss.
417
+
418
+ - 0cf352b: fix(packages): Setup's package list and creator agree with the builder on writability
419
+
420
+ Two disagreements between Setup › Packages and the application builder about the
421
+ same package:
422
+
423
+ - **Display**: `ScopeBadge` defaulted a missing scope to `project`, so writable
424
+ database bases wore the same badge as read-only code packages. Scope-less
425
+ entries now show **可写/Writable** (emerald), `project` reads **只读 · 代码包 /
426
+ Read-only · code** — matching the builder's labeling.
427
+ - **Semantics**: the create-package dialog hardcoded `scope: 'project'` onto new
428
+ runtime-created bases, which made the builder's switcher/landing mislabel
429
+ Setup-created packages as read-only. New bases are now created scope-less,
430
+ the same shape the builder's own creator produces.
431
+
432
+ - 7782698: fix(components): page:header record title honours `nameField` via the unified ADR-0079 resolver
433
+
434
+ The default console record detail page renders the synthesized `page:header`
435
+ (`buildDefaultPageSchema`, renderViaSchema default-on), whose record-chip title
436
+ chain probed `objSchema.primaryField` (not a spec property — always undefined),
437
+ `titleFormat`, then hardcoded `name`/`full_name`/`title`/`subject`/
438
+ `display_name`/`label` record keys. It never consulted the object's declared
439
+ `nameField`/`displayNameField`, so an object titled by e.g. `subject` rendered
440
+ `<ObjectLabel> <id-prefix>` as its H1 instead of the record's real name.
441
+
442
+ `PageHeaderRenderer` now resolves through `getRecordDisplayName(objSchema, data,
443
+ { deriveFromRecordKeys: false })` after the author overrides and before the
444
+ legacy probes — mirroring `DetailView.resolveDisplayTitle` so both headers
445
+ agree. `RecordDetailView`'s `primaryField` derivation and
446
+ `buildDefaultPageSchema`'s highlight-strip dedup also honour
447
+ `nameField`/`displayNameField`.
448
+
449
+ - 790558b: fix(studio): make the Automations and Interfaces pillars authorable in a fresh package
450
+
451
+ Dogfooding a brand-new package end-to-end (design objects → automations →
452
+ interfaces → publish → use) surfaced two blocking dead-ends in the pillar
453
+ Studio, both now fixed:
454
+
455
+ - **Automations pillar had no way to create a flow.** For a package with zero
456
+ flows the rail rendered an endless "加载中…" (loading conflated with empty)
457
+ and offered no create affordance, so automations could never be authored.
458
+ It now tracks the list-loaded state (real empty state "还没有自动化 — 点「新建」开始")
459
+ and has a "+ 新建" inline creator that saves a minimal, valid `start → end`
460
+ autolaunched flow skeleton as a draft and opens it in the flow designer.
461
+
462
+ - **Interfaces nav items could not be bound to a target — and silently failed
463
+ to save.** Selecting a nav item showed no inspector, and the item shape the
464
+ editor produced (`{ label, object }`, no `id`/`type`) failed the app spec's
465
+ navigation union ("navigation.N: Invalid input"), so the draft never
466
+ persisted and the published app navigation stayed empty. The right panel now
467
+ renders a `StudioNavItemInspector` with a business-friendly object picker
468
+ (populated from the package's published ∪ draft objects) that emits a
469
+ spec-valid `ObjectNavItem` (`{ id, type:'object', objectName, label }`), and
470
+ the nav save drops still-unbound placeholders + backfills a snake_case id so
471
+ one blank item can't fail the whole save.
472
+
473
+ Also fills in the Home builder-cover i18n keys (`home.build.*`,
474
+ `home.template.*`) in `en`/`zh` so the "Build an app" / "Start with a template"
475
+ cards resolve real strings instead of falling back to defaults.
476
+
477
+ - 3c7abf9: feat(studio): Data pillar left rail gains search + inline "new object"
478
+
479
+ Closes the two remaining v1 rail gaps from the builder design (§4): the objects rail
480
+ now has a **search** filter and an inline **新建对象** creator (显示名 + auto-derived
481
+ snake_case 标识符 — hand-editable, since CJK labels can't derive one). Creating saves
482
+ the object as a **draft in the current package** (same runtime-create path the classic
483
+ Studio editor uses), seeded with one text field, and lands in 表单 · 布局 — the
484
+ metadata-level designer.
485
+
486
+ Draft-only objects (no physical table until the package publish) now get honest
487
+ placeholders instead of broken surfaces: the Records grid explains that data arrives
488
+ after publish (instead of firing SQL at a table that doesn't exist), and 预览 explains
489
+ there is no published definition yet.
490
+
491
+ - 839f6c2: fix(studio): stamp packageId on pillar draft saves → true package-scoped publish
492
+
493
+ Studio pillar draft-saves now pass the active `packageId`, so each draft row is
494
+ stamped with its package binding (`sys_metadata.package_id`) instead of `null`.
495
+ This makes the package-scoped surfaces reliable: the top-bar count + Changes review
496
+ filter via `GET /meta/_drafts?packageId=`, and Publish promotes exactly this
497
+ package's drafts via `POST /packages/:id/publish-drafts` (which matches
498
+ `WHERE package_id = X`). Replaces the previous "publish all pending" fallback that
499
+ was only needed because null-package drafts couldn't be package-filtered or picked
500
+ up by publish-drafts.
501
+
502
+ - 87e7c23: feat(studio): builder landing + `studio:builder` entry — the builder joins the login journey
503
+
504
+ The pillar application builder was a URL-only surface (zero links anywhere pointed at
505
+ `/studio/...`). Now it has a front door wired into the platform journey:
506
+
507
+ - **BuilderLanding** — pick or create a writable base package (writable bases lead,
508
+ read-only code packages listed for browsing), then jump into the full-screen pillar
509
+ builder. Served standalone at bare **`/studio`** (bookmarkable) and embeddable via
510
+ the **`studio:builder`** component ref, which the framework's Studio app references
511
+ from its new 「App Builder」 nav entry — so the journey is: login → Home → Studio →
512
+ App Builder → package → build.
513
+ - `/studio/:packageId` now lands on **`data`** (the pillar order's first surface)
514
+ instead of `interfaces`.
515
+ - Package-list parsing/creation is extracted to `packages-io` and shared by the
516
+ landing and the top-bar package switcher.
517
+
518
+ - 5ba3d0e: feat(studio): WYSIWYG form-layout designer in the Data pillar
519
+
520
+ The Data pillar's Form view gains a **布局 (Layout)** designer: the object's default
521
+ form rendered WYSIWYG, where an admin adds **sections**, drag-reorders fields within
522
+ a section and drags them **across** sections, and clicks a field to edit it in the
523
+ **same** protocol inspector the grid uses — one screen, no Data↔Interface switch.
524
+
525
+ Sections persist as the object's `fieldGroups`, and membership/order as `field.group`
526
+ plus field order, via the existing draft → publish. The drag/section chrome (dnd-kit)
527
+ is the only new code; the data model and all mutations reuse the existing, tested
528
+ `object-fields-io` helpers (`readGroups`/`addGroup`/`renameGroup`/`removeGroup`/
529
+ `moveGroup`/`clearFieldGroup`/`groupEntries`).
530
+
531
+ Also fixes the Data pillar clobbering an in-progress draft when the metadata client
532
+ identity churned (e.g. toggling the live preview): the object baseline is now loaded
533
+ exactly once per selected object.
534
+
535
+ - c0164ad: fix(studio): surface spec-validation failures on the field at save/publish
536
+
537
+ When a Studio metadata draft failed spec validation, the designer got a single
538
+ opaque banner (and, on a partial publish, a false "published!" toast) — the
539
+ server was already returning field-anchored issues, but the client threw them
540
+ away. Two problems, both fixed:
541
+
542
+ - **`parseError` (data-objectstack)** read `String(body.error)`, which yields
543
+ `"[object Object]"` for the dispatcher's object-shaped error, and ignored the
544
+ validation `issues`. It now reads the message from either shape (string or
545
+ `{ message }`) and exposes `MetadataError.issues`, accepting all live server
546
+ shapes — top-level `body.issues` (REST server) and `error.details.issues`
547
+ (HTTP dispatcher).
548
+
549
+ - **Studio save/publish (app-shell)** now render those issues **field-anchored**.
550
+ A new `formatMetadataError` helper turns a caught error into one line per
551
+ offending field (`• fields.amount.type — Invalid option: …`); the save banners
552
+ render it with `whitespace-pre-line`. `doPublish` no longer claims success when
553
+ the response carries `data.failed[]` — it lists which drafts failed and why
554
+ (the server returns 200 with the failures buried, so the UI used to swallow
555
+ them). `formatPublishFailures` formats those per-draft.
556
+
557
+ Verified end-to-end against a live backend: an invalid object draft returns 422
558
+ with field-anchored issues, and the Studio banner shows
559
+ `• fields.amount.type — Invalid option: expected one of "text"|…` instead of a
560
+ generic message. Unit-tested: `parseError` on the dispatcher shape, and the
561
+ `formatMetadataError` / `formatPublishFailures` helpers.
562
+
563
+ - 7034306: fix(studio): Interfaces designs the CURRENT package's app, not another's
564
+
565
+ The Interfaces pillar resolved its app with an unscoped `list('app')` and a
566
+ client-side `.find()` by package — but list rows carry no `packageId`, so the
567
+ match never hit and it fell through to `?? apps[0]`, the first app in the whole
568
+ system. Opening `/studio/<pkg>/interfaces` for a package with no app therefore
569
+ rendered a **different** package's navigation tree (e.g. `showcase_app`), and a
570
+ package that genuinely had no app was stuck on an endless "加载中…".
571
+
572
+ Now the query is scoped to the package (`list('app', { packageId })`, matching
573
+ the header's own resolution) with no cross-package fallback; a freshly-created
574
+ (still-draft) app is picked up via `listDrafts({ packageId, type: 'app' })` so it
575
+ stays designable before its first publish. When the package has no app, the nav
576
+ rail and canvas show a real empty state ("这个软件包还没有应用") with a 创建应用
577
+ action wired to the header's existing create flow, and edit mode now renders the
578
+ nav canvas even on an empty tree so the first item can be added.
579
+
580
+ - 34accfc: fix(studio): close the three journey dead-ends found in UX review
581
+
582
+ - **Navigation**: the standalone `/studio` landing gets a slim frame with an
583
+ ObjectOS wordmark → Home, and the builder top bar gets a Home button — the
584
+ builder is no longer a browser-back-only dead end.
585
+ - **Fresh-package empty state**: an empty writable package no longer shows an
586
+ endless 加载中… — the rail says 还没有对象, the main pane explains the first
587
+ act (从第一个对象开始), and the object creator auto-opens.
588
+ - **创建应用 on-ramp**: when the package ships no app, the top-bar bridge slot
589
+ offers 创建应用 (draft `app` item, name + identifier popover) instead of
590
+ nothing; after creation it shows 应用「…」待发布, and flips to 打开应用 once
591
+ the package publish lands.
592
+
593
+ - 65efc01: feat(studio): package-level draft publish (replaces per-item publish)
594
+
595
+ The pillar Studio now publishes at the **package** level, not item-by-item. Edits
596
+ across Data / Automation / Interface accumulate as per-item **drafts**; the top bar
597
+ shows a pending-draft **count**, a **变更** (Changes) review, and one **发布** that
598
+ publishes **all** pending drafts in a single governed pass — reusing
599
+ `usePublishAllDrafts` (per-package `publish-drafts` with structure-before-seeds + the
600
+ ADR-0038 L3 probes, and by-reference for orphan / null-package drafts).
601
+
602
+ - The per-pillar **发布** buttons are removed; **保存草稿** stays (drafts accumulate).
603
+ - The Data grid's drag-reorder no longer **auto-publishes** — it saves a draft like
604
+ every other edit, so nothing goes live outside the one package publish.
605
+ - After a publish, pillars re-read the fresh published baseline (a publish nonce),
606
+ and a draft-save refreshes the pending count.
607
+
608
+ - d8b9547: feat(studio): package switcher + inline "new writable package" in the top bar
609
+
610
+ The pillar Studio's top-bar package name becomes a **switcher**: it lists the app's
611
+ packages (kernel/system packages hidden), marks each **可写** (database base) or
612
+ **只读** (code package — the ADR-0070 D4 gate refuses authoring into these), and
613
+ switches by navigation. A **新建软件包** inline form creates a writable base
614
+ (`POST /packages {id,name}` — 名称 + auto-derived, hand-editable package id) and
615
+ jumps straight into its Data pillar.
616
+
617
+ The current package also shows a proactive **只读** badge, so users learn the
618
+ package is read-only _before_ hitting the save-time gate. Writability display is a
619
+ heuristic (`scope: 'project'` = code, scope-less = base); the server-side gate stays
620
+ the authority.
621
+
622
+ - 20c1695: Studio pillars now follow the app's active locale instead of hardcoding Chinese.
623
+ `StudioDesignSurface` pinned `const locale = 'zh-CN'` in its Interfaces / Data /
624
+ Automations pillars, so the builder always rendered Chinese even when the console
625
+ ran in English (while the Home page and the rest of the app followed the active
626
+ locale). Every inline string across the design surface — package switcher,
627
+ publish/app-bridge header, the four pillars (Data, Automations, Interfaces,
628
+ Access), and the nav-item inspector — is now extracted into the metadata-admin
629
+ `engine.studio.*` catalog with English + Chinese entries, and a new
630
+ `useMetadataLocale()` hook threads the live `useObjectTranslation().language`
631
+ (the same source the LocaleSwitcher drives) so switching the console language
632
+ re-renders the Studio in lock-step. `AppNavCanvas` (used by the Studio and the
633
+ metadata-admin App preview) is likewise localized via `engine.appNav.*` — its
634
+ previously hardcoded English "NAVIGATION", "Add nav item", "Remove nav item", and
635
+ empty-state strings now follow the active locale.
636
+ - 00e7735: fix(studio): say what the Form preview shows — published definition, not the draft
637
+
638
+ The Data pillar's Form view has two sub-modes: **布局** (the WYSIWYG layout designer,
639
+ rendered from the draft) and **预览** (the live runtime ObjectForm). The preview
640
+ renders the **published** definition on purpose — a draft with structural changes has
641
+ no physical columns yet (DDL lands at publish), so a draft-with-data preview would
642
+ break — but the UI never said so: after arranging a draft in 布局, switching to 预览
643
+ silently showed the old shape, reading as "my changes are lost".
644
+
645
+ Now the sub-mode captions state their source (布局 = 草稿 · 含未发布改动 / 预览 =
646
+ 已发布定义), and when unpublished changes exist the preview shows an amber note:
647
+ confirm the draft in 布局, or publish (top bar) first to see the published effect.
648
+ Publishing stays a deliberate user action — nothing auto-publishes.
649
+
650
+ - e84d64d: Block record-scoped toolbar actions launched with zero rows selected (#2210).
651
+
652
+ A flow/script action that also mounts on list rows (`locations` includes
653
+ `list_item`) has no record to run on when triggered from the list toolbar with
654
+ nothing selected — pre-fix the wizard opened anyway, collected input, and died
655
+ at its first record-bound node ("Update requires an ID or options.multi=true").
656
+ The console runtime now blocks up front with "select a row first", mirroring
657
+ the existing multi-selection guard. Pure object-level toolbar actions
658
+ (`locations: ['list_toolbar']` only) keep triggering without a record.
659
+
660
+ The action renderers (button/icon/menu/group) now forward the `locations`
661
+ declaration to the ActionRunner — previously it was dropped by their
662
+ allow-list payloads, so the runtime could not tell the two shapes apart.
663
+
664
+ - 3106584: Warn when `userFilters` / `quickFilters` on an object list view are
665
+ suppressed instead of dropping them silently (#2219).
666
+
667
+ ADR-0053 correctly reserves those fields for page lists (InterfaceListPage
668
+ "filters" mode) and suppresses them on the object default list, but until the
669
+ phase-4 schema guardrail lands the author got zero signal — a valid schema
670
+ and a toolbar with nothing where the filter controls should be. ObjectView
671
+ now logs a one-shot warning per object/view naming the offending fields and
672
+ where they belong.
673
+
674
+ - Updated dependencies [8bf6295]
675
+ - Updated dependencies [1948c5b]
676
+ - Updated dependencies [bce581a]
677
+ - Updated dependencies [9cd9be1]
678
+ - Updated dependencies [5160832]
679
+ - Updated dependencies [69d6b94]
680
+ - Updated dependencies [c38d107]
681
+ - Updated dependencies [243a9ba]
682
+ - Updated dependencies [289be5b]
683
+ - Updated dependencies [7782698]
684
+ - Updated dependencies [19f2533]
685
+ - Updated dependencies [790558b]
686
+ - Updated dependencies [c0164ad]
687
+ - Updated dependencies [09e1b26]
688
+ - Updated dependencies [e84d64d]
689
+ - @object-ui/types@11.4.0
690
+ - @object-ui/components@11.4.0
691
+ - @object-ui/fields@11.4.0
692
+ - @object-ui/i18n@11.4.0
693
+ - @object-ui/data-objectstack@11.4.0
694
+ - @object-ui/auth@11.4.0
695
+ - @object-ui/collaboration@11.4.0
696
+ - @object-ui/core@11.4.0
697
+ - @object-ui/layout@11.4.0
698
+ - @object-ui/permissions@11.4.0
699
+ - @object-ui/plugin-editor@11.4.0
700
+ - @object-ui/providers@11.4.0
701
+ - @object-ui/react@11.4.0
702
+
3
703
  ## 11.3.0
4
704
 
5
705
  ### Patch Changes