{
  "name": "image-reveal",
  "type": "registry:ui",
  "title": "Image Reveal",
  "description": "Interactive cursor-following image reveal hover effect with spring damping and responsive boundaries.",
  "dependencies": [
    "framer-motion",
    "lucide-react"
  ],
  "files": [
    {
      "path": "registry/media/image-reveal/image-reveal.tsx",
      "type": "registry:ui",
      "target": "components/ui/image-reveal.tsx",
      "content": "import React, { useState, useRef } from \"react\";\nimport { motion, useMotionValue, useSpring } from \"framer-motion\";\nimport { ArrowUpRight } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface VisualItem {\n  id: string | number;\n  url: string;\n  label: string;\n  tag?: string;\n}\n\nexport interface ImageRevealProps {\n  items?: VisualItem[];\n  className?: string;\n}\n\nconst defaultItems: VisualItem[] = [\n  {\n    id: 1,\n    url: \"https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?auto=format&fit=crop&w=600&q=80\",\n    label: \"Neural Architecture\",\n    tag: \"Design\",\n  },\n  {\n    id: 2,\n    url: \"https://images.unsplash.com/photo-1634017839464-5c339ebe3cb4?auto=format&fit=crop&w=600&q=80\",\n    label: \"Kinetic Geometry\",\n    tag: \"3D\",\n  },\n  {\n    id: 3,\n    url: \"https://images.unsplash.com/photo-1550684848-fac1c5b4e853?auto=format&fit=crop&w=600&q=80\",\n    label: \"Liquid Interface\",\n    tag: \"Motion\",\n  },\n];\n\nexport const ImageReveal: React.FC<ImageRevealProps> = ({\n  items = defaultItems,\n  className,\n}) => {\n  const [focusedItem, setFocusedItem] = useState<VisualItem | null>(null);\n  const containerRef = useRef<HTMLDivElement>(null);\n\n  const cursorX = useMotionValue(0);\n  const cursorY = useMotionValue(0);\n  const smoothX = useSpring(cursorX, { stiffness: 350, damping: 35 });\n  const smoothY = useSpring(cursorY, { stiffness: 350, damping: 35 });\n\n  const onMouseTrack = (e: React.MouseEvent) => {\n    if (containerRef.current) {\n      const rect = containerRef.current.getBoundingClientRect();\n      cursorX.set(e.clientX - rect.left);\n      cursorY.set(e.clientY - rect.top);\n    }\n  };\n\n  return (\n    <div\n      ref={containerRef}\n      className={cn(\n        \"relative mx-auto w-full max-w-lg rounded-xl border border-zinc-200 dark:border-zinc-800 bg-white/70 dark:bg-zinc-900/70 backdrop-blur-md overflow-hidden select-none isolate\",\n        className\n      )}\n      onMouseMove={onMouseTrack}\n      onMouseLeave={() => setFocusedItem(null)}\n    >\n      <div className=\"divide-y divide-zinc-200/60 dark:divide-zinc-800/60\">\n        {items.map((item) => {\n          const isHovered = focusedItem?.id === item.id;\n          return (\n            <div\n              key={item.id}\n              className=\"p-4 cursor-pointer relative flex items-center justify-between group transition-colors hover:bg-zinc-100/50 dark:hover:bg-zinc-800/50\"\n              onMouseEnter={() => setFocusedItem(item)}\n            >\n              <div className=\"flex items-center gap-3 relative z-20\">\n                <span className=\"text-xs font-mono text-zinc-400 dark:text-zinc-500\">\n                  0{item.id}\n                </span>\n                <span className=\"text-base sm:text-lg font-semibold tracking-tight text-zinc-900 dark:text-zinc-100 group-hover:text-indigo-600 dark:group-hover:text-indigo-400 transition-colors\">\n                  {item.label}\n                </span>\n                {item.tag && (\n                  <span className=\"text-[10px] font-mono uppercase px-2 py-0.5 rounded-full bg-zinc-100 dark:bg-zinc-800 text-zinc-500 dark:text-zinc-400 border border-zinc-200/60 dark:border-zinc-700/60\">\n                    {item.tag}\n                  </span>\n                )}\n              </div>\n\n              <div className=\"relative z-20 flex items-center justify-center w-8 h-8 rounded-full border border-zinc-200 dark:border-zinc-700 bg-white/80 dark:bg-zinc-800/80 text-zinc-600 dark:text-zinc-300 group-hover:bg-indigo-600 group-hover:text-white group-hover:border-indigo-600 transition-all duration-200 group-hover:scale-105\">\n                <ArrowUpRight className=\"w-4 h-4\" />\n              </div>\n\n              <div\n                className={cn(\n                  \"h-[2px] bg-indigo-500 dark:bg-indigo-400 absolute bottom-0 left-0 transition-all duration-300 ease-out\",\n                  isHovered ? \"w-full\" : \"w-0\"\n                )}\n              />\n            </div>\n          );\n        })}\n      </div>\n\n      {focusedItem && (\n        <motion.div\n          className=\"absolute z-30 pointer-events-none rounded-xl overflow-hidden shadow-2xl border border-white/20 dark:border-zinc-700/50 hidden sm:block w-36 h-48\"\n          style={{\n            left: smoothX,\n            top: smoothY,\n            x: \"-50%\",\n            y: \"-50%\",\n          }}\n          initial={{ opacity: 0, scale: 0.6 }}\n          animate={{ opacity: 1, scale: 1 }}\n          exit={{ opacity: 0, scale: 0.6 }}\n          transition={{ type: \"spring\", stiffness: 350, damping: 28 }}\n        >\n          <img\n            src={focusedItem.url}\n            alt={focusedItem.label}\n            className=\"w-full h-full object-cover\"\n          />\n        </motion.div>\n      )}\n    </div>\n  );\n};\n\nexport default ImageReveal;\n"
    }
  ],
  "$schema": "https://www.cooki-ui.design/schema/registry-item.json"
}