@bookklik/senangstart-css 0.2.10 → 0.2.12
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/.agent/skills/add-utility/SKILL.md +65 -0
- package/.agent/workflows/add-utility.md +2 -0
- package/.agent/workflows/build.md +2 -0
- package/.agent/workflows/dev.md +2 -0
- package/AGENTS.md +30 -0
- package/dist/senangstart-css.js +362 -151
- package/dist/senangstart-css.min.js +175 -174
- package/dist/senangstart-tw.js +4 -4
- package/dist/senangstart-tw.min.js +1 -1
- package/docs/ms/reference/visual/ring-color.md +2 -2
- package/docs/ms/reference/visual/ring-offset.md +3 -3
- package/docs/ms/reference/visual/ring.md +5 -5
- package/docs/public/assets/senangstart-css.min.js +175 -174
- package/docs/public/llms.txt +10 -10
- package/docs/reference/visual/ring-color.md +2 -2
- package/docs/reference/visual/ring-offset.md +3 -3
- package/docs/reference/visual/ring.md +5 -5
- package/package.json +1 -1
- package/src/cdn/tw-conversion-engine.js +4 -4
- package/src/cli/commands/build.js +42 -14
- package/src/cli/commands/dev.js +157 -93
- package/src/compiler/generators/css.js +371 -199
- package/src/compiler/tokenizer.js +25 -23
- package/src/core/tokenizer-core.js +46 -19
- package/src/definitions/visual-borders.js +10 -10
- package/src/utils/common.js +456 -39
- package/src/utils/node-io.js +82 -0
- package/tests/integration/dev-recovery.test.js +231 -0
- package/tests/unit/cli/memory-limits.test.js +169 -0
- package/tests/unit/compiler/css-generation-error-handling.test.js +204 -0
- package/tests/unit/compiler/generators/css-errors.test.js +102 -0
- package/tests/unit/convert-tailwind.test.js +518 -442
- package/tests/unit/utils/common.test.js +376 -26
- package/tests/unit/utils/file-timeout.test.js +154 -0
- package/tests/unit/utils/theme-validation.test.js +181 -0
- package/tests/unit/compiler/generators/css.coverage.test.js +0 -833
- package/tests/unit/convert-tailwind.cli.test.js +0 -95
- package/tests/unit/security.test.js +0 -206
- /package/tests/unit/{convert-tailwind.coverage.test.js → convert-tailwind-edgecases.test.js} +0 -0
package/dist/senangstart-css.js
CHANGED
|
@@ -39,15 +39,60 @@
|
|
|
39
39
|
if (typeof value !== "string") {
|
|
40
40
|
return "";
|
|
41
41
|
}
|
|
42
|
+
if (value.length > 1e3) {
|
|
43
|
+
return "";
|
|
44
|
+
}
|
|
42
45
|
let sanitized = value;
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
sanitized = sanitized.replace(/[\\`$]/g, "");
|
|
47
|
+
const dangerousUrlProtocols = [
|
|
48
|
+
"javascript:",
|
|
49
|
+
"vbscript:",
|
|
50
|
+
"data:",
|
|
51
|
+
"about:",
|
|
52
|
+
"file:",
|
|
53
|
+
"ftp:",
|
|
54
|
+
"mailto:"
|
|
55
|
+
].join("|");
|
|
56
|
+
const urlRegex = /url\s*\((?:[^()]|\((?:[^()]|\([^()]*\))*\))*\)/gi;
|
|
57
|
+
sanitized = sanitized.replace(urlRegex, (match) => {
|
|
58
|
+
if (dangerousUrlProtocols.split("|").some((protocol) => match.toLowerCase().includes(protocol))) {
|
|
59
|
+
return "url(about:blank)";
|
|
60
|
+
}
|
|
61
|
+
return match;
|
|
62
|
+
});
|
|
63
|
+
const scriptVectors = [
|
|
64
|
+
/expression\s*\(/gi,
|
|
65
|
+
// IE expression()
|
|
66
|
+
/\beval\s*\(/gi,
|
|
67
|
+
// eval()
|
|
68
|
+
/\balert\s*\(/gi,
|
|
69
|
+
// alert()
|
|
70
|
+
/\bdocument\./gi,
|
|
71
|
+
// document access
|
|
72
|
+
/\bwindow\./gi,
|
|
73
|
+
// window access
|
|
74
|
+
/on\w+\s*=/gi,
|
|
75
|
+
// event handlers (onclick=, etc.)
|
|
76
|
+
/<script[^>]*>/gi,
|
|
77
|
+
// <script> tags
|
|
78
|
+
/<\/script>/gi
|
|
79
|
+
// <\/script> tags
|
|
80
|
+
];
|
|
81
|
+
for (const pattern of scriptVectors) {
|
|
82
|
+
sanitized = sanitized.replace(pattern, "");
|
|
83
|
+
}
|
|
84
|
+
const atRules = /@(?:import|charset|namespace|supports|keyframes|font-face|media|page)/gi;
|
|
46
85
|
sanitized = sanitized.replace(atRules, "");
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
|
|
86
|
+
sanitized = sanitized.replace(/[;]/g, "_");
|
|
87
|
+
const openBrackets = (sanitized.match(/\[/g) || []).length;
|
|
88
|
+
const closeBrackets = (sanitized.match(/\]/g) || []).length;
|
|
89
|
+
if (Math.abs(openBrackets - closeBrackets) > 3 || Math.max(openBrackets, closeBrackets) > 10) {
|
|
90
|
+
return "";
|
|
91
|
+
}
|
|
92
|
+
sanitized = sanitized.replace(/@/g, "");
|
|
93
|
+
if (sanitized.length > 500) {
|
|
94
|
+
sanitized = sanitized.substring(0, 500);
|
|
95
|
+
}
|
|
51
96
|
return sanitized;
|
|
52
97
|
}
|
|
53
98
|
|
|
@@ -6492,11 +6537,11 @@ video {
|
|
|
6492
6537
|
supportsArbitrary: true,
|
|
6493
6538
|
values: [
|
|
6494
6539
|
{ value: "none", css: "box-shadow: 0 0 0 0 transparent;", description: "No ring", descriptionMs: "Tiada cincin" },
|
|
6495
|
-
{ value: "thin", css: "box-shadow: 0 0 0 1px var(--ring-color);", description: "Thin ring (1px)", descriptionMs: "Cincin nipis (1px)" },
|
|
6496
|
-
{ value: "regular", css: "box-shadow: 0 0 0 2px var(--ring-color);", description: "Regular ring (2px)", descriptionMs: "Cincin biasa (2px)" },
|
|
6497
|
-
{ value: "small", css: "box-shadow: 0 0 0 4px var(--ring-color);", description: "Small ring (4px)", descriptionMs: "Cincin kecil (4px)" },
|
|
6498
|
-
{ value: "medium", css: "box-shadow: 0 0 0 6px var(--ring-color);", description: "Medium ring (6px)", descriptionMs: "Cincin sederhana (6px)" },
|
|
6499
|
-
{ value: "big", css: "box-shadow: 0 0 0 8px var(--ring-color);", description: "Big ring (8px)", descriptionMs: "Cincin besar (8px)" }
|
|
6540
|
+
{ value: "thin", css: "box-shadow: var(--ring-inset) 0 0 0 1px var(--ss-ring-color);", description: "Thin ring (1px)", descriptionMs: "Cincin nipis (1px)" },
|
|
6541
|
+
{ value: "regular", css: "box-shadow: var(--ring-inset) 0 0 0 2px var(--ss-ring-color);", description: "Regular ring (2px)", descriptionMs: "Cincin biasa (2px)" },
|
|
6542
|
+
{ value: "small", css: "box-shadow: var(--ring-inset) 0 0 0 4px var(--ss-ring-color);", description: "Small ring (4px)", descriptionMs: "Cincin kecil (4px)" },
|
|
6543
|
+
{ value: "medium", css: "box-shadow: var(--ring-inset) 0 0 0 6px var(--ss-ring-color);", description: "Medium ring (6px)", descriptionMs: "Cincin sederhana (6px)" },
|
|
6544
|
+
{ value: "big", css: "box-shadow: var(--ring-inset) 0 0 0 8px var(--ss-ring-color);", description: "Big ring (8px)", descriptionMs: "Cincin besar (8px)" }
|
|
6500
6545
|
],
|
|
6501
6546
|
examples: [
|
|
6502
6547
|
{ code: '<button visual="focus-visible:ring:small ring-color:primary">Focus me</button>', description: "Focus ring on keyboard focus" },
|
|
@@ -6526,8 +6571,8 @@ video {
|
|
|
6526
6571
|
usesScale: "colors",
|
|
6527
6572
|
supportsArbitrary: true,
|
|
6528
6573
|
values: [
|
|
6529
|
-
{ value: "primary", css: "--ring-color: var(--c-primary);", description: "Primary ring color", descriptionMs: "Warna cincin utama" },
|
|
6530
|
-
{ value: "blue-500", css: "--ring-color: var(--c-blue-500);", description: "Blue ring color", descriptionMs: "Warna cincin biru" }
|
|
6574
|
+
{ value: "primary", css: "--ss-ring-color: var(--c-primary);", description: "Primary ring color", descriptionMs: "Warna cincin utama" },
|
|
6575
|
+
{ value: "blue-500", css: "--ss-ring-color: var(--c-blue-500);", description: "Blue ring color", descriptionMs: "Warna cincin biru" }
|
|
6531
6576
|
],
|
|
6532
6577
|
examples: [
|
|
6533
6578
|
{ code: '<button visual="ring:small ring-color:primary">Colored ring</button>', description: "Ring with custom color" }
|
|
@@ -6542,9 +6587,9 @@ video {
|
|
|
6542
6587
|
category: "visual",
|
|
6543
6588
|
supportsArbitrary: true,
|
|
6544
6589
|
values: [
|
|
6545
|
-
{ value: "0", css: "--ring-offset: 0px;", description: "No offset", descriptionMs: "Tiada ruang" },
|
|
6546
|
-
{ value: "2", css: "--ring-offset: 2px;", description: "2px offset", descriptionMs: "Ruang 2px" },
|
|
6547
|
-
{ value: "4", css: "--ring-offset: 4px;", description: "4px offset", descriptionMs: "Ruang 4px" }
|
|
6590
|
+
{ value: "0", css: "--ss-ring-offset-width: 0px;", description: "No offset", descriptionMs: "Tiada ruang" },
|
|
6591
|
+
{ value: "2", css: "--ss-ring-offset-width: 2px;", description: "2px offset", descriptionMs: "Ruang 2px" },
|
|
6592
|
+
{ value: "4", css: "--ss-ring-offset-width: 4px;", description: "4px offset", descriptionMs: "Ruang 4px" }
|
|
6548
6593
|
],
|
|
6549
6594
|
examples: [
|
|
6550
6595
|
{ code: '<button visual="ring:small ring-offset:2 ring-color:primary">With offset</button>', description: "Ring with offset" }
|
|
@@ -7178,7 +7223,8 @@ video {
|
|
|
7178
7223
|
}
|
|
7179
7224
|
css += " --ss-divide-x-reverse: 0;\n";
|
|
7180
7225
|
css += " --ss-divide-y-reverse: 0;\n";
|
|
7181
|
-
css += " --ring-inset:
|
|
7226
|
+
css += " --ring-inset: ;\n";
|
|
7227
|
+
css += " --ss-ring-color: var(--c-primary);\n";
|
|
7182
7228
|
css += "}\n\n";
|
|
7183
7229
|
return css;
|
|
7184
7230
|
}
|
|
@@ -7328,7 +7374,7 @@ video {
|
|
|
7328
7374
|
};
|
|
7329
7375
|
const resolvePositioningValue = (val, arb) => {
|
|
7330
7376
|
if (arb) return val;
|
|
7331
|
-
if (val === "0") return "0";
|
|
7377
|
+
if (!val || val === "0") return "0";
|
|
7332
7378
|
if (val.startsWith("-")) {
|
|
7333
7379
|
const positiveVal = val.substring(1);
|
|
7334
7380
|
if (positioningPercentages[positiveVal]) {
|
|
@@ -7528,8 +7574,8 @@ video {
|
|
|
7528
7574
|
if (isArbitrary) {
|
|
7529
7575
|
cssValue = value;
|
|
7530
7576
|
} else {
|
|
7531
|
-
const isNegative = value.startsWith("-");
|
|
7532
|
-
const cleanValue = isNegative ? value.substring(1) : value;
|
|
7577
|
+
const isNegative = value && value.startsWith("-");
|
|
7578
|
+
const cleanValue = isNegative ? value.substring(1) : value || "";
|
|
7533
7579
|
let baseValue;
|
|
7534
7580
|
if (cleanValue.startsWith("tw-")) {
|
|
7535
7581
|
const twValue = cleanValue.slice(3);
|
|
@@ -7596,7 +7642,7 @@ video {
|
|
|
7596
7642
|
},
|
|
7597
7643
|
// Background Image
|
|
7598
7644
|
"bg-image": () => {
|
|
7599
|
-
if (value === "none") return "background-image: none;";
|
|
7645
|
+
if (!value || value === "none") return "background-image: none;";
|
|
7600
7646
|
if (value.startsWith("gradient-to-")) {
|
|
7601
7647
|
const directionMap = {
|
|
7602
7648
|
"t": "to top",
|
|
@@ -7954,6 +8000,9 @@ video {
|
|
|
7954
8000
|
},
|
|
7955
8001
|
// Outline Color
|
|
7956
8002
|
"outline": () => {
|
|
8003
|
+
if (value === "none") {
|
|
8004
|
+
return "outline: none;";
|
|
8005
|
+
}
|
|
7957
8006
|
const cssValue = resolveColorValue(value, isArbitrary);
|
|
7958
8007
|
return `outline-color: ${cssValue};`;
|
|
7959
8008
|
},
|
|
@@ -7999,7 +8048,11 @@ video {
|
|
|
7999
8048
|
"big": "8px"
|
|
8000
8049
|
};
|
|
8001
8050
|
const width2 = isArbitrary ? value : ringPresets[value] || (parseInt(value) ? `${value}px` : `var(--s-${value})`);
|
|
8002
|
-
return `--ss-ring-width: ${width2}; box-shadow: var(--ring-inset) 0 0 0 calc(var(--ss-ring-width) + var(--ss-ring-offset-width, 0px)) var(--
|
|
8051
|
+
return `--ss-ring-width: ${width2}; box-shadow: var(--ring-inset) 0 0 0 calc(var(--ss-ring-width) + var(--ss-ring-offset-width, 0px)) var(--ss-ring-color);`;
|
|
8052
|
+
},
|
|
8053
|
+
// Ring Inset
|
|
8054
|
+
"ring-inset": () => {
|
|
8055
|
+
return "--ring-inset: inset;";
|
|
8003
8056
|
},
|
|
8004
8057
|
// Box shadow
|
|
8005
8058
|
"shadow": () => {
|
|
@@ -8781,69 +8834,122 @@ video {
|
|
|
8781
8834
|
const generator = rules[property];
|
|
8782
8835
|
return generator ? generator() : "";
|
|
8783
8836
|
}
|
|
8784
|
-
function
|
|
8785
|
-
|
|
8786
|
-
|
|
8787
|
-
switch (attrType) {
|
|
8788
|
-
case "layout":
|
|
8789
|
-
cssDeclaration = generateLayoutRule(token, config);
|
|
8790
|
-
break;
|
|
8791
|
-
case "space":
|
|
8792
|
-
cssDeclaration = generateSpaceRule(token, config);
|
|
8793
|
-
break;
|
|
8794
|
-
case "visual":
|
|
8795
|
-
cssDeclaration = generateVisualRule(token, config);
|
|
8796
|
-
break;
|
|
8797
|
-
}
|
|
8798
|
-
if (!cssDeclaration) return "";
|
|
8799
|
-
const isDivide = raw.startsWith("divide");
|
|
8800
|
-
let selector = "";
|
|
8801
|
-
if (isDivide) {
|
|
8802
|
-
selector = `[${attrType}~="${raw}"] > :not([hidden]) ~ :not([hidden])`;
|
|
8803
|
-
} else {
|
|
8804
|
-
selector = `[${attrType}~="${raw}"]`;
|
|
8837
|
+
function isValidCSSRule(declaration) {
|
|
8838
|
+
if (!declaration || typeof declaration !== "string") {
|
|
8839
|
+
return false;
|
|
8805
8840
|
}
|
|
8806
|
-
|
|
8841
|
+
declaration = declaration.trim();
|
|
8842
|
+
if (!declaration) return false;
|
|
8843
|
+
if (!declaration.endsWith(";")) return false;
|
|
8844
|
+
const parts = declaration.substring(0, declaration.length - 1).split(":");
|
|
8845
|
+
if (parts.length < 2) return false;
|
|
8846
|
+
const property = parts[0].trim();
|
|
8847
|
+
const value = parts.slice(1).join(":").trim();
|
|
8848
|
+
if (!property || !value) return false;
|
|
8849
|
+
return true;
|
|
8850
|
+
}
|
|
8851
|
+
function generateRule(token, config, skipDarkWrapper = false, interactIds = /* @__PURE__ */ new Set()) {
|
|
8852
|
+
try {
|
|
8853
|
+
if (!token || typeof token !== "object") {
|
|
8854
|
+
console.warn("[SenangStart] Invalid token object:", token);
|
|
8855
|
+
return "";
|
|
8856
|
+
}
|
|
8857
|
+
const { raw, attrType, breakpoint, state } = token;
|
|
8858
|
+
if (!attrType || typeof attrType !== "string") {
|
|
8859
|
+
console.warn("[SenangStart] Invalid token attrType:", attrType);
|
|
8860
|
+
return "";
|
|
8861
|
+
}
|
|
8862
|
+
if (!raw || typeof raw !== "string") {
|
|
8863
|
+
console.warn("[SenangStart] Invalid token raw:", raw);
|
|
8864
|
+
return "";
|
|
8865
|
+
}
|
|
8866
|
+
let cssDeclaration = "";
|
|
8867
|
+
switch (attrType) {
|
|
8868
|
+
case "layout":
|
|
8869
|
+
try {
|
|
8870
|
+
cssDeclaration = generateLayoutRule(token, config);
|
|
8871
|
+
} catch (e) {
|
|
8872
|
+
console.warn(`[SenangStart] Error generating layout rule for "${raw}": ${e.message}`);
|
|
8873
|
+
return "";
|
|
8874
|
+
}
|
|
8875
|
+
break;
|
|
8876
|
+
case "space":
|
|
8877
|
+
try {
|
|
8878
|
+
cssDeclaration = generateSpaceRule(token, config);
|
|
8879
|
+
} catch (e) {
|
|
8880
|
+
console.warn(`[SenangStart] Error generating space rule for "${raw}": ${e.message}`);
|
|
8881
|
+
return "";
|
|
8882
|
+
}
|
|
8883
|
+
break;
|
|
8884
|
+
case "visual":
|
|
8885
|
+
try {
|
|
8886
|
+
cssDeclaration = generateVisualRule(token, config);
|
|
8887
|
+
} catch (e) {
|
|
8888
|
+
console.warn(`[SenangStart] Error generating visual rule for "${raw}": ${e.message}`);
|
|
8889
|
+
return "";
|
|
8890
|
+
}
|
|
8891
|
+
break;
|
|
8892
|
+
default:
|
|
8893
|
+
console.warn(`[SenangStart] Unknown attrType: ${attrType}`);
|
|
8894
|
+
return "";
|
|
8895
|
+
}
|
|
8896
|
+
if (!cssDeclaration) return "";
|
|
8897
|
+
if (!isValidCSSRule(cssDeclaration)) {
|
|
8898
|
+
console.warn(`[SenangStart] Invalid CSS rule generated for "${raw}": ${cssDeclaration}`);
|
|
8899
|
+
return "";
|
|
8900
|
+
}
|
|
8901
|
+
const isDivide = raw && raw.startsWith("divide");
|
|
8902
|
+
let selector = "";
|
|
8807
8903
|
if (isDivide) {
|
|
8808
|
-
selector = `[${attrType}~="${raw}"] > :not([hidden]) ~ :not([hidden])
|
|
8904
|
+
selector = `[${attrType}~="${raw}"] > :not([hidden]) ~ :not([hidden])`;
|
|
8809
8905
|
} else {
|
|
8810
|
-
|
|
8811
|
-
|
|
8812
|
-
|
|
8813
|
-
|
|
8814
|
-
|
|
8906
|
+
selector = `[${attrType}~="${raw}"]`;
|
|
8907
|
+
}
|
|
8908
|
+
if (state && state !== "dark") {
|
|
8909
|
+
if (isDivide) {
|
|
8910
|
+
selector = `[${attrType}~="${raw}"] > :not([hidden]) ~ :not([hidden]):${state}`;
|
|
8911
|
+
} else {
|
|
8912
|
+
const getStateSelector = (s) => {
|
|
8913
|
+
const map = {
|
|
8914
|
+
"expanded": '[aria-expanded="true"]',
|
|
8915
|
+
"selected": '[aria-selected="true"]',
|
|
8916
|
+
"disabled": ":disabled"
|
|
8917
|
+
};
|
|
8918
|
+
return map[s] || `:${s}`;
|
|
8815
8919
|
};
|
|
8816
|
-
|
|
8817
|
-
|
|
8818
|
-
|
|
8819
|
-
|
|
8820
|
-
|
|
8821
|
-
|
|
8822
|
-
|
|
8823
|
-
|
|
8824
|
-
|
|
8825
|
-
|
|
8826
|
-
|
|
8827
|
-
|
|
8828
|
-
|
|
8829
|
-
|
|
8830
|
-
|
|
8831
|
-
|
|
8832
|
-
|
|
8833
|
-
|
|
8834
|
-
|
|
8835
|
-
|
|
8836
|
-
|
|
8837
|
-
|
|
8838
|
-
selectors.push(peerSelector);
|
|
8920
|
+
const selectors = [];
|
|
8921
|
+
selectors.push(`${selector}${getStateSelector(state)}`);
|
|
8922
|
+
const groupTriggers = {
|
|
8923
|
+
"hover": "hoverable",
|
|
8924
|
+
"focus": "focusable",
|
|
8925
|
+
"focus-visible": "focusable",
|
|
8926
|
+
"active": "pressable",
|
|
8927
|
+
"expanded": "expandable",
|
|
8928
|
+
"selected": "selectable"
|
|
8929
|
+
};
|
|
8930
|
+
if (groupTriggers[state]) {
|
|
8931
|
+
const parentAttr = groupTriggers[state];
|
|
8932
|
+
let triggerState = state;
|
|
8933
|
+
if (state === "focus" || state === "focus-visible") triggerState = "focus-within";
|
|
8934
|
+
const triggerSelector = getStateSelector(triggerState);
|
|
8935
|
+
const groupSelector = `[layout~="${parentAttr}"]:not([layout~="disabled"])${triggerSelector} ${selector}`;
|
|
8936
|
+
selectors.push(groupSelector);
|
|
8937
|
+
if (interactIds && interactIds.size > 0) {
|
|
8938
|
+
for (const id of interactIds) {
|
|
8939
|
+
const peerSelector = `[interact~="${id}"]:not([layout~="disabled"])${triggerSelector} ~ [listens~="${id}"]${selector}`;
|
|
8940
|
+
selectors.push(peerSelector);
|
|
8941
|
+
}
|
|
8839
8942
|
}
|
|
8840
8943
|
}
|
|
8944
|
+
selector = selectors.join(",\n");
|
|
8841
8945
|
}
|
|
8842
|
-
selector = selectors.join(",\n");
|
|
8843
8946
|
}
|
|
8844
|
-
|
|
8845
|
-
return `${selector} { ${cssDeclaration} }
|
|
8947
|
+
return `${selector} { ${cssDeclaration} }
|
|
8846
8948
|
`;
|
|
8949
|
+
} catch (e) {
|
|
8950
|
+
console.warn(`[SenangStart] Error in generateRule: ${e.message}`);
|
|
8951
|
+
return "";
|
|
8952
|
+
}
|
|
8847
8953
|
}
|
|
8848
8954
|
function getDarkModeSelector(config) {
|
|
8849
8955
|
const darkMode = config.darkMode || "media";
|
|
@@ -8855,13 +8961,33 @@ video {
|
|
|
8855
8961
|
}
|
|
8856
8962
|
return null;
|
|
8857
8963
|
}
|
|
8858
|
-
function
|
|
8859
|
-
|
|
8860
|
-
|
|
8861
|
-
|
|
8862
|
-
|
|
8863
|
-
|
|
8864
|
-
|
|
8964
|
+
function generateCSSWithErrors(tokens, config) {
|
|
8965
|
+
const errors = [];
|
|
8966
|
+
try {
|
|
8967
|
+
let css = "";
|
|
8968
|
+
if (!config || typeof config !== "object") {
|
|
8969
|
+
errors.push({ type: "config", message: "Invalid config provided" });
|
|
8970
|
+
return { css: "", errors };
|
|
8971
|
+
}
|
|
8972
|
+
if (!Array.isArray(tokens)) {
|
|
8973
|
+
errors.push({ type: "tokens", message: "Invalid tokens provided" });
|
|
8974
|
+
return { css: "", errors };
|
|
8975
|
+
}
|
|
8976
|
+
try {
|
|
8977
|
+
css += generateCSSVariables(config);
|
|
8978
|
+
} catch (e) {
|
|
8979
|
+
errors.push({ type: "variables", message: e.message });
|
|
8980
|
+
console.warn(`[SenangStart] Error generating CSS variables: ${e.message}`);
|
|
8981
|
+
}
|
|
8982
|
+
if (config.preflight !== false) {
|
|
8983
|
+
try {
|
|
8984
|
+
css += generatePreflight(config);
|
|
8985
|
+
} catch (e) {
|
|
8986
|
+
errors.push({ type: "preflight", message: e.message });
|
|
8987
|
+
console.warn(`[SenangStart] Error generating preflight: ${e.message}`);
|
|
8988
|
+
}
|
|
8989
|
+
}
|
|
8990
|
+
css += `/* SenangStart CSS - Animation Keyframes */
|
|
8865
8991
|
@keyframes spin {
|
|
8866
8992
|
to { transform: rotate(360deg); }
|
|
8867
8993
|
}
|
|
@@ -8878,93 +9004,178 @@ video {
|
|
|
8878
9004
|
|
|
8879
9005
|
/* SenangStart CSS - Utility Classes */
|
|
8880
9006
|
`;
|
|
8881
|
-
|
|
8882
|
-
|
|
8883
|
-
|
|
8884
|
-
|
|
8885
|
-
|
|
8886
|
-
|
|
8887
|
-
|
|
8888
|
-
for (const token of tokens) {
|
|
8889
|
-
if (token.state === "dark") {
|
|
8890
|
-
darkTokens.push(token);
|
|
8891
|
-
} else if (token.breakpoint) {
|
|
8892
|
-
if (!breakpointTokens[token.breakpoint]) {
|
|
8893
|
-
breakpointTokens[token.breakpoint] = [];
|
|
9007
|
+
const baseTokens = [];
|
|
9008
|
+
const darkTokens = [];
|
|
9009
|
+
const breakpointTokens = {};
|
|
9010
|
+
const { screens } = config.theme || {};
|
|
9011
|
+
if (screens && typeof screens === "object") {
|
|
9012
|
+
for (const bp of Object.keys(screens)) {
|
|
9013
|
+
breakpointTokens[bp] = [];
|
|
8894
9014
|
}
|
|
8895
|
-
breakpointTokens[token.breakpoint].push(token);
|
|
8896
|
-
} else {
|
|
8897
|
-
baseTokens.push(token);
|
|
8898
9015
|
}
|
|
8899
|
-
|
|
8900
|
-
|
|
8901
|
-
|
|
8902
|
-
|
|
8903
|
-
|
|
9016
|
+
for (const token of tokens) {
|
|
9017
|
+
try {
|
|
9018
|
+
if (token && typeof token === "object") {
|
|
9019
|
+
if (token.state === "dark") {
|
|
9020
|
+
darkTokens.push(token);
|
|
9021
|
+
} else if (token.breakpoint) {
|
|
9022
|
+
if (!breakpointTokens[token.breakpoint]) {
|
|
9023
|
+
breakpointTokens[token.breakpoint] = [];
|
|
9024
|
+
}
|
|
9025
|
+
breakpointTokens[token.breakpoint].push(token);
|
|
9026
|
+
} else {
|
|
9027
|
+
baseTokens.push(token);
|
|
9028
|
+
}
|
|
9029
|
+
} else {
|
|
9030
|
+
errors.push({ type: "token_format", token, message: "Token is not an object" });
|
|
9031
|
+
}
|
|
9032
|
+
} catch (e) {
|
|
9033
|
+
errors.push({ type: "token_processing", token: token?.raw, message: e.message });
|
|
9034
|
+
console.warn(`[SenangStart] Error processing token: ${e.message}`);
|
|
9035
|
+
}
|
|
8904
9036
|
}
|
|
8905
|
-
|
|
8906
|
-
|
|
8907
|
-
|
|
8908
|
-
|
|
8909
|
-
|
|
8910
|
-
|
|
8911
|
-
|
|
9037
|
+
const interactIds = /* @__PURE__ */ new Set();
|
|
9038
|
+
for (const token of tokens) {
|
|
9039
|
+
try {
|
|
9040
|
+
if (token && token.attrType === "interact" && token.raw) {
|
|
9041
|
+
interactIds.add(token.raw);
|
|
9042
|
+
}
|
|
9043
|
+
} catch (e) {
|
|
9044
|
+
errors.push({ type: "interact_collection", token: token?.raw, message: e.message });
|
|
9045
|
+
console.warn(`[SenangStart] Error collecting interact IDs: ${e.message}`);
|
|
8912
9046
|
}
|
|
8913
|
-
baseDisplayTokens.get(token.attrType).add(token.raw);
|
|
8914
9047
|
}
|
|
8915
|
-
|
|
8916
|
-
|
|
8917
|
-
|
|
8918
|
-
|
|
8919
|
-
|
|
8920
|
-
|
|
8921
|
-
|
|
8922
|
-
|
|
9048
|
+
const displayProps = ["flex", "grid", "inline-flex", "inline-grid", "block", "inline", "hidden", "contents"];
|
|
9049
|
+
const baseDisplayTokens = /* @__PURE__ */ new Map();
|
|
9050
|
+
for (const token of baseTokens) {
|
|
9051
|
+
try {
|
|
9052
|
+
if (token.attrType && displayProps.includes(token.property)) {
|
|
9053
|
+
if (!baseDisplayTokens.has(token.attrType)) {
|
|
9054
|
+
baseDisplayTokens.set(token.attrType, /* @__PURE__ */ new Set());
|
|
9055
|
+
}
|
|
9056
|
+
baseDisplayTokens.get(token.attrType).add(token.raw);
|
|
9057
|
+
}
|
|
9058
|
+
} catch (e) {
|
|
9059
|
+
errors.push({ type: "display_track", token: token?.raw, message: e.message });
|
|
9060
|
+
console.warn(`[SenangStart] Error tracking display properties: ${e.message}`);
|
|
9061
|
+
}
|
|
9062
|
+
}
|
|
9063
|
+
for (const token of baseTokens) {
|
|
9064
|
+
try {
|
|
9065
|
+
const rule = generateRule(token, config, false, interactIds);
|
|
9066
|
+
if (rule) {
|
|
9067
|
+
css += rule;
|
|
9068
|
+
} else {
|
|
9069
|
+
errors.push({ type: "rule_generation", token: token.raw, message: "No rule generated" });
|
|
9070
|
+
}
|
|
9071
|
+
} catch (e) {
|
|
9072
|
+
errors.push({ type: "rule_generation", token: token.raw, message: e.message });
|
|
9073
|
+
console.warn(`[SenangStart] Error generating base rule: ${e.message}`);
|
|
9074
|
+
}
|
|
9075
|
+
}
|
|
9076
|
+
for (const [bp, bpTokens] of Object.entries(breakpointTokens)) {
|
|
9077
|
+
try {
|
|
9078
|
+
if (bpTokens.length > 0) {
|
|
9079
|
+
const screenWidth = screens && screens[bp] ? screens[bp] : bp;
|
|
9080
|
+
css += `
|
|
9081
|
+
@media (min-width: ${screenWidth}) {
|
|
8923
9082
|
`;
|
|
8924
|
-
|
|
8925
|
-
|
|
8926
|
-
|
|
8927
|
-
|
|
8928
|
-
|
|
8929
|
-
|
|
8930
|
-
|
|
8931
|
-
|
|
9083
|
+
const processedResetSelectors = /* @__PURE__ */ new Set();
|
|
9084
|
+
for (const bpToken of bpTokens) {
|
|
9085
|
+
try {
|
|
9086
|
+
if (bpToken.attrType && displayProps.includes(bpToken.property)) {
|
|
9087
|
+
if (baseDisplayTokens.has(bpToken.attrType)) {
|
|
9088
|
+
const baseDisplays = baseDisplayTokens.get(bpToken.attrType);
|
|
9089
|
+
if (baseDisplays.size > 0 && !baseDisplays.has(bpToken.raw) && !processedResetSelectors.has(bpToken.raw)) {
|
|
9090
|
+
const selector = `[${bpToken.attrType}~="${bpToken.raw}"]`;
|
|
9091
|
+
css += ` ${selector} { display: revert-layer; }
|
|
8932
9092
|
`;
|
|
8933
|
-
|
|
9093
|
+
processedResetSelectors.add(bpToken.raw);
|
|
9094
|
+
}
|
|
9095
|
+
}
|
|
9096
|
+
}
|
|
9097
|
+
} catch (e) {
|
|
9098
|
+
errors.push({ type: "display_reset", token: bpToken.raw, message: e.message });
|
|
9099
|
+
console.warn(`[SenangStart] Error generating display reset: ${e.message}`);
|
|
9100
|
+
}
|
|
9101
|
+
}
|
|
9102
|
+
for (const token of bpTokens) {
|
|
9103
|
+
try {
|
|
9104
|
+
const rule = generateRule(token, config, false, interactIds);
|
|
9105
|
+
if (rule) {
|
|
9106
|
+
css += " " + rule;
|
|
9107
|
+
} else {
|
|
9108
|
+
errors.push({ type: "responsive_rule", token: token.raw, message: "No rule generated" });
|
|
9109
|
+
}
|
|
9110
|
+
} catch (e) {
|
|
9111
|
+
errors.push({ type: "responsive_rule", token: token.raw, message: e.message });
|
|
9112
|
+
console.warn(`[SenangStart] Error generating responsive rule: ${e.message}`);
|
|
8934
9113
|
}
|
|
8935
9114
|
}
|
|
9115
|
+
css += "}\n";
|
|
8936
9116
|
}
|
|
9117
|
+
} catch (e) {
|
|
9118
|
+
errors.push({ type: "breakpoint_generation", message: `Error generating breakpoint ${bp}: ${e.message}` });
|
|
9119
|
+
console.warn(`[SenangStart] Error generating breakpoint ${bp}: ${e.message}`);
|
|
8937
9120
|
}
|
|
8938
|
-
for (const token of bpTokens) {
|
|
8939
|
-
css += " " + generateRule(token, config, false, interactIds);
|
|
8940
|
-
}
|
|
8941
|
-
css += "}\n";
|
|
8942
9121
|
}
|
|
8943
|
-
|
|
8944
|
-
|
|
8945
|
-
|
|
8946
|
-
|
|
8947
|
-
|
|
8948
|
-
|
|
9122
|
+
if (darkTokens.length > 0) {
|
|
9123
|
+
try {
|
|
9124
|
+
const darkMode = config.darkMode || "media";
|
|
9125
|
+
const darkSelector = getDarkModeSelector(config);
|
|
9126
|
+
if (darkMode === "media") {
|
|
9127
|
+
css += `
|
|
8949
9128
|
/* Dark Mode (prefers-color-scheme) */
|
|
8950
9129
|
`;
|
|
8951
|
-
|
|
9130
|
+
css += `@media (prefers-color-scheme: dark) {
|
|
8952
9131
|
`;
|
|
8953
|
-
|
|
8954
|
-
|
|
8955
|
-
|
|
8956
|
-
|
|
8957
|
-
|
|
8958
|
-
|
|
9132
|
+
for (const token of darkTokens) {
|
|
9133
|
+
try {
|
|
9134
|
+
const rule = generateRule(token, config, true, interactIds);
|
|
9135
|
+
if (rule) {
|
|
9136
|
+
css += " " + rule;
|
|
9137
|
+
} else {
|
|
9138
|
+
errors.push({ type: "dark_rule", token: token.raw, message: "No rule generated" });
|
|
9139
|
+
}
|
|
9140
|
+
} catch (e) {
|
|
9141
|
+
errors.push({ type: "dark_rule", token: token.raw, message: e.message });
|
|
9142
|
+
console.warn(`[SenangStart] Error generating dark rule (media): ${e.message}`);
|
|
9143
|
+
}
|
|
9144
|
+
}
|
|
9145
|
+
css += "}\n";
|
|
9146
|
+
} else {
|
|
9147
|
+
css += `
|
|
8959
9148
|
/* Dark Mode (${darkSelector}) */
|
|
8960
9149
|
`;
|
|
8961
|
-
|
|
8962
|
-
|
|
8963
|
-
|
|
8964
|
-
|
|
9150
|
+
for (const token of darkTokens) {
|
|
9151
|
+
try {
|
|
9152
|
+
const baseRule = generateRule(token, config, true, interactIds);
|
|
9153
|
+
if (baseRule) {
|
|
9154
|
+
const wrappedRule = baseRule.replace(/^(\[[^\]]+\])/, `${darkSelector} $1`);
|
|
9155
|
+
css += wrappedRule;
|
|
9156
|
+
} else {
|
|
9157
|
+
errors.push({ type: "dark_rule", token: token.raw, message: "No rule generated" });
|
|
9158
|
+
}
|
|
9159
|
+
} catch (e) {
|
|
9160
|
+
errors.push({ type: "dark_rule", token: token.raw, message: e.message });
|
|
9161
|
+
console.warn(`[SenangStart] Error generating dark rule (selector): ${e.message}`);
|
|
9162
|
+
}
|
|
9163
|
+
}
|
|
9164
|
+
}
|
|
9165
|
+
} catch (e) {
|
|
9166
|
+
errors.push({ type: "dark_mode_generation", message: e.message });
|
|
9167
|
+
console.warn(`[SenangStart] Error generating dark mode rules: ${e.message}`);
|
|
8965
9168
|
}
|
|
8966
9169
|
}
|
|
9170
|
+
return { css, errors };
|
|
9171
|
+
} catch (e) {
|
|
9172
|
+
errors.push({ type: "fatal", message: e.message });
|
|
9173
|
+
console.error(`[SenangStart] Fatal error in generateCSSWithErrors: ${e.message}`);
|
|
9174
|
+
return { css: "", errors };
|
|
8967
9175
|
}
|
|
9176
|
+
}
|
|
9177
|
+
function generateCSS(tokens, config) {
|
|
9178
|
+
const { css } = generateCSSWithErrors(tokens, config);
|
|
8968
9179
|
return css;
|
|
8969
9180
|
}
|
|
8970
9181
|
|