Phase X-BX' XBX-1,3,5,6 완료: 유형 B 파이프라인 정상 동작

- XBX-1: normalizer 불릿 depth 보존 (D1/D2 마커) + 조립 로직 계층 반영
- XBX-3: 하단 구조 개선 — 하나의 큰 박스 안에 중제목 헤더 + 세로 구분선 2분할
- XBX-5: before→filled→after 파이프라인 연결 확인 (filled 2.2MB, 측정/재배분 정상)
- XBX-6: Type B에서 Sonnet 재구성 + renderer 스킵 — code_assembled 직접 사용
- final.html: 4,934 bytes → 2.2MB (Type B 정상 출력)
- Type A 코드 한 글자도 안 건드림

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-07 06:00:18 +09:00
parent 82f25caa6e
commit 17e77e310f
4 changed files with 114 additions and 23 deletions

View File

@@ -727,7 +727,7 @@ def _assemble_type_b(run: Path, ctx: dict):
links = " ".join(f'<span style="color:#2563eb;font-size:{font_size-2}px;cursor:pointer;">[{t}→]</span>' for t in popup_titles)
popup_html = f'<div style="position:absolute;top:4px;right:8px;text-align:right;z-index:1;">{links}</div>'
# 소제목(###) + 불릿을 카드형으로 분리
# 소제목(### 또는 D1:) + 불릿(D2:)을 카드형으로 분리
sections = [] # [(소제목, [불릿들])]
current_section = ("", [])
for line in content_lines:
@@ -735,6 +735,18 @@ def _assemble_type_b(run: Path, ctx: dict):
if current_section[0] or current_section[1]:
sections.append(current_section)
current_section = (line.lstrip("# ").strip(), [])
elif re.match(r'^D1:\s*', line):
# D1 = 1단 불릿 = 소제목 (카드 제목)
title_text = re.sub(r'^D1:\s*', '', line).lstrip("")
if current_section[0] or current_section[1]:
sections.append(current_section)
current_section = (bold(title_text, rn), [])
elif re.match(r'^D[2-9]:\s*', line):
# D2+ = 하위 불릿 = 본문
clean = re.sub(r'^D[2-9]:\s*', '', line).lstrip("")
if clean.startswith("출처:"):
continue
current_section[1].append(bold(clean, rn))
else:
clean = line.lstrip("")
if clean.startswith("출처:"):
@@ -864,9 +876,18 @@ def _assemble_type_b(run: Path, ctx: dict):
stripped = line.strip()
if not stripped:
continue
# D마커 제거 + depth별 스타일
depth = 1
dm = re.match(r'^D(\d+):\s*', stripped)
if dm:
depth = int(dm.group(1))
stripped = re.sub(r'^D\d+:\s*', '', stripped)
clean = stripped.lstrip("- ").lstrip("")
clean = bold(clean, rn)
bullets += f'<div style="padding-left:{bl_indent}px;font-size:{font_size}px;margin-bottom:2px;">• {clean}</div>\n'
pad = bl_indent * depth
fs = font_size if depth == 1 else font_size - 1
weight = "font-weight:600;" if depth == 1 else ""
bullets += f'<div style="padding-left:{pad}px;font-size:{fs}px;margin-bottom:2px;{weight}">• {clean}</div>\n'
bl_html = (
f'<div style="height:100%;padding:{gap_small}px;box-sizing:border-box;">'
@@ -897,10 +918,21 @@ def _assemble_type_b(run: Path, ctx: dict):
if not table_summaries:
# 표 요약 없으면 content 그대로
for line in content_lines_br:
clean = line.strip().lstrip("- ").lstrip("")
stripped = line.strip()
if not stripped:
continue
depth = 1
dm = re.match(r'^D(\d+):\s*', stripped)
if dm:
depth = int(dm.group(1))
stripped = re.sub(r'^D\d+:\s*', '', stripped)
clean = stripped.lstrip("- ").lstrip("")
if clean:
clean = bold(clean, rn)
bullets += f'<div style="padding-left:{bl_indent}px;font-size:{font_size}px;margin-bottom:2px;">• {clean}</div>\n'
pad = bl_indent * depth
fs = font_size if depth == 1 else font_size - 1
weight = "font-weight:600;" if depth == 1 else ""
bullets += f'<div style="padding-left:{pad}px;font-size:{fs}px;margin-bottom:2px;{weight}">• {clean}</div>\n'
# X'-6: 본문 표 요약이 있으면 하단 우측에 추가
table_summaries = enh.get("table_summaries", {})
@@ -976,13 +1008,13 @@ body{{background:#e5e5e5;padding:10px;font-family:'Pretendard Variable','Noto Sa
<div style="position:absolute;left:{pad}px;top:{top_top}px;width:{inner_w}px;height:{top_h}px;border:2px solid {_color_palette[0]};border-radius:6px;overflow:hidden;">
{top_html}</div>
<div style="position:absolute;left:{pad}px;top:{bottom_top}px;width:{inner_w}px;height:{bottom_h}px;">
<div style="font-weight:700;font-size:{font_size+1}px;color:#1a365d;margin-bottom:4px;">{bold(bottom_title, "")}</div>
<div style="display:flex;gap:{gap_block}px;height:calc(100% - {int(font_size*1.5 + 4)}px);">
<div style="flex:1;border:2px solid {_color_palette[1]};border-radius:6px;overflow:hidden;">
<div style="position:absolute;left:{pad}px;top:{bottom_top}px;width:{inner_w}px;height:{bottom_h}px;border:2px solid {_color_palette[1]};border-radius:6px;overflow:hidden;">
<div style="font-weight:700;font-size:{font_size+1}px;color:#1a365d;padding:{gap_small}px {gap_small}px 4px;border-bottom:1px solid #e2e8f0;">{bold(bottom_title, "")}</div>
<div style="display:flex;height:calc(100% - {int(font_size*1.5 + gap_small + 5)}px);">
<div style="flex:1;overflow:hidden;">
{bl_html}</div>
<div style="flex:1;border:2px solid {_color_palette[2]};border-radius:6px;overflow:hidden;">
<div style="width:1px;background:#cbd5e1;flex-shrink:0;"></div>
<div style="flex:1;overflow:hidden;">
{br_html}</div>
</div></div>