{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "smile",
  "title": "smile",
  "description": "Animated smile icon",
  "dependencies": [
    "motion"
  ],
  "files": [
    {
      "path": "icons/smile.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 SmileIconHandle {\n  startAnimation: () => void;\n  stopAnimation: () => void;\n}\n\ninterface SmileIconProps extends HTMLAttributes<HTMLDivElement> {\n  size?: number;\n}\n\n// recognition: a small head tilt, the smile widens, and it blinks —\n// the eyes are drawn shut and open again via scaleY at their own height\nconst svgVariants: Variants = {\n  normal: { rotate: 0, transition: { duration: 0.3 } },\n  animate: {\n    rotate: [0, -6, -6, 0],\n    transition: { duration: 1.1, ease: 'easeInOut', times: [0, 0.25, 0.75, 1] },\n  },\n};\n\nconst mouthVariants: Variants = {\n  normal: {\n    d: 'M8 15C8.91212 16.2144 10.3643 17 12 17C13.6357 17 15.0879 16.2144 16 15',\n    transition: { duration: 0.3, ease: 'easeOut' },\n  },\n  animate: {\n    d: [\n      'M8 15C8.91212 16.2144 10.3643 17 12 17C13.6357 17 15.0879 16.2144 16 15',\n      'M7.6 14.6C8.7 16.6 10.3 17.7 12 17.7C13.7 17.7 15.3 16.6 16.4 14.6',\n      'M7.8 14.8C8.8 16.45 10.35 17.35 12 17.35C13.65 17.35 15.2 16.45 16.2 14.8',\n      'M8 15C8.91212 16.2144 10.3643 17 12 17C13.6357 17 15.0879 16.2144 16 15',\n    ],\n    transition: {\n      duration: 1.1,\n      times: [0, 0.36, 0.76, 1],\n      ease: [0.23, 1, 0.32, 1],\n    },\n  },\n};\n\nconst eyeVariants: Variants = {\n  normal: { scaleY: 1, transition: { duration: 0.2 } },\n  animate: {\n    scaleY: [1, 0.15, 1, 1, 0.15, 1],\n    transition: {\n      duration: 1.1,\n      times: [0, 0.12, 0.24, 0.5, 0.62, 0.74],\n      ease: 'easeInOut',\n    },\n  },\n};\n\nconst SmileIcon = forwardRef<SmileIconHandle, SmileIconProps>(\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        <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          <circle\n            cx=\"12\"\n            cy=\"12\"\n            r=\"10\"\n            stroke=\"currentColor\"\n            strokeLinecap=\"round\"\n            strokeLinejoin=\"round\"\n            strokeWidth=\"1.5\"\n          />\n          <motion.path\n            d=\"M8 15C8.91212 16.2144 10.3643 17 12 17C13.6357 17 15.0879 16.2144 16 15\"\n            stroke=\"currentColor\"\n            strokeLinecap=\"round\"\n            strokeLinejoin=\"round\"\n            strokeWidth=\"1.5\"\n            variants={mouthVariants}\n            animate={controls}\n            initial=\"normal\"\n          />\n          <motion.path\n            d=\"M15.625 8.387V8.91649M8.375 8.387V8.91649M8.75 8.75C8.75 8.33579 8.58211 8 8.375 8C8.16789 8 8 8.33579 8 8.75C8 9.16421 8.16789 9.5 8.375 9.5C8.58211 9.5 8.75 9.16421 8.75 8.75ZM16 8.75C16 8.33579 15.8321 8 15.625 8C15.4179 8 15.25 8.33579 15.25 8.75C15.25 9.16421 15.4179 9.5 15.625 9.5C15.8321 9.5 16 9.16421 16 8.75Z\"\n            stroke=\"currentColor\"\n            strokeLinecap=\"round\"\n            strokeLinejoin=\"round\"\n            strokeWidth=\"1.5\"\n            variants={eyeVariants}\n            animate={controls}\n            initial=\"normal\"\n            style={{ transformOrigin: '12px 8.7px' }}\n          />\n        </motion.svg>\n      </div>\n    );\n  }\n);\n\nSmileIcon.displayName = 'SmileIcon';\n\nexport { SmileIcon };\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"
}