{
  "name": "tilt-spotlight",
  "type": "registry:ui",
  "componentName": "tilt",
  "description": "Tilt effect combined with spotlight for enhanced hover interactions.",
  "files": [
    {
      "path": "tilt-spotlight.tsx",
      "content": "import { Spotlight } from '@/components/core/spotlight';\nimport { Tilt } from '@/components/core/tilt';\n\nexport function TiltSpotlight() {\n  return (\n    <div className='aspect-video max-w-sm'>\n      <Tilt\n        rotationFactor={6}\n        isRevese\n        style={{\n          transformOrigin: 'center center',\n        }}\n        springOptions={{\n          stiffness: 26.7,\n          damping: 4.1,\n          mass: 0.2,\n        }}\n        className='group relative rounded-lg'\n      >\n        <Spotlight\n          className='z-10 from-white/50 via-white/20 to-white/10 blur-2xl'\n          size={248}\n          springOptions={{\n            stiffness: 26.7,\n            damping: 4.1,\n            mass: 0.2,\n          }}\n        />\n        <img\n          // src='https://images.beta.cosmos.so/f7fcb95d-981b-4cb3-897f-e35f6c20e830?format=jpeg'\n          src='https://images.beta.cosmos.so/40fbc749-6796-485b-9588-20204dd7c8f0?format=jpeg'\n          alt='Ghost in the shell - Kôkaku kidôtai'\n          className='h-32 w-full rounded-lg object-cover grayscale duration-700 group-hover:grayscale-0'\n        />\n      </Tilt>\n      <div className='flex flex-col space-y-0.5 pb-0 pt-3'>\n        <h3 className='font-mono text-sm font-medium text-zinc-500 dark:text-zinc-400'>\n          2001: A Space Odyssey\n        </h3>\n        <p className='text-sm text-black dark:text-white'>Stanley Kubrick</p>\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:component"
    },
    {
      "path": "components/core/tilt.tsx",
      "content": "'use client';\n\nimport React, { useRef } from 'react';\nimport {\n  motion,\n  useMotionTemplate,\n  useMotionValue,\n  useSpring,\n  useTransform,\n  MotionStyle,\n  SpringOptions,\n} from 'motion/react';\n\nexport type TiltProps = {\n  children: React.ReactNode;\n  className?: string;\n  style?: MotionStyle;\n  rotationFactor?: number;\n  isRevese?: boolean;\n  springOptions?: SpringOptions;\n};\n\nexport function Tilt({\n  children,\n  className,\n  style,\n  rotationFactor = 15,\n  isRevese = false,\n  springOptions,\n}: TiltProps) {\n  const ref = useRef<HTMLDivElement>(null);\n\n  const x = useMotionValue(0);\n  const y = useMotionValue(0);\n\n  const xSpring = useSpring(x, springOptions);\n  const ySpring = useSpring(y, springOptions);\n\n  const rotateX = useTransform(\n    ySpring,\n    [-0.5, 0.5],\n    isRevese\n      ? [rotationFactor, -rotationFactor]\n      : [-rotationFactor, rotationFactor]\n  );\n  const rotateY = useTransform(\n    xSpring,\n    [-0.5, 0.5],\n    isRevese\n      ? [-rotationFactor, rotationFactor]\n      : [rotationFactor, -rotationFactor]\n  );\n\n  const transform = useMotionTemplate`perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`;\n\n  const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {\n    if (!ref.current) return;\n\n    const rect = ref.current.getBoundingClientRect();\n    const width = rect.width;\n    const height = rect.height;\n    const mouseX = e.clientX - rect.left;\n    const mouseY = e.clientY - rect.top;\n\n    const xPos = mouseX / width - 0.5;\n    const yPos = mouseY / height - 0.5;\n\n    x.set(xPos);\n    y.set(yPos);\n  };\n\n  const handleMouseLeave = () => {\n    x.set(0);\n    y.set(0);\n  };\n\n  return (\n    <motion.div\n      ref={ref}\n      className={className}\n      style={{\n        transformStyle: 'preserve-3d',\n        ...style,\n        transform,\n      }}\n      onMouseMove={handleMouseMove}\n      onMouseLeave={handleMouseLeave}\n    >\n      {children}\n    </motion.div>\n  );\n}\n",
      "type": "registry:ui"
    },
    {
      "path": "components/core/spotlight.tsx",
      "content": "'use client';\nimport React, { useRef, useState, useCallback, useEffect } from 'react';\nimport { motion, useSpring, useTransform, SpringOptions } from 'motion/react';\nimport { cn } from '@/lib/utils';\n\nexport type SpotlightProps = {\n  className?: string;\n  size?: number;\n  springOptions?: SpringOptions;\n};\n\nexport function Spotlight({\n  className,\n  size = 200,\n  springOptions = { bounce: 0 },\n}: SpotlightProps) {\n  const containerRef = useRef<HTMLDivElement>(null);\n  const [isHovered, setIsHovered] = useState(false);\n  const [parentElement, setParentElement] = useState<HTMLElement | null>(null);\n\n  const mouseX = useSpring(0, springOptions);\n  const mouseY = useSpring(0, springOptions);\n\n  const spotlightLeft = useTransform(mouseX, (x) => `${x - size / 2}px`);\n  const spotlightTop = useTransform(mouseY, (y) => `${y - size / 2}px`);\n\n  useEffect(() => {\n    if (containerRef.current) {\n      const parent = containerRef.current.parentElement;\n      if (parent) {\n        parent.style.position = 'relative';\n        parent.style.overflow = 'hidden';\n        setParentElement(parent);\n      }\n    }\n  }, []);\n\n  const handleMouseMove = useCallback(\n    (event: MouseEvent) => {\n      if (!parentElement) return;\n      const { left, top } = parentElement.getBoundingClientRect();\n      mouseX.set(event.clientX - left);\n      mouseY.set(event.clientY - top);\n    },\n    [mouseX, mouseY, parentElement]\n  );\n\n  useEffect(() => {\n    if (!parentElement) return;\n\n    const abortController = new AbortController();\n\n    parentElement.addEventListener('mousemove', handleMouseMove, {\n      signal: abortController.signal,\n    });\n    parentElement.addEventListener('mouseenter', () => setIsHovered(true), {\n      signal: abortController.signal,\n    });\n    parentElement.addEventListener('mouseleave', () => setIsHovered(false), {\n      signal: abortController.signal,\n    });\n\n    return () => {\n      abortController.abort();\n    };\n  }, [parentElement, handleMouseMove]);\n\n  return (\n    <motion.div\n      ref={containerRef}\n      className={cn(\n        'pointer-events-none absolute rounded-full bg-[radial-gradient(circle_at_center,var(--tw-gradient-stops),transparent_80%)] blur-xl transition-opacity duration-200',\n        'from-zinc-100 via-zinc-200 to-zinc-400 dark:from-zinc-50 dark:via-zinc-100 dark:to-zinc-200',\n        isHovered ? 'opacity-100' : 'opacity-0',\n        className\n      )}\n      style={{\n        width: size,\n        height: size,\n        left: spotlightLeft,\n        top: spotlightTop,\n      }}\n    />\n  );\n}\n",
      "type": "registry:ui"
    }
  ]
}