{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "copy-button",
  "title": "Copy Button",
  "description": "Copies a value and says whether it worked, including when the clipboard refuses.",
  "dependencies": [
    "lucide-react@^1.31.0"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/default/copy-button/copy-button.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Check, Clipboard } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport type CopyButtonProps = Omit<\n  React.ButtonHTMLAttributes<HTMLButtonElement>,\n  \"children\" | \"value\"\n> & {\n  value: string\n  /** Shown beside the icon. Omit for an icon-only control. */\n  children?: React.ReactNode\n  label?: string\n  copiedLabel?: string\n  errorLabel?: string\n  onCopied?: (value: string) => void\n  onCopyError?: (error: unknown) => void\n}\n\nexport function CopyButton({\n  value,\n  children,\n  label = \"Copy\",\n  copiedLabel = \"Copied\",\n  errorLabel = \"Copy failed\",\n  onCopied,\n  onCopyError,\n  className,\n  onClick,\n  ...buttonProps\n}: CopyButtonProps) {\n  const [state, setState] = React.useState<\"idle\" | \"copied\" | \"error\">(\"idle\")\n  const timer = React.useRef<ReturnType<typeof setTimeout> | null>(null)\n\n  React.useEffect(\n    () => () => {\n      if (timer.current) clearTimeout(timer.current)\n    },\n    []\n  )\n\n  async function copy(event: React.MouseEvent<HTMLButtonElement>) {\n    onClick?.(event)\n    if (event.defaultPrevented) return\n\n    try {\n      await navigator.clipboard.writeText(value)\n      setState(\"copied\")\n      onCopied?.(value)\n    } catch (error) {\n      // Denied permission, an insecure context, or a sandboxed frame.\n      setState(\"error\")\n      onCopyError?.(error)\n    }\n\n    if (timer.current) clearTimeout(timer.current)\n    timer.current = setTimeout(() => setState(\"idle\"), 1600)\n  }\n\n  const name =\n    state === \"copied\" ? copiedLabel : state === \"error\" ? errorLabel : label\n\n  return (\n    <button\n      type=\"button\"\n      data-slot=\"copy-button\"\n      data-state={state}\n      aria-label={children ? undefined : name}\n      className={cn(\n        \"text-muted-foreground hover:bg-muted hover:text-foreground inline-flex min-h-8 items-center gap-1.5 rounded-md px-2 text-xs transition-colors duration-150 motion-reduce:transition-none\",\n        className\n      )}\n      onClick={copy}\n      {...buttonProps}\n    >\n      {state === \"copied\" ? (\n        <Check aria-hidden=\"true\" size={14} />\n      ) : (\n        <Clipboard aria-hidden=\"true\" size={14} />\n      )}\n      {children}\n      {/* The outcome is announced whether or not the label is visible. */}\n      <span aria-live=\"polite\" className=\"sr-only\">\n        {state === \"idle\" ? \"\" : name}\n      </span>\n    </button>\n  )\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "clipboard",
    "control"
  ],
  "type": "registry:ui"
}