Skip to content

refactor(fields)!: 文档字段例子改表单托管,删 registerFields + createFieldRenderer (#3910) - #3953

Merged
yinlianghui merged 1 commit into
mainfrom
claude/issue-3910-fields-catalog-form-hosted
Aug 9, 2026
Merged

refactor(fields)!: 文档字段例子改表单托管,删 registerFields + createFieldRenderer (#3910)#3953
yinlianghui merged 1 commit into
mainfrom
claude/issue-3910-fields-catalog-form-hosted

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes #3910

落地 #3798 裁决 B(维护者已确认),即 #3308「注册只剩一条路」意图的 remove 侧。PR #3793 先把误导性的 @deprecated 注释改为如实说明,是本单的安全垫。

基线 sha:b14ab3afe(派发单写的 cb5e32d73 在开工时已被 main 推进,按当下显式 sha 建 worktree)。

病根

文档的字段 demo 把裸字段节点渲染成带 label 的可编辑输入框,而真实应用做不到这件事:活路径上裸字段节点既没有 label 的宿主,也没有 value 的宿主。它之所以能渲染,是因为文档站另外注册了一个 demo 专用适配器,覆写了活路径拥有的同一批 field:类型 键。字段文档是 AI 作者的一手抄写来源,于是它一直在教一种不成立的写法。

实测(本单 probe,未入 commit):

A 裸节点走活路径          -> 整个子树渲染为空(无 label、0 个 input)
B 表单托管 + 字段级 value -> input.value === ""      (字段级 value 被忽略)
C 表单托管 + defaultValues-> input.value === "FORM_LEVEL"

四个部分

1. catalog 表单托管(74 个 JSON)examples/schema-catalog/src/schemas/fields-* 下 74 个裸节点例子改为 { "type": "form", "showSubmit": false, "showCancel": false, "fields": [...] }(另有 2 个 fields-select 例子本来就是表单托管,未动,它们也是仓内既有先例)。

关键:这不是无脑套壳。表单渲染器在 schema props 之后展开 react-hook-form 的状态,所以字段级 value 会被静默覆盖(上面 B/C 两条实测)。因此 24 个带 value 的例子把值搬到表单的 defaultValues,否则「只读展示值」「预勾选」「默认日期」这些例子的全部看点会在看起来正常的情况下丢失。

改完做了机械等价性校验:74 个文件逐个与 HEAD 版本对比,value 归位后逐键完全一致,没有多加也没有漏掉任何一个键。脚本放 scratchpad(文件名带 3910),改后逐结构族人工复核 diff(标量 / 布尔 / 对象值 / grid 行数组 / vector 数组 / options / 带 schema 键的 object)。

2. 删两个 demo 包装器 — 从 @object-ui/fields 删除 registerFields()createFieldRenderer(),连带删掉 37 个「只为把组件引用喂给包装器」而存在的静态 widget import(活路径走 fieldWidgetMap 的 lazy loader,不需要静态引用;widget 仍由文件末尾的 export * from './widgets/…' 公开导出)。删除点留 tombstone,并把原本写在 registerFields() 函数体内的 capability-multiselect tombstone(#3308)一并搬出保留。

dist/index.d.ts 已确认零残留,registerField / registerAllFields 仍在。

3. 消费者清零证明(带阳性反查) — 正查在同一路径集合上排除注释与散文后命中 0;阳性反查用邻近词证明扫描器与路径都是活的:

$ git grep -n -E "\b(registerFields|createFieldRenderer)\b" -- . ':!*.md' ':!*test*' | grep -vE '^[^:]+:[0-9]+:\s*(//|\*|/\*)'
--> 0

$ git grep -n "registerAllFields" -- . ':!*.md' ':!*test*'   -> 3 个文件命中
$ git grep -n "withFieldCarrier" -- . ':!*.md' ':!*test*'    -> 5 个文件命中
$ git grep -n "@object-ui/fields" -- . ':!*.md' ':!*test*'   -> 116 个文件命中

跨仓同样两组(一律 git -C,不用 cd):git -C /home/user/objectstack grep 对两个被删导出命中 0,而 @object-ui/fields 在该仓命中 1 个文件 —— 证明那次扫描也是活的。

4. 文档站与逐页重验ObjectUIProviderregisterFields() 调用换成副作用 import(模块加载即跑 registerAllFields(),唯一的注册缝)。InteractiveDemo 无需改动:它本来就只渲染 SchemaRenderer,节点换成 form 之后由真表单渲染器接管 label 与状态。26 个 content/docs/fields/*.mdx 页也无需改动 —— 每页只用 SchemaExample id=…,全站共 76 处引用,与 76 个 catalog 例子一一对应。

顺带修掉三处注释性误归因:LookupField / UserField / CapabilityMultiSelectField 里把 field.field 嵌套说成是被删包装器的产物,实际产出者是表单渲染器的 metadata 槽(#3090);complex-widgets.test.tsx 的用例名同病,一并改指真实产出者(覆盖保留,不删)。

浏览器实证(26 页逐页)

本地起站点(自选空闲端口 5321,收工按记下的 PID 收掉),Playwright + 预装 Chromium 逐页截查:

判据 结果
HTTP 200 26 / 26
渲染出真 form 元素 26 / 26(全站共 76 个 form,等于 76 个例子)
字段项数 = 例子字段数 78 / 78(74 单字段 + 2 个双字段 select)
每个字段项渲染出 label 78 / 78
残留裸字段节点(form 外的 data-obj-type 0
新增控制台报错 0

按 widget 类别抽样(每类至少一页)结论:

  • 文本类 text / textarea / email / url / phone / password — label + input/textarea,正常
  • 数值类 number / currency / percent — label + 带前缀的数值控件(currency 页实测 $EUR 前缀),正常
  • 日期时间 date / datetime / time — label + 原生日期时间控件;time18:00 / 09:00 种子值正确显示
  • 选择类 select / lookup / user — label + 下拉或记录选择器按钮,正常
  • 布尔 boolean — label + switch,pre-checkedtrue 生效
  • 只读展示类 auto_number / formula / summary / vector — label + 值展示(无 input 是只读字段的正确形态);ORD-0001 / TKT-202403-0567 / INV-1234 / 42 / 15750.50 / 4.52 均正确显示
  • 富文本 rich-textmarkdown + html)、objectfile / imagelocation — label + 对应控件;object 的只读例子以 pre 展示种子 JSON,location 的经纬度正确显示
  • grid — 见下方「已知遗留」

已知遗留(不在本单范围,已另立单)

  • [fields] grid columns have two incompatible key spellings: declared type says name, GridField reads field — spec-compliant grid metadata renders empty cells #3951 grid 三个 demo 的表格行数正确但单元格全空,并伴随一条 React key 警告。根因是 grid 列键存在两种拼写:声明类型 GridColumnDefinition(也是 GridFieldMetadata.columns 的声明类型)与文档散文都写 name,而 GridField 自己的 GridColumn 接口和全部读点都用 field。用同样的行数据直接渲染 GridField、只改列键拼写即可复现(name 拼写:全空 + 1 条 key 警告;field 拼写:值正常、0 警告),与表单托管无关,改动前就是空的。本单故意没有把例子改成 field —— 那会把声明类型里不存在的键固化进文档,正是裁决 B 要消灭的失效类;该选哪一侧收敛是契约决策,留给维护者。
  • [fields] a boolean field's visible form label is associated with nothing — BooleanField overrides the control id with the field name, so the form's label for dangles #3952 表单里的 boolean 字段,可见 label 的 for 指向一个不存在的 id(BooleanField 用字段 name 覆写了 FormControl 给的控件 id),所以点 label 无法切换开关。同样是改动前既有、与本单无关的活路径缺陷。
  • selectrole-gated-optionscurrent_user 谓词告警:该例子改动前就是表单托管形态、本单未动,告警属既有现象(demo 上下文没有 current_user)。
  • 首轮遍历时 auto-number 页出现一条 404「Failed to load resource」,随后 4 次定向复现均为 0 —— dev server 首次编译的瞬态产物,不可复现,未归因到本改动。

changeset 与 Bump

.changeset/fields-catalog-form-hosted-3910.md@object-ui/fields: minor。依据仓内 Bump Policy 门:scripts/check-changeset-no-major.mjs 机械禁止 major(fixed 组 40 包会被一起推上去、脱离 @objectstack 节奏),AGENTS.md §版本号策略明写「objectui 自身的破坏性变更也标 minor,破坏语义写在正文」。破坏语义与迁移写法已在 changeset 正文写明。@object-ui/site@object-ui/example-*ignore 列表,无需条目。

两个门实跑:

node scripts/check-changeset-no-major.mjs   -> ✅ No changeset declares a `major` bump.
node scripts/check-changeset-presence.mjs   -> ✅ 6 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)

content/docs/releases/** 未触。

验证

pnpm exec vitest run packages/fields/ examples/schema-catalog/ packages/plugin-form/ \
                     packages/plugin-detail/ packages/components/src/renderers/form/
  -> Test Files  199 passed (199)
     Tests      3078 passed (3078)

pnpm exec turbo run type-check --concurrency=2
  -> Tasks: 78 successful, 78 total

pnpm --filter '@object-ui/site' types:check   -> ✓ Types generated successfully(site 的脚本名是 types:check,不在上面那轮里,单独跑)

lint(fields / catalog / site)                -> 0 errors(warnings 是仓库既有基线)
node scripts/check-control-bytes.mjs           -> ✅ OK (3847 tracked text files)
改动文件另做 grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f]' 自扫 -> 0

新增 pin:examples/schema-catalog/test/fields-form-hosted.test.tsx —— 每个 fields-* 例子(1)根节点是 form 且 fields 非空、(2)字段条目不带死的 valueObject.hasOwn,不是真值判断,否则 false/0/'' 会被放过)、(3)经真 SchemaRenderer + 真表单渲染器渲染出 form 且 label 落在该字段自己的 data-field 容器里;另有 3 个带值例子直接断言种子值进 DOM。capability-multiselect-retired.test.ts 按三种处置逐条重判(详见文件头注释):只钉活路径那条保留(第二条路没了之后它才真正钉住东西)、「跑完 registerFields 之后」那条随路径删除(测试不能调用已不存在的函数,对着删掉的路径断言会因「什么都没跑」而空绿)、38 键那条改指活路径;另加一条钉住两个导出确已消失、且活路径的两个导出仍在。

反向自证(方向预先判定为「双向变红」,实测吻合):把两处肢体分别加回 —— (A) 还原一个裸节点 JSON、(B) 重新导出 registerFields —— 恰好 4 条断言变红且只有这 4 条(A 打中该例子的 shape / dead-value / render 三条,B 打中新增的导出 pin),撤销变异后 236 条全绿。变异未入 commit。


Generated by Claude Code

…gisterFields + createFieldRenderer (#3910)

Ruling B of #3798 (maintainer-confirmed), the remove side of #3308's "one
registration path" intent. PR #3793 corrected the misleading `@deprecated`
note first and is the safety net this builds on.

The docs field demos rendered a BARE field node as a labelled, editable
input, which no application can do: on the live path a bare field node has
no host for its label or its value. That only worked because the docs site
registered a demo-only adapter over the same `field:<type>` keys the live
path owns. The field docs are a first-hand transcription source for AI
authors, so they were teaching a shape that does not work.

- catalog: 74 bare-node examples under `examples/schema-catalog/src/schemas/
  fields-*` become `{ type: 'form', showSubmit: false, showCancel: false,
  fields: [...] }` (2 were already form-hosted). A node's `value` moves to
  the form's `defaultValues`: the form spreads react-hook-form's state after
  the schema props, so a field-level `value` is silently ignored (measured —
  field-level yields an empty input, form-level yields the value). All 74
  round-trip to their original content with `value` relocated and nothing
  else added or dropped.
- fields: delete `registerFields()` and `createFieldRenderer()`, plus the 37
  static widget imports that existed only to feed the wrapper. Tombstones
  record why, and carry forward the `capability-multiselect` tombstone that
  lived inside the removed function.
- site: `ObjectUIProvider` drops the `registerFields()` call for a
  side-effect import; the module's `registerAllFields()` is the one seam.
- three widget comments credited the `field.field` nesting to the deleted
  wrapper, which never produced it — re-pointed at the form renderer's
  declared metadata slot (#3090), the real producer.

The 26 `content/docs/fields/*.mdx` pages need no edits: each consumes only
`<SchemaExample id>` (76 references, one per catalog example).

New catalog pins: every `fields-*` example is form-rooted, carries no dead
field-level `value`, and renders a real `form` with its label in the field's
own chrome; three value-bearing examples assert the seeded value reaches the
DOM. Reverse-verified — restoring either limb turns exactly these red.
@vercel

vercel Bot commented Aug 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectui Ignored Ignored Aug 9, 2026 3:31pm

Request Review

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Main entry (gzip) 28.1 KB 350 KB
Entry file index-BY-dB2X8.js
Status PASS

📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 8.66KB 3.13KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 7.57KB 2.97KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 22.10KB 4.37KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.13KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.64KB 2.21KB
auth (SocialSignInButtons.js) 9.60KB 3.89KB
auth (UserMenu.js) 3.40KB 1.22KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 35.76KB 9.11KB
auth (createAuthenticatedFetch.js) 4.37KB 1.69KB
auth (index.js) 2.35KB 1.07KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 4.91KB 0.87KB
auth (useIsWorkspaceAdmin.js) 1.61KB 0.85KB
collaboration (CommentThread.js) 26.07KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.65KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 482.64KB 106.39KB
core (index.js) 3.00KB 1.14KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 139.61KB 35.99KB
fields (index.js) 227.41KB 56.29KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.32KB 1.77KB
i18n (index.js) 2.65KB 1.06KB
i18n (pickLocalized.js) 1.70KB 0.83KB
i18n (provider.js) 9.48KB 3.27KB
i18n (useObjectLabel.js) 27.59KB 6.63KB
i18n (useSafeTranslation.js) 4.52KB 1.96KB
layout (index.js) 38.53KB 10.71KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.74KB
mobile (index.js) 1.50KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.71KB 0.42KB
mobile (useResponsiveConfig.js) 1.36KB 0.63KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 8.75KB 3.06KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 3.67KB 1.12KB
permissions (evaluator.js) 4.41KB 1.44KB
permissions (index.js) 0.91KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.52KB
permissions (usePermissions.js) 1.55KB 0.71KB
plugin-ai (index.js) 15.71KB 3.79KB
plugin-calendar (index.js) 44.98KB 12.37KB
plugin-charts (index.js) 61.04KB 17.31KB
plugin-chatbot (index.js) 180.33KB 42.79KB
plugin-dashboard (index.js) 118.02KB 30.47KB
plugin-designer (index.js) 210.51KB 42.51KB
plugin-detail (index.js) 236.63KB 59.02KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 112.10KB 27.10KB
plugin-gantt (index.js) 162.55KB 39.57KB
plugin-grid (index.js) 187.63KB 49.66KB
plugin-kanban (index.js) 48.30KB 13.28KB
plugin-list (index.js) 109.73KB 26.55KB
plugin-map (index.js) 16.81KB 5.24KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 40.58KB 10.58KB
plugin-timeline (index.js) 25.76KB 7.33KB
plugin-tree (index.js) 8.50KB 2.88KB
plugin-view (index.js) 84.03KB 20.55KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.71KB 3.53KB
providers (index.js) 0.44KB 0.22KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.67KB 2.37KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 20.15KB 6.72KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.02KB 0.55KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 4.09KB 1.74KB
sdui-parser (index.js) 4.47KB 2.03KB
sdui-parser (parse.js) 10.04KB 2.82KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 4.69KB 1.48KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.87KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-retry.js) 4.32KB 2.02KB
types (index.js) 2.71KB 1.34KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 2.59KB 1.31KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (spec-report.js) 5.05KB 1.93KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 0.20KB 0.18KB
types (ui-action.js) 3.40KB 1.71KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

Copy link
Copy Markdown
Collaborator Author

PM 验收(session_01GTRjn8xBqp75dk7kFupVRt):通过,转 ready 并挂 auto-merge。#3798 裁决 B 落地,#3910 独立批。

核验记录(head 25ca132c9,基 b14ab3afe,实物核验 + CI 亲读):

  1. 删除侧三重证明:分支上 registerFields/createFieldRenderer 声明式正查 0 命中;阳性反查 registerAllFields 6 命中(扫描器活);origin/main 对照 1 命中(删除真实发生)。dev 的消费者清零(含 git -C 跨仓查 objectstack,阳性反查齐)采信。
  2. 表单托管形态:抽查 catalog JSON 均为 {type:'form',showSubmit:false,showCancel:false,fields:[...]};实际 76 例(非 issue 预估 ~60),74 转换、2 例本已托管。关键实现判断正确:字段级 value 被表单渲染器忽略,24 例迁移到 defaultValues —— 不迁移则只读/预选类例子静默丢演示内容,机械 round-trip 核对过。
  3. 前提修正采信:InteractiveDemo 与 26 个 MDX 页无需改动(消费面只有 SchemaExample id),真正调用点在 ObjectUIProvider(改为副作用 import);issue 预期的两处调整以实际情况为准。
  4. 浏览器实证:26/26 页 HTTP 200,76 表单、78 label 齐,0 裸字段节点、0 新增控制台报错;grid 三例空单元格未遮盖,根因(列键 name/field 双拼写)证明 pre-existing 且与托管无关,已立 [fields] grid columns have two incompatible key spellings: declared type says name, GridField reads field — spec-compliant grid metadata renders empty cells #3951 —— 拒绝把文档改成契约没有的键,处置正确。
  5. 测试与门:3078 测试全绿 + 双向反向验证(预判红向命中,4 断言精确翻红后还原);type-check 78/78 + site types:check 单独跑过;changeset minor 判级依据(AGENTS.md §版本号策略 + no-major 门)核验成立,迁移说明完整;⛔ releases/ 零触碰;控制字节门过;提交文案控制词扫描 0。
  6. CI 亲读终态:20/20 全 completed、0 失败(Test shard×4 至 15:37:42Z、Type Check 15:35:04Z、Lint 15:35:16Z;coverage/dependabot path-filter skipped 计绿)。
  7. 收尾:capability-multiselect widget 在实际注册路径上根本不存在 —— 代码注释却说它可从记录表单用 #3308 的旧 changeset 不改(如实记录 PR3793 历史)、capability-multiselect 墓碑随删除迁出保留 —— 两处判断均正确。

out-of-scope findings #3951/#3952 由 PM 分诊(另评)。


Generated by Claude Code

@yinlianghui
yinlianghui marked this pull request as ready for review August 9, 2026 16:19
@yinlianghui
yinlianghui added this pull request to the merge queue Aug 9, 2026
Merged via the queue into main with commit 65bb513 Aug 9, 2026
21 checks passed
@yinlianghui
yinlianghui deleted the claude/issue-3910-fields-catalog-form-hosted branch August 9, 2026 16:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docs/fields: 落地 #3798 裁决 B —— catalog 字段例子改表单托管,删 registerFields + createFieldRenderer 两个 demo 包装器

2 participants