UNPKG

aura-glass

Version:

A comprehensive glassmorphism design system for React applications with 142+ production-ready components

734 lines (731 loc) 29.1 kB
'use client'; import { jsxs, jsx } from 'react/jsx-runtime'; import { useReducedMotion } from '../../hooks/useReducedMotion.js'; import { motion } from 'framer-motion'; import { forwardRef, useState, useRef, useCallback, useEffect } from 'react'; import { useMotionPreference } from '../../hooks/useMotionPreference.js'; import '../../primitives/GlassCore.js'; import '../../primitives/glass/GlassAdvanced.js'; import { OptimizedGlassCore } from '../../primitives/OptimizedGlassCore.js'; import '../../primitives/glass/OptimizedGlassAdvanced.js'; import '../../primitives/MotionNative.js'; import '../../primitives/motion/MotionFramer.js'; import { useA11yId } from '../../utils/a11y.js'; import { createGlassStyle } from '../../utils/createGlassStyle.js'; import { useGlassSound } from '../../utils/soundDesign.js'; const defaultModels = [{ id: "stylegan2-faces", name: "StyleGAN2 Faces", description: "High-quality human face generation", type: "stylegan", category: "faces", resolution: 1024, latentDim: 512, trained: true }, { id: "dcgan-art", name: "DCGAN Art", description: "Abstract art generation", type: "dcgan", category: "art", resolution: 256, latentDim: 100, trained: true }, { id: "biggan-objects", name: "BigGAN Objects", description: "Conditional object generation", type: "biggan", category: "objects", resolution: 512, latentDim: 128, trained: true }, { id: "cyclegan-style", name: "CycleGAN Style Transfer", description: "Unpaired image-to-image translation", type: "cyclegan", category: "style_transfer", resolution: 256, latentDim: 256, trained: true }, { id: "progressive-landscapes", name: "Progressive Landscapes", description: "Landscape scene generation", type: "progressive", category: "landscapes", resolution: 512, latentDim: 256, trained: true }, { id: "stylegan3-general", name: "StyleGAN3 General", description: "General purpose image generation", type: "stylegan", category: "general", resolution: 512, latentDim: 512, trained: false }]; const defaultGenerationParams = { seed: 42, truncation: 0.7, styleStrength: 1.0, noiseStrength: 0.5, batchSize: 4, interpolationSteps: 10 }; const defaultTrainingConfig = { epochs: 100, batchSize: 32, learningRate: 0.0002, beta1: 0.5, beta2: 0.999, discriminatorSteps: 1, generatorSteps: 1 }; const GlassGANGenerator = /*#__PURE__*/forwardRef(({ availableModels = defaultModels, selectedModel = "stylegan2-faces", generationParams = {}, trainingConfig = {}, showModelSelector = true, showGenerationControls = true, showTrainingControls = true, showLatentSpace = true, showProgress = true, enableInterpolation = true, enableRealTime = false, maxGenerations = 16, canvasWidth = 256, canvasHeight = 256, onModelSelect, onGenerate, onTrainingProgress, className = "", ...props }, ref) => { const prefersReducedMotion = useReducedMotion(); const [isGenerating, setIsGenerating] = useState(false); const [isTraining, setIsTraining] = useState(false); const [currentModel, setCurrentModel] = useState(selectedModel); const [generatedImages, setGeneratedImages] = useState([]); const [latentVectors, setLatentVectors] = useState([]); const [trainingProgress, setTrainingProgress] = useState({ epoch: 0, generatorLoss: 0, discriminatorLoss: 0 }); const [interpolationImages, setInterpolationImages] = useState([]); const [generationProgress, setGenerationProgress] = useState(0); const [params, setParams] = useState({ ...defaultGenerationParams, ...generationParams }); const [training, setTraining] = useState({ ...defaultTrainingConfig, ...trainingConfig }); useRef([]); const latentSpaceCanvasRef = useRef(null); useRef(null); useA11yId("glass-gan-generator"); const { shouldAnimate } = useMotionPreference(); const { play } = useGlassSound(); // Get selected model const model = availableModels.find(m => m.id === currentModel) || availableModels[0]; // Generate random latent vector const generateLatentVector = useCallback(dim => { return Array.from({ length: dim }, () => (Math.random() - 0.5) * 2); }, []); // Simulate GAN generation const generateGANImage = useCallback((latentVector, model, seed) => { const canvas = document.createElement("canvas"); canvas.width = model.resolution; canvas.height = model.resolution; const ctx = canvas.getContext("2d"); if (!ctx) return ""; // Create different patterns based on GAN type const imageData = ctx.createImageData(canvas.width, canvas.height); const data = imageData.data; switch (model.type) { case "stylegan": // StyleGAN-like high quality patterns for (let y = 0; y < canvas.height; y++) { for (let x = 0; x < canvas.width; x++) { const i = (y * canvas.width + x) * 4; // Use latent vector to influence generation const latentInfluence = latentVector.slice(0, 8).reduce((sum, val) => sum + val, 0) / 8; const nx = (x / canvas.width - 0.5) * 2; const ny = (y / canvas.height - 0.5) * 2; if (model.category === "faces") { // Face-like patterns const faceShape = Math.exp(-(nx * nx + ny * ny * 0.8) * 2); const detail = Math.sin(nx * 10) * Math.cos(ny * 10) * 0.1; data[i] = Math.floor((0.8 + latentInfluence * 0.2 + detail) * faceShape * 255); data[i + 1] = Math.floor((0.7 + latentInfluence * 0.3 + detail) * faceShape * 255); data[i + 2] = Math.floor((0.6 + latentInfluence * 0.4 + detail) * faceShape * 255); data[i + 3] = 255; } else { // General patterns const pattern = Math.sin(nx * 5 + latentInfluence) * Math.cos(ny * 5 + latentInfluence); data[i] = Math.floor((pattern + 1) * 128); data[i + 1] = Math.floor((Math.sin(pattern * 2) + 1) * 128); data[i + 2] = Math.floor((Math.cos(pattern * 3) + 1) * 128); data[i + 3] = 255; } } } break; case "dcgan": // DCGAN-like artistic patterns for (let y = 0; y < canvas.height; y++) { for (let x = 0; x < canvas.width; x++) { const i = (y * canvas.width + x) * 4; const latentSum = latentVector.slice(0, 5).reduce((sum, val) => sum + val, 0); const freq = 0.02 * (1 + latentSum * 0.1); const r = Math.sin(x * freq) * Math.cos(y * freq); const g = Math.sin((x + y) * freq * 0.8); const b = Math.cos((x - y) * freq * 1.2); data[i] = Math.floor((r + 1) * 128); data[i + 1] = Math.floor((g + 1) * 128); data[i + 2] = Math.floor((b + 1) * 128); data[i + 3] = 255; } } break; case "biggan": // BigGAN-like class-conditional patterns for (let y = 0; y < canvas.height; y++) { for (let x = 0; x < canvas.width; x++) { const i = (y * canvas.width + x) * 4; const classVector = latentVector.slice(-10); const dominantClass = Math.floor(Math.abs(classVector[0]) * 10); const colors = [[255, 100, 100], [100, 255, 100], [100, 100, 255], [255, 255, 100], [255, 100, 255], [100, 255, 255], [200, 150, 100], [150, 100, 200], [100, 200, 150], [180, 180, 180]]; const color = colors[dominantClass] || [128, 128, 128]; const noise = (Math.random() - 0.5) * 50; data[i] = Math.max(0, Math.min(255, color[0] + noise)); data[i + 1] = Math.max(0, Math.min(255, color[1] + noise)); data[i + 2] = Math.max(0, Math.min(255, color[2] + noise)); data[i + 3] = 255; } } break; default: // Default pattern for (let y = 0; y < canvas.height; y++) { for (let x = 0; x < canvas.width; x++) { const i = (y * canvas.width + x) * 4; const pattern = latentVector.slice(0, 3).reduce((sum, val, idx) => { return sum + val * Math.sin((x + y) * 0.01 * (idx + 1)); }, 0); data[i] = Math.floor((Math.sin(pattern) + 1) * 128); data[i + 1] = Math.floor((Math.cos(pattern * 1.1) + 1) * 128); data[i + 2] = Math.floor((Math.sin(pattern * 0.9) + 1) * 128); data[i + 3] = 255; } } } ctx.putImageData(imageData, 0, 0); return canvas.toDataURL(); }, []); // Generate batch of images const generateImages = useCallback(async () => { if (!model.trained) { play("error"); return; } setIsGenerating(true); setGenerationProgress(0); play("processing"); const newImages = []; const newLatentVectors = []; for (let i = 0; i < params.batchSize; i++) { setGenerationProgress(i / params.batchSize * 100); // Generate latent vector const latentVector = generateLatentVector(model.latentDim); // Apply truncation trick const truncatedVector = latentVector.map(val => val * params.truncation); // Generate image const imageUrl = generateGANImage(truncatedVector, model, params.seed + i); newImages.push(imageUrl); newLatentVectors.push(truncatedVector); // Add delay for realistic generation time await new Promise(resolve => setTimeout(resolve, 200)); } setGeneratedImages(prev => [...newImages, ...prev].slice(0, maxGenerations)); setLatentVectors(prev => [...newLatentVectors, ...prev].slice(0, maxGenerations)); setGenerationProgress(100); onGenerate?.(newImages, params); setIsGenerating(false); play("success"); }, [model, params, generateLatentVector, generateGANImage, maxGenerations, onGenerate, play]); // Generate interpolation between two latent vectors const generateInterpolation = useCallback(async () => { if (latentVectors.length < 2) return; const vec1 = latentVectors[0]; const vec2 = latentVectors[1]; const steps = params.interpolationSteps; const interpolatedImages = []; for (let i = 0; i <= steps; i++) { const t = i / steps; const interpolatedVector = vec1.map((val, idx) => val * (1 - t) + vec2[idx] * t); const imageUrl = generateGANImage(interpolatedVector, model, params.seed); interpolatedImages.push(imageUrl); } setInterpolationImages(interpolatedImages); }, [latentVectors, params.interpolationSteps, params.seed, generateGANImage, model]); // Simulate model training const trainModel = useCallback(async () => { if (!model) return; setIsTraining(true); play("processing"); for (let epoch = 0; epoch < training.epochs; epoch++) { // Simulate training step const generatorLoss = 1.0 + Math.random() * 0.5 - epoch * 0.01; const discriminatorLoss = 0.8 + Math.random() * 0.3 - epoch * 0.005; setTrainingProgress({ epoch: epoch + 1, generatorLoss: Math.max(0.1, generatorLoss), discriminatorLoss: Math.max(0.1, discriminatorLoss) }); onTrainingProgress?.(epoch + 1, { generator: generatorLoss, discriminator: discriminatorLoss }); await new Promise(resolve => setTimeout(resolve, 100)); } // Mark model as trained const modelIndex = availableModels.findIndex(m => m.id === currentModel); if (modelIndex !== -1) { availableModels[modelIndex].trained = true; } setIsTraining(false); play("success"); }, [model, training.epochs, currentModel, availableModels, onTrainingProgress, play]); // Visualize latent space const visualizeLatentSpace = useCallback(() => { const canvas = latentSpaceCanvasRef.current; if (!canvas || latentVectors.length === 0) return; const ctx = canvas.getContext("2d"); if (!ctx) return; ctx.clearRect(0, 0, canvas.width, canvas.height); // Draw latent vectors as points in 2D projection latentVectors.slice(0, 10).forEach((vector, idx) => { // Project high-dimensional vector to 2D using first two dimensions const x = (vector[0] + 2) / 4 * canvas.width; const y = (vector[1] + 2) / 4 * canvas.height; ctx.fillStyle = `hsl(${idx * 36}, 70%, 60%)`; ctx.beginPath(); ctx.arc(x, y, 5, 0, Math.PI * 2); ctx.fill(); // Draw connections between nearby points latentVectors.slice(idx + 1, 10).forEach((otherVector, otherIdx) => { const distance = Math.sqrt(Math.pow(vector[0] - otherVector[0], 2) + Math.pow(vector[1] - otherVector[1], 2)); if (distance < 1.0) { const ox = (otherVector[0] + 2) / 4 * canvas.width; const oy = (otherVector[1] + 2) / 4 * canvas.height; ctx.strokeStyle = `rgba(255, 255, 255, ${0.3 * (1 - distance)})`; ctx.lineWidth = 1; ctx.beginPath(); ctx.moveTo(x, y); ctx.lineTo(ox, oy); ctx.stroke(); } }); }); }, [latentVectors]); // Handle model selection const handleModelSelect = useCallback(modelId => { setCurrentModel(modelId); onModelSelect?.(modelId); play("select"); }, [onModelSelect, play]); // Real-time generation useEffect(() => { if (enableRealTime && model.trained) { const interval = setInterval(() => { if (!isGenerating) { generateImages(); } }, 5000); return () => clearInterval(interval); } }, [enableRealTime, model.trained, isGenerating, generateImages]); // Update latent space visualization useEffect(() => { visualizeLatentSpace(); }, [latentVectors, visualizeLatentSpace]); const ModelSelector = () => jsxs("div", { className: 'space-y-4', children: [jsx("h4", { className: 'glass-text-sm font-medium text-primary/80', children: "GAN Models" }), jsx("div", { className: 'glass-grid glass-grid-cols-1 md:grid-cols-2 glass-gap-3', children: availableModels.map(ganModel => jsx(motion.div, { className: ` p-3 rounded-lg border cursor-pointer transition-all duration-200 ${currentModel === ganModel.id ? "border-blue-400 bg-blue-400/20" : "border-white/20 hover:border-white/40 bg-white/5"} `, whileHover: shouldAnimate ? { scale: 1.01 } : {}, whileTap: shouldAnimate ? { scale: 0.99 } : {}, onClick: () => handleModelSelect(ganModel.id), children: jsxs("div", { className: "glass-flex glass-items-start glass-justify-between", children: [jsxs("div", { className: "glass-flex-1", children: [jsxs("div", { className: 'glass-flex glass-items-center space-x-2 mb-1', children: [jsx("h5", { className: 'glass-text-sm font-medium text-primary/90', children: ganModel.name }), ganModel.trained ? jsx("span", { className: 'glass-px-2 glass-py-0.5 glass-surface-green/20 glass-text-secondary glass-radius glass-text-xs font-medium', children: "Trained" }) : jsx("span", { className: 'glass-px-2 glass-py-0.5 glass-surface-primary/20 glass-text-secondary glass-radius glass-text-xs font-medium', children: "Untrained" })] }), jsx("p", { className: 'glass-text-xs text-primary/60 mb-2', children: ganModel.description }), jsxs("div", { className: "glass-flex glass-items-center glass-justify-between", children: [jsxs("div", { className: 'glass-flex glass-items-center space-x-4 glass-text-xs text-primary/50', children: [jsxs("span", { children: [ganModel.resolution, "px"] }), jsxs("span", { children: ["Z:", ganModel.latentDim] })] }), jsx("span", { className: ` px-2 py-0.5 rounded text-xs font-medium ${ganModel.type === "stylegan" ? "bg-purple-500/20 text-purple-300" : ganModel.type === "dcgan" ? "bg-blue-500/20 text-blue-300" : ganModel.type === "biggan" ? "bg-green-500/20 text-green-300" : ganModel.type === "cyclegan" ? "bg-red-500/20 text-red-300" : "bg-gray-500/20 text-gray-300"} `, children: ganModel.type.toUpperCase() })] })] }), currentModel === ganModel.id && jsx("div", { className: 'text-primary ml-2', children: "\u2713" })] }) }, ganModel.id)) })] }); const GenerationControls = () => jsxs("div", { className: 'space-y-4', children: [jsx("h4", { className: 'glass-text-sm font-medium text-primary/80', children: "Generation Parameters" }), jsxs("div", { className: 'glass-grid glass-grid-cols-1 md:grid-cols-2 glass-gap-4', children: [jsxs("div", { children: [jsxs("label", { className: 'block glass-text-xs text-primary/70 mb-1', children: ["Seed: ", params.seed] }), jsx("input", { type: "range", min: "0", max: "1000", value: params.seed, onChange: e => setParams(prev => ({ ...prev, seed: parseInt(e.target.value) })), className: 'glass-w-full h-2 glass-surface-subtle/20 glass-radius-lg appearance-none cursor-pointer' })] }), jsxs("div", { children: [jsxs("label", { className: 'block glass-text-xs text-primary/70 mb-1', children: ["Truncation: ", params.truncation.toFixed(2)] }), jsx("input", { type: "range", min: "0.1", max: "2.0", step: "0.1", value: params.truncation, onChange: e => setParams(prev => ({ ...prev, truncation: parseFloat(e.target.value) })), className: 'glass-w-full h-2 glass-surface-subtle/20 glass-radius-lg appearance-none cursor-pointer' })] }), jsxs("div", { children: [jsxs("label", { className: 'block glass-text-xs text-primary/70 mb-1', children: ["Style Strength: ", params.styleStrength.toFixed(2)] }), jsx("input", { type: "range", min: "0.0", max: "2.0", step: "0.1", value: params.styleStrength, onChange: e => setParams(prev => ({ ...prev, styleStrength: parseFloat(e.target.value) })), className: 'glass-w-full h-2 glass-surface-subtle/20 glass-radius-lg appearance-none cursor-pointer' })] }), jsxs("div", { children: [jsxs("label", { className: 'block glass-text-xs text-primary/70 mb-1', children: ["Batch Size: ", params.batchSize] }), jsx("input", { type: "range", min: "1", max: "8", value: params.batchSize, onChange: e => setParams(prev => ({ ...prev, batchSize: parseInt(e.target.value) })), className: 'glass-w-full h-2 glass-surface-subtle/20 glass-radius-lg appearance-none cursor-pointer' })] })] })] }); return jsxs(OptimizedGlassCore, { ref: ref, variant: "frosted", className: `p-6 space-y-6 ${className}`, ...props, children: [jsxs("div", { className: "glass-flex glass-items-center glass-justify-between", children: [jsxs("div", { children: [jsx("h3", { className: 'glass-text-xl font-semibold text-primary/90', children: "GAN Generator" }), jsx("p", { className: 'glass-text-sm text-primary/60', children: "Generative Adversarial Networks for image synthesis" })] }), jsxs("div", { className: 'glass-flex glass-items-center space-x-2', children: [enableRealTime && model.trained && jsxs("div", { className: 'glass-flex glass-items-center space-x-1 text-primary', children: [jsx("div", { className: 'w-2 h-2 glass-surface-green glass-radius-full animate-pulse' }), jsx("span", { className: "glass-text-xs", children: "Auto-gen" })] }), isGenerating && jsxs("div", { className: 'glass-flex glass-items-center space-x-1 text-primary', children: [jsx("div", { className: 'w-4 h-4 glass-border-2 glass-border-blue glass-border-t-transparent glass-radius-full animate-spin' }), jsx("span", { className: "glass-text-xs", children: "Generating..." })] }), isTraining && jsxs("div", { className: 'glass-flex glass-items-center space-x-1 text-primary', children: [jsx("div", { className: 'w-4 h-4 glass-border-2 glass-border-orange-400 glass-border-t-transparent glass-radius-full animate-spin' }), jsx("span", { className: "glass-text-xs", children: "Training..." })] })] })] }), generatedImages.length > 0 && jsxs("div", { className: 'space-y-4', children: [jsx("h4", { className: 'glass-text-sm font-medium text-primary/80', children: "Generated Images" }), jsx("div", { className: 'glass-grid glass-grid-cols-2 md:grid-cols-4 lg:grid-cols-6 glass-gap-3', children: generatedImages.slice(0, maxGenerations).map((imageUrl, index) => jsxs(motion.div, { className: 'relative aspect-square glass-radius-lg overflow-hidden glass-surface-subtle/10 group cursor-pointer', whileHover: shouldAnimate ? { scale: 1.05 } : {}, initial: { opacity: 0, scale: 0.8 }, animate: prefersReducedMotion ? {} : { opacity: 1, scale: 1 }, transition: prefersReducedMotion ? { duration: 0 } : { duration: 0.3 }, children: [jsx("img", { src: imageUrl, alt: `Generated ${index + 1}`, className: 'glass-w-full glass-h-full object-cover' }), jsx("div", { className: 'absolute inset-0 glass-surface-dark/50 opacity-0 group-hover:opacity-100 transition-opacity glass-flex glass-items-center glass-justify-center', children: jsx("button", { className: 'glass-p-2 glass-surface-subtle/20 glass-radius-lg text-primary hover:glass-surface-subtle/30 transition-colors', children: jsx("svg", { className: 'w-4 h-4', fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" }) }) }) })] }, index)) })] }), isGenerating && showProgress && jsxs("div", { className: ` p-3 rounded-lg border border-blue-400/30 ${createGlassStyle({ blur: "sm", opacity: 0.8 }).background} `, children: [jsxs("div", { className: 'glass-flex glass-items-center glass-justify-between mb-2', children: [jsx("span", { className: 'glass-text-sm text-primary/80', children: "Generating batch..." }), jsxs("span", { className: 'glass-text-sm font-medium text-primary', children: [Math.round(generationProgress), "%"] })] }), jsx("div", { className: 'glass-w-full glass-surface-subtle/20 glass-radius-full h-2', children: jsx(motion.div, { className: 'glass-surface-blue h-2 glass-radius-full', animate: { width: `${generationProgress}%` }, transition: prefersReducedMotion ? { duration: 0 } : { duration: 0.3 } }) })] }), jsxs("div", { className: 'glass-grid glass-grid-cols-1 lg:grid-cols-2 glass-gap-6', children: [showModelSelector && jsx(ModelSelector, {}), showGenerationControls && jsx(GenerationControls, {})] }), showLatentSpace && latentVectors.length > 0 && jsxs("div", { className: 'space-y-4', children: [jsx("h4", { className: 'glass-text-sm font-medium text-primary/80', children: "Latent Space (2D Projection)" }), jsx("canvas", { ref: latentSpaceCanvasRef, width: 400, height: 300, className: 'glass-w-full max-w-md glass-border glass-border-white/20 glass-radius-lg glass-surface-dark/20' })] }), enableInterpolation && interpolationImages.length > 0 && jsxs("div", { className: 'space-y-4', children: [jsx("h4", { className: 'glass-text-sm font-medium text-primary/80', children: "Latent Interpolation" }), jsx("div", { className: 'glass-flex space-x-2 overflow-x-auto pb-2', children: interpolationImages.map((imageUrl, index) => jsx("div", { className: 'glass-flex-shrink-0 w-16 h-16 glass-radius glass-border glass-border-white/20 overflow-hidden', children: jsx("img", { src: imageUrl, alt: `Interpolation ${index}`, className: 'glass-w-full glass-h-full object-cover' }) }, index)) })] }), isTraining && showTrainingControls && jsxs("div", { className: ` p-3 rounded-lg border border-orange-400/30 ${createGlassStyle({ blur: "sm", opacity: 0.8 }).background} `, children: [jsxs("div", { className: 'glass-flex glass-items-center glass-justify-between mb-2', children: [jsx("span", { className: 'glass-text-sm text-primary/80', children: "Training Model..." }), jsxs("span", { className: 'glass-text-sm font-medium text-primary', children: ["Epoch ", trainingProgress.epoch] })] }), jsxs("div", { className: 'glass-grid glass-grid-cols-2 glass-gap-4 glass-text-xs text-primary/60', children: [jsxs("div", { children: ["Generator Loss: ", trainingProgress.generatorLoss.toFixed(3)] }), jsxs("div", { children: ["Discriminator Loss:", " ", trainingProgress.discriminatorLoss.toFixed(3)] })] })] }), jsxs("div", { className: 'glass-flex glass-items-center glass-justify-between pt-4 glass-border-t glass-border-white/10', children: [jsxs("div", { className: 'glass-flex glass-items-center space-x-4', children: [jsx(motion.button, { className: 'glass-px-4 glass-py-2 glass-surface-blue hover:glass-surface-blue text-primary glass-radius-lg glass-text-sm font-medium transition-colors disabled:opacity-50', whileHover: shouldAnimate ? { scale: 1.02 } : {}, whileTap: shouldAnimate ? { scale: 0.98 } : {}, onClick: generateImages, disabled: isGenerating || !model.trained, children: isGenerating ? "Generating..." : "Generate Images" }), enableInterpolation && latentVectors.length >= 2 && jsx(motion.button, { className: 'glass-px-4 glass-py-2 glass-border glass-border-white/30 hover:border-white/50 text-primary/80 glass-radius-lg glass-text-sm transition-colors', whileHover: shouldAnimate ? { scale: 1.02 } : {}, whileTap: shouldAnimate ? { scale: 0.98 } : {}, onClick: generateInterpolation, children: "Interpolate" }), showTrainingControls && !model.trained && jsx(motion.button, { className: 'glass-px-4 glass-py-2 glass-surface-primary hover:glass-surface-primary text-primary glass-radius-lg glass-text-sm font-medium transition-colors disabled:opacity-50', whileHover: shouldAnimate ? { scale: 1.02 } : {}, whileTap: shouldAnimate ? { scale: 0.98 } : {}, onClick: trainModel, disabled: isTraining, children: isTraining ? "Training..." : "Train Model" })] }), jsxs("div", { className: 'glass-flex glass-items-center space-x-2 glass-text-xs text-primary/60', children: [jsxs("span", { children: ["Generated: ", generatedImages.length] }), jsx("span", { children: "\u2022" }), jsxs("span", { children: ["Model: ", model.name] })] })] })] }); }); GlassGANGenerator.displayName = "GlassGANGenerator"; export { GlassGANGenerator }; //# sourceMappingURL=GlassGANGenerator.js.map