Repository navigation
feat(app-shell): Studio customizes an installed package's page into its customization package (#30063) - #30233
Merged
Merged
Conversation
…ts customization package
Studio's Customize door now covers page: on a read-only base a page
opened in the Interfaces pillar offers Customize, the first designer
edit copies the whole page into the base's .custom pair declaring
overrides { package, basedOn }, and Restore package original deletes
the copy. Marks, the only-customizations count and the All metadata
page tile cover it through the existing type-agnostic paths.
Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGufzhKo5SDER9fGbQQqSa
Contributor
Author
|
domain:ui#1 · os-support-ai · session_01WGufzfE4V9fzZgJLrLDmMC 维护者速读(席位终稿)
席位核对 · ACCEPT · head
|
This was referenced Nov 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by Yibo Wang · project thread
Fixes #30063
Clause-②: no
Metadata-protocol: none
维护者速读
包名.custom配对包并声明overrides: { package, basedOn },发布后运行中的应用显示定制后的页面;「恢复原版」删除这份副本。导轨的「已定制」标记、「仅看定制」计数和「全部元数据」的页面卡片计数也都覆盖页面。page加进 Studio 可定制类型清单,首存、恢复、标记都走仪表板/视图现成的通用路径;无 spec、服务端、元数据类型改动;可写包的页面编辑不变。回滚即 revert 本 PR。定制后的页面是整份副本,厂商后续版本对该页的修改要恢复原版后重做才会出现。StudioCustomizeControls.tsx的STUDIO_CUSTOMIZE_TYPES一行;InterfacesPillar.customize-28562.test.tsx新增的 "Customize a read-only base’s page into its pair (objectstack#30063)" 一组测试。pnpm --dir ui exec vitest run packages/app-shell/src/views/studio-design/InterfacesPillar.customize-28562.test.tsx -t 30063,预期 5 条通过、其余跳过。What changes
Studio's Customize door (
STUDIO_CUSTOMIZE_TYPESinStudioCustomizeControls.tsx) now listspagebeside view, dashboard, report and app.pageisoverridablein the spec's classification (isOverridableMetadataType), soisStudioCustomizeType('page')now answers true. That one line is the whole runtime change. Everything else a page needs was already type-generic in the Interfaces pillar, and is now reached for pages:leafCustomizable). Until it is pressed, the designer edits nothing. The first saved designer edit asks the ensure door once and writes the whole page into the pair (overrideDraftBody: the page without its_keys, plusoverrides: { package: BASE, basedOn: BASE_MANIFEST_VERSION }), pinned to "no draft there" (ifNoneMatch: '*'). Later edits save into the pair's copy at the version they were answered. The base is never written./apps/APP/page/NAME.No spec, server or metadata-type change (card Acceptance 5): the browser check below ran end to end against an unchanged server.
Out of scope, as the card says: a renamed page's label can be hidden by the base's translation. That is filed as #30062 (its third finding) and is not fixed here.
Diff: 244 changed lines (+233 / −11, 5 files), under the 1,500 split line, so Acceptance 1–4 ship together.
Tests
InterfacesPillar.customize-28562.test.tsx(the shared harness), new top-level describe "Customize a read-only base’s page into its pair (objectstack#30063)", 5 tests. The page designer is reduced to a stand-in canvas (its first text block and one in-place edit), the way the harness already stands in for the dashboard canvas:overrides: { package, basedOn: '4.0.0' }andifNoneMatch: '*'; the base row is unchanged; the override chip appears; the next edit saves into the pair atifMatch: 'v1'without asking the door again.DELETE …state=draft&package=PAIR, thenDELETE …package=PAIR), and the shipped page reads back with Customize offered again.data-kind="customized"), "only customizations" reads 1 and lists the page alone.overrides.Also:
StudioCustomizeControls.customize-28562.test.tsx:pagemoves from the "is not customized" list to the "is customized" list.metadataAreaHost.customizations-28568.test.tsx: the All metadata page tile counts a page's override ("1 customized"), and the header's total includes it.Ablation (one-time, not kept)
Committed first, then
node scripts/ablation-replace.mjsreplaced'report', 'page', 'app'with'report', /* ablated-30063 */ 'app'inStudioCustomizeControls.tsx(anchor 1 → 0, blob1452690e→f2d008e3) and ranpnpm --dir ui exec vitest run packages/app-shell/src/views/studio-design/InterfacesPillar.customize-28562.test.tsx -t 30063(ui vitest resolves@object-ui/*to src, so no build leg):1452690e= HEAD,git diff HEADempty (the script's own check).Acceptance notes
examples/app-crmfrom this branch (objectstack dev --seed-admin --fresh -p 4317, Console Vite on 5317 against it), admin, zh-CN, headeee39383(before theorigin/mainmerge; the merge brought no change to this PR's diff, and the suites below were rerun after it). Screenshots are in the project'sui-orders/30063-shots/folder; file names below.欢迎页(crm_welcome) in Studio → Interfaces reads 只读 and now offers 定制 (01-studio-page-readonly-offers-customize.png).POST /packages/com.example.crm/ensure-customization→ 201, thenPUT /meta/page/crm_welcome?mode=draft&package=com.example.crm.custom→ 200; the header shows the override chip and "保存到 CRM (minimal example) 的定制" (02-first-edit-saved-into-custom-pair.png).03-publish-lists-the-page-in-the-pair.png); 全部发布 sentPOST /packages/com.example.crm.custom/publish-drafts→ 200./apps/com.example.crm/page/crm_welcomeshows the edited text (04-runtime-serves-the-customized-page.png).GET /packages/com.example.crm/customizationsthen listed{type: page, name: crm_welcome, basedOn: 4.0.0}.05-rail-marks-the-page-and-only-customizations-counts-it.png); the All metadata page tile reads 已定制 1 and the header "CRM (minimal example) 已定制 1 项" (06-all-metadata-page-tile-counts-the-customization.png).07-restore-package-original-confirm.png), then sentDELETE …?state=draft&package=com.example.crm.customandDELETE …?package=com.example.crm.custom(both 200). Studio shows the shipped text with 定制 offered again (08-restored-shipped-page-customize-offered-again.png), the runtime serves the shipped page (09-runtime-serves-the-shipped-page-again.png), the customization listing is empty, and the base's page body read before and after is identical.08-…png; a reload shows 0 marks and no filter). The marks read (usePackageCustomizations) re-reads only onpublishNonce, and Restore does not bump it. That code path is shared with dashboards, views and the app, so it is not page-specific.06-…png); the app tile reads the same since Studio: customize an installed app's navigation through the existing app override, so a new object can get a menu entry #30059. The word comes fromtypeStateWord, which reads the type'sallowEnvironmentOverlay, not the customize door. Carrier: Declared overrides: a mapping override publishes but is never served, doc and mapping overrides cannot be restored, and an override's label can be hidden by translation #30062 item 4 (branchclaude/project-thread-yenpdr) makestypeStateWordinPageShell.tsxread the customize door's own type table (isStudioCustomizeType). Serial: whichever of the two PRs lands second updates the Studio: one vocabulary for an item's state across the pillars, All metadata and Access #29705 test's tile expectation forpagefrom 只读 to 可定制; this PR does not touchPageShell.tsx.STUDIO_CUSTOMIZE_TYPESdoc comment inStudioCustomizeControls.tsx: the two hunks overlap, so whichever lands second mergesorigin/mainand keeps both sentences (the page in the types list, the dataset in the area editor). fix(metadata-protocol,app-shell): serve a pair's mapping override, let a pair remove any override it holds, name a working remedy for an excluded type (#30062) #30226 (Declared overrides: a mapping override publishes but is never served, doc and mapping overrides cannot be restored, and an override's label can be hidden by translation #30062) editsPageShell.tsx(see the tile note above); this PR does not touch it. feat(app-shell): Studio adds an action to an installed app's object, saved in its customization package (#30088) #30211 editsStudioDesignSurface.tsx, which this PR does not touch.Verification
All under
scripts/pm/os-verify-lock.shunless noted. Heada73ecd64(origin/maindfafae81merged, clean) for the rows marked "after the merge"; the first rows ran oneee39383.turbo run build --filter='@object-ui/app-shell...', 68/68 tasks; then the full build (--filter=!@objectstack/docs), 119/119, for the dev boot.packages/app-shell/src/views/studio-design/directory: 324 files, 2397 tests, all passed. After the merge (full build 119/119 first), that directory plusmetadataAreaHost.customizations-28568.test.tsx: 325 files, 2415 tests, all passed.@object-ui/app-shelltype-check(both tsconfigs) → exit 0, before and after the merge.npx eslinton the 4 touched source/test files (unlocked): 0 errors, 15 warnings (allreact-refresh/only-export-componentson existing exports ofStudioCustomizeControls.tsx).node scripts/check-sdui-manifest.mjs→ exit 0.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack: 35 commands (the same 35 before and after the merge), each run ona73ecd64and recorded:: exit 0;--ran→ "35 derived, 35 run, 0 NOT-MEASURED, 0 UNRUN". Record: project fileui-orders/30063-gates-ran.list.🤖 Generated with Claude Code
Generated by Claude Code