GitHub Markdown 數學式:渲染規則與替代方案
GitHub 分兩階段渲染 Markdown 數學式:
- 伺服器端(
cmark-gfm+ 數學清理器): 找出$ ... $與$$ ... $$的邊界,對 ASCII 標點符號套用 CommonMark 的反斜線取消跳脫處理,並在發出<math-renderer>標籤之前,拒絕不允許的 LaTeX 巨集。 - 客戶端(
<math-renderer>+MathJax.tex2mml): 將 TeX 轉換為 MathML,並注入頁面中以使用瀏覽器原生的 MathML Core 進行排版。
這兩個階段都存在會破壞一般 LaTeX 的錯誤。以下規則是針對這些錯誤的替代方案。
何時使用此技能
- 撰寫或編輯將在 GitHub 上閱讀的
.md檔案中的數學式。 - 修正那些在 GitHub 上顯示為被擠壓、紅色錯誤或原始
$$文字的方程式。 - 如果檔案只會經過 LaTeX、Pandoc、Jupyter、MkDocs 或 VS Code 處理,則不需要此技能。如果一個檔案在多個地方被閱讀,以下的替代寫法本身是標準的 TeX/MathJax,因此在其他地方仍然能正常渲染。
工作流程
- 撰寫時,遵循以下的規則 1 到 4。
- 在提交前,對已變更的 Markdown 檔案執行檢查腳本(請參閱腳本)。若不使用
--check參數執行,它會直接原地修正檔案。 - 使用
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, \rbrace | CommonMark 會將 \{ 轉換為 { |
單行中的 \\\\ | \cr | CommonMark 會將 \\\\ 轉換為 \\ |
\\, | \thinspace | CommonMark 會將 \\, 轉換為 , |
\\, | , | 會渲染成雙逗號 |
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在以下三種情況下無法正確識別$$ ... $$區塊,導致留下原始$$文字並將底線間的文字設為斜體:- 頂層
$$ ... $$區塊前後沒有空行。 - 多行
$$\begin{aligned} ... \end{aligned}$$區塊中,$$與\begin{aligned}在同一行,而不是獨佔一行。 - 縮排在清單項目(
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 修復了其中某個錯誤,對應的替代方案將不再必要。