{
  "$schema": "https://shadcn-vue.com/schema/registry-item.json",
  "name": "attachment",
  "title": "Attachment",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-vue/components/attachment/Attachment.vue",
      "content": "<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport type { HTMLAttributes } from 'vue'\nimport { FileCode, FileText, Image as ImageIcon, Loader2, X } from 'lucide-vue-next'\nimport { cn } from '@/lib/utils'\nimport { attachmentMediaVariants, attachmentVariants } from './attachment.variants'\n\nconst props = withDefaults(\n  defineProps<{\n    title: string\n    description?: string\n    state?: 'idle' | 'uploading' | 'processing' | 'error' | 'done'\n    size?: 'default' | 'sm' | 'xs'\n    orientation?: 'horizontal' | 'vertical'\n    media?: 'file' | 'image' | 'code'\n    src?: string\n    alt?: string\n    removable?: boolean\n    class?: HTMLAttributes['class']\n  }>(),\n  {\n    state: 'done',\n    size: 'default',\n    orientation: 'horizontal',\n    media: 'file',\n    alt: '',\n    removable: false,\n  },\n)\n\nconst emit = defineEmits<{\n  remove: []\n}>()\n\nconst busy = computed(() => props.state === 'uploading' || props.state === 'processing')\n</script>\n\n<template>\n  <div\n    data-uipkge\n    data-slot=\"attachment\"\n    :data-state=\"state\"\n    :data-size=\"size\"\n    :data-orientation=\"orientation\"\n    :class=\"cn(attachmentVariants({ size, orientation }), props.class)\"\n  >\n    <div data-slot=\"attachment-media\" :class=\"cn(attachmentMediaVariants({ size }))\">\n      <img v-if=\"src && media === 'image' && !busy\" :src=\"src\" :alt=\"alt\" class=\"size-full object-cover\" />\n      <Loader2 v-else-if=\"busy\" class=\"size-4 motion-safe:animate-spin\" aria-hidden=\"true\" />\n      <FileCode v-else-if=\"media === 'code'\" aria-hidden=\"true\" />\n      <ImageIcon v-else-if=\"media === 'image'\" aria-hidden=\"true\" />\n      <FileText v-else aria-hidden=\"true\" />\n    </div>\n    <div data-slot=\"attachment-content\" class=\"min-w-0 flex-1 leading-tight\">\n      <span data-slot=\"attachment-title\" :class=\"cn('block truncate font-medium', busy && 'animate-pulse')\">\n        {{ title }}\n      </span>\n      <span\n        v-if=\"description\"\n        data-slot=\"attachment-description\"\n        :class=\"cn('text-muted-foreground mt-0.5 block truncate text-xs', state === 'error' && 'text-destructive/80')\"\n      >\n        {{ description }}\n      </span>\n    </div>\n    <button\n      v-if=\"removable\"\n      type=\"button\"\n      data-slot=\"attachment-remove\"\n      class=\"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      @click=\"emit('remove')\"\n    >\n      <X class=\"size-3.5\" />\n    </button>\n  </div>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/attachment/Attachment.vue"
    },
    {
      "path": "packages/registry-vue/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": "~/app/components/ui/attachment/attachment.variants.ts"
    },
    {
      "path": "packages/registry-vue/components/attachment/index.ts",
      "content": "export { default as Attachment } from './Attachment.vue'\nexport { attachmentVariants, attachmentMediaVariants, type AttachmentVariants } from './attachment.variants'\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/attachment/index.ts"
    }
  ],
  "dependencies": [
    "class-variance-authority",
    "lucide-vue-next"
  ],
  "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"
  ]
}