{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "attachment",
  "title": "Attachment",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-react/components/attachment/attachment.tsx",
      "content": "import * as React from 'react'\nimport { FileCode, FileText, Image as ImageIcon, Loader2, X } from 'lucide-react'\nimport { cn } from '@/lib/utils'\nimport { attachmentMediaVariants, attachmentVariants, type AttachmentVariants } from './attachment.variants'\n\nexport interface AttachmentProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'>, AttachmentVariants {\n  title: string\n  description?: string\n  state?: 'idle' | 'uploading' | 'processing' | 'error' | 'done'\n  media?: 'file' | 'image' | 'code'\n  src?: string\n  alt?: string\n  removable?: boolean\n  onRemove?: () => void\n}\n\nconst Attachment = React.forwardRef<HTMLDivElement, AttachmentProps>(\n  (\n    {\n      title,\n      description,\n      state = 'done',\n      size = 'default',\n      orientation = 'horizontal',\n      media = 'file',\n      src,\n      alt = '',\n      removable = false,\n      onRemove,\n      className,\n      ...props\n    },\n    ref,\n  ) => {\n    const busy = state === 'uploading' || state === 'processing'\n    return (\n      <div\n        ref={ref}\n        data-uipkge=\"\"\n        data-slot=\"attachment\"\n        data-state={state}\n        data-size={size}\n        data-orientation={orientation}\n        className={cn(attachmentVariants({ size, orientation }), className)}\n        {...props}\n      >\n        <div data-slot=\"attachment-media\" className={cn(attachmentMediaVariants({ size }))}>\n          {src && media === 'image' && !busy ? (\n            <img src={src} alt={alt} className=\"size-full object-cover\" />\n          ) : busy ? (\n            <Loader2 className=\"size-4 motion-safe:animate-spin\" aria-hidden=\"true\" />\n          ) : media === 'code' ? (\n            <FileCode aria-hidden=\"true\" />\n          ) : media === 'image' ? (\n            <ImageIcon aria-hidden=\"true\" />\n          ) : (\n            <FileText aria-hidden=\"true\" />\n          )}\n        </div>\n        <div data-slot=\"attachment-content\" className=\"min-w-0 flex-1 leading-tight\">\n          <span data-slot=\"attachment-title\" className={cn('block truncate font-medium', busy && 'animate-pulse')}>\n            {title}\n          </span>\n          {description ? (\n            <span\n              data-slot=\"attachment-description\"\n              className={cn(\n                'text-muted-foreground mt-0.5 block truncate text-xs',\n                state === 'error' && 'text-destructive/80',\n              )}\n            >\n              {description}\n            </span>\n          ) : null}\n        </div>\n        {removable ? (\n          <button\n            type=\"button\"\n            data-slot=\"attachment-remove\"\n            className=\"text-muted-foreground hover:bg-accent hover:text-foreground relative z-10 inline-flex size-7 shrink-0 items-center justify-center rounded-md\"\n            aria-label={`Remove ${title}`}\n            onClick={onRemove}\n          >\n            <X className=\"size-3.5\" />\n          </button>\n        ) : null}\n      </div>\n    )\n  },\n)\nAttachment.displayName = 'Attachment'\n\nexport { Attachment }\n",
      "type": "registry:ui",
      "target": "~/components/ui/attachment/attachment.tsx"
    },
    {
      "path": "packages/registry-react/components/attachment/attachment.variants.ts",
      "content": "import type { VariantProps } from 'class-variance-authority'\nimport { cva } from 'class-variance-authority'\n\n/**\n * Variant definitions live in their own file (rather than the package\n * `index.ts`) so consuming Vue SFCs can import without creating a circular\n * dependency through the index. See card.variants.ts for the canonical\n * example + the SSR symptom that motivated the split.\n */\n\nexport const attachmentVariants = cva(\n  'group/attachment relative flex w-fit max-w-full min-w-0 shrink-0 rounded-xl border bg-card text-card-foreground transition-colors data-[state=error]:border-destructive/30 data-[state=idle]:border-dashed',\n  {\n    variants: {\n      size: {\n        default: 'gap-2 p-2 text-sm',\n        sm: 'gap-2 p-1.5 text-xs',\n        xs: 'gap-1.5 rounded-lg p-1 text-xs',\n      },\n      orientation: {\n        horizontal: 'min-w-40 items-center',\n        vertical: 'w-24 flex-col',\n      },\n    },\n    defaultVariants: {\n      size: 'default',\n      orientation: 'horizontal',\n    },\n  },\n)\n\nexport const attachmentMediaVariants = cva(\n  \"relative flex aspect-square shrink-0 items-center justify-center overflow-hidden rounded-lg bg-muted text-foreground group-data-[orientation=vertical]/attachment:w-full group-data-[state=error]/attachment:bg-destructive/10 group-data-[state=error]/attachment:text-destructive [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4\",\n  {\n    variants: {\n      size: {\n        default: 'w-10',\n        sm: 'w-8',\n        xs: 'w-7 rounded-md [&_svg:not([class*=\"size-\"])]:size-3.5',\n      },\n    },\n    defaultVariants: {\n      size: 'default',\n    },\n  },\n)\n\nexport type AttachmentVariants = VariantProps<typeof attachmentVariants>\n",
      "type": "registry:ui",
      "target": "~/components/ui/attachment/attachment.variants.ts"
    },
    {
      "path": "packages/registry-react/components/attachment/index.ts",
      "content": "export { Attachment, type AttachmentProps } from './attachment'\nexport { attachmentVariants, attachmentMediaVariants, type AttachmentVariants } from './attachment.variants'\n",
      "type": "registry:ui",
      "target": "~/components/ui/attachment/index.ts"
    }
  ],
  "dependencies": [
    "class-variance-authority",
    "lucide-react"
  ],
  "devDependencies": [],
  "registryDependencies": [],
  "description": "Single file or image chip. Drive look with props: state, size, orientation, media, src, removable. Not a dropzone (see file-upload).",
  "categories": [
    "data-display"
  ]
}