@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.
Files changed (39) hide show
  1. package/.agent/skills/add-utility/SKILL.md +65 -0
  2. package/.agent/workflows/add-utility.md +2 -0
  3. package/.agent/workflows/build.md +2 -0
  4. package/.agent/workflows/dev.md +2 -0
  5. package/AGENTS.md +30 -0
  6. package/dist/senangstart-css.js +362 -151
  7. package/dist/senangstart-css.min.js +175 -174
  8. package/dist/senangstart-tw.js +4 -4
  9. package/dist/senangstart-tw.min.js +1 -1
  10. package/docs/ms/reference/visual/ring-color.md +2 -2
  11. package/docs/ms/reference/visual/ring-offset.md +3 -3
  12. package/docs/ms/reference/visual/ring.md +5 -5
  13. package/docs/public/assets/senangstart-css.min.js +175 -174
  14. package/docs/public/llms.txt +10 -10
  15. package/docs/reference/visual/ring-color.md +2 -2
  16. package/docs/reference/visual/ring-offset.md +3 -3
  17. package/docs/reference/visual/ring.md +5 -5
  18. package/package.json +1 -1
  19. package/src/cdn/tw-conversion-engine.js +4 -4
  20. package/src/cli/commands/build.js +42 -14
  21. package/src/cli/commands/dev.js +157 -93
  22. package/src/compiler/generators/css.js +371 -199
  23. package/src/compiler/tokenizer.js +25 -23
  24. package/src/core/tokenizer-core.js +46 -19
  25. package/src/definitions/visual-borders.js +10 -10
  26. package/src/utils/common.js +456 -39
  27. package/src/utils/node-io.js +82 -0
  28. package/tests/integration/dev-recovery.test.js +231 -0
  29. package/tests/unit/cli/memory-limits.test.js +169 -0
  30. package/tests/unit/compiler/css-generation-error-handling.test.js +204 -0
  31. package/tests/unit/compiler/generators/css-errors.test.js +102 -0
  32. package/tests/unit/convert-tailwind.test.js +518 -442
  33. package/tests/unit/utils/common.test.js +376 -26
  34. package/tests/unit/utils/file-timeout.test.js +154 -0
  35. package/tests/unit/utils/theme-validation.test.js +181 -0
  36. package/tests/unit/compiler/generators/css.coverage.test.js +0 -833
  37. package/tests/unit/convert-tailwind.cli.test.js +0 -95
  38. package/tests/unit/security.test.js +0 -206
  39. /package/tests/unit/{convert-tailwind.coverage.test.js → convert-tailwind-edgecases.test.js} +0 -0
@@ -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
- const dangerousChars = /[;]/g;
44
- sanitized = sanitized.replace(dangerousChars, "_");
45
- const atRules = /@import|@charset|@namespace|@supports|@keyframes/gi;
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
- const expression = /expression\s*\(/gi;
48
- sanitized = sanitized.replace(expression, "");
49
- const dangerousUrls = /(url\s*\(\s*['"]?)(javascript:|data:)([^)]*\))/gi;
50
- sanitized = sanitized.replace(dangerousUrls, "$1about:blank$3");
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: 0 0 0 0;\n";
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(--c-primary);`;
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 generateRule(token, config, skipDarkWrapper = false, interactIds = /* @__PURE__ */ new Set()) {
8785
- const { raw, attrType, breakpoint, state } = token;
8786
- let cssDeclaration = "";
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
- if (state && state !== "dark") {
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]):${state}`;
8904
+ selector = `[${attrType}~="${raw}"] > :not([hidden]) ~ :not([hidden])`;
8809
8905
  } else {
8810
- const getStateSelector = (s) => {
8811
- const map = {
8812
- "expanded": '[aria-expanded="true"]',
8813
- "selected": '[aria-selected="true"]',
8814
- "disabled": ":disabled"
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
- return map[s] || `:${s}`;
8817
- };
8818
- const selectors = [];
8819
- selectors.push(`${selector}${getStateSelector(state)}`);
8820
- const groupTriggers = {
8821
- "hover": "hoverable",
8822
- "focus": "focusable",
8823
- "focus-visible": "focusable",
8824
- "active": "pressable",
8825
- "expanded": "expandable",
8826
- "selected": "selectable"
8827
- };
8828
- if (groupTriggers[state]) {
8829
- const parentAttr = groupTriggers[state];
8830
- let triggerState = state;
8831
- if (state === "focus" || state === "focus-visible") triggerState = "focus-within";
8832
- const triggerSelector = getStateSelector(triggerState);
8833
- const groupSelector = `[layout~="${parentAttr}"]:not([layout~="disabled"])${triggerSelector} ${selector}`;
8834
- selectors.push(groupSelector);
8835
- if (interactIds && interactIds.size > 0) {
8836
- for (const id of interactIds) {
8837
- const peerSelector = `[interact~="${id}"]:not([layout~="disabled"])${triggerSelector} ~ [listens~="${id}"]${selector}`;
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 generateCSS(tokens, config) {
8859
- let css = "";
8860
- css += generateCSSVariables(config);
8861
- if (config.preflight !== false) {
8862
- css += generatePreflight(config);
8863
- }
8864
- css += `/* SenangStart CSS - Animation Keyframes */
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
- const baseTokens = [];
8882
- const darkTokens = [];
8883
- const breakpointTokens = {};
8884
- const { screens } = config.theme;
8885
- for (const bp of Object.keys(screens)) {
8886
- breakpointTokens[bp] = [];
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
- const interactIds = /* @__PURE__ */ new Set();
8901
- for (const token of tokens) {
8902
- if (token.attrType === "interact") {
8903
- interactIds.add(token.raw);
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
- const displayProps = ["flex", "grid", "inline-flex", "inline-grid", "block", "inline", "hidden", "contents"];
8907
- const baseDisplayTokens = /* @__PURE__ */ new Map();
8908
- for (const token of baseTokens) {
8909
- if (token.attrType && displayProps.includes(token.property)) {
8910
- if (!baseDisplayTokens.has(token.attrType)) {
8911
- baseDisplayTokens.set(token.attrType, /* @__PURE__ */ new Set());
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
- for (const token of baseTokens) {
8917
- css += generateRule(token, config, false, interactIds);
8918
- }
8919
- for (const [bp, bpTokens] of Object.entries(breakpointTokens)) {
8920
- if (bpTokens.length > 0) {
8921
- css += `
8922
- @media (min-width: ${screens[bp]}) {
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
- const processedResetSelectors = /* @__PURE__ */ new Set();
8925
- for (const bpToken of bpTokens) {
8926
- if (bpToken.attrType && displayProps.includes(bpToken.property)) {
8927
- if (baseDisplayTokens.has(bpToken.attrType)) {
8928
- const baseDisplays = baseDisplayTokens.get(bpToken.attrType);
8929
- if (baseDisplays.size > 0 && !baseDisplays.has(bpToken.raw) && !processedResetSelectors.has(bpToken.raw)) {
8930
- const selector = `[${bpToken.attrType}~="${bpToken.raw}"]`;
8931
- css += ` ${selector} { display: revert-layer; }
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
- processedResetSelectors.add(bpToken.raw);
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
- if (darkTokens.length > 0) {
8945
- const darkMode = config.darkMode || "media";
8946
- const darkSelector = getDarkModeSelector(config);
8947
- if (darkMode === "media") {
8948
- css += `
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
- css += `@media (prefers-color-scheme: dark) {
9130
+ css += `@media (prefers-color-scheme: dark) {
8952
9131
  `;
8953
- for (const token of darkTokens) {
8954
- css += " " + generateRule(token, config, true, interactIds);
8955
- }
8956
- css += "}\n";
8957
- } else {
8958
- css += `
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
- for (const token of darkTokens) {
8962
- const baseRule = generateRule(token, config, true, interactIds);
8963
- const wrappedRule = baseRule.replace(/^(\[[^\]]+\])/, `${darkSelector} $1`);
8964
- css += wrappedRule;
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