ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Slate v2 marked Enter 撤销契约:从红测试到 split 修复的 undo 光标与标记恢复实战

Slate v2 marked Enter 撤销契约:从红测试到 split 修复的 undo 光标与标记恢复实战 Slate v2 marked Enter 撤销契约从红测试到 split 修复的 undo 光标与标记恢复实战【免费下载链接】plateRich-text editor with AI and shadcn/ui项目地址: https://gitcode.com/GitHub_Trending/pl/plate导读本文以 2026-05-07-slate-v2-marked-enter-undo-caret-ralplan.md 为骨架完整还原 Slate v2 中一个精确且边界清晰的撤销undo回归问题在加粗单词前按 Enter 将段落一分为二之后调用editor.undo()能否恢复分割前的原始段落、加粗bold标记属性与原始光标位置。文中将带你走完一条严格的 TDD 执行链路——先写一条必红的slate-history契约测试再由红测试暴露的真实数据丢失点定位最小属主补丁最后同步 issue 台账并跑通验证命令。读完你既能复现#3499的两段式复现光标落点 撤销恢复标记也能掌握 Slate v2 历史层with-history与核心 split 变换的底层协作原理。1. 问题定位#3499的剩余价值不是光标而是撤销该 Ralplan 的结论Current Verdict非常明确不要重复已被修复的 marked-Enter 光标落点问题该部分由#3964/#4357覆盖剩余的有价值 claim 更窄、更锋利Prove marked Enter plus undo restores the exact pre-split document, mark props, and selection.即marked Enter 之后执行 undo必须恢复分割前的完整文档、标记属性与选区。计划的完成目标是一份可交付执行的强计划score0.92而不是实现本身——实现证明归属 Ralph。1.1 意图与边界Intent And Boundary意图关闭#3499在 marked-Enter 光标证明之后遗留的 history/mark 恢复部分保持修复包属主package-owned除非浏览器证明暴露出 React 导出/导入缺陷避免把一个具体的 undo 回归扩大成一次广泛的 mark-state 架构重设计。期望结果slate-history中一条红色包测试证明Enter 在加粗单词前将该词移到下一个 blockundo 恢复原始段落、加粗标记属性与原始选区issue 台账能精确说明哪些已修复、哪些改进、哪些相关、哪些仍未认领除非后续证据证明当前 operation/history 契约无法表达该行为否则不新增公共 API。范围内文件对应到本仓库的真实位置历史契约测试slate-history测试目录本仓库对应 history.spec.tsx 与 with-history.spec.tsx历史层实现with-history.ts核心分割入口insertBreak.ts当前splitNodes({ always: true })行为仅作为操作来源参考issue 覆盖矩阵、fork dossier、PR 引用与完成状态同步。非目标Non-goals除非新的 undo 测试证明既有包证明过窄否则不重开#3964/#4357没有精确 repro 证据前不认领#4195、#3841、#5629、#4648规划门禁期间不改 React keydown、DOM 选区修复或浏览器事件处理不单独改变协同 undo 语义需要独立的 slate-yjs migration-backbone 回答不新增公共 mark-restoration hook。决策边界Ralph 允许在实现前先添加一条红色公共 history 契约测试Ralph 只有在红测试定位到当前故障后才能修补 history batching、inverse replay 或选区恢复只有失败证明显示 history 层收到不完整操作时Ralph 才能检查和修补核心 split 行为只有在原始两段式 repro光标落点 撤销标记恢复都证明通过后才允许Fixes #3499。2. 决策简报为什么选红测试 最小属主补丁而不是修 React四条原则History undo 恢复保存 commit 之前的可观察状态Mark props 是文档数据不是视图装饰选区恢复必须显式且可重放而不是 DOM focus 修复的副作用Issue 关闭需要精确 repro 证明而非家族相似性。关键驱动证据gitcrawl threads --numbers 3499 --include-closed --json报告该 issue 仍为 open要求两个结果新行上 boldyou开头的光标、以及 undo 后you仍为 bold 的状态issue-coverage-matrix.md 已将#3499归类为相关光标落点已覆盖但精确 undo/mark 恢复需要独立 history 证明——本仓库第 333 行已将其升级为Fixes状态并标注marked-enter-undo-caretpr-description.md 将#3499保留为 undo/mark 恢复的开放债务。四个备选方案及裁决方案优点缺点裁决1. 视为已被#3964修复避免新增 lane忽略 issue 明确的 undo/mark 半部分拒绝2. 先修 React/DOM 选区处理贴合 richtext 浏览器 repro 面剩余 claim 是 undo 状态恢复已有包属主与包测试首轮拒绝3. 在slate-history增加 marked Enter undo 红契约测试无需公共 API 扩张即可证明精确剩余问题若 history 收到已丢失的 commit可能暴露更深层的 split 操作数据问题采纳4. 设计公共 mark/selection 恢复 API给消费者逃生舱让每个应用作者为 core/history 不变量失败买单除非红测试证明不变量不可能否则拒绝选定方向先写红色slate-history证明再由红测试指出的最小属主打补丁。后果是实现目标大概率只动包且 API 风险低TDD 是强制的issue claim 面向行为浏览器证明是条件触发的仅当包状态正确而 richtext 路径仍失败时使用。3. 置信度记分卡六个维度的加权得分维度权重得分依据React 19.2 运行时性能0.200.90未选择任何 React 表面浏览器/React 证明被包证明门控不会污染首个补丁Slate 原生、无观点 DX0.200.94计划不加 API使用现有Editor.insertBreak、editor.undo()与 snapshot 契约Plate 与 slate-yjs 迁移主干0.150.91本地 history 是属主任何 operation/history 元数据变更必须在关闭前记录 Plate/slate-yjs 影响防回归测试策略0.200.94Ralph 必须先写一条红色slate-history契约并重跑现有 history selection/insertBreak 守卫研究证据完整性0.150.90ClawSweeper 已审查目标、邻接 issue、覆盖矩阵、dossier、实时台账及当前源码/测试属主shadcn 式可组合性与极简0.100.94不计划扩展组件 API、hook API、prop 形状或渲染器加权总分0.92达到 Ralph 执行门槛。4. 架构北极星undo 必须恢复分割前的精确可见状态北极星原则朴素而严格undo 应恢复分割前的精确可见状态。如果一个已保存的 history batch 捕获了 split commit那么inverse operation replay 保存的 selection必须足以恢复原始树与选区。对应本仓库的源码属主insertBreak.ts 是核心分割入口——它委托给从slate导入的insertBreakBase在.tmp/slate-v2工作区中该入口通过命令注册表委托给splitNodes({ always: true })任何分割操作都经由这一层产生with-history.ts 是历史层核心e.undo()第 59–89 行将 batch 内操作map(OperationApi.inverse).reverse()得到逆操作序列在withoutSavingwithoutNormalizing中重放随后恢复batch.selectionBeforee.redo()第 33–57 行重放正向操作并选择selectionAfter ?? selectionBeforeshouldSave第 178–184 行明确将纯set_selection操作排除在历史保存之外——这正是选区恢复必须显式可重放原则的实现位置shouldMerge第 153–174 行只在连续insert_text/remove_text且路径、偏移连续时才合并保证 batch 粒度可控历史栈有 100 条上限第 126 行防止无限增长。可以看到文档中引用的.tmp/slate-v2/packages/slate-history/src/with-history.ts第 48/57/60/63/211 行语义重放逆操作、恢复selectionBefore、记录新 batch 的selectionBefore、排除set_selection与本仓库真实源码逐条对应。5. 内部运行时目标红测试的预期形状初始目标Internal Runtime Target用真实 editor 更新路径写一条 history 契约在单个段落中、bold leaf 之前分割断言分割后光标落在被移动 bold 文本的开头调用 undo断言原始段落、bold leaf 与原始选区都被恢复。预期起始形状before: paragraph(hey , bold(you)), selection at [0,0] offset 4 or [0,1] offset 0 after Enter: paragraph(hey ), paragraph(bold(you)), selection at [1,0] offset 0 after undo: paragraph(hey , bold(you)), selection at the original split point注意分割点既可能落在普通 leaf 末尾[0,0]4也可能落在 bold leaf 开头[0,1]0确切路径形态必须由红测试裁定——这是计划中刻意留白、交由证据决定的技术不确定性。6. 公共 API 目标与迁移主干约束公共 API零变更。期望行为全部通过既有调用可观察Editor.insertBreak(editor)editor.undo()Editor.getSnapshot(editor)后续任何公共 API 提案必须首先证明当前 operation replay 与 selection snapshot 无法表达该不变量。Plate 迁移主干Plate 应继承正确的 raw Slate history 行为本 lane 不引入任何 Plate adapter、插件 API 或editor.api/editor.tf兼容工作。若实现改变了 operation 形状或 history 元数据后续迁移 pass 必须说明 Plate 插件如何继续依赖确定性的 snapshots 与 undo batches。slate-yjs 迁移主干不要混淆本地 undo 与协同 undo。本计划可为本地 raw Slate history 关闭#3499若修复改变 operation inversion、history 元数据或 commit 分组关闭前必须另行记录 slate-yjs 影响。外部生态对比React 19.2 / Lexical / ProseMirror / Tiptap在本计划中被显式跳过skipped with reason仅在红测试暴露真实设计分叉时才需要。7. 全量 Issue 台账核算7.1 邻接 issue 的决策Issue当前决策理由#3499Ralph 的目标光标半部分似已覆盖undo/mark 恢复仍开放且有直接的slate-history证明路线#3964已修复既有包证明覆盖 marked Enter 光标落点#4357已修复与#3964同属 marked Enter 新块聚焦族#4195相关同属回车落点族此处尚无精确 repro#3841相关Firefox 自定义insertBreak覆盖需要浏览器证明#5629相关标点词导航是相邻策略不属于本 undo lane#4648不认领标点策略超出本目标#3756相关守卫同属 history/selection 族但更宽泛多 block 删除选区恢复不自动关闭#3497不认领React 父级重渲染焦点丢失不属于本地 history undo 恢复7.2 台账同步状态实时台账 gitcrawl-live-open-ledger.md 已包含被审 issue 的当前行raw corpus 无行需要改动issue-coverage-matrix.md 已将#3499指向本计划作为活跃未认领目标现为Fixesfork-issue-dossier.md 将#3499记录为issue-reviewed且 Ralph-ready而不再仅仅相关于#3964pr-description.md 将#3499命名为下一个计划中的 history 证明目标同时保留 no-fixed-claim 边界。8. 回归证明矩阵哪些已有包证明哪些缺失行为证明路线状态Marked Enter 光标落点snapshot-contract.tmp/slate-v2/packages/slate/test/snapshot-contract.ts:1189既有包证明Marked Enter undo 恢复 marks新增slate-history契约缺失Marked Enter undo 恢复选区新增slate-history契约缺失浏览器 richtext 复现仅当包证明不足时使用 Playwright richtext条件触发#3841Firefox 自定义覆盖浏览器特定证明首次实现范围外本仓库当前已有的历史测试属主为 history.spec.tsx 与 with-history.spec.tsx它们正是新增红契约测试应归入的目录。9. 浏览器压力与对等策略包证明优先。浏览器证明不允许驱动首个补丁除非包契约已经变绿且可见浏览器路径仍然失败。潜在后续浏览器行Chrome richtext在 bold 单词前分割 → undo → 断言 DOM/selection 与包 snapshot 镜像一致。显式不认领不关闭#3841的 Firefox不做移动端/原生对等声明不做标点词边界声明。10. 高风险预检Pre-Mortem应用状态已应用。初始风险清单包测试通过但浏览器 selection 仍指向错误 DOM 位置history 修复保留 marks但破坏了history-contract.ts已覆盖的多 block 操作选区恢复本地 operation inversion 正确却产生不清晰的协同 undo 故事。证明响应首个实现保持纯包级重跑既有 history selection 测试围绕deleteFragment、moveNodes、反向 join、insertBreak仅在包行为变绿后才升级到浏览器证明。11. 硬切割与已拒绝的替代方案切割 React-first 修复切割公共 mark-restoration API切割广泛的 mark-state 架构工作切割标点词导航策略从本 lane 切割协同 undo 重设计在两个原始 bug 都被证明前切割任何Fixes #3499措辞。12. 维护者异议台账异议当前回答状态已被#3964修复不完整。#3964覆盖光标落点#3499还要求 undo 保留 bold 文本keep这是旧 Slate0.57.1的噪音也许但 v2 有直接 history 证明路线与当前源码属主红测试裁决keep浏览器 richtext repro 意味着 React 拥有它仅当包状态已正确history 先获得证明keep修 history 可能触碰协同 undo如果 operation 语义改变则为真Ralph 必须在认领关闭前记录 Plate/slate-yjs 影响keep13. 实施阶段与属主Phase 0规划完成。Phase 1红证明为 marked Enter undo 增加一条slate-history契约测试确认它在任何实现补丁之前失败。Phase 2最小属主补丁若 history replay/selection restore 错误 → 修补with-history.ts若 split 操作在 history 看到之前已丢失 → 修补核心 split 属主若包状态正确但浏览器错误 → 转向 React/浏览器证明。Phase 3认领同步仅在证明支持认领后更新 issue 覆盖矩阵、fork dossier、PR 引用与完成文件。14. 快速驱动门禁可复现的验证命令首次实现门禁文档原命令作用于.tmp/slate-v2工作区在本仓库中对应的测试属主文件为 history.spec.tsx 与 with-history.spec.tsxbun test ./packages/slate-history/test/history-contract.ts -t marked Enter undo bun test ./packages/slate/test/snapshot-contract.ts -t insertBreak after marked text bun --filter slate-history typecheck bun --filter slate typecheck bun lint:fix仅当被触碰的属主需要时再放宽bun test ./packages/slate-history/test/history-contract.ts bun test ./packages/slate/test/snapshot-contract.ts bun check15. Ralph 执行结果红测试暴露的真实数据丢失执行状态done。实现结果在slate-history增加了 marked Enter undo 证明history-contract.ts红测试暴露了真实损失核心 split 在被移动的 bold leaf 之前创建了一个空的右侧文本 leaf导致 selection 落在了空 leaf 上修补核心 split 属主splitNodes本仓库对应 splitNodes.ts它同样委托给slate的splitNodesBase在文本 leaf 末尾分割时跟踪下一个兄弟的起始在移动的 marked 文本之前跳过制造空 split leaf。Issue 认领Fixes #3499marked Enter 在 bold 单词前将该词移入新 block将 selection 置于被移动单词开头undo 恢复原始 marked 段落与选区#3756仍是更宽泛的相关 history-selection 守卫。验证命令bun test ./packages/slate-history/test/history-contract.ts -t marked Enter undo bun test ./packages/slate/test/snapshot-contract.ts -t insertBreak after marked text bun test ./packages/slate-history/test/history-contract.ts bun test ./packages/slate/test/snapshot-contract.ts bun --filter slate-history typecheck bun --filter slate typecheck bun lint:fix同步的文件issue-coverage-matrix.md、fork-issue-dossier.md、pr-description.md 以及 open-issues 台账。16. 最终完成门禁与交付要点计划以done收尾的依据score0.92、无维度低于0.85、ClawSweeper 相关 issue pass 完成、planning 表面的 issue-ledger 同步完成、分类发生变化的 issue 的 fork dossier 同步完成、PR 引用文档最新、TDD / intent-boundary / high-risk / steelman 各行均已应用或带理由跳过、实现证明已分配给 Ralph、无未解决的公共 API 或协作问题阻塞执行。关闭时就绪后的用户评审交接清单Final User-Review Handoff Outline精确的 issue 认领文本证明命令与结果变更的 issue 行.tmp/slate-v2中变更的源文件是否需要浏览器证明剩余的相关系列非认领项。17. 留给红测试裁定的开放问题当前 v2 是否已能在#3499精确的 split-before-mark 形状下于 undo 时保留 bold leaf——由红测试裁决规范分割点是 Enter 前的[0,0]4还是[0,1]0——由红测试裁决#3756保持为相关守卫而非自动关闭目标浏览器证明是条件性的本 lane 的 Chrome richtext 已足够除非包状态变绿且浏览器行为仍分歧。结语这个 Ralplan 是窄 claim 红测试 最小属主补丁 精确台账方法论的完整样本它拒绝把#3499当作#3964的重复拒绝把问题外包给 React/DOM 修复拒绝在证据不足时认领相邻 issue也拒绝为一条 history 不变量引入公共 API。最终红测试证明的根因——split 在移动 marked 文本前制造了空 leaf——恰好落在核心 split 属主上修复路径干净利落。对任何想在 Slate v2或任何基于操作日志的编辑器上做回归修复的开发者而言这份文档从如何把一个大 issue 切出最小可证明的 claim到如何用一条测试命令锁定证据都提供了可直接复用的执行模板。【免费下载链接】plateRich-text editor with AI and shadcn/ui项目地址: https://gitcode.com/GitHub_Trending/pl/plate创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表