chromonym 1.4.0 → 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 +240 -91
  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,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"]}
package/dist/identify.js CHANGED
@@ -1,29 +1,29 @@
1
- import { COLORSPACE_NAMES, COLORSPACES, DEFAULT_METRICS } from './colorspaces/registry';
2
- import { toRgba } from './convert';
3
- import { detectFormat } from './detectFormat';
4
- import { nearest } from './indexing';
1
+ import { toRgba } from './convert.js';
2
+ import { detectFormat } from './detectFormat.js';
3
+ import { nearest } from './indexing.js';
4
+ import { web } from './palettes/web.js';
5
5
  /**
6
6
  * Identify the nearest-named color for any color input.
7
7
  *
8
- * The distance metric defaults to the colorspace's recommended choice
8
+ * The distance metric defaults to the palette's own `defaultMetric`
9
9
  * (web/x11: `deltaE76`, pantone: `deltaE2000`). Override with the `metric`
10
10
  * option — e.g. `{ metric: 'euclidean-srgb' }` for maximum speed, or
11
11
  * `{ metric: 'deltaE2000' }` for strictest perceptual accuracy.
12
12
  *
13
- * Returns `null` if the input isn't a recognized color shape or the
14
- * colorspace name isn't one of `web` / `x11` / `pantone`.
13
+ * Returns `null` if the input isn't a recognized color shape.
15
14
  *
16
- * 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.
17
19
  */
18
20
  export function identify(input, opts = {}) {
19
21
  const format = detectFormat(input);
20
22
  if (format === 'UNKNOWN')
21
23
  return null;
22
- const colorspace = (opts.colorspace ?? 'web');
23
- if (!COLORSPACE_NAMES.has(colorspace))
24
- return null;
24
+ const palette = (opts.palette ?? web);
25
25
  const rgba = toRgba(input, format);
26
- const name = nearest(rgba, COLORSPACES[colorspace], opts.metric ?? DEFAULT_METRICS[colorspace]);
26
+ const name = nearest(rgba, palette, opts.metric ?? palette.defaultMetric);
27
27
  return name || null;
28
28
  }
29
29
  //# sourceMappingURL=identify.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"identify.js","sourceRoot":"","sources":["../src/identify.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAGxF,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AASrC;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,QAAQ,CACtB,KAAiB,EACjB,IAAI,GAAgD,EAAE;IAEtD,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACnC,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAEtC,MAAM,UAAU,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,KAAK,CAAM,CAAC;IACnD,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,UAAU,CAAC;QAAE,OAAO,IAAI,CAAC;IAEnD,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACnC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,WAAW,CAAC,UAAU,CAAC,EAAE,IAAI,CAAC,MAAM,IAAI,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC;IAChG,OAAQ,IAAiC,IAAI,IAAI,CAAC;AACpD,CAAC"}
1
+ {"version":3,"file":"identify.js","sourceRoot":"","sources":["../src/identify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAMxC;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,QAAQ,CACtB,KAAiB,EACjB,IAAI,GAA6C,EAAE;IAEnD,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACnC,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAEtC,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,GAAG,CAAM,CAAC;IAC3C,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACnC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC;IAC1E,OAAQ,IAAsB,IAAI,IAAI,CAAC;AACzC,CAAC","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"]}
package/dist/index.d.ts CHANGED
@@ -1,15 +1,15 @@
1
- export { type PantoneColorName, pantone } from './colorspaces/pantone';
2
- export { type WebColorName, web } from './colorspaces/web';
3
- export { type X11ColorName, x11 } from './colorspaces/x11';
4
- export { hexToRgba, rgbaToHex } from './conversions/hex';
5
- export { hslToRgba, rgbaToHsl } from './conversions/hsl';
6
- export { hsvToRgba, rgbaToHsv } from './conversions/hsv';
7
- export { pantoneToRgba, rgbaToPantone } from './conversions/pantone';
8
- export { rgbaToRgb, rgbToRgba } from './conversions/rgb';
9
- export { convert, fromRgba, toRgba } from './convert';
10
- export { type DetectedFormat, detectFormat, isColor } from './detectFormat';
11
- export { identify } from './identify';
12
- export { resolve } from './resolve';
13
- export type { ColorFormat, ColorInput, Colorspace, ColorspaceName, ColorValue, HexColor, HslInput, HslObject, HslString, HsvInput, HsvObject, HsvString, PantoneCode, Rgba, RgbaInput, RgbaString, RgbaTuple, RgbInput, RgbObject, RgbString, RgbTuple, } from './types';
14
- export { COLOR_FORMATS } from './types';
1
+ export { hexToRgba, rgbaToHex } from './conversions/hex.js';
2
+ export { hslToRgba, rgbaToHsl } from './conversions/hsl.js';
3
+ export { hsvToRgba, rgbaToHsv } from './conversions/hsv.js';
4
+ export { pantoneToRgba, rgbaToPantone } from './conversions/pantone.js';
5
+ export { rgbaToRgb, rgbToRgba } from './conversions/rgb.js';
6
+ export { convert, fromRgba, toRgba } from './convert.js';
7
+ export { type DetectedFormat, detectFormat, isColor } from './detectFormat.js';
8
+ export { identify } from './identify.js';
9
+ export { type PantoneColorName, pantone } from './palettes/pantone.js';
10
+ export { type WebColorName, web } from './palettes/web.js';
11
+ export { type X11ColorName, x11 } from './palettes/x11.js';
12
+ export { resolve } from './resolve.js';
13
+ export type { ColorFormat, ColorInput, ColorValue, DistanceMetric, HexColor, HslInput, HslObject, HslString, HsvInput, HsvObject, HsvString, NormalizeFn, Palette, Rgba, RgbaInput, RgbaString, RgbaTuple, RgbInput, RgbObject, RgbString, RgbTuple, } from './types.js';
14
+ export { COLOR_FORMATS } from './types.js';
15
15
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,gBAAgB,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,KAAK,YAAY,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,KAAK,YAAY,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAC3D,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,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,KAAK,cAAc,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAC5E,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,YAAY,EACV,WAAW,EACX,UAAU,EACV,UAAU,EACV,cAAc,EACd,UAAU,EACV,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,SAAS,EACT,QAAQ,EACR,SAAS,EACT,SAAS,EACT,WAAW,EACX,IAAI,EACJ,SAAS,EACT,UAAU,EACV,SAAS,EACT,QAAQ,EACR,SAAS,EACT,SAAS,EACT,QAAQ,GACT,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACxE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,EAAE,KAAK,cAAc,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC/E,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,KAAK,gBAAgB,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,KAAK,YAAY,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,KAAK,YAAY,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,YAAY,EACV,WAAW,EACX,UAAU,EACV,UAAU,EACV,cAAc,EACd,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,SAAS,EACT,QAAQ,EACR,SAAS,EACT,SAAS,EACT,WAAW,EACX,OAAO,EACP,IAAI,EACJ,SAAS,EACT,UAAU,EACV,SAAS,EACT,QAAQ,EACR,SAAS,EACT,SAAS,EACT,QAAQ,GACT,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC","sourcesContent":["// Public API barrel — explicit named re-exports only (tree-shake friendly).\n// No default export; no side effects at module scope.\n\nexport { hexToRgba, rgbaToHex } from './conversions/hex.js';\nexport { hslToRgba, rgbaToHsl } from './conversions/hsl.js';\nexport { hsvToRgba, rgbaToHsv } from './conversions/hsv.js';\nexport { pantoneToRgba, rgbaToPantone } from './conversions/pantone.js';\nexport { rgbaToRgb, rgbToRgba } from './conversions/rgb.js';\nexport { convert, fromRgba, toRgba } from './convert.js';\nexport { type DetectedFormat, detectFormat, isColor } from './detectFormat.js';\nexport { identify } from './identify.js';\nexport { type PantoneColorName, pantone } from './palettes/pantone.js';\nexport { type WebColorName, web } from './palettes/web.js';\nexport { type X11ColorName, x11 } from './palettes/x11.js';\nexport { resolve } from './resolve.js';\nexport type {\n ColorFormat,\n ColorInput,\n ColorValue,\n DistanceMetric,\n HexColor,\n HslInput,\n HslObject,\n HslString,\n HsvInput,\n HsvObject,\n HsvString,\n NormalizeFn,\n Palette,\n Rgba,\n RgbaInput,\n RgbaString,\n RgbaTuple,\n RgbInput,\n RgbObject,\n RgbString,\n RgbTuple,\n} from './types.js';\nexport { COLOR_FORMATS } from './types.js';\n"]}
package/dist/index.js CHANGED
@@ -1,16 +1,16 @@
1
1
  // Public API barrel — explicit named re-exports only (tree-shake friendly).
2
2
  // No default export; no side effects at module scope.
3
- export { pantone } from './colorspaces/pantone';
4
- export { web } from './colorspaces/web';
5
- export { x11 } from './colorspaces/x11';
6
- export { hexToRgba, rgbaToHex } from './conversions/hex';
7
- export { hslToRgba, rgbaToHsl } from './conversions/hsl';
8
- export { hsvToRgba, rgbaToHsv } from './conversions/hsv';
9
- export { pantoneToRgba, rgbaToPantone } from './conversions/pantone';
10
- export { rgbaToRgb, rgbToRgba } from './conversions/rgb';
11
- export { convert, fromRgba, toRgba } from './convert';
12
- export { detectFormat, isColor } from './detectFormat';
13
- export { identify } from './identify';
14
- export { resolve } from './resolve';
15
- export { COLOR_FORMATS } from './types';
3
+ export { hexToRgba, rgbaToHex } from './conversions/hex.js';
4
+ export { hslToRgba, rgbaToHsl } from './conversions/hsl.js';
5
+ export { hsvToRgba, rgbaToHsv } from './conversions/hsv.js';
6
+ export { pantoneToRgba, rgbaToPantone } from './conversions/pantone.js';
7
+ export { rgbaToRgb, rgbToRgba } from './conversions/rgb.js';
8
+ export { convert, fromRgba, toRgba } from './convert.js';
9
+ export { detectFormat, isColor } from './detectFormat.js';
10
+ export { identify } from './identify.js';
11
+ export { pantone } from './palettes/pantone.js';
12
+ export { web } from './palettes/web.js';
13
+ export { x11 } from './palettes/x11.js';
14
+ export { resolve } from './resolve.js';
15
+ export { COLOR_FORMATS } from './types.js';
16
16
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,sDAAsD;AAEtD,OAAO,EAAyB,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAqB,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAqB,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAC3D,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,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAuB,YAAY,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAC5E,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAwBpC,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,sDAAsD;AAEtD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACxE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,EAAuB,YAAY,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC/E,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAyB,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAqB,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAqB,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAwBvC,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC","sourcesContent":["// Public API barrel — explicit named re-exports only (tree-shake friendly).\n// No default export; no side effects at module scope.\n\nexport { hexToRgba, rgbaToHex } from './conversions/hex.js';\nexport { hslToRgba, rgbaToHsl } from './conversions/hsl.js';\nexport { hsvToRgba, rgbaToHsv } from './conversions/hsv.js';\nexport { pantoneToRgba, rgbaToPantone } from './conversions/pantone.js';\nexport { rgbaToRgb, rgbToRgba } from './conversions/rgb.js';\nexport { convert, fromRgba, toRgba } from './convert.js';\nexport { type DetectedFormat, detectFormat, isColor } from './detectFormat.js';\nexport { identify } from './identify.js';\nexport { type PantoneColorName, pantone } from './palettes/pantone.js';\nexport { type WebColorName, web } from './palettes/web.js';\nexport { type X11ColorName, x11 } from './palettes/x11.js';\nexport { resolve } from './resolve.js';\nexport type {\n ColorFormat,\n ColorInput,\n ColorValue,\n DistanceMetric,\n HexColor,\n HslInput,\n HslObject,\n HslString,\n HsvInput,\n HsvObject,\n HsvString,\n NormalizeFn,\n Palette,\n Rgba,\n RgbaInput,\n RgbaString,\n RgbaTuple,\n RgbInput,\n RgbObject,\n RgbString,\n RgbTuple,\n} from './types.js';\nexport { COLOR_FORMATS } from './types.js';\n"]}
@@ -1,44 +1,50 @@
1
- import type { Colorspace, DistanceMetric, Rgba } from './types';
1
+ import type { DistanceMetric, NormalizeFn, Palette, Rgba } from './types.js';
2
2
  /**
3
- * Name normalization and per-colorspace lookup indexes.
3
+ * Per-palette lookup indexes for name resolution and nearest-match.
4
4
  *
5
- * Tree-shaking note: this module never imports colorspace data itself.
6
- * Callers pass their own colorspace in, so importing (say) `pantoneToRgba`
7
- * never drags in `web` or `x11` through here.
8
- *
9
- * Caches are keyed by the colorspace object reference (WeakMap), so each
10
- * colorspace's index is built once per process and reused by every caller.
5
+ * Caches are keyed by the Palette wrapper (WeakMap), so each palette's
6
+ * indexes are built once per process and reused by every caller —
7
+ * including BYO palettes the user supplies.
11
8
  */
12
- export type NormalizeFn = (name: string) => string;
13
- /** Lowercase + strip non-alphanumeric. Used for web and x11. */
14
- export declare const standardNormalize: NormalizeFn;
9
+ export { pantoneNormalize, standardNormalize } from './palettes/normalize.js';
10
+ export type { NormalizeFn };
11
+ type AnyPalette = Palette<string>;
15
12
  /**
16
- * Pantone: also strip a leading "Pantone" or "PMS" prefix so that
17
- * "185 C", "185c", "Pantone 185 C", "PMS185C" all map to "185c".
13
+ * Normalized-name → canonical-key map for a given palette. Built once
14
+ * per palette using its own `normalize` function.
18
15
  */
19
- export declare const pantoneNormalize: NormalizeFn;
16
+ export declare function getNameIndex(space: AnyPalette): Map<string, string>;
20
17
  /**
21
- * Normalized-name → canonical-key map for a given colorspace.
22
- * Each (colorspace, normalize) pair must be stable — callers are
23
- * expected to always pair a given colorspace with the same normalizer.
18
+ * Hex → canonical-key map (reverse of `palette.colors`). Built once per
19
+ * palette. Used by `convert(color, { palette, format: 'NAME' })` for
20
+ * O(1) exact reverse lookup. Returns `undefined` on miss — the caller
21
+ * decides whether that's an error (convert throws; use `identify` for
22
+ * nearest-match semantics).
23
+ *
24
+ * Hex values are lowercased for the key so callers can normalize their
25
+ * input with `rgbaToHex(...).toLowerCase()` before lookup.
24
26
  */
25
- export declare function getNameIndex(space: Colorspace, normalize: NormalizeFn): Map<string, string>;
27
+ export declare function getReverseNameIndex(space: AnyPalette): Map<string, string>;
26
28
  /**
27
- * Canonical-key → Rgba entries for a given colorspace, built by parsing
29
+ * Canonical-key → Rgba entries for a given palette, built by parsing
28
30
  * each hex value once. Used by nearest-match lookups (identify, rgbaToPantone).
29
31
  */
30
- export declare function getRgbaIndex(space: Colorspace): Array<readonly [string, Rgba]>;
32
+ export declare function getRgbaIndex(space: AnyPalette): Array<readonly [string, Rgba]>;
31
33
  /**
32
- * Find the nearest-named entry in a colorspace to the given Rgba, using
34
+ * Find the nearest-named entry in a palette to the given Rgba, using
33
35
  * squared Euclidean distance in sRGB (alpha ignored). Used by `identify`
34
36
  * and `rgbaToPantone` when metric === 'euclidean-srgb'.
35
37
  */
36
- export declare function nearestByRgb(target: Rgba, space: Colorspace): string;
38
+ export declare function nearestByRgb(target: Rgba, space: AnyPalette): string;
37
39
  /**
38
- * Find the nearest-named entry in a colorspace under the specified distance
40
+ * Find the nearest-named entry in a palette under the specified distance
39
41
  * metric. Dispatches to the right index (rgba / linear-rgb / Lab) and
40
42
  * the right distance function. See `DistanceMetric` in types.ts for how
41
43
  * to pick a metric.
44
+ *
45
+ * `metric` is required — callers should pass `space.defaultMetric` if
46
+ * they want the palette's declared preference. A silent default would
47
+ * hide the distance-metric choice from the hot path.
42
48
  */
43
- export declare function nearest(target: Rgba, space: Colorspace, metric?: DistanceMetric): string;
49
+ export declare function nearest(target: Rgba, space: AnyPalette, metric: DistanceMetric): string;
44
50
  //# sourceMappingURL=indexing.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"indexing.d.ts","sourceRoot":"","sources":["../src/indexing.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAY,IAAI,EAAE,MAAM,SAAS,CAAC;AAE1E;;;;;;;;;GASG;AAEH,MAAM,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;AAEnD,gEAAgE;AAChE,eAAO,MAAM,iBAAiB,EAAE,WAA8D,CAAC;AAE/F;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,WAID,CAAC;AAK/B;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAU3F;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,KAAK,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAO9E;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,GAAG,MAAM,CAYpE;AAsED;;;;;GAKG;AACH,wBAAgB,OAAO,CACrB,MAAM,EAAE,IAAI,EACZ,KAAK,EAAE,UAAU,EACjB,MAAM,GAAE,cAAiC,GACxC,MAAM,CAaR"}
1
+ {"version":3,"file":"indexing.d.ts","sourceRoot":"","sources":["../src/indexing.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAY,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvF;;;;;;GAMG;AAKH,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC9E,YAAY,EAAE,WAAW,EAAE,CAAC;AAE5B,KAAK,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AAKlC;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAWnE;AAID;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAU1E;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,KAAK,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAS9E;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,GAAG,MAAM,CAYpE;AAsED;;;;;;;;;GASG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,cAAc,GAAG,MAAM,CAavF","sourcesContent":["import { hexToRgba } from './conversions/hex.js';\nimport { rgbaToLab, rgbaToLinearRgb, rgbaToOklab } from './math/colorSpace.js';\nimport { deltaE76Squared, deltaE94, deltaE2000, deltaEokSquared } from './math/deltaE.js';\nimport { squaredDistanceRgb } from './math/euclideanDistance.js';\nimport type { DistanceMetric, HexColor, NormalizeFn, Palette, Rgba } from './types.js';\n\n/**\n * Per-palette lookup indexes for name resolution and nearest-match.\n *\n * Caches are keyed by the Palette wrapper (WeakMap), so each palette's\n * indexes are built once per process and reused by every caller —\n * including BYO palettes the user supplies.\n */\n\n// Re-export normalizers from their own module for backward compat with any\n// caller that expected them here. The functions live in ./palettes/normalize\n// so palette modules can import them without pulling this file's math graph.\nexport { pantoneNormalize, standardNormalize } from './palettes/normalize.js';\nexport type { NormalizeFn };\n\ntype AnyPalette = Palette<string>;\n\nconst nameIndexCache = new WeakMap<AnyPalette, Map<string, string>>();\nconst rgbaIndexCache = new WeakMap<AnyPalette, Array<readonly [string, Rgba]>>();\n\n/**\n * Normalized-name → canonical-key map for a given palette. Built once\n * per palette using its own `normalize` function.\n */\nexport function getNameIndex(space: AnyPalette): Map<string, string> {\n let idx = nameIndexCache.get(space);\n if (idx === undefined) {\n idx = new Map();\n const { normalize } = space;\n for (const key of Object.keys(space.colors)) {\n idx.set(normalize(key), key);\n }\n nameIndexCache.set(space, idx);\n }\n return idx;\n}\n\nconst reverseIndexCache = new WeakMap<AnyPalette, Map<string, string>>();\n\n/**\n * Hex → canonical-key map (reverse of `palette.colors`). Built once per\n * palette. Used by `convert(color, { palette, format: 'NAME' })` for\n * O(1) exact reverse lookup. Returns `undefined` on miss — the caller\n * decides whether that's an error (convert throws; use `identify` for\n * nearest-match semantics).\n *\n * Hex values are lowercased for the key so callers can normalize their\n * input with `rgbaToHex(...).toLowerCase()` before lookup.\n */\nexport function getReverseNameIndex(space: AnyPalette): Map<string, string> {\n let idx = reverseIndexCache.get(space);\n if (idx === undefined) {\n idx = new Map();\n for (const [name, hex] of Object.entries(space.colors)) {\n idx.set((hex as string).toLowerCase(), name);\n }\n reverseIndexCache.set(space, idx);\n }\n return idx;\n}\n\n/**\n * Canonical-key → Rgba entries for a given palette, built by parsing\n * each hex value once. Used by nearest-match lookups (identify, rgbaToPantone).\n */\nexport function getRgbaIndex(space: AnyPalette): Array<readonly [string, Rgba]> {\n let idx = rgbaIndexCache.get(space);\n if (idx === undefined) {\n idx = Object.entries(space.colors).map(\n ([name, hex]) => [name, hexToRgba(hex as HexColor)] as const,\n );\n rgbaIndexCache.set(space, idx);\n }\n return idx;\n}\n\n/**\n * Find the nearest-named entry in a palette to the given Rgba, using\n * squared Euclidean distance in sRGB (alpha ignored). Used by `identify`\n * and `rgbaToPantone` when metric === 'euclidean-srgb'.\n */\nexport function nearestByRgb(target: Rgba, space: AnyPalette): string {\n const entries = getRgbaIndex(space);\n let bestName = entries[0]?.[0] ?? '';\n let bestDistance = Number.POSITIVE_INFINITY;\n for (const [name, candidate] of entries) {\n const d = squaredDistanceRgb(target, candidate);\n if (d < bestDistance) {\n bestDistance = d;\n bestName = name;\n }\n }\n return bestName;\n}\n\n// Precomputed Lab entries for delta-E metrics. Same shape as rgba index.\ntype LabTriple = readonly [number, number, number];\nconst labIndexCache = new WeakMap<AnyPalette, Array<readonly [string, LabTriple]>>();\n\nfunction getLabIndex(space: AnyPalette): Array<readonly [string, LabTriple]> {\n let idx = labIndexCache.get(space);\n if (idx === undefined) {\n idx = getRgbaIndex(space).map(([name, rgba]) => [name, rgbaToLab(rgba)] as const);\n labIndexCache.set(space, idx);\n }\n return idx;\n}\n\n// Precomputed linear-RGB entries for euclidean-linear metric.\nconst linearIndexCache = new WeakMap<AnyPalette, Array<readonly [string, LabTriple]>>();\n\nfunction getLinearIndex(space: AnyPalette): Array<readonly [string, LabTriple]> {\n let idx = linearIndexCache.get(space);\n if (idx === undefined) {\n idx = getRgbaIndex(space).map(([name, rgba]) => [name, rgbaToLinearRgb(rgba)] as const);\n linearIndexCache.set(space, idx);\n }\n return idx;\n}\n\n// Precomputed OKLAB entries for deltaEok metric.\nconst oklabIndexCache = new WeakMap<AnyPalette, Array<readonly [string, LabTriple]>>();\n\nfunction getOklabIndex(space: AnyPalette): Array<readonly [string, LabTriple]> {\n let idx = oklabIndexCache.get(space);\n if (idx === undefined) {\n idx = getRgbaIndex(space).map(([name, rgba]) => [name, rgbaToOklab(rgba)] as const);\n oklabIndexCache.set(space, idx);\n }\n return idx;\n}\n\n/**\n * Generic argmin over an `[name, value]` entries array with a custom\n * distance function. Factored so the three metric branches in `nearest`\n * share one loop body instead of duplicating it three times.\n */\nfunction argminBy<T>(\n entries: Array<readonly [string, T]>,\n target: T,\n dist: (a: T, b: T) => number,\n): string {\n let bestName = entries[0]?.[0] ?? '';\n let bestDistance = Number.POSITIVE_INFINITY;\n for (const [name, candidate] of entries) {\n const d = dist(target, candidate);\n if (d < bestDistance) {\n bestDistance = d;\n bestName = name;\n }\n }\n return bestName;\n}\n\n// Squared Euclidean on a [number, number, number] triple. Used for the\n// linear-RGB metric (sharing shape with Lab but different meaning).\nfunction squaredDistanceTriple(a: LabTriple, b: LabTriple): number {\n const d0 = a[0] - b[0];\n const d1 = a[1] - b[1];\n const d2 = a[2] - b[2];\n return d0 * d0 + d1 * d1 + d2 * d2;\n}\n\n/**\n * Find the nearest-named entry in a palette under the specified distance\n * metric. Dispatches to the right index (rgba / linear-rgb / Lab) and\n * the right distance function. See `DistanceMetric` in types.ts for how\n * to pick a metric.\n *\n * `metric` is required — callers should pass `space.defaultMetric` if\n * they want the palette's declared preference. A silent default would\n * hide the distance-metric choice from the hot path.\n */\nexport function nearest(target: Rgba, space: AnyPalette, metric: DistanceMetric): string {\n if (metric === 'euclidean-srgb') return nearestByRgb(target, space);\n if (metric === 'euclidean-linear') {\n return argminBy(getLinearIndex(space), rgbaToLinearRgb(target), squaredDistanceTriple);\n }\n if (metric === 'deltaEok') {\n return argminBy(getOklabIndex(space), rgbaToOklab(target), deltaEokSquared);\n }\n const labTarget = rgbaToLab(target);\n const labIndex = getLabIndex(space);\n if (metric === 'deltaE76') return argminBy(labIndex, labTarget, deltaE76Squared);\n if (metric === 'deltaE94') return argminBy(labIndex, labTarget, deltaE94);\n return argminBy(labIndex, labTarget, deltaE2000);\n}\n"]}
package/dist/indexing.js CHANGED
@@ -1,49 +1,72 @@
1
- import { hexToRgba } from './conversions/hex';
2
- import { rgbaToLab, rgbaToLinearRgb, rgbaToOklab } from './math/colorSpace';
3
- import { deltaE76Squared, deltaE94, deltaE2000, deltaEokSquared } from './math/deltaE';
4
- import { squaredDistanceRgb } from './math/euclideanDistance';
5
- /** Lowercase + strip non-alphanumeric. Used for web and x11. */
6
- export const standardNormalize = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, '');
1
+ import { hexToRgba } from './conversions/hex.js';
2
+ import { rgbaToLab, rgbaToLinearRgb, rgbaToOklab } from './math/colorSpace.js';
3
+ import { deltaE76Squared, deltaE94, deltaE2000, deltaEokSquared } from './math/deltaE.js';
4
+ import { squaredDistanceRgb } from './math/euclideanDistance.js';
7
5
  /**
8
- * Pantone: also strip a leading "Pantone" or "PMS" prefix so that
9
- * "185 C", "185c", "Pantone 185 C", "PMS185C" all map to "185c".
6
+ * Per-palette lookup indexes for name resolution and nearest-match.
7
+ *
8
+ * Caches are keyed by the Palette wrapper (WeakMap), so each palette's
9
+ * indexes are built once per process and reused by every caller —
10
+ * including BYO palettes the user supplies.
10
11
  */
11
- export const pantoneNormalize = (s) => s
12
- .toLowerCase()
13
- .replace(/^(pantone|pms)\s*/, '')
14
- .replace(/[^a-z0-9]/g, '');
12
+ // Re-export normalizers from their own module for backward compat with any
13
+ // caller that expected them here. The functions live in ./palettes/normalize
14
+ // so palette modules can import them without pulling this file's math graph.
15
+ export { pantoneNormalize, standardNormalize } from './palettes/normalize.js';
15
16
  const nameIndexCache = new WeakMap();
16
17
  const rgbaIndexCache = new WeakMap();
17
18
  /**
18
- * Normalized-name → canonical-key map for a given colorspace.
19
- * Each (colorspace, normalize) pair must be stable — callers are
20
- * expected to always pair a given colorspace with the same normalizer.
19
+ * Normalized-name → canonical-key map for a given palette. Built once
20
+ * per palette using its own `normalize` function.
21
21
  */
22
- export function getNameIndex(space, normalize) {
22
+ export function getNameIndex(space) {
23
23
  let idx = nameIndexCache.get(space);
24
24
  if (idx === undefined) {
25
25
  idx = new Map();
26
- for (const key of Object.keys(space)) {
26
+ const { normalize } = space;
27
+ for (const key of Object.keys(space.colors)) {
27
28
  idx.set(normalize(key), key);
28
29
  }
29
30
  nameIndexCache.set(space, idx);
30
31
  }
31
32
  return idx;
32
33
  }
34
+ const reverseIndexCache = new WeakMap();
33
35
  /**
34
- * Canonical-key → Rgba entries for a given colorspace, built by parsing
36
+ * Hex → canonical-key map (reverse of `palette.colors`). Built once per
37
+ * palette. Used by `convert(color, { palette, format: 'NAME' })` for
38
+ * O(1) exact reverse lookup. Returns `undefined` on miss — the caller
39
+ * decides whether that's an error (convert throws; use `identify` for
40
+ * nearest-match semantics).
41
+ *
42
+ * Hex values are lowercased for the key so callers can normalize their
43
+ * input with `rgbaToHex(...).toLowerCase()` before lookup.
44
+ */
45
+ export function getReverseNameIndex(space) {
46
+ let idx = reverseIndexCache.get(space);
47
+ if (idx === undefined) {
48
+ idx = new Map();
49
+ for (const [name, hex] of Object.entries(space.colors)) {
50
+ idx.set(hex.toLowerCase(), name);
51
+ }
52
+ reverseIndexCache.set(space, idx);
53
+ }
54
+ return idx;
55
+ }
56
+ /**
57
+ * Canonical-key → Rgba entries for a given palette, built by parsing
35
58
  * each hex value once. Used by nearest-match lookups (identify, rgbaToPantone).
36
59
  */
37
60
  export function getRgbaIndex(space) {
38
61
  let idx = rgbaIndexCache.get(space);
39
62
  if (idx === undefined) {
40
- idx = Object.entries(space).map(([name, hex]) => [name, hexToRgba(hex)]);
63
+ idx = Object.entries(space.colors).map(([name, hex]) => [name, hexToRgba(hex)]);
41
64
  rgbaIndexCache.set(space, idx);
42
65
  }
43
66
  return idx;
44
67
  }
45
68
  /**
46
- * Find the nearest-named entry in a colorspace to the given Rgba, using
69
+ * Find the nearest-named entry in a palette to the given Rgba, using
47
70
  * squared Euclidean distance in sRGB (alpha ignored). Used by `identify`
48
71
  * and `rgbaToPantone` when metric === 'euclidean-srgb'.
49
72
  */
@@ -115,12 +138,16 @@ function squaredDistanceTriple(a, b) {
115
138
  return d0 * d0 + d1 * d1 + d2 * d2;
116
139
  }
117
140
  /**
118
- * Find the nearest-named entry in a colorspace under the specified distance
141
+ * Find the nearest-named entry in a palette under the specified distance
119
142
  * metric. Dispatches to the right index (rgba / linear-rgb / Lab) and
120
143
  * the right distance function. See `DistanceMetric` in types.ts for how
121
144
  * to pick a metric.
145
+ *
146
+ * `metric` is required — callers should pass `space.defaultMetric` if
147
+ * they want the palette's declared preference. A silent default would
148
+ * hide the distance-metric choice from the hot path.
122
149
  */
123
- export function nearest(target, space, metric = 'euclidean-srgb') {
150
+ export function nearest(target, space, metric) {
124
151
  if (metric === 'euclidean-srgb')
125
152
  return nearestByRgb(target, space);
126
153
  if (metric === 'euclidean-linear') {
@@ -1 +1 @@
1
- {"version":3,"file":"indexing.js","sourceRoot":"","sources":["../src/indexing.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"indexing.js","sourceRoot":"","sources":["../src/indexing.ts"],"names":[],"mappings":"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","sourcesContent":["import { hexToRgba } from './conversions/hex.js';\nimport { rgbaToLab, rgbaToLinearRgb, rgbaToOklab } from './math/colorSpace.js';\nimport { deltaE76Squared, deltaE94, deltaE2000, deltaEokSquared } from './math/deltaE.js';\nimport { squaredDistanceRgb } from './math/euclideanDistance.js';\nimport type { DistanceMetric, HexColor, NormalizeFn, Palette, Rgba } from './types.js';\n\n/**\n * Per-palette lookup indexes for name resolution and nearest-match.\n *\n * Caches are keyed by the Palette wrapper (WeakMap), so each palette's\n * indexes are built once per process and reused by every caller —\n * including BYO palettes the user supplies.\n */\n\n// Re-export normalizers from their own module for backward compat with any\n// caller that expected them here. The functions live in ./palettes/normalize\n// so palette modules can import them without pulling this file's math graph.\nexport { pantoneNormalize, standardNormalize } from './palettes/normalize.js';\nexport type { NormalizeFn };\n\ntype AnyPalette = Palette<string>;\n\nconst nameIndexCache = new WeakMap<AnyPalette, Map<string, string>>();\nconst rgbaIndexCache = new WeakMap<AnyPalette, Array<readonly [string, Rgba]>>();\n\n/**\n * Normalized-name → canonical-key map for a given palette. Built once\n * per palette using its own `normalize` function.\n */\nexport function getNameIndex(space: AnyPalette): Map<string, string> {\n let idx = nameIndexCache.get(space);\n if (idx === undefined) {\n idx = new Map();\n const { normalize } = space;\n for (const key of Object.keys(space.colors)) {\n idx.set(normalize(key), key);\n }\n nameIndexCache.set(space, idx);\n }\n return idx;\n}\n\nconst reverseIndexCache = new WeakMap<AnyPalette, Map<string, string>>();\n\n/**\n * Hex → canonical-key map (reverse of `palette.colors`). Built once per\n * palette. Used by `convert(color, { palette, format: 'NAME' })` for\n * O(1) exact reverse lookup. Returns `undefined` on miss — the caller\n * decides whether that's an error (convert throws; use `identify` for\n * nearest-match semantics).\n *\n * Hex values are lowercased for the key so callers can normalize their\n * input with `rgbaToHex(...).toLowerCase()` before lookup.\n */\nexport function getReverseNameIndex(space: AnyPalette): Map<string, string> {\n let idx = reverseIndexCache.get(space);\n if (idx === undefined) {\n idx = new Map();\n for (const [name, hex] of Object.entries(space.colors)) {\n idx.set((hex as string).toLowerCase(), name);\n }\n reverseIndexCache.set(space, idx);\n }\n return idx;\n}\n\n/**\n * Canonical-key → Rgba entries for a given palette, built by parsing\n * each hex value once. Used by nearest-match lookups (identify, rgbaToPantone).\n */\nexport function getRgbaIndex(space: AnyPalette): Array<readonly [string, Rgba]> {\n let idx = rgbaIndexCache.get(space);\n if (idx === undefined) {\n idx = Object.entries(space.colors).map(\n ([name, hex]) => [name, hexToRgba(hex as HexColor)] as const,\n );\n rgbaIndexCache.set(space, idx);\n }\n return idx;\n}\n\n/**\n * Find the nearest-named entry in a palette to the given Rgba, using\n * squared Euclidean distance in sRGB (alpha ignored). Used by `identify`\n * and `rgbaToPantone` when metric === 'euclidean-srgb'.\n */\nexport function nearestByRgb(target: Rgba, space: AnyPalette): string {\n const entries = getRgbaIndex(space);\n let bestName = entries[0]?.[0] ?? '';\n let bestDistance = Number.POSITIVE_INFINITY;\n for (const [name, candidate] of entries) {\n const d = squaredDistanceRgb(target, candidate);\n if (d < bestDistance) {\n bestDistance = d;\n bestName = name;\n }\n }\n return bestName;\n}\n\n// Precomputed Lab entries for delta-E metrics. Same shape as rgba index.\ntype LabTriple = readonly [number, number, number];\nconst labIndexCache = new WeakMap<AnyPalette, Array<readonly [string, LabTriple]>>();\n\nfunction getLabIndex(space: AnyPalette): Array<readonly [string, LabTriple]> {\n let idx = labIndexCache.get(space);\n if (idx === undefined) {\n idx = getRgbaIndex(space).map(([name, rgba]) => [name, rgbaToLab(rgba)] as const);\n labIndexCache.set(space, idx);\n }\n return idx;\n}\n\n// Precomputed linear-RGB entries for euclidean-linear metric.\nconst linearIndexCache = new WeakMap<AnyPalette, Array<readonly [string, LabTriple]>>();\n\nfunction getLinearIndex(space: AnyPalette): Array<readonly [string, LabTriple]> {\n let idx = linearIndexCache.get(space);\n if (idx === undefined) {\n idx = getRgbaIndex(space).map(([name, rgba]) => [name, rgbaToLinearRgb(rgba)] as const);\n linearIndexCache.set(space, idx);\n }\n return idx;\n}\n\n// Precomputed OKLAB entries for deltaEok metric.\nconst oklabIndexCache = new WeakMap<AnyPalette, Array<readonly [string, LabTriple]>>();\n\nfunction getOklabIndex(space: AnyPalette): Array<readonly [string, LabTriple]> {\n let idx = oklabIndexCache.get(space);\n if (idx === undefined) {\n idx = getRgbaIndex(space).map(([name, rgba]) => [name, rgbaToOklab(rgba)] as const);\n oklabIndexCache.set(space, idx);\n }\n return idx;\n}\n\n/**\n * Generic argmin over an `[name, value]` entries array with a custom\n * distance function. Factored so the three metric branches in `nearest`\n * share one loop body instead of duplicating it three times.\n */\nfunction argminBy<T>(\n entries: Array<readonly [string, T]>,\n target: T,\n dist: (a: T, b: T) => number,\n): string {\n let bestName = entries[0]?.[0] ?? '';\n let bestDistance = Number.POSITIVE_INFINITY;\n for (const [name, candidate] of entries) {\n const d = dist(target, candidate);\n if (d < bestDistance) {\n bestDistance = d;\n bestName = name;\n }\n }\n return bestName;\n}\n\n// Squared Euclidean on a [number, number, number] triple. Used for the\n// linear-RGB metric (sharing shape with Lab but different meaning).\nfunction squaredDistanceTriple(a: LabTriple, b: LabTriple): number {\n const d0 = a[0] - b[0];\n const d1 = a[1] - b[1];\n const d2 = a[2] - b[2];\n return d0 * d0 + d1 * d1 + d2 * d2;\n}\n\n/**\n * Find the nearest-named entry in a palette under the specified distance\n * metric. Dispatches to the right index (rgba / linear-rgb / Lab) and\n * the right distance function. See `DistanceMetric` in types.ts for how\n * to pick a metric.\n *\n * `metric` is required — callers should pass `space.defaultMetric` if\n * they want the palette's declared preference. A silent default would\n * hide the distance-metric choice from the hot path.\n */\nexport function nearest(target: Rgba, space: AnyPalette, metric: DistanceMetric): string {\n if (metric === 'euclidean-srgb') return nearestByRgb(target, space);\n if (metric === 'euclidean-linear') {\n return argminBy(getLinearIndex(space), rgbaToLinearRgb(target), squaredDistanceTriple);\n }\n if (metric === 'deltaEok') {\n return argminBy(getOklabIndex(space), rgbaToOklab(target), deltaEokSquared);\n }\n const labTarget = rgbaToLab(target);\n const labIndex = getLabIndex(space);\n if (metric === 'deltaE76') return argminBy(labIndex, labTarget, deltaE76Squared);\n if (metric === 'deltaE94') return argminBy(labIndex, labTarget, deltaE94);\n return argminBy(labIndex, labTarget, deltaE2000);\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"clamp.d.ts","sourceRoot":"","sources":["../../src/math/clamp.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAK/D"}
1
+ {"version":3,"file":"clamp.d.ts","sourceRoot":"","sources":["../../src/math/clamp.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAK/D","sourcesContent":["/**\n * Clamp n to [lo, hi]. Caller must ensure n is a finite number.\n */\nexport function clamp(n: number, lo: number, hi: number): number {\n return Math.max(lo, Math.min(hi, n));\n}\n\n/**\n * Require a finite number at an API boundary; throws otherwise.\n * Used by converter entry points to reject NaN, Infinity, and non-numeric\n * values before they propagate through color math.\n */\nexport function requireFinite(n: unknown, label: string): number {\n if (typeof n !== 'number' || !Number.isFinite(n)) {\n throw new Error(`Expected finite number for ${label}, got ${typeof n}: ${String(n)}`);\n }\n return n;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"clamp.js","sourceRoot":"","sources":["../../src/math/clamp.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,UAAU,KAAK,CAAC,CAAS,EAAE,EAAU,EAAE,EAAU;IACrD,OAAO,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;AACvC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,CAAU,EAAE,KAAa;IACrD,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;QACjD,MAAM,IAAI,KAAK,CAAC,8BAA8B,KAAK,SAAS,OAAO,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACxF,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC"}
1
+ {"version":3,"file":"clamp.js","sourceRoot":"","sources":["../../src/math/clamp.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,UAAU,KAAK,CAAC,CAAS,EAAE,EAAU,EAAE,EAAU;IACrD,OAAO,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;AACvC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,CAAU,EAAE,KAAa;IACrD,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;QACjD,MAAM,IAAI,KAAK,CAAC,8BAA8B,KAAK,SAAS,OAAO,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACxF,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC","sourcesContent":["/**\n * Clamp n to [lo, hi]. Caller must ensure n is a finite number.\n */\nexport function clamp(n: number, lo: number, hi: number): number {\n return Math.max(lo, Math.min(hi, n));\n}\n\n/**\n * Require a finite number at an API boundary; throws otherwise.\n * Used by converter entry points to reject NaN, Infinity, and non-numeric\n * values before they propagate through color math.\n */\nexport function requireFinite(n: unknown, label: string): number {\n if (typeof n !== 'number' || !Number.isFinite(n)) {\n throw new Error(`Expected finite number for ${label}, got ${typeof n}: ${String(n)}`);\n }\n return n;\n}\n"]}
@@ -1,4 +1,4 @@
1
- import type { Rgba } from '../types';
1
+ import type { Rgba } from '../types.js';
2
2
  /**
3
3
  * Color-space conversions needed by the perceptual distance metrics.
4
4
  *
@@ -1 +1 @@
1
- {"version":3,"file":"colorSpace.d.ts","sourceRoot":"","sources":["../../src/math/colorSpace.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAErC;;;;;;;;;;;;GAYG;AAEH,sEAAsE;AACtE,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGpD;AAED,6FAA6F;AAC7F,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,0DAA0D;AAC1D,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAEpE;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAKxF;AAaD,6FAA6F;AAC7F,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAQlF;AAED,iFAAiF;AACjF,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAI9D;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAa1F;AAED,mCAAmC;AACnC,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAGhE;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAItF;AAED,gDAAgD;AAChD,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAGtF;AAED,yDAAyD;AACzD,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAGhE"}
1
+ {"version":3,"file":"colorSpace.d.ts","sourceRoot":"","sources":["../../src/math/colorSpace.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAExC;;;;;;;;;;;;GAYG;AAEH,sEAAsE;AACtE,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGpD;AAED,6FAA6F;AAC7F,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,0DAA0D;AAC1D,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAEpE;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAKxF;AAaD,6FAA6F;AAC7F,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAQlF;AAED,iFAAiF;AACjF,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAI9D;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAa1F;AAED,mCAAmC;AACnC,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAGhE;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAItF;AAED,gDAAgD;AAChD,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAGtF;AAED,yDAAyD;AACzD,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAGhE","sourcesContent":["import type { Rgba } from '../types.js';\n\n/**\n * Color-space conversions needed by the perceptual distance metrics.\n *\n * Two pipelines:\n * CIELAB: sRGB (0..255) → linear RGB → XYZ (D65) → Lab\n * OKLAB: sRGB (0..255) → linear RGB → LMS (cbrt) → OKLAB\n *\n * References:\n * sRGB EOTF / companding: IEC 61966-2-1\n * sRGB → XYZ (D65): Bruce Lindbloom, http://www.brucelindbloom.com/\n * XYZ → Lab: CIE 15:2004\n * OKLAB: Ottosson, https://bottosson.github.io/posts/oklab/\n */\n\n/** sRGB → linear for a single channel in [0, 255]. Returns [0, 1]. */\nexport function srgbToLinear(channel: number): number {\n const s = channel / 255;\n return s <= 0.04045 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;\n}\n\n/** Linear → sRGB for a single channel in [0, 1]. Returns [0, 1] (scale by 255 if needed). */\nexport function linearToSrgb(linear: number): number {\n return linear <= 0.0031308 ? 12.92 * linear : 1.055 * linear ** (1 / 2.4) - 0.055;\n}\n\n/** Rgba → linear RGB triple in [0, 1] (alpha dropped). */\nexport function rgbaToLinearRgb(rgba: Rgba): [number, number, number] {\n return [srgbToLinear(rgba.r), srgbToLinear(rgba.g), srgbToLinear(rgba.b)];\n}\n\n/**\n * Linear RGB (D65) → CIE XYZ (D65), using the standard sRGB matrix.\n * Input and output both in [0, 1] on each axis (approximately; Y = 1 is white).\n */\nexport function linearRgbToXyz(r: number, g: number, b: number): [number, number, number] {\n const x = r * 0.4124564 + g * 0.3575761 + b * 0.1804375;\n const y = r * 0.2126729 + g * 0.7151522 + b * 0.072175;\n const z = r * 0.0193339 + g * 0.119192 + b * 0.9503041;\n return [x, y, z];\n}\n\n// D65 white reference (CIE 1931 2° observer).\nconst XN = 0.95047;\nconst YN = 1.0;\nconst ZN = 1.08883;\n\n// f(t) in the CIE Lab formula; cube-root with a linear toe near origin.\nfunction labF(t: number): number {\n const DELTA = 6 / 29;\n return t > DELTA ** 3 ? Math.cbrt(t) : t / (3 * DELTA ** 2) + 4 / 29;\n}\n\n/** CIE XYZ (D65) → CIE Lab. L ∈ [0, 100] approx; a,b unbounded but typically [-128, 127]. */\nexport function xyzToLab(x: number, y: number, z: number): [number, number, number] {\n const fx = labF(x / XN);\n const fy = labF(y / YN);\n const fz = labF(z / ZN);\n const l = 116 * fy - 16;\n const a = 500 * (fx - fy);\n const b = 200 * (fy - fz);\n return [l, a, b];\n}\n\n/** Rgba (sRGB, 0..255) → CIE Lab. Canonical pipeline for perceptual distance. */\nexport function rgbaToLab(rgba: Rgba): [number, number, number] {\n const [r, g, b] = rgbaToLinearRgb(rgba);\n const [x, y, z] = linearRgbToXyz(r, g, b);\n return xyzToLab(x, y, z);\n}\n\n/**\n * Linear sRGB → OKLAB (Björn Ottosson, 2020). OKLAB is strictly more\n * perceptually uniform than CIELAB, especially in the blue/purple region\n * where even CIEDE2000 has residual error. Distance in OKLAB is plain\n * Euclidean — no weighting formula needed, because the space itself is\n * designed around uniformity.\n *\n * Reference: https://bottosson.github.io/posts/oklab/\n */\nexport function linearRgbToOklab(r: number, g: number, b: number): [number, number, 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\n const l_ = Math.cbrt(l);\n const m_ = Math.cbrt(m);\n const s_ = Math.cbrt(s);\n\n const L = 0.2104542553 * l_ + 0.793617785 * m_ - 0.0040720468 * s_;\n const a = 1.9779984951 * l_ - 2.428592205 * m_ + 0.4505937099 * s_;\n const b2 = 0.0259040371 * l_ + 0.7827717662 * m_ - 0.808675766 * s_;\n return [L, a, b2];\n}\n\n/** Rgba (sRGB, 0..255) → OKLAB. */\nexport function rgbaToOklab(rgba: Rgba): [number, number, number] {\n const [r, g, b] = rgbaToLinearRgb(rgba);\n return linearRgbToOklab(r, g, b);\n}\n\n/**\n * OKLAB → OKLCh (polar form of OKLAB). L stays the same; a,b become\n * chroma and hue. Useful for hue-aware manipulation and naming strategies\n * that want to reason about hue distance independently of lightness.\n *\n * Returns [L, C, h] where C >= 0 and h is in degrees [0, 360).\n * Achromatic colors (C ≈ 0) return h = 0 by convention.\n */\nexport function oklabToOklch(L: number, a: number, b: number): [number, number, number] {\n const C = Math.sqrt(a * a + b * b);\n const h = C === 0 ? 0 : ((Math.atan2(b, a) * 180) / Math.PI + 360) % 360;\n return [L, C, h];\n}\n\n/** OKLCh → OKLAB. Inverse of `oklabToOklch`. */\nexport function oklchToOklab(L: number, C: number, h: number): [number, number, number] {\n const rad = (h * Math.PI) / 180;\n return [L, C * Math.cos(rad), C * Math.sin(rad)];\n}\n\n/** Rgba (sRGB, 0..255) → OKLCh. Composition shortcut. */\nexport function rgbaToOklch(rgba: Rgba): [number, number, number] {\n const [L, a, b] = rgbaToOklab(rgba);\n return oklabToOklch(L, a, b);\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"colorSpace.js","sourceRoot":"","sources":["../../src/math/colorSpace.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"colorSpace.js","sourceRoot":"","sources":["../../src/math/colorSpace.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Rgba } from '../types.js';\n\n/**\n * Color-space conversions needed by the perceptual distance metrics.\n *\n * Two pipelines:\n * CIELAB: sRGB (0..255) → linear RGB → XYZ (D65) → Lab\n * OKLAB: sRGB (0..255) → linear RGB → LMS (cbrt) → OKLAB\n *\n * References:\n * sRGB EOTF / companding: IEC 61966-2-1\n * sRGB → XYZ (D65): Bruce Lindbloom, http://www.brucelindbloom.com/\n * XYZ → Lab: CIE 15:2004\n * OKLAB: Ottosson, https://bottosson.github.io/posts/oklab/\n */\n\n/** sRGB → linear for a single channel in [0, 255]. Returns [0, 1]. */\nexport function srgbToLinear(channel: number): number {\n const s = channel / 255;\n return s <= 0.04045 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;\n}\n\n/** Linear → sRGB for a single channel in [0, 1]. Returns [0, 1] (scale by 255 if needed). */\nexport function linearToSrgb(linear: number): number {\n return linear <= 0.0031308 ? 12.92 * linear : 1.055 * linear ** (1 / 2.4) - 0.055;\n}\n\n/** Rgba → linear RGB triple in [0, 1] (alpha dropped). */\nexport function rgbaToLinearRgb(rgba: Rgba): [number, number, number] {\n return [srgbToLinear(rgba.r), srgbToLinear(rgba.g), srgbToLinear(rgba.b)];\n}\n\n/**\n * Linear RGB (D65) → CIE XYZ (D65), using the standard sRGB matrix.\n * Input and output both in [0, 1] on each axis (approximately; Y = 1 is white).\n */\nexport function linearRgbToXyz(r: number, g: number, b: number): [number, number, number] {\n const x = r * 0.4124564 + g * 0.3575761 + b * 0.1804375;\n const y = r * 0.2126729 + g * 0.7151522 + b * 0.072175;\n const z = r * 0.0193339 + g * 0.119192 + b * 0.9503041;\n return [x, y, z];\n}\n\n// D65 white reference (CIE 1931 2° observer).\nconst XN = 0.95047;\nconst YN = 1.0;\nconst ZN = 1.08883;\n\n// f(t) in the CIE Lab formula; cube-root with a linear toe near origin.\nfunction labF(t: number): number {\n const DELTA = 6 / 29;\n return t > DELTA ** 3 ? Math.cbrt(t) : t / (3 * DELTA ** 2) + 4 / 29;\n}\n\n/** CIE XYZ (D65) → CIE Lab. L ∈ [0, 100] approx; a,b unbounded but typically [-128, 127]. */\nexport function xyzToLab(x: number, y: number, z: number): [number, number, number] {\n const fx = labF(x / XN);\n const fy = labF(y / YN);\n const fz = labF(z / ZN);\n const l = 116 * fy - 16;\n const a = 500 * (fx - fy);\n const b = 200 * (fy - fz);\n return [l, a, b];\n}\n\n/** Rgba (sRGB, 0..255) → CIE Lab. Canonical pipeline for perceptual distance. */\nexport function rgbaToLab(rgba: Rgba): [number, number, number] {\n const [r, g, b] = rgbaToLinearRgb(rgba);\n const [x, y, z] = linearRgbToXyz(r, g, b);\n return xyzToLab(x, y, z);\n}\n\n/**\n * Linear sRGB → OKLAB (Björn Ottosson, 2020). OKLAB is strictly more\n * perceptually uniform than CIELAB, especially in the blue/purple region\n * where even CIEDE2000 has residual error. Distance in OKLAB is plain\n * Euclidean — no weighting formula needed, because the space itself is\n * designed around uniformity.\n *\n * Reference: https://bottosson.github.io/posts/oklab/\n */\nexport function linearRgbToOklab(r: number, g: number, b: number): [number, number, 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\n const l_ = Math.cbrt(l);\n const m_ = Math.cbrt(m);\n const s_ = Math.cbrt(s);\n\n const L = 0.2104542553 * l_ + 0.793617785 * m_ - 0.0040720468 * s_;\n const a = 1.9779984951 * l_ - 2.428592205 * m_ + 0.4505937099 * s_;\n const b2 = 0.0259040371 * l_ + 0.7827717662 * m_ - 0.808675766 * s_;\n return [L, a, b2];\n}\n\n/** Rgba (sRGB, 0..255) → OKLAB. */\nexport function rgbaToOklab(rgba: Rgba): [number, number, number] {\n const [r, g, b] = rgbaToLinearRgb(rgba);\n return linearRgbToOklab(r, g, b);\n}\n\n/**\n * OKLAB → OKLCh (polar form of OKLAB). L stays the same; a,b become\n * chroma and hue. Useful for hue-aware manipulation and naming strategies\n * that want to reason about hue distance independently of lightness.\n *\n * Returns [L, C, h] where C >= 0 and h is in degrees [0, 360).\n * Achromatic colors (C ≈ 0) return h = 0 by convention.\n */\nexport function oklabToOklch(L: number, a: number, b: number): [number, number, number] {\n const C = Math.sqrt(a * a + b * b);\n const h = C === 0 ? 0 : ((Math.atan2(b, a) * 180) / Math.PI + 360) % 360;\n return [L, C, h];\n}\n\n/** OKLCh → OKLAB. Inverse of `oklabToOklch`. */\nexport function oklchToOklab(L: number, C: number, h: number): [number, number, number] {\n const rad = (h * Math.PI) / 180;\n return [L, C * Math.cos(rad), C * Math.sin(rad)];\n}\n\n/** Rgba (sRGB, 0..255) → OKLCh. Composition shortcut. */\nexport function rgbaToOklch(rgba: Rgba): [number, number, number] {\n const [L, a, b] = rgbaToOklab(rgba);\n return oklabToOklch(L, a, b);\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"deltaE.d.ts","sourceRoot":"","sources":["../../src/math/deltaE.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,MAAM,GAAG,GAAG,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;AAE7D;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,GAAG,MAAM,CAKtD;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,GAAG,MAAM,CAE/C;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,GAAG,MAAM,CAwB/C;AAgCD,wBAAgB,UAAU,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,SAAI,EAAE,EAAE,SAAI,EAAE,EAAE,SAAI,GAAG,MAAM,CAoFzE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,GAAG,MAAM,CAKtD;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,GAAG,MAAM,CAE/C"}
1
+ {"version":3,"file":"deltaE.d.ts","sourceRoot":"","sources":["../../src/math/deltaE.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,MAAM,GAAG,GAAG,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;AAE7D;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,GAAG,MAAM,CAKtD;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,GAAG,MAAM,CAE/C;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,GAAG,MAAM,CAwB/C;AAgCD,wBAAgB,UAAU,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,SAAI,EAAE,EAAE,SAAI,EAAE,EAAE,SAAI,GAAG,MAAM,CAoFzE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,GAAG,MAAM,CAKtD;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,GAAG,MAAM,CAE/C","sourcesContent":["/**\n * Color-difference metrics. Inputs are `Lab` triples (L, a, b). The\n * CIELAB family (ΔE*76 / ΔE*94 / ΔE*00) takes coordinates from\n * `rgbaToLab`; ΔE*ok takes coordinates from `rgbaToOklab`. Both spaces\n * share the `Lab` tuple shape but are not interchangeable — see\n * `deltaEokSquared` below.\n *\n * Within the CIELAB family, the metrics differ in how each region of\n * Lab space is weighted:\n * ΔE*76 — simple Euclidean in Lab (CIE 1976). Fast, decent.\n * ΔE*94 — adds chroma/hue weighting (CIE 1994). Better saturation handling.\n * ΔE00 — also adjusts L/C/h + rotation correction in blue-purple region\n * (CIE 2000, aka CIEDE2000). Current industry standard.\n *\n * References:\n * CIE 15:2004 (Colorimetry), CIE TC1-29 (ΔE2000).\n * G. Sharma, W. Wu, E.N. Dalal, \"The CIEDE2000 Color-Difference Formula\"\n * (Color Research & Application, 2005) — canonical test vectors.\n */\n\nexport type Lab = readonly [l: number, a: number, b: number];\n\n/**\n * ΔE*ab (CIE 1976). Simple Euclidean distance in Lab.\n * Returns the *square* for speed — identify loops only need argmin, and\n * sqrt is monotonic. Use `deltaE76` if you need the actual distance.\n */\nexport function deltaE76Squared(p: Lab, q: Lab): number {\n const dl = p[0] - q[0];\n const da = p[1] - q[1];\n const db = p[2] - q[2];\n return dl * dl + da * da + db * db;\n}\n\nexport function deltaE76(p: Lab, q: Lab): number {\n return Math.sqrt(deltaE76Squared(p, q));\n}\n\n/**\n * ΔE*94 (CIE 1994). Weights the chroma and hue contributions relative to\n * Lab Euclidean distance — fixes the \"saturated colors feel too far apart\"\n * problem of ΔE*76. Uses graphic-arts constants (kL=1, K1=0.045, K2=0.015).\n *\n * Not symmetric in the strict sense (reference vs sample), but we treat\n * both inputs as graphic-arts samples — the formula reduces to a\n * symmetric form below.\n */\nexport function deltaE94(p: Lab, q: Lab): number {\n const [l1, a1, b1] = p;\n const [l2, a2, b2] = q;\n const dL = l1 - l2;\n const c1 = Math.hypot(a1, b1);\n const c2 = Math.hypot(a2, b2);\n const dC = c1 - c2;\n const da = a1 - a2;\n const db = b1 - b2;\n const dH2 = da * da + db * db - dC * dC;\n // dH² can go slightly negative due to floating-point — clamp.\n const dH2Safe = dH2 < 0 ? 0 : dH2;\n\n const K1 = 0.045;\n const K2 = 0.015;\n const sL = 1;\n const sC = 1 + K1 * c1;\n const sH = 1 + K2 * c1;\n\n const termL = dL / sL;\n const termC = dC / sC;\n const termH = Math.sqrt(dH2Safe) / sH;\n\n return Math.sqrt(termL * termL + termC * termC + termH * termH);\n}\n\n// ---- ΔE00 helpers --------------------------------------------------------\n\nconst DEG = Math.PI / 180;\nconst RAD = 180 / Math.PI;\n\nfunction hueDeg(b: number, a: number): number {\n if (b === 0 && a === 0) return 0;\n return (Math.atan2(b, a) * RAD + 360) % 360;\n}\n\n/**\n * ΔE*00 (CIE 2000 / CIEDE2000). Current industry-standard perceptual\n * color-difference formula. Fixes the saturated-blue failure of ΔE76/94\n * with a rotation term in the blue-purple region (h ≈ 275°).\n *\n * kL / kC / kH are parametric factors (default 1/1/1 = graphic arts).\n * Reference: G. Sharma, W. Wu, E.N. Dalal, \"The CIEDE2000 Color-Difference\n * Formula\" (Color Research & Application, 2005).\n */\n// Precomputed constant: 25^7 = 6,103,515,625. Appears twice in the formula.\nconst POW_25_7 = 6103515625;\n\n// Inline pow(x, 7) — Math.pow with non-integer exponent is slower in V8\n// than three multiplies. x² → x⁴ → x⁶·x.\nfunction pow7(x: number): number {\n const x2 = x * x;\n const x4 = x2 * x2;\n return x4 * x2 * x;\n}\n\nexport function deltaE2000(p: Lab, q: Lab, kL = 1, kC = 1, kH = 1): number {\n const l1 = p[0];\n const a1 = p[1];\n const b1 = p[2];\n const l2 = q[0];\n const a2 = q[1];\n const b2 = q[2];\n\n // Chromas and mean chroma. Lab values stay within ±150 so overflow\n // protection in Math.hypot is unnecessary — plain sqrt(a²+b²) is ~3×\n // faster in V8 and more than precise enough for our range.\n const c1 = Math.sqrt(a1 * a1 + b1 * b1);\n const c2 = Math.sqrt(a2 * a2 + b2 * b2);\n const cBar = (c1 + c2) / 2;\n const cBar7 = pow7(cBar);\n\n // G-factor boost for a* in low-chroma regions.\n const g = 0.5 * (1 - Math.sqrt(cBar7 / (cBar7 + POW_25_7)));\n const a1p = (1 + g) * a1;\n const a2p = (1 + g) * a2;\n\n // Primed chroma and hue.\n const c1p = Math.sqrt(a1p * a1p + b1 * b1);\n const c2p = Math.sqrt(a2p * a2p + b2 * b2);\n const h1p = hueDeg(b1, a1p);\n const h2p = hueDeg(b2, a2p);\n\n // Differences.\n const dLp = l2 - l1;\n const dCp = c2p - c1p;\n\n let dhp: number;\n if (c1p * c2p === 0) {\n dhp = 0;\n } else {\n const raw = h2p - h1p;\n if (Math.abs(raw) <= 180) dhp = raw;\n else if (raw > 180) dhp = raw - 360;\n else dhp = raw + 360;\n }\n const dHp = 2 * Math.sqrt(c1p * c2p) * Math.sin((dhp * DEG) / 2);\n\n // Means.\n const lBarP = (l1 + l2) / 2;\n const cBarP = (c1p + c2p) / 2;\n\n let hBarP: number;\n if (c1p * c2p === 0) {\n hBarP = h1p + h2p;\n } else if (Math.abs(h1p - h2p) <= 180) {\n hBarP = (h1p + h2p) / 2;\n } else if (h1p + h2p < 360) {\n hBarP = (h1p + h2p + 360) / 2;\n } else {\n hBarP = (h1p + h2p - 360) / 2;\n }\n\n // Weighting function T.\n const hBarRad = hBarP * DEG;\n const t =\n 1 -\n 0.17 * Math.cos(hBarRad - 30 * DEG) +\n 0.24 * Math.cos(2 * hBarRad) +\n 0.32 * Math.cos(3 * hBarRad + 6 * DEG) -\n 0.2 * Math.cos(4 * hBarRad - 63 * DEG);\n\n // Lightness, chroma, hue weighting factors.\n const lBarMinus50Sq = (lBarP - 50) ** 2;\n const sL = 1 + (0.015 * lBarMinus50Sq) / Math.sqrt(20 + lBarMinus50Sq);\n const sC = 1 + 0.045 * cBarP;\n const sH = 1 + 0.015 * cBarP * t;\n\n // Rotation term — the blue-purple correction that ΔE94 lacks.\n const hShift = (hBarP - 275) / 25;\n const dTheta = 30 * Math.exp(-(hShift * hShift));\n const cBarP7 = pow7(cBarP);\n const rC = 2 * Math.sqrt(cBarP7 / (cBarP7 + POW_25_7));\n const rT = -Math.sin(2 * dTheta * DEG) * rC;\n\n const termL = dLp / (kL * sL);\n const termC = dCp / (kC * sC);\n const termH = dHp / (kH * sH);\n\n return Math.sqrt(termL * termL + termC * termC + termH * termH + rT * termC * termH);\n}\n\n/**\n * Squared Euclidean distance in OKLAB space (Björn Ottosson, 2020).\n * OKLAB is perceptually uniform by construction — plain Euclidean IS the\n * perceptual distance, no weighting needed. Returns the square for\n * argmin speed; `deltaEok` returns the actual distance.\n *\n * Often gives more visually-accurate nearest-matches than CIEDE2000 in\n * the saturated blue/purple region.\n *\n * NOTE: the body is byte-identical to `deltaE76Squared` — both compute\n * `dl² + da² + db²`. The semantic difference is entirely in *which* space\n * the caller passes coordinates from: CIELAB (for ΔE*76) vs OKLAB (for this).\n * Kept separate so callers and tests can't cross-wire the indexes.\n */\nexport function deltaEokSquared(p: Lab, q: Lab): number {\n const dl = p[0] - q[0];\n const da = p[1] - q[1];\n const db = p[2] - q[2];\n return dl * dl + da * da + db * db;\n}\n\nexport function deltaEok(p: Lab, q: Lab): number {\n return Math.sqrt(deltaEokSquared(p, q));\n}\n"]}