新闻详情

新闻详情

首页 / 资讯中心 / 详情

BlockNote 剪贴板快照深度解析:跨父子边界选择(childrenToNextParent)的 Markdown 复制输出

发布时间:2026/9/25 6:11:22来源:尧图网络
BlockNote 剪贴板快照深度解析:跨父子边界选择(childrenToNextParent)的 Markdown 复制输出
前端富文本UI组件AI 应用【免费下载链接】BlockNoteA React Rich Text Editor thats block-based (Notion style) and extensible. Built on top of Prosemirror and Tiptap.项目地址https://gitcode.com/gh_mirrors/bl/BlockNote点击查看免费下载本篇文章以 BlockNote 仓库中 tests/src/unit/core/clipboard/copy/snapshots/text/plain/childrenToNextParent.md 这一测试快照文件为切入点围绕它所属的复制copy测试体系完整讲解 BlockNote 在用户选中嵌套子块 其后的兄弟父块这一跨父子边界范围时text/plain剪贴板数据Markdown 形式是如何生成的。读完本文你将掌握 BlockNote 复制测试的完整链路测试用例定义 → 编辑器初始化 → 选区构造 → 片段序列化 → 快照断言理解嵌套块复制时 Markdown 输出每块一行、块间空行分隔的底层原理并能够依据快照文件反推、验证 BlockNote 的复制行为。快照文件是什么一个 Markdown 剪贴板输出的黄金基准打开 childrenToNextParent.md文件内容非常简短Nested Paragraph 1 Nested Paragraph 2 Nested Paragraph 3 Paragraph 2它不是一个普通文档而是BlockNote 复制测试的期望输出快照snapshot当用户在编辑器内复制一段跨越嵌套层级的选择范围时写入系统剪贴板text/plain数据应恰好等于上述文本。快照文件由 vitest 的toMatchFileSnapshot断言自动比对见 copyTestExecutors.ts任何复制逻辑的改动导致输出变化测试都会失败并提示差异从而锁定剪贴板行为不被无意破坏。与它同目录的 HTML 快照 childrenToNextParent.html 记录了同一选择范围在text/html剪贴板中的数据pNested Paragraph 1/p pNested Paragraph 2/p pNested Paragraph 3/p pParagraph 2/pHTML 与 Markdown 两种快照一一对应共同构成同一选区、多格式输出的验证矩阵。测试用例定义childrenToNextParent 的文档与选区快照对应的测试用例定义在 copyTestInstances.ts 中名为childrenToNextParent。它先用PartialBlock数组描述测试文档结构{ testCase: { name: childrenToNextParent, document: [ { type: paragraph, content: Paragraph 1, children: [ { type: paragraph, content: Nested Paragraph 1 }, { type: paragraph, content: Nested Paragraph 2 }, { type: paragraph, content: Nested Paragraph 3 }, ], }, { type: paragraph, content: Paragraph 2, }, ], getCopySelection: (doc) { const startPos getPosOfTextNode(doc, Nested Paragraph 1); const endPos getPosOfTextNode(doc, Paragraph 2, true); return TextSelection.create(doc, startPos, endPos); }, }, executeTest: testCopyHTML, }该文档对应一棵典型的 Notion 风格嵌套树顶级块Paragraph 1父块内含三个子块Nested Paragraph 1/2/3顶级块Paragraph 2父块的兄弟块。选区selection跨越了父子边界起点是第一个嵌套子块Nested Paragraph 1的文本开头终点是顶级兄弟块Paragraph 2的文本末尾。这就是测试名 childrenToNextParent子块到下一个父块的含义——它专门验证选中内容同时覆盖某父块的多个子块与下一个父块本身这一场景确保复制出的块列表结构正确、顺序正确、不丢块。选区的定位依赖测试工具函数 getPosOfTextNode默认返回目标文本节点前的位置传true时返回节点之后的位置pos node.nodeSize以此构造TextSelection.create(doc, startPos, endPos)。该函数通过 ProseMirror 的doc.descendants遍历查找文本内容完全匹配的节点。测试执行管线从文档到快照的三步链路整个复制测试由 runTests.test.ts 驱动。它对copyTestInstancesHTML与copyTestInstancesMarkdown两组用例分别运行Copy tests (HTML)与Copy tests (Markdown)两组 describe。其中 Markdown 组的关键在于copyTestInstancesMarkdown并非重新定义文档而是直接复用 HTML 组的同一批测试用例仅把执行器换成testCopyMarkdown见 copyTestInstances.ts——同一选区在两种剪贴板格式下被分别验证。testCopyMarkdown执行器copyTestExecutors.ts的调用链如下export const testCopyMarkdown async (editor, testCase) { initTestEditor(editor, testCase.document, testCase.getCopySelection); const { markdown } selectedFragmentToHTML(editor.prosemirrorView, editor); await expect(markdown).toMatchFileSnapshot( ./__snapshots__/text/plain/${testCase.name}.md, ); };三个步骤环环相扣初始化编辑器与选区initTestEditor 重置 mock 块 ID 计数器__TEST_OPTIONS.mockID 0保证快照中块 ID 稳定可复现通过editor.replaceBlocks载入测试文档再在editor.transact事务中调用getCopySelection(tr.doc)设置选区生成剪贴板数据调用核心 APIselectedFragmentToHTML取出其中markdown字段快照断言toMatchFileSnapshot将实际输出与__snapshots__/text/plain/${name}.md比对即与本文主角childrenToNextParent.md比对。底层原理Markdown 剪贴板数据如何从选区产生selectedFragmentToHTML定义于 copyExtension.ts它一次生成三种剪贴板数据返回{ clipboardHTML, externalHTML, markdown }字段用途生成方式clipboardHTML写入blocknote/htmlBlockNote 内部格式ProseMirror 默认剪贴板序列化view.serializeForClipboardexternalHTML写入text/html外部通用 HTMLfragmentToExternalHTML走外部 HTML 导出器markdown写入text/plain纯文本由externalHTML经cleanHTMLToMarkdown转换其中markdown的生成遵循两条规则普通场景cleanHTMLToMarkdown(externalHTML)—— 先由外部 HTML 导出器把选中片段序列化为块级 HTML再转换为 Markdown纯代码块场景当选区完全位于meta.code true的块内如 codeBlock时直接取doc.textBetween($from.pos, $to.pos)的原始文本避免 Markdown 围栏fences和反斜杠转义残留注释见 copyTestInstances.ts。随后实际写入剪贴板的动作发生在 copyToClipboardevent.preventDefault()阻止浏览器默认行为依次setData(blocknote/html, clipboardHTML)、setData(text/html, externalHTML)、setData(text/plain, markdown)。因此本快照文件实质上就是用户按 Ctrl/CmdC 时系统剪贴板text/plain数据的精确还原。为何输出是每块一行 空行分隔markdown的生成链路是externalHTML → cleanHTMLToMarkdown。cleanHTMLToMarkdown 做了两件事移除EMPTY_BLOCK_PLACEHOLDER——外部 HTML 导出器会用占位字符填充空的内联内容块以保证 HTML 往返不丢块但 Markdown 不需要它必须剔除否则会出现幽灵字符源码注释明确说明这一点调用htmlToMarkdown把块级 HTML 转成 Markdown。对于本例这种全部由普通段落paragraph组成的选中范围p.../p会被转换为各自独立的 Markdown 段落段落之间以空行分隔于是得到快照中的 4 行文本 3 个空行。这里没有任何缩进或列表标记因为选中块均为顶层级别的普通段落——注意Nested Paragraph 1/2/3虽是Paragraph 1的子块但在 plain/text 序列化中它们被扁平化为独立的 Markdown 段落层级信息由块结构承载而非缩进符号表达。这正是 Markdown 剪贴板输出与文档树层级之间的关键差异点。横向对照相邻用例验证嵌套行为同目录下另两个快照可作为对照进一步验证跨父边界选择的序列化行为childrenToNextParentsChildren.md 对应 childrenToNextParentsChildren 用例Paragraph 2也有自己的三个子块Nested Paragraph 4/5/6选区覆盖全部六个子块加Paragraph 2输出为 7 段连续文本同样块间空行分隔childToParent.md 对应childToParent用例选区从父块Paragraph 1到第一个子块Nested Paragraph 1验证的是反向父到子边界。三个用例共同覆盖了子→子multipleChildren、父→子childToParent、子→下一个父childrenToNextParent、子→下一个父的子childrenToNextParentsChildren这四种嵌套选区组合构成完整的嵌套复制验证矩阵。如何运行与复现该快照验证快照所属测试运行在tests/工作区中。仓库采用 pnpm workspace 管理见 pnpm-workspace.yaml核心包为blocknote/corepackages/core测试通过 vite.config.ts 配置。可执行以下命令复现# 在仓库根目录安装依赖若尚未安装 pnpm install # 运行复制相关单元测试含 HTML 与 Markdown 两组快照断言 pnpm --filter blocknote/test run test -- src/unit/core/clipboard/copy运行后 vitest 会逐条执行runTests.test.ts中的用例将selectedFragmentToHTML实际生成的 markdown 与 childrenToNextParent.md 等快照比对。若复制逻辑发生变化导致输出与快照不一致测试会失败并生成.new后缀的差异文件便于开发者审查是否符合预期后再决定是否更新快照。结语从一行快照反推整个复制子系统childrenToNextParent.md表面上只是 7 行纯文本但它浓缩了 BlockNote 剪贴板子系统的一条完整验证链测试用例copyTestInstances.ts定义文档与跨父边界选区 → 执行器copyTestExecutors.ts初始化编辑器并调用核心 API → copyExtension.ts 的selectedFragmentToHTML产出三格式数据 → markdownExporter.ts 的cleanHTMLToMarkdown完成 HTML→Markdown 转换 → 快照锁定结果。阅读这类快照时建议始终对照其 HTML 同名快照与测试用例中的document/getCopySelection即可快速还原什么样的选区产生什么样的剪贴板输出这是理解 BlockNote 数据序列化行为最高效的入口。赞分享前端富文本UI组件AI 应用【免费下载链接】BlockNoteA React Rich Text Editor thats block-based (Notion style) and extensible. Built on top of Prosemirror and Tiptap.项目地址https://gitcode.com/gh_mirrors/bl/BlockNote点击查看免费下载相关推荐BlockNote 剪贴板复制测试深度解析text/plain 快照如何保证嵌套块复制后的 Markdown 输出BlockNote 剪贴板复制测试深度解析text/plain 快照如何保证嵌套块复制后的 Markdown 输出 BlockNote 在复制/剪切时会在剪贴前端富文本UI组件AI 应用BlockNote 嵌套块复制的剪贴板输出解析从 childrenToNextParentsChildren 测试快照看 text/plain Markdown 链路BlockNote 嵌套块复制的剪贴板输出解析从 childrenToNextParentsChildren 测试快照看 text/plain Markdow前端富文本UI组件AI 应用BlockNote 代码块部分选择的剪贴板复制text/plain 快照与源码级解析BlockNote 代码块部分选择的剪贴板复制text/plain 快照与源码级解析 BlockNote 是一款基于 ProseMirror 与 Tiptap前端富文本UI组件AI 应用上一篇如何快速获取抖音直播数据DouyinLiveWebFetcher完整实战指南下一篇Origami Simulator 实时折纸模拟从打开浏览器到折出立体模型创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Atlas 300V Pro 24G部署YOLO全攻略:从模型转换到性能调优 2026/9/25 6:54:39

Atlas 300V Pro 24G部署YOLO全攻略:从模型转换到性能调优

作为常年跟边缘计算设备打交道的人,这两年被问得最多的硬件之一,就是昇腾系列的Atlas 300V Pro 24G。尤其是最近,社区里关于“Atlas 300V Pro 24G到底是不是运算加速卡”“怎么在这卡上部署YOLO模型”的讨论明显多了起来。很多人第一次接触这…

阅读更多 →
AIMLInterviews 指南:ML 系统设计中的非结构化数据预处理——文本、图像、视频全流程拆解 2026/9/25 6:54:39

AIMLInterviews 指南:ML 系统设计中的非结构化数据预处理——文本、图像、视频全流程拆解

示例工程教程人工智能 【免费下载链接】AIMLInterviews This repo is meant to serve as a guide for Machine Learning/AI technical interviews. 项目地址: https://gitcode.com/gh_mirrors/ma/AIMLInterviews 点击查看 免费下载 在 ML System Design 面试中&am…

阅读更多 →
腾讯混元图像编辑深度拆解:局部重绘与多轮迭代实操指南 2026/9/25 6:54:39

腾讯混元图像编辑深度拆解:局部重绘与多轮迭代实操指南

1. 腾讯混元模型图像编辑能力深度拆解1.1 这个模型到底能做什么腾讯混元模型支持图像编辑这件事,在圈子里其实已经不算新闻了。但真正上手用过的人,和只在热搜上扫过一眼的人,理解深度完全不在一个层面。我前后花了大概三周时间,把…

阅读更多 →
创维E900-S刷机全攻略:MSO9280芯片TTL线刷与救砖指南 2026/9/25 6:54:38

创维E900-S刷机全攻略:MSO9280芯片TTL线刷与救砖指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Atlas 300V 24G部署YOLOv5:从环境搭建到推理调优全指南 2026/9/25 6:54:32

Atlas 300V 24G部署YOLOv5:从环境搭建到推理调优全指南

最近项目里拿到一块 Atlas 300V 24G,身边不少同事第一反应是“这卡到底是不是运算加速卡?能不能直接拿来跑 YOLO?”说实话,我第一次接触昇腾推理卡时也是一头雾水,性能和显存看起来都不错,但部署流程跟 GPU…

阅读更多 →
【Dify】文本卡片自动生成应用 2026/9/25 6:54:32

【Dify】文本卡片自动生成应用

文本信息视觉化已成为内容创作者和学习者提升效率的重要工具。将原始文本转化为结构化、高质量的卡片图片,能够降低美工门槛,让知识与创意实现快速传播。 本文介绍一种基于Dify智能体的文本卡片自动生成与迭代工作流,涵盖模型协作、流程节点拆解及多场景应用,助力自学者实…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉