Phase X'-1~5 완료: 제목/들여쓰기/캡션/빈칸/카드 디자인
X'-1: 제목 원본 MDX frontmatter에서 가져오기 (Kei가 바꾸지 않음) X'-2: 들여쓰기 계층 (소제목→불릿 indent 적용) X'-3: 이미지 캡션 normalized.images alt text에서 추출 X'-4: 상단 컨테이너 justify-content:space-between X'-5: 카드 디자인 다크 그라데이션 + 밝은 텍스트 X'-6 미완료: 본문 표(팝업 아닌)를 하단 우측에 Kei 요약 배치 → 다음 세션 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -726,33 +726,51 @@ 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'<div class="bl" style="font-size:{font_size}px;"><span class="bl-m">•</span><span class="bl-t">{item}</span></div>'
|
||||
f'<div style="padding-left:{indent_body}px;margin-bottom:1px;">'
|
||||
f'<span style="color:{text_color};font-size:{font_size-1}px;line-height:1.5;">• {item}</span></div>'
|
||||
for item in sec_items
|
||||
)
|
||||
if sec_title:
|
||||
bullets += (
|
||||
f'<div style="background:#1e293b;color:#fff;border-radius:4px;'
|
||||
f'padding:{int(font_size*0.4)}px {int(font_size*0.6)}px;margin-bottom:{card_gap}px;">'
|
||||
f'<div style="font-size:{font_size}px;font-weight:700;color:#fbbf24;margin-bottom:2px;">{bold(sec_title, rn)}</div>'
|
||||
f'<div style="font-size:{font_size-1}px;line-height:1.5;">{items_html}</div></div>\n'
|
||||
f'<div style="background:{bg};border-radius:{int(font_size*0.4)}px;'
|
||||
f'padding:{card_pad}px {int(card_pad*1.5)}px;margin-bottom:{card_gap}px;">'
|
||||
f'<div style="font-size:{font_size}px;font-weight:700;color:#fbbf24;'
|
||||
f'margin-bottom:{int(font_size*0.3)}px;">{bold(sec_title, rn)}</div>'
|
||||
f'{items_html}</div>\n'
|
||||
)
|
||||
else:
|
||||
bullets += items_html
|
||||
else:
|
||||
# 소제목 없는 경우 → 일반 불릿
|
||||
for sec_title, sec_items in sections:
|
||||
for item in sec_items:
|
||||
bullets += f'<div class="bl" style="font-size:{font_size}px;"><span class="bl-m">•</span><span class="bl-t">{item}</span></div>\n'
|
||||
bullets += (
|
||||
f'<div style="padding-left:{indent_body}px;margin-bottom:1px;">'
|
||||
f'<span style="font-size:{font_size}px;">• {item}</span></div>\n'
|
||||
)
|
||||
|
||||
# 이미지 캡션: 출처 → [이미지:] 마커 → 없으면 빈 문자열
|
||||
# X'-3: 이미지 캡션 — normalized.images alt → 출처 → [이미지:] 마커 순
|
||||
img_caption = ""
|
||||
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("출처:"):
|
||||
@@ -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'<div style="position:relative;height:100%;padding:{gap_small}px;box-sizing:border-box;">'
|
||||
f'<div style="position:relative;height:100%;padding:{gap_small}px;box-sizing:border-box;'
|
||||
f'display:flex;flex-direction:column;justify-content:space-between;">'
|
||||
f'{popup_html}'
|
||||
f'<div style="font-weight:700;font-size:{font_size+1}px;color:#1a365d;margin-bottom:4px;">{topic_title}</div>'
|
||||
f'<div style="display:flex;gap:{max(6, int(font_size*0.8))}px;align-items:flex-start;">'
|
||||
f'<div style="display:flex;gap:{max(6, int(font_size*0.8))}px;align-items:flex-start;flex:1;">'
|
||||
f'<div style="flex:1;font-size:{font_size}px;line-height:1.55;color:#333;">{bullets}</div>'
|
||||
f'{img_block}</div></div>'
|
||||
)
|
||||
@@ -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
|
||||
if stripped.startswith("### "):
|
||||
# 소제목
|
||||
sub_title = stripped.lstrip("# ").strip()
|
||||
bullets += f'<div style="font-weight:700;font-size:{font_size}px;color:#1e40af;margin-top:{int(font_size*0.4)}px;">{bold(sub_title, rn)}</div>\n'
|
||||
else:
|
||||
clean = stripped.lstrip("• ")
|
||||
clean = bold(clean, rn)
|
||||
bullets += f'<div class="bl" style="font-size:{font_size}px;"><span class="bl-m">•</span><span class="bl-t">{clean}</span></div>\n'
|
||||
bullets += f'<div style="padding-left:{bl_indent}px;font-size:{font_size}px;margin-bottom:1px;">• {clean}</div>\n'
|
||||
|
||||
bl_html = (
|
||||
f'<div style="height:100%;padding:{gap_small}px;box-sizing:border-box;">'
|
||||
f'<div style="font-weight:700;font-size:{font_size+1}px;color:#1a365d;margin-bottom:4px;">{topic_title}</div>'
|
||||
f'<div style="font-size:{font_size}px;line-height:1.55;color:#333;">{bullets}</div></div>'
|
||||
f'<div style="line-height:1.55;color:#333;">{bullets}</div></div>'
|
||||
)
|
||||
|
||||
# 하단 우측
|
||||
|
||||
@@ -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"),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user