{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "use-copy-to-clipboard",
  "title": "useCopyToClipboard",
  "description": "A clipboard-copy hook with a timed copied state and a non-throwing error channel.",
  "files": [
    {
      "path": "src/registry/hooks/use-copy-to-clipboard.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\n\nexport interface UseCopyToClipboardOptions {\n  /** How long `copied` stays true before falling back to false, in ms. Defaults to 2000. */\n  resetDelay?: number\n}\n\nexport interface UseCopyToClipboardResult {\n  copied: boolean\n  copy: (text: string) => Promise<boolean>\n  error: Error | null\n}\n\nfunction toError(reason: unknown): Error {\n  if (reason instanceof Error) return reason\n  if (reason && typeof reason === \"object\" && \"message\" in reason) {\n    return new Error(String((reason as { message: unknown }).message))\n  }\n  return new Error(\"Clipboard write failed\")\n}\n\n/**\n * Clipboard copy plus an auto-resetting state machine. A successful `copy` sets\n * `copied` to true and it falls back to false after `resetDelay`; calling `copy`\n * again restarts the timer, and unmounting clears it. A failure (permission denied /\n * insecure context / no API) only sets `error` and returns false — it never throws.\n * The hook itself never touches `navigator` during render, only inside `copy`, so it\n * is safe in a server-rendered component.\n */\nexport function useCopyToClipboard(\n  options: UseCopyToClipboardOptions = {},\n): UseCopyToClipboardResult {\n  const { resetDelay = 2000 } = options\n  const [copied, setCopied] = React.useState(false)\n  const [error, setError] = React.useState<Error | null>(null)\n  const timeoutRef = React.useRef<ReturnType<typeof setTimeout> | null>(null)\n\n  const clearResetTimer = () => {\n    if (timeoutRef.current !== null) {\n      clearTimeout(timeoutRef.current)\n      timeoutRef.current = null\n    }\n  }\n\n  // Clean up on unmount, so a late timer can't setState on a gone component.\n  React.useEffect(() => clearResetTimer, [])\n\n  const copy = React.useCallback(\n    (text: string) => {\n      if (typeof navigator === \"undefined\" || !navigator.clipboard?.writeText) {\n        setError(new Error(\"Clipboard API unavailable (insecure context or unsupported browser)\"))\n        return Promise.resolve(false)\n      }\n\n      return navigator.clipboard.writeText(text).then(\n        () => {\n          setError(null)\n          setCopied(true)\n          clearResetTimer()\n          timeoutRef.current = setTimeout(() => {\n            setCopied(false)\n            timeoutRef.current = null\n          }, resetDelay)\n          return true\n        },\n        reason => {\n          setError(toError(reason))\n          return false\n        },\n      )\n    },\n    [resetDelay],\n  )\n\n  return { copied, copy, error }\n}\n\nexport default useCopyToClipboard\n",
      "type": "registry:hook"
    }
  ],
  "type": "registry:hook"
}