feat(IMP-14): A-4 — slide_base embedded vs standalone mode contract

Step 13 owns iframe-vs-standalone CSS contract in slide_base.html via
3-valued embedded_mode enum (auto / embedded / standalone). Removes
SlideCanvas.tsx runtime CSS injection workaround; frontend now passes
?embedded=1 query so auto-mode script attaches html.embedded class and
scopes the standalone body centering/min-height/padding reset.

- templates/phase_z2/slide_base.html: conditional html.embedded class +
  CSP-safe auto-mode <script> + additive html.embedded body/.slide rules
- src/phase_z2_pipeline.py: render_slide gains keyword-only embedded_mode
  ("auto" default) + ValueError guard; 3 existing call sites unchanged
- Front/client/src/components/SlideCanvas.tsx: derive embeddedSrc with
  ?embedded=1 (query-preserving), drop reset CSS injection block
- tests/phase_z2/test_slide_base_embedded_mode.py: 6 cases — auto script,
  CSS rules, embedded/standalone explicit modes, byte-determinism,
  invalid-mode guard
This commit is contained in:
2026-05-18 07:21:31 +09:00
parent 7d5639ad72
commit 7a52cebfaa
4 changed files with 120 additions and 24 deletions

View File

@@ -2021,12 +2021,23 @@ def _attempt_salvage_chain(
def render_slide(slide_title: str, slide_footer: Optional[str],
zones_data: list[dict], layout_preset: str,
layout_css: dict, gap_px: int = GRID_GAP) -> str:
layout_css: dict, gap_px: int = GRID_GAP,
*, embedded_mode: str = "auto") -> str:
"""Single slide HTML — slide_base.html + 8-preset layout vocabulary.
layout_css = build_layout_css() 결과 — areas/cols/rows 문자열 + 동적 heights flag.
Template 은 layout_css.{areas,cols,rows} 를 grid CSS 에 직접 주입.
embedded_mode (IMP-14): "auto" | "embedded" | "standalone". Controls
slide_base.html body CSS contract. Default "auto" preserves backward-compat
with run_overflow_check standalone path and lets iframe consumers signal via
?embedded=1 or window.self!==window.top.
"""
if embedded_mode not in ("auto", "embedded", "standalone"):
raise ValueError(
f"render_slide: invalid embedded_mode={embedded_mode!r}; "
"expected one of 'auto', 'embedded', 'standalone'"
)
env = Environment(
loader=FileSystemLoader(str(TEMPLATE_DIR)),
autoescape=select_autoescape(["html"]),
@@ -2050,6 +2061,7 @@ def render_slide(slide_title: str, slide_footer: Optional[str],
layout_css=layout_css,
gap_px=gap_px,
token_css=_read_token_css(),
embedded_mode=embedded_mode,
)