{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "morphy-button",
  "type": "registry:ui",
  "title": "Morphy Button",
  "description": "Morphing button with dynamic dot motion, reversible animation.",
  "author": "Ahdeetai <https://aditya.is-cool.dev>",
  "registryDependencies": [],
  "dependencies": [
    "@radix-ui/react-slot",
    "class-variance-authority",
    "clsx",
    "tailwind-merge"
  ],
  "files": [
    {
      "type": "registry:ui",
      "path": "components/ui/morphy-button.tsx",
      "content": "'use client';\n\nimport * as React from 'react';\nimport { Slot } from '@radix-ui/react-slot';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from '@/lib/utils';\n\nconst morphyButtonVariants = cva(\n  'group relative inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm font-medium transition-all focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 overflow-hidden [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 rounded-full',\n  {\n    variants: {\n      size: {\n        default: 'h-9 px-6 py-2',\n        sm: 'h-8 px-5 text-xs',\n        lg: 'h-10 px-10',\n      },\n    },\n    defaultVariants: {\n      size: 'default',\n    },\n  },\n);\n\nexport interface MorphyButtonProps\n  extends\n    React.ButtonHTMLAttributes<HTMLButtonElement>,\n    VariantProps<typeof morphyButtonVariants> {\n  asChild?: boolean;\n  dotClassName?: string;\n  animate?: 'normal' | 'reverse';\n}\n\nconst MorphyButton = React.forwardRef<HTMLButtonElement, MorphyButtonProps>(\n  (\n    {\n      className,\n      size,\n      asChild = false,\n      children,\n      dotClassName,\n      animate = 'normal',\n      ...props\n    },\n    ref,\n  ) => {\n    const [isHovered, setIsHovered] = React.useState(false);\n    const timeoutRef = React.useRef<NodeJS.Timeout | null>(null);\n    const Comp = asChild ? Slot : 'button';\n    const buttonSize = size || 'default';\n\n    const handleTouchStart = () => {\n      setIsHovered(true);\n      if (timeoutRef.current) clearTimeout(timeoutRef.current);\n      timeoutRef.current = setTimeout(() => setIsHovered(false), 1500);\n    };\n\n    React.useEffect(() => {\n      return () => {\n        if (timeoutRef.current) clearTimeout(timeoutRef.current);\n      };\n    }, []);\n\n    const active = animate === 'reverse' ? !isHovered : isHovered;\n\n    const userHasTextColor = className?.includes('text-');\n\n    return (\n      <Comp\n        ref={ref}\n        onMouseEnter={() => setIsHovered(true)}\n        onMouseLeave={() => setIsHovered(false)}\n        onTouchStart={handleTouchStart}\n        className={cn(\n          morphyButtonVariants({ size }),\n          'transition-colors duration-700 ease-in-out border',\n          active ? 'border-black dark:border-white' : 'border-transparent',\n          className,\n        )}\n        {...props}\n      >\n        <div\n          className={cn(\n            'absolute inset-0 transition-colors duration-700 ease-in-out rounded-[inherit]',\n            active ? 'bg-zinc-200 dark:bg-zinc-800' : 'bg-black dark:bg-white',\n          )}\n        />\n        <div\n          className={cn(\n            'absolute top-1/2 -translate-y-1/2 rounded-full transition-all duration-700 ease-in-out bg-black dark:bg-white',\n            'w-[200%] h-[200%] -left-full',\n            buttonSize === 'sm' &&\n              (active ? 'w-2 h-2 left-3' : 'w-[200%] h-[200%] -left-full'),\n            buttonSize === 'default' &&\n              (active ? 'w-2.5 h-2.5 left-3' : 'w-[200%] h-[200%] -left-full'),\n            buttonSize === 'lg' &&\n              (active ? 'w-3 h-3 left-4' : 'w-[200%] h-[200%] -left-full'),\n            dotClassName,\n          )}\n        />\n        <span\n          className={cn(\n            'relative z-10 font-bold transition-all duration-700 ease-in-out',\n            active ? 'translate-x-1.5' : 'translate-x-0',\n            !userHasTextColor &&\n              (active\n                ? 'text-black dark:text-white'\n                : 'text-white dark:text-black'),\n          )}\n        >\n          {children}\n        </span>\n      </Comp>\n    );\n  },\n);\n\nMorphyButton.displayName = 'MorphyButton';\n\nexport { MorphyButton, morphyButtonVariants };\n"
    },
    {
      "type": "registry:lib",
      "path": "lib/utils.ts",
      "content": "import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs));\n}"
    }
  ]
}
