{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "color-picker",
  "type": "registry:component",
  "title": "Color Picker",
  "description": "A color picker with OKLCH, HSL, RGB, and HEX support, plus Tailwind CSS presets.",
  "dependencies": [],
  "registryDependencies": [
    "input"
  ],
  "files": [
    {
      "path": "registry/new-york/blocks/color-picker/color-picker.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport { Input } from \"@/registry/new-york/blocks/input/input\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\ntype OklchColor = { l: number; c: number; h: number; a: number };\ntype ColorFormat = \"oklch\" | \"hex\" | \"hsl\" | \"rgb\";\ntype PresetColor = { label: string; value: string };\n\ntype ColorPickerProps = {\n  value?: string;\n  defaultValue?: string;\n  onValueChange?: (value: string) => void;\n  format?: ColorFormat;\n  showAlpha?: boolean;\n  presets?: \"tailwind\" | PresetColor[];\n  className?: string;\n};\n\n// =============================================================================\n// Color Conversion Utilities\n// =============================================================================\n\nfunction oklchToOklab(l: number, c: number, h: number) {\n  const hRad = (h * Math.PI) / 180;\n  return { L: l, a: c * Math.cos(hRad), b: c * Math.sin(hRad) };\n}\n\nfunction oklabToOklch(L: number, a: number, b: number) {\n  const c = Math.sqrt(a * a + b * b);\n  let h = (Math.atan2(b, a) * 180) / Math.PI;\n  if (h < 0) h += 360;\n  return { l: L, c, h };\n}\n\nfunction oklabToLinearRgb(L: number, a: number, b: number) {\n  const l_ = L + 0.3963377774 * a + 0.2158037573 * b;\n  const m_ = L - 0.1055613458 * a - 0.0638541728 * b;\n  const s_ = L - 0.0894841775 * a - 1.291485548 * b;\n  const l = l_ * l_ * l_;\n  const m = m_ * m_ * m_;\n  const s = s_ * s_ * s_;\n  return {\n    r: +4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,\n    g: -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,\n    b: -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s,\n  };\n}\n\nfunction linearRgbToOklab(r: number, g: number, b: number) {\n  const l = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b;\n  const m = 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b;\n  const s = 0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b;\n  const l_ = Math.cbrt(l);\n  const m_ = Math.cbrt(m);\n  const s_ = Math.cbrt(s);\n  return {\n    L: 0.2104542553 * l_ + 0.793617785 * m_ - 0.0040720468 * s_,\n    a: 1.9779984951 * l_ - 2.428592205 * m_ + 0.4505937099 * s_,\n    b: 0.0259040371 * l_ + 0.7827717662 * m_ - 0.808675766 * s_,\n  };\n}\n\nfunction linearToSrgb(c: number): number {\n  return c <= 0.0031308 ? 12.92 * c : 1.055 * Math.pow(c, 1 / 2.4) - 0.055;\n}\n\nfunction srgbToLinear(c: number): number {\n  return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);\n}\n\nfunction oklchToRgb(l: number, c: number, h: number): [number, number, number] {\n  const lab = oklchToOklab(l, c, h);\n  const lin = oklabToLinearRgb(lab.L, lab.a, lab.b);\n  return [\n    Math.round(clamp01(linearToSrgb(lin.r)) * 255),\n    Math.round(clamp01(linearToSrgb(lin.g)) * 255),\n    Math.round(clamp01(linearToSrgb(lin.b)) * 255),\n  ];\n}\n\nfunction rgbToOklch(r: number, g: number, b: number) {\n  const lab = linearRgbToOklab(srgbToLinear(r / 255), srgbToLinear(g / 255), srgbToLinear(b / 255));\n  return oklabToOklch(lab.L, lab.a, lab.b);\n}\n\nfunction rgbToHsl(r: number, g: number, b: number): [number, number, number] {\n  const r1 = r / 255,\n    g1 = g / 255,\n    b1 = b / 255;\n  const max = Math.max(r1, g1, b1),\n    min = Math.min(r1, g1, b1);\n  const d = max - min,\n    l = (max + min) / 2;\n  if (d === 0) return [0, 0, l * 100];\n  const s = l > 0.5 ? d / (2 - max - min) : d / (max - min);\n  let h = 0;\n  if (max === r1) h = ((g1 - b1) / d + (g1 < b1 ? 6 : 0)) / 6;\n  else if (max === g1) h = ((b1 - r1) / d + 2) / 6;\n  else h = ((r1 - g1) / d + 4) / 6;\n  return [Math.round(h * 360), Math.round(s * 100), Math.round(l * 100)];\n}\n\nfunction hslToRgb(h: number, s: number, l: number): [number, number, number] {\n  const h1 = h / 360,\n    s1 = s / 100,\n    l1 = l / 100;\n  if (s1 === 0) {\n    const v = Math.round(l1 * 255);\n    return [v, v, v];\n  }\n  const hue2rgb = (p: number, q: number, t: number) => {\n    let t1 = t;\n    if (t1 < 0) t1 += 1;\n    if (t1 > 1) t1 -= 1;\n    if (t1 < 1 / 6) return p + (q - p) * 6 * t1;\n    if (t1 < 1 / 2) return q;\n    if (t1 < 2 / 3) return p + (q - p) * (2 / 3 - t1) * 6;\n    return p;\n  };\n  const q = l1 < 0.5 ? l1 * (1 + s1) : l1 + s1 - l1 * s1;\n  const p = 2 * l1 - q;\n  return [\n    Math.round(hue2rgb(p, q, h1 + 1 / 3) * 255),\n    Math.round(hue2rgb(p, q, h1) * 255),\n    Math.round(hue2rgb(p, q, h1 - 1 / 3) * 255),\n  ];\n}\n\nfunction rgbToHex(r: number, g: number, b: number, a?: number): string {\n  const hex = `#${[r, g, b].map((v) => v.toString(16).padStart(2, \"0\")).join(\"\")}`;\n  if (a !== undefined && a < 1)\n    return (\n      hex +\n      Math.round(a * 255)\n        .toString(16)\n        .padStart(2, \"0\")\n    );\n  return hex;\n}\n\nfunction hexToRgb(hex: string) {\n  let h = hex.replace(/^#/, \"\");\n  if (h.length === 3) h = h[0] + h[0] + h[1] + h[1] + h[2] + h[2];\n  if (h.length === 4) h = h[0] + h[0] + h[1] + h[1] + h[2] + h[2] + h[3] + h[3];\n  return {\n    r: parseInt(h.slice(0, 2), 16),\n    g: parseInt(h.slice(2, 4), 16),\n    b: parseInt(h.slice(4, 6), 16),\n    a: h.length === 8 ? parseInt(h.slice(6, 8), 16) / 255 : 1,\n  };\n}\n\nfunction isInGamut(r: number, g: number, b: number) {\n  return r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255;\n}\n\nfunction gamutMapOklch(l: number, c: number, h: number): [number, number, number] {\n  let rgb = oklchToRgb(l, c, h);\n  if (isInGamut(rgb[0], rgb[1], rgb[2])) return rgb;\n  let lo = 0,\n    hi = c;\n  for (let i = 0; i < 20; i++) {\n    const mid = (lo + hi) / 2;\n    rgb = oklchToRgb(l, mid, h);\n    if (isInGamut(rgb[0], rgb[1], rgb[2])) lo = mid;\n    else hi = mid;\n  }\n  return oklchToRgb(l, lo, h);\n}\n\nfunction parseColor(input: string): OklchColor {\n  const s = input.trim().toLowerCase();\n  const oklchMatch = s.match(\n    /oklch\\(\\s*([\\d.]+)(?:%?)\\s+([\\d.]+)\\s+([\\d.]+)\\s*(?:\\/\\s*([\\d.]+%?))?\\s*\\)/\n  );\n  if (oklchMatch) {\n    let a = 1;\n    if (oklchMatch[4])\n      a = oklchMatch[4].endsWith(\"%\") ? parseFloat(oklchMatch[4]) / 100 : parseFloat(oklchMatch[4]);\n    return {\n      l: parseFloat(oklchMatch[1]),\n      c: parseFloat(oklchMatch[2]),\n      h: parseFloat(oklchMatch[3]),\n      a,\n    };\n  }\n  const hslMatch = s.match(\n    /hsla?\\(\\s*([\\d.]+)\\s*[,\\s]\\s*([\\d.]+)%?\\s*[,\\s]\\s*([\\d.]+)%?\\s*(?:[,/]\\s*([\\d.]+%?))?\\s*\\)/\n  );\n  if (hslMatch) {\n    const [r, g, b] = hslToRgb(\n      parseFloat(hslMatch[1]),\n      parseFloat(hslMatch[2]),\n      parseFloat(hslMatch[3])\n    );\n    let a = 1;\n    if (hslMatch[4])\n      a = hslMatch[4].endsWith(\"%\") ? parseFloat(hslMatch[4]) / 100 : parseFloat(hslMatch[4]);\n    return { ...rgbToOklch(r, g, b), a };\n  }\n  const rgbMatch = s.match(\n    /rgba?\\(\\s*([\\d.]+)\\s*[,\\s]\\s*([\\d.]+)\\s*[,\\s]\\s*([\\d.]+)\\s*(?:[,/]\\s*([\\d.]+%?))?\\s*\\)/\n  );\n  if (rgbMatch) {\n    let a = 1;\n    if (rgbMatch[4])\n      a = rgbMatch[4].endsWith(\"%\") ? parseFloat(rgbMatch[4]) / 100 : parseFloat(rgbMatch[4]);\n    return {\n      ...rgbToOklch(parseFloat(rgbMatch[1]), parseFloat(rgbMatch[2]), parseFloat(rgbMatch[3])),\n      a,\n    };\n  }\n  if (s.startsWith(\"#\")) {\n    const { r, g, b, a } = hexToRgb(s);\n    return { ...rgbToOklch(r, g, b), a };\n  }\n  return { l: 0.5, c: 0, h: 0, a: 1 };\n}\n\nfunction formatColor(color: OklchColor, format: ColorFormat): string {\n  const { l, c, h, a } = color;\n  switch (format) {\n    case \"oklch\": {\n      const ls = round4(l),\n        cs = round4(c),\n        hs = round2(h);\n      return a < 1 ? `oklch(${ls} ${cs} ${hs} / ${round2(a)})` : `oklch(${ls} ${cs} ${hs})`;\n    }\n    case \"hex\": {\n      const [r, g, b] = gamutMapOklch(l, c, h);\n      return rgbToHex(r, g, b, a < 1 ? a : undefined);\n    }\n    case \"rgb\": {\n      const [r, g, b] = gamutMapOklch(l, c, h);\n      return a < 1 ? `rgba(${r}, ${g}, ${b}, ${round2(a)})` : `rgb(${r}, ${g}, ${b})`;\n    }\n    case \"hsl\": {\n      const [r, g, b] = gamutMapOklch(l, c, h);\n      const [hh, ss, ll] = rgbToHsl(r, g, b);\n      return a < 1 ? `hsla(${hh}, ${ss}%, ${ll}%, ${round2(a)})` : `hsl(${hh}, ${ss}%, ${ll}%)`;\n    }\n  }\n}\n\nfunction clamp01(v: number) {\n  return Math.max(0, Math.min(1, v));\n}\nfunction clamp(v: number, min: number, max: number) {\n  return Math.max(min, Math.min(max, v));\n}\nfunction round4(v: number) {\n  return Math.round(v * 10000) / 10000;\n}\nfunction round2(v: number) {\n  return Math.round(v * 100) / 100;\n}\n\n// =============================================================================\n// Tailwind CSS v4 Default Palette (OKLCH)\n// =============================================================================\n\ntype TwPaletteEntry = { name: string; shades: Record<number, [number, number, number]> };\n\nconst TAILWIND_PALETTE: TwPaletteEntry[] = [\n  {\n    name: \"Red\",\n    shades: {\n      50: [0.9705, 0.0129, 17.38],\n      100: [0.9356, 0.0309, 17.72],\n      200: [0.8845, 0.0593, 18.33],\n      300: [0.8077, 0.1035, 19.57],\n      400: [0.7106, 0.1661, 22.22],\n      500: [0.6368, 0.2078, 25.33],\n      600: [0.5771, 0.2152, 27.33],\n      700: [0.5054, 0.1905, 27.52],\n      800: [0.4437, 0.1613, 26.9],\n      900: [0.3958, 0.1331, 25.72],\n      950: [0.2575, 0.0886, 26.04],\n    },\n  },\n  {\n    name: \"Orange\",\n    shades: {\n      50: [0.9796, 0.0158, 73.68],\n      100: [0.9542, 0.0372, 75.16],\n      200: [0.9015, 0.073, 70.7],\n      300: [0.8366, 0.1165, 66.29],\n      400: [0.7576, 0.159, 55.93],\n      500: [0.7049, 0.1867, 47.6],\n      600: [0.6461, 0.1943, 41.12],\n      700: [0.5534, 0.1739, 38.4],\n      800: [0.4698, 0.143, 37.3],\n      900: [0.4084, 0.1165, 38.17],\n      950: [0.2659, 0.0762, 36.26],\n    },\n  },\n  {\n    name: \"Amber\",\n    shades: {\n      50: [0.9869, 0.0214, 95.28],\n      100: [0.9619, 0.059, 95.62],\n      200: [0.9243, 0.1151, 95.75],\n      300: [0.879, 0.1534, 91.61],\n      400: [0.8369, 0.1644, 84.43],\n      500: [0.7686, 0.1647, 70.08],\n      600: [0.6658, 0.1574, 58.32],\n      700: [0.5553, 0.1455, 49],\n      800: [0.4732, 0.1247, 46.2],\n      900: [0.4137, 0.1054, 45.9],\n      950: [0.2791, 0.0742, 45.64],\n    },\n  },\n  {\n    name: \"Yellow\",\n    shades: {\n      50: [0.9873, 0.0262, 102.21],\n      100: [0.9729, 0.0693, 103.19],\n      200: [0.9451, 0.1243, 101.54],\n      300: [0.9052, 0.1657, 98.11],\n      400: [0.8606, 0.1731, 91.94],\n      500: [0.7952, 0.1617, 86.05],\n      600: [0.6806, 0.1423, 75.83],\n      700: [0.5538, 0.1207, 66.44],\n      800: [0.4762, 0.1034, 61.91],\n      900: [0.421, 0.0897, 57.71],\n      950: [0.2857, 0.0639, 53.81],\n    },\n  },\n  {\n    name: \"Lime\",\n    shades: {\n      50: [0.9857, 0.0311, 120.76],\n      100: [0.9669, 0.0659, 122.33],\n      200: [0.9382, 0.1217, 124.32],\n      300: [0.8972, 0.1786, 126.67],\n      400: [0.8493, 0.2073, 128.85],\n      500: [0.7681, 0.2044, 130.85],\n      600: [0.6482, 0.1754, 131.68],\n      700: [0.5322, 0.1405, 131.59],\n      800: [0.4528, 0.1129, 130.93],\n      900: [0.405, 0.0956, 131.06],\n      950: [0.2741, 0.0688, 132.11],\n    },\n  },\n  {\n    name: \"Green\",\n    shades: {\n      50: [0.9819, 0.0181, 155.83],\n      100: [0.9624, 0.0434, 156.74],\n      200: [0.925, 0.0806, 155.99],\n      300: [0.8712, 0.1363, 154.45],\n      400: [0.8003, 0.1821, 151.71],\n      500: [0.7227, 0.1921, 149.58],\n      600: [0.6271, 0.1699, 149.21],\n      700: [0.5273, 0.1371, 150.07],\n      800: [0.4479, 0.1083, 151.33],\n      900: [0.3925, 0.0896, 152.54],\n      950: [0.2664, 0.0628, 152.93],\n    },\n  },\n  {\n    name: \"Emerald\",\n    shades: {\n      50: [0.9793, 0.0207, 166.11],\n      100: [0.9505, 0.0507, 163.05],\n      200: [0.9049, 0.0895, 164.15],\n      300: [0.8452, 0.1299, 164.98],\n      400: [0.7729, 0.1535, 163.22],\n      500: [0.6959, 0.1491, 162.48],\n      600: [0.596, 0.1274, 163.23],\n      700: [0.5081, 0.1049, 165.61],\n      800: [0.4318, 0.0865, 166.91],\n      900: [0.378, 0.073, 168.94],\n      950: [0.2621, 0.0487, 172.55],\n    },\n  },\n  {\n    name: \"Teal\",\n    shades: {\n      50: [0.9836, 0.0142, 180.72],\n      100: [0.9527, 0.0498, 180.8],\n      200: [0.91, 0.0927, 180.43],\n      300: [0.8549, 0.1251, 181.07],\n      400: [0.7845, 0.1325, 181.91],\n      500: [0.7038, 0.123, 182.5],\n      600: [0.6002, 0.1038, 184.7],\n      700: [0.5109, 0.0861, 186.39],\n      800: [0.437, 0.0705, 188.22],\n      900: [0.3861, 0.059, 188.42],\n      950: [0.2773, 0.0447, 192.52],\n    },\n  },\n  {\n    name: \"Cyan\",\n    shades: {\n      50: [0.9841, 0.0189, 200.87],\n      100: [0.9563, 0.0443, 203.39],\n      200: [0.9167, 0.0772, 205.04],\n      300: [0.8651, 0.1153, 207.08],\n      400: [0.7971, 0.1339, 211.53],\n      500: [0.7148, 0.1257, 215.22],\n      600: [0.6089, 0.1109, 221.72],\n      700: [0.5198, 0.0936, 223.13],\n      800: [0.45, 0.0771, 224.28],\n      900: [0.3982, 0.0664, 227.39],\n      950: [0.3018, 0.0541, 229.7],\n    },\n  },\n  {\n    name: \"Sky\",\n    shades: {\n      50: [0.9771, 0.0125, 236.62],\n      100: [0.9514, 0.0252, 236.82],\n      200: [0.9014, 0.0555, 230.9],\n      300: [0.8276, 0.1013, 230.32],\n      400: [0.7535, 0.139, 232.66],\n      500: [0.6847, 0.1479, 237.32],\n      600: [0.5876, 0.1389, 241.97],\n      700: [0.5, 0.1193, 242.75],\n      800: [0.4434, 0.124, 240.79],\n      900: [0.3912, 0.0845, 240.88],\n      950: [0.2935, 0.0632, 243.16],\n    },\n  },\n  {\n    name: \"Blue\",\n    shades: {\n      50: [0.9705, 0.0142, 254.6],\n      100: [0.9319, 0.0316, 255.59],\n      200: [0.8823, 0.0571, 254.13],\n      300: [0.8091, 0.0956, 251.81],\n      400: [0.7137, 0.1434, 254.62],\n      500: [0.6231, 0.188, 259.81],\n      600: [0.5461, 0.2152, 262.88],\n      700: [0.4882, 0.2172, 264.38],\n      800: [0.4244, 0.1809, 265.64],\n      900: [0.3791, 0.1378, 265.52],\n      950: [0.2823, 0.0874, 267.94],\n    },\n  },\n  {\n    name: \"Indigo\",\n    shades: {\n      50: [0.9619, 0.0179, 272.31],\n      100: [0.9299, 0.0334, 272.79],\n      200: [0.8699, 0.0622, 274.04],\n      300: [0.7853, 0.1041, 274.71],\n      400: [0.6801, 0.1583, 276.93],\n      500: [0.5854, 0.2041, 277.12],\n      600: [0.5106, 0.2301, 276.97],\n      700: [0.4568, 0.2146, 277.02],\n      800: [0.3984, 0.1773, 277.37],\n      900: [0.3588, 0.1354, 278.7],\n      950: [0.2573, 0.0861, 281.29],\n    },\n  },\n  {\n    name: \"Violet\",\n    shades: {\n      50: [0.9786, 0.0142, 308.3],\n      100: [0.9433, 0.0284, 294.59],\n      200: [0.8943, 0.0549, 293.28],\n      300: [0.8112, 0.1013, 293.57],\n      400: [0.709, 0.1592, 293.54],\n      500: [0.6056, 0.2189, 292.72],\n      600: [0.5413, 0.2466, 293.01],\n      700: [0.4907, 0.2412, 292.58],\n      800: [0.432, 0.2106, 292.76],\n      900: [0.3796, 0.1783, 293.74],\n      950: [0.2827, 0.1351, 291.09],\n    },\n  },\n  {\n    name: \"Purple\",\n    shades: {\n      50: [0.9768, 0.0142, 308.3],\n      100: [0.9464, 0.0327, 307.17],\n      200: [0.9024, 0.0604, 306.7],\n      300: [0.8268, 0.1082, 306.38],\n      400: [0.7217, 0.1767, 305.5],\n      500: [0.6268, 0.2325, 303.9],\n      600: [0.5575, 0.2525, 302.32],\n      700: [0.4955, 0.2369, 301.92],\n      800: [0.4383, 0.1983, 303.72],\n      900: [0.3807, 0.1661, 304.99],\n      950: [0.2905, 0.1432, 302.72],\n    },\n  },\n  {\n    name: \"Fuchsia\",\n    shades: {\n      50: [0.9773, 0.0174, 320.06],\n      100: [0.952, 0.036, 318.85],\n      200: [0.903, 0.0732, 319.62],\n      300: [0.833, 0.1322, 321.43],\n      400: [0.7477, 0.207, 322.16],\n      500: [0.6668, 0.2591, 322.15],\n      600: [0.5915, 0.2569, 322.9],\n      700: [0.518, 0.2258, 323.95],\n      800: [0.4519, 0.1922, 324.59],\n      900: [0.4007, 0.1601, 325.61],\n      950: [0.2932, 0.1309, 325.66],\n    },\n  },\n  {\n    name: \"Pink\",\n    shades: {\n      50: [0.9714, 0.0141, 343.2],\n      100: [0.9482, 0.0276, 342.26],\n      200: [0.8994, 0.0589, 343.23],\n      300: [0.8228, 0.1095, 346.02],\n      400: [0.7253, 0.1752, 349.76],\n      500: [0.6559, 0.2118, 354.31],\n      600: [0.5916, 0.218, 0.58],\n      700: [0.5246, 0.199, 3.96],\n      800: [0.4587, 0.1697, 3.82],\n      900: [0.4078, 0.1442, 2.43],\n      950: [0.2845, 0.1048, 3.91],\n    },\n  },\n  {\n    name: \"Rose\",\n    shades: {\n      50: [0.9694, 0.0151, 12.42],\n      100: [0.9414, 0.0297, 12.58],\n      200: [0.8924, 0.0559, 10],\n      300: [0.8097, 0.1061, 11.64],\n      400: [0.7192, 0.169, 13.43],\n      500: [0.645, 0.2154, 16.44],\n      600: [0.5858, 0.222, 17.58],\n      700: [0.5143, 0.1978, 16.93],\n      800: [0.4546, 0.1713, 13.7],\n      900: [0.4103, 0.1502, 10.27],\n      950: [0.2708, 0.1009, 12.09],\n    },\n  },\n];\n\nconst SHADE_KEYS = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950] as const;\n\n// =============================================================================\n// Canvas Drawing\n// =============================================================================\n\nfunction drawColorArea(canvas: HTMLCanvasElement, hue: number) {\n  const ctx = canvas.getContext(\"2d\", { willReadFrequently: false });\n  if (!ctx) return;\n  const { width, height } = canvas;\n  const imageData = ctx.createImageData(width, height);\n  const data = imageData.data;\n  for (let y = 0; y < height; y++) {\n    const lightness = 1 - y / (height - 1);\n    for (let x = 0; x < width; x++) {\n      const chroma = (x / (width - 1)) * 0.4;\n      const [r, g, b] = gamutMapOklch(lightness, chroma, hue);\n      const idx = (y * width + x) * 4;\n      data[idx] = r;\n      data[idx + 1] = g;\n      data[idx + 2] = b;\n      data[idx + 3] = 255;\n    }\n  }\n  ctx.putImageData(imageData, 0, 0);\n}\n\nfunction drawHueStrip(canvas: HTMLCanvasElement) {\n  const ctx = canvas.getContext(\"2d\");\n  if (!ctx) return;\n  const { width, height } = canvas;\n  const imageData = ctx.createImageData(width, height);\n  const data = imageData.data;\n  for (let x = 0; x < width; x++) {\n    const hue = (x / (width - 1)) * 360;\n    const [r, g, b] = oklchToRgb(0.7, 0.15, hue);\n    for (let y = 0; y < height; y++) {\n      const idx = (y * width + x) * 4;\n      data[idx] = r;\n      data[idx + 1] = g;\n      data[idx + 2] = b;\n      data[idx + 3] = 255;\n    }\n  }\n  ctx.putImageData(imageData, 0, 0);\n}\n\nfunction drawAlphaStrip(canvas: HTMLCanvasElement, l: number, c: number, h: number) {\n  const ctx = canvas.getContext(\"2d\");\n  if (!ctx) return;\n  const { width, height } = canvas;\n  const imageData = ctx.createImageData(width, height);\n  const data = imageData.data;\n  const [cr, cg, cb] = gamutMapOklch(l, c, h);\n  for (let x = 0; x < width; x++) {\n    const alpha = x / (width - 1);\n    for (let y = 0; y < height; y++) {\n      const idx = (y * width + x) * 4;\n      const isLight = (Math.floor(x / 5) + Math.floor(y / 5)) % 2 === 0;\n      const bg = isLight ? 255 : 200;\n      data[idx] = Math.round(cr * alpha + bg * (1 - alpha));\n      data[idx + 1] = Math.round(cg * alpha + bg * (1 - alpha));\n      data[idx + 2] = Math.round(cb * alpha + bg * (1 - alpha));\n      data[idx + 3] = 255;\n    }\n  }\n  ctx.putImageData(imageData, 0, 0);\n}\n\n// =============================================================================\n// Hook: Drag\n// =============================================================================\n\nfunction useDrag(onDrag: (x: number, y: number, rect: DOMRect) => void) {\n  const ref = React.useRef<HTMLDivElement>(null);\n  const dragging = React.useRef(false);\n  const handlePointerDown = React.useCallback(\n    (e: React.PointerEvent) => {\n      e.preventDefault();\n      dragging.current = true;\n      const el = ref.current;\n      if (!el) return;\n      el.setPointerCapture(e.pointerId);\n      onDrag(e.clientX, e.clientY, el.getBoundingClientRect());\n    },\n    [onDrag]\n  );\n  const handlePointerMove = React.useCallback(\n    (e: React.PointerEvent) => {\n      if (!dragging.current) return;\n      const el = ref.current;\n      if (!el) return;\n      onDrag(e.clientX, e.clientY, el.getBoundingClientRect());\n    },\n    [onDrag]\n  );\n  const handlePointerUp = React.useCallback(() => {\n    dragging.current = false;\n  }, []);\n  return { ref, handlePointerDown, handlePointerMove, handlePointerUp };\n}\n\n// =============================================================================\n// Sub-components\n// =============================================================================\n\n/** Fixed content height for consistent layout across tabs */\nconst CONTENT_H = \"h-48\";\n\nfunction ColorArea({ color, onChange }: { color: OklchColor; onChange: (c: OklchColor) => void }) {\n  const canvasRef = React.useRef<HTMLCanvasElement>(null);\n  React.useEffect(() => {\n    const c = canvasRef.current;\n    if (!c) return;\n    c.width = 220;\n    c.height = 140;\n    drawColorArea(c, color.h);\n  }, [color.h]);\n  const handleDrag = React.useCallback(\n    (cx: number, cy: number, rect: DOMRect) => {\n      const x = clamp((cx - rect.left) / rect.width, 0, 1);\n      const y = clamp((cy - rect.top) / rect.height, 0, 1);\n      onChange({ ...color, l: round4(1 - y), c: round4(x * 0.4) });\n    },\n    [color, onChange]\n  );\n  const drag = useDrag(handleDrag);\n  const thumbX = (color.c / 0.4) * 100,\n    thumbY = (1 - color.l) * 100;\n  const [tr, tg, tb] = gamutMapOklch(color.l, color.c, color.h);\n\n  return (\n    <div\n      ref={drag.ref}\n      className=\"relative w-full cursor-crosshair  rounded-sm flex-1\"\n      onPointerDown={drag.handlePointerDown}\n      onPointerMove={drag.handlePointerMove}\n      onPointerUp={drag.handlePointerUp}\n      role=\"slider\"\n      aria-label=\"Color area: drag to adjust lightness and chroma\"\n      tabIndex={0}\n      onKeyDown={(e) => {\n        const s = e.shiftKey ? 0.05 : 0.01;\n        if (e.key === \"ArrowRight\") onChange({ ...color, c: clamp(color.c + s * 0.4, 0, 0.4) });\n        else if (e.key === \"ArrowLeft\") onChange({ ...color, c: clamp(color.c - s * 0.4, 0, 0.4) });\n        else if (e.key === \"ArrowUp\") onChange({ ...color, l: clamp(color.l + s, 0, 1) });\n        else if (e.key === \"ArrowDown\") onChange({ ...color, l: clamp(color.l - s, 0, 1) });\n      }}\n    >\n      <canvas\n        ref={canvasRef}\n        className=\"block h-full w-full rounded-sm\"\n        style={{ imageRendering: \"pixelated\" }}\n      />\n      <div\n        className=\"pointer-events-none absolute size-3.5 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-white shadow-xs\"\n        style={{\n          left: `${thumbX}%`,\n          top: `${thumbY}%`,\n          backgroundColor: `rgb(${tr}, ${tg}, ${tb})`,\n        }}\n      />\n    </div>\n  );\n}\n\nfunction HueSlider({ hue, onChange }: { hue: number; onChange: (h: number) => void }) {\n  const canvasRef = React.useRef<HTMLCanvasElement>(null);\n  React.useEffect(() => {\n    const c = canvasRef.current;\n    if (!c) return;\n    c.width = 220;\n    c.height = 12;\n    drawHueStrip(c);\n  }, []);\n  const handleDrag = React.useCallback(\n    (cx: number, _: number, rect: DOMRect) => {\n      onChange(round2(clamp((cx - rect.left) / rect.width, 0, 1) * 360));\n    },\n    [onChange]\n  );\n  const drag = useDrag(handleDrag);\n  const [r, g, b] = oklchToRgb(0.7, 0.15, hue);\n  return (\n    <div\n      ref={drag.ref}\n      className=\"relative h-2 w-full cursor-pointer rounded-full\"\n      onPointerDown={drag.handlePointerDown}\n      onPointerMove={drag.handlePointerMove}\n      onPointerUp={drag.handlePointerUp}\n      role=\"slider\"\n      aria-label=\"Hue\"\n      aria-valuemin={0}\n      aria-valuemax={360}\n      aria-valuenow={Math.round(hue)}\n      tabIndex={0}\n      onKeyDown={(e) => {\n        const s = e.shiftKey ? 10 : 1;\n        if (e.key === \"ArrowRight\") onChange((hue + s) % 360);\n        else if (e.key === \"ArrowLeft\") onChange((hue - s + 360) % 360);\n      }}\n    >\n      <canvas ref={canvasRef} className=\"block h-full w-full rounded-full\" />\n      <div\n        className=\"pointer-events-none absolute top-1/2 size-3.5 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-white shadow-sm\"\n        style={{ left: `${(hue / 360) * 100}%`, backgroundColor: `rgb(${r}, ${g}, ${b})` }}\n      />\n    </div>\n  );\n}\n\nfunction AlphaSlider({ color, onChange }: { color: OklchColor; onChange: (a: number) => void }) {\n  const canvasRef = React.useRef<HTMLCanvasElement>(null);\n  React.useEffect(() => {\n    const c = canvasRef.current;\n    if (!c) return;\n    c.width = 220;\n    c.height = 12;\n    drawAlphaStrip(c, color.l, color.c, color.h);\n  }, [color.l, color.c, color.h]);\n  const handleDrag = React.useCallback(\n    (cx: number, _: number, rect: DOMRect) => {\n      onChange(round2(clamp((cx - rect.left) / rect.width, 0, 1)));\n    },\n    [onChange]\n  );\n  const drag = useDrag(handleDrag);\n  const [r, g, b] = gamutMapOklch(color.l, color.c, color.h);\n  return (\n    <div\n      ref={drag.ref}\n      className=\"relative h-2 w-full cursor-pointer rounded-full\"\n      onPointerDown={drag.handlePointerDown}\n      onPointerMove={drag.handlePointerMove}\n      onPointerUp={drag.handlePointerUp}\n      role=\"slider\"\n      aria-label=\"Opacity\"\n      aria-valuemin={0}\n      aria-valuemax={100}\n      aria-valuenow={Math.round(color.a * 100)}\n      tabIndex={0}\n      onKeyDown={(e) => {\n        const s = e.shiftKey ? 0.1 : 0.01;\n        if (e.key === \"ArrowRight\") onChange(clamp(color.a + s, 0, 1));\n        else if (e.key === \"ArrowLeft\") onChange(clamp(color.a - s, 0, 1));\n      }}\n    >\n      <canvas ref={canvasRef} className=\"block h-full w-full rounded-full\" />\n      <div\n        className=\"pointer-events-none absolute top-1/2 size-3.5 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-white shadow-sm\"\n        style={{ left: `${color.a * 100}%`, backgroundColor: `rgba(${r}, ${g}, ${b}, ${color.a})` }}\n      />\n    </div>\n  );\n}\n\n// --- Numeric Input -----------------------------------------------------------\n\nfunction NumField({\n  label,\n  value,\n  min,\n  max,\n  step,\n  onChange,\n}: {\n  label: string;\n  value: number;\n  min: number;\n  max: number;\n  step?: number;\n  onChange: (v: number) => void;\n}) {\n  const [text, setText] = React.useState(String(value));\n  React.useEffect(() => {\n    setText(String(value));\n  }, [value]);\n  const commit = () => {\n    const n = parseFloat(text);\n    if (!isNaN(n)) onChange(clamp(n, min, max));\n    else setText(String(value));\n  };\n  return (\n    <div className=\"flex flex-1 flex-col gap-0.5\">\n      <label className=\"text-[10px] font-medium uppercase text-muted-foreground text-center\">\n        {label}\n      </label>\n      <Input\n        type=\"text\"\n        inputMode=\"decimal\"\n        value={text}\n        onChange={(e) => setText(e.target.value)}\n        onBlur={commit}\n        onKeyDown={(e) => {\n          if (e.key === \"Enter\") commit();\n          if (e.key === \"ArrowUp\") {\n            e.preventDefault();\n            onChange(clamp(value + (step ?? 1), min, max));\n          }\n          if (e.key === \"ArrowDown\") {\n            e.preventDefault();\n            onChange(clamp(value - (step ?? 1), min, max));\n          }\n        }}\n        className=\"h-7 rounded-lg px-1 text-center text-xs tabular-nums shadow-none\"\n        aria-label={label}\n      />\n    </div>\n  );\n}\n\n// --- Values Tab (full input panel) -------------------------------------------\n\ntype InputMode = \"oklch\" | \"hsl\" | \"rgb\" | \"hex\";\n\nfunction ValuesPanel({\n  color,\n  onChange,\n  showAlpha,\n}: {\n  color: OklchColor;\n  onChange: (c: OklchColor) => void;\n  showAlpha: boolean;\n}) {\n  const [mode, setMode] = React.useState<InputMode>(\"oklch\");\n  const modes: InputMode[] = [\"oklch\", \"hsl\", \"rgb\", \"hex\"];\n  const [r, g, b] = gamutMapOklch(color.l, color.c, color.h);\n  const bgColor = color.a < 1 ? `rgba(${r}, ${g}, ${b}, ${color.a})` : `rgb(${r}, ${g}, ${b})`;\n\n  return (\n    <div className={cn(CONTENT_H, \"flex flex-col gap-3 justify-start\")}>\n      {/* Preview swatch */}\n      <div className=\"flex items-center gap-3\">\n        <div className=\"size-10 shrink-0 rounded-sm\" style={{ backgroundColor: bgColor }} />\n        <div className=\"flex flex-col gap-0.5 min-w-0 flex-1\">\n          <span className=\"text-[10px] font-medium text-muted-foreground uppercase\">\n            Current Color\n          </span>\n          <span className=\"text-xs font-mono text-foreground truncate\">\n            {formatColor(color, mode)}\n          </span>\n        </div>\n      </div>\n\n      {/* Color space tabs */}\n      <div className=\"flex gap-0.5 rounded-lg bg-muted p-0.5\">\n        {modes.map((m) => (\n          <button\n            key={m}\n            type=\"button\"\n            onClick={() => setMode(m)}\n            className={cn(\n              \"flex-1 rounded-sm px-1.5 py-0.5 text-[10px] font-medium uppercase transition-colors\",\n              mode === m\n                ? \"bg-background text-foreground shadow-sm\"\n                : \"text-muted-foreground hover:text-foreground\"\n            )}\n          >\n            {m}\n          </button>\n        ))}\n      </div>\n\n      {/* Inputs */}\n      <div className=\"flex gap-1\">\n        {mode === \"oklch\" && (\n          <>\n            <NumField\n              label=\"L\"\n              value={round4(color.l)}\n              min={0}\n              max={1}\n              step={0.01}\n              onChange={(l) => onChange({ ...color, l })}\n            />\n            <NumField\n              label=\"C\"\n              value={round4(color.c)}\n              min={0}\n              max={0.4}\n              step={0.005}\n              onChange={(c) => onChange({ ...color, c })}\n            />\n            <NumField\n              label=\"H\"\n              value={round2(color.h)}\n              min={0}\n              max={360}\n              step={1}\n              onChange={(h) => onChange({ ...color, h })}\n            />\n            {showAlpha && (\n              <NumField\n                label=\"A\"\n                value={round2(color.a)}\n                min={0}\n                max={1}\n                step={0.01}\n                onChange={(a) => onChange({ ...color, a })}\n              />\n            )}\n          </>\n        )}\n        {mode === \"hsl\" &&\n          (() => {\n            const [hh, ss, ll] = rgbToHsl(r, g, b);\n            const update = (h: number, s: number, l: number) => {\n              const [nr, ng, nb] = hslToRgb(h, s, l);\n              onChange({ ...rgbToOklch(nr, ng, nb), a: color.a });\n            };\n            return (\n              <>\n                <NumField\n                  label=\"H\"\n                  value={hh}\n                  min={0}\n                  max={360}\n                  step={1}\n                  onChange={(v) => update(v, ss, ll)}\n                />\n                <NumField\n                  label=\"S\"\n                  value={ss}\n                  min={0}\n                  max={100}\n                  step={1}\n                  onChange={(v) => update(hh, v, ll)}\n                />\n                <NumField\n                  label=\"L\"\n                  value={ll}\n                  min={0}\n                  max={100}\n                  step={1}\n                  onChange={(v) => update(hh, ss, v)}\n                />\n                {showAlpha && (\n                  <NumField\n                    label=\"A\"\n                    value={round2(color.a)}\n                    min={0}\n                    max={1}\n                    step={0.01}\n                    onChange={(a) => onChange({ ...color, a })}\n                  />\n                )}\n              </>\n            );\n          })()}\n        {mode === \"rgb\" &&\n          (() => {\n            const update = (nr: number, ng: number, nb: number) =>\n              onChange({ ...rgbToOklch(nr, ng, nb), a: color.a });\n            return (\n              <>\n                <NumField\n                  label=\"R\"\n                  value={r}\n                  min={0}\n                  max={255}\n                  step={1}\n                  onChange={(v) => update(v, g, b)}\n                />\n                <NumField\n                  label=\"G\"\n                  value={g}\n                  min={0}\n                  max={255}\n                  step={1}\n                  onChange={(v) => update(r, v, b)}\n                />\n                <NumField\n                  label=\"B\"\n                  value={b}\n                  min={0}\n                  max={255}\n                  step={1}\n                  onChange={(v) => update(r, g, v)}\n                />\n                {showAlpha && (\n                  <NumField\n                    label=\"A\"\n                    value={round2(color.a)}\n                    min={0}\n                    max={1}\n                    step={0.01}\n                    onChange={(a) => onChange({ ...color, a })}\n                  />\n                )}\n              </>\n            );\n          })()}\n        {mode === \"hex\" && <HexInput color={color} onChange={onChange} showAlpha={showAlpha} />}\n      </div>\n    </div>\n  );\n}\n\nfunction HexInput({\n  color,\n  onChange,\n  showAlpha,\n}: {\n  color: OklchColor;\n  onChange: (c: OklchColor) => void;\n  showAlpha: boolean;\n}) {\n  const [r, g, b] = gamutMapOklch(color.l, color.c, color.h);\n  const hex = rgbToHex(r, g, b, showAlpha && color.a < 1 ? color.a : undefined);\n  const [text, setText] = React.useState(hex);\n  React.useEffect(() => {\n    setText(hex);\n  }, [hex]);\n  const commit = () => {\n    const cleaned = text.startsWith(\"#\") ? text : `#${text}`;\n    if (/^#([0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(cleaned)) {\n      const parsed = hexToRgb(cleaned);\n      onChange({ ...rgbToOklch(parsed.r, parsed.g, parsed.b), a: parsed.a });\n    } else setText(hex);\n  };\n  return (\n    <div className=\"flex flex-1 flex-col gap-0.5\">\n      <label className=\"text-[10px] font-medium uppercase text-muted-foreground text-center\">\n        Hex\n      </label>\n      <Input\n        type=\"text\"\n        value={text}\n        onChange={(e) => setText(e.target.value)}\n        onBlur={commit}\n        onKeyDown={(e) => {\n          if (e.key === \"Enter\") commit();\n        }}\n        className=\"h-7 rounded-lg px-1.5 text-center text-xs font-mono tabular-nums shadow-none\"\n        aria-label=\"Hex color value\"\n      />\n    </div>\n  );\n}\n\n// --- Tailwind Panel (vertical columns, horizontal scroll) --------------------\n\nfunction TailwindPanel({\n  color,\n  onSelect,\n}: {\n  color: OklchColor;\n  onSelect: (c: OklchColor) => void;\n}) {\n  return (\n    <div className={cn(CONTENT_H, \"overflow-x-auto overflow-y-visible rounded-sm\")}>\n      <div className=\"flex gap-px h-full\">\n        {TAILWIND_PALETTE.map((family) => (\n          <div key={family.name} className=\"flex flex-col items-center gap-px shrink-0\">\n            {SHADE_KEYS.map((shade) => {\n              const [l, c, h] = family.shades[shade];\n              const [r, g, b] = gamutMapOklch(l, c, h);\n              const isActive =\n                Math.abs(color.l - l) < 0.005 &&\n                Math.abs(color.c - c) < 0.005 &&\n                Math.abs(color.h - h) < 0.5;\n              return (\n                <button\n                  key={shade}\n                  type=\"button\"\n                  title={`${family.name} ${shade}`}\n                  aria-label={`${family.name} ${shade}`}\n                  className={cn(\n                    \"w-8 flex-1 rounded-xs transition-all hover:transition-none duration-300 hover:scale-105 focus-visible:border-2 focus-visible:border-foreground\",\n                    isActive\n                      ? \"inset-ring-2 inset-ring-foreground/30\"\n                      : \"hover:border-foreground/20\"\n                  )}\n                  style={{ backgroundColor: `rgb(${r}, ${g}, ${b})` }}\n                  onClick={() => onSelect({ l, c, h, a: 1 })}\n                />\n              );\n            })}\n          </div>\n        ))}\n      </div>\n    </div>\n  );\n}\n\n// --- Custom Presets ---\n\nfunction CustomPresetsPanel({\n  presets,\n  onSelect,\n}: {\n  presets: PresetColor[];\n  onSelect: (c: OklchColor) => void;\n}) {\n  return (\n    <div className={cn(CONTENT_H, \"flex flex-wrap content-start gap-1 overflow-y-auto p-0.5\")}>\n      {presets.map((preset, i) => {\n        const parsed = parseColor(preset.value);\n        const [r, g, b] = gamutMapOklch(parsed.l, parsed.c, parsed.h);\n        return (\n          <button\n            key={i}\n            type=\"button\"\n            title={preset.label}\n            aria-label={preset.label}\n            className=\"size-7 rounded-sm border border-transparent transition-all hover:scale-110 hover:border-foreground/30 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring\"\n            style={{ backgroundColor: `rgb(${r}, ${g}, ${b})` }}\n            onClick={() => onSelect(parsed)}\n          />\n        );\n      })}\n    </div>\n  );\n}\n\n// =============================================================================\n// Main Component\n// =============================================================================\n\ntype TopTab = \"picker\" | \"tailwind\" | \"values\";\n\nfunction ColorPicker({\n  value,\n  defaultValue,\n  onValueChange,\n  format = \"oklch\",\n  showAlpha = false,\n  presets,\n  className,\n}: ColorPickerProps) {\n  const [internalColor, setInternalColor] = React.useState<OklchColor>(() =>\n    parseColor(value ?? defaultValue ?? \"oklch(0.7 0.15 230)\")\n  );\n  React.useEffect(() => {\n    if (value !== undefined) setInternalColor(parseColor(value));\n  }, [value]);\n\n  const hasTailwind = presets === \"tailwind\";\n  const hasCustomPresets = Array.isArray(presets);\n  const showTabs = hasTailwind || hasCustomPresets;\n\n  const tabs: { id: TopTab; label: string }[] = [\n    { id: \"picker\", label: \"Picker\" },\n    ...(showTabs\n      ? [{ id: \"tailwind\" as TopTab, label: hasTailwind ? \"Tailwind\" : \"Presets\" }]\n      : []),\n    { id: \"values\", label: \"Values\" },\n  ];\n\n  const [topTab, setTopTab] = React.useState<TopTab>(\"picker\");\n  const color = value !== undefined ? parseColor(value) : internalColor;\n\n  const updateColor = React.useCallback(\n    (newColor: OklchColor) => {\n      if (value === undefined) setInternalColor(newColor);\n      onValueChange?.(formatColor(newColor, format));\n    },\n    [value, format, onValueChange]\n  );\n\n  return (\n    <div\n      data-slot=\"color-picker\"\n      className={cn(\n        \"flex w-[264px] flex-col gap-0 rounded-xl border bg-popover shadow-lg overflow-hidden\",\n        className\n      )}\n    >\n      {/* Line-style tabs */}\n      <div className=\"flex border-b\">\n        {tabs.map((tab) => (\n          <button\n            key={tab.id}\n            type=\"button\"\n            onClick={() => setTopTab(tab.id)}\n            className={cn(\n              \"flex-1 py-1 text-[11px] font-medium transition-colors relative\",\n              topTab === tab.id ? \"text-foreground\" : \"text-muted-foreground hover:text-foreground\"\n            )}\n          >\n            {tab.label}\n            {topTab === tab.id && (\n              <span className=\"absolute bottom-0 left-2 right-2 h-0.5 rounded-full bg-foreground\" />\n            )}\n          </button>\n        ))}\n      </div>\n\n      {/* Content area (fixed height) */}\n      <div className=\"p-2.5\">\n        {topTab === \"picker\" && (\n          <div className={cn(CONTENT_H, \"flex flex-col gap-2\")}>\n            <ColorArea color={color} onChange={updateColor} />\n            <div className=\"flex items-center gap-1.5\">\n              <div\n                className=\"size-7 shrink-0 rounded-sm\"\n                style={{\n                  backgroundColor: ((): string => {\n                    const [r, g, b] = gamutMapOklch(color.l, color.c, color.h);\n                    return color.a < 1 ? `rgba(${r},${g},${b},${color.a})` : `rgb(${r},${g},${b})`;\n                  })(),\n                }}\n              />\n              <div className=\"flex flex-1 flex-col gap-1.5 pr-2\">\n                <HueSlider hue={color.h} onChange={(h) => updateColor({ ...color, h })} />\n                {showAlpha && (\n                  <AlphaSlider color={color} onChange={(a) => updateColor({ ...color, a })} />\n                )}\n              </div>\n            </div>\n          </div>\n        )}\n\n        {topTab === \"tailwind\" && hasTailwind && (\n          <TailwindPanel color={color} onSelect={updateColor} />\n        )}\n        {topTab === \"tailwind\" && hasCustomPresets && (\n          <CustomPresetsPanel presets={presets} onSelect={updateColor} />\n        )}\n        {topTab === \"values\" && (\n          <ValuesPanel color={color} onChange={updateColor} showAlpha={showAlpha} />\n        )}\n      </div>\n    </div>\n  );\n}\n\nexport { ColorPicker, parseColor, formatColor };\nexport type { ColorPickerProps, ColorFormat, OklchColor, PresetColor };\n",
      "type": "registry:component",
      "target": "components/ui/color-picker.tsx"
    },
    {
      "path": "registry/new-york/blocks/input/input.tsx",
      "content": "import * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nfunction Input({ className, type, ...props }: React.ComponentProps<\"input\">) {\n  return (\n    <input\n      type={type}\n      data-slot=\"input\"\n      className={cn(\n        \"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-10 w-full min-w-0 rounded-xl border bg-transparent px-4 py-2 shadow-md shadow-shade transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 text-sm\",\n        \"focus-visible:border-primary focus-visible:ring-primary/30 focus-visible:ring-[3px]\",\n        \"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive\",\n        \"hover:shadow-none hover:bg-muted hover:border-border/0 transition-all duration-150 ease-out hover:duration-75\",\n        className\n      )}\n      {...props}\n    />\n  );\n}\n\nexport { Input };\n",
      "type": "registry:component",
      "target": "components/ui/input.tsx"
    }
  ]
}