{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "rocket-01",
  "title": "rocket-01",
  "description": "Animated rocket icon",
  "dependencies": [
    "motion"
  ],
  "files": [
    {
      "path": "icons/rocket-01.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 Rocket01IconHandle {\n  startAnimation: () => void;\n  stopAnimation: () => void;\n}\n\ninterface Rocket01IconProps extends HTMLAttributes<HTMLDivElement> {\n  size?: number;\n}\n\nconst ROCKET_BODY =\n  'M11.8013 6.48949L13.2869 5.00392C14.9596 3.3312 17.1495 2.63737 19.4671 2.52399C20.3686 2.47989 20.8193 2.45784 21.1807 2.81928C21.5422 3.18071 21.5201 3.63143 21.476 4.53289C21.3626 6.8505 20.6688 9.04042 18.9961 10.7131L17.5105 12.1987C16.2871 13.4221 15.9393 13.77 16.1961 15.097C16.4496 16.1107 16.6949 17.0923 15.9578 17.8294C15.0637 18.7235 14.2481 18.7235 13.354 17.8294L6.17058 10.646C5.27649 9.75188 5.27646 8.9363 6.17058 8.04219C6.90767 7.30509 7.88929 7.55044 8.90297 7.80389C10.23 8.06073 10.5779 7.71289 11.8013 6.48949Z';\n\nconst ROCKET_DURATION = 0.94;\nconst ROCKET_TIMES = [\n  0, 0.07, 0.14, 0.22, 0.32, 0.43, 0.5, 0.56, 0.64, 0.73, 0.82, 0.9,\n  0.96, 1,\n];\n\nconst departingRocketVariants: Variants = {\n  normal: {\n    transform: 'translate(0%, 0%) rotate(0deg)',\n    transition: { duration: 0.16, ease: [0.23, 1, 0.32, 1] },\n  },\n  animate: {\n    transform: [\n      'translate(0%, 0%) rotate(0deg)',\n      'translate(-4%, 4%) rotate(1.5deg)',\n      'translate(-1%, 1%) rotate(0.5deg)',\n      'translate(8%, -8%) rotate(-1deg)',\n      'translate(24%, -24%) rotate(-3deg)',\n      'translate(55%, -55%) rotate(-5deg)',\n      'translate(92%, -92%) rotate(-5deg)',\n      'translate(130%, -130%) rotate(-4deg)',\n      'translate(150%, -150%) rotate(-3deg)',\n      'translate(150%, -150%) rotate(-3deg)',\n      'translate(150%, -150%) rotate(-3deg)',\n      'translate(150%, -150%) rotate(-3deg)',\n      'translate(150%, -150%) rotate(-3deg)',\n      'translate(150%, -150%) rotate(-3deg)',\n    ],\n    transition: {\n      duration: ROCKET_DURATION,\n      ease: 'linear',\n      times: ROCKET_TIMES,\n    },\n  },\n};\n\nconst arrivingRocketVariants: Variants = {\n  normal: {\n    transform: 'translate(-150%, 150%) rotate(-6deg)',\n    transition: { duration: 0.16, ease: [0.23, 1, 0.32, 1] },\n  },\n  animate: {\n    transform: [\n      'translate(-150%, 150%) rotate(-6deg)',\n      'translate(-150%, 150%) rotate(-6deg)',\n      'translate(-150%, 150%) rotate(-6deg)',\n      'translate(-150%, 150%) rotate(-6deg)',\n      'translate(-150%, 150%) rotate(-6deg)',\n      'translate(-150%, 150%) rotate(-6deg)',\n      'translate(-150%, 150%) rotate(-6deg)',\n      'translate(-120%, 120%) rotate(-6deg)',\n      'translate(-85%, 85%) rotate(-5deg)',\n      'translate(-52%, 52%) rotate(-4deg)',\n      'translate(-26%, 26%) rotate(-2.5deg)',\n      'translate(-8%, 8%) rotate(-0.8deg)',\n      'translate(-1%, 1%) rotate(0.8deg)',\n      'translate(0%, 0%) rotate(0deg)',\n    ],\n    transition: {\n      duration: ROCKET_DURATION,\n      ease: 'linear',\n      times: ROCKET_TIMES,\n    },\n  },\n};\n\n// Exhaust keeps the rocket's direction but lags and stretches as acceleration builds.\nconst departingThrustVariants: Variants = {\n  normal: {\n    transform: 'none',\n    pathLength: 1,\n    pathOffset: 0,\n    visibility: 'visible',\n    transition: { duration: 0.18, ease: [0.23, 1, 0.32, 1] },\n  },\n  animate: (i: number) => {\n    const lag = [1, 0.78, 0.62][i] ?? 1;\n\n    return {\n      transform: [\n        'translate3d(0px, 0px, 0px) scale(1)',\n        `translate3d(${-0.12 * lag}px, ${0.12 * lag}px, 0px) scale(1.04)`,\n        `translate3d(${-0.35 * lag}px, ${0.35 * lag}px, 0px) scale(1.12)`,\n        `translate3d(${-0.9 * lag}px, ${0.9 * lag}px, 0px) scale(1.3)`,\n        `translate3d(${-1.8 * lag}px, ${1.8 * lag}px, 0px) scale(1.45)`,\n        `translate3d(${-3 * lag}px, ${3 * lag}px, 0px) scale(1.26)`,\n        `translate3d(${-4.4 * lag}px, ${4.4 * lag}px, 0px) scale(1.08)`,\n        `translate3d(${-5.6 * lag}px, ${5.6 * lag}px, 0px) scale(1)`,\n      ],\n      pathLength: [1, 1, 0.98, 0.9, 0.68, 0.36, 0, 0],\n      pathOffset: [0, 0, 0.02, 0.1, 0.32, 0.66, 1, 1],\n      visibility: [\n        'visible',\n        'visible',\n        'visible',\n        'visible',\n        'visible',\n        'visible',\n        'hidden',\n        'hidden',\n      ],\n      transition: {\n        duration: 0.64,\n        delay: i * 0.018,\n        ease: [0.77, 0, 0.175, 1],\n        times: [0, 0.1, 0.22, 0.38, 0.55, 0.72, 0.88, 1],\n      },\n    };\n  },\n};\n\n// The replacement rocket draws fresh exhaust outward from its nozzle as it arrives.\nconst arrivingThrustVariants: Variants = {\n  normal: {\n    transform: 'translate3d(-5px, 5px, 0px) scale(1.5)',\n    pathLength: 0,\n    pathOffset: 1,\n    visibility: 'hidden',\n  },\n  animate: (i: number) => {\n    const lag = [1, 0.76, 0.6][i] ?? 1;\n\n    return {\n      transform: [\n        `translate3d(${-5 * lag}px, ${5 * lag}px, 0px) scale(1.5)`,\n        `translate3d(${-4.2 * lag}px, ${4.2 * lag}px, 0px) scale(1.42)`,\n        `translate3d(${-2.4 * lag}px, ${2.4 * lag}px, 0px) scale(1.25)`,\n        `translate3d(${-0.55 * lag}px, ${0.55 * lag}px, 0px) scale(1.06)`,\n        'translate3d(0px, 0px, 0px) scale(1)',\n      ],\n      pathLength: [0, 0.18, 0.62, 1, 1],\n      pathOffset: [1, 0.82, 0.38, 0, 0],\n      visibility: ['hidden', 'visible', 'visible', 'visible', 'visible'],\n      transition: {\n        duration: 0.4,\n        delay: 0.5 + i * 0.022,\n        ease: [0.23, 1, 0.32, 1],\n        times: [0, 0.16, 0.46, 0.8, 1],\n      },\n    };\n  },\n};\n\nconst generatedGeometryVariants: Variants = {\n  normal: { visibility: 'hidden', transition: { duration: 0 } },\n  animate: { visibility: 'visible', transition: { duration: 0 } },\n};\n\nfunction RocketGeometry() {\n  return (\n    <>\n      <path\n        d={ROCKET_BODY}\n        stroke=\"currentColor\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"1.5\"\n      />\n      <path\n        d=\"M17.125 7H17M17.25 7C17.25 7.13807 17.1381 7.25 17 7.25C16.8619 7.25 16.75 7.13807 16.75 7C16.75 6.86193 16.8619 6.75 17 6.75C17.1381 6.75 17.25 6.86193 17.25 7Z\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        strokeWidth=\"1.5\"\n      />\n    </>\n  );\n}\n\ninterface RocketTrailsProps {\n  phase: 'departing' | 'arriving';\n}\n\nfunction RocketTrails({ phase }: RocketTrailsProps) {\n  const variants =\n    phase === 'departing'\n      ? departingThrustVariants\n      : arrivingThrustVariants;\n\n  return (\n    <>\n      <motion.path\n        d=\"M7.5 16.5L2.5 21.5\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeWidth=\"1.5\"\n        vectorEffect=\"non-scaling-stroke\"\n        variants={variants}\n        custom={0}\n        style={{ transformBox: 'view-box', originX: 0.3125, originY: 0.6875 }}\n      />\n      <motion.path\n        d=\"M10.5 19.5L8.5 21.5\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeWidth=\"1.5\"\n        vectorEffect=\"non-scaling-stroke\"\n        variants={variants}\n        custom={1}\n        style={{ transformBox: 'view-box', originX: 0.4375, originY: 0.8125 }}\n      />\n      <motion.path\n        d=\"M4.5 13.5L2.5 15.5\"\n        stroke=\"currentColor\"\n        strokeLinecap=\"round\"\n        strokeWidth=\"1.5\"\n        vectorEffect=\"non-scaling-stroke\"\n        variants={variants}\n        custom={2}\n        style={{ transformBox: 'view-box', originX: 0.1875, originY: 0.5625 }}\n      />\n    </>\n  );\n}\n\nconst Rocket01Icon = forwardRef<Rocket01IconHandle, Rocket01IconProps>(\n  ({ onMouseEnter, onMouseLeave, className, size = 28, ...props }, ref) => {\n    const controls = useAnimation();\n    const { handleMouseEnter, handleMouseLeave } = useIconAnimation({\n      controls,\n      loops: false,\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        <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=\"hidden\"\n        >\n          <motion.g\n            variants={departingRocketVariants}\n            animate={controls}\n            initial=\"normal\"\n            style={{ transformOrigin: '12px 12px' }}\n          >\n            <RocketGeometry />\n            <RocketTrails phase=\"departing\" />\n          </motion.g>\n          <motion.g\n            variants={generatedGeometryVariants}\n            animate={controls}\n            initial=\"normal\"\n          >\n            <motion.g\n              variants={arrivingRocketVariants}\n              animate={controls}\n              initial=\"normal\"\n              style={{ transformOrigin: '12px 12px' }}\n            >\n              <RocketGeometry />\n              <RocketTrails phase=\"arriving\" />\n            </motion.g>\n          </motion.g>\n        </svg>\n      </div>\n    );\n  }\n);\n\nRocket01Icon.displayName = 'Rocket01Icon';\n\nexport { Rocket01Icon };\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"
}