From 1dc81e06929a3b73f5bdd947206f325f64addcf6 Mon Sep 17 00:00:00 2001 From: kyeongmin Date: Tue, 12 May 2026 22:20:30 +0900 Subject: [PATCH] feat(step14+step21): add zone_geometries_px artifact (IMP-01 #1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add slide-relative bbox export of .zone elements via getBoundingClientRect - Inline Selenium JS collects zone_geometries_px = [{position, template_id, x, y, w, h}] - write_debug_json adds top-level additive zone_geometries_px field - Existing visual_runtime_check / zones / frame_slot_metrics / PASS/FAIL logic unchanged - AI/Kei/V4/frame selection paths not touched Refs Gitea #1 (IMP-01 A-6 Zone DOM 좌표 export) --- src/phase_z2_pipeline.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/phase_z2_pipeline.py b/src/phase_z2_pipeline.py index 65a4a7f..5208614 100644 --- a/src/phase_z2_pipeline.py +++ b/src/phase_z2_pipeline.py @@ -850,11 +850,14 @@ def run_overflow_check(html_path: Path) -> dict: const slideM = measure(slide); slideM.size_correct = slide.clientWidth === 1280 && slide.clientHeight === 720; + // A-6 (IMP-01 #1) — slide-relative bbox base + const slideRect = slide.getBoundingClientRect(); const body = document.querySelector('.slide-body'); const bodyM = body ? measure(body) : null; const zones = []; + const zone_geometries_px = []; slide.querySelectorAll('.zone').forEach((z) => { const pos = z.getAttribute('data-zone-position') || 'unknown'; const tid = z.getAttribute('data-template-id') || '?'; @@ -862,6 +865,17 @@ def run_overflow_check(html_path: Path) -> dict: m.position = pos; m.template_id = tid; + // A-6 (IMP-01 #1) — zone bbox in slide-relative px (additive trace, no layout side effect) + const zoneRect = z.getBoundingClientRect(); + zone_geometries_px.push({ + position: pos, + template_id: tid, + x: Math.round(zoneRect.left - slideRect.left), + y: Math.round(zoneRect.top - slideRect.top), + w: Math.round(zoneRect.width), + h: Math.round(zoneRect.height), + }); + // 내부 clipping 검사 — frame-family root/cell 단위. // tolerance / threshold 그대로. inner_content_signals 만 추가 보강 (detection 데이터 늘림). const clipped = []; @@ -926,7 +940,7 @@ def run_overflow_check(html_path: Path) -> dict: }); }); - return { slide: slideM, slide_body: bodyM, zones, frame_slot_metrics }; + return { slide: slideM, slide_body: bodyM, zones, frame_slot_metrics, zone_geometries_px }; """) screenshot_path = html_path.parent / "preview.png" @@ -1228,6 +1242,8 @@ def write_debug_json(run_dir: Path, layout_preset: str, "composition_planner_debug": composition_debug, "zones": debug_zones, "visual_runtime_check": visual_runtime_check, + # A-6 (IMP-01 #1) — additive top-level zone bbox trace (slide-relative px) + "zone_geometries_px": (visual_runtime_check or {}).get("zone_geometries_px", []), } debug_path = run_dir / "debug.json" debug_path.write_text(json.dumps(debug, ensure_ascii=False, indent=2), encoding="utf-8")