{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "copy-button",
  "type": "registry:ui",
  "title": "Copy Button",
  "description": "A button with a copy to clipboard animation.",
  "dependencies": [
    "motion",
    "lucide-react",
    "class-variance-authority"
  ],
  "files": [
    {
      "path": "registry/buttons/copy/index.tsx",
      "content": "'use client';\n\nimport * as React from 'react';\nimport { AnimatePresence, HTMLMotionProps, motion } from 'motion/react';\nimport { CheckIcon, CopyIcon } from 'lucide-react';\nimport { cva, type VariantProps } from 'class-variance-authority';\n\nimport { cn } from '@/lib/utils';\n\nconst buttonVariants = cva(\n  'inline-flex items-center justify-center cursor-pointer rounded-md transition-colors disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',\n  {\n    variants: {\n      variant: {\n        default:\n          'bg-primary text-primary-foreground shadow-xs hover:bg-primary/90',\n        muted: 'bg-muted text-muted-foreground',\n        destructive:\n          'bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',\n        outline:\n          'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50',\n        secondary:\n          'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80',\n        ghost:\n          'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',\n      },\n      size: {\n        default: 'size-8 rounded-lg [&_svg]:size-4',\n        sm: 'size-6 [&_svg]:size-3',\n        md: 'size-10 rounded-lg [&_svg]:size-5',\n        lg: 'size-12 rounded-xl [&_svg]:size-6',\n      },\n    },\n    defaultVariants: {\n      variant: 'default',\n      size: 'default',\n    },\n  },\n);\n\ntype CopyButtonProps = Omit<HTMLMotionProps<'button'>, 'children' | 'onCopy'> &\n  VariantProps<typeof buttonVariants> & {\n    content?: string;\n    delay?: number;\n    onCopy?: (content: string) => void;\n    isCopied?: boolean;\n    onCopyChange?: (isCopied: boolean) => void;\n  };\n\nfunction CopyButton({\n  content,\n  className,\n  size,\n  variant,\n  delay = 3000,\n  onClick,\n  onCopy,\n  isCopied,\n  onCopyChange,\n  ...props\n}: CopyButtonProps) {\n  const [localIsCopied, setLocalIsCopied] = React.useState(isCopied ?? false);\n  const Icon = localIsCopied ? CheckIcon : CopyIcon;\n\n  React.useEffect(() => {\n    setLocalIsCopied(isCopied ?? false);\n  }, [isCopied]);\n\n  const handleIsCopied = React.useCallback(\n    (isCopied: boolean) => {\n      setLocalIsCopied(isCopied);\n      onCopyChange?.(isCopied);\n    },\n    [onCopyChange],\n  );\n\n  const handleCopy = React.useCallback(\n    (e: React.MouseEvent<HTMLButtonElement>) => {\n      if (isCopied) return;\n      if (content) {\n        navigator.clipboard\n          .writeText(content)\n          .then(() => {\n            handleIsCopied(true);\n            setTimeout(() => handleIsCopied(false), delay);\n            onCopy?.(content);\n          })\n          .catch((error) => {\n            console.error('Error copying command', error);\n          });\n      }\n      onClick?.(e);\n    },\n    [isCopied, content, delay, onClick, onCopy, handleIsCopied],\n  );\n\n  return (\n    <motion.button\n      data-slot=\"copy-button\"\n      whileHover={{ scale: 1.05 }}\n      whileTap={{ scale: 0.95 }}\n      className={cn(buttonVariants({ variant, size }), className)}\n      onClick={handleCopy}\n      {...props}\n    >\n      <AnimatePresence mode=\"wait\">\n        <motion.span\n          key={localIsCopied ? 'check' : 'copy'}\n          data-slot=\"copy-button-icon\"\n          initial={{ scale: 0 }}\n          animate={{ scale: 1 }}\n          exit={{ scale: 0 }}\n          transition={{ duration: 0.15 }}\n        >\n          <Icon />\n        </motion.span>\n      </AnimatePresence>\n    </motion.button>\n  );\n}\n\nexport { CopyButton, buttonVariants, type CopyButtonProps };\n",
      "type": "registry:ui",
      "target": "components/animate-ui/buttons/copy.tsx"
    }
  ]
}