import { useState } from "react"; import { Loader2 } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Textarea } from "@/components/ui/textarea"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Progress } from "@/components/ui/progress"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { useContentJob } from "@/hooks/useContentJob"; import { getContentJobAsset } from "@/api/contentJobs"; import type { IconSetBundle } from "@/types/content-bundles"; import { IconResultGrid } from "./IconResultGrid"; import { IconDownloadBar } from "./IconDownloadBar"; const STYLE_OPTIONS = [ { value: "outline", label: "Outline" }, { value: "filled", label: "Filled" }, { value: "rounded", label: "Rounded" }, ]; const COUNT_OPTIONS = [ { value: "1", label: "1" }, { value: "4", label: "4" }, { value: "8", label: "8" }, { value: "16", label: "16" }, ]; interface IconGeneratePanelProps { companyId: string; } export function IconGeneratePanel({ companyId }: IconGeneratePanelProps) { const [description, setDescription] = useState(""); const [style, setStyle] = useState("outline"); const [count, setCount] = useState("4"); const [bundle, setBundle] = useState(null); const [selectedIds, setSelectedIds] = useState>(new Set()); const job = useContentJob(companyId); const isGenerating = job.status === "queued" || job.status === "running"; const isIdle = job.status === "idle"; async function handleSubmit() { if (!description.trim() || isGenerating) return; setBundle(null); setSelectedIds(new Set()); await job.submit("icon-set", { description, style, count: parseInt(count, 10) }); } // Fetch asset when job completes if (job.status === "done" && job.resultAssetId && !bundle) { void getContentJobAsset(companyId, job.resultAssetId).then(async (assetUrl) => { const res = await fetch(assetUrl); const text = await res.text(); try { const parsed = JSON.parse(text) as IconSetBundle; setBundle(parsed); } catch { // ignore parse error } }); } function handleToggleIcon(name: string) { setSelectedIds((prev) => { const next = new Set(prev); if (next.has(name)) { next.delete(name); } else { next.add(name); } return next; }); } function handleDownload(format: string) { if (!bundle) return; const selected = bundle.icons.filter((icon) => selectedIds.has(icon.name)); for (const icon of selected) { let blobData: Blob; let filename: string; if (format === "svg") { blobData = new Blob([icon.svgSource], { type: "image/svg+xml" }); filename = `${icon.name}.svg`; } else { const sizeMap: Record = { "png-16": "16", "png-32": "32", "png-64": "64" }; const size = sizeMap[format] ?? "32"; const pngData = icon.pngs[size]; if (!pngData) continue; const byteString = atob(pngData); const ab = new ArrayBuffer(byteString.length); const ia = new Uint8Array(ab); for (let i = 0; i < byteString.length; i++) { ia[i] = byteString.charCodeAt(i); } blobData = new Blob([ab], { type: "image/png" }); filename = `${icon.name}-${size}.png`; } const url = URL.createObjectURL(blobData); const a = document.createElement("a"); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } } function handleClearSelection() { setSelectedIds(new Set()); } return ( Generate Icons