"use client"; import { useState, useRef, useEffect } from "react"; // ── Types ────────────────────────────────────────────────────────────────────── export type AssetType = "image" | "video" | "audio"; interface Voice { id:string; name:string; emoji:string; tags:string[]; source:"preset"|"cloned"; createdAt?:string; } interface StorySegment { id:string; text:string; duration:number; emotion:string; } interface WorkflowCard { id:string; icon:string; title:string; desc:string; steps:string[]; recommended?:boolean; } export interface BerylizeConfig { script:string; voiceId:string; lipSyncQuality:"natural"|"precise"|"exaggerated"; style:string; segments:StorySegment[]; language:string; exportPreset:string; workflow:string; makeitbang:boolean; bangOptions:string[]; } export interface BerylizeContext { type: AssetType; src: string; prompt: string; id?: string; } interface BerylizeModalProps { context: BerylizeContext; onClose: () => void; onBerylize: (config: BerylizeConfig) => void; history?: BerylizeContext[]; } // ── Static data ──────────────────────────────────────────────────────────────── const PRESET_VOICES: Voice[] = [ { id:"v_kizzy", name:"Kizzy", emoji:"👑", tags:["warm","confident","premium"], source:"preset" }, { id:"v_eve", name:"Eve", emoji:"🤖", tags:["sharp","clear","technical"], source:"preset" }, { id:"v_amanda", name:"Amanda", emoji:"💼", tags:["corporate","polished","calm"], source:"preset" }, { id:"v_india", name:"India", emoji:"🌟", tags:["energetic","youthful","bright"], source:"preset" }, { id:"v_jessica", name:"Jessica", emoji:"🎯", tags:["strategic","direct","powerful"], source:"preset" }, { id:"v_nu", name:"Nu", emoji:"💡", tags:["innovative","visionary","bold"], source:"preset" }, ]; const EMOTIONS = ["Neutral","Confident","Warm","Excited","Serious","Playful","Empathetic","Commanding"]; const STYLES = ["Professional","Warm & Personal","Energetic","Authoritative","Conversational","Cinematic"]; const LANGUAGES = ["English","Spanish","French","Mandarin","Arabic","Portuguese","German","Japanese","Korean","Hindi"]; const EXPORT_PRESETS = [ { id:"instagram", label:"Instagram Reel", icon:"📱", spec:"9:16 · 60s · 1080p" }, { id:"youtube", label:"YouTube Short", icon:"▶", spec:"9:16 · 60s · 1080p" }, { id:"linkedin", label:"LinkedIn", icon:"💼", spec:"16:9 · 10min · 1080p" }, { id:"tiktok", label:"TikTok", icon:"🎵", spec:"9:16 · 3min · 1080p" }, { id:"web", label:"Web / Embed", icon:"🌐", spec:"16:9 · unlimited · 4K" }, { id:"raw", label:"Raw Export", icon:"📁", spec:"Any · unlimited · 4K" }, ]; const BANG_OPTIONS = [ { id:"camera", label:"Cinematic camera moves", icon:"🎬" }, { id:"music", label:"Background score", icon:"🎵" }, { id:"subtitles",label:"Auto-subtitles (burned in)", icon:"💬" }, { id:"colorgrd", label:"Cinematic color grade", icon:"🎨" }, { id:"kenburns", label:"Ken Burns effect", icon:"🖼" }, { id:"lowerthird",label:"Lower-third name card", icon:"📛" }, ]; // Context-aware workflow cards per asset type const WORKFLOWS: Record = { image: [ { id:"img_talking", icon:"🗣", title:"Talking Portrait", desc:"Bring this image to life with voice + lip-sync", steps:["Script","Voice","Lip-sync"], recommended:true }, { id:"img_reel", icon:"📱", title:"Social Media Reel", desc:"8s punchy reel, auto-captions, export-ready", steps:["Script","Voice","Export"] }, { id:"img_cinematic",icon:"🎬",title:"Cinematic Scene", desc:"Cinematic camera + music + color grade", steps:["Motion","Score","Grade"] }, { id:"img_combo", icon:"⚡", title:"Full Combo", desc:"Image + voice + motion → masterpiece", steps:["Voice","Motion","Polish"] }, ], video: [ { id:"vid_voice", icon:"🎙", title:"Add Voice & Lip-sync", desc:"Perfect lip-sync to your existing video", steps:["Script","Voice","Sync"], recommended:true }, { id:"vid_dub", icon:"🌍", title:"Auto-Dub", desc:"Translate + re-voice in any of 10 languages", steps:["Language","Voice","Dub"] }, { id:"vid_enhance", icon:"✨", title:"Cinematic Enhance", desc:"Music, color grade, subtitles, name card", steps:["Score","Grade","Captions"] }, { id:"vid_dialogue",icon:"💬", title:"Dialogue Version", desc:"Add multi-character dialogue to your video", steps:["Cast","Script","Mix"] }, ], audio: [ { id:"aud_character",icon:"👤",title:"Generate Character", desc:"Create a face that matches this voice", steps:["Character","Image","Polish"], recommended:true }, { id:"aud_talking", icon:"🗣",title:"Full Talking Head", desc:"Character image → lip-synced talking video", steps:["Character","Animate","Export"] }, { id:"aud_scene", icon:"🎬",title:"Build Short Film", desc:"Full scene: character + script + motion + music",steps:["Cast","Scene","Score"] }, { id:"aud_combo", icon:"⚡",title:"Voice → Masterpiece", desc:"One voice. Infinite expression.", steps:["Image","Motion","Export"] }, ], }; // Smart copy per context const CTX = { image: { banner:"You have an Image.", action:"Turn it into a complete talking video.", color:"#3b82f6", icon:"🖼" }, video: { banner:"You have a Video.", action:"Add voice + perfect lip-sync.", color:"#8b5cf6", icon:"▶" }, audio: { banner:"You have a Voice.", action:"Generate a character + talking video.", color:"#22c55e", icon:"🎙" }, }; function uid() { return Math.random().toString(36).slice(2,9); } // ── Component ────────────────────────────────────────────────────────────────── export default function BerylizeModal({ context, onClose, onBerylize, history=[] }: BerylizeModalProps) { const [phase, setPhase] = useState<"workflows"|"build">("workflows"); const [workflow, setWorkflow] = useState(null); const [step, setStep] = useState<1|2|3>(1); const [script, setScript] = useState(""); const [segments, setSegments] = useState([]); const [storyboard, setStoryboard] = useState(false); const [selectedVoice, setSelectedVoice] = useState(PRESET_VOICES[0]); const [clonedVoices, setClonedVoices] = useState([]); const [ytLink, setYtLink] = useState(""); const [ytTimestamp, setYtTimestamp] = useState(""); const [voiceSearch, setVoiceSearch] = useState(""); const [lipSync, setLipSync] = useState<"natural"|"precise"|"exaggerated">("precise"); const [style, setStyle] = useState(STYLES[0]); const [language, setLanguage] = useState(LANGUAGES[0]); const [exportPreset, setExportPreset] = useState(EXPORT_PRESETS[0].id); const [makeitbang, setMakeitbang] = useState(false); const [bangOpts, setBangOpts] = useState([]); const [multiVoice, setMultiVoice] = useState(false); const [autoDub, setAutoDub] = useState(false); const [consistency, setConsistency] = useState(95); const [cloneStatus, setCloneStatus] = useState<"idle"|"cloning"|"done">("idle"); const [dragOver, setDragOver] = useState(false); const [berylizing, setBerylizing] = useState(false); const [berylProgress, setBerylProgress] = useState(0); const [berylDone, setBerylDone] = useState(false); const [showHistory, setShowHistory] = useState(false); const audioRef = useRef(null); const ctx = CTX[context.type]; const flows = WORKFLOWS[context.type]; // Auto-pick recommended workflow useEffect(() => { const rec = flows.find(f => f.recommended); if (rec) setWorkflow(rec); }, [context.type]); const autoSegment = (text: string) => { const sentences = text.match(/[^.!?]+[.!?]+/g) || [text]; return sentences.map(s => ({ id:uid(), text:s.trim(), duration:Math.max(2,Math.ceil(s.trim().split(" ").length*.4)), emotion:"Neutral" })); }; const handleCloneYT = () => { if (!ytLink.trim()) return; setCloneStatus("cloning"); setTimeout(() => { const v: Voice = { id:`v_yt_${uid()}`, name:`YT Clone ${clonedVoices.length+1}`, emoji:"🎬", tags:["youtube","cloned"], source:"cloned", createdAt:new Date().toLocaleDateString() }; setClonedVoices(p=>[v,...p]); setSelectedVoice(v); setYtLink(""); setCloneStatus("done"); setTimeout(()=>setCloneStatus("idle"),3000); }, 2000); }; const handleAudioUpload = (file: File) => { setCloneStatus("cloning"); setTimeout(() => { const v: Voice = { id:`v_up_${uid()}`, name:file.name.replace(/\.[^.]+$/,"").slice(0,22), emoji:"🎙️", tags:["uploaded","cloned"], source:"cloned", createdAt:new Date().toLocaleDateString() }; setClonedVoices(p=>[v,...p]); setSelectedVoice(v); setCloneStatus("done"); setTimeout(()=>setCloneStatus("idle"),3000); }, 1800); }; const handleBerylize = () => { if (!script.trim() && context.type !== "video") return; setBerylizing(true); let p = 0; const iv = setInterval(() => { p += Math.random() * 8 + 3; if (p >= 100) { clearInterval(iv); p = 100; setBerylDone(true); setBerylizing(false); } setBerylProgress(Math.min(p,100)); }, 200); onBerylize({ script, voiceId:selectedVoice.id, lipSyncQuality:lipSync, style, segments, language, exportPreset, workflow:workflow?.id||"", makeitbang, bangOptions:bangOpts }); }; const allVoices = [...clonedVoices,...PRESET_VOICES].filter(v=> !voiceSearch || v.name.toLowerCase().includes(voiceSearch.toLowerCase()) || v.tags.some(t=>t.includes(voiceSearch.toLowerCase())) ); const stepLabels = context.type==="video" ? ["","Voice & Script","Style & Lip-sync","Export & Polish"] : context.type==="audio" ? ["","Script & Story","Character & Voice","Export & Polish"] : ["","Script & Story","Voice & Sound","Style & Export"]; const canProceed = step===1 ? (script.trim().length>0||context.type==="video") : true; return (
{if(e.target===e.currentTarget)onClose();}}>
{/* ── SMART CONTEXT BANNER ── */}
{ctx.icon}
✨ Berylize It! AI-Powered
{ctx.banner} {ctx.action}
{history.length>0 && ( )}
{/* ── HISTORY PANEL (slide-in) ── */} {showHistory && (
Recent Berylized Projects — click to remix
{history.map((h,i)=>(
{h.type==="image" ? :
{h.type==="video"?"▶":"🎙"}
}
{h.prompt.slice(0,20)}
))}
)}
{/* ─────────────── PHASE 1: WORKFLOW PICKER ─────────────── */} {phase==="workflows" && (
{/* Left: asset preview */}
Your {context.type}
{context.type==="image" ? {(e.target as HTMLImageElement).style.display="none"}}/> : context.type==="video" ?
Video Asset
:
🎙
Audio Asset
}
{context.type}
{context.prompt && (
"{context.prompt.slice(0,70)}{context.prompt.length>70?"...":""}"
)} {/* Consistency meter */}
Character Lock
{consistency}%
Beryl LLM reference lock
setConsistency(+e.target.value)} style={{width:"100%",marginTop:6,accentColor:"#3b82f6",cursor:"pointer"}}/>
{/* Right: workflow cards */}
Recommended Workflows
Choose how you want to transform your {context.type}. One click to begin.
{flows.map(f=>(
setWorkflow(f)} style={{ background:workflow?.id===f.id?`${ctx.color}14`:"#151520", border:`2px solid ${workflow?.id===f.id?ctx.color:"#1e1e2a"}`, borderRadius:14,padding:"16px 18px",position:"relative",overflow:"hidden", }}> {f.recommended &&
⭐ Recommended
}
{f.icon}
{f.title}
{f.desc}
{f.steps.map((s,i)=>(
{s} {i→}
))}
{workflow?.id===f.id && (
)}
))}
{/* Make It Bang toggle */}
💥
Make It Bang
Add cinematic enhancements that make every second hit harder
setMakeitbang(v=>!v)} style={{width:40,height:22,borderRadius:11,background:makeitbang?"#f59e0b":"#1a1a24",border:`1px solid ${makeitbang?"#f59e0b":"#2a2a3a"}`,position:"relative",transition:"all .2s",cursor:"pointer",flexShrink:0}}>
{makeitbang && (
{BANG_OPTIONS.map(o=>(
setBangOpts(p=>p.includes(o.id)?p.filter(x=>x!==o.id):[...p,o.id])} style={{ background:bangOpts.includes(o.id)?"rgba(245,158,11,.15)":"rgba(255,255,255,.03)", border:`1px solid ${bangOpts.includes(o.id)?"#f59e0b":"#2a2a3a"}`, borderRadius:8,padding:"8px 10px",display:"flex",alignItems:"center",gap:8, }}> {o.icon} {o.label} {bangOpts.includes(o.id) && }
))}
)}
)} {/* ─────────────── PHASE 2: BUILD FLOW ─────────────── */} {phase==="build" && (
{/* Step bar */}
{workflow && ✦ {workflow.title}}
{[1,2,3].map((s,i)=>(
!berylizing&&setStep(s as 1|2|3)} style={{ display:"flex",alignItems:"center",gap:7,cursor:"pointer",padding:"6px 12px",borderRadius:8, background:step===s?`${ctx.color}18`:"transparent",border:`1px solid ${step===s?ctx.color+"44":"transparent"}`,flexShrink:0, }}>
s?"#22c55e":step===s?ctx.color:"#1a1a24",color:"#fff",border:`1px solid ${step>s?"#22c55e":step===s?ctx.color:"#2a2a3a"}`, }}>{step>s?"✓":s}
{stepLabels[s]}
{i<2 &&
s?"#22c55e":"#1e1e2a",margin:"0 4px"}}/>}
))}
{/* Left: preview */}
Your {context.type}
{context.type==="image" ? {(e.target as HTMLImageElement).style.display="none"}}/> :
{context.type==="video"?"▶":"🎙"}
}
{/* Mini consistency */}
Character Lock
{consistency}%
{/* Right: step content */}
{/* STEP 1 */} {step===1 && (
{context.type==="video" ? "🎙️ What should she say?" : context.type==="audio" ? "📖 Write your story" : "✍️ What should she say?"}
{context.type==="video" ? "Add a voice script to your video — timing handled automatically." : "Write the script. We handle timing, pacing, and emotion."}
Storyboard mode
{setStoryboard(v=>!v);if(!storyboard&&script)setSegments(autoSegment(script));}} style={{width:36,height:20,borderRadius:10,background:storyboard?ctx.color:"#1a1a24",border:"1px solid #2a2a3a",position:"relative",transition:"all .2s",cursor:"pointer"}}>