Refine README and detail preview layout

This commit is contained in:
2026-04-15 17:59:46 +09:00
parent 66c00924ed
commit 05d43a7999
2 changed files with 152 additions and 75 deletions

198
README.md
View File

@@ -1,61 +1,90 @@
# Kei Design Agent # C.E.L. Slide Pipeline
MDX 콘텐츠를 분석해 1280x720 슬라이드 HTML로 변환하는 파이프라인입니다. MDX 기반 콘텐츠를 분석해 1280x720 슬라이드 HTML로 변환하는 파이프라인입니다.
이 문서는 "지금 실제 코드 기준으로 파이프라인이 어떻게 동작하는지"를 빠르게 파악하기 위한 개요 문서입니다. 과거 Phase 문서와 일부 legacy 경로는 남아 있지만, 아래 설명은 현재 메인 경로를 기준으로 정리했습니다. 이 문서는 현재 코드 기준으로 이 프로젝트가 무엇을 하는지, 어떤 프로세스로 동작하는지, 현재 어디까지 와 있는지, 앞으로 무엇을 개선하려는지를 빠르게 파악하기 위한 개요 문서입니다.
## 한눈에 보기 ## 무엇을 하는가
파이프라인의 큰 흐름은 아래와 같습니다. 이 프로젝트는 MDX 문서를 입력으로 받아 다음 과정을 거쳐 슬라이드 결과물을 생성합니다.
1. MDX를 코드가 정규화한다. - 콘텐츠 정규화
2. Kei가 문서 의미와 topic을 읽는다. - 문서 의미 분석
3. 코드는 `normalized.sections`를 기준으로 실제 슬라이드 구조를 다시 만든다. - 슬라이드 구조 해석
4. 코드는 schema, recipe, tag를 바탕으로 블록을 고른다. - schema / recipe / block 선택
5. Type B는 코드가 템플릿을 조립해 `final.html`을 만든다. - HTML 조립
6. Type A는 아직 AI/renderer 비중이 더 크다. - fit / overflow 검증
7. Selenium과 vision gate로 측정/검증한 뒤 run 산출물 저장한다. - 최종 산출물 저장
최종 산출물은 보통 다음 형태로 저장됩니다.
- `final.html`
- `final_context.json`
- `steps/*.html`
- popup / detail HTML
## 한눈에 보는 프로세스
```text
MDX Input
Stage 0 Normalize
Stage 1A/1B AI Meaning Analysis
Phase Y Group / Schema / Recipe Parsing
Stage 1.5a Space Allocation
Stage 1.7 Block Selection
Stage 1.8 Fit / Measure / Adjustment
Stage 2 Assemble final slide HTML
Stage 4 Validate with Selenium / Vision
Stage 5 Save final artifacts
```
핵심 원칙은 다음과 같습니다. 핵심 원칙은 다음과 같습니다.
- source of truth는 `normalized.sections` - source of truth는 `normalized.sections`
- block 선택은 문서명 하드코딩이 아니라 shape, schema, tag 기반 - 문서명 하드코딩보다 shape, schema, recipe, tag를 우선한다
- 흐름의 우선순위는 `구조 -> payload -> layout -> fit` - 흐름의 우선순위는 `구조 -> payload -> layout -> fit`
- popup/detail은 overflow를 덮는 임시 장치가 아니라 `메인 요약 + 상세 보기` 2단 표현 계약 - popup / detail은 overflow를 덮는 임시 장치가 아니라 `메인 요약 + 상세 보기`의 표현 계약이다
## 타입 구조 ## 타입 구조
현재 메인 타입 선택은 사실상 `A``B`입니다. 현재 메인 타입 선택은 사실상 `Type A``Type B`입니다.
### Type A ### Type A
- 본문 외에 sidebar, reference, 부록성 영역이 함께 필요한 슬라이드 - 본문 외에 sidebar, reference, 부록성 영역이 함께 필요한 슬라이드
- 현재는 Type B보다 덜 닫혀 있고, AI 생성 + renderer 경로 비중이 큽니다 - 현재는 Type B보다 덜 닫혀 있고, AI 생성 + renderer 경로 비중이 더 큼
### Type B ### Type B
- top, bottom 같은 본문 zone 조합으로 해결되는 슬라이드 - top / bottom 같은 본문 zone 조합으로 해결되는 슬라이드
- 현재 가장 안정적인 메인 경로입니다 - 현재 가장 안정적인 메인 경로
- 최근 구조화 작업은 대부분 이 Type B 경로를 중심으로 진행되었습니다 - 최근 구조화 작업은 대부분 이 Type B 경로를 중심으로 진행
### Type B' / B'' ### Type B' / B''
- 역사적으로 실험/호환 경로에서 나온 변형입니다 - 역사적으로 실험/호환 과정에서 나온 변형 경로
- 문서와 일부 legacy 코드에 흔적이 남아 있습니다 - 일부 legacy 코드와 과거 산출물에 흔적이 남아 있
- 현재 메인 개념의 1급 타입으로 보기보다, 과거 흐름과 호환 레이어로 이해하는 편이 맞습니다 - 현재 메인 1급 타입이라기보다, 과거 흐름과 검증된 표시 계약을 담고 있는 보조 경로에 가까움
## 단계별 파이프라인 ## 단계별 파이프라인
아래는 현재 기준의 실질적인 단계입니다.
| 단계 | 담당 | 주요 파일 | 하는 일 | 주요 산출물 | | 단계 | 담당 | 주요 파일 | 하는 일 | 주요 산출물 |
|---|---|---|---|---| |---|---|---|---|---|
| Stage 0 | 코드 | `src/mdx_normalizer.py` | MDX를 정규화하고 sections, tables, images, popups로 분리 | `NormalizedContent` | | Stage 0 | 코드 | `src/mdx_normalizer.py` | MDX를 정규화하고 sections, tables, images, popups로 분리 | `NormalizedContent` |
| Stage 1A | AI (Kei/Opus) | `src/kei_client.py` | title, core message, topic, 초기 layout 힌트 추출 | `Analysis`, `Topic[]` | | Stage 1A | AI (Kei/Opus) | `src/kei_client.py` | title, core message, topic, 초기 layout 힌트 추출 | `Analysis`, `Topic[]` |
| Phase Y | 코드 | `src/pipeline.py`, `src/section_parser.py` | `normalized.sections` 기반으로 group schema, recipe, zone/page_structure 결정 | `PageStructure`, `mdx_sections` | | Phase Y | 코드 | `src/pipeline.py`, `src/section_parser.py` | `normalized.sections` 기반으로 group, schema, recipe, zone/page_structure 결정 | `PageStructure`, `mdx_sections` |
| Stage 1B | AI (Kei/Opus) | `src/kei_client.py` | topic별 relation, source_data, 표현 힌트 보강 | 보강된 `Topic[]` | | Stage 1B | AI (Kei/Opus) | `src/kei_client.py` | topic별 relation, source_data, 표현 힌트 보강 | 보강된 `Topic[]` |
| Stage 1B-ST | AI (Kei/Opus) | `src/kei_client.py` | structured_text 생성 | `Topic.structured_text` | | Stage 1B-ST | AI (Kei/Opus) | `src/kei_client.py` | structured_text 생성 | `Topic.structured_text` |
| Stage 1.5a | 코드 | `src/space_allocator.py` | zone/container 크기, preset, font hierarchy 계산 | `containers`, `font_hierarchy` | | Stage 1.5a | 코드 | `src/space_allocator.py` | zone / container 크기, preset, font hierarchy 계산 | `containers`, `font_hierarchy` |
| Stage 1.7 | 코드 | `src/block_reference.py`, `templates/catalog.yaml` | tag_match, schema_match, fallback 기준으로 block 선택 | `references` | | Stage 1.7 | 코드 | `src/block_reference.py`, `templates/catalog.yaml` | tag_match, schema_match, fallback 기준으로 block 선택 | `references` |
| Stage 1.8 | 코드 + Selenium + 일부 AI | `src/pipeline.py`, `src/slide_measurer.py` | fit 측정, overflow 확인, 재배분, 보정 | `fit_result`, `measurement` | | Stage 1.8 | 코드 + Selenium + 일부 AI | `src/pipeline.py`, `src/slide_measurer.py` | fit 측정, overflow 확인, 재배분, 보정 | `fit_result`, `measurement` |
| Stage 2 | 코드 중심 | `src/block_assembler.py` | Type B 기준 slide-base + block template + payload 조립 | `generated_html` | | Stage 2 | 코드 중심 | `src/block_assembler.py` | Type B 기준 slide-base + block template + payload 조립 | `generated_html` |
@@ -70,12 +99,12 @@ MDX 콘텐츠를 분석해 1280x720 슬라이드 HTML로 변환하는 파이프
지금 실전에서 가장 중요한 경로는 아래입니다. 지금 실전에서 가장 중요한 경로는 아래입니다.
1. Stage 0에서 MDX를 정규화 1. Stage 0에서 MDX를 정규화
2. Stage 1A/1B에서 Kei가 의미와 topic 추출 2. Stage 1A / 1B에서 AI가 문서 의미와 topic 추출
3. Phase Y에서 코드가 `normalized.sections`를 읽고 page_structure를 다시 생성 3. Phase Y에서 코드가 `normalized.sections`를 읽고 page_structure를 다시 생성
4. Stage 1.7에서 block 선택 4. Stage 1.7에서 block / recipe 후보 선택
5. Stage 1.8에서 fit/overflow 검증 5. Stage 1.8에서 fit / overflow 검증
6. Stage 2에서 `assemble_slide_html_final()`로 최종 HTML 조립 6. Stage 2에서 `assemble_slide_html_final()`로 최종 HTML 조립
7. Stage 4/5에서 측정과 산출물 저장 7. Stage 4 / 5에서 측정과 산출물 저장
Type B의 핵심 파일은 아래입니다. Type B의 핵심 파일은 아래입니다.
@@ -92,7 +121,7 @@ Type A는 현재도 살아 있지만, Type B만큼 단단하게 닫힌 상태는
- AI 생성 비중이 더 큼 - AI 생성 비중이 더 큼
- `src/renderer.py` 의존도가 더 큼 - `src/renderer.py` 의존도가 더 큼
- sidebar/reference 구조를 포함하는 에서 의미가 큼 - sidebar / reference 구조를 포함하는 문서에서 의미가 큼
## schema -> recipe -> block ## schema -> recipe -> block
@@ -118,14 +147,16 @@ block 이름이 아니라 표현 규칙입니다.
- `single_block` - `single_block`
- `two_col_text_visual` - `two_col_text_visual`
- `two_col_text_detail`
- `stacked_summary_detail` - `stacked_summary_detail`
recipe는 이런 계약을 가질 수 있습니다. recipe는 보통 이런 계약을 가질 수 있습니다.
- left/right kind - left / right kind
- top/bottom kind - top / bottom kind
- ratio - ratio
- vertical align - vertical align
- direct render 우선 여부
### block ### block
@@ -138,23 +169,23 @@ recipe는 이런 계약을 가질 수 있습니다.
- `compare-detail-gradient` - `compare-detail-gradient`
- `card-icon-desc` - `card-icon-desc`
즉, "무슨 문서냐"가 아니라 "무슨 구조냐"를 먼저 읽고, 그 구조에 맞는 표현 규칙을 정한 뒤, 마지막에 구현 블록을 고르는 방향으로 가고 있습니다. 즉, 무슨 문서냐”보다 “무슨 구조냐를 먼저 읽고, 그 구조에 맞는 표현 규칙을 정한 뒤, 마지막에 구현 block을 고르는 방향으로 가고 있습니다.
## popup / detail 계약 ## popup / detail 계약
popup은 지금 다음 철학으로 정리되는 중입니다. popup은 현재 다음 철학으로 정리되는 중입니다.
- 메인 슬라이드에는 크기에 맞는 요약만 남긴다 - 메인 슬라이드에는 zone 크기에 맞는 요약만 남긴다
- 큰 표, 시각 컴포넌트, 과다 bullet은 상세 popup으로 분리한다 - 큰 표, 시각 컴포넌트, 과다 bullet은 상세 popup으로 분리한다
- 메인에서는 `자세히보기` 링크를 제공한다 - 메인에서는 `자세히보기` 링크를 제공한다
현재 popup 관련 핵심은 아래입니다. 현재 popup 관련 핵심은 아래입니다.
- `PopupItem` 모델이 도입되어 popup 데이터를 명시적으로 다 - `PopupItem` 모델 popup 데이터를 명시적으로 다루기 시작함
- `popup_id`, `popup_file` 생애주기를 분리해 관리 중 - `popup_id` `popup_file` 생애주기를 분리해 관리 중
- 최종 목표는 popup 판단을 휴리스틱이 아니라 명시적 contract로 만드는 것 - 최종 목표는 popup 판단을 휴리스틱이 아니라 명시적 contract로 만드는 것
다만 아직 일부 구간 추측 로직과 이중 관리가 남아 있어, 이 부분은 계속 정리 중입니다. 다만 아직 일부 구간에는 추측 로직과 이중 관리가 남아 있어, 이 부분은 계속 정리 중입니다.
## run 산출물 구조 ## run 산출물 구조
@@ -165,22 +196,22 @@ popup은 지금 다음 철학으로 정리되는 중입니다.
- `final.html` - `final.html`
- `final_context.json` - `final_context.json`
- `steps/*.html` - `steps/*.html`
- popup/detail html - popup / detail HTML
### `final.html` ### final.html
- 최종 렌더 결과 - 최종 렌더 결과
- 실제 눈으로 보는 산출물 - 실제 눈으로 보는 산출물
### `final_context.json` ### final_context.json
- 각 단계 결과를 최종 context 형태로 저장 - 각 단계 결과를 최종 context 형태로 저장
- block 선택, page_structure, measurement, quality_score 등을 확인할 때 가장 중요 - block 선택, page_structure, measurement, quality_score 등을 확인할 때 가장 중요
### `steps/*.html` ### steps/*.html
- 단계별 디버그/설명용 보드 - 단계별 디버그 / 설명용 보드
- 현재 검토용으로 유용하지만, 일부 인코딩/설명 품질은 더 다듬을 필요가 있습니다 - 현재 검토용으로 유용하지만, 일부 인코딩설명 품질은 더 다듬을 필요가 있
## 자주 봐야 하는 파일 ## 자주 봐야 하는 파일
@@ -199,39 +230,83 @@ popup은 지금 다음 철학으로 정리되는 중입니다.
- [templates/blocks/new/prerequisites-3col.html](templates/blocks/new/prerequisites-3col.html) - [templates/blocks/new/prerequisites-3col.html](templates/blocks/new/prerequisites-3col.html)
- [templates/blocks/redesign/process-product-2col.html](templates/blocks/redesign/process-product-2col.html) - [templates/blocks/redesign/process-product-2col.html](templates/blocks/redesign/process-product-2col.html)
- [templates/blocks/cards/compare-detail-gradient.html](templates/blocks/cards/compare-detail-gradient.html) - [templates/blocks/cards/compare-detail-gradient.html](templates/blocks/cards/compare-detail-gradient.html)
- [templates/blocks/slide-base.html](templates/blocks/slide-base.html) - `templates/blocks/slide-base.html`
주의: 현재 삭제/legacy 정리 여부를 별도로 확인해야 하는 경로입니다.
### 검증 / 측정 ### 검증 / 측정
- [src/slide_measurer.py](src/slide_measurer.py) - [src/slide_measurer.py](src/slide_measurer.py)
- [src/validators.py](src/validators.py) - [src/validators.py](src/validators.py)
- [src/step_visualizer.py](src/step_visualizer.py)
### 역사 / 계획 문서 ### 계획 / 히스토리
- [PIPELINE.md](PIPELINE.md) - [PIPELINE.md](PIPELINE.md)
- [docs/history/PHASE-Y-PLAN.md](docs/history/PHASE-Y-PLAN.md) - [docs/history/PHASE-Y-PLAN.md](docs/history/PHASE-Y-PLAN.md)
- [ARCHITECTURE_OVERVIEW.md](ARCHITECTURE_OVERVIEW.md)
## 현재 상태 요약 ## 현재 상태
### 잘 닫혀가는 것 ### 비교적 잘 닫혀가는 것
- Type B 메인 경로 - Type B 메인 경로
- `normalized.sections` 기반 구조 해석 - `normalized.sections` 기반 구조 해석
- schema / recipe 기반 block selection - schema / recipe 기반 block selection의 골격
- `prerequisites-3col`, `process-product-2col` 같은 redesign 블록 자산화 - redesign block 자산화
- popup/detail 2단 표현 계약의 초안 연결 - popup / detail 2단 표현 계약의 초안 연결
### 아직 정리 중인 것 ### 아직 정리 중인 것
- Type A 전체 안정화 - Type A 전체 안정화
- popup을 완전한 source of truth로 정리 - popup을 완전한 source of truth로 정리
- `tag_match``schema_match`의 완전한 동등 점수 비교 - tag_match 와 schema_match의 완전한 동등 점수 비교
- step 보드 인코딩/설명 품질 - step 보드 인코딩 / 설명 품질
- legacy 경로와 문서의 정리 - fit loop의 공간 재분배 고도화
- legacy 경로와 문서 정리
## 읽는 방법 추천 ### 최근 Type B에서 특히 중요해진 방향
- recipe direct render가 block 선택에 끌려가지 않도록 구조 계약을 더 강하게 만든다
- Type B direct render가 Type B'의 검증된 표시 계약을 최대한 재사용하도록 정리한다
- sample은 복제 대상이 아니라 evaluation rule의 기준으로 사용한다
- validation은 “같아야 한다”보다 “어긋나면 안 된다”에 초점을 둔다
## 향후 개선 방향
현재 이후의 개선 방향은 아래 축으로 정리됩니다.
### 1. 구조 계약 강화
- top / bottom zone의 contract를 더 구체화
- `parallel_cluster_plus_visual`, `full_text + detail_preview` 같은 recipe를 표현 범주 수준으로 강화
- recipe가 block를 끌고 가는 것이 아니라 recipe가 block를 통제하도록 정리
### 2. 표시 계약 통합
- Type B direct render가 Type B'에서 이미 검증된 bullet / indent / body-text 구조를 재사용하도록 통합
- `.rdr-*` 계열 신규 CSS를 계속 키우기보다, 기존 검증된 계약을 최대한 재사용
### 3. detail preview 개선
- popup source가 표면 `헤더 + 일부 행`
- popup source가 리스트면 `앞부분 몇 개`
- popup source가 컴포넌트면 구조화된 preview
즉 “링크만 있는 상세”가 아니라 “상세가 있다는 걸 바로 이해할 수 있는 preview”를 만드는 방향
### 4. fit loop 고도화
지금은 주로 overflow 대응 중심이지만, 앞으로는 아래까지 확장하려고 합니다.
- 빈 공간 감지
- 이미지 확대
- preview 행 수 조정
- zone 비율 재배분
### 5. 검증판과 final 일치화
- `stage_4` 보드와 실제 `final_context.json` / `final.html`의 상태 차이를 줄이기
- 검수 보드를 더 믿을 수 있는 상태로 정리
## 읽는 순서 추천
프로세스를 빠르게 파악하려면 아래 순서가 좋습니다. 프로세스를 빠르게 파악하려면 아래 순서가 좋습니다.
@@ -241,8 +316,7 @@ popup은 지금 다음 철학으로 정리되는 중입니다.
4. [src/block_assembler.py](src/block_assembler.py) 4. [src/block_assembler.py](src/block_assembler.py)
5. 최근 run의 `final_context.json` 5. 최근 run의 `final_context.json`
히스토리까지 보려면 아래 문서를 이어서 보면 좋습니다. 히스토리와 설계 변화까지 보려면 아래 문서를 이어서 보면 좋습니다.
- [PIPELINE.md](PIPELINE.md) - [PIPELINE.md](PIPELINE.md)
- [docs/history/PHASE-Y-PLAN.md](docs/history/PHASE-Y-PLAN.md) - [docs/history/PHASE-Y-PLAN.md](docs/history/PHASE-Y-PLAN.md)

View File

@@ -1297,7 +1297,8 @@ def _recipe_direct_render(
text-decoration: underline; text-decoration: underline;
} }
.rdr-table-preview { .rdr-table-preview {
margin-top: 8px; margin-top: 4px;
width: 100%;
} }
.rdr-preview-table { .rdr-preview-table {
border-collapse: collapse; border-collapse: collapse;
@@ -1526,24 +1527,26 @@ def assemble_slide_html_final(ctx: "PipelineContext", title_text: str = "", meas
# right sub_type 확인 — visual_detail이면 summary + popup # right sub_type 확인 — visual_detail이면 summary + popup
right_sub_type = sub_types[1].get("sub_type", "") if len(sub_types) > 1 else "" right_sub_type = sub_types[1].get("sub_type", "") if len(sub_types) > 1 else ""
# 우측: 요약 + popup 링크 # 우측: 제목(+자세히보기 우측) + 표 preview
right_parts = [] right_parts = []
if len(sub_titles) > 1: right_title = sub_titles[1] if len(sub_titles) > 1 else ""
right_title = sub_titles[1] pf = popup_file_map.get(role_name, "")
# 헤더 라인: 제목 좌측 + 자세히보기 우측 (한 줄)
if right_title or pf:
link_html = f'<a href="{pf}" class="rdr-detail-link">자세히보기 →</a>' if pf else ""
right_parts.append( right_parts.append(
f'<div style="font-weight:700;font-size:13px;color:#1a365d;margin-bottom:8px;">' f'<div style="display:flex;justify-content:space-between;align-items:baseline;margin-bottom:6px;">'
f'{right_title}</div>' f'<div style="font-weight:700;font-size:12px;color:#1a365d;">{right_title}</div>'
f'{link_html}</div>'
) )
# structured detail preview + exactly one link
if role_name in popup_file_map: # 표 preview (전체 너비)
pf = popup_file_map[role_name] if pf:
preview_html = _build_detail_preview(role_name, popups) preview_html = _build_detail_preview(role_name, popups)
if preview_html: if preview_html:
right_parts.append(preview_html) right_parts.append(preview_html)
right_parts.append(
f'<div class="rdr-detail-link-wrap">'
f'<a href="{pf}" class="rdr-detail-link">자세히보기 →</a></div>'
)
right_html = "\n".join(right_parts) if right_parts else "" right_html = "\n".join(right_parts) if right_parts else ""
ratio = recipe.get("ratio", "6:4") ratio = recipe.get("ratio", "6:4")