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.

mobile.fullscreenLongText 对 rich-text 字段是空承诺:ObjectForm 给 field:markdown/field:html 盖了 flag,RichTextField 根本不读;string-multiline 分支全仓无人产出 #3301

Closed

Description

@xuyushun441-sys

在实施 #3245(PR #3300,把 mobile_fullscreen 从 FormField 挪到字段元数据载体上)时顺带发现,不在 #3300 修 —— 那单的 scope 被限制在「flag 到不了自动生成字段的 TextAreaField」,且这里的修法要么动 packages/fields 源码、要么删一段公开文档承诺的行为,两者都需要单独裁决。

现象

ObjectForm 的长文本判定(packages/plugin-form/src/ObjectForm.tsx,#3300 之后的行号 1063-1065)对 5 种 type 盖 mobile_fullscreen:

const isTextarea = t === 'textarea' || t === 'field:textarea' ||
  t === 'string-multiline' || t === 'field:markdown' || t === 'field:html';

全仓只有两个 reader:

reader 位置 覆盖的 type
TextAreaField(读元数据 field.mobile_fullscreen) packages/fields/src/widgets/TextAreaField.tsx:63 field:textarea
form 渲染器内置分支(读 FormField props) packages/components/src/renderers/form/form.tsx:1893-1895 裸 textarea

于是 5 个里有 3 个是盖了没人读:

  1. field:markdown / field:html —— 两者都解析到 RichTextField(packages/fields/src/index.tsx:2127-2128,均 import('./widgets/RichTextField'))。RichTextField.tsx 里 grep -n "fullscreen\|mobile_" 零命中:它既不读元数据也不读 prop,全屏入口从来不渲染。
  2. 'string-multiline' —— 这个字符串在整个仓库里只出现在上面这一行(grep -rn "string-multiline" 排除 node_modules/dist 后仅 1 处命中,就是判定式自己)。没有任何生产者产出它,也不是注册表 key,是条死分支。

为什么这是缺陷而不是「将来再说」

ObjectFormSchema.mobile 的 JSDoc(packages/types/src/objectql.ts:1265)把 rich-text 写进了承诺里:

fullscreenLongText: true,   // textarea/rich-text get an "expand" button

「rich-text 有 expand 按钮」这句话从写下起就没有实现过。这正是 ADR-0049 enforce-or-remove / AGENTS.md #0.1 针对的 declared-but-unenforced 形态:文档和类型注释各自声明了一个没有 enforcement 的行为,作者(包括 AI 生成的表单元数据)照着写、静默无效果、没有任何一处报错。#3245 的六步断链能藏这么久,也是同一个机制。

两条路(需裁决,不要照着猜)

A. 补齐 enforcement —— 让 RichTextField 也实现全屏入口(读同一个元数据 flag,与 TextAreaField 同一契约、同一 data-testid 约定),并给 string-multiline 一个真实生产者、或直接删掉这个分支。

  • 长期正确性:文档承诺兑现,mobile_fullscreen 的语义变成「所有长文本 widget 都实现的一个能力」,而不是「碰巧只有一个 widget 实现的一个键」。
  • 代价:动 packages/fields 源码;富文本编辑器的全屏对话框不是 textarea 那种 <Textarea> 搬进 Dialog 就完事,draft/commit 语义要重新想(编辑器实例是否重建、未提交内容如何搬运)。

B. 收窄声明 —— 把判定式砍到真正有 reader 的两种(textarea / field:textarea),同步删掉 JSDoc 里的 "rich-text" 和死掉的 string-multiline。

  • 长期正确性:declared = enforced 立刻成立,代价最小,没有任何行为回归(这三条今天本来就不产生效果)。
  • 代价:公开文档的能力面缩小一格。如果后续真要做 rich-text 全屏,再走 A。

倾向 B 先落地、A 另开单:B 把「声明」拉回到「已实现」这条线上,是零风险的;A 是一个真实的 UX 特性,值得有自己的验收标准和浏览器验证,混在一起做会让「消除空承诺」这件事被富文本编辑器的复杂度拖住。但这取决于维护者是否认为 rich-text 全屏是近期要做的能力 —— 若是,直接走 A 更省一次往返。

补充:消费侧还有一个 as any 洞

TextAreaField.tsx:54 的 const textareaField = field as any; 让这个 widget 对元数据的每一次读取都不受类型检查(rows / maxLength / placeholder / label / mobile_fullscreen)。#3300 已经消掉了生产侧的 as FormField,并把 mobile_fullscreen 正式声明到 BaseFieldMetadata 上,但消费侧这一半没动 —— packages/fields 不在那单的 scope 内。彻底去掉它需要先决定 widget 如何收窄 FieldMetadata 联合(rows/max_length 只在长文本成员上)。记在这里,不必然与上面同单处理。

证据等级

静态证据(grep + 阅读四处源码),未做浏览器复现。RichTextField 无 reader 与 string-multiline 全仓仅一处命中都是可机械复核的事实;「用户看不到 expand 按钮」是由此推导的。

关联:#3245、PR #3300、#3232、#3233。

Activity

  1. xuyushun441-sys commented on Aug 3, 2026

    @xuyushun441-sys
    ContributorAuthor

    维护者裁决(2026-08-03)+ 认领

    裁决:给 RichTextField 接上全屏能力——mobile.fullscreenLongText 的承诺对 field:markdown / field:html 兑现,而非收窄盖章范围。string-multiline 分支按证据处理:确证全仓无产出则作为死分支清理,有产出则一并接上并报告。

    认领(PM 会话 session_01NVPjPzmmAJ2Ngtvgg5MSRa):


    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