chromonym 1.4.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/NOTICE.md +32 -4
  2. package/README.md +216 -75
  3. package/dist/conversions/hex.d.ts +1 -1
  4. package/dist/conversions/hex.d.ts.map +1 -1
  5. package/dist/conversions/hex.js +2 -2
  6. package/dist/conversions/hex.js.map +1 -1
  7. package/dist/conversions/hsl.d.ts +1 -1
  8. package/dist/conversions/hsl.d.ts.map +1 -1
  9. package/dist/conversions/hsl.js +2 -2
  10. package/dist/conversions/hsl.js.map +1 -1
  11. package/dist/conversions/hsv.d.ts +1 -1
  12. package/dist/conversions/hsv.d.ts.map +1 -1
  13. package/dist/conversions/hsv.js +2 -2
  14. package/dist/conversions/hsv.js.map +1 -1
  15. package/dist/conversions/index.d.ts +5 -5
  16. package/dist/conversions/index.d.ts.map +1 -1
  17. package/dist/conversions/index.js +5 -5
  18. package/dist/conversions/index.js.map +1 -1
  19. package/dist/conversions/pantone.d.ts +9 -7
  20. package/dist/conversions/pantone.d.ts.map +1 -1
  21. package/dist/conversions/pantone.js +11 -10
  22. package/dist/conversions/pantone.js.map +1 -1
  23. package/dist/conversions/rgb.d.ts +1 -1
  24. package/dist/conversions/rgb.d.ts.map +1 -1
  25. package/dist/conversions/rgb.js +1 -1
  26. package/dist/conversions/rgb.js.map +1 -1
  27. package/dist/convert.d.ts +45 -5
  28. package/dist/convert.d.ts.map +1 -1
  29. package/dist/convert.js +68 -17
  30. package/dist/convert.js.map +1 -1
  31. package/dist/detectFormat.d.ts +1 -1
  32. package/dist/detectFormat.d.ts.map +1 -1
  33. package/dist/detectFormat.js +2 -6
  34. package/dist/detectFormat.js.map +1 -1
  35. package/dist/identify.d.ts +13 -16
  36. package/dist/identify.d.ts.map +1 -1
  37. package/dist/identify.js +12 -12
  38. package/dist/identify.js.map +1 -1
  39. package/dist/index.d.ts +14 -14
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +13 -13
  42. package/dist/index.js.map +1 -1
  43. package/dist/indexing.d.ts +30 -24
  44. package/dist/indexing.d.ts.map +1 -1
  45. package/dist/indexing.js +49 -22
  46. package/dist/indexing.js.map +1 -1
  47. package/dist/math/clamp.d.ts.map +1 -1
  48. package/dist/math/clamp.js.map +1 -1
  49. package/dist/math/colorSpace.d.ts +1 -1
  50. package/dist/math/colorSpace.d.ts.map +1 -1
  51. package/dist/math/colorSpace.js.map +1 -1
  52. package/dist/math/deltaE.d.ts.map +1 -1
  53. package/dist/math/deltaE.js.map +1 -1
  54. package/dist/math/euclideanDistance.d.ts +1 -1
  55. package/dist/math/euclideanDistance.d.ts.map +1 -1
  56. package/dist/math/euclideanDistance.js.map +1 -1
  57. package/dist/math/hueSector.d.ts.map +1 -1
  58. package/dist/math/hueSector.js.map +1 -1
  59. package/dist/palettes/index.d.ts +4 -0
  60. package/dist/palettes/index.d.ts.map +1 -0
  61. package/dist/palettes/index.js +4 -0
  62. package/dist/palettes/index.js.map +1 -0
  63. package/dist/palettes/normalize.d.ts +17 -0
  64. package/dist/palettes/normalize.d.ts.map +1 -0
  65. package/dist/palettes/normalize.js +19 -0
  66. package/dist/palettes/normalize.js.map +1 -0
  67. package/dist/palettes/pantone.d.ts +1834 -0
  68. package/dist/palettes/pantone.d.ts.map +1 -0
  69. package/dist/palettes/pantone.js +925 -0
  70. package/dist/palettes/pantone.js.map +1 -0
  71. package/dist/palettes/web.d.ts +320 -0
  72. package/dist/palettes/web.d.ts.map +1 -0
  73. package/dist/{colorspaces → palettes}/web.js +18 -3
  74. package/dist/palettes/web.js.map +1 -0
  75. package/dist/palettes/x11.d.ts +1346 -0
  76. package/dist/palettes/x11.d.ts.map +1 -0
  77. package/dist/palettes/x11.js +686 -0
  78. package/dist/palettes/x11.js.map +1 -0
  79. package/dist/resolve.d.ts +9 -7
  80. package/dist/resolve.d.ts.map +1 -1
  81. package/dist/resolve.js +18 -17
  82. package/dist/resolve.js.map +1 -1
  83. package/dist/types.d.ts +10 -6
  84. package/dist/types.d.ts.map +1 -1
  85. package/dist/types.js +2 -1
  86. package/dist/types.js.map +1 -1
  87. package/package.json +32 -18
  88. package/dist/colorspaces/index.d.ts +0 -4
  89. package/dist/colorspaces/index.d.ts.map +0 -1
  90. package/dist/colorspaces/index.js +0 -4
  91. package/dist/colorspaces/index.js.map +0 -1
  92. package/dist/colorspaces/pantone.d.ts +0 -919
  93. package/dist/colorspaces/pantone.d.ts.map +0 -1
  94. package/dist/colorspaces/pantone.js +0 -918
  95. package/dist/colorspaces/pantone.js.map +0 -1
  96. package/dist/colorspaces/registry.d.ts +0 -26
  97. package/dist/colorspaces/registry.d.ts.map +0 -1
  98. package/dist/colorspaces/registry.js +0 -36
  99. package/dist/colorspaces/registry.js.map +0 -1
  100. package/dist/colorspaces/web.d.ts +0 -156
  101. package/dist/colorspaces/web.d.ts.map +0 -1
  102. package/dist/colorspaces/web.js.map +0 -1
  103. package/dist/colorspaces/x11.d.ts +0 -667
  104. package/dist/colorspaces/x11.d.ts.map +0 -1
  105. package/dist/colorspaces/x11.js +0 -666
  106. package/dist/colorspaces/x11.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"hsv.d.ts","sourceRoot":"","sources":["../../src/conversions/hsv.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAK1D;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CA+B/C;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,CAuB/C"}
1
+ {"version":3,"file":"hsv.d.ts","sourceRoot":"","sources":["../../src/conversions/hsv.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAK7D;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CA+B/C;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,CAuB/C","sourcesContent":["import { requireFinite } from '../math/clamp.js';\nimport { hueSectorToPrime } from '../math/hueSector.js';\nimport type { HsvInput, HsvString, Rgba } from '../types.js';\n\nconst HSV_RE =\n /^hsv\\s*\\(\\s*(-?\\d+(?:\\.\\d+)?)\\s*,\\s*(-?\\d+(?:\\.\\d+)?)%?\\s*,\\s*(-?\\d+(?:\\.\\d+)?)%?\\s*\\)$/i;\n\n/**\n * Parse HSV input (string or object) into canonical Rgba.\n * Hue wraps modulo 360; saturation and value are 0..100 percent.\n */\nexport function hsvToRgba(input: HsvInput): Rgba {\n let h: number;\n let s: number;\n let v: number;\n\n if (typeof input === 'string') {\n const match = HSV_RE.exec(input);\n if (!match) throw new Error(`Invalid hsv string: ${input}`);\n h = Number(match[1]);\n s = Number(match[2]);\n v = Number(match[3]);\n } else {\n h = requireFinite(input.h, 'h');\n s = requireFinite(input.s, 's');\n v = requireFinite(input.v, 'v');\n }\n\n const saturation = s / 100;\n const value = v / 100;\n const chroma = value * saturation;\n const hueSector = (((h % 360) + 360) % 360) / 60;\n const secondary = chroma * (1 - Math.abs((hueSector % 2) - 1));\n const valueOffset = value - chroma;\n\n const [rPrime, gPrime, bPrime] = hueSectorToPrime(hueSector, chroma, secondary);\n return {\n r: Math.round((rPrime + valueOffset) * 255),\n g: Math.round((gPrime + valueOffset) * 255),\n b: Math.round((bPrime + valueOffset) * 255),\n a: 1,\n };\n}\n\n/**\n * Emit an `hsv(h, s%, v%)` string from Rgba. Alpha is dropped; all\n * three components are rounded to whole numbers.\n */\nexport function rgbaToHsv(rgba: Rgba): HsvString {\n const rNorm = rgba.r / 255;\n const gNorm = rgba.g / 255;\n const bNorm = rgba.b / 255;\n const max = Math.max(rNorm, gNorm, bNorm);\n const min = Math.min(rNorm, gNorm, bNorm);\n const delta = max - min;\n const value = max;\n const saturation = max === 0 ? 0 : delta / max;\n\n let hue = 0;\n if (delta !== 0) {\n if (max === rNorm) {\n hue = (gNorm - bNorm) / delta + (gNorm < bNorm ? 6 : 0);\n } else if (max === gNorm) {\n hue = (bNorm - rNorm) / delta + 2;\n } else {\n hue = (rNorm - gNorm) / delta + 4;\n }\n hue *= 60;\n }\n\n return `hsv(${Math.round(hue)}, ${Math.round(saturation * 100)}%, ${Math.round(value * 100)}%)` as HsvString;\n}\n"]}
@@ -1,5 +1,5 @@
1
- import { requireFinite } from '../math/clamp';
2
- import { hueSectorToPrime } from '../math/hueSector';
1
+ import { requireFinite } from '../math/clamp.js';
2
+ import { hueSectorToPrime } from '../math/hueSector.js';
3
3
  const HSV_RE = /^hsv\s*\(\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)%?\s*,\s*(-?\d+(?:\.\d+)?)%?\s*\)$/i;
4
4
  /**
5
5
  * Parse HSV input (string or object) into canonical Rgba.
@@ -1 +1 @@
1
- {"version":3,"file":"hsv.js","sourceRoot":"","sources":["../../src/conversions/hsv.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hsv.js","sourceRoot":"","sources":["../../src/conversions/hsv.ts"],"names":[],"mappings":"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","sourcesContent":["import { requireFinite } from '../math/clamp.js';\nimport { hueSectorToPrime } from '../math/hueSector.js';\nimport type { HsvInput, HsvString, Rgba } from '../types.js';\n\nconst HSV_RE =\n /^hsv\\s*\\(\\s*(-?\\d+(?:\\.\\d+)?)\\s*,\\s*(-?\\d+(?:\\.\\d+)?)%?\\s*,\\s*(-?\\d+(?:\\.\\d+)?)%?\\s*\\)$/i;\n\n/**\n * Parse HSV input (string or object) into canonical Rgba.\n * Hue wraps modulo 360; saturation and value are 0..100 percent.\n */\nexport function hsvToRgba(input: HsvInput): Rgba {\n let h: number;\n let s: number;\n let v: number;\n\n if (typeof input === 'string') {\n const match = HSV_RE.exec(input);\n if (!match) throw new Error(`Invalid hsv string: ${input}`);\n h = Number(match[1]);\n s = Number(match[2]);\n v = Number(match[3]);\n } else {\n h = requireFinite(input.h, 'h');\n s = requireFinite(input.s, 's');\n v = requireFinite(input.v, 'v');\n }\n\n const saturation = s / 100;\n const value = v / 100;\n const chroma = value * saturation;\n const hueSector = (((h % 360) + 360) % 360) / 60;\n const secondary = chroma * (1 - Math.abs((hueSector % 2) - 1));\n const valueOffset = value - chroma;\n\n const [rPrime, gPrime, bPrime] = hueSectorToPrime(hueSector, chroma, secondary);\n return {\n r: Math.round((rPrime + valueOffset) * 255),\n g: Math.round((gPrime + valueOffset) * 255),\n b: Math.round((bPrime + valueOffset) * 255),\n a: 1,\n };\n}\n\n/**\n * Emit an `hsv(h, s%, v%)` string from Rgba. Alpha is dropped; all\n * three components are rounded to whole numbers.\n */\nexport function rgbaToHsv(rgba: Rgba): HsvString {\n const rNorm = rgba.r / 255;\n const gNorm = rgba.g / 255;\n const bNorm = rgba.b / 255;\n const max = Math.max(rNorm, gNorm, bNorm);\n const min = Math.min(rNorm, gNorm, bNorm);\n const delta = max - min;\n const value = max;\n const saturation = max === 0 ? 0 : delta / max;\n\n let hue = 0;\n if (delta !== 0) {\n if (max === rNorm) {\n hue = (gNorm - bNorm) / delta + (gNorm < bNorm ? 6 : 0);\n } else if (max === gNorm) {\n hue = (bNorm - rNorm) / delta + 2;\n } else {\n hue = (rNorm - gNorm) / delta + 4;\n }\n hue *= 60;\n }\n\n return `hsv(${Math.round(hue)}, ${Math.round(saturation * 100)}%, ${Math.round(value * 100)}%)` as HsvString;\n}\n"]}
@@ -1,6 +1,6 @@
1
- export { hexToRgba, rgbaToHex } from './hex';
2
- export { hslToRgba, rgbaToHsl } from './hsl';
3
- export { hsvToRgba, rgbaToHsv } from './hsv';
4
- export { pantoneToRgba, rgbaToPantone } from './pantone';
5
- export { rgbaToRgb, rgbToRgba } from './rgb';
1
+ export { hexToRgba, rgbaToHex } from './hex.js';
2
+ export { hslToRgba, rgbaToHsl } from './hsl.js';
3
+ export { hsvToRgba, rgbaToHsv } from './hsv.js';
4
+ export { pantoneToRgba, rgbaToPantone } from './pantone.js';
5
+ export { rgbaToRgb, rgbToRgba } from './rgb.js';
6
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/conversions/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/conversions/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC","sourcesContent":["export { hexToRgba, rgbaToHex } from './hex.js';\nexport { hslToRgba, rgbaToHsl } from './hsl.js';\nexport { hsvToRgba, rgbaToHsv } from './hsv.js';\nexport { pantoneToRgba, rgbaToPantone } from './pantone.js';\nexport { rgbaToRgb, rgbToRgba } from './rgb.js';\n"]}
@@ -1,6 +1,6 @@
1
- export { hexToRgba, rgbaToHex } from './hex';
2
- export { hslToRgba, rgbaToHsl } from './hsl';
3
- export { hsvToRgba, rgbaToHsv } from './hsv';
4
- export { pantoneToRgba, rgbaToPantone } from './pantone';
5
- export { rgbaToRgb, rgbToRgba } from './rgb';
1
+ export { hexToRgba, rgbaToHex } from './hex.js';
2
+ export { hslToRgba, rgbaToHsl } from './hsl.js';
3
+ export { hsvToRgba, rgbaToHsv } from './hsv.js';
4
+ export { pantoneToRgba, rgbaToPantone } from './pantone.js';
5
+ export { rgbaToRgb, rgbToRgba } from './rgb.js';
6
6
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/conversions/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/conversions/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC","sourcesContent":["export { hexToRgba, rgbaToHex } from './hex.js';\nexport { hslToRgba, rgbaToHsl } from './hsl.js';\nexport { hsvToRgba, rgbaToHsv } from './hsv.js';\nexport { pantoneToRgba, rgbaToPantone } from './pantone.js';\nexport { rgbaToRgb, rgbToRgba } from './rgb.js';\n"]}
@@ -1,17 +1,19 @@
1
- import type { PantoneCode, Rgba } from '../types';
1
+ import { type PantoneColorName } from '../palettes/pantone.js';
2
+ import type { Rgba } from '../types.js';
2
3
  /**
3
4
  * Resolve a Pantone code to Rgba.
4
5
  * Accepts any casing, spacing, and common prefixes ("PMS", "Pantone")
5
6
  * since non-alphanumeric characters are stripped before lookup.
6
7
  * Throws on unknown codes.
7
8
  */
8
- export declare function pantoneToRgba(code: PantoneCode): Rgba;
9
+ export declare function pantoneToRgba(code: string): Rgba;
9
10
  /**
10
- * Find the nearest Pantone Coated code to the given Rgba. Uses ΔE*00
11
- * (CIEDE2000) — the industry-standard perceptual metric — by default, since
12
- * Pantone values cluster densely in the saturated blue/purple region where
13
- * sRGB Euclidean distance gives visually wrong answers.
11
+ * Find the nearest Pantone Coated code to the given Rgba. Uses the
12
+ * `pantone` palette's own `defaultMetric` (ΔE*00 / CIEDE2000) — the
13
+ * industry-standard perceptual metric, since Pantone values cluster densely
14
+ * in the saturated blue/purple region where sRGB Euclidean distance gives
15
+ * visually wrong answers.
14
16
  * Alpha is ignored. Always returns a code.
15
17
  */
16
- export declare function rgbaToPantone(rgba: Rgba): PantoneCode;
18
+ export declare function rgbaToPantone(rgba: Rgba): PantoneColorName;
17
19
  //# sourceMappingURL=pantone.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pantone.d.ts","sourceRoot":"","sources":["../../src/conversions/pantone.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAY,WAAW,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAG5D;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CAKrD;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,GAAG,WAAW,CAErD"}
1
+ {"version":3,"file":"pantone.d.ts","sourceRoot":"","sources":["../../src/conversions/pantone.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,gBAAgB,EAAW,MAAM,wBAAwB,CAAC;AACxE,OAAO,KAAK,EAAY,IAAI,EAAE,MAAM,aAAa,CAAC;AAGlD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAKhD;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,GAAG,gBAAgB,CAE1D","sourcesContent":["import { getNameIndex, nearest } from '../indexing.js';\nimport { type PantoneColorName, pantone } from '../palettes/pantone.js';\nimport type { HexColor, Rgba } from '../types.js';\nimport { hexToRgba } from './hex.js';\n\n/**\n * Resolve a Pantone code to Rgba.\n * Accepts any casing, spacing, and common prefixes (\"PMS\", \"Pantone\")\n * since non-alphanumeric characters are stripped before lookup.\n * Throws on unknown codes.\n */\nexport function pantoneToRgba(code: string): Rgba {\n const canonical = getNameIndex(pantone).get(pantone.normalize(code));\n if (canonical === undefined) throw new Error(`Unknown Pantone code: ${code}`);\n const hex = pantone.colors[canonical as keyof typeof pantone.colors];\n return hexToRgba(hex as HexColor);\n}\n\n/**\n * Find the nearest Pantone Coated code to the given Rgba. Uses the\n * `pantone` palette's own `defaultMetric` (ΔE*00 / CIEDE2000) — the\n * industry-standard perceptual metric, since Pantone values cluster densely\n * in the saturated blue/purple region where sRGB Euclidean distance gives\n * visually wrong answers.\n * Alpha is ignored. Always returns a code.\n */\nexport function rgbaToPantone(rgba: Rgba): PantoneColorName {\n return nearest(rgba, pantone, pantone.defaultMetric) as PantoneColorName;\n}\n"]}
@@ -1,6 +1,6 @@
1
- import { pantone } from '../colorspaces/pantone';
2
- import { getNameIndex, nearest, pantoneNormalize } from '../indexing';
3
- import { hexToRgba } from './hex';
1
+ import { getNameIndex, nearest } from '../indexing.js';
2
+ import { pantone } from '../palettes/pantone.js';
3
+ import { hexToRgba } from './hex.js';
4
4
  /**
5
5
  * Resolve a Pantone code to Rgba.
6
6
  * Accepts any casing, spacing, and common prefixes ("PMS", "Pantone")
@@ -8,20 +8,21 @@ import { hexToRgba } from './hex';
8
8
  * Throws on unknown codes.
9
9
  */
10
10
  export function pantoneToRgba(code) {
11
- const canonical = getNameIndex(pantone, pantoneNormalize).get(pantoneNormalize(code));
11
+ const canonical = getNameIndex(pantone).get(pantone.normalize(code));
12
12
  if (canonical === undefined)
13
13
  throw new Error(`Unknown Pantone code: ${code}`);
14
- const hex = pantone[canonical];
14
+ const hex = pantone.colors[canonical];
15
15
  return hexToRgba(hex);
16
16
  }
17
17
  /**
18
- * Find the nearest Pantone Coated code to the given Rgba. Uses ΔE*00
19
- * (CIEDE2000) — the industry-standard perceptual metric — by default, since
20
- * Pantone values cluster densely in the saturated blue/purple region where
21
- * sRGB Euclidean distance gives visually wrong answers.
18
+ * Find the nearest Pantone Coated code to the given Rgba. Uses the
19
+ * `pantone` palette's own `defaultMetric` (ΔE*00 / CIEDE2000) — the
20
+ * industry-standard perceptual metric, since Pantone values cluster densely
21
+ * in the saturated blue/purple region where sRGB Euclidean distance gives
22
+ * visually wrong answers.
22
23
  * Alpha is ignored. Always returns a code.
23
24
  */
24
25
  export function rgbaToPantone(rgba) {
25
- return nearest(rgba, pantone, 'deltaE2000');
26
+ return nearest(rgba, pantone, pantone.defaultMetric);
26
27
  }
27
28
  //# sourceMappingURL=pantone.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pantone.js","sourceRoot":"","sources":["../../src/conversions/pantone.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEtE,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAElC;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,IAAiB;IAC7C,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC;IACtF,IAAI,SAAS,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,IAAI,EAAE,CAAC,CAAC;IAC9E,MAAM,GAAG,GAAG,OAAO,CAAC,SAAiC,CAAC,CAAC;IACvD,OAAO,SAAS,CAAC,GAAe,CAAC,CAAC;AACpC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,IAAU;IACtC,OAAO,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,YAAY,CAAgB,CAAC;AAC7D,CAAC"}
1
+ {"version":3,"file":"pantone.js","sourceRoot":"","sources":["../../src/conversions/pantone.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAAyB,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAExE,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAErC;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;IACrE,IAAI,SAAS,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,IAAI,EAAE,CAAC,CAAC;IAC9E,MAAM,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,SAAwC,CAAC,CAAC;IACrE,OAAO,SAAS,CAAC,GAAe,CAAC,CAAC;AACpC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,IAAU;IACtC,OAAO,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,aAAa,CAAqB,CAAC;AAC3E,CAAC","sourcesContent":["import { getNameIndex, nearest } from '../indexing.js';\nimport { type PantoneColorName, pantone } from '../palettes/pantone.js';\nimport type { HexColor, Rgba } from '../types.js';\nimport { hexToRgba } from './hex.js';\n\n/**\n * Resolve a Pantone code to Rgba.\n * Accepts any casing, spacing, and common prefixes (\"PMS\", \"Pantone\")\n * since non-alphanumeric characters are stripped before lookup.\n * Throws on unknown codes.\n */\nexport function pantoneToRgba(code: string): Rgba {\n const canonical = getNameIndex(pantone).get(pantone.normalize(code));\n if (canonical === undefined) throw new Error(`Unknown Pantone code: ${code}`);\n const hex = pantone.colors[canonical as keyof typeof pantone.colors];\n return hexToRgba(hex as HexColor);\n}\n\n/**\n * Find the nearest Pantone Coated code to the given Rgba. Uses the\n * `pantone` palette's own `defaultMetric` (ΔE*00 / CIEDE2000) — the\n * industry-standard perceptual metric, since Pantone values cluster densely\n * in the saturated blue/purple region where sRGB Euclidean distance gives\n * visually wrong answers.\n * Alpha is ignored. Always returns a code.\n */\nexport function rgbaToPantone(rgba: Rgba): PantoneColorName {\n return nearest(rgba, pantone, pantone.defaultMetric) as PantoneColorName;\n}\n"]}
@@ -1,4 +1,4 @@
1
- import type { Rgba, RgbaInput, RgbInput, RgbString } from '../types';
1
+ import type { Rgba, RgbaInput, RgbInput, RgbString } from '../types.js';
2
2
  /**
3
3
  * Normalize any RGB/RGBA input shape (string, tuple, or object) into
4
4
  * the canonical Rgba representation. Alpha defaults to 1 when absent.
@@ -1 +1 @@
1
- {"version":3,"file":"rgb.d.ts","sourceRoot":"","sources":["../../src/conversions/rgb.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAa,SAAS,EAAE,MAAM,UAAU,CAAC;AAahF;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,QAAQ,GAAG,SAAS,GAAG,IAAI,CA+B3D;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,CAK/C"}
1
+ {"version":3,"file":"rgb.d.ts","sourceRoot":"","sources":["../../src/conversions/rgb.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAa,SAAS,EAAE,MAAM,aAAa,CAAC;AAanF;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,QAAQ,GAAG,SAAS,GAAG,IAAI,CA+B3D;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,CAK/C","sourcesContent":["import { clamp, requireFinite } from '../math/clamp.js';\nimport type { Rgba, RgbaInput, RgbInput, RgbObject, RgbString } from '../types.js';\n\nconst RGB_RE =\n /^rgba?\\s*\\(\\s*(-?\\d+(?:\\.\\d+)?)\\s*,\\s*(-?\\d+(?:\\.\\d+)?)\\s*,\\s*(-?\\d+(?:\\.\\d+)?)\\s*(?:,\\s*(\\d*\\.?\\d+)\\s*)?\\)$/i;\n\nfunction sanitizeChannel(n: unknown, label: string): number {\n return clamp(requireFinite(n, label), 0, 255);\n}\n\nfunction sanitizeAlpha(n: unknown): number {\n return clamp(requireFinite(n, 'alpha'), 0, 1);\n}\n\n/**\n * Normalize any RGB/RGBA input shape (string, tuple, or object) into\n * the canonical Rgba representation. Alpha defaults to 1 when absent.\n * Rejects non-finite numeric inputs; clamps r/g/b to [0,255] and a to [0,1].\n * Throws on malformed strings.\n */\nexport function rgbToRgba(input: RgbInput | RgbaInput): Rgba {\n if (Array.isArray(input)) {\n const rawA = input.length === 4 ? input[3] : 1;\n return {\n r: sanitizeChannel(input[0], 'r'),\n g: sanitizeChannel(input[1], 'g'),\n b: sanitizeChannel(input[2], 'b'),\n a: sanitizeAlpha(rawA),\n };\n }\n\n if (typeof input === 'string') {\n const match = RGB_RE.exec(input);\n if (!match) throw new Error(`Invalid rgb(a) string: ${input}`);\n return {\n r: sanitizeChannel(Number(match[1]), 'r'),\n g: sanitizeChannel(Number(match[2]), 'g'),\n b: sanitizeChannel(Number(match[3]), 'b'),\n a: sanitizeAlpha(match[4] !== undefined ? Number(match[4]) : 1),\n };\n }\n\n // Object form — TypeScript's Array.isArray narrowing doesn't exclude\n // readonly tuple types from the union, so cast after the guards.\n const obj = input as RgbObject | Rgba;\n return {\n r: sanitizeChannel(obj.r, 'r'),\n g: sanitizeChannel(obj.g, 'g'),\n b: sanitizeChannel(obj.b, 'b'),\n a: sanitizeAlpha('a' in obj ? obj.a : 1),\n };\n}\n\n/**\n * Emit an `rgb(r, g, b)` string. Alpha is dropped; values are\n * already-clamped by rgbToRgba, but we re-clamp defensively for direct\n * Rgba-object callers who bypass the input normalizer.\n */\nexport function rgbaToRgb(rgba: Rgba): RgbString {\n const r = clamp(Math.round(rgba.r), 0, 255);\n const g = clamp(Math.round(rgba.g), 0, 255);\n const b = clamp(Math.round(rgba.b), 0, 255);\n return `rgb(${r}, ${g}, ${b})` as RgbString;\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { clamp, requireFinite } from '../math/clamp';
1
+ import { clamp, requireFinite } from '../math/clamp.js';
2
2
  const RGB_RE = /^rgba?\s*\(\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*(?:,\s*(\d*\.?\d+)\s*)?\)$/i;
3
3
  function sanitizeChannel(n, label) {
4
4
  return clamp(requireFinite(n, label), 0, 255);
@@ -1 +1 @@
1
- {"version":3,"file":"rgb.js","sourceRoot":"","sources":["../../src/conversions/rgb.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rgb.js","sourceRoot":"","sources":["../../src/conversions/rgb.ts"],"names":[],"mappings":"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","sourcesContent":["import { clamp, requireFinite } from '../math/clamp.js';\nimport type { Rgba, RgbaInput, RgbInput, RgbObject, RgbString } from '../types.js';\n\nconst RGB_RE =\n /^rgba?\\s*\\(\\s*(-?\\d+(?:\\.\\d+)?)\\s*,\\s*(-?\\d+(?:\\.\\d+)?)\\s*,\\s*(-?\\d+(?:\\.\\d+)?)\\s*(?:,\\s*(\\d*\\.?\\d+)\\s*)?\\)$/i;\n\nfunction sanitizeChannel(n: unknown, label: string): number {\n return clamp(requireFinite(n, label), 0, 255);\n}\n\nfunction sanitizeAlpha(n: unknown): number {\n return clamp(requireFinite(n, 'alpha'), 0, 1);\n}\n\n/**\n * Normalize any RGB/RGBA input shape (string, tuple, or object) into\n * the canonical Rgba representation. Alpha defaults to 1 when absent.\n * Rejects non-finite numeric inputs; clamps r/g/b to [0,255] and a to [0,1].\n * Throws on malformed strings.\n */\nexport function rgbToRgba(input: RgbInput | RgbaInput): Rgba {\n if (Array.isArray(input)) {\n const rawA = input.length === 4 ? input[3] : 1;\n return {\n r: sanitizeChannel(input[0], 'r'),\n g: sanitizeChannel(input[1], 'g'),\n b: sanitizeChannel(input[2], 'b'),\n a: sanitizeAlpha(rawA),\n };\n }\n\n if (typeof input === 'string') {\n const match = RGB_RE.exec(input);\n if (!match) throw new Error(`Invalid rgb(a) string: ${input}`);\n return {\n r: sanitizeChannel(Number(match[1]), 'r'),\n g: sanitizeChannel(Number(match[2]), 'g'),\n b: sanitizeChannel(Number(match[3]), 'b'),\n a: sanitizeAlpha(match[4] !== undefined ? Number(match[4]) : 1),\n };\n }\n\n // Object form — TypeScript's Array.isArray narrowing doesn't exclude\n // readonly tuple types from the union, so cast after the guards.\n const obj = input as RgbObject | Rgba;\n return {\n r: sanitizeChannel(obj.r, 'r'),\n g: sanitizeChannel(obj.g, 'g'),\n b: sanitizeChannel(obj.b, 'b'),\n a: sanitizeAlpha('a' in obj ? obj.a : 1),\n };\n}\n\n/**\n * Emit an `rgb(r, g, b)` string. Alpha is dropped; values are\n * already-clamped by rgbToRgba, but we re-clamp defensively for direct\n * Rgba-object callers who bypass the input normalizer.\n */\nexport function rgbaToRgb(rgba: Rgba): RgbString {\n const r = clamp(Math.round(rgba.r), 0, 255);\n const g = clamp(Math.round(rgba.g), 0, 255);\n const b = clamp(Math.round(rgba.b), 0, 255);\n return `rgb(${r}, ${g}, ${b})` as RgbString;\n}\n"]}
package/dist/convert.d.ts CHANGED
@@ -1,21 +1,61 @@
1
- import { type DetectedFormat } from './detectFormat';
2
- import type { ColorFormat, ColorInput, ColorValue, Rgba } from './types';
1
+ import { type DetectedFormat } from './detectFormat.js';
2
+ import type { ColorFormat, ColorInput, ColorValue, Palette, Rgba } from './types.js';
3
3
  /**
4
4
  * Normalize any recognized color input to canonical `Rgba`.
5
5
  * Pass `knownFormat` when the caller has already run `detectFormat` to skip
6
6
  * the redundant detection. Throws on `UNKNOWN` input.
7
+ *
8
+ * Palette names (Pantone codes, BYO keys) are not accepted here — they
9
+ * require palette data to parse. Use `convert(name, { palette })` or
10
+ * `resolve(name, { palette })` at the higher level.
7
11
  */
8
12
  export declare function toRgba(input: ColorInput, knownFormat?: DetectedFormat): Rgba;
9
13
  /**
10
- * Emit a canonical `Rgba` in the target format. Colorspace-independent.
14
+ * Emit a canonical `Rgba` in the target structural format. Palette-independent.
15
+ *
16
+ * For palette-key output (e.g. nearest Pantone code, brand name), use
17
+ * `convert(rgba, { format: 'NAME', palette })` — or `identify` for fuzzy,
18
+ * `rgbaToPantone` for the low-level Pantone-only path.
11
19
  */
12
20
  export declare function fromRgba(rgba: Rgba, format?: ColorFormat): ColorValue;
21
+ /**
22
+ * Extract string keys from a Palette's `colors` map.
23
+ * Matches the `PaletteKey<P>` helper in `identify.ts` — kept local so
24
+ * `convert` doesn't depend on `identify`.
25
+ */
26
+ type PaletteKey<P extends Palette> = Extract<keyof P['colors'], string>;
13
27
  /**
14
28
  * Detect the input color format, normalize to Rgba, and emit the result
15
- * in the requested output format. Colorspace-independent.
16
- * Throws if the input isn't a recognized color shape.
29
+ * in the requested output format.
30
+ *
31
+ * Without a `palette` option, `convert` is strictly structural —
32
+ * HEX ↔ RGB ↔ RGBA ↔ HSL ↔ HSV, no palette data ever touched, tree-shakes
33
+ * to the bone.
34
+ *
35
+ * With a `palette` option, `convert` understands the palette's naming
36
+ * scheme both ways:
37
+ * - Input can be a palette name (`'185 C'`, `'Acme Red'`) — parsed via
38
+ * `palette.normalize` and looked up in `palette.colors`.
39
+ * - Output can be `format: 'NAME'` — the exact canonical key from
40
+ * `palette.colors`, throws if the color isn't a pixel-exact match (use
41
+ * `identify` for nearest-match).
42
+ *
43
+ * The palette data comes from the caller's own import, so tree-shaking
44
+ * is preserved: callers who don't pass `palette` never pull a palette.
45
+ *
46
+ * Throws on unrecognized input (parser-flavored — `identify`/`resolve`
47
+ * are the `null`-returning alternatives).
17
48
  */
18
49
  export declare function convert(input: ColorInput, opts?: {
19
50
  format?: ColorFormat;
20
51
  }): ColorValue;
52
+ export declare function convert<P extends Palette>(input: ColorInput | string, opts: {
53
+ palette: P;
54
+ format: 'NAME';
55
+ }): PaletteKey<P>;
56
+ export declare function convert<P extends Palette>(input: ColorInput | string, opts: {
57
+ palette: P;
58
+ format?: ColorFormat;
59
+ }): ColorValue;
60
+ export {};
21
61
  //# sourceMappingURL=convert.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["../src/convert.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,cAAc,EAAgB,MAAM,gBAAgB,CAAC;AACnE,OAAO,KAAK,EACV,WAAW,EACX,UAAU,EACV,UAAU,EAKV,IAAI,EAGL,MAAM,SAAS,CAAC;AAYjB;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,UAAU,EAAE,WAAW,CAAC,EAAE,cAAc,GAAG,IAAI,CAkB5E;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,GAAE,WAAmB,GAAG,UAAU,CAe5E;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,WAAW,CAAA;CAAO,GAAG,UAAU,CAE1F"}
1
+ {"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["../src/convert.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,cAAc,EAAgB,MAAM,mBAAmB,CAAC;AAEtE,OAAO,KAAK,EACV,WAAW,EACX,UAAU,EACV,UAAU,EAIV,OAAO,EACP,IAAI,EAGL,MAAM,YAAY,CAAC;AAYpB;;;;;;;;GAQG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,UAAU,EAAE,WAAW,CAAC,EAAE,cAAc,GAAG,IAAI,CAgB5E;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,GAAE,WAAmB,GAAG,UAAU,CAqB5E;AAED;;;;GAIG;AACH,KAAK,UAAU,CAAC,CAAC,SAAS,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAC;AAExE;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,wBAAgB,OAAO,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,GAAG,UAAU,CAAC;AAGxF,wBAAgB,OAAO,CAAC,CAAC,SAAS,OAAO,EACvC,KAAK,EAAE,UAAU,GAAG,MAAM,EAC1B,IAAI,EAAE;IAAE,OAAO,EAAE,CAAC,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACnC,UAAU,CAAC,CAAC,CAAC,CAAC;AAGjB,wBAAgB,OAAO,CAAC,CAAC,SAAS,OAAO,EACvC,KAAK,EAAE,UAAU,GAAG,MAAM,EAC1B,IAAI,EAAE;IAAE,OAAO,EAAE,CAAC,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,GACzC,UAAU,CAAC","sourcesContent":["import { hexToRgba, rgbaToHex } from './conversions/hex.js';\nimport { hslToRgba, rgbaToHsl } from './conversions/hsl.js';\nimport { hsvToRgba, rgbaToHsv } from './conversions/hsv.js';\nimport { rgbaToRgb, rgbToRgba } from './conversions/rgb.js';\nimport { type DetectedFormat, detectFormat } from './detectFormat.js';\nimport { getNameIndex, getReverseNameIndex } from './indexing.js';\nimport type {\n ColorFormat,\n ColorInput,\n ColorValue,\n HexColor,\n HslInput,\n HsvInput,\n Palette,\n Rgba,\n RgbaInput,\n RgbInput,\n} from './types.js';\n\nfunction safeStringify(x: unknown): string {\n if (x === undefined) return 'undefined';\n try {\n const s = JSON.stringify(x);\n return s === undefined ? String(x) : s;\n } catch {\n return Object.prototype.toString.call(x);\n }\n}\n\n/**\n * Normalize any recognized color input to canonical `Rgba`.\n * Pass `knownFormat` when the caller has already run `detectFormat` to skip\n * the redundant detection. Throws on `UNKNOWN` input.\n *\n * Palette names (Pantone codes, BYO keys) are not accepted here — they\n * require palette data to parse. Use `convert(name, { palette })` or\n * `resolve(name, { palette })` at the higher level.\n */\nexport function toRgba(input: ColorInput, knownFormat?: DetectedFormat): Rgba {\n const format = knownFormat ?? detectFormat(input);\n switch (format) {\n case 'HEX':\n return hexToRgba(input as HexColor);\n case 'RGB':\n return rgbToRgba(input as RgbInput);\n case 'RGBA':\n return rgbToRgba(input as RgbaInput);\n case 'HSL':\n return hslToRgba(input as HslInput);\n case 'HSV':\n return hsvToRgba(input as HsvInput);\n default:\n throw new Error(`Unrecognized color input: ${safeStringify(input)}`);\n }\n}\n\n/**\n * Emit a canonical `Rgba` in the target structural format. Palette-independent.\n *\n * For palette-key output (e.g. nearest Pantone code, brand name), use\n * `convert(rgba, { format: 'NAME', palette })` — or `identify` for fuzzy,\n * `rgbaToPantone` for the low-level Pantone-only path.\n */\nexport function fromRgba(rgba: Rgba, format: ColorFormat = 'HEX'): ColorValue {\n switch (format) {\n case 'HEX':\n return rgbaToHex(rgba);\n case 'RGB':\n return rgbaToRgb(rgba);\n case 'RGBA':\n return rgba;\n case 'HSL':\n return rgbaToHsl(rgba);\n case 'HSV':\n return rgbaToHsv(rgba);\n default: {\n // Runtime safety for JS callers bypassing the ColorFormat union.\n const hint =\n format === ('PANTONE' as unknown)\n ? ` — use convert(rgba, { palette: pantone, format: 'NAME' }) or rgbaToPantone from 'chromonym/conversions/pantone'`\n : '';\n throw new Error(`Unsupported format: ${safeStringify(format)}${hint}`);\n }\n }\n}\n\n/**\n * Extract string keys from a Palette's `colors` map.\n * Matches the `PaletteKey<P>` helper in `identify.ts` — kept local so\n * `convert` doesn't depend on `identify`.\n */\ntype PaletteKey<P extends Palette> = Extract<keyof P['colors'], string>;\n\n/**\n * Detect the input color format, normalize to Rgba, and emit the result\n * in the requested output format.\n *\n * Without a `palette` option, `convert` is strictly structural —\n * HEX ↔ RGB ↔ RGBA ↔ HSL ↔ HSV, no palette data ever touched, tree-shakes\n * to the bone.\n *\n * With a `palette` option, `convert` understands the palette's naming\n * scheme both ways:\n * - Input can be a palette name (`'185 C'`, `'Acme Red'`) — parsed via\n * `palette.normalize` and looked up in `palette.colors`.\n * - Output can be `format: 'NAME'` — the exact canonical key from\n * `palette.colors`, throws if the color isn't a pixel-exact match (use\n * `identify` for nearest-match).\n *\n * The palette data comes from the caller's own import, so tree-shaking\n * is preserved: callers who don't pass `palette` never pull a palette.\n *\n * Throws on unrecognized input (parser-flavored — `identify`/`resolve`\n * are the `null`-returning alternatives).\n */\n\n// Overload 1: no palette — structural only. Input is strictly ColorInput.\nexport function convert(input: ColorInput, opts?: { format?: ColorFormat }): ColorValue;\n\n// Overload 2: palette present, format 'NAME' — returns a palette key.\nexport function convert<P extends Palette>(\n input: ColorInput | string,\n opts: { palette: P; format: 'NAME' },\n): PaletteKey<P>;\n\n// Overload 3: palette present, structural format — returns ColorValue.\nexport function convert<P extends Palette>(\n input: ColorInput | string,\n opts: { palette: P; format?: ColorFormat },\n): ColorValue;\n\n// Implementation\nexport function convert(\n input: ColorInput | string,\n opts: { format?: ColorFormat | 'NAME'; palette?: Palette } = {},\n): ColorValue | string {\n const { format = 'HEX', palette } = opts;\n\n // --- Parse input → Rgba ---\n // Structural detection first; palette lookup only as a fallback. This\n // way `convert('#ff0000', { palette: brand })` always means \"parse hex,\"\n // even if the brand palette happens to have a key that normalizes to\n // \"ff0000\".\n const detected = detectFormat(input as ColorInput);\n let rgba: Rgba;\n if (detected !== 'UNKNOWN') {\n rgba = toRgba(input as ColorInput, detected);\n } else if (palette !== undefined && typeof input === 'string') {\n const canonical = getNameIndex(palette).get(palette.normalize(input));\n if (canonical === undefined) {\n throw new Error(\n `Unrecognized color input: ${safeStringify(input)} (not structural, and not found in palette '${palette.name}')`,\n );\n }\n const hex = (palette.colors as Record<string, HexColor>)[canonical];\n if (hex === undefined) {\n throw new Error(`Unrecognized color input: ${safeStringify(input)}`);\n }\n rgba = hexToRgba(hex);\n } else {\n throw new Error(`Unrecognized color input: ${safeStringify(input)}`);\n }\n\n // --- Emit ---\n if (format === 'NAME') {\n if (palette === undefined) {\n throw new Error(`convert(_, { format: 'NAME' }) requires a 'palette' option`);\n }\n // Palette colors are alpha = 1 by construction; a partially-transparent\n // input isn't \"that named color\" in the strict-convert sense. Reject\n // here rather than silently drop alpha during the reverse lookup.\n if (rgba.a !== 1) {\n throw new Error(\n `format: 'NAME' requires fully-opaque input (a === 1). Got a=${rgba.a}. ` +\n `Strip alpha first or use identify() for fuzzy matching.`,\n );\n }\n const hex = rgbaToHex(rgba).toLowerCase();\n const name = getReverseNameIndex(palette).get(hex);\n if (name === undefined) {\n throw new Error(\n `No exact match for ${hex} in palette '${palette.name}'. Use identify() for nearest-match semantics.`,\n );\n }\n return name;\n }\n return fromRgba(rgba, format);\n}\n"]}
package/dist/convert.js CHANGED
@@ -1,9 +1,9 @@
1
- import { hexToRgba, rgbaToHex } from './conversions/hex';
2
- import { hslToRgba, rgbaToHsl } from './conversions/hsl';
3
- import { hsvToRgba, rgbaToHsv } from './conversions/hsv';
4
- import { pantoneToRgba, rgbaToPantone } from './conversions/pantone';
5
- import { rgbaToRgb, rgbToRgba } from './conversions/rgb';
6
- import { detectFormat } from './detectFormat';
1
+ import { hexToRgba, rgbaToHex } from './conversions/hex.js';
2
+ import { hslToRgba, rgbaToHsl } from './conversions/hsl.js';
3
+ import { hsvToRgba, rgbaToHsv } from './conversions/hsv.js';
4
+ import { rgbaToRgb, rgbToRgba } from './conversions/rgb.js';
5
+ import { detectFormat } from './detectFormat.js';
6
+ import { getNameIndex, getReverseNameIndex } from './indexing.js';
7
7
  function safeStringify(x) {
8
8
  if (x === undefined)
9
9
  return 'undefined';
@@ -19,6 +19,10 @@ function safeStringify(x) {
19
19
  * Normalize any recognized color input to canonical `Rgba`.
20
20
  * Pass `knownFormat` when the caller has already run `detectFormat` to skip
21
21
  * the redundant detection. Throws on `UNKNOWN` input.
22
+ *
23
+ * Palette names (Pantone codes, BYO keys) are not accepted here — they
24
+ * require palette data to parse. Use `convert(name, { palette })` or
25
+ * `resolve(name, { palette })` at the higher level.
22
26
  */
23
27
  export function toRgba(input, knownFormat) {
24
28
  const format = knownFormat ?? detectFormat(input);
@@ -33,14 +37,16 @@ export function toRgba(input, knownFormat) {
33
37
  return hslToRgba(input);
34
38
  case 'HSV':
35
39
  return hsvToRgba(input);
36
- case 'PANTONE':
37
- return pantoneToRgba(input);
38
40
  default:
39
41
  throw new Error(`Unrecognized color input: ${safeStringify(input)}`);
40
42
  }
41
43
  }
42
44
  /**
43
- * Emit a canonical `Rgba` in the target format. Colorspace-independent.
45
+ * Emit a canonical `Rgba` in the target structural format. Palette-independent.
46
+ *
47
+ * For palette-key output (e.g. nearest Pantone code, brand name), use
48
+ * `convert(rgba, { format: 'NAME', palette })` — or `identify` for fuzzy,
49
+ * `rgbaToPantone` for the low-level Pantone-only path.
44
50
  */
45
51
  export function fromRgba(rgba, format = 'HEX') {
46
52
  switch (format) {
@@ -54,16 +60,61 @@ export function fromRgba(rgba, format = 'HEX') {
54
60
  return rgbaToHsl(rgba);
55
61
  case 'HSV':
56
62
  return rgbaToHsv(rgba);
57
- case 'PANTONE':
58
- return rgbaToPantone(rgba);
63
+ default: {
64
+ // Runtime safety for JS callers bypassing the ColorFormat union.
65
+ const hint = format === 'PANTONE'
66
+ ? ` — use convert(rgba, { palette: pantone, format: 'NAME' }) or rgbaToPantone from 'chromonym/conversions/pantone'`
67
+ : '';
68
+ throw new Error(`Unsupported format: ${safeStringify(format)}${hint}`);
69
+ }
59
70
  }
60
71
  }
61
- /**
62
- * Detect the input color format, normalize to Rgba, and emit the result
63
- * in the requested output format. Colorspace-independent.
64
- * Throws if the input isn't a recognized color shape.
65
- */
72
+ // Implementation
66
73
  export function convert(input, opts = {}) {
67
- return fromRgba(toRgba(input), opts.format ?? 'HEX');
74
+ const { format = 'HEX', palette } = opts;
75
+ // --- Parse input → Rgba ---
76
+ // Structural detection first; palette lookup only as a fallback. This
77
+ // way `convert('#ff0000', { palette: brand })` always means "parse hex,"
78
+ // even if the brand palette happens to have a key that normalizes to
79
+ // "ff0000".
80
+ const detected = detectFormat(input);
81
+ let rgba;
82
+ if (detected !== 'UNKNOWN') {
83
+ rgba = toRgba(input, detected);
84
+ }
85
+ else if (palette !== undefined && typeof input === 'string') {
86
+ const canonical = getNameIndex(palette).get(palette.normalize(input));
87
+ if (canonical === undefined) {
88
+ throw new Error(`Unrecognized color input: ${safeStringify(input)} (not structural, and not found in palette '${palette.name}')`);
89
+ }
90
+ const hex = palette.colors[canonical];
91
+ if (hex === undefined) {
92
+ throw new Error(`Unrecognized color input: ${safeStringify(input)}`);
93
+ }
94
+ rgba = hexToRgba(hex);
95
+ }
96
+ else {
97
+ throw new Error(`Unrecognized color input: ${safeStringify(input)}`);
98
+ }
99
+ // --- Emit ---
100
+ if (format === 'NAME') {
101
+ if (palette === undefined) {
102
+ throw new Error(`convert(_, { format: 'NAME' }) requires a 'palette' option`);
103
+ }
104
+ // Palette colors are alpha = 1 by construction; a partially-transparent
105
+ // input isn't "that named color" in the strict-convert sense. Reject
106
+ // here rather than silently drop alpha during the reverse lookup.
107
+ if (rgba.a !== 1) {
108
+ throw new Error(`format: 'NAME' requires fully-opaque input (a === 1). Got a=${rgba.a}. ` +
109
+ `Strip alpha first or use identify() for fuzzy matching.`);
110
+ }
111
+ const hex = rgbaToHex(rgba).toLowerCase();
112
+ const name = getReverseNameIndex(palette).get(hex);
113
+ if (name === undefined) {
114
+ throw new Error(`No exact match for ${hex} in palette '${palette.name}'. Use identify() for nearest-match semantics.`);
115
+ }
116
+ return name;
117
+ }
118
+ return fromRgba(rgba, format);
68
119
  }
69
120
  //# sourceMappingURL=convert.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"convert.js","sourceRoot":"","sources":["../src/convert.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAuB,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAcnE,SAAS,aAAa,CAAC,CAAU;IAC/B,IAAI,CAAC,KAAK,SAAS;QAAE,OAAO,WAAW,CAAC;IACxC,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QAC5B,OAAO,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC3C,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,MAAM,CAAC,KAAiB,EAAE,WAA4B;IACpE,MAAM,MAAM,GAAG,WAAW,IAAI,YAAY,CAAC,KAAK,CAAC,CAAC;IAClD,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,KAAK;YACR,OAAO,SAAS,CAAC,KAAiB,CAAC,CAAC;QACtC,KAAK,KAAK;YACR,OAAO,SAAS,CAAC,KAAiB,CAAC,CAAC;QACtC,KAAK,MAAM;YACT,OAAO,SAAS,CAAC,KAAkB,CAAC,CAAC;QACvC,KAAK,KAAK;YACR,OAAO,SAAS,CAAC,KAAiB,CAAC,CAAC;QACtC,KAAK,KAAK;YACR,OAAO,SAAS,CAAC,KAAiB,CAAC,CAAC;QACtC,KAAK,SAAS;YACZ,OAAO,aAAa,CAAC,KAAoB,CAAC,CAAC;QAC7C;YACE,MAAM,IAAI,KAAK,CAAC,6BAA6B,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACzE,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,QAAQ,CAAC,IAAU,EAAE,MAAM,GAAgB,KAAK;IAC9D,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,KAAK;YACR,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC;QACzB,KAAK,KAAK;YACR,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC;QACzB,KAAK,MAAM;YACT,OAAO,IAAI,CAAC;QACd,KAAK,KAAK;YACR,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC;QACzB,KAAK,KAAK;YACR,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC;QACzB,KAAK,SAAS;YACZ,OAAO,aAAa,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,OAAO,CAAC,KAAiB,EAAE,IAAI,GAA6B,EAAE;IAC5E,OAAO,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC;AACvD,CAAC"}
1
+ {"version":3,"file":"convert.js","sourceRoot":"","sources":["../src/convert.ts"],"names":[],"mappings":"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","sourcesContent":["import { hexToRgba, rgbaToHex } from './conversions/hex.js';\nimport { hslToRgba, rgbaToHsl } from './conversions/hsl.js';\nimport { hsvToRgba, rgbaToHsv } from './conversions/hsv.js';\nimport { rgbaToRgb, rgbToRgba } from './conversions/rgb.js';\nimport { type DetectedFormat, detectFormat } from './detectFormat.js';\nimport { getNameIndex, getReverseNameIndex } from './indexing.js';\nimport type {\n ColorFormat,\n ColorInput,\n ColorValue,\n HexColor,\n HslInput,\n HsvInput,\n Palette,\n Rgba,\n RgbaInput,\n RgbInput,\n} from './types.js';\n\nfunction safeStringify(x: unknown): string {\n if (x === undefined) return 'undefined';\n try {\n const s = JSON.stringify(x);\n return s === undefined ? String(x) : s;\n } catch {\n return Object.prototype.toString.call(x);\n }\n}\n\n/**\n * Normalize any recognized color input to canonical `Rgba`.\n * Pass `knownFormat` when the caller has already run `detectFormat` to skip\n * the redundant detection. Throws on `UNKNOWN` input.\n *\n * Palette names (Pantone codes, BYO keys) are not accepted here — they\n * require palette data to parse. Use `convert(name, { palette })` or\n * `resolve(name, { palette })` at the higher level.\n */\nexport function toRgba(input: ColorInput, knownFormat?: DetectedFormat): Rgba {\n const format = knownFormat ?? detectFormat(input);\n switch (format) {\n case 'HEX':\n return hexToRgba(input as HexColor);\n case 'RGB':\n return rgbToRgba(input as RgbInput);\n case 'RGBA':\n return rgbToRgba(input as RgbaInput);\n case 'HSL':\n return hslToRgba(input as HslInput);\n case 'HSV':\n return hsvToRgba(input as HsvInput);\n default:\n throw new Error(`Unrecognized color input: ${safeStringify(input)}`);\n }\n}\n\n/**\n * Emit a canonical `Rgba` in the target structural format. Palette-independent.\n *\n * For palette-key output (e.g. nearest Pantone code, brand name), use\n * `convert(rgba, { format: 'NAME', palette })` — or `identify` for fuzzy,\n * `rgbaToPantone` for the low-level Pantone-only path.\n */\nexport function fromRgba(rgba: Rgba, format: ColorFormat = 'HEX'): ColorValue {\n switch (format) {\n case 'HEX':\n return rgbaToHex(rgba);\n case 'RGB':\n return rgbaToRgb(rgba);\n case 'RGBA':\n return rgba;\n case 'HSL':\n return rgbaToHsl(rgba);\n case 'HSV':\n return rgbaToHsv(rgba);\n default: {\n // Runtime safety for JS callers bypassing the ColorFormat union.\n const hint =\n format === ('PANTONE' as unknown)\n ? ` — use convert(rgba, { palette: pantone, format: 'NAME' }) or rgbaToPantone from 'chromonym/conversions/pantone'`\n : '';\n throw new Error(`Unsupported format: ${safeStringify(format)}${hint}`);\n }\n }\n}\n\n/**\n * Extract string keys from a Palette's `colors` map.\n * Matches the `PaletteKey<P>` helper in `identify.ts` — kept local so\n * `convert` doesn't depend on `identify`.\n */\ntype PaletteKey<P extends Palette> = Extract<keyof P['colors'], string>;\n\n/**\n * Detect the input color format, normalize to Rgba, and emit the result\n * in the requested output format.\n *\n * Without a `palette` option, `convert` is strictly structural —\n * HEX ↔ RGB ↔ RGBA ↔ HSL ↔ HSV, no palette data ever touched, tree-shakes\n * to the bone.\n *\n * With a `palette` option, `convert` understands the palette's naming\n * scheme both ways:\n * - Input can be a palette name (`'185 C'`, `'Acme Red'`) — parsed via\n * `palette.normalize` and looked up in `palette.colors`.\n * - Output can be `format: 'NAME'` — the exact canonical key from\n * `palette.colors`, throws if the color isn't a pixel-exact match (use\n * `identify` for nearest-match).\n *\n * The palette data comes from the caller's own import, so tree-shaking\n * is preserved: callers who don't pass `palette` never pull a palette.\n *\n * Throws on unrecognized input (parser-flavored — `identify`/`resolve`\n * are the `null`-returning alternatives).\n */\n\n// Overload 1: no palette — structural only. Input is strictly ColorInput.\nexport function convert(input: ColorInput, opts?: { format?: ColorFormat }): ColorValue;\n\n// Overload 2: palette present, format 'NAME' — returns a palette key.\nexport function convert<P extends Palette>(\n input: ColorInput | string,\n opts: { palette: P; format: 'NAME' },\n): PaletteKey<P>;\n\n// Overload 3: palette present, structural format — returns ColorValue.\nexport function convert<P extends Palette>(\n input: ColorInput | string,\n opts: { palette: P; format?: ColorFormat },\n): ColorValue;\n\n// Implementation\nexport function convert(\n input: ColorInput | string,\n opts: { format?: ColorFormat | 'NAME'; palette?: Palette } = {},\n): ColorValue | string {\n const { format = 'HEX', palette } = opts;\n\n // --- Parse input → Rgba ---\n // Structural detection first; palette lookup only as a fallback. This\n // way `convert('#ff0000', { palette: brand })` always means \"parse hex,\"\n // even if the brand palette happens to have a key that normalizes to\n // \"ff0000\".\n const detected = detectFormat(input as ColorInput);\n let rgba: Rgba;\n if (detected !== 'UNKNOWN') {\n rgba = toRgba(input as ColorInput, detected);\n } else if (palette !== undefined && typeof input === 'string') {\n const canonical = getNameIndex(palette).get(palette.normalize(input));\n if (canonical === undefined) {\n throw new Error(\n `Unrecognized color input: ${safeStringify(input)} (not structural, and not found in palette '${palette.name}')`,\n );\n }\n const hex = (palette.colors as Record<string, HexColor>)[canonical];\n if (hex === undefined) {\n throw new Error(`Unrecognized color input: ${safeStringify(input)}`);\n }\n rgba = hexToRgba(hex);\n } else {\n throw new Error(`Unrecognized color input: ${safeStringify(input)}`);\n }\n\n // --- Emit ---\n if (format === 'NAME') {\n if (palette === undefined) {\n throw new Error(`convert(_, { format: 'NAME' }) requires a 'palette' option`);\n }\n // Palette colors are alpha = 1 by construction; a partially-transparent\n // input isn't \"that named color\" in the strict-convert sense. Reject\n // here rather than silently drop alpha during the reverse lookup.\n if (rgba.a !== 1) {\n throw new Error(\n `format: 'NAME' requires fully-opaque input (a === 1). Got a=${rgba.a}. ` +\n `Strip alpha first or use identify() for fuzzy matching.`,\n );\n }\n const hex = rgbaToHex(rgba).toLowerCase();\n const name = getReverseNameIndex(palette).get(hex);\n if (name === undefined) {\n throw new Error(\n `No exact match for ${hex} in palette '${palette.name}'. Use identify() for nearest-match semantics.`,\n );\n }\n return name;\n }\n return fromRgba(rgba, format);\n}\n"]}
@@ -1,4 +1,4 @@
1
- import type { ColorFormat, ColorInput } from './types';
1
+ import type { ColorFormat, ColorInput } from './types.js';
2
2
  /**
3
3
  * A color comes in, a format key comes out.
4
4
  * 'UNKNOWN' signals the caller that the input isn't a recognized color shape.
@@ -1 +1 @@
1
- {"version":3,"file":"detectFormat.d.ts","sourceRoot":"","sources":["../src/detectFormat.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAEvD;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,SAAS,CAAC;AAErD,kGAAkG;AAClG,eAAO,MAAM,MAAM,QAA8C,CAAC;AASlE;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,CAE3D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,cAAc,CA8B9D"}
1
+ {"version":3,"file":"detectFormat.d.ts","sourceRoot":"","sources":["../src/detectFormat.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE1D;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,SAAS,CAAC;AAErD,kGAAkG;AAClG,eAAO,MAAM,MAAM,QAA8C,CAAC;AAMlE;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,CAE3D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,cAAc,CA8B9D","sourcesContent":["import type { ColorFormat, ColorInput } from './types.js';\n\n/**\n * A color comes in, a format key comes out.\n * 'UNKNOWN' signals the caller that the input isn't a recognized color shape.\n * Used as the dispatch key for format-specific converters.\n */\nexport type DetectedFormat = ColorFormat | 'UNKNOWN';\n\n/** Shared with `src/conversions/hex.ts`. Capture group is used there; .test() ignores it here. */\nexport const HEX_RE = /^#([0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i;\nconst RGBA_STR_RE = /^rgba\\s*\\(/i;\nconst RGB_STR_RE = /^rgb\\s*\\(/i;\nconst HSL_STR_RE = /^hsl\\s*\\(/i;\nconst HSV_STR_RE = /^hsv\\s*\\(/i;\n\n/**\n * Runtime type-guard — returns `true` if `input` looks like something any of\n * chromonym's converters can parse. Cheaper than catching a `convert` throw\n * and more ergonomic than hand-rolling `detectFormat(x) !== 'UNKNOWN'`.\n */\nexport function isColor(input: unknown): input is ColorInput {\n return detectFormat(input as ColorInput) !== 'UNKNOWN';\n}\n\nexport function detectFormat(input: ColorInput): DetectedFormat {\n if (typeof input === 'string') {\n // Runtime guard against JS callers that bypass the string template union.\n if ((input as string).length === 0) return 'UNKNOWN';\n if (HEX_RE.test(input)) return 'HEX';\n if (RGBA_STR_RE.test(input)) return 'RGBA';\n if (RGB_STR_RE.test(input)) return 'RGB';\n if (HSL_STR_RE.test(input)) return 'HSL';\n if (HSV_STR_RE.test(input)) return 'HSV';\n return 'UNKNOWN';\n }\n\n if (Array.isArray(input)) {\n if (input.length === 4) return 'RGBA';\n if (input.length === 3) return 'RGB';\n return 'UNKNOWN';\n }\n\n if (input !== null && typeof input === 'object') {\n // Use Object.hasOwn — `in` walks the prototype chain and can be\n // abused by attacker-controlled prototypes to misclassify inputs.\n const has = Object.hasOwn;\n if (has(input, 'h') && has(input, 'v')) return 'HSV';\n if (has(input, 'h') && has(input, 'l')) return 'HSL';\n if (has(input, 'r') && has(input, 'a')) return 'RGBA';\n if (has(input, 'r')) return 'RGB';\n return 'UNKNOWN';\n }\n\n return 'UNKNOWN';\n}\n"]}
@@ -4,9 +4,6 @@ const RGBA_STR_RE = /^rgba\s*\(/i;
4
4
  const RGB_STR_RE = /^rgb\s*\(/i;
5
5
  const HSL_STR_RE = /^hsl\s*\(/i;
6
6
  const HSV_STR_RE = /^hsv\s*\(/i;
7
- // Conservative: matches "<digits> C|U|M" with optional "Pantone" prefix.
8
- // Refine once the real Pantone code list lands.
9
- const PANTONE_RE = /^(?:pantone\s+)?\d+\s*[CUM]$/i;
10
7
  /**
11
8
  * Runtime type-guard — returns `true` if `input` looks like something any of
12
9
  * chromonym's converters can parse. Cheaper than catching a `convert` throw
@@ -17,7 +14,8 @@ export function isColor(input) {
17
14
  }
18
15
  export function detectFormat(input) {
19
16
  if (typeof input === 'string') {
20
- if (input === '')
17
+ // Runtime guard against JS callers that bypass the string template union.
18
+ if (input.length === 0)
21
19
  return 'UNKNOWN';
22
20
  if (HEX_RE.test(input))
23
21
  return 'HEX';
@@ -29,8 +27,6 @@ export function detectFormat(input) {
29
27
  return 'HSL';
30
28
  if (HSV_STR_RE.test(input))
31
29
  return 'HSV';
32
- if (PANTONE_RE.test(input))
33
- return 'PANTONE';
34
30
  return 'UNKNOWN';
35
31
  }
36
32
  if (Array.isArray(input)) {
@@ -1 +1 @@
1
- {"version":3,"file":"detectFormat.js","sourceRoot":"","sources":["../src/detectFormat.ts"],"names":[],"mappings":"AASA,kGAAkG;AAClG,MAAM,CAAC,MAAM,MAAM,GAAG,2CAA2C,CAAC;AAClE,MAAM,WAAW,GAAG,aAAa,CAAC;AAClC,MAAM,UAAU,GAAG,YAAY,CAAC;AAChC,MAAM,UAAU,GAAG,YAAY,CAAC;AAChC,MAAM,UAAU,GAAG,YAAY,CAAC;AAChC,yEAAyE;AACzE,gDAAgD;AAChD,MAAM,UAAU,GAAG,+BAA+B,CAAC;AAEnD;;;;GAIG;AACH,MAAM,UAAU,OAAO,CAAC,KAAc;IACpC,OAAO,YAAY,CAAC,KAAmB,CAAC,KAAK,SAAS,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAiB;IAC5C,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,IAAI,KAAK,KAAK,EAAE;YAAE,OAAO,SAAS,CAAC;QACnC,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QACrC,IAAI,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO,MAAM,CAAC;QAC3C,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QACzC,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QACzC,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QACzC,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO,SAAS,CAAC;QAC7C,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,MAAM,CAAC;QACtC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QACrC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,gEAAgE;QAChE,kEAAkE;QAClE,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC;QAC1B,IAAI,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;QACrD,IAAI,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;QACrD,IAAI,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC;YAAE,OAAO,MAAM,CAAC;QACtD,IAAI,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;QAClC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC"}
1
+ {"version":3,"file":"detectFormat.js","sourceRoot":"","sources":["../src/detectFormat.ts"],"names":[],"mappings":"AASA,kGAAkG;AAClG,MAAM,CAAC,MAAM,MAAM,GAAG,2CAA2C,CAAC;AAClE,MAAM,WAAW,GAAG,aAAa,CAAC;AAClC,MAAM,UAAU,GAAG,YAAY,CAAC;AAChC,MAAM,UAAU,GAAG,YAAY,CAAC;AAChC,MAAM,UAAU,GAAG,YAAY,CAAC;AAEhC;;;;GAIG;AACH,MAAM,UAAU,OAAO,CAAC,KAAc;IACpC,OAAO,YAAY,CAAC,KAAmB,CAAC,KAAK,SAAS,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAiB;IAC5C,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,0EAA0E;QAC1E,IAAK,KAAgB,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,SAAS,CAAC;QACrD,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QACrC,IAAI,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO,MAAM,CAAC;QAC3C,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QACzC,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QACzC,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QACzC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,MAAM,CAAC;QACtC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QACrC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,gEAAgE;QAChE,kEAAkE;QAClE,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC;QAC1B,IAAI,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;QACrD,IAAI,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;QACrD,IAAI,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC;YAAE,OAAO,MAAM,CAAC;QACtD,IAAI,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;QAClC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC","sourcesContent":["import type { ColorFormat, ColorInput } from './types.js';\n\n/**\n * A color comes in, a format key comes out.\n * 'UNKNOWN' signals the caller that the input isn't a recognized color shape.\n * Used as the dispatch key for format-specific converters.\n */\nexport type DetectedFormat = ColorFormat | 'UNKNOWN';\n\n/** Shared with `src/conversions/hex.ts`. Capture group is used there; .test() ignores it here. */\nexport const HEX_RE = /^#([0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i;\nconst RGBA_STR_RE = /^rgba\\s*\\(/i;\nconst RGB_STR_RE = /^rgb\\s*\\(/i;\nconst HSL_STR_RE = /^hsl\\s*\\(/i;\nconst HSV_STR_RE = /^hsv\\s*\\(/i;\n\n/**\n * Runtime type-guard — returns `true` if `input` looks like something any of\n * chromonym's converters can parse. Cheaper than catching a `convert` throw\n * and more ergonomic than hand-rolling `detectFormat(x) !== 'UNKNOWN'`.\n */\nexport function isColor(input: unknown): input is ColorInput {\n return detectFormat(input as ColorInput) !== 'UNKNOWN';\n}\n\nexport function detectFormat(input: ColorInput): DetectedFormat {\n if (typeof input === 'string') {\n // Runtime guard against JS callers that bypass the string template union.\n if ((input as string).length === 0) return 'UNKNOWN';\n if (HEX_RE.test(input)) return 'HEX';\n if (RGBA_STR_RE.test(input)) return 'RGBA';\n if (RGB_STR_RE.test(input)) return 'RGB';\n if (HSL_STR_RE.test(input)) return 'HSL';\n if (HSV_STR_RE.test(input)) return 'HSV';\n return 'UNKNOWN';\n }\n\n if (Array.isArray(input)) {\n if (input.length === 4) return 'RGBA';\n if (input.length === 3) return 'RGB';\n return 'UNKNOWN';\n }\n\n if (input !== null && typeof input === 'object') {\n // Use Object.hasOwn — `in` walks the prototype chain and can be\n // abused by attacker-controlled prototypes to misclassify inputs.\n const has = Object.hasOwn;\n if (has(input, 'h') && has(input, 'v')) return 'HSV';\n if (has(input, 'h') && has(input, 'l')) return 'HSL';\n if (has(input, 'r') && has(input, 'a')) return 'RGBA';\n if (has(input, 'r')) return 'RGB';\n return 'UNKNOWN';\n }\n\n return 'UNKNOWN';\n}\n"]}
@@ -1,28 +1,25 @@
1
- import type { PantoneColorName } from './colorspaces/pantone';
2
- import type { WebColorName } from './colorspaces/web';
3
- import type { X11ColorName } from './colorspaces/x11';
4
- import type { ColorInput, ColorspaceName, DistanceMetric } from './types';
5
- type ColorNameByColorspace = {
6
- web: WebColorName;
7
- x11: X11ColorName;
8
- pantone: PantoneColorName;
9
- };
1
+ import { web } from './palettes/web.js';
2
+ import type { ColorInput, DistanceMetric, Palette } from './types.js';
3
+ /** Extract string keys from a Palette's `colors` map. */
4
+ type PaletteKey<P extends Palette> = Extract<keyof P['colors'], string>;
10
5
  /**
11
6
  * Identify the nearest-named color for any color input.
12
7
  *
13
- * The distance metric defaults to the colorspace's recommended choice
8
+ * The distance metric defaults to the palette's own `defaultMetric`
14
9
  * (web/x11: `deltaE76`, pantone: `deltaE2000`). Override with the `metric`
15
10
  * option — e.g. `{ metric: 'euclidean-srgb' }` for maximum speed, or
16
11
  * `{ metric: 'deltaE2000' }` for strictest perceptual accuracy.
17
12
  *
18
- * Returns `null` if the input isn't a recognized color shape or the
19
- * colorspace name isn't one of `web` / `x11` / `pantone`.
13
+ * Returns `null` if the input isn't a recognized color shape.
20
14
  *
21
- * Defaults: colorspace = 'web', metric = DEFAULT_METRICS[colorspace].
15
+ * Defaults: palette = `web`.
16
+ *
17
+ * BYO palettes: pass any object matching `Palette<Name>` and the
18
+ * return type is inferred from its `colors` keys — no registration needed.
22
19
  */
23
- export declare function identify<C extends ColorspaceName = 'web'>(input: ColorInput, opts?: {
24
- colorspace?: C;
20
+ export declare function identify<P extends Palette = typeof web>(input: ColorInput, opts?: {
21
+ palette?: P;
25
22
  metric?: DistanceMetric;
26
- }): ColorNameByColorspace[C] | null;
23
+ }): PaletteKey<P> | null;
27
24
  export {};
28
25
  //# sourceMappingURL=identify.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"identify.d.ts","sourceRoot":"","sources":["../src/identify.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAItD,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE1E,KAAK,qBAAqB,GAAG;IAC3B,GAAG,EAAE,YAAY,CAAC;IAClB,GAAG,EAAE,YAAY,CAAC;IAClB,OAAO,EAAE,gBAAgB,CAAC;CAC3B,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,cAAc,GAAG,KAAK,EACvD,KAAK,EAAE,UAAU,EACjB,IAAI,GAAE;IAAE,UAAU,CAAC,EAAE,CAAC,CAAC;IAAC,MAAM,CAAC,EAAE,cAAc,CAAA;CAAO,GACrD,qBAAqB,CAAC,CAAC,CAAC,GAAG,IAAI,CAUjC"}
1
+ {"version":3,"file":"identify.d.ts","sourceRoot":"","sources":["../src/identify.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AACxC,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAEtE,yDAAyD;AACzD,KAAK,UAAU,CAAC,CAAC,SAAS,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAC;AAExE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,GAAG,EACrD,KAAK,EAAE,UAAU,EACjB,IAAI,GAAE;IAAE,OAAO,CAAC,EAAE,CAAC,CAAC;IAAC,MAAM,CAAC,EAAE,cAAc,CAAA;CAAO,GAClD,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,CAQtB","sourcesContent":["import { toRgba } from './convert.js';\nimport { detectFormat } from './detectFormat.js';\nimport { nearest } from './indexing.js';\nimport { web } from './palettes/web.js';\nimport type { ColorInput, DistanceMetric, Palette } from './types.js';\n\n/** Extract string keys from a Palette's `colors` map. */\ntype PaletteKey<P extends Palette> = Extract<keyof P['colors'], string>;\n\n/**\n * Identify the nearest-named color for any color input.\n *\n * The distance metric defaults to the palette's own `defaultMetric`\n * (web/x11: `deltaE76`, pantone: `deltaE2000`). Override with the `metric`\n * option — e.g. `{ metric: 'euclidean-srgb' }` for maximum speed, or\n * `{ metric: 'deltaE2000' }` for strictest perceptual accuracy.\n *\n * Returns `null` if the input isn't a recognized color shape.\n *\n * Defaults: palette = `web`.\n *\n * BYO palettes: pass any object matching `Palette<Name>` and the\n * return type is inferred from its `colors` keys — no registration needed.\n */\nexport function identify<P extends Palette = typeof web>(\n input: ColorInput,\n opts: { palette?: P; metric?: DistanceMetric } = {},\n): PaletteKey<P> | null {\n const format = detectFormat(input);\n if (format === 'UNKNOWN') return null;\n\n const palette = (opts.palette ?? web) as P;\n const rgba = toRgba(input, format);\n const name = nearest(rgba, palette, opts.metric ?? palette.defaultMetric);\n return (name as PaletteKey<P>) || null;\n}\n"]}