aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
620 lines (617 loc) • 25.5 kB
JavaScript
'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 defaultPromptSuggestions = [{
id: "cosmic-abstract",
text: "Cosmic abstract painting with swirling galaxies and nebulae in vibrant colors",
style: "abstract expressionism",
category: "abstract",
tags: ["space", "cosmic", "vibrant", "swirling"]
}, {
id: "cyberpunk-city",
text: "Cyberpunk cityscape at night with neon lights reflecting on wet streets",
style: "digital art",
category: "architecture",
tags: ["cyberpunk", "neon", "city", "night"]
}, {
id: "ethereal-portrait",
text: "Ethereal portrait of a person made of flowing light and energy",
style: "digital painting",
category: "portrait",
tags: ["ethereal", "light", "energy", "flowing"]
}, {
id: "surreal-landscape",
text: "Surreal landscape with floating islands and impossible waterfalls",
style: "surrealism",
category: "landscape",
tags: ["surreal", "floating", "impossible", "waterfalls"]
}, {
id: "biomech-fusion",
text: "Biomechanical fusion of organic forms and technological components",
style: "biomechanical art",
category: "surreal",
tags: ["biomechanical", "organic", "technology", "fusion"]
}];
const stylePresets = ["photorealistic", "oil painting", "watercolor", "digital art", "abstract expressionism", "impressionism", "cyberpunk", "steampunk", "art nouveau", "minimalist", "baroque", "surrealism"];
const GlassGenerativeArt = /*#__PURE__*/forwardRef(({
prompt = "",
suggestions = defaultPromptSuggestions,
generationSettings = {},
showPromptLibrary = true,
showAdvancedSettings = true,
showGenerationHistory = true,
enableIterativeGeneration = true,
enablePromptEnhancement = true,
enableStyleMixing = false,
realTimeGeneration = false,
onPromptChange,
onGenerate,
onImageGenerated,
className = "",
...props
}, ref) => {
const prefersReducedMotion = useReducedMotion();
const [currentPrompt, setCurrentPrompt] = useState(prompt);
const [isGenerating, setIsGenerating] = useState(false);
const [generationProgress, setGenerationProgress] = useState(0);
const [enablePromptEnhancementState, setEnablePromptEnhancementState] = useState(enablePromptEnhancement);
const [generatedImages, setGeneratedImages] = useState([]);
const [generationHistory, setGenerationHistory] = useState([]);
const [settings, setSettings] = useState({
model: "stable-diffusion",
style: "photorealistic",
resolution: "768x768",
steps: 25,
guidance: 7.5,
iterations: 1,
...generationSettings
});
const canvasRef = useRef(null);
useA11yId("glass-generative-art");
const {
shouldAnimate
} = useMotionPreference();
const {
play
} = useGlassSound();
// Enhanced prompt generation
const enhancePrompt = useCallback(basePrompt => {
const enhancementPhrases = ["highly detailed", "professional quality", "studio lighting", "8k resolution", "masterpiece", "trending on artstation", "photorealistic", "cinematic composition"];
const randomEnhancements = enhancementPhrases.sort(() => Math.random() - 0.5).slice(0, 3).join(", ");
return `${basePrompt}, ${randomEnhancements}, ${settings.style} style`;
}, [settings.style]);
// Generate art simulation
const generateArt = useCallback(async promptText => {
if (!promptText.trim()) return;
setIsGenerating(true);
setGenerationProgress(0);
play("processing");
const enhancedPrompt = enablePromptEnhancement ? enhancePrompt(promptText) : promptText;
// Simulate generation steps
const steps = [{
label: "Initializing model...",
duration: 300
}, {
label: "Processing prompt...",
duration: 500
}, {
label: "Generating base composition...",
duration: 1000
}, {
label: "Adding details...",
duration: 1500
}, {
label: "Applying style...",
duration: 800
}, {
label: "Refining image...",
duration: 700
}, {
label: "Finalizing...",
duration: 400
}];
for (let i = 0; i < steps.length; i++) {
await new Promise(resolve => setTimeout(resolve, steps[i].duration));
setGenerationProgress((i + 1) / steps.length * 100);
}
// Generate multiple iterations if requested
const newImages = [];
for (let i = 0; i < settings.iterations; i++) {
const canvas = canvasRef.current;
if (canvas) {
const ctx = canvas.getContext("2d");
if (ctx) {
// Create unique generative art based on prompt
canvas.width = 512;
canvas.height = 512;
// Background based on prompt category
const promptLower = promptText.toLowerCase();
let bgGradient;
if (promptLower.includes("space") || promptLower.includes("cosmic")) {
bgGradient = ctx.createRadialGradient(256, 256, 0, 256, 256, 400);
bgGradient.addColorStop(0, "#1a1a2e");
bgGradient.addColorStop(0.5, "#16213e");
bgGradient.addColorStop(1, "#0f3460");
} else if (promptLower.includes("cyberpunk") || promptLower.includes("neon")) {
bgGradient = ctx.createLinearGradient(0, 0, 512, 512);
bgGradient.addColorStop(0, "#0a0a0a");
bgGradient.addColorStop(0.5, "#1a0f2e");
bgGradient.addColorStop(1, "#2d1b69");
} else if (promptLower.includes("nature") || promptLower.includes("landscape")) {
bgGradient = ctx.createLinearGradient(0, 0, 0, 512);
bgGradient.addColorStop(0, "#87ceeb");
bgGradient.addColorStop(0.7, "#98fb98");
bgGradient.addColorStop(1, "#228b22");
} else {
bgGradient = ctx.createLinearGradient(0, 0, 512, 512);
bgGradient.addColorStop(0, "#667eea");
bgGradient.addColorStop(1, "#764ba2");
}
ctx.fillStyle = bgGradient;
ctx.fillRect(0, 0, 512, 512);
// Add generative elements based on style
if (settings.style.includes("abstract")) {
// Abstract shapes and forms
for (let j = 0; j < 15; j++) {
ctx.save();
ctx.translate(Math.random() * 512, Math.random() * 512);
ctx.rotate(Math.random() * Math.PI * 2);
const gradient = ctx.createRadialGradient(0, 0, 0, 0, 0, 50 + Math.random() * 100);
gradient.addColorStop(0, `hsla(${Math.random() * 360}, 70%, 60%, 0.8)`);
gradient.addColorStop(1, `hsla(${Math.random() * 360}, 50%, 40%, 0.3)`);
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.ellipse(0, 0, Math.random() * 80 + 20, Math.random() * 120 + 30, 0, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
} else if (settings.style.includes("geometric")) {
// Geometric patterns
for (let j = 0; j < 20; j++) {
ctx.save();
ctx.translate(Math.random() * 512, Math.random() * 512);
ctx.rotate(Math.random() * Math.PI * 2);
ctx.strokeStyle = `hsla(${Math.random() * 360}, 60%, 50%, 0.7)`;
ctx.lineWidth = Math.random() * 3 + 1;
ctx.beginPath();
const size = Math.random() * 60 + 20;
ctx.rect(-size / 2, -size / 2, size, size);
ctx.stroke();
ctx.restore();
}
} else if (promptLower.includes("particle") || settings.style.includes("digital")) {
// Particle effects
for (let j = 0; j < 100; j++) {
ctx.fillStyle = `hsla(${Math.random() * 60 + 180}, 80%, 60%, ${Math.random() * 0.8 + 0.2})`;
ctx.beginPath();
ctx.arc(Math.random() * 512, Math.random() * 512, Math.random() * 3 + 1, 0, Math.PI * 2);
ctx.fill();
}
}
// Add texture overlay
const imageData = ctx.getImageData(0, 0, 512, 512);
const data = imageData.data;
for (let j = 0; j < data.length; j += 4) {
const noise = (Math.random() - 0.5) * 20;
data[j] = Math.max(0, Math.min(255, data[j] + noise));
data[j + 1] = Math.max(0, Math.min(255, data[j + 1] + noise));
data[j + 2] = Math.max(0, Math.min(255, data[j + 2] + noise));
}
ctx.putImageData(imageData, 0, 0);
const imageUrl = canvas.toDataURL("image/png");
newImages.push(imageUrl);
}
}
}
setGeneratedImages(prev => [...newImages, ...prev].slice(0, 12));
// Add to history
const historyEntry = {
id: Date.now().toString(),
prompt: enhancedPrompt,
imageUrl: newImages[0] || "",
settings: {
...settings
},
timestamp: Date.now()
};
setGenerationHistory(prev => [historyEntry, ...prev].slice(0, 20));
setIsGenerating(false);
play("success");
if (newImages[0]) {
onImageGenerated?.(newImages[0], historyEntry);
}
}, [settings, enablePromptEnhancement, enhancePrompt, onImageGenerated, play]);
// Handle prompt changes
useEffect(() => {
if (realTimeGeneration && currentPrompt && currentPrompt !== prompt) {
const debounceTimer = setTimeout(() => {
generateArt(currentPrompt);
}, 2000);
return () => clearTimeout(debounceTimer);
}
}, [currentPrompt, realTimeGeneration, generateArt, prompt]);
const PromptLibrary = () => jsxs("div", {
className: 'space-y-4',
children: [jsx("h4", {
className: 'glass-text-sm font-medium text-primary/80',
children: "Prompt Library"
}), jsx("div", {
className: "glass-grid glass-gap-2",
children: suggestions.map(suggestion => jsx(motion.div, {
className: 'glass-p-3 glass-radius-lg glass-border glass-border-white/20 hover:border-white/40 glass-surface-subtle/5 cursor-pointer transition-colors',
whileHover: shouldAnimate ? {
scale: 1.01
} : {},
onClick: () => {
setCurrentPrompt(suggestion.text);
onPromptChange?.(suggestion.text);
play("select");
},
children: jsx("div", {
className: "glass-flex glass-items-start glass-justify-between",
children: jsxs("div", {
className: "glass-flex-1",
children: [jsx("p", {
className: 'glass-text-sm text-primary/90 mb-1',
children: suggestion.text
}), jsxs("div", {
className: 'glass-flex glass-items-center space-x-2',
children: [jsx("span", {
className: `
px-2 py-0.5 rounded text-xs font-medium
${suggestion.category === "abstract" ? "bg-purple-500/20 text-purple-300" : suggestion.category === "landscape" ? "bg-green-500/20 text-green-300" : suggestion.category === "portrait" ? "bg-blue-500/20 text-blue-300" : suggestion.category === "architecture" ? "bg-gray-500/20 text-gray-300" : suggestion.category === "nature" ? "bg-emerald-500/20 text-emerald-300" : "bg-pink-500/20 text-pink-300"}
`,
children: suggestion.category
}), suggestion.tags.slice(0, 2).map(tag => jsx("span", {
className: 'glass-px-1.5 glass-py-0.5 glass-surface-subtle/10 text-primary/60 glass-radius glass-text-xs',
children: tag
}, tag))]
})]
})
})
}, suggestion.id))
})]
});
const AdvancedSettings = () => jsxs("div", {
className: 'space-y-4',
children: [jsx("h4", {
className: 'glass-text-sm font-medium text-primary/80',
children: "Generation Settings"
}), jsxs("div", {
className: 'glass-grid glass-grid-cols-1 md:grid-cols-2 glass-gap-4',
children: [jsxs("div", {
children: [jsx("label", {
className: 'block glass-text-xs text-primary/70 mb-2',
children: "Model"
}), jsxs("select", {
value: settings.model,
onChange: e => setSettings(prev => ({
...prev,
model: e.target.value
})),
className: 'glass-w-full glass-p-2 glass-surface-subtle/10 glass-border glass-border-white/20 glass-radius-lg text-primary/90 glass-text-sm',
children: [jsx("option", {
value: "stable-diffusion",
children: "Stable Diffusion"
}), jsx("option", {
value: "midjourney",
children: "Midjourney"
}), jsx("option", {
value: "dall-e",
children: "DALL-E"
}), jsx("option", {
value: "custom",
children: "Custom Model"
})]
})]
}), jsxs("div", {
children: [jsx("label", {
className: 'block glass-text-xs text-primary/70 mb-2',
children: "Style"
}), jsx("select", {
value: settings.style,
onChange: e => setSettings(prev => ({
...prev,
style: e.target.value
})),
className: 'glass-w-full glass-p-2 glass-surface-subtle/10 glass-border glass-border-white/20 glass-radius-lg text-primary/90 glass-text-sm',
children: stylePresets.map(style => jsx("option", {
value: style,
children: style.charAt(0).toUpperCase() + style.slice(1)
}, style))
})]
}), jsxs("div", {
children: [jsx("label", {
className: 'block glass-text-xs text-primary/70 mb-2',
children: "Resolution"
}), jsxs("select", {
value: settings.resolution,
onChange: e => setSettings(prev => ({
...prev,
resolution: e.target.value
})),
className: 'glass-w-full glass-p-2 glass-surface-subtle/10 glass-border glass-border-white/20 glass-radius-lg text-primary/90 glass-text-sm',
children: [jsx("option", {
value: "512x512",
children: "512 \u00D7 512"
}), jsx("option", {
value: "768x768",
children: "768 \u00D7 768"
}), jsx("option", {
value: "1024x1024",
children: "1024 \u00D7 1024"
}), jsx("option", {
value: "1920x1080",
children: "1920 \u00D7 1080"
})]
})]
}), jsxs("div", {
children: [jsxs("label", {
className: 'block glass-text-xs text-primary/70 mb-2',
children: ["Iterations: ", settings.iterations]
}), jsx("input", {
type: "range",
min: "1",
max: "4",
value: settings.iterations,
onChange: e => setSettings(prev => ({
...prev,
iterations: 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-2',
children: ["Steps: ", settings.steps]
}), jsx("input", {
type: "range",
min: "10",
max: "50",
value: settings.steps,
onChange: e => setSettings(prev => ({
...prev,
steps: 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-2',
children: ["Guidance: ", settings.guidance]
}), jsx("input", {
type: "range",
min: "1",
max: "20",
step: "0.5",
value: settings.guidance,
onChange: e => setSettings(prev => ({
...prev,
guidance: parseFloat(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: "AI Art Generator"
}), jsx("p", {
className: 'glass-text-sm text-primary/60',
children: "Create stunning AI-generated artwork from text prompts"
})]
}), jsxs("div", {
className: 'glass-flex glass-items-center space-x-2',
children: [realTimeGeneration && 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: "Live"
})]
}), 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"
})]
})]
})]
}), jsxs("div", {
className: 'space-y-4',
children: [jsxs("div", {
children: [jsx("label", {
className: 'block glass-text-sm font-medium text-primary/80 mb-2',
children: "Describe your artwork"
}), jsx("textarea", {
value: currentPrompt,
onChange: e => {
setCurrentPrompt(e.target.value);
onPromptChange?.(e.target.value);
},
placeholder: "A majestic dragon soaring through a cosmic nebula, digital art style, highly detailed...",
className: 'glass-w-full h-24 glass-p-3 glass-surface-subtle/10 glass-border glass-border-white/20 glass-radius-lg text-primary/90 placeholder-white/50 resize-none focus:outline-none focus:border-blue'
})]
}), jsxs("div", {
className: "glass-flex glass-items-center glass-justify-between",
children: [jsx("div", {
className: 'glass-flex glass-items-center space-x-4',
children: jsxs("label", {
className: 'glass-flex glass-items-center space-x-2 cursor-pointer',
children: [jsx("input", {
type: "checkbox",
checked: enablePromptEnhancementState,
onChange: e => setEnablePromptEnhancementState(e.target.checked),
className: 'w-4 h-4 glass-radius glass-border-white/30'
}), jsx("span", {
className: 'glass-text-sm text-primary/80',
children: "Enhance Prompt"
})]
})
}), jsx(motion.button, {
className: 'glass-px-6 glass-py-2 glass-surface-blue hover:glass-surface-blue text-primary glass-radius-lg font-medium transition-colors disabled:opacity-50',
whileHover: shouldAnimate ? {
scale: 1.02
} : {},
whileTap: shouldAnimate ? {
scale: 0.98
} : {},
onClick: () => generateArt(currentPrompt),
disabled: isGenerating || !currentPrompt.trim(),
children: isGenerating ? "Generating..." : "Generate Art"
})]
})]
}), isGenerating && 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 artwork..."
}), 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
}
})
})]
}), generatedImages.length > 0 && jsxs("div", {
className: 'space-y-4',
children: [jsx("h4", {
className: 'glass-text-sm font-medium text-primary/80',
children: "Generated Artwork"
}), jsx("div", {
className: 'glass-grid glass-grid-cols-2 md:grid-cols-3 lg:grid-cols-4 glass-gap-4',
children: generatedImages.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.02
} : {},
initial: {
opacity: 0,
scale: 0.9
},
animate: prefersReducedMotion ? {} : {
opacity: 1,
scale: 1
},
transition: prefersReducedMotion ? {
duration: 0
} : {
duration: 0.3
},
children: [jsx("img", {
src: imageUrl,
alt: `Generated art ${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-5 h-5',
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))
})]
}), jsxs("div", {
className: 'glass-grid glass-grid-cols-1 lg:grid-cols-2 glass-gap-6',
children: [showPromptLibrary && jsx(PromptLibrary, {}), showAdvancedSettings && jsx(AdvancedSettings, {})]
}), showGenerationHistory && generationHistory.length > 0 && jsxs("div", {
className: 'space-y-4',
children: [jsx("h4", {
className: 'glass-text-sm font-medium text-primary/80',
children: "Recent Generations"
}), jsx("div", {
className: 'space-y-2 glass-max-h-64 overflow-y-auto',
children: generationHistory.map(entry => jsxs("div", {
className: 'glass-flex glass-items-center space-x-3 glass-p-2 glass-radius-lg glass-surface-subtle/5 hover:glass-surface-subtle/10 cursor-pointer transition-colors',
onClick: () => {
setCurrentPrompt(entry.prompt);
onPromptChange?.(entry.prompt);
},
children: [jsx("img", {
src: entry.imageUrl,
alt: "Generated",
className: 'w-12 h-12 glass-radius object-cover'
}), jsxs("div", {
className: "glass-flex-1 glass-min-w-0",
children: [jsx("p", {
className: 'glass-text-sm text-primary/90 truncate',
children: entry.prompt
}), jsxs("div", {
className: 'glass-flex glass-items-center space-x-2 mt-1',
children: [jsx("span", {
className: 'glass-text-xs text-primary/60',
children: entry.settings.model
}), jsx("span", {
className: 'glass-text-xs text-primary/60',
children: entry.settings.resolution
}), jsx("span", {
className: 'glass-text-xs text-primary/60',
children: new Date(entry.timestamp).toLocaleDateString()
})]
})]
})]
}, entry.id))
})]
}), jsx("canvas", {
ref: canvasRef,
className: 'hidden'
})]
});
});
GlassGenerativeArt.displayName = "GlassGenerativeArt";
export { GlassGenerativeArt };
//# sourceMappingURL=GlassGenerativeArt.js.map