{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "bg-animated-fractal-dot-grid",
  "type": "registry:ui",
  "description": "Background animated fractal dot grid with performance optimization and responsive design",
  "dependencies": [
    "motion"
  ],
  "files": [
    {
      "path": "registry/default/ui/bg-animated-fractal-dot-grid.tsx",
      "content": "\"use client\"\n\nimport React, { useCallback, useEffect, useMemo, useRef, useState } from \"react\"\nimport { AnimatePresence, motion } from \"motion/react\"\n\ninterface FractalDotGridProps {\n  /** Size of each dot in pixels */\n  dotSize?: number\n  /** Spacing between dots in pixels */\n  dotSpacing?: number\n  /** Opacity of dots (0-1) */\n  dotOpacity?: number\n  /** Intensity of the wave effect when hovering */\n  waveIntensity?: number\n  /** Radius of the wave effect in pixels */\n  waveRadius?: number\n  /** Color of the dots (supports any valid CSS color) */\n  dotColor?: string\n  /** Color of the dot glow effect (supports any valid CSS color) */\n  glowColor?: string\n  /** Enable or disable the noise overlay */\n  enableNoise?: boolean\n  /** Opacity of the noise overlay (0-1) */\n  noiseOpacity?: number\n  /** Enable or disable the mouse glow effect */\n  enableMouseGlow?: boolean\n  /** Set the initial performance level */\n  initialPerformance?: \"low\" | \"medium\" | \"high\"\n}\n\nconst NoiseSVG = React.memo(() => (\n  <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"100%\" height=\"100%\">\n    <filter id=\"noise\">\n      <feTurbulence\n        type=\"fractalNoise\"\n        baseFrequency=\"0.65\"\n        numOctaves=\"3\"\n        stitchTiles=\"stitch\"\n      />\n    </filter>\n    <rect width=\"100%\" height=\"100%\" filter=\"url(#noise)\" />\n  </svg>\n))\n\nNoiseSVG.displayName = \"NoiseSVG\"\n\nconst NoiseOverlay: React.FC<{ opacity: number }> = ({ opacity }) => (\n  <div\n    className=\"absolute inset-0 h-full w-full mix-blend-overlay\"\n    style={{ opacity }}\n  >\n    <NoiseSVG />\n  </div>\n)\n\nconst useResponsive = () => {\n  const [windowSize, setWindowSize] = useState({\n    width: typeof window !== \"undefined\" ? window.innerWidth : 0,\n    height: typeof window !== \"undefined\" ? window.innerHeight : 0,\n  })\n\n  useEffect(() => {\n    const handleResize = () => {\n      setWindowSize({\n        width: window.innerWidth,\n        height: window.innerHeight,\n      })\n    }\n\n    window.addEventListener(\"resize\", handleResize)\n    return () => window.removeEventListener(\"resize\", handleResize)\n  }, [])\n\n  return {\n    isMobile: windowSize.width < 768,\n    isTablet: windowSize.width >= 768 && windowSize.width < 1024,\n    isDesktop: windowSize.width >= 1024,\n  }\n}\n\nconst usePerformance = (\n  initialPerformance: \"low\" | \"medium\" | \"high\" = \"medium\"\n) => {\n  const [performance, setPerformance] = useState(initialPerformance)\n  const [fps, setFps] = useState(60)\n\n  useEffect(() => {\n    let frameCount = 0\n    let lastTime = globalThis.performance.now()\n    let framerId: number\n\n    const measureFps = (time: number) => {\n      frameCount++\n      if (time - lastTime > 1000) {\n        setFps(Math.round((frameCount * 1000) / (time - lastTime)))\n        frameCount = 0\n        lastTime = time\n      }\n      framerId = requestAnimationFrame(measureFps)\n    }\n\n    framerId = requestAnimationFrame(measureFps)\n\n    return () => cancelAnimationFrame(framerId)\n  }, [])\n\n  useEffect(() => {\n    if (fps < 30 && performance !== \"low\") {\n      setPerformance(\"low\")\n    } else if (fps >= 30 && fps < 50 && performance !== \"medium\") {\n      setPerformance(\"medium\")\n    } else if (fps >= 50 && performance !== \"high\") {\n      setPerformance(\"high\")\n    }\n  }, [fps, performance])\n\n  return { performance, fps }\n}\n\nconst DotCanvas: React.FC<{\n  dotSize: number\n  dotSpacing: number\n  dotOpacity: number\n  waveIntensity: number\n  waveRadius: number\n  dotColor: string\n  glowColor: string\n  performance: \"low\" | \"medium\" | \"high\"\n  mousePos: { x: number; y: number }\n}> = React.memo(\n  ({\n    dotSize,\n    dotSpacing,\n    dotOpacity,\n    waveIntensity,\n    waveRadius,\n    dotColor,\n    glowColor,\n    performance,\n    mousePos,\n  }) => {\n    const canvasRef = useRef<HTMLCanvasElement>(null)\n    const animationRef = useRef<number | null>(null)\n\n    const drawDots = useCallback(\n      (ctx: CanvasRenderingContext2D, time: number) => {\n        const { width, height } = ctx.canvas\n        ctx.clearRect(0, 0, width, height)\n\n        const performanceSettings = {\n          low: { skip: 3 },\n          medium: { skip: 2 },\n          high: { skip: 1 },\n        }\n\n        const skip = performanceSettings[performance].skip\n\n        const cols = Math.ceil(width / dotSpacing)\n        const rows = Math.ceil(height / dotSpacing)\n\n        const centerX = mousePos.x * width\n        const centerY = mousePos.y * height\n\n        for (let i = 0; i < cols; i += skip) {\n          for (let j = 0; j < rows; j += skip) {\n            const x = i * dotSpacing\n            const y = j * dotSpacing\n\n            const distanceX = x - centerX\n            const distanceY = y - centerY\n            const distance = Math.sqrt(\n              distanceX * distanceX + distanceY * distanceY\n            )\n\n            let dotX = x\n            let dotY = y\n\n            if (distance < waveRadius) {\n              const waveStrength = Math.pow(1 - distance / waveRadius, 2)\n              const angle = Math.atan2(distanceY, distanceX)\n              const waveOffset =\n                Math.sin(distance * 0.05 - time * 0.005) *\n                waveIntensity *\n                waveStrength\n              dotX += Math.cos(angle) * waveOffset\n              dotY += Math.sin(angle) * waveOffset\n\n              const glowRadius = dotSize * (1 + waveStrength)\n              const gradient = ctx.createRadialGradient(\n                dotX,\n                dotY,\n                0,\n                dotX,\n                dotY,\n                glowRadius\n              )\n              gradient.addColorStop(\n                0,\n                glowColor.replace(\"1)\", `${dotOpacity * (1 + waveStrength)})`)\n              )\n              gradient.addColorStop(1, glowColor.replace(\"1)\", \"0)\"))\n              ctx.fillStyle = gradient\n            } else {\n              ctx.fillStyle = dotColor.replace(\"1)\", `${dotOpacity})`)\n            }\n\n            ctx.beginPath()\n            ctx.arc(dotX, dotY, dotSize / 2, 0, Math.PI * 2)\n            ctx.fill()\n          }\n        }\n      },\n      [\n        dotSize,\n        dotSpacing,\n        dotOpacity,\n        waveIntensity,\n        waveRadius,\n        dotColor,\n        glowColor,\n        performance,\n        mousePos,\n      ]\n    )\n\n    useEffect(() => {\n      const canvas = canvasRef.current\n      if (!canvas) return\n\n      const ctx = canvas.getContext(\"2d\")\n      if (!ctx) return\n\n      const resizeCanvas = () => {\n        canvas.width = window.innerWidth\n        canvas.height = window.innerHeight\n      }\n\n      resizeCanvas()\n      window.addEventListener(\"resize\", resizeCanvas)\n\n      let lastTime = 0\n      const animate = (time: number) => {\n        if (time - lastTime > 16) {\n          drawDots(ctx, time)\n          lastTime = time\n        }\n        animationRef.current = requestAnimationFrame(animate)\n      }\n\n      animationRef.current = requestAnimationFrame(animate)\n\n      return () => {\n        window.removeEventListener(\"resize\", resizeCanvas)\n        if (animationRef.current) {\n          cancelAnimationFrame(animationRef.current)\n        }\n      }\n    }, [drawDots])\n\n    return (\n      <canvas\n        ref={canvasRef}\n        className=\"absolute inset-0 h-full w-full bg-gray-100\"\n        style={{ mixBlendMode: \"multiply\" }}\n      />\n    )\n  }\n)\n\nDotCanvas.displayName = \"DotCanvas\"\n\nconst MouseGlow: React.FC<{\n  glowColor: string\n  mousePos: { x: number; y: number }\n}> = React.memo(({ glowColor, mousePos }) => (\n  <>\n    <div\n      className=\"pointer-events-none absolute h-40 w-40 rounded-full\"\n      style={{\n        background: `radial-gradient(circle, ${glowColor.replace(\n          \"1)\",\n          \"0.2)\"\n        )} 0%, ${glowColor.replace(\"1)\", \"0)\")} 70%)`,\n        left: `${mousePos.x * 100}%`,\n        top: `${mousePos.y * 100}%`,\n        transform: \"translate(-50%, -50%)\",\n        filter: \"blur(10px)\",\n      }}\n    />\n    <div\n      className=\"pointer-events-none absolute h-20 w-20 rounded-full\"\n      style={{\n        background: `radial-gradient(circle, ${glowColor.replace(\n          \"1)\",\n          \"0.4)\"\n        )} 0%, ${glowColor.replace(\"1)\", \"0)\")} 70%)`,\n        left: `${mousePos.x * 100}%`,\n        top: `${mousePos.y * 100}%`,\n        transform: \"translate(-50%, -50%)\",\n      }}\n    />\n  </>\n))\n\nMouseGlow.displayName = \"MouseGlow\"\n\nexport function FractalDotGrid({\n  dotSize = 4,\n  dotSpacing = 20,\n  dotOpacity = 0.3,\n  waveIntensity = 30,\n  waveRadius = 200,\n  dotColor = \"rgba(100, 100, 255, 1)\",\n  glowColor = \"rgba(100, 100, 255, 1)\",\n  enableNoise = true,\n  noiseOpacity = 0.03,\n  enableMouseGlow = true,\n  initialPerformance = \"medium\",\n}: FractalDotGridProps) {\n  const containerRef = useRef<HTMLDivElement>(null)\n  const { isMobile, isTablet } = useResponsive()\n  const { performance } = usePerformance(initialPerformance)\n  const [mousePos, setMousePos] = useState({ x: 0, y: 0 })\n\n  const handleMouseMove = useCallback((event: MouseEvent) => {\n    const { clientX, clientY } = event\n    const { left, top, width, height } =\n      containerRef.current?.getBoundingClientRect() ?? {\n        left: 0,\n        top: 0,\n        width: 0,\n        height: 0,\n      }\n    const x = (clientX - left) / width\n    const y = (clientY - top) / height\n    setMousePos({ x, y })\n  }, [])\n\n  useEffect(() => {\n    window.addEventListener(\"mousemove\", handleMouseMove)\n    return () => window.removeEventListener(\"mousemove\", handleMouseMove)\n  }, [handleMouseMove])\n\n  const responsiveDotSize = useMemo(() => {\n    if (isMobile) return dotSize * 0.75\n    if (isTablet) return dotSize * 0.9\n    return dotSize\n  }, [isMobile, isTablet, dotSize])\n\n  const responsiveDotSpacing = useMemo(() => {\n    if (isMobile) return dotSpacing * 1.5\n    if (isTablet) return dotSpacing * 1.25\n    return dotSpacing\n  }, [isMobile, isTablet, dotSpacing])\n\n  return (\n    <AnimatePresence>\n      <motion.div\n        ref={containerRef}\n        key=\"fractal-dot-grid\"\n        initial={{ opacity: 0 }}\n        animate={{ opacity: 1 }}\n        exit={{ opacity: 0 }}\n        transition={{ duration: 1.5, ease: \"easeOut\" }}\n        className=\"absolute inset-0 h-full w-full overflow-hidden\"\n      >\n        <DotCanvas\n          dotSize={responsiveDotSize}\n          dotSpacing={responsiveDotSpacing}\n          dotOpacity={dotOpacity}\n          waveIntensity={waveIntensity}\n          waveRadius={waveRadius}\n          dotColor={dotColor}\n          glowColor={glowColor}\n          performance={performance}\n          mousePos={mousePos}\n        />\n        {enableNoise && <NoiseOverlay opacity={noiseOpacity} />}\n        {enableMouseGlow && (\n          <MouseGlow glowColor={glowColor} mousePos={mousePos} />\n        )}\n      </motion.div>\n    </AnimatePresence>\n  )\n}\n\nexport default FractalDotGrid\n",
      "type": "registry:ui"
    }
  ]
}