Add Type B slide pipeline and recipe rendering updates

This commit is contained in:
2026-04-15 16:39:50 +09:00
parent 51548fdc41
commit 66c00924ed
22 changed files with 6260 additions and 1322 deletions

View File

@@ -468,9 +468,9 @@ def build_containers_type_b(
inner_w = slide_width - pad * 2
# 역할을 zone별로 분류
top_roles = [] # zone=top
bottom_roles = [] # zone=bottom_left, bottom_right
footer_role = None # zone=footer
top_roles = [] # zone=top
bottom_roles = [] # zone=bottom (전체폭) 또는 bottom_left/bottom_right (2분할)
footer_role = None # zone=footer (Phase Y: 결론은 slide-base가 처리, 여기서 무시)
for role_name, info in page_structure.items():
if not isinstance(info, dict):
@@ -478,32 +478,46 @@ def build_containers_type_b(
zone = info.get("zone", "")
if zone == "top":
top_roles.append((role_name, info))
elif zone in ("bottom_left", "bottom_right"):
elif zone in ("bottom", "bottom_left", "bottom_right"):
bottom_roles.append((role_name, info))
elif zone == "footer":
footer_role = (role_name, info)
# 전체 가용 높이: 슬라이드 - 패딩*2 - 헤더 - gap
total_available = slide_height - pad * 2 - header_h - gap_block
# Phase Y: slide-base.html 기준으로 가용 높이 계산
# slide-base: .slide-body = top:65px, height:590px
# 하단 footer pill = 41px (slide-base가 관리, 여기서 빼지 않음)
slide_body_top = 65 # slide-base .slide-body top
slide_body_h = 590 # slide-base .slide-body height
total_available = slide_body_h
# footer 높이: weight 비율 (최소 보장)
footer_weight = footer_role[1].get("weight", 0.1) if footer_role else 0.1
footer_h_raw = int(total_available * footer_weight)
_footer_min = int(14 * tokens.get("line_height_ko", 1.7) + pad)
footer_h = max(_footer_min, footer_h_raw)
# footer zone이 있으면 기존 방식으로 공간 배분 (하위 호환)
# footer zone이 없으면 (Phase Y) slide-base footer가 처리 → 전체를 zone에 사용
if footer_role:
footer_weight = footer_role[1].get("weight", 0.1)
footer_h_raw = int(total_available * footer_weight)
_footer_min = int(14 * tokens.get("line_height_ko", 1.7) + pad)
footer_h = max(_footer_min, footer_h_raw)
middle_h = total_available - footer_h - gap_block
else:
footer_h = 0
middle_h = total_available
# 중간 영역: footer + gap 제외
middle_h = total_available - footer_h - gap_block
# Phase Y: zone 제목 + gap 공간 확보
zone_count = len(top_roles) + len(bottom_roles)
zone_title_h = 28 # zone 제목 높이 (assembler와 동일)
zone_gap = 16 # zone 간 여백 (assembler와 동일)
zone_overhead = zone_count * zone_title_h + max(0, zone_count - 1) * zone_gap
usable_h = middle_h - zone_overhead
# 상단/하단 높이: weight 비율로
# 상단/하단 높이: weight 비율로 (usable 영역에서)
top_weight = sum(info.get("weight", 0) for _, info in top_roles)
bottom_weight = sum(info.get("weight", 0) for _, info in bottom_roles)
total_mid_weight = top_weight + bottom_weight
if total_mid_weight <= 0:
total_mid_weight = 1
top_h = int(middle_h * top_weight / total_mid_weight)
bottom_h = middle_h - top_h - gap_small # gap_small: 상단-하단 사이
top_h = int(usable_h * top_weight / total_mid_weight)
bottom_h = usable_h - top_h
# 상단: 이미지가 있으면 좌텍스트+우이미지 나란히 → 폭 분할
img_ratio = 0
@@ -541,16 +555,20 @@ def build_containers_type_b(
},
)
# 하단 역할: 2분할
bottom_col_w = (inner_w - gap_block) // 2
# 하단 역할: zone에 따라 전체폭 또는 2분할
has_bottom_full = any(info.get("zone") == "bottom" for _, info in bottom_roles)
bottom_col_w = inner_w if has_bottom_full else (inner_w - gap_block) // 2
for role_name, info in bottom_roles:
zone = info.get("zone", "bottom_left")
w = inner_w if zone == "bottom" else bottom_col_w
specs[role_name] = ContainerSpec(
role=role_name,
zone=info.get("zone", "bottom_left"),
zone=zone,
topic_ids=info.get("topic_ids", []),
weight=info.get("weight", 0),
height_px=bottom_h,
width_px=bottom_col_w,
width_px=w,
max_height_cost=_max_allowed_height_cost(bottom_h),
block_constraints={},
)