Repository navigation
Commit 69c39b1
feat(app-shell): Studio item toolbar — layers, references, history with rollback, audit and source on every pillar item (#28296)
<!-- claude-projects-attribution: {"github_login":"os-tesla"} -->
_Requested by **Tesla** · [project
thread](https://claude.ai/code/project/chan_01AXsQkLFKRdQCqQ3RTr6Jao?thread=cmsg_01AXsQkLFKRdQCqQ3RTr6JaoY3Lv183dpSdzAqvyvWdkwi)_
Fixes #28239
Clause-②: no
Metadata-protocol: none
## 维护者速读
- 改了什么:Studio 的 Data、Interfaces、Automations
三个支柱,打开任一项时,项头部多出一组工具:分层、引用关系、历史(可回滚)、审计日志、源码,与 Setup
项页面右侧那套是同一批组件;这些组件搬到共享目录 `views/item-tools/`,Setup 改为从那里引用,行为不变。
- 为什么改:维护者裁决 D-5 / D-7(「setup 右侧这套界面,包括看版本历史的,本身就应该属于 studio」):作者在
Studio 改了东西,要撤销还得跑去 Setup;后续定制卡都要在编辑处看历史和回滚。
- 风险与回滚:Setup 页面只换了引用路径、引用面板的加载搬进同语义的 hook,Setup 原有测试全部不改语义照绿;Studio
只在头部加按钮组。整个 PR 可单独回退。
-
先看:`ui/packages/app-shell/src/views/item-tools/StudioItemTools.tsx`(工具栏与回滚门槛)、`StudioDesignSurface.tsx`
三处头部接线、`ResourceEditPage.tsx` 引用面板换 hook 的那几处。
- 两分钟验证:Studio 打开任一对象 / 页面 / 流程,点头部「历史」→ 选一个旧版本点回滚 →
出现应用内确认框,确认后该项重新读取;只读包里同一入口没有回滚按钮、源码只读。
- 需要你做的:无,席位落地。
## What changes
- **Shared directory.** `LayeredDiff`, `ResourceHistoryPage`
(`HistoryPanel` and the history route page), `AuditPanel` and
`JsonSourceEditor` move, with their own tests, to
`ui/packages/app-shell/src/views/item-tools/` (`git mv`, so history
follows). The references panel and its lazy scan leave
`ResourceEditPage.tsx` as `item-tools/ReferencesPanel.tsx`
(`ReferencesPanel` + `useReferencesScan`, the same idle / loading /
loaded / error union and re-entry guard). Setup's item page imports all
five from there; its behaviour is unchanged.
- **Studio item toolbar.** New `item-tools/StudioItemTools.tsx`: five
icon buttons in each pillar's item-header actions slot, each opening the
panel Setup's sheet holds.
- History and Audit are offered only when `useCanReadAuthoringTrail`
admits the caller, as on Setup.
- Rollback is offered on Setup's Save gate (an authoring caller on an
item that is not read-only), asks the same in-app confirm, writes
through the same `client.rollback`, and bumps the pillar's own reload
nonce (`objReloadNonce` / `leafReloadNonce` / `flowReloadNonce`) so the
item is re-read.
- Source edits the pillar's own draft buffer (Data's object draft, an
Interfaces leaf's draft, the open flow's draft) and the pillar's
autosave sends it; it is read-only on a read-only package, where the
pillar has no designer for the item, and on an Interfaces object entry
(the object is edited in Data), where it shows the served body.
- Layers and Source-without-a-buffer read `client.layered(type, name, {
packageId })` lazily, with an error arm and a retry.
- **Not carried over** (card item 3): Setup's autosave toggle and
`DraftReviewPanel`.
- **i18n.** One contiguous `engine.studio.itemTools.*` block per pack
(en, zh); every tool label reuses Setup's existing `engine.edit.*` keys.
### Move map (old path → new path)
Under `ui/packages/app-shell/src/views/`:
| Old | New |
|---|---|
| `metadata-admin/LayeredDiff.tsx` | `item-tools/LayeredDiff.tsx` |
| `metadata-admin/LayeredDiff.overlayScope.test.tsx` |
`item-tools/LayeredDiff.overlayScope.test.tsx` |
| `metadata-admin/ResourceHistoryPage.tsx` |
`item-tools/ResourceHistoryPage.tsx` |
| `metadata-admin/AuditPanel.tsx` | `item-tools/AuditPanel.tsx` |
| `metadata-admin/AuditPanel.emptyPlaceholderAffordance-8504.test.tsx` |
`item-tools/AuditPanel.emptyPlaceholderAffordance-8504.test.tsx` |
| `metadata-admin/AuditPanel.loadFailure.test.tsx` |
`item-tools/AuditPanel.loadFailure.test.tsx` |
| `metadata-admin/AuditPanel.lockState.test.tsx` |
`item-tools/AuditPanel.lockState.test.tsx` |
| `metadata-admin/JsonSourceEditor.tsx` |
`item-tools/JsonSourceEditor.tsx` |
| `metadata-admin/JsonSourceEditor.fallback.test.tsx` |
`item-tools/JsonSourceEditor.fallback.test.tsx` |
| `metadata-admin/JsonSourceEditor.monaco-export.test.tsx` |
`item-tools/JsonSourceEditor.monaco-export.test.tsx` |
| `metadata-admin/ResourceEditPage.tsx` (`ReferencesState`,
`ReferencesPanel`, the references loader) |
`item-tools/ReferencesPanel.tsx` (`ReferencesState`, `ReferencesPanel`,
`useReferencesScan`) |
Import re-paths only: `src/index.ts` (the `LayeredDiff` and
`MetadataResourceHistoryPage` exports keep their names),
`metadata-admin/index.ts`, `ResourceRouter.tsx`,
`PermissionMatrixEditor.tsx`, `DraftReviewPanel.tsx`, and the tests
`metadataAdminFaces.displayLocale-9909`, `useMonacoFallback.fastfail`,
`useMonacoFallback.loaderRejection-11800`,
`ResourceHistoryPage.rollbackConfirm-13333` (stays in `metadata-admin/`;
its native-confirm file list re-pathed), and `i18n`'s
`ariaLabelLiteralCensus-12145` path row.
### Size note
Rename-aware (`git diff -M --numstat`): **+900 / −186 = 1,086 lines**,
under the order's ~1,500 split line, so move and wiring ship as one PR.
`dispatch-gates.mjs` counts without rename detection (+3,033 / −2,319 =
5,352) and reports it over the 3,000 human-merge line; the seat reads
GitHub's own additions/deletions on this PR for the landing class.
## Tests
- New `studio-design/StudioDesignSurface.itemTools-28239.test.tsx` (8
tests, real pillars and real Setup page, the metadata client doubled):
- each of Data (`object`), Interfaces (`page`) and Automations (`flow`)
shows the five buttons in order and opens all five, each reading its own
door for that item (`layered` with the package, `references`, `history`,
`audit`), Source holding the item's JSON, editable;
- rollback from Studio: the in-app confirm names
`object/showcase_account` and version 1, accepting calls
`client.rollback('object', 'showcase_account', 1)` once, and the pillar
re-reads the object;
- a read-only package: history without rollback, Source read-only with
its sentence; CONTROL: the writable package offers the two rollbacks;
- a `setup.access`-only caller gets Layers, References and Source, not
History or Audit;
- CONTROL: Setup's item page still opens Layers, References, History,
Audit and the Source tab.
- Changed: `DataPillar.headerActions-12511` — the header's control set
on every tab is now the rail toggle plus the five tools (was the rail
toggle alone); its docblock says why (rulings D-5 / D-7).
- Moved tests keep their bodies; only relative imports were re-pathed.
## Acceptance notes
- Interfaces keeps its existing ⋯ → View definition sheet (a source
page's code editor, other items read-only JSON) beside the new Source
tool, which edits the leaf's JSON. Folding the two is a follow-up for
the Studio customization cards, not done here.
- `docs/adr/0005-metadata-customization-overlay.md` still names
`metadata-admin/LayeredDiff.tsx`; that file is Tier H and left untouched
(the claim's file surface excludes `docs/adr/**`).
- The references count badge appears after the References sheet has been
opened once (the scan is lazy, as on Setup); Layers carries no
overlay-count badge in Studio because Studio does not read the layers
until the sheet opens.
- `views/setup/PackagedAutomationPage.tsx`, the object pickers and
`ResourceEditPage.tsx`'s validation region are untouched.
## Verification
Head `b0fe67bc`, local, under `scripts/pm/os-verify-lock.sh`.
<details><summary>Tests</summary>
- `pnpm --dir ui exec vitest run` over `item-tools/`, the new test,
`DataPillar.headerActions-12511`, every
`metadata-admin/ResourceEditPage*`,
`ResourceHistoryPage.rollbackConfirm-13333`,
`metadataAdminFaces.displayLocale-9909`, `useMonacoFallback*`,
`trailDoorsGate-12475`, `PermissionMatrixEditor*`,
`DraftReviewPanel.test.tsx`: Test Files 99 passed (99), Tests 539 passed
(539).
- Every `DataPillar.*`, `AutomationsPillar.*`, `InterfacesPillar.*`,
`StudioItemHeader.*` test (head `0754547f`, before two type-only commits
to `StudioItemTools.tsx`): Test Files 55 passed (55), Tests 244 passed
(244).
- `packages/i18n/`: Test Files 93 passed (93), Tests 1433 passed, 13
skipped.
- The full `studio-design/` directory was not run locally (over the
foreground limit); CI's Test UI shards run it.
</details>
<details><summary>Type-check</summary>
- `pnpm --filter @object-ui/app-shell run type-check` (both programs,
after `env -u OS_SKIP_DTS pnpm turbo run build
--filter='@object-ui/app-shell^...'`): exit 0.
- `pnpm --filter @object-ui/i18n run type-check`: exit 0.
</details>
<details><summary>Derived gates</summary>
`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` → 34 families, each run with stdin closed,
every one `exit 0` (including `node scripts/check-sdui-manifest.mjs`,
`pnpm check:cross-package-test-inputs`, `pnpm
check:type-check-coverage`, `pnpm check:type-check-debt`, `pnpm
check:test-source-alias`, and `pnpm --dir ui exec vitest run scripts/`:
Test Files 169 passed, 2 skipped). `--ran` reconciliation: 34 derived,
34 run, 0 NOT-MEASURED, 0 UNRUN. Tier: no path-derived mandate.
</details>
---
_Generated by [Claude
Code](https://claude.ai/code/session_01L9qiby6kg33hPqfAa7kpc5)_
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01L9qiby6kg33hPqfAa7kpc5
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 7abe151 commit 69c39b1
29 files changed
Lines changed: 900 additions & 186 deletions
File tree
- .changeset
- ui/packages
- app-shell/src
- views
- item-tools
- metadata-admin
- studio-design
- i18n/src
- __tests__
- namespaces/studio
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
437 | 437 | | |
438 | 438 | | |
439 | 439 | | |
440 | | - | |
| 440 | + | |
441 | 441 | | |
442 | 442 | | |
443 | 443 | | |
444 | | - | |
| 444 | + | |
445 | 445 | | |
446 | 446 | | |
447 | 447 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
52 | 52 | | |
53 | 53 | | |
54 | 54 | | |
55 | | - | |
| 55 | + | |
56 | 56 | | |
57 | 57 | | |
58 | 58 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
49 | 49 | | |
50 | 50 | | |
51 | 51 | | |
52 | | - | |
| 52 | + | |
53 | 53 | | |
54 | 54 | | |
55 | 55 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
36 | 36 | | |
37 | 37 | | |
38 | 38 | | |
39 | | - | |
| 39 | + | |
40 | 40 | | |
41 | 41 | | |
42 | 42 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
29 | 29 | | |
30 | 30 | | |
31 | 31 | | |
32 | | - | |
33 | | - | |
| 32 | + | |
| 33 | + | |
34 | 34 | | |
35 | 35 | | |
36 | 36 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
25 | | - | |
26 | | - | |
| 25 | + | |
| 26 | + | |
27 | 27 | | |
28 | 28 | | |
29 | 29 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
23 | 23 | | |
24 | 24 | | |
25 | 25 | | |
26 | | - | |
| 26 | + | |
27 | 27 | | |
28 | 28 | | |
29 | 29 | | |
| |||
0 commit comments