{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "webcam-pixel-grid",
  "type": "registry:block",
  "title": "Webcam pixel grid",
  "description": "Webcam pixel grid",
  "files": [
    {
      "path": "components/usages/webcampixelgridusage.tsx",
      "content": "import WebcamPixelGrid from \"@/registry/open-source/webcam-pixel-grid\";\n\nexport default function WebcamPixelGridDemo() {\n    return (\n        <div className=\"relative h-screen w-screen bg-black overflow-hidden\">\n\n\n            {/* Webcam pixel grid background */}\n            <div className=\"absolute inset-0\">\n                <WebcamPixelGrid\n                    gridCols={60}\n                    gridRows={40}\n                    maxElevation={50}\n                    motionSensitivity={0.25}\n                    elevationSmoothing={0.2}\n                    colorMode=\"webcam\"\n                    backgroundColor=\"#030303\"\n                    mirror={true}\n                    gapRatio={0.05}\n                    invertColors={false}\n                    darken={0.6}\n                    borderColor=\"#ffffff\"\n                    borderOpacity={0.06}\n                    className=\"w-full h-full\"\n                    onWebcamReady={() => console.log(\"Webcam ready!\")}\n                    onWebcamError={(err) => console.error(\"Webcam error:\", err)}\n                />\n            </div>\n\n            {/* Gradient overlay for better text readability */}\n            <div className=\"absolute inset-0 bg-linear-to-b from-black/40 via-transparent to-black/60 pointer-events-none\" />\n\n            {/* Hero content */}\n            <div className=\"relative z-10 flex h-full flex-col items-center justify-center px-4\">\n                <div className=\"max-w-4xl text-center\">\n                    {/* Badge */}\n                    <div className=\"mb-6 inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/5 px-4 py-1.5 text-sm text-white/70 backdrop-blur-xs\">\n                        Introducing AI SaaS Template &rarr;\n                    </div>\n\n                    {/* Title */}\n                    <h1 className=\"mb-6 text-xl font-bold tracking-tight text-white sm:text-6xl md:text-8xl\">\n                        Ship stunning landing pages faster.\n                    </h1>\n\n                    {/* Description */}\n                    <p className=\"mx-auto mb-10 max-w-2xl text-base text-white/60 sm:text-xl\">\n                        Build amazing landing pages with component blocks and templates from aceternity, without having to worry about styling and animations.\n                    </p>\n\n                    {/* Buttons */}\n                    <div className=\"flex flex-col items-center justify-center gap-4 sm:flex-row\">\n                        <button className=\"group relative inline-flex h-12 items-center justify-center gap-2 rounded-full bg-white px-8 text-base font-medium text-black transition-all hover:bg-white/90 hover:scale-105\">\n                            Get Started\n                            <svg\n                                className=\"h-4 w-4 transition-transform group-hover:translate-x-0.5\"\n                                fill=\"none\"\n                                viewBox=\"0 0 24 24\"\n                                stroke=\"currentColor\"\n                            >\n                                <path\n                                    strokeLinecap=\"round\"\n                                    strokeLinejoin=\"round\"\n                                    strokeWidth={2}\n                                    d=\"M17 8l4 4m0 0l-4 4m4-4H3\"\n                                />\n                            </svg>\n                        </button>\n                        <button className=\"inline-flex h-12 items-center justify-center gap-2 rounded-full border border-white/20 bg-white/5 px-8 text-base font-medium text-white backdrop-blur-xs transition-all hover:bg-white/10 hover:border-white/30\">\n                            View Documentation\n                        </button>\n                    </div>\n                </div>\n\n                {/* Scroll indicator */}\n\n            </div>\n        </div>\n    );\n}",
      "type": "registry:block",
      "target": "~/example.tsx"
    },
    {
      "path": "components/usages/webcampixelgridusage.tsx",
      "content": "import WebcamPixelGrid from \"@/registry/open-source/webcam-pixel-grid\";\n\nexport default function WebcamPixelGridDemo() {\n    return (\n        <div className=\"relative h-screen w-screen bg-black overflow-hidden\">\n\n\n            {/* Webcam pixel grid background */}\n            <div className=\"absolute inset-0\">\n                <WebcamPixelGrid\n                    gridCols={60}\n                    gridRows={40}\n                    maxElevation={50}\n                    motionSensitivity={0.25}\n                    elevationSmoothing={0.2}\n                    colorMode=\"webcam\"\n                    backgroundColor=\"#030303\"\n                    mirror={true}\n                    gapRatio={0.05}\n                    invertColors={false}\n                    darken={0.6}\n                    borderColor=\"#ffffff\"\n                    borderOpacity={0.06}\n                    className=\"w-full h-full\"\n                    onWebcamReady={() => console.log(\"Webcam ready!\")}\n                    onWebcamError={(err) => console.error(\"Webcam error:\", err)}\n                />\n            </div>\n\n            {/* Gradient overlay for better text readability */}\n            <div className=\"absolute inset-0 bg-linear-to-b from-black/40 via-transparent to-black/60 pointer-events-none\" />\n\n            {/* Hero content */}\n            <div className=\"relative z-10 flex h-full flex-col items-center justify-center px-4\">\n                <div className=\"max-w-4xl text-center\">\n                    {/* Badge */}\n                    <div className=\"mb-6 inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/5 px-4 py-1.5 text-sm text-white/70 backdrop-blur-xs\">\n                        Introducing AI SaaS Template &rarr;\n                    </div>\n\n                    {/* Title */}\n                    <h1 className=\"mb-6 text-xl font-bold tracking-tight text-white sm:text-6xl md:text-8xl\">\n                        Ship stunning landing pages faster.\n                    </h1>\n\n                    {/* Description */}\n                    <p className=\"mx-auto mb-10 max-w-2xl text-base text-white/60 sm:text-xl\">\n                        Build amazing landing pages with component blocks and templates from aceternity, without having to worry about styling and animations.\n                    </p>\n\n                    {/* Buttons */}\n                    <div className=\"flex flex-col items-center justify-center gap-4 sm:flex-row\">\n                        <button className=\"group relative inline-flex h-12 items-center justify-center gap-2 rounded-full bg-white px-8 text-base font-medium text-black transition-all hover:bg-white/90 hover:scale-105\">\n                            Get Started\n                            <svg\n                                className=\"h-4 w-4 transition-transform group-hover:translate-x-0.5\"\n                                fill=\"none\"\n                                viewBox=\"0 0 24 24\"\n                                stroke=\"currentColor\"\n                            >\n                                <path\n                                    strokeLinecap=\"round\"\n                                    strokeLinejoin=\"round\"\n                                    strokeWidth={2}\n                                    d=\"M17 8l4 4m0 0l-4 4m4-4H3\"\n                                />\n                            </svg>\n                        </button>\n                        <button className=\"inline-flex h-12 items-center justify-center gap-2 rounded-full border border-white/20 bg-white/5 px-8 text-base font-medium text-white backdrop-blur-xs transition-all hover:bg-white/10 hover:border-white/30\">\n                            View Documentation\n                        </button>\n                    </div>\n                </div>\n\n                {/* Scroll indicator */}\n\n            </div>\n        </div>\n    );\n}",
      "type": "registry:ui"
    },
    {
      "path": "registry/open-source/webcam-pixel-grid.tsx",
      "content": "\"use client\";\nimport React, { useRef, useEffect, useState, useCallback } from \"react\";\nimport { cn } from \"@/registry/utilities/cn\";\n\ntype WebcamPixelGridProps = {\n    /** Number of columns in the grid */\n    gridCols?: number;\n    /** Number of rows in the grid */\n    gridRows?: number;\n    /** Maximum elevation for motion detection */\n    maxElevation?: number;\n    /** Motion sensitivity (0-1) */\n    motionSensitivity?: number;\n    /** Smoothing factor for elevation transitions */\n    elevationSmoothing?: number;\n    /** Color mode: 'webcam' uses actual colors, 'monochrome' uses single color */\n    colorMode?: \"webcam\" | \"monochrome\";\n    /** Base color when in monochrome mode */\n    monochromeColor?: string;\n    /** Background color */\n    backgroundColor?: string;\n    /** Whether to mirror the webcam feed */\n    mirror?: boolean;\n    /** Gap between cells (0-1, fraction of cell size) */\n    gapRatio?: number;\n    /** Invert the colors */\n    invertColors?: boolean;\n    /** Darken factor (0-1, 0 = no darkening, 1 = fully dark) */\n    darken?: number;\n    /** Border color for cells */\n    borderColor?: string;\n    /** Border opacity (0-1) */\n    borderOpacity?: number;\n    /** Additional class name */\n    className?: string;\n    /** Callback when webcam access is denied */\n    onWebcamError?: (error: Error) => void;\n    /** Callback when webcam is ready */\n    onWebcamReady?: () => void;\n};\n\ntype PixelData = {\n    r: number;\n    g: number;\n    b: number;\n    motion: number;\n    targetElevation: number;\n    currentElevation: number;\n};\n\nconst WebcamPixelGrid: React.FC<WebcamPixelGridProps> = ({\n    gridCols = 64,\n    gridRows = 48,\n    maxElevation = 15,\n    motionSensitivity = 0.4,\n    elevationSmoothing = 0.1,\n    colorMode = \"webcam\",\n    monochromeColor = \"#00ff88\",\n    backgroundColor = \"#0a0a0a\",\n    mirror = true,\n    gapRatio = 0.1,\n    invertColors = false,\n    darken = 0,\n    borderColor = \"#ffffff\",\n    borderOpacity = 0.08,\n    className,\n    onWebcamError,\n    onWebcamReady,\n}) => {\n    const videoRef = useRef<HTMLVideoElement>(null);\n    const processingCanvasRef = useRef<HTMLCanvasElement>(null);\n    const displayCanvasRef = useRef<HTMLCanvasElement>(null);\n    const previousFrameRef = useRef<Uint8ClampedArray | null>(null);\n    const pixelDataRef = useRef<PixelData[][]>([]);\n    const animationRef = useRef<number>(0);\n    const [isReady, setIsReady] = useState(false);\n    const [error, setError] = useState<string | null>(null);\n    const [showErrorPopup, setShowErrorPopup] = useState(true);\n\n    // Parse monochrome color\n    const monoRGB = React.useMemo(() => {\n        const hex = monochromeColor.replace(\"#\", \"\");\n        return {\n            r: parseInt(hex.slice(0, 2), 16),\n            g: parseInt(hex.slice(2, 4), 16),\n            b: parseInt(hex.slice(4, 6), 16),\n        };\n    }, [monochromeColor]);\n\n    // Parse border color\n    const borderRGB = React.useMemo(() => {\n        const hex = borderColor.replace(\"#\", \"\");\n        return {\n            r: parseInt(hex.slice(0, 2), 16),\n            g: parseInt(hex.slice(2, 4), 16),\n            b: parseInt(hex.slice(4, 6), 16),\n        };\n    }, [borderColor]);\n\n    // Initialize pixel data\n    useEffect(() => {\n        pixelDataRef.current = Array.from({ length: gridRows }, () =>\n            Array.from({ length: gridCols }, () => ({\n                r: 30,\n                g: 30,\n                b: 30,\n                motion: 0,\n                targetElevation: 0,\n                currentElevation: 0,\n            })),\n        );\n    }, [gridCols, gridRows]);\n\n    const streamRef = useRef<MediaStream | null>(null);\n\n    // Request camera access\n    const requestCameraAccess = useCallback(async () => {\n        try {\n            const stream = await navigator.mediaDevices.getUserMedia({\n                video: {\n                    width: { ideal: 640 },\n                    height: { ideal: 480 },\n                    facingMode: \"user\",\n                },\n            });\n\n            streamRef.current = stream;\n\n            if (videoRef.current) {\n                videoRef.current.srcObject = stream;\n                await videoRef.current.play();\n                setIsReady(true);\n                setError(null);\n                setShowErrorPopup(false);\n                onWebcamReady?.();\n            }\n        } catch (err) {\n            const error =\n                err instanceof Error ? err : new Error(\"Webcam access denied\");\n            setError(error.message);\n            onWebcamError?.(error);\n        }\n    }, [onWebcamError, onWebcamReady]);\n\n    // Initialize webcam on mount\n    useEffect(() => {\n        requestCameraAccess();\n\n        return () => {\n            if (streamRef.current) {\n                streamRef.current.getTracks().forEach((track) => track.stop());\n            }\n        };\n    }, [requestCameraAccess]);\n\n    // Main render loop\n    const render = useCallback(() => {\n        const video = videoRef.current;\n        const processingCanvas = processingCanvasRef.current;\n        const displayCanvas = displayCanvasRef.current;\n\n        if (!video || !processingCanvas || !displayCanvas || video.readyState < 2) {\n            animationRef.current = requestAnimationFrame(render);\n            return;\n        }\n\n        const procCtx = processingCanvas.getContext(\"2d\", {\n            willReadFrequently: true,\n        });\n        const dispCtx = displayCanvas.getContext(\"2d\");\n\n        if (!procCtx || !dispCtx) {\n            animationRef.current = requestAnimationFrame(render);\n            return;\n        }\n\n        // Set processing canvas size to grid dimensions\n        processingCanvas.width = gridCols;\n        processingCanvas.height = gridRows;\n\n        // Draw video to processing canvas (scaled down)\n        procCtx.save();\n        if (mirror) {\n            procCtx.scale(-1, 1);\n            procCtx.drawImage(video, -gridCols, 0, gridCols, gridRows);\n        } else {\n            procCtx.drawImage(video, 0, 0, gridCols, gridRows);\n        }\n        procCtx.restore();\n\n        // Get pixel data\n        const imageData = procCtx.getImageData(0, 0, gridCols, gridRows);\n        const currentData = imageData.data;\n        const previousData = previousFrameRef.current;\n\n        // Update pixel data with motion detection\n        const pixels = pixelDataRef.current;\n        for (let row = 0; row < gridRows; row++) {\n            for (let col = 0; col < gridCols; col++) {\n                const idx = (row * gridCols + col) * 4;\n                const r = currentData[idx];\n                const g = currentData[idx + 1];\n                const b = currentData[idx + 2];\n\n                const pixel = pixels[row]?.[col];\n                if (!pixel) continue;\n\n                // Calculate motion\n                let motion = 0;\n                if (previousData) {\n                    const prevR = previousData[idx];\n                    const prevG = previousData[idx + 1];\n                    const prevB = previousData[idx + 2];\n                    const diff =\n                        Math.abs(r - prevR) + Math.abs(g - prevG) + Math.abs(b - prevB);\n                    motion = Math.min(1, diff / 255 / motionSensitivity);\n                }\n\n                // Smooth motion\n                pixel.motion = pixel.motion * 0.7 + motion * 0.3;\n\n                // Set colors\n                let finalR = r;\n                let finalG = g;\n                let finalB = b;\n\n                if (colorMode === \"monochrome\") {\n                    const brightness = (r + g + b) / 3 / 255;\n                    finalR = Math.round(monoRGB.r * brightness);\n                    finalG = Math.round(monoRGB.g * brightness);\n                    finalB = Math.round(monoRGB.b * brightness);\n                }\n\n                // Apply invert\n                if (invertColors) {\n                    finalR = 255 - finalR;\n                    finalG = 255 - finalG;\n                    finalB = 255 - finalB;\n                }\n\n                // Apply darken\n                if (darken > 0) {\n                    const darkenFactor = 1 - darken;\n                    finalR = Math.round(finalR * darkenFactor);\n                    finalG = Math.round(finalG * darkenFactor);\n                    finalB = Math.round(finalB * darkenFactor);\n                }\n\n                pixel.r = finalR;\n                pixel.g = finalG;\n                pixel.b = finalB;\n\n                // Set target elevation\n                pixel.targetElevation = pixel.motion * maxElevation;\n\n                // Smooth elevation transition\n                pixel.currentElevation +=\n                    (pixel.targetElevation - pixel.currentElevation) * elevationSmoothing;\n            }\n        }\n\n        // Store current frame for next comparison\n        previousFrameRef.current = new Uint8ClampedArray(currentData);\n\n        // Render to display canvas\n        const dpr = window.devicePixelRatio || 1;\n        const displayWidth = displayCanvas.clientWidth;\n        const displayHeight = displayCanvas.clientHeight;\n\n        displayCanvas.width = displayWidth * dpr;\n        displayCanvas.height = displayHeight * dpr;\n        dispCtx.scale(dpr, dpr);\n\n        // Clear canvas\n        dispCtx.fillStyle = backgroundColor;\n        dispCtx.fillRect(0, 0, displayWidth, displayHeight);\n\n        // Calculate cell size (always square, cover entire container like object-fit: cover)\n        const cellSize = Math.max(\n            displayWidth / gridCols,\n            displayHeight / gridRows,\n        );\n        const gap = cellSize * gapRatio;\n\n        // Calculate offset to center the grid (negative offset for overflow, creating cover effect)\n        const gridWidth = cellSize * gridCols;\n        const gridHeight = cellSize * gridRows;\n        const offsetXGrid = (displayWidth - gridWidth) / 2;\n        const offsetYGrid = (displayHeight - gridHeight) / 2;\n\n        // Draw cells with 3D effect\n        for (let row = 0; row < gridRows; row++) {\n            for (let col = 0; col < gridCols; col++) {\n                const pixel = pixels[row]?.[col];\n                if (!pixel) continue;\n\n                const x = offsetXGrid + col * cellSize;\n                const y = offsetYGrid + row * cellSize;\n                const elevation = pixel.currentElevation;\n\n                // Calculate 3D offset (isometric-like projection) - MUCH larger effect\n                const offsetX = -elevation * 1.2;\n                const offsetY = -elevation * 1.8;\n\n                // Draw shadow - larger and more visible\n                if (elevation > 0.5) {\n                    dispCtx.fillStyle = `rgba(0, 0, 0, ${Math.min(0.6, elevation * 0.04)})`;\n                    dispCtx.fillRect(\n                        x + gap / 2 + elevation * 1.5,\n                        y + gap / 2 + elevation * 2.0,\n                        cellSize - gap,\n                        cellSize - gap,\n                    );\n                }\n\n                // Draw side faces for 3D effect - thicker sides\n                if (elevation > 0.5) {\n                    // Right side\n                    dispCtx.fillStyle = `rgb(${Math.max(0, pixel.r - 80)}, ${Math.max(0, pixel.g - 80)}, ${Math.max(0, pixel.b - 80)})`;\n                    dispCtx.beginPath();\n                    dispCtx.moveTo(\n                        x + cellSize - gap / 2 + offsetX,\n                        y + gap / 2 + offsetY,\n                    );\n                    dispCtx.lineTo(x + cellSize - gap / 2, y + gap / 2);\n                    dispCtx.lineTo(x + cellSize - gap / 2, y + cellSize - gap / 2);\n                    dispCtx.lineTo(\n                        x + cellSize - gap / 2 + offsetX,\n                        y + cellSize - gap / 2 + offsetY,\n                    );\n                    dispCtx.closePath();\n                    dispCtx.fill();\n\n                    // Bottom side\n                    dispCtx.fillStyle = `rgb(${Math.max(0, pixel.r - 50)}, ${Math.max(0, pixel.g - 50)}, ${Math.max(0, pixel.b - 50)})`;\n                    dispCtx.beginPath();\n                    dispCtx.moveTo(\n                        x + gap / 2 + offsetX,\n                        y + cellSize - gap / 2 + offsetY,\n                    );\n                    dispCtx.lineTo(x + gap / 2, y + cellSize - gap / 2);\n                    dispCtx.lineTo(x + cellSize - gap / 2, y + cellSize - gap / 2);\n                    dispCtx.lineTo(\n                        x + cellSize - gap / 2 + offsetX,\n                        y + cellSize - gap / 2 + offsetY,\n                    );\n                    dispCtx.closePath();\n                    dispCtx.fill();\n                }\n\n                // Draw top face (main cell) - brighter when elevated\n                const brightness = 1 + elevation * 0.05;\n                dispCtx.fillStyle = `rgb(${Math.min(255, Math.round(pixel.r * brightness))}, ${Math.min(255, Math.round(pixel.g * brightness))}, ${Math.min(255, Math.round(pixel.b * brightness))})`;\n                dispCtx.fillRect(\n                    x + gap / 2 + offsetX,\n                    y + gap / 2 + offsetY,\n                    cellSize - gap,\n                    cellSize - gap,\n                );\n\n                // Draw light border around top face\n                dispCtx.strokeStyle = `rgba(${borderRGB.r}, ${borderRGB.g}, ${borderRGB.b}, ${borderOpacity + elevation * 0.008})`;\n                dispCtx.lineWidth = 0.5;\n                dispCtx.strokeRect(\n                    x + gap / 2 + offsetX,\n                    y + gap / 2 + offsetY,\n                    cellSize - gap,\n                    cellSize - gap,\n                );\n            }\n        }\n\n        animationRef.current = requestAnimationFrame(render);\n    }, [\n        gridCols,\n        gridRows,\n        mirror,\n        motionSensitivity,\n        colorMode,\n        monoRGB,\n        maxElevation,\n        elevationSmoothing,\n        backgroundColor,\n        gapRatio,\n        invertColors,\n        darken,\n        borderRGB,\n        borderOpacity,\n    ]);\n\n    // Start render loop when ready\n    useEffect(() => {\n        if (!isReady) return;\n\n        animationRef.current = requestAnimationFrame(render);\n\n        return () => {\n            cancelAnimationFrame(animationRef.current);\n        };\n    }, [isReady, render]);\n\n    return (\n        <div className={cn(\"relative h-full w-full\", className)}>\n            {/* Hidden video element */}\n            <video\n                ref={videoRef}\n                className=\"pointer-events-none absolute h-0 w-0 opacity-0\"\n                playsInline\n                muted\n            />\n\n            {/* Hidden processing canvas */}\n            <canvas\n                ref={processingCanvasRef}\n                className=\"pointer-events-none absolute h-0 w-0 opacity-0\"\n            />\n\n            {/* Display canvas with fade-in */}\n            <canvas\n                ref={displayCanvasRef}\n                className={cn(\n                    \"h-full w-full transition-opacity duration-1000\",\n                    isReady ? \"opacity-100\" : \"opacity-0\",\n                )}\n                style={{ backgroundColor }}\n            />\n\n            {/* Error popup */}\n            {error && showErrorPopup && (\n                <div className=\"animate-in fade-in slide-in-from-top-2 fixed top-4 right-4 z-50 duration-300\">\n                    <div className=\"relative flex max-w-sm items-start gap-3 rounded-lg border border-white/10 bg-black/80 p-4 shadow-2xl backdrop-blur-xl\">\n                        {/* Close button */}\n                        <button\n                            onClick={() => setShowErrorPopup(false)}\n                            className=\"absolute top-2 right-2 rounded-md p-1 text-white/40 transition-colors hover:bg-white/10 hover:text-white/70\"\n                        >\n                            <svg\n                                className=\"h-4 w-4\"\n                                fill=\"none\"\n                                viewBox=\"0 0 24 24\"\n                                stroke=\"currentColor\"\n                            >\n                                <path\n                                    strokeLinecap=\"round\"\n                                    strokeLinejoin=\"round\"\n                                    strokeWidth={2}\n                                    d=\"M6 18L18 6M6 6l12 12\"\n                                />\n                            </svg>\n                        </button>\n\n                        {/* Camera icon */}\n                        <div className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-white/10\">\n                            <svg\n                                className=\"h-5 w-5 text-white/60\"\n                                fill=\"none\"\n                                viewBox=\"0 0 24 24\"\n                                stroke=\"currentColor\"\n                            >\n                                <path\n                                    strokeLinecap=\"round\"\n                                    strokeLinejoin=\"round\"\n                                    strokeWidth={1.5}\n                                    d=\"M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z\"\n                                />\n                            </svg>\n                        </div>\n\n                        {/* Content */}\n                        <div className=\"flex-1 pr-4\">\n                            <p className=\"text-sm font-medium text-white/90\">\n                                Camera access needed\n                            </p>\n                            <p className=\"mt-1 text-xs text-white/50\">\n                                Enable camera for the interactive background effect\n                            </p>\n                            <button\n                                onClick={requestCameraAccess}\n                                className=\"mt-3 inline-flex items-center gap-1.5 rounded-md bg-white/10 px-3 py-1.5 text-xs font-medium text-white transition-colors hover:bg-white/20\"\n                            >\n                                <svg\n                                    className=\"h-3.5 w-3.5\"\n                                    fill=\"none\"\n                                    viewBox=\"0 0 24 24\"\n                                    stroke=\"currentColor\"\n                                >\n                                    <path\n                                        strokeLinecap=\"round\"\n                                        strokeLinejoin=\"round\"\n                                        strokeWidth={2}\n                                        d=\"M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z\"\n                                    />\n                                </svg>\n                                Enable Camera\n                            </button>\n                        </div>\n                    </div>\n                </div>\n            )}\n\n            {/* Minimized error indicator */}\n            {error && !showErrorPopup && (\n                <button\n                    onClick={() => setShowErrorPopup(true)}\n                    className=\"fixed top-4 right-4 z-50 flex h-10 w-10 items-center justify-center rounded-full border border-white/10 bg-black/60 text-white/50 shadow-lg backdrop-blur-xl transition-all hover:scale-105 hover:bg-black/80 hover:text-white/80\"\n                    title=\"Camera access required\"\n                >\n                    <svg\n                        className=\"h-5 w-5\"\n                        fill=\"none\"\n                        viewBox=\"0 0 24 24\"\n                        stroke=\"currentColor\"\n                    >\n                        <path\n                            strokeLinecap=\"round\"\n                            strokeLinejoin=\"round\"\n                            strokeWidth={1.5}\n                            d=\"M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z\"\n                        />\n                        <path\n                            strokeLinecap=\"round\"\n                            strokeLinejoin=\"round\"\n                            strokeWidth={2}\n                            d=\"M3 3l18 18\"\n                            className=\"text-red-400\"\n                            stroke=\"currentColor\"\n                        />\n                    </svg>\n                </button>\n            )}\n        </div>\n    );\n};\n\nexport default WebcamPixelGrid;\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/utilities/cn.ts",
      "content": "import { ClassValue, clsx } from \"clsx\";\r\nimport { twMerge } from \"tailwind-merge\";\r\n\r\nexport function cn(...inputs: ClassValue[]) {\r\n\treturn twMerge(clsx(inputs));\r\n}\r\n",
      "type": "registry:ui"
    }
  ]
}