GitHub 마크다운 수학: 렌더링 규칙 및 해결 방법
GitHub는 마크다운 수학을 두 단계로 렌더링합니다.
- 서버 측 (
cmark-gfm+ 수학 검증기):$ ... $및$$ ... $$경계를 찾고, CommonMark 백슬래시 이스케이프 해제를 ASCII 문장 부호에 적용하며, 허용되지 않는 LaTeX 매크로를 거부한 후<math-renderer>태그를 내보냅니다. - 클라이언트 측 (
<math-renderer>+MathJax.tex2mml): TeX를 MathML로 변환하고 브라우저 네이티브 MathML Core 레이아웃을 위해 페이지에 삽입합니다.
두 단계 모두 일반 LaTeX를 망가뜨리는 버그가 있습니다. 아래 규칙은 해당 버그에 대한 해결 방법입니다.
이 스킬을 사용해야 하는 경우
- GitHub에서 읽을
.md파일에 수학을 작성하거나 편집할 때. - GitHub에서 수식이 압축되어 보이거나, 빨간색으로 표시되거나, 원시
$$텍스트로 나타나는 문제를 수정할 때. - LaTeX, Pandoc, Jupyter, MkDocs 또는 VS Code만 거치는 파일에는 필요하지 않습니다. 파일이 여러 환경에서 모두 읽히는 경우 아래의 대체 구문은 일반 TeX/MathJax이므로 다른 곳에서도 계속 렌더링됩니다.
워크플로우
- 작성하는 동안 아래 규칙 1~4를 따릅니다.
- 커밋하기 전에 변경된 마크다운 파일에 대해 검사 스크립트를 실행합니다(스크립트 참조).
--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 | 짝을 이루는 *가 마크다운 강조가 됨 |
1. \tag{...} 절대 사용 금지 (1문자 세로 압축 방지)
- 버그:
MathJax.tex2mml은\tag{2.1}이 포함된 모든 수식을<mtable displaystyle="true"><mlabeledtr><mtd>...로 직렬화합니다. 브라우저 MathML Core 명세는<mlabeledtr>를 제거했으므로, Chromium/Blink와 Firefox는 이를 테이블 행으로 처리하지 않고 수식을 1문자 너비 열로 압축합니다. - 해결 방법: 대신 표시 수식 끝에
\qquad\text{(2.1)}를 넣으십시오. 레이블에 프라임이 필요한 경우 중첩된$\tag{3.3$'$}$대신\text{(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*를다음 매크로는 허용되지 않습니다: operatorname이라는 배너와 함께 차단합니다. - 해결 방법:
\operatorname{Foo}를\mathrm{Foo}로 바꾸십시오. 바로 뒤에 문자나 백슬래시 매크로가 오는 경우(예:\operatorname{Im} D_t또는\operatorname{pr} v), 연산자 간격을 유지하기 위해\thinspace를 추가하십시오(\mathrm{Im}\thinspace D_t).- 일반 TeX
\hbox{...}는\text{...}로,{\rm ...}는{\mathrm{...}}로 바꾸십시오.
<!-- GitHub에서 깨짐 ("다음 매크로는 허용되지 않습니다: 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의 마크다운 파서는$ ... $및$$ ... $$내부에 CommonMark ASCII 문장 부호 이스케이프 해제를 적용합니다.\{는{가 되고\}는}가 됩니다. 이로 인해\left\{에서Missing or unrecognized delimiter for \left오류가 발생하고 푸아송 괄호\{F, G\}의 중괄호가 조용히 제거됩니다.\\(줄바꿈이 뒤따르지 않는 경우)는\가 됩니다. 이는\begin{pmatrix} 0 & 1 \\ -1 & 0 \end{pmatrix}와 같은 한 줄 행렬과 케이스를\-1로 깨뜨려 빨간색의 정의되지 않은 매크로 오류를 발생시킵니다.\,는,,(이중 쉼표)가 되고\,는,(dx앞의 리터럴 쉼표)가 됩니다.- 수학 블록 내부의 짝을 이루는
*(예:U_+(z - 2q_*), \quad z = w + Dq_*)는 마크다운<em>강조로 파싱될 수 있습니다.
- 해결 방법:
\{와\}대신\lbrace와\rbrace를 사용하십시오 (뒤에 문자가 오면 공백 추가:\lbrace F, G\rbrace).- 한 줄 행렬 및 환경의 행 나누기에는
\\대신\cr를 사용하거나, 여러 줄$$...$$블록 내에서는\\바로 뒤에 개행 문자를 넣으십시오. \,대신\thinspace를 사용하고 (문자 앞에 후행 공백 포함:dt\thinspace dx),\,는,로 바꾸십시오.- 수식에 여러 개의 별표가 포함된 경우
*(q_*) 대신\ast(q_\ast)를 사용하십시오.
<!-- GitHub에서 깨짐 -->
인라인 푸아송 괄호 $\{F, G\} = 0$, 집합 $\left\{ x : x > 0 \right\}$, 적분 $\int dx\,dy$, 행렬 $\bigl(\begin{smallmatrix} 0 & 1 \\ -1 & 0 \end{smallmatrix}\bigr)$.
<!-- GitHub에서 작동 -->
인라인 푸아송 괄호 $\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}와 같은 줄에 있는 여러 줄$$\begin{aligned} ... \end{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는 위 규칙을 마크다운 파일에 적용합니다. $...$ 및 $$...$$의 내용만 다시 쓰고, 펜스 코드와 인라인 코드는 그대로 두며, 수학이 없는 파일은 건드리지 않으며, 멱등성을 가집니다. 어디서든 실행할 수 있습니다.
# 수정하지 않고 확인 (변경이 필요한 파일이 있으면 1로 종료, CI 또는 pre-commit 훅에서 사용 가능)
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{...}는 해당 인수에 중첩된 중괄호가 없는 경우에만 변환됩니다.\mathrm{...}뒤에\thinspace는 고정된 이름 목록 (Im,im,pr,graph,Div,ker,dim,deg,span,spec,End,Re,tr,rank)에 대해서만 추가됩니다. 다른 이름은 직접 추가하십시오.- 별표 중
q_*만q_\ast로 다시 작성됩니다. 다른*는 직접 확인하십시오 (규칙 3). - 인용구 수식은 한 단계 깊이 (
>)까지만 처리되며 중첩 (> >)은 처리되지 않습니다. - 두 개의 통화 기호가 있는 줄 (
$5 and $10)은 인라인 수학으로 읽힐 수 있습니다. 이는 그 사이의 텍스트에 규칙이 다시 작성하는 백슬래시 시퀀스가 포함되어 있지 않으면 무해합니다. - 이 규칙은 관찰된 GitHub의 동작을 설명합니다. GitHub가 이러한 버그 중 하나를 수정하면 해당 해결 방법은 불필요해집니다.