diff --git a/scripts/assemble_stage2.py b/scripts/assemble_stage2.py
index 452efa6..e4c501c 100644
--- a/scripts/assemble_stage2.py
+++ b/scripts/assemble_stage2.py
@@ -726,38 +726,56 @@ def _assemble_type_b(run: Path, ctx: dict):
if current_section[0] or current_section[1]:
sections.append(current_section)
- # 카드형 HTML 생성
+ # X'-2: 카드형 HTML — 소제목별 들여쓰기 계층
+ # X'-5: 카드 디자인 — 다크 그라데이션 배경, 밝은 텍스트
+ _card_colors = [
+ ("linear-gradient(135deg, #1a365d, #2d3748)", "#e2e8f0"),
+ ("linear-gradient(135deg, #1e3a2f, #2d4a3e)", "#e2e8f0"),
+ ("linear-gradient(135deg, #3b1f2b, #4a2d3b)", "#e2e8f0"),
+ ("linear-gradient(135deg, #2d2b55, #3d3b65)", "#e2e8f0"),
+ ]
+ card_pad = int(font_size * 0.6)
+ card_gap = max(3, int(font_size * 0.4))
+ indent_body = int(font_size * 1.2) # 본문 들여쓰기
+
bullets = ""
if len(sections) > 1 and sections[0][0]:
- # 소제목이 있는 경우 → 카드형
- card_gap = max(3, int(font_size * 0.4))
- for sec_title, sec_items in sections:
+ for ci, (sec_title, sec_items) in enumerate(sections):
+ bg, text_color = _card_colors[ci % len(_card_colors)]
items_html = "".join(
- f'
•{item}
'
+ f''
+ f'• {item}
'
for item in sec_items
)
if sec_title:
bullets += (
- f''
- f'
{bold(sec_title, rn)}
'
- f'
{items_html}
\n'
+ f''
+ f'
{bold(sec_title, rn)}
'
+ f'{items_html}
\n'
)
else:
bullets += items_html
else:
- # 소제목 없는 경우 → 일반 불릿
for sec_title, sec_items in sections:
for item in sec_items:
- bullets += f'•{item}
\n'
+ bullets += (
+ f''
+ f'• {item}
\n'
+ )
- # 이미지 캡션: 출처 → [이미지:] 마커 → 없으면 빈 문자열
+ # X'-3: 이미지 캡션 — normalized.images alt → 출처 → [이미지:] 마커 순
img_caption = ""
- for line in all_text.split("\n"):
- stripped = line.strip().lstrip("• ")
- if stripped.startswith("출처:"):
- img_caption = re.sub(r'^출처:\s*', '', stripped)
- break
+ norm_images = ctx.get("normalized", {}).get("images", [])
+ if norm_images:
+ img_caption = norm_images[0].get("alt", "")
+ if not img_caption:
+ for line in all_text.split("\n"):
+ stripped = line.strip().lstrip("• ")
+ if stripped.startswith("출처:"):
+ img_caption = re.sub(r'^출처:\s*', '', stripped)
+ break
if not img_caption:
img_marker = re.search(r'\[이미지:\s*([^\]]+)\]', all_text)
if img_marker:
@@ -778,11 +796,13 @@ def _assemble_type_b(run: Path, ctx: dict):
primary_topic = topic_map.get(tids[0], {}) if tids else {}
topic_title = bold(primary_topic.get("title", ""), rn)
+ # X'-4: 상단 컨테이너 — 내용을 전체 높이에 균등 배분
top_html = (
- f''
+ f'
'
f'{popup_html}'
f'
{topic_title}
'
- f'
'
+ f'
'
f'
{bullets}
'
f'{img_block}
'
)
@@ -798,19 +818,26 @@ def _assemble_type_b(run: Path, ctx: dict):
primary_topic = topic_map.get(tids[0], {}) if tids else {}
topic_title = bold(primary_topic.get("title", ""), rn)
+ # X'-2: 들여쓰기 계층 (소제목+불릿)
+ bl_indent = int(font_size * 1.2)
bullets = ""
for line in all_text.split("\n"):
stripped = line.strip()
if not stripped or re.search(r'\[팝업:|\[이미지:', stripped):
continue
- clean = stripped.lstrip("• ")
- clean = bold(clean, rn)
- bullets += f'
•{clean}
\n'
+ if stripped.startswith("### "):
+ # 소제목
+ sub_title = stripped.lstrip("# ").strip()
+ bullets += f'
{bold(sub_title, rn)}
\n'
+ else:
+ clean = stripped.lstrip("• ")
+ clean = bold(clean, rn)
+ bullets += f'
• {clean}
\n'
bl_html = (
f'
'
f'
{topic_title}
'
- f'
{bullets}
'
+ f'
{bullets}
'
)
# 하단 우측
diff --git a/src/pipeline.py b/src/pipeline.py
index 5071b57..8480c35 100644
--- a/src/pipeline.py
+++ b/src/pipeline.py
@@ -172,9 +172,11 @@ async def generate_slide(
page_struct_raw = analysis_raw.get("page_structure", {})
page_structure = PageStructure(roles=page_struct_raw)
+ # X'-1: 제목은 원본 MDX frontmatter에서 가져옴 (Kei가 바꾸지 않음)
+ original_title = context.normalized.title or analysis_raw.get("title", "")
analysis = Analysis(
core_message=analysis_raw.get("core_message", ""),
- title=analysis_raw.get("title", ""),
+ title=original_title,
total_pages=analysis_raw.get("total_pages", 1),
layout_template=analysis_raw.get("layout_template", "A"),
)