From 427522bd1a96e39a41e2194a624e665197d5cfab Mon Sep 17 00:00:00 2001 From: gahusb Date: Mon, 18 May 2026 00:14:59 +0900 Subject: [PATCH] =?UTF-8?q?feat(insta-lab):=20import=5Fdesign=5Ftheme=20?= =?UTF-8?q?=E2=80=94=20Vision=20=ED=98=B8=EC=B6=9C=20+=20Jinja=20sanity=20?= =?UTF-8?q?+=20=EB=B0=B1=EC=97=85=20=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- insta-lab/app/design_importer.py | 153 +++++++++++++++++++++++- insta-lab/tests/test_design_importer.py | 65 ++++++++++ 2 files changed, 217 insertions(+), 1 deletion(-) diff --git a/insta-lab/app/design_importer.py b/insta-lab/app/design_importer.py index bf75b3f..c8179ce 100644 --- a/insta-lab/app/design_importer.py +++ b/insta-lab/app/design_importer.py @@ -3,18 +3,28 @@ CLI (이 phase 이후 추가): python -m app.design_importer """ +import base64 +import datetime import json import logging +import re from pathlib import Path -from typing import Dict, List +from typing import Any, Dict, List, Tuple +from anthropic import Anthropic +from jinja2 import BaseLoader, Environment, TemplateSyntaxError from PIL import Image +from .config import ANTHROPIC_API_KEY, ANTHROPIC_MODEL_SONNET + logger = logging.getLogger(__name__) __all__ = [ "_resolve_page_mapping", "_validate_images", + "_call_vision", + "_validate_html_template", + "import_design_theme", ] # 페이지 1 (커버) 키워드 우선순위 — 먼저 매치된 키워드를 가진 첫 파일만 page 1 @@ -108,3 +118,144 @@ def _validate_images(pages_dir: Path) -> None: raise ValueError( f"모든 카드 디자인은 1080x1350이어야 함. 잘못된 파일: {msg}" ) + + +# ── Vision 호출 + HTML 생성 ─────────────────────────────────────────────────── + +_VISION_SYSTEM_PROMPT = """너는 인스타그램 카드 뉴스 디자인을 모방하는 프론트엔드 디자이너다. + +입력: 10장의 카드 디자인 이미지 (각 1080×1350, placeholder 텍스트가 박혀있음) + 파일명 → 페이지 번호 매핑. +출력: 단일 Jinja2 HTML 파일 본문 (코드펜스·설명 텍스트 금지). + +핵심 제약 — placeholder 텍스트 마스킹: +PNG에는 디자인 placeholder 텍스트가 이미 그려져 있다. 동적 카피로 교체할 때 +원본 텍스트가 비치면 안 된다. 각 텍스트 영역마다 두 layer를 그려라: + (a) 마스킹 박스: position: absolute로 placeholder 영역과 같은 좌표. + background는 그 영역 주변 픽셀 색 (카드 배경색)에서 추출. padding 8px 여유. + (b) 동적 텍스트 layer: 마스킹 박스와 동일 좌표. + font-size·font-weight·color는 원본 placeholder의 스타일을 모방. + {{ headline }} / {{ body }} / {{ cta }} Jinja 변수 사용. + +페이지 종류별 영역 가이드: +- page 1 (cover): 메인 headline 1개 영역 +- page 2~9 (body): headline 영역 + body 영역 +- page 10 (cta): headline + body + cta 영역 + +요구사항: +- 컨테이너 width 1080px, height 1350px +- 각 페이지마다 `background-image: url('pages/{{filename}}')`로 사용자 PNG 로드 +- page_no 1~10 분기: {% if page_no == N %}...{% endif %} 구조 +- 폰트는 Noto Sans KR (Google Fonts CDN). letter-spacing -0.02em, line-height 1.3 기본 +- 텍스트 영역은 word-wrap: break-word + overflow: hidden (동적 카피가 길어도 마스킹 박스 밖으로 안 새도록) +- HTML 에