拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Plate 导航反馈契约(Navigation Feedback)设计规范:从 TOC、脚注到搜索跳转的统一编辑体验

Plate 导航反馈契约Navigation Feedback设计规范从 TOC、脚注到搜索跳转的统一编辑体验【免费下载链接】plateRich-text editor with AI and shadcn/ui项目地址: https://gitcode.com/GitHub_Trending/pl/plate导读本文围绕 Plate 富文本编辑器项目中的共享「导航反馈」规范展开系统讲解docs/plans/2026-04-06-navigation-feedback-spec.md所定义的跨界面编辑定律任何成功的导航跳转都应移动焦点/光标、将目标滚动进视口、并对落点目标做短暂高亮。这份规范的目标是把当前 TOC目录、脚注导航、搜索跳转等功能各自重复实现的闪烁/滚动/选中修复逻辑收敛为一个编辑器级共享原语。读完本文你将掌握该契约的完整 API 设计flashTarget/navigate、目标模型、分层架构lib 层 React 层、分阶段落地计划与测试矩阵并能结合仓库源码看清它在packages/core、packages/footnote、packages/toc中的实际落地形态。本文为文档解读 源码印证文章文中引用的文件路径均以仓库根目录为基准。1. 为什么需要一个共享导航反馈契约1.1 现状每个跳转功能都在各写各的在 Plate 的编辑器行为体系中以下场景都需要在跳转后给出视觉反馈TOC 点击跳转点击目录条目后滚动到对应标题脚注导航从参考文献跳转到定义、或从定义跳回引用处搜索跳转跳转到搜索命中的文本位置本规范中属于延迟项。每个功能实现跳转反馈时都需要反复处理同一组逻辑目标解析与交接target resolution handoff瞬时高亮的计时transient highlight timing上一次目标状态的新旧替换与清理replacement/clearing of prior target state视觉 token 的逐步漂移visual-token drift。正如契约文档 docs/plans/2026-04-06-navigation-feedback-contract.md 指出的没有共享契约就会出现重复的定时器、重复的状态、重复的渲染逻辑长期维护与 DX 体验都是最差的。1.2 已有律法三条编辑器定律规范中明确导航反馈的三条规则已经在多份文档中作为跨界面定律存在成功的导航应让焦点/光标落在目标处move focus or caret将目标滚动进视口scroll target into view短暂高亮落点目标briefly highlight the landed target。这些规则分布在markdown-editing-spec.md其中EDIT-NAV-FEEDBACK-*已标记为locked即该行为定律已被锁定editor-protocol-matrix.md协议矩阵中为 footnote definition / footnote reference / search target / heading target 四类场景各登记了一行跨界面导航反馈协议editor-behavior-architecture.md架构文档将导航反馈作为共享编辑器领域对待。规范的核心判断是定律已经存在但缺失的是共享运行时契约shared runtime contract。这正是本规范要填补的空缺。1.3 两个真实消费者的差异不能强行抽象成同一个原语契约文档特别强调当前两个消费者相关但不同质脚注导航是选中驱动的它拥有具体的光标/选区点属于 selection-driven caret movement focus/scrollTOC 点击是DOM 滚动优先的先滚动之后才附加块级选中装饰block-selection chrome属于 DOM-scroll-first。因此契约应当标准化两者的交集目标替换、自动清理、节点高亮语义而不是假装两个流程今天已经是同一个原语。设计原则第一条即只标准化当前消费者已经挣得的交集overlap。2. 架构决策契约放哪、怎么分层2.1 ADR永久契约落在platejs/core契约文档给出了三个候选方案并逐一评估方案优点缺点结论Option Aplatejs/core共享导航插件跨界面契约的天然永久归宿与现有共享插件、DOM、node-prop 缝对齐API 对人类与 Agent 最可发现需触碰 core 插件架构过早过度泛化会抬高回退成本✅推荐Option Bplatejs/selection作为主宿主已拥有选区邻近 UI 与 overlay 面语义上错误导航不是选区功能会把廉价节点高亮伪装成选区特性可能把 overlay 假设拖入基础契约❌Option Ctoc/footnote/search各自本地实现每个功能本地 diff 最小必然漂移重复定时器/状态/渲染逻辑长期 DX 与维护最差❌ADR 的最终结论是在packages/core中实现共享导航插件表面shared plugin surface包含共享 transforms 与共享渲染状态注入。决策驱动因素为跨界面复用、低渲染成本、低概念成本、可预测的归属、面向更广目标类型的未来路径。selection将来可以作为可选的 range/overlay 适配器宿主但不是导航契约的主要归属。文档还明确排除了把契约放在floating浮动层或新建独立导航包理由是契约不属于单一功能族、本质上不是 overlay 几何、不单纯是选区问题、且被多个当前与未来界面所需。2.2 两层形态lib 层管契约React 层只做适配规范与契约文档共同强调不能让 React store 悄悄成为真正的契约。因此采用两层结构Lib 层在packages/core内新增一个编辑器作用域的 lib 插件拥有当前导航目标current nav target、请求/脉冲 idnav request id / pulse id、前一目标替换replacement、自动清理定时器auto-clear timerReact 层只提供把活跃导航目标暴露给渲染器与 hooks 所需的薄适配表面。约束条件如果插件/编辑器状态可以干净地驱动inject.nodeProps与渲染 hooks就不要默认新建独立的NavigationFeedbackStore只有真实渲染器约束逼不得已时才引入专用 store。硬性要求必须证明导航目标变化能触发渲染更新使inject.nodeProps无需依赖无关的选区变化即可增删高亮属性。2.3 两类一等消费者模式Phase 1 明确支持两种模式且不允许为了让抽象看起来干净而把 TOC 强扭成脚注的形状Selection-driven navigate选中驱动导航面向脚注跳转这类拥有具体 caret/selection 点的消费者Flash-only target feedback仅闪烁目标反馈面向 TOC 这类应保留当前非文本选区导航行为、但复用共享闪烁计时与替换语义的消费者。3. 渲染策略node 属性优先overlay 是后路3.1 为什么选择>editor.tf.navigation.flashTarget({ target: { type: node, path }, variant: navigated, });editor.tf.navigation.navigate({ target: { type: node, path }, flash: { variant: navigated }, focus: true, scroll: true, select: { anchor: point, focus: point, }, });设计意图非常明确flashTarget(...)是一等公民不是 fallback 辅助函数navigate(...)面向选中驱动流程负责把 select、focus、scroll、flash 协调在一起不要求每个消费者都必须提供选区语义。4.2 初始目标类型target kindsPhase 1A 支持node目标按 Slate path 定位可选但延迟block-id、range、自定义 DOM rect 或虚拟目标明确不加不在早期引入更通用的目标代数target algebra除非确实出现需要它的消费者。这意味着search 被延迟要么团队有意将range提升进目标模型要么证明真实搜索跳转只需要 node 目标语义。5. 精确文件落点与分阶段路线5.1 Core 包内文件清单Lib 插件巷packages/core/src/lib/plugins/packages/core/src/lib/plugins/navigation-feedback/NavigationFeedbackPlugin.tspackages/core/src/lib/plugins/navigation-feedback/index.tspackages/core/src/lib/plugins/navigation-feedback/types.tspackages/core/src/lib/plugins/navigation-feedback/transforms/flashTarget.tspackages/core/src/lib/plugins/navigation-feedback/transforms/navigate.tspackages/core/src/lib/plugins/navigation-feedback/transforms/index.ts并接入packages/core/src/lib/plugins/index.ts与packages/core/src/lib/plugins/getCorePlugins.ts。React 侧巷packages/core/src/react/plugins/packages/core/src/react/plugins/navigation-feedback/NavigationFeedbackPlugin.tspackages/core/src/react/plugins/navigation-feedback/useNavigationFeedback.ts仓库最终落地名为useNavigationHighlight.ts见第 6 节packages/core/src/react/plugins/navigation-feedback/index.ts并接入packages/core/src/react/plugins/index.ts与packages/core/src/react/editor/getPlateCorePlugins.ts。若 node-prop 注入需要可复用 core 帮助函数优先复用既有注入缝packages/core/src/internal/plugin/pipeInjectNodeProps.tsxpackages/core/src/internal/plugin/pluginInjectNodeProps.ts若滚动集成需要共享选项表面可参考packages/core/src/lib/plugins/dom/DOMPlugin.ts。默认分工lib 插件拥有 transforms 与规范契约React 层只拥有渲染面适配器/hook 表面除非渲染管线证明必要否则不让 React store 成为契约本身。5.2 功能包集成点脚注packages/footnote/src/lib/transforms/focusFootnoteDefinition.ts、packages/footnote/src/lib/transforms/focusFootnoteReference.tsTOCpackages/toc/src/react/hooks/useTocElement.ts仓库实际实现位于useContentController.ts见第 6 节搜索延迟直到 range-vs-node 目标语义明确。高亮样式初期应留在应用/编辑器 UI如apps/www/src/app/globals.css不要为了发布包级样式而阻塞 core 插件设计。5.3 分阶段路线图阶段内容关键约束Phase 1Acore 契约 选中驱动消费者lib 插件、React 插件/hook、flashTarget/navigatetransforms、data-nav-target/data-nav-highlightnode-prop 注入、定时器替换/自动清理集成脚注 ref→def 与 def→ref目标类型仅node先在选中驱动消费者上验证契约不把 search 拖入本阶段Phase 1B非选中消费者TOC 作为 flash-first 消费者接入同一契约保留 TOC 现有滚动行为复用共享闪烁计时/替换语义不强制文本选区TOC 也应落光标作为独立 UX 决策不偷偷塞进基础契约Phase 2显式目标模型扩展决定 search 需要range还是仅node如需range按真正的扩展设计仅当 1A/1B 稳定后才做Phase 3加固统一 variant 命名与超时策略确保替换语义确定性增加可见目标反馈的浏览器测试—Phase 4延迟扩展range 目标、selection中的 overlay 适配器、讨论/评论锚点消费者仅按需5.4 测试计划单元 / 包测试core插件在getPlateCorePlugins中注册flashTarget设置目标状态新 flash 替换旧状态自动清理定时器清理目标navigate按顺序执行 selection scroll flashnode-prop 注入正确增删预期 data 属性渲染失效路径无需选区变化即可更新高亮属性。功能消费者脚注 transforms 调用共享导航 API 而非本地持有 flashTOC 点击路径复用共享闪烁计时/替换语义且不强制选区语义。集成测试从一个目标导航到另一个目标时干净地交换高亮内联 void 目标高亮可用块级目标高亮可用。浏览器验证脚注 ref→def、def→ref 可见地闪烁目标TOC 跳转可见地闪烁目标搜索跳转验证延迟。5.5 风险与缓解风险缓解过早的过度泛化抽象只从已挣得的交集起步flashTarget 选中驱动navigate目标仅node渲染层耦合保持样式薄、基于 data 属性core 表面膨胀先只暴露最小的flashTarget/navigateTOC 与脚注并非同一原语在契约中显式声明两种消费者模式功能包仍做本地闪烁显式迁移首批消费者并删除本地高亮逻辑目标状态更新无法干净触发节点树重渲染Phase 1A 中证明渲染失效缝仅在插件/编辑器状态 hooks 无法重绘属性时才引入最小 React store6. 源码印证契约在仓库中的实际落地形态本规范的后续计划文档 docs/plans/2026-04-10-navigation-feedback-path-ref-runtime.md 显示导航反馈在 core 中以运行时PathRef状态落地。当前仓库中该契约已经实现且文件布局与规范中的精确文件落点基本一一对应。6.1 Lib 层packages/core/src/lib/plugins/navigation-feedback/仓库实际文件为NavigationFeedbackPlugin.tstypes.tstransforms/flashTarget.tstransforms/navigate.tstransforms/index.tsNavigationFeedbackPlugin.spec.ts类型定义types.ts中可以看到完整的运行时模型export type NavigationFeedbackTarget { path: Path; type: node; }; export type NavigationFeedbackActiveTarget NavigationFeedbackTarget { cycle: 0 | 1; duration: number; pulse: number; variant: string; }; export type NavigationFeedbackStoredTarget Omit NavigationFeedbackActiveTarget, path { pathRef: PathRef; };值得注意的设计细节存储态用PathRef而非裸Path目标在文档编辑插入/删除后仍能保持位置有效性这是与规范中替换/清理前一目标状态直接对应的实现机制cycle: 0 | 1与pulse脉冲计数每次 flash 递增 pulsecycle pulse % 2用于驱动 CSS 动画的交替触发同一节点连续点击两次也能重新播放动画duration默认 1600msNavigationFeedbackPlugin.ts中options.duration: 1600flashTarget内 fallback 为 800ms。插件通过extendEditorApi暴露查询面、通过extendEditorTransforms暴露命令面// api 面查询 editor.api.navigation.activeTarget() editor.api.navigation.clear() editor.api.navigation.isTarget(path) // transforms 面命令 editor.tf.navigation.clear() editor.tf.navigation.flashTarget(options) editor.tf.navigation.navigate(options)flashTarget的实现要点transforms/flashTarget.ts使用WeakMapSlateEditor, timeout与WeakMapSlateEditor, pulse保存每编辑器的定时器与脉冲计数避免污染编辑器对象新 flash 会先clearNavigationTimeout 清理上一目标的 DOM 属性与pathRef保证确定性替换通过editor.api.toDOMNode(node)拿到目标 DOM 元素直接设置四个属性data-nav-targettruedata-nav-highlight{variant}默认navigateddata-nav-cycle{0|1}data-nav-pulse{n}以及 CSS 变量--plate-nav-feedback-duration: {duration}ms超时后调用clearNavigationFeedbackTarget(editor, pulse)带 pulse 校验——只有脉冲匹配时才清理避免后发的 flash 被先发的定时器误清。navigate的实现要点transforms/navigate.ts按顺序执行select若提供 point/range→focus→scrollIntoView滚动目标点优先级为scrollTargetselect.focusselect.anchorselectpoint editor.api.start(target.path)→flashTarget除非flash: false这正是契约文档中navigate 把 select、focus、scroll、flash 协调在一起的落地。6.2 React 层packages/core/src/react/plugins/navigation-feedback/仓库实际文件为NavigationFeedbackPlugin.tsuseNavigationHighlight.tsNavigationFeedbackPlugin.spec.tsxindex.ts规范草案中的 hook 名为useNavigationFeedback.ts最终落地为useNavigationHighlight.ts这是文档与实现的唯一命名差异。该 hook 供渲染器消费活跃导航目标配合 node-prop 注入缝完成高亮属性的渲染面适配。6.3 脚注消费者选中驱动 navigate在packages/footnote/src/lib/transforms/focusFootnoteDefinition.ts与focusFootnoteReference.ts中脚注跳转已改为调用共享 APIreturn editor.tf.navigation.navigate({ ... });测试 insertFootnote.spec.ts 中也可以看到对editor.api.navigation.activeTarget()的断言如 L307、L324、L363验证了跳转后活跃目标状态确实被设置。6.4 TOC 消费者flash-first在 useContentController.ts 中TOC 点击路径通过editor.tf.navigation.flashTarget(...)复用共享闪烁语义见 L74 附近且不强制文本选区——正是规范 Phase 1B 所要求的保留 TOC 当前滚动行为、复用共享闪烁计时/替换语义、不强制选区。调试记录 2026-04-07-debug-toc-demo-nav-progress.md 证实了浏览器端的实际表现在/blocks/toc-demo点击Benefits of Using TOC后产生了一行aria-currentlocation的目录条目以及一个data-nav-targettrue的标题高亮。6.5 搜索仍在延迟清单搜索跳转的高亮仍处于延迟状态符合规范 Phase 2 的决策——在rangevsnode目标语义明确之前不接入。相关计划如 2026-04-09-editor-behavior-replan-next-batch.md仍将其列为shared navigation feedback for search jumps待办。7. 交接与协作指引契约文档还给出了执行协作的参考建议推理强度by lanecore 契约 包边界为high功能集成、测试与浏览器验证为medium按角色分工architect压测 core/插件归属与 API 形态executor实现 core 插件与首批消费者test-engineer补充单元/集成/浏览器覆盖code-reviewer做最终 API 与分层评审verifier提供完成证据验证路径包测试先行 → 应用集成测试其次 → 浏览器验证最后之后才把契约视为真实。8. 小结把跳转后高亮沉淀为共享编辑器定律回顾2026-04-06-navigation-feedback-spec.md的 Outcome本规范要达成的三项目标在仓库中均有对应EDIT-NAV-FEEDBACK-*成为共享跨界面定律——已写入 markdown-editing-spec.mdlocked与 editor-protocol-matrix.mdTOC、脚注、搜索跳转显式复用同一个瞬态导航反馈原语——脚注与 TOC 已在源码中调用editor.tf.navigation.flashTarget/navigate搜索按计划延迟架构文档将导航反馈视为共享编辑器领域而非每功能各写各的 hack——editor-behavior-architecture.md 承接此定位。对于后续想要扩展新跳转面讨论/评论锚点、自定义大纲、未来的 range 高亮的开发者正确姿势是功能包只负责目标解析resolve target然后调用 core 的editor.tf.navigation.flashTarget(...)或editor.tf.navigation.navigate(...)让共享契约统一处理焦点、滚动、闪烁与状态替换——这就是本文从规范到源码所呈现的完整闭环。输出文章【免费下载链接】plateRich-text editor with AI and shadcn/ui项目地址: https://gitcode.com/GitHub_Trending/pl/plate创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门