{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "opentui/select",
  "title": "Select",
  "description": "Single-select dropdown with arrow-key navigation",
  "dependencies": [
    "@opentui/react"
  ],
  "registryDependencies": [
    "https://termcn.dev/r/opentui/use-theme.json"
  ],
  "files": [
    {
      "path": "registry/opentui/ui/select.tsx",
      "content": "/* @jsxImportSource @opentui/react */\nimport { useKeyboard } from \"@opentui/react\";\nimport { useState } from \"react\";\n\nimport { useTheme } from \"@/hooks/use-theme\";\n\nexport interface SelectOption<T = string> {\n  value: T;\n  label: string;\n  hint?: string;\n  disabled?: boolean;\n}\n\nexport interface SelectProps<T = string> {\n  options: SelectOption<T>[];\n  value?: T;\n  onChange?: (value: T) => void;\n  onSubmit?: (value: T) => void;\n  label?: string;\n  cursor?: string;\n  cursorColor?: string;\n}\n\nexport const Select = <T = string,>({\n  options,\n  value: controlledValue,\n  onChange,\n  onSubmit,\n  label,\n  cursor = \"›\",\n  cursorColor,\n}: SelectProps<T>) => {\n  const theme = useTheme();\n  const [activeIndex, setActiveIndex] = useState(0);\n\n  const resolvedCursorColor = cursorColor ?? theme.colors.primary;\n\n  useKeyboard((key) => {\n    if (key.name === \"up\") {\n      setActiveIndex((i) => {\n        let next = i - 1;\n        while (next >= 0 && options[next]?.disabled) {\n          next -= 1;\n        }\n        return next < 0 ? i : next;\n      });\n    } else if (key.name === \"down\") {\n      setActiveIndex((i) => {\n        let next = i + 1;\n        while (next < options.length && options[next]?.disabled) {\n          next += 1;\n        }\n        return next >= options.length ? i : next;\n      });\n    } else if (key.name === \"return\") {\n      const opt = options[activeIndex];\n      if (opt && !opt.disabled) {\n        onChange?.(opt.value);\n        onSubmit?.(opt.value);\n      }\n    }\n  });\n\n  return (\n    <box flexDirection=\"column\">\n      {label && (\n        <text>\n          <b>{label}</b>\n        </text>\n      )}\n      {options.map((opt, idx) => {\n        const isActive = idx === activeIndex;\n        const isSelected =\n          controlledValue !== undefined && opt.value === controlledValue;\n        let optColor: string;\n        if (opt.disabled) {\n          optColor = theme.colors.mutedForeground;\n        } else if (isActive) {\n          optColor = resolvedCursorColor;\n        } else {\n          optColor = theme.colors.foreground;\n        }\n        return (\n          <box key={idx} gap={1}>\n            <text fg={isActive ? resolvedCursorColor : undefined}>\n              {isActive ? cursor : \" \"}\n            </text>\n            <text fg={opt.disabled ? \"#666\" : optColor}>\n              {isActive || isSelected ? <b>{opt.label}</b> : opt.label}\n            </text>\n            {opt.hint && <text fg=\"#666\">{opt.hint}</text>}\n          </box>\n        );\n      })}\n    </box>\n  );\n};\n",
      "type": "registry:ui",
      "target": "components/ui/select.tsx"
    }
  ],
  "categories": [
    "selection"
  ],
  "type": "registry:ui"
}