{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "file",
  "type": "registry:component",
  "title": "File",
  "description": "Display file parts with icon, name, size, and a download button.",
  "dependencies": [
    "@assistant-ui/react",
    "lucide-react",
    "class-variance-authority"
  ],
  "files": [
    {
      "type": "registry:component",
      "path": "components/assistant-ui/file.tsx",
      "content": "\"use client\";\n\nimport { memo, type FC } from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport {\n  FileIcon,\n  FileTextIcon,\n  ImageIcon,\n  MusicIcon,\n  VideoIcon,\n  BracesIcon,\n  DownloadIcon,\n} from \"lucide-react\";\nimport type { FileMessagePartComponent } from \"@assistant-ui/react\";\nimport { cn } from \"@/lib/utils\";\n\nconst fileVariants = cva(\n  \"aui-file-root inline-flex items-center gap-3 rounded-lg transition-colors\",\n  {\n    variants: {\n      variant: {\n        outline: \"border-border hover:bg-muted/50 border\",\n        ghost: \"hover:bg-muted/50\",\n        muted: \"bg-muted/50 hover:bg-muted/70\",\n      },\n      size: {\n        sm: \"px-2.5 py-1.5 text-xs\",\n        default: \"px-3 py-2 text-sm\",\n        lg: \"px-4 py-3 text-base\",\n      },\n    },\n    defaultVariants: {\n      variant: \"outline\",\n      size: \"default\",\n    },\n  },\n);\n\nfunction getMimeTypeIcon(mimeType: string): FC<{ className?: string }> {\n  const type = mimeType.toLowerCase();\n  if (type.startsWith(\"image/\")) {\n    return ImageIcon;\n  }\n  if (type === \"application/pdf\") {\n    return FileTextIcon;\n  }\n  if (type === \"application/json\") {\n    return BracesIcon;\n  }\n  if (type.startsWith(\"text/\")) {\n    return FileTextIcon;\n  }\n  if (type.startsWith(\"audio/\")) {\n    return MusicIcon;\n  }\n  if (type.startsWith(\"video/\")) {\n    return VideoIcon;\n  }\n  return FileIcon;\n}\n\nexport type FileDataKind = \"data-uri\" | \"url\" | \"base64\" | \"id\";\n\nfunction getFileDataKind(\n  data: string,\n  sourceType?: \"url\" | \"id\",\n): FileDataKind {\n  if (sourceType === \"url\" && /^data:/i.test(data)) return \"data-uri\";\n  if (sourceType) return sourceType;\n  if (/^data:/i.test(data)) return \"data-uri\";\n  if (/^https?:\\/\\//i.test(data)) return \"url\";\n  return \"base64\";\n}\n\nfunction getBase64Size(base64: string): number {\n  const commaIndex = base64.indexOf(\",\");\n  const base64Data = commaIndex >= 0 ? base64.slice(commaIndex + 1) : base64;\n  const padding = (base64Data.match(/=/g) || []).length;\n  return Math.floor((base64Data.length * 3) / 4) - padding;\n}\n\nfunction formatFileSize(bytes: number): string {\n  if (bytes < 1024) {\n    return `${bytes} B`;\n  }\n  if (bytes < 1024 * 1024) {\n    return `${(bytes / 1024).toFixed(1)} KB`;\n  }\n  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\nexport type FileRootProps = React.ComponentProps<\"div\"> &\n  VariantProps<typeof fileVariants>;\n\nfunction FileRoot({\n  className,\n  variant,\n  size,\n  children,\n  ...props\n}: FileRootProps) {\n  return (\n    <div\n      data-slot=\"file-root\"\n      data-variant={variant}\n      data-size={size}\n      className={cn(fileVariants({ variant, size, className }))}\n      {...props}\n    >\n      {children}\n    </div>\n  );\n}\n\ntype FileIconDisplayProps = React.ComponentProps<\"span\"> & {\n  mimeType?: string;\n};\n\nfunction FileIconDisplay({\n  mimeType,\n  className,\n  children,\n  ...props\n}: FileIconDisplayProps) {\n  const IconComponent = mimeType ? getMimeTypeIcon(mimeType) : FileIcon;\n\n  return (\n    <span\n      data-slot=\"file-icon\"\n      className={cn(\"text-muted-foreground shrink-0\", className)}\n      {...props}\n    >\n      {children ?? <IconComponent className=\"size-5\" />}\n    </span>\n  );\n}\n\nfunction FileName({\n  className,\n  children,\n  ...props\n}: React.ComponentProps<\"span\">) {\n  return (\n    <span\n      data-slot=\"file-name\"\n      className={cn(\"min-w-0 flex-1 truncate font-medium\", className)}\n      {...props}\n    >\n      {children || \"Unnamed file\"}\n    </span>\n  );\n}\n\ntype FileSizeProps = React.ComponentProps<\"span\"> & {\n  bytes: number;\n};\n\nfunction FileSize({ bytes, className, ...props }: FileSizeProps) {\n  return (\n    <span\n      data-slot=\"file-size\"\n      className={cn(\"text-muted-foreground shrink-0\", className)}\n      {...props}\n    >\n      {formatFileSize(bytes)}\n    </span>\n  );\n}\n\ntype FileDownloadProps = Omit<React.ComponentProps<\"a\">, \"href\"> & {\n  data: string;\n  mimeType: string;\n  filename?: string;\n  sourceType?: \"url\" | \"id\";\n};\n\nfunction FileDownload({\n  data,\n  mimeType,\n  filename,\n  sourceType,\n  className,\n  children,\n  ...props\n}: FileDownloadProps) {\n  if (typeof data !== \"string\") return null;\n  const kind = getFileDataKind(data, sourceType);\n  if (kind === \"id\") return null;\n  if (kind === \"url\" && !/^(https?:\\/\\/|blob:)/i.test(data)) return null;\n  const href = kind === \"base64\" ? `data:${mimeType};base64,${data}` : data;\n\n  return (\n    <a\n      data-slot=\"file-download\"\n      href={href}\n      download={filename || \"download\"}\n      {...(kind === \"url\" && { target: \"_blank\", rel: \"noopener noreferrer\" })}\n      className={cn(\n        \"text-muted-foreground hover:bg-accent hover:text-accent-foreground shrink-0 rounded-md p-1 transition-colors\",\n        className,\n      )}\n      {...props}\n    >\n      {children || <DownloadIcon className=\"size-4\" />}\n    </a>\n  );\n}\n\nconst FileImpl: FileMessagePartComponent = ({\n  filename,\n  data,\n  mimeType,\n  sourceType,\n}) => {\n  const kind = getFileDataKind(data, sourceType);\n  const showSize =\n    typeof data === \"string\" && (kind === \"base64\" || kind === \"data-uri\");\n\n  return (\n    <FileRoot>\n      <FileIconDisplay mimeType={mimeType} />\n      <div className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n        <FileName>{filename}</FileName>\n        {showSize && (\n          <FileSize bytes={getBase64Size(data)} className=\"text-xs\" />\n        )}\n      </div>\n      <FileDownload\n        data={data}\n        mimeType={mimeType}\n        {...(filename !== undefined && { filename })}\n        {...(sourceType !== undefined && { sourceType })}\n      />\n    </FileRoot>\n  );\n};\n\nconst File = memo(FileImpl) as unknown as FileMessagePartComponent & {\n  Root: typeof FileRoot;\n  Icon: typeof FileIconDisplay;\n  Name: typeof FileName;\n  Size: typeof FileSize;\n  Download: typeof FileDownload;\n};\n\nFile.displayName = \"File\";\nFile.Root = FileRoot;\nFile.Icon = FileIconDisplay;\nFile.Name = FileName;\nFile.Size = FileSize;\nFile.Download = FileDownload;\n\nexport {\n  File,\n  FileRoot,\n  FileIconDisplay,\n  FileName,\n  FileSize,\n  FileDownload,\n  fileVariants,\n  getMimeTypeIcon,\n  getFileDataKind,\n  getBase64Size,\n  formatFileSize,\n};\n"
    }
  ]
}