{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "uploadthing-file-card",
  "title": "UploadThing File Card",
  "description": "A beautiful file card displaying uploaded file info with copy URL, download, open, and remove actions.",
  "dependencies": [
    "@uploadthing/react",
    "uploadthing"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/blocks/uploadthing/uploadthing-file-card/components/elements/uploadthing-file-card.tsx",
      "content": "\"use client\";\n\nimport { useState } from \"react\";\nimport { cn } from \"@/lib/utils\";\n\ninterface UploadThingFileCardProps {\n  name: string;\n  size: number;\n  type: string;\n  url: string;\n  onRemove?: () => void;\n  onDownload?: () => void;\n  className?: string;\n  showPreview?: boolean;\n}\n\nfunction formatFileSize(bytes: number): string {\n  if (bytes === 0) return \"0 B\";\n  const k = 1024;\n  const sizes = [\"B\", \"KB\", \"MB\", \"GB\"];\n  const i = Math.floor(Math.log(bytes) / Math.log(k));\n  return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;\n}\n\nfunction getFileIcon(type: string): React.ReactNode {\n  if (type.startsWith(\"image/\")) {\n    return (\n      <svg className=\"w-6 h-6\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2}>\n        <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\" />\n        <circle cx=\"9\" cy=\"9\" r=\"2\" />\n        <path d=\"m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21\" />\n      </svg>\n    );\n  }\n  if (type.startsWith(\"video/\")) {\n    return (\n      <svg className=\"w-6 h-6\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2}>\n        <path d=\"m22 8-6 4 6 4V8Z\" />\n        <rect x=\"2\" y=\"6\" width=\"14\" height=\"12\" rx=\"2\" ry=\"2\" />\n      </svg>\n    );\n  }\n  if (type === \"application/pdf\") {\n    return (\n      <svg className=\"w-6 h-6\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2}>\n        <path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\" />\n        <polyline points=\"14 2 14 8 20 8\" />\n        <line x1=\"16\" y1=\"13\" x2=\"8\" y2=\"13\" />\n        <line x1=\"16\" y1=\"17\" x2=\"8\" y2=\"17\" />\n        <polyline points=\"10 9 9 9 8 9\" />\n      </svg>\n    );\n  }\n  return (\n    <svg className=\"w-6 h-6\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2}>\n      <path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\" />\n      <polyline points=\"14 2 14 8 20 8\" />\n    </svg>\n  );\n}\n\nfunction CopyIcon({ className }: { className?: string }) {\n  return (\n    <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2}>\n      <rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\" />\n      <path d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\" />\n    </svg>\n  );\n}\n\nfunction CheckIcon({ className }: { className?: string }) {\n  return (\n    <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2}>\n      <polyline points=\"20 6 9 17 4 12\" />\n    </svg>\n  );\n}\n\nfunction DownloadIcon({ className }: { className?: string }) {\n  return (\n    <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2}>\n      <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\" />\n      <polyline points=\"7 10 12 15 17 10\" />\n      <line x1=\"12\" y1=\"15\" x2=\"12\" y2=\"3\" />\n    </svg>\n  );\n}\n\nfunction ExternalLinkIcon({ className }: { className?: string }) {\n  return (\n    <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2}>\n      <path d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\" />\n      <polyline points=\"15 3 21 3 21 9\" />\n      <line x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\" />\n    </svg>\n  );\n}\n\nfunction XIcon({ className }: { className?: string }) {\n  return (\n    <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2}>\n      <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n      <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n    </svg>\n  );\n}\n\nexport function UploadThingFileCard({\n  name,\n  size,\n  type,\n  url,\n  onRemove,\n  onDownload,\n  className,\n  showPreview = true,\n}: UploadThingFileCardProps) {\n  const [copied, setCopied] = useState(false);\n  const isImage = type.startsWith(\"image/\");\n\n  const handleCopy = async () => {\n    await navigator.clipboard.writeText(url);\n    setCopied(true);\n    setTimeout(() => setCopied(false), 2000);\n  };\n\n  const handleDownload = () => {\n    if (onDownload) {\n      onDownload();\n    } else {\n      const a = document.createElement(\"a\");\n      a.href = url;\n      a.download = name;\n      a.click();\n    }\n  };\n\n  return (\n    <div\n      data-slot=\"uploadthing-file-card\"\n      className={cn(\n        \"flex items-center gap-4 p-4 rounded-lg border border-border bg-card\",\n        \"transition-colors hover:bg-accent/50\",\n        className\n      )}\n    >\n      {showPreview && isImage ? (\n        <div className=\"w-12 h-12 rounded-md overflow-hidden bg-muted flex-shrink-0\">\n          <img src={url} alt={name} className=\"w-full h-full object-cover\" />\n        </div>\n      ) : (\n        <div className=\"w-12 h-12 rounded-md bg-muted flex items-center justify-center flex-shrink-0 text-muted-foreground\">\n          {getFileIcon(type)}\n        </div>\n      )}\n\n      <div className=\"flex-1 min-w-0\">\n        <p className=\"font-medium text-sm truncate\">{name}</p>\n        <div className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n          <span>{formatFileSize(size)}</span>\n          <span>•</span>\n          <span>{type.split(\"/\")[1]?.toUpperCase() || type}</span>\n        </div>\n      </div>\n\n      <div className=\"flex items-center gap-1\">\n        <button\n          type=\"button\"\n          onClick={handleCopy}\n          className=\"p-2 rounded-md hover:bg-muted transition-colors text-muted-foreground hover:text-foreground\"\n          title=\"Copy URL\"\n        >\n          {copied ? (\n            <CheckIcon className=\"w-4 h-4 text-green-500\" />\n          ) : (\n            <CopyIcon className=\"w-4 h-4\" />\n          )}\n        </button>\n\n        <button\n          type=\"button\"\n          onClick={handleDownload}\n          className=\"p-2 rounded-md hover:bg-muted transition-colors text-muted-foreground hover:text-foreground\"\n          title=\"Download\"\n        >\n          <DownloadIcon className=\"w-4 h-4\" />\n        </button>\n\n        <a\n          href={url}\n          target=\"_blank\"\n          rel=\"noopener noreferrer\"\n          className=\"p-2 rounded-md hover:bg-muted transition-colors text-muted-foreground hover:text-foreground\"\n          title=\"Open in new tab\"\n        >\n          <ExternalLinkIcon className=\"w-4 h-4\" />\n        </a>\n\n        {onRemove && (\n          <button\n            type=\"button\"\n            onClick={onRemove}\n            className=\"p-2 rounded-md hover:bg-destructive/10 transition-colors text-muted-foreground hover:text-destructive\"\n            title=\"Remove\"\n          >\n            <XIcon className=\"w-4 h-4\" />\n          </button>\n        )}\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:component"
    }
  ],
  "docs": "Displays file name, size, type with appropriate icons. Image files show thumbnail preview. Includes copy URL, download, and remove actions.",
  "categories": [
    "uploadthing"
  ],
  "type": "registry:block"
}