{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "ghost",
  "title": "ghost",
  "description": "Animated ghost icon",
  "dependencies": [
    "motion"
  ],
  "files": [
    {
      "path": "icons/ghost.tsx",
      "content": "'use client';\n\nimport type { Variants } from 'motion/react';\nimport { motion, useAnimation } from 'motion/react';\nimport type { HTMLAttributes } from 'react';\nimport { forwardRef } from 'react';\nimport { useIconAnimation } from '@/lib/use-icon-animation';\nimport { cn } from '@/lib/utils';\n\nexport interface GhostIconHandle {\n  startAnimation: () => void;\n  stopAnimation: () => void;\n}\n\ninterface GhostIconProps extends HTMLAttributes<HTMLDivElement> {\n  size?: number;\n}\n\n// while you hover, it haunts — a weightless bob with shifty eyes that\n// glance one way, then the other\nconst svgVariants: Variants = {\n  normal: { translateY: 0, rotate: 0, transition: { duration: 0.3 } },\n  animate: {\n    translateY: [0, -1.8, 0],\n    rotate: [0, -3, 3, 0],\n    transition: { duration: 2, ease: 'easeInOut', repeat: Infinity },\n  },\n};\n\nconst eyeVariants: Variants = {\n  normal: { translateX: 0, transition: { duration: 0.3 } },\n  animate: {\n    translateX: [0, -0.9, -0.9, 0.9, 0.9, 0],\n    transition: {\n      duration: 2,\n      ease: 'easeInOut',\n      times: [0, 0.15, 0.4, 0.55, 0.85, 1],\n      repeat: Infinity,\n    },\n  },\n};\n\nconst GhostIcon = forwardRef<GhostIconHandle, GhostIconProps>(\n  ({ onMouseEnter, onMouseLeave, className, size = 28, ...props }, ref) => {\n    const controls = useAnimation();\n    const { handleMouseEnter, handleMouseLeave } = useIconAnimation({\n      controls,\n      loops: true,\n      onMouseEnter,\n      onMouseLeave,\n      ref,\n    });\n\n    return (\n      <div\n        className={cn(className)}\n        onMouseEnter={handleMouseEnter}\n        onMouseLeave={handleMouseLeave}\n        {...props}\n      >\n        <motion.svg\n          xmlns=\"http://www.w3.org/2000/svg\"\n          width={size}\n          height={size}\n          viewBox=\"0 0 24 24\"\n          fill=\"none\"\n          overflow=\"visible\"\n          variants={svgVariants}\n          animate={controls}\n          initial=\"normal\"\n          style={{ transformOrigin: '12px 12px' }}\n        >\n          <path\n            d=\"M12 2C8.20498 2 5.12851 5.13401 5.12851 9V10C2.9875 10 1.91699 10 1.63289 10.4961C1.55535 10.6315 1.51014 10.7836 1.50092 10.9401C1.46717 11.5135 2.35788 12.1184 4.13931 13.3282L4.79658 13.7746C5.00395 13.9154 5.12851 14.1525 5.12851 14.4064C5.12851 18.6002 8.46587 22 12.5827 22H19.7129C19.8323 22 19.892 22 19.9307 21.9969C20.6674 21.9374 21.0779 21.1012 20.6825 20.4651C20.6617 20.4317 20.6259 20.383 20.5543 20.2857C20.4436 20.1354 20.3879 20.0597 20.3355 19.985C19.4192 18.6789 18.9098 17.1221 18.8736 15.5172C18.8715 15.4253 18.8715 15.3311 18.8715 15.1429V14.6686C18.8715 14.5388 18.8715 14.4739 18.8788 14.413C18.9087 14.1661 19.0278 13.9394 19.2129 13.7771C19.2585 13.7372 19.3115 13.7011 19.4176 13.6291L19.8607 13.3282C21.6421 12.1184 22.5328 11.5135 22.4991 10.9401C22.4899 10.7836 22.4447 10.6315 22.3671 10.4961C22.083 10 21.0125 10 18.8715 10V9C18.8715 5.13401 15.795 2 12 2Z\"\n            stroke=\"currentColor\"\n            strokeLinecap=\"round\"\n            strokeLinejoin=\"round\"\n            strokeWidth=\"1.5\"\n          />\n          <motion.path\n            d=\"M15 10.125V10.25M15.25 10.25C15.25 10.3881 15.1381 10.5 15 10.5C14.8619 10.5 14.75 10.3881 14.75 10.25C14.75 10.1119 14.8619 10 15 10C15.1381 10 15.25 10.1119 15.25 10.25Z\"\n            stroke=\"currentColor\"\n            strokeLinecap=\"round\"\n            strokeLinejoin=\"round\"\n            strokeWidth=\"1.5\"\n            variants={eyeVariants}\n            animate={controls}\n            initial=\"normal\"\n          />\n          <motion.path\n            d=\"M9 10.125V10.25M9.25 10.25C9.25 10.3881 9.13807 10.5 9 10.5C8.86193 10.5 8.75 10.3881 8.75 10.25C8.75 10.1119 8.86193 10 9 10C9.13807 10 9.25 10.1119 9.25 10.25Z\"\n            stroke=\"currentColor\"\n            strokeLinecap=\"round\"\n            strokeLinejoin=\"round\"\n            strokeWidth=\"1.5\"\n            variants={eyeVariants}\n            animate={controls}\n            initial=\"normal\"\n          />\n        </motion.svg>\n      </div>\n    );\n  }\n);\n\nGhostIcon.displayName = 'GhostIcon';\n\nexport { GhostIcon };\n",
      "type": "registry:ui"
    },
    {
      "path": "lib/use-icon-animation.ts",
      "content": "'use client';\n\nimport type { LegacyAnimationControls } from 'motion/react';\nimport { useReducedMotion } from 'motion/react';\nimport type {\n  ForwardedRef,\n  MouseEventHandler,\n} from 'react';\nimport { useCallback, useImperativeHandle, useRef } from 'react';\n\nexport interface AnimatedIconHandle {\n  startAnimation: () => void;\n  stopAnimation: () => void;\n}\n\ninterface UseIconAnimationOptions {\n  controls: LegacyAnimationControls;\n  loops?: boolean;\n  onMouseEnter?: MouseEventHandler<HTMLDivElement>;\n  onMouseLeave?: MouseEventHandler<HTMLDivElement>;\n  ref: ForwardedRef<AnimatedIconHandle>;\n}\n\nexport function useIconAnimation({\n  controls,\n  loops = false,\n  onMouseEnter,\n  onMouseLeave,\n  ref,\n}: UseIconAnimationOptions) {\n  const shouldReduceMotion = useReducedMotion();\n  const isControlledRef = useRef(false);\n  const isPlayingRef = useRef(false);\n  const runRef = useRef(0);\n\n  const startAnimation = useCallback(() => {\n    if (shouldReduceMotion || isPlayingRef.current) return;\n\n    isPlayingRef.current = true;\n    const run = ++runRef.current;\n    controls.set('normal');\n    void controls.start('animate').then(() => {\n      if (runRef.current === run) isPlayingRef.current = false;\n    });\n  }, [controls, shouldReduceMotion]);\n\n  const stopAnimation = useCallback(() => {\n    if (!loops) return;\n\n    runRef.current++;\n    isPlayingRef.current = false;\n    void controls.start('normal');\n  }, [controls, loops]);\n\n  useImperativeHandle(\n    ref,\n    () => {\n      isControlledRef.current = true;\n      return { startAnimation, stopAnimation };\n    },\n    [startAnimation, stopAnimation]\n  );\n\n  const handleMouseEnter = useCallback<MouseEventHandler<HTMLDivElement>>(\n    (event) => {\n      onMouseEnter?.(event);\n      if (!isControlledRef.current) startAnimation();\n    },\n    [onMouseEnter, startAnimation]\n  );\n\n  const handleMouseLeave = useCallback<MouseEventHandler<HTMLDivElement>>(\n    (event) => {\n      onMouseLeave?.(event);\n      if (!isControlledRef.current) stopAnimation();\n    },\n    [onMouseLeave, stopAnimation]\n  );\n\n  return { handleMouseEnter, handleMouseLeave };\n}\n",
      "type": "registry:lib"
    }
  ],
  "type": "registry:ui"
}