{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "copy",
  "title": "Copy Button",
  "description": "A copy button component with a variety of styles and animations.",
  "dependencies": [
    "motion",
    "lucide-react",
    "class-variance-authority"
  ],
  "registryDependencies": [
    "@unlumen-ui/button",
    "@unlumen-ui/hooks-use-controlled-state"
  ],
  "files": [
    {
      "path": "registry/components/buttons/copy/index.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { AnimatePresence, motion } from \"motion/react\";\nimport { Check, Copy } from \"lucide-react\";\n\nimport {\n  Button as ButtonPrimitive,\n  type ButtonProps as ButtonPrimitiveProps,\n} from \"@/components/unlumen-ui/primitives/button\";\nimport { cn } from \"@/lib/utils\";\nimport { useControlledState } from \"@/hooks/use-controlled-state\";\n\nconst buttonVariants = cva(\n  \"flex items-center justify-center rounded-md transition-[box-shadow,_color,_background-color,_border-color,_outline-color,_text-decoration-color,_fill,_stroke] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 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        accent: \"bg-accent text-accent-foreground shadow-xs hover:bg-accent/90\",\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        link: \"text-primary underline-offset-4 hover:underline\",\n      },\n      size: {\n        default: \"size-9\",\n        xs: \"size-7 [&_svg:not([class*='size-'])]:size-3.5 rounded-md\",\n        sm: \"size-8 rounded-md\",\n        lg: \"size-10 rounded-md\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      size: \"default\",\n    },\n  },\n);\n\ntype CopyButtonProps = Omit<ButtonPrimitiveProps, \"children\"> &\n  VariantProps<typeof buttonVariants> & {\n    content: string;\n    copied?: boolean;\n    onCopiedChange?: (copied: boolean, content?: string) => void;\n    delay?: number;\n  };\n\nfunction CopyButton({\n  className,\n  content,\n  copied,\n  onCopiedChange,\n  onClick,\n  variant,\n  size,\n  delay = 3000,\n  ...props\n}: CopyButtonProps) {\n  const [isCopied, setIsCopied] = useControlledState({\n    value: copied,\n    onChange: onCopiedChange,\n  });\n\n  const handleCopy = React.useCallback(\n    (e: React.MouseEvent<HTMLButtonElement>) => {\n      onClick?.(e);\n      if (copied) return;\n      if (content) {\n        navigator.clipboard\n          .writeText(content)\n          .then(() => {\n            setIsCopied(true);\n            onCopiedChange?.(true, content);\n            setTimeout(() => {\n              setIsCopied(false);\n              onCopiedChange?.(false);\n            }, delay);\n          })\n          .catch((error) => {\n            console.error(\"Error copying command\", error);\n          });\n      }\n    },\n    [onClick, copied, content, setIsCopied, onCopiedChange, delay],\n  );\n\n  const Icon = isCopied ? Check : Copy;\n\n  return (\n    <ButtonPrimitive\n      data-slot=\"copy-button\"\n      className={cn(buttonVariants({ variant, size, className }))}\n      onClick={handleCopy}\n      {...props}\n    >\n      <AnimatePresence mode=\"popLayout\">\n        <motion.span\n          key={isCopied ? \"check\" : \"copy\"}\n          data-slot=\"copy-button-icon\"\n          initial={{ scale: 0, opacity: 0.4, filter: \"blur(4px)\" }}\n          animate={{ scale: 1, opacity: 1, filter: \"blur(0px)\" }}\n          exit={{ scale: 0, opacity: 0.4, filter: \"blur(4px)\" }}\n          transition={{ duration: 0.25 }}\n        >\n          <Icon />\n        </motion.span>\n      </AnimatePresence>\n    </ButtonPrimitive>\n  );\n}\n\nexport { CopyButton, buttonVariants, type CopyButtonProps };\n",
      "type": "registry:ui",
      "target": "components/unlumen-ui/copy.tsx"
    }
  ],
  "type": "registry:ui"
}