{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "queue",
  "title": "Queue",
  "description": "Messages queued while the agent is busy, reorderable.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "utils",
    "https://ui.cuelark.com/r/theme.json",
    "https://ui.cuelark.com/r/motion.json"
  ],
  "files": [
    {
      "path": "src/registry/cuelark/ui/queue.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { GripVerticalIcon, XIcon } from \"lucide-react\";\n\nimport { gsap, motionScale } from \"@/lib/motion\";\nimport { cn } from \"@/lib/utils\";\n\ntype QueueItem = { id: string; text: string };\n\ntype QueueProps = Omit<React.ComponentProps<\"div\">, \"onChange\"> & {\n  items: QueueItem[];\n  onItemsChange?: (items: QueueItem[]) => void;\n  label?: React.ReactNode;\n};\n\n/**\n * Messages queued while the agent is busy. Each waits in line above the composer; reorder with Alt+↑/↓, remove with\n * ×, and the next one slides out when the agent picks it up.\n */\nfunction Queue({ items, onItemsChange, label = \"Queued\", className, ...props }: QueueProps) {\n  const ref = React.useRef<HTMLUListElement>(null);\n  const prev = React.useRef(items.map((i) => i.id));\n\n  React.useLayoutEffect(() => {\n    const el = ref.current;\n    if (!el || !motionScale(el)) return;\n    Array.from(el.children).forEach((li) => {\n      const id = (li as HTMLElement).dataset.id!;\n      if (!prev.current.includes(id)) gsap.fromTo(li, { opacity: 0, y: 6 }, { opacity: 1, y: 0, duration: 0.3, ease: \"cue\" });\n    });\n    prev.current = items.map((i) => i.id);\n  }, [items]);\n\n  const move = (i: number, d: number) => {\n    const j = i + d;\n    if (j < 0 || j >= items.length) return;\n    const next = [...items];\n    [next[i], next[j]] = [next[j]!, next[i]!];\n    onItemsChange?.(next);\n  };\n\n  if (!items.length) return null;\n  return (\n    <div data-slot=\"queue\" className={cn(\"grid gap-1.5 rounded-xl border border-dashed border-border bg-muted/30 p-2\", className)} {...props}>\n      <div className=\"flex items-center justify-between px-1\">\n        <span className=\"cue-label\">{label}</span>\n        <span className=\"font-mono text-[0.6875rem] text-muted-foreground\">{items.length}</span>\n      </div>\n      <ul ref={ref} className=\"grid gap-1\">\n        {items.map((it, i) => (\n          <li\n            key={it.id}\n            data-id={it.id}\n            tabIndex={0}\n            onKeyDown={(e) => {\n              if (e.altKey && (e.key === \"ArrowUp\" || e.key === \"ArrowDown\")) {\n                e.preventDefault();\n                move(i, e.key === \"ArrowUp\" ? -1 : 1);\n              }\n              if (e.key === \"Delete\" || e.key === \"Backspace\") onItemsChange?.(items.filter((x) => x.id !== it.id));\n            }}\n            className=\"group/q flex items-center gap-2 rounded-lg bg-card px-2 py-1.5 text-sm shadow-control ring-1 ring-border outline-none focus-visible:ring-3 focus-visible:ring-ring/50\"\n          >\n            <GripVerticalIcon className=\"size-3.5 shrink-0 text-muted-foreground/60\" />\n            <span className=\"font-mono text-[0.6875rem] text-muted-foreground\">{i + 1}</span>\n            <span className=\"flex-1 truncate\">{it.text}</span>\n            <button type=\"button\" aria-label={`Remove “${it.text}”`} onClick={() => onItemsChange?.(items.filter((x) => x.id !== it.id))} className=\"relative after:absolute after:-inset-2 grid size-5 place-items-center rounded text-muted-foreground opacity-0 group-hover/q:opacity-100 group-focus-within/q:opacity-100 hover:bg-muted hover:text-foreground pointer-coarse:opacity-100\">\n              <XIcon className=\"size-3\" />\n            </button>\n          </li>\n        ))}\n      </ul>\n    </div>\n  );\n}\n\nexport { Queue };\nexport type { QueueItem, QueueProps };\n",
      "type": "registry:ui"
    }
  ],
  "docs": "Docs and examples: https://ui.cuelark.com/components/queue",
  "categories": [
    "agents"
  ],
  "type": "registry:ui"
}