@tenphi/tasty 3.0.2 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/{babel-R2qT7yLN.d.ts → babel-D8dm92E_.d.ts} +2 -2
- package/dist/{collector-D8o4Wdq-.d.ts → collector-BFuqdF2F.d.ts} +2 -2
- package/dist/{collector-BpkjNQDo.js → collector-Di3C8btw.js} +3 -3
- package/dist/{collector-BpkjNQDo.js.map → collector-Di3C8btw.js.map} +1 -1
- package/dist/{config-DOCuTykY.js → config-BrDt11hO.js} +861 -290
- package/dist/config-BrDt11hO.js.map +1 -0
- package/dist/{config-De8L9NWM.d.ts → config-DH7I0Ggx.d.ts} +73 -2
- package/dist/core/index.d.ts +5 -5
- package/dist/core/index.js +6 -6
- package/dist/{core-p6iTbQnn.js → core-YWn1zgh4.js} +9 -10
- package/dist/core-YWn1zgh4.js.map +1 -0
- package/dist/{css-writer-BHjTF0YN.js → css-writer-CQYbT4g_.js} +3 -3
- package/dist/{css-writer-BHjTF0YN.js.map → css-writer-CQYbT4g_.js.map} +1 -1
- package/dist/{format-rules-Cb0YIjyS.js → format-rules-DdmLdntR.js} +11 -9
- package/dist/format-rules-DdmLdntR.js.map +1 -0
- package/dist/{hydrate-xnaB3SDm.js → hydrate-D2ivfS-B.js} +2 -2
- package/dist/{hydrate-xnaB3SDm.js.map → hydrate-D2ivfS-B.js.map} +1 -1
- package/dist/{index-DzGxoeyN.d.ts → index-DljBkZhV.d.ts} +59 -6
- package/dist/{index-KUYya3x7.d.ts → index-LC3O3Jj4.d.ts} +102 -2
- package/dist/index.d.ts +5 -5
- package/dist/index.js +62 -8
- package/dist/index.js.map +1 -1
- package/dist/{keyframes-DiXjNoBZ.js → keyframes-CoLLjBcd.js} +2 -2
- package/dist/{keyframes-DiXjNoBZ.js.map → keyframes-CoLLjBcd.js.map} +1 -1
- package/dist/{merge-styles-DLm4wdnb.d.ts → merge-styles-Cx_6ySBv.d.ts} +2 -2
- package/dist/{merge-styles-B0lMso5W.js → merge-styles-bdh7-1uh.js} +2 -2
- package/dist/{merge-styles-B0lMso5W.js.map → merge-styles-bdh7-1uh.js.map} +1 -1
- package/dist/{resolve-recipes-C9nAuwgR.js → resolve-recipes-9JYSgubn.js} +3 -3
- package/dist/{resolve-recipes-C9nAuwgR.js.map → resolve-recipes-9JYSgubn.js.map} +1 -1
- package/dist/ssr/astro-client.js +1 -1
- package/dist/ssr/astro.js +3 -3
- package/dist/ssr/index.d.ts +1 -1
- package/dist/ssr/index.js +3 -3
- package/dist/ssr/next.d.ts +1 -1
- package/dist/ssr/next.js +4 -4
- package/dist/static/index.d.ts +2 -2
- package/dist/static/index.js +1 -1
- package/dist/zero/babel.d.ts +1 -1
- package/dist/zero/babel.js +4 -4
- package/dist/zero/index.d.ts +1 -1
- package/dist/zero/index.js +1 -1
- package/dist/zero/next.d.ts +1 -1
- package/docs/configuration.md +137 -6
- package/docs/dsl.md +99 -2
- package/docs/injector.md +11 -0
- package/docs/styles.md +3 -1
- package/package.json +6 -6
- package/dist/config-DOCuTykY.js.map +0 -1
- package/dist/core-p6iTbQnn.js.map +0 -1
- package/dist/format-rules-Cb0YIjyS.js.map +0 -1
|
@@ -18,6 +18,23 @@ const CSS_WIDE_KEYWORDS = new Set([
|
|
|
18
18
|
"unset",
|
|
19
19
|
"revert-layer"
|
|
20
20
|
]);
|
|
21
|
+
/**
|
|
22
|
+
* Color functions that *derive* a color from other colors. They take no alpha
|
|
23
|
+
* channel, so opacity has to wrap the whole call in `color-mix()` instead of
|
|
24
|
+
* being appended after a slash.
|
|
25
|
+
*/
|
|
26
|
+
const DERIVED_COLOR_FUNCS_LIST = [
|
|
27
|
+
"color-mix",
|
|
28
|
+
"color-contrast",
|
|
29
|
+
"contrast-color",
|
|
30
|
+
"light-dark"
|
|
31
|
+
];
|
|
32
|
+
const DERIVED_COLOR_FUNCS = new Set(DERIVED_COLOR_FUNCS_LIST);
|
|
33
|
+
/**
|
|
34
|
+
* Every color function the parser recognizes. The ones outside
|
|
35
|
+
* `DERIVED_COLOR_FUNCS` take channels as arguments, optionally followed by
|
|
36
|
+
* `/ <alpha>` — which is where an opacity suffix writes.
|
|
37
|
+
*/
|
|
21
38
|
const COLOR_FUNCS = new Set([
|
|
22
39
|
"rgb",
|
|
23
40
|
"rgba",
|
|
@@ -31,9 +48,20 @@ const COLOR_FUNCS = new Set([
|
|
|
31
48
|
"color",
|
|
32
49
|
"device-cmyk",
|
|
33
50
|
"gray",
|
|
34
|
-
|
|
35
|
-
"color-contrast"
|
|
51
|
+
...DERIVED_COLOR_FUNCS_LIST
|
|
36
52
|
]);
|
|
53
|
+
/** A value that is a single `name(args)` call: captures the name and the args. */
|
|
54
|
+
const RE_FUNC_CALL = /^([a-z][a-z0-9-]*)\((.+)\)$/i;
|
|
55
|
+
/**
|
|
56
|
+
* The color function a value calls at its top level, lowercased — or `null` when
|
|
57
|
+
* the value is not a single color function call.
|
|
58
|
+
*/
|
|
59
|
+
function colorFuncName(value) {
|
|
60
|
+
const match = value.match(RE_FUNC_CALL);
|
|
61
|
+
if (!match) return null;
|
|
62
|
+
const name = match[1].toLowerCase();
|
|
63
|
+
return COLOR_FUNCS.has(name) ? name : null;
|
|
64
|
+
}
|
|
37
65
|
const RE_UNIT_NUM = /^[+-]?(?:\d*\.\d+|\d+)([a-z][a-z0-9]*)$/;
|
|
38
66
|
const RE_NUMBER = /^[+-]?(?:\d*\.\d+|\d+)$/;
|
|
39
67
|
const RE_HEX = /^(?:[0-9a-f]{3,4}|[0-9a-f]{6}(?:[0-9a-f]{2})?)$/;
|
|
@@ -110,141 +138,6 @@ function foldDslCase(src) {
|
|
|
110
138
|
return out + src.slice(last).toLowerCase();
|
|
111
139
|
}
|
|
112
140
|
//#endregion
|
|
113
|
-
//#region src/parser/lru.ts
|
|
114
|
-
var Lru = class {
|
|
115
|
-
limit;
|
|
116
|
-
map = /* @__PURE__ */ new Map();
|
|
117
|
-
head = null;
|
|
118
|
-
tail = null;
|
|
119
|
-
onEvict;
|
|
120
|
-
constructor(limit = 1e3, onEvict) {
|
|
121
|
-
this.limit = limit;
|
|
122
|
-
let normalized = Number.isFinite(this.limit) ? Math.floor(this.limit) : 1e3;
|
|
123
|
-
if (normalized <= 0) normalized = 1e3;
|
|
124
|
-
this.limit = normalized;
|
|
125
|
-
this.onEvict = onEvict;
|
|
126
|
-
}
|
|
127
|
-
setOnEvict(fn) {
|
|
128
|
-
this.onEvict = fn;
|
|
129
|
-
}
|
|
130
|
-
get(key) {
|
|
131
|
-
const node = this.map.get(key);
|
|
132
|
-
if (!node) return void 0;
|
|
133
|
-
this.touch(key, node);
|
|
134
|
-
return node.value;
|
|
135
|
-
}
|
|
136
|
-
set(key, value) {
|
|
137
|
-
let node = this.map.get(key);
|
|
138
|
-
if (node) {
|
|
139
|
-
node.value = value;
|
|
140
|
-
this.touch(key, node);
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
node = {
|
|
144
|
-
prev: null,
|
|
145
|
-
next: this.head,
|
|
146
|
-
value
|
|
147
|
-
};
|
|
148
|
-
if (this.head) {
|
|
149
|
-
const headNode = this.map.get(this.head);
|
|
150
|
-
if (headNode) headNode.prev = key;
|
|
151
|
-
}
|
|
152
|
-
this.head = key;
|
|
153
|
-
if (!this.tail) this.tail = key;
|
|
154
|
-
this.map.set(key, node);
|
|
155
|
-
if (this.map.size > this.limit) this.evict();
|
|
156
|
-
}
|
|
157
|
-
delete(key) {
|
|
158
|
-
const node = this.map.get(key);
|
|
159
|
-
if (!node) return;
|
|
160
|
-
if (node.prev) {
|
|
161
|
-
const prevNode = this.map.get(node.prev);
|
|
162
|
-
if (prevNode) prevNode.next = node.next;
|
|
163
|
-
}
|
|
164
|
-
if (node.next) {
|
|
165
|
-
const nextNode = this.map.get(node.next);
|
|
166
|
-
if (nextNode) nextNode.prev = node.prev;
|
|
167
|
-
}
|
|
168
|
-
if (this.head === key) this.head = node.next;
|
|
169
|
-
if (this.tail === key) this.tail = node.prev;
|
|
170
|
-
this.map.delete(key);
|
|
171
|
-
}
|
|
172
|
-
keys() {
|
|
173
|
-
return this.map.keys();
|
|
174
|
-
}
|
|
175
|
-
touch(key, node) {
|
|
176
|
-
if (this.head === key) return;
|
|
177
|
-
if (node.prev) {
|
|
178
|
-
const prevNode = this.map.get(node.prev);
|
|
179
|
-
if (prevNode) prevNode.next = node.next;
|
|
180
|
-
}
|
|
181
|
-
if (node.next) {
|
|
182
|
-
const nextNode = this.map.get(node.next);
|
|
183
|
-
if (nextNode) nextNode.prev = node.prev;
|
|
184
|
-
}
|
|
185
|
-
if (this.tail === key) this.tail = node.prev;
|
|
186
|
-
node.prev = null;
|
|
187
|
-
node.next = this.head;
|
|
188
|
-
if (this.head) {
|
|
189
|
-
const headNode = this.map.get(this.head);
|
|
190
|
-
if (headNode) headNode.prev = key;
|
|
191
|
-
}
|
|
192
|
-
this.head = key;
|
|
193
|
-
}
|
|
194
|
-
evict() {
|
|
195
|
-
const old = this.tail;
|
|
196
|
-
if (!old) return;
|
|
197
|
-
const node = this.map.get(old);
|
|
198
|
-
if (!node) {
|
|
199
|
-
if (this.head === old) this.head = null;
|
|
200
|
-
this.tail = null;
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
if (node.prev) {
|
|
204
|
-
const prevNode = this.map.get(node.prev);
|
|
205
|
-
if (prevNode) prevNode.next = null;
|
|
206
|
-
}
|
|
207
|
-
this.tail = node.prev;
|
|
208
|
-
if (this.head === old) this.head = null;
|
|
209
|
-
this.map.delete(old);
|
|
210
|
-
if (this.onEvict) try {
|
|
211
|
-
this.onEvict(old, node.value);
|
|
212
|
-
} catch {}
|
|
213
|
-
}
|
|
214
|
-
clear() {
|
|
215
|
-
this.map.clear();
|
|
216
|
-
this.head = this.tail = null;
|
|
217
|
-
}
|
|
218
|
-
};
|
|
219
|
-
//#endregion
|
|
220
|
-
//#region src/utils/function-color.ts
|
|
221
|
-
const RE_FUNC_NAME = /^([a-z][a-z0-9-]*)\s*\(/i;
|
|
222
|
-
const RE_COLOR_OUT = /^(?:rgb|hsl|hwb|lab|lch|oklab|oklch|color)\(|^#|^var\(--/i;
|
|
223
|
-
/**
|
|
224
|
-
* Resolve a `name(...)` value produced by a registered custom parse function
|
|
225
|
-
* into its concrete color output.
|
|
226
|
-
*
|
|
227
|
-
* A color function is just a `functions` entry whose output is an already
|
|
228
|
-
* supported color (`rgb`, `hsl`, `#…`, `oklch`, …). This helper delegates the
|
|
229
|
-
* value to the global parser (which already runs the registered parse function)
|
|
230
|
-
* and returns the result only when it looks like a color. Returns `null` when
|
|
231
|
-
* `str` is not a registered custom function or its output is not a color.
|
|
232
|
-
*
|
|
233
|
-
* This is the generic replacement for the previously hardcoded okhsl/okhst
|
|
234
|
-
* conversion branches scattered across `strToRgb`, `resolveToRgbaValues`, and
|
|
235
|
-
* the `#token.alpha` injection path.
|
|
236
|
-
*/
|
|
237
|
-
function resolveFunctionColor(str) {
|
|
238
|
-
const m = RE_FUNC_NAME.exec(str);
|
|
239
|
-
if (!m) return null;
|
|
240
|
-
const name = m[1].toLowerCase();
|
|
241
|
-
getGlobalParser();
|
|
242
|
-
if (!(name in getGlobalParseFunctions())) return null;
|
|
243
|
-
const out = getGlobalParser().process(str).output;
|
|
244
|
-
if (!out || !RE_COLOR_OUT.test(out)) return null;
|
|
245
|
-
return out;
|
|
246
|
-
}
|
|
247
|
-
//#endregion
|
|
248
141
|
//#region src/utils/color-math.ts
|
|
249
142
|
const OKLab_to_LMS_M = [
|
|
250
143
|
[
|
|
@@ -984,6 +877,141 @@ function oklchStringToRgb(oklchStr) {
|
|
|
984
877
|
return `rgb(${Math.round(r)} ${Math.round(g)} ${Math.round(b)})`;
|
|
985
878
|
}
|
|
986
879
|
//#endregion
|
|
880
|
+
//#region src/parser/lru.ts
|
|
881
|
+
var Lru = class {
|
|
882
|
+
limit;
|
|
883
|
+
map = /* @__PURE__ */ new Map();
|
|
884
|
+
head = null;
|
|
885
|
+
tail = null;
|
|
886
|
+
onEvict;
|
|
887
|
+
constructor(limit = 1e3, onEvict) {
|
|
888
|
+
this.limit = limit;
|
|
889
|
+
let normalized = Number.isFinite(this.limit) ? Math.floor(this.limit) : 1e3;
|
|
890
|
+
if (normalized <= 0) normalized = 1e3;
|
|
891
|
+
this.limit = normalized;
|
|
892
|
+
this.onEvict = onEvict;
|
|
893
|
+
}
|
|
894
|
+
setOnEvict(fn) {
|
|
895
|
+
this.onEvict = fn;
|
|
896
|
+
}
|
|
897
|
+
get(key) {
|
|
898
|
+
const node = this.map.get(key);
|
|
899
|
+
if (!node) return void 0;
|
|
900
|
+
this.touch(key, node);
|
|
901
|
+
return node.value;
|
|
902
|
+
}
|
|
903
|
+
set(key, value) {
|
|
904
|
+
let node = this.map.get(key);
|
|
905
|
+
if (node) {
|
|
906
|
+
node.value = value;
|
|
907
|
+
this.touch(key, node);
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
node = {
|
|
911
|
+
prev: null,
|
|
912
|
+
next: this.head,
|
|
913
|
+
value
|
|
914
|
+
};
|
|
915
|
+
if (this.head) {
|
|
916
|
+
const headNode = this.map.get(this.head);
|
|
917
|
+
if (headNode) headNode.prev = key;
|
|
918
|
+
}
|
|
919
|
+
this.head = key;
|
|
920
|
+
if (!this.tail) this.tail = key;
|
|
921
|
+
this.map.set(key, node);
|
|
922
|
+
if (this.map.size > this.limit) this.evict();
|
|
923
|
+
}
|
|
924
|
+
delete(key) {
|
|
925
|
+
const node = this.map.get(key);
|
|
926
|
+
if (!node) return;
|
|
927
|
+
if (node.prev) {
|
|
928
|
+
const prevNode = this.map.get(node.prev);
|
|
929
|
+
if (prevNode) prevNode.next = node.next;
|
|
930
|
+
}
|
|
931
|
+
if (node.next) {
|
|
932
|
+
const nextNode = this.map.get(node.next);
|
|
933
|
+
if (nextNode) nextNode.prev = node.prev;
|
|
934
|
+
}
|
|
935
|
+
if (this.head === key) this.head = node.next;
|
|
936
|
+
if (this.tail === key) this.tail = node.prev;
|
|
937
|
+
this.map.delete(key);
|
|
938
|
+
}
|
|
939
|
+
keys() {
|
|
940
|
+
return this.map.keys();
|
|
941
|
+
}
|
|
942
|
+
touch(key, node) {
|
|
943
|
+
if (this.head === key) return;
|
|
944
|
+
if (node.prev) {
|
|
945
|
+
const prevNode = this.map.get(node.prev);
|
|
946
|
+
if (prevNode) prevNode.next = node.next;
|
|
947
|
+
}
|
|
948
|
+
if (node.next) {
|
|
949
|
+
const nextNode = this.map.get(node.next);
|
|
950
|
+
if (nextNode) nextNode.prev = node.prev;
|
|
951
|
+
}
|
|
952
|
+
if (this.tail === key) this.tail = node.prev;
|
|
953
|
+
node.prev = null;
|
|
954
|
+
node.next = this.head;
|
|
955
|
+
if (this.head) {
|
|
956
|
+
const headNode = this.map.get(this.head);
|
|
957
|
+
if (headNode) headNode.prev = key;
|
|
958
|
+
}
|
|
959
|
+
this.head = key;
|
|
960
|
+
}
|
|
961
|
+
evict() {
|
|
962
|
+
const old = this.tail;
|
|
963
|
+
if (!old) return;
|
|
964
|
+
const node = this.map.get(old);
|
|
965
|
+
if (!node) {
|
|
966
|
+
if (this.head === old) this.head = null;
|
|
967
|
+
this.tail = null;
|
|
968
|
+
return;
|
|
969
|
+
}
|
|
970
|
+
if (node.prev) {
|
|
971
|
+
const prevNode = this.map.get(node.prev);
|
|
972
|
+
if (prevNode) prevNode.next = null;
|
|
973
|
+
}
|
|
974
|
+
this.tail = node.prev;
|
|
975
|
+
if (this.head === old) this.head = null;
|
|
976
|
+
this.map.delete(old);
|
|
977
|
+
if (this.onEvict) try {
|
|
978
|
+
this.onEvict(old, node.value);
|
|
979
|
+
} catch {}
|
|
980
|
+
}
|
|
981
|
+
clear() {
|
|
982
|
+
this.map.clear();
|
|
983
|
+
this.head = this.tail = null;
|
|
984
|
+
}
|
|
985
|
+
};
|
|
986
|
+
//#endregion
|
|
987
|
+
//#region src/utils/function-color.ts
|
|
988
|
+
const RE_FUNC_NAME = /^([a-z][a-z0-9-]*)\s*\(/i;
|
|
989
|
+
const RE_COLOR_OUT = /^(?:rgb|hsl|hwb|lab|lch|oklab|oklch|color)\(|^#|^var\(--/i;
|
|
990
|
+
/**
|
|
991
|
+
* Resolve a `name(...)` value produced by a registered custom parse function
|
|
992
|
+
* into its concrete color output.
|
|
993
|
+
*
|
|
994
|
+
* A color function is just a `functions` entry whose output is an already
|
|
995
|
+
* supported color (`rgb`, `hsl`, `#…`, `oklch`, …). This helper delegates the
|
|
996
|
+
* value to the global parser (which already runs the registered parse function)
|
|
997
|
+
* and returns the result only when it looks like a color. Returns `null` when
|
|
998
|
+
* `str` is not a registered custom function or its output is not a color.
|
|
999
|
+
*
|
|
1000
|
+
* This is the generic replacement for the previously hardcoded okhsl/okhst
|
|
1001
|
+
* conversion branches scattered across `strToRgb`, `resolveToRgbaValues`, and
|
|
1002
|
+
* the `#token.alpha` injection path.
|
|
1003
|
+
*/
|
|
1004
|
+
function resolveFunctionColor(str) {
|
|
1005
|
+
const m = RE_FUNC_NAME.exec(str);
|
|
1006
|
+
if (!m) return null;
|
|
1007
|
+
const name = m[1].toLowerCase();
|
|
1008
|
+
getGlobalParser();
|
|
1009
|
+
if (!(name in getGlobalParseFunctions())) return null;
|
|
1010
|
+
const out = getGlobalParser().process(str).output;
|
|
1011
|
+
if (!out || !RE_COLOR_OUT.test(out)) return null;
|
|
1012
|
+
return out;
|
|
1013
|
+
}
|
|
1014
|
+
//#endregion
|
|
987
1015
|
//#region src/utils/color-space.ts
|
|
988
1016
|
let currentColorSpace = "oklch";
|
|
989
1017
|
const colorSpaceCache = new Lru(500);
|
|
@@ -1003,9 +1031,6 @@ function resetColorSpace() {
|
|
|
1003
1031
|
function getColorSpaceSuffix() {
|
|
1004
1032
|
return currentColorSpace;
|
|
1005
1033
|
}
|
|
1006
|
-
function getColorSpaceFunc() {
|
|
1007
|
-
return currentColorSpace;
|
|
1008
|
-
}
|
|
1009
1034
|
function formatNum(n, precision) {
|
|
1010
1035
|
return parseFloat(n.toFixed(precision)).toString();
|
|
1011
1036
|
}
|
|
@@ -1262,6 +1287,91 @@ function strToColorSpace(color) {
|
|
|
1262
1287
|
return result;
|
|
1263
1288
|
}
|
|
1264
1289
|
/**
|
|
1290
|
+
* Set the alpha of a whole color, replacing any it already carries.
|
|
1291
|
+
*
|
|
1292
|
+
* CSS relative color syntax is how opacity is applied to every color Tasty
|
|
1293
|
+
* emits. `oklch(from <color> l c h / <alpha>)` copies the channels over and
|
|
1294
|
+
* writes the alpha slot, which needs nothing from the color except that it *is*
|
|
1295
|
+
* a color: a `color-mix()`, a `light-dark()`, a `currentcolor`, a
|
|
1296
|
+
* `--name-color` written by hand-authored CSS with no companion variable — all
|
|
1297
|
+
* of them work, where writing into a channel slot directly requires components
|
|
1298
|
+
* the engine may have no way to compute.
|
|
1299
|
+
*
|
|
1300
|
+
* Alpha is *replaced*, not composed. A token holding `rgb(255 0 0 / .8)` faded
|
|
1301
|
+
* to `.5` is alpha `.5`, matching what the channel-components form did and what
|
|
1302
|
+
* a statically-known color still does. `color-mix()` against `transparent`
|
|
1303
|
+
* cannot do this — it would multiply the two to `.4`.
|
|
1304
|
+
*
|
|
1305
|
+
* The alpha slot takes a `<number>` or a `<percentage>`, so an opacity custom
|
|
1306
|
+
* property passes straight through in whichever form the author declared it.
|
|
1307
|
+
*
|
|
1308
|
+
* The space is always `oklch`, regardless of the configured {@link ColorSpace}:
|
|
1309
|
+
* it is unbounded, so a wide-gamut color survives the round trip that a
|
|
1310
|
+
* gamut-limited space would clamp, and it is the space the computed value used
|
|
1311
|
+
* to be reported in. Channels are copied rather than interpolated, so the polar
|
|
1312
|
+
* form costs nothing even for an achromatic color, whose hue is carried through
|
|
1313
|
+
* untouched.
|
|
1314
|
+
*/
|
|
1315
|
+
function overrideColorAlpha(color, alpha) {
|
|
1316
|
+
return `oklch(from ${color} l c h / ${alpha})`;
|
|
1317
|
+
}
|
|
1318
|
+
/**
|
|
1319
|
+
* Compose an alpha onto a color, multiplying with any it already carries.
|
|
1320
|
+
*
|
|
1321
|
+
* This is the counterpart to {@link overrideColorAlpha}, and the difference is
|
|
1322
|
+
* the point. A design token names a color, so fading it *sets* its alpha. But
|
|
1323
|
+
* `currentcolor` is the color an element **inherits**, which an ancestor may
|
|
1324
|
+
* already have faded — `#current.4` there means "40% of what reaches me", and a
|
|
1325
|
+
* nested `#current.18` under it composes to `.072`. Design systems build ramps
|
|
1326
|
+
* out of exactly that, so `#current` composes and a token replaces.
|
|
1327
|
+
*
|
|
1328
|
+
* `color-mix()` against `transparent` is what composes: mixing premultiplied
|
|
1329
|
+
* leaves the channels alone and multiplies the alphas. Its percentage slot takes
|
|
1330
|
+
* no `<number>`, so a `$prop` alpha has to be scaled — which is why an opacity
|
|
1331
|
+
* property used as `#current.$prop` has to hold a unitless number, where a token
|
|
1332
|
+
* accepts either form.
|
|
1333
|
+
*/
|
|
1334
|
+
function mixColorAlpha(color, percentage) {
|
|
1335
|
+
return `color-mix(in oklab, ${color} ${percentage}, transparent)`;
|
|
1336
|
+
}
|
|
1337
|
+
/**
|
|
1338
|
+
* Matches what {@link overrideColorAlpha} builds. The first group is greedy so a
|
|
1339
|
+
* nested override splits on its outermost layer.
|
|
1340
|
+
*/
|
|
1341
|
+
const RE_ALPHA_OVERRIDE = /^oklch\(from (.+) l c h \/ (.+)\)$/;
|
|
1342
|
+
/**
|
|
1343
|
+
* Split what {@link overrideColorAlpha} builds back into the color it faded and
|
|
1344
|
+
* the alpha, or `null` when the value is not one of those.
|
|
1345
|
+
*/
|
|
1346
|
+
function parseAlphaOverride(value) {
|
|
1347
|
+
const match = value.match(RE_ALPHA_OVERRIDE);
|
|
1348
|
+
return match ? {
|
|
1349
|
+
color: match[1],
|
|
1350
|
+
alpha: match[2]
|
|
1351
|
+
} : null;
|
|
1352
|
+
}
|
|
1353
|
+
/** Channel names of each color space, in `<func>()` argument order. */
|
|
1354
|
+
const COLOR_SPACE_CHANNELS = {
|
|
1355
|
+
rgb: "r g b",
|
|
1356
|
+
hsl: "h s l",
|
|
1357
|
+
oklch: "l c h"
|
|
1358
|
+
};
|
|
1359
|
+
/**
|
|
1360
|
+
* Express a color as components of the configured color space *by reference*,
|
|
1361
|
+
* using CSS relative color syntax: `from <color> l c h`.
|
|
1362
|
+
*
|
|
1363
|
+
* Static colors are decomposed into numbers (see `getColorSpaceComponents`), but
|
|
1364
|
+
* a color the engine cannot evaluate at build time — a `color-mix()`, a
|
|
1365
|
+
* `light-dark()`, a `color()` in a space with no conversion, anything reached
|
|
1366
|
+
* through `var()` — has no numbers to decompose. Handing back the relative form
|
|
1367
|
+
* keeps the `--name-color-{space}` companion usable anyway: the browser resolves
|
|
1368
|
+
* the channels, so `oklch(var(--name-color-oklch) / .5)` still applies opacity to
|
|
1369
|
+
* whatever the color turns out to be.
|
|
1370
|
+
*/
|
|
1371
|
+
function toRelativeColorSpaceComponents(color) {
|
|
1372
|
+
return `from ${color} ${COLOR_SPACE_CHANNELS[currentColorSpace]}`;
|
|
1373
|
+
}
|
|
1374
|
+
/**
|
|
1265
1375
|
* Extract the decomposed components of a color in the configured color space.
|
|
1266
1376
|
* Returns a space-separated string of components without the wrapping function.
|
|
1267
1377
|
* Alpha is NOT included — components are used for alpha composition via `/ alpha`.
|
|
@@ -1282,6 +1392,41 @@ function getColorSpaceComponents(color) {
|
|
|
1282
1392
|
return result;
|
|
1283
1393
|
}
|
|
1284
1394
|
/**
|
|
1395
|
+
* Rewrite a color into the `--name-color-{space}` components that `#name.alpha`
|
|
1396
|
+
* composes opacity onto.
|
|
1397
|
+
*
|
|
1398
|
+
* A `var()` chain is rewritten reference by reference so the fallback order
|
|
1399
|
+
* survives; a color the engine can evaluate is decomposed into numbers; anything
|
|
1400
|
+
* left — a derived color function, a `color()` in a space with no conversion —
|
|
1401
|
+
* falls back to relative color syntax and lets the browser do it.
|
|
1402
|
+
*
|
|
1403
|
+
* Returns the input unchanged when there is nothing to rewrite, so callers can
|
|
1404
|
+
* tell whether the conversion found anything.
|
|
1405
|
+
*
|
|
1406
|
+
* Example: `var(--primary-color, var(--secondary-color))`
|
|
1407
|
+
* → `var(--primary-color-oklch, var(--secondary-color-oklch))`
|
|
1408
|
+
*/
|
|
1409
|
+
function convertColorChainToComponentChain(colorValue) {
|
|
1410
|
+
const suffix = getColorSpaceSuffix();
|
|
1411
|
+
const faded = parseAlphaOverride(colorValue);
|
|
1412
|
+
if (faded) return convertColorChainToComponentChain(faded.color);
|
|
1413
|
+
const componentVarMatch = colorValue.match(/^(?:rgb|hsl|oklch)a?\(\s*(var\(--[a-z0-9-]+-color-(?:rgb|hsl|oklch)\))\s*\//);
|
|
1414
|
+
if (componentVarMatch) return componentVarMatch[1];
|
|
1415
|
+
if (colorFuncName(colorValue)) {
|
|
1416
|
+
const components = getColorSpaceComponents(colorValue);
|
|
1417
|
+
return components !== colorValue ? components : toRelativeColorSpaceComponents(colorValue);
|
|
1418
|
+
}
|
|
1419
|
+
const match = colorValue.match(/var\(--([a-z0-9-]+)-color\s*(?:,\s*(.+))?\)/);
|
|
1420
|
+
if (!match) {
|
|
1421
|
+
const components = getColorSpaceComponents(colorValue);
|
|
1422
|
+
if (components !== colorValue) return components;
|
|
1423
|
+
return colorValue;
|
|
1424
|
+
}
|
|
1425
|
+
const [, name, fallback] = match;
|
|
1426
|
+
if (!fallback) return `var(--${name}-color-${suffix})`;
|
|
1427
|
+
return `var(--${name}-color-${suffix}, ${convertColorChainToComponentChain(fallback.trim())})`;
|
|
1428
|
+
}
|
|
1429
|
+
/**
|
|
1285
1430
|
* Convert a color initial value (from @property definitions) to components
|
|
1286
1431
|
* in the configured color space.
|
|
1287
1432
|
*/
|
|
@@ -1354,6 +1499,53 @@ const finalizeGroup = (d, parts) => {
|
|
|
1354
1499
|
//#endregion
|
|
1355
1500
|
//#region src/parser/classify.ts
|
|
1356
1501
|
/**
|
|
1502
|
+
* Convert an opacity suffix to the alpha value it denotes.
|
|
1503
|
+
*
|
|
1504
|
+
* The authored digits are kept verbatim — `.07` stays `.07` rather than being
|
|
1505
|
+
* multiplied into `7.000000000000001%` — and a `$prop` suffix passes the
|
|
1506
|
+
* reference straight through, so it works whether the property holds a
|
|
1507
|
+
* `<number>` or a `<percentage>`. Both are what the alpha slot accepts.
|
|
1508
|
+
*/
|
|
1509
|
+
function alphaSuffixToAlpha(rawAlpha) {
|
|
1510
|
+
if (rawAlpha.startsWith("$")) return `var(--${rawAlpha.slice(1)})`;
|
|
1511
|
+
if (rawAlpha === "0") return "0";
|
|
1512
|
+
return `.${rawAlpha}`;
|
|
1513
|
+
}
|
|
1514
|
+
/** Apply an opacity suffix to a color, replacing any alpha it already carries. */
|
|
1515
|
+
function fadeColor(color, rawAlpha) {
|
|
1516
|
+
return overrideColorAlpha(color, alphaSuffixToAlpha(rawAlpha));
|
|
1517
|
+
}
|
|
1518
|
+
/**
|
|
1519
|
+
* Convert an opacity suffix to the percentage `color-mix()` needs, by shifting
|
|
1520
|
+
* the decimal point rather than multiplying: `.07` is `7%`, not the
|
|
1521
|
+
* `7.000000000000001%` that `parseFloat('.07') * 100` produces.
|
|
1522
|
+
*/
|
|
1523
|
+
function alphaSuffixToPercentage(rawAlpha) {
|
|
1524
|
+
if (rawAlpha.startsWith("$")) return `calc(var(--${rawAlpha.slice(1)}) * 100%)`;
|
|
1525
|
+
if (rawAlpha === "0") return "0%";
|
|
1526
|
+
const digits = rawAlpha.length === 1 ? `${rawAlpha}0` : rawAlpha;
|
|
1527
|
+
const whole = String(Number(digits.slice(0, 2)));
|
|
1528
|
+
const fraction = digits.slice(2);
|
|
1529
|
+
return `${fraction ? `${whole}.${fraction}` : whole}%`;
|
|
1530
|
+
}
|
|
1531
|
+
/**
|
|
1532
|
+
* Apply an opacity suffix to `currentcolor`, composing with any alpha an
|
|
1533
|
+
* ancestor already applied. See {@link mixColorAlpha} for why this differs from
|
|
1534
|
+
* how a token is faded.
|
|
1535
|
+
*/
|
|
1536
|
+
function fadeCurrentColor(rawAlpha) {
|
|
1537
|
+
return mixColorAlpha("currentcolor", alphaSuffixToPercentage(rawAlpha));
|
|
1538
|
+
}
|
|
1539
|
+
/**
|
|
1540
|
+
* Whether parsed function arguments hold a color. Colors reach either the color
|
|
1541
|
+
* bucket (`#token`, a nested color function, `transparent`) or — for the CSS
|
|
1542
|
+
* named colors, which the parser has no token syntax for — the modifier bucket.
|
|
1543
|
+
*/
|
|
1544
|
+
function hasColorArgs(parsed) {
|
|
1545
|
+
const namedColors = getNamedColorHex();
|
|
1546
|
+
return parsed.groups.some((group) => group.colors.length > 0 || group.mods.some((mod) => namedColors.has(mod)));
|
|
1547
|
+
}
|
|
1548
|
+
/**
|
|
1357
1549
|
* Re-parses a value through the parser until it stabilizes (no changes)
|
|
1358
1550
|
* or max iterations reached. This allows units to reference other units.
|
|
1359
1551
|
* Example: { x: '8px', y: '2x' } -> '1y' resolves to '16px'
|
|
@@ -1455,17 +1647,10 @@ function classify(raw, opts, recurse) {
|
|
|
1455
1647
|
processed: "currentcolor"
|
|
1456
1648
|
};
|
|
1457
1649
|
const currentAlphaMatch = token.match(/^#current\.(\$[a-z_][a-z0-9-_]*|[0-9]+)$/i);
|
|
1458
|
-
if (currentAlphaMatch) {
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
else if (rawAlpha === "0") percentage = "0%";
|
|
1463
|
-
else percentage = `${parseFloat("." + rawAlpha) * 100}%`;
|
|
1464
|
-
return {
|
|
1465
|
-
bucket: 0,
|
|
1466
|
-
processed: `color-mix(in oklab, currentcolor ${percentage}, transparent)`
|
|
1467
|
-
};
|
|
1468
|
-
}
|
|
1650
|
+
if (currentAlphaMatch) return {
|
|
1651
|
+
bucket: 0,
|
|
1652
|
+
processed: fadeCurrentColor(currentAlphaMatch[1])
|
|
1653
|
+
};
|
|
1469
1654
|
if (token[0] === "$" || token[0] === "#") {
|
|
1470
1655
|
const predefinedTokens = getGlobalPredefinedTokens();
|
|
1471
1656
|
if (predefinedTokens) {
|
|
@@ -1482,10 +1667,14 @@ function classify(raw, opts, recurse) {
|
|
|
1482
1667
|
if (baseKey in predefinedTokens) {
|
|
1483
1668
|
const resolvedValue = predefinedTokens[baseKey];
|
|
1484
1669
|
if (resolvedValue.startsWith("#")) return classify(`${foldDslCase(resolvedValue)}.${rawAlpha}`, opts, recurse);
|
|
1485
|
-
const funcMatch = resolvedValue.match(
|
|
1670
|
+
const funcMatch = resolvedValue.match(RE_FUNC_CALL);
|
|
1486
1671
|
if (funcMatch) {
|
|
1487
1672
|
const [, funcName, args] = funcMatch;
|
|
1488
1673
|
const lowerFunc = funcName.toLowerCase();
|
|
1674
|
+
if (DERIVED_COLOR_FUNCS.has(lowerFunc)) return {
|
|
1675
|
+
bucket: 0,
|
|
1676
|
+
processed: fadeColor(classify(foldDslCase(resolvedValue), opts, recurse).processed, rawAlpha)
|
|
1677
|
+
};
|
|
1489
1678
|
const isCustomFunc = !!(opts.functions && lowerFunc in opts.functions && !COLOR_FUNCS.has(lowerFunc) && !COLOR_FUNCS.has(funcName.replace(/a$/i, "").toLowerCase()));
|
|
1490
1679
|
const normalizedFunc = isCustomFunc ? lowerFunc : funcName.replace(/a$/i, "").toLowerCase();
|
|
1491
1680
|
if (!(COLOR_FUNCS.has(normalizedFunc) || COLOR_FUNCS.has(lowerFunc) || isCustomFunc)) return classify(`${resolvedValue}.${rawAlpha}`, opts, recurse);
|
|
@@ -1573,13 +1762,9 @@ function classify(raw, opts, recurse) {
|
|
|
1573
1762
|
const alphaMatch = token.match(/^#([a-z0-9-]+)\.(\$[a-z_][a-z0-9-_]*|[0-9]+)$/i);
|
|
1574
1763
|
if (alphaMatch) {
|
|
1575
1764
|
const [, base, rawAlpha] = alphaMatch;
|
|
1576
|
-
let alpha;
|
|
1577
|
-
if (rawAlpha.startsWith("$")) alpha = `var(--${rawAlpha.slice(1)})`;
|
|
1578
|
-
else if (rawAlpha === "0") alpha = "0";
|
|
1579
|
-
else alpha = `.${rawAlpha}`;
|
|
1580
1765
|
return {
|
|
1581
1766
|
bucket: 0,
|
|
1582
|
-
processed:
|
|
1767
|
+
processed: fadeColor(`var(--${base}-color)`, rawAlpha)
|
|
1583
1768
|
};
|
|
1584
1769
|
}
|
|
1585
1770
|
const name = token.slice(1);
|
|
@@ -1597,10 +1782,12 @@ function classify(raw, opts, recurse) {
|
|
|
1597
1782
|
const fname = token.slice(0, openIdx);
|
|
1598
1783
|
const inner = token.slice(openIdx + 1, -1);
|
|
1599
1784
|
if (COLOR_FUNCS.has(fname)) {
|
|
1600
|
-
const
|
|
1785
|
+
const parsedInner = recurse(inner);
|
|
1786
|
+
const argProcessed = parsedInner.output.replace(/,\s+/g, ",");
|
|
1787
|
+
const processed = `${canonicalFuncName(fname)}(${argProcessed})`;
|
|
1601
1788
|
return {
|
|
1602
|
-
bucket: 0,
|
|
1603
|
-
processed
|
|
1789
|
+
bucket: fname === "light-dark" && !hasColorArgs(parsedInner) ? 1 : 0,
|
|
1790
|
+
processed
|
|
1604
1791
|
};
|
|
1605
1792
|
}
|
|
1606
1793
|
if (opts.functions && fname in opts.functions) {
|
|
@@ -2076,8 +2263,8 @@ function normalizeColorTokenValue(value) {
|
|
|
2076
2263
|
if (value === false) return null;
|
|
2077
2264
|
return value;
|
|
2078
2265
|
}
|
|
2079
|
-
const COLOR_VAR_PATTERN =
|
|
2080
|
-
const COLOR_VAR_COMPONENTS_PATTERN =
|
|
2266
|
+
const COLOR_VAR_PATTERN = /^var\(--([a-z0-9-]+)-color[,)]/;
|
|
2267
|
+
const COLOR_VAR_COMPONENTS_PATTERN = /^(?:[a-z-]+\(\s*)?var\(--([a-z0-9-]+)-color-(?:rgb|hsl|oklch)[,)]/;
|
|
2081
2268
|
const RGB_ALPHA_PATTERN = /\/\s*([0-9.]+)\)/;
|
|
2082
2269
|
const RE_HEX_COLOR = /^#[0-9a-fA-F]{3,8}$/;
|
|
2083
2270
|
const RE_VAR_COLOR = /^var\(--[a-z0-9-]+-color/;
|
|
@@ -2267,11 +2454,17 @@ function parseColor(val, ignoreError = false) {
|
|
|
2267
2454
|
}
|
|
2268
2455
|
firstColor = extractedColor;
|
|
2269
2456
|
}
|
|
2270
|
-
|
|
2271
|
-
|
|
2457
|
+
const faded = parseAlphaOverride(firstColor);
|
|
2458
|
+
const baseColor = faded ? faded.color : firstColor;
|
|
2459
|
+
let nameMatch = baseColor.match(COLOR_VAR_PATTERN);
|
|
2460
|
+
if (!nameMatch) nameMatch = baseColor.match(COLOR_VAR_COMPONENTS_PATTERN);
|
|
2272
2461
|
let opacity;
|
|
2273
|
-
if (
|
|
2274
|
-
const
|
|
2462
|
+
if (faded) {
|
|
2463
|
+
const { alpha } = faded;
|
|
2464
|
+
const v = parseFloat(alpha);
|
|
2465
|
+
if (!isNaN(v)) opacity = alpha.endsWith("%") ? v : v * 100;
|
|
2466
|
+
} else if (baseColor.startsWith("rgb") || baseColor.startsWith("hsl") || baseColor.startsWith("lch") || baseColor.startsWith("oklch")) {
|
|
2467
|
+
const alphaMatch = baseColor.match(RGB_ALPHA_PATTERN);
|
|
2275
2468
|
if (alphaMatch) {
|
|
2276
2469
|
const v = parseFloat(alphaMatch[1]);
|
|
2277
2470
|
if (!isNaN(v)) opacity = v * 100;
|
|
@@ -3230,6 +3423,217 @@ function rscClassRegexGlobal(prefix) {
|
|
|
3230
3423
|
return new RegExp(`\\.(${escapeRegex(prefix)}[a-z0-9]+)\\.\\1`, "g");
|
|
3231
3424
|
}
|
|
3232
3425
|
//#endregion
|
|
3426
|
+
//#region src/utils/warnings.ts
|
|
3427
|
+
const PREFIX = "[Tasty]";
|
|
3428
|
+
function warn(...args) {
|
|
3429
|
+
console.warn(PREFIX, ...args);
|
|
3430
|
+
}
|
|
3431
|
+
//#endregion
|
|
3432
|
+
//#region src/injector/batch.ts
|
|
3433
|
+
/**
|
|
3434
|
+
* Deferred CSSOM writes ("batched injection").
|
|
3435
|
+
*
|
|
3436
|
+
* Every `insertRule()` on a live stylesheet invalidates style for the sheet's
|
|
3437
|
+
* scope, so Blink recalculates style the next time anything reads layout or
|
|
3438
|
+
* computed style. When components inject during React's render phase and other
|
|
3439
|
+
* components read layout in the same pass, the two interleave:
|
|
3440
|
+
*
|
|
3441
|
+
* inject -> read (forced recalc) -> inject -> read (forced recalc) -> ...
|
|
3442
|
+
*
|
|
3443
|
+
* Batching moves every sheet write out from between those reads. Writes are
|
|
3444
|
+
* queued in FIFO order and drained in one go, so the tree is invalidated once
|
|
3445
|
+
* per flush instead of once per component.
|
|
3446
|
+
*
|
|
3447
|
+
* ## Ordering
|
|
3448
|
+
*
|
|
3449
|
+
* A single queue holds *all* writes — component rules, global rules, keyframes,
|
|
3450
|
+
* `@property`, `@font-face`, `@counter-style`, `@function` and raw CSS. Draining
|
|
3451
|
+
* it in insertion order keeps the sheet byte-identical to unbatched output,
|
|
3452
|
+
* which matters because equal-specificity rules resolve by document order.
|
|
3453
|
+
*
|
|
3454
|
+
* ## Batch windows — why queuing is safe
|
|
3455
|
+
*
|
|
3456
|
+
* Deferring a write past React's layout phase would let a `useLayoutEffect`
|
|
3457
|
+
* measure an element whose rules are not in the sheet yet, reading the unstyled
|
|
3458
|
+
* box. `<TastyBatchProvider>` closes that hole by opening a *window* during its
|
|
3459
|
+
* render and closing it — flushing — in its `useInsertionEffect`, which React
|
|
3460
|
+
* runs in the mutation phase, before any layout effect:
|
|
3461
|
+
*
|
|
3462
|
+
* provider renders -> window OPEN
|
|
3463
|
+
* children render -> injections queued
|
|
3464
|
+
* provider insertionEffect -> FLUSH, window CLOSED
|
|
3465
|
+
* layout effects run -> rules are in the sheet
|
|
3466
|
+
*
|
|
3467
|
+
* So in the default (`batchInjection: true`) mode a write is only ever queued
|
|
3468
|
+
* inside a commit whose flush is already guaranteed by that same commit. Every
|
|
3469
|
+
* injection outside a window — a deep update the provider did not re-render
|
|
3470
|
+
* for, a `useLayoutEffect` that injects, an event handler, an async callback —
|
|
3471
|
+
* is written straight through, exactly as with batching off.
|
|
3472
|
+
*
|
|
3473
|
+
* `batchInjection: 'always'` opts out of the gate and queues unconditionally.
|
|
3474
|
+
* That wins on more commits, at the cost of the measurement hazard above.
|
|
3475
|
+
*
|
|
3476
|
+
* ## Flush points (earliest wins)
|
|
3477
|
+
*
|
|
3478
|
+
* 1. `<TastyBatchProvider>`'s `useInsertionEffect` — closes the window.
|
|
3479
|
+
* 2. A microtask — a backstop for a render that was aborted or suspended and
|
|
3480
|
+
* therefore never reached its insertion effect. An aborted render mounts
|
|
3481
|
+
* nothing, so nothing can measure what it queued. Microtasks also always
|
|
3482
|
+
* drain before paint, so styles are never visually missing.
|
|
3483
|
+
* 3. `flushStyles()` — explicit, and called internally by every injector read
|
|
3484
|
+
* API (`getCSSText`, `cleanup`, `gc`, `destroy`, ...).
|
|
3485
|
+
*/
|
|
3486
|
+
/**
|
|
3487
|
+
* FIFO of pending writes. Drained with a moving `head` index rather than
|
|
3488
|
+
* `shift()` so a large batch stays O(n) instead of O(n²).
|
|
3489
|
+
*/
|
|
3490
|
+
let queue = [];
|
|
3491
|
+
let head = 0;
|
|
3492
|
+
let microtaskScheduled = false;
|
|
3493
|
+
let flushing = false;
|
|
3494
|
+
/**
|
|
3495
|
+
* Whether a `<TastyBatchProvider>` has rendered in the current commit and will
|
|
3496
|
+
* therefore flush in its insertion effect, making it safe to queue a write.
|
|
3497
|
+
*
|
|
3498
|
+
* A flag rather than a depth count, because renders and insertion effects do
|
|
3499
|
+
* not pair up one-to-one. StrictMode double-invokes render but runs the
|
|
3500
|
+
* insertion effect once, so a counter ends the commit stuck above zero — and a
|
|
3501
|
+
* window that stays open past its commit turns the next provider-less commit
|
|
3502
|
+
* into `'always'` mode behind the user's back, which is exactly the measurement
|
|
3503
|
+
* hazard `true` exists to avoid.
|
|
3504
|
+
*
|
|
3505
|
+
* Clearing on the first close is safe with nested or sibling providers: React
|
|
3506
|
+
* finishes every render in a commit before running any insertion effect, so once
|
|
3507
|
+
* one closes, no further render-phase injection can arrive in that commit.
|
|
3508
|
+
*/
|
|
3509
|
+
let windowOpen = false;
|
|
3510
|
+
/** Whether any window has ever been opened, i.e. a provider is in the tree. */
|
|
3511
|
+
let everOpened = false;
|
|
3512
|
+
/** Dev-only: warn at most once that batching is on with no provider mounted. */
|
|
3513
|
+
let warnedNoProvider = false;
|
|
3514
|
+
/**
|
|
3515
|
+
* Queue a sheet write.
|
|
3516
|
+
*
|
|
3517
|
+
* Returns a handle whose `cancel()` drops the write if the caller disposes
|
|
3518
|
+
* before the flush. The handle is intentionally tiny — one is allocated per
|
|
3519
|
+
* injected class, on the cache-miss path only.
|
|
3520
|
+
*/
|
|
3521
|
+
function enqueueStyleWrite(run) {
|
|
3522
|
+
if (flushing) {
|
|
3523
|
+
run();
|
|
3524
|
+
return {
|
|
3525
|
+
run,
|
|
3526
|
+
cancelled: false,
|
|
3527
|
+
done: true
|
|
3528
|
+
};
|
|
3529
|
+
}
|
|
3530
|
+
const entry = {
|
|
3531
|
+
run,
|
|
3532
|
+
cancelled: false,
|
|
3533
|
+
done: false
|
|
3534
|
+
};
|
|
3535
|
+
queue.push(entry);
|
|
3536
|
+
scheduleMicrotaskFlush();
|
|
3537
|
+
return entry;
|
|
3538
|
+
}
|
|
3539
|
+
/** Whether any write is still waiting to hit a stylesheet. */
|
|
3540
|
+
function hasPendingStyleWrites() {
|
|
3541
|
+
return head < queue.length;
|
|
3542
|
+
}
|
|
3543
|
+
/**
|
|
3544
|
+
* Open a batch window. Called from `<TastyBatchProvider>`'s render, so every
|
|
3545
|
+
* descendant injection in this commit is covered by the provider's insertion
|
|
3546
|
+
* effect.
|
|
3547
|
+
*
|
|
3548
|
+
* Called during render on purpose: the window must be open before children
|
|
3549
|
+
* render. It is idempotent — so StrictMode's double render costs nothing — and
|
|
3550
|
+
* carries no other side effect. A render that is thrown away is recovered by the
|
|
3551
|
+
* microtask backstop.
|
|
3552
|
+
*
|
|
3553
|
+
* A no-op without a `document`. On the server there is no sheet to batch
|
|
3554
|
+
* against, and `useInsertionEffect` never runs, so nothing would ever close a
|
|
3555
|
+
* window this opened. Skipping it keeps the provider inert during SSR and RSC
|
|
3556
|
+
* instead of merely harmless.
|
|
3557
|
+
*/
|
|
3558
|
+
function openBatchWindow() {
|
|
3559
|
+
if (typeof document === "undefined") return;
|
|
3560
|
+
windowOpen = true;
|
|
3561
|
+
everOpened = true;
|
|
3562
|
+
}
|
|
3563
|
+
/**
|
|
3564
|
+
* Close a batch window and flush. Called from the provider's
|
|
3565
|
+
* `useInsertionEffect`, i.e. after every render in the commit and before any
|
|
3566
|
+
* layout effect.
|
|
3567
|
+
*/
|
|
3568
|
+
function closeBatchWindow() {
|
|
3569
|
+
windowOpen = false;
|
|
3570
|
+
flushStyles();
|
|
3571
|
+
}
|
|
3572
|
+
/** Whether a batch window is currently open. */
|
|
3573
|
+
function isBatchWindowOpen() {
|
|
3574
|
+
return windowOpen;
|
|
3575
|
+
}
|
|
3576
|
+
function scheduleMicrotaskFlush() {
|
|
3577
|
+
if (microtaskScheduled || flushing) return;
|
|
3578
|
+
microtaskScheduled = true;
|
|
3579
|
+
queueMicrotask(() => {
|
|
3580
|
+
microtaskScheduled = false;
|
|
3581
|
+
windowOpen = false;
|
|
3582
|
+
flushStyles();
|
|
3583
|
+
});
|
|
3584
|
+
}
|
|
3585
|
+
/**
|
|
3586
|
+
* Drain every pending sheet write, in insertion order.
|
|
3587
|
+
*
|
|
3588
|
+
* Safe to call when the queue is empty (the common case, so the guard comes
|
|
3589
|
+
* first) and safe to call re-entrantly: a nested `flushStyles()` is a no-op,
|
|
3590
|
+
* and work a draining write triggers is written in place by
|
|
3591
|
+
* `enqueueStyleWrite` rather than queued behind the rest of the batch.
|
|
3592
|
+
*/
|
|
3593
|
+
function flushStyles() {
|
|
3594
|
+
if (head >= queue.length) return;
|
|
3595
|
+
if (flushing) return;
|
|
3596
|
+
flushing = true;
|
|
3597
|
+
try {
|
|
3598
|
+
while (head < queue.length) {
|
|
3599
|
+
const entry = queue[head++];
|
|
3600
|
+
if (entry.cancelled) {
|
|
3601
|
+
entry.done = true;
|
|
3602
|
+
continue;
|
|
3603
|
+
}
|
|
3604
|
+
entry.run();
|
|
3605
|
+
entry.done = true;
|
|
3606
|
+
}
|
|
3607
|
+
} finally {
|
|
3608
|
+
queue = [];
|
|
3609
|
+
head = 0;
|
|
3610
|
+
flushing = false;
|
|
3611
|
+
}
|
|
3612
|
+
}
|
|
3613
|
+
/**
|
|
3614
|
+
* Dev-only: `batchInjection: true` batches nothing unless a window opens, and no
|
|
3615
|
+
* window has ever opened, so no provider is in the tree. Called by the injector
|
|
3616
|
+
* the first time it declines to batch.
|
|
3617
|
+
*/
|
|
3618
|
+
function warnBatchProviderMissing() {
|
|
3619
|
+
if (everOpened || warnedNoProvider || !isDevEnv()) return;
|
|
3620
|
+
warnedNoProvider = true;
|
|
3621
|
+
warn("[Tasty] batchInjection needs <TastyBatchProvider> mounted to batch.");
|
|
3622
|
+
}
|
|
3623
|
+
/**
|
|
3624
|
+
* Drop every pending write without applying it. Test helper — production code
|
|
3625
|
+
* should call `flushStyles()` instead.
|
|
3626
|
+
*/
|
|
3627
|
+
function resetStyleBatch() {
|
|
3628
|
+
queue = [];
|
|
3629
|
+
head = 0;
|
|
3630
|
+
microtaskScheduled = false;
|
|
3631
|
+
flushing = false;
|
|
3632
|
+
windowOpen = false;
|
|
3633
|
+
everOpened = false;
|
|
3634
|
+
warnedNoProvider = false;
|
|
3635
|
+
}
|
|
3636
|
+
//#endregion
|
|
3233
3637
|
//#region src/properties/property-type-resolver.ts
|
|
3234
3638
|
/**
|
|
3235
3639
|
* PropertyTypeResolver
|
|
@@ -3239,6 +3643,10 @@ function rscClassRegexGlobal(prefix) {
|
|
|
3239
3643
|
*/
|
|
3240
3644
|
const CUSTOM_PROP_DECL = /^\s*(--[a-z0-9_-]+)\s*:\s*(.+?)\s*$/i;
|
|
3241
3645
|
const SINGLE_VAR_REF = /^var\((--[a-z0-9_-]+)\)$/i;
|
|
3646
|
+
/** A `--name-color-{space}` companion holding a color's channel components. */
|
|
3647
|
+
const COLOR_COMPONENTS_DECL = /^\s*(--[a-z0-9_-]+-color-(?:rgb|hsl|oklch))\s*:\s*(.+?)\s*$/i;
|
|
3648
|
+
/** A component list the browser can type as `<number>+`. */
|
|
3649
|
+
const NUMERIC_COMPONENTS = /^[\d\s.+-]+$/;
|
|
3242
3650
|
var PropertyTypeResolver = class {
|
|
3243
3651
|
/** propName → the prop it depends on */
|
|
3244
3652
|
pendingDeps = /* @__PURE__ */ new Map();
|
|
@@ -3251,6 +3659,15 @@ var PropertyTypeResolver = class {
|
|
|
3251
3659
|
scanDeclarations(declarations, isPropertyDefined, registerProperty) {
|
|
3252
3660
|
if (!declarations.includes("--")) return;
|
|
3253
3661
|
const parts = declarations.split(/;+/);
|
|
3662
|
+
for (const part of parts) {
|
|
3663
|
+
const match = COLOR_COMPONENTS_DECL.exec(part);
|
|
3664
|
+
if (!match) continue;
|
|
3665
|
+
const propName = match[1];
|
|
3666
|
+
if (isPropertyDefined(propName)) continue;
|
|
3667
|
+
if (NUMERIC_COMPONENTS.test(match[2])) continue;
|
|
3668
|
+
if (isPropertyDefined(propName.slice(0, propName.lastIndexOf("-")))) continue;
|
|
3669
|
+
registerProperty(propName, "*", getDefaultComponents());
|
|
3670
|
+
}
|
|
3254
3671
|
for (const part of parts) {
|
|
3255
3672
|
if (!part.trim()) continue;
|
|
3256
3673
|
const match = CUSTOM_PROP_DECL.exec(part);
|
|
@@ -3553,27 +3970,28 @@ function borderStyle({ border }) {
|
|
|
3553
3970
|
}
|
|
3554
3971
|
borderStyle.__lookupStyles = ["border"];
|
|
3555
3972
|
//#endregion
|
|
3556
|
-
//#region src/styles/
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
const
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
return colorValue;
|
|
3973
|
+
//#region src/styles/color.ts
|
|
3974
|
+
function colorStyle({ color }) {
|
|
3975
|
+
if (!color) return null;
|
|
3976
|
+
if (color === true) color = "currentColor";
|
|
3977
|
+
color = parseColor(color, true).color || resolveCustomProperties(color);
|
|
3978
|
+
const match = color.match(/var\(--(.+?)-color/);
|
|
3979
|
+
let name = "";
|
|
3980
|
+
if (match) name = match[1];
|
|
3981
|
+
const styles = { color };
|
|
3982
|
+
if (name && name !== "current") {
|
|
3983
|
+
const suffix = getColorSpaceSuffix();
|
|
3984
|
+
Object.assign(styles, {
|
|
3985
|
+
"--current-color": color,
|
|
3986
|
+
[`--current-color-${suffix}`]: convertColorChainToComponentChain(color)
|
|
3987
|
+
});
|
|
3572
3988
|
}
|
|
3573
|
-
|
|
3574
|
-
if (!fallback) return `var(--${name}-color-${suffix})`;
|
|
3575
|
-
return `var(--${name}-color-${suffix}, ${convertColorChainToComponentChain(fallback.trim())})`;
|
|
3989
|
+
return styles;
|
|
3576
3990
|
}
|
|
3991
|
+
colorStyle.__lookupStyles = ["color"];
|
|
3992
|
+
//#endregion
|
|
3993
|
+
//#region src/styles/createStyle.ts
|
|
3994
|
+
const CACHE = {};
|
|
3577
3995
|
function createStyle(styleName, cssStyle, converter) {
|
|
3578
3996
|
const key = `${styleName}.${cssStyle ?? ""}`;
|
|
3579
3997
|
if (!CACHE[key]) {
|
|
@@ -3617,6 +4035,10 @@ function createStyle(styleName, cssStyle, converter) {
|
|
|
3617
4035
|
[finalCssStyle]: colorSpaceStr,
|
|
3618
4036
|
[`${finalCssStyle}-${suffix}`]: getColorSpaceComponents(colorSpaceStr)
|
|
3619
4037
|
};
|
|
4038
|
+
if (color && colorFuncName(color)) return {
|
|
4039
|
+
[finalCssStyle]: color,
|
|
4040
|
+
[`${finalCssStyle}-${suffix}`]: convertColorChainToComponentChain(color)
|
|
4041
|
+
};
|
|
3620
4042
|
return { [finalCssStyle]: color ?? "" };
|
|
3621
4043
|
}
|
|
3622
4044
|
const processed = parseStyle(styleValue);
|
|
@@ -3628,27 +4050,6 @@ function createStyle(styleName, cssStyle, converter) {
|
|
|
3628
4050
|
return CACHE[key];
|
|
3629
4051
|
}
|
|
3630
4052
|
//#endregion
|
|
3631
|
-
//#region src/styles/color.ts
|
|
3632
|
-
function colorStyle({ color }) {
|
|
3633
|
-
if (!color) return null;
|
|
3634
|
-
if (color === true) color = "currentColor";
|
|
3635
|
-
if (typeof color === "string" && (color.startsWith("#") || color.startsWith("(#"))) color = parseColor(color).color || color;
|
|
3636
|
-
else if (typeof color === "string") color = resolveCustomProperties(color);
|
|
3637
|
-
const match = color.match(/var\(--(.+?)-color/);
|
|
3638
|
-
let name = "";
|
|
3639
|
-
if (match) name = match[1];
|
|
3640
|
-
const styles = { color };
|
|
3641
|
-
if (name && name !== "current") {
|
|
3642
|
-
const suffix = getColorSpaceSuffix();
|
|
3643
|
-
Object.assign(styles, {
|
|
3644
|
-
"--current-color": color,
|
|
3645
|
-
[`--current-color-${suffix}`]: convertColorChainToComponentChain(color)
|
|
3646
|
-
});
|
|
3647
|
-
}
|
|
3648
|
-
return styles;
|
|
3649
|
-
}
|
|
3650
|
-
colorStyle.__lookupStyles = ["color"];
|
|
3651
|
-
//#endregion
|
|
3652
4053
|
//#region src/styles/display.ts
|
|
3653
4054
|
/**
|
|
3654
4055
|
* Process textOverflow into CSS properties for truncation/clamping.
|
|
@@ -4587,12 +4988,6 @@ scrollMarginStyle.__lookupStyles = [
|
|
|
4587
4988
|
"scrollMarginInline"
|
|
4588
4989
|
];
|
|
4589
4990
|
//#endregion
|
|
4590
|
-
//#region src/utils/warnings.ts
|
|
4591
|
-
const PREFIX = "[Tasty]";
|
|
4592
|
-
function warn(...args) {
|
|
4593
|
-
console.warn(PREFIX, ...args);
|
|
4594
|
-
}
|
|
4595
|
-
//#endregion
|
|
4596
4991
|
//#region src/styles/scrollbar.ts
|
|
4597
4992
|
/**
|
|
4598
4993
|
* Standard CSS scrollbar styling via `scrollbar-width`, `scrollbar-color`,
|
|
@@ -4638,10 +5033,10 @@ function scrollbarStyle({ scrollbar, overflow }) {
|
|
|
4638
5033
|
scrollbarStyle.__lookupStyles = ["scrollbar", "overflow"];
|
|
4639
5034
|
//#endregion
|
|
4640
5035
|
//#region src/styles/shadow.ts
|
|
4641
|
-
function toBoxShadow(
|
|
4642
|
-
const { values, mods, colors } =
|
|
5036
|
+
function toBoxShadow(group) {
|
|
5037
|
+
const { values, mods, colors } = group;
|
|
4643
5038
|
const mod = mods[0] || "";
|
|
4644
|
-
const shadowColor =
|
|
5039
|
+
const shadowColor = colors[0] ?? "";
|
|
4645
5040
|
return [
|
|
4646
5041
|
mod,
|
|
4647
5042
|
...values,
|
|
@@ -4652,7 +5047,8 @@ function shadowStyle({ shadow }) {
|
|
|
4652
5047
|
if (!shadow) return null;
|
|
4653
5048
|
if (shadow === true) shadow = "var(--shadow)";
|
|
4654
5049
|
if (CSS_WIDE_KEYWORDS.has(shadow)) return { "box-shadow": shadow };
|
|
4655
|
-
|
|
5050
|
+
const { groups } = parseStyle(shadow);
|
|
5051
|
+
return { "box-shadow": groups.map(toBoxShadow).join(",") };
|
|
4656
5052
|
}
|
|
4657
5053
|
shadowStyle.__lookupStyles = ["shadow"];
|
|
4658
5054
|
//#endregion
|
|
@@ -5264,6 +5660,8 @@ var SheetManager = class {
|
|
|
5264
5660
|
propertyTypeResolver: new PropertyTypeResolver(),
|
|
5265
5661
|
usageMap: /* @__PURE__ */ new Map(),
|
|
5266
5662
|
touchCount: 0,
|
|
5663
|
+
touchTick: -1,
|
|
5664
|
+
touchedTick: /* @__PURE__ */ new Set(),
|
|
5267
5665
|
serverClassSyncIndex: 0,
|
|
5268
5666
|
rscStylesScanned: false,
|
|
5269
5667
|
injectionMode: this.detectInjectionMode(root)
|
|
@@ -6174,6 +6572,16 @@ function formatCounterStyleRule(name, descriptors) {
|
|
|
6174
6572
|
//#endregion
|
|
6175
6573
|
//#region src/injector/injector.ts
|
|
6176
6574
|
/**
|
|
6575
|
+
* Placeholder `KeyframesInfo` for a cache entry reserved by a batched write and
|
|
6576
|
+
* not yet backed by a real rule. Only ever read through `entry.pending`, which
|
|
6577
|
+
* is checked before the info is used.
|
|
6578
|
+
*/
|
|
6579
|
+
const EMPTY_KEYFRAMES_INFO = {
|
|
6580
|
+
name: "",
|
|
6581
|
+
ruleIndex: -3,
|
|
6582
|
+
sheetIndex: -3
|
|
6583
|
+
};
|
|
6584
|
+
/**
|
|
6177
6585
|
* Extract class names from `<style data-tasty-rsc>` tags.
|
|
6178
6586
|
* The doubled-specificity pattern `.tXXX.tXXX` makes extraction reliable.
|
|
6179
6587
|
*/
|
|
@@ -6234,6 +6642,56 @@ var StyleInjector = class {
|
|
|
6234
6642
|
get _sheetManager() {
|
|
6235
6643
|
return this.sheetManager;
|
|
6236
6644
|
}
|
|
6645
|
+
/**
|
|
6646
|
+
* Whether sheet writes should be queued instead of applied immediately.
|
|
6647
|
+
*
|
|
6648
|
+
* Only ever true on the client: SSR collects CSS as text and the RSC path
|
|
6649
|
+
* returns it as strings, so neither has a live sheet to batch writes against.
|
|
6650
|
+
*
|
|
6651
|
+
* In the default `true` mode a write is only queued inside an open batch
|
|
6652
|
+
* window — a commit in which `<TastyBatchProvider>` rendered and will
|
|
6653
|
+
* therefore flush in its insertion effect, before any layout effect can
|
|
6654
|
+
* measure. Everything else falls through to a synchronous write, so enabling
|
|
6655
|
+
* the flag cannot make a `useLayoutEffect` read an unstyled element.
|
|
6656
|
+
* `'always'` drops the gate and accepts that trade for wider coverage.
|
|
6657
|
+
*/
|
|
6658
|
+
get batching() {
|
|
6659
|
+
const mode = this.config.batchInjection;
|
|
6660
|
+
if (!mode || typeof document === "undefined") return false;
|
|
6661
|
+
if (mode === "always") return true;
|
|
6662
|
+
if (isBatchWindowOpen()) return true;
|
|
6663
|
+
warnBatchProviderMissing();
|
|
6664
|
+
return false;
|
|
6665
|
+
}
|
|
6666
|
+
/**
|
|
6667
|
+
* Apply a sheet write now, or queue it when batching is on.
|
|
6668
|
+
* Returns the queue handle when deferred, so the caller can cancel it if it
|
|
6669
|
+
* disposes before the flush.
|
|
6670
|
+
*/
|
|
6671
|
+
writeSheet(task) {
|
|
6672
|
+
if (this.batching) return enqueueStyleWrite(task);
|
|
6673
|
+
task();
|
|
6674
|
+
return null;
|
|
6675
|
+
}
|
|
6676
|
+
/**
|
|
6677
|
+
* Insert a global (non-class) rule, honouring batching.
|
|
6678
|
+
*
|
|
6679
|
+
* `onApplied` records the caller's dedupe bookkeeping. When the write is
|
|
6680
|
+
* queued it runs eagerly, so a repeat call before the flush bails out instead
|
|
6681
|
+
* of queueing the same rule twice — the same reasoning as
|
|
6682
|
+
* `insertPropertyRule`'s eager marking. When written synchronously it runs
|
|
6683
|
+
* only on success, preserving the existing retry-on-failure behaviour.
|
|
6684
|
+
*/
|
|
6685
|
+
writeGlobalRule(registry, rules, key, root, onApplied) {
|
|
6686
|
+
if (this.batching) {
|
|
6687
|
+
onApplied?.();
|
|
6688
|
+
enqueueStyleWrite(() => {
|
|
6689
|
+
this.sheetManager.insertGlobalRule(registry, rules, key, root);
|
|
6690
|
+
});
|
|
6691
|
+
return;
|
|
6692
|
+
}
|
|
6693
|
+
if (this.sheetManager.insertGlobalRule(registry, rules, key, root)) onApplied?.();
|
|
6694
|
+
}
|
|
6237
6695
|
constructor(config = {}) {
|
|
6238
6696
|
if (config.namePrefix !== void 0) validateNamePrefix(config.namePrefix);
|
|
6239
6697
|
this.config = config;
|
|
@@ -6311,12 +6769,18 @@ var StyleInjector = class {
|
|
|
6311
6769
|
};
|
|
6312
6770
|
const cacheKey = options?.cacheKey;
|
|
6313
6771
|
let className;
|
|
6314
|
-
let isPreAllocated = false;
|
|
6315
6772
|
if (cacheKey && registry.cacheKeyToClassName.has(cacheKey)) {
|
|
6316
6773
|
className = registry.cacheKeyToClassName.get(cacheKey);
|
|
6317
6774
|
const existingRuleInfo = registry.rules.get(className);
|
|
6318
|
-
|
|
6319
|
-
|
|
6775
|
+
if (existingRuleInfo.ruleIndex === -3) {
|
|
6776
|
+
registry.refCounts.set(className, (registry.refCounts.get(className) || 0) + 1);
|
|
6777
|
+
if (registry.metrics) registry.metrics.hits++;
|
|
6778
|
+
return {
|
|
6779
|
+
className,
|
|
6780
|
+
dispose: () => this.dispose(className, registry)
|
|
6781
|
+
};
|
|
6782
|
+
}
|
|
6783
|
+
if (!(existingRuleInfo.ruleIndex === -1 && existingRuleInfo.sheetIndex === -1)) {
|
|
6320
6784
|
const currentRefCount = registry.refCounts.get(className) || 0;
|
|
6321
6785
|
registry.refCounts.set(className, currentRefCount + 1);
|
|
6322
6786
|
if (registry.metrics) registry.metrics.hits++;
|
|
@@ -6357,39 +6821,70 @@ var StyleInjector = class {
|
|
|
6357
6821
|
rootPrefix: void 0
|
|
6358
6822
|
};
|
|
6359
6823
|
});
|
|
6360
|
-
|
|
6361
|
-
|
|
6362
|
-
|
|
6363
|
-
|
|
6364
|
-
|
|
6365
|
-
|
|
6366
|
-
|
|
6367
|
-
|
|
6368
|
-
|
|
6369
|
-
|
|
6370
|
-
|
|
6824
|
+
const applySheetWrite = () => {
|
|
6825
|
+
if (this.config.autoPropertyTypes !== false) {
|
|
6826
|
+
const resolver = registry.propertyTypeResolver;
|
|
6827
|
+
const defined = registry.injectedProperties;
|
|
6828
|
+
for (const rule of rulesToInsert) {
|
|
6829
|
+
if (!rule.declarations) continue;
|
|
6830
|
+
resolver.scanDeclarations(rule.declarations, (name) => defined.has(name), (name, syntax, initialValue) => {
|
|
6831
|
+
this.property(name, {
|
|
6832
|
+
syntax,
|
|
6833
|
+
inherits: true,
|
|
6834
|
+
initialValue,
|
|
6835
|
+
root
|
|
6836
|
+
});
|
|
6371
6837
|
});
|
|
6372
|
-
}
|
|
6838
|
+
}
|
|
6373
6839
|
}
|
|
6374
|
-
|
|
6375
|
-
|
|
6376
|
-
|
|
6377
|
-
|
|
6840
|
+
const ruleInfo = this.sheetManager.insertRule(registry, rulesToInsert, className, root);
|
|
6841
|
+
if (!ruleInfo) {
|
|
6842
|
+
if (registry.metrics) registry.metrics.misses++;
|
|
6843
|
+
return null;
|
|
6844
|
+
}
|
|
6845
|
+
registry.rules.set(className, ruleInfo);
|
|
6846
|
+
if (cacheKey) registry.cacheKeyToClassName.set(cacheKey, className);
|
|
6847
|
+
if (registry.metrics) {
|
|
6848
|
+
registry.metrics.totalInsertions++;
|
|
6849
|
+
registry.metrics.misses++;
|
|
6850
|
+
}
|
|
6851
|
+
return ruleInfo;
|
|
6852
|
+
};
|
|
6853
|
+
if (this.batching) {
|
|
6854
|
+
registry.rules.set(className, {
|
|
6855
|
+
className,
|
|
6856
|
+
ruleIndex: -3,
|
|
6857
|
+
sheetIndex: -3
|
|
6858
|
+
});
|
|
6859
|
+
if (cacheKey) registry.cacheKeyToClassName.set(cacheKey, className);
|
|
6860
|
+
registry.refCounts.set(className, 1);
|
|
6861
|
+
const queued = enqueueStyleWrite(() => {
|
|
6862
|
+
if (applySheetWrite()) return;
|
|
6863
|
+
registry.rules.set(className, {
|
|
6864
|
+
className,
|
|
6865
|
+
ruleIndex: -1,
|
|
6866
|
+
sheetIndex: -1
|
|
6867
|
+
});
|
|
6868
|
+
});
|
|
6378
6869
|
return {
|
|
6379
6870
|
className,
|
|
6380
|
-
dispose: () => {
|
|
6871
|
+
dispose: () => {
|
|
6872
|
+
if (!queued.done && registry.refCounts.get(className) === 1 && registry.rules.get(className)?.ruleIndex === -3) {
|
|
6873
|
+
queued.cancelled = true;
|
|
6874
|
+
registry.rules.delete(className);
|
|
6875
|
+
if (cacheKey) registry.cacheKeyToClassName.delete(cacheKey);
|
|
6876
|
+
registry.refCounts.set(className, 0);
|
|
6877
|
+
return;
|
|
6878
|
+
}
|
|
6879
|
+
this.dispose(className, registry);
|
|
6880
|
+
}
|
|
6381
6881
|
};
|
|
6382
6882
|
}
|
|
6883
|
+
if (!applySheetWrite()) return {
|
|
6884
|
+
className,
|
|
6885
|
+
dispose: () => {}
|
|
6886
|
+
};
|
|
6383
6887
|
registry.refCounts.set(className, 1);
|
|
6384
|
-
if (isPreAllocated) registry.rules.set(className, ruleInfo);
|
|
6385
|
-
else {
|
|
6386
|
-
registry.rules.set(className, ruleInfo);
|
|
6387
|
-
if (cacheKey) registry.cacheKeyToClassName.set(cacheKey, className);
|
|
6388
|
-
}
|
|
6389
|
-
if (registry.metrics) {
|
|
6390
|
-
registry.metrics.totalInsertions++;
|
|
6391
|
-
registry.metrics.misses++;
|
|
6392
|
-
}
|
|
6393
6888
|
return {
|
|
6394
6889
|
className,
|
|
6395
6890
|
dispose: () => this.dispose(className, registry)
|
|
@@ -6404,24 +6899,41 @@ var StyleInjector = class {
|
|
|
6404
6899
|
const root = options?.root || document;
|
|
6405
6900
|
const registry = this.sheetManager.getRegistry(root);
|
|
6406
6901
|
if (!rules || rules.length === 0) return { dispose: () => {} };
|
|
6407
|
-
|
|
6408
|
-
|
|
6409
|
-
|
|
6410
|
-
|
|
6411
|
-
|
|
6412
|
-
|
|
6413
|
-
|
|
6414
|
-
|
|
6415
|
-
|
|
6416
|
-
|
|
6417
|
-
|
|
6902
|
+
const key = `global:${this.globalRuleCounter++}`;
|
|
6903
|
+
const applyWrite = () => {
|
|
6904
|
+
if (this.config.autoPropertyTypes !== false) {
|
|
6905
|
+
const resolver = registry.propertyTypeResolver;
|
|
6906
|
+
const defined = registry.injectedProperties;
|
|
6907
|
+
for (const rule of rules) {
|
|
6908
|
+
if (!rule.declarations) continue;
|
|
6909
|
+
resolver.scanDeclarations(rule.declarations, (name) => defined.has(name), (name, syntax, initialValue) => {
|
|
6910
|
+
this.property(name, {
|
|
6911
|
+
syntax,
|
|
6912
|
+
inherits: true,
|
|
6913
|
+
initialValue,
|
|
6914
|
+
root
|
|
6915
|
+
});
|
|
6418
6916
|
});
|
|
6419
|
-
}
|
|
6917
|
+
}
|
|
6420
6918
|
}
|
|
6919
|
+
const inserted = this.sheetManager.insertGlobalRule(registry, rules, key, root);
|
|
6920
|
+
if (registry.metrics) registry.metrics.totalInsertions++;
|
|
6921
|
+
return inserted;
|
|
6922
|
+
};
|
|
6923
|
+
if (this.batching) {
|
|
6924
|
+
let info = null;
|
|
6925
|
+
const queued = enqueueStyleWrite(() => {
|
|
6926
|
+
info = applyWrite();
|
|
6927
|
+
});
|
|
6928
|
+
return { dispose: () => {
|
|
6929
|
+
if (!queued.done) {
|
|
6930
|
+
queued.cancelled = true;
|
|
6931
|
+
return;
|
|
6932
|
+
}
|
|
6933
|
+
if (info) this.sheetManager.deleteGlobalRule(registry, key);
|
|
6934
|
+
} };
|
|
6421
6935
|
}
|
|
6422
|
-
const
|
|
6423
|
-
const info = this.sheetManager.insertGlobalRule(registry, rules, key, root);
|
|
6424
|
-
if (registry.metrics) registry.metrics.totalInsertions++;
|
|
6936
|
+
const info = applyWrite();
|
|
6425
6937
|
return { dispose: () => {
|
|
6426
6938
|
if (info) this.sheetManager.deleteGlobalRule(registry, key);
|
|
6427
6939
|
} };
|
|
@@ -6433,12 +6945,24 @@ var StyleInjector = class {
|
|
|
6433
6945
|
*/
|
|
6434
6946
|
injectRawCSS(css, options) {
|
|
6435
6947
|
const root = options?.root || document;
|
|
6436
|
-
return this.sheetManager.injectRawCSS(css, root);
|
|
6948
|
+
if (!this.batching) return this.sheetManager.injectRawCSS(css, root);
|
|
6949
|
+
let result = null;
|
|
6950
|
+
const queued = enqueueStyleWrite(() => {
|
|
6951
|
+
result = this.sheetManager.injectRawCSS(css, root);
|
|
6952
|
+
});
|
|
6953
|
+
return { dispose: () => {
|
|
6954
|
+
if (!queued.done) {
|
|
6955
|
+
queued.cancelled = true;
|
|
6956
|
+
return;
|
|
6957
|
+
}
|
|
6958
|
+
result?.dispose();
|
|
6959
|
+
} };
|
|
6437
6960
|
}
|
|
6438
6961
|
/**
|
|
6439
6962
|
* Get raw CSS text for SSR
|
|
6440
6963
|
*/
|
|
6441
6964
|
getRawCSSText(options) {
|
|
6965
|
+
flushStyles();
|
|
6442
6966
|
const root = options?.root || document;
|
|
6443
6967
|
return this.sheetManager.getRawCSSText(root);
|
|
6444
6968
|
}
|
|
@@ -6475,6 +6999,7 @@ var StyleInjector = class {
|
|
|
6475
6999
|
* Force bulk cleanup of unused styles
|
|
6476
7000
|
*/
|
|
6477
7001
|
cleanup(root) {
|
|
7002
|
+
flushStyles();
|
|
6478
7003
|
const registry = this.sheetManager.getRegistry(root || document);
|
|
6479
7004
|
this.sheetManager.forceCleanup(registry);
|
|
6480
7005
|
}
|
|
@@ -6482,6 +7007,7 @@ var StyleInjector = class {
|
|
|
6482
7007
|
* Get CSS text from all sheets (for SSR)
|
|
6483
7008
|
*/
|
|
6484
7009
|
getCSSText(options) {
|
|
7010
|
+
flushStyles();
|
|
6485
7011
|
const root = options?.root || document;
|
|
6486
7012
|
const registry = this.sheetManager.getRegistry(root);
|
|
6487
7013
|
return this.sheetManager.getCSSText(registry);
|
|
@@ -6490,6 +7016,7 @@ var StyleInjector = class {
|
|
|
6490
7016
|
* Get CSS only for the provided tasty classNames (e.g., ["t0","t3"])
|
|
6491
7017
|
*/
|
|
6492
7018
|
getCSSTextForClasses(classNames, options) {
|
|
7019
|
+
flushStyles();
|
|
6493
7020
|
const root = options?.root || document;
|
|
6494
7021
|
const registry = this.sheetManager.getRegistry(root);
|
|
6495
7022
|
const cssChunks = [];
|
|
@@ -6514,6 +7041,7 @@ var StyleInjector = class {
|
|
|
6514
7041
|
* Get cache performance metrics
|
|
6515
7042
|
*/
|
|
6516
7043
|
getMetrics(options) {
|
|
7044
|
+
flushStyles();
|
|
6517
7045
|
const root = options?.root || document;
|
|
6518
7046
|
const registry = this.sheetManager.getRegistry(root);
|
|
6519
7047
|
return this.sheetManager.getMetrics(registry);
|
|
@@ -6593,7 +7121,7 @@ var StyleInjector = class {
|
|
|
6593
7121
|
declarations
|
|
6594
7122
|
};
|
|
6595
7123
|
registry.injectedProperties.set(cssName, normalizePropertyDefinition(definition));
|
|
6596
|
-
this.
|
|
7124
|
+
this.writeGlobalRule(registry, [rule], `property:${cacheKey}`, root);
|
|
6597
7125
|
}
|
|
6598
7126
|
/**
|
|
6599
7127
|
* Check whether a given @property name was already injected by this injector.
|
|
@@ -6604,6 +7132,7 @@ var StyleInjector = class {
|
|
|
6604
7132
|
* - `--name` → checks `--name` (legacy format)
|
|
6605
7133
|
*/
|
|
6606
7134
|
isPropertyDefined(name, options) {
|
|
7135
|
+
flushStyles();
|
|
6607
7136
|
const root = options?.root || document;
|
|
6608
7137
|
const registry = this.sheetManager.getRegistry(root);
|
|
6609
7138
|
const effectiveResult = getEffectiveDefinition(name, {});
|
|
@@ -6625,7 +7154,9 @@ var StyleInjector = class {
|
|
|
6625
7154
|
selector: "@font-face",
|
|
6626
7155
|
declarations: formatFontFaceDeclarations(family, descriptors)
|
|
6627
7156
|
};
|
|
6628
|
-
|
|
7157
|
+
this.writeGlobalRule(registry, [rule], `fontface:${hash}`, root, () => {
|
|
7158
|
+
registry.injectedFontFaces.add(hash);
|
|
7159
|
+
});
|
|
6629
7160
|
}
|
|
6630
7161
|
/**
|
|
6631
7162
|
* Inject a CSS @counter-style rule.
|
|
@@ -6643,13 +7174,17 @@ var StyleInjector = class {
|
|
|
6643
7174
|
const existingIsStrong = registry.injectedCounterStyles.get(name);
|
|
6644
7175
|
if (existingIsStrong !== void 0) {
|
|
6645
7176
|
if (isWeak || existingIsStrong === true) return;
|
|
6646
|
-
this.
|
|
7177
|
+
this.writeSheet(() => {
|
|
7178
|
+
this.sheetManager.deleteGlobalRule(registry, `counterstyle:${name}`);
|
|
7179
|
+
});
|
|
6647
7180
|
}
|
|
6648
7181
|
const rule = {
|
|
6649
7182
|
selector: `@counter-style ${name}`,
|
|
6650
7183
|
declarations: formatCounterStyleDeclarations(descriptors)
|
|
6651
7184
|
};
|
|
6652
|
-
|
|
7185
|
+
this.writeGlobalRule(registry, [rule], `counterstyle:${name}`, root, () => {
|
|
7186
|
+
registry.injectedCounterStyles.set(name, !isWeak);
|
|
7187
|
+
});
|
|
6653
7188
|
}
|
|
6654
7189
|
/**
|
|
6655
7190
|
* Inject a CSS @function rule (custom function).
|
|
@@ -6668,13 +7203,17 @@ var StyleInjector = class {
|
|
|
6668
7203
|
const existingIsStrong = registry.injectedFunctions.get(cssName);
|
|
6669
7204
|
if (existingIsStrong !== void 0) {
|
|
6670
7205
|
if (isWeak || existingIsStrong === true) return;
|
|
6671
|
-
this.
|
|
7206
|
+
this.writeSheet(() => {
|
|
7207
|
+
this.sheetManager.deleteGlobalRule(registry, `function:${cssName}`);
|
|
7208
|
+
});
|
|
6672
7209
|
}
|
|
6673
7210
|
const rule = {
|
|
6674
7211
|
selector: formatFunctionPrelude(name, definition.args, definition.returns),
|
|
6675
7212
|
declarations: formatFunctionDeclarations(definition)
|
|
6676
7213
|
};
|
|
6677
|
-
|
|
7214
|
+
this.writeGlobalRule(registry, [rule], `function:${cssName}`, root, () => {
|
|
7215
|
+
registry.injectedFunctions.set(cssName, !isWeak);
|
|
7216
|
+
});
|
|
6678
7217
|
}
|
|
6679
7218
|
/**
|
|
6680
7219
|
* Inject keyframes and return object with toString() and dispose()
|
|
@@ -6712,29 +7251,53 @@ var StyleInjector = class {
|
|
|
6712
7251
|
registry.keyframesNameToContent.set(providedName, contentHash);
|
|
6713
7252
|
}
|
|
6714
7253
|
} else actualName = makeKeyframeName(this.namePrefix, String(registry.keyframesCounter++));
|
|
6715
|
-
const
|
|
6716
|
-
|
|
6717
|
-
|
|
6718
|
-
|
|
7254
|
+
const applyWrite = () => {
|
|
7255
|
+
const result = this.sheetManager.insertKeyframes(registry, steps, actualName, root);
|
|
7256
|
+
if (!result) return false;
|
|
7257
|
+
const { info, declarations } = result;
|
|
7258
|
+
if (this.config.autoPropertyTypes !== false && declarations) registry.propertyTypeResolver.scanDeclarations(declarations, (name) => registry.injectedProperties.has(name), (name, syntax, initialValue) => {
|
|
7259
|
+
this.property(name, {
|
|
7260
|
+
syntax,
|
|
7261
|
+
inherits: true,
|
|
7262
|
+
initialValue,
|
|
7263
|
+
root
|
|
7264
|
+
});
|
|
7265
|
+
});
|
|
7266
|
+
const entry = registry.keyframesCache.get(contentHash);
|
|
7267
|
+
if (entry) {
|
|
7268
|
+
entry.info = info;
|
|
7269
|
+
entry.pending = void 0;
|
|
7270
|
+
} else registry.keyframesCache.set(contentHash, {
|
|
7271
|
+
name: actualName,
|
|
7272
|
+
refCount: 1,
|
|
7273
|
+
info
|
|
7274
|
+
});
|
|
7275
|
+
if (registry.metrics) {
|
|
7276
|
+
registry.metrics.totalInsertions++;
|
|
7277
|
+
registry.metrics.misses++;
|
|
7278
|
+
}
|
|
7279
|
+
return true;
|
|
6719
7280
|
};
|
|
6720
|
-
|
|
6721
|
-
|
|
6722
|
-
|
|
6723
|
-
|
|
6724
|
-
|
|
6725
|
-
|
|
6726
|
-
|
|
7281
|
+
if (this.batching) {
|
|
7282
|
+
const entry = {
|
|
7283
|
+
name: actualName,
|
|
7284
|
+
refCount: 1,
|
|
7285
|
+
info: EMPTY_KEYFRAMES_INFO
|
|
7286
|
+
};
|
|
7287
|
+
registry.keyframesCache.set(contentHash, entry);
|
|
7288
|
+
entry.pending = enqueueStyleWrite(() => {
|
|
7289
|
+
if (applyWrite()) return;
|
|
7290
|
+
if (registry.keyframesCache.get(contentHash) === entry) registry.keyframesCache.delete(contentHash);
|
|
6727
7291
|
});
|
|
6728
|
-
|
|
6729
|
-
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
info
|
|
6733
|
-
});
|
|
6734
|
-
if (registry.metrics) {
|
|
6735
|
-
registry.metrics.totalInsertions++;
|
|
6736
|
-
registry.metrics.misses++;
|
|
7292
|
+
return {
|
|
7293
|
+
toString: () => actualName,
|
|
7294
|
+
dispose: () => this.disposeKeyframes(contentHash, registry)
|
|
7295
|
+
};
|
|
6737
7296
|
}
|
|
7297
|
+
if (!applyWrite()) return {
|
|
7298
|
+
toString: () => "",
|
|
7299
|
+
dispose: () => {}
|
|
7300
|
+
};
|
|
6738
7301
|
return {
|
|
6739
7302
|
toString: () => actualName,
|
|
6740
7303
|
dispose: () => this.disposeKeyframes(contentHash, registry)
|
|
@@ -6748,7 +7311,8 @@ var StyleInjector = class {
|
|
|
6748
7311
|
if (!entry) return;
|
|
6749
7312
|
entry.refCount--;
|
|
6750
7313
|
if (entry.refCount <= 0) {
|
|
6751
|
-
|
|
7314
|
+
if (entry.pending && !entry.pending.done) entry.pending.cancelled = true;
|
|
7315
|
+
else this.sheetManager.deleteKeyframes(registry, entry.info);
|
|
6752
7316
|
registry.keyframesCache.delete(contentHash);
|
|
6753
7317
|
for (const [name, hash] of registry.keyframesNameToContent.entries()) if (hash === contentHash) {
|
|
6754
7318
|
registry.keyframesNameToContent.delete(name);
|
|
@@ -6773,6 +7337,11 @@ var StyleInjector = class {
|
|
|
6773
7337
|
const root = options?.root || document;
|
|
6774
7338
|
const registry = this.sheetManager.getRegistry(root);
|
|
6775
7339
|
const now = Date.now();
|
|
7340
|
+
if (registry.touchTick !== now) {
|
|
7341
|
+
registry.touchTick = now;
|
|
7342
|
+
registry.touchedTick.clear();
|
|
7343
|
+
} else if (registry.touchedTick.has(className)) return;
|
|
7344
|
+
registry.touchedTick.add(className);
|
|
6776
7345
|
const parts = className.indexOf(" ") === -1 ? [className] : className.split(" ");
|
|
6777
7346
|
for (const cls of parts) {
|
|
6778
7347
|
if (!this.classRegex.test(cls)) continue;
|
|
@@ -6813,6 +7382,7 @@ var StyleInjector = class {
|
|
|
6813
7382
|
* @returns Number of styles evicted.
|
|
6814
7383
|
*/
|
|
6815
7384
|
gc(options) {
|
|
7385
|
+
flushStyles();
|
|
6816
7386
|
if (typeof document === "undefined") return 0;
|
|
6817
7387
|
if (this.pendingGCHandle != null) {
|
|
6818
7388
|
if (typeof cancelIdleCallback !== "undefined") cancelIdleCallback(this.pendingGCHandle);
|
|
@@ -6862,6 +7432,7 @@ var StyleInjector = class {
|
|
|
6862
7432
|
* Destroy all resources for a root
|
|
6863
7433
|
*/
|
|
6864
7434
|
destroy(root) {
|
|
7435
|
+
flushStyles();
|
|
6865
7436
|
const targetRoot = root || document;
|
|
6866
7437
|
this.sheetManager.cleanup(targetRoot);
|
|
6867
7438
|
if (this.pendingGCHandle != null && !this.sheetManager.hasActiveRoots()) {
|
|
@@ -12310,6 +12881,6 @@ function resetConfig() {
|
|
|
12310
12881
|
delete storage[GLOBAL_INJECTOR_KEY];
|
|
12311
12882
|
}
|
|
12312
12883
|
//#endregion
|
|
12313
|
-
export { SheetManager as $,
|
|
12884
|
+
export { SheetManager as $, colorInitialValueToComponents as $t, FLOW_STYLES as A, STYLE_TO_CHUNK as At, createStateParserContext as B, getGlobalParser as Bt, BASE_STYLES as C, APPEARANCE_CHUNK_STYLES as Ct, COLOR_STYLES as D, FONT_CHUNK_STYLES as Dt, BLOCK_STYLES as E, DISPLAY_CHUNK_STYLES as Et, hasPipelineCacheEntry as F, registerFunctionPolyfill as Ft, StyleInjector as G, stringifyStyles as Gt, extractPredefinedStateRefs as H, normalizeColorTokenValue as Ht, isSelector as I, registerLocalFunctionPolyfills as It, hasLocalCounterStyle as J, okhslFunction as Jt, extractLocalCounterStyle as K, okhstFunction as Kt, renderStyles as L, CUSTOM_UNITS as Lt, OUTER_STYLES as M, formatFunctionRule as Mt, POSITION_STYLES as N, hasLocalFunctions as Nt, CONTAINER_STYLES as O, LAYOUT_CHUNK_STYLES as Ot, TEXT_STYLES as P, parseFunctionName as Pt, hasLocalFontFace as Q, StyleParser as Qt, parseStateKey as R, DIRECTIONS as Rt, baseStylePropsRegistry as S, propHandlerRegistry as St, BLOCK_OUTER_STYLES as T, DIMENSION_CHUNK_STYLES as Tt, getGlobalPredefinedStates as U, parseColor as Ut, extractLocalPredefinedStates as V, getGlobalPredefinedTokens as Vt, setGlobalPredefinedStates as W, parseStyle as Wt, fontFaceContentHash as X, createColorFunc as Xt, extractLocalFontFace as Y, okhslPlugin as Yt, formatFontFaceRule as Z, isDevEnv as Zt, isFunctionsPolyfillEnabled as _, hashString as _t, getGlobalCounterStyles as a, resolveFunctionColor as an, closeBatchWindow as at, resetConfig as b, hasLocalProperties as bt, getGlobalInjector as c, getRgbValuesFromRgbaString as cn, openBatchWindow as ct, getGlobalStyles as d, strToRgb as dn, DEFAULT_ZERO_NAME_PREFIX as dt, convertColorChainToComponentChain as en, STYLE_HANDLER_MAP as et, getNamePrefix as f, normalizeDslName as fn, makeClassName as ft, isConfigLocked as g, validateNamePrefix as gt, hasStylesGenerated as h, tastyClassRegex as ht, getGlobalConfigTokens as i, overrideColorAlpha as in, PropertyTypeResolver as it, INNER_STYLES as j, extractLocalFunctions as jt, DIMENSION_STYLES as k, POSITION_CHUNK_STYLES as kt, getGlobalKeyframes as l, hexToRgb as ln, resetStyleBatch as lt, hasGlobalRecipes as m, makeKeyframeName as mt, getConfig as n, getColorSpaceSuffix as nn, styleHandlers as nt, getGlobalFontFaces as o, Lru as on, flushStyles as ot, hasGlobalKeyframes as p, makeCounterStyleName as pt, formatCounterStyleRule as q, okhstPlugin as qt, getEffectiveProperties as r, getComponentPropertySyntax as rn, createStyle as rt, getGlobalFunctions as s, getNamedColorHex as sn, hasPendingStyleWrites as st, configure as t, getColorSpaceComponents as tn, defineHandler as tt, getGlobalRecipes as u, hslToRgbValues as un, DEFAULT_NAME_PREFIX as ut, isTestEnvironment as v, extractLocalProperties as vt, BLOCK_INNER_STYLES as w, CHUNK_NAMES as wt, generateTypographyTokens as x, parsePropertyToken as xt, markStylesGenerated as y, getEffectiveDefinition as yt, camelToKebab as z, filterMods as zt };
|
|
12314
12885
|
|
|
12315
|
-
//# sourceMappingURL=config-
|
|
12886
|
+
//# sourceMappingURL=config-BrDt11hO.js.map
|