Repository navigation
feat(studio): a read-only base and its customization pair as one surface - #28747
Conversation
…urface Each Studio pillar rail lists the pair's added items beside the base's, marks customized and added rows from the served customization read, and offers an only-customizations filter. The package switcher folds the pair under its base, a pair's pillar URL lands on the base, and New on a read-only base lands in the pair through the ensure door. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jV3gDN72FtkqvGmfBd
Pins the marks, the only-customizations filter, the switcher fold, the pair URL redirect and its metadata-area exemption, New into the pair, the added item's save at the pair, and that nothing third-party is drawn. A deep link to an item the pair adds opens once the pair is known. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jV3gDN72FtkqvGmfBd
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jV3gDN72FtkqvGmfBd
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jV3gDN72FtkqvGmfBd
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jV3gDN72FtkqvGmfBd
|
domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y 席位核对 · ACCEPT · 2026-11-01T17:07ZHead-sha:
Generated by Claude Code |
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 41330449085 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 分类: 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
Brings in the All metadata area's form editor. Its two tests construct and compare the surface's customization, which now also carries ensurePackage and the base's customization read. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jV3gDN72FtkqvGmfBd
|
domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y 席位核对 · ACCEPT (re-check after the queue failure) · 2026-11-01T18:11ZHead-sha: This replaces the ACCEPT at
Generated by Claude Code |
Requested by Steedos · project thread
Fixes #28572
Clause-②: no
Metadata-protocol: none
维护者速读
.custom合成一个界面。四个栏位(数据、自动化、权限、界面)在同一个列表里同时列出基础包的项和定制包新增的项,并标出「已定制」或「新增」。每个栏位多了一个「仅看定制」开关。包切换器不再单独列出定制包,对子地址会跳到基础包。在只读基础包上点「新建」,会经由 ensure 接口建到定制包里。app-shell的 studio-design 目录和 studio 文案(en/zh),没有改协议、服务端或 spec。可写包,以及没有定制包的只读包,读取和显示都和原来一样(有对照测试)。回滚就是 revert 这个 PR。StudioRailCustomizations.tsx(共用的合并、标记、筛选和深链兜底逻辑),然后看StudioDesignSurface.tsx里的StudioDesignSurface外壳(对子地址跳转,以及 metadata 区不跳转)和InterfacesPillar的doCreateSurface(新建进对子)。cd ui && pnpm exec vitest run packages/app-shell/src/views/studio-design/StudioDesignSurface.oneSurface-28572.test.tsx,10 条全过。What changes
Studio now shows a read-only base and its paired customization package (
<base>.custom) as one surface (ADR-0146 D3, "On top, one surface"). Everything is inui/packages/app-shell/src/views/studio-design/**and the studio i18n namespace.addedIn: <pair>(withPairItems). An override the pair serves under the base's name stays the base's row, as ADR-0146 D8 item 2 serves it. The Interfaces rail does this for pages, dashboards, reports and views (its off-nav sections).GET /api/v1/packages/:id/customizations) through the one rulecustomizationMarks.tsalready holds. An override the read lists is "customized". Any other row of the pair is "added". Every other row has no mark. Nothing is computed from the documents. A failed read marks nothing and shows the server's sentence under the rail's search. The read refreshes on Publish.StudioRail's newfiltersslot), shown once some row carries a mark. On Interfaces it lists every marked item in place of the nav tree, including ones a nav entry opens.<pair>") (foldCustomizationPairs)./studio/<pair>/<pillar>lands on/studio/<base>/<pillar>, with its query and router state kept. The pairing comes from the spec's rule (customizationBasePackageId), so the redirect never waits on the package list. A?surface=link to an item the pair adds opens it once the pair is known (useDeepLinkFallback). The rail's first read happens before the package list answers, so it opens its default first. It then switches to the linked item if the author has not moved off that default.Surface.addedInand its siblings in the Data, Automations and Access pillars). They are saved as they are, with no override declaration. They show no Customize and no read-only link, and the item header names the pair (StudioPairItemChip).StudioBaseCustomization.ensurePackage) in all four pillars. It then saves into the package the door answered, with the door'snamespacewhere a name is prefixed (objects). After the create, each rail re-reads, so the new row is listed as added. On Interfaces, the "+" offers only page, dashboard and report there. The created item links no nav entry, because the base's app takes no edit. The dialog says where it lands. A walled admin is offered no New on a read-only base.en,zh). That namespace is typedRecord<string, string>and sits outside the locales packs, soClause-②: noholds as claimed.Named decisions and deviations
nothing a third package contributes is drawnpins that no rail asks another package's list and no row names one. The server side is the card runtime: a served read naming which package contributes to an installed base — extension fields, flows on its objects, navigation entries — so Studio can draw contributed items greyed with their source (ADR-0029 D7, ADR-0146 D3) #28722.addedIn, taken from the read that listed it). ⛔ No write names a.customid spelled in the browser. The spec's pairing rule is used for READS only: the redirect, and matching a "New similar" arrival to the rail (pairIdOf)./studio/<pair>/metadatais not redirected. The "All metadata" area lists the pair's items under?package=<pair>and re-roots there, so redirecting it would loop. The testthe pair's "All metadata" URL stays on the pairpins this. Ablating the exemption reddens it.Tests
StudioDesignSurface.oneSurface-28572.test.tsx, 10 tests. It mounts the realStudioDesignSurfaceand metadata client over a fetch stub. Covered:200), saving into the pair, with no app save;InterfacesPillar.customize-28562.test.tsx(its host passes the newensurePackageandmarks) andStudioCustomizeControls.customize-28562.test.tsx(the door answer now carriesnamespace).scripts/ablation-replace.mjsagainst the new file, with the file restored to HEAD each time. Each went red:addedInstamp;Acceptance notes
Verification
pnpm --filter @object-ui/app-shell --filter @object-ui/i18n run type-check(source andtsconfig.test.json): exit 0. ESLint on the touched files: 0 errors.check:i18n-keys,check:i18n-driftandcheck:i18n-dead-keys(fromui/): exit 0.studio-locale.i18nandzh-base-term.i18n-13131, fromui/underos-verify-lock: all passed. Because the deep-link fallback landed after the first three chunks ran, the 14 files there that touch?surface=links were run again at the final code: 14/14.packages/i18n/(94 files): passed.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack: 34 commands, run and reconciled with--ran(results in the card report).origin/maintwice (7 commits, then 5, no conflict). Neither range touchesstudio-design/**or the studio namespace, so there is no parallel same fix. The gate sweep and the suites ran at 65e3b1b, andcheck-sdui-manifestwas re-run at the head.https://claude.ai/code/session_01QsR2jV3gDN72FtkqvGmfBd
🤖 Generated with Claude Code