{
  "$schema": "https://shadcn-vue.com/schema/registry-item.json",
  "name": "kanban",
  "title": "Kanban",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-vue/components/kanban/Kanban.vue",
      "content": "<script setup lang=\"ts\">\nimport { provide, ref, type HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\nimport { KanbanContextKey, type KanbanMoveEvent } from './context'\n\ninterface Props {\n  class?: HTMLAttributes['class']\n}\n\nconst props = defineProps<Props>()\n\nconst emit = defineEmits<{\n  'card-move': [event: KanbanMoveEvent]\n}>()\n\nconst draggingCardId = ref<string | null>(null)\nconst draggingColumnId = ref<string | null>(null)\nconst overColumnId = ref<string | null>(null)\nconst grabbedCardId = ref<string | null>(null)\n// Keyboard moves are silent to a screen reader — the card just appears\n// somewhere else. This region narrates pick up / move / drop / cancel.\nconst announcement = ref('')\n\nfunction setDraggingCard(cardId: string | null, columnId: string | null) {\n  draggingCardId.value = cardId\n  draggingColumnId.value = columnId\n}\n\nfunction setOverColumn(columnId: string | null) {\n  overColumnId.value = columnId\n}\n\nfunction setGrabbedCard(cardId: string | null) {\n  grabbedCardId.value = cardId\n}\n\nfunction emitMove(event: KanbanMoveEvent) {\n  emit('card-move', event)\n}\n\nfunction announce(message: string) {\n  // Re-assigning the same string would not re-trigger the live region.\n  announcement.value = announcement.value === message ? `${message} ` : message\n}\n\nprovide(KanbanContextKey, {\n  draggingCardId,\n  draggingColumnId,\n  overColumnId,\n  grabbedCardId,\n  setDraggingCard,\n  setOverColumn,\n  setGrabbedCard,\n  emitMove,\n  announce,\n})\n</script>\n\n<template>\n  <div data-uipkge data-slot=\"kanban\" :class=\"cn('w-full', props.class)\">\n    <slot />\n    <div data-slot=\"kanban-live-region\" class=\"sr-only\" role=\"status\" aria-live=\"polite\" aria-atomic=\"true\">\n      {{ announcement }}\n    </div>\n  </div>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/kanban/Kanban.vue"
    },
    {
      "path": "packages/registry-vue/components/kanban/KanbanBoard.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\n\ninterface Props {\n  class?: HTMLAttributes['class']\n}\n\nconst props = defineProps<Props>()\n</script>\n\n<template>\n  <div data-slot=\"kanban-board\" :class=\"cn('flex w-full items-start gap-4 overflow-x-auto pb-4', props.class)\">\n    <slot />\n  </div>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/kanban/KanbanBoard.vue"
    },
    {
      "path": "packages/registry-vue/components/kanban/KanbanColumn.vue",
      "content": "<script setup lang=\"ts\">\nimport { computed, inject, provide, toRef, type HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\nimport { KanbanColumnContextKey, KanbanContextKey } from './context'\nimport { kanbanColumnVariants } from './kanban.variants'\n\ninterface Props {\n  id: string\n  /** Accessible name for the column, also used in keyboard move\n   *  announcements (\"moved to In progress\"). Falls back to the id. */\n  label?: string\n  class?: HTMLAttributes['class']\n}\n\nconst props = defineProps<Props>()\nconst kanban = inject(KanbanContextKey)\n\nprovide(KanbanColumnContextKey, {\n  columnId: props.id,\n  label: toRef(props, 'label'),\n})\n\nconst isOver = computed(() => kanban?.overColumnId.value === props.id)\n\nfunction handleDragOver(e: DragEvent) {\n  e.preventDefault()\n  if (e.dataTransfer) {\n    e.dataTransfer.dropEffect = 'move'\n  }\n  if (kanban && kanban.overColumnId.value !== props.id) {\n    kanban.setOverColumn(props.id)\n  }\n}\n\nfunction handleDragLeave(e: DragEvent) {\n  const currentTarget = e.currentTarget as HTMLElement | null\n  const relatedTarget = e.relatedTarget as HTMLElement | null\n  if (currentTarget?.contains(relatedTarget)) return\n  if (kanban && kanban.overColumnId.value === props.id) {\n    kanban.setOverColumn(null)\n  }\n}\n\nfunction handleDrop(e: DragEvent) {\n  e.preventDefault()\n  if (kanban && kanban.draggingCardId.value && kanban.draggingColumnId.value) {\n    kanban.emitMove({\n      cardId: kanban.draggingCardId.value,\n      fromColumnId: kanban.draggingColumnId.value,\n      toColumnId: props.id,\n    })\n  }\n  kanban?.setDraggingCard(null, null)\n  kanban?.setOverColumn(null)\n}\n</script>\n\n<template>\n  <div\n    data-uipkge\n    data-slot=\"kanban-column\"\n    role=\"group\"\n    :aria-label=\"label ?? id\"\n    :data-column-id=\"id\"\n    :data-over=\"isOver ? '' : undefined\"\n    :class=\"cn(kanbanColumnVariants({ isOver }), props.class)\"\n    @dragover=\"handleDragOver\"\n    @dragleave=\"handleDragLeave\"\n    @drop=\"handleDrop\"\n  >\n    <slot :is-over=\"isOver\" />\n  </div>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/kanban/KanbanColumn.vue"
    },
    {
      "path": "packages/registry-vue/components/kanban/KanbanColumnHeader.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\n\ninterface Props {\n  class?: HTMLAttributes['class']\n}\n\nconst props = defineProps<Props>()\n</script>\n\n<template>\n  <div data-slot=\"kanban-column-header\" :class=\"cn('flex items-center justify-between gap-2 px-1 py-0.5', props.class)\">\n    <slot />\n  </div>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/kanban/KanbanColumnHeader.vue"
    },
    {
      "path": "packages/registry-vue/components/kanban/KanbanColumnDot.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\n\ninterface Props {\n  color?: string\n  class?: HTMLAttributes['class']\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n  color: 'bg-primary',\n})\n</script>\n\n<template>\n  <span data-slot=\"kanban-column-dot\" :class=\"cn('size-2 shrink-0 rounded-full', props.color, props.class)\" />\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/kanban/KanbanColumnDot.vue"
    },
    {
      "path": "packages/registry-vue/components/kanban/KanbanColumnTitle.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\n\ninterface Props {\n  class?: HTMLAttributes['class']\n}\n\nconst props = defineProps<Props>()\n</script>\n\n<template>\n  <h3 data-slot=\"kanban-column-title\" :class=\"cn('text-foreground text-sm font-semibold tracking-tight', props.class)\">\n    <slot />\n  </h3>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/kanban/KanbanColumnTitle.vue"
    },
    {
      "path": "packages/registry-vue/components/kanban/KanbanColumnCount.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\n\ninterface Props {\n  count?: number | string\n  class?: HTMLAttributes['class']\n}\n\nconst props = defineProps<Props>()\n</script>\n\n<template>\n  <span\n    data-slot=\"kanban-column-count\"\n    :class=\"cn('bg-muted text-muted-foreground rounded-md px-1.5 py-0.5 text-xs font-medium tabular-nums', props.class)\"\n  >\n    <slot>{{ count }}</slot>\n  </span>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/kanban/KanbanColumnCount.vue"
    },
    {
      "path": "packages/registry-vue/components/kanban/KanbanColumnAdd.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\nimport { Plus } from 'lucide-vue-next'\n\ninterface Props {\n  class?: HTMLAttributes['class']\n}\n\nconst props = defineProps<Props>()\n</script>\n\n<template>\n  <button\n    type=\"button\"\n    data-slot=\"kanban-column-add\"\n    :class=\"\n      cn(\n        'text-muted-foreground hover:bg-background hover:text-foreground focus-visible:ring-ring inline-flex size-6 items-center justify-center rounded-md transition-colors focus-visible:ring-1 focus-visible:outline-none',\n        props.class,\n      )\n    \"\n  >\n    <slot>\n      <Plus class=\"size-3.5\" />\n    </slot>\n  </button>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/kanban/KanbanColumnAdd.vue"
    },
    {
      "path": "packages/registry-vue/components/kanban/KanbanColumnBody.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\n\ninterface Props {\n  class?: HTMLAttributes['class']\n}\n\nconst props = defineProps<Props>()\n</script>\n\n<template>\n  <div data-slot=\"kanban-column-body\" :class=\"cn('flex flex-1 flex-col gap-2 overflow-y-auto py-1', props.class)\">\n    <slot />\n  </div>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/kanban/KanbanColumnBody.vue"
    },
    {
      "path": "packages/registry-vue/components/kanban/KanbanColumnEmpty.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\n\ninterface Props {\n  class?: HTMLAttributes['class']\n}\n\nconst props = defineProps<Props>()\n</script>\n\n<template>\n  <div\n    data-slot=\"kanban-column-empty\"\n    :class=\"\n      cn(\n        'border-border/60 text-muted-foreground/60 flex flex-1 items-center justify-center rounded-lg border border-dashed py-8 text-xs',\n        props.class,\n      )\n    \"\n  >\n    <slot>No cards</slot>\n  </div>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/kanban/KanbanColumnEmpty.vue"
    },
    {
      "path": "packages/registry-vue/components/kanban/KanbanCard.vue",
      "content": "<script setup lang=\"ts\">\nimport { computed, inject, ref, type HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\nimport { KanbanColumnContextKey, KanbanContextKey } from './context'\nimport { kanbanCardVariants } from './kanban.variants'\n\ninterface Props {\n  id: string\n  disabled?: boolean\n  /** Disable the keyboard grab (Space / arrows) while leaving pointer\n   *  dragging intact. Default: enabled. */\n  keyboardDraggable?: boolean\n  class?: HTMLAttributes['class']\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n  disabled: false,\n  keyboardDraggable: true,\n})\n\nconst kanban = inject(KanbanContextKey)\nconst column = inject(KanbanColumnContextKey)\n\nconst cardEl = ref<HTMLElement | null>(null)\n\nconst isGrabbed = computed(() => kanban?.grabbedCardId.value === props.id)\nconst isDragging = computed(() => kanban?.draggingCardId.value === props.id || isGrabbed.value)\n\nconst canKeyboardDrag = computed(() => props.keyboardDraggable && !props.disabled && !!kanban && !!column)\n\nfunction columnName(el: HTMLElement | null) {\n  return el?.getAttribute('aria-label') || el?.dataset.columnId || 'column'\n}\n\n/** Ordered, enabled columns of the board this card sits in. */\nfunction boardColumns(): HTMLElement[] {\n  const board = cardEl.value?.closest('[data-slot=\"kanban\"]') ?? document\n  return Array.from(board.querySelectorAll<HTMLElement>('[data-slot=\"kanban-column\"]'))\n}\n\nfunction focusSelfAfterMove() {\n  // The consumer owns the data, so the card is re-rendered (often as a new\n  // node) in its new column. Re-find it by id and restore focus.\n  requestAnimationFrame(() => {\n    const moved = document.querySelector<HTMLElement>(`[data-slot=\"kanban-card\"][data-card-id=\"${props.id}\"]`)\n    moved?.focus()\n  })\n}\n\nfunction grab() {\n  if (!canKeyboardDrag.value) return\n  kanban!.setGrabbedCard(props.id)\n  kanban!.setDraggingCard(props.id, column!.columnId)\n  kanban!.setOverColumn(column!.columnId)\n  kanban!.announce(`Picked up card. Use the arrow keys to move it, space to drop, escape to cancel.`)\n}\n\nfunction release(cancelled: boolean) {\n  if (!kanban) return\n  kanban.setGrabbedCard(null)\n  kanban.setDraggingCard(null, null)\n  kanban.setOverColumn(null)\n  kanban.announce(cancelled ? 'Move cancelled.' : 'Card dropped.')\n}\n\nfunction moveToColumn(delta: -1 | 1) {\n  const columns = boardColumns()\n  const currentIdx = columns.findIndex((el) => el.dataset.columnId === column!.columnId)\n  if (currentIdx === -1) return\n  const target = columns[currentIdx + delta]\n  // Deliberately not wrapping: running off the end of a board should stop,\n  // not teleport the card back to the first column.\n  if (!target?.dataset.columnId) return\n  kanban!.emitMove({\n    cardId: props.id,\n    fromColumnId: column!.columnId,\n    toColumnId: target.dataset.columnId,\n  })\n  kanban!.setDraggingCard(props.id, target.dataset.columnId)\n  kanban!.setOverColumn(target.dataset.columnId)\n  kanban!.announce(`Moved to ${columnName(target)}.`)\n  focusSelfAfterMove()\n}\n\nfunction moveWithinColumn(delta: -1 | 1) {\n  const columnEl = cardEl.value?.closest<HTMLElement>('[data-slot=\"kanban-column\"]')\n  if (!columnEl) return\n  const cards = Array.from(columnEl.querySelectorAll<HTMLElement>('[data-slot=\"kanban-card\"]'))\n  const currentIdx = cards.findIndex((el) => el.dataset.cardId === props.id)\n  if (currentIdx === -1) return\n  const targetIdx = currentIdx + delta\n  if (targetIdx < 0 || targetIdx > cards.length - 1) return\n  kanban!.emitMove({\n    cardId: props.id,\n    fromColumnId: column!.columnId,\n    toColumnId: column!.columnId,\n    toIndex: targetIdx,\n  })\n  kanban!.announce(`Position ${targetIdx + 1} of ${cards.length}.`)\n  focusSelfAfterMove()\n}\n\nfunction handleKeydown(e: KeyboardEvent) {\n  if (!canKeyboardDrag.value) return\n\n  if (e.key === ' ' || e.key === 'Spacebar') {\n    e.preventDefault()\n    if (isGrabbed.value) release(false)\n    else grab()\n    return\n  }\n  if (!isGrabbed.value) return\n  if (e.key === 'Escape') {\n    e.preventDefault()\n    release(true)\n    return\n  }\n  if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') {\n    e.preventDefault()\n    moveToColumn(e.key === 'ArrowLeft' ? -1 : 1)\n    return\n  }\n  if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {\n    e.preventDefault()\n    moveWithinColumn(e.key === 'ArrowUp' ? -1 : 1)\n  }\n}\n\nfunction handleBlur() {\n  // A grabbed card that loses focus (click elsewhere, Tab) would otherwise\n  // stay stuck in the held state with no way back to it.\n  if (isGrabbed.value) release(true)\n}\n\nfunction handleDragStart(e: DragEvent) {\n  if (props.disabled) {\n    e.preventDefault()\n    return\n  }\n  if (e.dataTransfer) {\n    e.dataTransfer.effectAllowed = 'move'\n    e.dataTransfer.setData('text/plain', props.id)\n  }\n  kanban?.setDraggingCard(props.id, column?.columnId ?? null)\n}\n\nfunction handleDragEnd() {\n  kanban?.setDraggingCard(null, null)\n  kanban?.setOverColumn(null)\n}\n</script>\n\n<template>\n  <!-- `role=\"button\"` rather than a real <button> so consumers can nest\n       interactive content (menus, links) inside a card — the HTML spec\n       forbids that inside <button>. Matches <BoardCard>. -->\n  <div\n    ref=\"cardEl\"\n    data-uipkge\n    data-slot=\"kanban-card\"\n    :data-card-id=\"id\"\n    :data-state=\"isGrabbed ? 'grabbed' : isDragging ? 'dragging' : 'idle'\"\n    :data-disabled=\"disabled || undefined\"\n    :role=\"canKeyboardDrag ? 'button' : undefined\"\n    :tabindex=\"canKeyboardDrag ? 0 : undefined\"\n    :aria-disabled=\"disabled || undefined\"\n    :aria-roledescription=\"canKeyboardDrag ? 'draggable card' : undefined\"\n    :aria-pressed=\"canKeyboardDrag ? isGrabbed : undefined\"\n    :draggable=\"!disabled\"\n    :class=\"cn(kanbanCardVariants({ isDragging }), disabled && 'pointer-events-none opacity-50', props.class)\"\n    @dragstart=\"handleDragStart\"\n    @dragend=\"handleDragEnd\"\n    @keydown=\"handleKeydown\"\n    @blur=\"handleBlur\"\n  >\n    <slot :is-dragging=\"isDragging\" :is-grabbed=\"isGrabbed\" />\n  </div>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/kanban/KanbanCard.vue"
    },
    {
      "path": "packages/registry-vue/components/kanban/KanbanCardHeader.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\n\ninterface Props {\n  class?: HTMLAttributes['class']\n}\n\nconst props = defineProps<Props>()\n</script>\n\n<template>\n  <div data-slot=\"kanban-card-header\" :class=\"cn('flex flex-col gap-1', props.class)\">\n    <slot />\n  </div>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/kanban/KanbanCardHeader.vue"
    },
    {
      "path": "packages/registry-vue/components/kanban/KanbanCardTitle.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\n\ninterface Props {\n  class?: HTMLAttributes['class']\n}\n\nconst props = defineProps<Props>()\n</script>\n\n<template>\n  <p data-slot=\"kanban-card-title\" :class=\"cn('text-foreground text-sm leading-snug font-medium', props.class)\">\n    <slot />\n  </p>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/kanban/KanbanCardTitle.vue"
    },
    {
      "path": "packages/registry-vue/components/kanban/KanbanCardDescription.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\n\ninterface Props {\n  class?: HTMLAttributes['class']\n}\n\nconst props = defineProps<Props>()\n</script>\n\n<template>\n  <p data-slot=\"kanban-card-description\" :class=\"cn('text-muted-foreground line-clamp-2 text-xs', props.class)\">\n    <slot />\n  </p>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/kanban/KanbanCardDescription.vue"
    },
    {
      "path": "packages/registry-vue/components/kanban/KanbanCardFooter.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\n\ninterface Props {\n  class?: HTMLAttributes['class']\n}\n\nconst props = defineProps<Props>()\n</script>\n\n<template>\n  <div\n    data-slot=\"kanban-card-footer\"\n    :class=\"cn('text-muted-foreground mt-1 flex items-center justify-between gap-2 pt-1 text-xs', props.class)\"\n  >\n    <slot />\n  </div>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/kanban/KanbanCardFooter.vue"
    },
    {
      "path": "packages/registry-vue/components/kanban/context.ts",
      "content": "import type { InjectionKey, Ref } from 'vue'\n\nexport interface KanbanMoveEvent {\n  cardId: string\n  fromColumnId: string\n  toColumnId: string\n  toIndex?: number\n}\n\nexport interface KanbanContext {\n  draggingCardId: Ref<string | null>\n  draggingColumnId: Ref<string | null>\n  overColumnId: Ref<string | null>\n  /** Set while a card is held by keyboard (Space), not by pointer drag. */\n  grabbedCardId: Ref<string | null>\n  setDraggingCard: (cardId: string | null, columnId: string | null) => void\n  setOverColumn: (columnId: string | null) => void\n  setGrabbedCard: (cardId: string | null) => void\n  emitMove: (event: KanbanMoveEvent) => void\n  /** Speak a message through the board's polite live region. */\n  announce: (message: string) => void\n}\n\nexport const KanbanContextKey: InjectionKey<KanbanContext> = Symbol('KanbanContext')\n\nexport interface KanbanColumnContext {\n  columnId: string\n  /** Column label, used to announce keyboard moves. Falls back to the id. */\n  label: Ref<string | undefined>\n}\n\nexport const KanbanColumnContextKey: InjectionKey<KanbanColumnContext> = Symbol('KanbanColumnContext')\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/kanban/context.ts"
    },
    {
      "path": "packages/registry-vue/components/kanban/kanban.variants.ts",
      "content": "import type { VariantProps } from 'class-variance-authority'\nimport { cva } from 'class-variance-authority'\n\nexport const kanbanColumnVariants = cva(\n  'flex min-h-[300px] w-72 shrink-0 flex-col gap-2 rounded-xl border bg-muted/40 p-3 transition-colors duration-200 ease-out',\n  {\n    variants: {\n      isOver: {\n        true: 'border-primary/50 bg-primary/5 ring-2 ring-primary/20',\n        false: 'border-border/70',\n      },\n    },\n    defaultVariants: {\n      isOver: false,\n    },\n  },\n)\n\nexport const kanbanCardVariants = cva(\n  'group relative flex cursor-grab flex-col gap-2 rounded-lg border bg-card p-3 text-card-foreground shadow-xs transition-[border-color,box-shadow,opacity] duration-150 ease-out active:cursor-grabbing hover:border-border hover:shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',\n  {\n    variants: {\n      isDragging: {\n        // Keyboard grab reuses this state, so the held card stays fully\n        // opaque — unlike a pointer drag there is no drag image to look at.\n        true: 'shadow-none ring-2 ring-primary/40 opacity-100 data-[state=dragging]:opacity-40',\n        false: 'opacity-100',\n      },\n    },\n    defaultVariants: {\n      isDragging: false,\n    },\n  },\n)\n\nexport type KanbanColumnVariantsProps = VariantProps<typeof kanbanColumnVariants>\nexport type KanbanCardVariantsProps = VariantProps<typeof kanbanCardVariants>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/kanban/kanban.variants.ts"
    },
    {
      "path": "packages/registry-vue/components/kanban/index.ts",
      "content": "export { default as Kanban } from './Kanban.vue'\nexport { default as KanbanBoard } from './KanbanBoard.vue'\nexport { default as KanbanColumn } from './KanbanColumn.vue'\nexport { default as KanbanColumnHeader } from './KanbanColumnHeader.vue'\nexport { default as KanbanColumnDot } from './KanbanColumnDot.vue'\nexport { default as KanbanColumnTitle } from './KanbanColumnTitle.vue'\nexport { default as KanbanColumnCount } from './KanbanColumnCount.vue'\nexport { default as KanbanColumnAdd } from './KanbanColumnAdd.vue'\nexport { default as KanbanColumnBody } from './KanbanColumnBody.vue'\nexport { default as KanbanColumnEmpty } from './KanbanColumnEmpty.vue'\nexport { default as KanbanCard } from './KanbanCard.vue'\nexport { default as KanbanCardHeader } from './KanbanCardHeader.vue'\nexport { default as KanbanCardTitle } from './KanbanCardTitle.vue'\nexport { default as KanbanCardDescription } from './KanbanCardDescription.vue'\nexport { default as KanbanCardFooter } from './KanbanCardFooter.vue'\nexport * from './kanban.variants'\nexport * from './context'\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/kanban/index.ts"
    }
  ],
  "dependencies": [
    "class-variance-authority",
    "lucide-vue-next"
  ],
  "devDependencies": [],
  "registryDependencies": [],
  "description": "Composable compound Kanban primitive for building board, pipeline, and agile workflows with tactile card drag-and-drop mechanics.",
  "categories": [
    "data-display",
    "layout"
  ]
}