{
  "$schema": "https://shadcn-vue.com/schema/registry-item.json",
  "name": "clipboard",
  "title": "Clipboard",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-vue/components/clipboard/Clipboard.vue",
      "content": "<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, ref } from 'vue'\nimport type { HTMLAttributes } from 'vue'\nimport { Check, Copy } from 'lucide-vue-next'\nimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'\nimport { cn } from '@/lib/utils'\n\nconst props = withDefaults(\n  defineProps<{\n    /** Text to copy to the clipboard. */\n    text?: string\n    /** Optional visible label next to the icon. */\n    label?: string\n    /** Disable the button (no copy, no tooltip). */\n    disabled?: boolean\n    /** Hide the copy icon (useful when a label is shown). */\n    hideIcon?: boolean\n    /** Tooltip text shown on hover before copying. */\n    tooltip?: string\n    /** Feedback text shown after a successful copy. */\n    successText?: string\n    /** Feedback text shown after a failed copy. */\n    errorText?: string\n    /** How long (ms) the success/error feedback stays before resetting. */\n    timeout?: number\n    /** Show the feedback as a tooltip rather than swapping the icon. */\n    feedbackTooltip?: boolean\n    class?: HTMLAttributes['class']\n  }>(),\n  {\n    text: '',\n    label: '',\n    disabled: false,\n    hideIcon: false,\n    tooltip: 'Copy',\n    successText: 'Copied!',\n    errorText: 'Failed',\n    timeout: 2000,\n    feedbackTooltip: true,\n  },\n)\n\nconst emit = defineEmits<{\n  (e: 'copy', text: string): void\n  (e: 'success', text: string): void\n  (e: 'error', error: Error): void\n}>()\n\ntype State = 'idle' | 'success' | 'error'\nconst state = ref<State>('idle')\nlet resetTimer: ReturnType<typeof setTimeout> | null = null\n\nfunction setFeedback(nextState: State) {\n  state.value = nextState\n  if (resetTimer) clearTimeout(resetTimer)\n  resetTimer = setTimeout(() => {\n    state.value = 'idle'\n  }, props.timeout)\n}\n\nasync function copy() {\n  if (props.disabled) return\n  const value = props.text\n  emit('copy', value)\n  try {\n    if (navigator.clipboard?.writeText) {\n      const write = navigator.clipboard.writeText(value)\n      setFeedback('success')\n      await write\n    } else {\n      // Legacy fallback for non-secure contexts.\n      const ta = document.createElement('textarea')\n      ta.value = value\n      ta.style.position = 'fixed'\n      ta.style.opacity = '0'\n      document.body.appendChild(ta)\n      ta.select()\n      document.execCommand('copy')\n      document.body.removeChild(ta)\n      setFeedback('success')\n    }\n    emit('success', value)\n  } catch (err) {\n    setFeedback('error')\n    emit('error', err as Error)\n  }\n}\n\nconst currentTooltip = computed(() => {\n  if (state.value === 'success') return props.successText\n  if (state.value === 'error') return props.errorText\n  return props.tooltip\n})\n\nonBeforeUnmount(() => {\n  if (resetTimer) clearTimeout(resetTimer)\n})\n</script>\n\n<template>\n  <TooltipProvider :delay-duration=\"300\">\n    <Tooltip>\n      <TooltipTrigger as-child>\n        <button\n          type=\"button\"\n          data-uipkge\n          data-slot=\"clipboard\"\n          :data-feedback-state=\"state\"\n          :disabled=\"disabled\"\n          :class=\"\n            cn(\n              'inline-flex items-center gap-1.5 rounded-md text-sm transition-colors',\n              'text-muted-foreground hover:text-foreground',\n              'focus-visible:ring-ring/50 outline-none focus-visible:ring-[3px]',\n              'disabled:cursor-not-allowed disabled:opacity-50',\n              props.class,\n            )\n          \"\n          :aria-label=\"currentTooltip\"\n          @click=\"copy\"\n        >\n          <span v-if=\"!hideIcon\" data-slot=\"clipboard-icon\" class=\"inline-flex\">\n            <Check v-if=\"state === 'success'\" class=\"size-4 text-emerald-500\" />\n            <Copy v-else class=\"size-4\" />\n          </span>\n          <span v-if=\"label\" data-slot=\"clipboard-label\">{{ label }}</span>\n          <slot :state=\"state\" />\n        </button>\n      </TooltipTrigger>\n      <TooltipContent v-if=\"feedbackTooltip || state === 'idle'\">\n        {{ currentTooltip }}\n      </TooltipContent>\n    </Tooltip>\n  </TooltipProvider>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/clipboard/Clipboard.vue"
    },
    {
      "path": "packages/registry-vue/components/clipboard/index.ts",
      "content": "export { default as Clipboard } from './Clipboard.vue'\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/clipboard/index.ts"
    }
  ],
  "dependencies": [
    "lucide-vue-next",
    "reka-ui"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/vue/tooltip.json"
  ],
  "description": "Copy-to-clipboard button with success/error feedback. Swaps the icon to a check on success, shows a tooltip with configurable feedback text, supports a visible label, disabled state, a custom timeout for feedback reset, and copy/success/error events. Includes a legacy execCommand fallback for non-secure contexts. Composes the tooltip primitive.",
  "categories": [
    "utility",
    "control"
  ]
}