Slate v2 装饰与注释(Decorations / Annotations)架构重构:从遗留 decorate 回调走向投影优先的覆盖层系统
Slate v2 装饰与注释Decorations / Annotations架构重构从遗留 decorate 回调走向投影优先的覆盖层系统【免费下载链接】plateRich-text editor with AI and shadcn/ui项目地址: https://gitcode.com/GitHub_Trending/pl/plate导读本文基于 plate 仓库中 Slate v2 研究线对 decorations / annotations 问题簇的完整调研结论系统梳理了旧版 Slatedecorate抽象为何反复制造同一类 bug因为它同时承担了渲染期标记、跨节点覆盖层、外部驱动的临时高亮和注释型锚点四种职责。读者读完本文后将掌握旧装饰系统在语义、拓扑、性能、DOM/选区时机与注释锚点五个维度上的失败模式理解 ProseMirror、Lexical、Tiptap 等编辑器各自的取舍以及最终推荐的装饰与注释分离、共享投影管线的重构方案、React 19.2 运行时姿态与目标 API 形状。本文以研究计划文档 docs/plans/2026-04-14-slate-v2-decorations-annotations-cluster-research.md 为骨架其核心产出是 docs/slate-v2/decorations-annotations-cluster.md文中结论由本地 issue 语料docs/slate-issues、Slate 上游 issue#3383、PR#4993/#4997讨论以及仓库源码共同支撑。为什么装饰与注释值得独立成簇旧版 Slate 的 issue 语料习惯于把 decorations 摊进更大的主题桶——React 运行时、选区、性能、API 易用性——这掩盖了真实模式。跨主题重新扫描后见 docs/slate-issues/issue-clusters.md 中明确标注的 cross-cutting seam发现 decorations 实际在同一时间承担了至少四类不同工作文本上的渲染期标记render-time marks跨节点的视觉覆盖层cross-node visual overlays外部驱动的临时高亮externally-driven transient highlights评论与协作光标所需的注释型锚点annotation-like anchors这种职责过载正是同一族 bug 反复以不同症状出现的原因。结论不是有一个 bug而是有一个糟糕的抽象边界。旧版decorate回调从语法高亮、搜索命中、AI 建议、远程光标、评论、诊断、占位 UI 到审阅锚点全部包办这不算灵活而是一个垃圾抽象garbage abstraction修复方式不是更聪明的回调而是拆分职责。核心问题家族1. 语义坍缩leaf props 过于有损最干净的例证是#3383语义相同但元数据不同的重叠 mark 或 decoration一旦全部拍平进 leaf props 就无法共存。问题不在于高亮很难而在于 leaf 模型只能良好保留正交属性当两个覆盖层对同一 key 携带不同载荷时一个胜出、另一个消亡。相关压力还包括#2564marks 与 inlines 在语义上早已混淆#2465渲染期 mark 的可用性脆弱因为渲染器工作在切分后的 leaves 上而非更丰富的覆盖层模型。仓库中的 packages/slate/src/interfaces/text.ts 直接印证了这一模型Text.decorations(node, decorations)的返回类型是{ leaf, position }[]——装饰被切成叶子片段 位置的扁平行DecoratedRange只是对上游slate类型的透传。结论leaf 切分对基础排版够用但无力保留多个相互独立的覆盖层载荷。2. 范围拓扑装饰想跨越多于一个文本 leaf第二个簇是形状问题而非速度问题。用户期望装饰能够跨兄弟节点、桥接 inline 边界、从高阶节点操作、在不改动文档状态的前提下预览或遮罩内容。相关压力#4392跨节点 decorate#4426范围遮罩range masking#4477协作写作中锚定选区的评论。这些都是同一问题的变体公开的decorate(entry)契约对更丰富的覆盖层行为而言太文本 leaf 形状但又太隐式无法暴露真正的覆盖层/注释模型。一旦覆盖层需要超出给这段 leaf 片段打标记该 API 就不再诚实。3. 性能与失效装饰传播快速爆炸这是最热门的运行时簇。本地 issue 文档已标记#4483动态 decorations 的重渲染成本旧语料中关于嵌套 leaf 失效与大装饰树的笔记。#4993的关键细节在树顶端计算全部装饰再向下传递会让Range.intersection成为瓶颈混合深度树会非常残酷因为每一层都要用大量装饰集与众多后代做相交运算类似代码容器 多行的嵌套结构会把这变成实际上的不可用。#4993认为顶层拍平是一次回归因为它摧毁了旧的只重装饰树中发生变化的部分的行为。#4997的关键细节selector/store 风格订阅可以比纯 context 传播更好地局部化重渲染这在decorate函数本身频繁变化时有帮助但它仍然把decorate放在一个脆弱的位置一个必须与 Slate 协调reconciliation和 DOM 选区修复在时间上完美对齐的 prop。结论这里的性能痛点不是泛泛的装饰很慢而是失效模型之痛invalidation-model pain。4. 选区、IME 与异步时机装饰后的 DOM 与编辑器状态相互漂移这是装饰债从恼人升级为破坏编辑的地方。相关压力#3309装饰后的文本无法被选中#3162decorate IME 输入失步#4712带text字段的装饰范围干扰选区#5987异步 decorate 更新落地时光标跳动#4581删除 decoration/void 后输入在 Firefox 中可能崩溃。这一族问题反复表达同一件事装饰应用改变了 DOM 结构或 leaf 边界选区映射与组合composition时机对此极其敏感外部定时的装饰更新让问题更糟。#4997是最有价值的线程因为它没有停在性能似乎变好了。它发现了更硬的失败模式selector 风格订阅模型看起来很有希望但随后一个真实的 debounced 装饰复现产生了光标跳动、DOM 中出现幽灵纯文本、内部状态损坏。作者的结论很直白当装饰变化由外部驱动、且不与编辑器onChange同步时decorate作为 prop 就是一座脆弱的纸牌屋。运行时契约——装饰、选区协调、外部驱动更新三者之间——在结构上就是脆弱的。5. 注释压力评论与光标不只是更多装饰旧讨论反复汇聚到同一想法从文档结构派生的装饰是一回事外部维护的锚定覆盖层是另一回事。#4477要求注释锚点#4993的讨论明确回溯更早的 annotation 概念并指出装饰并不适合做光标API 相关评论提到 keyed overlays、range refs 与命令式维护的装饰类实体。这就是注释压力而非单纯的装饰压力。有用且必须区分的定义decoration装饰从节点内容或局部结构派生的投影derived projectionannotation注释随时间锚定到某个范围上的持久或外部拥有的锚点。一旦强制二者走同一个decorate漏斗就会得到失效语义模糊、稳定/不稳定 decorate 引用的压力、为协作光标或评论而打的补丁、外部状态变化需要重新广播进树时的时机 bug。注释需要显式的所有权与生命周期语义装饰不会免费给出这些。#4993与#4997实际教会了什么#4993契约本就模糊#4993的真正论点不只是性能而是契约歧义。存在两种互不兼容的预期旧 Slate 式预期节点变化 → 局部重装饰decorate函数引用变化 → 全量重装饰Plate / slate-yjs 时代预期稳定的decorate函数仍应反映变化的外部状态。这才是真正的断裂线。#4993声称强制整棵树顶层重算代价过高且破坏了高效的局部装饰传播同时它也暴露了下游库的合理抱怨如果 Slate 期望decorate按引用失效那么该契约从未被明确说明且对天然保持稳定函数、变化外部状态的框架是敌意的。#4997更快的订阅并不能修复语义错配#4997尝试了聪明方案用 subscription/selector 取代原始 context 搅动只重渲染装饰切片真正变化了的节点。这改善了纯性能故事但异步/防抖装饰更新立刻击穿了它。这个结果很有价值因为它证明问题比用错了重渲染原语更深——更好的订阅机制解决不了外部拥有的装饰时机、选区协调风险、藏在decorate里的注释类覆盖层语义。#4997有用不是因为合并了而是因为它撞到了墙。当前架构判断slate-v2 的方向更接近真相研究结论是明确的不要随便复活遗留decorate语义不要假装评论/光标/锚点能被更好的 leaf 装饰管道解决不要把投影局部装饰和注释锚点再次折叠进一个无差别 API。当前slate-v2方向更符合证据投影局部装饰行为保持狭窄、注释锚点得到显式对待、引擎不会为了讨好遗留decorate的怪癖而被加宽。对应的执行权威文档是 docs/slate-v2/decoration-roadmap.md它把装饰车道锁定为显式、高性能、React 原生的覆盖层架构并明确指出下一阶段重点是 source-scoped按来源作用域失效而不是架构命名或示例。本地 slate-v2 已有的正确直觉好消息是 slate-v2 已经拥有大部分正确直觉只是散落在过多文档中。1. 投影切片projection slices已经胜过第二个装饰模型最重要的本地规则已经写下来projection proof 必须把 range 语义与 React overlay store 分开editable text 应把 leaves 与 projection slices 分开。这是正确的接缝Core 拥有逻辑 range 含义React 拥有订阅广度与切片投递渲染器消费切片而不是再次发明装饰。2. 持久锚点需要 range refs / bookmarks而非回调技巧注释工作已经找到了诚实的底层设施range refs 必须事务感知且默认向内inward。评论、审阅锚点、持久诊断等持久跨度不是最新一次 decorate 函数返回了什么。它们需要 id、生命周期、亲和affinity语义、事务感知的 rebase、提交时发布。这正是 bookmark/range-ref 领域。仓库中的 packages/slate/src/interfaces/location-ref.ts 印证了底层设施RangeRef的affinity类型已支持backward | forward | inward | outward | nullcurrent可随操作变换后随时读取——这是事务感知锚点的直接实现证据。3. 装饰文本改变了 DOM 契约桥接层必须承认本地浏览器证明已经杀死了朴素假设装饰后的多 leaf 文本需要累计偏移映射装饰后的剪贴板与选中文本助手应剥离仅渲染包装与 FEFF。重写不能止步于React 少渲染。它还必须保持诚实的 Slate 偏移 ↔ DOM 偏移映射、忽略仅渲染包装的剪贴板语义、忽略 FEFF 或占位符垃圾的选中文本语义。配套证据还有 异步 decorate 刷新必须导出 DOM 选区以及代码块场景下的 语言切换必须触发 redecorate、格式变化必须重建代码行。4. 巨型文档要 corridor occlusion而非虚假的基础分块本地巨型文档姿态已优于旧 Slate 思维相关参考见 docs/slate-v2/references/chunking-review.md 与 docs/slate-v2/references/replacement-family-ledger.md。正确默认值是selector-first 运行时、活跃编辑走廊corridor、走廊外遮蔽occlusion、重型覆盖层与文本树重渲染分离。跨编辑器扫描多数编辑器设计文档在这里变弱要么盲目崇拜 ProseMirror要么从五个仓库各挑几个漂亮术语就自称战略。诚实的取舍更窄。ProseMirror值得偷师decoration.ts 中显式的装饰种类与映射纪律inline decorations、widget decorations、node decorations、跨事务映射、分层DecorationSetselection.ts 与 history 中严肃的选区 bookmark 处理。需要拒绝的让 PM 装饰引擎成为所有覆盖层用例的最终真理。原因它擅长映射的文档附着覆盖层但在产品需要更丰富的重叠审阅/建议 UI 时偏弱——Tiptap 自己的文档都承认重叠建议被 ProseMirror 装饰限制挡住了。所以偷的是显式覆盖层类型、映射集合、bookmarks而不是所有东西都用同一个装饰引擎。Lexical值得偷师useReactDecorators.tsx 中通过useSyncExternalStore实现的 React 侧装饰订阅显式的节点装饰表面LexicalDecoratorNodeLexicalOnChangePlugin中 dirty-set 感知的更新过滤useYjsCollaboration中带独立光标容器的光标覆盖层分离。需要拒绝的假装DecoratorNode是通用文本装饰答案。Lexical 装饰擅长节点级 React 门户与嵌入式 UI但不是通用重叠 inline 装饰 注释锚点底层。偷的是门户/widget 层、订阅/store 纪律、dirty-tag 过滤、文本 leaf 模型之外的协作光标 UI而不是让每个覆盖层都成为 decorator node。Tiptap值得偷师残酷的产品打包区分——comments 作为独立功能系统、mark views 作为编辑器内显式渲染表面。需要拒绝的依赖 ProseMirror 装饰限制来对待审阅/建议 UX——其 AI 建议文档明确说重叠建议因 ProseMirror 装饰限制无法显示。这对 Tiptap 的产品面没问题但不是全新重写的正确北极星。偷的是注释是真正的注释产品而不是可爱的高亮与mark views 与输出序列化分离拒绝的是把重叠覆盖层不可用当成引擎法则。Premirror Pretext值得偷师snapshot → measure → compose → viewport 拆分、把布局作为独立确定性模型、为页面 chrome 与诊断使用显式 widget 装饰、文档位置与组合布局之间的选区投影与映射。需要拒绝的把页面布局测量拖进核心编辑热路径。这条车道对分页、页面 chrome、屏外规划与未来虚拟化是金子但对普通 inline 装饰语义是过度设计。偷的是布局是派生状态、布局有自己的失效与剖析模型、覆盖层可以从组合输出投影而不拥有文档语义。use-editable、rich-textarea与edix它们主要让你对这个空间的下限保持诚实。它们证明了contenteditable 表面需要突变回滚与选区恢复textarea 背景覆盖层非常适合纯文本装饰、自动补全、菜单与 IME 安全高亮小声明式 contenteditable 状态管理器在模型简单、选区快照显式时可行。都不适合作为带持久锚点的结构化富文本引擎的脊柱值得偷的是小表面教训、显式选区快照、DOM 回滚偏执与 IME 尊重。TanStack DB这不是编辑器仓库这正是它有用的原因。值得偷师规范化集合心智normalized collections、useLiveQuery.ts 通过useSyncExternalStore的 live-query 订阅设计、仅在版本或集合身份变化时重建稳定快照。这是注释与覆盖层索引远优于又一个临时 React context 堆的心智模型。偷规范化注释集合、可见范围/选中线程/块局部覆盖层的 live queries不偷面向普通编辑器消费者的数据库形状公共 API。EditContext这是未来平台压力不是当下交付指南。概念上值得偷师显式共享文本缓冲、显式updateSelection(...)、显式updateLayout(...)、通过textformatupdate的显式 IME 装饰请求、以及外部更新在模型/布局通道诚实时不必然取消组合的性质——这正是旧 Slate 从未拥有的方向。不偷今天就硬依赖 EditContext只偷架构形状。候选地图其余部分docs/analysis/editor-architecture-candidates.md 中其余条目仍然重要只是对本重写而言不那么直接VS Code LSP偷服务边界——diagnostics、语义分析、code actions、线程/审阅智能可以活在核心编辑器引擎之外以注释或诊断源的身份重新进入不偷桌面应用级渲染模型。urql偷可组合源管道、缓存与派生层、可扩展更新流的心智作为多个覆盖层源组合而不变成单体 Monolith 的灵感。Open UI / 更丰富的文本字段当作平台压力而非实现指导。平台本身仍缺乏对更丰富文本字段的一致答案任何严肃编辑器架构都仍需显式处理文本缓冲所有权、选区所有权、绘制期覆盖层与 IME 交互。短清单指向同一方向Tiptap 的产品打包、ProseMirror 的严格映射覆盖层语义、Lexical 的 React/运行时订阅、Premirror/Pretext 的布局分离、use-editable/rich-textarea/edix的轻量小表面教训、TanStack DB 的规范化响应式索引、VS Code/LSP 的服务边界、EditContext/Open UI 的未来平台形状。深入一层的黄金洞见1. ProseMirror 真正的赢点是子作用域传播而不只是DecorationSetdecoration.ts 中最强的点不是它有装饰而是forChild(...)只把相关的相交 inline 装饰加上子拥有的子树集合交给每个子节点。这与旧 Slate 最糟的行为完全相反没有贯穿整棵树的顶层扁平装饰列表而是子局部重叠切片与分层覆盖层所有权。这个具体想法值得偷。2. ProseMirror 的 bookmarks 正是旧 Slate 缺失的持久性界线bookmarks 是独立于文档的映射选区无需挂载 DOM 即可映射、之后对当前文档解析、保存的是持久表示而非陈旧的已解析句柄。这是持久锚点的正确心智模型也是注释应当骑乘 bookmark/range-ref 语义的原因。3. Lexical 已经把 Slate 一直揉在一起的三个职责拆开了MarkNode是带 id 的 inline 包装可横跨文本、inline 元素甚至 inline 装饰节点DecoratorNode面向节点级渲染 UIuseYjsCollaboration把远程光标 UI 放在独立 DOM 容器。胜出架构不是一个覆盖层系统而是inline 身份承载包装、锚定 widget/portal 节点、以及适当的全外部覆盖层 chrome。4. Lexical 的 dirty sets 与 tags 是正确的失效词汇失效应该以 dirty leaves、dirty elements 与 tags 来表述而不是某个回调变了祝你好运。对 Slate v2 覆盖层意味着事务元数据携带失效提示、覆盖层源声明什么会使自己变脏、React 订阅消费狭窄的失效作用域。5. Tiptap 无意中证明了评论与建议应该保持分离Tiptap 文档同时说了两件有趣的事comments 支持重叠线程与丰富产品行为而作为 ProseMirror 装饰渲染的 AI 建议因装饰引擎限制无法重叠。如果系统需要持久重叠、工作流、线程元数据、程序化 CRUD它要的是注释语义如果只需要临时预览、样式、类似 diff 的视觉投影它往往可以活在装饰语义里。试图用一个机制强行承载两者正是编辑器 API 变蠢的地方。6. VS Code 证明严肃编辑器会激进地拆分视觉通道VS Code 的教训不是抄 Monaco而是diagnostics 变成 inline class 装饰、overview ruler 标记、minimap 标记、sticky 策略与 z-index 的组合ghost text 用 injected text 做 inline 预览、用 view zones 做附加行injected text 拥有自己的光标停止语义。即使是成熟文本编辑器也不信任一个range decoration原语包办一切而是拆成 inline 插入文本、行/块附加 zone、ruler/minimap/gutter 的带外诊断通道。这几乎完美映射到提议的 Slate v2 拆分文本装饰、widgets/chrome、带外诊断通道。7. Premirror 的失效计划纪律是巨型文档的超能力真正的好东西不是分页本身而是坚持显式失效范围、双向位置映射、每事务剖析计数器、按区域增量重组。严肃的覆盖层运行时在巨型文档上就应这样思考知道什么变了、哪些范围或语义岛屿受影响、哪些视口/布局区域需要重算、度量成本。8. EditContext 暴露了一个缺失的覆盖层车道IME 拥有的格式化IME 组合格式化是自己的通道——textformatupdate不是评论、不是语法高亮、也不是普通搜索高亮而是平台驱动的瞬态输入法视觉状态。面向未来的设计应为平台/IME 覆盖层车道留出独立优先级与生命周期规则而不是假装它们是普通注释。React 19.2 运行时姿态React 19.2 不会神奇地解决编辑器架构但确实让正确形状更清晰。1.useSyncExternalStore应作为覆盖层订阅骨干这是 React 面向覆盖层状态的明确赢家它是官方订阅 hook匹配本地投影 store 方向也适合规范化注释/装饰索引。文档中的重要警告getSnapshot必须返回不可变的缓存快照subscribe身份变化会导致重新订阅若外部 store 在 Transition 期间变化React 可能重启并把该更新当作阻塞应用不鼓励从外部 store 值挂起。由此得到一条硬规则活跃编辑走廊不能依赖懒加载/挂起的覆盖层 store 读取或不稳定快照。2.startTransition只用于非紧急覆盖层工作Transition 更新不阻塞、可被中断、不能控制文本输入。因此输入、DOM 选区同步、光标移动、IME 组合处理、光标附近的紧急可见覆盖层变化必须留在 transition 之外。transition 的好用途重建屏外覆盖层索引、重算侧边栏模型、过滤线程列表、加载/重投影不可见页面、昂贵的诊断面板。3.useDeferredValue用于滞后视图而非编辑器真理适用于陈旧但可用的 UI线程侧边栏、搜索结果列表、diff/建议面板、minimap、inspector 面板。绝不用于光标周围的真实活动文本装饰、已提交的选区真理、DOM 桥映射。4.useEffectEvent完美适配带最新配置的桥接监听器适用于从 effects 触发、需要最新 props/state、但不应让 effect 本身重新订阅的逻辑selectionchange 监听回调、resize/scroll/layout 桥接回调、编辑器订阅周围的 analytics/logging hooks、绑定编辑器状态的副作用通知。不要滥用它作为真实依赖的逃生舱。5.Activity是巨型文档与侧边栏工具不是编辑原语Activity hidden在保留状态的同时清理 Effects 并降低隐藏子树的优先级非常适合注释侧边栏、审阅面板、隐藏页面 chrome 表面、预渲染的下一面板或标签页。但它也意味着隐藏子树的订阅消失了。所以把真理之源覆盖层 store 放在隐藏 Activity 子树之外用 Activity 保留 UI 状态而不让所有 Effects 存活不要把活跃编辑走廊藏在 Activity 里还指望输入健康。DX 非协商项如果这次重写真想做好DX 门槛必须残酷。API 不能要求用户靠交换函数身份让装饰刷新为正确性构建 WeakMap 缓存猜测稳定回调是否等于陈旧输出手动把范围扇出到文本 leaves为了正确复制文本而理解 DOM 包装泄漏。API 应当给用户显式源注册显式刷新语义显式注释 CRUD瞬态与持久覆盖层的明显区分局部切片的直白订阅 hooks在大型文档上不会拖垮性能的稳定默认值。如果消费者在能高亮搜索结果之前要先学五个历史 footgun这个 API 就是坏的。性能非协商项必须要有分层或索引化传播而非扁平自顶向下扫描来自事务与源刷新的显式失效作用域块/文本运行时 id 索引重叠友好载荷存储活跃走廊优先级屏外遮蔽与延迟工作剖析计数器与冻结的基准车道。绝不能有按回调身份大范围重渲染默认全文档重算摧毁多重性的 leaf-prop 拍平强制所有覆盖层 UI 穿过文本 leaves隐藏侧边栏或页面意外地让昂贵订阅保持存活。何时停止研究新的一轮研究不再改变架构形状、只会用不同仓库吉祥物复述同一结构时就该停止——这条线基本已经到了。已经收敛的持久锚点需要 bookmark/range-ref 语义瞬态覆盖层需要投影切片与窄订阅widget/chrome UI 需要自己的车道失效必须显式巨型文档需要 corridor/region 规划而非全树重绘IME/输入状态是独立严肃子系统。这些已足够开始设计。进一步研究不太可能改变的拆分装饰与注释的必要性widget/chrome 层必要性显式失效与 range/bookmark 持久性必要性selector-first React 订阅必要性。仍未知、但应在设计中而非研究中回答的精确公共 API 名称注释是专用 store 还是编辑器拥有的注册表源刷新作用域如何表达widget/chrome 条目按 block-key、runtime-id-key 还是两者遗留decorate的迁移适配器长什么样哪些车道默认紧急 vs transition/deferred。推荐做法本趟之后停止广泛研究做最后一轮设计——一份书面架构规格、精确类型形状与所有权边界、一两个薄原型、提前冻结基准车道。只有原型暴露矛盾时才针对该特定矛盾重开研究。绝对最佳重写方案短答两者都做——Decorations与Annotations——但绝不是一个 API 两个营销名。它们共享投影管道不共享所有权语义。1. Core 拥有逻辑范围与持久锚点Core 应拥有逻辑Range含义projectRange(editor, range)或等价的纯投影入口事务感知的 range refs / bookmarks锚点 rebase 与亲和策略。Core 不应拥有React 订阅、DOM 绘制覆盖层、视口剔除、页面布局。这让引擎保持 document-first。2. 装饰是派生的、瞬态的、重叠友好的装饰应意味着从已提交快照状态或外部状态派生、瞬态、廉价丢弃与重算、重叠友好、不为持久 id 提供真理。支持语法高亮、搜索命中、诊断、拼写检查式/审阅式临时范围、选区派生高亮投影。不应要求对象拍平 leaf props——逻辑装饰载荷必须保留多重性两个高亮叠在同一跨度上系统应持有两个高亮而不是拍平成唯一胜者。3. 注释是持久的锚定实体注释应意味着稳定 id、元数据、显式生命周期、由 range ref/bookmark 语义支撑的锚点、经事务 rebase、未挂载也可解析。支持评论与线程、远程光标与选区、需要身份与工作流的审阅建议、持久诊断、书签或用户拥有的锚点。这不是可选项——评论不只是有观点的装饰文本。4. 两者都应喂给同一个投影运行时共享层应接受逻辑范围或锚点解析投影为按稳定运行时 id 键控的运行时局部切片按文本运行时 id、块运行时 id、以及可能更高的语义岛屿 id 索引为已挂载消费者暴露窄订阅。即顶部语义分离、底部共享投影/索引管道。5. 增加第三层widgets / portals / chrome文本切片不够承载一切。还需要一等 widget/chrome 层评论按钮、选区 affordances、远程光标标签、审阅气球、诊断 popover、页面 chrome 与断页标记。ProseMirror widget 装饰、Lexical decorator portals、Premirror 页面 chrome 都在说同一件事某些 UI 锚定到文档但不应建模为 inline 文本样式。因此重写至少应有三个渲染层文本装饰、持久注释、锚定 widgets/portals/chrome。6. React 运行时应 selector-first 且索引驱动不要把覆盖层数组传下树不要失效巨型 context。要做useSyncExternalStore或等价订阅语义、稳定快照读取、按运行时 id 订阅、dirty 作用域失效、为聚合视图提供可选派生 selector。本地投影证明与 Lexical 装饰订阅模型在此对齐TanStack DB 是更好的 store 心智模型。7. 失效必须显式旧decorate的歧义是毒药。新设计应直说节点变化使局部派生投影失效显式源刷新使声明的作用域失效注释变更只重投影受影响的锚点全文档重算允许但绝不隐式发生。外部状态装饰需要显式刷新路径而不是也许稳定函数身份意味着全量刷新——这个歧义该死。8. 巨型文档姿态corridor 优先虚拟化可选重写在任何虚拟化幻想之前就应擅长巨型文档。默认姿态活跃编辑走廊、局部覆盖层订阅、走廊外遮蔽、延迟屏外覆盖层投影、语义岛屿而非盲目子桶。虚拟化随后锚点活在已挂载 React 节点之上、注释在屏外依然有效、覆盖层索引无需物化整个文档即可回答视口查询、页面布局或屏外规划可消费同一锚点/投影数据。Premirror/Pretext 在此相关因为它们证明布局可以是派生模型但它们不是过度复杂化普通编辑的借口。9. 剪贴板与 DOM 桥契约保持严格装饰与注释绝不允许再次腐蚀桥接层。保持本地规则仅渲染包装不泄漏进剪贴板语义DOM 偏移映射在切分 leaves 上累计零宽/占位符哨兵不泄漏进选中文本真理选区协调以已提交语义为准而非碰巧在场的包装 DOM。如果重写变快了但复制/选中/IME 再次变假就是失败。10. 兼容性策略不要制造decorate(entry) 2.0。做法为遗留decorate保留窄兼容适配器把它归类为投影源为遗留外部状态调用者提供显式刷新 hooks不把它作为新架构文档中的首选表面。v2 首选表面应谈论装饰源decoration sources、注释 storeannotation stores、投影运行时projection runtime、widget 层——而不是一个魔法回调。推荐目标形状CoreEditor.projectRange(editor, range)、Editor.projectRanges(editor, ranges)、事务感知rangeRef/bookmark API、注释锚点 rebase 原语。React 运行时createSlateProjectionStore(editor, options)、useTextProjections(runtimeId, layer?)、useBlockProjections(runtimeId, layer?)、useAnchoredWidgets(runtimeId | blockId)、显式refresh(sourceId, scope?)。Decorations注册派生源源返回逻辑范围 载荷 层 优先级重叠是一等公民。Annotations带 id 与元数据的 CRUD store底层用 bookmarks/range refs可解析为切片与 widgets。Widgets通过 portals 或显式 chrome 层渲染的锚定 UI 条目。最终建议若目标是绝对最佳重写保留投影局部装饰增加一等注释让它们成为共享投影管道上的独立系统增加 widget/chrome 层而非把一切塞进 inline leaves让失效显式让订阅索引驱动让巨型文档策略 corridor-first 而非 chunk-first。这样才能诚实覆盖语法高亮、搜索、诊断、评论、追踪审阅建议、远程光标、持久锚点、巨型文档、未来虚拟化与未来页面布局而不假装一个回调应该掌管整个该死的东西。工作簇摘要遗留装饰系统是一个混合抽象覆盖渲染期标记、跨节点覆盖层与注释型锚点并且沿四个轴失败语义损失、失效成本、DOM/选区时机脆弱性、以及持久锚点缺失所有权语义。深度阅读与源码锚点本地 issue 语料 docs/slate-issues/issue-clusters.mdcross-cutting seam 章节、docs/slate-issues/requirements-from-issues.md、docs/slate-issues/open-issues-ledger.md、docs/slate-issues/open-issues-dossiers/4541-4392.md、docs/slate-issues/open-issues-dossiers/3313-2733.md、docs/slate-issues/open-issues-dossiers/5994-5918.md上游线索Slate issue#3383同语义重叠载荷有损、PR#4993契约歧义 自顶向下失效爆炸、PR#4997selector 订阅改善性能但仍会在异步/外部 redecorate 时机下崩溃本地 v2 设计压力 docs/slate-v2/decorations-annotations-cluster.md本簇完整成文版、docs/slate-v2/decoration-roadmap.md、docs/slate-v2/references/chunking-review.md、docs/slate-v2/references/replacement-family-ledger.md、docs/slate-v2/references/slate-batch-engine.md、docs/analysis/editor-architecture-candidates.md解决方案级证据 投影证明必须拆分 range 语义与 React overlay store、range refs 必须事务感知且默认向内、editable text 拆分 leaves 与 projection slices、装饰后多 leaf 文本需要累计偏移映射、装饰剪贴板与选中文本助手剥离渲染包装与 FEFF、异步 decorate 刷新必须导出 DOM 选区研究归档 docs/research/sources/editor-architecture/decorations-annotations-overlay-corpus.md含 ProseMirror/Lexical/Tiptap/Premirror/VS Code 等外部仓库的逐文件锚点、docs/research/systems/editor-architecture-landscape.md、docs/research/systems/slate-v2-overlay-architecture.md源码实现证据 packages/slate/src/interfaces/text.tsText.decorations返回{ leaf, position }[]切片印证 leaf-prop 拍平模型、packages/slate/src/interfaces/location-ref.tsRangeRef的inward/outwardaffinity 与事务变换印证持久锚点底层【免费下载链接】plateRich-text editor with AI and shadcn/ui项目地址: https://gitcode.com/GitHub_Trending/pl/plate创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考