From c42e65fc7e897c078c03335fa935e6e7ab72bb22 Mon Sep 17 00:00:00 2001 From: kyeongmin Date: Tue, 24 Mar 2026 17:25:47 +0900 Subject: [PATCH] Initial commit: Kei Design Agent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 콘텐츠를 시각적으로 구조화된 슬라이드 HTML로 변환하는 독립 에이전트. 아키텍처 (4단계 파이프라인): 1. Kei 실장 (Opus) — 콘텐츠 유형 분류 + 블록 배치 2. 디자인 팀장 (Sonnet) — 레이아웃 컨셉 (블록 배치 + 페이지 수) 3. 텍스트 편집자 (Sonnet) — 슬롯 텍스트 정리 (핵심 유지) 4. CSS Grid 렌더러 — HTML 조립 블록 템플릿 7종: comparison, card-grid, relationship, process, quote-block, conclusion-bar, comparison-table 기술 스택: FastAPI + Anthropic API + Jinja2 + CSS Grid Pretendard Variable 한국어 폰트 Co-Authored-By: Claude Opus 4.6 (1M context) --- .env.example | 3 + .gitignore | 11 + CLAUDE.md | 321 +++++++++++ PLAN.md | 171 ++++++ PROGRESS.md | 96 ++++ docs/FIGMA-COMPONENT-EXTRACTION-PLAN.md | 388 ++++++++++++++ docs/RESEARCH.md | 686 ++++++++++++++++++++++++ pyproject.toml | 31 ++ src/__init__.py | 0 src/config.py | 20 + src/content_editor.py | 161 ++++++ src/design_director.py | 172 ++++++ src/kei_client.py | 141 +++++ src/main.py | 60 +++ src/pipeline.py | 71 +++ src/renderer.py | 171 ++++++ static/base.css | 69 +++ static/index.html | 253 +++++++++ static/tokens.css | 42 ++ templates/blocks/card-grid.html | 65 +++ templates/blocks/comparison-table.html | 58 ++ templates/blocks/comparison.html | 51 ++ templates/blocks/conclusion-bar.html | 31 ++ templates/blocks/process.html | 61 +++ templates/blocks/quote-block.html | 29 + templates/blocks/relationship.html | 88 +++ templates/slide-base.html | 52 ++ tests/__init__.py | 0 28 files changed, 3302 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 CLAUDE.md create mode 100644 PLAN.md create mode 100644 PROGRESS.md create mode 100644 docs/FIGMA-COMPONENT-EXTRACTION-PLAN.md create mode 100644 docs/RESEARCH.md create mode 100644 pyproject.toml create mode 100644 src/__init__.py create mode 100644 src/config.py create mode 100644 src/content_editor.py create mode 100644 src/design_director.py create mode 100644 src/kei_client.py create mode 100644 src/main.py create mode 100644 src/pipeline.py create mode 100644 src/renderer.py create mode 100644 static/base.css create mode 100644 static/index.html create mode 100644 static/tokens.css create mode 100644 templates/blocks/card-grid.html create mode 100644 templates/blocks/comparison-table.html create mode 100644 templates/blocks/comparison.html create mode 100644 templates/blocks/conclusion-bar.html create mode 100644 templates/blocks/process.html create mode 100644 templates/blocks/quote-block.html create mode 100644 templates/blocks/relationship.html create mode 100644 templates/slide-base.html create mode 100644 tests/__init__.py diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..520cdf1 --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +ANTHROPIC_API_KEY=sk-ant-... +KEI_API_URL=http://localhost:8000 +LOG_LEVEL=DEBUG diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5822109 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +.env +__pycache__/ +*.pyc +.pytest_cache/ +.ruff_cache/ +*.egg-info/ +dist/ +build/ +.venv/ +node_modules/ +data/ diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..f2f0197 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,321 @@ +# Design Agent — 콘텐츠 시각 구조화 슬라이드 생성기 + +## 프로젝트 목적 + +텍스트 콘텐츠를 **1페이지 가로 슬라이드**로 시각 구조화하는 독립 에이전트. +콘텐츠의 의미를 분석하여 적합한 레이아웃 블록을 선택하고, 핵심만 추출하여 깔끔한 HTML/CSS로 렌더링한다. + +**핵심 원칙:** 전체 페이지를 하나의 고정 템플릿으로 찍어내는 것이 아니라, 콘텐츠를 분석 → 각 덩어리별로 적합한 레이아웃 블록 선택 → 조합하여 배치. + +--- + +## 아키텍처 + +``` +Kei (실장) — Kei Persona API 호출 + "이 콘텐츠는 비교+정의+관계도 구조다. 이렇게 배치해라." + ↓ +디자인 팀장 (Sonnet) + "비교는 2단, 정의는 카드 3열, 관계도는 벤. 핵심만 남기고 나머지 버려." + ↓ +실행자 (CSS Grid 렌더러) + "팀장이 정한 대로 CSS Grid로 조립." +``` + +### 역할 분리 + +| 역할 | 담당 | 하는 일 | 하지 않는 일 | +|------|------|---------|------------| +| Kei (실장) | Opus via Kei API | 콘텐츠 의미 분석, 유형 분류, 배치 방향 결정 | 디자인, CSS 작성 | +| 디자인 팀장 | Sonnet | 블록 타입 선택, 콘텐츠 선별(70% 버림), 슬롯 채우기, 세부 기준 수립 | 콘텐츠 의미 판단 | +| 실행자 | CSS Grid 렌더러 | 확정적 HTML/CSS 생성, 디자인 토큰 적용 | 판단, 선택 | + +--- + +## 핵심 프로세스 + +``` +사용자 콘텐츠 입력 (텍스트 붙여넣기 또는 파일 업로드) + ↓ +[1단계] Kei 실장(Opus) — 콘텐츠 유형 분류 + → "이건 비교(A vs B) + 정의(3개 용어) + 관계도(상위/하위)" + → 적합한 블록 조합 결정 + ↓ +[2단계] 디자인 팀장(Sonnet) — 레이아웃 컨셉만 + → "이 파트는 카드로, 이건 비교로, 2페이지 필요" + → 블록 배치 + 페이지 수 + 슬롯 목록 (텍스트는 채우지 않음) + ↓ +[3단계] 텍스트 편집자(Sonnet, Kei 역할) — 슬롯 텍스트 정리 + → 도메인 전문가로서 원본 핵심을 유지하며 각 슬롯 분량에 맞게 편집 + → 과도한 요약 금지, 출처 보존, 개조식 작성 + ↓ +[4단계] 실행자(CSS Grid) — 확정적 HTML 생성 + → 블록 타입에 맞는 CSS 템플릿 적용 + → 디자인 토큰 (색상, 여백, 폰트 크기) 적용 + → 다중 페이지 시 page-break 처리 + ↓ +미리보기 → 사용자 확인 → HTML 다운로드 +``` + +**핵심 원칙:** 디자인 팀장은 레이아웃만 결정하고 콘텐츠를 건드리지 않는다. 텍스트 정리는 도메인 지식이 있는 Kei 역할(텍스트 편집자)이 한다. +``` + +--- + +## 콘텐츠 유형 분류 기준 + +Opus가 콘텐츠를 분석하여 아래 유형으로 분류한다. +**이 분류는 하드코딩이 아니라, Opus가 매번 사고하여 판단한다.** + +| 텍스트 패턴 | 유형 | 적합한 블록 | +|------------|------|-----------| +| "A vs B", 장단점, 차이점 | 비교 | 2단 병렬 / 비교 테이블 | +| "1단계 → 2단계 → 3단계" | 프로세스 | 플로우차트 / 단계 카드 | +| "X는 Y를 포함한다", 상위-하위 | 구성/관계 | 벤 다이어그램 / 트리 | +| 수치, KPI, 통계 | 핵심 지표 | 큰 숫자 + 보조 텍스트 | +| 용어 정의, 개념 설명 | 정의 | 카드 3열 / 아이콘 카드 | +| 기능/특성 나열 | 목록 | 아이콘 리스트 / 카드 그리드 | +| 연도별 사건, 로드맵 | 시간 순서 | 타임라인 (가로/세로) | +| 핵심 메시지, 결론 | 강조 | 결론 바 / 인용 블록 | +| 문제 상황, 경고 | 문제 제기 | 경고 박스 / 강조 인용 | + +--- + +## 블록 타입 정의 + +각 블록은 독립적인 CSS 컴포넌트로, 슬롯(교체 가능한 위치)을 가진다. + +### 1. 비교 블록 (comparison) +- 2단 병렬 레이아웃 +- 슬롯: 좌측 제목/내용, 우측 제목/내용 +- 용도: A vs B, 장단점, Before/After + +### 2. 카드 그리드 (card-grid) +- 2~4열 카드 배열 +- 슬롯: 카드별 아이콘/제목/설명/출처 +- 용도: 용어 정의, 개념 설명, 기능 나열 + +### 3. 관계도 (relationship) +- 벤 다이어그램 또는 트리 구조 +- 슬롯: 중심 요소, 하위 요소들, 관계 설명 +- 용도: 상위-하위 관계, 포함 관계, 기술 융합 + +### 4. 프로세스 (process) +- 가로 또는 세로 단계 흐름 +- 슬롯: 단계별 번호/제목/설명 +- 용도: 절차, 워크플로우, 파이프라인 + +### 5. 타임라인 (timeline) +- 시간 축 기반 배치 +- 슬롯: 날짜/제목/설명 +- 용도: 연혁, 로드맵, 일정 + +### 6. 핵심 지표 (big-number) +- 큰 숫자 + 보조 텍스트 +- 슬롯: 숫자, 단위, 설명 +- 용도: KPI, 통계, 성과 수치 + +### 7. 강조 인용 (quote-block) +- 배경색 + 좌측 라인 + 인용 텍스트 +- 슬롯: 인용 텍스트, 출처 +- 용도: 문제 제기, 핵심 메시지, 정의 + +### 8. 결론 바 (conclusion-bar) +- 하단 전체 폭 강조 영역 +- 슬롯: 핵심 한 줄 +- 용도: 슬라이드 결론, 요약 메시지 + +### 9. 비교 테이블 (comparison-table) +- 테이블 형식의 다항목 비교 +- 슬롯: 행/열 헤더, 셀 내용 +- 용도: 다차원 비교, 기능 매트릭스 + +### 10. 이미지 참조 (image-ref) +- 이미지 썸네일 + 캡션 +- 슬롯: 이미지 경로, 캡션 텍스트 +- 용도: 근거 자료, 문서 참조, 사진 + +--- + +## 페이지 구성 원칙 + +### 레이아웃 배치 규칙 +- CSS Grid 기반 (`grid-template-areas`) +- 가로 슬라이드 비율: 16:9 (1280×720 또는 1920×1080) +- 최대 블록 수: 1페이지에 4~6개 +- 정보 계층: 위 → 아래 (문제 제기 → 분석 → 결론) +- 여백: 블록 간 최소 20px, 페이지 패딩 40px + +### 블록 조합 예시 + +``` +┌─────────────────────────────────────────────┐ +│ [강조 인용] 문제 제기 │ +├──────────────────┬──────────────────────────┤ +│ [비교] │ [카드 그리드] │ +│ 2단 비교 │ 정의 3열 │ +├──────────────────┴──────────────────────────┤ +│ [관계도] 벤 다이어그램 │ +├─────────────────────────────────────────────┤ +│ [결론 바] 핵심 한 줄 │ +└─────────────────────────────────────────────┘ +``` + +--- + +## 디자인 원칙 (절대 규칙) + +### DO (해야 하는 것) +- 여백을 충분히 확보한다 (여백 > 장식) +- 색상은 최대 3개 (메인 1개 + 포인트 1개 + 중성 1개) +- 폰트 크기 체계를 일관되게 유지 (제목/소제목/본문/캡션 4단계) +- 흑백 기조 + 포인트 컬러 최소 사용 +- 정보 계층을 시각적으로 명확히 표현 +- 한 슬라이드에 메시지는 1개 + +### DON'T (하지 않는 것) +- 그라데이션 배경 금지 +- CSS 애니메이션/트랜지션 금지 +- 호버 효과 금지 +- 그림자(box-shadow) 최소화 (1개 레벨만) +- 원본 콘텐츠를 전부 넣으려 하지 않는다 (70% 버려라) +- 다크 테마 금지 (요청하지 않는 한) +- 둥근 모서리 과다 사용 금지 (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; /* 강조 테두리 */ +} +``` + +--- + +## 교본 (레퍼런스) 관리 + +### 저장 위치 +``` +D:\ad-hoc\kei\design_agent\ +├── CLAUDE.md ← 이 파일 +├── templates/ ← 블록별 HTML/CSS 교본 +│ ├── comparison.html ← 비교 블록 교본 +│ ├── card-grid.html ← 카드 그리드 교본 +│ ├── relationship.html ← 관계도 교본 +│ ├── process.html ← 프로세스 교본 +│ ├── timeline.html ← 타임라인 교본 +│ ├── big-number.html ← 핵심 지표 교본 +│ ├── quote-block.html ← 강조 인용 교본 +│ ├── conclusion-bar.html ← 결론 바 교본 +│ ├── comparison-table.html ← 비교 테이블 교본 +│ └── image-ref.html ← 이미지 참조 교본 +├── samples/ ← 완성 슬라이드 샘플 (레퍼런스 이미지 + HTML) +├── design-tokens.css ← 공통 디자인 토큰 +└── docs/ ← 조사 자료, 기술 문서 +``` + +### 교본 추가 방법 +1. 좋은 디자인 샘플을 찾는다 (CodePen, 직접 제작 등) +2. HTML/CSS 코드를 `templates/` 폴더에 저장한다 +3. 슬롯 위치를 `{{SLOT_NAME}}` 형식으로 표시한다 +4. CLAUDE.md의 블록 타입 정의에 참조를 추가한다 + +### 교본 품질 기준 +- 디자인 원칙(DO/DON'T)을 준수하는가 +- 슬롯이 명확하게 분리되어 있는가 +- 디자인 토큰을 사용하는가 (하드코딩 색상 아닌 CSS 변수) +- 1페이지 안에 들어가는 크기인가 + +--- + +## Kei API 연동 + +### 연동 방식 +- Design Agent는 Kei Persona 서버(`localhost:8000`)의 API를 호출하여 콘텐츠 분석을 요청한다 +- Kei 서버가 떠있어야 Design Agent가 동작한다 +- 향후 글벗에 붙일 때도 같은 API 호출 방식 + +### 호출 포인트 +| 단계 | API | 용도 | +|------|-----|------| +| 1단계 콘텐츠 분류 | Kei API (Opus) | 콘텐츠 유형 판단 + 배치 방향 | +| 2단계 콘텐츠 선별 | Kei API (Sonnet) | 핵심 추출 + 슬롯 채우기 | +| 3단계 렌더링 | 로컬 (CSS Grid) | HTML 생성 (API 불필요) | + +### 독립 실행 가능 +- Kei API 없이도 2-3단계만으로 동작 가능 (사용자가 직접 유형 선택) +- Kei API 연결 시 1단계 자동화 + +--- + +## 기술 스택 (예정) + +| 역할 | 도구 | 비고 | +|------|------|------| +| 프론트엔드 | React + Vite | Kei와 동일 스택 | +| 렌더링 | CSS Grid + 디자인 토큰 | 순수 CSS, 프레임워크 없음 | +| AI 콘텐츠 분석 | Kei API (Opus + Sonnet) | localhost:8000 | +| 출력 | HTML 다운로드 | PDF 불필요 | + +--- + +## 향후 연결 가능성 + +``` +현재: 독립 개발 + 테스트 + ↓ +검증 후 선택지: + A) Kei 본체에 합치기 (대화 안에서 "슬라이드로 정리해줘") + B) 글벗에 붙이기 (문서 자동화 → 시각화 단계) + C) 둘 다 +``` + +독립적으로 만들어두면 어디에 붙이든 API 호출만 하면 된다. + +--- + +## 업계 근거 + +- **SlideSpeak**: 16개 레이아웃 타입 + 슬롯 기반 매핑 (가장 실용적 아키텍처) +- **Beautiful.ai**: 300개 템플릿 + 규칙 기반 자동 레이아웃 조정 +- **Napkin AI**: NLP로 텍스트 패턴 → 시각화 유형 자동 매핑 +- **PPTAgent (EMNLP 2025)**: 레퍼런스 슬라이드 클러스터링 → 유형별 패턴 추출 → 편집 방식 생성 +- **InfoDesignLM (ICDAR 2025)**: 텍스트만으로 인포그래픽 레이아웃 생성, GPT-4o 능가 +- **Microsoft LIDA**: 4단계 파이프라인 (요약 → 목표 → 시각화 → 스타일링) +- **Dr. Andrew Abela Chart Chooser**: 콘텐츠 유형 → 시각화 유형 결정 트리 + +--- + +## 금지 사항 + +1. Kei Persona Agent 코드를 수정하지 않는다 +2. 디자인 판단을 하드코딩하지 않는다 (Opus/Sonnet이 사고한다) +3. 전체 페이지를 하나의 고정 템플릿으로 만들지 않는다 (블록 조합 방식) +4. 콘텐츠를 전부 넣으려 하지 않는다 (핵심만 추출) +5. 그라데이션, 애니메이션, 다크 테마를 기본으로 사용하지 않는다 +6. 교본 없이 자유 디자인을 하지 않는다 (교본 참조 필수) diff --git a/PLAN.md b/PLAN.md new file mode 100644 index 0000000..71814e3 --- /dev/null +++ b/PLAN.md @@ -0,0 +1,171 @@ +# Design Agent — 실행 계획 + +## Phase 1: 기반 구축 + +### DA-1: 프로젝트 셋업 +- **파일:** pyproject.toml, .env, .gitignore +- **내용:** Python 환경, 의존성 정의, 환경 변수 +- **의존성:** 없음 +- **완료 기준:** `pip install -e .` 성공 + +### DA-2: FastAPI 서버 기본 구조 +- **파일:** src/main.py, src/config.py +- **내용:** FastAPI 앱, CORS, health endpoint, 설정 관리 +- **의존성:** DA-1 +- **완료 기준:** `uvicorn src.main:app --reload` 정상 시작, `/api/health` 200 반환 + +### DA-3: 디자인 토큰 + 기본 CSS +- **파일:** static/tokens.css, static/base.css +- **내용:** CLAUDE.md에 정의된 디자인 토큰을 CSS 변수로 구현, Pretendard 폰트 설정, 16:9 슬라이드 컨테이너 +- **의존성:** 없음 +- **완료 기준:** 빈 슬라이드가 16:9 비율로 렌더링, Pretendard 폰트 적용 확인 + +--- + +## Phase 2: 블록 템플릿 제작 + +### DA-4: 블록 템플릿 — 비교 (comparison) +- **파일:** templates/blocks/comparison.html +- **내용:** 2단 병렬 레이아웃, Jinja2 슬롯 ({{left_title}}, {{left_content}}, {{right_title}}, {{right_content}}) +- **의존성:** DA-3 +- **완료 기준:** 더미 데이터로 렌더링 시 2단 비교 표시, 디자인 토큰 적용 + +### DA-5: 블록 템플릿 — 카드 그리드 (card-grid) +- **파일:** templates/blocks/card-grid.html +- **내용:** 2~4열 카드 배열, Jinja2 슬롯 ({{cards[n].icon}}, {{cards[n].title}}, {{cards[n].description}}) +- **의존성:** DA-3 +- **완료 기준:** 3개 카드 렌더링, 카드 수에 따라 자동 배열 + +### DA-6: 블록 템플릿 — 관계도 (relationship) +- **파일:** templates/blocks/relationship.html +- **내용:** 벤 다이어그램 (CSS 원형), Jinja2 슬롯 ({{center}}, {{items[n]}}) +- **의존성:** DA-3 +- **완료 기준:** 3원 벤 다이어그램 렌더링, 라벨 표시 + +### DA-7: 블록 템플릿 — 프로세스 (process) +- **파일:** templates/blocks/process.html +- **내용:** 가로 단계 흐름, Jinja2 슬롯 ({{steps[n].number}}, {{steps[n].title}}, {{steps[n].description}}) +- **의존성:** DA-3 +- **완료 기준:** 4단계 프로세스 렌더링, 연결선 표시 + +### DA-8: 블록 템플릿 — 강조 인용 (quote-block) +- **파일:** templates/blocks/quote-block.html +- **내용:** 배경색 + 좌측 라인 + 인용 텍스트, Jinja2 슬롯 ({{quote_text}}, {{source}}) +- **의존성:** DA-3 +- **완료 기준:** 인용 블록 렌더링, 강조 스타일 적용 + +### DA-9: 블록 템플릿 — 결론 바 (conclusion-bar) +- **파일:** templates/blocks/conclusion-bar.html +- **내용:** 하단 전체 폭 강조 영역, Jinja2 슬롯 ({{conclusion_text}}) +- **의존성:** DA-3 +- **완료 기준:** 결론 바 렌더링, 강조 색상 적용 + +### DA-10: 블록 템플릿 — 비교 테이블 (comparison-table) +- **파일:** templates/blocks/comparison-table.html +- **내용:** 다항목 비교 테이블, Jinja2 슬롯 ({{headers}}, {{rows}}) +- **의존성:** DA-3 +- **완료 기준:** 5행 3열 테이블 렌더링 + +### DA-11: 슬라이드 조합 렌더러 +- **파일:** src/renderer.py, templates/slide-base.html +- **내용:** Jinja2로 블록 조합 → HTML 생성. grid-template-areas로 블록 배치. 다중 페이지 지원. +- **다중 페이지:** `.slide` div 여러 개 + `page-break-after: always` (인쇄 시 페이지 분리) +- **의존성:** DA-4 ~ DA-10 +- **완료 기준:** JSON 블록 배치 명세 → 완성 HTML 출력 (1페이지 또는 다중 페이지) + +--- + +## Phase 3: AI 파이프라인 연결 + +### DA-12: Kei API 연동 — 콘텐츠 분류 (Opus) +- **파일:** src/kei_client.py +- **내용:** Kei API (`localhost:8000/api/message`)에 콘텐츠 전송 → Opus 분류 결과 수신. Kei API 미연결 시 수동 분류 fallback +- **의존성:** DA-2 +- **완료 기준:** 테스트 콘텐츠 전송 → 유형 분류 JSON 반환 + +### DA-13: 디자인 팀장 — 레이아웃 컨셉 (Sonnet) +- **파일:** src/design_director.py +- **내용:** Anthropic API 직접 호출. Opus 분류 결과 + 원본 콘텐츠 → 레이아웃 컨셉만 결정. 텍스트 정리 안 함. +- **출력:** 블록 배치 + 페이지 수 + 슬롯 목록 (텍스트 없이 구조만) +- **기술:** Anthropic API (Sonnet), JSON 반환 +- **의존성:** DA-12 +- **완료 기준:** "이 파트는 카드로, 이건 비교로, 2페이지 필요" 수준의 컨셉 JSON 반환 + +### DA-13b: 텍스트 편집자 — 슬롯 텍스트 정리 (Kei 역할) +- **파일:** src/content_editor.py (신규) +- **내용:** Anthropic API 직접 호출. 디자인 팀장의 레이아웃 컨셉 + 원본 콘텐츠 → 각 슬롯에 맞는 텍스트 편집. 도메인 지식 보존, 핵심 유지. +- **역할:** 도메인 전문가로서 콘텐츠를 정리하는 편집자 (Kei persona 규칙 일부 적용) +- **규칙:** 핵심 내용 유지, 개조식, 출처 보존, 슬롯 분량 준수, 내용 날조 금지 +- **기술:** Anthropic API (Sonnet), JSON 반환 +- **의존성:** DA-13 +- **완료 기준:** 슬롯별 텍스트가 채워진 JSON 반환. 원본 핵심 내용 보존 확인. + +### DA-14: 전체 파이프라인 연결 (3단계) +- **파일:** src/pipeline.py +- **내용:** 콘텐츠 입력 → Opus 분류 → 디자인 팀장 컨셉 → 텍스트 편집자 정리 → 렌더러 조립 → HTML 출력 +- **기술:** 순차 호출, 다중 페이지 지원 +- **의존성:** DA-11, DA-12, DA-13, DA-13b +- **완료 기준:** 텍스트 입력 → 완성 슬라이드 HTML 출력 (엔드투엔드, 다중 페이지 포함) + +--- + +## Phase 4: UI + 출력 + +### DA-15: 프론트엔드 — 콘텐츠 입력 + 미리보기 +- **파일:** static/index.html (별도 HTML 파일), main.py (FileResponse로 서빙) +- **내용:** 텍스트 입력 영역 + iframe 미리보기 + HTML 다운로드 버튼 +- **기술:** FileResponse (FastAPI 내장), fetch API + 수동 SSE 파싱 +- **의존성:** DA-14 +- **완료 기준:** 텍스트 붙여넣기 → 슬라이드 미리보기 표시 + HTML 다운로드 +- **주의:** HTML/JS를 Python 문자열에 넣지 않는다 (이스케이프 충돌 방지) + +--- + +## 버그 수정 + +### BF-2: 블록 내용 비어있음 (렌더러 Jinja2 include 문제) +- **파일:** src/renderer.py, templates/slide-base.html +- **내용:** `include` → 블록별 개별 `render()` 후 HTML 삽입 +- **기술:** Jinja2 `get_template().render()` (내장) +- **의존성:** 없음 (기존 코드 수정만) +- **완료 기준:** 콘텐츠 입력 → 슬라이드에 텍스트가 표시됨 + +### BF-3: 한글 깨짐 (다운로드 파일) +- **파일:** static/index.html +- **내용:** download() Blob에 UTF-8 BOM 추가 +- **기술:** JavaScript `'\uFEFF'` 1줄 +- **의존성:** 없음 +- **완료 기준:** 다운로드한 HTML 파일에서 한글 정상 표시 + +### DA-16: 통합 테스트 +- **파일:** tests/test_pipeline.py, tests/test_renderer.py +- **내용:** 전체 파이프라인 테스트 + 블록 렌더링 테스트 +- **의존성:** BF-2, BF-3 +- **완료 기준:** 테스트 전체 통과 + +--- + +## 의존 관계 + +``` +DA-1 → DA-2 → DA-12 → DA-13 ─┐ + ├→ DA-14 → DA-15 → DA-16 +DA-3 → DA-4~DA-10 → DA-11 ────┘ +``` + +Phase 1(DA-1~3)과 Phase 2(DA-4~11)는 AI 없이 진행 가능. +Phase 3(DA-12~14)부터 Kei API + Anthropic API 필요. + +--- + +## 기술 스택 + +| 역할 | 도구 | 비고 | +|------|------|------| +| 서버 | FastAPI + uvicorn | Kei와 동일 | +| 템플릿 엔진 | Jinja2 | 블록 상속 + 슬롯 변수 | +| 렌더링 | CSS Grid + 디자인 토큰 | 순수 CSS | +| 한국어 폰트 | Pretendard Variable | word-break: keep-all | +| AI (실장) | Kei API (Opus) | localhost:8000 | +| AI (팀장) | Anthropic API (Sonnet) | Structured Outputs | +| 테스트 | pytest | 렌더링 + 파이프라인 | diff --git a/PROGRESS.md b/PROGRESS.md new file mode 100644 index 0000000..e7515f1 --- /dev/null +++ b/PROGRESS.md @@ -0,0 +1,96 @@ +# Design Agent — 진행 상황 + +## 현재 상태 요약 + +| 상태 | 개수 | +|------|------| +| done | 13 | +| in-progress | 0 | +| todo | 3 | +| blocked | 0 | +| **전체** | **16** | + +--- + +## Phase 1: 기반 구축 + +| 태스크 | 상태 | 담당 | 시작 | 완료 | 메모 | +|--------|------|------|------|------|------| +| DA-1: 프로젝트 셋업 | done | - | - | - | pyproject.toml, .env | +| DA-2: FastAPI 서버 | done | - | - | - | DA-1 이후 | +| DA-3: 디자인 토큰 + 기본 CSS | done | - | - | - | 독립 작업 가능 | + +## Phase 2: 블록 템플릿 제작 + +| 태스크 | 상태 | 담당 | 시작 | 완료 | 메모 | +|--------|------|------|------|------|------| +| DA-4: 비교 블록 | done | - | - | - | DA-3 이후 | +| DA-5: 카드 그리드 | done | - | - | - | DA-3 이후 | +| DA-6: 관계도 | done | - | - | - | DA-3 이후 | +| DA-7: 프로세스 | done | - | - | - | DA-3 이후 | +| DA-8: 강조 인용 | done | - | - | - | DA-3 이후 | +| DA-9: 결론 바 | done | - | - | - | DA-3 이후 | +| DA-10: 비교 테이블 | done | - | - | - | DA-3 이후 | +| DA-11: 슬라이드 조합 렌더러 | done | - | - | - | DA-4~10 이후 | + +## Phase 3: AI 파이프라인 연결 + +| 태스크 | 상태 | 담당 | 시작 | 완료 | 메모 | +|--------|------|------|------|------|------| +| DA-12: Kei API 연동 (Opus) | done | - | - | - | DA-2 이후 | +| DA-13: 디자인 팀장 — 레이아웃 컨셉만 | todo | - | - | - | 기존에서 텍스트 정리 제거. 컨셉만 반환 | +| DA-13b: 텍스트 편집자 (Kei 역할) | todo | - | - | - | 신규. 도메인 전문가로 슬롯 텍스트 정리 | +| DA-14: 전체 파이프라인 (3단계) | todo | - | - | - | 분류→컨셉→텍스트→렌더링. 다중 페이지 | + +## Phase 4: UI + 출력 + +| 태스크 | 상태 | 담당 | 시작 | 완료 | 메모 | +|--------|------|------|------|------|------| +| DA-15: 프론트엔드 | done | - | - | - | DA-14 이후. HTML 다운로드만 | +| DA-16: 통합 테스트 | done | - | - | - | DA-15 이후 | + +--- + +## 버그 수정 이력 + +### BF-1: 프론트엔드 SSE 파싱 실패 [발견: DA-15 이후] +- **현상:** 서버는 정상 응답하지만 브라우저에서 결과 미표시. "시작 중..." 고정. +- **원인:** main.py Python 문자열 안에 HTML/JS를 넣어서 `\n`이 실제 줄바꿈으로 변환 → JS `split('\n\n')` 깨짐. 또한 Windows SSE가 `\r\n\r\n`(CRLF)로 구분. +- **해결:** static/index.html 별도 파일로 분리. FileResponse로 서빙. SSE split을 `/\r?\n\r?\n/` 정규식으로 변경. +- **기술:** FileResponse (FastAPI 내장), 추가 의존성 0 +- **충돌 검토:** API 경로와 충돌 없음. 기존 코드 변경 없음. Kei persona 무관. +- **상태:** done + +### BF-2: 블록 내용이 비어있음 (Jinja2 include 변수 전달 실패) [발견: BF-1 이후] +- **현상:** 슬라이드 HTML은 생성되지만 모든 블록 텍스트가 비어있음. 레이아웃 구조만 있고 내용 없음. +- **원인:** renderer.py에서 Jinja2 `include`로 블록 템플릿을 삽입하는데, `include`는 블록별 변수를 개별 전달하지 못함. Sonnet이 채운 data가 템플릿에 도달 안 함. +- **해결:** `include` 대신 각 블록 템플릿을 `env.get_template().render(**data)`로 개별 렌더링 후 완성된 HTML을 삽입. `render_standalone_block()`이 이미 이 방식으로 동작 중 → 통일. +- **기술:** Jinja2 `get_template().render()` (내장), 추가 의존성 0 +- **수정 파일:** renderer.py, templates/slide-base.html +- **충돌 검토:** 블록 템플릿 7개 변경 없음. pipeline.py 호출 시그니처 동일. Kei persona 무관. +- **상태:** done + +### BF-3: 한글 깨짐 (다운로드 HTML 파일) [발견: BF-1 이후] +- **현상:** 다운로드한 HTML 파일에서 한글이 `ê±´ì¤ì°ì` 같은 깨진 문자로 표시. +- **원인:** Blob 다운로드 시 UTF-8 BOM 미포함. 일부 에디터/브라우저가 인코딩 자동 감지 실패. +- **해결:** download() 함수에서 Blob 생성 시 UTF-8 BOM(`'\uFEFF'`) 접두사 추가. +- **기술:** JavaScript BOM 1줄, 추가 의존성 0 +- **수정 파일:** static/index.html +- **충돌 검토:** 미리보기(iframe)에 영향 없음. SSE 파싱에 영향 없음. +- **상태:** done + +## 블로킹 이슈 + +없음 + +--- + +## 완료된 준비 사항 + +| 항목 | 파일 | 상태 | +|------|------|------| +| 프로젝트 규칙 | CLAUDE.md | 완료 | +| 실행 계획 | PLAN.md | 완료 | +| 진행 추적 | PROGRESS.md | 완료 (이 파일) | +| 기술 조사 | docs/RESEARCH.md | 완료 | +| 폴더 구조 | templates/, samples/, docs/ | 생성 완료 | diff --git a/docs/FIGMA-COMPONENT-EXTRACTION-PLAN.md b/docs/FIGMA-COMPONENT-EXTRACTION-PLAN.md new file mode 100644 index 0000000..3e3c477 --- /dev/null +++ b/docs/FIGMA-COMPONENT-EXTRACTION-PLAN.md @@ -0,0 +1,388 @@ +# Figma → 컴포넌트 추출 + 카탈로그 구축 계획 + +## 목적 + +Figma 디자인(바론컨설턴트 홈페이지 기획팀 공유)에서 재사용 가능한 슬라이드 콘텐츠 블록을 추출하고, 디자인 팀장(Sonnet)이 선택할 수 있는 카탈로그로 체계화한다. + +--- + +## 현재 상태 + +### 보유 자산 + +| 항목 | 상태 | 위치 | +|------|------|------| +| Figma API 접근 | ✅ 가능 | Token: `.env` | +| 기존 블록 템플릿 7종 | ✅ 완성 | `templates/blocks/` | +| 디자인 토큰 | ✅ 완성 | `static/tokens.css` | +| 슬라이드 렌더러 | ✅ 완성 | `src/renderer.py` | +| 디자인 팀장 (DA-13) | ❌ todo | `src/design_director.py` | +| 블록 카탈로그 | ❌ 없음 | - | + +### Figma 파일 구조 + +``` +바론 공유 2025.05.13 (node: 1574-6254) +├── 1장 바론컨설턴트 +├── 2장 디지털전환 +│ ├── 2-1 건설산업에서의 디지털전환 (1920x8538, 스크롤형) +│ ├── 2-2 디지털전환과 소프트웨어 (1920x9123, 스크롤형) +│ └── 건설산업에서의 디지털전환 (1920x8536, 스크롤형) +│ [자세히보기] +│ ├── 2-1장 자세히보기 (4프레임: 건설산업/BIM/GIS/디지털트윈) +│ ├── 2-2장 자세히보기 +│ └── 2-3장 자세히보기 +├── 3장 제공서비스 +│ ├── 3-1장 솔루션프로그램 자세히보기 +│ └── 3-3장 빅룸 자세히보기 +└── 모션작업 +``` + +### 기존 블록 vs Figma에서 발견된 패턴 + +| 패턴 | 기존 블록 | Figma에서 발견 | 갭 | +|------|----------|--------------|-----| +| 2단 비교 | ✅ comparison | ✅ | - | +| 카드 그리드 | ✅ card-grid | ✅ (변형 다수) | 변형 추가 필요 | +| 벤 다이어그램 | ✅ relationship | ✅ | - | +| 단계 흐름 | ✅ process | ✅ | - | +| 강조 인용 | ✅ quote-block | ✅ (큰따옴표 장식) | 변형 추가 필요 | +| 결론 바 | ✅ conclusion-bar | ✅ | - | +| 비교 테이블 | ✅ comparison-table | ✅ | - | +| **이미지 갤러리** | ❌ | ✅ (2열, 3열, 2x2) | **신규** | +| **타임라인** | ❌ | ✅ (세로 원형 4단계) | **신규** | +| **섹션 타이틀** | ❌ | ✅ (영문+한글 공통 헤더) | **신규** | +| **사례 카드** | ❌ | ✅ (출처+불릿 카드) | **신규** | +| **핵심 지표** | ❌ (정의만) | ✅ (큰 숫자+보조) | **신규** | +| **아이콘 리스트** | ❌ | ✅ (아이콘+제목+설명) | **신규** | +| **Hero 섹션** | ❌ | ✅ (배경+원형이미지+텍스트) | **신규** | +| **CTA 버튼 바** | ❌ | ✅ (자세히보기 버튼) | **필요 시** | + +--- + +## 작업 계획 + +### Phase A: Figma 분석 + 패턴 추출 + +#### A-1: Figma 전체 섹션 이미지 렌더링 +- **작업:** 각 섹션/프레임을 이미지로 렌더링하여 시각적으로 패턴 식별 +- **방법:** Figma API `/v1/images/{file_key}?ids={node_ids}` +- **산출물:** `docs/figma-screenshots/` 폴더에 PNG 저장 +- **완료 기준:** 모든 자세히보기 프레임(8개)의 스크린샷 확보 + +#### A-2: Figma 노드 구조 심층 분석 +- **작업:** 각 프레임의 depth=5 수준까지 노드 트리 분석 +- **방법:** Figma API `/v1/files/{key}/nodes?ids={ids}&depth=5` +- **추출 정보:** + - TEXT 노드: 폰트, 크기, 색상, 내용 + - FRAME/GROUP: 레이아웃 방식 (auto-layout, constraints) + - RECTANGLE: 배경색, 테두리, 둥근 모서리 + - INSTANCE: 재사용 컴포넌트 식별 +- **산출물:** `docs/figma-analysis/` 폴더에 구조 문서 + +#### A-3: 디자인 패턴 분류 + 명명 +- **작업:** 추출된 시각 요소를 재사용 가능한 블록 단위로 분류 +- **기준:** + - 2회 이상 반복되는 패턴 → 블록 후보 + - 슬롯(교체 가능한 위치)이 명확한 것 → 우선 순위 높음 + - 콘텐츠 유형과 매칭되는 것 → 우선 순위 높음 +- **산출물:** 패턴 목록 + 각 패턴의 Figma 원본 노드 ID + +### Phase B: HTML/CSS 컴포넌트 제작 + +#### B-1: 신규 블록 템플릿 제작 (6~8종) +- **파일:** `templates/blocks/{name}.html` +- **제작 순서 (우선순위):** + 1. `section-title.html` — 공통 헤더 (모든 슬라이드에서 사용) + 2. `example-card.html` — 사례 카드 (출처+불릿, 정책 문서 인용) + 3. `image-gallery.html` — 이미지 갤러리 (2~4장, 근거 자료) + 4. `timeline.html` — 타임라인 (세로/가로, 연혁/로드맵) + 5. `big-number.html` — 핵심 지표 (큰 숫자 + 보조 텍스트) + 6. `icon-list.html` — 아이콘 리스트 (아이콘+제목+설명, 기능 나열) +- **규칙:** + - 디자인 토큰(`var(--color-*)`) 사용 (하드코딩 색상 금지) + - Jinja2 슬롯 (`{{ variable }}`) 형식 + - ` + + +
+ {% 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 `