{
  "name": "noise-background",
  "type": "registry:ui",
  "files": [
    {
      "path": "components/ui/noise-background.tsx",
      "content": "\"use client\";\n\nimport { cn } from \"@/lib/utils\";\nimport {\n  motion,\n  useAnimationFrame,\n  useMotionTemplate,\n  useMotionValue,\n  useSpring,\n  useTransform,\n  MotionValue,\n} from \"motion/react\";\nimport { useEffect, useRef } from \"react\";\n\n// Helper component for gradient layers\nfunction GradientLayer({\n  springX,\n  springY,\n  gradientColor,\n  opacity,\n  multiplier,\n}: {\n  springX: MotionValue<number>;\n  springY: MotionValue<number>;\n  gradientColor: string;\n  opacity: number;\n  multiplier: number;\n}) {\n  const x = useTransform(springX, (val) => val * multiplier);\n  const y = useTransform(springY, (val) => val * multiplier);\n  const background = useMotionTemplate`radial-gradient(circle at ${x}px ${y}px, ${gradientColor} 0%, transparent 50%)`;\n\n  return (\n    <motion.div\n      className=\"absolute inset-0\"\n      style={{\n        opacity,\n        background,\n      }}\n    />\n  );\n}\n\ninterface NoiseBackgroundProps {\n  children?: React.ReactNode;\n  className?: string;\n  containerClassName?: string;\n  gradientColors?: string[];\n  noiseIntensity?: number;\n  speed?: number;\n  backdropBlur?: boolean;\n  animating?: boolean;\n}\n\nexport const NoiseBackground = ({\n  children,\n  className,\n  containerClassName,\n  gradientColors = [\n    \"rgb(255, 100, 150)\",\n    \"rgb(100, 150, 255)\",\n    \"rgb(255, 200, 100)\",\n  ],\n  noiseIntensity = 0.2,\n  speed = 0.1,\n  backdropBlur = false,\n  animating = true,\n}: NoiseBackgroundProps) => {\n  const containerRef = useRef<HTMLDivElement>(null);\n  const x = useMotionValue(0);\n  const y = useMotionValue(0);\n\n  // Use spring animation for smooth movement\n  const springX = useSpring(x, { stiffness: 100, damping: 30 });\n  const springY = useSpring(y, { stiffness: 100, damping: 30 });\n\n  // Transform for top gradient strip\n  const topGradientX = useTransform(springX, (val) => val * 0.1 - 50);\n\n  const velocityRef = useRef({ x: 0, y: 0 });\n  const lastDirectionChangeRef = useRef(0);\n\n  // Initialize position to center\n  useEffect(() => {\n    if (!containerRef.current) return;\n\n    const container = containerRef.current;\n    const rect = container.getBoundingClientRect();\n    const centerX = rect.width / 2;\n    const centerY = rect.height / 2;\n    x.set(centerX);\n    y.set(centerY);\n  }, [x, y]);\n\n  // Generate random velocity\n  const generateRandomVelocityRef = useRef(() => {\n    const angle = Math.random() * Math.PI * 2;\n    const magnitude = speed * (0.5 + Math.random() * 0.5); // Random speed between 0.5x and 1x\n    return {\n      x: Math.cos(angle) * magnitude,\n      y: Math.sin(angle) * magnitude,\n    };\n  });\n\n  // Update generateRandomVelocity when speed changes\n  useEffect(() => {\n    generateRandomVelocityRef.current = () => {\n      const angle = Math.random() * Math.PI * 2;\n      const magnitude = speed * (0.5 + Math.random() * 0.5);\n      return {\n        x: Math.cos(angle) * magnitude,\n        y: Math.sin(angle) * magnitude,\n      };\n    };\n    velocityRef.current = generateRandomVelocityRef.current();\n  }, [speed]);\n\n  // Animate using motion/react's useAnimationFrame\n  useAnimationFrame((time) => {\n    if (!animating || !containerRef.current) return;\n\n    const rect = containerRef.current.getBoundingClientRect();\n    const maxX = rect.width;\n    const maxY = rect.height;\n\n    // Change direction randomly every 1.5-3 seconds\n    if (time - lastDirectionChangeRef.current > 1500 + Math.random() * 1500) {\n      velocityRef.current = generateRandomVelocityRef.current();\n      lastDirectionChangeRef.current = time;\n    }\n\n    // Update position based on velocity (deltaTime is ~16ms per frame at 60fps)\n    const deltaTime = 16; // Approximate frame time\n    const currentX = x.get();\n    const currentY = y.get();\n\n    let newX = currentX + velocityRef.current.x * deltaTime;\n    let newY = currentY + velocityRef.current.y * deltaTime;\n\n    // When hitting edges, generate a completely new random direction\n    // This ensures truly random movement in all 360 degrees, not just horizontal/vertical\n    const padding = 20; // Keep some distance from edges\n\n    if (\n      newX < padding ||\n      newX > maxX - padding ||\n      newY < padding ||\n      newY > maxY - padding\n    ) {\n      // Generate completely random direction (full 360 degrees)\n      const angle = Math.random() * Math.PI * 2;\n      const magnitude = speed * (0.5 + Math.random() * 0.5);\n      velocityRef.current = {\n        x: Math.cos(angle) * magnitude,\n        y: Math.sin(angle) * magnitude,\n      };\n      // Reset timer to allow immediate new direction\n      lastDirectionChangeRef.current = time;\n      // Clamp position to stay within bounds\n      newX = Math.max(padding, Math.min(maxX - padding, newX));\n      newY = Math.max(padding, Math.min(maxY - padding, newY));\n    }\n\n    x.set(newX);\n    y.set(newY);\n  });\n\n  return (\n    <div\n      ref={containerRef}\n      className={cn(\n        \"group relative overflow-hidden rounded-2xl bg-neutral-200 p-2 backdrop-blur-sm dark:bg-neutral-800\",\n        \"shadow-[0px_0.5px_1px_0px_var(--color-neutral-400)_inset,0px_1px_0px_0px_var(--color-neutral-100)]\",\n        \"dark:shadow-[0px_1px_0px_0px_var(--color-neutral-950)_inset,0px_1px_0px_0px_var(--color-neutral-800)]\",\n        backdropBlur &&\n          \"after:absolute after:inset-0 after:h-full after:w-full after:backdrop-blur-lg after:content-['']\",\n        containerClassName,\n      )}\n      style={\n        {\n          \"--noise-opacity\": noiseIntensity,\n        } as React.CSSProperties\n      }\n    >\n      {/* Moving gradient layers */}\n      <GradientLayer\n        springX={springX}\n        springY={springY}\n        gradientColor={gradientColors[0]}\n        opacity={0.4}\n        multiplier={1}\n      />\n      <GradientLayer\n        springX={springX}\n        springY={springY}\n        gradientColor={gradientColors[1]}\n        opacity={0.3}\n        multiplier={0.7}\n      />\n      <GradientLayer\n        springX={springX}\n        springY={springY}\n        gradientColor={gradientColors[2] || gradientColors[0]}\n        opacity={0.25}\n        multiplier={1.2}\n      />\n\n      {/* Top gradient strip */}\n      <motion.div\n        className=\"absolute inset-x-0 top-0 h-1 rounded-t-2xl opacity-80 blur-sm\"\n        style={{\n          background: `linear-gradient(to right, ${gradientColors.join(\", \")})`,\n          x: animating ? topGradientX : 0,\n        }}\n      />\n\n      {/* Static Noise Pattern */}\n      <div className=\"pointer-events-none absolute inset-0 overflow-hidden\">\n        <img\n          src=\"https://assets.aceternity.com/noise.webp\"\n          alt=\"\"\n          className=\"h-full w-full object-cover opacity-[var(--noise-opacity)]\"\n          style={{ mixBlendMode: \"overlay\" }}\n        />\n      </div>\n\n      {/* Content */}\n      <div className={cn(\"relative z-10\", className)}>{children}</div>\n    </div>\n  );\n};\n",
      "type": "registry:ui",
      "target": "components/ui/noise-background.tsx"
    }
  ],
  "author": "Manu Arora <hi@manuarora.in>",
  "title": "Noise Background"
}