
Plate Slate v2 修复 Android Mark 切换选择错乱无 Appium 的实施证据路径【免费下载链接】plateRich-text editor with AI and shadcn/ui项目地址: https://gitcode.com/GitHub_Trending/pl/plate本文围绕 Plate 仓库中 Slate v2 的 Android 输入运行时问题展开在折叠选区上切换 mark如加粗后继续输入时Android 软键盘消失、光标跳动的缺陷上游 issue#6022。文章完整还原「无 Appium 真机证据」前提下能合法证明的范围、红/绿契约测试的验证方法以及底层「标记插入后旧 leaf 选择位点回放」这一选择错乱根因的源码级修复帮助你掌握移动 IME 场景下「包级契约证明 vs 真机证明」的分层证据方法论。问题背景issue #6022 与上游 PR #6027计划文档 针对的是 Slate 上游 issue#6022[Android] Soft keyboard dismisses and cursor jumps when typing after toggling a mark on a collapsed selection即在 Android 上当用户在**折叠选区collapsed selection**上通过工具栏切换一个 mark例如加粗随后继续输入时软键盘会立即收起且光标发生跳动。计划文档明确记录了该缺陷的三条证据链视频证据issue 中的录屏显示工具栏切换 bold 后键盘立即消失后续的输入/重新聚焦尝试均无效操作日志证据issue 评论中的日志显示Android 在折叠 mark 切换后输入时会插入一个带 mark 的节点随后选择selection在旧文本路径与插入后的新文本路径之间来回振荡上游 PR 证据上游#6027定位到了同一个责任方owner——Android 输入管理器在一次 marked 插入改变了 leaf 结构后仍然从「插入前的文本位点」调度了一次后续选择写入。计划文档中记录的责任方文件为 fork 工作副本中的.tmp/slate-v2/packages/slate-react/src/hooks/android-input-manager/android-input-manager.ts该路径为计划文档所引用的 fork 开发副本路径不在当前仓库目录树中当前仓库中的对应核心包源码位于 packages/slate。Non-Appium 证据边界允许证明什么、禁止证明什么这篇计划文档最有价值的方法论贡献是它先划定了证据边界。在没有任何真实 Android IME / 设备产物raw device artifacts的情况下该工作流lane不得声称Fixes #6022。文档把可证明范围显式拆成两组当前允许的证明Allowed proof通过 Android 输入管理器完成 marked 折叠输入marked collapsed typing的包级契约package contractmarked 插入之后的**模型层选择model selection**是否正确Slate 拆分文本节点后不再回放到旧 leaf 的过期选择stale old-leaf selection restoration。当前禁止的证明Not allowed proof键盘可见性keyboard visibilityAndroid Chrome / WebView 的 IME 稳定性原始设备层面的 issue 关闭raw-device issue closure。这个边界直接决定了文档的验收口径只能更新 issue ledger 的状态为supporting proof improved支撑证据增强而不是fixed除非后续存在真机证据。这一点在 issue 覆盖矩阵 中也得到了贯彻其中#6022的行明确写着当前的包级证明覆盖了「marked 折叠输入后旧 leaf 选择过期」这一子缺陷但键盘可见性与 IME 稳定性仍需 Android 真机证据。根因解析marked 插入后不得回放原始旧 leaf 选择配套的方案文档 2026-05-23-slate-react-android-marked-inserts-must-not-replay-raw-old-leaf-selection.md 给出了完整的根因与修复说明这是理解本问题的核心。症状文本树正确光标却错了Android 上的 marked 输入会出现「模型正确而光标错误」的状态。当带激活 marks 的折叠输入把当前文本 leaf 拆分时任何基于旧 DOM 位点的延迟选择恢复都必须在到达编辑器之前被归一化normalize到新的 marked leaf 上。具体症状为文本插入产生了预期的拆分结构{ text: a }, { text: w, bold: true }但选择却落在了原始的[0,0]2——这已经超出了原 leaf长度为 1的合法范围——而不是应该落在的[0,1]1插入的 marked 兄弟 leaf 内上游 Android issue 中展示的键盘消失与光标跳动正是选择持续指向非法/过期位点的用户可见表现。方案文档还记录了两条走不通的路What Didnt Work对调试此类问题很有参考价值只断言文档文本与 marks 是不够的——文本树完全正确而选择仍指向旧 leaf把包级测试当成 Android 原始证据是错误的——它只能证明 Slate 输入管理器的契约不能证明键盘可见性或 IME 稳定性。修复保留 flush 时机把选择放置交给at归一化修复策略是保留 Android 管理器中已调度的 action使待处理的文本 diff 仍能正常 flush但让既有的at位点归一化normalization来独占选择放置不再在performAction()归一化位点之后跑第二次原始选择写入。修复后的关键调用形态为scheduleAction(() {}, { at: newPoint, preserveInsertPositionHint: true, })为什么这能生效performAction()内部已经通过normalizePoint(editor, action.at)归一化待处理的位点。对于旧的[0,0]2归一化会走过那个仅含一个字符的旧 leaf落到插入的 marked 兄弟节点[0,1]1上。而修复前的调度 action 在归一化之后又用tx.selection.set(...)拿着原始旧位点把选择覆盖回去了。删掉这次第二写入就同时保住了正确的归一化结果与原有的 flush 时机行为。作为对照当前仓库中 mark 与插入相关的基础实现分别位于 addMark、toggleMark 与 insertText——本缺陷正是在「toggleMark 改变 leaf 结构」与「insertText 的延迟选择恢复」两条路径交汇时暴露的。包级契约测试红转绿的复现计划文档要求新增的契约测试就放在 Android 输入管理器测试旁边fork 副本中为.tmp/slate-v2/packages/slate-react/test/android-input-manager-contract.test.ts。其核心断言序列如下Editor.select(editor, range(1)) Editor.addMark(editor, bold, true) EDITOR_TO_PENDING_INSERTION_MARKS.set(editor, { bold: true }) manager.handleDOMBeforeInput(beforeInputEvent(insertText, w)) manager.flush() expect(Editor.getSnapshot(editor).selection).toEqual({ anchor: { path: [0, 1], offset: 1 }, focus: { path: [0, 1], offset: 1 }, })这段测试精确复现了#6022的操作序列折叠选区定位 → 添加 bold mark → 通过beforeinput事件注入insertText w→ flush Android 输入管理器然后断言选择必须落在插入的 marked leaf 上。红/绿验证记录计划文档的 Verification 一节完整记录了红失败转绿通过的验证过程是「先红后绿」纪律的范本阶段命令计划文档记录的原始工作区命令结果红bun test:vitest test/android-input-manager-contract.test.tsslate-react包失败选择恢复到[0,0]2而非[0,1]1绿同上通过7个测试全部通过类型检查bun --filter slate-react typecheck通过Lintbun lint:fix通过移动设备证明守卫bun test:mobile-device-proof通过并确认 semantic/proxy 行不能满足原始 Android/iOS 声明完成度检查node tooling/scripts/completion-check.mjs通过其中test:mobile-device-proof这一行值得单独强调它用测试的方式强制了 Non-Appium 边界本身——即语义/代理proxy证据行永远不能冒充真机声明从工具层面堵住了「拿包级测试关闭设备 issue」的口子。适用前提说明以上命令与路径.tmp/slate-v2/...均为计划文档中记录的 fork 开发工作副本内的执行环境属于该次执行过程的原始记录在当前仓库中可参照的对应实现入口为 packages/slate。工作计划与最终进展计划文档给出的四步工作计划与最终进展如下全部完成补齐包级契约为经过 Android 输入管理器的折叠 mark 输入添加缺失的 package contract按需修复仅当该契约在当前 Slate v2 中证明为红时才修复android-input-manager.ts实际确实为红见上聚焦验证运行slate-react的聚焦验证诚实的 ledger 状态把 issue ledger 状态更新为supporting proof improved而非fixed除非后续出现真机证据。Progress 清单全部已勾选包括issue 与 PR 源读取、现有 Android 输入管理器源码与测试读取、视频转录助手成功运行、聚焦契约新增、红证据捕获新契约在 marked 插入后选择恢复到原始[0,0]2而失败、实现修复调度的 Android diff flush 保留归一化后的at位点但不再执行第二次原始选择写入、聚焦契约验证通过、ledger 更新为诚实的无 Appium 状态。变更文件与 ledger 联动本次执行改动了 6 个文件计划文档 Changed Files 一节fork 副本中的android-input-manager.ts实现与android-input-manager-contract.test.ts契约测试.tmp/slate-v2/packages/slate-react/下属 fork 工作副本路径gitcrawl-v2-sync-ledger.md其中 2026-05-23 同步记录明确写了「该执行仍不做Fixes #6022声明因为可见的键盘消失需要原始 Android Chrome/WebView 证明」并把#6022的同步状态定为implementation-reviewed, no fixed claimfork-issue-dossier.md#6022条目保持related状态记录 2026-05-23 无 Appium 证明细节修复前选择恢复到[0,0]2修复后归一化到[0,1]1并给出可直接使用的 PR 描述文本issue-coverage-matrix.md#6022行保持Related注明精确关闭仍需 Android/设备证据方案文档 2026-05-23-slate-react-android-marked-inserts-must-not-replay-raw-old-leaf-selection.md。三处 ledger 的状态表述彼此一致实现层已按#6027的根因类别修复了「旧 leaf 选择回放」这一选择子缺陷但用户可见的键盘消失必须等真机 Android 证据。剩余证据缺口与预防准则计划文档最后的 Remaining Proof Gap 一节给出了明确的关闭门槛在一条真实的 Android Chrome/WebView 证明流lane对该精确的折叠 mark 切换流程证明键盘可见性、IME 稳定性、选择正确性与后续输入之前不得关闭或自动认领#6022。方案文档的 Prevention 一节则沉淀了三条可复用的工程准则Android / 输入管理器测试必须同时断言文档文本与选择二者缺一不可当一个待处理位点基于 diff 之前的 DOM 状态时在运行时责任方runtime owner处只归一化一次避免其后再用原始点做selection.set(...)写入不要把这类包级证明提升为 Android issue 的关闭依据除非存在覆盖键盘可见性与后续输入的原始 Android Chrome/WebView 证明流。这套「先划证据边界 → 契约先红后绿 → ledger 诚实分级 → 真机门槛兜底」的流程为在 CI 无法触达真机 IME 的前提下处理移动输入类缺陷提供了一个可复制、可审计的完整范式。【免费下载链接】plateRich-text editor with AI and shadcn/ui项目地址: https://gitcode.com/GitHub_Trending/pl/plate创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考