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.
102 lines
2.8 KiB
TypeScript
102 lines
2.8 KiB
TypeScript
/**
|
|
* BottomActions - 하단 액션 버튼 영역
|
|
*
|
|
* 생성하기, 다운로드, 연동하기 버튼 컴포넌트
|
|
*/
|
|
|
|
import { Sparkles, Download, Link2, Loader2 } from "lucide-react";
|
|
import { Button } from "@/components/ui/button";
|
|
import { toast } from "sonner";
|
|
import type { SlidePlan, UserSelection } from "../types/designAgent";
|
|
import { serializeSlidePlan } from "../utils/slidePlanUtils";
|
|
|
|
interface BottomActionsProps {
|
|
slidePlan: SlidePlan | null;
|
|
userSelection: UserSelection;
|
|
isLoading: boolean;
|
|
onGenerate: () => void;
|
|
}
|
|
|
|
export default function BottomActions({
|
|
slidePlan,
|
|
userSelection,
|
|
isLoading,
|
|
onGenerate,
|
|
}: BottomActionsProps) {
|
|
const handleDownload = () => {
|
|
if (!slidePlan) {
|
|
toast.error("슬라이드 플랜이 없습니다. 먼저 생성하기를 눌러주세요.");
|
|
return;
|
|
}
|
|
|
|
const json = serializeSlidePlan(slidePlan, userSelection);
|
|
console.log("[Download] SlidePlan JSON:", json);
|
|
|
|
// JSON 파일 다운로드
|
|
const blob = new Blob([json], { type: "application/json" });
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement("a");
|
|
a.href = url;
|
|
a.download = `slide-plan-${Date.now()}.json`;
|
|
a.click();
|
|
URL.revokeObjectURL(url);
|
|
|
|
toast.success("SlidePlan JSON이 다운로드되었습니다.");
|
|
};
|
|
|
|
const handleConnect = () => {
|
|
toast.info("연동하기 기능은 파이프라인 연결 후 활성화됩니다.");
|
|
};
|
|
|
|
return (
|
|
<div className="flex items-center justify-center gap-3 px-6 py-3 bg-white border-t border-slate-200">
|
|
<div className="flex items-center gap-1.5 mr-2">
|
|
<span className="w-5 h-5 rounded-full bg-blue-600 text-white text-xs flex items-center justify-center font-bold">4</span>
|
|
<span className="text-xs text-slate-500 font-medium">액션</span>
|
|
</div>
|
|
{/* 생성하기 */}
|
|
<Button
|
|
onClick={onGenerate}
|
|
disabled={isLoading}
|
|
className="gap-2 min-w-[120px]"
|
|
size="default"
|
|
>
|
|
{isLoading ? (
|
|
<>
|
|
<Loader2 className="w-4 h-4 animate-spin" />
|
|
생성 중...
|
|
</>
|
|
) : (
|
|
<>
|
|
<Sparkles className="w-4 h-4" />
|
|
생성하기
|
|
</>
|
|
)}
|
|
</Button>
|
|
|
|
{/* 다운로드 */}
|
|
<Button
|
|
variant="outline"
|
|
onClick={handleDownload}
|
|
disabled={!slidePlan || isLoading}
|
|
className="gap-2 min-w-[120px]"
|
|
size="default"
|
|
>
|
|
<Download className="w-4 h-4" />
|
|
다운로드
|
|
</Button>
|
|
|
|
{/* 연동하기 */}
|
|
<Button
|
|
variant="outline"
|
|
onClick={handleConnect}
|
|
className="gap-2 min-w-[120px] text-slate-500"
|
|
size="default"
|
|
>
|
|
<Link2 className="w-4 h-4" />
|
|
연동하기
|
|
</Button>
|
|
</div>
|
|
);
|
|
}
|