""" /discovery/:id 리포트 페이지를 단일 HTML 파일로 내보낸다. npm run build && npx vite preview --port 3012 python3 docs/reports/landing/export_html.py taeha docs/reports/taeha/Taeha_AI_Answer_Readiness_Report.html - 헤드리스 Chrome으로 렌더된 DOM을 덤프하고 빌드 CSS를 인라인한다 - Navbar·Footer·스크립트 제거, motion 애니메이션 고정 - ScoreRing 오프셋과 카테고리 진행 바 폭을 점수 텍스트에서 다시 계산한다 """ import glob, re, subprocess, sys CHROME = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" BASE = "http://localhost:3012" slug, out = sys.argv[1], sys.argv[2] title = sys.argv[3] if len(sys.argv) > 3 else f"{slug} AI Answer Readiness Report" dom = subprocess.run( [CHROME, "--headless=new", "--disable-gpu", "--window-size=1440,900", "--virtual-time-budget=20000", "--dump-dom", f"{BASE}/discovery/{slug}"], capture_output=True, text=True).stdout css = open(glob.glob("dist/assets/*.css")[0], encoding="utf-8").read() m = re.search(r'
(.*)
\s*', dom, re.S) body = m.group(1) body = re.sub(r"", "", body, flags=re.S) body = re.sub(r"", "", body, flags=re.S, count=1) body = re.sub(r"", "", body, flags=re.S) # ScoreRing: dashoffset = circumference * (1 - score/100) def fix_ring(mm): circ = float(mm.group(2)); score = int(mm.group(5)) return f"{mm.group(1)}{circ * (1 - min(score, 100) / 100):.4f}{mm.group(4)}{score}{mm.group(6)}" body, n_ring = re.subn( r'(stroke-dasharray="([\d.]+)"\s+stroke-dashoffset=")([\d.]+)(">
]*>)(\d+)()', fix_ring, body) # 카테고리 바:
NN% def fix_bar(mm): return f'{mm.group(1)}width: {mm.group(3)}%;{mm.group(4)}{mm.group(3)}%{mm.group(5)}' body, n_bar = re.subn( r'(
]*>
)(\d+)(%)', lambda mm: f'{mm.group(1)}width: {mm.group(3)}%;{mm.group(2)}{mm.group(3)}{mm.group(4)}', body) html = f""" {title}
{body}
""" open(out, "w", encoding="utf-8").write(html) print(f"saved {out} ({len(html):,} bytes) rings={n_ring} bars={n_bar}")