Community程式設計與開發github.com

gustavdelius/github-markdown-math

在 GitHub 上渲染的 Markdown 中撰寫、審查與修正 LaTeX 數學式($...$ 與 $$...$$),避免常見錯誤。

github-markdown-math 是什麼?

這個技能旨在解決 GitHub 在其 Markdown 渲染流程中,處理 LaTeX 數學式時會遇到的特定錯誤。GitHub 使用兩階段處理:伺服器端的 `cmark-gfm` 與數學清理器,以及客戶端基於 MathML Core 的 `MathJax.tex2mml`。這兩個階段都存在一些已知問題,例如 `\tag` 導致方程式被壓縮成垂直條狀、`\operatorname` 觸發「不允許的巨集」錯誤、以及 CommonMark 對反斜線的處理會破壞分隔符號和間距。此技能提供了一套具體的替代寫法,例如用 `\text{(2.1)}` 或 `\qquad\text{(2.1)}` 替代 `\tag{2.1}`,用 `\mathrm{Foo}` 替代 `\operatorname{Foo}`,以及在需要時用 `\lbrace`/`\rbrace`、`\cr` 和 `\thinspace` 來避開解析錯誤。此技能也包含一個由正規表達式驅動的 Python 腳本(`fix_github_math.py`),可自動化應用這些修正。這個技能對任何在 GitHub 上撰寫或維護包含數學式的 Markdown 文件(如 README、維基、議題)的開發者而言至關重要,能確保方程式正確渲染,且對於同時需要與 Pandoc 或 Jupyter 等工具相容的文件來說,這些替代寫法仍然有效。

相容平台✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/gustavdelius/claude-skills/tree/HEAD/github-markdown-math

Installed? Explore more 程式設計與開發 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

GitHub Markdown 數學式:渲染規則與替代方案

GitHub 分兩階段渲染 Markdown 數學式:

  1. 伺服器端(cmark-gfm + 數學清理器): 找出 $ ... $ 與 $$ ... $$ 的邊界,對 ASCII 標點符號套用 CommonMark 的反斜線取消跳脫處理,並在發出 <math-renderer> 標籤之前,拒絕不允許的 LaTeX 巨集。
  2. 客戶端(<math-renderer> + MathJax.tex2mml): 將 TeX 轉換為 MathML,並注入頁面中以使用瀏覽器原生的 MathML Core 進行排版。

這兩個階段都存在會破壞一般 LaTeX 的錯誤。以下規則是針對這些錯誤的替代方案。

何時使用此技能

  • 撰寫或編輯將在 GitHub 上閱讀的 .md 檔案中的數學式。
  • 修正那些在 GitHub 上顯示為被擠壓、紅色錯誤或原始 $$ 文字的方程式。
  • 如果檔案只會經過 LaTeX、Pandoc、Jupyter、MkDocs 或 VS Code 處理,則不需要此技能。如果一個檔案在多個地方被閱讀,以下的替代寫法本身是標準的 TeX/MathJax,因此在其他地方仍然能正常渲染。

工作流程

  1. 撰寫時,遵循以下的規則 1 到 4。
  2. 在提交前,對已變更的 Markdown 檔案執行檢查腳本(請參閱腳本)。若不使用 --check 參數執行,它會直接原地修正檔案。
  3. 使用 git diff 檢視結果。此腳本是基於正規表達式的,請將其輸出視為草稿。無法在本地端驗證渲染結果;若有疑慮,請在 GitHub 上預覽。

快速參考

請勿使用請使用原因
\tag{2.1}\qquad\text{(2.1)}\tag 會將方程式壓縮成一個垂直條狀
\operatorname{Foo}\mathrm{Foo}operatorname 被清理器封鎖
\mathrm{Im} D_t\mathrm{Im}\thinspace D_t還原運算子的間距
\hbox{...}, {\rm ...}\text{...}, {\mathrm{...}}純 TeX 巨集被封鎖
\{, \}\lbrace, \rbraceCommonMark 會將 \{ 轉換為 {
單行中的 \\\\\crCommonMark 會將 \\\\ 轉換為 \\
\\,\thinspaceCommonMark 會將 \\, 轉換為 ,
\\,, 會渲染成雙逗號
q_*(區塊中有多個 *)q_\ast成對的 * 會變成 Markdown 強調

1. 永遠不要使用 \tag{...}(防止 1 字元垂直擠壓)

  • 錯誤: MathJax.tex2mml 將任何包含 \tag{2.1} 的方程式序列化為 <mtable displaystyle="true"><mlabeledtr><mtd>...。瀏覽器的 MathML Core 規格已移除 <mlabeledtr>,因此 Chromium/Blink 和 Firefox 不會將其視為表格行,並將方程式壓縮成一個 1 字元寬的欄位。
  • 替代方案: 改為在顯示方程式結尾處放置 \qquad\text{(2.1)}。若標籤中有撇號,請在 \text{(3.3′)} 內部使用 Unicode ′,而不是巢狀的 $\tag{3.3$'$}$。
<!-- 在 GitHub 上會壞掉(被壓縮成垂直欄位) -->
$$
\partial_t \mathcal{Q} + \partial_x \mathcal{J} = 0 \tag{2.1}
$$

<!-- 在 GitHub 上可正常運作 -->
$$
\partial_t \mathcal{Q} + \partial_x \mathcal{J} = 0 \qquad\text{(2.1)}
$$

2. 永遠不要使用 \operatorname{...}, \hbox{...} 或 {\rm ...}(防止不允許的巨集錯誤)

  • 錯誤: GitHub 伺服器端的清理器會封鎖 \operatorname 和 \operatorname*,並顯示訊息「The following macros are not allowed: operatorname」。
  • 替代方案:
    • 將 \operatorname{Foo} 替換為 \mathrm{Foo}。當它後面緊跟著一個字母或反斜線巨集時(例如 \operatorname{Im} D_t 或 \operatorname{pr} v),請加入 \thinspace (\mathrm{Im}\thinspace D_t)以保留運算子的間距。
    • 將純 TeX 的 \hbox{...} 替換為 \text{...},並將 {\rm ...} 替換為 {\mathrm{...}}。
<!-- 在 GitHub 上會壞掉("The following macros are not allowed: operatorname") -->
$\operatorname{diag}(1, -1)$ and $\operatorname{Im} D_t$

<!-- 在 GitHub 上可正常運作 -->
$\mathrm{diag}(1, -1)$ and $\mathrm{Im}\thinspace D_t$

3. 確保分隔符號、間距與換行能通過 CommonMark 的取消跳脫處理

  • 錯誤: 在數學式到達 <math-renderer> 之前,GitHub 的 Markdown 解析器會對 $ ... $ 和 $$ ... $$ 內部的 ASCII 標點符號套用 CommonMark 的反斜線取消跳脫處理:
    • \{ 變成 { 且 \} 變成 }。這會導致 \left\{ 出現「Missing or unrecognized delimiter for \left」錯誤,並無聲地從 Poisson 括號 \{F, G\} 中移除大括號。
    • \\\\(當後方沒有換行符號時)變成 \\。這會破壞單行矩陣和 cases 環境,例如將 \begin{pmatrix} 0 & 1 \\ -1 & 0 \end{pmatrix} 變成 \-1,導致紅色的未定義巨集錯誤。
    • \\, 變成 ,,(雙逗號)而 \\, 變成 ,(在 dx 前的文字逗號)。
    • 數學區塊內成對的 *(例如 U_+(z - 2q_*), \quad z = w + Dq_*)可能被解析為 Markdown 的 <em> 強調。
  • 替代方案:
    • 使用 \lbrace 和 \rbrace 代替 \{ 和 \}(如果後面跟著字母,請加入空格:\lbrace F, G\rbrace)。
    • 對單行矩陣和環境中的換行,使用 \cr 代替 \\\\,或者在多行 $$ ... $$ 區塊內的 \\\\ 後面立即放一個換行符號。
    • 使用 \thinspace 代替 \\,(在字母前加入尾隨空格:dt\thinspace dx),並將 \\, 替換為 , 。
    • 當一個方程式中包含多個星號時,使用 \ast(q_\ast)代替 *(q_*)。
<!-- 在 GitHub 上會壞掉 -->
行內 Poisson 括號 $\{F, G\} = 0$,集合 $\left\{ x : x > 0 \right\}$,積分 $\int dx\,dy$,矩陣 $\bigl(\begin{smallmatrix} 0 & 1 \\ -1 & 0 \end{smallmatrix}\bigr)$。

<!-- 在 GitHub 上可正常運作 -->
行內 Poisson 括號 $\lbrace F, G\rbrace = 0$,集合 $\left\lbrace x : x > 0 \right\rbrace$,積分 $\int dx\thinspace dy$,矩陣 $\bigl(\begin{smallmatrix} 0 & 1 \cr -1 & 0 \end{smallmatrix}\bigr)$。

4. 為 cmark-gfm 格式化 $$ ... $$ 區塊邊界

  • 錯誤: 當一個方程式包含多個 _ 或 * 字元時,cmark-gfm 在以下三種情況下無法正確識別 $$ ... $$ 區塊,導致留下原始 $$ 文字並將底線間的文字設為斜體:

    1. 頂層 $$ ... $$ 區塊前後沒有空行。
    2. 多行 $$\begin{aligned} ... \end{aligned}$$ 區塊中,$$ 與 \begin{aligned} 在同一行,而不是獨佔一行。
    3. 縮排在清單項目(1. ... 或 - ...)或引述區塊(> ...)內部的多行 $$ ... $$ 區塊。
  • 替代方案:

    • 頂層顯示數學式: 將開頭與結尾的 $$ 各自放在第 0 欄的獨立行上,前後各留一個空行:
    
    $$
    \begin{aligned}
    a &= b + c, \\
    d &= e + f.
    \end{aligned} \qquad\text{(1.1)}
    $$
    
    
    • 清單項目或引述區塊內的顯示數學式: 將整個方程式放在單一行中,寫成 $$\displaystyle ...$$(在環境內部使用 \cr 處理換行),前後用空行包圍:
    1. 在編號清單項目內部:
    
       $$\displaystyle S_d = \int_{\mathbb{R}} dt\thinspace \mathcal{L}_d(\phi_t, \psi_t) \qquad\text{(1.2)}$$
    
    

腳本

scripts/fix_github_math.py 將上述規則應用於 Markdown 檔案。它只改寫 $...$ 和 $$...$$ 的內容,不碰圍籬式程式碼區塊和行內程式碼,不會觸碰不含數學式的檔案,且具有冪等性。從任何地方執行:

# 檢查但不修改(若任一檔案需要修改則退出碼為 1;可用於 CI 或 pre-commit hook)
python3 ~/.claude/skills/github-markdown-math/scripts/fix_github_math.py --check path/to/file.md

# 直接原地修正
python3 ~/.claude/skills/github-markdown-math/scripts/fix_github_math.py path/to/file.md

如果此技能是安裝在專案中(.claude/skills/github-markdown-math/),請使用該路徑。

限制

  • 它基於正規表達式,因此請檢視 diff。它也無法確認 GitHub 能正確渲染結果。
  • 僅當 \operatorname{...} 的引數沒有巢狀大括號時才會轉換。僅針對一個固定的名稱列表(Im, im, pr, graph, Div, ker, dim, deg, span, spec, End, Re, tr, rank)在 \mathrm{...} 之後加入 \thinspace;其他的請手動添加。
  • 在星號中,只有 q_* 會被改寫為 q_\ast;其他 * 請手動檢查(規則 3)。
  • 引述區塊方程式只處理一層深度(> ),不處理巢狀(> > )。
  • 包含兩個貨幣符號的行(例如 $5 and $10)可能被視為行內數學式。除非其間的文字包含規則會改寫的反斜線序列,否則這並無害。
  • 這些規則描述了截至目前觀察到的 GitHub 行為。如果 GitHub 修復了其中某個錯誤,對應的替代方案將不再必要。

相關技能