color-value-tools 1.1.0 → 1.1.2

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.
package/dist/cjs/index.js CHANGED
@@ -2,10 +2,19 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.isCssVariable = isCssVariable;
4
4
  exports.isHexColor = isHexColor;
5
+ exports.isOklchColor = isOklchColor;
6
+ exports.isColorFunction = isColorFunction;
5
7
  exports.isRgbColor = isRgbColor;
6
8
  exports.isHslColor = isHslColor;
7
9
  exports.getColorType = getColorType;
8
10
  exports.extractCssVariableName = extractCssVariableName;
11
+ exports.parseCssVar = parseCssVar;
12
+ exports.parseOklchString = parseOklchString;
13
+ exports.parseColorFn = parseColorFn;
14
+ exports.rgbToDisplayP3 = rgbToDisplayP3;
15
+ exports.displayP3ToRgb = displayP3ToRgb;
16
+ exports.toDisplayP3Hex = toDisplayP3Hex;
17
+ exports.shortHexToRgba = shortHexToRgba;
9
18
  exports.normalizeHex = normalizeHex;
10
19
  exports.hexToRgb = hexToRgb;
11
20
  exports.hexToRgba = hexToRgba;
@@ -67,6 +76,28 @@ exports.toHslString = toHslString;
67
76
  exports.colorDeltaE = colorDeltaE;
68
77
  exports.randomColor = randomColor;
69
78
  exports.toNearestNamedColor = toNearestNamedColor;
79
+ exports.toOklchString = toOklchString;
80
+ exports.toColorP3String = toColorP3String;
81
+ exports.interpolateColors = interpolateColors;
82
+ exports.createColorScale = createColorScale;
83
+ exports.midpointColor = midpointColor;
84
+ exports.tints = tints;
85
+ exports.shades = shades;
86
+ exports.tones = tones;
87
+ exports.simulateProtanopia = simulateProtanopia;
88
+ exports.simulateDeuteranopia = simulateDeuteranopia;
89
+ exports.simulateTritanopia = simulateTritanopia;
90
+ exports.simulateColorBlindness = simulateColorBlindness;
91
+ exports.isReadableOnBackground = isReadableOnBackground;
92
+ exports.bestContrastPalette = bestContrastPalette;
93
+ exports.clearColorCache = clearColorCache;
94
+ exports.getCacheStats = getCacheStats;
95
+ exports.enableCache = enableCache;
96
+ exports.disableCache = disableCache;
97
+ exports.normalizeColorCached = normalizeColorCached;
98
+ exports.generateGradientColors = generateGradientColors;
99
+ exports.generateTints = generateTints;
100
+ exports.generateShades = generateShades;
70
101
  const NAMED_COLORS = {
71
102
  aliceblue: '#f0f8ff', antiquewhite: '#faebd7', aqua: '#00ffff', aquamarine: '#7fffd4', azure: '#f0ffff',
72
103
  beige: '#f5f5dc', bisque: '#ffe4c4', black: '#000000', blanchedalmond: '#ffebcd', blue: '#0000ff',
@@ -107,7 +138,13 @@ function isCssVariable(value) {
107
138
  }
108
139
  function isHexColor(value) {
109
140
  const trimmed = value.trim();
110
- return /^#?([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/.test(trimmed);
141
+ return /^#?([0-9A-Fa-f]{3}|[0-9A-Fa-f]{4}|[0-9A-Fa-f]{6})$/.test(trimmed);
142
+ }
143
+ function isOklchColor(value) {
144
+ return /^\s*oklcha?\s*\(/i.test(value.trim());
145
+ }
146
+ function isColorFunction(value) {
147
+ return /^\s*color\s*\(\s*(display-p3|srgb|srgb-linear)\s/i.test(value.trim());
111
148
  }
112
149
  function isRgbColor(value) {
113
150
  const trimmed = value.trim().toLowerCase();
@@ -118,7 +155,8 @@ function isHslColor(value) {
118
155
  return trimmed.startsWith('hsl(') || trimmed.startsWith('hsla(');
119
156
  }
120
157
  function getColorType(value) {
121
- const trimmed = value.trim().toLowerCase();
158
+ const trimmed = value.trim();
159
+ const lower = trimmed.toLowerCase();
122
160
  if (isCssVariable(trimmed))
123
161
  return 'css-var';
124
162
  if (isHexColor(trimmed))
@@ -127,13 +165,116 @@ function getColorType(value) {
127
165
  return 'rgb';
128
166
  if (isHslColor(trimmed))
129
167
  return 'hsl';
130
- if (trimmed in NAMED_COLORS)
168
+ if (isOklchColor(trimmed))
169
+ return 'oklch';
170
+ if (isColorFunction(trimmed))
171
+ return 'color';
172
+ if (lower in NAMED_COLORS)
131
173
  return 'named';
132
174
  return 'unknown';
133
175
  }
134
176
  function extractCssVariableName(value) {
135
- const match = value.match(/var\((--[^)]+)\)/);
136
- return (match === null || match === void 0 ? void 0 : match[1]) || value;
177
+ var _a;
178
+ const match = value.match(/var\(\s*(--[^,)]+)/);
179
+ return ((_a = match === null || match === void 0 ? void 0 : match[1]) === null || _a === void 0 ? void 0 : _a.trim()) || value;
180
+ }
181
+ function parseCssVar(value) {
182
+ var _a;
183
+ const m = value.trim().match(/^var\(\s*(--[^,)]+)(?:,\s*([\s\S]+?))?\s*\)$/);
184
+ if (!m)
185
+ return null;
186
+ return { variableName: m[1].trim(), fallback: (_a = m[2]) === null || _a === void 0 ? void 0 : _a.trim() };
187
+ }
188
+ // ─── Section 1.1 — OKLCH / color() CSS string parsing ────────────────────────
189
+ function parseOklchString(str) {
190
+ const m = str.trim().match(/oklcha?\(\s*([\d.]+%?)\s+([\d.]+)\s+([\d.]+)(?:\s*\/\s*([\d.]+))?\s*\)/i);
191
+ if (!m)
192
+ return null;
193
+ let L = parseFloat(m[1]);
194
+ if (m[1].endsWith('%'))
195
+ L = L / 100;
196
+ const C = parseFloat(m[2]);
197
+ const H = parseFloat(m[3]);
198
+ const alpha = m[4] !== undefined ? parseFloat(m[4]) : 1;
199
+ return { L, C, H, alpha };
200
+ }
201
+ function parseColorFn(str) {
202
+ const m = str.trim().match(/color\(\s*([\w-]+)\s+([\d.]+%?)\s+([\d.]+%?)\s+([\d.]+%?)(?:\s*\/\s*([\d.]+))?\s*\)/i);
203
+ if (!m)
204
+ return null;
205
+ const space = m[1].toLowerCase();
206
+ const parseVal = (v) => v.endsWith('%') ? parseFloat(v) / 100 : parseFloat(v);
207
+ const r = parseVal(m[2]);
208
+ const g = parseVal(m[3]);
209
+ const b = parseVal(m[4]);
210
+ const alpha = m[5] !== undefined ? parseFloat(m[5]) : 1;
211
+ return { space, r, g, b, alpha };
212
+ }
213
+ // ─── Section 1.2 — Display P3 ────────────────────────────────────────────────
214
+ function rgbToDisplayP3(rgb) {
215
+ // Remove sRGB gamma (linearize)
216
+ const rl = srgbChanToLinear(rgb.r / 255);
217
+ const gl = srgbChanToLinear(rgb.g / 255);
218
+ const bl = srgbChanToLinear(rgb.b / 255);
219
+ // sRGB-linear → P3-linear matrix
220
+ const pr = 0.8226 * rl + 0.1774 * gl + 0.0000 * bl;
221
+ const pg = 0.0332 * rl + 0.9669 * gl + 0.0000 * bl;
222
+ const pb = 0.0171 * rl + 0.0724 * gl + 0.9103 * bl;
223
+ // Apply P3 gamma (same as sRGB)
224
+ return {
225
+ r: linearChanToSrgb(pr),
226
+ g: linearChanToSrgb(pg),
227
+ b: linearChanToSrgb(pb),
228
+ };
229
+ }
230
+ function displayP3ToRgb(p3) {
231
+ // Remove P3 gamma
232
+ const rl = srgbChanToLinear(p3.r);
233
+ const gl = srgbChanToLinear(p3.g);
234
+ const bl = srgbChanToLinear(p3.b);
235
+ // P3-linear → sRGB-linear inverse matrix
236
+ const sr = 1.2247 * rl + (-0.2247) * gl + 0.0000 * bl;
237
+ const sg = (-0.0421) * rl + 1.0432 * gl + 0.0000 * bl;
238
+ const sb = (-0.0197) * rl + (-0.0786) * gl + 1.0983 * bl;
239
+ // Apply sRGB gamma
240
+ return {
241
+ r: Math.round(Math.max(0, Math.min(255, linearChanToSrgb(sr)))),
242
+ g: Math.round(Math.max(0, Math.min(255, linearChanToSrgb(sg)))),
243
+ b: Math.round(Math.max(0, Math.min(255, linearChanToSrgb(sb)))),
244
+ };
245
+ }
246
+ // Helpers for Display P3 (operate on 0-1 values)
247
+ function srgbChanToLinear(v) {
248
+ return v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
249
+ }
250
+ function linearChanToSrgb(v) {
251
+ const t = v <= 0.0031308 ? 12.92 * v : 1.055 * Math.pow(v, 1 / 2.4) - 0.055;
252
+ return Math.max(0, Math.min(1, t));
253
+ }
254
+ function toDisplayP3Hex(color) {
255
+ const n = normalizeColorRaw(color);
256
+ const p3 = rgbToDisplayP3({ r: n.r, g: n.g, b: n.b });
257
+ const back = displayP3ToRgb(p3);
258
+ return rgbToHex(back);
259
+ }
260
+ // Internal raw normalizer (no cache, no circular dep issues)
261
+ function normalizeColorRaw(color) {
262
+ var _a, _b, _c, _d;
263
+ const n = normalizeColor(color);
264
+ return { r: (_a = n.r) !== null && _a !== void 0 ? _a : 0, g: (_b = n.g) !== null && _b !== void 0 ? _b : 0, b: (_c = n.b) !== null && _c !== void 0 ? _c : 0, a: (_d = n.a) !== null && _d !== void 0 ? _d : 1 };
265
+ }
266
+ // ─── Section 1.3 — Short RGBA hex #RGBA ──────────────────────────────────────
267
+ function shortHexToRgba(hex) {
268
+ let h = hex.trim();
269
+ if (!h.startsWith('#'))
270
+ h = `#${h}`;
271
+ if (!/^#[0-9a-f]{4}$/i.test(h))
272
+ return null;
273
+ const r = parseInt(h[1] + h[1], 16);
274
+ const g = parseInt(h[2] + h[2], 16);
275
+ const b = parseInt(h[3] + h[3], 16);
276
+ const a = parseInt(h[4] + h[4], 16) / 255;
277
+ return { r, g, b, a };
137
278
  }
138
279
  function normalizeHex(hex) {
139
280
  let cleanHex = hex.trim();
@@ -563,6 +704,52 @@ function normalizeColor(input) {
563
704
  }
564
705
  if (str === 'transparent')
565
706
  return { type: 'named', hex: '#000000', r: 0, g: 0, b: 0, a: 0 };
707
+ // 4-digit #RGBA hex
708
+ if (/^#[0-9a-f]{4}$/i.test(str)) {
709
+ const rgba = shortHexToRgba(str);
710
+ if (rgba) {
711
+ const { r, g, b, a } = rgba;
712
+ const hex = rgbToHex({ r, g, b });
713
+ const [h, s, l] = rgbToHsl({ r, g, b });
714
+ const [, , vv] = rgbToHsv({ r, g, b });
715
+ return { type: 'hex', hex, r, g, b, a, h, s, l, v: vv };
716
+ }
717
+ }
718
+ // oklch / oklcha
719
+ if (isOklchColor(str)) {
720
+ const parsed = parseOklchString(str);
721
+ if (parsed) {
722
+ const { r, g, b } = oklchToRgb({ L: parsed.L, C: parsed.C, H: parsed.H });
723
+ const hex = rgbToHex({ r, g, b });
724
+ const [h, s, l] = rgbToHsl({ r, g, b });
725
+ const [, , vv] = rgbToHsv({ r, g, b });
726
+ return { type: 'oklch', hex, r, g, b, a: parsed.alpha, h, s, l, v: vv };
727
+ }
728
+ }
729
+ // color(display-p3 ...) / color(srgb ...)
730
+ if (isColorFunction(str)) {
731
+ const parsed = parseColorFn(str);
732
+ if (parsed) {
733
+ const { r: p3r, g: p3g, b: p3b, alpha } = parsed;
734
+ // Convert from p3/srgb (0-1) to sRGB 0-255
735
+ let r, g, b;
736
+ if (parsed.space === 'display-p3') {
737
+ const srgb = displayP3ToRgb({ r: p3r, g: p3g, b: p3b });
738
+ r = srgb.r;
739
+ g = srgb.g;
740
+ b = srgb.b;
741
+ }
742
+ else {
743
+ r = Math.round(Math.max(0, Math.min(255, p3r * 255)));
744
+ g = Math.round(Math.max(0, Math.min(255, p3g * 255)));
745
+ b = Math.round(Math.max(0, Math.min(255, p3b * 255)));
746
+ }
747
+ const hex = rgbToHex({ r, g, b });
748
+ const [h, s, l] = rgbToHsl({ r, g, b });
749
+ const [, , vv] = rgbToHsv({ r, g, b });
750
+ return { type: 'color', hex, r, g, b, a: alpha, h, s, l, v: vv };
751
+ }
752
+ }
566
753
  return { type: 'unknown' };
567
754
  }
568
755
  function complement(color) {
@@ -613,47 +800,109 @@ function monochromatic(color, steps = 5) {
613
800
  }
614
801
  return result;
615
802
  }
803
+ function _lerpHue(h1, h2, t, mode = 'shorter') {
804
+ let d = h2 - h1;
805
+ if (mode === 'shorter') {
806
+ if (d > 180)
807
+ d -= 360;
808
+ else if (d < -180)
809
+ d += 360;
810
+ }
811
+ else if (mode === 'longer') {
812
+ if (d > 0 && d < 180)
813
+ d -= 360;
814
+ else if (d < 0 && d > -180)
815
+ d += 360;
816
+ }
817
+ else if (mode === 'increasing') {
818
+ if (d < 0)
819
+ d += 360;
820
+ }
821
+ else if (mode === 'decreasing') {
822
+ if (d > 0)
823
+ d -= 360;
824
+ }
825
+ return h1 + d * t;
826
+ }
827
+ function _formatMixResult(r, g, b, a, out) {
828
+ if (out === 'hex')
829
+ return rgbToHex({ r: Math.round(r), g: Math.round(g), b: Math.round(b) });
830
+ if (out === 'rgba')
831
+ return rgbToRgbaString({ r: Math.round(r), g: Math.round(g), b: Math.round(b) }, a);
832
+ if (out === 'rgb')
833
+ return `rgb(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)})`;
834
+ if (out === 'hsl') {
835
+ const [hh, ss, ll] = rgbToHsl({ r: Math.round(r), g: Math.round(g), b: Math.round(b) });
836
+ return `hsl(${hh}, ${ss}%, ${ll}%)`;
837
+ }
838
+ return rgbToHex({ r: Math.round(r), g: Math.round(g), b: Math.round(b) });
839
+ }
616
840
  function mixColors(c1, c2, t, opts) {
617
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
841
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35;
618
842
  const o1 = normalizeColor(c1);
619
843
  const o2 = normalizeColor(c2);
620
844
  t = Math.max(0, Math.min(1, t));
621
845
  const mode = (opts === null || opts === void 0 ? void 0 : opts.mode) || 'rgb';
622
846
  const out = (opts === null || opts === void 0 ? void 0 : opts.format) || 'hex';
847
+ const hueMode = (_a = opts === null || opts === void 0 ? void 0 : opts.hueInterpolation) !== null && _a !== void 0 ? _a : 'shorter';
623
848
  let r = 0, g = 0, b = 0, a = 1;
624
849
  if (mode === 'hsl') {
625
- const h1 = (_a = o1.h) !== null && _a !== void 0 ? _a : rgbToHsl({ r: (_b = o1.r) !== null && _b !== void 0 ? _b : 0, g: (_c = o1.g) !== null && _c !== void 0 ? _c : 0, b: (_d = o1.b) !== null && _d !== void 0 ? _d : 0 })[0];
626
- const s1 = (_e = o1.s) !== null && _e !== void 0 ? _e : 0;
627
- const l1 = (_f = o1.l) !== null && _f !== void 0 ? _f : 0;
628
- const h2 = (_g = o2.h) !== null && _g !== void 0 ? _g : rgbToHsl({ r: (_h = o2.r) !== null && _h !== void 0 ? _h : 0, g: (_j = o2.g) !== null && _j !== void 0 ? _j : 0, b: (_k = o2.b) !== null && _k !== void 0 ? _k : 0 })[0];
629
- const s2 = (_l = o2.s) !== null && _l !== void 0 ? _l : 0;
630
- const l2 = (_m = o2.l) !== null && _m !== void 0 ? _m : 0;
631
- const ih = h1 + (((h2 - h1 + 540) % 360) - 180) * t;
632
- const is = s1 + (s2 - s1) * t;
633
- const il = l1 + (l2 - l1) * t;
634
- const rgb = hslToRgb(ih, is, il);
850
+ const h1 = (_b = o1.h) !== null && _b !== void 0 ? _b : rgbToHsl({ r: (_c = o1.r) !== null && _c !== void 0 ? _c : 0, g: (_d = o1.g) !== null && _d !== void 0 ? _d : 0, b: (_e = o1.b) !== null && _e !== void 0 ? _e : 0 })[0];
851
+ const s1 = (_f = o1.s) !== null && _f !== void 0 ? _f : 0;
852
+ const l1 = (_g = o1.l) !== null && _g !== void 0 ? _g : 0;
853
+ const h2 = (_h = o2.h) !== null && _h !== void 0 ? _h : rgbToHsl({ r: (_j = o2.r) !== null && _j !== void 0 ? _j : 0, g: (_k = o2.g) !== null && _k !== void 0 ? _k : 0, b: (_l = o2.b) !== null && _l !== void 0 ? _l : 0 })[0];
854
+ const s2 = (_m = o2.s) !== null && _m !== void 0 ? _m : 0;
855
+ const l2 = (_o = o2.l) !== null && _o !== void 0 ? _o : 0;
856
+ const ih = _lerpHue(h1, h2, t, hueMode);
857
+ const rgb = hslToRgb(ih, s1 + (s2 - s1) * t, l1 + (l2 - l1) * t);
635
858
  r = rgb.r;
636
859
  g = rgb.g;
637
860
  b = rgb.b;
638
- a = ((_o = o1.a) !== null && _o !== void 0 ? _o : 1) + (((_p = o2.a) !== null && _p !== void 0 ? _p : 1) - ((_q = o1.a) !== null && _q !== void 0 ? _q : 1)) * t;
861
+ a = ((_p = o1.a) !== null && _p !== void 0 ? _p : 1) + (((_q = o2.a) !== null && _q !== void 0 ? _q : 1) - ((_r = o1.a) !== null && _r !== void 0 ? _r : 1)) * t;
639
862
  }
640
- else {
641
- r = (((_r = o1.r) !== null && _r !== void 0 ? _r : 0) * (1 - t)) + (((_s = o2.r) !== null && _s !== void 0 ? _s : 0) * t);
642
- g = (((_t = o1.g) !== null && _t !== void 0 ? _t : 0) * (1 - t)) + (((_u = o2.g) !== null && _u !== void 0 ? _u : 0) * t);
643
- b = (((_v = o1.b) !== null && _v !== void 0 ? _v : 0) * (1 - t)) + (((_w = o2.b) !== null && _w !== void 0 ? _w : 0) * t);
644
- a = ((_x = o1.a) !== null && _x !== void 0 ? _x : 1) * (1 - t) + ((_y = o2.a) !== null && _y !== void 0 ? _y : 1) * t;
863
+ else if (mode === 'lab') {
864
+ const lab1 = rgbToLab({ r: (_s = o1.r) !== null && _s !== void 0 ? _s : 0, g: (_t = o1.g) !== null && _t !== void 0 ? _t : 0, b: (_u = o1.b) !== null && _u !== void 0 ? _u : 0 });
865
+ const lab2 = rgbToLab({ r: (_v = o2.r) !== null && _v !== void 0 ? _v : 0, g: (_w = o2.g) !== null && _w !== void 0 ? _w : 0, b: (_x = o2.b) !== null && _x !== void 0 ? _x : 0 });
866
+ const mixed = labToRgb({ L: lab1.L + (lab2.L - lab1.L) * t, a: lab1.a + (lab2.a - lab1.a) * t, b: lab1.b + (lab2.b - lab1.b) * t });
867
+ r = mixed.r;
868
+ g = mixed.g;
869
+ b = mixed.b;
870
+ a = ((_y = o1.a) !== null && _y !== void 0 ? _y : 1) + (((_z = o2.a) !== null && _z !== void 0 ? _z : 1) - ((_0 = o1.a) !== null && _0 !== void 0 ? _0 : 1)) * t;
645
871
  }
646
- if (out === 'hex')
647
- return rgbToHex({ r: Math.round(r), g: Math.round(g), b: Math.round(b) });
648
- if (out === 'rgba')
649
- return rgbToRgbaString({ r: Math.round(r), g: Math.round(g), b: Math.round(b) }, a);
650
- if (out === 'rgb')
651
- return `rgb(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)})`;
652
- if (out === 'hsl') {
653
- const [hh, ss, ll] = rgbToHsl({ r: Math.round(r), g: Math.round(g), b: Math.round(b) });
654
- return `hsl(${hh}, ${ss}%, ${ll}%)`;
872
+ else if (mode === 'lch') {
873
+ const lch1 = rgbToLch({ r: (_1 = o1.r) !== null && _1 !== void 0 ? _1 : 0, g: (_2 = o1.g) !== null && _2 !== void 0 ? _2 : 0, b: (_3 = o1.b) !== null && _3 !== void 0 ? _3 : 0 });
874
+ const lch2 = rgbToLch({ r: (_4 = o2.r) !== null && _4 !== void 0 ? _4 : 0, g: (_5 = o2.g) !== null && _5 !== void 0 ? _5 : 0, b: (_6 = o2.b) !== null && _6 !== void 0 ? _6 : 0 });
875
+ const mixed = lchToRgb({ L: lch1.L + (lch2.L - lch1.L) * t, C: lch1.C + (lch2.C - lch1.C) * t, H: _lerpHue(lch1.H, lch2.H, t, hueMode) });
876
+ r = mixed.r;
877
+ g = mixed.g;
878
+ b = mixed.b;
879
+ a = ((_7 = o1.a) !== null && _7 !== void 0 ? _7 : 1) + (((_8 = o2.a) !== null && _8 !== void 0 ? _8 : 1) - ((_9 = o1.a) !== null && _9 !== void 0 ? _9 : 1)) * t;
655
880
  }
656
- return rgbToHex({ r: Math.round(r), g: Math.round(g), b: Math.round(b) });
881
+ else if (mode === 'oklab') {
882
+ const ok1 = rgbToOklab({ r: (_10 = o1.r) !== null && _10 !== void 0 ? _10 : 0, g: (_11 = o1.g) !== null && _11 !== void 0 ? _11 : 0, b: (_12 = o1.b) !== null && _12 !== void 0 ? _12 : 0 });
883
+ const ok2 = rgbToOklab({ r: (_13 = o2.r) !== null && _13 !== void 0 ? _13 : 0, g: (_14 = o2.g) !== null && _14 !== void 0 ? _14 : 0, b: (_15 = o2.b) !== null && _15 !== void 0 ? _15 : 0 });
884
+ const mixed = oklabToRgb({ L: ok1.L + (ok2.L - ok1.L) * t, a: ok1.a + (ok2.a - ok1.a) * t, b: ok1.b + (ok2.b - ok1.b) * t });
885
+ r = mixed.r;
886
+ g = mixed.g;
887
+ b = mixed.b;
888
+ a = ((_16 = o1.a) !== null && _16 !== void 0 ? _16 : 1) + (((_17 = o2.a) !== null && _17 !== void 0 ? _17 : 1) - ((_18 = o1.a) !== null && _18 !== void 0 ? _18 : 1)) * t;
889
+ }
890
+ else if (mode === 'oklch') {
891
+ const ok1 = rgbToOklch({ r: (_19 = o1.r) !== null && _19 !== void 0 ? _19 : 0, g: (_20 = o1.g) !== null && _20 !== void 0 ? _20 : 0, b: (_21 = o1.b) !== null && _21 !== void 0 ? _21 : 0 });
892
+ const ok2 = rgbToOklch({ r: (_22 = o2.r) !== null && _22 !== void 0 ? _22 : 0, g: (_23 = o2.g) !== null && _23 !== void 0 ? _23 : 0, b: (_24 = o2.b) !== null && _24 !== void 0 ? _24 : 0 });
893
+ const mixed = oklchToRgb({ L: ok1.L + (ok2.L - ok1.L) * t, C: ok1.C + (ok2.C - ok1.C) * t, H: _lerpHue(ok1.H, ok2.H, t, hueMode) });
894
+ r = mixed.r;
895
+ g = mixed.g;
896
+ b = mixed.b;
897
+ a = ((_25 = o1.a) !== null && _25 !== void 0 ? _25 : 1) + (((_26 = o2.a) !== null && _26 !== void 0 ? _26 : 1) - ((_27 = o1.a) !== null && _27 !== void 0 ? _27 : 1)) * t;
898
+ }
899
+ else {
900
+ r = ((_28 = o1.r) !== null && _28 !== void 0 ? _28 : 0) * (1 - t) + ((_29 = o2.r) !== null && _29 !== void 0 ? _29 : 0) * t;
901
+ g = ((_30 = o1.g) !== null && _30 !== void 0 ? _30 : 0) * (1 - t) + ((_31 = o2.g) !== null && _31 !== void 0 ? _31 : 0) * t;
902
+ b = ((_32 = o1.b) !== null && _32 !== void 0 ? _32 : 0) * (1 - t) + ((_33 = o2.b) !== null && _33 !== void 0 ? _33 : 0) * t;
903
+ a = ((_34 = o1.a) !== null && _34 !== void 0 ? _34 : 1) * (1 - t) + ((_35 = o2.a) !== null && _35 !== void 0 ? _35 : 1) * t;
904
+ }
905
+ return _formatMixResult(r, g, b, a, out);
657
906
  }
658
907
  function relativeLuminance(color) {
659
908
  var _a, _b, _c;
@@ -974,3 +1223,188 @@ function toNearestNamedColor(color) {
974
1223
  }
975
1224
  return bestName;
976
1225
  }
1226
+ // ─── Formatting ───────────────────────────────────────────────────────────────
1227
+ function toOklchString(color, alpha) {
1228
+ var _a, _b, _c;
1229
+ const n = normalizeColor(color);
1230
+ const { L, C, H } = rgbToOklch({ r: (_a = n.r) !== null && _a !== void 0 ? _a : 0, g: (_b = n.g) !== null && _b !== void 0 ? _b : 0, b: (_c = n.b) !== null && _c !== void 0 ? _c : 0 });
1231
+ const Lr = +L.toFixed(4), Cr = +C.toFixed(4), Hr = +H.toFixed(2);
1232
+ if (alpha !== undefined)
1233
+ return `oklch(${Lr} ${Cr} ${Hr} / ${+alpha.toFixed(3)})`;
1234
+ return `oklch(${Lr} ${Cr} ${Hr})`;
1235
+ }
1236
+ function toColorP3String(color, alpha) {
1237
+ var _a, _b, _c;
1238
+ const n = normalizeColor(color);
1239
+ const p3 = rgbToDisplayP3({ r: (_a = n.r) !== null && _a !== void 0 ? _a : 0, g: (_b = n.g) !== null && _b !== void 0 ? _b : 0, b: (_c = n.b) !== null && _c !== void 0 ? _c : 0 });
1240
+ const r = +p3.r.toFixed(4), g = +p3.g.toFixed(4), b = +p3.b.toFixed(4);
1241
+ if (alpha !== undefined)
1242
+ return `color(display-p3 ${r} ${g} ${b} / ${+alpha.toFixed(3)})`;
1243
+ return `color(display-p3 ${r} ${g} ${b})`;
1244
+ }
1245
+ // ─── Section 2: Interpolation ─────────────────────────────────────────────────
1246
+ function interpolateColors(color1, color2, steps, options) {
1247
+ if (steps < 2)
1248
+ return steps === 1 ? [mixColors(color1, color2, 0.5, options)] : [];
1249
+ const result = [];
1250
+ for (let i = 0; i < steps; i++) {
1251
+ result.push(mixColors(color1, color2, i / (steps - 1), options));
1252
+ }
1253
+ return result;
1254
+ }
1255
+ function createColorScale(anchors, steps, options) {
1256
+ const normalized = anchors.map((a, i, arr) => {
1257
+ var _a;
1258
+ return ({
1259
+ color: typeof a === 'string' ? a : a.color,
1260
+ position: typeof a === 'string' ? i / Math.max(arr.length - 1, 1) : ((_a = a.position) !== null && _a !== void 0 ? _a : i / Math.max(arr.length - 1, 1)),
1261
+ });
1262
+ });
1263
+ normalized.sort((a, b) => a.position - b.position);
1264
+ const result = [];
1265
+ for (let i = 0; i < steps; i++) {
1266
+ const t = steps === 1 ? 0 : i / (steps - 1);
1267
+ let lo = normalized[0], hi = normalized[normalized.length - 1];
1268
+ for (let j = 0; j < normalized.length - 1; j++) {
1269
+ if (t >= normalized[j].position && t <= normalized[j + 1].position) {
1270
+ lo = normalized[j];
1271
+ hi = normalized[j + 1];
1272
+ break;
1273
+ }
1274
+ }
1275
+ const span = hi.position - lo.position;
1276
+ const localT = span === 0 ? 0 : (t - lo.position) / span;
1277
+ result.push(mixColors(lo.color, hi.color, localT, options));
1278
+ }
1279
+ return result;
1280
+ }
1281
+ function midpointColor(color1, color2, options) {
1282
+ var _a;
1283
+ return mixColors(color1, color2, 0.5, { mode: (_a = options === null || options === void 0 ? void 0 : options.space) !== null && _a !== void 0 ? _a : 'oklab' });
1284
+ }
1285
+ // ─── Section 3: Tints / Shades / Tones ───────────────────────────────────────
1286
+ function tints(color, steps = 5) {
1287
+ return interpolateColors(color, '#ffffff', steps, { space: 'oklab' });
1288
+ }
1289
+ function shades(color, steps = 5) {
1290
+ return interpolateColors(color, '#000000', steps, { space: 'oklab' });
1291
+ }
1292
+ function tones(color, steps = 5, gray = '#808080') {
1293
+ return interpolateColors(color, gray, steps, { space: 'oklab' });
1294
+ }
1295
+ // ─── Section 4: Color blindness simulation ────────────────────────────────────
1296
+ function _simulateCB(color, matrix) {
1297
+ var _a, _b, _c;
1298
+ const n = normalizeColor(color);
1299
+ // linearize
1300
+ const lin = (v) => { const c = v / 255; return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4); };
1301
+ const enc = (v) => { const c = v <= 0.0031308 ? 12.92 * v : 1.055 * Math.pow(v, 1 / 2.4) - 0.055; return Math.round(Math.max(0, Math.min(1, c)) * 255); };
1302
+ const r = lin((_a = n.r) !== null && _a !== void 0 ? _a : 0), g = lin((_b = n.g) !== null && _b !== void 0 ? _b : 0), b = lin((_c = n.b) !== null && _c !== void 0 ? _c : 0);
1303
+ const nr = enc(matrix[0][0] * r + matrix[0][1] * g + matrix[0][2] * b);
1304
+ const ng = enc(matrix[1][0] * r + matrix[1][1] * g + matrix[1][2] * b);
1305
+ const nb = enc(matrix[2][0] * r + matrix[2][1] * g + matrix[2][2] * b);
1306
+ return rgbToHex({ r: nr, g: ng, b: nb });
1307
+ }
1308
+ const _PROTANOPIA_M = [[0.56667, 0.43333, 0.00000], [0.55833, 0.44167, 0.00000], [0.00000, 0.24167, 0.75833]];
1309
+ const _DEUTERANOPIA_M = [[0.62500, 0.37500, 0.00000], [0.70000, 0.30000, 0.00000], [0.00000, 0.30000, 0.70000]];
1310
+ const _TRITANOPIA_M = [[0.95000, 0.05000, 0.00000], [0.00000, 0.43333, 0.56667], [0.00000, 0.47500, 0.52500]];
1311
+ function simulateProtanopia(color) { return _simulateCB(color, _PROTANOPIA_M); }
1312
+ function simulateDeuteranopia(color) { return _simulateCB(color, _DEUTERANOPIA_M); }
1313
+ function simulateTritanopia(color) { return _simulateCB(color, _TRITANOPIA_M); }
1314
+ function simulateColorBlindness(color, type) {
1315
+ if (type === 'protanopia')
1316
+ return simulateProtanopia(color);
1317
+ if (type === 'deuteranopia')
1318
+ return simulateDeuteranopia(color);
1319
+ return simulateTritanopia(color);
1320
+ }
1321
+ function isReadableOnBackground(textColor, background, options) {
1322
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
1323
+ const level = (_a = options === null || options === void 0 ? void 0 : options.level) !== null && _a !== void 0 ? _a : 'AA';
1324
+ const large = (_b = options === null || options === void 0 ? void 0 : options.largeText) !== null && _b !== void 0 ? _b : false;
1325
+ const minRequired = level === 'AAA' ? (large ? 4.5 : 7) : (large ? 3 : 4.5);
1326
+ let minRatio;
1327
+ if (typeof background === 'string') {
1328
+ minRatio = contrastRatio(textColor, background);
1329
+ }
1330
+ else if (background.type === 'semi-transparent') {
1331
+ const underlay = (_c = background.underlay) !== null && _c !== void 0 ? _c : '#ffffff';
1332
+ const fg = normalizeColor(background.color);
1333
+ const bg = normalizeColor(underlay);
1334
+ const alpha = (_d = fg.a) !== null && _d !== void 0 ? _d : 1;
1335
+ const cr = Math.round(((_e = fg.r) !== null && _e !== void 0 ? _e : 0) * alpha + ((_f = bg.r) !== null && _f !== void 0 ? _f : 255) * (1 - alpha));
1336
+ const cg = Math.round(((_g = fg.g) !== null && _g !== void 0 ? _g : 0) * alpha + ((_h = bg.g) !== null && _h !== void 0 ? _h : 255) * (1 - alpha));
1337
+ const cb = Math.round(((_j = fg.b) !== null && _j !== void 0 ? _j : 0) * alpha + ((_k = bg.b) !== null && _k !== void 0 ? _k : 255) * (1 - alpha));
1338
+ minRatio = contrastRatio(textColor, rgbToHex({ r: cr, g: cg, b: cb }));
1339
+ }
1340
+ else {
1341
+ const ratios = background.stops.map(stop => contrastRatio(textColor, stop));
1342
+ minRatio = Math.min(...ratios);
1343
+ }
1344
+ const wLevel = wcagLevel(textColor, typeof background === 'string' ? background : '#ffffff');
1345
+ return { readable: minRatio >= minRequired, minContrastRatio: +minRatio.toFixed(2), wcagLevel: wLevel };
1346
+ }
1347
+ function bestContrastPalette(background, palettes, options) {
1348
+ let bestIdx = 0;
1349
+ let bestScore = -1;
1350
+ let bestMin = 0, bestAvg = 0;
1351
+ palettes.forEach((palette, idx) => {
1352
+ var _a;
1353
+ const ratios = palette.map(c => contrastRatio(background, c));
1354
+ const weights = (_a = options === null || options === void 0 ? void 0 : options.weights) !== null && _a !== void 0 ? _a : ratios.map(() => 1);
1355
+ const totalW = weights.reduce((s, w) => s + w, 0);
1356
+ const avg = ratios.reduce((s, r, i) => { var _a; return s + r * ((_a = weights[i]) !== null && _a !== void 0 ? _a : 1); }, 0) / totalW;
1357
+ const min = Math.min(...ratios);
1358
+ // Score: weighted avg with heavy penalty on minimum
1359
+ const score = avg * 0.4 + min * 0.6;
1360
+ if (score > bestScore) {
1361
+ bestScore = score;
1362
+ bestIdx = idx;
1363
+ bestMin = min;
1364
+ bestAvg = avg;
1365
+ }
1366
+ });
1367
+ return {
1368
+ paletteIndex: bestIdx,
1369
+ palette: palettes[bestIdx],
1370
+ minContrastRatio: +bestMin.toFixed(2),
1371
+ avgContrastRatio: +bestAvg.toFixed(2),
1372
+ };
1373
+ }
1374
+ // ─── Section 5.1: Caching ────────────────────────────────────────────────────
1375
+ let _cacheEnabled = true;
1376
+ const _normalizeCache = new Map();
1377
+ let _cacheHits = 0;
1378
+ function clearColorCache() { _normalizeCache.clear(); _cacheHits = 0; }
1379
+ function getCacheStats() { return { size: _normalizeCache.size, hits: _cacheHits }; }
1380
+ function enableCache() { _cacheEnabled = true; }
1381
+ function disableCache() { _cacheEnabled = false; }
1382
+ // Cached normalizer wrapper — use this in performance-sensitive contexts
1383
+ function normalizeColorCached(input) {
1384
+ if (!_cacheEnabled)
1385
+ return normalizeColor(input);
1386
+ const cached = _normalizeCache.get(input);
1387
+ if (cached) {
1388
+ _cacheHits++;
1389
+ return cached;
1390
+ }
1391
+ const result = normalizeColor(input);
1392
+ _normalizeCache.set(input, result);
1393
+ return result;
1394
+ }
1395
+ // ─── Section 5.2: Generator functions ────────────────────────────────────────
1396
+ function* generateGradientColors(start, end, steps, options) {
1397
+ for (let i = 0; i < steps; i++) {
1398
+ yield mixColors(start, end, steps === 1 ? 0 : i / (steps - 1), options);
1399
+ }
1400
+ }
1401
+ function* generateTints(color, steps, options) {
1402
+ for (let i = 0; i < steps; i++) {
1403
+ yield mixColors(color, '#ffffff', steps === 1 ? 0 : i / (steps - 1), { mode: 'oklab', ...options });
1404
+ }
1405
+ }
1406
+ function* generateShades(color, steps, options) {
1407
+ for (let i = 0; i < steps; i++) {
1408
+ yield mixColors(color, '#000000', steps === 1 ? 0 : i / (steps - 1), { mode: 'oklab', ...options });
1409
+ }
1410
+ }