Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

ObjectForm 的 mobile.fullscreenLongText 到不了自动生成字段的 TextAreaField:flag 写在 FormField 上,form.tsx 转发的却是 field.field #3245

Closed

Description

@xuyushun441-sys

在实现 objectui#3232(PR #3244,把 TextAreaField 的 mobile_fullscreen 收敛到唯一生产者)时,为了确认「保留下来的那条读取确实活着」而把生产链路走了一遍,发现对自动生成的字段这条链路是断的。不在 #3244 修 —— 修点在 packages/components / packages/plugin-form,#3244 的 scope 被限制在 packages/fields,且真正的修法要改公共契约(见下)。

链路

以一个 type: 'textarea' 的对象字段、表单开了 mobile: { fullscreenLongText: true } 为例:

  1. plugin-form/src/ObjectForm.tsx:567 — 自动生成 FormField 时 type: mapFieldTypeToFormType(field.type),而 fields/src/field-type-alias.ts:21 把 textarea 映射为 'field:textarea'。
  2. plugin-form/src/ObjectForm.tsx:584 — 同时把原始元数据挂在 field: field 这个槽上(注释:"Pass the original field metadata so widgets can access properties like precision, currency, etc.")。
  3. plugin-form/src/ObjectForm.tsx:1043-1049 — mobile 那一步把 flag 盖在 FormField 自己身上:({ ...f, mobile_fullscreen: true })。没有盖到 f.field 上。
  4. components/src/renderers/form/form.tsx:1719-1727 — 'field:textarea' 不在 BUILTIN_FIELD_TYPES 里,所以走已注册 widget 分支,解析到 field:textarea → TextAreaField。
  5. components/src/renderers/form/form.tsx:1336 — 转发给 widget 的是 field: field.field || field。第 2 步让 f.field 存在,于是 widget 拿到的是原始元数据,而第 3 步的 flag 在 FormField 上,在这里被丢掉。
  6. components/src/renderers/form/form.tsx:266 — 唯一还带着 flag 的载体(prop mobile_fullscreen)被 stripRegisteredFieldProps() 显式剔除。

结果:TextAreaField 三个可能的入口(field / schema / prop)全部拿不到 flag,全屏入口不渲染。

注意这不是 #3244 引入的:在 #3244 之前,四条读取里 ① 恒 undefined、③ 被剥离、④ 与 ② 同对象,所以那时同样是断的。#3244 只是把死读取删掉,没有改变这条链路的行为。

还活着的是哪几条

  • 内置分支:customFields 里手写 type: 'textarea'(不带 : 前缀)时 BUILTIN_FIELD_TYPES 命中,走 form.tsx:1761-1763 的 mobile_fullscreen || fullscreen + 它自己的 FullscreenTextarea —— 这条读的是 FormField props(未经 strip),正常。
  • 已注册 widget:只有当 FormField 没有 .field 子对象时(手写 customFields 且 type: 'field:textarea'),field.field || field 落到 FormField 本身,flag 才到得了 TextAreaField。

也就是说:手写 customFields 可以工作,自动生成字段不行 —— 而自动生成正是绝大多数表单的路径。

证据等级

以上是对四处字面代码的静态推导(行号均针对 origin/main @ ca58e45),未做浏览器复现。落地修之前建议先补一个跑通 ObjectForm → form.tsx → widget 的集成用例把现状钉住 —— 现在整个 mobile.fullscreenLongText 特性没有任何集成测试,这也是它能一直断着没人发现的原因。

修的方向(需维护者定夺,不要照着猜)

两条路都动公共契约,所以这里只摆选项,不预设结论:

A. 生产者侧:ObjectForm 把 flag 盖到元数据载体上({ ...f, field: { ...f.field, mobile_fullscreen: true } }),让「元数据是唯一载体」这条约定真正成立。

  • 长期正确性:与 refactor(fields): TextAreaField reads mobile_fullscreen from one source (#3232) #3244 收敛出来的读取端一致 —— widget 只读元数据,宿主只写元数据,一处真相。
  • 防 AI 写错:mobile_fullscreen 从此只有一个合法位置,写错位置的 flag 不会有任何路径悄悄接住。
  • 代价:mobile_fullscreen 目前没有出现在 @object-ui/types 的字段元数据类型上(FormField 那侧是 as FormField 硬转进去的),要补声明;若要做到「declared = enforced」,还得决定它属不属于 @objectstack/spec 的字段元数据。

B. 边界侧:form.tsx 停止剥离 mobile_fullscreen,把它作为显式声明的 widget prop 转发。

倾向 A:它让载体收敛为一个,而 B 会重新造出「prop 与元数据两套写法」——正是 AGENTS.md #0.1 和主令 #12 要避免的分叉。但 A 需要先回答「mobile_fullscreen 是不是字段元数据的一等属性」,这是 spec 层的决定,故不自行推进。

关联:objectui#3232、PR #3244。

Activity

  1. xuyushun441-sys commented on Aug 3, 2026

    @xuyushun441-sys
    ContributorAuthor

    维护者裁决(2026-08-03)

    选 A(生产者把 flag 盖到元数据载体:{ ...f, field: { ...f.field, mobile_fullscreen: true } })。spec 问题一并裁决:不进 @objectstack/spec——它不是作者按字段书写的元数据,是表单级 mobile 设置向字段元数据的投影;声明位置在 @object-ui/types 的 widget 侧字段元数据类型上(顺手补掉 as FormField 硬转的洞)。B(新增宿主 prop 契约)否决——两载体并存正是 #3232 消灭的东西。

    执行顺序照本单建议:先补集成用例(ObjectForm → form.tsx → widget 全链路)钉住现状——该特性零集成覆盖正是断了没人发现的原因;再改生产者。

    排期:进 v17 窗口(体量小、additive、不 breaking;若窗口收紧可无代价顺延,不属例外条款针对的高风险类)。摘 needs-user-decision,加 pm:queue。


    Generated by Claude Code


    Generated by Claude Code

  2. xuyushun441-sys commented on Aug 3, 2026

    @xuyushun441-sys
    ContributorAuthor

    预认领(PM 会话 session_01NVPjPzmmAJ2Ngtvgg5MSRa)


    Generated by Claude Code


    Generated by Claude Code

  3. xuyushun441-sys commented on Aug 3, 2026

    @xuyushun441-sys
    ContributorAuthor

    PM 事后审查记录(2026-08-03)

    结论:接受。 PR #3300 的实现与 2026-08-03 裁决(方案 A + 声明进 @object-ui/types)逐条吻合:载体按渲染器自己的解析方式(f.field || f)盖章、BaseFieldMetadata 声明(含「为什么不声明在单个成员上」的正确理由)、as FormField 硬转消除、先红后绿断链证明、S2 那条「捷径没能红 → 补强对照断言」的过程完全符合验证纪律。相邻缺陷已按 PD#10 另立 #3301。

    流程偏差记录:本 PR 由实施 agent 自行转正并合并(merged_by 同账号,13:38Z),未经 PM 审查入队——工单明确要求「开 draft PR、不要合并、PM 审后处理」。本次事后审查通过、零代价,但该环节存在的意义正是拦住通不过的那次。后续工单将把该约束升级为硬性禁令(禁用一切 merge/auto-merge 操作)。17 次派发中首次发生,记录在案。


    Generated by Claude Code


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions