在实施 #3301 (给 RichTextField 接上全屏能力,PR #3302 )时顺带发现,不在 #3302 修 —— 那单的范围栅栏明确排除 packages/components。
现象
packages/components/src/renderers/form/form.tsx:1945-1947,未注册的裸 textarea 类型走的内置分支:
const { mobile_fullscreen, fullscreen, label } = fieldProps as any ;
if ( mobile_fullscreen || fullscreen ) {
// renders FullscreenTextarea
fullscreen 是一个没有生产者 的别名。全仓 grep fullscreen:(排除 node_modules/dist,排除 mobile_fullscreen / fullscreenLongText)只有 3 类命中,没有一处是表单字段:
命中
是什么
packages/types/src/zod/feedback.zod.ts:30
fullscreen: z.boolean() —— loading/feedback 遮罩 的属性,与长文本编辑无关
packages/components/src/renderers/feedback/loading.tsx:75
同上,loading 组件的默认值
form.tsx:226 / form.tsx:283
stripRegisteredFieldProps 里把这个键丢弃 的两处(fullscreen: _fullscreen)
@objectstack/spec 侧同样为零(objectstack 仓 packages/spec/src grep fullscreen 无命中)。
唯一真实的生产者是 ObjectForm,它产出的键叫 mobile_fullscreen(#3245 / #3300 已把它规范到字段元数据这一个载体上)。所以 || fullscreen 这一项从写下起就恒为 undefined 。
为什么这是缺陷
这正是 AGENTS.md #0.1 明令禁止的形态:消费侧的宽容兜底 。一个键有两种拼法、其中一种没有任何生产者,后果不是「多一层保险」,而是:
它把一个从不成立的契约写进了代码,下一个作者(尤其是 AI 生成的表单元数据)看到 fullscreen 会以为这是受支持的拼法,写下去、静默无效、没有任何一处报错——和 mobile.fullscreenLongText 对 rich-text 字段是空承诺:ObjectForm 给 field:markdown/field:html 盖了 flag,RichTextField 根本不读;string-multiline 分支全仓无人产出 #3301 、ObjectForm 的 mobile.fullscreenLongText 到不了自动生成字段的 TextAreaField:flag 写在 FormField 上,form.tsx 转发的却是 field.field #3245 完全同一个机制。
它让「mobile_fullscreen 是唯一载体」这个刚刚在 TextAreaField 读取的 mobileFullscreen prop 全仓无人生产,注释却说"宿主表单传入" #3232 / field 与 schema 是同一个意思的两个载体,~25 个 widget 读 field || schema #3233 / ObjectForm 的 mobile.fullscreenLongText 到不了自动生成字段的 TextAreaField:flag 写在 FormField 上,form.tsx 转发的却是 field.field #3245 / fix(plugin-form,types): carry mobile_fullscreen on the field metadata, where widgets actually read (#3245) #3300 一路收敛出来的结论,在这一行上不成立。TextAreaField 与(feat(fields): RichTextField honors mobile_fullscreen with a fullscreen editing dialog (#3301) #3302 之后的)RichTextField 都已经收敛成单读 ;只有这个内置分支还留着 ||。
顺带记录一个更大的重复,供裁决时一并考虑:FullscreenTextarea(form.tsx:308-)是全屏编辑对话框的第三份实现 ——#3302 已把 widget 侧的两份合并成共享的 FullscreenFieldEditor(packages/fields/src/widgets/FullscreenFieldEditor.tsx),但内置分支这一份仍是独立的。两份实现是否要合并,取决于 packages/components 能否依赖 packages/fields(按 AGENTS.md §3 的拓扑,方向是反的,所以这不是一次简单搬运),需要单独裁决,不要顺手做。
修法建议(需裁决)
A. 直接删掉 || fullscreen ,只读 mobile_fullscreen。零行为回归(这一项今天本来就恒假),declared = enforced 立刻成立。这是与 #3232 / #3233 同向的收敛,我倾向这条。
B. 若认为 fullscreen 才是想要的拼法 ,那就该在生产者 (ObjectForm)改名并一次性收敛,而不是让消费者同时接受两种——但 mobile_fullscreen 已经声明在 @object-ui/types 的 BaseFieldMetadata 上(#3300 ),改名成本明显更高且没有收益。
无论走哪条,都值得补一条钉死「拼错的 flag 不生效」的测试——目前这个内置分支的全屏行为没有任何测试覆盖到别名这一半。
证据等级
静态证据(全仓 + objectstack 仓 grep,阅读 form.tsx 三处相关源码),未做浏览器复现 。「fullscreen 无生产者」是可机械复核的事实。
关联:#3301 (PR #3302 )、#3245 (PR #3300 )、#3232 、#3233 。
在实施 #3301(给
RichTextField接上全屏能力,PR #3302)时顺带发现,不在 #3302 修 —— 那单的范围栅栏明确排除packages/components。现象
packages/components/src/renderers/form/form.tsx:1945-1947,未注册的裸textarea类型走的内置分支:fullscreen是一个没有生产者的别名。全仓 grepfullscreen:(排除node_modules/dist,排除mobile_fullscreen/fullscreenLongText)只有 3 类命中,没有一处是表单字段:packages/types/src/zod/feedback.zod.ts:30fullscreen: z.boolean()—— loading/feedback 遮罩的属性,与长文本编辑无关packages/components/src/renderers/feedback/loading.tsx:75form.tsx:226/form.tsx:283stripRegisteredFieldProps里把这个键丢弃的两处(fullscreen: _fullscreen)@objectstack/spec侧同样为零(objectstack仓packages/spec/srcgrepfullscreen无命中)。唯一真实的生产者是
ObjectForm,它产出的键叫mobile_fullscreen(#3245 / #3300 已把它规范到字段元数据这一个载体上)。所以|| fullscreen这一项从写下起就恒为 undefined。为什么这是缺陷
这正是 AGENTS.md #0.1 明令禁止的形态:消费侧的宽容兜底。一个键有两种拼法、其中一种没有任何生产者,后果不是「多一层保险」,而是:
fullscreen会以为这是受支持的拼法,写下去、静默无效、没有任何一处报错——和mobile.fullscreenLongText对 rich-text 字段是空承诺:ObjectForm 给field:markdown/field:html盖了 flag,RichTextField 根本不读;string-multiline分支全仓无人产出 #3301、ObjectForm 的 mobile.fullscreenLongText 到不了自动生成字段的 TextAreaField:flag 写在 FormField 上,form.tsx 转发的却是 field.field #3245 完全同一个机制。mobile_fullscreen是唯一载体」这个刚刚在TextAreaField读取的mobileFullscreenprop 全仓无人生产,注释却说"宿主表单传入" #3232 /field与schema是同一个意思的两个载体,~25 个 widget 读field || schema#3233 / ObjectForm 的 mobile.fullscreenLongText 到不了自动生成字段的 TextAreaField:flag 写在 FormField 上,form.tsx 转发的却是 field.field #3245 / fix(plugin-form,types): carrymobile_fullscreenon the field metadata, where widgets actually read (#3245) #3300 一路收敛出来的结论,在这一行上不成立。TextAreaField与(feat(fields): RichTextField honorsmobile_fullscreenwith a fullscreen editing dialog (#3301) #3302 之后的)RichTextField都已经收敛成单读;只有这个内置分支还留着||。顺带记录一个更大的重复,供裁决时一并考虑:
FullscreenTextarea(form.tsx:308-)是全屏编辑对话框的第三份实现——#3302 已把 widget 侧的两份合并成共享的FullscreenFieldEditor(packages/fields/src/widgets/FullscreenFieldEditor.tsx),但内置分支这一份仍是独立的。两份实现是否要合并,取决于packages/components能否依赖packages/fields(按 AGENTS.md §3 的拓扑,方向是反的,所以这不是一次简单搬运),需要单独裁决,不要顺手做。修法建议(需裁决)
A. 直接删掉
|| fullscreen,只读mobile_fullscreen。零行为回归(这一项今天本来就恒假),declared = enforced 立刻成立。这是与 #3232 / #3233 同向的收敛,我倾向这条。B. 若认为
fullscreen才是想要的拼法,那就该在生产者(ObjectForm)改名并一次性收敛,而不是让消费者同时接受两种——但mobile_fullscreen已经声明在@object-ui/types的BaseFieldMetadata上(#3300),改名成本明显更高且没有收益。无论走哪条,都值得补一条钉死「拼错的 flag 不生效」的测试——目前这个内置分支的全屏行为没有任何测试覆盖到别名这一半。
证据等级
静态证据(全仓 +
objectstack仓 grep,阅读form.tsx三处相关源码),未做浏览器复现。「fullscreen无生产者」是可机械复核的事实。关联:#3301(PR #3302)、#3245(PR #3300)、#3232、#3233。