用 Markdown 大纲画 Markmap 思维导图

Markdown 写作约 4 分钟

想把一份 Markdown 大纲变成思维导图,做法是把大纲放进语言名为 markmap 的围栏代码块,标题和嵌套的列表项会变成分支,预览里画成 SVG 树。妙言把 Markmap 库打包在应用里,所以普通大纲离线也能渲染,文字不会离开你的 Mac。代价是可控的东西少,V4.5.0 里颜色、间距和初始展开层级都由应用固定,普通预览里的导图不响应点击和滚轮,Markmap 官方文档里的那些选项在这里也不会生效。

Markmap 是什么

Markmap 官方文档把它描述成 Markdown 和思维导图的结合,它解析 Markdown,取出层级结构,再渲染成可以交互的导图,标题级别或列表缩进决定谁是谁的父节点。输入就是普通大纲,所以可以先把结构当笔记写下来,再看成导图,笔记在任何编辑器里仍然是可读的文字。

写法

```markmap
# 发布计划
## 设计
- 线框图
- 图标
## 构建
- 编辑器
  - 标签页
  - 搜索
- 预览
## 发布
- 公证
- 更新 appcast
```

第一个 # 标题是根节点,## 标题是它的分支,列表项挂在最近的标题下面。列表按缩进嵌套,这里习惯每级两个空格,层级可以混用,## 标题下的列表比标题深一层。

围栏不用手打,在妙言里输入 /markmap 再按 Tab,或者在「格式」菜单的「Tab快捷输入」子菜单里选 /markmap 那一项,会插入一个带根节点、两个分支和三个条目的小模板,根节点标题处于选中状态,占位文字是中文。

妙言里是怎么渲染的

带语言名的围栏块到达页面时是一个类名为 language-markmap 的 code 元素,预览加载完成以后,脚本(diagram-handler.js)找到这些元素,交给打包好的 Markmap 加载器(markmap.min.js,旁边是 markmap-view.min.js 和 d3.min.js)。加载器读取块里的文字,去掉公共缩进,把大纲转成树,再画成 SVG 替换原来的代码。这些文件都在预览资源包里(markmap-lib 0.14.1 和 markmap-view 0.14.0),用相对路径引用,普通大纲不会发出网络请求。例外是公式,节点里写了 $x^2$ 这类公式,Markmap 库会去 jsDelivr 拉 KaTeX 的样式和字体,要保持离线的导图里别放公式。

读 V4.5.0 的加载器代码,有几个行为是固定的:

  • 盒子高度由内容算出来,大约每个非空行 30 像素,再加上随深度增加的余量,最低 300 像素,大纲越长盒子越高,字号不会变小。
  • 节点颜色来自内置的十二色调色板,按深度循环使用,图的背景随浅色或深色外观由应用的 CSS 决定,你自己指定的分支配色不会被读取。
  • 默认全部展开,在普通预览里也一直保持展开,应用的处理脚本给这个块设了 pointer-events: none,点击到不了节点,也就折叠不了。
  • 导图创建时开了滚轮缩放、关了拖动平移,但指针事件被关掉以后滚轮同样不起作用,首次渲染时图会自动适配盒子。

选项:别抱太大期望

Markmap 上游允许在文档的 front matter 里用 markmap 键设置选项,选项页列出了 color、colorFreezeLevel、maxWidth 和 initialExpandLevel,比如用 initialExpandLevel: 2 让更深的层级首次渲染时折叠起来:

---
markmap:
  initialExpandLevel: 2
  colorFreezeLevel: 2
---

妙言 V4.5.0 打包的 Markmap 库能识别围栏里的这段并把它从大纲中剥掉,但应用自己的加载器是用固定选项创建导图的,不会把解析出来的选项再传回去。用 V4.5.0 的预览文件加载带 initialExpandLevel: 1 的块,所有层级照样全部画出来。需要折叠层级或自定义颜色的话,可以用 Markmap 官方的网页工具或支持完整选项的编辑器画这一张,再把截图放进笔记。

妙言里有一个选项,但只在演示模式下有效:块里写一行 <!--markmap-height=400-->,把笔记当幻灯片演示时,导图高度就按这个像素值来,普通预览里高度仍然是算出来的。

Markmap 还是 Mermaid 的 mindmap

Mermaid 有自己的 mindmap 图表类型,以关键字 mindmap 开头,靠缩进表示层级,只有一个根节点,节点可以有多种形状,Mermaid 文档把它标为实验性功能。妙言 V4.5.0 打包的 Mermaid 是 11.14.0,能画出 mindmap 块,不过语法以后还可能变,留一个小例子,更新以后试一下。

两者解决的问题不同。Markmap 读你已经写好的标题和列表,适合本身就是笔记的大纲,Mermaid mindmap 是另一套语法,适合希望导图和流程图、时序图用同一种图表语言写在一份文档里的情况。后一种可以看 Markdown 里画 Mermaid 图,三种图表语言的围栏写法在 Markdown 语法速查与 GFM 差异 里也有。

导出与常见问题排查

  • 块显示成普通代码块。 语言名必须正好是 markmap,围栏也要闭合,先数一下反引号。
  • 导图只有一个节点。 块里要有标题或列表项,只有一段正文的话,整句话会变成唯一的一个节点。
  • 特别大的大纲。 每个节点都是 SVG 里的一个 HTML 标签,几百个节点即使装得下也很难读,按分支拆成几个块更好。
  • 节点里的链接和格式。 粗体、行内代码和链接都能在节点里渲染出来,但普通预览里导图不接收指针事件,链接点不了,别拿导图当导航用。
  • 导出。 导出 PDF 时,妙言会等所有 Markmap 块渲染完再打印,导出以后还是打开文件看一次,图是画好的快照,大图最可能被缩放。

验证的办法是插入模板,切到预览(⌘3)或分栏模式(⌘\),确认出现带颜色分支的树。

常见问题

妙言里的 Markmap 需要联网吗?

普通大纲不需要,库文件随应用打包,和 PlantUML 不同,后者会把图发到公共服务器。节点里有公式是例外,它会从 CDN 加载 KaTeX。

能让导图默认折叠吗?

不能,V4.5.0 会忽略 front matter 里的选项,预览里的导图也不响应点击,所有层级都会显示,大纲太大就拆成几个块。

能直接编辑导图吗?

不能,导图由文字画出来,改大纲,预览会随之更新。