@tenphi/tasty 0.13.1 → 0.14.1
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/README.md +155 -51
- package/dist/config.d.ts +13 -1
- package/dist/config.js +5 -1
- package/dist/config.js.map +1 -1
- package/dist/core/index.d.ts +5 -3
- package/dist/core/index.js +4 -3
- package/dist/debug.d.ts +26 -141
- package/dist/debug.js +356 -635
- package/dist/debug.js.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.js +4 -3
- package/dist/parser/classify.js +2 -1
- package/dist/parser/classify.js.map +1 -1
- package/dist/parser/parser.js +1 -1
- package/dist/plugins/okhsl-plugin.js +2 -275
- package/dist/plugins/okhsl-plugin.js.map +1 -1
- package/dist/plugins/types.d.ts +1 -1
- package/dist/properties/index.js +2 -15
- package/dist/properties/index.js.map +1 -1
- package/dist/ssr/format-property.js +9 -7
- package/dist/ssr/format-property.js.map +1 -1
- package/dist/styles/color.js +9 -5
- package/dist/styles/color.js.map +1 -1
- package/dist/styles/createStyle.js +24 -21
- package/dist/styles/createStyle.js.map +1 -1
- package/dist/styles/index.js +1 -1
- package/dist/styles/predefined.js +1 -1
- package/dist/styles/predefined.js.map +1 -1
- package/dist/styles/types.d.ts +19 -4
- package/dist/tasty.d.ts +9 -9
- package/dist/tasty.js +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/utils/color-math.d.ts +46 -0
- package/dist/utils/color-math.js +749 -0
- package/dist/utils/color-math.js.map +1 -0
- package/dist/utils/color-space.d.ts +5 -0
- package/dist/utils/color-space.js +229 -0
- package/dist/utils/color-space.js.map +1 -0
- package/dist/utils/colors.js +3 -1
- package/dist/utils/colors.js.map +1 -1
- package/dist/utils/mod-attrs.js +1 -1
- package/dist/utils/mod-attrs.js.map +1 -1
- package/dist/utils/process-tokens.d.ts +3 -13
- package/dist/utils/process-tokens.js +18 -98
- package/dist/utils/process-tokens.js.map +1 -1
- package/dist/utils/styles.d.ts +2 -15
- package/dist/utils/styles.js +22 -217
- package/dist/utils/styles.js.map +1 -1
- package/docs/PIPELINE.md +519 -0
- package/docs/README.md +31 -0
- package/docs/adoption.md +16 -6
- package/docs/comparison.md +16 -9
- package/docs/configuration.md +26 -3
- package/docs/debug.md +152 -339
- package/docs/design-system.md +1 -1
- package/docs/dsl.md +3 -1
- package/docs/getting-started.md +29 -13
- package/docs/injector.md +2 -2
- package/docs/methodology.md +3 -1
- package/docs/runtime.md +59 -9
- package/docs/ssr.md +3 -3
- package/docs/styles.md +1 -1
- package/docs/tasty-static.md +13 -2
- package/package.json +4 -3
- package/dist/utils/hsl-to-rgb.js +0 -38
- package/dist/utils/hsl-to-rgb.js.map +0 -1
- package/dist/utils/okhsl-to-rgb.js +0 -296
- package/dist/utils/okhsl-to-rgb.js.map +0 -1
|
@@ -0,0 +1,749 @@
|
|
|
1
|
+
//#region src/utils/color-math.ts
|
|
2
|
+
const OKLab_to_LMS_M = [
|
|
3
|
+
[
|
|
4
|
+
1,
|
|
5
|
+
.3963377773761749,
|
|
6
|
+
.2158037573099136
|
|
7
|
+
],
|
|
8
|
+
[
|
|
9
|
+
1,
|
|
10
|
+
-.1055613458156586,
|
|
11
|
+
-.0638541728258133
|
|
12
|
+
],
|
|
13
|
+
[
|
|
14
|
+
1,
|
|
15
|
+
-.0894841775298119,
|
|
16
|
+
-1.2914855480194092
|
|
17
|
+
]
|
|
18
|
+
];
|
|
19
|
+
const LMS_to_linear_sRGB_M = [
|
|
20
|
+
[
|
|
21
|
+
4.076741636075959,
|
|
22
|
+
-3.307711539258062,
|
|
23
|
+
.2309699031821041
|
|
24
|
+
],
|
|
25
|
+
[
|
|
26
|
+
-1.2684379732850313,
|
|
27
|
+
2.6097573492876878,
|
|
28
|
+
-.3413193760026569
|
|
29
|
+
],
|
|
30
|
+
[
|
|
31
|
+
-.004196076138675526,
|
|
32
|
+
-.703418617935936,
|
|
33
|
+
1.7076146940746113
|
|
34
|
+
]
|
|
35
|
+
];
|
|
36
|
+
const OKLab_to_linear_sRGB_coefficients = [
|
|
37
|
+
[[-1.8817030993265873, -.8093650129914302], [
|
|
38
|
+
1.19086277,
|
|
39
|
+
1.76576728,
|
|
40
|
+
.59662641,
|
|
41
|
+
.75515197,
|
|
42
|
+
.56771245
|
|
43
|
+
]],
|
|
44
|
+
[[1.8144407988010998, -1.194452667805235], [
|
|
45
|
+
.73956515,
|
|
46
|
+
-.45954404,
|
|
47
|
+
.08285427,
|
|
48
|
+
.1254107,
|
|
49
|
+
.14503204
|
|
50
|
+
]],
|
|
51
|
+
[[.13110757611180954, 1.813339709266608], [
|
|
52
|
+
1.35733652,
|
|
53
|
+
-.00915799,
|
|
54
|
+
-1.1513021,
|
|
55
|
+
-.50559606,
|
|
56
|
+
.00692167
|
|
57
|
+
]]
|
|
58
|
+
];
|
|
59
|
+
const dotXY = (a, b) => a[0] * b[0] + a[1] * b[1];
|
|
60
|
+
function srgbToLinear(c) {
|
|
61
|
+
return c <= .04045 ? c / 12.92 : ((c + .055) / 1.055) ** 2.4;
|
|
62
|
+
}
|
|
63
|
+
const INV_GAMMA = 1 / 2.4;
|
|
64
|
+
function srgbLinearToGamma(val) {
|
|
65
|
+
const sign = val < 0 ? -1 : 1;
|
|
66
|
+
const abs = Math.abs(val);
|
|
67
|
+
return abs > .0031308 ? sign * (1.055 * abs ** INV_GAMMA - .055) : 12.92 * val;
|
|
68
|
+
}
|
|
69
|
+
const TAU = 2 * Math.PI;
|
|
70
|
+
const K1 = .206;
|
|
71
|
+
const K2 = .03;
|
|
72
|
+
const K3 = (1 + K1) / (1 + K2);
|
|
73
|
+
const clamp = (value, min, max) => Math.max(Math.min(value, max), min);
|
|
74
|
+
const constrainAngle = (angle) => (angle % 360 + 360) % 360;
|
|
75
|
+
const toeInv = (x) => (x ** 2 + K1 * x) / (K3 * (x + K2));
|
|
76
|
+
const oklabToLinearSrgb = (lab) => {
|
|
77
|
+
const L = lab[0];
|
|
78
|
+
const a = lab[1];
|
|
79
|
+
const b = lab[2];
|
|
80
|
+
const l_ = L + .3963377773761749 * a + .2158037573099136 * b;
|
|
81
|
+
const m_ = L - .1055613458156586 * a - .0638541728258133 * b;
|
|
82
|
+
const s_ = L - .0894841775298119 * a - 1.2914855480194092 * b;
|
|
83
|
+
const l = l_ * l_ * l_;
|
|
84
|
+
const m = m_ * m_ * m_;
|
|
85
|
+
const s = s_ * s_ * s_;
|
|
86
|
+
return [
|
|
87
|
+
4.076741636075959 * l - 3.307711539258062 * m + .2309699031821041 * s,
|
|
88
|
+
-1.2684379732850313 * l + 2.6097573492876878 * m - .3413193760026569 * s,
|
|
89
|
+
-.004196076138675526 * l - .703418617935936 * m + 1.7076146940746113 * s
|
|
90
|
+
];
|
|
91
|
+
};
|
|
92
|
+
const linearSrgbToOklab = (rgb) => {
|
|
93
|
+
const r = rgb[0];
|
|
94
|
+
const g = rgb[1];
|
|
95
|
+
const b = rgb[2];
|
|
96
|
+
const l = .4122214708 * r + .5363325363 * g + .0514459929 * b;
|
|
97
|
+
const m = .2119034982 * r + .6806995451 * g + .1073969566 * b;
|
|
98
|
+
const s = .0883024619 * r + .2817188376 * g + .6299787005 * b;
|
|
99
|
+
const l_ = Math.cbrt(l);
|
|
100
|
+
const m_ = Math.cbrt(m);
|
|
101
|
+
const s_ = Math.cbrt(s);
|
|
102
|
+
return [
|
|
103
|
+
.2104542553 * l_ + .793617785 * m_ - .0040720468 * s_,
|
|
104
|
+
1.9779984951 * l_ - 2.428592205 * m_ + .4505937099 * s_,
|
|
105
|
+
.0259040371 * l_ + .7827717662 * m_ - .808675766 * s_
|
|
106
|
+
];
|
|
107
|
+
};
|
|
108
|
+
const computeMaxSaturationOKLC = (a, b) => {
|
|
109
|
+
const okCoeff = OKLab_to_linear_sRGB_coefficients;
|
|
110
|
+
const lmsToRgb = LMS_to_linear_sRGB_M;
|
|
111
|
+
const tmp2 = [a, b];
|
|
112
|
+
const tmp3 = [
|
|
113
|
+
0,
|
|
114
|
+
a,
|
|
115
|
+
b
|
|
116
|
+
];
|
|
117
|
+
let chnlCoeff;
|
|
118
|
+
let chnlLMS;
|
|
119
|
+
if (dotXY(okCoeff[0][0], tmp2) > 1) {
|
|
120
|
+
chnlCoeff = okCoeff[0][1];
|
|
121
|
+
chnlLMS = lmsToRgb[0];
|
|
122
|
+
} else if (dotXY(okCoeff[1][0], tmp2) > 1) {
|
|
123
|
+
chnlCoeff = okCoeff[1][1];
|
|
124
|
+
chnlLMS = lmsToRgb[1];
|
|
125
|
+
} else {
|
|
126
|
+
chnlCoeff = okCoeff[2][1];
|
|
127
|
+
chnlLMS = lmsToRgb[2];
|
|
128
|
+
}
|
|
129
|
+
const [k0, k1, k2, k3, k4] = chnlCoeff;
|
|
130
|
+
const [wl, wm, ws] = chnlLMS;
|
|
131
|
+
let sat = k0 + k1 * a + k2 * b + k3 * (a * a) + k4 * a * b;
|
|
132
|
+
const dotYZ = (mat, vec) => mat[1] * vec[1] + mat[2] * vec[2];
|
|
133
|
+
const kl = dotYZ(OKLab_to_LMS_M[0], tmp3);
|
|
134
|
+
const km = dotYZ(OKLab_to_LMS_M[1], tmp3);
|
|
135
|
+
const ks = dotYZ(OKLab_to_LMS_M[2], tmp3);
|
|
136
|
+
const l_ = 1 + sat * kl;
|
|
137
|
+
const m_ = 1 + sat * km;
|
|
138
|
+
const s_ = 1 + sat * ks;
|
|
139
|
+
const l = l_ ** 3;
|
|
140
|
+
const m = m_ ** 3;
|
|
141
|
+
const s = s_ ** 3;
|
|
142
|
+
const lds = 3 * kl * (l_ * l_);
|
|
143
|
+
const mds = 3 * km * (m_ * m_);
|
|
144
|
+
const sds = 3 * ks * (s_ * s_);
|
|
145
|
+
const lds2 = 6 * (kl * kl) * l_;
|
|
146
|
+
const mds2 = 6 * (km * km) * m_;
|
|
147
|
+
const sds2 = 6 * (ks * ks) * s_;
|
|
148
|
+
const f = wl * l + wm * m + ws * s;
|
|
149
|
+
const f1 = wl * lds + wm * mds + ws * sds;
|
|
150
|
+
const f2 = wl * lds2 + wm * mds2 + ws * sds2;
|
|
151
|
+
sat = sat - f * f1 / (f1 * f1 - .5 * f * f2);
|
|
152
|
+
return sat;
|
|
153
|
+
};
|
|
154
|
+
const findCuspOKLCH = (a, b) => {
|
|
155
|
+
const S_cusp = computeMaxSaturationOKLC(a, b);
|
|
156
|
+
const rgb_at_max = oklabToLinearSrgb([
|
|
157
|
+
1,
|
|
158
|
+
S_cusp * a,
|
|
159
|
+
S_cusp * b
|
|
160
|
+
]);
|
|
161
|
+
const L_cusp = Math.cbrt(1 / Math.max(Math.max(rgb_at_max[0], rgb_at_max[1]), Math.max(rgb_at_max[2], 0)));
|
|
162
|
+
return [L_cusp, L_cusp * S_cusp];
|
|
163
|
+
};
|
|
164
|
+
const findGamutIntersectionOKLCH = (a, b, l1, c1, l0, cusp) => {
|
|
165
|
+
const lmsToRgb = LMS_to_linear_sRGB_M;
|
|
166
|
+
const tmp3 = [
|
|
167
|
+
0,
|
|
168
|
+
a,
|
|
169
|
+
b
|
|
170
|
+
];
|
|
171
|
+
const floatMax = Number.MAX_VALUE;
|
|
172
|
+
let t;
|
|
173
|
+
const dotYZ = (mat, vec) => mat[1] * vec[1] + mat[2] * vec[2];
|
|
174
|
+
const dotXYZ = (vec, x, y, z) => vec[0] * x + vec[1] * y + vec[2] * z;
|
|
175
|
+
if ((l1 - l0) * cusp[1] - (cusp[0] - l0) * c1 <= 0) {
|
|
176
|
+
const denom = c1 * cusp[0] + cusp[1] * (l0 - l1);
|
|
177
|
+
t = denom === 0 ? 0 : cusp[1] * l0 / denom;
|
|
178
|
+
} else {
|
|
179
|
+
const denom = c1 * (cusp[0] - 1) + cusp[1] * (l0 - l1);
|
|
180
|
+
t = denom === 0 ? 0 : cusp[1] * (l0 - 1) / denom;
|
|
181
|
+
const dl = l1 - l0;
|
|
182
|
+
const dc = c1;
|
|
183
|
+
const kl = dotYZ(OKLab_to_LMS_M[0], tmp3);
|
|
184
|
+
const km = dotYZ(OKLab_to_LMS_M[1], tmp3);
|
|
185
|
+
const ks = dotYZ(OKLab_to_LMS_M[2], tmp3);
|
|
186
|
+
const ldt_ = dl + dc * kl;
|
|
187
|
+
const mdt_ = dl + dc * km;
|
|
188
|
+
const sdt_ = dl + dc * ks;
|
|
189
|
+
const L = l0 * (1 - t) + t * l1;
|
|
190
|
+
const C = t * c1;
|
|
191
|
+
const l_ = L + C * kl;
|
|
192
|
+
const m_ = L + C * km;
|
|
193
|
+
const s_ = L + C * ks;
|
|
194
|
+
const l = l_ ** 3;
|
|
195
|
+
const m = m_ ** 3;
|
|
196
|
+
const s = s_ ** 3;
|
|
197
|
+
const ldt = 3 * ldt_ * l_ * l_;
|
|
198
|
+
const mdt = 3 * mdt_ * m_ * m_;
|
|
199
|
+
const sdt = 3 * sdt_ * s_ * s_;
|
|
200
|
+
const ldt2 = 6 * ldt_ * ldt_ * l_;
|
|
201
|
+
const mdt2 = 6 * mdt_ * mdt_ * m_;
|
|
202
|
+
const sdt2 = 6 * sdt_ * sdt_ * s_;
|
|
203
|
+
const r_ = dotXYZ(lmsToRgb[0], l, m, s) - 1;
|
|
204
|
+
const r1 = dotXYZ(lmsToRgb[0], ldt, mdt, sdt);
|
|
205
|
+
const r2 = dotXYZ(lmsToRgb[0], ldt2, mdt2, sdt2);
|
|
206
|
+
const ur = r1 / (r1 * r1 - .5 * r_ * r2);
|
|
207
|
+
let tr = -r_ * ur;
|
|
208
|
+
const g_ = dotXYZ(lmsToRgb[1], l, m, s) - 1;
|
|
209
|
+
const g1 = dotXYZ(lmsToRgb[1], ldt, mdt, sdt);
|
|
210
|
+
const g2 = dotXYZ(lmsToRgb[1], ldt2, mdt2, sdt2);
|
|
211
|
+
const ug = g1 / (g1 * g1 - .5 * g_ * g2);
|
|
212
|
+
let tg = -g_ * ug;
|
|
213
|
+
const b_ = dotXYZ(lmsToRgb[2], l, m, s) - 1;
|
|
214
|
+
const b1 = dotXYZ(lmsToRgb[2], ldt, mdt, sdt);
|
|
215
|
+
const b2 = dotXYZ(lmsToRgb[2], ldt2, mdt2, sdt2);
|
|
216
|
+
const ub = b1 / (b1 * b1 - .5 * b_ * b2);
|
|
217
|
+
let tb = -b_ * ub;
|
|
218
|
+
tr = ur >= 0 ? tr : floatMax;
|
|
219
|
+
tg = ug >= 0 ? tg : floatMax;
|
|
220
|
+
tb = ub >= 0 ? tb : floatMax;
|
|
221
|
+
t += Math.min(tr, Math.min(tg, tb));
|
|
222
|
+
}
|
|
223
|
+
return t;
|
|
224
|
+
};
|
|
225
|
+
const computeSt = (cusp) => [cusp[1] / cusp[0], cusp[1] / (1 - cusp[0])];
|
|
226
|
+
const computeStMid = (a, b) => [.11516993 + 1 / (7.4477897 + 4.1590124 * b + a * (-2.19557347 + 1.75198401 * b + a * (-2.13704948 - 10.02301043 * b + a * (-4.24894561 + 5.38770819 * b + 4.69891013 * a)))), .11239642 + 1 / (1.6132032 - .68124379 * b + a * (.40370612 + .90148123 * b + a * (-.27087943 + .6122399 * b + a * (.00299215 - .45399568 * b - .14661872 * a))))];
|
|
227
|
+
const getCs = (L, a, b, cusp) => {
|
|
228
|
+
const cMax = findGamutIntersectionOKLCH(a, b, L, 1, L, cusp);
|
|
229
|
+
const stMax = computeSt(cusp);
|
|
230
|
+
const k = cMax / Math.min(L * stMax[0], (1 - L) * stMax[1]);
|
|
231
|
+
const stMid = computeStMid(a, b);
|
|
232
|
+
let ca = L * stMid[0];
|
|
233
|
+
let cb = (1 - L) * stMid[1];
|
|
234
|
+
const cMid = .9 * k * Math.sqrt(Math.sqrt(1 / (1 / ca ** 4 + 1 / cb ** 4)));
|
|
235
|
+
ca = L * .4;
|
|
236
|
+
cb = (1 - L) * .8;
|
|
237
|
+
return [
|
|
238
|
+
Math.sqrt(1 / (1 / ca ** 2 + 1 / cb ** 2)),
|
|
239
|
+
cMid,
|
|
240
|
+
cMax
|
|
241
|
+
];
|
|
242
|
+
};
|
|
243
|
+
const okhslToOklab = (hsl) => {
|
|
244
|
+
let h = hsl[0];
|
|
245
|
+
const s = hsl[1];
|
|
246
|
+
const l = hsl[2];
|
|
247
|
+
const L = toeInv(l);
|
|
248
|
+
let a = 0;
|
|
249
|
+
let b = 0;
|
|
250
|
+
h = constrainAngle(h) / 360;
|
|
251
|
+
if (L !== 0 && L !== 1 && s !== 0) {
|
|
252
|
+
const a_ = Math.cos(TAU * h);
|
|
253
|
+
const b_ = Math.sin(TAU * h);
|
|
254
|
+
const [c0, cMid, cMax] = getCs(L, a_, b_, findCuspOKLCH(a_, b_));
|
|
255
|
+
const mid = .8;
|
|
256
|
+
const midInv = 1.25;
|
|
257
|
+
let t, k0, k1, k2;
|
|
258
|
+
if (s < mid) {
|
|
259
|
+
t = midInv * s;
|
|
260
|
+
k0 = 0;
|
|
261
|
+
k1 = mid * c0;
|
|
262
|
+
k2 = 1 - k1 / cMid;
|
|
263
|
+
} else {
|
|
264
|
+
t = 5 * (s - .8);
|
|
265
|
+
k0 = cMid;
|
|
266
|
+
k1 = .2 * cMid ** 2 * 1.25 ** 2 / c0;
|
|
267
|
+
k2 = 1 - k1 / (cMax - cMid);
|
|
268
|
+
}
|
|
269
|
+
const c = k0 + t * k1 / (1 - k2 * t);
|
|
270
|
+
a = c * a_;
|
|
271
|
+
b = c * b_;
|
|
272
|
+
}
|
|
273
|
+
return [
|
|
274
|
+
L,
|
|
275
|
+
a,
|
|
276
|
+
b
|
|
277
|
+
];
|
|
278
|
+
};
|
|
279
|
+
/**
|
|
280
|
+
* HSL to RGB.
|
|
281
|
+
* Algorithm from CSS Color 4 spec.
|
|
282
|
+
*
|
|
283
|
+
* @param h - Hue in degrees (0-360)
|
|
284
|
+
* @param s - Saturation (0-1)
|
|
285
|
+
* @param l - Lightness (0-1)
|
|
286
|
+
* @returns RGB values in 0-255 range (may have fractional values)
|
|
287
|
+
*/
|
|
288
|
+
function hslToRgbValues(h, s, l) {
|
|
289
|
+
const a = s * Math.min(l, 1 - l);
|
|
290
|
+
const f = (n) => {
|
|
291
|
+
const k = (n + h / 30) % 12;
|
|
292
|
+
return l - a * Math.max(-1, Math.min(k - 3, 9 - k, 1));
|
|
293
|
+
};
|
|
294
|
+
return [
|
|
295
|
+
f(0) * 255,
|
|
296
|
+
f(8) * 255,
|
|
297
|
+
f(4) * 255
|
|
298
|
+
];
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* RGB (0-255) to HSL.
|
|
302
|
+
* @returns [h (0-360), s (0-1), l (0-1)]
|
|
303
|
+
*/
|
|
304
|
+
function rgbToHsl(r, g, b) {
|
|
305
|
+
r /= 255;
|
|
306
|
+
g /= 255;
|
|
307
|
+
b /= 255;
|
|
308
|
+
const max = Math.max(r, g, b);
|
|
309
|
+
const min = Math.min(r, g, b);
|
|
310
|
+
const l = (max + min) / 2;
|
|
311
|
+
if (max === min) return [
|
|
312
|
+
0,
|
|
313
|
+
0,
|
|
314
|
+
l
|
|
315
|
+
];
|
|
316
|
+
const d = max - min;
|
|
317
|
+
const s = l > .5 ? d / (2 - max - min) : d / (max + min);
|
|
318
|
+
let h;
|
|
319
|
+
if (max === r) h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
|
|
320
|
+
else if (max === g) h = ((b - r) / d + 2) / 6;
|
|
321
|
+
else h = ((r - g) / d + 4) / 6;
|
|
322
|
+
return [
|
|
323
|
+
h * 360,
|
|
324
|
+
s,
|
|
325
|
+
l
|
|
326
|
+
];
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* RGB (0-255) to OKLCH via sRGB -> linear sRGB -> OKLab -> OKLCH.
|
|
330
|
+
* @returns [L, C, H] where H is in degrees (0-360)
|
|
331
|
+
*/
|
|
332
|
+
function rgbToOklch(r, g, b) {
|
|
333
|
+
const [L, a, bLab] = linearSrgbToOklab([
|
|
334
|
+
srgbToLinear(r / 255),
|
|
335
|
+
srgbToLinear(g / 255),
|
|
336
|
+
srgbToLinear(b / 255)
|
|
337
|
+
]);
|
|
338
|
+
const C = Math.sqrt(a * a + bLab * bLab);
|
|
339
|
+
let H = Math.atan2(bLab, a) * 180 / Math.PI;
|
|
340
|
+
if (H < 0) H += 360;
|
|
341
|
+
return [
|
|
342
|
+
L,
|
|
343
|
+
C,
|
|
344
|
+
H
|
|
345
|
+
];
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* OKHSL to sRGB (0-1 range).
|
|
349
|
+
* @param h - Hue in degrees (0-360)
|
|
350
|
+
* @param s - Saturation (0-1)
|
|
351
|
+
* @param l - Lightness (0-1)
|
|
352
|
+
* @returns sRGB values in 0-1 range, clamped to gamut
|
|
353
|
+
*/
|
|
354
|
+
function okhslToSrgb(h, s, l) {
|
|
355
|
+
const linearRGB = oklabToLinearSrgb(okhslToOklab([
|
|
356
|
+
h,
|
|
357
|
+
s,
|
|
358
|
+
l
|
|
359
|
+
]));
|
|
360
|
+
return [
|
|
361
|
+
clamp(srgbLinearToGamma(linearRGB[0]), 0, 1),
|
|
362
|
+
clamp(srgbLinearToGamma(linearRGB[1]), 0, 1),
|
|
363
|
+
clamp(srgbLinearToGamma(linearRGB[2]), 0, 1)
|
|
364
|
+
];
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* OKLCH to sRGB (0-255 range).
|
|
368
|
+
* @param L - Lightness (0-1)
|
|
369
|
+
* @param C - Chroma (typically 0-0.4)
|
|
370
|
+
* @param H - Hue in degrees (0-360)
|
|
371
|
+
* @returns RGB values in 0-255 range, clamped to gamut
|
|
372
|
+
*/
|
|
373
|
+
function oklchToRgbValues(L, C, H) {
|
|
374
|
+
const hRad = H * Math.PI / 180;
|
|
375
|
+
const linear = oklabToLinearSrgb([
|
|
376
|
+
L,
|
|
377
|
+
C * Math.cos(hRad),
|
|
378
|
+
C * Math.sin(hRad)
|
|
379
|
+
]);
|
|
380
|
+
return [
|
|
381
|
+
clamp(srgbLinearToGamma(linear[0]), 0, 1) * 255,
|
|
382
|
+
clamp(srgbLinearToGamma(linear[1]), 0, 1) * 255,
|
|
383
|
+
clamp(srgbLinearToGamma(linear[2]), 0, 1) * 255
|
|
384
|
+
];
|
|
385
|
+
}
|
|
386
|
+
let _namedColorHex = null;
|
|
387
|
+
function getNamedColorHex() {
|
|
388
|
+
if (_namedColorHex) return _namedColorHex;
|
|
389
|
+
_namedColorHex = new Map([
|
|
390
|
+
["aliceblue", "#f0f8ff"],
|
|
391
|
+
["antiquewhite", "#faebd7"],
|
|
392
|
+
["aqua", "#00ffff"],
|
|
393
|
+
["aquamarine", "#7fffd4"],
|
|
394
|
+
["azure", "#f0ffff"],
|
|
395
|
+
["beige", "#f5f5dc"],
|
|
396
|
+
["bisque", "#ffe4c4"],
|
|
397
|
+
["black", "#000000"],
|
|
398
|
+
["blanchedalmond", "#ffebcd"],
|
|
399
|
+
["blue", "#0000ff"],
|
|
400
|
+
["blueviolet", "#8a2be2"],
|
|
401
|
+
["brown", "#a52a2a"],
|
|
402
|
+
["burlywood", "#deb887"],
|
|
403
|
+
["cadetblue", "#5f9ea0"],
|
|
404
|
+
["chartreuse", "#7fff00"],
|
|
405
|
+
["chocolate", "#d2691e"],
|
|
406
|
+
["coral", "#ff7f50"],
|
|
407
|
+
["cornflowerblue", "#6495ed"],
|
|
408
|
+
["cornsilk", "#fff8dc"],
|
|
409
|
+
["crimson", "#dc143c"],
|
|
410
|
+
["cyan", "#00ffff"],
|
|
411
|
+
["darkblue", "#00008b"],
|
|
412
|
+
["darkcyan", "#008b8b"],
|
|
413
|
+
["darkgoldenrod", "#b8860b"],
|
|
414
|
+
["darkgray", "#a9a9a9"],
|
|
415
|
+
["darkgreen", "#006400"],
|
|
416
|
+
["darkgrey", "#a9a9a9"],
|
|
417
|
+
["darkkhaki", "#bdb76b"],
|
|
418
|
+
["darkmagenta", "#8b008b"],
|
|
419
|
+
["darkolivegreen", "#556b2f"],
|
|
420
|
+
["darkorange", "#ff8c00"],
|
|
421
|
+
["darkorchid", "#9932cc"],
|
|
422
|
+
["darkred", "#8b0000"],
|
|
423
|
+
["darksalmon", "#e9967a"],
|
|
424
|
+
["darkseagreen", "#8fbc8f"],
|
|
425
|
+
["darkslateblue", "#483d8b"],
|
|
426
|
+
["darkslategray", "#2f4f4f"],
|
|
427
|
+
["darkslategrey", "#2f4f4f"],
|
|
428
|
+
["darkturquoise", "#00ced1"],
|
|
429
|
+
["darkviolet", "#9400d3"],
|
|
430
|
+
["deeppink", "#ff1493"],
|
|
431
|
+
["deepskyblue", "#00bfff"],
|
|
432
|
+
["dimgray", "#696969"],
|
|
433
|
+
["dimgrey", "#696969"],
|
|
434
|
+
["dodgerblue", "#1e90ff"],
|
|
435
|
+
["firebrick", "#b22222"],
|
|
436
|
+
["floralwhite", "#fffaf0"],
|
|
437
|
+
["forestgreen", "#228b22"],
|
|
438
|
+
["fuchsia", "#ff00ff"],
|
|
439
|
+
["gainsboro", "#dcdcdc"],
|
|
440
|
+
["ghostwhite", "#f8f8ff"],
|
|
441
|
+
["gold", "#ffd700"],
|
|
442
|
+
["goldenrod", "#daa520"],
|
|
443
|
+
["gray", "#808080"],
|
|
444
|
+
["green", "#008000"],
|
|
445
|
+
["greenyellow", "#adff2f"],
|
|
446
|
+
["grey", "#808080"],
|
|
447
|
+
["honeydew", "#f0fff0"],
|
|
448
|
+
["hotpink", "#ff69b4"],
|
|
449
|
+
["indianred", "#cd5c5c"],
|
|
450
|
+
["indigo", "#4b0082"],
|
|
451
|
+
["ivory", "#fffff0"],
|
|
452
|
+
["khaki", "#f0e68c"],
|
|
453
|
+
["lavender", "#e6e6fa"],
|
|
454
|
+
["lavenderblush", "#fff0f5"],
|
|
455
|
+
["lawngreen", "#7cfc00"],
|
|
456
|
+
["lemonchiffon", "#fffacd"],
|
|
457
|
+
["lightblue", "#add8e6"],
|
|
458
|
+
["lightcoral", "#f08080"],
|
|
459
|
+
["lightcyan", "#e0ffff"],
|
|
460
|
+
["lightgoldenrodyellow", "#fafad2"],
|
|
461
|
+
["lightgray", "#d3d3d3"],
|
|
462
|
+
["lightgreen", "#90ee90"],
|
|
463
|
+
["lightgrey", "#d3d3d3"],
|
|
464
|
+
["lightpink", "#ffb6c1"],
|
|
465
|
+
["lightsalmon", "#ffa07a"],
|
|
466
|
+
["lightseagreen", "#20b2aa"],
|
|
467
|
+
["lightskyblue", "#87cefa"],
|
|
468
|
+
["lightslategray", "#778899"],
|
|
469
|
+
["lightslategrey", "#778899"],
|
|
470
|
+
["lightsteelblue", "#b0c4de"],
|
|
471
|
+
["lightyellow", "#ffffe0"],
|
|
472
|
+
["lime", "#00ff00"],
|
|
473
|
+
["limegreen", "#32cd32"],
|
|
474
|
+
["linen", "#faf0e6"],
|
|
475
|
+
["magenta", "#ff00ff"],
|
|
476
|
+
["maroon", "#800000"],
|
|
477
|
+
["mediumaquamarine", "#66cdaa"],
|
|
478
|
+
["mediumblue", "#0000cd"],
|
|
479
|
+
["mediumorchid", "#ba55d3"],
|
|
480
|
+
["mediumpurple", "#9370db"],
|
|
481
|
+
["mediumseagreen", "#3cb371"],
|
|
482
|
+
["mediumslateblue", "#7b68ee"],
|
|
483
|
+
["mediumspringgreen", "#00fa9a"],
|
|
484
|
+
["mediumturquoise", "#48d1cc"],
|
|
485
|
+
["mediumvioletred", "#c71585"],
|
|
486
|
+
["midnightblue", "#191970"],
|
|
487
|
+
["mintcream", "#f5fffa"],
|
|
488
|
+
["mistyrose", "#ffe4e1"],
|
|
489
|
+
["moccasin", "#ffe4b5"],
|
|
490
|
+
["navajowhite", "#ffdead"],
|
|
491
|
+
["navy", "#000080"],
|
|
492
|
+
["oldlace", "#fdf5e6"],
|
|
493
|
+
["olive", "#808000"],
|
|
494
|
+
["olivedrab", "#6b8e23"],
|
|
495
|
+
["orange", "#ffa500"],
|
|
496
|
+
["orangered", "#ff4500"],
|
|
497
|
+
["orchid", "#da70d6"],
|
|
498
|
+
["palegoldenrod", "#eee8aa"],
|
|
499
|
+
["palegreen", "#98fb98"],
|
|
500
|
+
["paleturquoise", "#afeeee"],
|
|
501
|
+
["palevioletred", "#db7093"],
|
|
502
|
+
["papayawhip", "#ffefd5"],
|
|
503
|
+
["peachpuff", "#ffdab9"],
|
|
504
|
+
["peru", "#cd853f"],
|
|
505
|
+
["pink", "#ffc0cb"],
|
|
506
|
+
["plum", "#dda0dd"],
|
|
507
|
+
["powderblue", "#b0e0e6"],
|
|
508
|
+
["purple", "#800080"],
|
|
509
|
+
["rebeccapurple", "#663399"],
|
|
510
|
+
["red", "#ff0000"],
|
|
511
|
+
["rosybrown", "#bc8f8f"],
|
|
512
|
+
["royalblue", "#4169e1"],
|
|
513
|
+
["saddlebrown", "#8b4513"],
|
|
514
|
+
["salmon", "#fa8072"],
|
|
515
|
+
["sandybrown", "#f4a460"],
|
|
516
|
+
["seagreen", "#2e8b57"],
|
|
517
|
+
["seashell", "#fff5ee"],
|
|
518
|
+
["sienna", "#a0522d"],
|
|
519
|
+
["silver", "#c0c0c0"],
|
|
520
|
+
["skyblue", "#87ceeb"],
|
|
521
|
+
["slateblue", "#6a5acd"],
|
|
522
|
+
["slategray", "#708090"],
|
|
523
|
+
["slategrey", "#708090"],
|
|
524
|
+
["snow", "#fffafa"],
|
|
525
|
+
["springgreen", "#00ff7f"],
|
|
526
|
+
["steelblue", "#4682b4"],
|
|
527
|
+
["tan", "#d2b48c"],
|
|
528
|
+
["teal", "#008080"],
|
|
529
|
+
["thistle", "#d8bfd8"],
|
|
530
|
+
["tomato", "#ff6347"],
|
|
531
|
+
["turquoise", "#40e0d0"],
|
|
532
|
+
["violet", "#ee82ee"],
|
|
533
|
+
["wheat", "#f5deb3"],
|
|
534
|
+
["white", "#ffffff"],
|
|
535
|
+
["whitesmoke", "#f5f5f5"],
|
|
536
|
+
["yellow", "#ffff00"],
|
|
537
|
+
["yellowgreen", "#9acd32"]
|
|
538
|
+
]);
|
|
539
|
+
return _namedColorHex;
|
|
540
|
+
}
|
|
541
|
+
const hexCharToNum = (c) => {
|
|
542
|
+
if (c >= 48 && c <= 57) return c - 48;
|
|
543
|
+
if (c >= 65 && c <= 70) return c - 55;
|
|
544
|
+
if (c >= 97 && c <= 102) return c - 87;
|
|
545
|
+
return -1;
|
|
546
|
+
};
|
|
547
|
+
/**
|
|
548
|
+
* Parse a hex color string to RGBA values (RGB 0-255, alpha 0-1).
|
|
549
|
+
* Supports 3, 4, 6, and 8 character hex values (with or without `#`).
|
|
550
|
+
* For 3/6-char hex (no alpha channel), alpha defaults to 1.
|
|
551
|
+
*/
|
|
552
|
+
function hexToRgbaValues(hex) {
|
|
553
|
+
let start = 0;
|
|
554
|
+
if (hex.charCodeAt(0) === 35) start = 1;
|
|
555
|
+
const len = hex.length - start;
|
|
556
|
+
if (len === 3) {
|
|
557
|
+
const r = hexCharToNum(hex.charCodeAt(start));
|
|
558
|
+
const g = hexCharToNum(hex.charCodeAt(start + 1));
|
|
559
|
+
const b = hexCharToNum(hex.charCodeAt(start + 2));
|
|
560
|
+
if (r < 0 || g < 0 || b < 0) return null;
|
|
561
|
+
return [
|
|
562
|
+
r * 17,
|
|
563
|
+
g * 17,
|
|
564
|
+
b * 17,
|
|
565
|
+
1
|
|
566
|
+
];
|
|
567
|
+
}
|
|
568
|
+
if (len === 4) {
|
|
569
|
+
const r = hexCharToNum(hex.charCodeAt(start));
|
|
570
|
+
const g = hexCharToNum(hex.charCodeAt(start + 1));
|
|
571
|
+
const b = hexCharToNum(hex.charCodeAt(start + 2));
|
|
572
|
+
const a = hexCharToNum(hex.charCodeAt(start + 3));
|
|
573
|
+
if (r < 0 || g < 0 || b < 0 || a < 0) return null;
|
|
574
|
+
return [
|
|
575
|
+
r * 17,
|
|
576
|
+
g * 17,
|
|
577
|
+
b * 17,
|
|
578
|
+
a * 17 / 255
|
|
579
|
+
];
|
|
580
|
+
}
|
|
581
|
+
if (len === 6) {
|
|
582
|
+
const r1 = hexCharToNum(hex.charCodeAt(start));
|
|
583
|
+
const r2 = hexCharToNum(hex.charCodeAt(start + 1));
|
|
584
|
+
const g1 = hexCharToNum(hex.charCodeAt(start + 2));
|
|
585
|
+
const g2 = hexCharToNum(hex.charCodeAt(start + 3));
|
|
586
|
+
const b1 = hexCharToNum(hex.charCodeAt(start + 4));
|
|
587
|
+
const b2 = hexCharToNum(hex.charCodeAt(start + 5));
|
|
588
|
+
if (r1 < 0 || r2 < 0 || g1 < 0 || g2 < 0 || b1 < 0 || b2 < 0) return null;
|
|
589
|
+
return [
|
|
590
|
+
r1 * 16 + r2,
|
|
591
|
+
g1 * 16 + g2,
|
|
592
|
+
b1 * 16 + b2,
|
|
593
|
+
1
|
|
594
|
+
];
|
|
595
|
+
}
|
|
596
|
+
if (len === 8) {
|
|
597
|
+
const r1 = hexCharToNum(hex.charCodeAt(start));
|
|
598
|
+
const r2 = hexCharToNum(hex.charCodeAt(start + 1));
|
|
599
|
+
const g1 = hexCharToNum(hex.charCodeAt(start + 2));
|
|
600
|
+
const g2 = hexCharToNum(hex.charCodeAt(start + 3));
|
|
601
|
+
const b1 = hexCharToNum(hex.charCodeAt(start + 4));
|
|
602
|
+
const b2 = hexCharToNum(hex.charCodeAt(start + 5));
|
|
603
|
+
const a1 = hexCharToNum(hex.charCodeAt(start + 6));
|
|
604
|
+
const a2 = hexCharToNum(hex.charCodeAt(start + 7));
|
|
605
|
+
if (r1 < 0 || r2 < 0 || g1 < 0 || g2 < 0 || b1 < 0 || b2 < 0 || a1 < 0 || a2 < 0) return null;
|
|
606
|
+
return [
|
|
607
|
+
r1 * 16 + r2,
|
|
608
|
+
g1 * 16 + g2,
|
|
609
|
+
b1 * 16 + b2,
|
|
610
|
+
(a1 * 16 + a2) / 255
|
|
611
|
+
];
|
|
612
|
+
}
|
|
613
|
+
return null;
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* Convert hex color string to `rgb()` CSS string.
|
|
617
|
+
* Supports 3, 4, 6, and 8 character hex values (with or without `#`).
|
|
618
|
+
*/
|
|
619
|
+
function hexToRgb(hex) {
|
|
620
|
+
const matched = hex.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i, (_m, r, g, b) => "#" + r + r + g + g + b + b).substring(1).match(/.{2}/g);
|
|
621
|
+
if (!matched) return null;
|
|
622
|
+
const rgba = matched.map((x, i) => parseInt(x, 16) * (i === 3 ? 1 / 255 : 1));
|
|
623
|
+
if (rgba.some((v) => Number.isNaN(v))) return null;
|
|
624
|
+
if (rgba.length >= 3) return `rgb(${rgba.slice(0, 3).join(" ")}${rgba.length > 3 ? ` / ${rgba[3]}` : ""})`;
|
|
625
|
+
return null;
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Extract RGB values from an `rgb()`/`rgba()` string.
|
|
629
|
+
* Supports comma-separated, space-separated, fractional, percentage,
|
|
630
|
+
* and slash alpha notation.
|
|
631
|
+
*
|
|
632
|
+
* @returns Array of RGB values (0-255 range), converting percentages as needed.
|
|
633
|
+
*/
|
|
634
|
+
function getRgbValuesFromRgbaString(str) {
|
|
635
|
+
const match = str.match(/rgba?\(([^)]+)\)/i);
|
|
636
|
+
if (!match) return [];
|
|
637
|
+
const [colorPart] = match[1].trim().split("/");
|
|
638
|
+
return colorPart.trim().split(/[,\s]+/).filter(Boolean).slice(0, 3).map((part) => {
|
|
639
|
+
part = part.trim();
|
|
640
|
+
if (part.endsWith("%")) return parseFloat(part) / 100 * 255;
|
|
641
|
+
return parseFloat(part);
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Convert any recognized color string to an `rgb()` CSS string.
|
|
646
|
+
* Handles hex, `okhsl()`, `hsl()`/`hsla()`, named CSS colors,
|
|
647
|
+
* and `rgb()`/`rgba()` pass-through.
|
|
648
|
+
*/
|
|
649
|
+
function strToRgb(color, _ignoreAlpha = false) {
|
|
650
|
+
if (!color) return void 0;
|
|
651
|
+
if (color.startsWith("rgb")) return color;
|
|
652
|
+
if (color.startsWith("#")) return hexToRgb(color);
|
|
653
|
+
if (color.startsWith("oklch(")) return oklchStringToRgb(color);
|
|
654
|
+
if (color.startsWith("okhsl(")) return okhslStringToRgb(color);
|
|
655
|
+
if (color.startsWith("hsl")) return hslStringToRgb(color);
|
|
656
|
+
const namedHex = getNamedColorHex().get(color.toLowerCase());
|
|
657
|
+
if (namedHex) return hexToRgb(namedHex);
|
|
658
|
+
return null;
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* Convert an HSL/HSLA color string to an `rgb()`/`rgba()` CSS string.
|
|
662
|
+
* Supports modern space-separated and legacy comma-separated syntax,
|
|
663
|
+
* deg/turn/rad hue units, and slash alpha notation.
|
|
664
|
+
*/
|
|
665
|
+
function hslStringToRgb(hslStr) {
|
|
666
|
+
const match = hslStr.match(/hsla?\(([^)]+)\)/i);
|
|
667
|
+
if (!match) return null;
|
|
668
|
+
const [colorPart, slashAlpha] = match[1].trim().split("/");
|
|
669
|
+
const parts = colorPart.trim().split(/[,\s]+/).filter(Boolean);
|
|
670
|
+
if (parts.length < 3) return null;
|
|
671
|
+
const alphaPart = slashAlpha?.trim() || (parts.length >= 4 ? parts[3] : null);
|
|
672
|
+
let h = parseFloat(parts[0]);
|
|
673
|
+
const hueStr = parts[0].toLowerCase();
|
|
674
|
+
if (hueStr.endsWith("turn")) h = parseFloat(hueStr) * 360;
|
|
675
|
+
else if (hueStr.endsWith("rad")) h = parseFloat(hueStr) * 180 / Math.PI;
|
|
676
|
+
h = (h % 360 + 360) % 360;
|
|
677
|
+
const parsePercent = (val) => {
|
|
678
|
+
const num = parseFloat(val);
|
|
679
|
+
return val.includes("%") ? num / 100 : num;
|
|
680
|
+
};
|
|
681
|
+
const s = Math.max(0, Math.min(1, parsePercent(parts[1])));
|
|
682
|
+
const l = Math.max(0, Math.min(1, parsePercent(parts[2])));
|
|
683
|
+
const [r, g, b] = hslToRgbValues(h, s, l);
|
|
684
|
+
if (alphaPart) {
|
|
685
|
+
const alpha = parseFloat(alphaPart.trim());
|
|
686
|
+
return `rgba(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)}, ${alpha})`;
|
|
687
|
+
}
|
|
688
|
+
return `rgb(${Math.round(r)} ${Math.round(g)} ${Math.round(b)})`;
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* Convert an `okhsl()` color string to an `rgb()`/`rgba()` CSS string.
|
|
692
|
+
* Supports deg/turn/rad hue units and percentage saturation/lightness.
|
|
693
|
+
*/
|
|
694
|
+
function okhslStringToRgb(okhslStr) {
|
|
695
|
+
const match = okhslStr.match(/okhsl\(([^)]+)\)/i);
|
|
696
|
+
if (!match) return null;
|
|
697
|
+
const [colorPart, alphaPart] = match[1].trim().split("/");
|
|
698
|
+
const parts = colorPart.trim().split(/[,\s]+/).filter(Boolean);
|
|
699
|
+
if (parts.length < 3) return null;
|
|
700
|
+
let h = parseFloat(parts[0]);
|
|
701
|
+
const hueStr = parts[0].toLowerCase();
|
|
702
|
+
if (hueStr.endsWith("turn")) h = parseFloat(hueStr) * 360;
|
|
703
|
+
else if (hueStr.endsWith("rad")) h = parseFloat(hueStr) * 180 / Math.PI;
|
|
704
|
+
else if (hueStr.endsWith("deg")) h = parseFloat(hueStr);
|
|
705
|
+
const parsePercent = (val) => {
|
|
706
|
+
const num = parseFloat(val);
|
|
707
|
+
return val.includes("%") ? num / 100 : num;
|
|
708
|
+
};
|
|
709
|
+
const s = Math.max(0, Math.min(1, parsePercent(parts[1])));
|
|
710
|
+
const l = Math.max(0, Math.min(1, parsePercent(parts[2])));
|
|
711
|
+
const [r, g, b] = okhslToSrgb(h, s, l);
|
|
712
|
+
const r255 = Math.round(Math.max(0, Math.min(1, r)) * 255);
|
|
713
|
+
const g255 = Math.round(Math.max(0, Math.min(1, g)) * 255);
|
|
714
|
+
const b255 = Math.round(Math.max(0, Math.min(1, b)) * 255);
|
|
715
|
+
if (alphaPart) return `rgba(${r255}, ${g255}, ${b255}, ${parseFloat(alphaPart.trim())})`;
|
|
716
|
+
return `rgb(${r255} ${g255} ${b255})`;
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* Convert an `oklch()` color string to an `rgb()`/`rgba()` CSS string.
|
|
720
|
+
* Supports deg/turn/rad hue units and percentage lightness.
|
|
721
|
+
*/
|
|
722
|
+
function oklchStringToRgb(oklchStr) {
|
|
723
|
+
const match = oklchStr.match(/oklch\(([^)]+)\)/i);
|
|
724
|
+
if (!match) return null;
|
|
725
|
+
const [colorPart, alphaPart] = match[1].trim().split("/");
|
|
726
|
+
const parts = colorPart.trim().split(/[,\s]+/).filter(Boolean);
|
|
727
|
+
if (parts.length < 3) return null;
|
|
728
|
+
const parsePercent = (val) => {
|
|
729
|
+
const num = parseFloat(val);
|
|
730
|
+
return val.includes("%") ? num / 100 : num;
|
|
731
|
+
};
|
|
732
|
+
const L = Math.max(0, Math.min(1, parsePercent(parts[0])));
|
|
733
|
+
const C = Math.max(0, parseFloat(parts[1]));
|
|
734
|
+
let H = parseFloat(parts[2]);
|
|
735
|
+
const hueStr = parts[2].toLowerCase();
|
|
736
|
+
if (hueStr.endsWith("turn")) H = parseFloat(hueStr) * 360;
|
|
737
|
+
else if (hueStr.endsWith("rad")) H = parseFloat(hueStr) * 180 / Math.PI;
|
|
738
|
+
else if (hueStr.endsWith("deg")) H = parseFloat(hueStr);
|
|
739
|
+
const [r, g, b] = oklchToRgbValues(L, C, H);
|
|
740
|
+
if (alphaPart) {
|
|
741
|
+
const alpha = parseFloat(alphaPart.trim());
|
|
742
|
+
return `rgba(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)}, ${alpha})`;
|
|
743
|
+
}
|
|
744
|
+
return `rgb(${Math.round(r)} ${Math.round(g)} ${Math.round(b)})`;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
//#endregion
|
|
748
|
+
export { getNamedColorHex, getRgbValuesFromRgbaString, hexToRgb, hexToRgbaValues, hslToRgbValues, okhslToSrgb, oklchToRgbValues, rgbToHsl, rgbToOklch, strToRgb };
|
|
749
|
+
//# sourceMappingURL=color-math.js.map
|