"use client"; import { useState, useEffect, useRef, useCallback } from "react"; import { uploadFile, generateMarketingStudioAd } from "../muapi.js"; const SCROLLBAR_STYLE = ` .custom-scrollbar-thin::-webkit-scrollbar { height: 4px; } .custom-scrollbar-thin::-webkit-scrollbar-track { background: transparent; } .custom-scrollbar-thin::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.1); border-radius: 10px; } .custom-scrollbar-thin::-webkit-scrollbar-thumb:hover { background: rgba(217, 255, 0, 0.3); } `; // ── Icons ──────────────────────────────────────────────────────────────────── const CheckSvg = () => ( ); const PlusSvg = () => ( ); const CloseSvg = () => ( ); const ProductIcon = () => ( ); const AvatarIcon = () => ( ); const RefIcon = () => ( ); // ── Assets ─────────────────────────────────────────────────────────────────── const ASSETS = { avatar: [ { id: "aa252283-8591-4d14-91a8-41ce54187992", name: "Priya", url: "https://d3adwkbyhxyrtq.cloudfront.net/web-app/Priya.webp" }, { id: "ba6c9b18-f79c-4dab-9649-88a181d0a038", name: "Elena", url: "https://d3adwkbyhxyrtq.cloudfront.net/web-app/Elena.webp" }, { id: "30e2cadd-987c-4a7a-81c3-094d4fb3a65e", name: "Kai", url: "https://d3adwkbyhxyrtq.cloudfront.net/web-app/Kai.webp" }, { id: "fbed59e1-4b8d-4625-9140-ef2044e0be72", name: "Sora", url: "https://d3adwkbyhxyrtq.cloudfront.net/web-app/Sora.webp" }, { id: "bcd9e6ee-c000-48e6-9f4b-a20fc2a674f7", name: "Minji", url: "https://d3adwkbyhxyrtq.cloudfront.net/web-app/Minji.webp" }, { id: "1da384ed-3856-45e4-bf4c-a496c7aa95ff", name: "Margot", url: "https://d3adwkbyhxyrtq.cloudfront.net/web-app/Margot.webp" }, { id: "b799c8f5-fb6e-4905-b33b-cdefac153ec3", name: "Niko", url: "https://d3adwkbyhxyrtq.cloudfront.net/web-app/Niko.webp" }, { id: "b6971dd4-55fa-4e64-b318-392b16504284", name: "Jin", url: "https://d3adwkbyhxyrtq.cloudfront.net/web-app/Jin.webp" } ], ugc: [ { id: 1, name: "UGC", url: "https://d3adwkbyhxyrtq.cloudfront.net/web-app/ugc.mp4" }, { id: 2, name: "Tutorial", url: "https://d3adwkbyhxyrtq.cloudfront.net/web-app/ugc_how_to.mp4" }, { id: 3, name: "Unboxing", url: "https://d3adwkbyhxyrtq.cloudfront.net/web-app/ugc_unboxing.mp4" }, { id: 4, name: "Hyper Motion", url: "https://d3adwkbyhxyrtq.cloudfront.net/web-app/hyper-motion-mini.mp4" }, { id: 5, name: "Product Review", url: "https://d3adwkbyhxyrtq.cloudfront.net/web-app/product_review.mp4" }, { id: 6, name: "TV Spot", url: "https://d3adwkbyhxyrtq.cloudfront.net/web-app/tv-spot-mini.mp4" } ] }; const OPTIONS = { ratio: ["9:16", "3:4", "4:3", "16:9", "1:1"], res: ["720p", "1080p"], duration: [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] }; // ── Components ─────────────────────────────────────────────────────────────── function UploadSlot({ icon, url, progress, label, onUpload, onClear, multiple = false, images = [] }) { const inputRef = useRef(null); return (
inputRef.current?.click()} title={`Upload ${label}`} className={`relative w-10 h-10 rounded-full border transition-all flex items-center justify-center cursor-pointer ${ url ? 'border-primary/40 bg-primary/5' : 'border-white/5 bg-white/5 hover:bg-white/10 hover:border-white/20' }`} > onUpload(e)} /> {progress > 0 && progress < 100 ? (
{progress}%
) : url ? (
{label}
) : (
{icon}
)} {/* Clear Button (Single) */} {url && !multiple && ( )}
); } function Dropdown({ isOpen, title, items, selectedId, onSelect, onClose, isVideo = false }) { const ref = useRef(null); useEffect(() => { if (!isOpen) return; const handler = (e) => { if (ref.current && !ref.current.contains(e.target)) onClose(); }; window.addEventListener("click", handler); return () => window.removeEventListener("click", handler); }, [isOpen, onClose]); if (!isOpen) return null; return (
{title}
{items.map(item => (
onSelect(item)} className={`relative rounded overflow-hidden border-2 transition-all group cursor-pointer ${ selectedId === item.id || selectedId === item.url ? 'border-primary shadow-glow' : 'border-white/5 hover:border-white/20' }`} > {isVideo ? (
))}
); } function SimpleDropdown({ isOpen, title, options, selected, onSelect, onClose }) { const ref = useRef(null); useEffect(() => { if (!isOpen) return; const handler = (e) => { if (ref.current && !ref.current.contains(e.target)) onClose(); }; window.addEventListener("click", handler); return () => window.removeEventListener("click", handler); }, [isOpen, onClose]); if (!isOpen) return null; return (
{title}
{options.map(opt => ( ))}
); } // ── Main Component ─────────────────────────────────────────────────────────── export default function MarketingStudio({ apiKey, droppedFiles, onFilesHandled }) { const PERSIST_KEY = "hg_marketing_studio_persistent"; const [prompt, setPrompt] = useState(""); const [productImage, setProductImage] = useState(null); const [avatarImage, setAvatarImage] = useState(null); const [additionalImages, setAdditionalImages] = useState([]); const [params, setParams] = useState({ ratio: "9:16", format: ASSETS.ugc[0].name, videoUrl: ASSETS.ugc[0].url, res: "1080p", duration: 5 }); const [history, setHistory] = useState([]); const [isGenerating, setIsGenerating] = useState(false); const [dropdown, setDropdown] = useState(null); // 'format' | 'avatar' | 'ratio' | 'res' | 'duration' const [uploadProgress, setUploadProgress] = useState({ product: 0, avatar: 0, additional: 0 }); const [fullscreenUrl, setFullscreenUrl] = useState(null); const textareaRef = useRef(null); // ── Persistence ─────────────────────────────────────────────────────────── useEffect(() => { try { const stored = localStorage.getItem(PERSIST_KEY); if (stored) { const data = JSON.parse(stored); if (data.prompt) setPrompt(data.prompt); if (data.params) setParams(data.params); if (data.productImage) setProductImage(data.productImage); if (data.avatarImage) setAvatarImage(data.avatarImage); if (data.additionalImages) setAdditionalImages(data.additionalImages); if (data.history) setHistory(data.history); } } catch (err) { console.warn("Load failed", err); } }, []); useEffect(() => { const timer = setTimeout(() => { const state = { prompt, params, productImage, avatarImage, additionalImages, history }; localStorage.setItem(PERSIST_KEY, JSON.stringify(state)); }, 500); return () => clearTimeout(timer); }, [prompt, params, productImage, avatarImage, additionalImages, history]); // ── Handlers ─────────────────────────────────────────────────────────────── const downloadFile = async (url, filename) => { try { const response = await fetch(url); const blob = await response.blob(); const blobUrl = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = blobUrl; a.download = filename; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(blobUrl); } catch { window.open(url, "_blank"); } }; const handleUpload = async (e, target) => { const files = Array.from(e.target.files); if (!files.length) return; if (target === 'additional') { const remaining = 6 - additionalImages.length; const toUpload = files.slice(0, remaining); for (const file of toUpload) { try { const url = await uploadFile(apiKey, file, (pct) => setUploadProgress(p => ({ ...p, additional: pct }))); setAdditionalImages(prev => [...prev, url].slice(0, 6)); } catch (err) { alert(err.message); } } } else { const file = files[0]; try { const url = await uploadFile(apiKey, file, (pct) => setUploadProgress(p => ({ ...p, [target]: pct }))); if (target === 'product') setProductImage(url); else setAvatarImage(url); } catch (err) { alert(err.message); } } setUploadProgress(p => ({ ...p, [target]: 0 })); }; const handleGenerate = async () => { if (!prompt.trim()) return alert("Please enter an ad script."); if (!productImage) return alert("Please upload a product image."); setIsGenerating(true); try { const result = await generateMarketingStudioAd(apiKey, { prompt, aspect_ratio: params.ratio, duration: params.duration, resolution: params.res, images_list: [productImage, avatarImage, ...additionalImages].filter(Boolean), video_files: params.videoUrl ? [params.videoUrl] : [] }); if (result?.url) { const entry = { id: Date.now(), url: result.url, prompt, format: params.format, timestamp: new Date().toISOString() }; setHistory(prev => [entry, ...prev]); setFullscreenUrl(result.url); } } catch (err) { alert("Generation failed: " + err.message); } finally { setIsGenerating(false); } }; const handleTextareaInput = (e) => { const el = e.target; el.style.height = "auto"; el.style.height = Math.min(el.scrollHeight, 250) + "px"; }; // ── Render ───────────────────────────────────────────────────────────────── return (
{/* ── MAIN CONTENT AREA ── */}
{history.length > 0 ? (
{history.map(entry => (
))}
) : (

START CREATING WITH
MARKETING STUDIO

Describe your scene, upload your product, and watch high-converting AI video ads come to life.

)}
{/* ── BOTTOM PROMPT BAR ── */}
{additionalImages.length > 0 && (
{additionalImages.map((img, idx) => (
))}
)} {/* Top Row: Full-width Textarea */}