@tenphi/tasty 0.0.0-snapshot.27b6708 → 0.0.0-snapshot.28eb83c

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 (106) hide show
  1. package/README.md +38 -54
  2. package/dist/{babel-BUQGeOXA.d.ts → babel-D6lOuSwk.d.ts} +2 -2
  3. package/dist/chunks/async-storage-DKK-wTD4.js.map +1 -0
  4. package/dist/chunks/build-config-BBThSdlo.js +45 -0
  5. package/dist/chunks/build-config-BBThSdlo.js.map +1 -0
  6. package/dist/{collector-DTahQUiV.js → chunks/collector-DcViW2ZQ.js} +28 -14
  7. package/dist/chunks/collector-DcViW2ZQ.js.map +1 -0
  8. package/dist/chunks/config-engine-DQVQK4NU.js +403 -0
  9. package/dist/chunks/config-engine-DQVQK4NU.js.map +1 -0
  10. package/dist/chunks/css-definitions-BZ9vm0ci.js +1284 -0
  11. package/dist/chunks/css-definitions-BZ9vm0ci.js.map +1 -0
  12. package/dist/chunks/css-resources-Cyl_axbI.js +149 -0
  13. package/dist/chunks/css-resources-Cyl_axbI.js.map +1 -0
  14. package/dist/chunks/debug-Vyauml7U.js +583 -0
  15. package/dist/chunks/debug-Vyauml7U.js.map +1 -0
  16. package/dist/chunks/dsl-CXvoaLnj.js +2097 -0
  17. package/dist/chunks/dsl-CXvoaLnj.js.map +1 -0
  18. package/dist/chunks/hydration-CLdVKcH2.js +59 -0
  19. package/dist/chunks/hydration-CLdVKcH2.js.map +1 -0
  20. package/dist/chunks/react-runtime-so_X_yMo.js +1526 -0
  21. package/dist/chunks/react-runtime-so_X_yMo.js.map +1 -0
  22. package/dist/chunks/runtime-engine-DQ5laRou.js +3056 -0
  23. package/dist/chunks/runtime-engine-DQ5laRou.js.map +1 -0
  24. package/dist/{merge-styles-oklji0KB.js → chunks/shared-utils-OIg0N_dp.js} +42 -3
  25. package/dist/chunks/shared-utils-OIg0N_dp.js.map +1 -0
  26. package/dist/{config-B5kHzuNz.js → chunks/style-engine-CBuOt1e4.js} +2387 -7442
  27. package/dist/chunks/style-engine-CBuOt1e4.js.map +1 -0
  28. package/dist/{css-writer-B-J87ncv.js → chunks/zero-engine-BFh7gKbK.js} +3 -3
  29. package/dist/chunks/zero-engine-BFh7gKbK.js.map +1 -0
  30. package/dist/{collector-DUaHCcTS.d.ts → collector-CAmndLXe.d.ts} +23 -3
  31. package/dist/{config-YsxGv4tq.d.ts → config-BWYrv_Aa.d.ts} +141 -37
  32. package/dist/core/index.d.ts +5 -5
  33. package/dist/core/index.js +9 -6
  34. package/dist/{index-Cd45t5NM.d.ts → index-BzAC3p06.d.ts} +118 -23
  35. package/dist/{index-PqN-DIpn.d.ts → index-D4kRLj2o.d.ts} +128 -68
  36. package/dist/index.d.ts +5 -5
  37. package/dist/index.js +10 -922
  38. package/dist/{merge-styles-CU7JbEwg.d.ts → merge-styles-BFktNP8J.d.ts} +2 -2
  39. package/dist/ssr/astro-client.js +1 -1
  40. package/dist/ssr/astro-middleware-extract-static.d.ts +11 -0
  41. package/dist/ssr/astro-middleware-extract-static.js +9 -0
  42. package/dist/ssr/astro-middleware-extract-static.js.map +1 -0
  43. package/dist/ssr/astro-middleware-extract.d.ts +11 -0
  44. package/dist/ssr/astro-middleware-extract.js +9 -0
  45. package/dist/ssr/astro-middleware-extract.js.map +1 -0
  46. package/dist/ssr/astro-middleware-static.d.ts +3 -1
  47. package/dist/ssr/astro-middleware.d.ts +3 -1
  48. package/dist/ssr/astro.d.ts +45 -3
  49. package/dist/ssr/astro.js +157 -14
  50. package/dist/ssr/astro.js.map +1 -1
  51. package/dist/ssr/index.d.ts +7 -7
  52. package/dist/ssr/index.js +4 -4
  53. package/dist/ssr/index.js.map +1 -1
  54. package/dist/ssr/next-config.d.ts +66 -0
  55. package/dist/ssr/next-config.js +135 -0
  56. package/dist/ssr/next-config.js.map +1 -0
  57. package/dist/ssr/next.d.ts +9 -2
  58. package/dist/ssr/next.js +26 -10
  59. package/dist/ssr/next.js.map +1 -1
  60. package/dist/static/index.d.ts +2 -2
  61. package/dist/static/index.js +1 -1
  62. package/dist/zero/babel.d.ts +1 -1
  63. package/dist/zero/babel.js +25 -24
  64. package/dist/zero/babel.js.map +1 -1
  65. package/dist/zero/index.d.ts +1 -1
  66. package/dist/zero/index.js +1 -1
  67. package/dist/zero/next.d.ts +1 -1
  68. package/docs/README.md +5 -3
  69. package/docs/adoption.md +3 -3
  70. package/docs/ai-agents.md +83 -78
  71. package/docs/comparison.md +12 -12
  72. package/docs/configuration.md +47 -47
  73. package/docs/debug.md +19 -7
  74. package/docs/dsl.md +3 -3
  75. package/docs/getting-started.md +10 -10
  76. package/docs/injector.md +62 -25
  77. package/docs/methodology.md +3 -3
  78. package/docs/migration-v3.md +49 -49
  79. package/docs/plugins.md +37 -33
  80. package/docs/react-api.md +2 -2
  81. package/docs/runtime-benchmarks.md +351 -0
  82. package/docs/ssr.md +219 -61
  83. package/docs/styles.md +43 -11
  84. package/docs/tasty-static.md +136 -103
  85. package/package.json +80 -11
  86. package/dist/async-storage-DKK-wTD4.js.map +0 -1
  87. package/dist/collector-DTahQUiV.js.map +0 -1
  88. package/dist/config-B5kHzuNz.js.map +0 -1
  89. package/dist/context-CA8YKeMn.js +0 -24
  90. package/dist/context-CA8YKeMn.js.map +0 -1
  91. package/dist/core-Dr4u1NVD.js +0 -1566
  92. package/dist/core-Dr4u1NVD.js.map +0 -1
  93. package/dist/css-writer-B-J87ncv.js.map +0 -1
  94. package/dist/format-global-rules-DklyaXv-.js +0 -22
  95. package/dist/format-global-rules-DklyaXv-.js.map +0 -1
  96. package/dist/format-rules-DKOA-6qu.js +0 -130
  97. package/dist/format-rules-DKOA-6qu.js.map +0 -1
  98. package/dist/hydrate-OeMX99We.js +0 -37
  99. package/dist/hydrate-OeMX99We.js.map +0 -1
  100. package/dist/index.js.map +0 -1
  101. package/dist/keyframes-CV8azJf3.js +0 -493
  102. package/dist/keyframes-CV8azJf3.js.map +0 -1
  103. package/dist/merge-styles-oklji0KB.js.map +0 -1
  104. package/dist/resolve-recipes-DTG81rzl.js +0 -144
  105. package/dist/resolve-recipes-DTG81rzl.js.map +0 -1
  106. /package/dist/{async-storage-DKK-wTD4.js → chunks/async-storage-DKK-wTD4.js} +0 -0
@@ -0,0 +1,2097 @@
1
+ import { a as warnOnceDev, o as isDevEnv } from "./shared-utils-OIg0N_dp.js";
2
+ //#region src/parser/const.ts
3
+ const VALUE_KEYWORDS = new Set([
4
+ "auto",
5
+ "max-content",
6
+ "min-content",
7
+ "fit-content",
8
+ "stretch",
9
+ "initial",
10
+ "inherit",
11
+ "revert",
12
+ "unset",
13
+ "revert-layer"
14
+ ]);
15
+ const CSS_WIDE_KEYWORDS = new Set([
16
+ "initial",
17
+ "inherit",
18
+ "revert",
19
+ "unset",
20
+ "revert-layer"
21
+ ]);
22
+ /**
23
+ * Color functions that *derive* a color from other colors. They take no alpha
24
+ * channel, so opacity has to wrap the whole call in `color-mix()` instead of
25
+ * being appended after a slash.
26
+ */
27
+ const DERIVED_COLOR_FUNCS_LIST = [
28
+ "color-mix",
29
+ "color-contrast",
30
+ "contrast-color",
31
+ "light-dark"
32
+ ];
33
+ const DERIVED_COLOR_FUNCS = new Set(DERIVED_COLOR_FUNCS_LIST);
34
+ /**
35
+ * Every color function the parser recognizes. The ones outside
36
+ * `DERIVED_COLOR_FUNCS` take channels as arguments, optionally followed by
37
+ * `/ <alpha>` — which is where an opacity suffix writes.
38
+ */
39
+ const COLOR_FUNCS = new Set([
40
+ "rgb",
41
+ "rgba",
42
+ "hsl",
43
+ "hsla",
44
+ "hwb",
45
+ "lab",
46
+ "lch",
47
+ "oklab",
48
+ "oklch",
49
+ "color",
50
+ "device-cmyk",
51
+ "gray",
52
+ ...DERIVED_COLOR_FUNCS_LIST
53
+ ]);
54
+ /** A value that is a single `name(args)` call: captures the name and the args. */
55
+ const RE_FUNC_CALL = /^([a-z][a-z0-9-]*)\((.+)\)$/i;
56
+ const RE_UNIT_NUM = /^[+-]?(?:\d*\.\d+|\d+)([a-z][a-z0-9]*)$/;
57
+ const RE_NUMBER = /^[+-]?(?:\d*\.\d+|\d+)$/;
58
+ const RE_HEX = /^(?:[0-9a-f]{3,4}|[0-9a-f]{6}(?:[0-9a-f]{2})?)$/;
59
+ const RE_RAW_UNIT = /^([+-]?(?:\d*\.\d+|\d+))([a-z%]+)$/;
60
+ const CANONICAL_FUNC_CASE = new Map([
61
+ ["translatex", "translateX"],
62
+ ["translatey", "translateY"],
63
+ ["translatez", "translateZ"],
64
+ ["scalex", "scaleX"],
65
+ ["scaley", "scaleY"],
66
+ ["scalez", "scaleZ"],
67
+ ["rotatex", "rotateX"],
68
+ ["rotatey", "rotateY"],
69
+ ["rotatez", "rotateZ"],
70
+ ["skewx", "skewX"],
71
+ ["skewy", "skewY"]
72
+ ]);
73
+ function canonicalFuncName(lowered) {
74
+ return CANONICAL_FUNC_CASE.get(lowered) ?? lowered;
75
+ }
76
+ //#endregion
77
+ //#region src/utils/string.ts
78
+ function toSnakeCase(str) {
79
+ return str.replace(/[A-Z]/g, (s) => `-${s.toLowerCase()}`);
80
+ }
81
+ /**
82
+ * Normalize a DSL identifier to the CSS custom-property name it emits.
83
+ *
84
+ * CSS custom-property names are case-sensitive, so `$myVar` has to keep its
85
+ * inner case to reference `--myVar`. A leading capital is the one exception:
86
+ * `$Foo` is not a supported way to name a property, so the first character is
87
+ * folded rather than kebab-cased (`--foo`, not `---foo`) and every later
88
+ * character is left alone.
89
+ *
90
+ * Every place that turns a `$name` / `#name` identifier into a CSS name goes
91
+ * through this, so authoring a token and referencing it always agree.
92
+ */
93
+ function normalizeDslName(name) {
94
+ if (!name) return name;
95
+ const first = name[0];
96
+ const lower = first.toLowerCase();
97
+ return lower === first ? name : lower + name.slice(1);
98
+ }
99
+ /** A DSL identifier: `$name`, `$$name`, `#name` or `##name`. */
100
+ const RE_DSL_IDENTIFIER = /(\$\$?|##?)([a-zA-Z_][a-zA-Z0-9_-]*)/g;
101
+ /**
102
+ * Case-fold a DSL string for parsing, preserving the case of custom-property
103
+ * names.
104
+ *
105
+ * Everything the parser matches on — keywords, units, modifiers, function names,
106
+ * color-token names — is compared lowercase, so the whole string used to be
107
+ * lowercased up front. That also folded custom-property names, which are
108
+ * case-sensitive in CSS: `$myVar` referenced `var(--myvar)` while the token
109
+ * definition emitted `--myVar`, so the two could never meet.
110
+ *
111
+ * Identifier bodies now keep their case (their first character still folds, see
112
+ * {@link normalizeDslName}); everything else is lowercased exactly as before.
113
+ * Hex literals are excluded — `#FF0000` is a color, not a name, and must fold to
114
+ * match {@link RE_HEX}.
115
+ */
116
+ function foldDslCase(src) {
117
+ const folded = src.toLowerCase();
118
+ if (folded === src || !src.includes("$") && !src.includes("#")) return folded;
119
+ RE_DSL_IDENTIFIER.lastIndex = 0;
120
+ let out = "";
121
+ let last = 0;
122
+ let match;
123
+ while (match = RE_DSL_IDENTIFIER.exec(src)) {
124
+ const [whole, prefix, body] = match;
125
+ out += src.slice(last, match.index).toLowerCase();
126
+ out += prefix + (prefix[0] === "#" && RE_HEX.test(body.toLowerCase()) ? body.toLowerCase() : normalizeDslName(body));
127
+ last = match.index + whole.length;
128
+ }
129
+ return out + src.slice(last).toLowerCase();
130
+ }
131
+ //#endregion
132
+ //#region src/utils/color-math.ts
133
+ const OKLab_to_LMS_M = [
134
+ [
135
+ 1,
136
+ .3963377773761749,
137
+ .2158037573099136
138
+ ],
139
+ [
140
+ 1,
141
+ -.1055613458156586,
142
+ -.0638541728258133
143
+ ],
144
+ [
145
+ 1,
146
+ -.0894841775298119,
147
+ -1.2914855480194092
148
+ ]
149
+ ];
150
+ const LMS_to_linear_sRGB_M = [
151
+ [
152
+ 4.076741636075959,
153
+ -3.307711539258062,
154
+ .2309699031821041
155
+ ],
156
+ [
157
+ -1.2684379732850313,
158
+ 2.6097573492876878,
159
+ -.3413193760026569
160
+ ],
161
+ [
162
+ -.004196076138675526,
163
+ -.703418617935936,
164
+ 1.7076146940746113
165
+ ]
166
+ ];
167
+ const OKLab_to_linear_sRGB_coefficients = [
168
+ [[-1.8817030993265873, -.8093650129914302], [
169
+ 1.19086277,
170
+ 1.76576728,
171
+ .59662641,
172
+ .75515197,
173
+ .56771245
174
+ ]],
175
+ [[1.8144407988010998, -1.194452667805235], [
176
+ .73956515,
177
+ -.45954404,
178
+ .08285427,
179
+ .1254107,
180
+ .14503204
181
+ ]],
182
+ [[.13110757611180954, 1.813339709266608], [
183
+ 1.35733652,
184
+ -.00915799,
185
+ -1.1513021,
186
+ -.50559606,
187
+ .00692167
188
+ ]]
189
+ ];
190
+ const dotXY = (a, b) => a[0] * b[0] + a[1] * b[1];
191
+ const INV_GAMMA = 1 / 2.4;
192
+ function srgbLinearToGamma(val) {
193
+ const sign = val < 0 ? -1 : 1;
194
+ const abs = Math.abs(val);
195
+ return abs > .0031308 ? sign * (1.055 * abs ** INV_GAMMA - .055) : 12.92 * val;
196
+ }
197
+ const TAU = 2 * Math.PI;
198
+ const K1 = .206;
199
+ const K2 = .03;
200
+ const K3 = 1.206 / 1.03;
201
+ const clamp$1 = (value, min, max) => Math.max(Math.min(value, max), min);
202
+ const constrainAngle = (angle) => (angle % 360 + 360) % 360;
203
+ const toe = (x) => .5 * (K3 * x - K1 + Math.sqrt((K3 * x - K1) * (K3 * x - K1) + 4 * K2 * K3 * x));
204
+ const toeInv = (x) => (x ** 2 + K1 * x) / (K3 * (x + K2));
205
+ const OKHST_REF_EPS = .05;
206
+ function yToL(y) {
207
+ return toe(Math.cbrt(Math.max(0, y)));
208
+ }
209
+ function yFromTone(t, eps = OKHST_REF_EPS) {
210
+ const den = Math.log(1 + eps) - Math.log(eps);
211
+ return Math.exp(t / 100 * den + Math.log(eps)) - eps;
212
+ }
213
+ function fromTone(t, eps = OKHST_REF_EPS) {
214
+ return yToL(yFromTone(t, eps));
215
+ }
216
+ const oklabToLinearSrgb = (lab) => {
217
+ const L = lab[0];
218
+ const a = lab[1];
219
+ const b = lab[2];
220
+ const l_ = L + .3963377773761749 * a + .2158037573099136 * b;
221
+ const m_ = L - .1055613458156586 * a - .0638541728258133 * b;
222
+ const s_ = L - .0894841775298119 * a - 1.2914855480194092 * b;
223
+ const l = l_ * l_ * l_;
224
+ const m = m_ * m_ * m_;
225
+ const s = s_ * s_ * s_;
226
+ return [
227
+ 4.076741636075959 * l - 3.307711539258062 * m + .2309699031821041 * s,
228
+ -1.2684379732850313 * l + 2.6097573492876878 * m - .3413193760026569 * s,
229
+ -.004196076138675526 * l - .703418617935936 * m + 1.7076146940746113 * s
230
+ ];
231
+ };
232
+ const computeMaxSaturationOKLC = (a, b) => {
233
+ const okCoeff = OKLab_to_linear_sRGB_coefficients;
234
+ const lmsToRgb = LMS_to_linear_sRGB_M;
235
+ const tmp2 = [a, b];
236
+ const tmp3 = [
237
+ 0,
238
+ a,
239
+ b
240
+ ];
241
+ let chnlCoeff;
242
+ let chnlLMS;
243
+ if (dotXY(okCoeff[0][0], tmp2) > 1) {
244
+ chnlCoeff = okCoeff[0][1];
245
+ chnlLMS = lmsToRgb[0];
246
+ } else if (dotXY(okCoeff[1][0], tmp2) > 1) {
247
+ chnlCoeff = okCoeff[1][1];
248
+ chnlLMS = lmsToRgb[1];
249
+ } else {
250
+ chnlCoeff = okCoeff[2][1];
251
+ chnlLMS = lmsToRgb[2];
252
+ }
253
+ const [k0, k1, k2, k3, k4] = chnlCoeff;
254
+ const [wl, wm, ws] = chnlLMS;
255
+ let sat = k0 + k1 * a + k2 * b + k3 * (a * a) + k4 * a * b;
256
+ const dotYZ = (mat, vec) => mat[1] * vec[1] + mat[2] * vec[2];
257
+ const kl = dotYZ(OKLab_to_LMS_M[0], tmp3);
258
+ const km = dotYZ(OKLab_to_LMS_M[1], tmp3);
259
+ const ks = dotYZ(OKLab_to_LMS_M[2], tmp3);
260
+ const l_ = 1 + sat * kl;
261
+ const m_ = 1 + sat * km;
262
+ const s_ = 1 + sat * ks;
263
+ const l = l_ ** 3;
264
+ const m = m_ ** 3;
265
+ const s = s_ ** 3;
266
+ const lds = 3 * kl * (l_ * l_);
267
+ const mds = 3 * km * (m_ * m_);
268
+ const sds = 3 * ks * (s_ * s_);
269
+ const lds2 = 6 * (kl * kl) * l_;
270
+ const mds2 = 6 * (km * km) * m_;
271
+ const sds2 = 6 * (ks * ks) * s_;
272
+ const f = wl * l + wm * m + ws * s;
273
+ const f1 = wl * lds + wm * mds + ws * sds;
274
+ const f2 = wl * lds2 + wm * mds2 + ws * sds2;
275
+ sat = sat - f * f1 / (f1 * f1 - .5 * f * f2);
276
+ return sat;
277
+ };
278
+ const findCuspOKLCH = (a, b) => {
279
+ const S_cusp = computeMaxSaturationOKLC(a, b);
280
+ const rgb_at_max = oklabToLinearSrgb([
281
+ 1,
282
+ S_cusp * a,
283
+ S_cusp * b
284
+ ]);
285
+ 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)));
286
+ return [L_cusp, L_cusp * S_cusp];
287
+ };
288
+ const findGamutIntersectionOKLCH = (a, b, l1, c1, l0, cusp) => {
289
+ const lmsToRgb = LMS_to_linear_sRGB_M;
290
+ const tmp3 = [
291
+ 0,
292
+ a,
293
+ b
294
+ ];
295
+ const floatMax = Number.MAX_VALUE;
296
+ let t;
297
+ const dotYZ = (mat, vec) => mat[1] * vec[1] + mat[2] * vec[2];
298
+ const dotXYZ = (vec, x, y, z) => vec[0] * x + vec[1] * y + vec[2] * z;
299
+ if ((l1 - l0) * cusp[1] - (cusp[0] - l0) * c1 <= 0) {
300
+ const denom = c1 * cusp[0] + cusp[1] * (l0 - l1);
301
+ t = denom === 0 ? 0 : cusp[1] * l0 / denom;
302
+ } else {
303
+ const denom = c1 * (cusp[0] - 1) + cusp[1] * (l0 - l1);
304
+ t = denom === 0 ? 0 : cusp[1] * (l0 - 1) / denom;
305
+ const dl = l1 - l0;
306
+ const dc = c1;
307
+ const kl = dotYZ(OKLab_to_LMS_M[0], tmp3);
308
+ const km = dotYZ(OKLab_to_LMS_M[1], tmp3);
309
+ const ks = dotYZ(OKLab_to_LMS_M[2], tmp3);
310
+ const ldt_ = dl + dc * kl;
311
+ const mdt_ = dl + dc * km;
312
+ const sdt_ = dl + dc * ks;
313
+ const L = l0 * (1 - t) + t * l1;
314
+ const C = t * c1;
315
+ const l_ = L + C * kl;
316
+ const m_ = L + C * km;
317
+ const s_ = L + C * ks;
318
+ const l = l_ ** 3;
319
+ const m = m_ ** 3;
320
+ const s = s_ ** 3;
321
+ const ldt = 3 * ldt_ * l_ * l_;
322
+ const mdt = 3 * mdt_ * m_ * m_;
323
+ const sdt = 3 * sdt_ * s_ * s_;
324
+ const ldt2 = 6 * ldt_ * ldt_ * l_;
325
+ const mdt2 = 6 * mdt_ * mdt_ * m_;
326
+ const sdt2 = 6 * sdt_ * sdt_ * s_;
327
+ const r_ = dotXYZ(lmsToRgb[0], l, m, s) - 1;
328
+ const r1 = dotXYZ(lmsToRgb[0], ldt, mdt, sdt);
329
+ const r2 = dotXYZ(lmsToRgb[0], ldt2, mdt2, sdt2);
330
+ const ur = r1 / (r1 * r1 - .5 * r_ * r2);
331
+ let tr = -r_ * ur;
332
+ const g_ = dotXYZ(lmsToRgb[1], l, m, s) - 1;
333
+ const g1 = dotXYZ(lmsToRgb[1], ldt, mdt, sdt);
334
+ const g2 = dotXYZ(lmsToRgb[1], ldt2, mdt2, sdt2);
335
+ const ug = g1 / (g1 * g1 - .5 * g_ * g2);
336
+ let tg = -g_ * ug;
337
+ const b_ = dotXYZ(lmsToRgb[2], l, m, s) - 1;
338
+ const b1 = dotXYZ(lmsToRgb[2], ldt, mdt, sdt);
339
+ const b2 = dotXYZ(lmsToRgb[2], ldt2, mdt2, sdt2);
340
+ const ub = b1 / (b1 * b1 - .5 * b_ * b2);
341
+ let tb = -b_ * ub;
342
+ tr = ur >= 0 ? tr : floatMax;
343
+ tg = ug >= 0 ? tg : floatMax;
344
+ tb = ub >= 0 ? tb : floatMax;
345
+ t += Math.min(tr, Math.min(tg, tb));
346
+ }
347
+ return t;
348
+ };
349
+ const computeSt = (cusp) => [cusp[1] / cusp[0], cusp[1] / (1 - cusp[0])];
350
+ 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))))];
351
+ const getCs = (L, a, b, cusp) => {
352
+ const cMax = findGamutIntersectionOKLCH(a, b, L, 1, L, cusp);
353
+ const stMax = computeSt(cusp);
354
+ const k = cMax / Math.min(L * stMax[0], (1 - L) * stMax[1]);
355
+ const stMid = computeStMid(a, b);
356
+ let ca = L * stMid[0];
357
+ let cb = (1 - L) * stMid[1];
358
+ const cMid = .9 * k * Math.sqrt(Math.sqrt(1 / (1 / ca ** 4 + 1 / cb ** 4)));
359
+ ca = L * .4;
360
+ cb = (1 - L) * .8;
361
+ return [
362
+ Math.sqrt(1 / (1 / ca ** 2 + 1 / cb ** 2)),
363
+ cMid,
364
+ cMax
365
+ ];
366
+ };
367
+ const okhslToOklab = (hsl) => {
368
+ let h = hsl[0];
369
+ const s = hsl[1];
370
+ const l = hsl[2];
371
+ const L = toeInv(l);
372
+ let a = 0;
373
+ let b = 0;
374
+ h = constrainAngle(h) / 360;
375
+ if (L !== 0 && L !== 1 && s !== 0) {
376
+ const a_ = Math.cos(TAU * h);
377
+ const b_ = Math.sin(TAU * h);
378
+ const [c0, cMid, cMax] = getCs(L, a_, b_, findCuspOKLCH(a_, b_));
379
+ const mid = .8;
380
+ const midInv = 1.25;
381
+ let t, k0, k1, k2;
382
+ if (s < mid) {
383
+ t = midInv * s;
384
+ k0 = 0;
385
+ k1 = mid * c0;
386
+ k2 = 1 - k1 / cMid;
387
+ } else {
388
+ t = 5 * (s - .8);
389
+ k0 = cMid;
390
+ k1 = .2 * cMid ** 2 * 1.25 ** 2 / c0;
391
+ k2 = 1 - k1 / (cMax - cMid);
392
+ }
393
+ const c = k0 + t * k1 / (1 - k2 * t);
394
+ a = c * a_;
395
+ b = c * b_;
396
+ }
397
+ return [
398
+ L,
399
+ a,
400
+ b
401
+ ];
402
+ };
403
+ /**
404
+ * HSL to RGB.
405
+ * Algorithm from CSS Color 4 spec.
406
+ *
407
+ * @param h - Hue in degrees (0-360)
408
+ * @param s - Saturation (0-1)
409
+ * @param l - Lightness (0-1)
410
+ * @returns RGB values in 0-255 range (may have fractional values)
411
+ */
412
+ function hslToRgbValues(h, s, l) {
413
+ const a = s * Math.min(l, 1 - l);
414
+ const f = (n) => {
415
+ const k = (n + h / 30) % 12;
416
+ return l - a * Math.max(-1, Math.min(k - 3, 9 - k, 1));
417
+ };
418
+ return [
419
+ f(0) * 255,
420
+ f(8) * 255,
421
+ f(4) * 255
422
+ ];
423
+ }
424
+ /**
425
+ * OKHSL to sRGB (0-1 range).
426
+ * @param h - Hue in degrees (0-360)
427
+ * @param s - Saturation (0-1)
428
+ * @param l - Lightness (0-1)
429
+ * @returns sRGB values in 0-1 range, clamped to gamut
430
+ */
431
+ function okhslToSrgb(h, s, l) {
432
+ const linearRGB = oklabToLinearSrgb(okhslToOklab([
433
+ h,
434
+ s,
435
+ l
436
+ ]));
437
+ return [
438
+ clamp$1(srgbLinearToGamma(linearRGB[0]), 0, 1),
439
+ clamp$1(srgbLinearToGamma(linearRGB[1]), 0, 1),
440
+ clamp$1(srgbLinearToGamma(linearRGB[2]), 0, 1)
441
+ ];
442
+ }
443
+ /**
444
+ * OKHST to sRGB (0-1 range).
445
+ * @param h - Hue in degrees (0-360)
446
+ * @param s - Saturation (0-1)
447
+ * @param t - Tone (0-1)
448
+ * @returns sRGB values in 0-1 range, clamped to gamut
449
+ */
450
+ function okhstToSrgb(h, s, t) {
451
+ return okhslToSrgb(h, clamp$1(s, 0, 1), clamp$1(fromTone(t * 100), 0, 1));
452
+ }
453
+ /**
454
+ * OKLCH to sRGB (0-255 range).
455
+ * @param L - Lightness (0-1)
456
+ * @param C - Chroma (typically 0-0.4)
457
+ * @param H - Hue in degrees (0-360)
458
+ * @returns RGB values in 0-255 range, clamped to gamut
459
+ */
460
+ function oklchToRgbValues(L, C, H) {
461
+ const hRad = H * Math.PI / 180;
462
+ const linear = oklabToLinearSrgb([
463
+ L,
464
+ C * Math.cos(hRad),
465
+ C * Math.sin(hRad)
466
+ ]);
467
+ return [
468
+ clamp$1(srgbLinearToGamma(linear[0]), 0, 1) * 255,
469
+ clamp$1(srgbLinearToGamma(linear[1]), 0, 1) * 255,
470
+ clamp$1(srgbLinearToGamma(linear[2]), 0, 1) * 255
471
+ ];
472
+ }
473
+ let _namedColorHex = null;
474
+ function getNamedColorHex() {
475
+ if (_namedColorHex) return _namedColorHex;
476
+ _namedColorHex = new Map([
477
+ ["aliceblue", "#f0f8ff"],
478
+ ["antiquewhite", "#faebd7"],
479
+ ["aqua", "#00ffff"],
480
+ ["aquamarine", "#7fffd4"],
481
+ ["azure", "#f0ffff"],
482
+ ["beige", "#f5f5dc"],
483
+ ["bisque", "#ffe4c4"],
484
+ ["black", "#000000"],
485
+ ["blanchedalmond", "#ffebcd"],
486
+ ["blue", "#0000ff"],
487
+ ["blueviolet", "#8a2be2"],
488
+ ["brown", "#a52a2a"],
489
+ ["burlywood", "#deb887"],
490
+ ["cadetblue", "#5f9ea0"],
491
+ ["chartreuse", "#7fff00"],
492
+ ["chocolate", "#d2691e"],
493
+ ["coral", "#ff7f50"],
494
+ ["cornflowerblue", "#6495ed"],
495
+ ["cornsilk", "#fff8dc"],
496
+ ["crimson", "#dc143c"],
497
+ ["cyan", "#00ffff"],
498
+ ["darkblue", "#00008b"],
499
+ ["darkcyan", "#008b8b"],
500
+ ["darkgoldenrod", "#b8860b"],
501
+ ["darkgray", "#a9a9a9"],
502
+ ["darkgreen", "#006400"],
503
+ ["darkgrey", "#a9a9a9"],
504
+ ["darkkhaki", "#bdb76b"],
505
+ ["darkmagenta", "#8b008b"],
506
+ ["darkolivegreen", "#556b2f"],
507
+ ["darkorange", "#ff8c00"],
508
+ ["darkorchid", "#9932cc"],
509
+ ["darkred", "#8b0000"],
510
+ ["darksalmon", "#e9967a"],
511
+ ["darkseagreen", "#8fbc8f"],
512
+ ["darkslateblue", "#483d8b"],
513
+ ["darkslategray", "#2f4f4f"],
514
+ ["darkslategrey", "#2f4f4f"],
515
+ ["darkturquoise", "#00ced1"],
516
+ ["darkviolet", "#9400d3"],
517
+ ["deeppink", "#ff1493"],
518
+ ["deepskyblue", "#00bfff"],
519
+ ["dimgray", "#696969"],
520
+ ["dimgrey", "#696969"],
521
+ ["dodgerblue", "#1e90ff"],
522
+ ["firebrick", "#b22222"],
523
+ ["floralwhite", "#fffaf0"],
524
+ ["forestgreen", "#228b22"],
525
+ ["fuchsia", "#ff00ff"],
526
+ ["gainsboro", "#dcdcdc"],
527
+ ["ghostwhite", "#f8f8ff"],
528
+ ["gold", "#ffd700"],
529
+ ["goldenrod", "#daa520"],
530
+ ["gray", "#808080"],
531
+ ["green", "#008000"],
532
+ ["greenyellow", "#adff2f"],
533
+ ["grey", "#808080"],
534
+ ["honeydew", "#f0fff0"],
535
+ ["hotpink", "#ff69b4"],
536
+ ["indianred", "#cd5c5c"],
537
+ ["indigo", "#4b0082"],
538
+ ["ivory", "#fffff0"],
539
+ ["khaki", "#f0e68c"],
540
+ ["lavender", "#e6e6fa"],
541
+ ["lavenderblush", "#fff0f5"],
542
+ ["lawngreen", "#7cfc00"],
543
+ ["lemonchiffon", "#fffacd"],
544
+ ["lightblue", "#add8e6"],
545
+ ["lightcoral", "#f08080"],
546
+ ["lightcyan", "#e0ffff"],
547
+ ["lightgoldenrodyellow", "#fafad2"],
548
+ ["lightgray", "#d3d3d3"],
549
+ ["lightgreen", "#90ee90"],
550
+ ["lightgrey", "#d3d3d3"],
551
+ ["lightpink", "#ffb6c1"],
552
+ ["lightsalmon", "#ffa07a"],
553
+ ["lightseagreen", "#20b2aa"],
554
+ ["lightskyblue", "#87cefa"],
555
+ ["lightslategray", "#778899"],
556
+ ["lightslategrey", "#778899"],
557
+ ["lightsteelblue", "#b0c4de"],
558
+ ["lightyellow", "#ffffe0"],
559
+ ["lime", "#00ff00"],
560
+ ["limegreen", "#32cd32"],
561
+ ["linen", "#faf0e6"],
562
+ ["magenta", "#ff00ff"],
563
+ ["maroon", "#800000"],
564
+ ["mediumaquamarine", "#66cdaa"],
565
+ ["mediumblue", "#0000cd"],
566
+ ["mediumorchid", "#ba55d3"],
567
+ ["mediumpurple", "#9370db"],
568
+ ["mediumseagreen", "#3cb371"],
569
+ ["mediumslateblue", "#7b68ee"],
570
+ ["mediumspringgreen", "#00fa9a"],
571
+ ["mediumturquoise", "#48d1cc"],
572
+ ["mediumvioletred", "#c71585"],
573
+ ["midnightblue", "#191970"],
574
+ ["mintcream", "#f5fffa"],
575
+ ["mistyrose", "#ffe4e1"],
576
+ ["moccasin", "#ffe4b5"],
577
+ ["navajowhite", "#ffdead"],
578
+ ["navy", "#000080"],
579
+ ["oldlace", "#fdf5e6"],
580
+ ["olive", "#808000"],
581
+ ["olivedrab", "#6b8e23"],
582
+ ["orange", "#ffa500"],
583
+ ["orangered", "#ff4500"],
584
+ ["orchid", "#da70d6"],
585
+ ["palegoldenrod", "#eee8aa"],
586
+ ["palegreen", "#98fb98"],
587
+ ["paleturquoise", "#afeeee"],
588
+ ["palevioletred", "#db7093"],
589
+ ["papayawhip", "#ffefd5"],
590
+ ["peachpuff", "#ffdab9"],
591
+ ["peru", "#cd853f"],
592
+ ["pink", "#ffc0cb"],
593
+ ["plum", "#dda0dd"],
594
+ ["powderblue", "#b0e0e6"],
595
+ ["purple", "#800080"],
596
+ ["rebeccapurple", "#663399"],
597
+ ["red", "#ff0000"],
598
+ ["rosybrown", "#bc8f8f"],
599
+ ["royalblue", "#4169e1"],
600
+ ["saddlebrown", "#8b4513"],
601
+ ["salmon", "#fa8072"],
602
+ ["sandybrown", "#f4a460"],
603
+ ["seagreen", "#2e8b57"],
604
+ ["seashell", "#fff5ee"],
605
+ ["sienna", "#a0522d"],
606
+ ["silver", "#c0c0c0"],
607
+ ["skyblue", "#87ceeb"],
608
+ ["slateblue", "#6a5acd"],
609
+ ["slategray", "#708090"],
610
+ ["slategrey", "#708090"],
611
+ ["snow", "#fffafa"],
612
+ ["springgreen", "#00ff7f"],
613
+ ["steelblue", "#4682b4"],
614
+ ["tan", "#d2b48c"],
615
+ ["teal", "#008080"],
616
+ ["thistle", "#d8bfd8"],
617
+ ["tomato", "#ff6347"],
618
+ ["turquoise", "#40e0d0"],
619
+ ["violet", "#ee82ee"],
620
+ ["wheat", "#f5deb3"],
621
+ ["white", "#ffffff"],
622
+ ["whitesmoke", "#f5f5f5"],
623
+ ["yellow", "#ffff00"],
624
+ ["yellowgreen", "#9acd32"]
625
+ ]);
626
+ return _namedColorHex;
627
+ }
628
+ /**
629
+ * Convert hex color string to `rgb()` CSS string.
630
+ * Supports 3, 4, 6, and 8 character hex values (with or without `#`).
631
+ */
632
+ function hexToRgb(hex) {
633
+ 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);
634
+ if (!matched) return null;
635
+ const rgba = matched.map((x, i) => parseInt(x, 16) * (i === 3 ? 1 / 255 : 1));
636
+ if (rgba.some((v) => Number.isNaN(v))) return null;
637
+ if (rgba.length >= 3) return `rgb(${rgba.slice(0, 3).join(" ")}${rgba.length > 3 ? ` / ${rgba[3]}` : ""})`;
638
+ return null;
639
+ }
640
+ /**
641
+ * Extract RGB values from an `rgb()`/`rgba()` string.
642
+ * Supports comma-separated, space-separated, fractional, percentage,
643
+ * and slash alpha notation.
644
+ *
645
+ * @returns Array of RGB values (0-255 range), converting percentages as needed.
646
+ */
647
+ function getRgbValuesFromRgbaString(str) {
648
+ const match = str.match(/rgba?\(([^)]+)\)/i);
649
+ if (!match) return [];
650
+ const [colorPart] = match[1].trim().split("/");
651
+ return colorPart.trim().split(/[,\s]+/).filter(Boolean).slice(0, 3).map((part) => {
652
+ part = part.trim();
653
+ if (part.endsWith("%")) return parseFloat(part) / 100 * 255;
654
+ return parseFloat(part);
655
+ });
656
+ }
657
+ /**
658
+ * Convert any recognized color string to an `rgb()` CSS string.
659
+ * Handles hex, `okhsl()`, `okhst()`, `hsl()`/`hsla()`, named CSS colors,
660
+ * and `rgb()`/`rgba()` pass-through.
661
+ */
662
+ function strToRgb(color, _ignoreAlpha = false) {
663
+ if (!color) return void 0;
664
+ if (color.startsWith("rgb")) return color;
665
+ if (color.startsWith("#")) return hexToRgb(color);
666
+ if (color.startsWith("oklch(")) return oklchStringToRgb(color);
667
+ if (color.startsWith("hsl")) return hslStringToRgb(color);
668
+ const namedHex = getNamedColorHex().get(color.toLowerCase());
669
+ if (namedHex) return hexToRgb(namedHex);
670
+ return null;
671
+ }
672
+ /**
673
+ * Convert an HSL/HSLA color string to an `rgb()`/`rgba()` CSS string.
674
+ * Supports modern space-separated and legacy comma-separated syntax,
675
+ * deg/turn/rad hue units, and slash alpha notation.
676
+ */
677
+ function hslStringToRgb(hslStr) {
678
+ const match = hslStr.match(/hsla?\(([^)]+)\)/i);
679
+ if (!match) return null;
680
+ const [colorPart, slashAlpha] = match[1].trim().split("/");
681
+ const parts = colorPart.trim().split(/[,\s]+/).filter(Boolean);
682
+ if (parts.length < 3) return null;
683
+ const alphaPart = slashAlpha?.trim() || (parts.length >= 4 ? parts[3] : null);
684
+ let h = parseFloat(parts[0]);
685
+ const hueStr = parts[0].toLowerCase();
686
+ if (hueStr.endsWith("turn")) h = parseFloat(hueStr) * 360;
687
+ else if (hueStr.endsWith("rad")) h = parseFloat(hueStr) * 180 / Math.PI;
688
+ h = (h % 360 + 360) % 360;
689
+ const parsePercent = (val) => {
690
+ const num = parseFloat(val);
691
+ return val.includes("%") ? num / 100 : num;
692
+ };
693
+ const s = Math.max(0, Math.min(1, parsePercent(parts[1])));
694
+ const l = Math.max(0, Math.min(1, parsePercent(parts[2])));
695
+ const [r, g, b] = hslToRgbValues(h, s, l);
696
+ if (alphaPart) {
697
+ const alpha = parseFloat(alphaPart.trim());
698
+ return `rgba(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)}, ${alpha})`;
699
+ }
700
+ return `rgb(${Math.round(r)} ${Math.round(g)} ${Math.round(b)})`;
701
+ }
702
+ /**
703
+ * Convert an `oklch()` color string to an `rgb()`/`rgba()` CSS string.
704
+ * Supports deg/turn/rad hue units and percentage lightness.
705
+ */
706
+ function oklchStringToRgb(oklchStr) {
707
+ const match = oklchStr.match(/oklch\(([^)]+)\)/i);
708
+ if (!match) return null;
709
+ const [colorPart, alphaPart] = match[1].trim().split("/");
710
+ const parts = colorPart.trim().split(/[,\s]+/).filter(Boolean);
711
+ if (parts.length < 3) return null;
712
+ const parsePercent = (val) => {
713
+ const num = parseFloat(val);
714
+ return val.includes("%") ? num / 100 : num;
715
+ };
716
+ const L = Math.max(0, Math.min(1, parsePercent(parts[0])));
717
+ const C = Math.max(0, parseFloat(parts[1]));
718
+ let H = parseFloat(parts[2]);
719
+ const hueStr = parts[2].toLowerCase();
720
+ if (hueStr.endsWith("turn")) H = parseFloat(hueStr) * 360;
721
+ else if (hueStr.endsWith("rad")) H = parseFloat(hueStr) * 180 / Math.PI;
722
+ else if (hueStr.endsWith("deg")) H = parseFloat(hueStr);
723
+ const [r, g, b] = oklchToRgbValues(L, C, H);
724
+ if (alphaPart) {
725
+ const alpha = parseFloat(alphaPart.trim());
726
+ return `rgba(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)}, ${alpha})`;
727
+ }
728
+ return `rgb(${Math.round(r)} ${Math.round(g)} ${Math.round(b)})`;
729
+ }
730
+ //#endregion
731
+ //#region src/utils/color-space.ts
732
+ /**
733
+ * Set the alpha of a whole color, replacing any it already carries.
734
+ *
735
+ * CSS relative color syntax is how opacity is applied to every color Tasty
736
+ * emits. `oklch(from <color> l c h / <alpha>)` copies the channels over and
737
+ * writes the alpha slot, which needs nothing from the color except that it *is*
738
+ * a color: a `color-mix()`, a `light-dark()`, a `currentcolor`, a
739
+ * `--name-color` written by hand-authored CSS that Tasty never defined — all of
740
+ * them work, where writing into a channel slot directly would require channels
741
+ * the engine has no way to compute.
742
+ *
743
+ * Alpha is *replaced*, not composed. A token holding `rgb(255 0 0 / .8)` faded
744
+ * to `.5` is alpha `.5`. `color-mix()` against `transparent` cannot do this —
745
+ * it would multiply the two to `.4`.
746
+ *
747
+ * The alpha slot takes a `<number>` or a `<percentage>`, so an opacity custom
748
+ * property passes straight through in whichever form the author declared it.
749
+ *
750
+ * The space is always `oklch`, regardless of the configured {@link ColorSpace}:
751
+ * it is unbounded, so a wide-gamut color survives the round trip that a
752
+ * gamut-limited space would clamp, and it is the space the computed value used
753
+ * to be reported in. Channels are copied rather than interpolated, so the polar
754
+ * form costs nothing even for an achromatic color, whose hue is carried through
755
+ * untouched.
756
+ */
757
+ function overrideColorAlpha(color, alpha) {
758
+ return `oklch(from ${color} l c h / ${alpha})`;
759
+ }
760
+ /**
761
+ * Compose an alpha onto a color, multiplying with any it already carries.
762
+ *
763
+ * This is the counterpart to {@link overrideColorAlpha}, and the difference is
764
+ * the point. A design token names a color, so fading it *sets* its alpha. But
765
+ * `currentcolor` is the color an element **inherits**, which an ancestor may
766
+ * already have faded — `#current.4` there means "40% of what reaches me", and a
767
+ * nested `#current.18` under it composes to `.072`. Design systems build ramps
768
+ * out of exactly that, so `#current` composes and a token replaces.
769
+ *
770
+ * `color-mix()` against `transparent` is what composes: mixing premultiplied
771
+ * leaves the channels alone and multiplies the alphas. Its percentage slot takes
772
+ * no `<number>`, so a `$prop` alpha has to be scaled — which is why an opacity
773
+ * property used as `#current.$prop` has to hold a unitless number, where a token
774
+ * accepts either form.
775
+ */
776
+ function mixColorAlpha(color, percentage) {
777
+ return `color-mix(in oklab, ${color} ${percentage}, transparent)`;
778
+ }
779
+ /**
780
+ * Matches what {@link overrideColorAlpha} builds. The first group is greedy so a
781
+ * nested override splits on its outermost layer.
782
+ */
783
+ const RE_ALPHA_OVERRIDE = /^oklch\(from (.+) l c h \/ (.+)\)$/;
784
+ /**
785
+ * Split what {@link overrideColorAlpha} builds back into the color it faded and
786
+ * the alpha, or `null` when the value is not one of those.
787
+ */
788
+ function parseAlphaOverride(value) {
789
+ const match = value.match(RE_ALPHA_OVERRIDE);
790
+ return match ? {
791
+ color: match[1],
792
+ alpha: match[2]
793
+ } : null;
794
+ }
795
+ //#endregion
796
+ //#region src/parser/types.ts
797
+ const makeEmptyPart = () => ({
798
+ mods: [],
799
+ values: [],
800
+ colors: [],
801
+ all: [],
802
+ output: ""
803
+ });
804
+ const makeEmptyDetails = () => ({
805
+ input: "",
806
+ output: "",
807
+ mods: [],
808
+ values: [],
809
+ colors: [],
810
+ all: [],
811
+ parts: []
812
+ });
813
+ const finalizePart = (p) => {
814
+ p.output = p.all.join(" ");
815
+ return p;
816
+ };
817
+ /**
818
+ * Aggregate parts into a StyleDetails group.
819
+ * Combines all parts' arrays into group-level arrays for backward compatibility.
820
+ */
821
+ const finalizeGroup = (d, parts) => {
822
+ d.parts = parts;
823
+ for (const part of parts) {
824
+ d.mods.push(...part.mods);
825
+ d.values.push(...part.values);
826
+ d.colors.push(...part.colors);
827
+ d.all.push(...part.all);
828
+ }
829
+ d.output = parts.map((p) => p.output).join(" / ");
830
+ return d;
831
+ };
832
+ //#endregion
833
+ //#region src/parser/classify.ts
834
+ /**
835
+ * Convert an opacity suffix to the alpha value it denotes.
836
+ *
837
+ * The authored digits are kept verbatim — `.07` stays `.07` rather than being
838
+ * multiplied into `7.000000000000001%` — and a `$prop` suffix passes the
839
+ * reference straight through, so it works whether the property holds a
840
+ * `<number>` or a `<percentage>`. Both are what the alpha slot accepts.
841
+ */
842
+ function alphaSuffixToAlpha(rawAlpha) {
843
+ if (rawAlpha.startsWith("$")) return `var(--${rawAlpha.slice(1)})`;
844
+ if (rawAlpha === "0") return "0";
845
+ return `.${rawAlpha}`;
846
+ }
847
+ /** Apply an opacity suffix to a color, replacing any alpha it already carries. */
848
+ function fadeColor(color, rawAlpha) {
849
+ return overrideColorAlpha(color, alphaSuffixToAlpha(rawAlpha));
850
+ }
851
+ /**
852
+ * Convert an opacity suffix to the percentage `color-mix()` needs, by shifting
853
+ * the decimal point rather than multiplying: `.07` is `7%`, not the
854
+ * `7.000000000000001%` that `parseFloat('.07') * 100` produces.
855
+ */
856
+ function alphaSuffixToPercentage(rawAlpha) {
857
+ if (rawAlpha.startsWith("$")) return `calc(var(--${rawAlpha.slice(1)}) * 100%)`;
858
+ if (rawAlpha === "0") return "0%";
859
+ const digits = rawAlpha.length === 1 ? `${rawAlpha}0` : rawAlpha;
860
+ const whole = String(Number(digits.slice(0, 2)));
861
+ const fraction = digits.slice(2);
862
+ return `${fraction ? `${whole}.${fraction}` : whole}%`;
863
+ }
864
+ /**
865
+ * Apply an opacity suffix to `currentcolor`, composing with any alpha an
866
+ * ancestor already applied. See {@link mixColorAlpha} for why this differs from
867
+ * how a token is faded.
868
+ */
869
+ function fadeCurrentColor(rawAlpha) {
870
+ return mixColorAlpha("currentcolor", alphaSuffixToPercentage(rawAlpha));
871
+ }
872
+ /**
873
+ * Whether parsed function arguments hold a color. Colors reach either the color
874
+ * bucket (`#token`, a nested color function, `transparent`) or — for the CSS
875
+ * named colors, which the parser has no token syntax for — the modifier bucket.
876
+ */
877
+ function hasColorArgs(parsed) {
878
+ const namedColors = getNamedColorHex();
879
+ return parsed.groups.some((group) => group.colors.length > 0 || group.mods.some((mod) => namedColors.has(mod)));
880
+ }
881
+ /**
882
+ * Re-parses a value through the parser until it stabilizes (no changes)
883
+ * or max iterations reached. This allows units to reference other units.
884
+ * Example: { x: '8px', y: '2x' } -> '1y' resolves to '16px'
885
+ */
886
+ function resolveUntilStable(value, opts, recurse, maxIterations = 10) {
887
+ let current = value;
888
+ for (let i = 0; i < maxIterations; i++) {
889
+ const unitMatch = current.match(RE_UNIT_NUM);
890
+ if (!unitMatch) break;
891
+ const unitName = unitMatch[1];
892
+ if (!opts.units || !(unitName in opts.units)) break;
893
+ const result = recurse(current);
894
+ if (result.output === current) break;
895
+ current = result.output;
896
+ }
897
+ return current;
898
+ }
899
+ function classify(raw, opts, recurse) {
900
+ const token = raw.trim();
901
+ if (!token) return {
902
+ bucket: 2,
903
+ processed: ""
904
+ };
905
+ {
906
+ let depth = 0;
907
+ let inQuote = 0;
908
+ for (let i = 0; i < token.length; i++) {
909
+ const ch = token[i];
910
+ if (inQuote) {
911
+ if (ch === inQuote && token[i - 1] !== "\\") inQuote = 0;
912
+ continue;
913
+ }
914
+ if (ch === "\"" || ch === "'") {
915
+ inQuote = ch;
916
+ continue;
917
+ }
918
+ if (ch === "(") depth++;
919
+ else if (ch === ")") depth = Math.max(0, depth - 1);
920
+ }
921
+ if (depth !== 0) {
922
+ console.warn("[Tasty] skipped invalid function token with unmatched parentheses:", token);
923
+ return {
924
+ bucket: 2,
925
+ processed: ""
926
+ };
927
+ }
928
+ }
929
+ if (token.startsWith("\"") && token.endsWith("\"") || token.startsWith("'") && token.endsWith("'")) return {
930
+ bucket: 1,
931
+ processed: token
932
+ };
933
+ if (token.startsWith("$$")) {
934
+ const rest = token.slice(2);
935
+ const openIdx = rest.indexOf("(");
936
+ if (openIdx > 0 && rest.endsWith(")")) {
937
+ const fname = rest.slice(0, openIdx);
938
+ if (/^[a-z_][a-z0-9-_]*$/i.test(fname)) {
939
+ const inner = rest.slice(openIdx + 1, -1).trim();
940
+ const cssName = `--${fname}`;
941
+ if (opts.functions && cssName in opts.functions) {
942
+ const groups = inner ? new StyleParser(opts).process(inner).groups : [];
943
+ const funcResult = opts.functions[cssName](groups);
944
+ if (funcResult !== "") return classify(funcResult, {
945
+ ...opts,
946
+ functions: void 0
947
+ }, recurse);
948
+ }
949
+ return {
950
+ bucket: 1,
951
+ processed: `--${fname}(${inner ? recurse(inner).output : ""})`
952
+ };
953
+ }
954
+ }
955
+ if (/^[a-z_][a-z0-9-_]*$/i.test(rest)) return {
956
+ bucket: 1,
957
+ processed: `--${rest}`
958
+ };
959
+ }
960
+ if (token.startsWith("##")) {
961
+ const rest = token.slice(2);
962
+ const openIdx = rest.indexOf("(");
963
+ if (openIdx > 0 && rest.endsWith(")")) {
964
+ const fname = rest.slice(0, openIdx);
965
+ if (/^[a-z_][a-z0-9-_]*$/i.test(fname)) {
966
+ const inner = rest.slice(openIdx + 1, -1).trim();
967
+ return {
968
+ bucket: 1,
969
+ processed: `--${fname}-color(${inner ? recurse(inner).output : ""})`
970
+ };
971
+ }
972
+ }
973
+ if (/^[a-z_][a-z0-9-_]*$/i.test(rest)) return {
974
+ bucket: 1,
975
+ processed: `--${rest}-color`
976
+ };
977
+ }
978
+ if (token === "#current") return {
979
+ bucket: 0,
980
+ processed: "currentcolor"
981
+ };
982
+ const currentAlphaMatch = token.match(/^#current\.(\$[a-z_][a-z0-9-_]*|[0-9]+)$/i);
983
+ if (currentAlphaMatch) return {
984
+ bucket: 0,
985
+ processed: fadeCurrentColor(currentAlphaMatch[1])
986
+ };
987
+ if (token[0] === "$" || token[0] === "#") {
988
+ const predefinedTokens = getGlobalPredefinedTokens();
989
+ if (predefinedTokens) {
990
+ const lookupKey = token.toLowerCase();
991
+ if (lookupKey in predefinedTokens) {
992
+ const tokenValue = predefinedTokens[lookupKey];
993
+ return classify(foldDslCase(tokenValue), opts, recurse);
994
+ }
995
+ if (token[0] === "#") {
996
+ const alphaMatch = token.match(/^(#[a-z0-9-]+)\.(\$[a-z_][a-z0-9-_]*|[0-9]+)$/i);
997
+ if (alphaMatch) {
998
+ const [, baseToken, rawAlpha] = alphaMatch;
999
+ const baseKey = baseToken.toLowerCase();
1000
+ if (baseKey in predefinedTokens) {
1001
+ const resolvedValue = predefinedTokens[baseKey];
1002
+ if (resolvedValue.startsWith("#")) return classify(`${foldDslCase(resolvedValue)}.${rawAlpha}`, opts, recurse);
1003
+ const funcMatch = resolvedValue.match(RE_FUNC_CALL);
1004
+ if (funcMatch) {
1005
+ const [, funcName, args] = funcMatch;
1006
+ const lowerFunc = funcName.toLowerCase();
1007
+ if (DERIVED_COLOR_FUNCS.has(lowerFunc)) return {
1008
+ bucket: 0,
1009
+ processed: fadeColor(classify(foldDslCase(resolvedValue), opts, recurse).processed, rawAlpha)
1010
+ };
1011
+ const isCustomFunc = !!(opts.functions && lowerFunc in opts.functions && !COLOR_FUNCS.has(lowerFunc) && !COLOR_FUNCS.has(funcName.replace(/a$/i, "").toLowerCase()));
1012
+ const normalizedFunc = isCustomFunc ? lowerFunc : funcName.replace(/a$/i, "").toLowerCase();
1013
+ if (!(COLOR_FUNCS.has(normalizedFunc) || COLOR_FUNCS.has(lowerFunc) || isCustomFunc)) return classify(`${resolvedValue}.${rawAlpha}`, opts, recurse);
1014
+ let alpha;
1015
+ if (rawAlpha.startsWith("$")) alpha = `var(--${rawAlpha.slice(1)})`;
1016
+ else alpha = rawAlpha === "0" ? "0" : `.${rawAlpha}`;
1017
+ const normalizeArgs = (a) => {
1018
+ let result = "";
1019
+ let depth = 0;
1020
+ for (let i = 0; i < a.length; i++) {
1021
+ const c = a[i];
1022
+ if (c === "(") {
1023
+ depth++;
1024
+ result += c;
1025
+ } else if (c === ")") {
1026
+ depth = Math.max(0, depth - 1);
1027
+ result += c;
1028
+ } else if (c === "," && depth === 0) {
1029
+ while (i + 1 < a.length && /\s/.test(a[i + 1])) i++;
1030
+ result += " ";
1031
+ } else result += c;
1032
+ }
1033
+ return result;
1034
+ };
1035
+ const findLastTopLevel = (str, ch) => {
1036
+ let depth = 0;
1037
+ for (let i = str.length - 1; i >= 0; i--) {
1038
+ const c = str[i];
1039
+ if (c === ")") depth++;
1040
+ else if (c === "(") depth = Math.max(0, depth - 1);
1041
+ else if (c === ch && depth === 0) return i;
1042
+ }
1043
+ return -1;
1044
+ };
1045
+ const slashIdx = findLastTopLevel(args, "/");
1046
+ const hasModernAlpha = slashIdx !== -1;
1047
+ let topLevelCommaCount = 0;
1048
+ let lastTopLevelComma = -1;
1049
+ {
1050
+ let depth = 0;
1051
+ for (let i = 0; i < args.length; i++) {
1052
+ const c = args[i];
1053
+ if (c === "(") depth++;
1054
+ else if (c === ")") depth = Math.max(0, depth - 1);
1055
+ else if (c === "," && depth === 0) {
1056
+ topLevelCommaCount++;
1057
+ lastTopLevelComma = i;
1058
+ }
1059
+ }
1060
+ }
1061
+ const hasLegacyAlpha = !hasModernAlpha && /a$/i.test(funcName) && topLevelCommaCount === 3;
1062
+ const constructed = `${normalizedFunc}(${hasModernAlpha || hasLegacyAlpha ? normalizeArgs(hasModernAlpha ? args.slice(0, slashIdx).trim() : args.slice(0, lastTopLevelComma).trim()) : normalizeArgs(args)} / ${alpha})`;
1063
+ if (!COLOR_FUNCS.has(normalizedFunc) && opts.functions && normalizedFunc in opts.functions) return classify(constructed, opts, recurse);
1064
+ return {
1065
+ bucket: 0,
1066
+ processed: constructed
1067
+ };
1068
+ }
1069
+ return classify(`${resolvedValue}.${rawAlpha}`, opts, recurse);
1070
+ }
1071
+ }
1072
+ }
1073
+ }
1074
+ }
1075
+ if (token.match(/^var\(--([a-zA-Z0-9-]+)-color\)$/)) return {
1076
+ bucket: 0,
1077
+ processed: token
1078
+ };
1079
+ if (token.startsWith("url(")) return {
1080
+ bucket: 1,
1081
+ processed: token
1082
+ };
1083
+ if (token[0] === "$") {
1084
+ const identMatch = token.match(/^\$([a-z_][a-zA-Z0-9-_]*)$/);
1085
+ if (identMatch) {
1086
+ const name = identMatch[1];
1087
+ const processed = `var(--${name})`;
1088
+ return {
1089
+ bucket: name.endsWith("-color") ? 3 : 1,
1090
+ processed
1091
+ };
1092
+ }
1093
+ }
1094
+ if (token[0] === "#" && token.length > 1) {
1095
+ const alphaMatch = token.match(/^#([a-z0-9-]+)\.(\$[a-z_][a-z0-9-_]*|[0-9]+)$/i);
1096
+ if (alphaMatch) {
1097
+ const [, base, rawAlpha] = alphaMatch;
1098
+ return {
1099
+ bucket: 0,
1100
+ processed: fadeColor(`var(--${base}-color)`, rawAlpha)
1101
+ };
1102
+ }
1103
+ const name = token.slice(1);
1104
+ if (RE_HEX.test(name)) return {
1105
+ bucket: 0,
1106
+ processed: `var(--${name}-color, #${name})`
1107
+ };
1108
+ return {
1109
+ bucket: 0,
1110
+ processed: `var(--${name}-color)`
1111
+ };
1112
+ }
1113
+ const openIdx = token.indexOf("(");
1114
+ if (openIdx > 0 && token.endsWith(")")) {
1115
+ const fname = token.slice(0, openIdx);
1116
+ const inner = token.slice(openIdx + 1, -1);
1117
+ if (COLOR_FUNCS.has(fname)) {
1118
+ const parsedInner = recurse(inner);
1119
+ const argProcessed = parsedInner.output.replace(/,\s+/g, ",");
1120
+ const processed = `${canonicalFuncName(fname)}(${argProcessed})`;
1121
+ return {
1122
+ bucket: fname === "light-dark" && !hasColorArgs(parsedInner) ? 1 : 0,
1123
+ processed
1124
+ };
1125
+ }
1126
+ if (opts.functions && fname in opts.functions) {
1127
+ const tmp = new StyleParser(opts).process(inner);
1128
+ return classify(opts.functions[fname](tmp.groups), {
1129
+ ...opts,
1130
+ functions: void 0
1131
+ }, recurse);
1132
+ }
1133
+ const argProcessed = recurse(inner).output;
1134
+ return {
1135
+ bucket: 1,
1136
+ processed: `${canonicalFuncName(fname)}(${argProcessed})`
1137
+ };
1138
+ }
1139
+ if (token.startsWith("(") && token.endsWith(")")) {
1140
+ const colorMatch = token.slice(1, -1).match(/^#([a-z0-9-]+)\s*,\s*(.*)$/i);
1141
+ if (colorMatch) {
1142
+ const [, name, fallback] = colorMatch;
1143
+ return {
1144
+ bucket: 0,
1145
+ processed: `var(--${name}-color, ${recurse(fallback).output})`
1146
+ };
1147
+ }
1148
+ }
1149
+ if (token.startsWith("(") && token.endsWith(")")) {
1150
+ const match = token.slice(1, -1).match(/^\$([a-z_][a-zA-Z0-9-_]*)\s*,\s*(.*)$/);
1151
+ if (match) {
1152
+ const [, name, fallback] = match;
1153
+ const processedFallback = recurse(fallback).output;
1154
+ return {
1155
+ bucket: name.endsWith("-color") ? 3 : 1,
1156
+ processed: `var(--${name}, ${processedFallback})`
1157
+ };
1158
+ }
1159
+ }
1160
+ if (token[0] === "(" && token[token.length - 1] === ")") return {
1161
+ bucket: 1,
1162
+ processed: `calc(${recurse(token.slice(1, -1)).output})`
1163
+ };
1164
+ const um = token.match(RE_UNIT_NUM);
1165
+ if (um) {
1166
+ const unit = um[1];
1167
+ const numericPart = parseFloat(token.slice(0, -unit.length));
1168
+ const handler = opts.units && opts.units[unit];
1169
+ if (handler) if (typeof handler === "string") {
1170
+ const rawMatch = handler.match(RE_RAW_UNIT);
1171
+ if (rawMatch) {
1172
+ const [, baseNum, cssUnit] = rawMatch;
1173
+ return {
1174
+ bucket: 1,
1175
+ processed: resolveUntilStable(`${numericPart * parseFloat(baseNum)}${cssUnit}`, opts, recurse)
1176
+ };
1177
+ }
1178
+ const base = handler;
1179
+ if (numericPart === 1) return {
1180
+ bucket: 1,
1181
+ processed: base
1182
+ };
1183
+ return {
1184
+ bucket: 1,
1185
+ processed: `calc(${numericPart} * ${base})`
1186
+ };
1187
+ } else return {
1188
+ bucket: 1,
1189
+ processed: handler(numericPart)
1190
+ };
1191
+ }
1192
+ if (/^[+-]?(?:\d*\.\d+|\d+)[a-z%]+$/.test(token)) return {
1193
+ bucket: 1,
1194
+ processed: token
1195
+ };
1196
+ if (RE_NUMBER.test(token)) return {
1197
+ bucket: 1,
1198
+ processed: token
1199
+ };
1200
+ if (VALUE_KEYWORDS.has(token)) return {
1201
+ bucket: 1,
1202
+ processed: token
1203
+ };
1204
+ if (token === "transparent" || token === "currentcolor") return {
1205
+ bucket: 0,
1206
+ processed: token
1207
+ };
1208
+ return {
1209
+ bucket: 2,
1210
+ processed: token
1211
+ };
1212
+ }
1213
+ //#endregion
1214
+ //#region src/parser/lru.ts
1215
+ /**
1216
+ * Doubly-linked-list LRU.
1217
+ *
1218
+ * The list holds direct node references rather than keys, so rewiring on a
1219
+ * `get()` of a non-MRU entry is pure pointer work. Storing keys instead cost
1220
+ * three extra `Map.get()` lookups per read (prev, next and the current head),
1221
+ * which is measurable on these read-mostly caches: they are hit on every
1222
+ * render once the style pipeline is warm.
1223
+ */
1224
+ var Lru = class {
1225
+ limit;
1226
+ map = /* @__PURE__ */ new Map();
1227
+ head = null;
1228
+ tail = null;
1229
+ onEvict;
1230
+ constructor(limit = 1e3, onEvict) {
1231
+ this.limit = limit;
1232
+ let normalized = Number.isFinite(this.limit) ? Math.floor(this.limit) : 1e3;
1233
+ if (normalized <= 0) normalized = 1e3;
1234
+ this.limit = normalized;
1235
+ this.onEvict = onEvict;
1236
+ }
1237
+ setOnEvict(fn) {
1238
+ this.onEvict = fn;
1239
+ }
1240
+ get(key) {
1241
+ const node = this.map.get(key);
1242
+ if (node === void 0) return void 0;
1243
+ if (this.head !== node) this.moveToHead(node);
1244
+ return node.value;
1245
+ }
1246
+ set(key, value) {
1247
+ const existing = this.map.get(key);
1248
+ if (existing !== void 0) {
1249
+ existing.value = value;
1250
+ if (this.head !== existing) this.moveToHead(existing);
1251
+ return;
1252
+ }
1253
+ const node = {
1254
+ key,
1255
+ prev: null,
1256
+ next: this.head,
1257
+ value
1258
+ };
1259
+ if (this.head) this.head.prev = node;
1260
+ this.head = node;
1261
+ if (!this.tail) this.tail = node;
1262
+ this.map.set(key, node);
1263
+ if (this.map.size > this.limit) this.evict();
1264
+ }
1265
+ delete(key) {
1266
+ const node = this.map.get(key);
1267
+ if (node === void 0) return;
1268
+ this.detach(node);
1269
+ this.map.delete(key);
1270
+ }
1271
+ keys() {
1272
+ return this.map.keys();
1273
+ }
1274
+ detach(node) {
1275
+ if (node.prev) node.prev.next = node.next;
1276
+ if (node.next) node.next.prev = node.prev;
1277
+ if (this.head === node) this.head = node.next;
1278
+ if (this.tail === node) this.tail = node.prev;
1279
+ node.prev = null;
1280
+ node.next = null;
1281
+ }
1282
+ moveToHead(node) {
1283
+ this.detach(node);
1284
+ node.next = this.head;
1285
+ if (this.head) this.head.prev = node;
1286
+ this.head = node;
1287
+ if (!this.tail) this.tail = node;
1288
+ }
1289
+ evict() {
1290
+ const node = this.tail;
1291
+ if (!node) return;
1292
+ this.detach(node);
1293
+ this.map.delete(node.key);
1294
+ if (this.onEvict) try {
1295
+ this.onEvict(node.key, node.value);
1296
+ } catch {}
1297
+ }
1298
+ clear() {
1299
+ this.map.clear();
1300
+ this.head = this.tail = null;
1301
+ }
1302
+ };
1303
+ //#endregion
1304
+ //#region src/parser/tokenizer.ts
1305
+ const isWhitespace = (ch) => ch === " " || ch === "\n" || ch === " " || ch === "\r" || ch === "\f";
1306
+ function scan(src, cb) {
1307
+ let depth = 0;
1308
+ let inUrl = false;
1309
+ let inQuote = 0;
1310
+ let start = 0;
1311
+ let i = 0;
1312
+ let pendingSlash = false;
1313
+ const flush = (isComma, isSlash) => {
1314
+ const actualSlash = isSlash || pendingSlash;
1315
+ pendingSlash = false;
1316
+ if (start < i) {
1317
+ const prevChar = start > 0 ? src[start - 1] : null;
1318
+ cb(src.slice(start, i), isComma, actualSlash, prevChar);
1319
+ } else if (isComma) cb("", true, false, null);
1320
+ else if (actualSlash) cb("", false, true, null);
1321
+ start = i + 1;
1322
+ };
1323
+ for (; i < src.length; i++) {
1324
+ const ch = src[i];
1325
+ if (inQuote) {
1326
+ if (ch === inQuote && src[i - 1] !== "\\") inQuote = 0;
1327
+ continue;
1328
+ }
1329
+ if (ch === "\"" || ch === "'") {
1330
+ inQuote = ch;
1331
+ continue;
1332
+ }
1333
+ if (ch === "(") {
1334
+ if (!depth) {
1335
+ if (src.slice(Math.max(0, i - 3), i + 1) === "url(") inUrl = true;
1336
+ }
1337
+ depth++;
1338
+ continue;
1339
+ }
1340
+ if (ch === ")") {
1341
+ depth = Math.max(0, depth - 1);
1342
+ if (inUrl && depth === 0) inUrl = false;
1343
+ continue;
1344
+ }
1345
+ if (inUrl) continue;
1346
+ if (!depth) {
1347
+ if (ch === ",") {
1348
+ flush(true, false);
1349
+ continue;
1350
+ }
1351
+ if (ch === "/") {
1352
+ const prevIsWhitespace = isWhitespace(src[i - 1]);
1353
+ const nextIsWhitespace = isWhitespace(src[i + 1]);
1354
+ if (prevIsWhitespace && nextIsWhitespace) {
1355
+ pendingSlash = true;
1356
+ start = i + 1;
1357
+ continue;
1358
+ }
1359
+ continue;
1360
+ }
1361
+ if (isWhitespace(ch)) {
1362
+ flush(false, false);
1363
+ continue;
1364
+ }
1365
+ }
1366
+ }
1367
+ flush(false, false);
1368
+ }
1369
+ //#endregion
1370
+ //#region src/parser/parser.ts
1371
+ var StyleParser = class {
1372
+ opts;
1373
+ cache;
1374
+ constructor(opts = {}) {
1375
+ this.opts = opts;
1376
+ this.cache = new Lru(this.opts.cacheSize ?? 1e3);
1377
+ }
1378
+ process(src) {
1379
+ const key = String(src);
1380
+ const hit = this.cache.get(key);
1381
+ if (hit) return hit;
1382
+ const stripped = foldDslCase(src.replace(/\/\*[^*]*\*+(?:[^/*][^*]*\*+)*\//g, ""));
1383
+ const groups = [];
1384
+ let currentGroup = makeEmptyDetails();
1385
+ let currentPart = makeEmptyPart();
1386
+ let parts = [];
1387
+ const pushToken = (bucket, processed) => {
1388
+ if (!processed) return;
1389
+ const mergeIntoPrevUrl = () => {
1390
+ const lastIdx = currentPart.values.length - 1;
1391
+ currentPart.values[lastIdx] += ` ${processed}`;
1392
+ const lastAllIdx = currentPart.all.length - 1;
1393
+ currentPart.all[lastAllIdx] += ` ${processed}`;
1394
+ };
1395
+ if (currentPart.values.length > 0 && currentPart.values[currentPart.values.length - 1].startsWith("url(")) {
1396
+ mergeIntoPrevUrl();
1397
+ return;
1398
+ }
1399
+ switch (bucket) {
1400
+ case 0:
1401
+ currentPart.colors.push(processed);
1402
+ break;
1403
+ case 1:
1404
+ currentPart.values.push(processed);
1405
+ break;
1406
+ case 2:
1407
+ currentPart.mods.push(processed);
1408
+ break;
1409
+ case 3:
1410
+ currentPart.colors.push(processed);
1411
+ currentPart.values.push(processed);
1412
+ break;
1413
+ }
1414
+ currentPart.all.push(processed);
1415
+ };
1416
+ const endPart = () => {
1417
+ if (currentPart.all.length > 0) {
1418
+ finalizePart(currentPart);
1419
+ parts.push(currentPart);
1420
+ }
1421
+ currentPart = makeEmptyPart();
1422
+ };
1423
+ const endGroup = () => {
1424
+ endPart();
1425
+ if (parts.length === 0) {
1426
+ parts.push(makeEmptyPart());
1427
+ finalizePart(parts[0]);
1428
+ }
1429
+ finalizeGroup(currentGroup, parts);
1430
+ groups.push(currentGroup);
1431
+ currentGroup = makeEmptyDetails();
1432
+ parts = [];
1433
+ currentPart = makeEmptyPart();
1434
+ };
1435
+ scan(stripped, (tok, isComma, isSlash, _prevChar) => {
1436
+ if (tok) {
1437
+ if (currentGroup.input) currentGroup.input += ` ${tok}`;
1438
+ else currentGroup.input = tok;
1439
+ const { bucket, processed } = classify(tok, this.opts, (sub) => this.process(sub));
1440
+ pushToken(bucket, processed);
1441
+ }
1442
+ if (isSlash) endPart();
1443
+ if (isComma) endGroup();
1444
+ });
1445
+ if (currentPart.all.length || parts.length || !groups.length) endGroup();
1446
+ const result = {
1447
+ output: groups.map((g) => g.output).join(", "),
1448
+ groups
1449
+ };
1450
+ Object.freeze(result);
1451
+ this.cache.set(key, result);
1452
+ return result;
1453
+ }
1454
+ setFunctions(functions) {
1455
+ this.opts.functions = functions;
1456
+ this.cache.clear();
1457
+ }
1458
+ setUnits(units) {
1459
+ this.opts.units = units;
1460
+ this.cache.clear();
1461
+ }
1462
+ updateOptions(patch) {
1463
+ Object.assign(this.opts, patch);
1464
+ if (patch.cacheSize) this.cache = new Lru(patch.cacheSize);
1465
+ else this.cache.clear();
1466
+ }
1467
+ /**
1468
+ * Clear the parser cache.
1469
+ * Call this when external state that affects parsing results has changed
1470
+ * (e.g., predefined tokens).
1471
+ */
1472
+ clearCache() {
1473
+ this.cache.clear();
1474
+ }
1475
+ /**
1476
+ * Get the current units configuration.
1477
+ */
1478
+ getUnits() {
1479
+ return this.opts.units;
1480
+ }
1481
+ };
1482
+ //#endregion
1483
+ //#region src/plugins/color-func.ts
1484
+ const clamp = (value, min, max) => Math.max(Math.min(value, max), min);
1485
+ /**
1486
+ * Parse an angle value with optional unit.
1487
+ * Supports: deg, turn, rad, or unitless (treated as degrees).
1488
+ */
1489
+ const parseAngle = (value) => {
1490
+ const match = value.match(/^([+-]?\d*\.?\d+)(deg|turn|rad)?$/);
1491
+ if (!match) return 0;
1492
+ const num = parseFloat(match[1]);
1493
+ switch (match[2]) {
1494
+ case "turn": return num * 360;
1495
+ case "rad": return num * 180 / Math.PI;
1496
+ default: return num;
1497
+ }
1498
+ };
1499
+ /**
1500
+ * Parse a percentage value (e.g., "50%") to a 0-1 range. A unitless number is
1501
+ * read as the factor it already looks like, so `okhsl(280 .8 .52)` and
1502
+ * `okhsl(280 80% 52%)` are the same color.
1503
+ */
1504
+ const parsePercentage = (value) => {
1505
+ const match = value.match(/^([+-]?\d*\.?\d+)%?$/);
1506
+ if (!match) return 0;
1507
+ const num = parseFloat(match[1]);
1508
+ return value.includes("%") ? num / 100 : num;
1509
+ };
1510
+ /**
1511
+ * Creates a color function handler for the tasty parser.
1512
+ *
1513
+ * A color function is a custom `functions` entry that converts its arguments
1514
+ * to an `rgb(...)` string (an already-supported color), so it needs no special
1515
+ * core integration — registering it as a parse function is enough.
1516
+ *
1517
+ * @param name The name of the color function (e.g., 'okhsl', 'okhst')
1518
+ * @param convert Converts the parsed first three channel values (after angle/
1519
+ * percent normalization) to sRGB `[r, g, b]` in the 0-1 range
1520
+ * @param label Optional diagnostic label shown in dev warnings when the wrong
1521
+ * number of arguments is supplied (e.g., 'H S L'). Has no effect on parsing
1522
+ * or output.
1523
+ */
1524
+ function createColorFunc(name, convert, label) {
1525
+ const conversionCache = new Lru(500);
1526
+ const expected = label ? ` (${label})` : "";
1527
+ return (groups) => {
1528
+ if (groups.length === 0 || groups[0].all.length < 3) {
1529
+ if (isDevEnv()) console.warn(`[Tasty] ${name}(): expected 3 values${expected}, got:`, groups);
1530
+ return "rgb(0% 0% 0%)";
1531
+ }
1532
+ const group = groups[0];
1533
+ const tokens = group.all;
1534
+ const alpha = group.parts.length > 1 && group.parts[1].all.length > 0 ? group.parts[1].output : void 0;
1535
+ const cacheKey = tokens.slice(0, 3).join(" ") + (alpha ? ` / ${alpha}` : "");
1536
+ const cached = conversionCache.get(cacheKey);
1537
+ if (cached) return cached;
1538
+ const h = parseAngle(tokens[0]);
1539
+ const c2 = parsePercentage(tokens[1]);
1540
+ const c3 = parsePercentage(tokens[2]);
1541
+ const misscaled = [tokens[1], tokens[2]].filter((raw, i) => !raw.includes("%") && (i === 0 ? c2 : c3) > 1);
1542
+ if (misscaled.length) warnOnceDev(`${name}:percent-scale`, `${name}(): channels are factors (0-1) or percentages, so ${misscaled.map((v) => `"${v}"`).join(" and ")} ${misscaled.length > 1 ? "clamp" : "clamps"} to 1 — add the missing '%', or write the value as a 0-1 number.`);
1543
+ const [r, g, b] = convert(h, clamp(c2, 0, 1), clamp(c3, 0, 1));
1544
+ const format = (n) => {
1545
+ const pct = n * 100;
1546
+ return parseFloat(pct.toFixed(1)).toString() + "%";
1547
+ };
1548
+ const result = alpha ? `rgb(${format(r)} ${format(g)} ${format(b)} / ${alpha})` : `rgb(${format(r)} ${format(g)} ${format(b)})`;
1549
+ conversionCache.set(cacheKey, result);
1550
+ return result;
1551
+ };
1552
+ }
1553
+ //#endregion
1554
+ //#region src/plugins/okhsl-plugin.ts
1555
+ /**
1556
+ * OKHSL Plugin for Tasty
1557
+ *
1558
+ * Converts OKHSL color syntax to RGB notation.
1559
+ * Supports angle units: deg, turn, rad, or unitless (degrees).
1560
+ *
1561
+ * Examples:
1562
+ * okhsl(240.5 50% 50%)
1563
+ * okhsl(240.5deg 50% 50%)
1564
+ * okhsl(0.25turn 50% 50%)
1565
+ * okhsl(1.57rad 50% 50%)
1566
+ */
1567
+ /**
1568
+ * The okhsl function handler for tasty parser.
1569
+ * Receives parsed style groups and returns an RGB color string.
1570
+ */
1571
+ const okhslFunction = createColorFunc("okhsl", okhslToSrgb, "H S L");
1572
+ /**
1573
+ * OKHSL Plugin for Tasty.
1574
+ *
1575
+ * Adds support for the `okhsl()` color function in tasty styles.
1576
+ *
1577
+ * @example
1578
+ * ```ts
1579
+ * import { configure } from '@tenphi/tasty';
1580
+ * import { okhslPlugin } from '@tenphi/tasty';
1581
+ *
1582
+ * configure({
1583
+ * plugins: [okhslPlugin()],
1584
+ * });
1585
+ *
1586
+ * // Now you can use okhsl in styles:
1587
+ * const Box = tasty({
1588
+ * styles: {
1589
+ * fill: 'okhsl(240 50% 50%)',
1590
+ * },
1591
+ * });
1592
+ * ```
1593
+ */
1594
+ const okhslPlugin = () => ({
1595
+ name: "okhsl",
1596
+ functions: { okhsl: okhslFunction }
1597
+ });
1598
+ //#endregion
1599
+ //#region src/plugins/okhst-plugin.ts
1600
+ /**
1601
+ * OKHST Plugin for Tasty
1602
+ *
1603
+ * Converts OKHST color syntax to RGB notation.
1604
+ * OKHST is OKHSL with the lightness axis replaced by a contrast-uniform tone axis.
1605
+ * Supports angle units: deg, turn, rad, or unitless (degrees).
1606
+ *
1607
+ * Examples:
1608
+ * okhst(240.5 50% 50%)
1609
+ * okhst(240.5deg 50% 50%)
1610
+ * okhst(0.25turn 50% 50%)
1611
+ * okhst(1.57rad 50% 50%)
1612
+ */
1613
+ /**
1614
+ * The okhst function handler for tasty parser.
1615
+ * Receives parsed style groups and returns an RGB color string.
1616
+ */
1617
+ const okhstFunction = createColorFunc("okhst", okhstToSrgb, "H S T");
1618
+ /**
1619
+ * OKHST Plugin for Tasty.
1620
+ *
1621
+ * Adds support for the `okhst()` color function in tasty styles.
1622
+ *
1623
+ * @example
1624
+ * ```ts
1625
+ * import { configure } from '@tenphi/tasty';
1626
+ * import { okhstPlugin } from '@tenphi/tasty';
1627
+ *
1628
+ * configure({
1629
+ * plugins: [okhstPlugin()],
1630
+ * });
1631
+ *
1632
+ * // Now you can use okhst in styles:
1633
+ * const Box = tasty({
1634
+ * styles: {
1635
+ * fill: 'okhst(240 50% 50%)',
1636
+ * },
1637
+ * });
1638
+ * ```
1639
+ */
1640
+ const okhstPlugin = () => ({
1641
+ name: "okhst",
1642
+ functions: { okhst: okhstFunction }
1643
+ });
1644
+ //#endregion
1645
+ //#region src/plugins/defaults.ts
1646
+ /**
1647
+ * Register the default color functions (okhsl, okhst) with the global parser.
1648
+ *
1649
+ * They are ordinary `functions` entries — the same mechanism any third-party
1650
+ * color plugin uses — so they need no special-casing in core.
1651
+ *
1652
+ * Idempotent and safe to call from the lazy parser initializer so that
1653
+ * zero-config usage keeps working. `resetGlobalParseFunctions()` re-invokes it
1654
+ * after clearing user-registered functions so the defaults survive a config reset.
1655
+ *
1656
+ * @internal
1657
+ */
1658
+ function registerDefaultFunctions() {
1659
+ customFunction("okhsl", okhslFunction);
1660
+ customFunction("okhst", okhstFunction);
1661
+ }
1662
+ //#endregion
1663
+ //#region src/utils/styles.ts
1664
+ /**
1665
+ * Normalize a color token value.
1666
+ * - Boolean `true` is converted to `'transparent'`
1667
+ * - Boolean `false` returns `null` (signals the token should be skipped)
1668
+ * - Other values are returned as-is
1669
+ *
1670
+ * @param value - The raw token value
1671
+ * @returns Normalized value or null if the token should be skipped
1672
+ */
1673
+ function normalizeColorTokenValue(value) {
1674
+ if (value === true) return "transparent";
1675
+ if (value === false) return null;
1676
+ return value;
1677
+ }
1678
+ const COLOR_VAR_PATTERN = /^var\(--([a-z0-9-]+)-color[,)]/;
1679
+ const RGB_ALPHA_PATTERN = /\/\s*([0-9.]+)\)/;
1680
+ const RE_HEX_COLOR = /^#[0-9a-fA-F]{3,8}$/;
1681
+ const RE_VAR_COLOR = /^var\(--[a-z0-9-]+-color/;
1682
+ function isSimpleColorFast(val) {
1683
+ switch (val.charCodeAt(0)) {
1684
+ case 35: return RE_HEX_COLOR.test(val);
1685
+ case 114:
1686
+ if (val.charCodeAt(1) === 103 && val.charCodeAt(2) === 98) return true;
1687
+ break;
1688
+ case 104:
1689
+ if (val.charCodeAt(1) === 115 && val.charCodeAt(2) === 108) return true;
1690
+ break;
1691
+ case 108:
1692
+ if (val.charCodeAt(1) === 99 && val.charCodeAt(2) === 104) return true;
1693
+ break;
1694
+ case 111:
1695
+ if (val.startsWith("oklch(")) return true;
1696
+ break;
1697
+ case 118:
1698
+ if (RE_VAR_COLOR.test(val)) return true;
1699
+ break;
1700
+ case 99:
1701
+ if (val === "currentColor" || val === "currentcolor") return true;
1702
+ break;
1703
+ case 116:
1704
+ if (val === "transparent") return true;
1705
+ break;
1706
+ }
1707
+ return getNamedColorHex().has(val.toLowerCase());
1708
+ }
1709
+ let colorWarningCount = 0;
1710
+ const MAX_COLOR_WARNINGS = 10;
1711
+ const CUSTOM_UNITS = {
1712
+ r: "6px",
1713
+ cr: "10px",
1714
+ bw: "1px",
1715
+ ow: "3px",
1716
+ x: "8px",
1717
+ sf: function sf(num) {
1718
+ return `minmax(0, ${num}fr)`;
1719
+ }
1720
+ };
1721
+ const DIRECTIONS = [
1722
+ "top",
1723
+ "right",
1724
+ "bottom",
1725
+ "left"
1726
+ ];
1727
+ let __tastyParser = null;
1728
+ function getOrCreateParser() {
1729
+ if (!__tastyParser) {
1730
+ __tastyParser = new StyleParser({ units: CUSTOM_UNITS });
1731
+ __tastyParser.setFunctions(__tastyParseFunctions);
1732
+ registerDefaultFunctions();
1733
+ }
1734
+ return __tastyParser;
1735
+ }
1736
+ const __tastyParseFunctions = {};
1737
+ /**
1738
+ * Register a parse-time function.
1739
+ *
1740
+ * @internal Use `configure({ functions })`. This bypasses the configuration lock
1741
+ * and is kept only for the lazy default-function bootstrap and the `@function`
1742
+ * polyfill, both of which must register after `configure()` has run.
1743
+ */
1744
+ function customFunction(name, fn) {
1745
+ __tastyParseFunctions[name] = fn;
1746
+ getOrCreateParser().setFunctions(__tastyParseFunctions);
1747
+ }
1748
+ /**
1749
+ * Reset parse functions back to the built-in set (okhsl/okhst).
1750
+ *
1751
+ * @internal Called by `resetConfig()`.
1752
+ */
1753
+ function resetGlobalParseFunctions() {
1754
+ for (const key of Object.keys(__tastyParseFunctions)) delete __tastyParseFunctions[key];
1755
+ registerDefaultFunctions();
1756
+ getOrCreateParser().setFunctions(__tastyParseFunctions);
1757
+ }
1758
+ /**
1759
+ * Get the global StyleParser instance.
1760
+ * Used by configure() to apply parser configuration.
1761
+ */
1762
+ function getGlobalParser() {
1763
+ return getOrCreateParser();
1764
+ }
1765
+ /**
1766
+ * Get the current custom functions registry.
1767
+ * Used by configure() to merge with new functions.
1768
+ */
1769
+ /**
1770
+ * The live parse-function registry.
1771
+ *
1772
+ * @internal Returns the mutable internal map; writing to it bypasses the
1773
+ * parser's cache invalidation, so the write silently does not take effect.
1774
+ * Read-only, internal use.
1775
+ */
1776
+ function getGlobalParseFunctions() {
1777
+ return __tastyParseFunctions;
1778
+ }
1779
+ /**
1780
+ * Storage for predefined tokens that are replaced during style parsing.
1781
+ * Keys are token names (with $ or # prefix), values are pre-processed CSS values.
1782
+ */
1783
+ let __globalPredefinedTokens = null;
1784
+ /**
1785
+ * Set global predefined tokens.
1786
+ * Called from configure() after processing token values.
1787
+ * Merges with existing tokens (new tokens override existing ones with same key).
1788
+ * Keys are normalized to lowercase (parser lowercases input before classification).
1789
+ * @internal
1790
+ */
1791
+ function setGlobalPredefinedTokens(tokens) {
1792
+ const normalizedTokens = {};
1793
+ for (const [key, value] of Object.entries(tokens)) {
1794
+ const lowerKey = key.toLowerCase();
1795
+ const lowerValue = value.toLowerCase();
1796
+ if (lowerKey.startsWith("#") && lowerValue === "#current") {
1797
+ console.warn(`[Tasty] Using #current to define color token "${key}" is not supported. The #current token represents currentcolor which cannot be used as a base for other tokens.`);
1798
+ continue;
1799
+ }
1800
+ normalizedTokens[lowerKey] = value;
1801
+ }
1802
+ __globalPredefinedTokens = __globalPredefinedTokens ? {
1803
+ ...__globalPredefinedTokens,
1804
+ ...normalizedTokens
1805
+ } : normalizedTokens;
1806
+ getOrCreateParser().clearCache();
1807
+ }
1808
+ /**
1809
+ * Get the current global predefined tokens.
1810
+ * Returns null if no tokens are configured.
1811
+ */
1812
+ function getGlobalPredefinedTokens() {
1813
+ return __globalPredefinedTokens;
1814
+ }
1815
+ /**
1816
+ * Reset global predefined tokens.
1817
+ * Used for testing.
1818
+ * @internal
1819
+ */
1820
+ function resetGlobalPredefinedTokens() {
1821
+ __globalPredefinedTokens = null;
1822
+ getOrCreateParser().clearCache();
1823
+ }
1824
+ /**
1825
+ *
1826
+ * @param {String} value
1827
+ * @param {Number} mode
1828
+ * @returns {Object<String,String|Array>}
1829
+ */
1830
+ function parseStyle(value) {
1831
+ let str;
1832
+ if (typeof value === "string") str = value;
1833
+ else if (typeof value === "number") str = String(value);
1834
+ else str = "";
1835
+ return getOrCreateParser().process(str);
1836
+ }
1837
+ /** A `$`-prefixed custom-property reference — the only DSL syntax that starts with `$`. */
1838
+ const RE_CUSTOM_PROPERTY_REF = /\$[a-zA-Z_$]/;
1839
+ /**
1840
+ * Substitute custom-property references (`$ident` → `var(--ident)`) inside a
1841
+ * value a handler would otherwise emit to CSS verbatim.
1842
+ *
1843
+ * Handlers for keyword-valued properties (`display`, `align`, `text-transform`,
1844
+ * `overflow`, …) pass their input straight through, since there are no units or
1845
+ * color tokens to resolve. That leaked the raw DSL into the stylesheet:
1846
+ * `display: '$my-display'` emitted `display: $my-display`, which the browser
1847
+ * drops as invalid. The same applies to color props whose value the parser could
1848
+ * not classify as a color — `$ident` is a color only when the name ends with
1849
+ * `-color`, so `fill: '$my-fill'` fell back to the unparsed input.
1850
+ *
1851
+ * Values without a `$` skip the parser entirely, so pass-through behaviour (and
1852
+ * the parser's case folding) is unchanged for every value that was already
1853
+ * valid CSS.
1854
+ */
1855
+ function resolveCustomProperties(value) {
1856
+ if (!RE_CUSTOM_PROPERTY_REF.test(value)) return value;
1857
+ return parseStyle(value).output || value;
1858
+ }
1859
+ /**
1860
+ * Parse color. Find it value, name and opacity.
1861
+ * Optimized to avoid heavy parseStyle calls for simple color patterns.
1862
+ */
1863
+ function parseColor(val, ignoreError = false) {
1864
+ if (typeof val !== "string") val = String(val ?? "");
1865
+ val = val.trim();
1866
+ if (!val) return {};
1867
+ const isSimpleColor = isSimpleColorFast(val);
1868
+ let firstColor;
1869
+ if (isSimpleColor) firstColor = val;
1870
+ else {
1871
+ const extractedColor = parseStyle(val).groups.find((g) => g.colors.length)?.colors[0];
1872
+ if (!extractedColor) {
1873
+ if (!ignoreError && colorWarningCount < MAX_COLOR_WARNINGS) {
1874
+ console.warn("[Tasty] unable to parse color:", val);
1875
+ colorWarningCount++;
1876
+ if (colorWarningCount === MAX_COLOR_WARNINGS) console.warn("[Tasty] color parsing warnings will be suppressed from now on");
1877
+ }
1878
+ return {};
1879
+ }
1880
+ firstColor = extractedColor;
1881
+ }
1882
+ const faded = parseAlphaOverride(firstColor);
1883
+ const baseColor = faded ? faded.color : firstColor;
1884
+ const nameMatch = baseColor.match(COLOR_VAR_PATTERN);
1885
+ let opacity;
1886
+ if (faded) {
1887
+ const { alpha } = faded;
1888
+ const v = parseFloat(alpha);
1889
+ if (!isNaN(v)) opacity = alpha.endsWith("%") ? v : v * 100;
1890
+ } else if (baseColor.startsWith("rgb") || baseColor.startsWith("hsl") || baseColor.startsWith("lch") || baseColor.startsWith("oklch")) {
1891
+ const alphaMatch = baseColor.match(RGB_ALPHA_PATTERN);
1892
+ if (alphaMatch) {
1893
+ const v = parseFloat(alphaMatch[1]);
1894
+ if (!isNaN(v)) opacity = v * 100;
1895
+ }
1896
+ }
1897
+ return {
1898
+ color: firstColor,
1899
+ name: nameMatch ? nameMatch[1] : void 0,
1900
+ opacity
1901
+ };
1902
+ }
1903
+ function filterMods(mods, allowedMods) {
1904
+ return mods.filter((mod) => allowedMods.includes(mod));
1905
+ }
1906
+ const _innerCache = /* @__PURE__ */ new WeakMap();
1907
+ const _topLevelCache = /* @__PURE__ */ new WeakMap();
1908
+ function stringifyStyles(styles) {
1909
+ if (styles == null || typeof styles !== "object") return "";
1910
+ const cached = _topLevelCache.get(styles);
1911
+ if (cached !== void 0) return cached;
1912
+ const obj = styles;
1913
+ const keys = Object.keys(obj).sort();
1914
+ const parts = [];
1915
+ for (const k of keys) {
1916
+ const v = obj[k];
1917
+ if (v === void 0 || typeof v === "function" || typeof v === "symbol") continue;
1918
+ const c0 = k.charCodeAt(0);
1919
+ const needsInnerSort = (c0 >= 65 && c0 <= 90 || c0 === 38) && v && typeof v === "object" && !Array.isArray(v);
1920
+ let sv;
1921
+ if (needsInnerSort) {
1922
+ sv = _innerCache.get(v);
1923
+ if (sv === void 0) {
1924
+ const innerObj = v;
1925
+ const innerKeys = Object.keys(innerObj).sort();
1926
+ const innerParts = [];
1927
+ for (const ik of innerKeys) {
1928
+ const ivs = JSON.stringify(innerObj[ik]);
1929
+ if (ivs !== void 0) innerParts.push(JSON.stringify(ik) + ":" + ivs);
1930
+ }
1931
+ sv = "{" + innerParts.join(",") + "}";
1932
+ _innerCache.set(v, sv);
1933
+ }
1934
+ } else sv = JSON.stringify(v);
1935
+ parts.push(JSON.stringify(k) + ":" + sv);
1936
+ }
1937
+ const result = "{" + parts.join(",") + "}";
1938
+ _topLevelCache.set(styles, result);
1939
+ return result;
1940
+ }
1941
+ //#endregion
1942
+ //#region src/utils/function-color.ts
1943
+ const RE_FUNC_NAME = /^([a-z][a-z0-9-]*)\s*\(/i;
1944
+ const RE_COLOR_OUT = /^(?:rgb|hsl|hwb|lab|lch|oklab|oklch|color)\(|^#|^var\(--/i;
1945
+ /**
1946
+ * Resolve a `name(...)` value produced by a registered custom parse function
1947
+ * into its concrete color output.
1948
+ *
1949
+ * A color function is just a `functions` entry whose output is an already
1950
+ * supported color (`rgb`, `hsl`, `#…`, `oklch`, …). This helper delegates the
1951
+ * value to the global parser (which already runs the registered parse function)
1952
+ * and returns the result only when it looks like a color. Returns `null` when
1953
+ * `str` is not a registered custom function or its output is not a color.
1954
+ *
1955
+ * This is the generic replacement for the previously hardcoded okhsl/okhst
1956
+ * conversion branches scattered across `strToRgb`, `resolveToRgbaValues`, and
1957
+ * the `#token.alpha` injection path.
1958
+ */
1959
+ function resolveFunctionColor(str) {
1960
+ const m = RE_FUNC_NAME.exec(str);
1961
+ if (!m) return null;
1962
+ const name = m[1].toLowerCase();
1963
+ getGlobalParser();
1964
+ if (!(name in getGlobalParseFunctions())) return null;
1965
+ const out = getGlobalParser().process(str).output;
1966
+ if (!out || !RE_COLOR_OUT.test(out)) return null;
1967
+ return out;
1968
+ }
1969
+ //#endregion
1970
+ //#region src/utils/colors.ts
1971
+ function color(name, opacity = 1) {
1972
+ if (opacity !== 1) return overrideColorAlpha(`var(--${name}-color)`, String(opacity));
1973
+ return `var(--${name}-color)`;
1974
+ }
1975
+ //#endregion
1976
+ //#region src/utils/dotize.ts
1977
+ const dotize = {
1978
+ valTypes: {
1979
+ none: "NONE",
1980
+ primitive: "PRIM",
1981
+ object: "OBJECT",
1982
+ array: "ARRAY"
1983
+ },
1984
+ getValType: function(val) {
1985
+ if (!val || typeof val != "object" || Array.isArray(val)) return dotize.valTypes.primitive;
1986
+ if (typeof val == "object") return dotize.valTypes.object;
1987
+ },
1988
+ getPathType: function(arrPath) {
1989
+ const arrPathTypes = [];
1990
+ for (const path in arrPath) {
1991
+ const pathVal = arrPath[path];
1992
+ if (!pathVal) arrPathTypes.push(dotize.valTypes.none);
1993
+ else if (dotize.isNumber(pathVal)) arrPathTypes.push(dotize.valTypes.array);
1994
+ else arrPathTypes.push(dotize.valTypes.object);
1995
+ }
1996
+ return arrPathTypes;
1997
+ },
1998
+ isUndefined: function(obj) {
1999
+ return typeof obj == "undefined";
2000
+ },
2001
+ isNumber: function(f) {
2002
+ return !isNaN(parseInt(f));
2003
+ },
2004
+ isEmptyObj: function(obj) {
2005
+ for (const prop in obj) if (Object.hasOwnProperty.call(obj, prop)) return false;
2006
+ return JSON.stringify(obj) === JSON.stringify({});
2007
+ },
2008
+ isPlainObject: function(obj) {
2009
+ if (typeof obj !== "object" || obj === null) return false;
2010
+ return Object.getPrototypeOf(obj) === Object.prototype;
2011
+ },
2012
+ isNotObject: function(obj) {
2013
+ return !obj || !this.isPlainObject(obj);
2014
+ },
2015
+ isEmptyArray: function(arr) {
2016
+ return Array.isArray(arr) && arr.length == 0;
2017
+ },
2018
+ isNotArray: function(arr) {
2019
+ return Array.isArray(arr) == false;
2020
+ },
2021
+ removeEmptyArrayItem: function(arr) {
2022
+ return arr.filter(function(el) {
2023
+ return el != null && el != "";
2024
+ });
2025
+ },
2026
+ getFieldName: function(field, prefix, isRoot, isArrayItem, isArray) {
2027
+ if (isArray) return (prefix ? prefix : "") + (dotize.isNumber(field) ? "[" + field + "]" : (isRoot && !prefix ? "" : ".") + field);
2028
+ else if (isArrayItem) return (prefix ? prefix : "") + "[" + field + "]";
2029
+ else return (prefix ? prefix + "." : "") + field;
2030
+ },
2031
+ startsWith: function(val, valToSearch) {
2032
+ return val.indexOf(valToSearch) == 0;
2033
+ },
2034
+ convert: function(obj, prefix = "") {
2035
+ let newObj = {};
2036
+ if (dotize.isNotObject(obj)) if (prefix) {
2037
+ newObj[prefix] = obj;
2038
+ return newObj;
2039
+ } else return obj;
2040
+ return (function recurse(o, p, isRoot) {
2041
+ const isArrayItem = Array.isArray(o);
2042
+ for (const f in o) {
2043
+ const currentProp = o[f];
2044
+ if (currentProp && typeof currentProp === "object" && !Array.isArray(currentProp) && dotize.isPlainObject(currentProp)) {
2045
+ if (isArrayItem && dotize.isEmptyObj(currentProp) == false) newObj = recurse(currentProp, dotize.getFieldName(f, p, isRoot, true));
2046
+ else if (dotize.isEmptyObj(currentProp) == false) newObj = recurse(currentProp, dotize.getFieldName(f, p, isRoot));
2047
+ else if (dotize.isEmptyObj(currentProp)) newObj[dotize.getFieldName(f, p, isRoot, isArrayItem)] = currentProp;
2048
+ } else if (isArrayItem || dotize.isNumber(f)) newObj[dotize.getFieldName(f, p, isRoot, true)] = currentProp;
2049
+ else newObj[dotize.getFieldName(f, p, isRoot)] = currentProp;
2050
+ }
2051
+ return newObj;
2052
+ })(obj, prefix, true);
2053
+ },
2054
+ backward: function(obj, prefix) {
2055
+ let newObj = {};
2056
+ const arStartRegex = /\[(\d+)\]/g;
2057
+ if (dotize.isNotObject(obj) && dotize.isNotArray(obj)) if (prefix) return obj[prefix];
2058
+ else return obj;
2059
+ for (let tProp in obj) {
2060
+ const tPropVal = obj[tProp];
2061
+ if (prefix) {
2062
+ const prefixRegex = new RegExp("^" + prefix);
2063
+ tProp = tProp.replace(prefixRegex, "");
2064
+ }
2065
+ tProp = tProp.replace(arStartRegex, ".$1");
2066
+ if (dotize.startsWith(tProp, ".")) tProp = tProp.replace(/^\./, "");
2067
+ const arrPath = tProp.split(".");
2068
+ const arrPathTypes = dotize.getPathType(arrPath);
2069
+ if (!dotize.isUndefined(arrPathTypes) && arrPathTypes[0] == dotize.valTypes.array && Array.isArray(newObj) == false) newObj = [];
2070
+ (function recurse(rPropVal, rObj, rPropValPrev, rObjPrev) {
2071
+ let currentPath = arrPath.shift();
2072
+ const currentPathType = arrPathTypes.shift();
2073
+ if (typeof currentPath == "undefined" || currentPath == "") {
2074
+ newObj = rPropVal;
2075
+ return;
2076
+ }
2077
+ const isArray = currentPathType == dotize.valTypes.array;
2078
+ if (dotize.isNumber(currentPath)) currentPath = parseInt(currentPath);
2079
+ if (arrPath.length > 0) {
2080
+ if (typeof rObj[currentPath] == "undefined") if (isArray) rObj[currentPath] = [];
2081
+ else rObj[currentPath] = {};
2082
+ recurse(rPropVal, rObj[currentPath], currentPath, rObj);
2083
+ return;
2084
+ }
2085
+ if (currentPathType == dotize.valTypes.array && rPropValPrev && rObjPrev) {
2086
+ if (Array.isArray(rObjPrev[rPropValPrev]) == false) rObjPrev[rPropValPrev] = [];
2087
+ rObjPrev[rPropValPrev].push(rPropVal);
2088
+ } else rObj[currentPath] = rPropVal;
2089
+ })(tPropVal, newObj);
2090
+ }
2091
+ return newObj;
2092
+ }
2093
+ };
2094
+ //#endregion
2095
+ export { hslToRgbValues as A, createColorFunc as C, getNamedColorHex as D, makeEmptyDetails as E, RE_NUMBER as F, RE_RAW_UNIT as I, normalizeDslName as M, toSnakeCase as N, getRgbValuesFromRgbaString as O, CSS_WIDE_KEYWORDS as P, okhslPlugin as S, Lru as T, setGlobalPredefinedTokens as _, DIRECTIONS as a, okhstPlugin as b, getGlobalParseFunctions as c, normalizeColorTokenValue as d, parseColor as f, resolveCustomProperties as g, resetGlobalPredefinedTokens as h, CUSTOM_UNITS as i, strToRgb as j, hexToRgb as k, getGlobalParser as l, resetGlobalParseFunctions as m, color as n, customFunction as o, parseStyle as p, resolveFunctionColor as r, filterMods as s, dotize as t, getGlobalPredefinedTokens as u, stringifyStyles as v, StyleParser as w, okhslFunction as x, okhstFunction as y };
2096
+
2097
+ //# sourceMappingURL=dsl-CXvoaLnj.js.map