UNPKG

aura-glass

Version:

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

885 lines (882 loc) 31.5 kB
'use client'; import { jsxs, jsx } from 'react/jsx-runtime'; import { useReducedMotion } from '../../hooks/useReducedMotion.js'; import { forwardRef, useState, useRef, useCallback, useEffect } from 'react'; import { motion } from 'framer-motion'; 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 { useMotionPreference } from '../../hooks/useMotionPreference.js'; import { useGlassSound } from '../../utils/soundDesign.js'; const defaultFilters = [{ id: "grayscale", name: "Grayscale", description: "Convert to black and white", category: "color", intensity: 1.0, parameters: { strength: 1.0 } }, { id: "sepia", name: "Sepia", description: "Vintage sepia tone effect", category: "vintage", intensity: 0.8, parameters: { warmth: 0.8 } }, { id: "blur", name: "Gaussian Blur", description: "Smooth blur effect", category: "blur", intensity: 1.0, parameters: { radius: 5 } }, { id: "sharpen", name: "Sharpen", description: "Enhance image details", category: "artistic", intensity: 0.6, parameters: { amount: 0.6 } }, { id: "brightness", name: "Brightness", description: "Adjust image brightness", category: "color", intensity: 1.2, parameters: { level: 1.2 } }, { id: "contrast", name: "Contrast", description: "Adjust image contrast", category: "color", intensity: 1.3, parameters: { level: 1.3 } }, { id: "saturation", name: "Saturation", description: "Adjust color saturation", category: "color", intensity: 1.5, parameters: { level: 1.5 } }, { id: "hue-shift", name: "Hue Shift", description: "Shift color hues", category: "color", intensity: 0.5, parameters: { degrees: 30 } }, { id: "edge-detect", name: "Edge Detection", description: "Detect and highlight edges", category: "artistic", intensity: 1.0, parameters: { threshold: 0.5 } }, { id: "emboss", name: "Emboss", description: "3D embossed effect", category: "artistic", intensity: 0.8, parameters: { strength: 0.8 } }, { id: "vintage", name: "Vintage Film", description: "Old film camera effect", category: "vintage", intensity: 0.9, parameters: { grain: 0.3, vignette: 0.5 } }, { id: "neon", name: "Neon Glow", description: "Cyberpunk neon effect", category: "modern", intensity: 1.2, parameters: { glow: 1.2, color: "#ff00ff" } }]; const defaultProcessingSettings = { quality: "medium", fps: 30, enableGPU: true, batchSize: 4 }; const GlassLiveFilter = /*#__PURE__*/forwardRef(({ videoSource, imageSource, availableFilters = defaultFilters, selectedFilters = [], processingSettings = {}, showFilterLibrary = true, showPreview = true, showControls = true, enableRealTimeProcessing = true, enableChaining = true, enableCustomFilters = false, maxFilters = 5, canvasWidth = 800, canvasHeight = 600, onFilterApply, onProcessingComplete, onError, className = "", ...props }, ref) => { useReducedMotion(); const [isProcessing, setIsProcessing] = useState(false); const [activeFilters, setActiveFilters] = useState(selectedFilters); const [filterParameters, setFilterParameters] = useState({}); const [processedImageUrl, setProcessedImageUrl] = useState(""); const [originalImageUrl, setOriginalImageUrl] = useState(imageSource || ""); useState(0); const [settings, setSettings] = useState({ ...defaultProcessingSettings, ...processingSettings }); const canvasRef = useRef(null); const videoRef = useRef(null); const animationFrameRef = useRef(); const processedCanvasRef = useRef(null); useA11yId("glass-live-filter"); const { shouldAnimate } = useMotionPreference(); const { play } = useGlassSound(); // Initialize video stream const initializeVideo = useCallback(async () => { const video = videoRef.current; if (!video) return; try { if (videoSource instanceof MediaStream) { video.srcObject = videoSource; } else if (typeof videoSource === "string") { video.src = videoSource; } await video.play(); } catch (error) { onError?.(error); } }, [videoSource, onError]); // Apply filters to image data const applyFilters = useCallback((imageData, filters) => { let processedData = new ImageData(new Uint8ClampedArray(imageData.data), imageData.width, imageData.height); filters.forEach(filter => { const params = filterParameters[filter.id] || filter.parameters || {}; switch (filter.id) { case "grayscale": processedData = applyGrayscale(processedData, params.strength || 1.0); break; case "sepia": processedData = applySepia(processedData, params.warmth || 0.8); break; case "blur": // Simplified blur - in production would use proper convolution processedData = applyBlur(processedData, params.radius || 5); break; case "brightness": processedData = applyBrightness(processedData, params.level || 1.2); break; case "contrast": processedData = applyContrast(processedData, params.level || 1.3); break; case "saturation": processedData = applySaturation(processedData, params.level || 1.5); break; case "hue-shift": processedData = applyHueShift(processedData, params.degrees || 30); break; case "edge-detect": processedData = applyEdgeDetection(processedData, params.threshold || 0.5); break; case "emboss": processedData = applyEmboss(processedData, params.strength || 0.8); break; case "vintage": processedData = applyVintage(processedData, params); break; case "neon": processedData = applyNeonGlow(processedData, params); break; } }); return processedData; }, [filterParameters]); // Filter implementations const applyGrayscale = (imageData, strength) => { const data = imageData.data; for (let i = 0; i < data.length; i += 4) { const gray = Math.round(0.299 * data[i] + 0.587 * data[i + 1] + 0.114 * data[i + 2]); data[i] = data[i] + (gray - data[i]) * strength; data[i + 1] = data[i + 1] + (gray - data[i + 1]) * strength; data[i + 2] = data[i + 2] + (gray - data[i + 2]) * strength; } return imageData; }; const applySepia = (imageData, warmth) => { const data = imageData.data; for (let i = 0; i < data.length; i += 4) { const r = data[i]; const g = data[i + 1]; const b = data[i + 2]; const tr = Math.min(255, r * 0.393 + g * 0.769 + b * 0.189); const tg = Math.min(255, r * 0.349 + g * 0.686 + b * 0.168); const tb = Math.min(255, r * 0.272 + g * 0.534 + b * 0.131); data[i] = r + (tr - r) * warmth; data[i + 1] = g + (tg - g) * warmth; data[i + 2] = b + (tb - b) * warmth; } return imageData; }; const applyBlur = (imageData, radius) => { // Simplified box blur - in production would use Gaussian const data = imageData.data; const width = imageData.width; const height = imageData.height; const output = new Uint8ClampedArray(data); const blurRadius = Math.floor(radius); for (let y = blurRadius; y < height - blurRadius; y++) { for (let x = blurRadius; x < width - blurRadius; x++) { let r = 0, g = 0, b = 0, a = 0; let count = 0; for (let dy = -blurRadius; dy <= blurRadius; dy++) { for (let dx = -blurRadius; dx <= blurRadius; dx++) { const idx = ((y + dy) * width + (x + dx)) * 4; r += data[idx]; g += data[idx + 1]; b += data[idx + 2]; a += data[idx + 3]; count++; } } const idx = (y * width + x) * 4; output[idx] = r / count; output[idx + 1] = g / count; output[idx + 2] = b / count; output[idx + 3] = a / count; } } return new ImageData(output, width, height); }; const applyBrightness = (imageData, level) => { const data = imageData.data; const factor = level; for (let i = 0; i < data.length; i += 4) { data[i] = Math.min(255, data[i] * factor); data[i + 1] = Math.min(255, data[i + 1] * factor); data[i + 2] = Math.min(255, data[i + 2] * factor); } return imageData; }; const applyContrast = (imageData, level) => { const data = imageData.data; const factor = level; const intercept = 128 * (1 - factor); for (let i = 0; i < data.length; i += 4) { data[i] = Math.max(0, Math.min(255, data[i] * factor + intercept)); data[i + 1] = Math.max(0, Math.min(255, data[i + 1] * factor + intercept)); data[i + 2] = Math.max(0, Math.min(255, data[i + 2] * factor + intercept)); } return imageData; }; const applySaturation = (imageData, level) => { const data = imageData.data; for (let i = 0; i < data.length; i += 4) { const gray = 0.299 * data[i] + 0.587 * data[i + 1] + 0.114 * data[i + 2]; data[i] = gray + (data[i] - gray) * level; data[i + 1] = gray + (data[i + 1] - gray) * level; data[i + 2] = gray + (data[i + 2] - gray) * level; } return imageData; }; const applyHueShift = (imageData, degrees) => { const data = imageData.data; for (let i = 0; i < data.length; i += 4) { const r = data[i] / 255; const g = data[i + 1] / 255; const b = data[i + 2] / 255; // Convert RGB to HSL, shift hue, convert back const max = Math.max(r, g, b); const min = Math.min(r, g, b); const diff = max - min; if (diff === 0) continue; let h = 0; if (max === r) h = (g - b) / diff % 6;else if (max === g) h = (b - r) / diff + 2;else h = (r - g) / diff + 4; h = (h * 60 + degrees) % 360; if (h < 0) h += 360; const l = (max + min) / 2; const s = diff / (1 - Math.abs(2 * l - 1)); // Convert back to RGB const c = (1 - Math.abs(2 * l - 1)) * s; const x = c * (1 - Math.abs(h / 60 % 2 - 1)); const m = l - c / 2; let nr = 0, ng = 0, nb = 0; if (h < 60) { nr = c; ng = x; nb = 0; } else if (h < 120) { nr = x; ng = c; nb = 0; } else if (h < 180) { nr = 0; ng = c; nb = x; } else if (h < 240) { nr = 0; ng = x; nb = c; } else if (h < 300) { nr = x; ng = 0; nb = c; } else { nr = c; ng = 0; nb = x; } data[i] = Math.round((nr + m) * 255); data[i + 1] = Math.round((ng + m) * 255); data[i + 2] = Math.round((nb + m) * 255); } return imageData; }; const applyEdgeDetection = (imageData, threshold) => { const data = imageData.data; const width = imageData.width; const height = imageData.height; const output = new Uint8ClampedArray(data.length); // Sobel edge detection for (let y = 1; y < height - 1; y++) { for (let x = 1; x < width - 1; x++) { const idx = (y * width + x) * 4; let gx = 0, gy = 0; for (let dy = -1; dy <= 1; dy++) { for (let dx = -1; dx <= 1; dx++) { const pixelIdx = ((y + dy) * width + (x + dx)) * 4; const gray = 0.299 * data[pixelIdx] + 0.587 * data[pixelIdx + 1] + 0.114 * data[pixelIdx + 2]; // Sobel kernels const sobelX = [[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]]; const sobelY = [[-1, -2, -1], [0, 0, 0], [1, 2, 1]]; gx += gray * sobelX[dy + 1][dx + 1]; gy += gray * sobelY[dy + 1][dx + 1]; } } const magnitude = Math.sqrt(gx * gx + gy * gy); const edge = magnitude > threshold * 255 ? 255 : 0; output[idx] = edge; output[idx + 1] = edge; output[idx + 2] = edge; output[idx + 3] = data[idx + 3]; } } return new ImageData(output, width, height); }; const applyEmboss = (imageData, strength) => { const data = imageData.data; const width = imageData.width; const height = imageData.height; const output = new Uint8ClampedArray(data); // Emboss kernel const kernel = [[-2, -1, 0], [-1, 1, 1], [0, 1, 2]]; for (let y = 1; y < height - 1; y++) { for (let x = 1; x < width - 1; x++) { let r = 0, g = 0, b = 0; for (let dy = -1; dy <= 1; dy++) { for (let dx = -1; dx <= 1; dx++) { const pixelIdx = ((y + dy) * width + (x + dx)) * 4; const weight = kernel[dy + 1][dx + 1]; r += data[pixelIdx] * weight; g += data[pixelIdx + 1] * weight; b += data[pixelIdx + 2] * weight; } } const idx = (y * width + x) * 4; output[idx] = Math.max(0, Math.min(255, r * strength + 128)); output[idx + 1] = Math.max(0, Math.min(255, g * strength + 128)); output[idx + 2] = Math.max(0, Math.min(255, b * strength + 128)); } } return new ImageData(output, width, height); }; const applyVintage = (imageData, params) => { let result = imageData; // Apply sepia first result = applySepia(result, 0.7); // Add grain const grain = params.grain || 0.3; const data = result.data; for (let i = 0; i < data.length; i += 4) { const noise = (Math.random() - 0.5) * grain * 255; data[i] = Math.max(0, Math.min(255, data[i] + noise)); data[i + 1] = Math.max(0, Math.min(255, data[i + 1] + noise)); data[i + 2] = Math.max(0, Math.min(255, data[i + 2] + noise)); } return result; }; const applyNeonGlow = (imageData, params) => { const data = imageData.data; const glow = params.glow || 1.2; // Enhance bright colors and add glow effect for (let i = 0; i < data.length; i += 4) { const brightness = (data[i] + data[i + 1] + data[i + 2]) / 3; if (brightness > 128) { data[i] = Math.min(255, data[i] * glow); data[i + 1] = Math.min(255, data[i + 1] * glow); data[i + 2] = Math.min(255, data[i + 2] * glow); } } return imageData; }; // Process image/video frame const processFrame = useCallback(() => { const canvas = canvasRef.current; const processedCanvas = processedCanvasRef.current; const video = videoRef.current; if (!canvas || !processedCanvas) return; const ctx = canvas.getContext("2d"); const processedCtx = processedCanvas.getContext("2d"); if (!ctx || !processedCtx) return; let imageData = null; // Get source image data if (video && !video.paused) { ctx.drawImage(video, 0, 0, canvas.width, canvas.height); imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); } else if (originalImageUrl) { const img = new Image(); img.onload = () => { ctx.drawImage(img, 0, 0, canvas.width, canvas.height); const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height); processImageData(imgData, processedCtx); }; img.src = originalImageUrl; return; } if (imageData) { processImageData(imageData, processedCtx); } if (enableRealTimeProcessing && video && !video.paused) { animationFrameRef.current = requestAnimationFrame(processFrame); } }, [activeFilters, enableRealTimeProcessing, originalImageUrl]); const processImageData = (imageData, ctx) => { const activeFilterObjects = availableFilters.filter(f => activeFilters.includes(f.id)); if (activeFilterObjects.length === 0) { ctx.putImageData(imageData, 0, 0); return; } setIsProcessing(true); // Process filters const processedData = applyFilters(imageData, activeFilterObjects); ctx.putImageData(processedData, 0, 0); const processedUrl = ctx.canvas.toDataURL(); setProcessedImageUrl(processedUrl); onProcessingComplete?.(processedUrl); setIsProcessing(false); }; // Filter management const addFilter = useCallback(filterId => { if (activeFilters.length >= maxFilters) { play("error"); return; } setActiveFilters(prev => [...prev, filterId]); const filter = availableFilters.find(f => f.id === filterId); if (filter) { setFilterParameters(prev => ({ ...prev, [filterId]: { ...filter.parameters } })); onFilterApply?.(filterId, filter.parameters); play("select"); } }, [activeFilters, maxFilters, availableFilters, onFilterApply, play]); const removeFilter = useCallback(filterId => { setActiveFilters(prev => prev.filter(id => id !== filterId)); setFilterParameters(prev => { const { [filterId]: removed, ...rest } = prev; return rest; }); play("remove"); }, [play]); const updateFilterParameter = useCallback((filterId, paramName, value) => { setFilterParameters(prev => ({ ...prev, [filterId]: { ...prev[filterId], [paramName]: value } })); }, []); // Initialize useEffect(() => { if (videoSource) { initializeVideo(); } return () => { if (animationFrameRef.current) { cancelAnimationFrame(animationFrameRef.current); } }; }, [videoSource, initializeVideo]); useEffect(() => { processFrame(); }, [activeFilters, filterParameters, processFrame]); const FilterLibrary = () => jsxs("div", { className: 'space-y-4', children: [jsx("h4", { className: 'glass-text-sm font-medium text-primary/80', children: "Filter Library" }), jsx("div", { className: 'glass-grid glass-grid-cols-2 md:grid-cols-3 lg:grid-cols-4 glass-gap-3', children: availableFilters.map(filter => jsxs(motion.div, { className: ` p-3 rounded-lg border cursor-pointer transition-all duration-200 ${activeFilters.includes(filter.id) ? "border-blue-400 bg-blue-400/20" : "border-white/20 hover:border-white/40 bg-white/5"} `, whileHover: shouldAnimate ? { scale: 1.02 } : {}, whileTap: shouldAnimate ? { scale: 0.98 } : {}, onClick: () => { if (activeFilters.includes(filter.id)) { removeFilter(filter.id); } else { addFilter(filter.id); } }, children: [jsx("div", { className: 'glass-text-sm font-medium text-primary/90 mb-1', children: filter.name }), jsx("div", { className: 'glass-text-xs text-primary/60 mb-2', children: filter.description }), jsxs("div", { className: "glass-flex glass-items-center glass-justify-between", children: [jsx("span", { className: ` px-2 py-0.5 rounded text-xs font-medium ${filter.category === "artistic" ? "bg-purple-500/20 text-purple-300" : filter.category === "color" ? "bg-blue-500/20 text-blue-300" : filter.category === "blur" ? "bg-gray-500/20 text-gray-300" : filter.category === "distortion" ? "bg-red-500/20 text-red-300" : filter.category === "vintage" ? "bg-orange-500/20 text-orange-300" : "bg-green-500/20 text-green-300"} `, children: filter.category }), activeFilters.includes(filter.id) && jsx("div", { className: 'text-primary', children: "\u2713" })] })] }, filter.id)) })] }); const ActiveFilters = () => jsxs("div", { className: 'space-y-4', children: [jsxs("div", { className: "glass-flex glass-items-center glass-justify-between", children: [jsxs("h4", { className: 'glass-text-sm font-medium text-primary/80', children: ["Active Filters (", activeFilters.length, "/", maxFilters, ")"] }), activeFilters.length > 0 && jsx("button", { onClick: () => { setActiveFilters([]); setFilterParameters({}); play("clear"); }, className: 'glass-text-xs text-primary hover:glass-text-secondary transition-colors', children: "Clear All" })] }), activeFilters.length === 0 ? jsx("p", { className: 'glass-text-sm text-primary/50 italic', children: "No filters applied" }) : jsx("div", { className: 'space-y-3', children: activeFilters.map((filterId, index) => { const filter = availableFilters.find(f => f.id === filterId); if (!filter) return null; return jsxs("div", { className: "glass-p-3 glass-radius-lg glass-border glass-border-white/10 glass-surface-subtle/5", children: [jsxs("div", { className: 'glass-flex glass-items-center glass-justify-between mb-2', children: [jsx("span", { className: 'glass-text-sm font-medium text-primary/90', children: filter.name }), jsxs("div", { className: 'glass-flex glass-items-center space-x-2', children: [jsxs("span", { className: 'glass-text-xs text-primary/60', children: ["#", index + 1] }), jsx("button", { onClick: () => removeFilter(filterId), className: 'text-primary hover:glass-text-secondary transition-colors', children: "\u00D7" })] })] }), filter.parameters && Object.entries(filter.parameters).map(([paramName, defaultValue]) => jsxs("div", { className: 'mt-2', children: [jsxs("label", { className: 'block glass-text-xs text-primary/70 mb-1', children: [paramName.charAt(0).toUpperCase() + paramName.slice(1), ":", typeof defaultValue === "number" ? ` ${(filterParameters[filterId]?.[paramName] ?? defaultValue).toFixed(2)}` : ""] }), typeof defaultValue === "number" ? jsx("input", { type: "range", min: paramName === "degrees" ? -180 : 0, max: paramName === "degrees" ? 180 : paramName === "radius" ? 20 : 3, step: paramName === "degrees" ? 1 : 0.1, value: filterParameters[filterId]?.[paramName] ?? defaultValue, onChange: e => updateFilterParameter(filterId, paramName, parseFloat(e.target.value)), className: 'glass-w-full h-2 glass-surface-subtle/20 glass-radius-lg appearance-none cursor-pointer' }) : jsx("input", { type: "color", value: filterParameters[filterId]?.[paramName] ?? defaultValue, onChange: e => updateFilterParameter(filterId, paramName, e.target.value), className: 'w-8 h-6 glass-radius glass-border glass-border-white/20' })] }, paramName))] }, filterId); }) })] }); 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: "Live Image Filter" }), jsx("p", { className: 'glass-text-sm text-primary/60', children: "Real-time image and video processing with custom filters" })] }), jsxs("div", { className: 'glass-flex glass-items-center space-x-2', children: [enableRealTimeProcessing && 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: "Real-time" })] }), isProcessing && 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: "Processing" })] })] })] }), showPreview && jsxs("div", { className: 'glass-grid glass-grid-cols-1 lg:grid-cols-2 glass-gap-4', children: [jsxs("div", { className: 'space-y-2', children: [jsx("h4", { className: 'glass-text-sm font-medium text-primary/80', children: "Original" }), jsxs("div", { className: 'relative aspect-video glass-surface-subtle/5 glass-border glass-border-white/20 glass-radius-lg overflow-hidden', children: [jsx("canvas", { ref: canvasRef, width: canvasWidth, height: canvasHeight, className: 'glass-w-full glass-h-full object-cover' }), videoSource && jsx("video", { ref: videoRef, className: 'hidden', autoPlay: true, muted: true, loop: true })] })] }), jsxs("div", { className: 'space-y-2', children: [jsx("h4", { className: 'glass-text-sm font-medium text-primary/80', children: "Filtered" }), jsxs("div", { className: 'relative aspect-video glass-surface-subtle/5 glass-border glass-border-white/20 glass-radius-lg overflow-hidden', children: [jsx("canvas", { ref: processedCanvasRef, width: canvasWidth, height: canvasHeight, className: 'glass-w-full glass-h-full object-cover' }), isProcessing && jsx("div", { className: 'absolute inset-0 glass-surface-dark/50 glass-flex glass-items-center glass-justify-center', children: jsx("div", { className: 'w-8 h-8 glass-border-2 glass-border-white glass-border-t-transparent glass-radius-full animate-spin' }) })] })] })] }), showControls && jsxs("div", { className: 'glass-grid glass-grid-cols-1 lg:grid-cols-2 glass-gap-6', children: [jsx(ActiveFilters, {}), jsxs("div", { className: 'space-y-4', children: [jsx("h4", { className: 'glass-text-sm font-medium text-primary/80', children: "Processing Settings" }), jsxs("div", { className: "glass-grid glass-grid-cols-2 glass-gap-4", children: [jsxs("div", { children: [jsx("label", { className: 'block glass-text-xs text-primary/70 mb-1', children: "Quality" }), jsxs("select", { value: settings.quality, onChange: e => setSettings(prev => ({ ...prev, quality: 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: "low", children: "Low" }), jsx("option", { value: "medium", children: "Medium" }), jsx("option", { value: "high", children: "High" }), jsx("option", { value: "ultra", children: "Ultra" })] })] }), jsxs("div", { children: [jsxs("label", { className: 'block glass-text-xs text-primary/70 mb-1', children: ["FPS: ", settings.fps] }), jsx("input", { type: "range", min: "1", max: "60", value: settings.fps, onChange: e => setSettings(prev => ({ ...prev, fps: parseInt(e.target.value) })), className: 'glass-w-full h-2 glass-surface-subtle/20 glass-radius-lg appearance-none cursor-pointer' })] })] }), 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: settings.enableGPU, onChange: e => setSettings(prev => ({ ...prev, enableGPU: e.target.checked })), className: 'w-4 h-4 glass-radius glass-border-white/30' }), jsx("span", { className: 'glass-text-sm text-primary/80', children: "GPU Acceleration" })] }) })] })] }), showFilterLibrary && jsx(FilterLibrary, {}), 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("input", { type: "file", accept: "image/*", onChange: e => { const file = e.target.files?.[0]; if (file) { const url = URL.createObjectURL(file); setOriginalImageUrl(url); play("upload"); } }, className: 'hidden', id: "image-upload" }), jsx(motion.label, { htmlFor: "image-upload", className: 'glass-px-4 glass-py-2 glass-surface-blue hover:glass-surface-blue text-primary glass-radius-lg glass-text-sm font-medium cursor-pointer transition-colors', whileHover: shouldAnimate ? { scale: 1.02 } : {}, whileTap: shouldAnimate ? { scale: 0.98 } : {}, children: "Upload Image" }), 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: () => processFrame(), children: "Apply Filters" })] }), processedImageUrl && jsx(motion.a, { href: processedImageUrl, download: "filtered-image.png", className: 'glass-px-4 glass-py-2 glass-surface-green hover:glass-surface-green text-primary glass-radius-lg glass-text-sm font-medium transition-colors', whileHover: shouldAnimate ? { scale: 1.02 } : {}, whileTap: shouldAnimate ? { scale: 0.98 } : {}, children: "Download Result" })] })] }); }); GlassLiveFilter.displayName = "GlassLiveFilter"; export { GlassLiveFilter }; //# sourceMappingURL=GlassLiveFilter.js.map