{
  "name": "stars-background",
  "type": "registry:ui",
  "files": [
    {
      "path": "components/ui/stars-background.tsx",
      "content": "\"use client\";\nimport { cn } from \"@/lib/utils\";\nimport React, {\n  useState,\n  useEffect,\n  useRef,\n  RefObject,\n  useCallback,\n} from \"react\";\n\ninterface StarProps {\n  x: number;\n  y: number;\n  radius: number;\n  opacity: number;\n  twinkleSpeed: number | null;\n}\n\ninterface StarBackgroundProps {\n  starDensity?: number;\n  allStarsTwinkle?: boolean;\n  twinkleProbability?: number;\n  minTwinkleSpeed?: number;\n  maxTwinkleSpeed?: number;\n  className?: string;\n}\n\nexport const StarsBackground: React.FC<StarBackgroundProps> = ({\n  starDensity = 0.00015,\n  allStarsTwinkle = true,\n  twinkleProbability = 0.7,\n  minTwinkleSpeed = 0.5,\n  maxTwinkleSpeed = 1,\n  className,\n}) => {\n  const [stars, setStars] = useState<StarProps[]>([]);\n  const canvasRef: RefObject<HTMLCanvasElement> =\n    useRef<HTMLCanvasElement>(null);\n\n  const generateStars = useCallback(\n    (width: number, height: number): StarProps[] => {\n      const area = width * height;\n      const numStars = Math.floor(area * starDensity);\n      return Array.from({ length: numStars }, () => {\n        const shouldTwinkle =\n          allStarsTwinkle || Math.random() < twinkleProbability;\n        return {\n          x: Math.random() * width,\n          y: Math.random() * height,\n          radius: Math.random() * 0.05 + 0.5,\n          opacity: Math.random() * 0.5 + 0.5,\n          twinkleSpeed: shouldTwinkle\n            ? minTwinkleSpeed +\n              Math.random() * (maxTwinkleSpeed - minTwinkleSpeed)\n            : null,\n        };\n      });\n    },\n    [\n      starDensity,\n      allStarsTwinkle,\n      twinkleProbability,\n      minTwinkleSpeed,\n      maxTwinkleSpeed,\n    ]\n  );\n\n  useEffect(() => {\n    const updateStars = () => {\n      if (canvasRef.current) {\n        const canvas = canvasRef.current;\n        const ctx = canvas.getContext(\"2d\");\n        if (!ctx) return;\n\n        const { width, height } = canvas.getBoundingClientRect();\n        canvas.width = width;\n        canvas.height = height;\n        setStars(generateStars(width, height));\n      }\n    };\n\n    updateStars();\n\n    const resizeObserver = new ResizeObserver(updateStars);\n    if (canvasRef.current) {\n      resizeObserver.observe(canvasRef.current);\n    }\n\n    return () => {\n      if (canvasRef.current) {\n        resizeObserver.unobserve(canvasRef.current);\n      }\n    };\n  }, [\n    starDensity,\n    allStarsTwinkle,\n    twinkleProbability,\n    minTwinkleSpeed,\n    maxTwinkleSpeed,\n    generateStars,\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    let animationFrameId: number;\n\n    const render = () => {\n      ctx.clearRect(0, 0, canvas.width, canvas.height);\n      stars.forEach((star) => {\n        ctx.beginPath();\n        ctx.arc(star.x, star.y, star.radius, 0, Math.PI * 2);\n        ctx.fillStyle = `rgba(255, 255, 255, ${star.opacity})`;\n        ctx.fill();\n\n        if (star.twinkleSpeed !== null) {\n          star.opacity =\n            0.5 +\n            Math.abs(Math.sin((Date.now() * 0.001) / star.twinkleSpeed) * 0.5);\n        }\n      });\n\n      animationFrameId = requestAnimationFrame(render);\n    };\n\n    render();\n\n    return () => {\n      cancelAnimationFrame(animationFrameId);\n    };\n  }, [stars]);\n\n  return (\n    <canvas\n      ref={canvasRef}\n      className={cn(\"h-full w-full absolute inset-0\", className)}\n    />\n  );\n};\n",
      "type": "registry:ui",
      "target": "components/ui/stars-background.tsx"
    }
  ],
  "author": "Manu Arora <hi@manuarora.in>",
  "title": "Stars Background"
}