{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "elements-conversation-search",
  "type": "registry:component",
  "title": "Search in conversation",
  "description": "Find inside a long thread, with every hit marked down the scrollbar.",
  "registryDependencies": [
    "https://r.assistant-ui.com/elements-surfaces.json"
  ],
  "dependencies": [
    "lucide-react"
  ],
  "files": [
    {
      "type": "registry:component",
      "path": "components/assistant-ui/elements/conversation-search.tsx",
      "sourcePath": "packages/ui/src/components/react/assistant-ui/elements/conversation-search.tsx",
      "content": "\"use client\";\n\nimport type { ComponentProps } from \"react\";\nimport { ChevronDownIcon, ChevronUpIcon, SearchIcon } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport { field, ghostButton, mono, paper } from \"./surfaces\";\n\nexport interface SearchHit {\n  id: string;\n  before: string;\n  match: string;\n  after: string;\n  position: number;\n}\n\nexport function ConversationSearch({\n  query,\n  hits,\n  activeIndex,\n  onQueryChange,\n  onStep,\n  className,\n  ...props\n}: Omit<\n  ComponentProps<\"div\">,\n  \"children\" | \"query\" | \"hits\" | \"activeIndex\" | \"onQueryChange\" | \"onStep\"\n> & {\n  query: string;\n  hits: readonly SearchHit[];\n  activeIndex: number;\n  onQueryChange?: (query: string) => void;\n  onStep?: (delta: number) => void;\n}) {\n  const index =\n    hits.length === 0\n      ? -1\n      : Math.min(Math.max(activeIndex, 0), hits.length - 1);\n  const active = index === -1 ? undefined : hits[index];\n\n  return (\n    <div\n      data-slot=\"conversation-search\"\n      className={cn(\"flex w-full max-w-sm gap-2\", className)}\n\n      {...props}\n    >\n      <div className=\"flex min-w-0 flex-1 flex-col gap-2\">\n        <div\n          className={cn(\n            paper,\n            \"flex items-center gap-2 rounded-full py-1.5 pr-1.5 pl-3\",\n          )}\n        >\n          <SearchIcon className=\"text-foreground/30 size-3.5 shrink-0\" />\n          <input\n            value={query}\n            onChange={(event) => onQueryChange?.(event.target.value)}\n            placeholder=\"Find in conversation\"\n            aria-label=\"Find in conversation\"\n            className=\"text-foreground/85 placeholder:text-foreground/30 min-w-0 flex-1 bg-transparent text-[13px] outline-none\"\n          />\n          <span\n            className={cn(mono, \"text-foreground/30 shrink-0 tabular-nums\")}\n          >\n            {hits.length === 0 ? \"0\" : `${index + 1}/${hits.length}`}\n          </span>\n          <button\n            type=\"button\"\n            aria-label=\"Previous match\"\n            onClick={() => onStep?.(-1)}\n            className={cn(ghostButton, \"size-6 shrink-0\")}\n          >\n            <ChevronUpIcon className=\"size-3.5\" />\n          </button>\n          <button\n            type=\"button\"\n            aria-label=\"Next match\"\n            onClick={() => onStep?.(1)}\n            className={cn(ghostButton, \"size-6 shrink-0\")}\n          >\n            <ChevronDownIcon className=\"size-3.5\" />\n          </button>\n        </div>\n\n        {active && (\n          <div\n            className={cn(\n              field,\n              \"fade-in animate-in rounded-xl px-3 py-2 text-xs leading-relaxed duration-200\",\n            )}\n          >\n            <span className=\"text-foreground/45\">{active.before}</span>\n            <span className=\"text-foreground/95 rounded bg-amber-400/35 px-0.5\">\n              {active.match}\n            </span>\n            <span className=\"text-foreground/45\">{active.after}</span>\n          </div>\n        )}\n      </div>\n\n      <div className=\"bg-foreground/[0.04] relative w-1.5 shrink-0 rounded-full\">\n        {hits.map((hit, i) => (\n          <span\n            key={hit.id}\n            aria-hidden\n            className={cn(\n              \"absolute inset-x-0 h-1 rounded-full transition-colors duration-200\",\n              i === index ? \"bg-amber-500\" : \"bg-amber-500/35\",\n            )}\n            style={{ top: `${hit.position}%` }}\n          />\n        ))}\n      </div>\n    </div>\n  );\n}\n"
    }
  ]
}