Repository navigation
Commit 1e6f542
feat(studio): a contributed nav entry shows the label the live app shows, and a contributor-only package says where its entries go (#30371)
<!-- claude-projects-attribution: {"github_login":"os-bill"} -->
_Requested by **Bill** · [project
thread](https://claude.ai/code/project/chan_016iMXWSZDzqWRWvWzdNo8wg?thread=cmsg_016iMXWSZDzqWRWvWzdNo8wgBZMqtfRtfa9Hih8oCkjd71)_
domain:ui#3 · os-bill · session_01Utp6CbPN5UYMnBaNJPJWfG
## 维护者速读
- 改了什么:Studio「界面」里别的包加进来的导航项,现在显示线上应用里看到的名字(如 Orders),id
降为下面一行灰字;只往别人应用里加导航、自己没有应用的包,不再说「还没有应用」,而是说「它向 X 的应用添加了 N 个导航项」并给打开按钮。
- 为什么改:之前树里显示 nav_orders 这类
id,和线上应用对不上;贡献包的空状态说「没有应用」,实际它的导航出现在别的应用里,读者会误会。
- 风险与回滚:只改 Studio 显示,不改元数据、不改服务端;名字只在 id 唯一时才取,重复 id 仍显示
id;读失败、包停用、目标应用不存在或计数为 0 时保持原文。回滚即 revert 本 PR。
- 先看:InterfacesNavRail.tsx 的
contributedNavLabel(名字从哪来);StudioRailCustomizations.tsx 的
useContributedElsewhere(空状态发哪两次读);StudioDesignSurface.tsx 合并后的空状态分支。
- 两分钟验证:在 app-multi-package 上打开 Studio 的 Multi-Package Core「界面」,看到
Orders 下面一行 nav_orders;再打开 Multi-Package Orders「界面」,看到「It adds 1
navigation entry to Multi-Package Core’s app.」。
- 需要你做的:无,席位落地。
Fixes #29851
Clause-②: yes (widening) — five new `@object-ui/i18n` studio keys, in
`en` and `zh`
Metadata-protocol: none
## What changes
- **The label (part 1, amendment 6598320473).** A navigation entry
another package contributes to a base's app now leads with the label the
live app shows, with its id as a muted second line and the package chip
under it. The label is looked up by `(app, id)` in the app row the
Interfaces pillar already reads (`GET /meta/app?package=`), which serves
the folded navigation. `servedContributedNavOf` keeps only the ids the
published layer does not hold and that occur exactly once in the served
tree, so an id the base also holds, or one two contributions share,
keeps the bare id and no label is guessed. A label-less entry reads what
it inherits through the rail's own resolver (`navEntryLabelText` plus
`useNavTargetLabel`). No request is added. This is a lookup by id in a
served read, not the join on names ruling 6566915346 refuses.
- **The contributor's empty state (part 2, option A).** A package with
no app of its own whose listed manifest declares
`navigationContributions` now says "This package has no app of its own.
It adds N navigation entries to OWNER's app.", with an Open button, in
the canvas and in the rail. The declared app is an address only: its
owner comes from the served `GET /meta/app/:app` (`_packageId`), and the
count from the owner's served `GET /packages/:owner/customizations`
`contributions`, filtered by `kind: nav`, this `packageId` and that app.
At most two reads per declared target app (one customization read per
owner), sent only on that empty state. When the package is disabled, the
target app is missing, either read fails, or the count is 0, the text
stays "This package has no app yet."
- The empty state reads the open package's declaration off the package
list the Studio surface already fetched (`fetchListedPackageManifests`,
passed down as `listedManifests`), so it sends no package-list request
of its own.
- **Strings.** `engine.studio.if.noOwnApp`, `.noOwnAppTitle`,
`.contributesOne`, `.contributesOther`, `.openOwner`, in the studio `en`
and `zh` packs. No metadata key is added.
- **Main moved under this branch.** The Interfaces nav rail moved out of
`StudioDesignSurface.tsx` into `InterfacesNavRail.tsx` (move-only split,
#29988). The merge takes main's move and carries this card's rail hunks
with it: `InterfacesServedContributedNavContext`, the `label` passed to
`ContributedRailRow` in `ContributedNavRows`, and `contributedNavLabel`.
`InterfacesNavRail.tsx` is outside the claim's listed file surface by
name only; it holds the same code the claim named in
`StudioDesignSurface.tsx`.
## Tests
- `contributionRows.test.ts`: the served entries keyed by id at any
depth and never the base's own; an id the base also holds keeps no
label; an id two contributions share keeps no label; no served row or
another app's gives nothing; the declared target apps are addresses,
each once, in order; the count of the owner's served nav contributions
for the package and app.
- `StudioDesignSurface.oneSurface-28572.test.tsx`, a new top-level
`describe` in the existing harness: a unique id shows its served label
with the id as a second line and a shared id keeps the bare id; CONTROL,
no served fold keeps the id alone; a contributor-only package says what
it adds from two reads and opens the owner; FALLBACK disabled package;
FALLBACK missing target app (the owner is never read); FALLBACK failed
app read or failed owner read; FALLBACK 0 entries; CONTROL, a package
with no app that declares no contribution reads nothing more.
## Acceptance notes
- Overlap with in-flight studio-design claims, read after the merge:
this diff touches `StudioDesignSurface.tsx` in the imports, the
surface's package-list effect, the `InterfacesPillar` props and its
no-app empty state; #30088 holds that file's actions branch, a different
region. It touches none of `ObjectListViewInspector.tsx` (#30177,
#30089), `ObjectActionsPanel.tsx` (#30088) or
`StudioBundleItemEditor.tsx` (#30285).
- The changeset carried `Clause-②: no` on the resume branch; it now says
`yes (widening)`, as the five new published i18n keys make it. The level
stays `patch` (kind `none`).
## Verification
All readings on head `98ed7206f4` (main merged at `9afcd6a6b6`),
repository objectstack-ai/objectstack.
<details><summary>Type-check and tests</summary>
- `pnpm exec turbo run type-check --filter=@object-ui/app-shell
--filter=@object-ui/i18n --concurrency=2`: Tasks 69 successful, 69
total; lock VERDICT command-exit 0.
- vitest (root `ui/`) over every test file that imports
`contributionRows`, `StudioRailCustomizations`, `InterfacesNavRail` or
`StudioDesignSurface` (193 files, by `git grep`), plus `packages/i18n/`:
Test Files 288 passed (288); Tests 2728 passed, 13 skipped (2741); lock
VERDICT command-exit 0.
- `pnpm --dir ui exec vitest run scripts/`: Test Files 170 passed, 2
skipped (172); Tests 4985 passed, 2 skipped.
- `pnpm check:new-line-citations` (ui): 0 new citations.
</details>
<details><summary>Derived gates</summary>
- `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derived 35 families from 9 changed paths
(661 changed lines, under the 3000 threshold). All 35 ran and exited 0,
`node scripts/check-sdui-manifest.mjs` included.
- `dispatch-gates --ran`: 35 derived, 35 run, 0 NOT-MEASURED, 0 UNRUN.
</details>
<details><summary>Ablation (each leg restored, git diff HEAD empty
after)</summary>
- `servedContributedNavOf` forced to return nothing (`node
scripts/ablation-replace.mjs`, anchor hit 1 to 0): 4 pins red, the three
helper pins and "a unique id shows its served label with the id as a
second line". Restored blob equals HEAD.
- `if (count === 0) return null;` deleted from
`useContributedElsewhere`: 2 pins red, "FALLBACK: a disabled package"
and "FALLBACK: 0 entries for the declared app". Restored blob equals
HEAD.
</details>
<details><summary>Live, en and zh, fresh boot of the merged
tree</summary>
Backend `examples/app-multi-package`, `objectstack dev --seed-admin
--fresh` on its own port; the Console from `ui/apps/console` src through
Vite pointed at it; Chromium through Playwright.
- Multi-Package Core, Interfaces, Sales group, contributed row: en
"Orders / nav_orders / from Multi-Package Orders"; zh "Orders /
nav_orders / 来自 Multi-Package Orders".
- Multi-Package Orders, Interfaces, canvas: en "This package has no app
of its own / It adds 1 navigation entry to Multi-Package Core’s app. /
Open Multi-Package Core"; zh "这个软件包没有自己的应用 / 它向 Multi-Package Core
的应用添加了 1 个导航项。 / 打开 Multi-Package Core". The rail says the same in one
line each.
- Shots: `ui-seat3/issue-29851/shots/` in the project folder
(`en-base-contributed-row.png`, `en-contributor-empty-state.png`,
`zh-base-contributed-row.png`, `zh-contributor-empty-state.png`).
</details>
<details><summary>Measured request counts</summary>
- Reads on opening a package's Interfaces pillar, as the browser sent
them. Multi-Package Core (has an app): two `GET /api/v1/packages`, no
`/meta/app/:app` and no other package's customizations. Multi-Package
Orders (contributor only): the same two `GET /api/v1/packages`, plus
`GET /meta/app/multi_crm` and `GET
/packages/com.example.multi.core/customizations`. The empty state adds
two reads and no package-list read.
- Platform packages on this boot (`GET /api/v1/packages`, 24 listed): 11
declare `navigationContributions` for `setup`. One of them is
`com.objectstack.setup`, which owns the app and never reaches the empty
state, so 10 contributor-only packages show the new sentence for
`setup`. `com.objectstack.mcp.connect-agent-ui` also targets `account`
(owner `com.objectstack.account`), so it sends four reads and reads "It
adds 1 navigation entry to Setup’s app." and "It adds 1 navigation entry
to Account’s app.". Across the 10 that is 11 targets, 22 reads in all,
each sent only when that package's pillar is opened.
</details>
---
_Generated by [Claude
Code](https://claude.ai/code/session_01Utp6Cb8MSvSrJz9nXGSj2F)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 26c2efd commit 1e6f542
9 files changed
Lines changed: 646 additions & 15 deletions
File tree
- .changeset
- ui/packages
- app-shell/src/views/studio-design
- i18n/src/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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
44 | 44 | | |
45 | 45 | | |
46 | 46 | | |
47 | | - | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
48 | 54 | | |
49 | 55 | | |
50 | 56 | | |
| |||
206 | 212 | | |
207 | 213 | | |
208 | 214 | | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
209 | 222 | | |
210 | 223 | | |
211 | 224 | | |
| |||
221 | 234 | | |
222 | 235 | | |
223 | 236 | | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
224 | 240 | | |
225 | 241 | | |
226 | 242 | | |
227 | 243 | | |
228 | 244 | | |
229 | 245 | | |
230 | 246 | | |
| 247 | + | |
231 | 248 | | |
232 | 249 | | |
233 | 250 | | |
| |||
239 | 256 | | |
240 | 257 | | |
241 | 258 | | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
242 | 274 | | |
243 | 275 | | |
244 | 276 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
55 | 55 | | |
56 | 56 | | |
57 | 57 | | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
58 | 70 | | |
59 | 71 | | |
60 | 72 | | |
| |||
122 | 134 | | |
123 | 135 | | |
124 | 136 | | |
125 | | - | |
| 137 | + | |
126 | 138 | | |
127 | 139 | | |
128 | 140 | | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
129 | 156 | | |
130 | 157 | | |
131 | 158 | | |
| |||
163 | 190 | | |
164 | 191 | | |
165 | 192 | | |
166 | | - | |
167 | | - | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
168 | 215 | | |
169 | 216 | | |
170 | 217 | | |
| |||
216 | 263 | | |
217 | 264 | | |
218 | 265 | | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
219 | 272 | | |
220 | 273 | | |
221 | 274 | | |
| |||
225 | 278 | | |
226 | 279 | | |
227 | 280 | | |
| 281 | + | |
228 | 282 | | |
229 | 283 | | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
230 | 292 | | |
231 | 293 | | |
232 | 294 | | |
| |||
240 | 302 | | |
241 | 303 | | |
242 | 304 | | |
243 | | - | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
244 | 308 | | |
245 | 309 | | |
246 | 310 | | |
| |||
254 | 318 | | |
255 | 319 | | |
256 | 320 | | |
| 321 | + | |
257 | 322 | | |
258 | 323 | | |
259 | 324 | | |
| |||
651 | 716 | | |
652 | 717 | | |
653 | 718 | | |
| 719 | + | |
| 720 | + | |
| 721 | + | |
| 722 | + | |
| 723 | + | |
| 724 | + | |
| 725 | + | |
| 726 | + | |
| 727 | + | |
| 728 | + | |
| 729 | + | |
| 730 | + | |
| 731 | + | |
| 732 | + | |
| 733 | + | |
| 734 | + | |
| 735 | + | |
| 736 | + | |
| 737 | + | |
| 738 | + | |
| 739 | + | |
| 740 | + | |
| 741 | + | |
| 742 | + | |
| 743 | + | |
| 744 | + | |
| 745 | + | |
| 746 | + | |
| 747 | + | |
| 748 | + | |
| 749 | + | |
| 750 | + | |
| 751 | + | |
| 752 | + | |
| 753 | + | |
| 754 | + | |
| 755 | + | |
| 756 | + | |
| 757 | + | |
| 758 | + | |
| 759 | + | |
| 760 | + | |
| 761 | + | |
| 762 | + | |
| 763 | + | |
| 764 | + | |
| 765 | + | |
| 766 | + | |
| 767 | + | |
| 768 | + | |
| 769 | + | |
| 770 | + | |
| 771 | + | |
| 772 | + | |
| 773 | + | |
| 774 | + | |
| 775 | + | |
| 776 | + | |
| 777 | + | |
| 778 | + | |
| 779 | + | |
| 780 | + | |
| 781 | + | |
| 782 | + | |
| 783 | + | |
| 784 | + | |
| 785 | + | |
| 786 | + | |
| 787 | + | |
| 788 | + | |
| 789 | + | |
| 790 | + | |
| 791 | + | |
| 792 | + | |
| 793 | + | |
| 794 | + | |
| 795 | + | |
| 796 | + | |
| 797 | + | |
| 798 | + | |
| 799 | + | |
| 800 | + | |
| 801 | + | |
| 802 | + | |
| 803 | + | |
| 804 | + | |
| 805 | + | |
| 806 | + | |
| 807 | + | |
| 808 | + | |
| 809 | + | |
| 810 | + | |
| 811 | + | |
| 812 | + | |
| 813 | + | |
| 814 | + | |
| 815 | + | |
| 816 | + | |
| 817 | + | |
| 818 | + | |
| 819 | + | |
| 820 | + | |
| 821 | + | |
| 822 | + | |
| 823 | + | |
| 824 | + | |
| 825 | + | |
| 826 | + | |
| 827 | + | |
| 828 | + | |
| 829 | + | |
| 830 | + | |
| 831 | + | |
| 832 | + | |
| 833 | + | |
| 834 | + | |
| 835 | + | |
| 836 | + | |
| 837 | + | |
| 838 | + | |
| 839 | + | |
| 840 | + | |
| 841 | + | |
| 842 | + | |
| 843 | + | |
| 844 | + | |
| 845 | + | |
0 commit comments