{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "search-input",
  "title": "Search input",
  "description": "The search/filter query input shell — a leading magnifier, a clear button, and a `suggestions` dropdown slot revealed on focus.",
  "registryDependencies": [
    "@engram/cn",
    "@engram/tokens"
  ],
  "files": [
    {
      "path": "src/components/search-input.tsx",
      "content": "\"use client\";\n\nimport {\n  type InputHTMLAttributes,\n  type ReactNode,\n  useRef,\n  useState,\n} from \"react\";\nimport { cn } from \"../lib/cn.js\";\n\nexport interface SearchInputProps\n  extends Omit<\n    InputHTMLAttributes<HTMLInputElement>,\n    \"value\" | \"onChange\" | \"size\"\n  > {\n  /** Current query text (controlled). */\n  value: string;\n  /** Fired on every keystroke with the new text. */\n  onValueChange?: (value: string) => void;\n  /** Fired when the clear (×) button is pressed; falls back to onValueChange(\"\"). */\n  onClear?: () => void;\n  /**\n   * Suggestion/autocomplete content shown in a dropdown while focused. Engram\n   * renders the input shell and the panel; the consumer owns the query grammar\n   * and what goes in here.\n   */\n  suggestions?: ReactNode;\n  className?: string;\n}\n\n/**\n * The search/filter query input shell — a leading magnifier, a clear button,\n * and a `suggestions` dropdown slot revealed on focus. Tokenizing and the DIM\n * filter grammar stay app-side; this is the UI the parser plugs into.\n */\nexport function SearchInput({\n  value,\n  onValueChange,\n  onClear,\n  suggestions,\n  className,\n  placeholder = \"Search…\",\n  ...props\n}: SearchInputProps) {\n  const [focused, setFocused] = useState(false);\n  const blurTimer = useRef<ReturnType<typeof setTimeout>>(undefined);\n\n  function clear() {\n    if (onClear) onClear();\n    else onValueChange?.(\"\");\n  }\n\n  return (\n    <div className={cn(\"relative\", className)}>\n      <div className=\"flex h-9 items-center border border-engram-border bg-engram-raised focus-within:border-engram-accent\">\n        <svg\n          viewBox=\"0 0 16 16\"\n          aria-hidden=\"true\"\n          className=\"mx-2.5 size-4 shrink-0 text-engram-faint\"\n        >\n          <circle\n            cx=\"7\"\n            cy=\"7\"\n            r=\"4.5\"\n            fill=\"none\"\n            stroke=\"currentColor\"\n            strokeWidth=\"1.5\"\n          />\n          <path\n            d=\"M10.5 10.5L14 14\"\n            stroke=\"currentColor\"\n            strokeWidth=\"1.5\"\n            fill=\"none\"\n          />\n        </svg>\n        <input\n          type=\"text\"\n          value={value}\n          placeholder={placeholder}\n          onChange={(e) => onValueChange?.(e.target.value)}\n          onFocus={() => {\n            clearTimeout(blurTimer.current);\n            setFocused(true);\n          }}\n          onBlur={() => {\n            // Delay so a click inside the suggestions panel registers first.\n            blurTimer.current = setTimeout(() => setFocused(false), 120);\n          }}\n          className=\"h-full min-w-0 flex-1 bg-transparent font-engram-mono text-engram-fg text-sm outline-none placeholder:text-engram-faint\"\n          {...props}\n        />\n        {value ? (\n          <button\n            type=\"button\"\n            aria-label=\"Clear search\"\n            onClick={clear}\n            className=\"mx-1 flex size-6 items-center justify-center text-engram-faint transition-colors hover:text-engram-fg\"\n          >\n            <svg viewBox=\"0 0 10 10\" aria-hidden=\"true\" className=\"size-2.5\">\n              <path\n                d=\"M1 1l8 8M9 1l-8 8\"\n                stroke=\"currentColor\"\n                strokeWidth=\"1.6\"\n                fill=\"none\"\n              />\n            </svg>\n          </button>\n        ) : null}\n      </div>\n      {focused && suggestions ? (\n        <div className=\"absolute inset-x-0 top-full z-50 mt-1 border border-engram-border-strong bg-engram-raised-2\">\n          {suggestions}\n        </div>\n      ) : null}\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/engram/search-input.tsx"
    }
  ],
  "meta": {
    "level": "component"
  },
  "docs": "Extend without forking: edit the copied source, use `asChild` (Radix Slot) to change the rendered element, pass the typed `annotations` prop for curated data (verdict/tags/per-plug), or use slot / render-prop props for arbitrary content. Requires the @engram/tokens theme (--engram-* CSS variables).",
  "categories": [
    "interaction"
  ],
  "type": "registry:component"
}