fix(study): HC-5 block math spacing — KaTeX \$\$...\$\$ 앞뒤 빈 줄 보장 자동 fix

문제: 보기/해설 본문의 \$\$ ... \$\$ block math 가 앞뒤 빈 줄 없으면
마크다운 파서가 라벨/텍스트와 같은 단락에 묶어 KaTeX 렌더 실패 → raw 표시.

운영 결과 (21회분 = 2,100문항):
- HC-5 detect 317건 모두 자동 fix 완료. 모든 회차 재검사 0건.
- 추가 fix: q1579 (2023년 1회 q81) 바이메탈 ASCII 다이어그램 fence wrap.

알고리즘:
- 자체 줄 \$\$...\$\$ (한 줄 안 시작·종료, 길이 4+) detect.
- 앞·뒤 라인이 비어있지 않으면 빈 줄 삽입 — idempotent.
- inline \$ ... \$ 영향 없음.
- 의미 변경 0 (빈 줄 삽입만, 본문 텍스트/수식 보존).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-04-30 08:29:39 +09:00
parent 87b6c38d99
commit 5404343a1a
+40 -1
View File
@@ -106,6 +106,40 @@ def hc4_strip_whitespace(text: str) -> str | None:
return stripped if stripped != text else None
def hc5_block_math_spacing(text: str) -> str | None:
"""HC-5: 자체 줄 block math (^$$...$$$ 단독 라인) 의 앞뒤로 빈 줄 보장.
KaTeX block math 가 인라인 텍스트와 같은 단락에 묶여 렌더 실패하는 케이스 fix.
빈 줄 삽입만 — 본문 내용 보존.
"""
if not text or "$$" not in text:
return None
lines = text.split("\n")
new_lines: list[str] = []
changed = False
for i, line in enumerate(lines):
s = line.strip()
# 자체 줄 block math: ^$$...$$$ (한 줄 안 시작·종료, 내용 1자 이상)
is_block = (
s.startswith("$$") and s.endswith("$$") and len(s) >= 4 and s != "$$"
)
if is_block:
# 앞에 비어있지 않은 라인 있으면 빈 줄 추가
if new_lines and new_lines[-1].strip():
new_lines.append("")
changed = True
new_lines.append(line)
# 다음 라인이 비어있지 않으면 빈 줄 추가
if i < len(lines) - 1 and lines[i + 1].strip():
new_lines.append("")
changed = True
else:
new_lines.append(line)
if not changed:
return None
return "\n".join(new_lines)
def apply_all_hc(text: str) -> tuple[str, list[str]]:
"""HC 룰 순서대로 적용. (최종 텍스트, 적용된 룰 라벨 리스트)."""
new = text
@@ -130,6 +164,11 @@ def apply_all_hc(text: str) -> tuple[str, list[str]]:
if r is not None:
new = r
applied.append("HC-4")
# HC-5 block math spacing (마지막 — HC-1~4 가 변경한 결과에도 적용)
r = hc5_block_math_spacing(new)
if r is not None:
new = r
applied.append("HC-5")
return new, applied
@@ -290,7 +329,7 @@ async def run(topic_id: int, exam_round: str, apply: bool, abort_threshold: int)
rule_counts[rl] = rule_counts.get(rl, 0) + 1
print("─── HC dry-run ───")
for rl in ["HC-1", "HC-2", "HC-3", "HC-4"]:
for rl in ["HC-1", "HC-2", "HC-3", "HC-4", "HC-5"]:
print(f" {rl}: {rule_counts.get(rl, 0)}")
print(f" 총 변경 대상 field: {len(hc_changes)}\n")