菜单

渲染管道

相关源文件

本文档描述了Joplin中的渲染管道,该管道负责将笔记内容从其存储格式(Markdown或HTML)转换为用户在笔记查看器中看到的视觉表示。该管道处理初始渲染和交互式更新,包括编辑器和查看器之间的同步滚动。

有关编辑器本身的信息,请参阅富文本编辑器Markdown编辑器。有关跨平台编辑器组件,请参阅跨平台编辑器组件

概述

渲染管道将内容从笔记编辑器转换为最终显示的格式,同时在编辑器和查看器之间保持双向同步。它处理多种输入格式,应用样式,渲染特殊元素(如数学公式和图表),并同步编辑器和查看器窗格之间的滚动。

来源:packages/app-desktop/gui/NoteTextViewer.tsx1-249 packages/app-desktop/gui/note-viewer/index.html1-859

关键组件

笔记查看器架构

笔记查看器包含两个主要组件

  1. NoteTextViewer React组件:创建和管理一个 iframe 来显示笔记内容
  2. Note Viewer iframe:渲染 HTML 内容并处理用户交互

NoteTextViewer 组件通过消息传递与 iframe 通信,实现双向数据流和事件处理。

来源:packages/app-desktop/gui/NoteTextViewer.tsx59-246 packages/app-desktop/gui/note-viewer/index.html41-46

NoteTextViewer 组件接口

NoteTextViewer 组件提供了一个包含以下关键方法的控制接口

  • setHtml(html, options):设置要显示 HTML 内容
  • send(channel, arg0, arg1):向 iframe 发送消息
  • focus():聚焦笔记查看器
  • focusLine(lineNumber):聚焦查看器中的特定行
  • hasFocus():检查查看器当前是否聚焦

来源:packages/app-desktop/gui/NoteTextViewer.tsx27-34 packages/app-desktop/gui/NoteTextViewer.tsx72-158

HTML 内容设置流程

渲染笔记时,会执行以下步骤

  1. 编辑器调用 NoteTextViewer 上的 setHtml(html, options)
  2. NoteTextViewer 设置插件资产的访问控制
  3. iframe 通过消息接收 HTML 内容
  4. iframe 使用新内容更新其 DOM
  5. 渲染过程中会添加源映射属性
  6. 根据新内容创建滚动映射
  7. 恢复或初始化滚动位置

来源:packages/app-desktop/gui/NoteTextViewer.tsx72-158 packages/app-desktop/gui/note-viewer/index.html441-482

源映射和行跟踪

渲染管道的一个关键方面是源映射系统,它向 HTML 元素添加属性以跟踪它们与源 markdown 行的关系。

源映射过程

在 markdown 到 HTML 的转换过程中,会将特殊属性添加到 HTML 元素中

  • class="maps-to-line":标记带有源行信息的元素
  • source-line="n":源 markdown 中的起始行号
  • source-line-end="m":源 markdown 中的结束行号

这些属性可以实现编辑器和查看器位置之间的精确同步。

来源:packages/renderer/MdToHtml/rules/source_map.ts1-41

源映射支持的元素

源映射系统支持以下 markdown 元素

元素类型最大嵌套级别
段落0
标题0
引用块0
表格0
代码块0
水平线0
HTML 块0
列表项99
数学块0

来源:packages/renderer/MdToHtml/rules/source_map.ts7-18

滚动同步系统

渲染管道中最复杂的方面之一是编辑器和查看器之间的双向滚动同步。

滚动映射系统

滚动映射系统维护编辑器行号和查看器滚动位置之间的映射。这使得无论编辑器和查看器之间内容布局有何差异,都能实现同步滚动。

来源:packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollHandler.ts1-249 packages/app-desktop/gui/note-viewer/scrollmap.js1-123

坐标系统之间的转换

滚动映射处理三种不同的坐标系统

  1. 基于行的逻辑百分比:一种与格式无关的表示,基于源行的比例
  2. 编辑器滚动百分比:编辑器中的实际 scrollTop/scrollHeight 比率
  3. 查看器滚动百分比:查看器中的实际 scrollTop/scrollHeight 比率

转换函数在这些系统之间进行转换

  • translateScrollPercentE2L:将编辑器百分比转换为逻辑百分比
  • translateScrollPercentL2E:将逻辑百分比转换为编辑器百分比
  • translateL2V:将逻辑百分比转换为查看器百分比
  • translateV2L:将查看器百分比转换为逻辑百分比

来源:packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollHandler.ts206-243 packages/app-desktop/gui/note-viewer/scrollmap.js87-122

滚动映射的创建和刷新

滚动映射在内容更改时创建,并在布局更改时刷新

  1. scrollmap.create(lineCount):使用源总行数创建新的滚动映射
  2. scrollmap.refresh():使缓存的映射无效,强制重新计算
  3. scrollmap.get_():通过分析带有 maps-to-line 类的 DOM 元素来惰性构建映射

来源:packages/app-desktop/gui/note-viewer/scrollmap.js13-80

插件集成

渲染管道通过以下方式支持与插件集成

  1. 插件资产:可以加载到查看器中的 JavaScript 和 CSS 文件
  2. PostMessageService:插件用于与查看器交互的通信通道

插件资产加载

渲染笔记时,可以包含插件资产

  1. setHtml 方法在选项中接受插件资产
  2. NoteTextViewer 为这些资产提供文件访问权限
  3. 笔记查看器 iframe 将这些资产添加到 DOM

资产管理系统跟踪已添加的资产,避免重复,并在不再需要时删除资产。

来源:packages/app-desktop/gui/NoteTextViewer.tsx75-94 packages/app-desktop/gui/note-viewer/index.html158-228

插件消息通信

插件可以通过消息系统与笔记查看器通信

  1. 笔记查看器中的 webviewApi 对象提供了一个 postMessage 方法
  2. PostMessageService 为不同的组件注册响应程序
  3. 消息在主应用程序和笔记查看器 iframe 之间传递

这使得插件无需直接 DOM 操作即可扩展查看器的功能。

来源:packages/app-desktop/gui/NoteTextViewer.tsx36-57 packages/app-desktop/gui/note-viewer/index.html55-79

格式转换

Markdown 转 HTML

Markdown 内容被转换为 HTML 以在笔记查看器中显示。在此转换过程中

  1. 源映射属性被添加到 HTML 元素
  2. 特殊元素(如数学公式和图表)被处理
  3. 插件扩展可以修改输出

来源:packages/renderer/MdToHtml/rules/source_map.ts1-41

HTML 转 Markdown (Turndown)

@joplin/turndown 包将来自富文本编辑器的 HTML 转换为 markdown 格式。这允许在不丢失内容的情况下无缝切换编辑模式。

来源:packages/turndown/package.json1-54 packages/turndown-plugin-gfm/package.json1-46

命令和事件

笔记查看器响应从编辑器发送的各种命令

命令目的
setHtml更新显示的內容
scrollToHash滚动到指定的锚点
setPercentScroll滚动到指定位置
setMarkers高亮搜索词
focus聚焦查看器
focusLine聚焦特定行

查看器还会将事件发送回编辑器

  • percentScroll:用户在查看器中滚动时
  • noteRenderComplete:内容完全渲染时
  • contextMenu:用户在查看器中右键单击时

来源: packages/app-desktop/gui/NoteEditor/editorCommandDeclarations.ts1-172 packages/app-desktop/gui/note-viewer/index.html81-136

处理边缘情况

渲染管道包含多种机制来处理边缘情况

  1. 图片加载:检测图片何时完全加载以更新滚动
  2. 缩放处理:对非整数缩放因子进行特殊处理,以防止滚动问题
  3. 布局更改:检测 DOM 突变和尺寸调整以更新滚动映射
  4. 平滑滚动:自定义实现以实现一致的行为

来源: packages/app-desktop/gui/note-viewer/index.html266-367 packages/app-desktop/gui/note-viewer/index.html608-668

结论

渲染管道处理将笔记从其存储格式转换为渲染显示,同时保持诸如同步滚动之类的交互式功能,这是一个复杂的过程。它通过以下方式实现:

  1. 分离查看器和编辑器的组件架构
  2. 源映射,将 HTML 元素链接到 markdown 源行
  3. 复杂的滚动同步系统
  4. 支持插件扩展
  5. 健壮的边缘情况和平台差异处理

理解此管道对于开发与笔记渲染交互的功能或实现自定义渲染扩展至关重要。