chromonym 1.4.1 → 3.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 (114) hide show
  1. package/NOTICE.md +64 -4
  2. package/README.md +290 -83
  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 +41 -19
  36. package/dist/identify.d.ts.map +1 -1
  37. package/dist/identify.js +37 -22
  38. package/dist/identify.js.map +1 -1
  39. package/dist/index.d.ts +15 -14
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +14 -13
  42. package/dist/index.js.map +1 -1
  43. package/dist/indexing.d.ts +48 -24
  44. package/dist/indexing.d.ts.map +1 -1
  45. package/dist/indexing.js +111 -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/editDistance.d.ts +17 -0
  55. package/dist/math/editDistance.d.ts.map +1 -0
  56. package/dist/math/editDistance.js +48 -0
  57. package/dist/math/editDistance.js.map +1 -0
  58. package/dist/math/euclideanDistance.d.ts +1 -1
  59. package/dist/math/euclideanDistance.d.ts.map +1 -1
  60. package/dist/math/euclideanDistance.js.map +1 -1
  61. package/dist/math/hueSector.d.ts.map +1 -1
  62. package/dist/math/hueSector.js.map +1 -1
  63. package/dist/palettes/crayola.d.ts +200 -0
  64. package/dist/palettes/crayola.d.ts.map +1 -0
  65. package/dist/palettes/crayola.js +121 -0
  66. package/dist/palettes/crayola.js.map +1 -0
  67. package/dist/palettes/index.d.ts +5 -0
  68. package/dist/palettes/index.d.ts.map +1 -0
  69. package/dist/palettes/index.js +5 -0
  70. package/dist/palettes/index.js.map +1 -0
  71. package/dist/palettes/normalize.d.ts +17 -0
  72. package/dist/palettes/normalize.d.ts.map +1 -0
  73. package/dist/palettes/normalize.js +19 -0
  74. package/dist/palettes/normalize.js.map +1 -0
  75. package/dist/palettes/pantone.d.ts +1834 -0
  76. package/dist/palettes/pantone.d.ts.map +1 -0
  77. package/dist/palettes/pantone.js +925 -0
  78. package/dist/palettes/pantone.js.map +1 -0
  79. package/dist/palettes/web.d.ts +320 -0
  80. package/dist/palettes/web.d.ts.map +1 -0
  81. package/dist/{colorspaces → palettes}/web.js +18 -3
  82. package/dist/palettes/web.js.map +1 -0
  83. package/dist/palettes/x11.d.ts +1346 -0
  84. package/dist/palettes/x11.d.ts.map +1 -0
  85. package/dist/palettes/x11.js +686 -0
  86. package/dist/palettes/x11.js.map +1 -0
  87. package/dist/resolve.d.ts +42 -7
  88. package/dist/resolve.d.ts.map +1 -1
  89. package/dist/resolve.js +48 -20
  90. package/dist/resolve.js.map +1 -1
  91. package/dist/types.d.ts +10 -6
  92. package/dist/types.d.ts.map +1 -1
  93. package/dist/types.js +2 -1
  94. package/dist/types.js.map +1 -1
  95. package/package.json +38 -18
  96. package/dist/colorspaces/index.d.ts +0 -4
  97. package/dist/colorspaces/index.d.ts.map +0 -1
  98. package/dist/colorspaces/index.js +0 -4
  99. package/dist/colorspaces/index.js.map +0 -1
  100. package/dist/colorspaces/pantone.d.ts +0 -919
  101. package/dist/colorspaces/pantone.d.ts.map +0 -1
  102. package/dist/colorspaces/pantone.js +0 -918
  103. package/dist/colorspaces/pantone.js.map +0 -1
  104. package/dist/colorspaces/registry.d.ts +0 -26
  105. package/dist/colorspaces/registry.d.ts.map +0 -1
  106. package/dist/colorspaces/registry.js +0 -36
  107. package/dist/colorspaces/registry.js.map +0 -1
  108. package/dist/colorspaces/web.d.ts +0 -156
  109. package/dist/colorspaces/web.d.ts.map +0 -1
  110. package/dist/colorspaces/web.js.map +0 -1
  111. package/dist/colorspaces/x11.d.ts +0 -667
  112. package/dist/colorspaces/x11.d.ts.map +0 -1
  113. package/dist/colorspaces/x11.js +0 -666
  114. package/dist/colorspaces/x11.js.map +0 -1
@@ -1,28 +1,50 @@
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;
1
+ import { web } from './palettes/web.js';
2
+ import type { ColorInput, DistanceMetric, HexColor, 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>;
5
+ /**
6
+ * One ranked nearest-match entry when `identify` is called with `k`.
7
+ * `value` is the palette's stored hex for the match (useful for rendering
8
+ * swatches). `distance` is in the selected metric's natural units — ΔE
9
+ * for `deltaE*` metrics, channel-unit Euclidean for the others.
10
+ */
11
+ export type IdentifyMatch<Name extends string> = {
12
+ readonly name: Name;
13
+ readonly value: HexColor;
14
+ readonly distance: number;
9
15
  };
10
16
  /**
11
- * Identify the nearest-named color for any color input.
17
+ * Identify the nearest-named color for any color input (or palette name
18
+ * from another palette via `source`).
19
+ *
20
+ * **Simple case — color in, name out:**
21
+ * identify('#ff0000') → 'red'
22
+ * identify('#ff0000', { palette: pantone }) → '172 C'
12
23
  *
13
- * The distance metric defaults to the colorspace's recommended choice
14
- * (web/x11: `deltaE76`, pantone: `deltaE2000`). Override with the `metric`
15
- * option — e.g. `{ metric: 'euclidean-srgb' }` for maximum speed, or
16
- * `{ metric: 'deltaE2000' }` for strictest perceptual accuracy.
24
+ * **Cross-palette — name from one palette, nearest in another:**
25
+ * identify('rebeccapurple', { palette: pantone, source: web }) → '267 C'
26
+ * identify('Razzmatazz', { palette: pantone, source: crayola }) → '213 C'
17
27
  *
18
- * Returns `null` if the input isn't a recognized color shape or the
19
- * colorspace name isn't one of `web` / `x11` / `pantone`.
28
+ * **Top-k ranked matches with ΔE distances:**
29
+ * identify('#ff0000', { palette: pantone, k: 3 })
30
+ * // [{ name: '172 C', value: '#fa4616', distance: 3.2 }, ...]
20
31
  *
21
- * Defaults: colorspace = 'web', metric = DEFAULT_METRICS[colorspace].
32
+ * The distance metric defaults to the target palette's `defaultMetric`
33
+ * (web/x11: `deltaE76`, pantone: `deltaE2000`, crayola: `deltaEok`).
34
+ * Override with `metric`.
35
+ *
36
+ * Returns `null` (or `[]` when `k` is set) if the input can't be parsed.
22
37
  */
23
- export declare function identify<C extends ColorspaceName = 'web'>(input: ColorInput, opts?: {
24
- colorspace?: C;
38
+ export declare function identify<P extends Palette = typeof web>(input: ColorInput | string, opts?: {
39
+ palette?: P;
40
+ source?: Palette;
41
+ metric?: DistanceMetric;
42
+ }): PaletteKey<P> | null;
43
+ export declare function identify<P extends Palette = typeof web>(input: ColorInput | string, opts: {
44
+ palette?: P;
45
+ source?: Palette;
25
46
  metric?: DistanceMetric;
26
- }): ColorNameByColorspace[C] | null;
47
+ k: number;
48
+ }): Array<IdentifyMatch<PaletteKey<P>>>;
27
49
  export {};
28
50
  //# 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":"AAIA,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AACxC,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,QAAQ,EAAE,OAAO,EAAQ,MAAM,YAAY,CAAC;AAEtF,yDAAyD;AACzD,KAAK,UAAU,CAAC,CAAC,SAAS,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAC;AAExE;;;;;GAKG;AACH,MAAM,MAAM,aAAa,CAAC,IAAI,SAAS,MAAM,IAAI;IAC/C,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B,CAAC;AAmBF;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,GAAG,EACrD,KAAK,EAAE,UAAU,GAAG,MAAM,EAC1B,IAAI,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,CAAC,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,cAAc,CAAA;CAAE,GAChE,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;AAGxB,wBAAgB,QAAQ,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,GAAG,EACrD,KAAK,EAAE,UAAU,GAAG,MAAM,EAC1B,IAAI,EAAE;IAAE,OAAO,CAAC,EAAE,CAAC,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,cAAc,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GAC1E,KAAK,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC","sourcesContent":["import { hexToRgba } from './conversions/hex.js';\nimport { toRgba } from './convert.js';\nimport { detectFormat } from './detectFormat.js';\nimport { getNameIndex, nearest, nearestAll } from './indexing.js';\nimport { web } from './palettes/web.js';\nimport type { ColorInput, DistanceMetric, HexColor, Palette, Rgba } 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 * One ranked nearest-match entry when `identify` is called with `k`.\n * `value` is the palette's stored hex for the match (useful for rendering\n * swatches). `distance` is in the selected metric's natural units — ΔE\n * for `deltaE*` metrics, channel-unit Euclidean for the others.\n */\nexport type IdentifyMatch<Name extends string> = {\n readonly name: Name;\n readonly value: HexColor;\n readonly distance: number;\n};\n\n/**\n * Parse a string input that may be a color literal or a palette name.\n * Returns the canonical Rgba, or `null` if nothing matches.\n */\nfunction parseInput(input: ColorInput | string, source?: Palette): Rgba | null {\n const detected = detectFormat(input as ColorInput);\n if (detected !== 'UNKNOWN') return toRgba(input as ColorInput, detected);\n if (source !== undefined && typeof input === 'string') {\n const canonical = getNameIndex(source).get(source.normalize(input));\n if (canonical === undefined) return null;\n const hex = (source.colors as Record<string, HexColor>)[canonical];\n if (hex === undefined) return null;\n return hexToRgba(hex);\n }\n return null;\n}\n\n/**\n * Identify the nearest-named color for any color input (or palette name\n * from another palette via `source`).\n *\n * **Simple case — color in, name out:**\n * identify('#ff0000') → 'red'\n * identify('#ff0000', { palette: pantone }) → '172 C'\n *\n * **Cross-palette — name from one palette, nearest in another:**\n * identify('rebeccapurple', { palette: pantone, source: web }) → '267 C'\n * identify('Razzmatazz', { palette: pantone, source: crayola }) → '213 C'\n *\n * **Top-k ranked matches with ΔE distances:**\n * identify('#ff0000', { palette: pantone, k: 3 })\n * // [{ name: '172 C', value: '#fa4616', distance: 3.2 }, ...]\n *\n * The distance metric defaults to the target palette's `defaultMetric`\n * (web/x11: `deltaE76`, pantone: `deltaE2000`, crayola: `deltaEok`).\n * Override with `metric`.\n *\n * Returns `null` (or `[]` when `k` is set) if the input can't be parsed.\n */\n\n// Overload 1: no `k` — single name (or null). Unchanged legacy shape.\nexport function identify<P extends Palette = typeof web>(\n input: ColorInput | string,\n opts?: { palette?: P; source?: Palette; metric?: DistanceMetric },\n): PaletteKey<P> | null;\n\n// Overload 2: `k` present — ranked Match[] with distances.\nexport function identify<P extends Palette = typeof web>(\n input: ColorInput | string,\n opts: { palette?: P; source?: Palette; metric?: DistanceMetric; k: number },\n): Array<IdentifyMatch<PaletteKey<P>>>;\n\n// Implementation.\nexport function identify(\n input: ColorInput | string,\n opts: { palette?: Palette; source?: Palette; metric?: DistanceMetric; k?: number } = {},\n): string | null | Array<IdentifyMatch<string>> {\n const palette = opts.palette ?? web;\n const metric = opts.metric ?? palette.defaultMetric;\n const rgba = parseInput(input, opts.source);\n\n if (opts.k !== undefined) {\n if (rgba === null) return [];\n const raw = nearestAll(rgba, palette, metric, opts.k);\n return raw.map((m) => {\n const value = (palette.colors as Record<string, HexColor>)[m.name];\n return { name: m.name, value: value as HexColor, distance: m.distance };\n });\n }\n\n if (rgba === null) return null;\n const name = nearest(rgba, palette, metric);\n return name || null;\n}\n"]}
package/dist/identify.js CHANGED
@@ -1,29 +1,44 @@
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 { hexToRgba } from './conversions/hex.js';
2
+ import { toRgba } from './convert.js';
3
+ import { detectFormat } from './detectFormat.js';
4
+ import { getNameIndex, nearest, nearestAll } from './indexing.js';
5
+ import { web } from './palettes/web.js';
5
6
  /**
6
- * Identify the nearest-named color for any color input.
7
- *
8
- * The distance metric defaults to the colorspace's recommended choice
9
- * (web/x11: `deltaE76`, pantone: `deltaE2000`). Override with the `metric`
10
- * option — e.g. `{ metric: 'euclidean-srgb' }` for maximum speed, or
11
- * `{ metric: 'deltaE2000' }` for strictest perceptual accuracy.
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`.
15
- *
16
- * Defaults: colorspace = 'web', metric = DEFAULT_METRICS[colorspace].
7
+ * Parse a string input that may be a color literal or a palette name.
8
+ * Returns the canonical Rgba, or `null` if nothing matches.
17
9
  */
10
+ function parseInput(input, source) {
11
+ const detected = detectFormat(input);
12
+ if (detected !== 'UNKNOWN')
13
+ return toRgba(input, detected);
14
+ if (source !== undefined && typeof input === 'string') {
15
+ const canonical = getNameIndex(source).get(source.normalize(input));
16
+ if (canonical === undefined)
17
+ return null;
18
+ const hex = source.colors[canonical];
19
+ if (hex === undefined)
20
+ return null;
21
+ return hexToRgba(hex);
22
+ }
23
+ return null;
24
+ }
25
+ // Implementation.
18
26
  export function identify(input, opts = {}) {
19
- const format = detectFormat(input);
20
- if (format === 'UNKNOWN')
21
- return null;
22
- const colorspace = (opts.colorspace ?? 'web');
23
- if (!COLORSPACE_NAMES.has(colorspace))
27
+ const palette = opts.palette ?? web;
28
+ const metric = opts.metric ?? palette.defaultMetric;
29
+ const rgba = parseInput(input, opts.source);
30
+ if (opts.k !== undefined) {
31
+ if (rgba === null)
32
+ return [];
33
+ const raw = nearestAll(rgba, palette, metric, opts.k);
34
+ return raw.map((m) => {
35
+ const value = palette.colors[m.name];
36
+ return { name: m.name, value: value, distance: m.distance };
37
+ });
38
+ }
39
+ if (rgba === null)
24
40
  return null;
25
- const rgba = toRgba(input, format);
26
- const name = nearest(rgba, COLORSPACES[colorspace], opts.metric ?? DEFAULT_METRICS[colorspace]);
41
+ const name = nearest(rgba, palette, metric);
27
42
  return name || null;
28
43
  }
29
44
  //# 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,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAkBxC;;;GAGG;AACH,SAAS,UAAU,CAAC,KAA0B,EAAE,MAAgB;IAC9D,MAAM,QAAQ,GAAG,YAAY,CAAC,KAAmB,CAAC,CAAC;IACnD,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC,KAAmB,EAAE,QAAQ,CAAC,CAAC;IACzE,IAAI,MAAM,KAAK,SAAS,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACtD,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;QACpE,IAAI,SAAS,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC;QACzC,MAAM,GAAG,GAAI,MAAM,CAAC,MAAmC,CAAC,SAAS,CAAC,CAAC;QACnE,IAAI,GAAG,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC;QACnC,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAqCD,kBAAkB;AAClB,MAAM,UAAU,QAAQ,CACtB,KAA0B,EAC1B,IAAI,GAAiF,EAAE;IAEvF,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,GAAG,CAAC;IACpC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,aAAa,CAAC;IACpD,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IAE5C,IAAI,IAAI,CAAC,CAAC,KAAK,SAAS,EAAE,CAAC;QACzB,IAAI,IAAI,KAAK,IAAI;YAAE,OAAO,EAAE,CAAC;QAC7B,MAAM,GAAG,GAAG,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;QACtD,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YACnB,MAAM,KAAK,GAAI,OAAO,CAAC,MAAmC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YACnE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,KAAiB,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC1E,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC/B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAC5C,OAAO,IAAI,IAAI,IAAI,CAAC;AACtB,CAAC","sourcesContent":["import { hexToRgba } from './conversions/hex.js';\nimport { toRgba } from './convert.js';\nimport { detectFormat } from './detectFormat.js';\nimport { getNameIndex, nearest, nearestAll } from './indexing.js';\nimport { web } from './palettes/web.js';\nimport type { ColorInput, DistanceMetric, HexColor, Palette, Rgba } 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 * One ranked nearest-match entry when `identify` is called with `k`.\n * `value` is the palette's stored hex for the match (useful for rendering\n * swatches). `distance` is in the selected metric's natural units — ΔE\n * for `deltaE*` metrics, channel-unit Euclidean for the others.\n */\nexport type IdentifyMatch<Name extends string> = {\n readonly name: Name;\n readonly value: HexColor;\n readonly distance: number;\n};\n\n/**\n * Parse a string input that may be a color literal or a palette name.\n * Returns the canonical Rgba, or `null` if nothing matches.\n */\nfunction parseInput(input: ColorInput | string, source?: Palette): Rgba | null {\n const detected = detectFormat(input as ColorInput);\n if (detected !== 'UNKNOWN') return toRgba(input as ColorInput, detected);\n if (source !== undefined && typeof input === 'string') {\n const canonical = getNameIndex(source).get(source.normalize(input));\n if (canonical === undefined) return null;\n const hex = (source.colors as Record<string, HexColor>)[canonical];\n if (hex === undefined) return null;\n return hexToRgba(hex);\n }\n return null;\n}\n\n/**\n * Identify the nearest-named color for any color input (or palette name\n * from another palette via `source`).\n *\n * **Simple case — color in, name out:**\n * identify('#ff0000') → 'red'\n * identify('#ff0000', { palette: pantone }) → '172 C'\n *\n * **Cross-palette — name from one palette, nearest in another:**\n * identify('rebeccapurple', { palette: pantone, source: web }) → '267 C'\n * identify('Razzmatazz', { palette: pantone, source: crayola }) → '213 C'\n *\n * **Top-k ranked matches with ΔE distances:**\n * identify('#ff0000', { palette: pantone, k: 3 })\n * // [{ name: '172 C', value: '#fa4616', distance: 3.2 }, ...]\n *\n * The distance metric defaults to the target palette's `defaultMetric`\n * (web/x11: `deltaE76`, pantone: `deltaE2000`, crayola: `deltaEok`).\n * Override with `metric`.\n *\n * Returns `null` (or `[]` when `k` is set) if the input can't be parsed.\n */\n\n// Overload 1: no `k` — single name (or null). Unchanged legacy shape.\nexport function identify<P extends Palette = typeof web>(\n input: ColorInput | string,\n opts?: { palette?: P; source?: Palette; metric?: DistanceMetric },\n): PaletteKey<P> | null;\n\n// Overload 2: `k` present — ranked Match[] with distances.\nexport function identify<P extends Palette = typeof web>(\n input: ColorInput | string,\n opts: { palette?: P; source?: Palette; metric?: DistanceMetric; k: number },\n): Array<IdentifyMatch<PaletteKey<P>>>;\n\n// Implementation.\nexport function identify(\n input: ColorInput | string,\n opts: { palette?: Palette; source?: Palette; metric?: DistanceMetric; k?: number } = {},\n): string | null | Array<IdentifyMatch<string>> {\n const palette = opts.palette ?? web;\n const metric = opts.metric ?? palette.defaultMetric;\n const rgba = parseInput(input, opts.source);\n\n if (opts.k !== undefined) {\n if (rgba === null) return [];\n const raw = nearestAll(rgba, palette, metric, opts.k);\n return raw.map((m) => {\n const value = (palette.colors as Record<string, HexColor>)[m.name];\n return { name: m.name, value: value as HexColor, distance: m.distance };\n });\n }\n\n if (rgba === null) return null;\n const name = nearest(rgba, palette, metric);\n return name || null;\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -1,15 +1,16 @@
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 { type IdentifyMatch, identify } from './identify.js';
9
+ export { type CrayolaColorName, crayola } from './palettes/crayola.js';
10
+ export { type PantoneColorName, pantone } from './palettes/pantone.js';
11
+ export { type WebColorName, web } from './palettes/web.js';
12
+ export { type X11ColorName, x11 } from './palettes/x11.js';
13
+ export { type ResolveMatch, resolve } from './resolve.js';
14
+ 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';
15
+ export { COLOR_FORMATS } from './types.js';
15
16
  //# 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,KAAK,aAAa,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,KAAK,gBAAgB,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACvE,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,KAAK,YAAY,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC1D,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 { type IdentifyMatch, identify } from './identify.js';\nexport { type CrayolaColorName, crayola } from './palettes/crayola.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 { type ResolveMatch, 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,17 @@
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 { crayola } from './palettes/crayola.js';
12
+ export { pantone } from './palettes/pantone.js';
13
+ export { web } from './palettes/web.js';
14
+ export { x11 } from './palettes/x11.js';
15
+ export { resolve } from './resolve.js';
16
+ export { COLOR_FORMATS } from './types.js';
16
17
  //# 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,EAAsB,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAyB,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACvE,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,EAAqB,OAAO,EAAE,MAAM,cAAc,CAAC;AAwB1D,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 { type IdentifyMatch, identify } from './identify.js';\nexport { type CrayolaColorName, crayola } from './palettes/crayola.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 { type ResolveMatch, 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,68 @@
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.
48
+ */
49
+ export declare function nearest(target: Rgba, space: AnyPalette, metric: DistanceMetric): string;
50
+ /**
51
+ * Rank every palette entry by distance to `target`. Returns an array of
52
+ * `{ name, distance }` sorted ascending (nearest first). Optional `k`
53
+ * truncates to the top-k matches.
54
+ *
55
+ * Distances are **real** metric values (not squared) — ΔE76 / ΔE94 /
56
+ * ΔE2000 / ΔE-OK are in standard ΔE units (≈1 = just-noticeable for
57
+ * most of the gamut on ΔE76/2000); `euclidean-srgb` / `euclidean-linear`
58
+ * are in their respective channel-unit Euclidean distances.
59
+ *
60
+ * Cost: O(n log n) for the sort vs O(n) for `nearest` — acceptable for
61
+ * top-K queries. For strict "what's the single nearest?" stay on
62
+ * `nearest` / `identify`.
42
63
  */
43
- export declare function nearest(target: Rgba, space: Colorspace, metric?: DistanceMetric): string;
64
+ export declare function nearestAll(target: Rgba, space: AnyPalette, metric: DistanceMetric, k?: number): Array<{
65
+ name: string;
66
+ distance: number;
67
+ }>;
44
68
  //# 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;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,IAAI,EACZ,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,cAAc,EACtB,CAAC,CAAC,EAAE,MAAM,GACT,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA2C3C","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\n/**\n * Rank every palette entry by distance to `target`. Returns an array of\n * `{ name, distance }` sorted ascending (nearest first). Optional `k`\n * truncates to the top-k matches.\n *\n * Distances are **real** metric values (not squared) — ΔE76 / ΔE94 /\n * ΔE2000 / ΔE-OK are in standard ΔE units (≈1 = just-noticeable for\n * most of the gamut on ΔE76/2000); `euclidean-srgb` / `euclidean-linear`\n * are in their respective channel-unit Euclidean distances.\n *\n * Cost: O(n log n) for the sort vs O(n) for `nearest` — acceptable for\n * top-K queries. For strict \"what's the single nearest?\" stay on\n * `nearest` / `identify`.\n */\nexport function nearestAll(\n target: Rgba,\n space: AnyPalette,\n metric: DistanceMetric,\n k?: number,\n): Array<{ name: string; distance: number }> {\n const ranked: Array<[string, number]> = [];\n\n if (metric === 'euclidean-srgb') {\n for (const [name, candidate] of getRgbaIndex(space)) {\n ranked.push([name, Math.sqrt(squaredDistanceRgb(target, candidate))]);\n }\n } else if (metric === 'euclidean-linear') {\n const tgt = rgbaToLinearRgb(target);\n for (const [name, candidate] of getLinearIndex(space)) {\n ranked.push([name, Math.sqrt(squaredDistanceTriple(tgt, candidate))]);\n }\n } else if (metric === 'deltaEok') {\n const tgt = rgbaToOklab(target);\n for (const [name, candidate] of getOklabIndex(space)) {\n ranked.push([name, Math.sqrt(deltaEokSquared(tgt, candidate))]);\n }\n } else {\n const tgt = rgbaToLab(target);\n const labIndex = getLabIndex(space);\n if (metric === 'deltaE76') {\n for (const [name, candidate] of labIndex) {\n ranked.push([name, Math.sqrt(deltaE76Squared(tgt, candidate))]);\n }\n } else if (metric === 'deltaE94') {\n for (const [name, candidate] of labIndex) {\n ranked.push([name, deltaE94(tgt, candidate)]);\n }\n } else {\n for (const [name, candidate] of labIndex) {\n ranked.push([name, deltaE2000(tgt, candidate)]);\n }\n }\n }\n\n ranked.sort((a, b) => a[1] - b[1]);\n const limit = k === undefined ? ranked.length : Math.min(Math.max(k, 0), ranked.length);\n const result: Array<{ name: string; distance: number }> = new Array(limit);\n for (let i = 0; i < limit; i++) {\n const entry = ranked[i];\n if (entry !== undefined) result[i] = { name: entry[0], distance: entry[1] };\n }\n return result;\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') {
@@ -137,4 +164,66 @@ export function nearest(target, space, metric = 'euclidean-srgb') {
137
164
  return argminBy(labIndex, labTarget, deltaE94);
138
165
  return argminBy(labIndex, labTarget, deltaE2000);
139
166
  }
167
+ /**
168
+ * Rank every palette entry by distance to `target`. Returns an array of
169
+ * `{ name, distance }` sorted ascending (nearest first). Optional `k`
170
+ * truncates to the top-k matches.
171
+ *
172
+ * Distances are **real** metric values (not squared) — ΔE76 / ΔE94 /
173
+ * ΔE2000 / ΔE-OK are in standard ΔE units (≈1 = just-noticeable for
174
+ * most of the gamut on ΔE76/2000); `euclidean-srgb` / `euclidean-linear`
175
+ * are in their respective channel-unit Euclidean distances.
176
+ *
177
+ * Cost: O(n log n) for the sort vs O(n) for `nearest` — acceptable for
178
+ * top-K queries. For strict "what's the single nearest?" stay on
179
+ * `nearest` / `identify`.
180
+ */
181
+ export function nearestAll(target, space, metric, k) {
182
+ const ranked = [];
183
+ if (metric === 'euclidean-srgb') {
184
+ for (const [name, candidate] of getRgbaIndex(space)) {
185
+ ranked.push([name, Math.sqrt(squaredDistanceRgb(target, candidate))]);
186
+ }
187
+ }
188
+ else if (metric === 'euclidean-linear') {
189
+ const tgt = rgbaToLinearRgb(target);
190
+ for (const [name, candidate] of getLinearIndex(space)) {
191
+ ranked.push([name, Math.sqrt(squaredDistanceTriple(tgt, candidate))]);
192
+ }
193
+ }
194
+ else if (metric === 'deltaEok') {
195
+ const tgt = rgbaToOklab(target);
196
+ for (const [name, candidate] of getOklabIndex(space)) {
197
+ ranked.push([name, Math.sqrt(deltaEokSquared(tgt, candidate))]);
198
+ }
199
+ }
200
+ else {
201
+ const tgt = rgbaToLab(target);
202
+ const labIndex = getLabIndex(space);
203
+ if (metric === 'deltaE76') {
204
+ for (const [name, candidate] of labIndex) {
205
+ ranked.push([name, Math.sqrt(deltaE76Squared(tgt, candidate))]);
206
+ }
207
+ }
208
+ else if (metric === 'deltaE94') {
209
+ for (const [name, candidate] of labIndex) {
210
+ ranked.push([name, deltaE94(tgt, candidate)]);
211
+ }
212
+ }
213
+ else {
214
+ for (const [name, candidate] of labIndex) {
215
+ ranked.push([name, deltaE2000(tgt, candidate)]);
216
+ }
217
+ }
218
+ }
219
+ ranked.sort((a, b) => a[1] - b[1]);
220
+ const limit = k === undefined ? ranked.length : Math.min(Math.max(k, 0), ranked.length);
221
+ const result = new Array(limit);
222
+ for (let i = 0; i < limit; i++) {
223
+ const entry = ranked[i];
224
+ if (entry !== undefined)
225
+ result[i] = { name: entry[0], distance: entry[1] };
226
+ }
227
+ return result;
228
+ }
140
229
  //# sourceMappingURL=indexing.js.map