讓 Obsidian 與 Docusaurus 一起協作
Docusaurus
Docusaurus 是 Facebook 的開源專案,主要作為靜態網站產生器 (我認為很像精簡版的 Next.js),可以用來快速打造文件網站,甚至也有許多人拿這個工具來寫部落格
(我印象中 Redux 的 document 也是用其建造)
由於使用 React 作為底層,如果熟悉 React 的話可以進行高度的客製化,這一點真的很棒,以前使用過 Hexo 或 Hugo 的時候根本改不動 QQ
緣由
先前我在自建的 Docusaurus 部落格上發表了幾篇筆記,後來使用 Obsidian 作為主要知識管理工具,發現更能專注於寫作與進入心流
我一直在思考怎麼融合 Obsidian 與 Docusaurus,並把寫作的 workflow 連結起來,於是終於慢慢思索出不會太折騰的方式 (對一般人來說也是有點折騰就是了...)
使用 Obsidian 會更傾向卡片盒筆記法,把知識點原子化,並使用筆記之間的連結組織起來,而不是寫一篇超長的文章,在這情況下處理文章之間的連結並能兼容兩者,就會變得麻煩且重要
LLM Prompt Engineering MOC 就是個例子
先前有考慮過 obsidian-export
,但是輸出後的 YAML 格式會被替換,會和 Docusauraus 不相容,後來還是決定自己手動設定 + Obsidian plugin 批次更改
Obsidian 重要 Plugin 參考
必須要裝的
Linter
: 對 YAML frontmatter 進行批次處理,也可以作為 md 的 prettier,非常強大- `obsidian-paste-image-rename : 除了自動複製貼上的圖片進行快速更名,也能把同一篇文章引用的圖片進行批次更名
Obsidian Link Converter
: 謹慎使用,把 Wikilink 轉換成一般的 MDLinks,使用前請先小規模測試或備份Obsidian Git
: 不多說,備份與同步神器
有了這些會舒服多
Paste URL into Selection
: 選起來的文字貼上連結,會變成 md Link 格式AutoTitleLink
: 自動 fetch 貼上連結 的標題Tag Wrangler
: 批次修改 tags 名稱很方便
Obsidian 的核心精神
- 使用內置的 explorer 搬遷筆記、圖片,以及對其進行更名,都會自動更新引用這些檔案的筆記
- 例如對 A 進行更名和搬遷,所有引用 A 的筆記,都會自動更新筆記內的引用路徑和名稱 (圖片也是同理)
- 但是要注意,不要使用一般的檔案總管,這樣會無法自動更新
- 在維持純文本和單純資料夾的格式之下,實現強大功能
- 備份和同步,只要處理整個資料夾就好,不受制於筆記與筆記軟體的耦合性 (例如 Notion)
- 只要最原始的檔案都是純文本,就可以保留許多用程式語言來批次處理的客製空間
- 超級好用的筆記連結自動完成
- Obsidian 的模糊搜索相當厲害,UX 滿點
在 Obsidian 筆記組織的原則
課題分離,不用整個大改
Obsidian 有不少把整個 vault 輸出成 SSG 的服務,例如 jackyzha0/quartz、甚至是官方的 obsidian publish,缺點是中間的過程都是黑魔法,很難符合自己的需求,而且可能會有許多 bug 要折騰很久
後來思考到,因為我的 vault 裡有許多個人的日記還有專案資料夾,這些是不需要 publish 出去的,所以我不需要這種 end to end 的服務,只要將要公開發表的筆記都放在一個資料夾統一處理即可
如果你的 Obsidian 已經很龐雜,先獨立一個資料夾專門給 SSG 來使用,把要發佈的筆記慢慢搬遷進來,後記我會簡稱為 pubish 資料夾
Pubish 資料夾的寫作原則
在 vault 新增一個資料夾,作為 Docusaurus 的文章基地之後,我認為有幾個規定需要考慮
在 publish 資料夾使用最廣為通行的 md 語法
- 只寫最通用的 md 格式,不使用 Obsidian 獨有的 format 和功能,例如 dataview、admonition
- 但是 code block 和 math jax 算是很普及的 md 功能了,可以使用
- 只用一般 relative path + markdown link,不使用 wiki link 格式
- 這是為了在 Docusaurus 裡也能維持筆記之間的連結
- relative path + markdown link 可能在 source 模式會顯得很長,但是使用 live preview 模式幾乎無感
- 輸入
[[
仍可享用 obsidian 厲害的模糊搜尋,按下 enter 也會變成 markdown link 格式
- 只用最單純的筆記連結,而不使用 block reference
最後,我會在 Obsidian 設定 attachment 資料夾,集中所有圖片,而不是散落在各個子資料夾之間,這樣會更方便處理
筆記檔名與 id
由於主要日常工作還是在自己的筆記系統,想維持檔名是可讀性高的格式 (例如有中文和空白) 所以需要額外功夫來處理筆記 route 和顯示的標題
在筆記檔名有中文和空白的情況下,會造成網址路徑很長 (因為轉換 URL encode),故使用 id
來固定 route 連結穩定度,title
則是設定 Docusaurus 的顯示名稱
因為我不喜歡在 Obsidian 有 h1,所以才需要設 title
,如果筆記內有 h1,就可以不用設 title
(我記得 Docusaurus 會自動取用 h1)
範例
讓 Obsidian 與 Docusaurus 一起協作.md
id: obs-docusaurus-cowork
title: 讓 Obsidian 與 Docusaurus 一起協作
id
需要自己想,可以先不加,但如果要發在社群或公開發表,建議要加,以增加連結的穩定度
title
可以用 Linter 批次處理,會自動用檔名或 h1 來添加
延伸 : 如何設定 link 格式
首先開啟 Obsidian 設定,找到 Files & Links,並套用以下設定
這樣往後新增 link 都會啟用 relative path + markdown link 格式
過往的筆記如果想要批次轉換,可使用 Obsidian Link Converter
,不過記得在設定選 relative path,wiki-link to MD Link 我實測下來問題不大,但是反向轉換就會比較多 bug
建議啟用指令模版,在資料夾的 scope 之下逐步轉換
老話一句,批次轉換前請 git 備份
使用 Linter 的小撇步
我主要會使用 linter 這個 plugin 來處理 YAML,裡面有很多針對 md 進行格式化的選項,也可以依喜好使用
- 我習慣把 Lint on Save 關掉,並把 linter 設入快捷鍵,自己手動觸發
- 如果想對數個檔案一次 linter,可以放在同一個資料夾,然後右鍵選取 Linter File
- 配合 git 備份,盡量小規模測試,不要一次大範圍改
這個功能會將中文和英文數字之間自動加上空格,超級好用
找出聯集,先服務 Docusaurus
Docusaurus 的限制會比 Obsidian 還要多,所以會 先以 Docusaurus 能動起來為主,以下是我觀察到 Docusaurus 的一些小坑
docs 和 blog 適用的 front matter 如下
📦 plugin-content-docs | Docusaurus
📦 plugin-content-blog | Docusaurus
如果你使用的 key 在 Docusaurus front matter 列表中卻是空值的話,Docusaurus 會報錯,解決辦法就是整個拿掉
---
id:
---
這樣會報錯
使用 single line array,避免一些縮排的坑,而且這種格式跟 md 的 list 語法太類似,很容易跟一些 plugin 相衝
---
tags:
- note-status/🌱
- note-framework/🔧how-to
---
bad
---
tags: [note-status/🌱, note-framework/🔧how-to]
---
good
如果你的現有筆記是 multi line,可以靠 linter 來幫你批次更改格式
這樣設定,就可以把 tags 格式自動進行批次轉換
重要 ! YAML key 請使用 tags,Docusaurus 只認得 tags
Obsidian 可以認得 tags 和 tag,所以可能過往的筆記會混合使用,可以用搜尋取代簡單解決
圖片處理
- 先假設 vault 的 attachment 資料夾叫做
img-src
,將img-src
複製到 Docusaurus 資料夾的static
下 - 鎖定 Docusaurus
docs
和blog
資料夾中的 md,將筆記中圖片的相對路徑移除,處理成根目錄,例如/img-src/
變成成/img-src
- 建議用 js 正則表達式來做,並在 npm script 裡同時啟用
圖片檔名不要含空白跟中文,這很重要
圖片檔名包含中文和空白,在 Obsidian 中的路徑會被處理成 URL Encode
上述把 img-src
放在 static
是屬於 absolute path 的方式,而 Docusaurus 在 relative path 圖片路徑有進行 bug 修復,可以支援 URL encode,但是 absolute path 碰到 URL encoded 的路徑還是會有問題 (可能是忘記吧...)
不想手動改圖檔名稱的話,可以使用 obsidian-paste-image-rename
主要流程
- 在 Obsidian 把特定資料夾作為發佈區,裡面的筆記要符合 Docusauraus 的規範,以及只使用一般 md 用法
- 筆記之間的連結使用 relative path + markdown link
- Obsidian 中圖片統一放在 attachment 資料夾,檔名不要有中文和空白
- 把 attachment 資料夾複製到 Docusaurus 的
static
資料夾底下 - 把 Docusaurus
docs
&blog
資料夾底下的 md,其圖片路徑取代為絕對路徑 (/img-src/
轉換為/img-src/
)
我的 prebuild script
初步開發階段,請酌量參考
require('dotenv').config();
const fs = require('fs-extra');
const path = require('path');
const glob = require('glob');
const src = process.env.SOURCE;
const srcDocDir = process.env.SOURCE_DOC_DIR;
const srcBlogDir = process.env.SOURCE_BLOG_DIR;
const srcImgDir = process.env.SOURCE_IMG_DIR;
const dest = process.env.DESTINATION;
const destDocDir = process.env.DESTINATION_DOC_DIR;
const destBlogDir = process.env.DESTINATION_BLOG_DIR;
const destImgDir = process.env.DESTINATION_IMG_DIR;
console.log(path.join(src, srcDocDir))
fs.removeSync(path.join(dest, destDocDir));
fs.removeSync(path.join(dest, destBlogDir));
fs.copySync(path.join(src, srcDocDir), path.join(dest, destDocDir));
fs.copySync(path.join(src, srcBlogDir), path.join(dest, destBlogDir));
fs.copySync(path.join(src, srcImgDir), path.join(dest, destImgDir));
// "../../../img_src" 改為 "/img_src/"
glob(path.join(dest, '{docs,blog}/**/*.md'), (err, files) => {
if (err) throw err;
files.forEach(file => {
fs.readFile(file, 'utf8', (err, data) => {
if (err) throw err;
// TODO: img-src 改為從 .env 讀取 SOURCE_IMG_DIR
const result = data.replace(/(\.\.\/)+img-src/g, '/img-src/');
fs.writeFile(file, result, 'utf8', (err) => {
if (err) throw err;
});
});
});
});
目前限制
Docusaurus docs 與 blog 之間沒辦法使用 relative path 互相連結,官方說後續會修正