From 688ddbbb174d0bcff76a0fb6bc0ccc020d52a4d5 Mon Sep 17 00:00:00 2001 From: kyeongmin Date: Wed, 25 Mar 2026 18:47:13 +0900 Subject: [PATCH] =?UTF-8?q?04.=20design=5Fagent=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=E2=80=94=20=EC=BD=98=ED=85=90=EC=B8=A0=20=EC=8B=9C=EA=B0=81=20?= =?UTF-8?q?=EA=B5=AC=EC=A1=B0=ED=99=94=20=EC=8A=AC=EB=9D=BC=EC=9D=B4?= =?UTF-8?q?=EB=93=9C=20=EC=83=9D=EC=84=B1=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 5단계 AI 파이프라인: 1. Kei 실장(Opus via Kei API) — 꼭지 추출 + 정보 구조 파악 2. 디자인 팀장 — FAISS 블록 검색 + Opus 추천 + Sonnet 블록 매핑 3. Kei 편집자(Kei API) — 도메인 전문 텍스트 정리 4. 디자인 실무자(Sonnet + Jinja2) — CSS 변수 조정 + HTML 조립 5. 디자인 팀장(Sonnet) — 균형 재검토 (최대 2회 루프) 블록 라이브러리 46개 (6 카테고리) + _legacy 13개 FAISS 블록 검색 (bge-m3, 1024차원) SVG N개 동적 배치 (cos/sin 좌표 계산) Pillow 이미지 크기 측정 + base64 인라인 컨테이너 예산 기반 블록 배치 (zone별 높이 px) Co-Authored-By: Claude Opus 4.6 (1M context) --- 04. design_agent/.env.example | 3 + 04. design_agent/.gitignore | 11 + 04. design_agent/CLAUDE.md | 516 +++++++++++ 04. design_agent/IMPROVEMENT-PHASE-A.md | 491 +++++++++++ 04. design_agent/IMPROVEMENT-PHASE-B.md | 368 ++++++++ 04. design_agent/IMPROVEMENT-PHASE-C.md | 177 ++++ 04. design_agent/IMPROVEMENT-PHASE-D.md | 354 ++++++++ 04. design_agent/IMPROVEMENT.md | 309 +++++++ 04. design_agent/PLAN.md | 373 ++++++++ 04. design_agent/PROGRESS.md | 237 +++++ 04. design_agent/README.md | 281 ++++++ 04. design_agent/docs/BLOCK_SLOTS_45.py | 105 +++ .../docs/FIGMA-COMPONENT-EXTRACTION-PLAN.md | 582 +++++++++++++ 04. design_agent/docs/PHASE2-PLAN.md | 245 ++++++ 04. design_agent/docs/PHASE2-PROCESS.md | 321 +++++++ 04. design_agent/docs/PHASE2-TECH-REVIEW.md | 396 +++++++++ 04. design_agent/docs/RESEARCH.md | 686 +++++++++++++++ 04. design_agent/docs/bg-texture-only.png | Bin 0 -> 958376 bytes .../docs/block-tests/card-image.html | 219 +++++ .../block-tests/cards_card-compare-3col.html | 162 ++++ .../block-tests/cards_card-dark-overlay.html | 174 ++++ .../block-tests/cards_card-icon-desc.html | 114 +++ .../block-tests/cards_card-image-3col.html | 199 +++++ .../block-tests/cards_card-image-round.html | 133 +++ .../docs/block-tests/cards_card-numbered.html | 136 +++ .../block-tests/cards_card-stat-number.html | 129 +++ .../block-tests/cards_card-step-vertical.html | 170 ++++ .../block-tests/cards_card-tag-image.html | 132 +++ .../block-tests/cards_card-text-grid.html | 137 +++ .../docs/block-tests/circle-label.html | 133 +++ .../docs/block-tests/compare-box.html | 124 +++ .../docs/block-tests/comparison-table.html | 278 ++++++ .../docs/block-tests/conclusion-bar.html | 93 ++ .../block-tests/emphasis_banner-gradient.html | 88 ++ .../emphasis_callout-solution.html | 104 +++ .../block-tests/emphasis_callout-warning.html | 95 +++ .../block-tests/emphasis_comparison-2col.html | 111 +++ .../emphasis_conclusion-accent-bar.html | 94 ++ .../emphasis_dark-bullet-list.html | 105 +++ .../block-tests/emphasis_divider-text.html | 80 ++ .../block-tests/emphasis_highlight-strip.html | 87 ++ .../block-tests/emphasis_quote-big-mark.html | 107 +++ .../emphasis_quote-left-border.html | 85 ++ .../block-tests/emphasis_quote-question.html | 92 ++ .../block-tests/emphasis_tab-label-row.html | 95 +++ .../headers_section-header-bar.html | 83 ++ .../headers_section-title-with-bg.html | 123 +++ .../block-tests/headers_topic-center.html | 91 ++ .../block-tests/headers_topic-left-right.html | 95 +++ .../block-tests/headers_topic-numbered.html | 105 +++ .../docs/block-tests/image-row.html | 120 +++ .../block-tests/media_image-before-after.html | 108 +++ .../block-tests/media_image-full-caption.html | 80 ++ .../block-tests/media_image-grid-2x2.html | 111 +++ .../block-tests/media_image-row-2col.html | 100 +++ .../block-tests/media_image-side-text.html | 130 +++ .../docs/block-tests/quote-block.html | 105 +++ .../docs/block-tests/section-title.html | 143 ++++ .../tables_compare-2col-split.html | 141 +++ .../tables_compare-3col-badge.html | 219 +++++ .../tables_table-simple-striped.html | 154 ++++ .../docs/block-tests/topic-header.html | 115 +++ .../docs/block-tests/venn_premium_bg.png | Bin 0 -> 1103945 bytes .../block-tests/visuals_circle-gradient.html | 113 +++ .../visuals_compare-pill-pair.html | 131 +++ .../visuals_flow-arrow-horizontal.html | 104 +++ .../visuals_keyword-circle-row.html | 153 ++++ .../block-tests/visuals_layer-diagram.html | 134 +++ .../visuals_process-horizontal.html | 142 +++ .../visuals_pyramid-hierarchy.html | 121 +++ .../visuals_timeline-horizontal.html | 113 +++ .../visuals_timeline-vertical.html | 167 ++++ .../block-tests/visuals_venn-2items-p2.html | 151 ++++ .../block-tests/visuals_venn-3items-p2.html | 161 ++++ .../docs/block-tests/visuals_venn-3items.html | 165 ++++ .../block-tests/visuals_venn-4items-p2.html | 171 ++++ .../docs/block-tests/visuals_venn-4items.html | 175 ++++ .../block-tests/visuals_venn-5items-p2.html | 181 ++++ .../docs/block-tests/visuals_venn-5items.html | 185 ++++ .../block-tests/visuals_venn-7items-p2.html | 201 +++++ .../block-tests/visuals_venn-diagram.html | 172 ++++ .../block-tests/visuals_venn-premium.html | 104 +++ .../block-tests/visuals_venn-svg-premium.html | 121 +++ .../docs/figma-analysis/1-1_d1.json | 1 + .../docs/figma-analysis/1-1_raw.json | 1 + .../docs/figma-analysis/1-1_미래_raw.json | 1 + .../docs/figma-analysis/2-1_01_detail.json | 1 + .../docs/figma-analysis/2-1_02_detail.json | 1 + .../docs/figma-analysis/DESIGN-ANALYSIS.md | 254 ++++++ .../docs/figma-assets/2-1_02_full_frame.png | Bin 0 -> 802768 bytes .../docs/figma-assets/2-2_01_full_hd.png | Bin 0 -> 1431195 bytes .../docs/figma-assets/2-2_01_icon_card_1.png | Bin 0 -> 6979 bytes .../docs/figma-assets/2-2_01_icon_card_2.png | Bin 0 -> 1079 bytes .../docs/figma-assets/2-2_01_icon_card_3.png | Bin 0 -> 497 bytes .../docs/figma-assets/2-2_01_icon_card_4.png | Bin 0 -> 6143 bytes .../docs/figma-assets/2-2_01_icon_card_5.png | Bin 0 -> 852 bytes .../docs/figma-assets/2-2_02_full_hd.png | Bin 0 -> 763715 bytes .../docs/figma-assets/2-2_03_full_hd.png | Bin 0 -> 734109 bytes .../docs/figma-assets/2-2_04_full_hd.png | Bin 0 -> 986863 bytes .../docs/figma-assets/2-2_icon_card_1.png | Bin 0 -> 279281 bytes .../docs/figma-assets/2-2_icon_card_2.png | Bin 0 -> 342391 bytes .../docs/figma-assets/2-2_icon_card_3.png | Bin 0 -> 344625 bytes .../docs/figma-assets/2-2_icon_card_4.png | Bin 0 -> 467625 bytes .../docs/figma-assets/2-2_icon_card_5.png | Bin 0 -> 272770 bytes .../docs/figma-assets/bg_header.png | Bin 0 -> 534270 bytes .../docs/figma-assets/bim_card_3col.png | Bin 0 -> 1692 bytes .../docs/figma-assets/bim_circle_label.png | Bin 0 -> 170 bytes .../figma-assets/bim_comparison_table.png | Bin 0 -> 162 bytes .../docs/figma-assets/bim_mountain_viz.png | Bin 0 -> 162 bytes .../docs/figma-assets/breadcrumb_area.png | Bin 0 -> 10565 bytes .../docs/figma-assets/card_3col.png | Bin 0 -> 397747 bytes .../docs/figma-assets/card_area.png | Bin 0 -> 384321 bytes .../figma-assets/card_img_construction.png | Bin 0 -> 101276 bytes .../docs/figma-assets/card_img_design.png | Bin 0 -> 103202 bytes .../docs/figma-assets/card_img_design_3d.png | Bin 0 -> 646 bytes .../docs/figma-assets/card_img_design_grp.png | Bin 0 -> 3761 bytes .../figma-assets/card_img_maintenance.png | Bin 0 -> 103563 bytes .../figma-assets/card_text_construction.png | Bin 0 -> 196 bytes .../docs/figma-assets/card_text_design.png | Bin 0 -> 242 bytes .../figma-assets/card_text_maintenance.png | Bin 0 -> 183 bytes .../docs/figma-assets/circle_label.png | Bin 0 -> 89021 bytes .../docs/figma-assets/close_btn.svg | 5 + .../docs/figma-assets/compare_box_left.png | Bin 0 -> 53803 bytes .../docs/figma-assets/compare_box_right.png | Bin 0 -> 54376 bytes .../docs/figma-assets/dx_bim_table.png | Bin 0 -> 158251 bytes .../docs/figma-assets/image_grid_left.png | Bin 0 -> 868147 bytes .../docs/figma-assets/image_grid_right.png | Bin 0 -> 953154 bytes .../docs/figma-assets/mountain_viz.png | Bin 0 -> 99708 bytes .../docs/figma-assets/section_title.png | Bin 0 -> 8628 bytes .../docs/figma-assets/topic_header.png | Bin 0 -> 32688 bytes .../docs/figma-assets/venn_bg.png | Bin 0 -> 1023582 bytes .../docs/figma-assets/venn_premium_bg.png | Bin 0 -> 1103945 bytes .../docs/figma-screenshots/1-1_미래.png | Bin 0 -> 322041 bytes .../docs/figma-screenshots/1장_1-1_미래.png | Bin 0 -> 322295 bytes .../docs/figma-screenshots/1장_가치.png | Bin 0 -> 296313 bytes .../docs/figma-screenshots/1장_기술.png | Bin 0 -> 212542 bytes .../figma-screenshots/2-1_01_건설산업.png | Bin 0 -> 156807 bytes .../docs/figma-screenshots/2-1_02_BIM.png | Bin 0 -> 262815 bytes .../docs/figma-screenshots/2-1_03_GIS.png | Bin 0 -> 412755 bytes .../figma-screenshots/2-1_04_디지털트윈.png | Bin 0 -> 322931 bytes .../docs/figma-screenshots/2-2_01.png | Bin 0 -> 463040 bytes .../docs/figma-screenshots/2-2_02.png | Bin 0 -> 252358 bytes .../docs/figma-screenshots/2-2_03.png | Bin 0 -> 256046 bytes .../docs/figma-screenshots/2-2_04.png | Bin 0 -> 314711 bytes .../figma-screenshots/2-2장_자세히보기.png | Bin 0 -> 432594 bytes .../docs/figma-screenshots/2-3_01.png | Bin 0 -> 468598 bytes .../docs/figma-screenshots/2-3_02.png | Bin 0 -> 379626 bytes .../docs/figma-screenshots/2-3_03.png | Bin 0 -> 653121 bytes .../docs/figma-screenshots/2-3_04.png | Bin 0 -> 590756 bytes .../docs/figma-screenshots/2-3_05.png | Bin 0 -> 407295 bytes .../docs/figma-screenshots/가치.png | Bin 0 -> 296331 bytes .../docs/figma-screenshots/기술.png | Bin 0 -> 212593 bytes 04. design_agent/docs/test-bg-layer.png | Bin 0 -> 1126112 bytes 04. design_agent/docs/test-bim-auto.html | 806 ++++++++++++++++++ 04. design_agent/docs/test-bim-page-v2.html | 401 +++++++++ 04. design_agent/docs/test-bim-page.html | 328 +++++++ .../docs/test-gemini-infographic.png | Bin 0 -> 1171859 bytes 04. design_agent/docs/test-layered-slide.html | 206 +++++ 04. design_agent/docs/test-layered-v2.html | 222 +++++ 04. design_agent/docs/test-relation.html | 4 + 04. design_agent/docs/test-relation.svg | 4 + 04. design_agent/docs/test-simple.html | 4 + 04. design_agent/docs/test-simple.svg | 4 + 04. design_agent/docs/test-slide-output.html | 402 +++++++++ 04. design_agent/package.json | 29 + 04. design_agent/pyproject.toml | 33 + 04. design_agent/scripts/build_block_index.py | 162 ++++ 04. design_agent/src/__init__.py | 0 04. design_agent/src/block_search.py | 208 +++++ 04. design_agent/src/config.py | 20 + 04. design_agent/src/content_editor.py | 311 +++++++ 04. design_agent/src/design_director.py | 622 ++++++++++++++ 04. design_agent/src/image_utils.py | 138 +++ 04. design_agent/src/kei_client.py | 258 ++++++ 04. design_agent/src/main.py | 61 ++ 04. design_agent/src/pipeline.py | 361 ++++++++ 04. design_agent/src/renderer.py | 343 ++++++++ 04. design_agent/src/svg_calculator.py | 156 ++++ 04. design_agent/static/base.css | 84 ++ 04. design_agent/static/index.html | 265 ++++++ 04. design_agent/static/tokens.css | 42 + 04. design_agent/templates/blocks/INDEX.md | 112 +++ .../templates/blocks/_legacy/card-grid.html | 65 ++ .../templates/blocks/_legacy/card-image.html | 95 +++ .../blocks/_legacy/circle-label.html | 56 ++ .../templates/blocks/_legacy/compare-box.html | 67 ++ .../blocks/_legacy/comparison-table.html | 97 +++ .../templates/blocks/_legacy/comparison.html | 51 ++ .../blocks/_legacy/conclusion-bar.html | 38 + .../templates/blocks/_legacy/image-row.html | 39 + .../templates/blocks/_legacy/process.html | 61 ++ .../templates/blocks/_legacy/quote-block.html | 29 + .../blocks/_legacy/relationship.html | 88 ++ .../blocks/_legacy/section-title.html | 69 ++ .../blocks/_legacy/topic-header.html | 38 + .../blocks/cards/card-compare-3col.html | 74 ++ .../blocks/cards/card-dark-overlay.html | 77 ++ .../blocks/cards/card-icon-desc.html | 48 ++ .../blocks/cards/card-image-3col.html | 96 +++ .../blocks/cards/card-image-round.html | 62 ++ .../templates/blocks/cards/card-numbered.html | 60 ++ .../blocks/cards/card-stat-number.html | 57 ++ .../blocks/cards/card-step-vertical.html | 93 ++ .../blocks/cards/card-tag-image.html | 63 ++ .../blocks/cards/card-text-grid.html | 66 ++ .../blocks/emphasis/banner-gradient.html | 33 + .../blocks/emphasis/callout-solution.html | 55 ++ .../blocks/emphasis/callout-warning.html | 45 + .../blocks/emphasis/comparison-2col.html | 52 ++ .../emphasis/conclusion-accent-bar.html | 38 + .../blocks/emphasis/dark-bullet-list.html | 48 ++ .../blocks/emphasis/details-block.html | 67 ++ .../blocks/emphasis/divider-text.html | 32 + .../blocks/emphasis/highlight-strip.html | 31 + .../blocks/emphasis/quote-big-mark.html | 59 ++ .../blocks/emphasis/quote-left-border.html | 30 + .../blocks/emphasis/quote-question.html | 37 + .../blocks/emphasis/tab-label-row.html | 39 + .../blocks/headers/section-header-bar.html | 32 + .../blocks/headers/section-title-with-bg.html | 69 ++ .../blocks/headers/topic-center.html | 41 + .../blocks/headers/topic-left-right.html | 39 + .../blocks/headers/topic-numbered.html | 57 ++ .../blocks/media/image-before-after.html | 60 ++ .../blocks/media/image-full-caption.html | 32 + .../blocks/media/image-grid-2x2.html | 42 + .../blocks/media/image-row-2col.html | 40 + .../blocks/media/image-side-text.html | 71 ++ .../blocks/tables/compare-2col-split.html | 71 ++ .../blocks/tables/compare-3col-badge.html | 110 +++ .../blocks/tables/table-simple-striped.html | 58 ++ .../blocks/visuals/circle-gradient.html | 58 ++ .../blocks/visuals/compare-pill-pair.html | 74 ++ .../blocks/visuals/flow-arrow-horizontal.html | 34 + .../blocks/visuals/keyword-circle-row.html | 56 ++ .../blocks/visuals/layer-diagram.html | 50 ++ .../blocks/visuals/process-horizontal.html | 61 ++ .../blocks/visuals/pyramid-hierarchy.html | 40 + .../blocks/visuals/timeline-horizontal.html | 37 + .../blocks/visuals/timeline-vertical.html | 74 ++ .../blocks/visuals/venn-diagram.html | 125 +++ 04. design_agent/templates/catalog.yaml | 739 ++++++++++++++++ 04. design_agent/templates/slide-base.html | 61 ++ 04. design_agent/tests/__init__.py | 0 244 files changed, 23955 insertions(+) create mode 100644 04. design_agent/.env.example create mode 100644 04. design_agent/.gitignore create mode 100644 04. design_agent/CLAUDE.md create mode 100644 04. design_agent/IMPROVEMENT-PHASE-A.md create mode 100644 04. design_agent/IMPROVEMENT-PHASE-B.md create mode 100644 04. design_agent/IMPROVEMENT-PHASE-C.md create mode 100644 04. design_agent/IMPROVEMENT-PHASE-D.md create mode 100644 04. design_agent/IMPROVEMENT.md create mode 100644 04. design_agent/PLAN.md create mode 100644 04. design_agent/PROGRESS.md create mode 100644 04. design_agent/README.md create mode 100644 04. design_agent/docs/BLOCK_SLOTS_45.py create mode 100644 04. design_agent/docs/FIGMA-COMPONENT-EXTRACTION-PLAN.md create mode 100644 04. design_agent/docs/PHASE2-PLAN.md create mode 100644 04. design_agent/docs/PHASE2-PROCESS.md create mode 100644 04. design_agent/docs/PHASE2-TECH-REVIEW.md create mode 100644 04. design_agent/docs/RESEARCH.md create mode 100644 04. design_agent/docs/bg-texture-only.png create mode 100644 04. design_agent/docs/block-tests/card-image.html create mode 100644 04. design_agent/docs/block-tests/cards_card-compare-3col.html create mode 100644 04. design_agent/docs/block-tests/cards_card-dark-overlay.html create mode 100644 04. design_agent/docs/block-tests/cards_card-icon-desc.html create mode 100644 04. design_agent/docs/block-tests/cards_card-image-3col.html create mode 100644 04. design_agent/docs/block-tests/cards_card-image-round.html create mode 100644 04. design_agent/docs/block-tests/cards_card-numbered.html create mode 100644 04. design_agent/docs/block-tests/cards_card-stat-number.html create mode 100644 04. design_agent/docs/block-tests/cards_card-step-vertical.html create mode 100644 04. design_agent/docs/block-tests/cards_card-tag-image.html create mode 100644 04. design_agent/docs/block-tests/cards_card-text-grid.html create mode 100644 04. design_agent/docs/block-tests/circle-label.html create mode 100644 04. design_agent/docs/block-tests/compare-box.html create mode 100644 04. design_agent/docs/block-tests/comparison-table.html create mode 100644 04. design_agent/docs/block-tests/conclusion-bar.html create mode 100644 04. design_agent/docs/block-tests/emphasis_banner-gradient.html create mode 100644 04. design_agent/docs/block-tests/emphasis_callout-solution.html create mode 100644 04. design_agent/docs/block-tests/emphasis_callout-warning.html create mode 100644 04. design_agent/docs/block-tests/emphasis_comparison-2col.html create mode 100644 04. design_agent/docs/block-tests/emphasis_conclusion-accent-bar.html create mode 100644 04. design_agent/docs/block-tests/emphasis_dark-bullet-list.html create mode 100644 04. design_agent/docs/block-tests/emphasis_divider-text.html create mode 100644 04. design_agent/docs/block-tests/emphasis_highlight-strip.html create mode 100644 04. design_agent/docs/block-tests/emphasis_quote-big-mark.html create mode 100644 04. design_agent/docs/block-tests/emphasis_quote-left-border.html create mode 100644 04. design_agent/docs/block-tests/emphasis_quote-question.html create mode 100644 04. design_agent/docs/block-tests/emphasis_tab-label-row.html create mode 100644 04. design_agent/docs/block-tests/headers_section-header-bar.html create mode 100644 04. design_agent/docs/block-tests/headers_section-title-with-bg.html create mode 100644 04. design_agent/docs/block-tests/headers_topic-center.html create mode 100644 04. design_agent/docs/block-tests/headers_topic-left-right.html create mode 100644 04. design_agent/docs/block-tests/headers_topic-numbered.html create mode 100644 04. design_agent/docs/block-tests/image-row.html create mode 100644 04. design_agent/docs/block-tests/media_image-before-after.html create mode 100644 04. design_agent/docs/block-tests/media_image-full-caption.html create mode 100644 04. design_agent/docs/block-tests/media_image-grid-2x2.html create mode 100644 04. design_agent/docs/block-tests/media_image-row-2col.html create mode 100644 04. design_agent/docs/block-tests/media_image-side-text.html create mode 100644 04. design_agent/docs/block-tests/quote-block.html create mode 100644 04. design_agent/docs/block-tests/section-title.html create mode 100644 04. design_agent/docs/block-tests/tables_compare-2col-split.html create mode 100644 04. design_agent/docs/block-tests/tables_compare-3col-badge.html create mode 100644 04. design_agent/docs/block-tests/tables_table-simple-striped.html create mode 100644 04. design_agent/docs/block-tests/topic-header.html create mode 100644 04. design_agent/docs/block-tests/venn_premium_bg.png create mode 100644 04. design_agent/docs/block-tests/visuals_circle-gradient.html create mode 100644 04. design_agent/docs/block-tests/visuals_compare-pill-pair.html create mode 100644 04. design_agent/docs/block-tests/visuals_flow-arrow-horizontal.html create mode 100644 04. design_agent/docs/block-tests/visuals_keyword-circle-row.html create mode 100644 04. design_agent/docs/block-tests/visuals_layer-diagram.html create mode 100644 04. design_agent/docs/block-tests/visuals_process-horizontal.html create mode 100644 04. design_agent/docs/block-tests/visuals_pyramid-hierarchy.html create mode 100644 04. design_agent/docs/block-tests/visuals_timeline-horizontal.html create mode 100644 04. design_agent/docs/block-tests/visuals_timeline-vertical.html create mode 100644 04. design_agent/docs/block-tests/visuals_venn-2items-p2.html create mode 100644 04. design_agent/docs/block-tests/visuals_venn-3items-p2.html create mode 100644 04. design_agent/docs/block-tests/visuals_venn-3items.html create mode 100644 04. design_agent/docs/block-tests/visuals_venn-4items-p2.html create mode 100644 04. design_agent/docs/block-tests/visuals_venn-4items.html create mode 100644 04. design_agent/docs/block-tests/visuals_venn-5items-p2.html create mode 100644 04. design_agent/docs/block-tests/visuals_venn-5items.html create mode 100644 04. design_agent/docs/block-tests/visuals_venn-7items-p2.html create mode 100644 04. design_agent/docs/block-tests/visuals_venn-diagram.html create mode 100644 04. design_agent/docs/block-tests/visuals_venn-premium.html create mode 100644 04. design_agent/docs/block-tests/visuals_venn-svg-premium.html create mode 100644 04. design_agent/docs/figma-analysis/1-1_d1.json create mode 100644 04. design_agent/docs/figma-analysis/1-1_raw.json create mode 100644 04. design_agent/docs/figma-analysis/1-1_미래_raw.json create mode 100644 04. design_agent/docs/figma-analysis/2-1_01_detail.json create mode 100644 04. design_agent/docs/figma-analysis/2-1_02_detail.json create mode 100644 04. design_agent/docs/figma-analysis/DESIGN-ANALYSIS.md create mode 100644 04. design_agent/docs/figma-assets/2-1_02_full_frame.png create mode 100644 04. design_agent/docs/figma-assets/2-2_01_full_hd.png create mode 100644 04. design_agent/docs/figma-assets/2-2_01_icon_card_1.png create mode 100644 04. design_agent/docs/figma-assets/2-2_01_icon_card_2.png create mode 100644 04. design_agent/docs/figma-assets/2-2_01_icon_card_3.png create mode 100644 04. design_agent/docs/figma-assets/2-2_01_icon_card_4.png create mode 100644 04. design_agent/docs/figma-assets/2-2_01_icon_card_5.png create mode 100644 04. design_agent/docs/figma-assets/2-2_02_full_hd.png create mode 100644 04. design_agent/docs/figma-assets/2-2_03_full_hd.png create mode 100644 04. design_agent/docs/figma-assets/2-2_04_full_hd.png create mode 100644 04. design_agent/docs/figma-assets/2-2_icon_card_1.png create mode 100644 04. design_agent/docs/figma-assets/2-2_icon_card_2.png create mode 100644 04. design_agent/docs/figma-assets/2-2_icon_card_3.png create mode 100644 04. design_agent/docs/figma-assets/2-2_icon_card_4.png create mode 100644 04. design_agent/docs/figma-assets/2-2_icon_card_5.png create mode 100644 04. design_agent/docs/figma-assets/bg_header.png create mode 100644 04. design_agent/docs/figma-assets/bim_card_3col.png create mode 100644 04. design_agent/docs/figma-assets/bim_circle_label.png create mode 100644 04. design_agent/docs/figma-assets/bim_comparison_table.png create mode 100644 04. design_agent/docs/figma-assets/bim_mountain_viz.png create mode 100644 04. design_agent/docs/figma-assets/breadcrumb_area.png create mode 100644 04. design_agent/docs/figma-assets/card_3col.png create mode 100644 04. design_agent/docs/figma-assets/card_area.png create mode 100644 04. design_agent/docs/figma-assets/card_img_construction.png create mode 100644 04. design_agent/docs/figma-assets/card_img_design.png create mode 100644 04. design_agent/docs/figma-assets/card_img_design_3d.png create mode 100644 04. design_agent/docs/figma-assets/card_img_design_grp.png create mode 100644 04. design_agent/docs/figma-assets/card_img_maintenance.png create mode 100644 04. design_agent/docs/figma-assets/card_text_construction.png create mode 100644 04. design_agent/docs/figma-assets/card_text_design.png create mode 100644 04. design_agent/docs/figma-assets/card_text_maintenance.png create mode 100644 04. design_agent/docs/figma-assets/circle_label.png create mode 100644 04. design_agent/docs/figma-assets/close_btn.svg create mode 100644 04. design_agent/docs/figma-assets/compare_box_left.png create mode 100644 04. design_agent/docs/figma-assets/compare_box_right.png create mode 100644 04. design_agent/docs/figma-assets/dx_bim_table.png create mode 100644 04. design_agent/docs/figma-assets/image_grid_left.png create mode 100644 04. design_agent/docs/figma-assets/image_grid_right.png create mode 100644 04. design_agent/docs/figma-assets/mountain_viz.png create mode 100644 04. design_agent/docs/figma-assets/section_title.png create mode 100644 04. design_agent/docs/figma-assets/topic_header.png create mode 100644 04. design_agent/docs/figma-assets/venn_bg.png create mode 100644 04. design_agent/docs/figma-assets/venn_premium_bg.png create mode 100644 04. design_agent/docs/figma-screenshots/1-1_미래.png create mode 100644 04. design_agent/docs/figma-screenshots/1장_1-1_미래.png create mode 100644 04. design_agent/docs/figma-screenshots/1장_가치.png create mode 100644 04. design_agent/docs/figma-screenshots/1장_기술.png create mode 100644 04. design_agent/docs/figma-screenshots/2-1_01_건설산업.png create mode 100644 04. design_agent/docs/figma-screenshots/2-1_02_BIM.png create mode 100644 04. design_agent/docs/figma-screenshots/2-1_03_GIS.png create mode 100644 04. design_agent/docs/figma-screenshots/2-1_04_디지털트윈.png create mode 100644 04. design_agent/docs/figma-screenshots/2-2_01.png create mode 100644 04. design_agent/docs/figma-screenshots/2-2_02.png create mode 100644 04. design_agent/docs/figma-screenshots/2-2_03.png create mode 100644 04. design_agent/docs/figma-screenshots/2-2_04.png create mode 100644 04. design_agent/docs/figma-screenshots/2-2장_자세히보기.png create mode 100644 04. design_agent/docs/figma-screenshots/2-3_01.png create mode 100644 04. design_agent/docs/figma-screenshots/2-3_02.png create mode 100644 04. design_agent/docs/figma-screenshots/2-3_03.png create mode 100644 04. design_agent/docs/figma-screenshots/2-3_04.png create mode 100644 04. design_agent/docs/figma-screenshots/2-3_05.png create mode 100644 04. design_agent/docs/figma-screenshots/가치.png create mode 100644 04. design_agent/docs/figma-screenshots/기술.png create mode 100644 04. design_agent/docs/test-bg-layer.png create mode 100644 04. design_agent/docs/test-bim-auto.html create mode 100644 04. design_agent/docs/test-bim-page-v2.html create mode 100644 04. design_agent/docs/test-bim-page.html create mode 100644 04. design_agent/docs/test-gemini-infographic.png create mode 100644 04. design_agent/docs/test-layered-slide.html create mode 100644 04. design_agent/docs/test-layered-v2.html create mode 100644 04. design_agent/docs/test-relation.html create mode 100644 04. design_agent/docs/test-relation.svg create mode 100644 04. design_agent/docs/test-simple.html create mode 100644 04. design_agent/docs/test-simple.svg create mode 100644 04. design_agent/docs/test-slide-output.html create mode 100644 04. design_agent/package.json create mode 100644 04. design_agent/pyproject.toml create mode 100644 04. design_agent/scripts/build_block_index.py create mode 100644 04. design_agent/src/__init__.py create mode 100644 04. design_agent/src/block_search.py create mode 100644 04. design_agent/src/config.py create mode 100644 04. design_agent/src/content_editor.py create mode 100644 04. design_agent/src/design_director.py create mode 100644 04. design_agent/src/image_utils.py create mode 100644 04. design_agent/src/kei_client.py create mode 100644 04. design_agent/src/main.py create mode 100644 04. design_agent/src/pipeline.py create mode 100644 04. design_agent/src/renderer.py create mode 100644 04. design_agent/src/svg_calculator.py create mode 100644 04. design_agent/static/base.css create mode 100644 04. design_agent/static/index.html create mode 100644 04. design_agent/static/tokens.css create mode 100644 04. design_agent/templates/blocks/INDEX.md create mode 100644 04. design_agent/templates/blocks/_legacy/card-grid.html create mode 100644 04. design_agent/templates/blocks/_legacy/card-image.html create mode 100644 04. design_agent/templates/blocks/_legacy/circle-label.html create mode 100644 04. design_agent/templates/blocks/_legacy/compare-box.html create mode 100644 04. design_agent/templates/blocks/_legacy/comparison-table.html create mode 100644 04. design_agent/templates/blocks/_legacy/comparison.html create mode 100644 04. design_agent/templates/blocks/_legacy/conclusion-bar.html create mode 100644 04. design_agent/templates/blocks/_legacy/image-row.html create mode 100644 04. design_agent/templates/blocks/_legacy/process.html create mode 100644 04. design_agent/templates/blocks/_legacy/quote-block.html create mode 100644 04. design_agent/templates/blocks/_legacy/relationship.html create mode 100644 04. design_agent/templates/blocks/_legacy/section-title.html create mode 100644 04. design_agent/templates/blocks/_legacy/topic-header.html create mode 100644 04. design_agent/templates/blocks/cards/card-compare-3col.html create mode 100644 04. design_agent/templates/blocks/cards/card-dark-overlay.html create mode 100644 04. design_agent/templates/blocks/cards/card-icon-desc.html create mode 100644 04. design_agent/templates/blocks/cards/card-image-3col.html create mode 100644 04. design_agent/templates/blocks/cards/card-image-round.html create mode 100644 04. design_agent/templates/blocks/cards/card-numbered.html create mode 100644 04. design_agent/templates/blocks/cards/card-stat-number.html create mode 100644 04. design_agent/templates/blocks/cards/card-step-vertical.html create mode 100644 04. design_agent/templates/blocks/cards/card-tag-image.html create mode 100644 04. design_agent/templates/blocks/cards/card-text-grid.html create mode 100644 04. design_agent/templates/blocks/emphasis/banner-gradient.html create mode 100644 04. design_agent/templates/blocks/emphasis/callout-solution.html create mode 100644 04. design_agent/templates/blocks/emphasis/callout-warning.html create mode 100644 04. design_agent/templates/blocks/emphasis/comparison-2col.html create mode 100644 04. design_agent/templates/blocks/emphasis/conclusion-accent-bar.html create mode 100644 04. design_agent/templates/blocks/emphasis/dark-bullet-list.html create mode 100644 04. design_agent/templates/blocks/emphasis/details-block.html create mode 100644 04. design_agent/templates/blocks/emphasis/divider-text.html create mode 100644 04. design_agent/templates/blocks/emphasis/highlight-strip.html create mode 100644 04. design_agent/templates/blocks/emphasis/quote-big-mark.html create mode 100644 04. design_agent/templates/blocks/emphasis/quote-left-border.html create mode 100644 04. design_agent/templates/blocks/emphasis/quote-question.html create mode 100644 04. design_agent/templates/blocks/emphasis/tab-label-row.html create mode 100644 04. design_agent/templates/blocks/headers/section-header-bar.html create mode 100644 04. design_agent/templates/blocks/headers/section-title-with-bg.html create mode 100644 04. design_agent/templates/blocks/headers/topic-center.html create mode 100644 04. design_agent/templates/blocks/headers/topic-left-right.html create mode 100644 04. design_agent/templates/blocks/headers/topic-numbered.html create mode 100644 04. design_agent/templates/blocks/media/image-before-after.html create mode 100644 04. design_agent/templates/blocks/media/image-full-caption.html create mode 100644 04. design_agent/templates/blocks/media/image-grid-2x2.html create mode 100644 04. design_agent/templates/blocks/media/image-row-2col.html create mode 100644 04. design_agent/templates/blocks/media/image-side-text.html create mode 100644 04. design_agent/templates/blocks/tables/compare-2col-split.html create mode 100644 04. design_agent/templates/blocks/tables/compare-3col-badge.html create mode 100644 04. design_agent/templates/blocks/tables/table-simple-striped.html create mode 100644 04. design_agent/templates/blocks/visuals/circle-gradient.html create mode 100644 04. design_agent/templates/blocks/visuals/compare-pill-pair.html create mode 100644 04. design_agent/templates/blocks/visuals/flow-arrow-horizontal.html create mode 100644 04. design_agent/templates/blocks/visuals/keyword-circle-row.html create mode 100644 04. design_agent/templates/blocks/visuals/layer-diagram.html create mode 100644 04. design_agent/templates/blocks/visuals/process-horizontal.html create mode 100644 04. design_agent/templates/blocks/visuals/pyramid-hierarchy.html create mode 100644 04. design_agent/templates/blocks/visuals/timeline-horizontal.html create mode 100644 04. design_agent/templates/blocks/visuals/timeline-vertical.html create mode 100644 04. design_agent/templates/blocks/visuals/venn-diagram.html create mode 100644 04. design_agent/templates/catalog.yaml create mode 100644 04. design_agent/templates/slide-base.html create mode 100644 04. design_agent/tests/__init__.py diff --git a/04. design_agent/.env.example b/04. design_agent/.env.example new file mode 100644 index 0000000..520cdf1 --- /dev/null +++ b/04. design_agent/.env.example @@ -0,0 +1,3 @@ +ANTHROPIC_API_KEY=sk-ant-... +KEI_API_URL=http://localhost:8000 +LOG_LEVEL=DEBUG diff --git a/04. design_agent/.gitignore b/04. design_agent/.gitignore new file mode 100644 index 0000000..5822109 --- /dev/null +++ b/04. design_agent/.gitignore @@ -0,0 +1,11 @@ +.env +__pycache__/ +*.pyc +.pytest_cache/ +.ruff_cache/ +*.egg-info/ +dist/ +build/ +.venv/ +node_modules/ +data/ diff --git a/04. design_agent/CLAUDE.md b/04. design_agent/CLAUDE.md new file mode 100644 index 0000000..c86cec2 --- /dev/null +++ b/04. design_agent/CLAUDE.md @@ -0,0 +1,516 @@ +# Design Agent — 콘텐츠 시각 구조화 슬라이드 생성기 + +## 프로젝트 목적 + +텍스트/MDX 콘텐츠를 **가로 슬라이드(1페이지 또는 다중 페이지)**로 시각 구조화하는 독립 에이전트. +콘텐츠의 의미를 분석하여 적합한 레이아웃 블록을 선택하고, 핵심만 추출하여 깔끔한 HTML/CSS로 렌더링한다. + +**핵심 원칙:** +- 전체 페이지를 하나의 고정 템플릿으로 찍어내는 것이 아니라, 콘텐츠를 분석 → 각 덩어리별로 적합한 레이아웃 블록 선택 → 조합하여 배치 +- 기획자(편집자)가 정리한 텍스트가 기준. **디자인이 텍스트에 맞춘다** (텍스트가 디자인에 맞추는 것이 아님) +- **모든 판단은 실장/팀장/편집자의 사고. 하드코딩 없음** + +--- + +## 아키텍처 (5단계 파이프라인) + +``` +[1단계] Kei 실장 (Sonnet) — AI 사고 + 꼭지 추출 → 정보 구조 파악 → 레이어/강조/배치/role 판단 + ↓ +[2단계] 디자인 팀장 — 2-Step + Step A: 레이아웃 프리셋 선택 (규칙 기반, LLM 불필요) + - 실장의 role 분석을 보고 프리셋 자동 결정 + Step B: 프리셋 안에서 블록 매핑 + 글자 수 가이드 (Sonnet) + - 선택된 프리셋의 CSS가 프롬프트에 포함됨 + - flow → body/main, reference → sidebar + ↓ +[3단계] Kei 텍스트 편집자 (Sonnet) — AI 사고 + 글자 수 가이드 참고하되 내용 의미 우선. 도메인 용어 보존하며 편집 + ↓ +[4단계] 디자인 실무자 (Sonnet + Jinja2 + CSS) — AI + 코드 + 편집자가 정리한 텍스트에 맞게 디자인 조정 + HTML 조립 + ↓ +[5단계] 디자인 팀장 (Sonnet) — AI 사고 + 전체 균형 재검토 → 공간 재배분 → 2차 조정 지시 +``` + +### 레이아웃 프리셋 (2단계 Step A) + +실장의 role 분석을 보고 **규칙 기반**으로 프리셋을 자동 선택한다. LLM 호출 불필요. + +| 프리셋 | 조건 | CSS grid | +|--------|------|----------| +| `sidebar-right` | reference 꼭지가 1개 이상 있음 | `"title title" "body sidebar" "footer footer"` / 65fr 35fr | +| `two-column` | 모든 flow 꼭지가 대등한 비교 | `"title title" "left right" "footer footer"` / 1fr 1fr | +| `hero-detail` | 고강조 꼭지 1개 + 나머지 보조 | `"title title" "hero hero" "detail detail" "footer footer"` | +| `single-column` | 모든 꼭지가 flow, 순차적 | `"title" "body" "footer"` / 1fr | + +**선택 규칙:** +``` +reference 꼭지 있음 → sidebar-right +모든 flow가 대등 비교 → two-column +고강조 1개 + 나머지 보조 → hero-detail +나머지 → single-column +``` + +### 역할 분리 + +| 역할 | 담당 | 방식 | 하는 일 | 하지 않는 일 | +|------|------|------|---------|------------| +| Kei 실장 | Sonnet | AI | 꼭지 추출, 정보 구조 파악, 레이어/강조/배치/role 판단 | 디자인, 텍스트 편집 | +| 디자인 팀장 Step A | 코드 | 규칙 | 실장의 role에 따라 레이아웃 프리셋 자동 선택 | AI 판단 불필요 | +| 디자인 팀장 Step B | Sonnet | AI | 프리셋 안에서 블록 매핑, 글자 수 가이드, zone 배정 | 레이아웃 구조 결정 (이미 정해짐) | +| 텍스트 편집자 | Sonnet | AI | 도메인 용어 보존하며 편집, 출처 보존, 표 편집 | 레이아웃 결정 | +| 디자인 실무자 | Sonnet + 코드 | AI + 코드 | 텍스트에 맞게 디자인 조정, HTML/CSS 조립 | 콘텐츠 의미 판단 | + +--- + +## 핵심 프로세스 + +``` +사용자 콘텐츠 입력 (텍스트/MDX 붙여넣기 또는 파일 업로드) + ↓ +[1단계] Kei 실장 — 꼭지 추출 + 분석 + + 꼭지 추출: + - 본문에서 핵심 꼭지 추출 (2~5개) + - 1페이지 적정 꼭지 수: 5개 + + 페이지 분리 판단: + - 중요도가 5개를 넘고 레이어도 동등하면 → 2페이지로 분리 + - 내용과 의미 기반으로 자연스러운 분할 (2/3, 3/4, 4/3, 5/2 등) + - 5개인데 내용이 많으면 → 꼭지 레이어를 보고 세부 내용은 자세히보기로 + + 각 꼭지 분석: + - 레이어 수준 (도입/핵심/보조/결론) + - 강조 판단 (어떤 꼭지를 눈에 띄게 할 것인가) + - 배치 방향 (세로로 긴 꼭지, 가로 나열 꼭지) + + 이미지 판단: + - 몇 개인지, 어떤 꼭지에 속하는지 + - 핵심인지(도표/차트) 보조인지(참고 문서 표지) + - 텍스트가 포함된 이미지인지 (너무 축소하면 안 됨) + + 표 판단: + - 행/열 규모 + - 전체 표시 가능한지, 요약 필요한지 + + 상세 콘텐츠 판단: + - 너무 구체적/세부적인 내용은 "자세히보기" 대상 + ↓ +[2단계] 디자인 팀장 — Step A + Step B + + Step A: 레이아웃 프리셋 선택 (규칙 기반, LLM 불필요) + - 실장의 role 분석을 보고 자동 선택: + reference 있음 → sidebar-right + 대등 비교 → two-column + 고강조 1개 → hero-detail + 나머지 → single-column + - 선택된 프리셋의 CSS grid가 Step B 프롬프트에 포함됨 + + Step B: 프리셋 안에서 블록 매핑 (Sonnet) + - 선택된 프리셋의 zone(body/sidebar/footer)에 꼭지를 배정 + - flow 꼭지 → body/main zone + - reference 꼭지 → sidebar zone + - detail_target 꼭지 → popup 연결 + - catalog에서 각 꼭지에 적합한 블록 타입 선택 + - 각 블록의 대략적 글자 수 가이드 + + 이미지 배치: + - 원본 이미지 크기 확인 (Pillow Image.open().size) + - 가로/세로 비율에 따라 영역 결정 + - 이미지는 원본 그대로 사용, 크기만 조절 + + 표 배치: + - 행×열 규모 보고 공간 안에 들어가는지 판단 + - 안 들어가면 요약 요청 또는 popup + + 자세히보기: + - detail_target 꼭지는
/로 popup 연결 + ↓ +[3단계] Kei 텍스트 편집자 — 텍스트 정리 + + - 팀장의 글자 수 가이드 참고하되, 내용 의미가 우선 + - 전체 컨텍스트와 핵심 용어 유지 + - 도메인 전문가로서 세련된 표현으로 편집 + - 출처 보존, 개조식 작성, 날조 금지 + - 결과 글자 수가 가이드와 다를 수 있음 (의미 > 글자 수) + - 표 내용도 편집 (핵심 행/열 선택, 요약 등) + - 자세히보기 대상은 요약 버전 + 상세 버전 둘 다 작성 + ↓ +[4단계] 디자인 실무자 — 디자인 조정 + HTML 조립 + + 텍스트 맞춤: + - 편집자가 정리한 텍스트 양에 맞게 디자인 조정 + - 텍스트가 가이드보다 길면 → 폰트/여백/박스를 조정 (텍스트를 자르지 않음) + - 빈 공간 방치 안 함 (박스 줄이거나 공간 활용) + + 이미지 처리: + - object-fit: contain (비율 유지, 잘리지 않게) + - 팀장이 정한 영역 크기에 맞춤 + + 표 처리: + - table-layout: fixed + container query 폰트 스케일링 + - 행/열 수에 따라 셀 크기 자동 조정 + + 자세히보기: + -
/로 접기/펼치기 + - 인쇄 시 자동 펼침 (JavaScript) + + HTML 조립: + - Jinja2 템플릿 렌더링 + - 다중 페이지 시 page-break 처리 + ↓ +[5단계] 디자인 팀장 — 전체 재검토 + + 균형 점검: + - 1차 조립 결과의 전체 균형 확인 + - 블록별 채움 비율 (텍스트 양 vs 공간) + - 블록 간 균형 (한쪽만 빽빽하고 다른 쪽 비어있지 않은지) + + 이미지/표 점검: + - 이미지 크기가 적절한지 (너무 작아서 안 보이지 않는지) + - 표가 읽을 수 있는 크기인지 + + 조정: + - 필요 시 공간 재배분 → 실무자에게 2차 조정 지시 + - 좌우 불균형, 어색한 빈 공간 해소 + - 최종 HTML 출력 + ↓ +미리보기 → HTML 다운로드 +``` + +**핵심 원칙:** +- 디자인 팀장은 레이아웃 + 공간 배분. 텍스트를 건드리지 않는다. +- 텍스트 편집자가 정리한 텍스트가 기준. 디자인이 텍스트에 맞춘다. +- 실무자는 텍스트를 자르지 않고, 디자인을 조정한다. +- 이미지는 원본 그대로 사용, 크기만 조절. +- 표는 표로 유지, 공간 안 되면 요약하거나 페이지 분리. +- 상세 내용은 `
`로 접기/펼치기. +- 1차 조립 후 팀장이 전체 균형을 재검토하여 2차 조정. +- **모든 기준은 하드코딩 없이 실장/팀장/편집자의 사고로 판단.** + +--- + +## 콘텐츠 유형 분류 기준 + +실장이 콘텐츠를 분석하여 아래 유형으로 분류한다. +**이 분류는 하드코딩이 아니라, 실장이 매번 사고하여 판단한다.** + +| 텍스트 패턴 | 유형 | 적합한 블록 | +|------------|------|-----------| +| "A vs B", 장단점, 차이점 | 비교 | 2단 병렬 / 비교 테이블 | +| "1단계 → 2단계 → 3단계" | 프로세스 | 플로우차트 / 단계 카드 | +| "X는 Y를 포함한다", 상위-하위 | 구성/관계 | 벤 다이어그램 / 트리 | +| 수치, KPI, 통계 | 핵심 지표 | 큰 숫자 + 보조 텍스트 | +| 용어 정의, 개념 설명 | 정의 | 카드 3열 / 아이콘 카드 | +| 기능/특성 나열 | 목록 | 아이콘 리스트 / 카드 그리드 | +| 연도별 사건, 로드맵 | 시간 순서 | 타임라인 (가로/세로) | +| 핵심 메시지, 결론 | 강조 | 결론 바 / 인용 블록 | +| 문제 상황, 경고 | 문제 제기 | 경고 박스 / 강조 인용 | +| 이미지 + 텍스트 | 이미지 참조 | 전체너비 / 사이드 / 썸네일 | +| 다항목 데이터 | 표 | 비교 테이블 | +| 세부/구체적 내용 | 자세히보기 | `
/` | + +--- + +## 블록 타입 정의 + +6개 카테고리, 18개 블록 변형. **상세는 `templates/blocks/INDEX.md` 참조.** + +| 카테고리 | 블록 수 | 구현 방식 | 주요 블록 | +|---------|--------|---------|---------| +| headers/ | 2 | HTML/CSS | section-title-with-bg, topic-left-right | +| cards/ | 3 | HTML/CSS | card-image-3col, card-text-grid, card-dark-overlay | +| tables/ | 1 | HTML/CSS | compare-3col-badge | +| visuals/ | 4 | **SVG** (수학적 좌표 계산) | venn-diagram, circle-gradient, compare-pill-pair, process-horizontal | +| emphasis/ | 5 | HTML/CSS | quote-left-border, conclusion-accent-bar, comparison-2col, banner-gradient, quote-question | +| media/ | 3 | HTML/CSS + 이미지 | image-row-2col, image-grid-2x2, image-side-text | + +각 블록은 독립적인 컴포넌트로 슬롯(교체 가능한 위치)을 가지며, `catalog.yaml`에 when/not_for/slots가 정의되어 있다. + +--- + +## 이미지 처리 원칙 + +- 원본 이미지를 **그대로 사용** (crop/재구성은 극히 예외) +- 팀장이 슬라이드 구조에 맞게 **크기만 조절** (가로/세로 비율 유지) +- 이미지 크기 읽기: Pillow `Image.open().size` (헤더만 읽음, 전체 로드 안 함) +- 가로형(ratio > 1.2) → 전체 너비 배치 +- 세로형(ratio < 0.8) → 텍스트 옆 배치 +- 텍스트 포함 도표 → 너무 작게 축소하면 안 됨 +- CSS: `object-fit: contain` (전체 보이게, 비율 유지) + +## 표 처리 원칙 + +- 표는 **표로 유지** (다른 형태로 전환하지 않음) +- 공간에 안 들어가면: 요약하거나 페이지 분리 +- CSS: `table-layout: fixed` + container query 폰트 스케일링 +- 표 내용 편집은 Kei 텍스트 편집자가 담당 + +## 자세히보기 (상세 콘텐츠) 원칙 + +- HTML 네이티브 `
/` 사용 (JavaScript 불필요) +- 슬라이드 표면: 요약/핵심만 표시 +- 펼치면: 전체 상세 내용 표시 +- 인쇄 시: JavaScript 6줄로 자동 펼침 +- 정보 밀도는 실장/팀장이 사고로 판단 (하드코딩 기준 없음) + +--- + +## 페이지 구성 원칙 + +### 레이아웃 배치 규칙 +- CSS Grid 기반 (`grid-template-areas`) +- 가로 슬라이드 비율: 16:9 (1280×720) +- 1페이지 적정 꼭지 수: 5개 +- 정보 계층: 위 → 아래 (문제 제기 → 분석 → 결론) +- 여백: 블록 간 최소 20px, 페이지 패딩 40px + +### 페이지 분리 기준 +- 꼭지 5개 이하 + 내용 적절 → 1페이지 +- 꼭지 5개 + 내용 많음 → 1페이지 + 일부 자세히보기 +- 꼭지 5개 초과 + 레이어 동등 → 2페이지 (의미 기반 분할) +- 분할 비율: 2/3, 3/4, 4/3, 5/2 등 내용에 따라 + +### 블록 조합 예시 + +``` +┌─────────────────────────────────────────────┐ +│ [강조 인용] 문제 제기 │ +├──────────────────┬──────────────────────────┤ +│ [사례 카드 2열] │ [카드 그리드 3열] │ +│ 정책 사례 │ 용어 정의 │ +├──────────────────┴──────────────────────────┤ +│ [관계도] 벤 다이어그램 │ +├─────────────────────────────────────────────┤ +│ [결론 바] 핵심 한 줄 │ +└─────────────────────────────────────────────┘ +``` + +--- + +## 글자 수 추정 (타이포그래피 기반) + +한글은 글자 너비가 거의 일정하므로, 블록 크기에서 글자 수를 계산할 수 있다. +**이 계산은 팀장의 글자 수 가이드를 보조하는 참고 도구이지, 하드코딩 기준이 아니다.** + +``` +계산식: + 한 줄 글자 수 = 블록 너비(px) ÷ (폰트 크기(px) × 0.97) + 줄 수 = 블록 높이(px) ÷ (폰트 크기(px) × 줄간격) + 총 글자 수 = 한 줄 글자 수 × 줄 수 × 안전 계수(0.85) +``` + +Pretendard 폰트 크기별 참고값 (1회 측정, 상수 저장): + +| 폰트 크기 | 한글 글자 너비 | 줄간격 1.6 기준 줄 높이 | +|----------|-------------|---------------------| +| 12px | ~11.6px | 19.2px | +| 16px | ~15.5px | 25.6px | +| 20px | ~19.4px | 32.0px | +| 24px | ~23.3px | 38.4px | + +--- + +## 디자인 원칙 (절대 규칙) + +### DO (해야 하는 것) +- 여백을 충분히 확보한다 (여백 > 장식) +- 색상은 최대 3개 (메인 1개 + 포인트 1개 + 중성 1개) +- 폰트 크기 체계를 일관되게 유지 (제목/소제목/본문/캡션 4단계) +- 흑백 기조 + 포인트 컬러 최소 사용 +- 정보 계층을 시각적으로 명확히 표현 + +### DON'T (하지 않는 것) +- HTML/CSS 블록의 배경 그라데이션 금지 (**SVG 시각화 블록, 디자인 의도가 명확한 블록(배너, 오버레이, 콜아웃 등)은 허용**) +- CSS 애니메이션/트랜지션 금지 +- 호버 효과 금지 +- 그림자(box-shadow) 최소화 (1개 레벨만. SVG filter는 예외) +- 다크 테마 금지 (요청하지 않는 한) +- 둥근 모서리 과다 사용 금지 (border-radius 최대 8px) +- 텍스트를 자르지 않는다 (디자인이 텍스트에 맞춘다) + +--- + +## 디자인 토큰 + +```css +:root { + /* 색상 */ + --color-primary: #1e293b; + --color-accent: #2563eb; + --color-neutral: #64748b; + --color-bg: #ffffff; + --color-bg-subtle: #f8fafc; + --color-border: #e2e8f0; + --color-danger: #dc2626; + + /* 폰트 크기 */ + --font-title: 2rem; + --font-subtitle: 1.25rem; + --font-body: 0.95rem; + --font-caption: 0.8rem; + + /* 여백 */ + --spacing-page: 40px; + --spacing-block: 20px; + --spacing-inner: 16px; + + /* 기타 */ + --radius: 6px; + --border-width: 1px; + --accent-border: 3px; +} +``` + +--- + +## Kei API 연동 + +### 호출 포인트 +| 단계 | API | 용도 | +|------|-----|------| +| 1단계 꼭지 추출 | Anthropic API (Sonnet) | 꼭지 추출 + 레이어 + 강조 + 배치 + 이미지/표/상세 판단 | +| 2단계 레이아웃 설계 | Anthropic API (Sonnet) | 블록 매핑 + 공간 배분 + 글자 수 가이드 | +| 3단계 텍스트 정리 | Anthropic API (Sonnet) | 의미 보존 편집 + 표 편집 + 자세히보기 작성 | +| 4단계 디자인 조정 + 조립 | Anthropic API (Sonnet) + Jinja2/CSS | 텍스트에 맞게 디자인 조정 + HTML 생성 | +| 5단계 재검토 | Anthropic API (Sonnet) | 균형 점검 + 2차 조정 | + +### 독립 실행 가능 +- Kei API 없이 Anthropic API 직접 호출로 동작 +- Kei API 연결 시 1단계에서 Kei RAG 지식 활용 가능 + +--- + +## 블록 라이브러리 + +### 핵심 원칙: 변형 기반 선택 +하나의 블록 유형에 **여러 변형(variation)**이 존재하고, 디자인 팀장이 콘텐츠 성격에 맞는 변형을 **검색하여 선택**한다. + +``` +콘텐츠 분석 → "이 꼭지는 좌:질문 우:설명 구조다" + ↓ +블록 라이브러리 검색 → headers/ 카테고리에서 후보 3~4개 반환 + ↓ +팀장이 선택 → topic-left-right.html +``` + +### 카테고리 구조 (6개, 18개 블록) + +``` +templates/blocks/ +├── INDEX.md ← 전체 인덱스 + 추가 예정 목록 +├── headers/ (2개) ← 타이틀, 꼭지 헤더 +│ ├── section-title-with-bg.html 배경 이미지 + 영문/한글 타이틀 +│ └── topic-left-right.html 좌:파란 제목 + 우:설명 +├── cards/ (3개) ← 카드 계열 (정보 나열) +│ ├── card-image-3col.html 상단 이미지 + 하단 텍스트 3열 +│ ├── card-text-grid.html 텍스트만 카드 2~4열 +│ └── card-dark-overlay.html 다크 이미지 배경 + 흰 텍스트 오버레이 +├── tables/ (1개) ← 표/비교 계열 +│ └── compare-3col-badge.html A|VS배지|B 3단 비교 +├── visuals/ (4개) ← 시각 요소 — **SVG로 제작** +│ ├── circle-gradient.html 파란 그라데이션 원 + 텍스트 +│ ├── compare-pill-pair.html 둥근 테두리 박스 2개 + VS +│ ├── venn-diagram.html 벤 다이어그램 (**SVG premium, 수학적 계산**) +│ └── process-horizontal.html 가로 단계 흐름 +├── emphasis/ (5개) ← 강조 (인용, 결론) +│ ├── quote-left-border.html 좌측 라인 + 인용 +│ ├── conclusion-accent-bar.html 좌측 파란 라인 + 밝은 배경 결론 +│ ├── comparison-2col.html 2단 병렬 비교 +│ ├── banner-gradient.html 파란 그라데이션 전체 너비 배너 +│ └── quote-question.html 질문형 강조 박스 +└── media/ (3개) ← 이미지/미디어 + ├── image-row-2col.html 이미지 2장 나란히 + ├── image-grid-2x2.html 이미지 4장 2x2 격자 + └── image-side-text.html 좌:이미지 우:텍스트+불릿 +``` + +### 시각화 방식 (Phase 1 확정) + +**블록 유형별 구현 방식:** + +| 블록 유형 | 구현 방식 | 이유 | +|----------|---------|------| +| 텍스트 블록 (카드, 비교, 표, 인용, 결론) | **HTML/CSS** | Jinja2 템플릿으로 조립 | +| 시각화 다이어그램 (관계도, 프로세스, 순환도) | **SVG** | 좌표 정확 + 그라데이션/글로우 가능 + 개수 자동 대응 | +| 실사 이미지 (시공 사진, 3D 렌더링) | **Figma export 또는 원본 이미지** | CSS/SVG로 재현 불가 | +| 배경 텍스처 (보케, 분위기) | **Gemini API 이미지 생성** | 실사 배경 전용 | + +**SVG 시각화 원칙 (검증 완료):** +- SVG 안에 `` 포함 → 원 좌표와 텍스트 좌표가 같은 공간 → **위치 100% 정확** +- `radialGradient`, `linearGradient` → 고급 그라데이션 +- `filter: feGaussianBlur`, `feDropShadow` → 글로우/그림자 +- 하이라이트 오버레이 → 구체(3D) 느낌 +- 수학적 계산 (`cos/sin`) → N개 원소 자동 배치 (`360/N` 간격) — **Phase 2 구현 예정. 현재는 3개 고정 SVG** + +**AI 이미지 생성은 시각화에 사용하지 않는다:** +- AI 생성 이미지는 원 위치가 매번 달라 텍스트 위치를 맞출 수 없음 (검증 완료) +- AI 이미지는 **실사 배경 텍스처** (보케, 헥사곤, 분위기)에만 사용 + +### 변형 검색 (향후) +변형이 수십~수백 개로 늘어나면: +1. 각 블록 HTML의 구조/슬롯/용도를 임베딩 +2. FAISS 인덱스로 구축 +3. 콘텐츠 분석 결과를 쿼리 → 적절한 변형 3~4개 반환 +4. 팀장이 최종 선택 + +현재 규모(13개)에서는 catalog.yaml만으로 충분. 40개 이상부터 FAISS 도입 검토. + +### catalog.yaml +- 디자인 팀장의 "메뉴판" +- 각 블록의 id, 시각 설명, 언제 쓰는지(when), 슬롯 목록 +- Figma에서 디자인 추출 → 템플릿 변환 → catalog에 등록 +- 블록 추가 시 반드시 catalog에도 등록 (미등록 = 팀장이 모름) + +### Figma 에셋 +Figma에서 추출한 이미지 에셋은 `docs/figma-assets/`에 저장: +- 배경 이미지 (bg_header.png 등) +- 3D 렌더링 이미지 (card_img_design.png 등) +- 시각화 에셋 (mountain_viz.png 등) +- 블록에서 `src="{{ image_path }}"`로 참조 + +--- + +## 기술 스택 + +| 역할 | 도구 | 비고 | +|------|------|------| +| 서버 | FastAPI + uvicorn | 포트 8001 | +| 템플릿 | Jinja2 | 카테고리별 블록 조합 | +| 렌더링 | CSS Grid + 디자인 토큰 | 슬라이드(16:9) + 웹(세로 스크롤) | +| 폰트 | Pretendard Variable | word-break: keep-all | +| AI | Anthropic API (Sonnet) | 5단계 모두 | +| 이미지 크기 | Pillow Image.open().size | 헤더만 읽음 | +| 시각화 | SVG (radialGradient, filter, 수학적 좌표 계산) | 다이어그램/관계도 | +| 실사 배경 | Gemini API | 배경 텍스처 전용 (시각화에는 사용 금지) | +| 자세히보기 | `
/` | HTML 내장 | +| Figma 연동 | Figma REST API + Framelink MCP | 에셋 추출, 디자인 토큰 | +| 변형 검색 | FAISS (향후) | 블록 40개+ 시 도입 | +| 출력 | HTML 다운로드 / .astro (Starlight) | | + +--- + +## 향후 연결 가능성 + +``` +현재: 독립 개발 + 테스트 + ↓ +검증 후 선택지: + A) Kei 본체에 합치기 (대화 안에서 "슬라이드로 정리해줘") + B) 글벗에 붙이기 (문서 자동화 → 시각화 단계) + C) 둘 다 +``` + +--- + +## 금지 사항 + +1. Kei Persona Agent 코드를 수정하지 않는다 +2. 디자인 판단을 하드코딩하지 않는다 (AI가 사고한다) +3. 전체 페이지를 하나의 고정 템플릿으로 만들지 않는다 (블록 조합 방식) +4. 텍스트를 자르지 않는다 (디자인이 텍스트에 맞춘다) +5. 이미지를 crop하지 않는다 (크기만 조절) +6. 그라데이션, 애니메이션, 다크 테마를 기본으로 사용하지 않는다 diff --git a/04. design_agent/IMPROVEMENT-PHASE-A.md b/04. design_agent/IMPROVEMENT-PHASE-A.md new file mode 100644 index 0000000..8f07f86 --- /dev/null +++ b/04. design_agent/IMPROVEMENT-PHASE-A.md @@ -0,0 +1,491 @@ +# Phase A: 슬라이드 품질 핵심 — 실행 상세 + +> "프레임에 내용이 안 보인다"의 직접 원인 해결. 8개 항목. +> 원칙: 하드코딩 금지. 모든 판단은 AI 사고. 회귀 금지. + +--- + +## 실행 순서 + +``` +[독립] A-6 (cover→contain), A-7 (table-layout: fixed) + → A-8 (container query, A-7 후) + → A-1 (Sonnet 디자인 조정 — 가장 큰 작업) + → A-2 (HTML 전달), A-3 (shrink), A-4 (rewrite) — 병렬 가능 + → A-5 (overflow 재검토, A-1 완료 후) +``` + +--- + +## A-6: object-fit: cover → contain ✅ 완료 + +### 현재 상태 +- `image-row-2col.html:30` — `object-fit: cover;` +- `image-grid-2x2.html:31` — `object-fit: cover;` +- cover는 이미지를 crop → CLAUDE.md "이미지를 crop하지 않는다" 위반 + +### 작업 +두 파일에서 `cover` → `contain` 변경 (CSS 1줄씩) + +### 충돌/회귀 +- 충돌: 없음. CSS 속성값만 변경 +- 회귀: 없음. CLAUDE.md 원칙 복구 +- 부작용: contain은 이미지 주변에 빈 공간(letterbox) 가능 → `background: var(--color-bg-subtle)` 추가로 자연스럽게 처리 + +### 수정 파일 +- `templates/blocks/media/image-row-2col.html` +- `templates/blocks/media/image-grid-2x2.html` + +### 구현 결과 +- `image-row-2col.html:29~31` — `object-fit: contain; height: 100%; background: var(--color-bg-subtle, #f8fafc);` + - cover → contain, 높이 하드코딩(354px) → 100%(부모 기준), letterbox 배경색 추가 +- `image-grid-2x2.html:29~31` — 동일 패턴 적용 (200px 하드코딩도 함께 제거) + +--- + +## A-7: table-layout: fixed ✅ 완료 + +### 현재 상태 +- `compare-3col-badge.html`에 table-layout 미지정 +- 열 너비가 내용 길이에 따라 불안정하게 변동 + +### 작업 +```css +.ct-table { + table-layout: fixed; + width: 100%; /* fixed는 width 필수 */ +} +``` + +### 충돌/회귀 +- 충돌: 없음. 기존 테이블 스타일에 속성 추가만 +- 회귀: 없음. fixed는 열 너비를 균등하게 고정 — 더 안정적 + +### 수정 파일 +- `templates/blocks/tables/compare-3col-badge.html` + +### 구현 결과 +- `.block-table-figma table`에 `table-layout: fixed;` 추가 (기존 `width: 100%`는 이미 있었음) + +--- + +## A-8: container query 폰트 스케일링 ✅ 완료 + +### 현재 상태 +- 표 셀 폰트 크기 고정 → 좁은 공간(sidebar 35%)에서 텍스트 잘림/넘침 +- @container 규칙 없음 + +### 작업 +```css +.block-compare-table { + container-type: inline-size; +} + +@container (max-width: 40rem) { + .ct-cell, .ct-header { + font-size: var(--font-caption); /* 0.8rem */ + } +} +@container (max-width: 25rem) { + .ct-cell, .ct-header { + font-size: var(--font-small); /* 0.7rem */ + } +} +``` + +### 하드코딩 점검 +- `40rem`, `25rem`은 font-size 기반 상대값 (px 고정이 아님) +- `var(--font-caption)`, `var(--font-small)`은 디자인 토큰 → 하드코딩 아님 + +### 충돌/회귀 +- 충돌: 없음. 신규 CSS 규칙 추가 +- 회귀: 없음. @container 미지원 브라우저에서는 무시 → 기존과 동일 +- 의존성: A-7 (table-layout: fixed) 먼저 적용해야 열 너비가 안정적 + +### 수정 파일 +- `templates/blocks/tables/compare-3col-badge.html` + +### 구현 결과 +- `.block-table-figma`에 `container-type: inline-size;` 추가 +- `@container (max-width: 40rem)` — 테이블/헤더/셀 폰트 축소 + 패딩 축소 +- `@container (max-width: 25rem)` — 추가 축소 + badge 패딩 축소 +- **추가:** `tr:hover` 제거 (Phase C-2 선행 처리 — CLAUDE.md "호버 효과 금지") + +--- + +## A-1: 4단계 Sonnet 디자인 조정 ✅ 완료 + +### 현재 상태 +- pipeline.py:73에서 `render_slide(layout_concept)` 직접 호출 +- 텍스트 양에 맞는 디자인 조정 과정이 없음 → 텍스트 넘침/빈공간 원인 +- CLAUDE.md: "디자인 실무자 (Sonnet + Jinja2 + CSS) — 텍스트에 맞게 폰트/여백/박스 조정" + +### API 선택 +- **Sonnet** (CLAUDE.md "4단계: Anthropic API (Sonnet)") +- 디자인 실무자는 Kei가 아님 — Sonnet이 맞음 + +### 핵심 아이디어: CSS 변수 cascade +블록 템플릿 20개가 이미 CSS 변수(`var(--font-body)`, `var(--spacing-inner)` 등)를 187회 사용 중. +area div에서 CSS 변수를 override하면 **템플릿 수정 없이** 모든 블록이 자동 조정됨. + +```html + +
+ {{ block.html }} +
+``` + +### 파이프라인 흐름 변경 + +``` +기존: + 3단계 fill_content → 4단계 render_slide → 5단계 review + +변경: + 3단계 fill_content → [신규] _adjust_design → 4단계 render_slide → 5단계 review +``` + +### 신규 함수: _adjust_design() + +**위치:** pipeline.py + +**입력:** layout_concept (data 채워진 상태) + +**처리:** +1. 코드가 각 area별 블록 수, 텍스트 총량(글자 수), zone budget_px를 계산 +2. Sonnet에게 전달: area별 정보 + 사용 가능한 CSS 변수 목록 +3. Sonnet이 area별 CSS 변수 override를 결정하여 JSON 반환 +4. layout_concept에 area_styles 저장 + +**Sonnet 프롬프트 구성:** +``` +당신은 디자인 실무자이다. 편집자가 정리한 텍스트가 각 영역에 잘 들어가도록 CSS를 조정한다. + +## 원칙 +- 텍스트를 자르지 않는다. 디자인이 텍스트에 맞춘다. +- 빈 공간을 방치하지 않는다. +- 조정 가능한 CSS 변수: --font-body, --font-subtitle, --font-caption, --spacing-inner, --spacing-block, --spacing-small + +## 각 영역 현황 +- body (예산 490px, 너비 65%): 3개 블록, 총 820자 + - quote-question: 120자 + - topic-header: 200자 + - comparison-table: 500자 +- sidebar (예산 490px, 너비 35%): 2개 블록, 총 400자 + - card-image: 250자 + - card-image: 150자 +- footer (예산 60px): 1개 블록, 80자 + +## 출력 (JSON만) +{"area_styles": {"body": "--font-body: 0.85rem; --spacing-inner: 10px;", "sidebar": "", "footer": ""}} +``` + +**Sonnet 출력 파싱:** +- `area_styles` dict 추출 +- 각 area별 CSS 문자열 → layout_concept 페이지에 저장 + +**실패 시:** area_styles가 빈 dict → style="" → 기존과 동일하게 렌더링 (안전) + +### renderer.py 변경 + +**render_multi_page() 192~197행:** + +기존: +```python +pages_rendered.append({ + "grid_areas": page.get("grid_areas", "'main'"), + ... + "blocks": blocks_grouped, + "page_number": page_idx + 1, +}) +``` + +변경: +```python +# area_styles를 각 grouped block에 주입 +area_styles = page.get("area_styles", {}) +for grouped_block in blocks_grouped: + grouped_block["style_override"] = area_styles.get(grouped_block["area"], "") + +pages_rendered.append({ + "grid_areas": page.get("grid_areas", "'main'"), + ... + "blocks": blocks_grouped, + "page_number": page_idx + 1, +}) +``` + +### slide-base.html 변경 + +**45행:** + +기존: +```html +
+``` + +변경: +```html +
+``` + +### 하드코딩 점검 +- CSS 조정값: Sonnet이 결정 → AI 판단 ✅ +- CSS 변수 목록: 프롬프트에 "조정 가능한 변수" 안내 → 가이드일 뿐 강제 아님 ✅ +- area별 글자 수: 코드가 계산 → 객관적 수치 ✅ +- 하드코딩 없음 ✅ + +### 충돌/회귀 +- pipeline.py: render_slide() 전에 삽입. 기존 흐름 안 건드림 ✅ +- renderer.py: blocks_grouped에 style_override 키 추가. 기존 키 영향 없음 ✅ +- slide-base.html: style 속성 추가. area_styles 없으면 빈 문자열 → 기존 동일 ✅ +- 템플릿 수정: 불필요 (CSS 변수 cascade로 자동 적용) ✅ +- 회귀: 없음. 실패 시 기존과 동일 동작 ✅ + +### 수정 파일 +- `src/pipeline.py` — _adjust_design() 신규 함수 + generate_slide()에 호출 추가 +- `src/renderer.py` — render_multi_page()에서 area_styles 주입 +- `templates/slide-base.html` — area div에 style_override 적용 + +### 구현 결과 +- **pipeline.py** `_adjust_design()` 신규 함수 (약 80행): + - 각 area별 block_count, total_chars, budget_px, width_pct, block_types 자동 집계 (코드) + - Sonnet(디자인 실무자)에게 area별 현황 전달 → CSS 변수 override를 JSON으로 반환받음 + - 출력: `page["area_styles"] = {"body": "--font-body: 0.85rem; ...", "sidebar": "", ...}` + - 실패 시: `area_styles = {}` → style="" → 기존과 동일 렌더링 (안전) +- **pipeline.py** `generate_slide()` 72행: `_adjust_design()` 호출 삽입 (render_slide 직전) +- **renderer.py** `render_multi_page()` 192~196행: area_styles를 grouped block의 `style_override`에 주입 +- **slide-base.html** 45행: `
` +- **CSS 변수 cascade 방식:** 블록 템플릿 수정 불필요 — 이미 `var(--font-body)` 등 187회 사용 중이므로 area div에서 override하면 자동 적용 + +--- + +## A-2: 5단계 HTML을 프롬프트에 전달 ✅ 완료 + +### 현재 상태 +- pipeline.py:103 `_review_balance(html, ...)` — html 파라미터 있지만 141~146행 프롬프트에서 미사용 +- 블록별 데이터 길이만 전달 → 시각적 균형 판단 불가 + +### 작업 +`_review_balance()` 프롬프트에 html 전문 포함 + +```python +user_prompt = ( + f"## 1차 조립 HTML\n{html}\n\n" + f"## 블록별 데이터 양\n" + "\n".join(block_summary) + ... +) +``` + +### 하드코딩 점검 +- html 전문 전달 (임의 잘라내기 없음) ✅ +- Sonnet 200K context → 전문 전달 가능 ✅ + +### 충돌/회귀 +- 프롬프트 텍스트만 변경. 파싱/함수 시그니처 동일 ✅ +- 회귀: 없음 ✅ + +### 수정 파일 +- `src/pipeline.py` — `_review_balance()` 프롬프트 부분 + +### 구현 결과 +- `_review_balance()` user_prompt에 `f"## 1차 조립 HTML\n{html}\n\n"` 추가 — html 전문 전달 +- 시스템 프롬프트에 "5. HTML 구조: 블록이 영역 안에 잘 배치되었는지" 점검 항목 추가 +- 출력 형식에 `target_ratio` 필드 추가 (A-3과 연동) + +--- + +## A-3: 5단계 shrink action + 기존 expand 하드코딩 수정 ✅ 완료 + +### 현재 상태 +- shrink: 조건에 없어서 무시됨 +- expand: `char_guide * 1.5` 하드코딩 (pipeline.py:186) +- CLAUDE.md: "모든 판단은 사고로. 하드코딩 없음" + +### 작업 + +**1) 5단계 프롬프트 출력 형식 변경** + +기존: +```json +{"adjustments": [{"block_area": "...", "action": "expand|shrink|rewrite", "detail": "..."}]} +``` + +변경: +```json +{"adjustments": [{"block_area": "...", "action": "expand|shrink|rewrite", "target_ratio": 1.4, "detail": "..."}]} +``` + +→ Sonnet(디자인 팀장)이 **얼마나** 조정할지를 `target_ratio`로 결정 + +**2) _apply_adjustments() 코드 변경** + +```python +for adj in adjustments: + area = adj.get("block_area", "") + action = adj.get("action", "") + ratio = adj.get("target_ratio") + detail = adj.get("detail", "") + + for page in layout_concept.get("pages", []): + for block in page.get("blocks", []): + if block.get("area") == area: + if action == "expand" and ratio: + for key in block.get("char_guide", {}): + block["char_guide"][key] = int(block["char_guide"][key] * ratio) + elif action == "shrink" and ratio: + for key in block.get("char_guide", {}): + block["char_guide"][key] = int(block["char_guide"][key] * ratio) + logger.info(f"조정: {area} → {action} ×{ratio} ({detail})") +``` + +→ ratio가 없으면(Sonnet 누락) 조정 안 함 (무동작이 안전) +→ expand/shrink 모두 Sonnet이 결정한 ratio 사용 + +### 하드코딩 점검 +- ratio: Sonnet이 결정 ✅ (기존 `1.5` 하드코딩 제거) +- ratio 없을 때 기본값: 적용 안 함 (하드코딩 기본값 없음) ✅ + +### 충돌/회귀 +- 기존 expand `* 1.5` 제거 → **기존 하드코딩을 수정하는 것이므로 회귀 아님, 개선임** +- 5단계 프롬프트 출력 형식 변경 → `_parse_json()` 파싱에 영향 없음 (JSON 구조) +- Sonnet이 target_ratio를 안 넣으면 → 조정 안 함 → 기존보다 보수적 (안전) + +### 수정 파일 +- `src/pipeline.py` — `_review_balance()` 프롬프트 + `_apply_adjustments()` 코드 + +### 구현 결과 +- `_apply_adjustments()` 전면 재작성: + - `ratio = adj.get("target_ratio")` — Sonnet이 결정한 비율 추출 + - `action == "expand" and ratio` → `char_guide[key] * ratio` + - `action == "shrink" and ratio` → `char_guide[key] * ratio` + - ratio 없으면(Sonnet 누락) 조정 안 함 → 안전 + - 기존 `* 1.5` 하드코딩 완전 제거 +- `_review_balance()` 프롬프트에 action별 target_ratio 설명 추가 + +--- + +## A-4: 5단계 rewrite action ✅ 완료 + +### 현재 상태 +- rewrite가 expand와 같은 조건(`action in ("expand", "rewrite")`)에 들어가지만 +- `if action == "expand"` 안에만 실제 로직 → rewrite는 로그만 찍고 끝 (no-op) + +### 작업 + +A-3에서 변경한 코드에 rewrite 분기 추가: + +```python +elif action == "rewrite": + if "data" in block: + del block["data"] + block["reason"] = f"재작성: {detail}" + logger.info(f"조정: {area} → rewrite ({detail})") +``` + +- data 삭제 → fill_content() 재호출(192행) 시 재매칭 + +### 하드코딩 점검 +- 없음 ✅ + +### 충돌/회귀 +- 기존 no-op → 실제 동작으로 변경 (개선, 회귀 아님) +- fill_content 재호출 시 topic_id 매칭으로 다른 블록도 재편집될 수 있음 → 기존 expand도 동일 동작 +- 회귀: 없음 ✅ + +### 수정 파일 +- `src/pipeline.py` — `_apply_adjustments()` (A-3과 같은 함수) + +### 구현 결과 +- `_apply_adjustments()`에 `elif action == "rewrite"` 분기 추가 +- data 삭제 (`del block["data"]`) → fill_content 재호출 시 topic_id로 재매칭 +- `block["reason"]` 업데이트 → 편집자에게 재작성 방향 전달 + +--- + +## A-5: overflow 정책 재검토 ✅ 완료 + +### 현재 상태 +- base.css:16 `.slide { overflow: hidden }` — 프레임 경계 +- base.css:74 `.slide > div { overflow: hidden }` — BF-8에서 추가한 area별 안전망 + +### 작업 + +```css +/* base.css:74 변경 */ +.slide > div { + overflow: visible; /* hidden → visible: A-1이 사전 조정하므로 잘림 방지 불필요 */ + min-height: 0; + min-width: 0; +} +``` + +`.slide { overflow: hidden }`(16행)은 **유지** — 프레임 바깥은 잘려야 함 + +### 하드코딩 점검 +- 없음 ✅ + +### 충돌/회귀 +- BF-8에서 추가한 `.slide > div { overflow: hidden }` 제거 → BF-8과 방향 다름 +- **그러나 BF-8의 overflow: hidden은 "텍스트를 자르지 않는다" 원칙과 충돌하는 임시 조치였음** +- A-1(Sonnet 디자인 조정)이 넘침을 사전 방지 → 안전망 불필요 +- **반드시 A-1 완료 후 적용** + +### 수정 파일 +- `static/base.css` + +### 구현 결과 +- base.css `.slide > div` — `overflow: hidden` → `overflow: visible` +- 주석 추가: "A-1(Sonnet 디자인 조정)이 텍스트 양에 맞게 CSS를 사전 조정하므로, area 레벨에서는 overflow: visible로 텍스트 잘림을 방지한다." +- `.slide { overflow: hidden }`(16행)은 유지 — 프레임 경계 보호 + +--- + +## 수정 파일 총괄 + +| 파일 | 항목 | 변경 성격 | +|------|------|----------| +| `templates/blocks/media/image-row-2col.html` | A-6 | CSS 1줄 변경 | +| `templates/blocks/media/image-grid-2x2.html` | A-6 | CSS 1줄 변경 | +| `templates/blocks/tables/compare-3col-badge.html` | A-7, A-8 | CSS 추가 | +| `src/pipeline.py` | A-1, A-2, A-3, A-4 | 신규 함수 + 기존 함수 수정 | +| `src/renderer.py` | A-1 | area_styles 주입 (3줄) | +| `templates/slide-base.html` | A-1 | style 속성 추가 (1줄) | +| `static/base.css` | A-5 | overflow 변경 (1줄) | + +--- + +## 검증 체크리스트 + +- [ ] A-6: image-row, image-grid에서 이미지가 crop 안 됨 +- [ ] A-7: 테이블 열 너비가 내용과 무관하게 균등 +- [ ] A-8: sidebar(35%)에 표가 들어가면 폰트 자동 축소 +- [ ] A-1: Sonnet이 area별 CSS 변수 override 출력 → 렌더링에 반영 +- [ ] A-1: _adjust_design 실패 시 기존과 동일하게 렌더링 (안전) +- [ ] A-2: 5단계 Sonnet이 HTML 구조를 보고 균형 판단 +- [ ] A-3: shrink 시 Sonnet이 결정한 ratio로 char_guide 축소 +- [ ] A-3: 기존 expand 1.5 하드코딩 제거됨 +- [ ] A-4: rewrite 시 해당 블록 data 삭제 후 재편집 +- [ ] A-5: area div에서 텍스트 잘림 없음 + +--- + +## 수정 이력 + +| 날짜 | 내용 | +|------|------| +| 2026-03-25 | 초안. 하드코딩 제거 반영 (A-2 html 전문, A-3 target_ratio, A-8 상대값). | +| 2026-03-25 | Phase A 전체 구현 완료. 검증 통과. | + +## 구현 완료 확인 + +| 항목 | 검증 결과 | +|------|----------| +| A-1 | `_adjust_design()` 함수 존재, pipeline에서 호출, renderer에서 area_styles 주입, slide-base에서 style_override 적용 | +| A-2 | `_review_balance()` 프롬프트에 html 전문 포함, target_ratio 출력 형식 추가 | +| A-3 | shrink action 구현 + expand 하드코딩(×1.5) 제거 → Sonnet target_ratio 기반 | +| A-4 | rewrite action 구현 — data 삭제 + reason 업데이트 + fill_content 재호출 | +| A-5 | `.slide > div { overflow: visible }` — 프레임(.slide)은 hidden 유지 | +| A-6 | image-row, image-grid: cover → contain + 높이 하드코딩 제거 | +| A-7 | table-layout: fixed + width: 100% | +| A-8 | container-type: inline-size + @container (40rem, 25rem) | +| 추가 | compare-3col-badge tr:hover 제거 (Phase C-2 선행 처리) | diff --git a/04. design_agent/IMPROVEMENT-PHASE-B.md b/04. design_agent/IMPROVEMENT-PHASE-B.md new file mode 100644 index 0000000..ec7d685 --- /dev/null +++ b/04. design_agent/IMPROVEMENT-PHASE-B.md @@ -0,0 +1,368 @@ +# Phase B: 누락 기능 구현 — 실행 상세 + +> 누락된 기능 구현. 실작업 5개 (B-6, B-7은 해결됨). +> 원칙: 하드코딩 금지. 모든 판단은 AI 사고. 회귀 금지. + +--- + +## 실행 순서 + +``` +[독립] B-1 (details 템플릿), B-4+B-5 (이미지/표 판단), B-8 (fallback 교체) + → B-2 (인쇄 JS, B-1 후) + → B-3 (catalog 등록, B-1 후) +``` + +--- + +## B-1: details-block 템플릿 제작 ✅ 완료 + +### 현재 상태 +- BLOCK_SLOTS에 정의 있음 (design_director.py:47~49): `required: ["summary_text", "detail_content"], optional: ["label"]` +- _apply_defaults에 기본값 있음 (content_editor.py:248): `{"summary_text": "(상세 내용)", "detail_content": ""}` +- **HTML 템플릿 파일이 templates/blocks/ 어디에도 없음** → 렌더링 불가 + +### API 선택 +- AI 호출 없음. HTML/CSS 템플릿만. + +### 작업 +`templates/blocks/emphasis/details-block.html` 신규 제작 + +**구조:** +```html +
+ + {% if label %}{{ label }}{% endif %} + {{ summary_text }} + +
{{ detail_content }}
+
+``` + +### 하드코딩 점검 — CSS 규칙 +- 색상: `var(--color-*)` 만 사용. `#직접값` 금지 +- 폰트: `var(--font-*)` 사용 +- 여백: `var(--spacing-*)` 사용 +- 테두리: `var(--border-width)`, `var(--accent-border)`, `var(--radius)` 사용 +- **기존 emphasis 블록의 직접값(#1e3a5f 등)을 따라하지 않는다** + +### 충돌/회귀 +- 충돌: 없음. 신규 파일 추가만 +- 회귀: 없음. BLOCK_SLOTS/_apply_defaults와 정합 +- 단발성: 아님. `
/`는 HTML 표준 — 브라우저 내장, 의존성 없음 + +### 수정 파일 +- 신규: `templates/blocks/emphasis/details-block.html` + +### 구현 결과 +- `templates/blocks/emphasis/details-block.html` 신규 제작 완료 +- HTML 구조: `
` → `` (label + summary_text) → `
` (detail_content) +- CSS: **`#직접값` 0개** — 전부 디자인 토큰으로 구현 + - 배경: `var(--color-bg-subtle)`, 테두리: `var(--color-border)`, 액센트: `var(--color-accent)` + - 폰트: `var(--font-body)`, `var(--font-caption)`, 여백: `var(--spacing-inner)`, `var(--spacing-block)` +- summary 마커: 기본 브라우저 마커 숨기고(`list-style: none`, `::-webkit-details-marker { display: none }`) 커스텀 ▶/▼ 표시 +- 좌측 파란 액센트 라인: `border-left: var(--accent-border) solid var(--color-accent)` — quote-left-border와 톤 통일 + +--- + +## B-2: 인쇄 시 details 자동 펼침 JS ✅ 완료 + +### 현재 상태 +- slide-base.html의 `@media print`에 page-break만 있음 +- details 자동 펼침 JS 없음 +- CLAUDE.md: "인쇄 시 JavaScript 6줄로 자동 펼침" + +### 작업 +slide-base.html `` 직전에 삽입: + +```html + +``` + +### 하드코딩 점검 +- 없음. DOM API만 사용. 고정값 없음. + +### 충돌/회귀 +- 충돌: 없음. `{% endfor %}` 이후, Jinja2 루프 밖에 삽입 +- 회귀: 없음. 기존 HTML에 `
` 없으면 querySelectorAll이 빈 NodeList → 무동작 +- 다운로드 HTML: renderer.py의 CSS 인라인 삽입은 `` → ` + + +
+ {% block content %}{% endblock %} +
+ + +``` + +**Block-level templates:** +```jinja2 +{# blocks/comparison.html #} +
+
+

{{ left_title }}

+

{{ left_content }}

+
+
+

{{ right_title }}

+

{{ right_content }}

+
+
+``` + +**Composition via includes:** +```jinja2 +{# Generated by renderer based on Sonnet's layout decision #} +{% extends "base_slide.html" %} +{% block content %} + {% include "blocks/quote-block.html" %} +
+ {% include "blocks/comparison.html" %} + {% include "blocks/card-grid.html" %} +
+ {% include "blocks/conclusion-bar.html" %} +{% endblock %} +``` + +### 3.3 Slot Constraints + +Each slot should have defined constraints that Sonnet respects: + +| Slot Type | Max Characters (Korean) | Required | Notes | +|---|---|---|---| +| slide_title | 30 | Yes | Single line | +| block_title | 20 | Yes | Single line | +| item_title | 15 | Yes | Single line | +| item_content | 80 | No | 2-3 lines | +| quote_text | 120 | Yes | 3-4 lines | +| big_number | 8 | Yes | Number + unit | +| conclusion | 60 | Yes | Single line | +| caption | 40 | No | Single line | + +**Korean consideration:** Korean characters are roughly 2x the width of Latin characters at the same font size. Character limits should be specified in characters, not words, since Korean doesn't use spaces the same way as English. + +--- + +## 4. HTML to PDF Conversion + +### 4.1 Playwright (Recommended) + +**Why Playwright over Puppeteer:** +- Native Python SDK (no Node.js dependency for a Python project) +- Multiple browser support (Chromium, Firefox, WebKit), though PDF only works in Chromium +- Growing community, active maintenance, better CI/CD integration +- Full CSS Grid support via real Chromium rendering engine + +**Python implementation:** +```python +from playwright.async_api import async_playwright + +async def html_to_pdf(html_content: str, output_path: str) -> None: + async with async_playwright() as p: + browser = await p.chromium.launch() + page = await browser.new_page() + await page.set_content(html_content, wait_until="networkidle") + await page.pdf( + path=output_path, + width="1280px", + height="720px", + print_background=True, + prefer_css_page_size=True, + ) + await browser.close() +``` + +**Key options:** +- `print_background=True` -- required for background colors/images +- `prefer_css_page_size=True` -- lets CSS `@page` rules control dimensions +- `width`/`height` -- custom page dimensions (accepts px, in, mm, cm units) + +### 4.2 Print CSS for Slide Format + +```css +@media print { + @page { + size: 1280px 720px; + margin: 0; + } + body { + margin: 0; + -webkit-print-color-adjust: exact; + print-color-adjust: exact; + } + .slide { + width: 1280px; + height: 720px; + page-break-after: always; + overflow: hidden; + } +} +``` + +**`-webkit-print-color-adjust: exact`** is critical -- without it, background colors and images may be stripped in PDF output. + +### 4.3 Quality Comparison + +Both Puppeteer and Playwright use Chromium's print-to-PDF engine, so output quality is identical. The choice comes down to: + +| Factor | Playwright | Puppeteer | +|---|---|---| +| Language | Python, JS, C#, Java | JS/Node.js only | +| PDF engine | Chromium only | Chromium only | +| CSS Grid quality | Excellent (Chromium) | Excellent (Chromium) | +| Korean font rendering | Excellent | Excellent | +| Install size | ~400MB (browser binary) | ~300MB | +| API ergonomics | Better async patterns | More established | + +**Recommendation:** Playwright, because the Design Agent backend is Python. No need to bridge to Node.js. + +### 4.4 Korean-Specific Considerations + +- Fonts must be available on the server. Self-host Pretendard/Noto Sans KR WOFF2 files or use CDN. +- Set `lang="ko"` on the HTML element for proper line-breaking algorithms. +- Ensure `@font-face` declarations are loaded before PDF generation (`wait_until="networkidle"`). + +--- + +## 5. Pure CSS Diagrams + +### 5.1 Venn Diagrams (Pure CSS) + +**Technique:** Overlapping circles with opacity and negative margins. + +```css +.venn-container { display: flex; align-items: center; justify-content: center; } +.venn-circle { + width: 200px; height: 200px; + border-radius: 50%; + opacity: 0.7; + display: flex; align-items: center; justify-content: center; + padding: 20px; + text-align: center; +} +.venn-a { background: var(--color-accent); } +.venn-b { background: var(--color-neutral); margin-left: -60px; } +``` + +**Advanced approach (Adrian Roselli):** CSS Grid + `shape-outside` for text wrapping within overlapping regions. More complex but better for text-heavy Venn diagrams. + +**Limitation:** Pure CSS Venn diagrams work well for 2-3 circles. Beyond that, SVG is more practical. + +### 5.2 Flowcharts / Process Arrows (Pure CSS) + +**Technique:** Flexbox/Grid layout + pseudo-elements for arrows. + +```css +.process-steps { display: flex; align-items: center; gap: 0; } +.process-step { + background: var(--color-bg-subtle); + padding: var(--spacing-inner); + position: relative; + flex: 1; +} +.process-step + .process-step::before { + content: ''; + position: absolute; + left: -12px; top: 50%; + transform: translateY(-50%); + border: 8px solid transparent; + border-left-color: var(--color-accent); +} +``` + +**CSS Anchor Positioning (2025-2026):** A new CSS feature for connecting elements with lines. Supported in Chrome 125+, Safari 26+, not yet in Firefox. Since we target Chromium (for PDF generation), this is usable but adds complexity. For the Design Agent, pseudo-element arrows are simpler and more reliable. + +### 5.3 Tree/Hierarchy Diagrams (Pure CSS) + +**Technique:** Nested `