{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "fireworks",
  "type": "registry:component",
  "title": "Fireworks",
  "author": "Toby Belhome",
  "description": "Add animated fireworks to your site background with this festive component. Perfect for celebrations. Built with Tailwind CSS and Framer Motion.",
  "dependencies": [],
  "registryDependencies": [
    "button",
    "http://localhost:3000/r/fireworks.json"
  ],
  "files": [
    {
      "path": "examples/motion/backgrounds/fireworks/01/fireworks.tsx",
      "content": "\"use client\";\n\nimport React, { useEffect, useRef } from \"react\";\nimport { cn } from \"@/lib/utils\";\n\ninterface Particle {\n  x: number;\n  y: number;\n  color: string;\n  velocity: {\n    x: number;\n    y: number;\n  };\n  alpha: number;\n  lifetime: number;\n  size: number;\n}\n\ninterface Firework {\n  x: number;\n  y: number;\n  color: string;\n  velocity: {\n    x: number;\n    y: number;\n  };\n  particles: Particle[];\n  exploded: boolean;\n  timeToExplode: number;\n}\n\nfunction FireworksBackground({\n  children,\n  className\n}: {\n  children: React.ReactNode;\n  className?: string;\n}) {\n  const canvasRef = useRef<HTMLCanvasElement>(null);\n  const fireworksRef = useRef<Firework[]>([]);\n  const animationFrameRef = useRef<number>(0);\n  const lastFireworkTimeRef = useRef<number>(Date.now());\n\n  const isDarkModeRef = useRef<boolean>(false);\n\n  const colors = [\"#9b87f5\", \"#D946EF\", \"#F97316\", \"#0EA5E9\", \"#ea384c\", \"#10B981\", \"#FCD34D\"];\n\n  const createFirework = (x?: number, y?: number, targetY?: number) => {\n    const canvas = canvasRef.current;\n    if (!canvas) return;\n\n    const startX = x || Math.random() * canvas.width;\n    const startY = canvas.height;\n    const color = colors[Math.floor(Math.random() * colors.length)];\n    const angle = (Math.random() * Math.PI) / 2 - Math.PI / 4;\n    const velocity = 6 + Math.random() * 4;\n\n    const target = targetY || canvas.height * (0.1 + Math.random() * 0.4);\n\n    const firework: Firework = {\n      x: startX,\n      y: startY,\n      color,\n      velocity: {\n        x: Math.sin(angle) * velocity,\n        y: -Math.cos(angle) * velocity * 1.5\n      },\n      particles: [],\n      exploded: false,\n      timeToExplode: target\n    };\n\n    fireworksRef.current.push(firework);\n  };\n\n  const explodeFirework = (firework: Firework) => {\n    const particleCount = 60 + Math.floor(Math.random() * 40);\n\n    for (let i = 0; i < particleCount; i++) {\n      const angle = Math.random() * Math.PI * 2;\n      const velocity = Math.random() * 5 + 1;\n\n      firework.particles.push({\n        x: firework.x,\n        y: firework.y,\n        color: firework.color,\n        velocity: {\n          x: Math.cos(angle) * velocity * (0.5 + Math.random()),\n          y: Math.sin(angle) * velocity * (0.5 + Math.random())\n        },\n        alpha: 1,\n        lifetime: Math.random() * 30 + 30,\n        size: Math.random() * 3 + 1\n      });\n    }\n  };\n\n  const updateAndDraw = () => {\n    const canvas = canvasRef.current;\n    const ctx = canvas?.getContext(\"2d\");\n\n    if (!canvas || !ctx) return;\n\n    const fillStyle = isDarkModeRef.current ? \"rgba(0, 0, 0, 0.1)\" : \"rgba(255, 255, 255, 0.1)\";\n\n    ctx.fillStyle = fillStyle;\n    ctx.fillRect(0, 0, canvas.width, canvas.height);\n\n    const currentFireworks = fireworksRef.current;\n    for (let i = 0; i < currentFireworks.length; i++) {\n      const firework = currentFireworks[i];\n\n      if (!firework.exploded) {\n        firework.x += firework.velocity.x;\n        firework.y += firework.velocity.y;\n        firework.velocity.y += 0.1;\n\n        ctx.beginPath();\n        ctx.arc(firework.x, firework.y, 3, 0, Math.PI * 2);\n        ctx.fillStyle = firework.color;\n        ctx.fill();\n\n        if (\n          firework.y <= firework.timeToExplode ||\n          firework.velocity.y >= 0 ||\n          firework.x < 0 ||\n          firework.x > canvas.width\n        ) {\n          if (firework.y > 0 && firework.y < canvas.height) {\n            explodeFirework(firework);\n          }\n\n          firework.exploded = true;\n        }\n      } else {\n        for (let j = 0; j < firework.particles.length; j++) {\n          const particle = firework.particles[j];\n\n          particle.x += particle.velocity.x;\n          particle.y += particle.velocity.y;\n          particle.velocity.y += 0.05;\n          particle.alpha -= 1 / particle.lifetime;\n\n          if (particle.alpha <= 0.1) {\n            firework.particles.splice(j, 1);\n            j--;\n            continue;\n          }\n\n          ctx.globalAlpha = particle.alpha;\n          ctx.beginPath();\n          ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2);\n          ctx.fillStyle = particle.color;\n          ctx.fill();\n          ctx.globalAlpha = 1;\n        }\n\n        if (firework.particles.length === 0) {\n          currentFireworks.splice(i, 1);\n          i--;\n        }\n      }\n    }\n\n    const now = Date.now();\n    if (now - lastFireworkTimeRef.current > 1000 + Math.random() * 2000) {\n      const numberOfFireworks = Math.floor(Math.random() * 2) + 1;\n      for (let i = 0; i < numberOfFireworks; i++) {\n        createFirework();\n      }\n      lastFireworkTimeRef.current = now;\n    }\n\n    animationFrameRef.current = requestAnimationFrame(updateAndDraw);\n  };\n\n  useEffect(() => {\n    const canvas = canvasRef.current;\n    if (!canvas) return;\n\n    const updateCanvasSize = () => {\n      canvas.width = window.innerWidth;\n      canvas.height = window.innerHeight;\n    };\n\n    updateCanvasSize();\n    window.addEventListener(\"resize\", updateCanvasSize);\n\n    const checkDarkMode = () => {\n      const htmlElement = document.documentElement;\n      const isDarkClass = htmlElement.classList.contains(\"dark\");\n\n      const prefersDark = window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n\n      isDarkModeRef.current = isDarkClass || prefersDark;\n    };\n\n    const mediaQueryListener = (e: MediaQueryListEvent) => {\n      isDarkModeRef.current = e.matches;\n    };\n\n    const mediaQuery = window.matchMedia(\"(prefers-color-scheme: dark)\");\n    mediaQuery.addEventListener(\"change\", mediaQueryListener);\n\n    checkDarkMode();\n\n    for (let i = 0; i < 3; i++) {\n      createFirework();\n    }\n    lastFireworkTimeRef.current = Date.now();\n\n    animationFrameRef.current = requestAnimationFrame(updateAndDraw);\n\n    return () => {\n      if (animationFrameRef.current) {\n        cancelAnimationFrame(animationFrameRef.current);\n      }\n      window.removeEventListener(\"resize\", updateCanvasSize);\n      mediaQuery.removeEventListener(\"change\", mediaQueryListener);\n    };\n  }, []);\n\n  return (\n    <div className={cn(\"relative w-full\", className)}>\n      <canvas ref={canvasRef} className=\"absolute inset-0 h-full w-full\" />\n      {children}\n    </div>\n  );\n}\n\nexport default FireworksBackground;\n",
      "type": "registry:component",
      "target": "components/fireworks/fireworks.tsx"
    },
    {
      "path": "examples/motion/backgrounds/fireworks/01/page.tsx",
      "content": "import { Button } from \"@/components/ui/button\";\nimport FireworksBackground from \"./fireworks\";\n\nexport default function FireworksBackgroundExample() {\n  return (\n    <FireworksBackground className=\"flex aspect-4/2 items-center justify-center\">\n      <div className=\"z-10 space-y-4 text-center lg:space-y-6\">\n        <h4 className=\"text-2xl font-semibold text-black/80 lg:text-3xl dark:text-white/80\">\n          Bundui Components\n        </h4>\n        <Button>Discover Excellence</Button>\n      </div>\n    </FireworksBackground>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/fireworks/fireworks-example.tsx"
    }
  ],
  "meta": {
    "isPro": false
  }
}