@zag-js/color-utils 1.41.0 → 2.0.0-next.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.
@@ -0,0 +1,370 @@
1
+ import {
2
+ __publicField
3
+ } from "./chunk-QZ7TP4HQ.mjs";
4
+
5
+ // src/oklab-color.ts
6
+ import { clampValue, mod, toFixedNumber } from "@zag-js/utils";
7
+ import { Color } from "./color.mjs";
8
+ import { HSBColor } from "./hsb-color.mjs";
9
+ import { RGBColor } from "./rgb-color.mjs";
10
+ import { linearChannelToSrgb, linearRgbToSrgb, oklabToLinearRgb, oklabToOklch, oklchToOklab } from "./oklab-math.mjs";
11
+ function oklabToHsb(L, a, b, alpha) {
12
+ const lin = oklabToLinearRgb(L, a, b);
13
+ return HSBColor.fromSrgbFloat(
14
+ linearChannelToSrgb(lin.r),
15
+ linearChannelToSrgb(lin.g),
16
+ linearChannelToSrgb(lin.b),
17
+ alpha
18
+ );
19
+ }
20
+ var OKLAB_HEAD = /^oklab\(\s*/i;
21
+ function parseCssNumber(token) {
22
+ const t = token.trim().toLowerCase();
23
+ if (t === "none") return void 0;
24
+ if (t.endsWith("%")) return Number.parseFloat(t) / 100;
25
+ const n = Number.parseFloat(t);
26
+ return Number.isFinite(n) ? n : void 0;
27
+ }
28
+ function parseOklabParams(inner) {
29
+ const [main, alphaPart] = inner.split(/\s*\/\s*/);
30
+ const tokens = main.trim().split(/\s+/).filter(Boolean);
31
+ if (tokens.length < 3) return;
32
+ const L = parseCssNumber(tokens[0]);
33
+ const a = parseCssNumber(tokens[1]);
34
+ const b = parseCssNumber(tokens[2]);
35
+ if (L === void 0 || a === void 0 || b === void 0) return;
36
+ let alpha = 1;
37
+ if (alphaPart) {
38
+ const at = alphaPart.trim().toLowerCase();
39
+ if (at !== "none") {
40
+ const av = at.endsWith("%") ? Number.parseFloat(at) / 100 : Number.parseFloat(at);
41
+ if (Number.isFinite(av)) alpha = clampValue(av, 0, 1);
42
+ }
43
+ }
44
+ return { L: clampValue(L, 0, 1), a, b, alpha };
45
+ }
46
+ var _OklabColor = class _OklabColor extends Color {
47
+ constructor(lightness, a, b, alpha) {
48
+ super();
49
+ __publicField(this, "lightness", lightness);
50
+ __publicField(this, "a", a);
51
+ __publicField(this, "b", b);
52
+ __publicField(this, "alpha", alpha);
53
+ }
54
+ static parse(value) {
55
+ if (!OKLAB_HEAD.test(value)) return;
56
+ const end = value.lastIndexOf(")");
57
+ if (end < 0) return;
58
+ const inner = value.slice(value.indexOf("(") + 1, end);
59
+ const p = parseOklabParams(inner);
60
+ if (!p) return;
61
+ return new _OklabColor(p.L, p.a, p.b, p.alpha);
62
+ }
63
+ toString(format = "css") {
64
+ switch (format) {
65
+ case "hex":
66
+ return this.toFormat("rgba").toString("hex");
67
+ case "hexa":
68
+ return this.toFormat("rgba").toString("hexa");
69
+ case "oklab":
70
+ return formatOklabCss(this.lightness, this.a, this.b, this.alpha);
71
+ case "css":
72
+ return formatOklabCss(this.lightness, this.a, this.b, this.alpha);
73
+ default:
74
+ return this.toFormat(format).toString(format);
75
+ }
76
+ }
77
+ toFormat(format) {
78
+ switch (format) {
79
+ case "oklab":
80
+ return this;
81
+ case "oklch":
82
+ return this.toOklch();
83
+ case "rgba":
84
+ return this.toRgb();
85
+ case "hsla":
86
+ return this.toRgb().toFormat("hsla");
87
+ case "hsba":
88
+ return oklabToHsb(this.lightness, this.a, this.b, this.alpha);
89
+ default:
90
+ throw new Error("Unsupported color conversion: oklab -> " + format);
91
+ }
92
+ }
93
+ toRgb() {
94
+ const { r, g, b } = linearRgbToSrgb(oklabToLinearRgb(this.lightness, this.a, this.b));
95
+ return new RGBColor(r, g, b, toFixedNumber(this.alpha, 4));
96
+ }
97
+ toOklch() {
98
+ const { L, C, H } = oklabToOklch(this.lightness, this.a, this.b);
99
+ return new OklchColor(L, H, C, toFixedNumber(this.alpha, 4));
100
+ }
101
+ clone() {
102
+ return new _OklabColor(this.lightness, this.a, this.b, this.alpha);
103
+ }
104
+ getChannelValue(channel) {
105
+ switch (channel) {
106
+ case "lightness":
107
+ return this.lightness;
108
+ case "a":
109
+ return this.a;
110
+ case "b":
111
+ return this.b;
112
+ case "alpha":
113
+ return this.alpha;
114
+ default:
115
+ throw new Error("Unsupported color channel: " + channel);
116
+ }
117
+ }
118
+ withChannelValue(channel, value) {
119
+ const { minValue, maxValue } = this.getChannelRange(channel);
120
+ const v = clampValue(value, minValue, maxValue);
121
+ switch (channel) {
122
+ case "lightness":
123
+ return new _OklabColor(v, this.a, this.b, this.alpha);
124
+ case "a":
125
+ return new _OklabColor(this.lightness, v, this.b, this.alpha);
126
+ case "b":
127
+ return new _OklabColor(this.lightness, this.a, v, this.alpha);
128
+ case "alpha":
129
+ return new _OklabColor(this.lightness, this.a, this.b, v);
130
+ default:
131
+ throw new Error("Unsupported color channel: " + channel);
132
+ }
133
+ }
134
+ formatChannelValue(channel, locale) {
135
+ const options = this.getChannelFormatOptions(channel);
136
+ const val = this.getChannelValue(channel);
137
+ return new Intl.NumberFormat(locale, options).format(val);
138
+ }
139
+ getChannelFormatOptions(channel) {
140
+ switch (channel) {
141
+ case "lightness":
142
+ case "a":
143
+ case "b":
144
+ case "alpha":
145
+ return { style: "decimal", maximumFractionDigits: 4 };
146
+ default:
147
+ throw new Error("Unknown color channel: " + channel);
148
+ }
149
+ }
150
+ getChannelRange(channel) {
151
+ switch (channel) {
152
+ case "lightness":
153
+ return { minValue: 0, maxValue: 1, step: 1e-3, pageSize: 0.05 };
154
+ case "a":
155
+ case "b":
156
+ return { minValue: -0.4, maxValue: 0.4, step: 1e-3, pageSize: 0.02 };
157
+ case "alpha":
158
+ return { minValue: 0, maxValue: 1, step: 0.01, pageSize: 0.1 };
159
+ default:
160
+ throw new Error("Unknown color channel: " + channel);
161
+ }
162
+ }
163
+ toJSON() {
164
+ return { l: this.lightness, a: this.a, b: this.b, alpha: this.alpha };
165
+ }
166
+ getFormat() {
167
+ return "oklab";
168
+ }
169
+ getChannels() {
170
+ return _OklabColor.colorChannels;
171
+ }
172
+ isEqual(color) {
173
+ if (color.getFormat() !== "oklab") return super.isEqual(color);
174
+ const o = color;
175
+ return this.lightness === o.lightness && this.a === o.a && this.b === o.b && this.alpha === o.alpha;
176
+ }
177
+ };
178
+ __publicField(_OklabColor, "colorChannels", ["lightness", "a", "b"]);
179
+ var OklabColor = _OklabColor;
180
+ function formatOklabCss(L, a, b, alpha) {
181
+ const l = toFixedNumber(L, 4);
182
+ const aa = toFixedNumber(a, 4);
183
+ const bb = toFixedNumber(b, 4);
184
+ if (alpha >= 1) return `oklab(${l} ${aa} ${bb})`;
185
+ return `oklab(${l} ${aa} ${bb} / ${toFixedNumber(alpha, 4)})`;
186
+ }
187
+ var OKLCH_HEAD = /^oklch\(\s*/i;
188
+ function parseCssNumberOrAngle(token) {
189
+ const t = token.trim().toLowerCase();
190
+ if (t === "none") return void 0;
191
+ if (t.endsWith("deg")) return Number.parseFloat(t);
192
+ if (t.endsWith("turn")) return Number.parseFloat(t) * 360;
193
+ if (t.endsWith("grad")) return Number.parseFloat(t) / 400 * 360;
194
+ if (t.endsWith("rad")) return Number.parseFloat(t) * 180 / Math.PI;
195
+ if (t.endsWith("%")) return Number.parseFloat(t) / 100;
196
+ const n = Number.parseFloat(t);
197
+ return Number.isFinite(n) ? n : void 0;
198
+ }
199
+ function parseOklchParams(inner) {
200
+ const [main, alphaPart] = inner.split(/\s*\/\s*/);
201
+ const tokens = main.trim().split(/\s+/).filter(Boolean);
202
+ if (tokens.length < 3) return;
203
+ const Lraw = parseCssNumberOrAngle(tokens[0]);
204
+ const Craw = parseCssNumberOrAngle(tokens[1]);
205
+ const Hraw = parseCssNumberOrAngle(tokens[2]);
206
+ if (Lraw === void 0 || Craw === void 0 || Hraw === void 0) return;
207
+ const L = tokens[0].trim().endsWith("%") ? Lraw : clampValue(Lraw, 0, 1);
208
+ const C = tokens[1].trim().endsWith("%") ? Craw * 0.5 : Craw;
209
+ const H = mod(Hraw, 360);
210
+ let alpha = 1;
211
+ if (alphaPart) {
212
+ const at = alphaPart.trim().toLowerCase();
213
+ if (at !== "none") {
214
+ const av = at.endsWith("%") ? Number.parseFloat(at) / 100 : Number.parseFloat(at);
215
+ if (Number.isFinite(av)) alpha = clampValue(av, 0, 1);
216
+ }
217
+ }
218
+ return { L, C: clampValue(C, 0, 0.5), H, alpha };
219
+ }
220
+ var _OklchColor = class _OklchColor extends Color {
221
+ constructor(lightness, hue, chroma, alpha) {
222
+ super();
223
+ __publicField(this, "lightness", lightness);
224
+ __publicField(this, "hue", hue);
225
+ __publicField(this, "chroma", chroma);
226
+ __publicField(this, "alpha", alpha);
227
+ }
228
+ static parse(value) {
229
+ if (!OKLCH_HEAD.test(value)) return;
230
+ const end = value.lastIndexOf(")");
231
+ if (end < 0) return;
232
+ const inner = value.slice(value.indexOf("(") + 1, end);
233
+ const p = parseOklchParams(inner);
234
+ if (!p) return;
235
+ return new _OklchColor(p.L, p.H, p.C, p.alpha);
236
+ }
237
+ toString(format = "css") {
238
+ switch (format) {
239
+ case "hex":
240
+ return this.toFormat("rgba").toString("hex");
241
+ case "hexa":
242
+ return this.toFormat("rgba").toString("hexa");
243
+ case "oklch":
244
+ return formatOklchCss(this.lightness, this.chroma, this.hue, this.alpha);
245
+ case "css":
246
+ return formatOklchCss(this.lightness, this.chroma, this.hue, this.alpha);
247
+ default:
248
+ return this.toFormat(format).toString(format);
249
+ }
250
+ }
251
+ toFormat(format) {
252
+ switch (format) {
253
+ case "oklch":
254
+ return this;
255
+ case "oklab":
256
+ return this.toOklab();
257
+ case "rgba":
258
+ return this.toRgb();
259
+ case "hsla":
260
+ return this.toRgb().toFormat("hsla");
261
+ case "hsba": {
262
+ const { a, b, L } = oklchToOklab(this.lightness, this.chroma, this.hue);
263
+ return oklabToHsb(L, a, b, this.alpha);
264
+ }
265
+ default:
266
+ throw new Error("Unsupported color conversion: oklch -> " + format);
267
+ }
268
+ }
269
+ toOklab() {
270
+ const { a, b, L } = oklchToOklab(this.lightness, this.chroma, this.hue);
271
+ return new OklabColor(L, a, b, toFixedNumber(this.alpha, 4));
272
+ }
273
+ toRgb() {
274
+ return this.toOklab().toFormat("rgba");
275
+ }
276
+ clone() {
277
+ return new _OklchColor(this.lightness, this.hue, this.chroma, this.alpha);
278
+ }
279
+ getChannelValue(channel) {
280
+ switch (channel) {
281
+ case "lightness":
282
+ return this.lightness;
283
+ case "hue":
284
+ return this.hue;
285
+ case "chroma":
286
+ return this.chroma;
287
+ case "alpha":
288
+ return this.alpha;
289
+ default:
290
+ throw new Error("Unsupported color channel: " + channel);
291
+ }
292
+ }
293
+ withChannelValue(channel, value) {
294
+ const { minValue, maxValue } = this.getChannelRange(channel);
295
+ let v = clampValue(value, minValue, maxValue);
296
+ if (channel === "hue") v = mod(v, 360);
297
+ switch (channel) {
298
+ case "lightness":
299
+ return new _OklchColor(v, this.hue, this.chroma, this.alpha);
300
+ case "hue":
301
+ return new _OklchColor(this.lightness, v, this.chroma, this.alpha);
302
+ case "chroma":
303
+ return new _OklchColor(this.lightness, this.hue, v, this.alpha);
304
+ case "alpha":
305
+ return new _OklchColor(this.lightness, this.hue, this.chroma, v);
306
+ default:
307
+ throw new Error("Unsupported color channel: " + channel);
308
+ }
309
+ }
310
+ formatChannelValue(channel, locale) {
311
+ const options = this.getChannelFormatOptions(channel);
312
+ const val = this.getChannelValue(channel);
313
+ return new Intl.NumberFormat(locale, options).format(val);
314
+ }
315
+ getChannelFormatOptions(channel) {
316
+ switch (channel) {
317
+ case "lightness":
318
+ case "chroma":
319
+ case "alpha":
320
+ return { style: "decimal", maximumFractionDigits: 4 };
321
+ case "hue":
322
+ return { style: "unit", unit: "degree", unitDisplay: "narrow" };
323
+ default:
324
+ throw new Error("Unknown color channel: " + channel);
325
+ }
326
+ }
327
+ getChannelRange(channel) {
328
+ switch (channel) {
329
+ case "lightness":
330
+ return { minValue: 0, maxValue: 1, step: 1e-3, pageSize: 0.05 };
331
+ case "hue":
332
+ return { minValue: 0, maxValue: 360, step: 0.01, pageSize: 15 };
333
+ case "chroma":
334
+ return { minValue: 0, maxValue: 0.5, step: 1e-3, pageSize: 0.02 };
335
+ case "alpha":
336
+ return { minValue: 0, maxValue: 1, step: 0.01, pageSize: 0.1 };
337
+ default:
338
+ throw new Error("Unknown color channel: " + channel);
339
+ }
340
+ }
341
+ toJSON() {
342
+ return { l: this.lightness, h: this.hue, c: this.chroma, alpha: this.alpha };
343
+ }
344
+ getFormat() {
345
+ return "oklch";
346
+ }
347
+ getChannels() {
348
+ return _OklchColor.colorChannels;
349
+ }
350
+ isEqual(color) {
351
+ if (color.getFormat() !== "oklch") return super.isEqual(color);
352
+ const o = color;
353
+ return this.lightness === o.lightness && this.hue === o.hue && this.chroma === o.chroma && this.alpha === o.alpha;
354
+ }
355
+ };
356
+ __publicField(_OklchColor, "colorChannels", ["lightness", "hue", "chroma"]);
357
+ var OklchColor = _OklchColor;
358
+ function formatOklchCss(L, C, H, alpha) {
359
+ const l = toFixedNumber(L, 4);
360
+ const c = toFixedNumber(C, 4);
361
+ const h = toFixedNumber(H, 2);
362
+ if (alpha >= 1) return `oklch(${l} ${c} ${h})`;
363
+ return `oklch(${l} ${c} ${h} / ${toFixedNumber(alpha, 4)})`;
364
+ }
365
+ export {
366
+ OklabColor,
367
+ OklchColor,
368
+ parseOklabParams,
369
+ parseOklchParams
370
+ };
@@ -0,0 +1,47 @@
1
+ /**
2
+ * OKLab / linear sRGB conversion (Björn Ottosson / CSS Color 4).
3
+ * https://bottosson.github.io/posts/oklab/
4
+ */
5
+ interface LinearRgb {
6
+ r: number;
7
+ g: number;
8
+ b: number;
9
+ }
10
+ interface Oklab {
11
+ L: number;
12
+ a: number;
13
+ b: number;
14
+ }
15
+ declare function srgbChannelToLinear(c: number): number;
16
+ declare function linearChannelToSrgb(c: number): number;
17
+ declare function srgbToLinearRgb(r255: number, g255: number, b255: number): LinearRgb;
18
+ declare function linearRgbToSrgb({ r, g, b }: LinearRgb): {
19
+ r: number;
20
+ g: number;
21
+ b: number;
22
+ };
23
+ declare function linearRgbToOklab({ r, g, b }: LinearRgb): Oklab;
24
+ declare function oklabToLinearRgb(L: number, a: number, b: number): LinearRgb;
25
+ declare function oklchToOklab(L: number, C: number, Hdeg: number): Oklab;
26
+ declare function oklabToOklch(L: number, a: number, b: number): {
27
+ L: number;
28
+ C: number;
29
+ H: number;
30
+ };
31
+ declare function isLinearRgbInSrgbGamut({ r, g, b }: LinearRgb, eps?: number): boolean;
32
+ /** Convert Display P3 linear RGB to linear sRGB via XYZ D65. */
33
+ declare function linearDisplayP3ToLinearSrgb(r: number, g: number, b: number): LinearRgb;
34
+ /**
35
+ * HSV (0-360, 0-1, 0-1) → RGB (0-1).
36
+ * Standard HSV-to-RGB conversion; the resulting RGB values are in whichever
37
+ * gamut the caller considers them to be (sRGB or Display P3).
38
+ */
39
+ declare function hsvToRgb(h: number, s: number, v: number): [number, number, number];
40
+ /**
41
+ * Check whether an HSV color (interpreted in the Display P3 gamut) falls
42
+ * inside the sRGB gamut. This is how Chrome DevTools draws the sRGB boundary
43
+ * on the color picker when in oklch/oklab mode.
44
+ */
45
+ declare function isDisplayP3HsvInSrgbGamut(h: number, s: number, v: number, eps?: number): boolean;
46
+
47
+ export { type LinearRgb, type Oklab, hsvToRgb, isDisplayP3HsvInSrgbGamut, isLinearRgbInSrgbGamut, linearChannelToSrgb, linearDisplayP3ToLinearSrgb, linearRgbToOklab, linearRgbToSrgb, oklabToLinearRgb, oklabToOklch, oklchToOklab, srgbChannelToLinear, srgbToLinearRgb };
@@ -0,0 +1,47 @@
1
+ /**
2
+ * OKLab / linear sRGB conversion (Björn Ottosson / CSS Color 4).
3
+ * https://bottosson.github.io/posts/oklab/
4
+ */
5
+ interface LinearRgb {
6
+ r: number;
7
+ g: number;
8
+ b: number;
9
+ }
10
+ interface Oklab {
11
+ L: number;
12
+ a: number;
13
+ b: number;
14
+ }
15
+ declare function srgbChannelToLinear(c: number): number;
16
+ declare function linearChannelToSrgb(c: number): number;
17
+ declare function srgbToLinearRgb(r255: number, g255: number, b255: number): LinearRgb;
18
+ declare function linearRgbToSrgb({ r, g, b }: LinearRgb): {
19
+ r: number;
20
+ g: number;
21
+ b: number;
22
+ };
23
+ declare function linearRgbToOklab({ r, g, b }: LinearRgb): Oklab;
24
+ declare function oklabToLinearRgb(L: number, a: number, b: number): LinearRgb;
25
+ declare function oklchToOklab(L: number, C: number, Hdeg: number): Oklab;
26
+ declare function oklabToOklch(L: number, a: number, b: number): {
27
+ L: number;
28
+ C: number;
29
+ H: number;
30
+ };
31
+ declare function isLinearRgbInSrgbGamut({ r, g, b }: LinearRgb, eps?: number): boolean;
32
+ /** Convert Display P3 linear RGB to linear sRGB via XYZ D65. */
33
+ declare function linearDisplayP3ToLinearSrgb(r: number, g: number, b: number): LinearRgb;
34
+ /**
35
+ * HSV (0-360, 0-1, 0-1) → RGB (0-1).
36
+ * Standard HSV-to-RGB conversion; the resulting RGB values are in whichever
37
+ * gamut the caller considers them to be (sRGB or Display P3).
38
+ */
39
+ declare function hsvToRgb(h: number, s: number, v: number): [number, number, number];
40
+ /**
41
+ * Check whether an HSV color (interpreted in the Display P3 gamut) falls
42
+ * inside the sRGB gamut. This is how Chrome DevTools draws the sRGB boundary
43
+ * on the color picker when in oklch/oklab mode.
44
+ */
45
+ declare function isDisplayP3HsvInSrgbGamut(h: number, s: number, v: number, eps?: number): boolean;
46
+
47
+ export { type LinearRgb, type Oklab, hsvToRgb, isDisplayP3HsvInSrgbGamut, isLinearRgbInSrgbGamut, linearChannelToSrgb, linearDisplayP3ToLinearSrgb, linearRgbToOklab, linearRgbToSrgb, oklabToLinearRgb, oklabToOklch, oklchToOklab, srgbChannelToLinear, srgbToLinearRgb };
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/oklab-math.ts
21
+ var oklab_math_exports = {};
22
+ __export(oklab_math_exports, {
23
+ hsvToRgb: () => hsvToRgb,
24
+ isDisplayP3HsvInSrgbGamut: () => isDisplayP3HsvInSrgbGamut,
25
+ isLinearRgbInSrgbGamut: () => isLinearRgbInSrgbGamut,
26
+ linearChannelToSrgb: () => linearChannelToSrgb,
27
+ linearDisplayP3ToLinearSrgb: () => linearDisplayP3ToLinearSrgb,
28
+ linearRgbToOklab: () => linearRgbToOklab,
29
+ linearRgbToSrgb: () => linearRgbToSrgb,
30
+ oklabToLinearRgb: () => oklabToLinearRgb,
31
+ oklabToOklch: () => oklabToOklch,
32
+ oklchToOklab: () => oklchToOklab,
33
+ srgbChannelToLinear: () => srgbChannelToLinear,
34
+ srgbToLinearRgb: () => srgbToLinearRgb
35
+ });
36
+ module.exports = __toCommonJS(oklab_math_exports);
37
+ function srgbChannelToLinear(c) {
38
+ return c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
39
+ }
40
+ function linearChannelToSrgb(c) {
41
+ return c <= 31308e-7 ? 12.92 * c : 1.055 * c ** (1 / 2.4) - 0.055;
42
+ }
43
+ function srgbToLinearRgb(r255, g255, b255) {
44
+ return {
45
+ r: srgbChannelToLinear(r255 / 255),
46
+ g: srgbChannelToLinear(g255 / 255),
47
+ b: srgbChannelToLinear(b255 / 255)
48
+ };
49
+ }
50
+ function linearRgbToSrgb({ r, g, b }) {
51
+ return {
52
+ r: Math.round(clamp01(linearChannelToSrgb(r)) * 255),
53
+ g: Math.round(clamp01(linearChannelToSrgb(g)) * 255),
54
+ b: Math.round(clamp01(linearChannelToSrgb(b)) * 255)
55
+ };
56
+ }
57
+ function clamp01(x) {
58
+ return x < 0 ? 0 : x > 1 ? 1 : x;
59
+ }
60
+ function linearRgbToOklab({ r, g, b }) {
61
+ const l = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b;
62
+ const m = 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b;
63
+ const s = 0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b;
64
+ const l_ = Math.cbrt(l);
65
+ const m_ = Math.cbrt(m);
66
+ const s_ = Math.cbrt(s);
67
+ return {
68
+ L: 0.2104542553 * l_ + 0.793617785 * m_ - 0.0040720468 * s_,
69
+ a: 1.9779984951 * l_ - 2.428592205 * m_ + 0.4505937099 * s_,
70
+ b: 0.0259040371 * l_ + 0.7827717662 * m_ - 0.808675766 * s_
71
+ };
72
+ }
73
+ function oklabToLinearRgb(L, a, b) {
74
+ const l_ = L + 0.3963377774 * a + 0.2158037573 * b;
75
+ const m_ = L - 0.1055613458 * a - 0.0638541728 * b;
76
+ const s_ = L - 0.0894841775 * a - 1.291485548 * b;
77
+ const l = l_ * l_ * l_;
78
+ const m = m_ * m_ * m_;
79
+ const s = s_ * s_ * s_;
80
+ return {
81
+ r: 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,
82
+ g: -1.2684380046 * l + 2.6097574011 * m - 0.341319396 * s,
83
+ b: -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s
84
+ };
85
+ }
86
+ function oklchToOklab(L, C, Hdeg) {
87
+ const h = Hdeg * Math.PI / 180;
88
+ return {
89
+ L,
90
+ a: C * Math.cos(h),
91
+ b: C * Math.sin(h)
92
+ };
93
+ }
94
+ function oklabToOklch(L, a, b) {
95
+ const C = Math.sqrt(a * a + b * b);
96
+ let H = Math.atan2(b, a) * 180 / Math.PI;
97
+ if (H < 0) H += 360;
98
+ return { L, C, H };
99
+ }
100
+ function isLinearRgbInSrgbGamut({ r, g, b }, eps = 1e-3) {
101
+ return r >= -eps && r <= 1 + eps && g >= -eps && g <= 1 + eps && b >= -eps && b <= 1 + eps;
102
+ }
103
+ function linearDisplayP3ToLinearSrgb(r, g, b) {
104
+ const x = 0.4865709486482162 * r + 0.26566769316909306 * g + 0.1982172852343625 * b;
105
+ const y = 0.2289745640697488 * r + 0.6917385218365064 * g + 0.079286914093745 * b;
106
+ const z = 0 * r + 0.04511338185890264 * g + 1.043944368900976 * b;
107
+ return {
108
+ r: 3.2404541621141054 * x - 1.5371385940306088 * y - 0.4985314095560158 * z,
109
+ g: -0.9692660305051868 * x + 1.8760108454466942 * y + 0.0415560175303498 * z,
110
+ b: 0.0556434309591147 * x - 0.2039769598730574 * y + 1.0572251882231791 * z
111
+ };
112
+ }
113
+ function hsvToRgb(h, s, v) {
114
+ const c = v * s;
115
+ const x = c * (1 - Math.abs(h / 60 % 2 - 1));
116
+ const m = v - c;
117
+ let r1 = 0, g1 = 0, b1 = 0;
118
+ if (h < 60) {
119
+ r1 = c;
120
+ g1 = x;
121
+ b1 = 0;
122
+ } else if (h < 120) {
123
+ r1 = x;
124
+ g1 = c;
125
+ b1 = 0;
126
+ } else if (h < 180) {
127
+ r1 = 0;
128
+ g1 = c;
129
+ b1 = x;
130
+ } else if (h < 240) {
131
+ r1 = 0;
132
+ g1 = x;
133
+ b1 = c;
134
+ } else if (h < 300) {
135
+ r1 = x;
136
+ g1 = 0;
137
+ b1 = c;
138
+ } else {
139
+ r1 = c;
140
+ g1 = 0;
141
+ b1 = x;
142
+ }
143
+ return [r1 + m, g1 + m, b1 + m];
144
+ }
145
+ function isDisplayP3HsvInSrgbGamut(h, s, v, eps = 1e-3) {
146
+ const [r, g, b] = hsvToRgb(h, s, v);
147
+ const lr = srgbChannelToLinear(r);
148
+ const lg = srgbChannelToLinear(g);
149
+ const lb = srgbChannelToLinear(b);
150
+ const srgb = linearDisplayP3ToLinearSrgb(lr, lg, lb);
151
+ return isLinearRgbInSrgbGamut(srgb, eps);
152
+ }
153
+ // Annotate the CommonJS export names for ESM import in node:
154
+ 0 && (module.exports = {
155
+ hsvToRgb,
156
+ isDisplayP3HsvInSrgbGamut,
157
+ isLinearRgbInSrgbGamut,
158
+ linearChannelToSrgb,
159
+ linearDisplayP3ToLinearSrgb,
160
+ linearRgbToOklab,
161
+ linearRgbToSrgb,
162
+ oklabToLinearRgb,
163
+ oklabToOklch,
164
+ oklchToOklab,
165
+ srgbChannelToLinear,
166
+ srgbToLinearRgb
167
+ });