{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "ink/color-picker",
  "title": "Color Picker",
  "description": "Color picker with palette, hex, and RGB input",
  "dependencies": [
    "ink"
  ],
  "registryDependencies": [
    "https://termcn.dev/r/ink/interaction.json",
    "https://termcn.dev/r/ink/terminal-style.json",
    "https://termcn.dev/r/ink/use-theme.json",
    "https://termcn.dev/r/ink/use-unicode.json"
  ],
  "files": [
    {
      "path": "registry/ui/color-picker.tsx",
      "content": "import { Box, Text } from \"ink\";\nimport React, { useState } from \"react\";\n\nimport { useInteraction } from \"@/hooks/use-interaction\";\nimport { useTheme } from \"@/hooks/use-theme\";\nimport { useUnicode } from \"@/hooks/use-unicode\";\nimport { resolveBorderStyle } from \"@/lib/terminal-style\";\n\nconst DEFAULT_PALETTE = [\n  \"#000000\",\n  \"#800000\",\n  \"#008000\",\n  \"#808000\",\n  \"#000080\",\n  \"#800080\",\n  \"#008080\",\n  \"#c0c0c0\",\n  \"#808080\",\n  \"#ff0000\",\n  \"#00ff00\",\n  \"#ffff00\",\n  \"#0000ff\",\n  \"#ff00ff\",\n  \"#00ffff\",\n  \"#ffffff\",\n];\n\nconst COLS = 8;\n\nexport interface ColorPickerProps {\n  value?: string;\n  defaultValue?: string;\n  onValueChange?: (color: string) => void;\n  onChange?: (color: string) => void;\n  onSubmit?: (color: string) => void;\n  label?: string;\n  palette?: string[];\n  autoFocus?: boolean;\n  id?: string;\n  isActive?: boolean;\n  disabled?: boolean;\n  \"aria-label\"?: string;\n}\n\nexport const ColorPicker = ({\n  value: controlledValue,\n  defaultValue,\n  onValueChange,\n  onChange,\n  onSubmit,\n  label,\n  palette = DEFAULT_PALETTE,\n  autoFocus = false,\n  id,\n  isActive = true,\n  disabled = false,\n  \"aria-label\": ariaLabel,\n}: ColorPickerProps) => {\n  const unicode = useUnicode();\n  const theme = useTheme();\n  const [paletteRow, setPaletteRow] = useState(0);\n  const [paletteCol, setPaletteCol] = useState(0);\n  const [hexInput, setHexInput] = useState(\"\");\n  const [mode, setMode] = useState<\"palette\" | \"hex\">(\"palette\");\n  const [internalValue, setInternalValue] = useState(\n    defaultValue ?? palette[0] ?? \"#000000\"\n  );\n\n  const rows = Math.ceil(palette.length / COLS);\n  const currentPaletteColor =\n    palette[paletteRow * COLS + paletteCol] ?? palette[0] ?? \"#000000\";\n  const currentColor = controlledValue ?? internalValue;\n\n  const applyColor = (color: string) => {\n    const changeHandler = onValueChange ?? onChange;\n    if (changeHandler) {\n      changeHandler(color);\n    } else {\n      setInternalValue(color);\n    }\n  };\n\n  const { isFocused } = useInteraction(\n    (input, key) => {\n      if (key.pageUp || key.pageDown) {\n        setMode((m) => (m === \"palette\" ? \"hex\" : \"palette\"));\n        return;\n      }\n\n      if (mode === \"palette\") {\n        if (key.upArrow) {\n          setPaletteRow((r) => Math.max(0, r - 1));\n        } else if (key.downArrow) {\n          setPaletteRow((r) => Math.min(rows - 1, r + 1));\n        } else if (key.leftArrow) {\n          setPaletteCol((c) => Math.max(0, c - 1));\n        } else if (key.rightArrow) {\n          setPaletteCol((c) => Math.min(COLS - 1, c + 1));\n        } else if (key.return) {\n          applyColor(currentPaletteColor);\n          onSubmit?.(currentPaletteColor);\n        }\n      } else if (key.backspace || key.delete) {\n        setHexInput((h) => h.slice(0, -1));\n      } else if (key.return) {\n        const hex = hexInput.startsWith(\"#\") ? hexInput : `#${hexInput}`;\n        if (/^#[0-9a-fA-F]{6}$/.test(hex)) {\n          applyColor(hex);\n          onSubmit?.(hex);\n        }\n      } else if (/^[0-9a-fA-F#]$/.test(input) && hexInput.length < 7) {\n        setHexInput((h) => h + input);\n      }\n    },\n    { autoFocus, disabled, id, isActive }\n  );\n\n  return (\n    <Box flexDirection=\"column\">\n      {label && <Text bold>{label}</Text>}\n\n      <Text aria-label={ariaLabel ?? label ?? \"Color picker\"}>{\"\"}</Text>\n      <Box aria-role=\"listbox\" flexDirection=\"column\">\n        {Array.from({ length: rows }, (_, r) => (\n          <Box key={r}>\n            {Array.from({ length: COLS }, (_c, c) => {\n              const idx = r * COLS + c;\n              if (idx >= palette.length) {\n                return null;\n              }\n              const paletteColor = palette[idx];\n              const isActive =\n                mode === \"palette\" && r === paletteRow && c === paletteCol;\n              return (\n                <Box\n                  key={paletteColor}\n                  aria-label={paletteColor}\n                  aria-role=\"option\"\n                  aria-state={{\n                    disabled,\n                    selected: currentColor === paletteColor,\n                  }}\n                  marginRight={isActive ? 0 : 0}\n                >\n                  <Text\n                    aria-hidden\n                    backgroundColor={paletteColor}\n                    color={isActive ? \"#ffffff\" : paletteColor}\n                    bold={isActive}\n                  >\n                    {isActive ? \"[]\" : \"  \"}\n                  </Text>\n                </Box>\n              );\n            })}\n          </Box>\n        ))}\n      </Box>\n\n      <Box marginTop={1} gap={1}>\n        <Text color={theme.colors.mutedForeground}>Selected:</Text>\n        <Text backgroundColor={currentColor} color={currentColor}>\n          {\"  \"}\n        </Text>\n        <Text color={theme.colors.foreground}>{currentColor}</Text>\n      </Box>\n\n      <Box\n        aria-label={`Hex color input: ${hexInput || \"empty\"}`}\n        aria-role=\"textbox\"\n        aria-state={{ disabled }}\n        borderStyle={resolveBorderStyle(\"round\", unicode)}\n        borderColor={\n          mode === \"hex\" ? theme.colors.focusRing : theme.colors.border\n        }\n        paddingX={1}\n        marginTop={1}\n      >\n        <Text aria-hidden color={theme.colors.mutedForeground}>\n          #\n        </Text>\n        <Text aria-hidden color={theme.colors.foreground}>\n          {hexInput || \"------\"}\n        </Text>\n        {mode === \"hex\" && isFocused && (\n          <Text aria-hidden color={theme.colors.focusRing}>\n            {unicode ? \"█\" : \"|\"}\n          </Text>\n        )}\n      </Box>\n\n      <Text aria-hidden color={theme.colors.mutedForeground} dimColor>\n        {mode === \"palette\"\n          ? unicode\n            ? \"↑↓←→: navigate · Enter: select · PageUp/PageDown: hex input\"\n            : \"arrows: navigate - Enter: select - PageUp/PageDown: hex input\"\n          : unicode\n            ? \"Type hex · Enter: apply · PageUp/PageDown: palette\"\n            : \"Type hex - Enter: apply - PageUp/PageDown: palette\"}\n      </Text>\n    </Box>\n  );\n};\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "selection"
  ],
  "type": "registry:ui"
}