把我的 Miro boards 搬到 Obsidian
Miro 的 Export 按钮只给你 PNG 或 PDF —— 那是数据的照片,不是数据本身。如果你的 boards 是积累多年的 load-bearing 思维地图,它们应该住在 Obsidian 里:editable、searchable、放在本地 vault。我如何通过 REST API 把 boards 从 Miro 里拽出来,转成 Excalidraw / Obsidian Canvas —— 保留位置、颜色、字体、curved connectors、mindmap nodes —— 这件事的教训其实比 Miro 大得多:任何 SaaS 都应该在你第一次打开之前就先想好 exit plan。
任何活在浏览器里的工具,都把你扣押在它的 UI 上。尤其当你已经投入了时间、卡片、boards —— 然后突然意识到,你的 map of thinking 正躺在别人的服务器上,格式还得通过他们的页面才打得开。
我有几块 Miro boards 早就变成 load-bearing 了 —— 多年来积累的反思、计划、思维地图。Export 按钮给你 PNG 或 PDF。那不是数据,那是数据的照片。
为什么要拽出来
Vendor lock-in 不是一个理论问题。非常具体:要通过 API 下载你自己的数据,得 upgrade 到 Expert tier。也就是说,你要付钱才有资格把本来就属于你的东西拿回来。
我很早就按照 Obsidian-first 的原则生活:所有重要的东西都放在本地 vault 里,存成 plain files,可以 git、可以 grep,十年后还能打开。一个不支持导出到这种格式的工具,是一个你必须在依赖变得无法摆脱之前就抽身离开的工具。
为什么 Export 救不了你
Miro 的 Export 按钮给你 PNG 或 PDF。那是 raster —— 结构已经丢了。你没办法改 box 里的文字,没办法拖动 shape,没办法 grep 一条标签。那是 board 的讣告,不是 board 本身。
Obsidian 有两个活在 vault 里的替代方案:Excalidraw plugin 和原生 Canvas。两个都是 JSON。两个都 editable。两个都 searchable。两个都和你的笔记一起躺在 vault 里,可以直接在 Obsidian 里 grep 文本。
在没有「Export to Obsidian」按钮的时候怎么拽
当 UI Export 给不了你想要的东西时,就钻进 API。Miro 有 REST API:会返回 board 上每一个 shape 的 JSON 描述。Excalidraw 和 Obsidian Canvas 都是 open-format JSON。两者之间,就是一层 mapping。
我写了两个 Python 脚本。miro_to_excalidraw.py 和 miro_to_canvas.py。Standard library only —— 零 deps。带上 token 和 board ID 一跑,得到一个文件,丢进 vault。
转换后保留下来的东西
任务不只是把 shapes 搬过去,而是把视觉保留下来,让 board 读起来和在 Miro 里一样。
- 位置和尺寸 —— 每个 shape 的坐标 1:1
- 颜色 含 fill opacity(40% 的黄色仍然是 40% 的黄色)
- Borders —— 颜色、粗细、样式(solid / dashed)
- 字体和 alignment —— monospace 依然 monospace,center 依然 center
- Connectors —— 箭头钉在 Miro 当初钉的那几个点上
- Curved connectors —— 用 bow 弧线代替直线
- Images 直接嵌进文件(不是外部 URL)
- Mindmap nodes —— 文本是从 experimental Mindmap API 里拉出来的
- Z-ordering —— overlap 不会乱掉
哪些地方比较别扭
Miro API 有几个坑,得用 reverse engineering 绕过去。
- Curved connectors —— Miro 不在 API 里暴露 control points。所以我没法还原原始曲线,只能合成一个 bow —— 两个 endpoint 之间一段固定半径的弧。视觉上接近,mathematically 不 identical。
- Z-index —— Miro API 根本不返回 z 字段。哪个 shape 在上、哪个在下?Heuristic:面积越大越靠后。这在真实的 boards 里成立,因为大容器通常压在小元素下面。
- Mindmap —— mindmap nodes 的文本走普通 items API 是拿不到的。得单独调一次 experimental Mindmap endpoint。Filled nodes 变成 boxes,unfilled 变成 plain text。
Excalidraw vs Canvas —— 怎么选
如果你以 drawings 的方式思考 —— sketchy lines、hand-drawn feel、freeform 构图 —— 选 Excalidraw。Excalidraw plugin 渲染出来的文件,和你在 whiteboards 上看到的是同一种美学。
如果你的 boards 主要是 方框加文字、用箭头连起来,并且你把 Obsidian 当成 second brain 用 —— 选 Canvas。Canvas 是原生的,一键打开,和 vault 的 backlinks 和 tags 完全打通。
我会把一部分 boards 转成 Excalidraw(看重 visual style 的时候),另一部分转成 Canvas(看重速度和原生体验的时候)。Repo 两种格式都支持 —— 运行时自己选。
总结
Python 3,standard library only。不用 pip install,不用 virtualenv,没有 node_modules。python3 miro_to_excalidraw.py 'BOARD_ID' 'output.excalidraw' —— 文件就到你手里了。
Repo: github.com/oleksiimazurenko/miro-to-obsidian。MIT license。欢迎 PR —— 特别是那些我还没遇到过的 edge cases。
教训比 Miro 大得多
SaaS lock-in 是一种毒品。头几个月免费。半年下来,成了习惯。一年下来,你的数据活在一个只有他们的 UI 才能读的格式里。两年下来,你为了把本来就属于你的东西挖出来,正在付 Expert tier 的费用。
结论不是「别用 SaaS」。结论是:任何一个你允许它变成 load-bearing 的工具,都需要一个在你第一次打开它之前就准备好的 exit plan。「我需要的时候怎么从这儿走出去」这个问题,权重和「UI 用起来多舒服」一样高。一个没有 plain-text 导出、也没有开放 API 的工具,是一个带着隐藏价签的工具,那张账单两年后才会到。
Obsidian-first 不是因为 Obsidian 完美。而是因为 plain markdown 文件这种格式,会比 Obsidian 本身活得更久。同样,一份裸的 Excalidraw JSON 文件会比 plugin 本身活得更久。同样,这个 Python 脚本会比 Miro 活得更久。