Skip to content

Commit 69c39b1

Browse files
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
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'@object-ui/app-shell': patch
3+
'@object-ui/i18n': patch
4+
---
5+
6+
feat(console): Studio's item header offers Setup's item tools — layers, references, history with rollback, audit and source — on every Data, Interfaces and Automations item
7+
8+
Clause-②: no
9+
Metadata-protocol: none
10+
11+
- **Before.** An item's layers (code / overlay / effective), what references it, its version history with rollback, its audit trail and its JSON source opened only from Setup's item page. A Studio author who wanted to undo a change went to Setup to do it.
12+
- **Now.** The open item's header in Studio's Data, Interfaces and Automations pillars carries the five as an icon group, each opening the same panel Setup's sheet holds. History and Audit are offered only to a caller the authoring-trail doors admit, as on Setup. Rollback asks the same in-app confirm, writes through the same rollback door, and the pillar re-reads the item; a read-only package shows its history without rollback. Source edits the pillar's own draft buffer, saved by its autosave, and is read-only where the item is.
13+
- **Moved.** `LayeredDiff`, `ResourceHistoryPage` (`HistoryPanel`), `AuditPanel`, `JsonSourceEditor` and the references panel now live in `views/item-tools/`, which Setup's item page imports. `@object-ui/app-shell`'s `LayeredDiff` and `MetadataResourceHistoryPage` exports are unchanged. Setup's page behaves as before.
14+
- **Not carried over.** Setup's autosave toggle and draft review panel: Studio keeps its own draft → publish and pending-changes review.

‎ui/packages/app-shell/src/index.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -437,11 +437,11 @@ export { MetadataDirectoryPage } from './views/metadata-admin/DirectoryPage.js';
437437
export { MetadataResourceRouter } from './views/metadata-admin/ResourceRouter.js';
438438
export { MetadataResourceListPage } from './views/metadata-admin/ResourceListPage.js';
439439
export { MetadataResourceEditPage } from './views/metadata-admin/ResourceEditPage.js';
440-
export { MetadataResourceHistoryPage } from './views/metadata-admin/ResourceHistoryPage.js';
440+
export { MetadataResourceHistoryPage } from './views/item-tools/ResourceHistoryPage.js';
441441
export { MetadataDiagnosticsPage } from './views/metadata-admin/DiagnosticsPage.js';
442442
export { MetadataQuickFind } from './views/metadata-admin/QuickFind.js';
443443
export { SchemaForm } from './views/metadata-admin/SchemaForm.js';
444-
export { LayeredDiff } from './views/metadata-admin/LayeredDiff.js';
444+
export { LayeredDiff } from './views/item-tools/LayeredDiff.js';
445445
// objectui#7611 — the Setup catalog's URL scope (`?scope=environment`), for a
446446
// host that routes its own legacy URLs onto the catalog (the console's
447447
// `system/positions` / `system/permissions`). A dependency-free leaf.

ui/packages/app-shell/src/views/metadata-admin/AuditPanel.emptyPlaceholderAffordance-8504.test.tsx renamed to ui/packages/app-shell/src/views/item-tools/AuditPanel.emptyPlaceholderAffordance-8504.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ import type {
5252
MetadataClient,
5353
} from '@object-ui/data-objectstack';
5454
import { AuditPanel } from './AuditPanel';
55-
import { t, translateConsoleValue } from './i18n';
55+
import { t, translateConsoleValue } from '../metadata-admin/i18n';
5656

5757
afterEach(cleanup);
5858

ui/packages/app-shell/src/views/metadata-admin/AuditPanel.loadFailure.test.tsx renamed to ui/packages/app-shell/src/views/item-tools/AuditPanel.loadFailure.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ import { render, screen, cleanup, waitFor } from '@testing-library/react';
4949
import userEvent from '@testing-library/user-event';
5050
import type { MetadataAuditEntry, MetadataClient } from '@object-ui/data-objectstack';
5151
import { AuditPanel } from './AuditPanel';
52-
import { t } from './i18n';
52+
import { t } from '../metadata-admin/i18n';
5353
// The designer rows this suite reads in zh live in `@object-ui/i18n`'s `studio`
5454
// namespace, whose `zh` catalogue is fetched on demand (objectstack#25922).
5555
// Resident before any case, so the zh rows below read synchronously.

ui/packages/app-shell/src/views/metadata-admin/AuditPanel.lockState.test.tsx renamed to ui/packages/app-shell/src/views/item-tools/AuditPanel.lockState.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import type {
3636
MetadataClient,
3737
} from '@object-ui/data-objectstack';
3838
import { AuditPanel } from './AuditPanel';
39-
import { translateConsoleValue } from './i18n';
39+
import { translateConsoleValue } from '../metadata-admin/i18n';
4040

4141
afterEach(cleanup);
4242

ui/packages/app-shell/src/views/metadata-admin/AuditPanel.tsx renamed to ui/packages/app-shell/src/views/item-tools/AuditPanel.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ import type {
2929
MetadataClient,
3030
MetadataAuditEntry,
3131
} from '@object-ui/data-objectstack';
32-
import { t, translateConsoleValue, type SupportedLocale } from './i18n.js';
33-
import { type LoadState, loadErrorMessage } from './loadState.js';
32+
import { t, translateConsoleValue, type SupportedLocale } from '../metadata-admin/i18n.js';
33+
import { type LoadState, loadErrorMessage } from '../metadata-admin/loadState.js';
3434

3535
export interface AuditPanelProps {
3636
type: string;

ui/packages/app-shell/src/views/metadata-admin/JsonSourceEditor.fallback.test.tsx renamed to ui/packages/app-shell/src/views/item-tools/JsonSourceEditor.fallback.test.tsx

File renamed without changes.

ui/packages/app-shell/src/views/metadata-admin/JsonSourceEditor.monaco-export.test.tsx renamed to ui/packages/app-shell/src/views/item-tools/JsonSourceEditor.monaco-export.test.tsx

File renamed without changes.

ui/packages/app-shell/src/views/metadata-admin/JsonSourceEditor.tsx renamed to ui/packages/app-shell/src/views/item-tools/JsonSourceEditor.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
import React from 'react';
2323
import { Skeleton } from '@object-ui/components';
2424
import * as jsonc from 'jsonc-parser';
25-
import { useMetadataLocale, t } from './i18n.js';
26-
import { useMonacoFallback } from './useMonacoFallback.js';
25+
import { useMetadataLocale, t } from '../metadata-admin/i18n.js';
26+
import { useMonacoFallback } from '../metadata-admin/useMonacoFallback.js';
2727

2828
// Lazy: Monaco's React wrapper itself pulls in the editor core
2929
// (~3MB), so we keep it out of the initial app-shell chunk.

ui/packages/app-shell/src/views/metadata-admin/LayeredDiff.overlayScope.test.tsx renamed to ui/packages/app-shell/src/views/item-tools/LayeredDiff.overlayScope.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { render, screen, cleanup } from '@testing-library/react';
2323
import { GetMetaItemLayeredResponseSchema } from '@objectstack/spec/api';
2424
import type { MetadataLayered } from '@object-ui/data-objectstack';
2525
import { LayeredDiff } from './LayeredDiff';
26-
import { translateConsoleValue } from './i18n';
26+
import { translateConsoleValue } from '../metadata-admin/i18n';
2727

2828
afterEach(cleanup);
2929

0 commit comments

Comments
 (0)