
Plate 富文本编辑器日期契约锁定与收口从 Date Contract Expansion Lane 关闭审计看窄契约治理【免费下载链接】plateRich-text editor with AI and shadcn/ui项目地址: https://gitcode.com/GitHub_Trending/pl/plate本文以 plate 仓库中 2026-04-10-date-contract-expansion-lane-closeout-plan.md 为骨架完整还原一次功能泳道Lane关闭审计的工程实践当代码、测试与文档早已围绕一个窄日期契约收敛时如何在路线图中正式宣告该泳道关闭而不是继续制造虚假的队列压力。读完本文你将掌握 plate 日期元素的完整数据契约规范值、回退值、Markdown 往返形状、关闭一条功能泳道的审计方法与决策模板以及仓库中与之配套的源码、测试与验证命令。一、背景为什么会有 Date Contract Expansion 这条泳道plate 的编辑器行为路线图 master-roadmap.md 中曾长期维护一条名为Lane 3: Date Contract Expansion的活跃待办泳道其工作假设是日期节点date node的数据契约还需要继续扩宽——例如引入更丰富的序列化负载、locale / timezone 语义、展示值与存储值分离等能力。然而在撰写本计划之前仓库中的代码与多数文档已经给出了相反的信号规范值YYYY-MM-DD已经锁定为唯一 shipped 的日期节点值Markdown 写出形状固定为date valueYYYY-MM-DD /遗留的 child-text 日期仅以读取兼容的方式保留更丰富的序列化日期语义被明确推迟deferred。这正是一种典型的代码已关闭、路线图未关闭状态。于是本计划的核心命题随之产生Lane 3 究竟还是一条约待实现的功能泳道还是仓库其实已经交付了窄日期契约、剩下的只是路线图与文档层面的真相收口truth cleanup当前假设很明确Lane 3 的大部分工作实际上已经完成剩余工作更可能是真相清理而不是又一次日期 schema 扩展批次。二、窄日期契约plate 日期元素的数据定律在展开审计之前先完整理解这条窄契约到底窄在哪里。它由四个相互锁定的结论构成这也是整篇文章的事实基石。2.1 规范值canonical value锁定为 YYYY-MM-DD日期节点的存储值只承认一种规范形态YYYY-MM-DD字符串存放在节点的date字段上。这一约定同时体现在插入变换 insertDate.ts插入时通过normalizeDateValue(date ?? new Date())将输入规范化为date字段随后以editor.tf.insertNodes写入一个type: editor.getType(KEYS.date)的 inline void 节点并在其后追加一个{ text: }文本节点以避免丢失编辑器焦点值工具 dateValue.tsformatDateValue负责把Date对象格式化为YYYY-MM-DD使用pad补齐两位月日parseCanonicalDateValue只接受匹配^\d{4}-\d{2}-\d{2}$且真实存在年/月/日逐项回验的日历日期公开文档 date.mdx/(elements)/date.mdx) 的 Value Shape 一节直接给出字段表date为规范YYYY-MM-DD值rawDate为不可规范化输入的回退值。2.2 回退值rawDate不可规范化的文本不丢失并非所有输入都能被安全地转成日历日期。normalizeDateValue的决策表详见 dateValue.ts 与 date.mdx/(elements)/date.mdx) 的 Date Normalization 一节如下输入存储结果有效的Date对象date: formatDateValue(value)有效的YYYY-MM-DD字符串date日历日期校验通过时无效的规范字符串rawDateMon Mar 23 2026这类可被 JavaScript 安全解析的旧格式date: 2026-03-23空字符串不写任何日期字段其他任意文本rawDate其中LEGACY_DATE_REGEX /^[A-Z][a-z]{2} [A-Z][a-z]{2} \d{2} \d{4}$/用于识别旧版Date.prototype.toDateString()输出形态如Mon Mar 23 2026这类遗留输入会被升级为规范值而sometime next week这类无法解析的松散文本则原样保留在rawDate中。2.3 Markdown 写出形状锁定为属性形式日期元素在 Markdown / MDX 中的写出形状是唯一且确定的Date: date value2026-03-23 /对应的底层规则位于 defaultRules.ts 的date规则中deserialize同时读取属性形式props.value与遗留 child-text 形式取首个文本子节点统一交给normalizeDateValue决定写入date还是rawDateserialize当date存在且无rawDate时写出propsToAttributes({ value: date })的自闭合属性标签否则退回 child-text 形式写出rawDate ?? date ?? 。也就是说规范值永远以属性形式写出不可规范化的遗留文本永远以 child-text 形式写出两条路径互不混淆。2.4 遗留 child-text 仅保留读取兼容date2024-01-01/date、dateMon Mar 23 2026/date这类旧式 child-text 写法只作为读取兼容路径存在不再是写出形状。这一点由 dateElement.spec.ts 的四个用例完整固化round-trips inline date elementsdate2024-01-01/date读入后得到date: 2024-01-01再序列化写出date value2024-01-01 /且二次读回与首次读回结构一致往返稳定reads attribute-bearing date elementsdate value2024-01-01 /直接读为规范值并原样写出keeps non-normalizable legacy child text on the raw fallback pathdatesometime next week/date落到rawDate写出时保持 child-text 形态upgrades safe legacy child-text datesdateMon Mar 23 2026/date被升级为date: 2026-03-23写出时变成属性形式。这四个用例正是窄契约在测试层的法律文本。2.5 更丰富的序列化语义保持显式推迟以下三类能力不属于当前契约且被明确标记为 deferred而不是含糊的以后再说展示值 vs 存储值的分离display-vs-value splitlocale / timezone 相关的负载语义超越当前规范属性形式的更丰富序列化日期负载。在 editor-protocol-matrix.md 的协议矩阵中日期一行明确写着display-vs-value, locale/timezone, or richer date payload均属 deferred且heavier serialized date semantics beyond the current canonical attribute writer remain deferred until a separate date-expansion lane chooses them explicitly——即只有出现新的产品证据并显式开新泳道才会重新评估。三、源码级证据窄契约已经 shipped计划中的 Current Repo Read 部分给出了四条代码证据链我在仓库中逐一核实全部成立契约环节落地文件核实结论插入写入规范日期insertDate.ts插入默认值经normalizeDateValue归一化不再产生toDateString()规范解析/格式化/回退标签dateValue.ts三种输入分支Date 对象 / 规范串 / 遗留串与rawDate兜底齐全插件本体BaseDatePlugin.tskey: KEYS.dateisElement / isInline / isVoid三标记并通过extendEditorTransforms绑定editor.tf.insert.dateMarkdown 往返测试dateElement.spec.ts四条用例锁定双形态读写与规范化升级交互渲染器date-node.tsxelement.date || element.rawDate决定显示日历选中时editor.tf.setNodes({ date: formatDateValue(date), rawDate: undefined })只写规范值静态渲染器date-node-static.tsx只读场景同样走getDateDisplayLabel(element)不发明额外序列化语义两个渲染器都基于getDateDisplayLabel生成可见标签Today/Yesterday/Tomorrow/ 本地化长日期 / 原始回退字符串其中相对日期今天/昨天/明天基于日历日比较isSameCalendarDay杜绝了new Date(node.date)直接解析带来的时区漂移问题——这正是 2026-04-09-date-media-expansion-consensus-plan.md 中canonical payload beats render convenience与parse canonicalYYYY-MM-DDas a calendar day without timezone drift原则的最终落地形态。此外isPointNextToNodeisPointNextToNode.ts提供了光标是否紧邻某类型节点的边界查询用于 inline void 节点在行首/行尾/独占点的键盘访问与退格处理配套测试见 isPointNextToNode.spec.tsx。四、决策时刻Option A 关闭 vs Option B 保持开启计划的核心是一道二选一决策题这也是任何泳道收口审计的标准决策模板。Option A关闭 Lane 3适用条件审计确认当前窄日期契约就是预期的正式交付答案。关闭意味着在路线图中正式关闭 Lane 3将日期负载开放问题标记为已解答将更丰富的日期负载、locale/timezone 语义、展示值与存储值分离排除在当前契约之外未来只有在出现新产品证据时才以全新泳道的形式重启更丰富的日期工作。Option B保持 Lane 3 开启仅当审计发现真实存在且尚未解决的扩宽决策时才使用典型触发条件包括仍有一个被当前文档低估的、受支持的更丰富序列化负载存在缺失的旧内容兼容路径产品行为中已经隐含了必须落地的展示值/存储值分离。计划同时给出了明确判断当前仓库依据并不指向 Option B——代码、测试与公开文档已经围绕同一个窄日期契约对齐没有发现真实的扩宽增量。推荐结论推荐选择Option A。理由直白在没有真实扩宽增量widening delta的情况下保持泳道开启只会制造虚假的队列压力fake queue pressure。这是一条非常值得借鉴的工程原则功能是否完成应以代码与测试为准而不是以路线图的陈旧条目为准。五、实施单元三个收口动作若审计确认无代码缺口则整个收口只涉及三个单元Unit 1日期契约审计目标确认是否真的缺少扩宽工作。审计清单与仓库现状一一对应insertDate.tsdateValue.tsdateElement.spec.tsdate-node.tsxdate-node-static.tsxdate.mdx/(elements)/date.mdx)markdown.mdx/(serializing)/markdown.mdx)审计需要回答四个问题规范YYYY-MM-DD是否是唯一 shipped 的节点值属性形式的 Markdown 写出是否已经是规范输出遗留 child-text 日期是否只作为兼容输入处理渲染器是否发明了法律law/spec中不存在的额外序列化语义预期结果无需新增代码或仅在发现一处矛盾时做一个极小的跟进修复。Unit 2路线图与研究文档收口目标让书面真相与已交付的日期契约一致。涉及文件master-roadmap.mddate-mdx-payload-contract.md2026-04-09-date-media-expansion-consensus-plan.md三项工作若 Unit 1 确认无真实扩宽缺口则在路线图中关闭 Lane 3将日期负载开放问题标记为已解答并写明当前产品答案将旧共识文档重构为历史背景或添加状态块说明日期半边已关闭媒体半边仍然独立。仓库的最终状态完全符合该单元的预期master-roadmap.md中 Lane 3 已标记为[x] Date Contract Expansion并注明This lane is now closed for the current date contractdate-mdx-payload-contract.md 的 frontmatter 已改为status: answered并在 Current answer 中完整列出 locked / deferred 清单2026-04-09-date-media-expansion-consensus-plan.md 开头即为Historical for the date half明确日期半边已由 shipped 窄契约关闭、媒体/嵌入半边仍作为活跃泳道的背景上下文。Unit 3公开文档一致性目标确保用户可见文档与代码、路线图口径完全一致。涉及四个产物date.mdx/(elements)/date.mdx)markdown.mdx/(serializing)/markdown.mdx)editor-protocol-matrix.mdmarkdown-parity-matrix.md三项工作确认四个产物使用完全相同的窄契约措辞删除任何暗示活跃泳道中仍有待定更丰富负载的过时表述将更丰富的日期负载保持为显式推迟而不是模糊的以后再说。仓库现状同样吻合markdown-parity-matrix.md将Date行标记为locked仅针对窄契约并列出EDIT-DATE-*系列测试覆盖editor-protocol-matrix.md以deferred明确标注三类推迟语义。六、验证命令审计后的回归清单计划给出的验证策略是分级的——如果 Unit 1 未发现代码差异验证只涉及文档只有在发现运行时/文档矛盾并需要改动代码时才执行完整验证链pnpm install # 日期 Markdown 往返专项测试 bun test packages/markdown/src/lib/dateElement.spec.ts # 日期包全部测试 bun test packages/date/src/lib/**/*.spec.ts # 应用层渲染器测试交互 静态 bun test apps/www/src/registry/ui/date-node.spec.tsx apps/www/src/registry/ui/date-node-static.spec.tsx # 构建与类型检查仅过滤受影响包 pnpm turbo build --filter./packages/date --filter./packages/markdown --filter./apps/www pnpm turbo typecheck --filter./packages/date --filter./packages/markdown --filter./apps/www # 统一 lint pnpm lint:fix其中date-node.spec.tsx与date-node-static.spec.tsx正是 2026-04-09-date-media-expansion-consensus-plan.md 中renderers 必须有规范日期显示行为的显式覆盖而不仅是包级 transform 测试要求的产物——它把规范日期渲染无时区漂移、日历编辑写规范值、不可规范化遗留文本走回退路径固化成了可回归的测试资产。七、退出标准与下一泳道Lane 3 达到可关闭状态需同时满足三个条件审计确认窄日期契约已经是 shipped 的产品答案路线图、parity、protocol、research 与公开文档就该答案达成一致任何更丰富的序列化日期语义都被清晰标记为 deferred而不是含糊暗示。下一泳道Lane 4 Media / Embed Expansion如果 Lane 3 按本计划关闭下一个真实队列项将是Lane 4媒体 / 嵌入扩展。它正是 2026-04-09-date-media-expansion-consensus-plan.md 中与日期同批提出、却未被关闭的另一半媒体/嵌入的规范化目前仍分散在 parseIframeUrl.ts、parseVideoUrl.ts、parseTwitterUrl.ts等解析器与 submitFloatingMedia.ts 的提交时变换中属于有行为但缺显式契约的状态与日期形成鲜明对照——这也反向印证了日期之所以能干净收口正是因为它的契约先于 UI 被显式锁定而媒体尚未走到这一步。八、方法论总结把这份关闭计划提炼成可复用的工程方法一共五步先看代码再信路线图功能是否完成以insertDate.ts、dateValue.ts、defaultRules.ts、dateElement.spec.ts这些代码与测试为准用测试固化契约窄契约的每一条边界属性写、child-text 读、遗留升级、回退兜底都必须有对应的 spec 用例显式推迟而非模糊承诺deferred要写清推迟了什么、为什么推迟、什么条件下才重启而不是一句以后再说文档同步收口路线图、研究开放问题、协议矩阵、parity 矩阵、公开文档五类产物必须同口径无增量即关闭没有真实的扩宽 delta就不要让陈旧泳道继续占据队列制造虚假压力。plate 的日期元素由此成为一个教科书级的窄契约治理样本规范值、回退值、序列化形状、兼容路径四件事边界清晰代码、测试、文档三层证据相互印证最终以一个干净的 Lane 关闭审计画上句号。【免费下载链接】plateRich-text editor with AI and shadcn/ui项目地址: https://gitcode.com/GitHub_Trending/pl/plate创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考