aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
885 lines (882 loc) • 31.5 kB
JavaScript
'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