feat(frontend): add Front/ — Vite/React frontend with backend pipeline integration

Mirror of design_agent_front/design-agent/ for shipping alongside backend.

Vite plugin (vitePluginPhaseZApi) endpoints :
  - POST /api/run   — spawn `python -m src.phase_z2_pipeline` with overrides
  - GET  /api/sample-mdx?mdx=03/04/05 — fixed sample MDX
  - GET  /frame-preview/{n} — figma preview thumbnails
  - GET  /data/runs/{run_id}/{path} — pipeline artifacts (final.html, step*.json, ...)

Env toggle forward (보고용) :
  PHASE_Z_ALLOW_RESTRUCTURE / PHASE_Z_ALLOW_REJECT / PHASE_Z_MAX_RANK=32

Components :
  - LeftMdxPanel (03/04/05 fix list + section tree)
  - SlideCanvas (iframe + slideOverrideCss prop for inline CSS inject)
  - FramePanel (label priority + confidence sort)
  - LayoutPanel

README with mermaid diagrams covering the 5-step demo flow.
node_modules / dist / .manus-logs / .env excluded via .gitignore.
This commit is contained in:
2026-05-14 14:45:30 +09:00
parent 52ccb7fc8b
commit 0f0d3fa91f
99 changed files with 20280 additions and 0 deletions

View File

@@ -0,0 +1,104 @@
import React from 'react';
/**
* Step 22를 위한 16:9 베이스 슬라이드 본문 레이아웃 템플릿
* 8종 중 우선 2종 샘플 구현
*/
interface LayoutProps {
title?: string;
sections?: {
id: string;
title: string;
content: string;
type?: 'text' | 'image' | 'chart';
}[];
}
/**
* Layout 1: Focus Split (좌측 텍스트 / 우측 강조형)
* 가장 범용적인 슬라이드 레이아웃
*/
export const FocusSplitLayout: React.FC<LayoutProps> = ({ sections = [] }) => {
const mainSection = sections[0] || { title: 'Main Point', content: '핵심 내용을 이곳에 작성합니다.' };
const subSection = sections[1] || { title: 'Supporting Detail', content: '보조 설명이나 이미지가 들어갑니다.' };
return (
<div className="w-full h-full flex gap-8 p-6 bg-white animate-in fade-in duration-500">
{/* 좌측 메인 영역 (60%) */}
<div className="flex-[3] flex flex-col justify-center space-y-6">
<div className="space-y-2">
<div className="w-12 h-1 bg-blue-600 rounded-full" />
<h2 className="text-4xl font-black text-slate-900 leading-tight">
{mainSection.title}
</h2>
</div>
<p className="text-xl text-slate-600 leading-relaxed font-medium">
{mainSection.content}
</p>
<div className="flex gap-4 pt-4">
<div className="px-4 py-2 bg-slate-50 border border-slate-100 rounded-xl text-sm font-bold text-slate-400 uppercase tracking-widest">
Key Insight
</div>
</div>
</div>
{/* 우측 강조 영역 (40%) */}
<div className="flex-[2] relative">
<div className="absolute inset-0 bg-gradient-to-br from-blue-50 to-indigo-50 rounded-[2rem] border-2 border-white shadow-inner flex flex-col items-center justify-center p-8 text-center overflow-hidden">
<div className="absolute top-0 right-0 w-32 h-32 bg-blue-500/5 rounded-full -mr-16 -mt-16" />
<div className="w-16 h-16 bg-white rounded-2xl shadow-lg flex items-center justify-center mb-6">
<div className="w-8 h-8 bg-blue-600 rounded-lg animate-pulse" />
</div>
<h3 className="text-2xl font-bold text-slate-800 mb-3">{subSection.title}</h3>
<p className="text-sm text-slate-500 font-medium leading-normal">
{subSection.content}
</p>
</div>
</div>
</div>
);
};
/**
* Layout 2: Quadrant Grid (2x2 그리드 핵심 요약형)
* 여러 요소를 병렬로 보여줄 때 최적
*/
export const QuadrantGridLayout: React.FC<LayoutProps> = ({ sections = [] }) => {
const items = sections.length >= 4 ? sections.slice(0, 4) : [
{ title: 'Strategy', content: '장기적 목표 및 실행 계획 수립' },
{ title: 'Operations', content: '효율적인 자원 배분 및 프로세스 최적화' },
{ title: 'Technology', content: '디지털 전환을 위한 기술 스택 도입' },
{ title: 'Growth', content: '지속 가능한 성장 동력 확보' },
];
return (
<div className="w-full h-full grid grid-cols-2 grid-rows-2 gap-6 p-6 bg-white animate-in zoom-in-95 duration-500">
{items.map((item, i) => (
<div
key={i}
className="group p-6 rounded-[1.5rem] border-2 border-slate-50 bg-slate-50/30 hover:bg-white hover:border-blue-100 hover:shadow-xl hover:shadow-blue-500/5 transition-all duration-300 flex flex-col justify-between"
>
<div className="space-y-3">
<div className="flex items-center gap-3">
<span className="flex items-center justify-center w-8 h-8 rounded-full bg-slate-900 text-white text-xs font-black">
0{i + 1}
</span>
<h3 className="text-xl font-black text-slate-800 group-hover:text-blue-600 transition-colors">
{item.title}
</h3>
</div>
<p className="text-sm text-slate-500 leading-relaxed font-medium pl-11">
{item.content}
</p>
</div>
<div className="pl-11 pt-4">
<div className="w-full h-1 bg-slate-100 rounded-full overflow-hidden">
<div className="w-1/3 h-full bg-blue-500 rounded-full group-hover:w-full transition-all duration-700" />
</div>
</div>
</div>
))}
</div>
);
};