Repository navigation
Commit 73a3ef9
[maintainer] refactor(app-shell): move Studio's package switcher out of StudioDesignSurface.tsx (slice 2) (#30008)
<!-- claude-projects-attribution: {"github_login":"os-support-ai"} -->
_Requested by **Yibo Wang** · [project
thread](https://claude.ai/code/project/chan_01BVUyyPie8gHwoPM18iABwB?thread=cmsg_01BVUyyPie8gHwoPM18iABwBCE4YXYCrA6G1yrthEDVvNJ)_
Refs #29988
## 维护者速读
- 改了什么:把 Studio 顶栏的「套件切换器」(套件列表、新建套件、套件详情、套件被删后的跳转)及它与主界面共用的两个常量(663
行)原样搬进同目录新文件 `PackageSwitcher.tsx`,主文件 11,884 → 11,205 行;顺带把 QA
清单里两处还指着旧文件的出处改指新文件。
- 为什么改:#29988 按裁定 #24586 A 逐片只搬不改,把这个文件拆到 1 万行以下;这是第 2 片(接缝表第 2 行),第 1
片是 #30002。
- 风险与回滚:无行为变化,搬走的两段代码逐字节一致,只多了 import 和三个 `export`;对外类型声明 700
个文件逐字节不变;回滚即 revert 本 PR。
- 先看:`StudioDesignSurface.tsx` 的 import 段;`PackageSwitcher.tsx` 的 import
段与三个 `export`;`studio-authoring.json` 两处出处改动与新加的 history 行。
- 两分钟验证:在本分支跑下方「Verification」里的两条 `cmp` 命令,预期都无任何输出(逐字节相同)。
- 需要你做的:无,席位落地。
Clause-②: no
Metadata-protocol: none
## Seam table
Carried forward from #30002. Base lines below are at `origin/main`
`f7aab8958` (this PR's base, after slice 1), where
`ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx`
is 11,884 lines. "Left" is the file's line count after that row and
every row above it: rows 1–2 are measured, the rest are estimates (lines
removed, plus about two import lines per slice). Every seam is
module-scope; no seam closes over another component's state.
| # | Seam | Base lines | Size | Reads from the rest of the module ·
hooks it calls | Imported by | Risk | Left |
|---|---|---|---|---|---|---|---|
| 1 | Nav item inspector (`StudioNavItemInspector`, nav target helpers,
`StudioPicker`) | landed | 978 | — | — | **landed in #30002** | 11,884
(measured) |
| **2** | **`PackageSwitcher` with `PACKAGE_LIST_TOAST_ID` and
`STUDIO_LANDING_PATH`** | 832–846, 897–1544 | **663** (15 + 648) |
nothing · `useHomePath`, `useMetadataLocale`, `useNavigate` |
`StudioDesignSurfaceBody` (all three); `DataPillar` (toast id) | low:
self-contained, props only | **11,205** (measured, this PR) |
| 3 | Interfaces nav tree rail: `NavTree`, `NavTreeGroup`,
`ContributedNavRows`, `OffNavSections`, the two rail contexts,
`navKindName`, `navOpenedItems`, with `KIND_ICON` / `navIcon` | 677–693,
2928–3352 | 442 | nothing · `useMetadataLocale`, `useNavTargetLabel`,
`useOpenContributor` | `InterfacesPillar` only | low: self-contained | ≈
10,765 |
| 4 | Flow rail helpers: `FlowRuntimeState` … `readFlowCopiedArrival`,
incl. `FlowStatusDot`, glyph tables, `pairIdOf` | 8740–9087 | 348 |
nothing · no hooks | `AutomationsPillar`; `AccessPillar` (`pairIdOf`); 2
tests import `FlowStatusDot` | low: pure helpers plus one prop-only
component; carries the spec-symbol ledger's `FlowRuntimeState` site | ≈
10,419 |
| 5 | Pillar kit: `useHeldEditReport`, `confirmDropUnsavedWork`,
`findTakenIdentifier`, `CreateTakenNotice`, `StudioHeldNotice`, the
"Show me" focus helpers, `announceDraftSaved`, `StudioReadOnlyLink`,
`StudioBaseCustomization` | 522–661, 709–787, 800–830, 848–895,
3360–3385 | 320 | nothing · `useStudioTabRowFold` | all four pillars and
`StudioDesignSurfaceBody` | low: five non-contiguous blocks; lets rows
7–10 move without an import cycle | ≈ 10,101 |
| 6 | Data pillar helpers (`addedFieldName` … `formPreviewSource`, the
OWD option keys) | 461–478, 6262–6502 | 259 | nothing · no hooks |
`DataPillar` only; 1 test imports `renderStudioGridList` | low: pure
helpers | ≈ 9,844 (under 10,000) |
| 7 | `AccessPillar` with its arrival reader and `StudioNameLabel` |
2902–2926, 10941–11882 | 967 | rows 4, 5 · 11 hooks |
`StudioDesignSurfaceBody`; 7 test files | medium: whole stateful
component; after rows 4–5 | ≈ 8,879 |
| 8 | `AutomationsPillar` | 9089–10939 | 1,851 | rows 4, 5 (and
`StudioPicker`, row 1) · 21 hooks | `StudioDesignSurfaceBody`; 23+ test
files | medium: after rows 4–5 | ≈ 7,030 |
| 9 | `DataPillar` | 6506–8738 | 2,233 | rows 2, 5, 6 (and
`StudioPicker`) · 17 hooks | `StudioDesignSurfaceBody`; 38+ test files |
medium: holds three `clickableNonControlCensus-13461` rows keyed by this
file | ≈ 4,799 |
| 10 | `InterfacesPillar` with `interfacesSaveBody`, `leafKeyOf`,
`customizeItemKey` | 502–520, 3357–3358, 3387–6260 | 2,895 | rows 1, 3,
5 · 23 hooks | `StudioDesignSurfaceBody`; 29+ test files | medium: close
to the 3,000-line mark; holds one census row | ≈ 1,906 |
| 11 | `StudioDesignSurface` / `StudioDesignSurfaceBody`, `PILLARS`,
`METADATA_AREA_TAB`, `PILLAR_FOR_SURFACE_TYPE`, publish-refusal and
backfill helpers | 437–460, 479–501, 663–675, 789–798, 1546–2900 | ≈
1,425 | rows 2, 5, 7–10 · 11 hooks | `index.ts`, the console's lazy
Studio route, 51 test files | stays: this is the file | — |
Unchanged from #30002: nothing below the whole-component level of rows
7–11 moves without a logic change, and none of it is needed, because
rows 3–6 bring the file under 10,000. Serial order: rows 3–6 in any
order, then rows 7–10 only after row 5.
## What changes
- `ui/packages/app-shell/src/views/studio-design/PackageSwitcher.tsx`
(new): row 2, base lines 832–846 and 897–1544, each byte for byte, under
a copyright line, a module comment and the imports the two blocks read.
The block-level edits are `export` on `const PACKAGE_LIST_TOAST_ID`,
`const STUDIO_LANDING_PATH` and `function PackageSwitcher(`, which the
surface (all three) and `DataPillar` (the toast id) import back. The
first block starts with the switcher's own doc comment, which at base
sat stranded above the toast id's doc comment; it moved with that block
unchanged.
-
`ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx`:
both blocks are gone. It imports the three names from the new module
beside the slice 1 import, and drops the import bindings only the
switcher used (`AppMark`, `STUDIO_PKG_READONLY_WORD`, `Popover` /
`PopoverTrigger` / `PopoverContent`, `AlertTriangle`, `Boxes`,
`Building2`, `Settings`, `useHomePath`, `CreatePackageDialog` /
`PackageDetailSheet` / `InstalledPackageRow`, `foldCustomizationPairs`);
`AppMarkApp` stays as `import type`. Nothing was exported before, so
there is no re-export.
- By-path readers (ruling 6597555122 item 3):
-
`ui/packages/app-shell/src/__tests__/homeRecoveryRedirectsFollowDeclaration-7373.test.ts`:
the Studio site's `alsoReads` gains `PackageSwitcher.tsx`. Its needle
`const homePath = useHomePath()` moved with the switcher, so the site
now holds only through this entry (ablation B1).
- `ui/scripts/__tests__/spec-symbol-ledger-app-shell-7265.test.ts`:
`SITES` gains `PackageSwitcher.tsx`.
- `docs/qa/platform-checklist/areas/studio-authoring.json`, source
re-points only, each with a revision bump and a history row (the area's
convention; "No clause changed"):
- `studio-authoring.app-and-nav-properties` r5 → r6: `source[2]` now
cites `StudioNavItemInspector.tsx (the Studio menu-entry inspector)`,
the slice 1 follow-up.
- `studio-authoring.first-run-loop` r11 → r12: `acceptance[0].verify`
named `StudioDesignSurface.tsx` as where the `studio-pkg-chip` testid is
declared; that testid moved with the switcher, so it now names
`PackageSwitcher.tsx`. The clause text is unchanged.
- `.changeset/29988-studio-package-switcher-module.md`:
`@object-ui/app-shell` patch, "internal: move-only".
## Tests
No behaviour changes, so no new behaviour test. The existing suites run
unchanged; results are in Verification.
## Acceptance notes
- **Readers re-grepped.** `git grep` of `StudioDesignSurface.tsx` /
`studio-design/StudioDesignSurface` over `scripts/**`, `ui/scripts/**`
and every ui test file finds the same three by-path readers as slice 1.
The two whole-text scanners gained the new module (above).
`clickableNonControlCensus-13461` walks the tree itself, keys rows by
file and handler, and none of its rows is a switcher handler, so it
needs no edit. No gate under `scripts/**` names the file.
- **Checklist rows re-read.** Every `StudioDesignSurface.tsx` citation
under `docs/qa/` was read against the moved block: only the
`studio-pkg-chip` declaration moved. The others cite the pillars, the
publish flow and `pkgWritable`, which stay.
- **Two test comments now name the old file for moved text** and are
left as they are (prose in test files, outside this slice's file
surface): `PackagesPage.confirmFlows-11784.test.tsx`
("`PackageSwitcher`'s eviction in `StudioDesignSurface.tsx`") and
`packages-io.inflightShare-11797.test.tsx` ("the `PACKAGE_LIST_TOAST_ID`
doc in `StudioDesignSurface.tsx`"). The seat can re-point them with the
next touch of either file.
- **Serial check.** No open PR touched `StudioDesignSurface.tsx`, either
extended reader or `studio-authoring.json` when the seat claimed
(02:34Z) or right before this push (see Verification). The branch was
restarted from `origin/main` after #30002 merged and is rebased on
`f19fbb8f`; nothing after `f7aab8958` touches `ui/` or `docs/qa/`.
## Verification
<details><summary>Move-only proof: byte-compare and the moved-lines
diff</summary>
```
B=ui/packages/app-shell/src/views/studio-design
git show f7aab89:$B/StudioDesignSurface.tsx | sed -n 832,846p > /tmp/blockA
git show f7aab89:$B/StudioDesignSurface.tsx | sed -n 897,1544p > /tmp/blockB
sed -n 43,57p $B/PackageSwitcher.tsx | sed 's/^export const PACKAGE_LIST_TOAST_ID = /const PACKAGE_LIST_TOAST_ID = /' | cmp /tmp/blockA -
tail -n +59 $B/PackageSwitcher.tsx | sed -e 's/^export const STUDIO_LANDING_PATH = /const STUDIO_LANDING_PATH = /' -e 's/^export function PackageSwitcher({/function PackageSwitcher({/' | cmp /tmp/blockB -
```
Both silent (identical): block A 15 lines, sha256 `aa1ecaea48757927…`;
block B 648 lines, sha256 `e5c09d177d162507…`.
`git diff --color=always --color-moved=zebra f7aab89 HEAD --
ui/packages/app-shell/src/views/studio-design/`: 664 lines removed and
664 added are coloured as moved (the 663 block lines plus import
specifier lines). Apart from import lines and the new module's header
comment, every line NOT coloured as moved:
```
+export const PACKAGE_LIST_TOAST_ID = 'studio-package-list';
+export const STUDIO_LANDING_PATH = '/studio';
+export function PackageSwitcher({
-const PACKAGE_LIST_TOAST_ID = 'studio-package-list';
-
-const STUDIO_LANDING_PATH = '/studio';
-
-function PackageSwitcher({
-
```
(the removed blank lines are the separators after each cut block).
</details>
<details><summary>Declaration files (ruling 6348891160's bar)</summary>
Base declarations emitted from the base source into a scratch `outDir`
under the lock, tree restored (`git diff HEAD` empty). All 700 base
`.d.ts` files, `StudioDesignSurface.d.ts` included (sha256
`d0f9665b1e9ed6fc…`), are byte-identical at head: the switcher was never
exported, so no declaration moved. The one addition is
`views/studio-design/PackageSwitcher.d.ts` / `.js`, reachable from no
entry in the package's `exports` map (`"."` only). Declared-surface
digest of `dist/index.d.ts` plus `StudioDesignSurface.d.ts`: 245
exports, `53af282f1e0f28e8…` on both sides, the same digest slice 1
recorded.
</details>
<details><summary>Build, type-check, lint and tests (head ac845e5e; the
rebased head 02c3a7f has a byte-identical ui/, docs/qa/ and changeset,
checked with git diff --quiet)</summary>
All under scripts/pm/os-verify-lock.sh, one locked chain: VERDICT
command-exit 0.
- npx turbo run build --filter='@object-ui/app-shell...'
--concurrency=2: exit 0.
- pnpm --dir ui --filter @object-ui/app-shell type-check (tsc --noEmit,
then tsconfig.test.json): exit 0.
- pnpm --dir ui run type-check:scripts: exit 0.
- npx eslint on the two source files and the 7373 test, from
packages/app-shell: exit 0.
- pnpm --dir ui exec vitest run --maxWorkers=3
packages/app-shell/src/views/studio-design/ plus the 7373 test,
PermissionMatrixEditor.saveSurface-12725,
builtinDesigners.lateArrival-11939, observableRegistries-11939,
PackagesPage.confirmFlows-11784, clickableNonControlCensus-13461 and
spec-symbol-ledger-app-shell-7265: exit 0, Test Files 329 passed (329),
Tests 2445 passed (2445).
- Narrowed, and declared: the full @object-ui/app-shell suite does not
finish locally on the shared box; the run covers every test in the moved
file's directory and every test outside it that imports the file, reads
it by path or names the switcher. CI's Test UI runs the rest.
- node scripts/check-platform-checklist.mjs: exit 0 (16 areas, 359
items); with --changed-from origin/main: exit 0.
</details>
<details><summary>Ablation of the two extended readers (one-time,
through scripts/ablation-replace.mjs; tree restored, git diff HEAD
empty)</summary>
| Leg | Mutation | Expected | Observed |
|---|---|---|---|
| B0 | none: the 7373 test as in this PR | green | exit 0: 15 passed |
| B1 | the Studio site's alsoReads loses PackageSwitcher.tsx (the old
file and the slice 1 module only) | red: the useHomePath needle moved |
exit 1: "resolves its target through the policy" fails for the Studio
site; 1 failed, 14 passed |
| B2 | a local interface ObjectLike added to PackageSwitcher.tsx, SITES
as in this PR | red | exit 1: "rule 1 finds no spec-named local
declaration in ...PackageSwitcher.tsx" fails; 1 failed, 20 passed |
| B2b | same mutation, the PackageSwitcher.tsx SITES row removed |
green, i.e. blind | exit 0: 20 passed |
Each mutation's landing was proven by the tool (anchor count 1 to 0,
blob hashes) and by a grep count of the changed text.
</details>
<details><summary>Derived gates: node scripts/pm/dispatch-gates.mjs
--commands on head 02c3a7f (42 commands)</summary>
```
node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0
node scripts/check-adr-0087-registration.mjs --self-test :: exit 0
node scripts/check-adr-links.mjs :: exit 0
node scripts/check-adr-links.mjs --self-test :: exit 0
node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0
node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/check-empty-changeset.mjs --base origin/main :: exit 0
node scripts/check-empty-changeset.mjs --self-test :: exit 0
node scripts/check-sdui-manifest.mjs :: exit 0
node scripts/check-sdui-manifest.mjs --self-test :: exit 0
node scripts/ci/plan-test-core-shards.mjs --self-test :: exit 0
node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0
node scripts/release-pending-publish.mjs --self-test :: exit 0
pnpm --dir ui exec vitest run scripts/ :: exit 0
pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 0
pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0
pnpm check:changeset-gate-self-tests :: exit 0
pnpm check:ci-filter-parity :: exit 0
pnpm check:closed-ceremony-text :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:dispatcher-error-vocabulary :: exit 0
pnpm check:doc-authoring :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:engine-double-contract :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:platform-checklist :: exit 0
pnpm check:published-files :: exit 0
pnpm check:query-options-erasure :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:registry-log-declared :: exit 0
pnpm check:rest-log-spy-declared :: exit 0
pnpm check:skill-identifier-liveness :: exit 0
pnpm check:test-source-alias :: exit 0
pnpm check:tier-file-adoption :: exit 0
pnpm check:type-check-coverage :: exit 0
pnpm check:type-check-debt :: exit 0
pnpm check:undeclared-dep-imports :: exit 0
pnpm check:watch-hint-literal :: exit 0
pnpm check:where-matcher :: exit 0
```
node scripts/pm/dispatch-gates.mjs --ran: "42 derived famil(ies)
accounted for — 42 run, 0 NOT-MEASURED". The formula and lint dists were
built first (cached), so check:doc-formula-expressions measured rather
than exiting 3.
</details>
<details><summary>Serial check before push</summary>
The 17 open PRs at 2026-11-04T03:22Z, each PR's full file list read page
by page: none touches StudioDesignSurface.tsx, either extended reader or
studio-authoring.json.
</details>
---
_Generated by [Claude
Code](https://claude.ai/code/session_01WGufzedfGwVcebpN5t3ETE)_
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01WGufzedfGwVcebpN5t3ETE
Co-authored-by: Claude <noreply@anthropic.com>1 parent 217ba78 commit 73a3ef9
6 files changed
Lines changed: 735 additions & 687 deletions
File tree
- .changeset
- docs/qa/platform-checklist/areas
- ui
- packages/app-shell/src
- __tests__
- views/studio-design
- scripts/__tests__
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
12 | | - | |
| 12 | + | |
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
| |||
40 | 40 | | |
41 | 41 | | |
42 | 42 | | |
43 | | - | |
| 43 | + | |
44 | 44 | | |
45 | 45 | | |
46 | 46 | | |
| |||
97 | 97 | | |
98 | 98 | | |
99 | 99 | | |
100 | | - | |
| 100 | + | |
| 101 | + | |
101 | 102 | | |
102 | 103 | | |
103 | 104 | | |
| |||
3511 | 3512 | | |
3512 | 3513 | | |
3513 | 3514 | | |
3514 | | - | |
| 3515 | + | |
3515 | 3516 | | |
3516 | 3517 | | |
3517 | 3518 | | |
| |||
3586 | 3587 | | |
3587 | 3588 | | |
3588 | 3589 | | |
3589 | | - | |
| 3590 | + | |
3590 | 3591 | | |
3591 | 3592 | | |
3592 | 3593 | | |
| |||
3619 | 3620 | | |
3620 | 3621 | | |
3621 | 3622 | | |
| 3623 | + | |
| 3624 | + | |
| 3625 | + | |
| 3626 | + | |
| 3627 | + | |
| 3628 | + | |
3622 | 3629 | | |
3623 | 3630 | | |
3624 | 3631 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
136 | 136 | | |
137 | 137 | | |
138 | 138 | | |
139 | | - | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
140 | 143 | | |
141 | 144 | | |
142 | 145 | | |
| |||
0 commit comments