{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "inbox-list",
  "type": "registry:component",
  "title": "Inbox list",
  "description": "Items waiting for a person with kind, title, meta and status.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "@wibo/utils",
    "@wibo/badge"
  ],
  "files": [
    {
      "path": "src/patterns/inbox-list.tsx",
      "type": "registry:component",
      "content": "import type { ComponentProps, ReactNode } from \"react\";\nimport {\n  Bell,\n  FileText,\n  MessageCircleQuestionMark,\n  Play,\n  ShieldCheck,\n  type LucideIcon,\n} from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport { Badge } from \"@/components/ui/badge\";\n\ntype InboxKind = \"approval\" | \"question\" | \"run\" | \"alert\" | \"document\";\ntype InboxStatus = \"pending\" | \"approved\" | \"rejected\" | \"expired\" | \"completed\" | \"failed\" | \"info\";\n\ninterface InboxItem {\n  id: string;\n  kind: InboxKind;\n  title: ReactNode;\n  /** Secondary line: source agent, record, requester. */\n  meta?: ReactNode;\n  status: InboxStatus;\n  /** Right-aligned time, already formatted. */\n  time?: ReactNode;\n  unread?: boolean;\n  href?: string;\n  /** Overrides the icon of the kind. */\n  icon?: LucideIcon;\n  /** Who it comes from, in place of the icon tile (28 px, an `AgentAvatar`). Decorative: name it in `meta`. */\n  mark?: ReactNode;\n}\n\nconst kindIcon: Record<InboxKind, LucideIcon> = {\n  approval: ShieldCheck,\n  question: MessageCircleQuestionMark,\n  run: Play,\n  alert: Bell,\n  document: FileText,\n};\n\nconst statusVariant = {\n  pending: \"warning\",\n  approved: \"success\",\n  rejected: \"danger\",\n  expired: \"outline\",\n  completed: \"success\",\n  failed: \"danger\",\n  info: \"info\",\n} as const;\n\nconst defaultStatusLabels: Record<InboxStatus, string> = {\n  pending: \"Pending\",\n  approved: \"Approved\",\n  rejected: \"Rejected\",\n  expired: \"Expired\",\n  completed: \"Completed\",\n  failed: \"Failed\",\n  info: \"Info\",\n};\n\nconst defaultKindLabels: Record<InboxKind, string> = {\n  approval: \"Approval\",\n  question: \"Question\",\n  run: \"Run\",\n  alert: \"Alert\",\n  document: \"Document\",\n};\n\ninterface InboxListProps extends Omit<ComponentProps<\"ul\">, \"onSelect\"> {\n  items: InboxItem[];\n  /** Called when an item without `href` is chosen. */\n  onSelect?: (item: InboxItem) => void;\n  selectedId?: string;\n  /** Rendered in place of the list when `items` is empty, usually a framed `EmptyState`. */\n  emptyState?: ReactNode;\n  statusLabels?: Partial<Record<InboxStatus, string>>;\n  kindLabels?: Partial<Record<InboxKind, string>>;\n  unreadLabel?: string;\n}\n\n/** List of things waiting for a person: approvals, agent questions, finished runs, alerts. */\nfunction InboxList({\n  items,\n  onSelect,\n  selectedId,\n  emptyState,\n  statusLabels,\n  kindLabels,\n  unreadLabel = \"Unread\",\n  className,\n  \"aria-label\": ariaLabel = \"Inbox\",\n  ...props\n}: InboxListProps) {\n  const status = { ...defaultStatusLabels, ...statusLabels };\n  const kinds = { ...defaultKindLabels, ...kindLabels };\n\n  if (items.length === 0 && emptyState) return <>{emptyState}</>;\n\n  return (\n    <ul\n      data-slot=\"inbox-list\"\n      aria-label={ariaLabel}\n      className={cn(\"divide-y divide-border overflow-hidden rounded-xl border border-border bg-card shadow-card\", className)}\n      {...props}\n    >\n      {items.map((item) => {\n        const Icon = item.icon ?? kindIcon[item.kind];\n        const selected = item.id === selectedId;\n        const rowClass = cn(\n          \"press-item flex w-full items-start gap-3 px-4 py-3 text-left duration-150 outline-none hover:bg-muted/50 focus-visible:bg-muted/50 focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-inset\",\n          selected && \"bg-muted\",\n        );\n        const body = (\n          <>\n            {item.mark ? (\n              <span aria-hidden=\"true\" className=\"mt-0.5 flex size-7 shrink-0 items-center justify-center\">\n                {item.mark}\n              </span>\n            ) : (\n              <span\n                aria-hidden=\"true\"\n                className=\"mt-0.5 flex size-7 shrink-0 items-center justify-center rounded-md border border-border bg-background text-muted-foreground\"\n              >\n                <Icon className=\"size-3.5\" />\n              </span>\n            )}\n            <span className=\"grid min-w-0 flex-1 gap-0.5\">\n              <span className=\"flex min-w-0 items-center gap-2\">\n                {item.unread ? (\n                  <span className=\"size-1.5 shrink-0 rounded-full bg-accent\">\n                    <span className=\"sr-only\">{unreadLabel}</span>\n                  </span>\n                ) : null}\n                <span className={cn(\"truncate text-sm text-foreground\", item.unread && \"font-medium\")}>\n                  {item.title}\n                </span>\n              </span>\n              <span className=\"truncate text-xs text-muted-foreground\">\n                <span className=\"sr-only\">{kinds[item.kind]}: </span>\n                {item.meta}\n              </span>\n            </span>\n            <span className=\"flex shrink-0 flex-col items-end gap-1\">\n              <Badge variant={statusVariant[item.status]}>{status[item.status]}</Badge>\n              {item.time ? (\n                <span className=\"font-mono text-xs text-muted-foreground tabular-nums\">{item.time}</span>\n              ) : null}\n            </span>\n          </>\n        );\n        return (\n          <li key={item.id} data-slot=\"inbox-item\" data-kind={item.kind} data-status={item.status}>\n            {item.href ? (\n              <a href={item.href} aria-current={selected ? \"true\" : undefined} className={rowClass}>\n                {body}\n              </a>\n            ) : (\n              <button\n                type=\"button\"\n                aria-current={selected ? \"true\" : undefined}\n                onClick={() => onSelect?.(item)}\n                className={rowClass}\n              >\n                {body}\n              </button>\n            )}\n          </li>\n        );\n      })}\n    </ul>\n  );\n}\n\nexport { InboxList };\nexport type { InboxItem, InboxKind, InboxListProps, InboxStatus };\n"
    }
  ]
}
