import { useEffect, useState, useRef } from 'react'; import { motion, AnimatePresence } from 'motion/react'; import { Sparkles, X } from 'lucide-react'; import { cn } from '@/lib/utils'; import { useI18n } from '@/lib/hooks/use-i18n'; interface AgentRevealModalProps { agents: Array<{ id: string; name: string; role: string; persona: string; avatar: string; color: string; }>; open: boolean; onClose: () => void; /** Called once after all cards are revealed — signals generation can continue */ onAllRevealed?: () => void; } function isUrl(str: string): boolean { return str.startsWith('http') || str.startsWith('/') || str.startsWith('data:'); } /** Lighten a hex color by mixing with white */ function lighten(hex: string, amount: number): string { const r = parseInt(hex.slice(1, 3), 16); const g = parseInt(hex.slice(3, 5), 16); const b = parseInt(hex.slice(5, 7), 16); const lr = Math.round(r + (255 - r) * amount); const lg = Math.round(g + (255 - g) * amount); const lb = Math.round(b + (255 - b) * amount); return `rgb(${lr},${lg},${lb})`; } const ROLE_ICONS: Record = { teacher: '👨‍🏫', assistant: '🤝', student: '🎓', }; export function AgentRevealModal({ agents, open, onClose, onAllRevealed }: AgentRevealModalProps) { const { t } = useI18n(); const [revealedCount, setRevealedCount] = useState(0); const [flipsComplete, setFlipsComplete] = useState(false); const allRevealedFiredRef = useRef(false); const onAllRevealedRef = useRef(onAllRevealed); onAllRevealedRef.current = onAllRevealed; const allRevealed = revealedCount >= agents.length && agents.length > 0; useEffect(() => { if (!open) { setRevealedCount(0); setFlipsComplete(false); allRevealedFiredRef.current = false; return; } let i = 0; const startTimeout = setTimeout(() => { i = 1; setRevealedCount(1); if (agents.length <= 1) { setTimeout(() => { if (!allRevealedFiredRef.current) { allRevealedFiredRef.current = true; onAllRevealedRef.current?.(); } }, 600); return; } const interval = setInterval(() => { i++; setRevealedCount(i); if (i >= agents.length) { clearInterval(interval); setTimeout(() => { if (!allRevealedFiredRef.current) { allRevealedFiredRef.current = true; onAllRevealedRef.current?.(); } }, 600); } }, 500); return () => clearInterval(interval); }, 400); return () => clearTimeout(startTimeout); }, [open, agents.length]); // Switch from preserve-3d to flat after all flip animations complete to enable scrolling useEffect(() => { if (!allRevealed) return; const timer = setTimeout(() => setFlipsComplete(true), 800); return () => clearTimeout(timer); }, [allRevealed]); return ( {open && ( {/* Close button */} {allRevealed && ( )} {/* Title */} {t('generation.agentRevealTitle')} {/* Cards */}
{agents.map((agent, index) => { const isRevealed = index < revealedCount; const roleIcon = ROLE_ICONS[agent.role] ?? '🎓'; return ( {/* ====== FRONT FACE ====== */}
{/* Outer colored border */}
{/* Inner card body */}
{/* Top gradient band with texture */}
{/* Color gradient fill */}
{/* Subtle noise texture */}
{/* Decorative corner accent lines */}
{/* Avatar — overlapping the band */}
{isUrl(agent.avatar) ? ( {agent.name} ) : ( {agent.avatar || agent.name.charAt(0)} )}
{/* Name + role row */}

{agent.name}

{roleIcon} {t(`settings.agentRoles.${agent.role}`)}
{/* Thin ornamental divider */}
{/* Persona text — fills remaining space */}

{agent.persona}

{/* Bottom edge glow */}
{/* ====== BACK FACE ====== */}
{/* Gradient border matching front style */}
{/* Decorative inner border */}
{/* Diamond pattern corners */} {/* Center icon */} ?
); })}
{/* Progress dots + continue */}
{agents.map((_, index) => (
))}
{allRevealed && ( {t('generation.continue')} )} )} ); }