{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "conversation-list",
  "title": "Conversation List",
  "description": "A date-grouped conversation sidebar with pinning.",
  "dependencies": [
    "lucide-react"
  ],
  "files": [
    {
      "path": "components/spectrumui/blocks/ai-assistants/conversation-list.tsx",
      "content": "'use client';\n\nimport { useState } from 'react';\nimport { MessageSquare, Pin } from 'lucide-react';\nimport { cn } from '@/lib/utils';\n\nexport interface ConversationEntry {\n  id: string;\n  title: string;\n  group: string;\n  pinned?: boolean;\n}\n\nexport type ConversationListVariant = 'Default' | 'Compact';\n\nexport interface ConversationListProps {\n  conversations: ConversationEntry[];\n  activeId?: string;\n  onSelect?: (id: string) => void;\n  onTogglePin?: (id: string) => void;\n  variant?: ConversationListVariant;\n  className?: string;\n}\n\nexport function ConversationList({\n  conversations,\n  activeId: activeProp,\n  onSelect,\n  onTogglePin,\n  variant = 'Default',\n  className,\n}: ConversationListProps) {\n  const [activeState, setActiveState] = useState(conversations[0]?.id);\n  const [pins, setPins] = useState<Set<string>>(\n    new Set(conversations.filter((c) => c.pinned).map((c) => c.id)),\n  );\n  const active = activeProp !== undefined ? activeProp : activeState;\n  const compact = variant === 'Compact';\n\n  function select(id: string) {\n    setActiveState(id);\n    onSelect?.(id);\n  }\n\n  function togglePin(id: string) {\n    setPins((previous) => {\n      const next = new Set(previous);\n      if (next.has(id)) next.delete(id);\n      else next.add(id);\n      return next;\n    });\n    onTogglePin?.(id);\n  }\n\n  const groups: string[] = [];\n  for (const conversation of conversations) {\n    if (!groups.includes(conversation.group)) groups.push(conversation.group);\n  }\n\n  return (\n    <nav\n      aria-label=\"Conversations\"\n      className={cn(\n        'w-full max-w-[300px] rounded-2xl border border-black/[0.08] bg-white p-2 shadow-xs dark:border-white/[0.09] dark:bg-[#0B0B0D]',\n        className,\n      )}\n    >\n      {groups.map((group) => (\n        <div key={group} className=\"[&:not(:first-child)]:mt-3\">\n          <p className=\"px-2 pb-1 font-mono text-[9.5px] font-medium uppercase tracking-[0.08em] text-neutral-400 dark:text-neutral-600\">\n            {group}\n          </p>\n          <ul>\n            {conversations\n              .filter((conversation) => conversation.group === group)\n              .map((conversation) => {\n                const isActive = conversation.id === active;\n                const pinned = pins.has(conversation.id);\n                return (\n                  <li key={conversation.id} className=\"group/item relative\">\n                    <button\n                      type=\"button\"\n                      aria-current={isActive ? 'true' : undefined}\n                      onClick={() => select(conversation.id)}\n                      className={cn(\n                        'flex w-full items-center gap-2 rounded-lg px-2 text-left transition-[background-color,transform] duration-150 active:scale-[0.99]',\n                        compact ? 'py-1.5' : 'py-2',\n                        'focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-neutral-400',\n                        isActive\n                          ? 'bg-black/[0.05] dark:bg-white/[0.07]'\n                          : 'hover:bg-black/[0.03] dark:hover:bg-white/[0.04]',\n                      )}\n                    >\n                      <MessageSquare\n                        className={cn(\n                          'size-3.5 shrink-0',\n                          isActive\n                            ? 'text-neutral-700 dark:text-neutral-200'\n                            : 'text-neutral-300 dark:text-neutral-600',\n                        )}\n                      />\n                      <span\n                        className={cn(\n                          'min-w-0 flex-1 truncate pr-6',\n                          compact ? 'text-[12px]' : 'text-[12.5px]',\n                          isActive\n                            ? 'font-medium text-neutral-900 dark:text-neutral-50'\n                            : 'text-neutral-600 dark:text-neutral-400',\n                        )}\n                      >\n                        {conversation.title}\n                      </span>\n                    </button>\n                    <button\n                      type=\"button\"\n                      aria-label={pinned ? `Unpin ${conversation.title}` : `Pin ${conversation.title}`}\n                      aria-pressed={pinned}\n                      onClick={() => togglePin(conversation.id)}\n                      className={cn(\n                        'absolute right-1.5 top-1/2 grid size-6 -translate-y-1/2 place-items-center rounded-md',\n                        'transition-[opacity,color,transform] duration-150 active:scale-[0.9]',\n                        'focus-visible:opacity-100 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-neutral-400',\n                        pinned\n                          ? 'text-neutral-700 dark:text-neutral-200'\n                          : 'text-neutral-400 opacity-100 [@media(hover:hover)]:opacity-0 [@media(hover:hover)]:group-hover/item:opacity-100 dark:text-neutral-500',\n                      )}\n                    >\n                      <Pin className={cn('size-3', pinned && 'fill-current')} />\n                    </button>\n                  </li>\n                );\n              })}\n          </ul>\n        </div>\n      ))}\n    </nav>\n  );\n}\n\nexport default ConversationList;\n",
      "type": "registry:block",
      "target": "components/spectrumui/blocks/ai-assistants/conversation-list.tsx"
    }
  ],
  "type": "registry:block"
}
