@kbach/react 0.6.7 → 0.6.9
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/dist/{chunk-5YN6PIXQ.mjs → chunk-35CBHSAG.mjs} +1 -1
- package/dist/{chunk-LXOMVLQI.mjs → chunk-XJFOY7HN.mjs} +38 -5
- package/dist/index.js +38 -5
- package/dist/index.mjs +1 -1
- package/dist/jsx-dev-runtime.js +38 -5
- package/dist/jsx-dev-runtime.mjs +2 -2
- package/dist/jsx-runtime.js +38 -5
- package/dist/jsx-runtime.mjs +2 -2
- package/dist/vite-plugin.js +60 -14
- package/dist/vite-plugin.mjs +60 -14
- package/package.json +1 -1
|
@@ -791,6 +791,13 @@ function hexToRgba(hex, alpha) {
|
|
|
791
791
|
const [r, g, b] = rgb;
|
|
792
792
|
return `rgba(${r},${g},${b},${alpha})`;
|
|
793
793
|
}
|
|
794
|
+
function withOpacityVar(color, varName) {
|
|
795
|
+
if (!color.startsWith("#")) return color;
|
|
796
|
+
const rgb = parseHexRgb(color);
|
|
797
|
+
if (!rgb) return color;
|
|
798
|
+
const [r, g, b] = rgb;
|
|
799
|
+
return `rgba(${r},${g},${b},var(${varName},1))`;
|
|
800
|
+
}
|
|
794
801
|
function resolveSpacing(value, negative, spacing, isArbitrary) {
|
|
795
802
|
if (isArbitrary) {
|
|
796
803
|
const resolved = getEffectiveIsWeb() ? value : toNativeValue(value);
|
|
@@ -1237,7 +1244,8 @@ var RESOLVERS = {
|
|
|
1237
1244
|
// ── Background ─────────────────────────────────────────────────────────────
|
|
1238
1245
|
bg: ({ value, isArbitrary }, { colors }) => {
|
|
1239
1246
|
const color = resolveColor(value, colors, isArbitrary);
|
|
1240
|
-
|
|
1247
|
+
if (!color) return null;
|
|
1248
|
+
return { backgroundColor: getEffectiveIsWeb() ? withOpacityVar(color, "--bg-opacity") : color };
|
|
1241
1249
|
},
|
|
1242
1250
|
"bg-opacity": ({ value, isArbitrary }, _) => {
|
|
1243
1251
|
if (!getEffectiveIsWeb()) return null;
|
|
@@ -1254,10 +1262,11 @@ var RESOLVERS = {
|
|
|
1254
1262
|
if (/^\d/.test(value) || /^(calc|min|max|clamp)/.test(value)) {
|
|
1255
1263
|
return { fontSize: getEffectiveIsWeb() ? value : toNativeValue(value) };
|
|
1256
1264
|
}
|
|
1257
|
-
return { color: value };
|
|
1265
|
+
return { color: getEffectiveIsWeb() ? withOpacityVar(value, "--text-opacity") : value };
|
|
1258
1266
|
}
|
|
1259
1267
|
const color = resolveColor(value, colors, false);
|
|
1260
|
-
|
|
1268
|
+
if (!color) return null;
|
|
1269
|
+
return { color: getEffectiveIsWeb() ? withOpacityVar(color, "--text-opacity") : color };
|
|
1261
1270
|
},
|
|
1262
1271
|
"text-opacity": ({ value, isArbitrary }, _) => {
|
|
1263
1272
|
if (!getEffectiveIsWeb()) return null;
|
|
@@ -2550,8 +2559,31 @@ var BASE_RESET = [
|
|
|
2550
2559
|
"a { color: inherit; text-decoration: none; }",
|
|
2551
2560
|
"ul, ol { margin: 0; padding: 0; list-style: none; }",
|
|
2552
2561
|
"img, video, svg { display: block; max-width: 100%; }",
|
|
2553
|
-
|
|
2562
|
+
// appearance: none is deliberately NOT applied to checkbox/radio/select below —
|
|
2563
|
+
// stripping it hides their native checkmark/arrow with nothing rendered in its
|
|
2564
|
+
// place, leaving an invisible checkbox or an arrow-less <select> that looks like
|
|
2565
|
+
// plain text. Text-like inputs, textarea, and button don't have that problem
|
|
2566
|
+
// (their native chrome is just a skin around content utilities can fully
|
|
2567
|
+
// restyle), so they keep the blank-canvas treatment.
|
|
2568
|
+
"input:not([type='checkbox']):not([type='radio']), textarea { appearance: none; -webkit-appearance: none; background: transparent; padding: 0; margin: 0; font: inherit; color: inherit; line-height: inherit; }",
|
|
2569
|
+
// Native checkbox/radio/range still get typography + spacing normalized, and
|
|
2570
|
+
// accent-color re-themes their native indicator to the current text color
|
|
2571
|
+
// instead of the browser/OS default blue, so they stay on-brand without
|
|
2572
|
+
// needing to be rebuilt from scratch.
|
|
2573
|
+
"input[type='checkbox'], input[type='radio'], input[type='range'] { margin: 0; font: inherit; accent-color: currentColor; }",
|
|
2574
|
+
// select keeps its native chrome (border/background/arrow are all part of the
|
|
2575
|
+
// same OS-drawn widget that appearance: none would blank out) — only typography
|
|
2576
|
+
// and spacing are normalized so it still matches surrounding text.
|
|
2577
|
+
"select { margin: 0; font: inherit; color: inherit; line-height: inherit; }",
|
|
2554
2578
|
"button { appearance: none; -webkit-appearance: none; background: transparent; padding: 0; margin: 0; font: inherit; color: inherit; cursor: pointer; line-height: inherit; text-align: inherit; }",
|
|
2579
|
+
"button, [role='button'] { cursor: pointer; }",
|
|
2580
|
+
":disabled { cursor: default; }",
|
|
2581
|
+
"textarea { resize: vertical; }",
|
|
2582
|
+
// Firefox renders placeholders at ~54% opacity by default; every other browser uses 1 —
|
|
2583
|
+
// normalize to 1 so placeholder color is consistent and fully controlled by the placeholder: modifier.
|
|
2584
|
+
"::placeholder { opacity: 1; }",
|
|
2585
|
+
"input[type='number']::-webkit-inner-spin-button, input[type='number']::-webkit-outer-spin-button { margin: 0; }",
|
|
2586
|
+
"input[type='search']::-webkit-search-decoration, input[type='search']::-webkit-search-cancel-button { -webkit-appearance: none; }",
|
|
2555
2587
|
"fieldset { padding: 0; margin: 0; }",
|
|
2556
2588
|
"table { border-collapse: collapse; border-spacing: 0; }"
|
|
2557
2589
|
].join("\n");
|
|
@@ -2807,7 +2839,8 @@ function styleValueToCSS(styles, forceImportant = false) {
|
|
|
2807
2839
|
if (val === void 0 || val === null || typeof val === "object" || RN_ONLY_PROPS.has(prop)) continue;
|
|
2808
2840
|
let cssVal;
|
|
2809
2841
|
if (typeof val === "number") {
|
|
2810
|
-
|
|
2842
|
+
const isCustomProp = prop.startsWith("--");
|
|
2843
|
+
cssVal = val === 0 || isCustomProp || CSS_UNITLESS.has(prop) ? String(val) : `${val}px`;
|
|
2811
2844
|
} else {
|
|
2812
2845
|
cssVal = String(val);
|
|
2813
2846
|
}
|
package/dist/index.js
CHANGED
|
@@ -910,6 +910,13 @@ function hexToRgba(hex, alpha) {
|
|
|
910
910
|
const [r, g, b] = rgb;
|
|
911
911
|
return `rgba(${r},${g},${b},${alpha})`;
|
|
912
912
|
}
|
|
913
|
+
function withOpacityVar(color, varName) {
|
|
914
|
+
if (!color.startsWith("#")) return color;
|
|
915
|
+
const rgb = parseHexRgb(color);
|
|
916
|
+
if (!rgb) return color;
|
|
917
|
+
const [r, g, b] = rgb;
|
|
918
|
+
return `rgba(${r},${g},${b},var(${varName},1))`;
|
|
919
|
+
}
|
|
913
920
|
function resolveSpacing(value, negative, spacing, isArbitrary) {
|
|
914
921
|
if (isArbitrary) {
|
|
915
922
|
const resolved = getEffectiveIsWeb() ? value : toNativeValue(value);
|
|
@@ -1356,7 +1363,8 @@ var RESOLVERS = {
|
|
|
1356
1363
|
// ── Background ─────────────────────────────────────────────────────────────
|
|
1357
1364
|
bg: ({ value, isArbitrary }, { colors }) => {
|
|
1358
1365
|
const color = resolveColor(value, colors, isArbitrary);
|
|
1359
|
-
|
|
1366
|
+
if (!color) return null;
|
|
1367
|
+
return { backgroundColor: getEffectiveIsWeb() ? withOpacityVar(color, "--bg-opacity") : color };
|
|
1360
1368
|
},
|
|
1361
1369
|
"bg-opacity": ({ value, isArbitrary }, _) => {
|
|
1362
1370
|
if (!getEffectiveIsWeb()) return null;
|
|
@@ -1373,10 +1381,11 @@ var RESOLVERS = {
|
|
|
1373
1381
|
if (/^\d/.test(value) || /^(calc|min|max|clamp)/.test(value)) {
|
|
1374
1382
|
return { fontSize: getEffectiveIsWeb() ? value : toNativeValue(value) };
|
|
1375
1383
|
}
|
|
1376
|
-
return { color: value };
|
|
1384
|
+
return { color: getEffectiveIsWeb() ? withOpacityVar(value, "--text-opacity") : value };
|
|
1377
1385
|
}
|
|
1378
1386
|
const color = resolveColor(value, colors, false);
|
|
1379
|
-
|
|
1387
|
+
if (!color) return null;
|
|
1388
|
+
return { color: getEffectiveIsWeb() ? withOpacityVar(color, "--text-opacity") : color };
|
|
1380
1389
|
},
|
|
1381
1390
|
"text-opacity": ({ value, isArbitrary }, _) => {
|
|
1382
1391
|
if (!getEffectiveIsWeb()) return null;
|
|
@@ -2669,8 +2678,31 @@ var BASE_RESET = [
|
|
|
2669
2678
|
"a { color: inherit; text-decoration: none; }",
|
|
2670
2679
|
"ul, ol { margin: 0; padding: 0; list-style: none; }",
|
|
2671
2680
|
"img, video, svg { display: block; max-width: 100%; }",
|
|
2672
|
-
|
|
2681
|
+
// appearance: none is deliberately NOT applied to checkbox/radio/select below —
|
|
2682
|
+
// stripping it hides their native checkmark/arrow with nothing rendered in its
|
|
2683
|
+
// place, leaving an invisible checkbox or an arrow-less <select> that looks like
|
|
2684
|
+
// plain text. Text-like inputs, textarea, and button don't have that problem
|
|
2685
|
+
// (their native chrome is just a skin around content utilities can fully
|
|
2686
|
+
// restyle), so they keep the blank-canvas treatment.
|
|
2687
|
+
"input:not([type='checkbox']):not([type='radio']), textarea { appearance: none; -webkit-appearance: none; background: transparent; padding: 0; margin: 0; font: inherit; color: inherit; line-height: inherit; }",
|
|
2688
|
+
// Native checkbox/radio/range still get typography + spacing normalized, and
|
|
2689
|
+
// accent-color re-themes their native indicator to the current text color
|
|
2690
|
+
// instead of the browser/OS default blue, so they stay on-brand without
|
|
2691
|
+
// needing to be rebuilt from scratch.
|
|
2692
|
+
"input[type='checkbox'], input[type='radio'], input[type='range'] { margin: 0; font: inherit; accent-color: currentColor; }",
|
|
2693
|
+
// select keeps its native chrome (border/background/arrow are all part of the
|
|
2694
|
+
// same OS-drawn widget that appearance: none would blank out) — only typography
|
|
2695
|
+
// and spacing are normalized so it still matches surrounding text.
|
|
2696
|
+
"select { margin: 0; font: inherit; color: inherit; line-height: inherit; }",
|
|
2673
2697
|
"button { appearance: none; -webkit-appearance: none; background: transparent; padding: 0; margin: 0; font: inherit; color: inherit; cursor: pointer; line-height: inherit; text-align: inherit; }",
|
|
2698
|
+
"button, [role='button'] { cursor: pointer; }",
|
|
2699
|
+
":disabled { cursor: default; }",
|
|
2700
|
+
"textarea { resize: vertical; }",
|
|
2701
|
+
// Firefox renders placeholders at ~54% opacity by default; every other browser uses 1 —
|
|
2702
|
+
// normalize to 1 so placeholder color is consistent and fully controlled by the placeholder: modifier.
|
|
2703
|
+
"::placeholder { opacity: 1; }",
|
|
2704
|
+
"input[type='number']::-webkit-inner-spin-button, input[type='number']::-webkit-outer-spin-button { margin: 0; }",
|
|
2705
|
+
"input[type='search']::-webkit-search-decoration, input[type='search']::-webkit-search-cancel-button { -webkit-appearance: none; }",
|
|
2674
2706
|
"fieldset { padding: 0; margin: 0; }",
|
|
2675
2707
|
"table { border-collapse: collapse; border-spacing: 0; }"
|
|
2676
2708
|
].join("\n");
|
|
@@ -2885,7 +2917,8 @@ function styleValueToCSS(styles, forceImportant = false) {
|
|
|
2885
2917
|
if (val === void 0 || val === null || typeof val === "object" || RN_ONLY_PROPS.has(prop)) continue;
|
|
2886
2918
|
let cssVal;
|
|
2887
2919
|
if (typeof val === "number") {
|
|
2888
|
-
|
|
2920
|
+
const isCustomProp = prop.startsWith("--");
|
|
2921
|
+
cssVal = val === 0 || isCustomProp || CSS_UNITLESS.has(prop) ? String(val) : `${val}px`;
|
|
2889
2922
|
} else {
|
|
2890
2923
|
cssVal = String(val);
|
|
2891
2924
|
}
|
package/dist/index.mjs
CHANGED
package/dist/jsx-dev-runtime.js
CHANGED
|
@@ -869,6 +869,13 @@ function hexToRgba(hex, alpha) {
|
|
|
869
869
|
const [r, g, b] = rgb;
|
|
870
870
|
return `rgba(${r},${g},${b},${alpha})`;
|
|
871
871
|
}
|
|
872
|
+
function withOpacityVar(color, varName) {
|
|
873
|
+
if (!color.startsWith("#")) return color;
|
|
874
|
+
const rgb = parseHexRgb(color);
|
|
875
|
+
if (!rgb) return color;
|
|
876
|
+
const [r, g, b] = rgb;
|
|
877
|
+
return `rgba(${r},${g},${b},var(${varName},1))`;
|
|
878
|
+
}
|
|
872
879
|
function resolveSpacing(value, negative, spacing, isArbitrary) {
|
|
873
880
|
if (isArbitrary) {
|
|
874
881
|
const resolved = getEffectiveIsWeb() ? value : toNativeValue(value);
|
|
@@ -1315,7 +1322,8 @@ var RESOLVERS = {
|
|
|
1315
1322
|
// ── Background ─────────────────────────────────────────────────────────────
|
|
1316
1323
|
bg: ({ value, isArbitrary }, { colors }) => {
|
|
1317
1324
|
const color = resolveColor(value, colors, isArbitrary);
|
|
1318
|
-
|
|
1325
|
+
if (!color) return null;
|
|
1326
|
+
return { backgroundColor: getEffectiveIsWeb() ? withOpacityVar(color, "--bg-opacity") : color };
|
|
1319
1327
|
},
|
|
1320
1328
|
"bg-opacity": ({ value, isArbitrary }, _) => {
|
|
1321
1329
|
if (!getEffectiveIsWeb()) return null;
|
|
@@ -1332,10 +1340,11 @@ var RESOLVERS = {
|
|
|
1332
1340
|
if (/^\d/.test(value) || /^(calc|min|max|clamp)/.test(value)) {
|
|
1333
1341
|
return { fontSize: getEffectiveIsWeb() ? value : toNativeValue(value) };
|
|
1334
1342
|
}
|
|
1335
|
-
return { color: value };
|
|
1343
|
+
return { color: getEffectiveIsWeb() ? withOpacityVar(value, "--text-opacity") : value };
|
|
1336
1344
|
}
|
|
1337
1345
|
const color = resolveColor(value, colors, false);
|
|
1338
|
-
|
|
1346
|
+
if (!color) return null;
|
|
1347
|
+
return { color: getEffectiveIsWeb() ? withOpacityVar(color, "--text-opacity") : color };
|
|
1339
1348
|
},
|
|
1340
1349
|
"text-opacity": ({ value, isArbitrary }, _) => {
|
|
1341
1350
|
if (!getEffectiveIsWeb()) return null;
|
|
@@ -2628,8 +2637,31 @@ var BASE_RESET = [
|
|
|
2628
2637
|
"a { color: inherit; text-decoration: none; }",
|
|
2629
2638
|
"ul, ol { margin: 0; padding: 0; list-style: none; }",
|
|
2630
2639
|
"img, video, svg { display: block; max-width: 100%; }",
|
|
2631
|
-
|
|
2640
|
+
// appearance: none is deliberately NOT applied to checkbox/radio/select below —
|
|
2641
|
+
// stripping it hides their native checkmark/arrow with nothing rendered in its
|
|
2642
|
+
// place, leaving an invisible checkbox or an arrow-less <select> that looks like
|
|
2643
|
+
// plain text. Text-like inputs, textarea, and button don't have that problem
|
|
2644
|
+
// (their native chrome is just a skin around content utilities can fully
|
|
2645
|
+
// restyle), so they keep the blank-canvas treatment.
|
|
2646
|
+
"input:not([type='checkbox']):not([type='radio']), textarea { appearance: none; -webkit-appearance: none; background: transparent; padding: 0; margin: 0; font: inherit; color: inherit; line-height: inherit; }",
|
|
2647
|
+
// Native checkbox/radio/range still get typography + spacing normalized, and
|
|
2648
|
+
// accent-color re-themes their native indicator to the current text color
|
|
2649
|
+
// instead of the browser/OS default blue, so they stay on-brand without
|
|
2650
|
+
// needing to be rebuilt from scratch.
|
|
2651
|
+
"input[type='checkbox'], input[type='radio'], input[type='range'] { margin: 0; font: inherit; accent-color: currentColor; }",
|
|
2652
|
+
// select keeps its native chrome (border/background/arrow are all part of the
|
|
2653
|
+
// same OS-drawn widget that appearance: none would blank out) — only typography
|
|
2654
|
+
// and spacing are normalized so it still matches surrounding text.
|
|
2655
|
+
"select { margin: 0; font: inherit; color: inherit; line-height: inherit; }",
|
|
2632
2656
|
"button { appearance: none; -webkit-appearance: none; background: transparent; padding: 0; margin: 0; font: inherit; color: inherit; cursor: pointer; line-height: inherit; text-align: inherit; }",
|
|
2657
|
+
"button, [role='button'] { cursor: pointer; }",
|
|
2658
|
+
":disabled { cursor: default; }",
|
|
2659
|
+
"textarea { resize: vertical; }",
|
|
2660
|
+
// Firefox renders placeholders at ~54% opacity by default; every other browser uses 1 —
|
|
2661
|
+
// normalize to 1 so placeholder color is consistent and fully controlled by the placeholder: modifier.
|
|
2662
|
+
"::placeholder { opacity: 1; }",
|
|
2663
|
+
"input[type='number']::-webkit-inner-spin-button, input[type='number']::-webkit-outer-spin-button { margin: 0; }",
|
|
2664
|
+
"input[type='search']::-webkit-search-decoration, input[type='search']::-webkit-search-cancel-button { -webkit-appearance: none; }",
|
|
2633
2665
|
"fieldset { padding: 0; margin: 0; }",
|
|
2634
2666
|
"table { border-collapse: collapse; border-spacing: 0; }"
|
|
2635
2667
|
].join("\n");
|
|
@@ -2829,7 +2861,8 @@ function styleValueToCSS(styles, forceImportant = false) {
|
|
|
2829
2861
|
if (val === void 0 || val === null || typeof val === "object" || RN_ONLY_PROPS.has(prop)) continue;
|
|
2830
2862
|
let cssVal;
|
|
2831
2863
|
if (typeof val === "number") {
|
|
2832
|
-
|
|
2864
|
+
const isCustomProp = prop.startsWith("--");
|
|
2865
|
+
cssVal = val === 0 || isCustomProp || CSS_UNITLESS.has(prop) ? String(val) : `${val}px`;
|
|
2833
2866
|
} else {
|
|
2834
2867
|
cssVal = String(val);
|
|
2835
2868
|
}
|
package/dist/jsx-dev-runtime.mjs
CHANGED
|
@@ -3,8 +3,8 @@ import {
|
|
|
3
3
|
Fragment,
|
|
4
4
|
jsx,
|
|
5
5
|
jsxs
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-35CBHSAG.mjs";
|
|
7
|
+
import "./chunk-XJFOY7HN.mjs";
|
|
8
8
|
|
|
9
9
|
// src/jsx-dev-runtime.tsx
|
|
10
10
|
function jsxDEV(type, props, key, isStaticChildren, source, self) {
|
package/dist/jsx-runtime.js
CHANGED
|
@@ -870,6 +870,13 @@ function hexToRgba(hex, alpha) {
|
|
|
870
870
|
const [r, g, b] = rgb;
|
|
871
871
|
return `rgba(${r},${g},${b},${alpha})`;
|
|
872
872
|
}
|
|
873
|
+
function withOpacityVar(color, varName) {
|
|
874
|
+
if (!color.startsWith("#")) return color;
|
|
875
|
+
const rgb = parseHexRgb(color);
|
|
876
|
+
if (!rgb) return color;
|
|
877
|
+
const [r, g, b] = rgb;
|
|
878
|
+
return `rgba(${r},${g},${b},var(${varName},1))`;
|
|
879
|
+
}
|
|
873
880
|
function resolveSpacing(value, negative, spacing, isArbitrary) {
|
|
874
881
|
if (isArbitrary) {
|
|
875
882
|
const resolved = getEffectiveIsWeb() ? value : toNativeValue(value);
|
|
@@ -1316,7 +1323,8 @@ var RESOLVERS = {
|
|
|
1316
1323
|
// ── Background ─────────────────────────────────────────────────────────────
|
|
1317
1324
|
bg: ({ value, isArbitrary }, { colors }) => {
|
|
1318
1325
|
const color = resolveColor(value, colors, isArbitrary);
|
|
1319
|
-
|
|
1326
|
+
if (!color) return null;
|
|
1327
|
+
return { backgroundColor: getEffectiveIsWeb() ? withOpacityVar(color, "--bg-opacity") : color };
|
|
1320
1328
|
},
|
|
1321
1329
|
"bg-opacity": ({ value, isArbitrary }, _) => {
|
|
1322
1330
|
if (!getEffectiveIsWeb()) return null;
|
|
@@ -1333,10 +1341,11 @@ var RESOLVERS = {
|
|
|
1333
1341
|
if (/^\d/.test(value) || /^(calc|min|max|clamp)/.test(value)) {
|
|
1334
1342
|
return { fontSize: getEffectiveIsWeb() ? value : toNativeValue(value) };
|
|
1335
1343
|
}
|
|
1336
|
-
return { color: value };
|
|
1344
|
+
return { color: getEffectiveIsWeb() ? withOpacityVar(value, "--text-opacity") : value };
|
|
1337
1345
|
}
|
|
1338
1346
|
const color = resolveColor(value, colors, false);
|
|
1339
|
-
|
|
1347
|
+
if (!color) return null;
|
|
1348
|
+
return { color: getEffectiveIsWeb() ? withOpacityVar(color, "--text-opacity") : color };
|
|
1340
1349
|
},
|
|
1341
1350
|
"text-opacity": ({ value, isArbitrary }, _) => {
|
|
1342
1351
|
if (!getEffectiveIsWeb()) return null;
|
|
@@ -2629,8 +2638,31 @@ var BASE_RESET = [
|
|
|
2629
2638
|
"a { color: inherit; text-decoration: none; }",
|
|
2630
2639
|
"ul, ol { margin: 0; padding: 0; list-style: none; }",
|
|
2631
2640
|
"img, video, svg { display: block; max-width: 100%; }",
|
|
2632
|
-
|
|
2641
|
+
// appearance: none is deliberately NOT applied to checkbox/radio/select below —
|
|
2642
|
+
// stripping it hides their native checkmark/arrow with nothing rendered in its
|
|
2643
|
+
// place, leaving an invisible checkbox or an arrow-less <select> that looks like
|
|
2644
|
+
// plain text. Text-like inputs, textarea, and button don't have that problem
|
|
2645
|
+
// (their native chrome is just a skin around content utilities can fully
|
|
2646
|
+
// restyle), so they keep the blank-canvas treatment.
|
|
2647
|
+
"input:not([type='checkbox']):not([type='radio']), textarea { appearance: none; -webkit-appearance: none; background: transparent; padding: 0; margin: 0; font: inherit; color: inherit; line-height: inherit; }",
|
|
2648
|
+
// Native checkbox/radio/range still get typography + spacing normalized, and
|
|
2649
|
+
// accent-color re-themes their native indicator to the current text color
|
|
2650
|
+
// instead of the browser/OS default blue, so they stay on-brand without
|
|
2651
|
+
// needing to be rebuilt from scratch.
|
|
2652
|
+
"input[type='checkbox'], input[type='radio'], input[type='range'] { margin: 0; font: inherit; accent-color: currentColor; }",
|
|
2653
|
+
// select keeps its native chrome (border/background/arrow are all part of the
|
|
2654
|
+
// same OS-drawn widget that appearance: none would blank out) — only typography
|
|
2655
|
+
// and spacing are normalized so it still matches surrounding text.
|
|
2656
|
+
"select { margin: 0; font: inherit; color: inherit; line-height: inherit; }",
|
|
2633
2657
|
"button { appearance: none; -webkit-appearance: none; background: transparent; padding: 0; margin: 0; font: inherit; color: inherit; cursor: pointer; line-height: inherit; text-align: inherit; }",
|
|
2658
|
+
"button, [role='button'] { cursor: pointer; }",
|
|
2659
|
+
":disabled { cursor: default; }",
|
|
2660
|
+
"textarea { resize: vertical; }",
|
|
2661
|
+
// Firefox renders placeholders at ~54% opacity by default; every other browser uses 1 —
|
|
2662
|
+
// normalize to 1 so placeholder color is consistent and fully controlled by the placeholder: modifier.
|
|
2663
|
+
"::placeholder { opacity: 1; }",
|
|
2664
|
+
"input[type='number']::-webkit-inner-spin-button, input[type='number']::-webkit-outer-spin-button { margin: 0; }",
|
|
2665
|
+
"input[type='search']::-webkit-search-decoration, input[type='search']::-webkit-search-cancel-button { -webkit-appearance: none; }",
|
|
2634
2666
|
"fieldset { padding: 0; margin: 0; }",
|
|
2635
2667
|
"table { border-collapse: collapse; border-spacing: 0; }"
|
|
2636
2668
|
].join("\n");
|
|
@@ -2830,7 +2862,8 @@ function styleValueToCSS(styles, forceImportant = false) {
|
|
|
2830
2862
|
if (val === void 0 || val === null || typeof val === "object" || RN_ONLY_PROPS.has(prop)) continue;
|
|
2831
2863
|
let cssVal;
|
|
2832
2864
|
if (typeof val === "number") {
|
|
2833
|
-
|
|
2865
|
+
const isCustomProp = prop.startsWith("--");
|
|
2866
|
+
cssVal = val === 0 || isCustomProp || CSS_UNITLESS.has(prop) ? String(val) : `${val}px`;
|
|
2834
2867
|
} else {
|
|
2835
2868
|
cssVal = String(val);
|
|
2836
2869
|
}
|
package/dist/jsx-runtime.mjs
CHANGED
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
_invalidateDefaultFontCache,
|
|
5
5
|
jsx,
|
|
6
6
|
jsxs
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-35CBHSAG.mjs";
|
|
8
8
|
import {
|
|
9
9
|
registerWebElement
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-XJFOY7HN.mjs";
|
|
11
11
|
export {
|
|
12
12
|
Fragment,
|
|
13
13
|
_invalidateDefaultFontCache,
|
package/dist/vite-plugin.js
CHANGED
|
@@ -70,8 +70,31 @@ var BASE_RESET = [
|
|
|
70
70
|
"a { color: inherit; text-decoration: none; }",
|
|
71
71
|
"ul, ol { margin: 0; padding: 0; list-style: none; }",
|
|
72
72
|
"img, video, svg { display: block; max-width: 100%; }",
|
|
73
|
-
|
|
73
|
+
// appearance: none is deliberately NOT applied to checkbox/radio/select below —
|
|
74
|
+
// stripping it hides their native checkmark/arrow with nothing rendered in its
|
|
75
|
+
// place, leaving an invisible checkbox or an arrow-less <select> that looks like
|
|
76
|
+
// plain text. Text-like inputs, textarea, and button don't have that problem
|
|
77
|
+
// (their native chrome is just a skin around content utilities can fully
|
|
78
|
+
// restyle), so they keep the blank-canvas treatment.
|
|
79
|
+
"input:not([type='checkbox']):not([type='radio']), textarea { appearance: none; -webkit-appearance: none; background: transparent; padding: 0; margin: 0; font: inherit; color: inherit; line-height: inherit; }",
|
|
80
|
+
// Native checkbox/radio/range still get typography + spacing normalized, and
|
|
81
|
+
// accent-color re-themes their native indicator to the current text color
|
|
82
|
+
// instead of the browser/OS default blue, so they stay on-brand without
|
|
83
|
+
// needing to be rebuilt from scratch.
|
|
84
|
+
"input[type='checkbox'], input[type='radio'], input[type='range'] { margin: 0; font: inherit; accent-color: currentColor; }",
|
|
85
|
+
// select keeps its native chrome (border/background/arrow are all part of the
|
|
86
|
+
// same OS-drawn widget that appearance: none would blank out) — only typography
|
|
87
|
+
// and spacing are normalized so it still matches surrounding text.
|
|
88
|
+
"select { margin: 0; font: inherit; color: inherit; line-height: inherit; }",
|
|
74
89
|
"button { appearance: none; -webkit-appearance: none; background: transparent; padding: 0; margin: 0; font: inherit; color: inherit; cursor: pointer; line-height: inherit; text-align: inherit; }",
|
|
90
|
+
"button, [role='button'] { cursor: pointer; }",
|
|
91
|
+
":disabled { cursor: default; }",
|
|
92
|
+
"textarea { resize: vertical; }",
|
|
93
|
+
// Firefox renders placeholders at ~54% opacity by default; every other browser uses 1 —
|
|
94
|
+
// normalize to 1 so placeholder color is consistent and fully controlled by the placeholder: modifier.
|
|
95
|
+
"::placeholder { opacity: 1; }",
|
|
96
|
+
"input[type='number']::-webkit-inner-spin-button, input[type='number']::-webkit-outer-spin-button { margin: 0; }",
|
|
97
|
+
"input[type='search']::-webkit-search-decoration, input[type='search']::-webkit-search-cancel-button { -webkit-appearance: none; }",
|
|
75
98
|
"fieldset { padding: 0; margin: 0; }",
|
|
76
99
|
"table { border-collapse: collapse; border-spacing: 0; }"
|
|
77
100
|
].join("\n");
|
|
@@ -701,6 +724,13 @@ function hexToRgba(hex, alpha) {
|
|
|
701
724
|
const [r, g, b] = rgb;
|
|
702
725
|
return `rgba(${r},${g},${b},${alpha})`;
|
|
703
726
|
}
|
|
727
|
+
function withOpacityVar(color, varName) {
|
|
728
|
+
if (!color.startsWith("#")) return color;
|
|
729
|
+
const rgb = parseHexRgb(color);
|
|
730
|
+
if (!rgb) return color;
|
|
731
|
+
const [r, g, b] = rgb;
|
|
732
|
+
return `rgba(${r},${g},${b},var(${varName},1))`;
|
|
733
|
+
}
|
|
704
734
|
function resolveSpacing(value, negative, spacing, isArbitrary) {
|
|
705
735
|
if (isArbitrary) {
|
|
706
736
|
const resolved = getEffectiveIsWeb() ? value : toNativeValue(value);
|
|
@@ -1147,7 +1177,8 @@ var RESOLVERS = {
|
|
|
1147
1177
|
// ── Background ─────────────────────────────────────────────────────────────
|
|
1148
1178
|
bg: ({ value, isArbitrary }, { colors }) => {
|
|
1149
1179
|
const color = resolveColor(value, colors, isArbitrary);
|
|
1150
|
-
|
|
1180
|
+
if (!color) return null;
|
|
1181
|
+
return { backgroundColor: getEffectiveIsWeb() ? withOpacityVar(color, "--bg-opacity") : color };
|
|
1151
1182
|
},
|
|
1152
1183
|
"bg-opacity": ({ value, isArbitrary }, _) => {
|
|
1153
1184
|
if (!getEffectiveIsWeb()) return null;
|
|
@@ -1164,10 +1195,11 @@ var RESOLVERS = {
|
|
|
1164
1195
|
if (/^\d/.test(value) || /^(calc|min|max|clamp)/.test(value)) {
|
|
1165
1196
|
return { fontSize: getEffectiveIsWeb() ? value : toNativeValue(value) };
|
|
1166
1197
|
}
|
|
1167
|
-
return { color: value };
|
|
1198
|
+
return { color: getEffectiveIsWeb() ? withOpacityVar(value, "--text-opacity") : value };
|
|
1168
1199
|
}
|
|
1169
1200
|
const color = resolveColor(value, colors, false);
|
|
1170
|
-
|
|
1201
|
+
if (!color) return null;
|
|
1202
|
+
return { color: getEffectiveIsWeb() ? withOpacityVar(color, "--text-opacity") : color };
|
|
1171
1203
|
},
|
|
1172
1204
|
"text-opacity": ({ value, isArbitrary }, _) => {
|
|
1173
1205
|
if (!getEffectiveIsWeb()) return null;
|
|
@@ -2732,7 +2764,8 @@ function styleValueToCSS(styles, forceImportant = false) {
|
|
|
2732
2764
|
if (val === void 0 || val === null || typeof val === "object" || RN_ONLY_PROPS.has(prop)) continue;
|
|
2733
2765
|
let cssVal;
|
|
2734
2766
|
if (typeof val === "number") {
|
|
2735
|
-
|
|
2767
|
+
const isCustomProp = prop.startsWith("--");
|
|
2768
|
+
cssVal = val === 0 || isCustomProp || CSS_UNITLESS.has(prop) ? String(val) : `${val}px`;
|
|
2736
2769
|
} else {
|
|
2737
2770
|
cssVal = String(val);
|
|
2738
2771
|
}
|
|
@@ -3192,6 +3225,23 @@ function toNumericScreens(screens) {
|
|
|
3192
3225
|
}
|
|
3193
3226
|
return out;
|
|
3194
3227
|
}
|
|
3228
|
+
function pushTemplateLiteralBody(body, into) {
|
|
3229
|
+
const interpolationRe = /\$\{(?:[^{}]|\{[^{}]*\})*\}/g;
|
|
3230
|
+
let im;
|
|
3231
|
+
while ((im = interpolationRe.exec(body)) !== null) {
|
|
3232
|
+
pushClassLikeStrings(im[0], into);
|
|
3233
|
+
}
|
|
3234
|
+
pushTokens(body.replace(/\$\{(?:[^{}]|\{[^{}]*\})*\}/g, " "), into);
|
|
3235
|
+
}
|
|
3236
|
+
function pushClassLikeStrings(text, into) {
|
|
3237
|
+
const quotedStringRe = /(["'`])((?:(?!\1).)*)\1/g;
|
|
3238
|
+
let qm;
|
|
3239
|
+
while ((qm = quotedStringRe.exec(text)) !== null) {
|
|
3240
|
+
const [, quote, content] = qm;
|
|
3241
|
+
if (quote === "`") pushTemplateLiteralBody(content, into);
|
|
3242
|
+
else pushTokens(content, into);
|
|
3243
|
+
}
|
|
3244
|
+
}
|
|
3195
3245
|
function extractClassStrings(code) {
|
|
3196
3246
|
const found = /* @__PURE__ */ new Set();
|
|
3197
3247
|
const simpleRe = /(?:className|kb)=["']([^"']+)["']/g;
|
|
@@ -3212,8 +3262,7 @@ function extractClassStrings(code) {
|
|
|
3212
3262
|
i++;
|
|
3213
3263
|
}
|
|
3214
3264
|
jsxExprRe.lastIndex = i + 1;
|
|
3215
|
-
|
|
3216
|
-
for (const s of innerStrings) pushTokens(s.slice(1, -1), found);
|
|
3265
|
+
pushClassLikeStrings(block, found);
|
|
3217
3266
|
}
|
|
3218
3267
|
const clsxCallRe = /(?:clsx|cn|classnames|cx)\(/g;
|
|
3219
3268
|
while ((m = clsxCallRe.exec(code)) !== null) {
|
|
@@ -3230,19 +3279,16 @@ function extractClassStrings(code) {
|
|
|
3230
3279
|
i++;
|
|
3231
3280
|
}
|
|
3232
3281
|
clsxCallRe.lastIndex = i + 1;
|
|
3233
|
-
|
|
3234
|
-
for (const s of innerStrings) pushTokens(s.slice(1, -1), found);
|
|
3282
|
+
pushClassLikeStrings(block, found);
|
|
3235
3283
|
}
|
|
3236
3284
|
const templateRe = /`([^`]{1,2000})`/g;
|
|
3237
|
-
while ((m = templateRe.exec(code)) !== null)
|
|
3238
|
-
const staticParts = m[1].replace(/\$\{(?:[^{}]|\{[^{}]*\})*\}/g, " ");
|
|
3239
|
-
pushTokens(staticParts, found);
|
|
3240
|
-
}
|
|
3285
|
+
while ((m = templateRe.exec(code)) !== null) pushTemplateLiteralBody(m[1], found);
|
|
3241
3286
|
return [...found];
|
|
3242
3287
|
}
|
|
3288
|
+
var NOT_A_CLASS_NAME_RE = /[${}]|-$|^[?:&|!"'=<>]+$/;
|
|
3243
3289
|
function pushTokens(str, into) {
|
|
3244
3290
|
for (const tok of splitClassTokens(str)) {
|
|
3245
|
-
if (tok) into.add(tok);
|
|
3291
|
+
if (tok && !NOT_A_CLASS_NAME_RE.test(tok)) into.add(tok);
|
|
3246
3292
|
}
|
|
3247
3293
|
}
|
|
3248
3294
|
function scanDir(dir, onFile) {
|
package/dist/vite-plugin.mjs
CHANGED
|
@@ -36,8 +36,31 @@ var BASE_RESET = [
|
|
|
36
36
|
"a { color: inherit; text-decoration: none; }",
|
|
37
37
|
"ul, ol { margin: 0; padding: 0; list-style: none; }",
|
|
38
38
|
"img, video, svg { display: block; max-width: 100%; }",
|
|
39
|
-
|
|
39
|
+
// appearance: none is deliberately NOT applied to checkbox/radio/select below —
|
|
40
|
+
// stripping it hides their native checkmark/arrow with nothing rendered in its
|
|
41
|
+
// place, leaving an invisible checkbox or an arrow-less <select> that looks like
|
|
42
|
+
// plain text. Text-like inputs, textarea, and button don't have that problem
|
|
43
|
+
// (their native chrome is just a skin around content utilities can fully
|
|
44
|
+
// restyle), so they keep the blank-canvas treatment.
|
|
45
|
+
"input:not([type='checkbox']):not([type='radio']), textarea { appearance: none; -webkit-appearance: none; background: transparent; padding: 0; margin: 0; font: inherit; color: inherit; line-height: inherit; }",
|
|
46
|
+
// Native checkbox/radio/range still get typography + spacing normalized, and
|
|
47
|
+
// accent-color re-themes their native indicator to the current text color
|
|
48
|
+
// instead of the browser/OS default blue, so they stay on-brand without
|
|
49
|
+
// needing to be rebuilt from scratch.
|
|
50
|
+
"input[type='checkbox'], input[type='radio'], input[type='range'] { margin: 0; font: inherit; accent-color: currentColor; }",
|
|
51
|
+
// select keeps its native chrome (border/background/arrow are all part of the
|
|
52
|
+
// same OS-drawn widget that appearance: none would blank out) — only typography
|
|
53
|
+
// and spacing are normalized so it still matches surrounding text.
|
|
54
|
+
"select { margin: 0; font: inherit; color: inherit; line-height: inherit; }",
|
|
40
55
|
"button { appearance: none; -webkit-appearance: none; background: transparent; padding: 0; margin: 0; font: inherit; color: inherit; cursor: pointer; line-height: inherit; text-align: inherit; }",
|
|
56
|
+
"button, [role='button'] { cursor: pointer; }",
|
|
57
|
+
":disabled { cursor: default; }",
|
|
58
|
+
"textarea { resize: vertical; }",
|
|
59
|
+
// Firefox renders placeholders at ~54% opacity by default; every other browser uses 1 —
|
|
60
|
+
// normalize to 1 so placeholder color is consistent and fully controlled by the placeholder: modifier.
|
|
61
|
+
"::placeholder { opacity: 1; }",
|
|
62
|
+
"input[type='number']::-webkit-inner-spin-button, input[type='number']::-webkit-outer-spin-button { margin: 0; }",
|
|
63
|
+
"input[type='search']::-webkit-search-decoration, input[type='search']::-webkit-search-cancel-button { -webkit-appearance: none; }",
|
|
41
64
|
"fieldset { padding: 0; margin: 0; }",
|
|
42
65
|
"table { border-collapse: collapse; border-spacing: 0; }"
|
|
43
66
|
].join("\n");
|
|
@@ -667,6 +690,13 @@ function hexToRgba(hex, alpha) {
|
|
|
667
690
|
const [r, g, b] = rgb;
|
|
668
691
|
return `rgba(${r},${g},${b},${alpha})`;
|
|
669
692
|
}
|
|
693
|
+
function withOpacityVar(color, varName) {
|
|
694
|
+
if (!color.startsWith("#")) return color;
|
|
695
|
+
const rgb = parseHexRgb(color);
|
|
696
|
+
if (!rgb) return color;
|
|
697
|
+
const [r, g, b] = rgb;
|
|
698
|
+
return `rgba(${r},${g},${b},var(${varName},1))`;
|
|
699
|
+
}
|
|
670
700
|
function resolveSpacing(value, negative, spacing, isArbitrary) {
|
|
671
701
|
if (isArbitrary) {
|
|
672
702
|
const resolved = getEffectiveIsWeb() ? value : toNativeValue(value);
|
|
@@ -1113,7 +1143,8 @@ var RESOLVERS = {
|
|
|
1113
1143
|
// ── Background ─────────────────────────────────────────────────────────────
|
|
1114
1144
|
bg: ({ value, isArbitrary }, { colors }) => {
|
|
1115
1145
|
const color = resolveColor(value, colors, isArbitrary);
|
|
1116
|
-
|
|
1146
|
+
if (!color) return null;
|
|
1147
|
+
return { backgroundColor: getEffectiveIsWeb() ? withOpacityVar(color, "--bg-opacity") : color };
|
|
1117
1148
|
},
|
|
1118
1149
|
"bg-opacity": ({ value, isArbitrary }, _) => {
|
|
1119
1150
|
if (!getEffectiveIsWeb()) return null;
|
|
@@ -1130,10 +1161,11 @@ var RESOLVERS = {
|
|
|
1130
1161
|
if (/^\d/.test(value) || /^(calc|min|max|clamp)/.test(value)) {
|
|
1131
1162
|
return { fontSize: getEffectiveIsWeb() ? value : toNativeValue(value) };
|
|
1132
1163
|
}
|
|
1133
|
-
return { color: value };
|
|
1164
|
+
return { color: getEffectiveIsWeb() ? withOpacityVar(value, "--text-opacity") : value };
|
|
1134
1165
|
}
|
|
1135
1166
|
const color = resolveColor(value, colors, false);
|
|
1136
|
-
|
|
1167
|
+
if (!color) return null;
|
|
1168
|
+
return { color: getEffectiveIsWeb() ? withOpacityVar(color, "--text-opacity") : color };
|
|
1137
1169
|
},
|
|
1138
1170
|
"text-opacity": ({ value, isArbitrary }, _) => {
|
|
1139
1171
|
if (!getEffectiveIsWeb()) return null;
|
|
@@ -2698,7 +2730,8 @@ function styleValueToCSS(styles, forceImportant = false) {
|
|
|
2698
2730
|
if (val === void 0 || val === null || typeof val === "object" || RN_ONLY_PROPS.has(prop)) continue;
|
|
2699
2731
|
let cssVal;
|
|
2700
2732
|
if (typeof val === "number") {
|
|
2701
|
-
|
|
2733
|
+
const isCustomProp = prop.startsWith("--");
|
|
2734
|
+
cssVal = val === 0 || isCustomProp || CSS_UNITLESS.has(prop) ? String(val) : `${val}px`;
|
|
2702
2735
|
} else {
|
|
2703
2736
|
cssVal = String(val);
|
|
2704
2737
|
}
|
|
@@ -3158,6 +3191,23 @@ function toNumericScreens(screens) {
|
|
|
3158
3191
|
}
|
|
3159
3192
|
return out;
|
|
3160
3193
|
}
|
|
3194
|
+
function pushTemplateLiteralBody(body, into) {
|
|
3195
|
+
const interpolationRe = /\$\{(?:[^{}]|\{[^{}]*\})*\}/g;
|
|
3196
|
+
let im;
|
|
3197
|
+
while ((im = interpolationRe.exec(body)) !== null) {
|
|
3198
|
+
pushClassLikeStrings(im[0], into);
|
|
3199
|
+
}
|
|
3200
|
+
pushTokens(body.replace(/\$\{(?:[^{}]|\{[^{}]*\})*\}/g, " "), into);
|
|
3201
|
+
}
|
|
3202
|
+
function pushClassLikeStrings(text, into) {
|
|
3203
|
+
const quotedStringRe = /(["'`])((?:(?!\1).)*)\1/g;
|
|
3204
|
+
let qm;
|
|
3205
|
+
while ((qm = quotedStringRe.exec(text)) !== null) {
|
|
3206
|
+
const [, quote, content] = qm;
|
|
3207
|
+
if (quote === "`") pushTemplateLiteralBody(content, into);
|
|
3208
|
+
else pushTokens(content, into);
|
|
3209
|
+
}
|
|
3210
|
+
}
|
|
3161
3211
|
function extractClassStrings(code) {
|
|
3162
3212
|
const found = /* @__PURE__ */ new Set();
|
|
3163
3213
|
const simpleRe = /(?:className|kb)=["']([^"']+)["']/g;
|
|
@@ -3178,8 +3228,7 @@ function extractClassStrings(code) {
|
|
|
3178
3228
|
i++;
|
|
3179
3229
|
}
|
|
3180
3230
|
jsxExprRe.lastIndex = i + 1;
|
|
3181
|
-
|
|
3182
|
-
for (const s of innerStrings) pushTokens(s.slice(1, -1), found);
|
|
3231
|
+
pushClassLikeStrings(block, found);
|
|
3183
3232
|
}
|
|
3184
3233
|
const clsxCallRe = /(?:clsx|cn|classnames|cx)\(/g;
|
|
3185
3234
|
while ((m = clsxCallRe.exec(code)) !== null) {
|
|
@@ -3196,19 +3245,16 @@ function extractClassStrings(code) {
|
|
|
3196
3245
|
i++;
|
|
3197
3246
|
}
|
|
3198
3247
|
clsxCallRe.lastIndex = i + 1;
|
|
3199
|
-
|
|
3200
|
-
for (const s of innerStrings) pushTokens(s.slice(1, -1), found);
|
|
3248
|
+
pushClassLikeStrings(block, found);
|
|
3201
3249
|
}
|
|
3202
3250
|
const templateRe = /`([^`]{1,2000})`/g;
|
|
3203
|
-
while ((m = templateRe.exec(code)) !== null)
|
|
3204
|
-
const staticParts = m[1].replace(/\$\{(?:[^{}]|\{[^{}]*\})*\}/g, " ");
|
|
3205
|
-
pushTokens(staticParts, found);
|
|
3206
|
-
}
|
|
3251
|
+
while ((m = templateRe.exec(code)) !== null) pushTemplateLiteralBody(m[1], found);
|
|
3207
3252
|
return [...found];
|
|
3208
3253
|
}
|
|
3254
|
+
var NOT_A_CLASS_NAME_RE = /[${}]|-$|^[?:&|!"'=<>]+$/;
|
|
3209
3255
|
function pushTokens(str, into) {
|
|
3210
3256
|
for (const tok of splitClassTokens(str)) {
|
|
3211
|
-
if (tok) into.add(tok);
|
|
3257
|
+
if (tok && !NOT_A_CLASS_NAME_RE.test(tok)) into.add(tok);
|
|
3212
3258
|
}
|
|
3213
3259
|
}
|
|
3214
3260
|
function scanDir(dir, onFile) {
|