color-value-tools 1.2.1 → 1.2.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.
Files changed (49) hide show
  1. package/README.md +18 -0
  2. package/dist/cjs/a11y.d.ts +40 -0
  3. package/dist/cjs/a11y.js +253 -0
  4. package/dist/cjs/cache.d.ts +9 -0
  5. package/dist/cjs/cache.js +38 -0
  6. package/dist/cjs/convert/format.d.ts +3 -0
  7. package/dist/cjs/convert/format.js +42 -0
  8. package/dist/cjs/convert/index.d.ts +2 -0
  9. package/dist/cjs/convert/index.js +18 -0
  10. package/dist/cjs/convert/spaces.d.ts +189 -0
  11. package/dist/cjs/convert/spaces.js +556 -0
  12. package/dist/cjs/index.d.ts +6 -397
  13. package/dist/cjs/index.js +20 -1466
  14. package/dist/cjs/internal/named-colors.d.ts +1 -0
  15. package/dist/cjs/internal/named-colors.js +165 -0
  16. package/dist/cjs/internal/transfer.d.ts +2 -0
  17. package/dist/cjs/internal/transfer.js +12 -0
  18. package/dist/cjs/manipulate.d.ts +15 -0
  19. package/dist/cjs/manipulate.js +223 -0
  20. package/dist/cjs/palette.d.ts +40 -0
  21. package/dist/cjs/palette.js +164 -0
  22. package/dist/cjs/parse.d.ts +102 -0
  23. package/dist/cjs/parse.js +257 -0
  24. package/dist/cli/{bin/cli.js → cli.js} +28 -26
  25. package/dist/esm/a11y.d.ts +40 -0
  26. package/dist/esm/a11y.js +237 -0
  27. package/dist/esm/cache.d.ts +9 -0
  28. package/dist/esm/cache.js +31 -0
  29. package/dist/esm/convert/format.d.ts +3 -0
  30. package/dist/esm/convert/format.js +37 -0
  31. package/dist/esm/convert/index.d.ts +2 -0
  32. package/dist/esm/convert/index.js +2 -0
  33. package/dist/esm/convert/spaces.d.ts +189 -0
  34. package/dist/esm/convert/spaces.js +520 -0
  35. package/dist/esm/index.d.ts +6 -0
  36. package/dist/esm/index.js +6 -1368
  37. package/dist/esm/internal/named-colors.d.ts +1 -0
  38. package/dist/esm/internal/named-colors.js +162 -0
  39. package/dist/esm/internal/transfer.d.ts +2 -0
  40. package/dist/esm/internal/transfer.js +8 -0
  41. package/dist/esm/manipulate.d.ts +15 -0
  42. package/dist/esm/manipulate.js +210 -0
  43. package/dist/esm/package.json +3 -1
  44. package/dist/esm/palette.d.ts +40 -0
  45. package/dist/esm/palette.js +145 -0
  46. package/dist/esm/parse.d.ts +102 -0
  47. package/dist/esm/parse.js +241 -0
  48. package/package.json +100 -8
  49. package/dist/cli/src/index.js +0 -1468
@@ -0,0 +1,556 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rgbToDisplayP3 = rgbToDisplayP3;
4
+ exports.displayP3ToRgb = displayP3ToRgb;
5
+ exports.shortHexToRgba = shortHexToRgba;
6
+ exports.normalizeHex = normalizeHex;
7
+ exports.hexToRgb = hexToRgb;
8
+ exports.hexToRgba = hexToRgba;
9
+ exports.hexToHsl = hexToHsl;
10
+ exports.hslToHex = hslToHex;
11
+ exports.rgbToHex = rgbToHex;
12
+ exports.rgbaToHex = rgbaToHex;
13
+ exports.rgbToRgbaString = rgbToRgbaString;
14
+ exports.rgbaStringToRgba = rgbaStringToRgba;
15
+ exports.rgbToHsl = rgbToHsl;
16
+ exports.hslToRgb = hslToRgb;
17
+ exports.rgbToHsv = rgbToHsv;
18
+ exports.hsvToRgb = hsvToRgb;
19
+ exports.hexToHsv = hexToHsv;
20
+ exports.hsvToHex = hsvToHex;
21
+ exports.hex8ToRgba = hex8ToRgba;
22
+ exports.rgbaToHex8 = rgbaToHex8;
23
+ exports.rgbToCmyk = rgbToCmyk;
24
+ exports.cmykToRgb = cmykToRgb;
25
+ exports.rgbToLab = rgbToLab;
26
+ exports.labToRgb = labToRgb;
27
+ exports.rgbToLch = rgbToLch;
28
+ exports.lchToRgb = lchToRgb;
29
+ exports.rgbToHwb = rgbToHwb;
30
+ exports.hwbToRgb = hwbToRgb;
31
+ exports.toHwbString = toHwbString;
32
+ exports.rgbToOklab = rgbToOklab;
33
+ exports.oklabToRgb = oklabToRgb;
34
+ exports.rgbToOklch = rgbToOklch;
35
+ exports.oklchToRgb = oklchToRgb;
36
+ exports.toHslString = toHslString;
37
+ // Pure numeric conversions between color spaces and hex/rgba strings.
38
+ const transfer_js_1 = require("../internal/transfer.js");
39
+ // ─── Display P3 ────────────────────────────────────────────────
40
+ function rgbToDisplayP3(rgb) {
41
+ // Remove sRGB gamma (linearize)
42
+ const rl = (0, transfer_js_1.srgbChanToLinear)(rgb.r / 255);
43
+ const gl = (0, transfer_js_1.srgbChanToLinear)(rgb.g / 255);
44
+ const bl = (0, transfer_js_1.srgbChanToLinear)(rgb.b / 255);
45
+ // sRGB-linear → P3-linear matrix
46
+ const pr = 0.8226 * rl + 0.1774 * gl + 0.0 * bl;
47
+ const pg = 0.0332 * rl + 0.9669 * gl + 0.0 * bl;
48
+ const pb = 0.0171 * rl + 0.0724 * gl + 0.9103 * bl;
49
+ // Apply P3 gamma (same as sRGB)
50
+ return {
51
+ r: (0, transfer_js_1.linearChanToSrgb)(pr),
52
+ g: (0, transfer_js_1.linearChanToSrgb)(pg),
53
+ b: (0, transfer_js_1.linearChanToSrgb)(pb),
54
+ };
55
+ }
56
+ function displayP3ToRgb(p3) {
57
+ // Remove P3 gamma
58
+ const rl = (0, transfer_js_1.srgbChanToLinear)(p3.r);
59
+ const gl = (0, transfer_js_1.srgbChanToLinear)(p3.g);
60
+ const bl = (0, transfer_js_1.srgbChanToLinear)(p3.b);
61
+ // P3-linear → sRGB-linear inverse matrix
62
+ const sr = 1.2247 * rl + -0.2247 * gl + 0.0 * bl;
63
+ const sg = -0.0421 * rl + 1.0432 * gl + 0.0 * bl;
64
+ const sb = -0.0197 * rl + -0.0786 * gl + 1.0983 * bl;
65
+ // Apply sRGB gamma and scale to 0-255
66
+ return {
67
+ r: Math.round(Math.max(0, Math.min(255, (0, transfer_js_1.linearChanToSrgb)(sr) * 255))),
68
+ g: Math.round(Math.max(0, Math.min(255, (0, transfer_js_1.linearChanToSrgb)(sg) * 255))),
69
+ b: Math.round(Math.max(0, Math.min(255, (0, transfer_js_1.linearChanToSrgb)(sb) * 255))),
70
+ };
71
+ }
72
+ // ─── Short RGBA hex #RGBA ──────────────────────────────────────
73
+ function shortHexToRgba(hex) {
74
+ let h = hex.trim();
75
+ if (!h.startsWith('#'))
76
+ h = `#${h}`;
77
+ if (!/^#[0-9a-f]{4}$/i.test(h))
78
+ return null;
79
+ const r = parseInt(h[1] + h[1], 16);
80
+ const g = parseInt(h[2] + h[2], 16);
81
+ const b = parseInt(h[3] + h[3], 16);
82
+ const a = parseInt(h[4] + h[4], 16) / 255;
83
+ return { r, g, b, a };
84
+ }
85
+ function normalizeHex(hex) {
86
+ let cleanHex = hex.trim();
87
+ if (!cleanHex.startsWith('#'))
88
+ cleanHex = `#${cleanHex}`;
89
+ if (cleanHex.length === 4) {
90
+ cleanHex = `#${cleanHex
91
+ .slice(1)
92
+ .split('')
93
+ .map((c) => c + c)
94
+ .join('')}`;
95
+ }
96
+ if (!/^#[0-9A-Fa-f]{6}$/.test(cleanHex)) {
97
+ return '#f5e477';
98
+ }
99
+ return cleanHex.toLowerCase();
100
+ }
101
+ function hexToRgb(hex) {
102
+ const normalizedHex = normalizeHex(hex);
103
+ const r = parseInt(normalizedHex.slice(1, 3), 16);
104
+ const g = parseInt(normalizedHex.slice(3, 5), 16);
105
+ const b = parseInt(normalizedHex.slice(5, 7), 16);
106
+ return [r, g, b];
107
+ }
108
+ function hexToRgba(hex, opacity = 1) {
109
+ const [r, g, b] = hexToRgb(hex);
110
+ return `rgba(${r}, ${g}, ${b}, ${opacity})`;
111
+ }
112
+ function hexToHsl(hex) {
113
+ const [r, g, b] = hexToRgb(hex);
114
+ const red = r / 255;
115
+ const green = g / 255;
116
+ const blue = b / 255;
117
+ const max = Math.max(red, green, blue);
118
+ const min = Math.min(red, green, blue);
119
+ let h = 0, s = 0;
120
+ const l = (max + min) / 2;
121
+ if (max !== min) {
122
+ const d = max - min;
123
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
124
+ switch (max) {
125
+ case red:
126
+ h = (green - blue) / d + (green < blue ? 6 : 0);
127
+ break;
128
+ case green:
129
+ h = (blue - red) / d + 2;
130
+ break;
131
+ case blue:
132
+ h = (red - green) / d + 4;
133
+ break;
134
+ }
135
+ h /= 6;
136
+ }
137
+ return [Math.round(h * 360), Math.round(s * 100), Math.round(l * 100)];
138
+ }
139
+ function hslToHex(h, s, l) {
140
+ h = ((h % 360) + 360) % 360;
141
+ s = Math.max(0, Math.min(100, s)) / 100;
142
+ l = Math.max(0, Math.min(100, l)) / 100;
143
+ const c = (1 - Math.abs(2 * l - 1)) * s;
144
+ const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
145
+ const m = l - c / 2;
146
+ let r = 0, g = 0, b = 0;
147
+ if (h >= 0 && h < 60) {
148
+ r = c;
149
+ g = x;
150
+ b = 0;
151
+ }
152
+ else if (h >= 60 && h < 120) {
153
+ r = x;
154
+ g = c;
155
+ b = 0;
156
+ }
157
+ else if (h >= 120 && h < 180) {
158
+ r = 0;
159
+ g = c;
160
+ b = x;
161
+ }
162
+ else if (h >= 180 && h < 240) {
163
+ r = 0;
164
+ g = x;
165
+ b = c;
166
+ }
167
+ else if (h >= 240 && h < 300) {
168
+ r = x;
169
+ g = 0;
170
+ b = c;
171
+ }
172
+ else {
173
+ r = c;
174
+ g = 0;
175
+ b = x;
176
+ }
177
+ r = Math.round((r + m) * 255);
178
+ g = Math.round((g + m) * 255);
179
+ b = Math.round((b + m) * 255);
180
+ return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`;
181
+ }
182
+ function clamp01(v) {
183
+ return Math.max(0, Math.min(1, v));
184
+ }
185
+ function toHex2(n) {
186
+ return Math.round(n).toString(16).padStart(2, '0');
187
+ }
188
+ function rgbToHex({ r, g, b }) {
189
+ return `#${toHex2(r)}${toHex2(g)}${toHex2(b)}`.toLowerCase();
190
+ }
191
+ function rgbaToHex({ r, g, b, a }) {
192
+ const aa = Math.round(clamp01(a) * 255);
193
+ return `#${toHex2(r)}${toHex2(g)}${toHex2(b)}${toHex2(aa)}`.toLowerCase();
194
+ }
195
+ function rgbToRgbaString({ r, g, b }, a) {
196
+ return `rgba(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)}, ${+a.toFixed(3)})`;
197
+ }
198
+ function rgbaStringToRgba(str) {
199
+ const s = str.trim().toLowerCase();
200
+ const m = s.match(/rgba?\(([^)]+)\)/);
201
+ if (!m)
202
+ return null;
203
+ const parts = m[1].split(/,\s*/).map((p) => p.trim());
204
+ if (parts.length < 3)
205
+ return null;
206
+ // Clamp channels to a valid byte and alpha to [0,1] — an out-of-range input
207
+ // like "rgb(300, -20, 0)" or "rgba(0,0,0,2)" is invalid CSS, but previously
208
+ // passed straight through unclamped, which could even corrupt downstream hex
209
+ // output (a negative channel stringifies with a leading "-" via toString(16)).
210
+ const parseChannel = (v) => {
211
+ const n = v.endsWith('%') ? parseFloat(v) * 2.55 : parseFloat(v);
212
+ return Math.round(Math.max(0, Math.min(255, n)));
213
+ };
214
+ const r = parseChannel(parts[0]);
215
+ const g = parseChannel(parts[1]);
216
+ const b = parseChannel(parts[2]);
217
+ const a = parts[3] !== undefined ? Math.max(0, Math.min(1, parseFloat(parts[3]))) : 1;
218
+ return { r, g, b, a };
219
+ }
220
+ function rgbToHsl({ r, g, b, }) {
221
+ const rd = r / 255, gd = g / 255, bd = b / 255;
222
+ const max = Math.max(rd, gd, bd), min = Math.min(rd, gd, bd);
223
+ let h = 0, s = 0;
224
+ const l = (max + min) / 2;
225
+ if (max !== min) {
226
+ const d = max - min;
227
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
228
+ switch (max) {
229
+ case rd:
230
+ h = (gd - bd) / d + (gd < bd ? 6 : 0);
231
+ break;
232
+ case gd:
233
+ h = (bd - rd) / d + 2;
234
+ break;
235
+ case bd:
236
+ h = (rd - gd) / d + 4;
237
+ break;
238
+ }
239
+ h /= 6;
240
+ }
241
+ return [Math.round(h * 360), Math.round(s * 100), Math.round(l * 100)];
242
+ }
243
+ function hslToRgb(h, s, l) {
244
+ h = ((h % 360) + 360) % 360;
245
+ s = Math.max(0, Math.min(100, s)) / 100;
246
+ l = Math.max(0, Math.min(100, l)) / 100;
247
+ const c = (1 - Math.abs(2 * l - 1)) * s;
248
+ const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
249
+ const m = l - c / 2;
250
+ let rd = 0, gd = 0, bd = 0;
251
+ if (h >= 0 && h < 60) {
252
+ rd = c;
253
+ gd = x;
254
+ bd = 0;
255
+ }
256
+ else if (h >= 60 && h < 120) {
257
+ rd = x;
258
+ gd = c;
259
+ bd = 0;
260
+ }
261
+ else if (h >= 120 && h < 180) {
262
+ rd = 0;
263
+ gd = c;
264
+ bd = x;
265
+ }
266
+ else if (h >= 180 && h < 240) {
267
+ rd = 0;
268
+ gd = x;
269
+ bd = c;
270
+ }
271
+ else if (h >= 240 && h < 300) {
272
+ rd = x;
273
+ gd = 0;
274
+ bd = c;
275
+ }
276
+ else {
277
+ rd = c;
278
+ gd = 0;
279
+ bd = x;
280
+ }
281
+ return {
282
+ r: Math.round((rd + m) * 255),
283
+ g: Math.round((gd + m) * 255),
284
+ b: Math.round((bd + m) * 255),
285
+ };
286
+ }
287
+ function rgbToHsv({ r, g, b, }) {
288
+ const rd = r / 255, gd = g / 255, bd = b / 255;
289
+ const max = Math.max(rd, gd, bd), min = Math.min(rd, gd, bd);
290
+ const v = max;
291
+ const d = max - min;
292
+ const s = max === 0 ? 0 : d / max;
293
+ let h = 0;
294
+ if (d !== 0) {
295
+ switch (max) {
296
+ case rd:
297
+ h = (gd - bd) / d + (gd < bd ? 6 : 0);
298
+ break;
299
+ case gd:
300
+ h = (bd - rd) / d + 2;
301
+ break;
302
+ case bd:
303
+ h = (rd - gd) / d + 4;
304
+ break;
305
+ }
306
+ h /= 6;
307
+ }
308
+ return [Math.round(h * 360), Math.round(s * 100), Math.round(v * 100)];
309
+ }
310
+ function hsvToRgb(h, s, v) {
311
+ h = ((h % 360) + 360) % 360;
312
+ s = Math.max(0, Math.min(100, s)) / 100;
313
+ v = Math.max(0, Math.min(100, v)) / 100;
314
+ const i = Math.floor(h / 60);
315
+ const f = h / 60 - i;
316
+ const p = v * (1 - s);
317
+ const q = v * (1 - f * s);
318
+ const t = v * (1 - (1 - f) * s);
319
+ let rd = 0, gd = 0, bd = 0;
320
+ switch (i) {
321
+ case 0:
322
+ rd = v;
323
+ gd = t;
324
+ bd = p;
325
+ break;
326
+ case 1:
327
+ rd = q;
328
+ gd = v;
329
+ bd = p;
330
+ break;
331
+ case 2:
332
+ rd = p;
333
+ gd = v;
334
+ bd = t;
335
+ break;
336
+ case 3:
337
+ rd = p;
338
+ gd = q;
339
+ bd = v;
340
+ break;
341
+ case 4:
342
+ rd = t;
343
+ gd = p;
344
+ bd = v;
345
+ break;
346
+ default:
347
+ rd = v;
348
+ gd = p;
349
+ bd = q;
350
+ break;
351
+ }
352
+ return { r: Math.round(rd * 255), g: Math.round(gd * 255), b: Math.round(bd * 255) };
353
+ }
354
+ function hexToHsv(hex) {
355
+ const [r, g, b] = hexToRgb(hex);
356
+ return rgbToHsv({ r, g, b });
357
+ }
358
+ function hsvToHex(h, s, v) {
359
+ const { r, g, b } = hsvToRgb(h, s, v);
360
+ return rgbToHex({ r, g, b });
361
+ }
362
+ function hex8ToRgba(hex) {
363
+ let h = hex.trim();
364
+ if (!h.startsWith('#'))
365
+ h = `#${h}`;
366
+ if (h.length === 5) {
367
+ // #rgba shorthand
368
+ h = `#${h[1]}${h[1]}${h[2]}${h[2]}${h[3]}${h[3]}${h[4]}${h[4]}`;
369
+ }
370
+ if (h.length !== 9)
371
+ return null;
372
+ const r = parseInt(h.slice(1, 3), 16);
373
+ const g = parseInt(h.slice(3, 5), 16);
374
+ const b = parseInt(h.slice(5, 7), 16);
375
+ const a = parseInt(h.slice(7, 9), 16) / 255;
376
+ return { r, g, b, a };
377
+ }
378
+ function rgbaToHex8({ r, g, b, a }) {
379
+ return rgbaToHex({ r, g, b, a });
380
+ }
381
+ function rgbToCmyk({ r, g, b }) {
382
+ const rd = r / 255, gd = g / 255, bd = b / 255;
383
+ const k = 1 - Math.max(rd, gd, bd);
384
+ if (k === 1)
385
+ return { c: 0, m: 0, y: 0, k: 1 };
386
+ const c = (1 - rd - k) / (1 - k);
387
+ const m = (1 - gd - k) / (1 - k);
388
+ const y = (1 - bd - k) / (1 - k);
389
+ return { c, m, y, k };
390
+ }
391
+ function cmykToRgb({ c, m, y, k }) {
392
+ const r = 255 * (1 - c) * (1 - k);
393
+ const g = 255 * (1 - m) * (1 - k);
394
+ const b = 255 * (1 - y) * (1 - k);
395
+ return { r: Math.round(r), g: Math.round(g), b: Math.round(b) };
396
+ }
397
+ function srgbToLinear(v) {
398
+ v = v / 255;
399
+ return v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
400
+ }
401
+ function linearToSrgb(v) {
402
+ const t = v <= 0.0031308 ? 12.92 * v : 1.055 * Math.pow(v, 1 / 2.4) - 0.055;
403
+ return Math.round(Math.max(0, Math.min(1, t)) * 255);
404
+ }
405
+ function rgbToXyz({ r, g, b }) {
406
+ const R = srgbToLinear(r);
407
+ const G = srgbToLinear(g);
408
+ const B = srgbToLinear(b);
409
+ const X = R * 0.4124564 + G * 0.3575761 + B * 0.1804375;
410
+ const Y = R * 0.2126729 + G * 0.7151522 + B * 0.072175;
411
+ const Z = R * 0.0193339 + G * 0.119192 + B * 0.9503041;
412
+ return { X: X * 100, Y: Y * 100, Z: Z * 100 };
413
+ }
414
+ function xyzToRgb({ X, Y, Z }) {
415
+ X = X / 100;
416
+ Y = Y / 100;
417
+ Z = Z / 100;
418
+ let R = X * 3.2404542 + Y * -1.5371385 + Z * -0.4985314;
419
+ let G = X * -0.969266 + Y * 1.8760108 + Z * 0.041556;
420
+ let B = X * 0.0556434 + Y * -0.2040259 + Z * 1.0572252;
421
+ R = linearToSrgb(R);
422
+ G = linearToSrgb(G);
423
+ B = linearToSrgb(B);
424
+ return { r: R, g: G, b: B };
425
+ }
426
+ function rgbToLab({ r, g, b }) {
427
+ const { X, Y, Z } = rgbToXyz({ r, g, b });
428
+ const refX = 95.047, refY = 100.0, refZ = 108.883;
429
+ const x = X / refX, y = Y / refY, z = Z / refZ;
430
+ const fx = x > 0.008856 ? Math.cbrt(x) : 7.787 * x + 16 / 116;
431
+ const fy = y > 0.008856 ? Math.cbrt(y) : 7.787 * y + 16 / 116;
432
+ const fz = z > 0.008856 ? Math.cbrt(z) : 7.787 * z + 16 / 116;
433
+ const L = 116 * fy - 16;
434
+ const a = 500 * (fx - fy);
435
+ const b2 = 200 * (fy - fz);
436
+ return { L, a, b: b2 };
437
+ }
438
+ function labToRgb({ L, a, b }) {
439
+ const refX = 95.047, refY = 100.0, refZ = 108.883;
440
+ let fy = (L + 16) / 116;
441
+ let fx = a / 500 + fy;
442
+ let fz = fy - b / 200;
443
+ const fx3 = Math.pow(fx, 3);
444
+ const fz3 = Math.pow(fz, 3);
445
+ const fy3 = Math.pow(fy, 3);
446
+ const xr = fx3 > 0.008856 ? fx3 : (fx - 16 / 116) / 7.787;
447
+ const yr = L > 903.3 * 0.008856 ? fy3 : L / 903.3;
448
+ const zr = fz3 > 0.008856 ? fz3 : (fz - 16 / 116) / 7.787;
449
+ const X = xr * refX, Y = yr * refY, Z = zr * refZ;
450
+ return xyzToRgb({ X, Y, Z });
451
+ }
452
+ function rgbToLch({ r, g, b }) {
453
+ const { L, a, b: bb } = rgbToLab({ r, g, b });
454
+ const C = Math.sqrt(a * a + bb * bb);
455
+ let H = Math.atan2(bb, a) * (180 / Math.PI);
456
+ if (H < 0)
457
+ H += 360;
458
+ return { L, C, H };
459
+ }
460
+ function lchToRgb({ L, C, H }) {
461
+ const a = Math.cos((H * Math.PI) / 180) * C;
462
+ const b = Math.sin((H * Math.PI) / 180) * C;
463
+ return labToRgb({ L, a, b });
464
+ }
465
+ // ─── HWB ─────────────────────────────────────────────────────────────────────
466
+ function rgbToHwb({ r, g, b, }) {
467
+ const rd = r / 255, gd = g / 255, bd = b / 255;
468
+ const max = Math.max(rd, gd, bd), min = Math.min(rd, gd, bd);
469
+ const w = min;
470
+ const bl = 1 - max;
471
+ let h = 0;
472
+ if (max !== min) {
473
+ const d = max - min;
474
+ switch (max) {
475
+ case rd:
476
+ h = (gd - bd) / d + (gd < bd ? 6 : 0);
477
+ break;
478
+ case gd:
479
+ h = (bd - rd) / d + 2;
480
+ break;
481
+ case bd:
482
+ h = (rd - gd) / d + 4;
483
+ break;
484
+ }
485
+ h /= 6;
486
+ }
487
+ return [Math.round(h * 360), Math.round(w * 100), Math.round(bl * 100)];
488
+ }
489
+ function hwbToRgb(H, W, B) {
490
+ H = ((H % 360) + 360) % 360;
491
+ const w = W / 100, b = B / 100;
492
+ if (w + b >= 1) {
493
+ const gray = Math.round((w / (w + b)) * 255);
494
+ return { r: gray, g: gray, b: gray };
495
+ }
496
+ const { r, g, b: rb } = hslToRgb(H, 100, 50);
497
+ const factor = 1 - w - b;
498
+ return {
499
+ r: Math.round((r / 255) * factor * 255 + w * 255),
500
+ g: Math.round((g / 255) * factor * 255 + w * 255),
501
+ b: Math.round((rb / 255) * factor * 255 + w * 255),
502
+ };
503
+ }
504
+ function toHwbString(H, W, B, alpha) {
505
+ if (alpha !== undefined)
506
+ return `hwb(${H} ${W}% ${B}% / ${+alpha.toFixed(3)})`;
507
+ return `hwb(${H} ${W}% ${B}%)`;
508
+ }
509
+ // ─── OKLCH / OKLAB ───────────────────────────────────────────────────────────
510
+ function srgbToOkLinear(v) {
511
+ v = v / 255;
512
+ return v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
513
+ }
514
+ function okLinearToSrgb(v) {
515
+ const t = v <= 0.0031308 ? 12.92 * v : 1.055 * Math.pow(v, 1 / 2.4) - 0.055;
516
+ return Math.round(Math.max(0, Math.min(1, t)) * 255);
517
+ }
518
+ function rgbToOklab({ r, g, b }) {
519
+ const R = srgbToOkLinear(r), G = srgbToOkLinear(g), B = srgbToOkLinear(b);
520
+ const l = Math.cbrt(0.4122214708 * R + 0.5363325363 * G + 0.0514459929 * B);
521
+ const m = Math.cbrt(0.2119034982 * R + 0.6806995451 * G + 0.1073969566 * B);
522
+ const s = Math.cbrt(0.0883024619 * R + 0.2817188376 * G + 0.6299787005 * B);
523
+ return {
524
+ L: 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,
525
+ a: 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,
526
+ b: 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s,
527
+ };
528
+ }
529
+ function oklabToRgb({ L, a, b }) {
530
+ const l = Math.pow(L + 0.3963377774 * a + 0.2158037573 * b, 3);
531
+ const m = Math.pow(L - 0.1055613458 * a - 0.0638541728 * b, 3);
532
+ const s = Math.pow(L - 0.0894841775 * a - 1.291485548 * b, 3);
533
+ const R = okLinearToSrgb(+4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s);
534
+ const G = okLinearToSrgb(-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s);
535
+ const B = okLinearToSrgb(-0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s);
536
+ return { r: R, g: G, b: B };
537
+ }
538
+ function rgbToOklch({ r, g, b }) {
539
+ const { L, a, b: bb } = rgbToOklab({ r, g, b });
540
+ const C = Math.sqrt(a * a + bb * bb);
541
+ let H = Math.atan2(bb, a) * (180 / Math.PI);
542
+ if (H < 0)
543
+ H += 360;
544
+ return { L, C, H };
545
+ }
546
+ function oklchToRgb({ L, C, H }) {
547
+ const a = Math.cos((H * Math.PI) / 180) * C;
548
+ const b = Math.sin((H * Math.PI) / 180) * C;
549
+ return oklabToRgb({ L, a, b });
550
+ }
551
+ // ─── Utilities ───────────────────────────────────────────────────────────────
552
+ function toHslString(h, s, l, alpha) {
553
+ if (alpha !== undefined)
554
+ return `hsla(${h}, ${s}%, ${l}%, ${+alpha.toFixed(3)})`;
555
+ return `hsl(${h}, ${s}%, ${l}%)`;
556
+ }