{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "magical-mouse",
  "type": "registry:ui",
  "title": "Magical Mouse",
  "description": "A magical mouse component for Next.js apps with next-themes and Tailwind CSS, supporting system, light, and dark modes.",
  "dependencies": [
    "lucide-react"
  ],
  "files": [
    {
      "path": "registry/bucharitesh/magical-mouse.tsx",
      "content": "'use client';\n\nimport { cn } from '@/lib/utils';\nimport { Sparkle } from 'lucide-react';\nimport * as React from 'react';\nimport { createRoot } from 'react-dom/client';\n\ninterface Point {\n  x: number;\n  y: number;\n}\n\ninterface MouseSparklesProps {\n  /**\n   * Custom icon component to render instead of the default Sparkle\n   * @default Sparkle from lucide-react\n   */\n  icon?: React.ReactNode;\n  /**\n   * Duration of the star animation in milliseconds\n   * @default 1500\n   */\n  starAnimationDuration?: number;\n  /**\n   * Minimum time between star spawns in milliseconds\n   * @default 250\n   */\n  minimumTimeBetweenStars?: number;\n  /**\n   * Minimum distance between star spawns in pixels\n   * @default 75\n   */\n  minimumDistanceBetweenStars?: number;\n  /**\n   * Duration of the glow effect in milliseconds\n   * @default 75\n   */\n  glowDuration?: number;\n  /**\n   * Maximum spacing between glow points in pixels\n   * @default 10\n   */\n  maximumGlowPointSpacing?: number;\n  /**\n   * Colors for the stars in RGB format\n   * @default [\"249 146 253\", \"252 254 255\"]\n   */\n  colors?: string[];\n  /**\n   * Sizes for the stars\n   * @default [\"1.4rem\", \"1rem\", \"0.6rem\"]\n   */\n  sizes?: string[];\n  /**\n   * Custom class name\n   */\n  className?: string;\n}\n\nconst MouseSparkles = React.forwardRef<HTMLDivElement, MouseSparklesProps>(\n  (\n    {\n      icon: Icon = <Sparkle className=\"h-full w-full\" />,\n      starAnimationDuration = 1500,\n      minimumTimeBetweenStars = 250,\n      minimumDistanceBetweenStars = 75,\n      glowDuration = 75,\n      maximumGlowPointSpacing = 10,\n      colors = ['249 146 253', '252 254 255'],\n      sizes = ['1.4rem', '1rem', '0.6rem'],\n      className,\n      ...props\n    },\n    ref\n  ) => {\n    const configRef = React.useRef({\n      starAnimationDuration,\n      minimumTimeBetweenStars,\n      minimumDistanceBetweenStars,\n      glowDuration,\n      maximumGlowPointSpacing,\n      colors,\n      sizes,\n      animations: ['fall-1', 'fall-2', 'fall-3'],\n    });\n\n    const lastRef = React.useRef({\n      starTimestamp: Date.now(),\n      starPosition: { x: 0, y: 0 },\n      mousePosition: { x: 0, y: 0 },\n    });\n\n    let count = 0;\n\n    const createStar = React.useCallback(\n      (position: Point) => {\n        const wrapper = document.createElement('div');\n        const color = selectRandom(configRef.current.colors);\n        const size = selectRandom(configRef.current.sizes);\n\n        wrapper.className = cn(\n          'absolute z-[10000] pointer-events-none',\n          className\n        );\n        wrapper.style.left = `${position.x}px`;\n        wrapper.style.top = `${position.y}px`;\n        wrapper.style.fontSize = size;\n        wrapper.style.color = `rgb(${color})`;\n        wrapper.style.textShadow = `0px 0px 1.5rem rgb(${color} / 0.5)`;\n        wrapper.style.animationName = configRef.current.animations[count++ % 3];\n        wrapper.style.animationDuration = `${configRef.current.starAnimationDuration}ms`;\n        wrapper.style.animationFillMode = 'forwards';\n\n        document.body.appendChild(wrapper);\n\n        const root = createRoot(wrapper);\n        root.render(Icon);\n\n        setTimeout(() => {\n          root.unmount();\n          document.body.removeChild(wrapper);\n        }, configRef.current.starAnimationDuration);\n      },\n      [Icon, className]\n    );\n\n    const createGlowPoint = React.useCallback(\n      (position: Point) => {\n        const glow = document.createElement('div');\n        glow.className = cn('absolute z-[9999] pointer-events-none', className);\n        glow.style.left = `${position.x}px`;\n        glow.style.top = `${position.y}px`;\n        glow.style.boxShadow = '0rem 0rem 1.2rem 0.6rem rgb(239 42 201)';\n\n        document.body.appendChild(glow);\n        setTimeout(\n          () => document.body.removeChild(glow),\n          configRef.current.glowDuration\n        );\n      },\n      [className]\n    );\n\n    const createGlow = React.useCallback(\n      (last: Point, current: Point) => {\n        const distance = calcDistance(last, current);\n        const quantity = Math.max(\n          Math.floor(distance / configRef.current.maximumGlowPointSpacing),\n          1\n        );\n\n        const dx = (current.x - last.x) / quantity;\n        const dy = (current.y - last.y) / quantity;\n\n        Array.from({ length: quantity }).forEach((_, index) => {\n          const x = last.x + dx * index;\n          const y = last.y + dy * index;\n          createGlowPoint({ x, y });\n        });\n      },\n      [createGlowPoint]\n    );\n\n    const handleOnMove = React.useCallback(\n      (e: { clientX: number; clientY: number }) => {\n        const mousePosition = { x: e.clientX, y: e.clientY };\n\n        if (\n          lastRef.current.mousePosition.x === 0 &&\n          lastRef.current.mousePosition.y === 0\n        ) {\n          lastRef.current.mousePosition = mousePosition;\n        }\n\n        const now = Date.now();\n        const hasMovedFarEnough =\n          calcDistance(lastRef.current.starPosition, mousePosition) >=\n          configRef.current.minimumDistanceBetweenStars;\n        const hasBeenLongEnough =\n          now - lastRef.current.starTimestamp >\n          configRef.current.minimumTimeBetweenStars;\n\n        if (hasMovedFarEnough || hasBeenLongEnough) {\n          createStar(mousePosition);\n          lastRef.current.starTimestamp = now;\n          lastRef.current.starPosition = mousePosition;\n        }\n\n        createGlow(lastRef.current.mousePosition, mousePosition);\n        lastRef.current.mousePosition = mousePosition;\n      },\n      [createStar, createGlow]\n    );\n\n    React.useEffect(() => {\n      window.addEventListener('mousemove', handleOnMove);\n      window.addEventListener('touchmove', (e) => handleOnMove(e.touches[0]));\n      document.body.addEventListener('mouseleave', () => {\n        lastRef.current.mousePosition = { x: 0, y: 0 };\n      });\n\n      return () => {\n        window.removeEventListener('mousemove', handleOnMove);\n        window.removeEventListener('touchmove', (e) =>\n          handleOnMove(e.touches[0])\n        );\n        document.body.removeEventListener('mouseleave', () => {\n          lastRef.current.mousePosition = { x: 0, y: 0 };\n        });\n      };\n    }, [handleOnMove]);\n\n    return null;\n  }\n);\n\nexport function rand(min: number, max: number) {\n  return Math.floor(Math.random() * (max - min + 1)) + min;\n}\n\nexport function selectRandom<T>(items: T[]): T {\n  return items[rand(0, items.length - 1)];\n}\n\nexport function calcDistance(a: Point, b: Point) {\n  const diffX = b.x - a.x;\n  const diffY = b.y - a.y;\n  return Math.sqrt(diffX ** 2 + diffY ** 2);\n}\n\nMouseSparkles.displayName = 'MouseSparkles';\n\nexport { MouseSparkles };\n",
      "type": "registry:ui"
    }
  ],
  "css": {
    "@keyframes fall-1": {
      "0%": {
        "transform": "translate(0px, 0px) rotateX(45deg) rotateY(30deg) rotateZ(0deg) scale(0.25)",
        "opacity": "0"
      },
      "25%": {
        "transform": "translate(10px, -10px) rotateX(45deg) rotateY(30deg) rotateZ(0deg) scale(1)",
        "opacity": "1"
      }
    },
    "@keyframes fall-2": {
      "0%": {
        "transform": "translate(0px, 0px) rotateX(-20deg) rotateY(10deg) scale(0.25)",
        "opacity": "0"
      },
      "10%": {
        "transform": "translate(-10px, -5px) rotateX(-20deg) rotateY(10deg) scale(1)",
        "opacity": "1"
      }
    },
    "@keyframes fall-3": {
      "0%": {
        "transform": "translate(0px, 0px) rotateX(0deg) rotateY(45deg) scale(0.5)",
        "opacity": "0"
      },
      "15%": {
        "transform": "translate(7px, 5px) rotateX(0deg) rotateY(45deg) scale(1)",
        "opacity": "1"
      }
    }
  }
}