chaincss 2.2.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/compiler/accessibility-engine.d.ts +57 -0
- package/dist/compiler/constraint-solver.d.ts +85 -0
- package/dist/compiler/intent-api.d.ts +73 -0
- package/dist/compiler/layout-intelligence.d.ts +71 -0
- package/dist/compiler/pass-manager.d.ts +157 -0
- package/dist/compiler/pattern-learner.d.ts +112 -0
- package/dist/compiler/responsive-inference.d.ts +63 -0
- package/dist/compiler/semantic-tokens.d.ts +57 -0
- package/dist/compiler/source-optimizer.d.ts +109 -0
- package/dist/compiler/style-ir.d.ts +183 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +3475 -0
- package/package.json +1 -1
- package/src/compiler/accessibility-engine.ts +502 -0
- package/src/compiler/constraint-solver.ts +407 -0
- package/src/compiler/intent-api.ts +505 -0
- package/src/compiler/layout-intelligence.ts +697 -0
- package/src/compiler/pass-manager.ts +657 -0
- package/src/compiler/pattern-learner.ts +398 -0
- package/src/compiler/responsive-inference.ts +415 -0
- package/src/compiler/semantic-tokens.ts +468 -0
- package/src/compiler/source-optimizer.ts +541 -0
- package/src/compiler/style-ir.ts +495 -0
- package/src/index.ts +175 -0
- package/ROADMAP.md +0 -31
package/dist/index.js
CHANGED
|
@@ -7640,6 +7640,3417 @@ var scrollTimeline = {
|
|
|
7640
7640
|
getPresets: getScrollPresets
|
|
7641
7641
|
};
|
|
7642
7642
|
|
|
7643
|
+
// src/compiler/style-ir.ts
|
|
7644
|
+
var idCounter = 0;
|
|
7645
|
+
function nextId(prefix = "ir") {
|
|
7646
|
+
return prefix + "-" + (idCounter++).toString(36) + "-" + Date.now().toString(36);
|
|
7647
|
+
}
|
|
7648
|
+
function resetIdCounter() {
|
|
7649
|
+
idCounter = 0;
|
|
7650
|
+
}
|
|
7651
|
+
function record(pass, action, previous, reason) {
|
|
7652
|
+
return { pass, action, timestamp: Date.now(), previous, reason };
|
|
7653
|
+
}
|
|
7654
|
+
function createDeclaration(property, value, source, meta = {}) {
|
|
7655
|
+
return {
|
|
7656
|
+
id: nextId("decl"),
|
|
7657
|
+
property,
|
|
7658
|
+
value,
|
|
7659
|
+
source,
|
|
7660
|
+
history: [record("parser", "created", void 0, "Parsed from StyleDefinition")],
|
|
7661
|
+
meta
|
|
7662
|
+
};
|
|
7663
|
+
}
|
|
7664
|
+
function createRule(selector, source) {
|
|
7665
|
+
return {
|
|
7666
|
+
id: nextId("rule"),
|
|
7667
|
+
selector,
|
|
7668
|
+
declarations: [],
|
|
7669
|
+
pseudoClasses: [],
|
|
7670
|
+
atRules: [],
|
|
7671
|
+
nestedRules: [],
|
|
7672
|
+
conditions: [],
|
|
7673
|
+
isDead: false,
|
|
7674
|
+
specificity: 0,
|
|
7675
|
+
hash: "",
|
|
7676
|
+
source: source || {},
|
|
7677
|
+
history: [record("parser", "created", void 0, "Parsed from StyleDefinition")],
|
|
7678
|
+
meta: {}
|
|
7679
|
+
};
|
|
7680
|
+
}
|
|
7681
|
+
function createIR(sourceFiles = []) {
|
|
7682
|
+
return {
|
|
7683
|
+
id: nextId("ir"),
|
|
7684
|
+
rules: [],
|
|
7685
|
+
diagnostics: [],
|
|
7686
|
+
meta: {
|
|
7687
|
+
version: "1.0.0",
|
|
7688
|
+
createdAt: Date.now(),
|
|
7689
|
+
sourceFiles,
|
|
7690
|
+
passCount: 0,
|
|
7691
|
+
passes: []
|
|
7692
|
+
}
|
|
7693
|
+
};
|
|
7694
|
+
}
|
|
7695
|
+
function parseIR(styles, sourceFile) {
|
|
7696
|
+
const ir = createIR(sourceFile ? [sourceFile] : []);
|
|
7697
|
+
for (const [componentName, styleDef] of Object.entries(styles)) {
|
|
7698
|
+
if (!styleDef || typeof styleDef !== "object") continue;
|
|
7699
|
+
const selectors = Array.isArray(styleDef.selectors) ? styleDef.selectors : styleDef.selector ? [styleDef.selector] : ["." + componentName];
|
|
7700
|
+
for (const selector of selectors) {
|
|
7701
|
+
const rule = createRule(selector, {
|
|
7702
|
+
file: sourceFile,
|
|
7703
|
+
component: componentName
|
|
7704
|
+
});
|
|
7705
|
+
for (const [prop, value] of Object.entries(styleDef)) {
|
|
7706
|
+
if (prop === "selectors" || prop === "selector" || prop.startsWith("_")) continue;
|
|
7707
|
+
if (prop === "hover" || prop === "atRules" || prop === "nestedRules" || prop === "themes") continue;
|
|
7708
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
7709
|
+
rule.declarations.push(createDeclaration(prop, value, rule.source));
|
|
7710
|
+
}
|
|
7711
|
+
}
|
|
7712
|
+
if (styleDef.hover && typeof styleDef.hover === "object") {
|
|
7713
|
+
const pc = {
|
|
7714
|
+
id: nextId("hover"),
|
|
7715
|
+
name: "hover",
|
|
7716
|
+
declarations: [],
|
|
7717
|
+
source: rule.source,
|
|
7718
|
+
history: [record("parser", "created", void 0, "Parsed hover block")]
|
|
7719
|
+
};
|
|
7720
|
+
for (const [prop, value] of Object.entries(styleDef.hover)) {
|
|
7721
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
7722
|
+
pc.declarations.push(createDeclaration(prop, value, rule.source));
|
|
7723
|
+
}
|
|
7724
|
+
}
|
|
7725
|
+
rule.pseudoClasses.push(pc);
|
|
7726
|
+
}
|
|
7727
|
+
if (styleDef.atRules && Array.isArray(styleDef.atRules)) {
|
|
7728
|
+
for (const atRule of styleDef.atRules) {
|
|
7729
|
+
const irAtRule = {
|
|
7730
|
+
id: nextId("atrule"),
|
|
7731
|
+
type: atRule.type || "media",
|
|
7732
|
+
query: atRule.query,
|
|
7733
|
+
name: atRule.name,
|
|
7734
|
+
declarations: [],
|
|
7735
|
+
nestedRules: [],
|
|
7736
|
+
source: rule.source,
|
|
7737
|
+
history: [record("parser", "created", void 0, "Parsed at-rule")]
|
|
7738
|
+
};
|
|
7739
|
+
if (atRule.styles && typeof atRule.styles === "object") {
|
|
7740
|
+
for (const [prop, value] of Object.entries(atRule.styles)) {
|
|
7741
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
7742
|
+
irAtRule.declarations.push(createDeclaration(prop, value, rule.source));
|
|
7743
|
+
}
|
|
7744
|
+
}
|
|
7745
|
+
}
|
|
7746
|
+
rule.atRules.push(irAtRule);
|
|
7747
|
+
}
|
|
7748
|
+
}
|
|
7749
|
+
if (styleDef._ifConditions && Array.isArray(styleDef._ifConditions)) {
|
|
7750
|
+
for (const cond of styleDef._ifConditions) {
|
|
7751
|
+
rule.conditions.push({
|
|
7752
|
+
id: nextId("cond"),
|
|
7753
|
+
property: cond.property,
|
|
7754
|
+
variable: cond.variable,
|
|
7755
|
+
conditions: cond.conditions || {},
|
|
7756
|
+
defaultValue: cond.defaultValue || "",
|
|
7757
|
+
source: rule.source
|
|
7758
|
+
});
|
|
7759
|
+
}
|
|
7760
|
+
}
|
|
7761
|
+
ir.rules.push(rule);
|
|
7762
|
+
}
|
|
7763
|
+
}
|
|
7764
|
+
return ir;
|
|
7765
|
+
}
|
|
7766
|
+
function kebab3(prop) {
|
|
7767
|
+
return prop.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
7768
|
+
}
|
|
7769
|
+
function generateCSS(ir, options) {
|
|
7770
|
+
let css = "";
|
|
7771
|
+
for (const rule of ir.rules) {
|
|
7772
|
+
if (rule.isDead) continue;
|
|
7773
|
+
if (rule.declarations.length > 0) {
|
|
7774
|
+
css += rule.selector + " {\n";
|
|
7775
|
+
for (const decl of rule.declarations) {
|
|
7776
|
+
css += " " + kebab3(decl.property) + ": " + decl.value + ";\n";
|
|
7777
|
+
}
|
|
7778
|
+
css += "}\n";
|
|
7779
|
+
}
|
|
7780
|
+
for (const pc of rule.pseudoClasses) {
|
|
7781
|
+
if (pc.declarations.length > 0) {
|
|
7782
|
+
css += rule.selector + ":" + pc.name + " {\n";
|
|
7783
|
+
for (const decl of pc.declarations) {
|
|
7784
|
+
css += " " + kebab3(decl.property) + ": " + decl.value + ";\n";
|
|
7785
|
+
}
|
|
7786
|
+
css += "}\n";
|
|
7787
|
+
}
|
|
7788
|
+
}
|
|
7789
|
+
for (const atRule of rule.atRules) {
|
|
7790
|
+
if (atRule.type === "media" && atRule.query) {
|
|
7791
|
+
css += "@media " + atRule.query + " {\n";
|
|
7792
|
+
css += rule.selector + " {\n";
|
|
7793
|
+
for (const decl of atRule.declarations) {
|
|
7794
|
+
css += " " + kebab3(decl.property) + ": " + decl.value + ";\n";
|
|
7795
|
+
}
|
|
7796
|
+
css += "}\n}\n";
|
|
7797
|
+
} else if (atRule.type === "keyframes" && atRule.name) {
|
|
7798
|
+
css += "@keyframes " + atRule.name + " {\n";
|
|
7799
|
+
for (const decl of atRule.declarations) {
|
|
7800
|
+
css += " " + decl.property + " { " + kebab3(decl.property) + ": " + decl.value + "; }\n";
|
|
7801
|
+
}
|
|
7802
|
+
css += "}\n";
|
|
7803
|
+
}
|
|
7804
|
+
}
|
|
7805
|
+
if (rule.conditions.length > 0) {
|
|
7806
|
+
css += "/* Native CSS if() */\n";
|
|
7807
|
+
css += rule.selector + " {\n";
|
|
7808
|
+
for (const cond of rule.conditions) {
|
|
7809
|
+
const entries = Object.entries(cond.conditions);
|
|
7810
|
+
if (entries.length === 1) {
|
|
7811
|
+
const [c, v] = entries[0];
|
|
7812
|
+
css += " " + kebab3(cond.property) + ": if(style(" + cond.variable + ": " + c + "): " + v + " else " + cond.defaultValue + ");\n";
|
|
7813
|
+
}
|
|
7814
|
+
}
|
|
7815
|
+
css += "}\n";
|
|
7816
|
+
}
|
|
7817
|
+
}
|
|
7818
|
+
return css;
|
|
7819
|
+
}
|
|
7820
|
+
function countNodes(ir) {
|
|
7821
|
+
let declarations = 0, pseudoClasses = 0, atRules = 0, conditions = 0;
|
|
7822
|
+
for (const rule of ir.rules) {
|
|
7823
|
+
declarations += rule.declarations.length;
|
|
7824
|
+
pseudoClasses += rule.pseudoClasses.length;
|
|
7825
|
+
atRules += rule.atRules.length;
|
|
7826
|
+
conditions += rule.conditions.length;
|
|
7827
|
+
}
|
|
7828
|
+
return { rules: ir.rules.length, declarations, pseudoClasses, atRules, conditions };
|
|
7829
|
+
}
|
|
7830
|
+
function findRule(ir, selector) {
|
|
7831
|
+
return ir.rules.find((r) => r.selector === selector);
|
|
7832
|
+
}
|
|
7833
|
+
function cloneIR(ir) {
|
|
7834
|
+
return JSON.parse(JSON.stringify(ir));
|
|
7835
|
+
}
|
|
7836
|
+
function debugIR(ir) {
|
|
7837
|
+
const counts = countNodes(ir);
|
|
7838
|
+
return [
|
|
7839
|
+
"StyleIR {",
|
|
7840
|
+
" id: " + ir.id,
|
|
7841
|
+
" rules: " + counts.rules,
|
|
7842
|
+
" declarations: " + counts.declarations,
|
|
7843
|
+
" pseudoClasses: " + counts.pseudoClasses,
|
|
7844
|
+
" atRules: " + counts.atRules,
|
|
7845
|
+
" conditions: " + counts.conditions,
|
|
7846
|
+
" diagnostics: " + ir.diagnostics.length,
|
|
7847
|
+
" passes: [" + ir.meta.passes.join(", ") + "]",
|
|
7848
|
+
"}"
|
|
7849
|
+
].join("\n");
|
|
7850
|
+
}
|
|
7851
|
+
function applyPass(ir, pass, passName) {
|
|
7852
|
+
const result = pass(ir);
|
|
7853
|
+
result.meta.passCount++;
|
|
7854
|
+
result.meta.passes.push(passName);
|
|
7855
|
+
return result;
|
|
7856
|
+
}
|
|
7857
|
+
function applyPasses(ir, passes) {
|
|
7858
|
+
let current = ir;
|
|
7859
|
+
for (const { name, pass } of passes) {
|
|
7860
|
+
current = applyPass(current, pass, name);
|
|
7861
|
+
}
|
|
7862
|
+
return current;
|
|
7863
|
+
}
|
|
7864
|
+
function compileViaIR(styles, passes = [], options) {
|
|
7865
|
+
let ir = parseIR(styles, options?.sourceFile);
|
|
7866
|
+
for (const { name, pass } of passes) {
|
|
7867
|
+
ir = applyPass(ir, pass, name);
|
|
7868
|
+
}
|
|
7869
|
+
const css = generateCSS(ir, options);
|
|
7870
|
+
return { css, ir };
|
|
7871
|
+
}
|
|
7872
|
+
var styleIR = {
|
|
7873
|
+
createIR,
|
|
7874
|
+
parseIR,
|
|
7875
|
+
generateCSS,
|
|
7876
|
+
createRule,
|
|
7877
|
+
createDeclaration,
|
|
7878
|
+
countNodes,
|
|
7879
|
+
findRule,
|
|
7880
|
+
cloneIR,
|
|
7881
|
+
debugIR,
|
|
7882
|
+
applyPass,
|
|
7883
|
+
applyPasses,
|
|
7884
|
+
compileViaIR,
|
|
7885
|
+
resetIdCounter
|
|
7886
|
+
};
|
|
7887
|
+
|
|
7888
|
+
// src/compiler/pass-manager.ts
|
|
7889
|
+
var intentRecoveryPass = (ir) => {
|
|
7890
|
+
for (const rule of ir.rules) {
|
|
7891
|
+
for (const decl of rule.declarations) {
|
|
7892
|
+
if (decl.property === "display" && decl.value === "flexbox") {
|
|
7893
|
+
decl.value = "flex";
|
|
7894
|
+
decl.history.push({
|
|
7895
|
+
pass: "intent-recovery",
|
|
7896
|
+
action: "corrected-value",
|
|
7897
|
+
timestamp: Date.now(),
|
|
7898
|
+
previous: "flexbox",
|
|
7899
|
+
reason: "flexbox \u2192 flex"
|
|
7900
|
+
});
|
|
7901
|
+
const hasJustify = rule.declarations.some((d) => d.property === "justifyContent");
|
|
7902
|
+
const hasAlign = rule.declarations.some((d) => d.property === "alignItems");
|
|
7903
|
+
if (!hasJustify) {
|
|
7904
|
+
rule.declarations.push({
|
|
7905
|
+
id: "ir-auto-" + Date.now(),
|
|
7906
|
+
property: "justifyContent",
|
|
7907
|
+
value: "center",
|
|
7908
|
+
history: [{
|
|
7909
|
+
pass: "intent-recovery",
|
|
7910
|
+
action: "added-default",
|
|
7911
|
+
timestamp: Date.now(),
|
|
7912
|
+
reason: "Added flexbox centering default"
|
|
7913
|
+
}],
|
|
7914
|
+
meta: {}
|
|
7915
|
+
});
|
|
7916
|
+
}
|
|
7917
|
+
if (!hasAlign) {
|
|
7918
|
+
rule.declarations.push({
|
|
7919
|
+
id: "ir-auto-" + Date.now() + 1,
|
|
7920
|
+
property: "alignItems",
|
|
7921
|
+
value: "center",
|
|
7922
|
+
history: [{
|
|
7923
|
+
pass: "intent-recovery",
|
|
7924
|
+
action: "added-default",
|
|
7925
|
+
timestamp: Date.now(),
|
|
7926
|
+
reason: "Added flexbox centering default"
|
|
7927
|
+
}],
|
|
7928
|
+
meta: {}
|
|
7929
|
+
});
|
|
7930
|
+
}
|
|
7931
|
+
}
|
|
7932
|
+
if (decl.property === "position" && decl.value === "abs") {
|
|
7933
|
+
decl.value = "absolute";
|
|
7934
|
+
decl.history.push({
|
|
7935
|
+
pass: "intent-recovery",
|
|
7936
|
+
action: "corrected-value",
|
|
7937
|
+
timestamp: Date.now(),
|
|
7938
|
+
previous: "abs",
|
|
7939
|
+
reason: "abs \u2192 absolute"
|
|
7940
|
+
});
|
|
7941
|
+
}
|
|
7942
|
+
}
|
|
7943
|
+
}
|
|
7944
|
+
return ir;
|
|
7945
|
+
};
|
|
7946
|
+
var unitResolutionPass = (ir) => {
|
|
7947
|
+
for (const rule of ir.rules) {
|
|
7948
|
+
for (const decl of rule.declarations) {
|
|
7949
|
+
if (typeof decl.value === "number") {
|
|
7950
|
+
const unitless = ["opacity", "zIndex", "flex", "fontWeight", "lineHeight", "order"];
|
|
7951
|
+
if (!unitless.includes(decl.property)) {
|
|
7952
|
+
decl.value = decl.value + "px";
|
|
7953
|
+
decl.history.push({
|
|
7954
|
+
pass: "unit-resolution",
|
|
7955
|
+
action: "added-unit",
|
|
7956
|
+
timestamp: Date.now(),
|
|
7957
|
+
previous: decl.value,
|
|
7958
|
+
reason: "Added px unit to number value"
|
|
7959
|
+
});
|
|
7960
|
+
}
|
|
7961
|
+
}
|
|
7962
|
+
}
|
|
7963
|
+
}
|
|
7964
|
+
return ir;
|
|
7965
|
+
};
|
|
7966
|
+
var validationPass = (ir) => {
|
|
7967
|
+
for (const rule of ir.rules) {
|
|
7968
|
+
const position = rule.declarations.find((d) => d.property === "position");
|
|
7969
|
+
const zIndex = rule.declarations.find((d) => d.property === "zIndex" || d.property === "z-index");
|
|
7970
|
+
if (position && position.value === "static" && zIndex) {
|
|
7971
|
+
ir.diagnostics.push({
|
|
7972
|
+
id: "diag-" + Date.now(),
|
|
7973
|
+
nodeId: rule.id,
|
|
7974
|
+
severity: "warning",
|
|
7975
|
+
message: "z-index has no effect on static positioned elements",
|
|
7976
|
+
suggestion: "Change position to relative, absolute, or fixed",
|
|
7977
|
+
pass: "validation"
|
|
7978
|
+
});
|
|
7979
|
+
}
|
|
7980
|
+
const display = rule.declarations.find((d) => d.property === "display");
|
|
7981
|
+
const hasFlexProps = rule.declarations.some(
|
|
7982
|
+
(d) => ["justifyContent", "alignItems", "flexDirection", "flexWrap"].includes(d.property)
|
|
7983
|
+
);
|
|
7984
|
+
if (hasFlexProps && (!display || display.value !== "flex" && display.value !== "inline-flex")) {
|
|
7985
|
+
ir.diagnostics.push({
|
|
7986
|
+
id: "diag-" + Date.now() + 1,
|
|
7987
|
+
nodeId: rule.id,
|
|
7988
|
+
severity: "warning",
|
|
7989
|
+
message: "Flex properties require display: flex or display: inline-flex",
|
|
7990
|
+
pass: "validation"
|
|
7991
|
+
});
|
|
7992
|
+
}
|
|
7993
|
+
}
|
|
7994
|
+
return ir;
|
|
7995
|
+
};
|
|
7996
|
+
var specificitySortPass = (ir) => {
|
|
7997
|
+
for (const rule of ir.rules) {
|
|
7998
|
+
let a = 0, b = 0, c = 0;
|
|
7999
|
+
const idMatches = rule.selector.match(/#[a-zA-Z0-9_-]+/g);
|
|
8000
|
+
if (idMatches) a += idMatches.length;
|
|
8001
|
+
const classMatches = rule.selector.match(/\.[a-zA-Z0-9_-]+/g);
|
|
8002
|
+
if (classMatches) b += classMatches.length;
|
|
8003
|
+
const pseudoMatches = rule.selector.match(/:[a-zA-Z-]+/g);
|
|
8004
|
+
if (pseudoMatches) b += pseudoMatches.length;
|
|
8005
|
+
const elemMatches = rule.selector.match(/^[a-zA-Z]+|[a-zA-Z]+(?=[.#[:])/g);
|
|
8006
|
+
if (elemMatches) c += elemMatches.length;
|
|
8007
|
+
rule.specificity = a * 1e4 + b * 100 + c;
|
|
8008
|
+
}
|
|
8009
|
+
ir.rules.sort((a, b) => a.specificity - b.specificity);
|
|
8010
|
+
return ir;
|
|
8011
|
+
};
|
|
8012
|
+
var deadEliminationPass = (ir) => {
|
|
8013
|
+
const before = ir.rules.length;
|
|
8014
|
+
ir.rules = ir.rules.filter((r) => !r.isDead);
|
|
8015
|
+
const eliminated = before - ir.rules.length;
|
|
8016
|
+
if (eliminated > 0) {
|
|
8017
|
+
ir.diagnostics.push({
|
|
8018
|
+
id: "diag-dead-" + Date.now(),
|
|
8019
|
+
nodeId: ir.id,
|
|
8020
|
+
severity: "info",
|
|
8021
|
+
message: "Eliminated " + eliminated + " dead rules",
|
|
8022
|
+
pass: "dead-elimination"
|
|
8023
|
+
});
|
|
8024
|
+
}
|
|
8025
|
+
return ir;
|
|
8026
|
+
};
|
|
8027
|
+
var atomicExtractionPass = (ir) => {
|
|
8028
|
+
const usageMap = /* @__PURE__ */ new Map();
|
|
8029
|
+
for (const rule of ir.rules) {
|
|
8030
|
+
for (const decl of rule.declarations) {
|
|
8031
|
+
const key = decl.property + ":" + decl.value;
|
|
8032
|
+
usageMap.set(key, (usageMap.get(key) || 0) + 1);
|
|
8033
|
+
}
|
|
8034
|
+
}
|
|
8035
|
+
for (const rule of ir.rules) {
|
|
8036
|
+
for (const decl of rule.declarations) {
|
|
8037
|
+
const key = decl.property + ":" + decl.value;
|
|
8038
|
+
const usage = usageMap.get(key) || 0;
|
|
8039
|
+
decl.meta.atomic = usage >= 3;
|
|
8040
|
+
decl.meta.usageCount = usage;
|
|
8041
|
+
}
|
|
8042
|
+
}
|
|
8043
|
+
return ir;
|
|
8044
|
+
};
|
|
8045
|
+
var mediaQueryPackingPass = (ir) => {
|
|
8046
|
+
const queryMap = /* @__PURE__ */ new Map();
|
|
8047
|
+
for (const rule of ir.rules) {
|
|
8048
|
+
for (const atRule of rule.atRules) {
|
|
8049
|
+
if (atRule.type === "media" && atRule.query) {
|
|
8050
|
+
const existing = queryMap.get(atRule.query) || [];
|
|
8051
|
+
existing.push(rule);
|
|
8052
|
+
queryMap.set(atRule.query, existing);
|
|
8053
|
+
}
|
|
8054
|
+
}
|
|
8055
|
+
}
|
|
8056
|
+
return ir;
|
|
8057
|
+
};
|
|
8058
|
+
var cssIfTranspilePass = (ir) => {
|
|
8059
|
+
for (const rule of ir.rules) {
|
|
8060
|
+
if (rule.conditions.length > 0) {
|
|
8061
|
+
rule.meta.hasCSSIf = true;
|
|
8062
|
+
}
|
|
8063
|
+
}
|
|
8064
|
+
return ir;
|
|
8065
|
+
};
|
|
8066
|
+
var cssCompressionPass = (ir) => {
|
|
8067
|
+
for (const rule of ir.rules) {
|
|
8068
|
+
for (const decl of rule.declarations) {
|
|
8069
|
+
if (typeof decl.value === "string" && /^#[0-9a-fA-F]{6}$/.test(decl.value)) {
|
|
8070
|
+
const hex = decl.value;
|
|
8071
|
+
if (hex[1] === hex[2] && hex[3] === hex[4] && hex[5] === hex[6]) {
|
|
8072
|
+
decl.value = "#" + hex[1] + hex[3] + hex[5];
|
|
8073
|
+
decl.history.push({
|
|
8074
|
+
pass: "css-compression",
|
|
8075
|
+
action: "shortened-hex",
|
|
8076
|
+
timestamp: Date.now(),
|
|
8077
|
+
previous: hex,
|
|
8078
|
+
reason: "Shortened hex color"
|
|
8079
|
+
});
|
|
8080
|
+
}
|
|
8081
|
+
}
|
|
8082
|
+
if (typeof decl.value === "string" && /^0\.\d+/.test(decl.value)) {
|
|
8083
|
+
const shortened = decl.value.replace(/^0\./, ".");
|
|
8084
|
+
decl.value = shortened;
|
|
8085
|
+
}
|
|
8086
|
+
}
|
|
8087
|
+
}
|
|
8088
|
+
return ir;
|
|
8089
|
+
};
|
|
8090
|
+
var diagnosticsExportPass = (ir) => {
|
|
8091
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8092
|
+
const unique = [];
|
|
8093
|
+
for (const diag of ir.diagnostics) {
|
|
8094
|
+
const key = diag.nodeId + ":" + diag.message;
|
|
8095
|
+
if (!seen.has(key)) {
|
|
8096
|
+
seen.add(key);
|
|
8097
|
+
unique.push(diag);
|
|
8098
|
+
}
|
|
8099
|
+
}
|
|
8100
|
+
ir.diagnostics = unique;
|
|
8101
|
+
return ir;
|
|
8102
|
+
};
|
|
8103
|
+
var DEFAULT_PIPELINE = [
|
|
8104
|
+
{
|
|
8105
|
+
name: "intent-recovery",
|
|
8106
|
+
priority: 1,
|
|
8107
|
+
description: "Fix typos and add defaults for common patterns",
|
|
8108
|
+
pass: intentRecoveryPass,
|
|
8109
|
+
requires: [],
|
|
8110
|
+
enabled: true
|
|
8111
|
+
},
|
|
8112
|
+
{
|
|
8113
|
+
name: "unit-resolution",
|
|
8114
|
+
priority: 2,
|
|
8115
|
+
description: "Resolve units and normalize values",
|
|
8116
|
+
pass: unitResolutionPass,
|
|
8117
|
+
requires: [],
|
|
8118
|
+
enabled: true
|
|
8119
|
+
},
|
|
8120
|
+
{
|
|
8121
|
+
name: "validation",
|
|
8122
|
+
priority: 3,
|
|
8123
|
+
description: "Run contrast checks and conflict detection",
|
|
8124
|
+
pass: validationPass,
|
|
8125
|
+
requires: ["intent-recovery"],
|
|
8126
|
+
enabled: true
|
|
8127
|
+
},
|
|
8128
|
+
{
|
|
8129
|
+
name: "specificity-sort",
|
|
8130
|
+
priority: 4,
|
|
8131
|
+
description: "Order rules by specificity",
|
|
8132
|
+
pass: specificitySortPass,
|
|
8133
|
+
requires: [],
|
|
8134
|
+
enabled: true
|
|
8135
|
+
},
|
|
8136
|
+
{
|
|
8137
|
+
name: "dead-elimination",
|
|
8138
|
+
priority: 5,
|
|
8139
|
+
description: "Remove unused selectors",
|
|
8140
|
+
pass: deadEliminationPass,
|
|
8141
|
+
requires: ["specificity-sort"],
|
|
8142
|
+
enabled: true
|
|
8143
|
+
},
|
|
8144
|
+
{
|
|
8145
|
+
name: "atomic-extraction",
|
|
8146
|
+
priority: 6,
|
|
8147
|
+
description: "Extract shared properties into atomic classes",
|
|
8148
|
+
pass: atomicExtractionPass,
|
|
8149
|
+
requires: ["unit-resolution"],
|
|
8150
|
+
enabled: true
|
|
8151
|
+
},
|
|
8152
|
+
{
|
|
8153
|
+
name: "media-query-packing",
|
|
8154
|
+
priority: 7,
|
|
8155
|
+
description: "Group same-query rules together",
|
|
8156
|
+
pass: mediaQueryPackingPass,
|
|
8157
|
+
requires: ["specificity-sort"],
|
|
8158
|
+
enabled: true
|
|
8159
|
+
},
|
|
8160
|
+
{
|
|
8161
|
+
name: "css-if-transpile",
|
|
8162
|
+
priority: 8,
|
|
8163
|
+
description: "Transpile conditional patterns to native CSS if()",
|
|
8164
|
+
pass: cssIfTranspilePass,
|
|
8165
|
+
requires: ["intent-recovery"],
|
|
8166
|
+
enabled: true
|
|
8167
|
+
},
|
|
8168
|
+
{
|
|
8169
|
+
name: "css-compression",
|
|
8170
|
+
priority: 9,
|
|
8171
|
+
description: "Minify CSS output",
|
|
8172
|
+
pass: cssCompressionPass,
|
|
8173
|
+
requires: [],
|
|
8174
|
+
enabled: true
|
|
8175
|
+
},
|
|
8176
|
+
{
|
|
8177
|
+
name: "diagnostics-export",
|
|
8178
|
+
priority: 10,
|
|
8179
|
+
description: "Collect and organize diagnostics",
|
|
8180
|
+
pass: diagnosticsExportPass,
|
|
8181
|
+
requires: ["validation"],
|
|
8182
|
+
enabled: true
|
|
8183
|
+
}
|
|
8184
|
+
];
|
|
8185
|
+
var PassManager = class {
|
|
8186
|
+
passes = [];
|
|
8187
|
+
results = [];
|
|
8188
|
+
constructor(passes = DEFAULT_PIPELINE) {
|
|
8189
|
+
this.passes = passes.filter((p) => p.enabled);
|
|
8190
|
+
this.validateDependencies();
|
|
8191
|
+
}
|
|
8192
|
+
/**
|
|
8193
|
+
* Validate that all pass dependencies are satisfied.
|
|
8194
|
+
*/
|
|
8195
|
+
validateDependencies() {
|
|
8196
|
+
const passNames = new Set(this.passes.map((p) => p.name));
|
|
8197
|
+
for (const pass of this.passes) {
|
|
8198
|
+
for (const req of pass.requires) {
|
|
8199
|
+
if (!passNames.has(req)) {
|
|
8200
|
+
throw new Error(
|
|
8201
|
+
'Pass "' + pass.name + '" requires "' + req + '" but it is not in the pipeline'
|
|
8202
|
+
);
|
|
8203
|
+
}
|
|
8204
|
+
}
|
|
8205
|
+
}
|
|
8206
|
+
}
|
|
8207
|
+
/**
|
|
8208
|
+
* Topological sort passes by dependencies.
|
|
8209
|
+
* Passes with no dependencies run first.
|
|
8210
|
+
*/
|
|
8211
|
+
sortByDependencies() {
|
|
8212
|
+
const sorted = [];
|
|
8213
|
+
const remaining = [...this.passes];
|
|
8214
|
+
const satisfied = /* @__PURE__ */ new Set();
|
|
8215
|
+
while (remaining.length > 0) {
|
|
8216
|
+
const ready = remaining.findIndex(
|
|
8217
|
+
(p) => p.requires.every((req) => satisfied.has(req))
|
|
8218
|
+
);
|
|
8219
|
+
if (ready === -1) {
|
|
8220
|
+
throw new Error("Circular dependency detected in pass pipeline");
|
|
8221
|
+
}
|
|
8222
|
+
const pass = remaining.splice(ready, 1)[0];
|
|
8223
|
+
sorted.push(pass);
|
|
8224
|
+
satisfied.add(pass.name);
|
|
8225
|
+
}
|
|
8226
|
+
return sorted;
|
|
8227
|
+
}
|
|
8228
|
+
/**
|
|
8229
|
+
* Run the full pipeline on an IR.
|
|
8230
|
+
*/
|
|
8231
|
+
run(ir) {
|
|
8232
|
+
const startTime = Date.now();
|
|
8233
|
+
const sorted = this.sortByDependencies();
|
|
8234
|
+
this.results = [];
|
|
8235
|
+
let current = ir;
|
|
8236
|
+
const from = countNodes(ir);
|
|
8237
|
+
for (const pass of sorted) {
|
|
8238
|
+
const passStart = Date.now();
|
|
8239
|
+
const before = countNodes(current);
|
|
8240
|
+
try {
|
|
8241
|
+
current = pass.pass(current);
|
|
8242
|
+
} catch (err) {
|
|
8243
|
+
this.results.push({
|
|
8244
|
+
name: pass.name,
|
|
8245
|
+
duration: Date.now() - passStart,
|
|
8246
|
+
nodesBefore: before.rules + before.declarations,
|
|
8247
|
+
nodesAfter: before.rules + before.declarations,
|
|
8248
|
+
changes: 0,
|
|
8249
|
+
errors: [err.message]
|
|
8250
|
+
});
|
|
8251
|
+
continue;
|
|
8252
|
+
}
|
|
8253
|
+
const after = countNodes(current);
|
|
8254
|
+
this.results.push({
|
|
8255
|
+
name: pass.name,
|
|
8256
|
+
duration: Date.now() - passStart,
|
|
8257
|
+
nodesBefore: before.rules + before.declarations,
|
|
8258
|
+
nodesAfter: after.rules + after.declarations,
|
|
8259
|
+
changes: Math.abs(after.rules + after.declarations - (before.rules + before.declarations)),
|
|
8260
|
+
errors: []
|
|
8261
|
+
});
|
|
8262
|
+
}
|
|
8263
|
+
const totalDuration = Date.now() - startTime;
|
|
8264
|
+
const to = countNodes(current);
|
|
8265
|
+
return {
|
|
8266
|
+
ir: current,
|
|
8267
|
+
css: "",
|
|
8268
|
+
// Will be generated separately
|
|
8269
|
+
results: this.results,
|
|
8270
|
+
totalDuration,
|
|
8271
|
+
summary: "Pipeline complete: " + this.results.length + " passes in " + totalDuration + "ms. Nodes: " + (from.rules + from.declarations) + " \u2192 " + (to.rules + to.declarations)
|
|
8272
|
+
};
|
|
8273
|
+
}
|
|
8274
|
+
/**
|
|
8275
|
+
* Get results from the last run.
|
|
8276
|
+
*/
|
|
8277
|
+
getResults() {
|
|
8278
|
+
return this.results;
|
|
8279
|
+
}
|
|
8280
|
+
/**
|
|
8281
|
+
* Print a human-readable report of pass results.
|
|
8282
|
+
*/
|
|
8283
|
+
report() {
|
|
8284
|
+
const lines = [
|
|
8285
|
+
"\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550",
|
|
8286
|
+
" ChainCSS Multi-Pass Pipeline Report",
|
|
8287
|
+
"\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"
|
|
8288
|
+
];
|
|
8289
|
+
for (const result of this.results) {
|
|
8290
|
+
const status = result.errors.length > 0 ? "\u274C" : "\u2713";
|
|
8291
|
+
lines.push(
|
|
8292
|
+
" " + status + " " + result.name.padEnd(22) + " " + result.duration.toString().padStart(4) + "ms nodes: " + result.nodesBefore + " \u2192 " + result.nodesAfter
|
|
8293
|
+
);
|
|
8294
|
+
if (result.errors.length > 0) {
|
|
8295
|
+
for (const err of result.errors) {
|
|
8296
|
+
lines.push(" \u26A0 " + err);
|
|
8297
|
+
}
|
|
8298
|
+
}
|
|
8299
|
+
}
|
|
8300
|
+
lines.push("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
|
|
8301
|
+
return lines.join("\n");
|
|
8302
|
+
}
|
|
8303
|
+
/**
|
|
8304
|
+
* Add a custom pass to the pipeline.
|
|
8305
|
+
*/
|
|
8306
|
+
addPass(pass) {
|
|
8307
|
+
this.passes.push(pass);
|
|
8308
|
+
return this;
|
|
8309
|
+
}
|
|
8310
|
+
/**
|
|
8311
|
+
* Remove a pass by name.
|
|
8312
|
+
*/
|
|
8313
|
+
removePass(name) {
|
|
8314
|
+
this.passes = this.passes.filter((p) => p.name !== name);
|
|
8315
|
+
return this;
|
|
8316
|
+
}
|
|
8317
|
+
/**
|
|
8318
|
+
* Enable/disable a pass.
|
|
8319
|
+
*/
|
|
8320
|
+
setPassEnabled(name, enabled) {
|
|
8321
|
+
const pass = this.passes.find((p) => p.name === name);
|
|
8322
|
+
if (pass) pass.enabled = enabled;
|
|
8323
|
+
this.passes = this.passes.filter((p) => p.enabled);
|
|
8324
|
+
return this;
|
|
8325
|
+
}
|
|
8326
|
+
/**
|
|
8327
|
+
* Get the list of pass names in execution order.
|
|
8328
|
+
*/
|
|
8329
|
+
getPassOrder() {
|
|
8330
|
+
return this.sortByDependencies().map((p) => p.name);
|
|
8331
|
+
}
|
|
8332
|
+
};
|
|
8333
|
+
function runDefaultPipeline(ir) {
|
|
8334
|
+
const manager = new PassManager(DEFAULT_PIPELINE.map((p) => ({ ...p })));
|
|
8335
|
+
return manager.run(ir);
|
|
8336
|
+
}
|
|
8337
|
+
|
|
8338
|
+
// src/compiler/constraint-solver.ts
|
|
8339
|
+
function tokenize(expr) {
|
|
8340
|
+
const tokens3 = [];
|
|
8341
|
+
let current = "";
|
|
8342
|
+
let inParens = 0;
|
|
8343
|
+
for (const char of expr) {
|
|
8344
|
+
if (char === "(") {
|
|
8345
|
+
inParens++;
|
|
8346
|
+
current += char;
|
|
8347
|
+
} else if (char === ")") {
|
|
8348
|
+
inParens--;
|
|
8349
|
+
current += char;
|
|
8350
|
+
} else if (char === " " && inParens === 0) {
|
|
8351
|
+
if (current) tokens3.push(current);
|
|
8352
|
+
current = "";
|
|
8353
|
+
} else {
|
|
8354
|
+
current += char;
|
|
8355
|
+
}
|
|
8356
|
+
}
|
|
8357
|
+
if (current) tokens3.push(current);
|
|
8358
|
+
return tokens3;
|
|
8359
|
+
}
|
|
8360
|
+
function parseExpression(expr) {
|
|
8361
|
+
const trimmed = expr.trim();
|
|
8362
|
+
const funcMatch = trimmed.match(/^([a-zA-Z]+)\((.+)\)$/);
|
|
8363
|
+
if (funcMatch) {
|
|
8364
|
+
const args = funcMatch[2].split(",").map((a) => a.trim());
|
|
8365
|
+
return {
|
|
8366
|
+
left: "",
|
|
8367
|
+
operator: "function",
|
|
8368
|
+
right: "",
|
|
8369
|
+
isFunction: true,
|
|
8370
|
+
functionName: funcMatch[1],
|
|
8371
|
+
functionArgs: args
|
|
8372
|
+
};
|
|
8373
|
+
}
|
|
8374
|
+
const tokens3 = tokenize(trimmed);
|
|
8375
|
+
if (tokens3.length === 3) {
|
|
8376
|
+
return {
|
|
8377
|
+
left: tokens3[0],
|
|
8378
|
+
operator: tokens3[1],
|
|
8379
|
+
right: tokens3[2],
|
|
8380
|
+
isFunction: false
|
|
8381
|
+
};
|
|
8382
|
+
}
|
|
8383
|
+
return {
|
|
8384
|
+
left: trimmed,
|
|
8385
|
+
operator: "",
|
|
8386
|
+
right: "",
|
|
8387
|
+
isFunction: false
|
|
8388
|
+
};
|
|
8389
|
+
}
|
|
8390
|
+
var KNOWN_REFERENCES = {
|
|
8391
|
+
"parent": "100%",
|
|
8392
|
+
"parent.width": "100%",
|
|
8393
|
+
"parent.height": "100%",
|
|
8394
|
+
"viewport": "100vw",
|
|
8395
|
+
"viewport.width": "100vw",
|
|
8396
|
+
"viewport.height": "100vh",
|
|
8397
|
+
"self": "100%",
|
|
8398
|
+
"self.width": "100%",
|
|
8399
|
+
"self.height": "100%"
|
|
8400
|
+
};
|
|
8401
|
+
function resolveReference(ref) {
|
|
8402
|
+
return KNOWN_REFERENCES[ref] || ref;
|
|
8403
|
+
}
|
|
8404
|
+
function resolveConstraint(constraint, context) {
|
|
8405
|
+
const { property, operator, expression } = constraint;
|
|
8406
|
+
const parsed = parseExpression(expression);
|
|
8407
|
+
if (operator === "<" && expression === "parent") {
|
|
8408
|
+
return {
|
|
8409
|
+
constraint,
|
|
8410
|
+
cssProperty: "max-" + property,
|
|
8411
|
+
cssValue: "100%",
|
|
8412
|
+
method: "direct",
|
|
8413
|
+
explanation: property + " < parent \u2192 max-" + property + ": 100%"
|
|
8414
|
+
};
|
|
8415
|
+
}
|
|
8416
|
+
if (operator === ">" && expression === "parent") {
|
|
8417
|
+
return {
|
|
8418
|
+
constraint,
|
|
8419
|
+
cssProperty: "min-" + property,
|
|
8420
|
+
cssValue: "100%",
|
|
8421
|
+
method: "direct",
|
|
8422
|
+
explanation: property + " > parent \u2192 min-" + property + ": 100%"
|
|
8423
|
+
};
|
|
8424
|
+
}
|
|
8425
|
+
if (operator === "=" && parsed.operator === "*") {
|
|
8426
|
+
const leftRef = resolveReference(parsed.left);
|
|
8427
|
+
const rightNum = parseFloat(parsed.right);
|
|
8428
|
+
if (!isNaN(rightNum)) {
|
|
8429
|
+
if (property === "height" && parsed.left === "width" || property === "width" && parsed.left === "height") {
|
|
8430
|
+
const ratio = rightNum;
|
|
8431
|
+
const gcd = findGCD(Math.round(ratio * 100), 100);
|
|
8432
|
+
const num = Math.round(ratio * 100) / gcd;
|
|
8433
|
+
const den = 100 / gcd;
|
|
8434
|
+
return {
|
|
8435
|
+
constraint,
|
|
8436
|
+
cssProperty: "aspect-ratio",
|
|
8437
|
+
cssValue: num + " / " + den,
|
|
8438
|
+
method: "aspect-ratio",
|
|
8439
|
+
explanation: property + " = " + expression + " \u2192 aspect-ratio: " + num + "/" + den
|
|
8440
|
+
};
|
|
8441
|
+
}
|
|
8442
|
+
return {
|
|
8443
|
+
constraint,
|
|
8444
|
+
cssProperty: property,
|
|
8445
|
+
cssValue: "calc(" + leftRef + " * " + rightNum + ")",
|
|
8446
|
+
method: "calc",
|
|
8447
|
+
explanation: property + " = " + expression + " \u2192 calc(" + leftRef + " * " + rightNum + ")"
|
|
8448
|
+
};
|
|
8449
|
+
}
|
|
8450
|
+
}
|
|
8451
|
+
if (operator === "=" && parsed.operator === "/") {
|
|
8452
|
+
const leftRef = resolveReference(parsed.left);
|
|
8453
|
+
const rightNum = parseFloat(parsed.right);
|
|
8454
|
+
if (!isNaN(rightNum)) {
|
|
8455
|
+
return {
|
|
8456
|
+
constraint,
|
|
8457
|
+
cssProperty: property,
|
|
8458
|
+
cssValue: "calc(" + leftRef + " / " + rightNum + ")",
|
|
8459
|
+
method: "calc",
|
|
8460
|
+
explanation: property + " = " + expression + " \u2192 calc(" + leftRef + " / " + rightNum + ")"
|
|
8461
|
+
};
|
|
8462
|
+
}
|
|
8463
|
+
}
|
|
8464
|
+
if (parsed.isFunction && parsed.functionName) {
|
|
8465
|
+
const resolvedArgs = (parsed.functionArgs || []).map(resolveReference);
|
|
8466
|
+
return {
|
|
8467
|
+
constraint,
|
|
8468
|
+
cssProperty: property,
|
|
8469
|
+
cssValue: parsed.functionName + "(" + resolvedArgs.join(", ") + ")",
|
|
8470
|
+
method: parsed.functionName,
|
|
8471
|
+
explanation: property + " = " + expression + " \u2192 " + parsed.functionName + "()"
|
|
8472
|
+
};
|
|
8473
|
+
}
|
|
8474
|
+
if (operator === "=" && !parsed.operator) {
|
|
8475
|
+
const resolved = resolveReference(expression);
|
|
8476
|
+
return {
|
|
8477
|
+
constraint,
|
|
8478
|
+
cssProperty: property,
|
|
8479
|
+
cssValue: resolved,
|
|
8480
|
+
method: "direct",
|
|
8481
|
+
explanation: property + " = " + expression + " \u2192 " + resolved
|
|
8482
|
+
};
|
|
8483
|
+
}
|
|
8484
|
+
if (operator === "=" && expression.includes("vw") || expression.includes("vh")) {
|
|
8485
|
+
return {
|
|
8486
|
+
constraint,
|
|
8487
|
+
cssProperty: property,
|
|
8488
|
+
cssValue: expression,
|
|
8489
|
+
method: "direct",
|
|
8490
|
+
explanation: property + " = " + expression
|
|
8491
|
+
};
|
|
8492
|
+
}
|
|
8493
|
+
return {
|
|
8494
|
+
constraint,
|
|
8495
|
+
cssProperty: property,
|
|
8496
|
+
cssValue: expression,
|
|
8497
|
+
method: "direct",
|
|
8498
|
+
explanation: property + " = " + expression + " (passthrough)"
|
|
8499
|
+
};
|
|
8500
|
+
}
|
|
8501
|
+
function resolveStickyUntil(selector, untilSelector) {
|
|
8502
|
+
return {
|
|
8503
|
+
constraint: {
|
|
8504
|
+
property: "position",
|
|
8505
|
+
operator: "=",
|
|
8506
|
+
expression: "sticky until " + untilSelector
|
|
8507
|
+
},
|
|
8508
|
+
cssProperty: "position",
|
|
8509
|
+
cssValue: "sticky; top: 0; animation: sticky-" + selector.replace(".", "") + " 1s linear both; animation-timeline: scroll(); animation-range: contain 0% contain 100%",
|
|
8510
|
+
method: "sticky",
|
|
8511
|
+
explanation: "sticky until " + untilSelector + " \u2192 position: sticky + scroll-timeline"
|
|
8512
|
+
};
|
|
8513
|
+
}
|
|
8514
|
+
function resolveContainerQuery(property, operator, value, condition) {
|
|
8515
|
+
const widthMatch = condition.match(/>\s*(\d+)(px|rem|em)?/);
|
|
8516
|
+
if (widthMatch) {
|
|
8517
|
+
const width = widthMatch[1] + (widthMatch[2] || "px");
|
|
8518
|
+
return {
|
|
8519
|
+
atRule: {
|
|
8520
|
+
type: "container",
|
|
8521
|
+
query: "(min-width: " + width + ")",
|
|
8522
|
+
declarations: [{ property, value }]
|
|
8523
|
+
},
|
|
8524
|
+
explanation: property + " " + operator + " " + value + " when > " + width + " \u2192 @container (min-width: " + width + ")"
|
|
8525
|
+
};
|
|
8526
|
+
}
|
|
8527
|
+
return {
|
|
8528
|
+
atRule: { type: "container", query: condition, declarations: [{ property, value }] },
|
|
8529
|
+
explanation: property + " when " + condition + " \u2192 @container " + condition
|
|
8530
|
+
};
|
|
8531
|
+
}
|
|
8532
|
+
var constraintSolverPass = (ir) => {
|
|
8533
|
+
for (const rule of ir.rules) {
|
|
8534
|
+
const constraints = rule.meta._constraints || [];
|
|
8535
|
+
if (constraints.length === 0) continue;
|
|
8536
|
+
for (const constraint of constraints) {
|
|
8537
|
+
const resolved = resolveConstraint(constraint);
|
|
8538
|
+
rule.declarations.push({
|
|
8539
|
+
id: "constraint-" + Date.now() + "-" + Math.random().toString(36).slice(2, 6),
|
|
8540
|
+
property: resolved.cssProperty,
|
|
8541
|
+
value: resolved.cssValue,
|
|
8542
|
+
history: [{
|
|
8543
|
+
pass: "constraint-solver",
|
|
8544
|
+
action: "resolved-constraint",
|
|
8545
|
+
timestamp: Date.now(),
|
|
8546
|
+
reason: resolved.explanation
|
|
8547
|
+
}],
|
|
8548
|
+
meta: { constraint }
|
|
8549
|
+
});
|
|
8550
|
+
}
|
|
8551
|
+
}
|
|
8552
|
+
return ir;
|
|
8553
|
+
};
|
|
8554
|
+
function parseConstraint(property, expression) {
|
|
8555
|
+
let operator = "=";
|
|
8556
|
+
let cleanExpr = expression;
|
|
8557
|
+
const opMatch = expression.match(/^([<>=!≈]+)\s*(.*)/);
|
|
8558
|
+
if (opMatch) {
|
|
8559
|
+
operator = opMatch[1];
|
|
8560
|
+
cleanExpr = opMatch[2];
|
|
8561
|
+
}
|
|
8562
|
+
let condition;
|
|
8563
|
+
const condMatch = cleanExpr.match(/^(.+)\s+when\s+(.+)$/);
|
|
8564
|
+
if (condMatch) {
|
|
8565
|
+
cleanExpr = condMatch[1];
|
|
8566
|
+
condition = condMatch[2];
|
|
8567
|
+
}
|
|
8568
|
+
return {
|
|
8569
|
+
property,
|
|
8570
|
+
operator,
|
|
8571
|
+
expression: cleanExpr,
|
|
8572
|
+
condition
|
|
8573
|
+
};
|
|
8574
|
+
}
|
|
8575
|
+
function findGCD(a, b) {
|
|
8576
|
+
return b === 0 ? a : findGCD(b, a % b);
|
|
8577
|
+
}
|
|
8578
|
+
var constraintSolver = {
|
|
8579
|
+
resolve: resolveConstraint,
|
|
8580
|
+
resolveStickyUntil,
|
|
8581
|
+
resolveContainerQuery,
|
|
8582
|
+
parseConstraint,
|
|
8583
|
+
parseExpression,
|
|
8584
|
+
resolveReference,
|
|
8585
|
+
pass: constraintSolverPass
|
|
8586
|
+
};
|
|
8587
|
+
|
|
8588
|
+
// src/compiler/layout-intelligence.ts
|
|
8589
|
+
var LAYOUT_PATTERNS = [
|
|
8590
|
+
// --- Flexbox Patterns ---
|
|
8591
|
+
{
|
|
8592
|
+
name: "stack-center",
|
|
8593
|
+
description: "Vertical stack with centered items",
|
|
8594
|
+
macro: "stack('vertical center')",
|
|
8595
|
+
example: "chain.stack('vertical center')",
|
|
8596
|
+
required: {
|
|
8597
|
+
display: "flex",
|
|
8598
|
+
flexDirection: "column",
|
|
8599
|
+
justifyContent: "center",
|
|
8600
|
+
alignItems: "center"
|
|
8601
|
+
},
|
|
8602
|
+
optional: ["gap", "padding"],
|
|
8603
|
+
minMatches: 4
|
|
8604
|
+
},
|
|
8605
|
+
{
|
|
8606
|
+
name: "stack-horizontal",
|
|
8607
|
+
description: "Horizontal stack with centered items",
|
|
8608
|
+
macro: "stack('horizontal center')",
|
|
8609
|
+
example: "chain.stack('horizontal center')",
|
|
8610
|
+
required: {
|
|
8611
|
+
display: "flex",
|
|
8612
|
+
flexDirection: "row",
|
|
8613
|
+
justifyContent: "center",
|
|
8614
|
+
alignItems: "center"
|
|
8615
|
+
},
|
|
8616
|
+
optional: ["gap"],
|
|
8617
|
+
minMatches: 4
|
|
8618
|
+
},
|
|
8619
|
+
{
|
|
8620
|
+
name: "flex-center",
|
|
8621
|
+
description: "Flexbox absolute centering",
|
|
8622
|
+
macro: "center()",
|
|
8623
|
+
example: "chain.center()",
|
|
8624
|
+
required: {
|
|
8625
|
+
display: "flex",
|
|
8626
|
+
justifyContent: "center",
|
|
8627
|
+
alignItems: "center"
|
|
8628
|
+
},
|
|
8629
|
+
minMatches: 3
|
|
8630
|
+
},
|
|
8631
|
+
{
|
|
8632
|
+
name: "flex-between",
|
|
8633
|
+
description: "Flexbox space-between alignment",
|
|
8634
|
+
macro: "stack('between')",
|
|
8635
|
+
example: "chain.stack('between')",
|
|
8636
|
+
required: {
|
|
8637
|
+
display: "flex",
|
|
8638
|
+
justifyContent: "space-between",
|
|
8639
|
+
alignItems: "center"
|
|
8640
|
+
},
|
|
8641
|
+
minMatches: 3
|
|
8642
|
+
},
|
|
8643
|
+
{
|
|
8644
|
+
name: "flex-row-wrap",
|
|
8645
|
+
description: "Flex row with wrapping",
|
|
8646
|
+
macro: "chain.flex().flexDir('row').flexWrap('wrap')",
|
|
8647
|
+
example: "chain.flex().flexDir('row').flexWrap('wrap')",
|
|
8648
|
+
required: {
|
|
8649
|
+
display: "flex",
|
|
8650
|
+
flexDirection: "row",
|
|
8651
|
+
flexWrap: "wrap"
|
|
8652
|
+
},
|
|
8653
|
+
minMatches: 3
|
|
8654
|
+
},
|
|
8655
|
+
// --- Grid Patterns ---
|
|
8656
|
+
{
|
|
8657
|
+
name: "grid-center",
|
|
8658
|
+
description: "Grid with centered items",
|
|
8659
|
+
macro: "gridCenter()",
|
|
8660
|
+
example: "chain.gridCenter()",
|
|
8661
|
+
required: {
|
|
8662
|
+
display: "grid",
|
|
8663
|
+
placeItems: "center"
|
|
8664
|
+
},
|
|
8665
|
+
minMatches: 2
|
|
8666
|
+
},
|
|
8667
|
+
{
|
|
8668
|
+
name: "grid-auto-fit",
|
|
8669
|
+
description: "Responsive auto-fit grid",
|
|
8670
|
+
macro: "gridList()",
|
|
8671
|
+
example: "chain.gridList()",
|
|
8672
|
+
required: {
|
|
8673
|
+
display: "grid"
|
|
8674
|
+
},
|
|
8675
|
+
optional: ["gridTemplateColumns", "gap"],
|
|
8676
|
+
minMatches: 1
|
|
8677
|
+
// Lower because gridTemplateColumns varies
|
|
8678
|
+
},
|
|
8679
|
+
// --- Positioning Patterns ---
|
|
8680
|
+
{
|
|
8681
|
+
name: "absolute-center",
|
|
8682
|
+
description: "Absolute positioning centering",
|
|
8683
|
+
macro: "absolute({ top: '50%', left: '50%' })",
|
|
8684
|
+
example: "chain.absolute({ top: '50%', left: '50%' }).transform('translate(-50%, -50%)')",
|
|
8685
|
+
required: {
|
|
8686
|
+
position: "absolute",
|
|
8687
|
+
top: "50%",
|
|
8688
|
+
left: "50%"
|
|
8689
|
+
},
|
|
8690
|
+
optional: ["transform"],
|
|
8691
|
+
minMatches: 3
|
|
8692
|
+
},
|
|
8693
|
+
{
|
|
8694
|
+
name: "sticky-top",
|
|
8695
|
+
description: "Sticky element at top",
|
|
8696
|
+
macro: "stickyHeader()",
|
|
8697
|
+
example: "chain.stickyHeader()",
|
|
8698
|
+
// from intent macros
|
|
8699
|
+
required: {
|
|
8700
|
+
position: "sticky",
|
|
8701
|
+
top: "0"
|
|
8702
|
+
},
|
|
8703
|
+
optional: ["zIndex", "backgroundColor", "backdropFilter"],
|
|
8704
|
+
minMatches: 2
|
|
8705
|
+
},
|
|
8706
|
+
// --- Sizing Patterns ---
|
|
8707
|
+
{
|
|
8708
|
+
name: "full-size",
|
|
8709
|
+
description: "Full width and height",
|
|
8710
|
+
macro: "chain.size('100%')",
|
|
8711
|
+
example: "chain.size('100%')",
|
|
8712
|
+
required: {
|
|
8713
|
+
width: "100%",
|
|
8714
|
+
height: "100%"
|
|
8715
|
+
},
|
|
8716
|
+
minMatches: 2
|
|
8717
|
+
},
|
|
8718
|
+
{
|
|
8719
|
+
name: "pill-shape",
|
|
8720
|
+
description: "Fully rounded pill element",
|
|
8721
|
+
macro: "pill()",
|
|
8722
|
+
example: "chain.pill()",
|
|
8723
|
+
required: {
|
|
8724
|
+
borderRadius: "9999px"
|
|
8725
|
+
},
|
|
8726
|
+
optional: ["padding", "display"],
|
|
8727
|
+
minMatches: 1
|
|
8728
|
+
},
|
|
8729
|
+
// --- Intent Macros (from intent-engine.ts) ---
|
|
8730
|
+
{
|
|
8731
|
+
name: "sticky-header",
|
|
8732
|
+
description: "Sticky header with backdrop blur",
|
|
8733
|
+
macro: "stickyHeader()",
|
|
8734
|
+
example: "chain.stickyHeader()",
|
|
8735
|
+
required: {
|
|
8736
|
+
position: "sticky",
|
|
8737
|
+
top: "0"
|
|
8738
|
+
},
|
|
8739
|
+
optional: ["zIndex", "backgroundColor", "backdropFilter", "borderBottom", "padding"],
|
|
8740
|
+
minMatches: 2
|
|
8741
|
+
},
|
|
8742
|
+
{
|
|
8743
|
+
name: "card-layout",
|
|
8744
|
+
description: "Card container with shadow and hover lift",
|
|
8745
|
+
macro: "card()",
|
|
8746
|
+
example: "chain.card()",
|
|
8747
|
+
required: {
|
|
8748
|
+
borderRadius: "12px",
|
|
8749
|
+
overflow: "hidden"
|
|
8750
|
+
},
|
|
8751
|
+
optional: ["display", "flexDirection", "backgroundColor", "boxShadow", "transition"],
|
|
8752
|
+
minMatches: 2
|
|
8753
|
+
},
|
|
8754
|
+
{
|
|
8755
|
+
name: "hero-section",
|
|
8756
|
+
description: "Full-width centered hero",
|
|
8757
|
+
macro: "hero()",
|
|
8758
|
+
example: "chain.hero()",
|
|
8759
|
+
required: {
|
|
8760
|
+
display: "flex",
|
|
8761
|
+
flexDirection: "column",
|
|
8762
|
+
justifyContent: "center",
|
|
8763
|
+
alignItems: "center",
|
|
8764
|
+
width: "100%"
|
|
8765
|
+
},
|
|
8766
|
+
optional: ["minHeight", "padding", "textAlign"],
|
|
8767
|
+
minMatches: 4
|
|
8768
|
+
},
|
|
8769
|
+
{
|
|
8770
|
+
name: "container-layout",
|
|
8771
|
+
description: "Centered max-width container",
|
|
8772
|
+
macro: "container()",
|
|
8773
|
+
example: "chain.container()",
|
|
8774
|
+
required: {
|
|
8775
|
+
marginLeft: "auto",
|
|
8776
|
+
marginRight: "auto"
|
|
8777
|
+
},
|
|
8778
|
+
optional: ["width", "maxWidth", "paddingLeft", "paddingRight"],
|
|
8779
|
+
minMatches: 2
|
|
8780
|
+
},
|
|
8781
|
+
{
|
|
8782
|
+
name: "sidebar-layout",
|
|
8783
|
+
description: "Sidebar + main content grid",
|
|
8784
|
+
macro: "sidebar()",
|
|
8785
|
+
example: "chain.sidebar()",
|
|
8786
|
+
required: {
|
|
8787
|
+
display: "grid"
|
|
8788
|
+
},
|
|
8789
|
+
optional: ["gridTemplateColumns", "gap", "minHeight"],
|
|
8790
|
+
minMatches: 1
|
|
8791
|
+
},
|
|
8792
|
+
{
|
|
8793
|
+
name: "grid-list",
|
|
8794
|
+
description: "Responsive auto-fit grid list",
|
|
8795
|
+
macro: "gridList()",
|
|
8796
|
+
example: "chain.gridList()",
|
|
8797
|
+
required: {
|
|
8798
|
+
display: "grid"
|
|
8799
|
+
},
|
|
8800
|
+
optional: ["gridTemplateColumns", "gap"],
|
|
8801
|
+
minMatches: 1
|
|
8802
|
+
},
|
|
8803
|
+
{
|
|
8804
|
+
name: "truncate-text",
|
|
8805
|
+
description: "Single-line text truncation with ellipsis",
|
|
8806
|
+
macro: "truncate()",
|
|
8807
|
+
example: "chain.truncate()",
|
|
8808
|
+
required: {
|
|
8809
|
+
overflow: "hidden",
|
|
8810
|
+
textOverflow: "ellipsis",
|
|
8811
|
+
whiteSpace: "nowrap"
|
|
8812
|
+
},
|
|
8813
|
+
minMatches: 3
|
|
8814
|
+
},
|
|
8815
|
+
{
|
|
8816
|
+
name: "sr-only",
|
|
8817
|
+
description: "Screen-reader only element",
|
|
8818
|
+
macro: "srOnly()",
|
|
8819
|
+
example: "chain.srOnly()",
|
|
8820
|
+
required: {
|
|
8821
|
+
position: "absolute",
|
|
8822
|
+
width: "1px",
|
|
8823
|
+
height: "1px"
|
|
8824
|
+
},
|
|
8825
|
+
optional: ["padding", "margin", "overflow", "clip"],
|
|
8826
|
+
minMatches: 2
|
|
8827
|
+
},
|
|
8828
|
+
// --- Chain.ts Special Methods ---
|
|
8829
|
+
{
|
|
8830
|
+
name: "inline-flex",
|
|
8831
|
+
description: "Inline flex container",
|
|
8832
|
+
macro: "inlineFlex()",
|
|
8833
|
+
example: "chain.inlineFlex()",
|
|
8834
|
+
required: {
|
|
8835
|
+
display: "inline-flex"
|
|
8836
|
+
},
|
|
8837
|
+
minMatches: 1
|
|
8838
|
+
},
|
|
8839
|
+
{
|
|
8840
|
+
name: "inline-grid",
|
|
8841
|
+
description: "Inline grid container",
|
|
8842
|
+
macro: "inlineGrid()",
|
|
8843
|
+
example: "chain.inlineGrid()",
|
|
8844
|
+
required: {
|
|
8845
|
+
display: "inline-grid"
|
|
8846
|
+
},
|
|
8847
|
+
minMatches: 1
|
|
8848
|
+
},
|
|
8849
|
+
{
|
|
8850
|
+
name: "flex-center-direction",
|
|
8851
|
+
description: "Flex centering with direction",
|
|
8852
|
+
macro: "flexCenter('row')",
|
|
8853
|
+
example: "chain.flexCenter('col')",
|
|
8854
|
+
required: {
|
|
8855
|
+
display: "flex",
|
|
8856
|
+
justifyContent: "center",
|
|
8857
|
+
alignItems: "center"
|
|
8858
|
+
},
|
|
8859
|
+
optional: ["flexDirection"],
|
|
8860
|
+
minMatches: 3
|
|
8861
|
+
},
|
|
8862
|
+
{
|
|
8863
|
+
name: "fixed-position",
|
|
8864
|
+
description: "Fixed positioning",
|
|
8865
|
+
macro: "fixed()",
|
|
8866
|
+
example: "chain.fixed({ top: 0 })",
|
|
8867
|
+
required: {
|
|
8868
|
+
position: "fixed"
|
|
8869
|
+
},
|
|
8870
|
+
optional: ["top", "right", "bottom", "left", "zIndex"],
|
|
8871
|
+
minMatches: 1
|
|
8872
|
+
},
|
|
8873
|
+
{
|
|
8874
|
+
name: "relative-position",
|
|
8875
|
+
description: "Relative positioning",
|
|
8876
|
+
macro: "relative()",
|
|
8877
|
+
example: "chain.relative()",
|
|
8878
|
+
required: {
|
|
8879
|
+
position: "relative"
|
|
8880
|
+
},
|
|
8881
|
+
minMatches: 1
|
|
8882
|
+
},
|
|
8883
|
+
{
|
|
8884
|
+
name: "hidden-element",
|
|
8885
|
+
description: "Hidden element",
|
|
8886
|
+
macro: "hide()",
|
|
8887
|
+
example: "chain.hide()",
|
|
8888
|
+
required: {
|
|
8889
|
+
display: "none"
|
|
8890
|
+
},
|
|
8891
|
+
minMatches: 1
|
|
8892
|
+
},
|
|
8893
|
+
{
|
|
8894
|
+
name: "unselectable",
|
|
8895
|
+
description: "Unselectable text",
|
|
8896
|
+
macro: "unselectable()",
|
|
8897
|
+
example: "chain.unselectable()",
|
|
8898
|
+
required: {
|
|
8899
|
+
userSelect: "none"
|
|
8900
|
+
},
|
|
8901
|
+
minMatches: 1
|
|
8902
|
+
},
|
|
8903
|
+
{
|
|
8904
|
+
name: "scrollable",
|
|
8905
|
+
description: "Scrollable container",
|
|
8906
|
+
macro: "scrollable()",
|
|
8907
|
+
example: "chain.scrollable()",
|
|
8908
|
+
required: {
|
|
8909
|
+
overflow: "auto"
|
|
8910
|
+
},
|
|
8911
|
+
minMatches: 1
|
|
8912
|
+
},
|
|
8913
|
+
{
|
|
8914
|
+
name: "square-shape",
|
|
8915
|
+
description: "Square element with equal sides",
|
|
8916
|
+
macro: "square()",
|
|
8917
|
+
example: "chain.square(100)",
|
|
8918
|
+
required: {
|
|
8919
|
+
width: "100px",
|
|
8920
|
+
height: "100px"
|
|
8921
|
+
},
|
|
8922
|
+
optional: ["borderRadius"],
|
|
8923
|
+
minMatches: 2
|
|
8924
|
+
},
|
|
8925
|
+
{
|
|
8926
|
+
name: "circle-shape",
|
|
8927
|
+
description: "Circle element",
|
|
8928
|
+
macro: "circle()",
|
|
8929
|
+
example: "chain.circle(50)",
|
|
8930
|
+
required: {
|
|
8931
|
+
borderRadius: "50%"
|
|
8932
|
+
},
|
|
8933
|
+
optional: ["width", "height"],
|
|
8934
|
+
minMatches: 1
|
|
8935
|
+
},
|
|
8936
|
+
{
|
|
8937
|
+
name: "bento-grid",
|
|
8938
|
+
description: "Bento box grid layout",
|
|
8939
|
+
macro: "bento()",
|
|
8940
|
+
example: "chain.bento(3)",
|
|
8941
|
+
required: {
|
|
8942
|
+
display: "grid"
|
|
8943
|
+
},
|
|
8944
|
+
optional: ["gridTemplateColumns", "gap", "gridAutoRows"],
|
|
8945
|
+
minMatches: 1
|
|
8946
|
+
},
|
|
8947
|
+
{
|
|
8948
|
+
name: "focus-ring",
|
|
8949
|
+
description: "Focus ring outline",
|
|
8950
|
+
macro: "focusRing()",
|
|
8951
|
+
example: "chain.focusRing()",
|
|
8952
|
+
required: {
|
|
8953
|
+
outline: "2px solid #3b82f6"
|
|
8954
|
+
},
|
|
8955
|
+
optional: ["outlineOffset"],
|
|
8956
|
+
minMatches: 1
|
|
8957
|
+
},
|
|
8958
|
+
{
|
|
8959
|
+
name: "shimmer-effect",
|
|
8960
|
+
description: "Shimmer loading animation",
|
|
8961
|
+
macro: "shimmer()",
|
|
8962
|
+
example: "chain.shimmer()",
|
|
8963
|
+
required: {
|
|
8964
|
+
background: "linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%)"
|
|
8965
|
+
},
|
|
8966
|
+
optional: ["backgroundSize", "animation"],
|
|
8967
|
+
minMatches: 1
|
|
8968
|
+
},
|
|
8969
|
+
{
|
|
8970
|
+
name: "skeleton-loader",
|
|
8971
|
+
description: "Skeleton loading state",
|
|
8972
|
+
macro: "skeleton()",
|
|
8973
|
+
example: "chain.skeleton(true)",
|
|
8974
|
+
required: {
|
|
8975
|
+
animation: "pulse 1.5s ease-in-out infinite"
|
|
8976
|
+
},
|
|
8977
|
+
optional: ["backgroundColor", "borderRadius"],
|
|
8978
|
+
minMatches: 1
|
|
8979
|
+
},
|
|
8980
|
+
{
|
|
8981
|
+
name: "safe-area-bottom",
|
|
8982
|
+
description: "Safe area padding for notched devices",
|
|
8983
|
+
macro: "safeArea('bottom')",
|
|
8984
|
+
example: "chain.safeArea('bottom')",
|
|
8985
|
+
required: {
|
|
8986
|
+
paddingBottom: "env(safe-area-inset-bottom)"
|
|
8987
|
+
},
|
|
8988
|
+
minMatches: 1
|
|
8989
|
+
},
|
|
8990
|
+
// --- Glass Morphism ---
|
|
8991
|
+
{
|
|
8992
|
+
name: "glass-effect",
|
|
8993
|
+
description: "Frosted glass effect",
|
|
8994
|
+
macro: "glass()",
|
|
8995
|
+
example: "chain.glass()",
|
|
8996
|
+
required: {
|
|
8997
|
+
backdropFilter: "blur(16px)"
|
|
8998
|
+
},
|
|
8999
|
+
optional: ["backgroundColor", "border", "borderRadius"],
|
|
9000
|
+
minMatches: 1
|
|
9001
|
+
}
|
|
9002
|
+
];
|
|
9003
|
+
function matchPattern(rule, pattern) {
|
|
9004
|
+
const declarations = rule.declarations;
|
|
9005
|
+
const propMap = new Map(declarations.map((d) => [d.property, String(d.value)]));
|
|
9006
|
+
const matchedProperties = [];
|
|
9007
|
+
let totalRequired = Object.keys(pattern.required).length;
|
|
9008
|
+
let matched = 0;
|
|
9009
|
+
for (const [prop, expectedValue] of Object.entries(pattern.required)) {
|
|
9010
|
+
const actualValue = propMap.get(prop);
|
|
9011
|
+
if (actualValue === String(expectedValue)) {
|
|
9012
|
+
matched++;
|
|
9013
|
+
matchedProperties.push(prop);
|
|
9014
|
+
}
|
|
9015
|
+
}
|
|
9016
|
+
if (pattern.optional) {
|
|
9017
|
+
for (const prop of pattern.optional) {
|
|
9018
|
+
if (propMap.has(prop)) {
|
|
9019
|
+
matchedProperties.push(prop);
|
|
9020
|
+
}
|
|
9021
|
+
}
|
|
9022
|
+
}
|
|
9023
|
+
const minMatches = pattern.minMatches || Object.keys(pattern.required).length;
|
|
9024
|
+
const confidence = matched >= minMatches ? Math.min(1, matched / totalRequired) : 0;
|
|
9025
|
+
if (confidence >= 0.75 && matched >= minMatches) {
|
|
9026
|
+
return {
|
|
9027
|
+
pattern,
|
|
9028
|
+
ruleId: rule.id,
|
|
9029
|
+
selector: rule.selector,
|
|
9030
|
+
matchedProperties,
|
|
9031
|
+
confidence
|
|
9032
|
+
};
|
|
9033
|
+
}
|
|
9034
|
+
return null;
|
|
9035
|
+
}
|
|
9036
|
+
function findDuplicates(matches) {
|
|
9037
|
+
const patternGroups = /* @__PURE__ */ new Map();
|
|
9038
|
+
for (const match of matches) {
|
|
9039
|
+
const key = match.pattern.name;
|
|
9040
|
+
const group = patternGroups.get(key) || [];
|
|
9041
|
+
group.push(match);
|
|
9042
|
+
patternGroups.set(key, group);
|
|
9043
|
+
}
|
|
9044
|
+
const duplicates = [];
|
|
9045
|
+
for (const [patternName, group] of patternGroups) {
|
|
9046
|
+
if (group.length >= 2) {
|
|
9047
|
+
duplicates.push({
|
|
9048
|
+
pattern: patternName,
|
|
9049
|
+
selectors: group.map((m) => m.selector),
|
|
9050
|
+
count: group.length
|
|
9051
|
+
});
|
|
9052
|
+
}
|
|
9053
|
+
}
|
|
9054
|
+
return duplicates;
|
|
9055
|
+
}
|
|
9056
|
+
function generateSuggestions(matches) {
|
|
9057
|
+
const suggestions = [];
|
|
9058
|
+
for (const match of matches) {
|
|
9059
|
+
if (match.confidence >= 0.85) {
|
|
9060
|
+
const propsCount = match.matchedProperties.length;
|
|
9061
|
+
suggestions.push({
|
|
9062
|
+
selector: match.selector,
|
|
9063
|
+
suggestion: match.pattern.macro,
|
|
9064
|
+
savings: propsCount - 1
|
|
9065
|
+
// Saving N-1 declarations
|
|
9066
|
+
});
|
|
9067
|
+
}
|
|
9068
|
+
}
|
|
9069
|
+
return suggestions;
|
|
9070
|
+
}
|
|
9071
|
+
var layoutIntelligencePass = (ir) => {
|
|
9072
|
+
const allMatches = [];
|
|
9073
|
+
for (const rule of ir.rules) {
|
|
9074
|
+
for (const pattern of LAYOUT_PATTERNS) {
|
|
9075
|
+
const match = matchPattern(rule, pattern);
|
|
9076
|
+
if (match) {
|
|
9077
|
+
allMatches.push(match);
|
|
9078
|
+
rule.meta.layoutPattern = pattern.name;
|
|
9079
|
+
rule.meta.layoutConfidence = match.confidence;
|
|
9080
|
+
}
|
|
9081
|
+
}
|
|
9082
|
+
}
|
|
9083
|
+
const duplicates = findDuplicates(allMatches);
|
|
9084
|
+
for (const dup of duplicates) {
|
|
9085
|
+
ir.diagnostics.push({
|
|
9086
|
+
id: "layout-dup-" + Date.now() + "-" + dup.pattern,
|
|
9087
|
+
nodeId: ir.rules[0]?.id || ir.id,
|
|
9088
|
+
severity: "info",
|
|
9089
|
+
message: 'Layout pattern "' + dup.pattern + '" found ' + dup.count + " times: " + dup.selectors.join(", "),
|
|
9090
|
+
suggestion: "Consider extracting: " + (LAYOUT_PATTERNS.find((p) => p.name === dup.pattern)?.macro || ""),
|
|
9091
|
+
pass: "layout-intelligence"
|
|
9092
|
+
});
|
|
9093
|
+
}
|
|
9094
|
+
const suggestions = generateSuggestions(allMatches);
|
|
9095
|
+
for (const sug of suggestions) {
|
|
9096
|
+
ir.diagnostics.push({
|
|
9097
|
+
id: "layout-sug-" + Date.now() + "-" + sug.selector.replace(/[.#]/g, ""),
|
|
9098
|
+
nodeId: ir.rules.find((r) => r.selector === sug.selector)?.id || ir.id,
|
|
9099
|
+
severity: "hint",
|
|
9100
|
+
message: '"' + sug.selector + '" could use ' + sug.suggestion + " (save " + sug.savings + " declarations)",
|
|
9101
|
+
suggestion: sug.suggestion,
|
|
9102
|
+
pass: "layout-intelligence"
|
|
9103
|
+
});
|
|
9104
|
+
}
|
|
9105
|
+
ir.meta = ir.meta || {};
|
|
9106
|
+
ir.meta.layoutMatches = allMatches;
|
|
9107
|
+
ir.meta.layoutDuplicates = duplicates;
|
|
9108
|
+
return ir;
|
|
9109
|
+
};
|
|
9110
|
+
function recognizeLayout(declarations) {
|
|
9111
|
+
const rule = {
|
|
9112
|
+
id: "temp-rule",
|
|
9113
|
+
selector: ".temp",
|
|
9114
|
+
declarations: Object.entries(declarations).map(([prop, value]) => ({
|
|
9115
|
+
id: "temp-decl-" + prop,
|
|
9116
|
+
property: prop,
|
|
9117
|
+
value,
|
|
9118
|
+
history: [],
|
|
9119
|
+
meta: {}
|
|
9120
|
+
})),
|
|
9121
|
+
pseudoClasses: [],
|
|
9122
|
+
atRules: [],
|
|
9123
|
+
nestedRules: [],
|
|
9124
|
+
conditions: [],
|
|
9125
|
+
isDead: false,
|
|
9126
|
+
specificity: 0,
|
|
9127
|
+
hash: "",
|
|
9128
|
+
source: {},
|
|
9129
|
+
history: [],
|
|
9130
|
+
meta: {}
|
|
9131
|
+
};
|
|
9132
|
+
const matches = [];
|
|
9133
|
+
for (const pattern of LAYOUT_PATTERNS) {
|
|
9134
|
+
const match = matchPattern(rule, pattern);
|
|
9135
|
+
if (match) matches.push(match);
|
|
9136
|
+
}
|
|
9137
|
+
return matches;
|
|
9138
|
+
}
|
|
9139
|
+
function suggestMacro(declarations) {
|
|
9140
|
+
const matches = recognizeLayout(declarations);
|
|
9141
|
+
if (matches.length === 0) return null;
|
|
9142
|
+
const best = matches.sort((a, b) => b.confidence - a.confidence)[0];
|
|
9143
|
+
return best.confidence >= 0.85 ? best.pattern.macro : null;
|
|
9144
|
+
}
|
|
9145
|
+
function getLayoutPatterns() {
|
|
9146
|
+
return [...LAYOUT_PATTERNS];
|
|
9147
|
+
}
|
|
9148
|
+
var layoutIntelligence = {
|
|
9149
|
+
recognize: recognizeLayout,
|
|
9150
|
+
suggestMacro,
|
|
9151
|
+
getPatterns: getLayoutPatterns,
|
|
9152
|
+
pass: layoutIntelligencePass,
|
|
9153
|
+
patterns: LAYOUT_PATTERNS
|
|
9154
|
+
};
|
|
9155
|
+
|
|
9156
|
+
// src/compiler/responsive-inference.ts
|
|
9157
|
+
var MOBILE_BREAKPOINT = 768;
|
|
9158
|
+
var TABLET_BREAKPOINT = 1024;
|
|
9159
|
+
var LARGE_FONT_THRESHOLD = 32;
|
|
9160
|
+
var LARGE_PADDING_THRESHOLD = 48;
|
|
9161
|
+
var LARGE_GAP_THRESHOLD = 32;
|
|
9162
|
+
var MAX_GRID_COLUMNS = 2;
|
|
9163
|
+
function detectFixedWidth(rule) {
|
|
9164
|
+
const issues = [];
|
|
9165
|
+
for (const decl of rule.declarations) {
|
|
9166
|
+
if ((decl.property === "width" || decl.property === "max-width") && typeof decl.value === "string") {
|
|
9167
|
+
const pxMatch = decl.value.match(/^(\d+(\.\d+)?)px$/);
|
|
9168
|
+
if (pxMatch) {
|
|
9169
|
+
const px = parseFloat(pxMatch[1]);
|
|
9170
|
+
if (px > MOBILE_BREAKPOINT) {
|
|
9171
|
+
issues.push({
|
|
9172
|
+
ruleId: rule.id,
|
|
9173
|
+
selector: rule.selector,
|
|
9174
|
+
property: decl.property,
|
|
9175
|
+
currentValue: decl.value,
|
|
9176
|
+
severity: px > TABLET_BREAKPOINT ? "error" : "warning",
|
|
9177
|
+
category: "overflow",
|
|
9178
|
+
message: "Fixed " + decl.property + ": " + decl.value + " will overflow on viewports < " + px + "px",
|
|
9179
|
+
suggestedFix: decl.property + ": min(100%, " + decl.value + ");",
|
|
9180
|
+
autoFixAvailable: true,
|
|
9181
|
+
affectedViewports: ["mobile", "tablet"]
|
|
9182
|
+
});
|
|
9183
|
+
}
|
|
9184
|
+
}
|
|
9185
|
+
}
|
|
9186
|
+
}
|
|
9187
|
+
return issues;
|
|
9188
|
+
}
|
|
9189
|
+
function detectGridColumns(rule) {
|
|
9190
|
+
const issues = [];
|
|
9191
|
+
for (const decl of rule.declarations) {
|
|
9192
|
+
if ((decl.property === "gridTemplateColumns" || decl.property === "grid-template-columns") && typeof decl.value === "string") {
|
|
9193
|
+
const columns = decl.value.split(/\s+/).filter(
|
|
9194
|
+
(c) => c.includes("fr") || c.includes("px") || c.includes("%")
|
|
9195
|
+
);
|
|
9196
|
+
const explicitColumns = columns.length;
|
|
9197
|
+
if (explicitColumns > MAX_GRID_COLUMNS) {
|
|
9198
|
+
const isFixed = columns.every((c) => c.includes("px"));
|
|
9199
|
+
const suggestion = isFixed ? "repeat(auto-fit, minmax(" + columns[0] + ", 1fr))" : "repeat(auto-fit, minmax(250px, 1fr))";
|
|
9200
|
+
issues.push({
|
|
9201
|
+
ruleId: rule.id,
|
|
9202
|
+
selector: rule.selector,
|
|
9203
|
+
property: decl.property,
|
|
9204
|
+
currentValue: decl.value,
|
|
9205
|
+
severity: explicitColumns >= 4 ? "error" : "warning",
|
|
9206
|
+
category: "grid",
|
|
9207
|
+
message: explicitColumns + " columns will not fit on mobile screens (\u2264 " + MOBILE_BREAKPOINT + "px)",
|
|
9208
|
+
suggestedFix: "grid-template-columns: " + suggestion + ";",
|
|
9209
|
+
autoFixAvailable: true,
|
|
9210
|
+
affectedViewports: ["mobile"]
|
|
9211
|
+
});
|
|
9212
|
+
}
|
|
9213
|
+
}
|
|
9214
|
+
if ((decl.property === "gridTemplateColumns" || decl.property === "grid-template-columns") && typeof decl.value === "string" && decl.value.includes("auto-fit") && !decl.value.includes("minmax")) {
|
|
9215
|
+
issues.push({
|
|
9216
|
+
ruleId: rule.id,
|
|
9217
|
+
selector: rule.selector,
|
|
9218
|
+
property: decl.property,
|
|
9219
|
+
currentValue: decl.value,
|
|
9220
|
+
severity: "info",
|
|
9221
|
+
category: "grid",
|
|
9222
|
+
message: "auto-fit without minmax() may collapse to 0 on empty containers. Consider: repeat(auto-fit, minmax(250px, 1fr))",
|
|
9223
|
+
suggestedFix: "grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));",
|
|
9224
|
+
autoFixAvailable: true,
|
|
9225
|
+
affectedViewports: ["all"]
|
|
9226
|
+
});
|
|
9227
|
+
}
|
|
9228
|
+
}
|
|
9229
|
+
return issues;
|
|
9230
|
+
}
|
|
9231
|
+
function detectLargeTypography(rule) {
|
|
9232
|
+
const issues = [];
|
|
9233
|
+
for (const decl of rule.declarations) {
|
|
9234
|
+
if ((decl.property === "fontSize" || decl.property === "font-size") && typeof decl.value === "string") {
|
|
9235
|
+
const pxMatch = decl.value.match(/^(\d+(\.\d+)?)px$/);
|
|
9236
|
+
if (pxMatch) {
|
|
9237
|
+
const px = parseFloat(pxMatch[1]);
|
|
9238
|
+
if (px > LARGE_FONT_THRESHOLD) {
|
|
9239
|
+
const minSize = Math.round(px * 0.5);
|
|
9240
|
+
issues.push({
|
|
9241
|
+
ruleId: rule.id,
|
|
9242
|
+
selector: rule.selector,
|
|
9243
|
+
property: decl.property,
|
|
9244
|
+
currentValue: decl.value,
|
|
9245
|
+
severity: "warning",
|
|
9246
|
+
category: "typography",
|
|
9247
|
+
message: "font-size: " + decl.value + " may be too large on mobile. Consider responsive scaling.",
|
|
9248
|
+
suggestedFix: "font-size: clamp(" + minSize + "px, " + Math.round(px / TABLET_BREAKPOINT * 100) + "vw, " + px + "px);",
|
|
9249
|
+
autoFixAvailable: true,
|
|
9250
|
+
affectedViewports: ["mobile", "tablet"]
|
|
9251
|
+
});
|
|
9252
|
+
}
|
|
9253
|
+
}
|
|
9254
|
+
}
|
|
9255
|
+
}
|
|
9256
|
+
return issues;
|
|
9257
|
+
}
|
|
9258
|
+
function detectLargeSpacing(rule) {
|
|
9259
|
+
const issues = [];
|
|
9260
|
+
for (const decl of rule.declarations) {
|
|
9261
|
+
if ((decl.property === "padding" || decl.property.startsWith("padding")) && typeof decl.value === "string") {
|
|
9262
|
+
const pxMatch = decl.value.match(/^(\d+(\.\d+)?)px$/);
|
|
9263
|
+
if (pxMatch) {
|
|
9264
|
+
const px = parseFloat(pxMatch[1]);
|
|
9265
|
+
if (px > LARGE_PADDING_THRESHOLD) {
|
|
9266
|
+
issues.push({
|
|
9267
|
+
ruleId: rule.id,
|
|
9268
|
+
selector: rule.selector,
|
|
9269
|
+
property: decl.property,
|
|
9270
|
+
currentValue: decl.value,
|
|
9271
|
+
severity: "info",
|
|
9272
|
+
category: "spacing",
|
|
9273
|
+
message: decl.property + ": " + decl.value + " may be excessive on mobile. Consider reducing to " + Math.round(px * 0.5) + "px on small screens.",
|
|
9274
|
+
suggestedFix: "@media (max-width: 768px) { " + rule.selector + " { " + decl.property + ": " + Math.round(px * 0.5) + "px; } }",
|
|
9275
|
+
autoFixAvailable: true,
|
|
9276
|
+
affectedViewports: ["mobile"]
|
|
9277
|
+
});
|
|
9278
|
+
}
|
|
9279
|
+
}
|
|
9280
|
+
}
|
|
9281
|
+
}
|
|
9282
|
+
return issues;
|
|
9283
|
+
}
|
|
9284
|
+
function detectViewportUnits(rule) {
|
|
9285
|
+
const issues = [];
|
|
9286
|
+
for (const decl of rule.declarations) {
|
|
9287
|
+
if ((decl.property === "height" || decl.property === "min-height") && typeof decl.value === "string" && decl.value.includes("100vh")) {
|
|
9288
|
+
issues.push({
|
|
9289
|
+
ruleId: rule.id,
|
|
9290
|
+
selector: rule.selector,
|
|
9291
|
+
property: decl.property,
|
|
9292
|
+
currentValue: decl.value,
|
|
9293
|
+
severity: "warning",
|
|
9294
|
+
category: "viewport",
|
|
9295
|
+
message: "100vh can cause issues on mobile browsers with dynamic toolbars. Consider 100dvh instead.",
|
|
9296
|
+
suggestedFix: decl.property + ": 100dvh;",
|
|
9297
|
+
autoFixAvailable: true,
|
|
9298
|
+
affectedViewports: ["mobile"]
|
|
9299
|
+
});
|
|
9300
|
+
}
|
|
9301
|
+
}
|
|
9302
|
+
return issues;
|
|
9303
|
+
}
|
|
9304
|
+
function detectLargeGaps(rule) {
|
|
9305
|
+
const issues = [];
|
|
9306
|
+
for (const decl of rule.declarations) {
|
|
9307
|
+
if ((decl.property === "gap" || decl.property === "grid-gap") && typeof decl.value === "string") {
|
|
9308
|
+
const pxMatch = decl.value.match(/^(\d+(\.\d+)?)px$/);
|
|
9309
|
+
if (pxMatch) {
|
|
9310
|
+
const px = parseFloat(pxMatch[1]);
|
|
9311
|
+
if (px > LARGE_GAP_THRESHOLD) {
|
|
9312
|
+
issues.push({
|
|
9313
|
+
ruleId: rule.id,
|
|
9314
|
+
selector: rule.selector,
|
|
9315
|
+
property: decl.property,
|
|
9316
|
+
currentValue: decl.value,
|
|
9317
|
+
severity: "info",
|
|
9318
|
+
category: "spacing",
|
|
9319
|
+
message: "gap: " + decl.value + " may be too large on mobile. Consider reducing to " + Math.round(px * 0.5) + "px on small screens.",
|
|
9320
|
+
suggestedFix: "@media (max-width: 768px) { " + rule.selector + " { gap: " + Math.round(px * 0.5) + "px; } }",
|
|
9321
|
+
autoFixAvailable: true,
|
|
9322
|
+
affectedViewports: ["mobile"]
|
|
9323
|
+
});
|
|
9324
|
+
}
|
|
9325
|
+
}
|
|
9326
|
+
}
|
|
9327
|
+
}
|
|
9328
|
+
return issues;
|
|
9329
|
+
}
|
|
9330
|
+
var responsiveInferencePass = (ir) => {
|
|
9331
|
+
const allIssues = [];
|
|
9332
|
+
for (const rule of ir.rules) {
|
|
9333
|
+
if (rule.isDead) continue;
|
|
9334
|
+
allIssues.push(...detectFixedWidth(rule));
|
|
9335
|
+
allIssues.push(...detectGridColumns(rule));
|
|
9336
|
+
allIssues.push(...detectLargeTypography(rule));
|
|
9337
|
+
allIssues.push(...detectLargeSpacing(rule));
|
|
9338
|
+
allIssues.push(...detectViewportUnits(rule));
|
|
9339
|
+
allIssues.push(...detectLargeGaps(rule));
|
|
9340
|
+
}
|
|
9341
|
+
for (const issue of allIssues) {
|
|
9342
|
+
ir.diagnostics.push({
|
|
9343
|
+
id: "responsive-" + issue.category + "-" + Date.now() + "-" + Math.random().toString(36).slice(2, 6),
|
|
9344
|
+
nodeId: issue.ruleId,
|
|
9345
|
+
severity: issue.severity,
|
|
9346
|
+
message: issue.message,
|
|
9347
|
+
suggestion: issue.suggestedFix,
|
|
9348
|
+
pass: "responsive-inference"
|
|
9349
|
+
});
|
|
9350
|
+
}
|
|
9351
|
+
ir.meta = ir.meta || {};
|
|
9352
|
+
ir.meta.responsiveIssues = allIssues;
|
|
9353
|
+
return ir;
|
|
9354
|
+
};
|
|
9355
|
+
function analyzeResponsive(selector, declarations) {
|
|
9356
|
+
const rule = {
|
|
9357
|
+
id: "temp-responsive",
|
|
9358
|
+
selector,
|
|
9359
|
+
declarations: Object.entries(declarations).map(([prop, value]) => ({
|
|
9360
|
+
id: "temp-" + prop,
|
|
9361
|
+
property: prop,
|
|
9362
|
+
value,
|
|
9363
|
+
history: [],
|
|
9364
|
+
meta: {}
|
|
9365
|
+
})),
|
|
9366
|
+
pseudoClasses: [],
|
|
9367
|
+
atRules: [],
|
|
9368
|
+
nestedRules: [],
|
|
9369
|
+
conditions: [],
|
|
9370
|
+
isDead: false,
|
|
9371
|
+
specificity: 0,
|
|
9372
|
+
hash: "",
|
|
9373
|
+
source: {},
|
|
9374
|
+
history: [],
|
|
9375
|
+
meta: {}
|
|
9376
|
+
};
|
|
9377
|
+
return [
|
|
9378
|
+
...detectFixedWidth(rule),
|
|
9379
|
+
...detectGridColumns(rule),
|
|
9380
|
+
...detectLargeTypography(rule),
|
|
9381
|
+
...detectLargeSpacing(rule),
|
|
9382
|
+
...detectViewportUnits(rule),
|
|
9383
|
+
...detectLargeGaps(rule)
|
|
9384
|
+
];
|
|
9385
|
+
}
|
|
9386
|
+
function generateResponsiveReport(issues) {
|
|
9387
|
+
const critical = issues.filter((i) => i.severity === "error");
|
|
9388
|
+
const warnings = issues.filter((i) => i.severity === "warning");
|
|
9389
|
+
const info = issues.filter((i) => i.severity === "info");
|
|
9390
|
+
let summary;
|
|
9391
|
+
if (issues.length === 0) {
|
|
9392
|
+
summary = "\u2705 No responsive issues detected.";
|
|
9393
|
+
} else if (critical.length > 0) {
|
|
9394
|
+
summary = "\u274C " + critical.length + " critical, " + warnings.length + " warnings, " + info.length + " suggestions.";
|
|
9395
|
+
} else if (warnings.length > 0) {
|
|
9396
|
+
summary = "\u26A0\uFE0F " + warnings.length + " warnings, " + info.length + " suggestions.";
|
|
9397
|
+
} else {
|
|
9398
|
+
summary = "\u{1F4A1} " + info.length + " responsive suggestions.";
|
|
9399
|
+
}
|
|
9400
|
+
return {
|
|
9401
|
+
issues,
|
|
9402
|
+
criticalCount: critical.length,
|
|
9403
|
+
warningCount: warnings.length,
|
|
9404
|
+
infoCount: info.length,
|
|
9405
|
+
summary
|
|
9406
|
+
};
|
|
9407
|
+
}
|
|
9408
|
+
function autoFixIssue(issue) {
|
|
9409
|
+
return issue.suggestedFix;
|
|
9410
|
+
}
|
|
9411
|
+
function autoFixAll(issues) {
|
|
9412
|
+
return issues.filter((i) => i.autoFixAvailable).map((i) => i.suggestedFix);
|
|
9413
|
+
}
|
|
9414
|
+
var responsiveInference = {
|
|
9415
|
+
analyze: analyzeResponsive,
|
|
9416
|
+
report: generateResponsiveReport,
|
|
9417
|
+
autoFix: autoFixIssue,
|
|
9418
|
+
autoFixAll,
|
|
9419
|
+
pass: responsiveInferencePass
|
|
9420
|
+
};
|
|
9421
|
+
|
|
9422
|
+
// src/compiler/pattern-learner.ts
|
|
9423
|
+
import crypto6 from "crypto";
|
|
9424
|
+
function fingerprintDeclarations(declarations) {
|
|
9425
|
+
const sorted = [...declarations].sort((a, b) => a.property.localeCompare(b.property));
|
|
9426
|
+
const properties = {};
|
|
9427
|
+
const propertyList = [];
|
|
9428
|
+
for (const decl of sorted) {
|
|
9429
|
+
properties[decl.property] = decl.value;
|
|
9430
|
+
propertyList.push(decl.property + ":" + decl.value);
|
|
9431
|
+
}
|
|
9432
|
+
const signature = propertyList.join("; ");
|
|
9433
|
+
const hash = crypto6.createHash("md5").update(signature).digest("hex").slice(0, 12);
|
|
9434
|
+
return {
|
|
9435
|
+
hash,
|
|
9436
|
+
signature,
|
|
9437
|
+
properties,
|
|
9438
|
+
propertyCount: sorted.length
|
|
9439
|
+
};
|
|
9440
|
+
}
|
|
9441
|
+
function clusterPatterns(rules, options = {}) {
|
|
9442
|
+
const minProperties = options.minProperties || 3;
|
|
9443
|
+
const minFrequency = options.minFrequency || 2;
|
|
9444
|
+
const groups = /* @__PURE__ */ new Map();
|
|
9445
|
+
for (const rule of rules) {
|
|
9446
|
+
if (rule.isDead) continue;
|
|
9447
|
+
if (rule.declarations.length < minProperties) continue;
|
|
9448
|
+
const fp = fingerprintDeclarations(rule.declarations);
|
|
9449
|
+
const existing = groups.get(fp.hash);
|
|
9450
|
+
if (existing) {
|
|
9451
|
+
existing.occurrences.push({
|
|
9452
|
+
ruleId: rule.id,
|
|
9453
|
+
selector: rule.selector,
|
|
9454
|
+
sourceFile: rule.source.file,
|
|
9455
|
+
component: rule.source.component
|
|
9456
|
+
});
|
|
9457
|
+
if (rule.source.file) existing.files.add(rule.source.file);
|
|
9458
|
+
} else {
|
|
9459
|
+
groups.set(fp.hash, {
|
|
9460
|
+
fingerprint: fp,
|
|
9461
|
+
occurrences: [{
|
|
9462
|
+
ruleId: rule.id,
|
|
9463
|
+
selector: rule.selector,
|
|
9464
|
+
sourceFile: rule.source.file,
|
|
9465
|
+
component: rule.source.component
|
|
9466
|
+
}],
|
|
9467
|
+
files: new Set(rule.source.file ? [rule.source.file] : [])
|
|
9468
|
+
});
|
|
9469
|
+
}
|
|
9470
|
+
}
|
|
9471
|
+
const clusters = [];
|
|
9472
|
+
for (const [, group] of groups) {
|
|
9473
|
+
if (group.occurrences.length < minFrequency) continue;
|
|
9474
|
+
const frequency = group.occurrences.length;
|
|
9475
|
+
const score = frequency * group.fingerprint.propertyCount;
|
|
9476
|
+
const suggestedName = generatePatternName(group.fingerprint.properties);
|
|
9477
|
+
const declarations = frequency * group.fingerprint.propertyCount;
|
|
9478
|
+
const linesEliminated = declarations - 1;
|
|
9479
|
+
const bundleReduction = estimateBundleSavings(declarations, frequency);
|
|
9480
|
+
clusters.push({
|
|
9481
|
+
fingerprint: group.fingerprint,
|
|
9482
|
+
occurrences: group.occurrences,
|
|
9483
|
+
frequency,
|
|
9484
|
+
fileCount: group.files.size,
|
|
9485
|
+
score,
|
|
9486
|
+
suggestedName,
|
|
9487
|
+
suggestedRecipe: generateRecipeCode(suggestedName, group.fingerprint.properties),
|
|
9488
|
+
savings: {
|
|
9489
|
+
declarations,
|
|
9490
|
+
linesEliminated,
|
|
9491
|
+
bundleReduction
|
|
9492
|
+
}
|
|
9493
|
+
});
|
|
9494
|
+
}
|
|
9495
|
+
clusters.sort((a, b) => b.score - a.score);
|
|
9496
|
+
return clusters;
|
|
9497
|
+
}
|
|
9498
|
+
function generatePatternName(properties) {
|
|
9499
|
+
const keys = Object.keys(properties);
|
|
9500
|
+
if (hasAll(keys, ["display", "justifyContent", "alignItems"]) && properties["display"] === "flex") {
|
|
9501
|
+
return "flexCenter";
|
|
9502
|
+
}
|
|
9503
|
+
if (hasAll(keys, ["display", "flexDirection", "justifyContent", "alignItems"]) && properties["flexDirection"] === "column") {
|
|
9504
|
+
return "stack";
|
|
9505
|
+
}
|
|
9506
|
+
if (hasAll(keys, ["padding", "borderRadius", "backgroundColor", "color"])) {
|
|
9507
|
+
const bg = String(properties["backgroundColor"] || "");
|
|
9508
|
+
if (bg.includes("2563eb") || bg.includes("blue")) return "primaryButton";
|
|
9509
|
+
if (bg.includes("e5e7eb") || bg.includes("gray")) return "secondaryButton";
|
|
9510
|
+
return "button";
|
|
9511
|
+
}
|
|
9512
|
+
if (hasAll(keys, ["position", "top"]) && properties["position"] === "sticky") {
|
|
9513
|
+
return "stickyHeader";
|
|
9514
|
+
}
|
|
9515
|
+
if (hasAll(keys, ["position", "top", "left"]) && properties["position"] === "absolute") {
|
|
9516
|
+
return "absoluteOverlay";
|
|
9517
|
+
}
|
|
9518
|
+
if (hasAll(keys, ["borderRadius"]) && properties["borderRadius"] === "9999px") {
|
|
9519
|
+
return "pill";
|
|
9520
|
+
}
|
|
9521
|
+
if (hasAll(keys, ["overflow", "textOverflow", "whiteSpace"])) {
|
|
9522
|
+
return "truncate";
|
|
9523
|
+
}
|
|
9524
|
+
if (hasAll(keys, ["backdropFilter"])) {
|
|
9525
|
+
return "glass";
|
|
9526
|
+
}
|
|
9527
|
+
if (keys.includes("display")) return String(properties["display"]) + "Layout";
|
|
9528
|
+
if (keys.includes("position")) return String(properties["position"]) + "Element";
|
|
9529
|
+
return "pattern-" + keys.slice(0, 3).join("-");
|
|
9530
|
+
}
|
|
9531
|
+
function hasAll(haystack, needles) {
|
|
9532
|
+
return needles.every((n) => haystack.includes(n));
|
|
9533
|
+
}
|
|
9534
|
+
function generateRecipeCode(name, properties) {
|
|
9535
|
+
const lines = Object.entries(properties).map(
|
|
9536
|
+
([prop, value]) => " " + prop + ": '" + value + "',"
|
|
9537
|
+
);
|
|
9538
|
+
return "chain.recipe('" + name + "', {\n" + lines.join("\n") + "\n})";
|
|
9539
|
+
}
|
|
9540
|
+
function estimateBundleSavings(declarations, frequency) {
|
|
9541
|
+
const avgBytesPerDecl = 25;
|
|
9542
|
+
const totalBytes = declarations * avgBytesPerDecl;
|
|
9543
|
+
if (totalBytes > 1e4) return "~" + Math.round(totalBytes / 1e3) + "KB";
|
|
9544
|
+
return "~" + totalBytes + "B";
|
|
9545
|
+
}
|
|
9546
|
+
function generateReport(clusters) {
|
|
9547
|
+
const highValue = clusters.filter((c) => c.score >= 10);
|
|
9548
|
+
const totalDeclarations = clusters.reduce((sum, c) => sum + c.savings.declarations, 0);
|
|
9549
|
+
const totalBytes = totalDeclarations * 25;
|
|
9550
|
+
let summary;
|
|
9551
|
+
if (clusters.length === 0) {
|
|
9552
|
+
summary = "No repeated patterns found. Your styles are already unique!";
|
|
9553
|
+
} else if (highValue.length > 0) {
|
|
9554
|
+
summary = "Found " + clusters.length + " patterns. " + highValue.length + " high-value patterns worth extracting.";
|
|
9555
|
+
} else {
|
|
9556
|
+
summary = "Found " + clusters.length + " patterns. None high-value enough to suggest extraction yet.";
|
|
9557
|
+
}
|
|
9558
|
+
return {
|
|
9559
|
+
clusters,
|
|
9560
|
+
totalPatterns: clusters.length,
|
|
9561
|
+
highValuePatterns: highValue,
|
|
9562
|
+
totalSavings: {
|
|
9563
|
+
declarations: totalDeclarations,
|
|
9564
|
+
estimatedBytes: totalBytes
|
|
9565
|
+
},
|
|
9566
|
+
summary
|
|
9567
|
+
};
|
|
9568
|
+
}
|
|
9569
|
+
var patternLearningPass = (ir) => {
|
|
9570
|
+
const clusters = clusterPatterns(ir.rules, {
|
|
9571
|
+
minProperties: 3,
|
|
9572
|
+
minFrequency: 2
|
|
9573
|
+
});
|
|
9574
|
+
if (clusters.length === 0) return ir;
|
|
9575
|
+
for (const cluster of clusters.slice(0, 5)) {
|
|
9576
|
+
ir.diagnostics.push({
|
|
9577
|
+
id: "pattern-" + cluster.fingerprint.hash,
|
|
9578
|
+
nodeId: ir.rules[0]?.id || ir.id,
|
|
9579
|
+
severity: "info",
|
|
9580
|
+
message: 'Pattern "' + cluster.suggestedName + '" found ' + cluster.frequency + " times across " + cluster.fileCount + " files. " + cluster.savings.bundleReduction + " potential savings.",
|
|
9581
|
+
suggestion: cluster.suggestedRecipe,
|
|
9582
|
+
pass: "pattern-learner"
|
|
9583
|
+
});
|
|
9584
|
+
}
|
|
9585
|
+
ir.meta = ir.meta || {};
|
|
9586
|
+
ir.meta.patternClusters = clusters;
|
|
9587
|
+
ir.meta.learningReport = generateReport(clusters);
|
|
9588
|
+
return ir;
|
|
9589
|
+
};
|
|
9590
|
+
function learnPatterns(rules, options) {
|
|
9591
|
+
const irRules = rules.map((r) => ({
|
|
9592
|
+
id: "learn-" + Math.random().toString(36).slice(2, 8),
|
|
9593
|
+
selector: r.selector,
|
|
9594
|
+
declarations: Object.entries(r.declarations).map(([prop, value]) => ({
|
|
9595
|
+
id: "learn-decl-" + prop,
|
|
9596
|
+
property: prop,
|
|
9597
|
+
value,
|
|
9598
|
+
history: [],
|
|
9599
|
+
meta: {}
|
|
9600
|
+
})),
|
|
9601
|
+
pseudoClasses: [],
|
|
9602
|
+
atRules: [],
|
|
9603
|
+
nestedRules: [],
|
|
9604
|
+
conditions: [],
|
|
9605
|
+
isDead: false,
|
|
9606
|
+
specificity: 0,
|
|
9607
|
+
hash: "",
|
|
9608
|
+
source: { file: r.sourceFile },
|
|
9609
|
+
history: [],
|
|
9610
|
+
meta: {}
|
|
9611
|
+
}));
|
|
9612
|
+
const clusters = clusterPatterns(irRules, options);
|
|
9613
|
+
return generateReport(clusters);
|
|
9614
|
+
}
|
|
9615
|
+
function getExtractionCandidates(rules, minScore) {
|
|
9616
|
+
const report = learnPatterns(rules);
|
|
9617
|
+
const threshold = minScore || 10;
|
|
9618
|
+
return report.clusters.filter((c) => c.score >= threshold);
|
|
9619
|
+
}
|
|
9620
|
+
var patternLearner = {
|
|
9621
|
+
learn: learnPatterns,
|
|
9622
|
+
extract: getExtractionCandidates,
|
|
9623
|
+
fingerprint: fingerprintDeclarations,
|
|
9624
|
+
cluster: clusterPatterns,
|
|
9625
|
+
report: generateReport,
|
|
9626
|
+
pass: patternLearningPass
|
|
9627
|
+
};
|
|
9628
|
+
|
|
9629
|
+
// src/compiler/source-optimizer.ts
|
|
9630
|
+
function findDuplicates2(rules) {
|
|
9631
|
+
const signatureMap = /* @__PURE__ */ new Map();
|
|
9632
|
+
for (const rule of rules) {
|
|
9633
|
+
if (rule.isDead) continue;
|
|
9634
|
+
if (rule.declarations.length < 3) continue;
|
|
9635
|
+
const sorted = [...rule.declarations].sort((a, b) => a.property.localeCompare(b.property));
|
|
9636
|
+
const signature = sorted.map((d) => d.property + ":" + d.value).join(";");
|
|
9637
|
+
const existing = signatureMap.get(signature);
|
|
9638
|
+
if (existing) {
|
|
9639
|
+
existing.occurrences.push({
|
|
9640
|
+
selector: rule.selector,
|
|
9641
|
+
file: rule.source.file,
|
|
9642
|
+
line: rule.source.line,
|
|
9643
|
+
component: rule.source.component
|
|
9644
|
+
});
|
|
9645
|
+
existing.count++;
|
|
9646
|
+
existing.savingsBytes += estimateRuleBytes(rule);
|
|
9647
|
+
} else {
|
|
9648
|
+
signatureMap.set(signature, {
|
|
9649
|
+
signature,
|
|
9650
|
+
occurrences: [{
|
|
9651
|
+
selector: rule.selector,
|
|
9652
|
+
file: rule.source.file,
|
|
9653
|
+
line: rule.source.line,
|
|
9654
|
+
component: rule.source.component
|
|
9655
|
+
}],
|
|
9656
|
+
count: 1,
|
|
9657
|
+
suggestion: "",
|
|
9658
|
+
savingsBytes: 0
|
|
9659
|
+
});
|
|
9660
|
+
}
|
|
9661
|
+
}
|
|
9662
|
+
const duplicates = [];
|
|
9663
|
+
for (const [, group] of signatureMap) {
|
|
9664
|
+
if (group.count >= 2) {
|
|
9665
|
+
group.suggestion = generateExtractSuggestion(group);
|
|
9666
|
+
duplicates.push(group);
|
|
9667
|
+
}
|
|
9668
|
+
}
|
|
9669
|
+
return duplicates.sort((a, b) => b.savingsBytes - a.savingsBytes);
|
|
9670
|
+
}
|
|
9671
|
+
function estimateRuleBytes(rule) {
|
|
9672
|
+
let bytes = rule.selector.length + 3;
|
|
9673
|
+
for (const decl of rule.declarations) {
|
|
9674
|
+
bytes += decl.property.length + String(decl.value).length + 6;
|
|
9675
|
+
}
|
|
9676
|
+
return bytes;
|
|
9677
|
+
}
|
|
9678
|
+
function generateExtractSuggestion(group) {
|
|
9679
|
+
const selectors = group.occurrences.map((o) => o.selector).join(", ");
|
|
9680
|
+
const files = [...new Set(group.occurrences.map((o) => o.file).filter(Boolean))];
|
|
9681
|
+
return "Extract as shared recipe or component. Found in: " + (files.length > 0 ? files.join(", ") : selectors);
|
|
9682
|
+
}
|
|
9683
|
+
function findDeadRules(rules) {
|
|
9684
|
+
const dead = [];
|
|
9685
|
+
for (const rule of rules) {
|
|
9686
|
+
if (!rule.isDead) continue;
|
|
9687
|
+
dead.push({
|
|
9688
|
+
ruleId: rule.id,
|
|
9689
|
+
selector: rule.selector,
|
|
9690
|
+
file: rule.source.file,
|
|
9691
|
+
line: rule.source.line,
|
|
9692
|
+
reason: rule.meta.deathReason || "Marked as dead by optimization pass",
|
|
9693
|
+
bytesWasted: estimateRuleBytes(rule)
|
|
9694
|
+
});
|
|
9695
|
+
}
|
|
9696
|
+
return dead;
|
|
9697
|
+
}
|
|
9698
|
+
function findSpecificityConflicts(rules) {
|
|
9699
|
+
const conflicts = [];
|
|
9700
|
+
const alive = rules.filter((r) => !r.isDead);
|
|
9701
|
+
for (let i = 0; i < alive.length; i++) {
|
|
9702
|
+
for (let j = i + 1; j < alive.length; j++) {
|
|
9703
|
+
const a = alive[i];
|
|
9704
|
+
const b = alive[j];
|
|
9705
|
+
if (!selectorsOverlap(a.selector, b.selector)) continue;
|
|
9706
|
+
const aProps = new Set(a.declarations.map((d) => d.property));
|
|
9707
|
+
const bProps = new Set(b.declarations.map((d) => d.property));
|
|
9708
|
+
const overlap = [...aProps].filter((p) => bProps.has(p));
|
|
9709
|
+
if (overlap.length === 0) continue;
|
|
9710
|
+
const diff = Math.abs(a.specificity - b.specificity);
|
|
9711
|
+
if (diff >= 100) {
|
|
9712
|
+
const higher = a.specificity > b.specificity ? a : b;
|
|
9713
|
+
const lower = a.specificity > b.specificity ? b : a;
|
|
9714
|
+
conflicts.push({
|
|
9715
|
+
higher: {
|
|
9716
|
+
selector: higher.selector,
|
|
9717
|
+
specificity: higher.specificity,
|
|
9718
|
+
file: higher.source.file,
|
|
9719
|
+
line: higher.source.line
|
|
9720
|
+
},
|
|
9721
|
+
lower: {
|
|
9722
|
+
selector: lower.selector,
|
|
9723
|
+
specificity: lower.specificity,
|
|
9724
|
+
file: lower.source.file,
|
|
9725
|
+
line: lower.source.line
|
|
9726
|
+
},
|
|
9727
|
+
property: overlap[0],
|
|
9728
|
+
severity: diff >= 1e4 ? "warning" : "info"
|
|
9729
|
+
});
|
|
9730
|
+
}
|
|
9731
|
+
}
|
|
9732
|
+
}
|
|
9733
|
+
return conflicts;
|
|
9734
|
+
}
|
|
9735
|
+
function selectorsOverlap(a, b) {
|
|
9736
|
+
const partsA = a.split(/[\s>+~]+/).filter(Boolean);
|
|
9737
|
+
const partsB = b.split(/[\s>+~]+/).filter(Boolean);
|
|
9738
|
+
for (const pa of partsA) {
|
|
9739
|
+
for (const pb of partsB) {
|
|
9740
|
+
if (pa.startsWith(".") && pb.startsWith(".") && pa === pb) return true;
|
|
9741
|
+
if (pa === pb && !pa.startsWith(".") && !pa.startsWith("#")) return true;
|
|
9742
|
+
}
|
|
9743
|
+
}
|
|
9744
|
+
return false;
|
|
9745
|
+
}
|
|
9746
|
+
function findAnimationConflicts(rules) {
|
|
9747
|
+
const animationMap = /* @__PURE__ */ new Map();
|
|
9748
|
+
for (const rule of rules) {
|
|
9749
|
+
if (rule.isDead) continue;
|
|
9750
|
+
for (const atRule of rule.atRules) {
|
|
9751
|
+
if (atRule.type === "keyframes" && atRule.name) {
|
|
9752
|
+
const existing = animationMap.get(atRule.name);
|
|
9753
|
+
if (existing) {
|
|
9754
|
+
existing.locations.push({
|
|
9755
|
+
file: rule.source.file,
|
|
9756
|
+
line: rule.source.line,
|
|
9757
|
+
selector: rule.selector
|
|
9758
|
+
});
|
|
9759
|
+
existing.count++;
|
|
9760
|
+
} else {
|
|
9761
|
+
animationMap.set(atRule.name, {
|
|
9762
|
+
name: atRule.name,
|
|
9763
|
+
locations: [{
|
|
9764
|
+
file: rule.source.file,
|
|
9765
|
+
line: rule.source.line,
|
|
9766
|
+
selector: rule.selector
|
|
9767
|
+
}],
|
|
9768
|
+
count: 1
|
|
9769
|
+
});
|
|
9770
|
+
}
|
|
9771
|
+
}
|
|
9772
|
+
}
|
|
9773
|
+
}
|
|
9774
|
+
return [...animationMap.values()].filter((a) => a.count >= 2);
|
|
9775
|
+
}
|
|
9776
|
+
function findMediaQueryRedundancies(rules) {
|
|
9777
|
+
const queryMap = /* @__PURE__ */ new Map();
|
|
9778
|
+
for (const rule of rules) {
|
|
9779
|
+
if (rule.isDead) continue;
|
|
9780
|
+
for (const atRule of rule.atRules) {
|
|
9781
|
+
if (atRule.type === "media" && atRule.query) {
|
|
9782
|
+
const normalized = atRule.query.replace(/\s+/g, " ").trim();
|
|
9783
|
+
const existing = queryMap.get(normalized);
|
|
9784
|
+
if (existing) {
|
|
9785
|
+
existing.count++;
|
|
9786
|
+
if (rule.source.file) existing.files.add(rule.source.file);
|
|
9787
|
+
} else {
|
|
9788
|
+
queryMap.set(normalized, {
|
|
9789
|
+
count: 1,
|
|
9790
|
+
files: new Set(rule.source.file ? [rule.source.file] : [])
|
|
9791
|
+
});
|
|
9792
|
+
}
|
|
9793
|
+
}
|
|
9794
|
+
}
|
|
9795
|
+
}
|
|
9796
|
+
const redundancies = [];
|
|
9797
|
+
for (const [query, data] of queryMap) {
|
|
9798
|
+
if (data.count >= 3) {
|
|
9799
|
+
redundancies.push({
|
|
9800
|
+
query,
|
|
9801
|
+
count: data.count,
|
|
9802
|
+
files: [...data.files],
|
|
9803
|
+
suggestion: "Extract as shared breakpoint: $breakpoints." + generateBreakpointName(query),
|
|
9804
|
+
savingsBytes: estimateMQSavings(query, data.count)
|
|
9805
|
+
});
|
|
9806
|
+
}
|
|
9807
|
+
}
|
|
9808
|
+
return redundancies.sort((a, b) => b.savingsBytes - a.savingsBytes);
|
|
9809
|
+
}
|
|
9810
|
+
function generateBreakpointName(query) {
|
|
9811
|
+
if (query.includes("768")) return "md";
|
|
9812
|
+
if (query.includes("1024")) return "lg";
|
|
9813
|
+
if (query.includes("1280")) return "xl";
|
|
9814
|
+
if (query.includes("640")) return "sm";
|
|
9815
|
+
return "custom";
|
|
9816
|
+
}
|
|
9817
|
+
function estimateMQSavings(query, count) {
|
|
9818
|
+
const queryBytes = query.length + 12;
|
|
9819
|
+
return (count - 1) * queryBytes;
|
|
9820
|
+
}
|
|
9821
|
+
function formatReport(report) {
|
|
9822
|
+
const lines = [
|
|
9823
|
+
"\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550",
|
|
9824
|
+
" ChainCSS Source-Aware Optimization Report",
|
|
9825
|
+
"\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550",
|
|
9826
|
+
""
|
|
9827
|
+
];
|
|
9828
|
+
if (report.duplicates.length > 0) {
|
|
9829
|
+
lines.push("\u{1F501} DUPLICATES (" + report.duplicates.length + " found)");
|
|
9830
|
+
for (const dup of report.duplicates.slice(0, 5)) {
|
|
9831
|
+
const selectors = dup.occurrences.map((o) => o.selector).join(" = ");
|
|
9832
|
+
lines.push(" \u2022 " + selectors);
|
|
9833
|
+
lines.push(" \u2192 " + dup.suggestion);
|
|
9834
|
+
lines.push(" \u2192 Savings: ~" + dup.savingsBytes + "B");
|
|
9835
|
+
}
|
|
9836
|
+
lines.push("");
|
|
9837
|
+
}
|
|
9838
|
+
if (report.deadRules.length > 0) {
|
|
9839
|
+
lines.push("\u{1F480} DEAD CODE (" + report.deadRules.length + " rules, ~" + report.deadRules.reduce((s2, d) => s2 + d.bytesWasted, 0) + "B)");
|
|
9840
|
+
for (const dead of report.deadRules.slice(0, 5)) {
|
|
9841
|
+
const location = dead.file ? dead.file + (dead.line ? ":" + dead.line : "") : "unknown";
|
|
9842
|
+
lines.push(" \u2022 " + dead.selector + " (" + location + ") \u2014 " + dead.reason);
|
|
9843
|
+
}
|
|
9844
|
+
lines.push("");
|
|
9845
|
+
}
|
|
9846
|
+
if (report.specificityConflicts.length > 0) {
|
|
9847
|
+
lines.push("\u2694\uFE0F SPECIFICITY WARS (" + report.specificityConflicts.length + " conflicts)");
|
|
9848
|
+
for (const conflict of report.specificityConflicts.slice(0, 5)) {
|
|
9849
|
+
lines.push(" \u2022 " + conflict.higher.selector + " (" + conflict.higher.specificity + ")");
|
|
9850
|
+
lines.push(" overrides: " + conflict.lower.selector + " (" + conflict.lower.specificity + ")");
|
|
9851
|
+
if (conflict.property) lines.push(" Property: " + conflict.property);
|
|
9852
|
+
}
|
|
9853
|
+
lines.push("");
|
|
9854
|
+
}
|
|
9855
|
+
if (report.animationConflicts.length > 0) {
|
|
9856
|
+
lines.push("\u{1F3AC} ANIMATION CONFLICTS (" + report.animationConflicts.length + " found)");
|
|
9857
|
+
for (const ac of report.animationConflicts.slice(0, 5)) {
|
|
9858
|
+
lines.push(" \u2022 @keyframes " + ac.name + " \u2014 defined " + ac.count + " times");
|
|
9859
|
+
for (const loc of ac.locations) {
|
|
9860
|
+
lines.push(" " + (loc.file || "unknown") + " \u2192 " + loc.selector);
|
|
9861
|
+
}
|
|
9862
|
+
}
|
|
9863
|
+
lines.push("");
|
|
9864
|
+
}
|
|
9865
|
+
if (report.mediaQueryRedundancies.length > 0) {
|
|
9866
|
+
lines.push("\u{1F4F1} MEDIA QUERY CONSOLIDATION (" + report.mediaQueryRedundancies.length + " redundant)");
|
|
9867
|
+
for (const mq of report.mediaQueryRedundancies.slice(0, 5)) {
|
|
9868
|
+
lines.push(" \u2022 " + mq.query + " \u2014 used " + mq.count + " times");
|
|
9869
|
+
lines.push(" \u2192 " + mq.suggestion);
|
|
9870
|
+
lines.push(" \u2192 Savings: ~" + mq.savingsBytes + "B");
|
|
9871
|
+
}
|
|
9872
|
+
lines.push("");
|
|
9873
|
+
}
|
|
9874
|
+
const s = report.summary;
|
|
9875
|
+
lines.push("\u{1F4CA} SUMMARY");
|
|
9876
|
+
lines.push(" \u2022 " + s.duplicatesCount + " duplicates \u2192 extract recipes");
|
|
9877
|
+
lines.push(" \u2022 " + s.deadCount + " dead rules \u2192 remove");
|
|
9878
|
+
lines.push(" \u2022 " + s.specificityCount + " specificity issues \u2192 fix cascade");
|
|
9879
|
+
lines.push(" \u2022 " + s.animationCount + " animation conflicts \u2192 scope names");
|
|
9880
|
+
lines.push(" \u2022 " + s.mediaQueryCount + " redundant media queries \u2192 consolidate");
|
|
9881
|
+
lines.push(" \u2022 Total potential savings: " + s.totalSavingsKB);
|
|
9882
|
+
lines.push("");
|
|
9883
|
+
lines.push("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
|
|
9884
|
+
return lines.join("\n");
|
|
9885
|
+
}
|
|
9886
|
+
function generateOptimizationReport(rules) {
|
|
9887
|
+
const duplicates = findDuplicates2(rules);
|
|
9888
|
+
const deadRules = findDeadRules(rules);
|
|
9889
|
+
const specificityConflicts = findSpecificityConflicts(rules);
|
|
9890
|
+
const animationConflicts = findAnimationConflicts(rules);
|
|
9891
|
+
const mediaQueryRedundancies = findMediaQueryRedundancies(rules);
|
|
9892
|
+
const totalSavingsBytes = duplicates.reduce((s, d) => s + d.savingsBytes, 0) + deadRules.reduce((s, d) => s + d.bytesWasted, 0) + mediaQueryRedundancies.reduce((s, m) => s + m.savingsBytes, 0);
|
|
9893
|
+
const report = {
|
|
9894
|
+
duplicates,
|
|
9895
|
+
deadRules,
|
|
9896
|
+
specificityConflicts,
|
|
9897
|
+
animationConflicts,
|
|
9898
|
+
mediaQueryRedundancies,
|
|
9899
|
+
summary: {
|
|
9900
|
+
totalIssues: duplicates.length + deadRules.length + specificityConflicts.length + animationConflicts.length + mediaQueryRedundancies.length,
|
|
9901
|
+
duplicatesCount: duplicates.length,
|
|
9902
|
+
deadCount: deadRules.length,
|
|
9903
|
+
specificityCount: specificityConflicts.length,
|
|
9904
|
+
animationCount: animationConflicts.length,
|
|
9905
|
+
mediaQueryCount: mediaQueryRedundancies.length,
|
|
9906
|
+
totalSavingsBytes,
|
|
9907
|
+
totalSavingsKB: totalSavingsBytes > 1e3 ? (totalSavingsBytes / 1e3).toFixed(1) + "KB" : totalSavingsBytes + "B"
|
|
9908
|
+
},
|
|
9909
|
+
formattedReport: ""
|
|
9910
|
+
};
|
|
9911
|
+
report.formattedReport = formatReport(report);
|
|
9912
|
+
return report;
|
|
9913
|
+
}
|
|
9914
|
+
var sourceOptimizerPass = (ir) => {
|
|
9915
|
+
const report = generateOptimizationReport(ir.rules);
|
|
9916
|
+
for (const dup of report.duplicates.slice(0, 3)) {
|
|
9917
|
+
ir.diagnostics.push({
|
|
9918
|
+
id: "source-dup-" + Date.now(),
|
|
9919
|
+
nodeId: ir.rules[0]?.id || ir.id,
|
|
9920
|
+
severity: "warning",
|
|
9921
|
+
message: "Duplicate: " + dup.occurrences.map((o) => o.selector).join(" = ") + " (" + dup.count + "\xD7)",
|
|
9922
|
+
suggestion: dup.suggestion,
|
|
9923
|
+
pass: "source-optimizer"
|
|
9924
|
+
});
|
|
9925
|
+
}
|
|
9926
|
+
for (const dead of report.deadRules.slice(0, 3)) {
|
|
9927
|
+
ir.diagnostics.push({
|
|
9928
|
+
id: "source-dead-" + Date.now(),
|
|
9929
|
+
nodeId: dead.ruleId,
|
|
9930
|
+
severity: "info",
|
|
9931
|
+
message: "Dead rule: " + dead.selector + " \u2014 " + dead.reason,
|
|
9932
|
+
pass: "source-optimizer"
|
|
9933
|
+
});
|
|
9934
|
+
}
|
|
9935
|
+
ir.meta = ir.meta || {};
|
|
9936
|
+
ir.meta.optimizationReport = report;
|
|
9937
|
+
return ir;
|
|
9938
|
+
};
|
|
9939
|
+
function optimizeSource(rules) {
|
|
9940
|
+
return generateOptimizationReport(rules);
|
|
9941
|
+
}
|
|
9942
|
+
var sourceOptimizer = {
|
|
9943
|
+
optimize: optimizeSource,
|
|
9944
|
+
findDuplicates: findDuplicates2,
|
|
9945
|
+
findDeadRules,
|
|
9946
|
+
findSpecificityConflicts,
|
|
9947
|
+
findAnimationConflicts,
|
|
9948
|
+
findMediaQueryRedundancies,
|
|
9949
|
+
report: generateOptimizationReport,
|
|
9950
|
+
format: formatReport,
|
|
9951
|
+
pass: sourceOptimizerPass
|
|
9952
|
+
};
|
|
9953
|
+
|
|
9954
|
+
// src/compiler/semantic-tokens.ts
|
|
9955
|
+
var DEFAULT_THEME = {
|
|
9956
|
+
// --- SURFACES ---
|
|
9957
|
+
surface: {
|
|
9958
|
+
interactive: {
|
|
9959
|
+
properties: {
|
|
9960
|
+
backgroundColor: "$colors.primary.500",
|
|
9961
|
+
color: "$colors.white",
|
|
9962
|
+
borderRadius: "$radii.md",
|
|
9963
|
+
cursor: "pointer",
|
|
9964
|
+
transition: "all 0.2s ease"
|
|
9965
|
+
},
|
|
9966
|
+
description: "Clickable surface \u2014 buttons, CTAs, links"
|
|
9967
|
+
},
|
|
9968
|
+
container: {
|
|
9969
|
+
properties: {
|
|
9970
|
+
backgroundColor: "$colors.neutral.100",
|
|
9971
|
+
color: "$colors.neutral.900",
|
|
9972
|
+
borderRadius: "$radii.lg",
|
|
9973
|
+
border: "1px solid $colors.neutral.200"
|
|
9974
|
+
},
|
|
9975
|
+
description: "Content container \u2014 cards, sections, panels"
|
|
9976
|
+
},
|
|
9977
|
+
overlay: {
|
|
9978
|
+
properties: {
|
|
9979
|
+
backgroundColor: "$colors.white",
|
|
9980
|
+
color: "$colors.neutral.900",
|
|
9981
|
+
borderRadius: "$radii.xl",
|
|
9982
|
+
boxShadow: "$shadows.xl",
|
|
9983
|
+
zIndex: "50"
|
|
9984
|
+
},
|
|
9985
|
+
description: "Modal overlay \u2014 dialogs, drawers"
|
|
9986
|
+
},
|
|
9987
|
+
sheet: {
|
|
9988
|
+
properties: {
|
|
9989
|
+
backgroundColor: "$colors.neutral.50",
|
|
9990
|
+
color: "$colors.neutral.800",
|
|
9991
|
+
borderRadius: "$radii.lg $radii.lg 0 0",
|
|
9992
|
+
boxShadow: "$shadows.lg"
|
|
9993
|
+
},
|
|
9994
|
+
description: "Bottom sheet \u2014 mobile menus, action sheets"
|
|
9995
|
+
},
|
|
9996
|
+
tooltip: {
|
|
9997
|
+
properties: {
|
|
9998
|
+
backgroundColor: "$colors.neutral.900",
|
|
9999
|
+
color: "$colors.white",
|
|
10000
|
+
borderRadius: "$radii.sm",
|
|
10001
|
+
padding: "4px 8px",
|
|
10002
|
+
fontSize: "12px",
|
|
10003
|
+
boxShadow: "$shadows.md"
|
|
10004
|
+
},
|
|
10005
|
+
description: "Tooltip \u2014 hover information popups"
|
|
10006
|
+
},
|
|
10007
|
+
input: {
|
|
10008
|
+
properties: {
|
|
10009
|
+
backgroundColor: "$colors.white",
|
|
10010
|
+
color: "$colors.neutral.900",
|
|
10011
|
+
borderRadius: "$radii.md",
|
|
10012
|
+
border: "1px solid $colors.neutral.300",
|
|
10013
|
+
padding: "8px 12px"
|
|
10014
|
+
},
|
|
10015
|
+
description: "Input field \u2014 text inputs, selects, textareas"
|
|
10016
|
+
}
|
|
10017
|
+
},
|
|
10018
|
+
// --- TEXT ---
|
|
10019
|
+
text: {
|
|
10020
|
+
primary: {
|
|
10021
|
+
properties: {
|
|
10022
|
+
color: "$colors.neutral.900",
|
|
10023
|
+
fontWeight: "400"
|
|
10024
|
+
},
|
|
10025
|
+
description: "Primary body text \u2014 main content"
|
|
10026
|
+
},
|
|
10027
|
+
secondary: {
|
|
10028
|
+
properties: {
|
|
10029
|
+
color: "$colors.neutral.600",
|
|
10030
|
+
fontWeight: "400"
|
|
10031
|
+
},
|
|
10032
|
+
description: "Secondary text \u2014 descriptions, captions"
|
|
10033
|
+
},
|
|
10034
|
+
muted: {
|
|
10035
|
+
properties: {
|
|
10036
|
+
color: "$colors.neutral.400",
|
|
10037
|
+
fontSize: "14px",
|
|
10038
|
+
fontWeight: "400"
|
|
10039
|
+
},
|
|
10040
|
+
description: "Muted text \u2014 placeholders, hints, meta info"
|
|
10041
|
+
},
|
|
10042
|
+
link: {
|
|
10043
|
+
properties: {
|
|
10044
|
+
color: "$colors.primary.500",
|
|
10045
|
+
fontWeight: "500",
|
|
10046
|
+
textDecoration: "underline",
|
|
10047
|
+
cursor: "pointer"
|
|
10048
|
+
},
|
|
10049
|
+
description: "Link text \u2014 hyperlinks, navigational text"
|
|
10050
|
+
},
|
|
10051
|
+
inverse: {
|
|
10052
|
+
properties: {
|
|
10053
|
+
color: "$colors.white",
|
|
10054
|
+
fontWeight: "500"
|
|
10055
|
+
},
|
|
10056
|
+
description: "Inverse text \u2014 text on dark backgrounds"
|
|
10057
|
+
},
|
|
10058
|
+
code: {
|
|
10059
|
+
properties: {
|
|
10060
|
+
fontFamily: "monospace",
|
|
10061
|
+
fontSize: "14px",
|
|
10062
|
+
color: "$colors.neutral.800",
|
|
10063
|
+
backgroundColor: "$colors.neutral.100",
|
|
10064
|
+
borderRadius: "$radii.sm",
|
|
10065
|
+
padding: "2px 6px"
|
|
10066
|
+
},
|
|
10067
|
+
description: "Code text \u2014 inline code, code blocks"
|
|
10068
|
+
}
|
|
10069
|
+
},
|
|
10070
|
+
// --- ELEVATION ---
|
|
10071
|
+
elevation: {
|
|
10072
|
+
flat: {
|
|
10073
|
+
properties: {
|
|
10074
|
+
boxShadow: "none",
|
|
10075
|
+
zIndex: "0"
|
|
10076
|
+
},
|
|
10077
|
+
description: "Flat \u2014 no elevation, base level"
|
|
10078
|
+
},
|
|
10079
|
+
raised: {
|
|
10080
|
+
properties: {
|
|
10081
|
+
boxShadow: "$shadows.sm",
|
|
10082
|
+
zIndex: "10"
|
|
10083
|
+
},
|
|
10084
|
+
description: "Raised \u2014 subtle lift, cards on light backgrounds"
|
|
10085
|
+
},
|
|
10086
|
+
floating: {
|
|
10087
|
+
properties: {
|
|
10088
|
+
boxShadow: "$shadows.md",
|
|
10089
|
+
zIndex: "20"
|
|
10090
|
+
},
|
|
10091
|
+
description: "Floating \u2014 dropdowns, popovers"
|
|
10092
|
+
},
|
|
10093
|
+
sticky: {
|
|
10094
|
+
properties: {
|
|
10095
|
+
boxShadow: "$shadows.md",
|
|
10096
|
+
zIndex: "30",
|
|
10097
|
+
position: "sticky",
|
|
10098
|
+
top: "0"
|
|
10099
|
+
},
|
|
10100
|
+
description: "Sticky \u2014 sticky headers, persistent nav"
|
|
10101
|
+
},
|
|
10102
|
+
overlay: {
|
|
10103
|
+
properties: {
|
|
10104
|
+
boxShadow: "$shadows.xl",
|
|
10105
|
+
zIndex: "50",
|
|
10106
|
+
position: "fixed"
|
|
10107
|
+
},
|
|
10108
|
+
description: "Overlay \u2014 modals, dialogs"
|
|
10109
|
+
},
|
|
10110
|
+
modal: {
|
|
10111
|
+
properties: {
|
|
10112
|
+
boxShadow: "$shadows.2xl",
|
|
10113
|
+
zIndex: "100",
|
|
10114
|
+
position: "fixed",
|
|
10115
|
+
backdropFilter: "blur(4px)"
|
|
10116
|
+
},
|
|
10117
|
+
description: "Modal \u2014 full-screen overlay with backdrop"
|
|
10118
|
+
}
|
|
10119
|
+
},
|
|
10120
|
+
// --- STATES ---
|
|
10121
|
+
state: {
|
|
10122
|
+
hover: {
|
|
10123
|
+
properties: {
|
|
10124
|
+
filter: "brightness(1.1)",
|
|
10125
|
+
transition: "filter 0.2s ease"
|
|
10126
|
+
},
|
|
10127
|
+
pseudoClass: "hover",
|
|
10128
|
+
description: "Hover state \u2014 slight brightening"
|
|
10129
|
+
},
|
|
10130
|
+
active: {
|
|
10131
|
+
properties: {
|
|
10132
|
+
filter: "brightness(0.95)",
|
|
10133
|
+
transform: "scale(0.98)",
|
|
10134
|
+
transition: "all 0.1s ease"
|
|
10135
|
+
},
|
|
10136
|
+
pseudoClass: "active",
|
|
10137
|
+
description: "Active/pressed state \u2014 slight darkening + press"
|
|
10138
|
+
},
|
|
10139
|
+
focus: {
|
|
10140
|
+
properties: {
|
|
10141
|
+
outline: "2px solid $colors.primary.500",
|
|
10142
|
+
outlineOffset: "2px"
|
|
10143
|
+
},
|
|
10144
|
+
pseudoClass: "focus-visible",
|
|
10145
|
+
description: "Focus state \u2014 accessible focus ring"
|
|
10146
|
+
},
|
|
10147
|
+
disabled: {
|
|
10148
|
+
properties: {
|
|
10149
|
+
opacity: "0.5",
|
|
10150
|
+
cursor: "not-allowed",
|
|
10151
|
+
pointerEvents: "none"
|
|
10152
|
+
},
|
|
10153
|
+
pseudoClass: "disabled",
|
|
10154
|
+
description: "Disabled state \u2014 reduced opacity, no interaction"
|
|
10155
|
+
},
|
|
10156
|
+
loading: {
|
|
10157
|
+
properties: {
|
|
10158
|
+
cursor: "wait",
|
|
10159
|
+
opacity: "0.7",
|
|
10160
|
+
pointerEvents: "none"
|
|
10161
|
+
},
|
|
10162
|
+
description: "Loading state \u2014 waiting cursor, partially transparent"
|
|
10163
|
+
},
|
|
10164
|
+
selected: {
|
|
10165
|
+
properties: {
|
|
10166
|
+
backgroundColor: "$colors.primary.50",
|
|
10167
|
+
color: "$colors.primary.700",
|
|
10168
|
+
fontWeight: "600"
|
|
10169
|
+
},
|
|
10170
|
+
pseudoClass: "selected",
|
|
10171
|
+
description: "Selected state \u2014 highlighted background"
|
|
10172
|
+
}
|
|
10173
|
+
},
|
|
10174
|
+
// --- SPACING ---
|
|
10175
|
+
spacing: {
|
|
10176
|
+
none: {
|
|
10177
|
+
properties: { padding: "0", gap: "0" },
|
|
10178
|
+
description: "No spacing"
|
|
10179
|
+
},
|
|
10180
|
+
tight: {
|
|
10181
|
+
properties: { padding: "4px 8px", gap: "4px" },
|
|
10182
|
+
description: "Tight spacing \u2014 icon buttons, chips, badges"
|
|
10183
|
+
},
|
|
10184
|
+
compact: {
|
|
10185
|
+
properties: { padding: "8px 12px", gap: "8px" },
|
|
10186
|
+
description: "Compact spacing \u2014 dense lists, toolbars"
|
|
10187
|
+
},
|
|
10188
|
+
comfortable: {
|
|
10189
|
+
properties: { padding: "12px 16px", gap: "12px" },
|
|
10190
|
+
description: "Comfortable spacing \u2014 form fields, cards (default)"
|
|
10191
|
+
},
|
|
10192
|
+
spacious: {
|
|
10193
|
+
properties: { padding: "24px 32px", gap: "24px" },
|
|
10194
|
+
description: "Spacious spacing \u2014 hero sections, feature cards"
|
|
10195
|
+
},
|
|
10196
|
+
generous: {
|
|
10197
|
+
properties: { padding: "48px 64px", gap: "32px" },
|
|
10198
|
+
description: "Generous spacing \u2014 landing pages, large sections"
|
|
10199
|
+
}
|
|
10200
|
+
}
|
|
10201
|
+
};
|
|
10202
|
+
var DARK_OVERRIDES = {
|
|
10203
|
+
surface: {
|
|
10204
|
+
interactive: { backgroundColor: "$colors.primary.400", color: "$colors.white" },
|
|
10205
|
+
container: { backgroundColor: "$colors.neutral.800", color: "$colors.neutral.100", border: "1px solid $colors.neutral.700" },
|
|
10206
|
+
overlay: { backgroundColor: "$colors.neutral.850", color: "$colors.neutral.100" },
|
|
10207
|
+
sheet: { backgroundColor: "$colors.neutral.800", color: "$colors.neutral.100" },
|
|
10208
|
+
input: { backgroundColor: "$colors.neutral.800", color: "$colors.neutral.100", border: "1px solid $colors.neutral.600" }
|
|
10209
|
+
},
|
|
10210
|
+
text: {
|
|
10211
|
+
primary: { color: "$colors.neutral.100" },
|
|
10212
|
+
secondary: { color: "$colors.neutral.400" },
|
|
10213
|
+
muted: { color: "$colors.neutral.500" },
|
|
10214
|
+
inverse: { color: "$colors.neutral.900" },
|
|
10215
|
+
code: { backgroundColor: "$colors.neutral.800", color: "$colors.neutral.200" }
|
|
10216
|
+
}
|
|
10217
|
+
};
|
|
10218
|
+
var HIGH_CONTRAST_OVERRIDES = {
|
|
10219
|
+
surface: {
|
|
10220
|
+
interactive: { backgroundColor: "$colors.black", color: "$colors.white", border: "2px solid $colors.white" },
|
|
10221
|
+
container: { backgroundColor: "$colors.white", color: "$colors.black", border: "2px solid $colors.black" },
|
|
10222
|
+
input: { backgroundColor: "$colors.white", color: "$colors.black", border: "2px solid $colors.black" }
|
|
10223
|
+
},
|
|
10224
|
+
text: {
|
|
10225
|
+
primary: { color: "$colors.black", fontWeight: "500" },
|
|
10226
|
+
secondary: { color: "$colors.neutral.800", fontWeight: "500" },
|
|
10227
|
+
muted: { color: "$colors.neutral.700" },
|
|
10228
|
+
link: { color: "$colors.blue.800", textDecoration: "underline" }
|
|
10229
|
+
}
|
|
10230
|
+
};
|
|
10231
|
+
function resolveSemantic(category, intent2, themeContext = { mode: "light" }) {
|
|
10232
|
+
const baseMap = DEFAULT_THEME[category];
|
|
10233
|
+
if (!baseMap) return null;
|
|
10234
|
+
const mapping = baseMap[intent2];
|
|
10235
|
+
if (!mapping) return null;
|
|
10236
|
+
const properties = { ...mapping.properties };
|
|
10237
|
+
if (themeContext.mode === "dark" && DARK_OVERRIDES[category]?.[intent2]) {
|
|
10238
|
+
Object.assign(properties, DARK_OVERRIDES[category][intent2]);
|
|
10239
|
+
}
|
|
10240
|
+
if (themeContext.mode === "high-contrast" && HIGH_CONTRAST_OVERRIDES[category]?.[intent2]) {
|
|
10241
|
+
Object.assign(properties, HIGH_CONTRAST_OVERRIDES[category][intent2]);
|
|
10242
|
+
}
|
|
10243
|
+
if (themeContext.containerContext === "dark" && category === "surface") {
|
|
10244
|
+
if (intent2 === "interactive") {
|
|
10245
|
+
properties.backgroundColor = "$colors.primary.300";
|
|
10246
|
+
}
|
|
10247
|
+
}
|
|
10248
|
+
return {
|
|
10249
|
+
properties,
|
|
10250
|
+
pseudoClass: mapping.pseudoClass,
|
|
10251
|
+
description: mapping.description
|
|
10252
|
+
};
|
|
10253
|
+
}
|
|
10254
|
+
function getSemanticIntents(category) {
|
|
10255
|
+
const map = DEFAULT_THEME[category];
|
|
10256
|
+
return map ? Object.keys(map) : [];
|
|
10257
|
+
}
|
|
10258
|
+
function getSemanticDescription(category, intent2) {
|
|
10259
|
+
return DEFAULT_THEME[category]?.[intent2]?.description || null;
|
|
10260
|
+
}
|
|
10261
|
+
var semanticTokensPass = (ir) => {
|
|
10262
|
+
for (const rule of ir.rules) {
|
|
10263
|
+
const semanticIntents = rule.meta._semantic || [];
|
|
10264
|
+
for (const { category, intent: intent2, theme } of semanticIntents) {
|
|
10265
|
+
const resolved = resolveSemantic(category, intent2, theme);
|
|
10266
|
+
if (!resolved) continue;
|
|
10267
|
+
for (const [prop, value] of Object.entries(resolved.properties)) {
|
|
10268
|
+
const decl = createDeclaration(prop, value);
|
|
10269
|
+
decl.history.push({
|
|
10270
|
+
pass: "semantic-tokens",
|
|
10271
|
+
action: "resolved-intent",
|
|
10272
|
+
timestamp: Date.now(),
|
|
10273
|
+
reason: category + ":" + intent2 + " \u2192 " + prop + ": " + value
|
|
10274
|
+
});
|
|
10275
|
+
decl.meta.semantic = { category, intent: intent2 };
|
|
10276
|
+
if (resolved.pseudoClass) {
|
|
10277
|
+
let pc = rule.pseudoClasses.find((p) => p.name === resolved.pseudoClass);
|
|
10278
|
+
if (!pc) {
|
|
10279
|
+
pc = {
|
|
10280
|
+
id: "semantic-pc-" + Date.now(),
|
|
10281
|
+
name: resolved.pseudoClass,
|
|
10282
|
+
declarations: [],
|
|
10283
|
+
source: rule.source,
|
|
10284
|
+
history: []
|
|
10285
|
+
};
|
|
10286
|
+
rule.pseudoClasses.push(pc);
|
|
10287
|
+
}
|
|
10288
|
+
pc.declarations.push(decl);
|
|
10289
|
+
} else {
|
|
10290
|
+
rule.declarations.push(decl);
|
|
10291
|
+
}
|
|
10292
|
+
}
|
|
10293
|
+
}
|
|
10294
|
+
}
|
|
10295
|
+
return ir;
|
|
10296
|
+
};
|
|
10297
|
+
var semanticTokens = {
|
|
10298
|
+
resolve: resolveSemantic,
|
|
10299
|
+
getIntents: getSemanticIntents,
|
|
10300
|
+
getDescription: getSemanticDescription,
|
|
10301
|
+
pass: semanticTokensPass,
|
|
10302
|
+
theme: DEFAULT_THEME,
|
|
10303
|
+
darkOverrides: DARK_OVERRIDES,
|
|
10304
|
+
highContrastOverrides: HIGH_CONTRAST_OVERRIDES
|
|
10305
|
+
};
|
|
10306
|
+
|
|
10307
|
+
// src/compiler/accessibility-engine.ts
|
|
10308
|
+
var WCAG = {
|
|
10309
|
+
MIN_CONTRAST_AA: 4.5,
|
|
10310
|
+
MIN_CONTRAST_AA_LARGE: 3,
|
|
10311
|
+
MIN_CONTRAST_AAA: 7,
|
|
10312
|
+
MIN_FONT_SIZE: 12,
|
|
10313
|
+
// px
|
|
10314
|
+
MIN_TOUCH_TARGET: 44,
|
|
10315
|
+
// px
|
|
10316
|
+
CRITERIA: {
|
|
10317
|
+
contrast: "1.4.3 Contrast (Minimum) \u2014 AA",
|
|
10318
|
+
fontSize: "1.4.4 Resize Text \u2014 AA",
|
|
10319
|
+
touchTarget: "2.5.8 Target Size \u2014 AA",
|
|
10320
|
+
focus: "2.4.7 Focus Visible \u2014 AA",
|
|
10321
|
+
motion: "2.3.3 Animation from Interactions \u2014 AAA",
|
|
10322
|
+
hoverOnly: "1.4.13 Content on Hover or Focus \u2014 AA",
|
|
10323
|
+
colorOnly: "1.4.1 Use of Color \u2014 A"
|
|
10324
|
+
}
|
|
10325
|
+
};
|
|
10326
|
+
function detectContrast(rule) {
|
|
10327
|
+
const issues = [];
|
|
10328
|
+
const color = rule.declarations.find(
|
|
10329
|
+
(d) => d.property === "color" && typeof d.value === "string"
|
|
10330
|
+
);
|
|
10331
|
+
const bg = rule.declarations.find(
|
|
10332
|
+
(d) => (d.property === "backgroundColor" || d.property === "background") && typeof d.value === "string"
|
|
10333
|
+
);
|
|
10334
|
+
if (color && bg) {
|
|
10335
|
+
const ratio = contrastRatio(String(color.value), String(bg.value));
|
|
10336
|
+
if (ratio > 0 && ratio < WCAG.MIN_CONTRAST_AA) {
|
|
10337
|
+
issues.push({
|
|
10338
|
+
ruleId: rule.id,
|
|
10339
|
+
selector: rule.selector,
|
|
10340
|
+
category: "contrast",
|
|
10341
|
+
severity: "error",
|
|
10342
|
+
wcagCriterion: WCAG.CRITERIA.contrast,
|
|
10343
|
+
message: "Contrast ratio " + ratio.toFixed(1) + ":1 fails WCAG AA (needs " + WCAG.MIN_CONTRAST_AA + ":1)",
|
|
10344
|
+
suggestion: "Darken text or lighten background. Current: " + color.value + " on " + bg.value,
|
|
10345
|
+
autoFixable: false
|
|
10346
|
+
// Can't auto-fix without knowing design intent
|
|
10347
|
+
});
|
|
10348
|
+
} else if (ratio > 0 && ratio < WCAG.MIN_CONTRAST_AAA) {
|
|
10349
|
+
issues.push({
|
|
10350
|
+
ruleId: rule.id,
|
|
10351
|
+
selector: rule.selector,
|
|
10352
|
+
category: "contrast",
|
|
10353
|
+
severity: "warning",
|
|
10354
|
+
wcagCriterion: WCAG.CRITERIA.contrast,
|
|
10355
|
+
message: "Contrast ratio " + ratio.toFixed(1) + ":1 passes AA but fails AAA (" + WCAG.MIN_CONTRAST_AAA + ":1)",
|
|
10356
|
+
suggestion: "Consider increasing contrast for better readability.",
|
|
10357
|
+
autoFixable: false
|
|
10358
|
+
});
|
|
10359
|
+
}
|
|
10360
|
+
}
|
|
10361
|
+
return issues;
|
|
10362
|
+
}
|
|
10363
|
+
function detectMinimumFontSize(rule) {
|
|
10364
|
+
const issues = [];
|
|
10365
|
+
for (const decl of rule.declarations) {
|
|
10366
|
+
if ((decl.property === "fontSize" || decl.property === "font-size") && typeof decl.value === "string") {
|
|
10367
|
+
const pxMatch = decl.value.match(/^(\d+(\.\d+)?)px$/);
|
|
10368
|
+
if (pxMatch) {
|
|
10369
|
+
const px = parseFloat(pxMatch[1]);
|
|
10370
|
+
if (px < WCAG.MIN_FONT_SIZE) {
|
|
10371
|
+
issues.push({
|
|
10372
|
+
ruleId: rule.id,
|
|
10373
|
+
selector: rule.selector,
|
|
10374
|
+
category: "font-size",
|
|
10375
|
+
severity: "warning",
|
|
10376
|
+
wcagCriterion: WCAG.CRITERIA.fontSize,
|
|
10377
|
+
message: "font-size: " + decl.value + " is below WCAG minimum of " + WCAG.MIN_FONT_SIZE + "px",
|
|
10378
|
+
suggestion: "font-size: max(" + WCAG.MIN_FONT_SIZE + "px, " + decl.value + ")",
|
|
10379
|
+
autoFixable: true
|
|
10380
|
+
});
|
|
10381
|
+
}
|
|
10382
|
+
}
|
|
10383
|
+
}
|
|
10384
|
+
}
|
|
10385
|
+
return issues;
|
|
10386
|
+
}
|
|
10387
|
+
function detectTouchTargets(rule) {
|
|
10388
|
+
const issues = [];
|
|
10389
|
+
const isInteractive = rule.declarations.some(
|
|
10390
|
+
(d) => d.property === "cursor" && d.value === "pointer"
|
|
10391
|
+
);
|
|
10392
|
+
const isButton = rule.selector.includes("btn") || rule.selector.includes("button");
|
|
10393
|
+
const isLink = rule.selector.includes("link") || rule.selector.includes("a");
|
|
10394
|
+
if (!isInteractive && !isButton && !isLink) return issues;
|
|
10395
|
+
const width = rule.declarations.find(
|
|
10396
|
+
(d) => (d.property === "width" || d.property === "min-width") && typeof d.value === "string"
|
|
10397
|
+
);
|
|
10398
|
+
const height = rule.declarations.find(
|
|
10399
|
+
(d) => (d.property === "height" || d.property === "min-height") && typeof d.value === "string"
|
|
10400
|
+
);
|
|
10401
|
+
const hasWidthIssue = width && extractPx(String(width.value)) < WCAG.MIN_TOUCH_TARGET;
|
|
10402
|
+
const hasHeightIssue = height && extractPx(String(height.value)) < WCAG.MIN_TOUCH_TARGET;
|
|
10403
|
+
if (hasWidthIssue || hasHeightIssue) {
|
|
10404
|
+
issues.push({
|
|
10405
|
+
ruleId: rule.id,
|
|
10406
|
+
selector: rule.selector,
|
|
10407
|
+
category: "touch-target",
|
|
10408
|
+
severity: "warning",
|
|
10409
|
+
wcagCriterion: WCAG.CRITERIA.touchTarget,
|
|
10410
|
+
message: 'Interactive element "' + rule.selector + '" may be too small for touch (needs \u2265 ' + WCAG.MIN_TOUCH_TARGET + "\xD7" + WCAG.MIN_TOUCH_TARGET + "px)",
|
|
10411
|
+
suggestion: "Add min-width: " + WCAG.MIN_TOUCH_TARGET + "px; min-height: " + WCAG.MIN_TOUCH_TARGET + "px;",
|
|
10412
|
+
autoFixable: true
|
|
10413
|
+
});
|
|
10414
|
+
}
|
|
10415
|
+
return issues;
|
|
10416
|
+
}
|
|
10417
|
+
function detectMissingFocus(rule) {
|
|
10418
|
+
const issues = [];
|
|
10419
|
+
const isInteractive = rule.declarations.some(
|
|
10420
|
+
(d) => d.property === "cursor" && d.value === "pointer"
|
|
10421
|
+
);
|
|
10422
|
+
if (!isInteractive) return issues;
|
|
10423
|
+
const outline = rule.declarations.find(
|
|
10424
|
+
(d) => d.property === "outline"
|
|
10425
|
+
);
|
|
10426
|
+
const hasFocusStyle = rule.pseudoClasses.some(
|
|
10427
|
+
(pc) => (pc.name === "focus" || pc.name === "focus-visible") && pc.declarations.length > 0
|
|
10428
|
+
);
|
|
10429
|
+
if (outline && String(outline.value) === "none" && !hasFocusStyle) {
|
|
10430
|
+
issues.push({
|
|
10431
|
+
ruleId: rule.id,
|
|
10432
|
+
selector: rule.selector,
|
|
10433
|
+
category: "focus",
|
|
10434
|
+
severity: "error",
|
|
10435
|
+
wcagCriterion: WCAG.CRITERIA.focus,
|
|
10436
|
+
message: '"' + rule.selector + '" has outline: none with no :focus-visible fallback',
|
|
10437
|
+
suggestion: 'Add .focusVisible(c => c.outline("2px solid #3b82f6").outlineOffset("2px"))',
|
|
10438
|
+
autoFixable: false
|
|
10439
|
+
});
|
|
10440
|
+
}
|
|
10441
|
+
if (!outline && !hasFocusStyle && isInteractive) {
|
|
10442
|
+
issues.push({
|
|
10443
|
+
ruleId: rule.id,
|
|
10444
|
+
selector: rule.selector,
|
|
10445
|
+
category: "focus",
|
|
10446
|
+
severity: "warning",
|
|
10447
|
+
wcagCriterion: WCAG.CRITERIA.focus,
|
|
10448
|
+
message: 'Interactive element "' + rule.selector + '" has no visible focus indicator',
|
|
10449
|
+
suggestion: "Add :focus-visible { outline: 2px solid #3b82f6; outline-offset: 2px; }",
|
|
10450
|
+
autoFixable: true
|
|
10451
|
+
});
|
|
10452
|
+
}
|
|
10453
|
+
return issues;
|
|
10454
|
+
}
|
|
10455
|
+
function detectMotionRespect(rule) {
|
|
10456
|
+
const issues = [];
|
|
10457
|
+
const hasAnimation = rule.declarations.some(
|
|
10458
|
+
(d) => (d.property === "animation" || d.property === "animation-name") && typeof d.value === "string" && String(d.value) !== "none"
|
|
10459
|
+
);
|
|
10460
|
+
const hasTransition = rule.declarations.some(
|
|
10461
|
+
(d) => d.property === "transition" && typeof d.value === "string"
|
|
10462
|
+
);
|
|
10463
|
+
const hasReducedMotionWrapper = rule.atRules.some(
|
|
10464
|
+
(at) => at.type === "media" && at.query && at.query.includes("prefers-reduced-motion")
|
|
10465
|
+
);
|
|
10466
|
+
if ((hasAnimation || hasTransition) && !hasReducedMotionWrapper) {
|
|
10467
|
+
issues.push({
|
|
10468
|
+
ruleId: rule.id,
|
|
10469
|
+
selector: rule.selector,
|
|
10470
|
+
category: "motion",
|
|
10471
|
+
severity: "warning",
|
|
10472
|
+
wcagCriterion: WCAG.CRITERIA.motion,
|
|
10473
|
+
message: 'Animations/transitions on "' + rule.selector + '" should respect prefers-reduced-motion',
|
|
10474
|
+
suggestion: "Wrap in @media (prefers-reduced-motion: no-preference) { ... }",
|
|
10475
|
+
autoFixable: true
|
|
10476
|
+
});
|
|
10477
|
+
}
|
|
10478
|
+
return issues;
|
|
10479
|
+
}
|
|
10480
|
+
function detectHoverOnly(rule) {
|
|
10481
|
+
const issues = [];
|
|
10482
|
+
const hasHover = rule.pseudoClasses.some((pc) => pc.name === "hover");
|
|
10483
|
+
const hasFocusVisible = rule.pseudoClasses.some(
|
|
10484
|
+
(pc) => pc.name === "focus" || pc.name === "focus-visible"
|
|
10485
|
+
);
|
|
10486
|
+
if (hasHover && !hasFocusVisible) {
|
|
10487
|
+
issues.push({
|
|
10488
|
+
ruleId: rule.id,
|
|
10489
|
+
selector: rule.selector,
|
|
10490
|
+
category: "hover-only",
|
|
10491
|
+
severity: "warning",
|
|
10492
|
+
wcagCriterion: WCAG.CRITERIA.hoverOnly,
|
|
10493
|
+
message: '"' + rule.selector + '" has hover styles but no :focus-visible fallback. Keyboard users cannot access this interaction.',
|
|
10494
|
+
suggestion: "Add the same styles to :focus-visible for keyboard accessibility.",
|
|
10495
|
+
autoFixable: true
|
|
10496
|
+
});
|
|
10497
|
+
}
|
|
10498
|
+
return issues;
|
|
10499
|
+
}
|
|
10500
|
+
function extractPx(value) {
|
|
10501
|
+
const match = value.match(/^(\d+(\.\d+)?)px$/);
|
|
10502
|
+
return match ? parseFloat(match[1]) : Infinity;
|
|
10503
|
+
}
|
|
10504
|
+
function auditRule(rule) {
|
|
10505
|
+
if (rule.isDead) return [];
|
|
10506
|
+
return [
|
|
10507
|
+
...detectContrast(rule),
|
|
10508
|
+
...detectMinimumFontSize(rule),
|
|
10509
|
+
...detectTouchTargets(rule),
|
|
10510
|
+
...detectMissingFocus(rule),
|
|
10511
|
+
...detectMotionRespect(rule),
|
|
10512
|
+
...detectHoverOnly(rule)
|
|
10513
|
+
];
|
|
10514
|
+
}
|
|
10515
|
+
function generateAccessibilityReport(rules) {
|
|
10516
|
+
const allIssues = [];
|
|
10517
|
+
for (const rule of rules) {
|
|
10518
|
+
allIssues.push(...auditRule(rule));
|
|
10519
|
+
}
|
|
10520
|
+
const errors = allIssues.filter((i) => i.severity === "error");
|
|
10521
|
+
const warnings = allIssues.filter((i) => i.severity === "warning");
|
|
10522
|
+
let summary;
|
|
10523
|
+
if (allIssues.length === 0) {
|
|
10524
|
+
summary = "\u2705 All accessibility checks passed.";
|
|
10525
|
+
} else if (errors.length > 0) {
|
|
10526
|
+
summary = "\u274C " + errors.length + " errors, " + warnings.length + " warnings \u2014 fix errors before shipping.";
|
|
10527
|
+
} else {
|
|
10528
|
+
summary = "\u26A0\uFE0F " + warnings.length + " warnings \u2014 recommended fixes available.";
|
|
10529
|
+
}
|
|
10530
|
+
return {
|
|
10531
|
+
issues: allIssues,
|
|
10532
|
+
errorCount: errors.length,
|
|
10533
|
+
warningCount: warnings.length,
|
|
10534
|
+
passedCount: rules.filter((r) => !r.isDead).length - allIssues.length,
|
|
10535
|
+
summary
|
|
10536
|
+
};
|
|
10537
|
+
}
|
|
10538
|
+
function autoFixFontSize(decl) {
|
|
10539
|
+
return "max(" + WCAG.MIN_FONT_SIZE + "px, " + decl.value + ")";
|
|
10540
|
+
}
|
|
10541
|
+
function autoFixTouchTarget() {
|
|
10542
|
+
return {
|
|
10543
|
+
"min-width": WCAG.MIN_TOUCH_TARGET + "px",
|
|
10544
|
+
"min-height": WCAG.MIN_TOUCH_TARGET + "px"
|
|
10545
|
+
};
|
|
10546
|
+
}
|
|
10547
|
+
var accessibilityPass = (ir) => {
|
|
10548
|
+
for (const rule of ir.rules) {
|
|
10549
|
+
const issues = auditRule(rule);
|
|
10550
|
+
for (const issue of issues) {
|
|
10551
|
+
ir.diagnostics.push({
|
|
10552
|
+
id: "a11y-" + issue.category + "-" + Date.now() + "-" + Math.random().toString(36).slice(2, 6),
|
|
10553
|
+
nodeId: issue.ruleId,
|
|
10554
|
+
severity: issue.severity,
|
|
10555
|
+
message: "[" + issue.wcagCriterion + "] " + issue.message,
|
|
10556
|
+
suggestion: issue.suggestion,
|
|
10557
|
+
pass: "accessibility"
|
|
10558
|
+
});
|
|
10559
|
+
if (issue.autoFixable) {
|
|
10560
|
+
if (issue.category === "font-size") {
|
|
10561
|
+
const decl = rule.declarations.find(
|
|
10562
|
+
(d) => d.property === "fontSize" || d.property === "font-size"
|
|
10563
|
+
);
|
|
10564
|
+
if (decl) {
|
|
10565
|
+
decl.value = autoFixFontSize(decl);
|
|
10566
|
+
decl.history.push({
|
|
10567
|
+
pass: "accessibility",
|
|
10568
|
+
action: "auto-fix-min-font",
|
|
10569
|
+
timestamp: Date.now(),
|
|
10570
|
+
reason: issue.message
|
|
10571
|
+
});
|
|
10572
|
+
}
|
|
10573
|
+
}
|
|
10574
|
+
if (issue.category === "touch-target") {
|
|
10575
|
+
const fixes = autoFixTouchTarget();
|
|
10576
|
+
for (const [prop, value] of Object.entries(fixes)) {
|
|
10577
|
+
rule.declarations.push({
|
|
10578
|
+
id: "a11y-fix-" + Date.now(),
|
|
10579
|
+
property: prop,
|
|
10580
|
+
value,
|
|
10581
|
+
history: [{
|
|
10582
|
+
pass: "accessibility",
|
|
10583
|
+
action: "auto-fix-touch-target",
|
|
10584
|
+
timestamp: Date.now(),
|
|
10585
|
+
reason: issue.message
|
|
10586
|
+
}],
|
|
10587
|
+
meta: { a11y: true }
|
|
10588
|
+
});
|
|
10589
|
+
}
|
|
10590
|
+
}
|
|
10591
|
+
if (issue.category === "focus" && issue.autoFixable) {
|
|
10592
|
+
rule.pseudoClasses.push({
|
|
10593
|
+
id: "a11y-focus-" + Date.now(),
|
|
10594
|
+
name: "focus-visible",
|
|
10595
|
+
declarations: [{
|
|
10596
|
+
id: "a11y-focus-outline",
|
|
10597
|
+
property: "outline",
|
|
10598
|
+
value: "2px solid #3b82f6",
|
|
10599
|
+
history: [{
|
|
10600
|
+
pass: "accessibility",
|
|
10601
|
+
action: "auto-fix-focus",
|
|
10602
|
+
timestamp: Date.now(),
|
|
10603
|
+
reason: issue.message
|
|
10604
|
+
}],
|
|
10605
|
+
meta: {}
|
|
10606
|
+
}, {
|
|
10607
|
+
id: "a11y-focus-offset",
|
|
10608
|
+
property: "outlineOffset",
|
|
10609
|
+
value: "2px",
|
|
10610
|
+
history: [],
|
|
10611
|
+
meta: {}
|
|
10612
|
+
}],
|
|
10613
|
+
source: rule.source,
|
|
10614
|
+
history: []
|
|
10615
|
+
});
|
|
10616
|
+
}
|
|
10617
|
+
if (issue.category === "motion" && issue.autoFixable) {
|
|
10618
|
+
const motionDecls = rule.declarations.filter(
|
|
10619
|
+
(d) => d.property === "animation" || d.property === "transition"
|
|
10620
|
+
);
|
|
10621
|
+
if (motionDecls.length > 0) {
|
|
10622
|
+
rule.atRules.push({
|
|
10623
|
+
id: "a11y-motion-" + Date.now(),
|
|
10624
|
+
type: "media",
|
|
10625
|
+
query: "(prefers-reduced-motion: no-preference)",
|
|
10626
|
+
declarations: motionDecls.map((d) => ({ ...d, id: d.id + "-motion" })),
|
|
10627
|
+
nestedRules: [],
|
|
10628
|
+
source: rule.source,
|
|
10629
|
+
history: [{
|
|
10630
|
+
pass: "accessibility",
|
|
10631
|
+
action: "auto-fix-motion",
|
|
10632
|
+
timestamp: Date.now(),
|
|
10633
|
+
reason: "Wrapped in prefers-reduced-motion query"
|
|
10634
|
+
}]
|
|
10635
|
+
});
|
|
10636
|
+
rule.declarations = rule.declarations.filter(
|
|
10637
|
+
(d) => !motionDecls.includes(d)
|
|
10638
|
+
);
|
|
10639
|
+
}
|
|
10640
|
+
}
|
|
10641
|
+
}
|
|
10642
|
+
}
|
|
10643
|
+
}
|
|
10644
|
+
ir.meta = ir.meta || {};
|
|
10645
|
+
ir.meta.accessibilityReport = generateAccessibilityReport(ir.rules);
|
|
10646
|
+
return ir;
|
|
10647
|
+
};
|
|
10648
|
+
function auditAccessibility(rules) {
|
|
10649
|
+
return generateAccessibilityReport(rules);
|
|
10650
|
+
}
|
|
10651
|
+
function checkRule(rule) {
|
|
10652
|
+
return auditRule(rule);
|
|
10653
|
+
}
|
|
10654
|
+
var accessibilityEngine = {
|
|
10655
|
+
audit: auditAccessibility,
|
|
10656
|
+
checkRule,
|
|
10657
|
+
pass: accessibilityPass,
|
|
10658
|
+
wcag: WCAG
|
|
10659
|
+
};
|
|
10660
|
+
|
|
10661
|
+
// src/compiler/intent-api.ts
|
|
10662
|
+
var INTENT_CATALOG = {
|
|
10663
|
+
// ==========================================================================
|
|
10664
|
+
// LAYOUT INTENTS
|
|
10665
|
+
// ==========================================================================
|
|
10666
|
+
"center-content": {
|
|
10667
|
+
name: "center-content",
|
|
10668
|
+
category: "layout",
|
|
10669
|
+
description: "Center content both horizontally and vertically",
|
|
10670
|
+
semantics: [
|
|
10671
|
+
{ category: "surface", intent: "container" }
|
|
10672
|
+
],
|
|
10673
|
+
properties: {
|
|
10674
|
+
display: "flex",
|
|
10675
|
+
justifyContent: "center",
|
|
10676
|
+
alignItems: "center"
|
|
10677
|
+
}
|
|
10678
|
+
},
|
|
10679
|
+
"stack": {
|
|
10680
|
+
name: "stack",
|
|
10681
|
+
category: "layout",
|
|
10682
|
+
description: "Vertical stack with consistent spacing",
|
|
10683
|
+
properties: {
|
|
10684
|
+
display: "flex",
|
|
10685
|
+
flexDirection: "column"
|
|
10686
|
+
},
|
|
10687
|
+
semantics: [
|
|
10688
|
+
{ category: "spacing", intent: "comfortable" }
|
|
10689
|
+
]
|
|
10690
|
+
},
|
|
10691
|
+
"sidebar-layout": {
|
|
10692
|
+
name: "sidebar-layout",
|
|
10693
|
+
category: "layout",
|
|
10694
|
+
description: "Two-column layout with mobile collapse",
|
|
10695
|
+
properties: {
|
|
10696
|
+
display: "grid",
|
|
10697
|
+
gridTemplateColumns: "280px 1fr",
|
|
10698
|
+
minHeight: "100vh"
|
|
10699
|
+
},
|
|
10700
|
+
semantics: [
|
|
10701
|
+
{ category: "spacing", intent: "comfortable" }
|
|
10702
|
+
],
|
|
10703
|
+
responsive: {
|
|
10704
|
+
"mobile": { gridTemplateColumns: "1fr" }
|
|
10705
|
+
}
|
|
10706
|
+
},
|
|
10707
|
+
"grid-list": {
|
|
10708
|
+
name: "grid-list",
|
|
10709
|
+
category: "layout",
|
|
10710
|
+
description: "Responsive auto-fit grid",
|
|
10711
|
+
properties: {
|
|
10712
|
+
display: "grid",
|
|
10713
|
+
gridTemplateColumns: "repeat(auto-fit, minmax(280px, 1fr))"
|
|
10714
|
+
},
|
|
10715
|
+
semantics: [
|
|
10716
|
+
{ category: "spacing", intent: "comfortable" }
|
|
10717
|
+
]
|
|
10718
|
+
},
|
|
10719
|
+
// ==========================================================================
|
|
10720
|
+
// COMPONENT INTENTS
|
|
10721
|
+
// ==========================================================================
|
|
10722
|
+
"card": {
|
|
10723
|
+
name: "card",
|
|
10724
|
+
category: "component",
|
|
10725
|
+
description: "Content card with shadow, radius, and hover lift",
|
|
10726
|
+
semantics: [
|
|
10727
|
+
{ category: "surface", intent: "container" },
|
|
10728
|
+
{ category: "elevation", intent: "raised" },
|
|
10729
|
+
{ category: "spacing", intent: "comfortable" }
|
|
10730
|
+
],
|
|
10731
|
+
properties: {
|
|
10732
|
+
display: "flex",
|
|
10733
|
+
flexDirection: "column",
|
|
10734
|
+
overflow: "hidden",
|
|
10735
|
+
transition: "box-shadow 0.2s ease, transform 0.2s ease"
|
|
10736
|
+
},
|
|
10737
|
+
states: {
|
|
10738
|
+
hover: {
|
|
10739
|
+
boxShadow: "0 10px 30px rgba(0,0,0,0.15)",
|
|
10740
|
+
transform: "translateY(-2px)"
|
|
10741
|
+
}
|
|
10742
|
+
},
|
|
10743
|
+
responsive: {
|
|
10744
|
+
"mobile": { padding: "16px" }
|
|
10745
|
+
},
|
|
10746
|
+
a11y: ["contrast", "focus-visible"]
|
|
10747
|
+
},
|
|
10748
|
+
"button-primary": {
|
|
10749
|
+
name: "button-primary",
|
|
10750
|
+
category: "component",
|
|
10751
|
+
description: "Primary call-to-action button",
|
|
10752
|
+
semantics: [
|
|
10753
|
+
{ category: "surface", intent: "interactive" },
|
|
10754
|
+
{ category: "spacing", intent: "compact" },
|
|
10755
|
+
{ category: "state", intent: "hover" },
|
|
10756
|
+
{ category: "state", intent: "focus" },
|
|
10757
|
+
{ category: "state", intent: "active" },
|
|
10758
|
+
{ category: "state", intent: "disabled" }
|
|
10759
|
+
],
|
|
10760
|
+
properties: {
|
|
10761
|
+
display: "inline-flex",
|
|
10762
|
+
alignItems: "center",
|
|
10763
|
+
justifyContent: "center",
|
|
10764
|
+
fontWeight: "600",
|
|
10765
|
+
border: "none",
|
|
10766
|
+
userSelect: "none"
|
|
10767
|
+
},
|
|
10768
|
+
a11y: ["contrast", "touch-target", "focus-visible"]
|
|
10769
|
+
},
|
|
10770
|
+
"button-secondary": {
|
|
10771
|
+
name: "button-secondary",
|
|
10772
|
+
category: "component",
|
|
10773
|
+
description: "Secondary outlined button",
|
|
10774
|
+
semantics: [
|
|
10775
|
+
{ category: "spacing", intent: "compact" },
|
|
10776
|
+
{ category: "state", intent: "focus" },
|
|
10777
|
+
{ category: "state", intent: "disabled" }
|
|
10778
|
+
],
|
|
10779
|
+
properties: {
|
|
10780
|
+
display: "inline-flex",
|
|
10781
|
+
alignItems: "center",
|
|
10782
|
+
justifyContent: "center",
|
|
10783
|
+
fontWeight: "500",
|
|
10784
|
+
backgroundColor: "transparent",
|
|
10785
|
+
border: "1px solid $colors.neutral.300",
|
|
10786
|
+
color: "$colors.neutral.700",
|
|
10787
|
+
userSelect: "none"
|
|
10788
|
+
},
|
|
10789
|
+
states: {
|
|
10790
|
+
hover: { backgroundColor: "$colors.neutral.50" }
|
|
10791
|
+
},
|
|
10792
|
+
a11y: ["contrast", "touch-target", "focus-visible"]
|
|
10793
|
+
},
|
|
10794
|
+
"input-field": {
|
|
10795
|
+
name: "input-field",
|
|
10796
|
+
category: "component",
|
|
10797
|
+
description: "Text input with focus and error states",
|
|
10798
|
+
semantics: [
|
|
10799
|
+
{ category: "surface", intent: "input" },
|
|
10800
|
+
{ category: "spacing", intent: "compact" },
|
|
10801
|
+
{ category: "state", intent: "focus" },
|
|
10802
|
+
{ category: "state", intent: "disabled" }
|
|
10803
|
+
],
|
|
10804
|
+
properties: {
|
|
10805
|
+
width: "100%",
|
|
10806
|
+
fontSize: "16px",
|
|
10807
|
+
lineHeight: "1.5",
|
|
10808
|
+
transition: "border-color 0.2s ease, box-shadow 0.2s ease"
|
|
10809
|
+
},
|
|
10810
|
+
a11y: ["contrast"]
|
|
10811
|
+
},
|
|
10812
|
+
"modal": {
|
|
10813
|
+
name: "modal",
|
|
10814
|
+
category: "component",
|
|
10815
|
+
description: "Modal dialog with overlay backdrop",
|
|
10816
|
+
semantics: [
|
|
10817
|
+
{ category: "surface", intent: "overlay" },
|
|
10818
|
+
{ category: "elevation", intent: "modal" },
|
|
10819
|
+
{ category: "spacing", intent: "spacious" }
|
|
10820
|
+
],
|
|
10821
|
+
properties: {
|
|
10822
|
+
display: "flex",
|
|
10823
|
+
flexDirection: "column",
|
|
10824
|
+
maxWidth: "560px",
|
|
10825
|
+
margin: "auto"
|
|
10826
|
+
},
|
|
10827
|
+
a11y: ["contrast", "focus-visible"]
|
|
10828
|
+
},
|
|
10829
|
+
"tooltip": {
|
|
10830
|
+
name: "tooltip",
|
|
10831
|
+
category: "component",
|
|
10832
|
+
description: "Hover tooltip",
|
|
10833
|
+
semantics: [
|
|
10834
|
+
{ category: "surface", intent: "tooltip" }
|
|
10835
|
+
],
|
|
10836
|
+
properties: {
|
|
10837
|
+
position: "absolute",
|
|
10838
|
+
zIndex: "50",
|
|
10839
|
+
pointerEvents: "none"
|
|
10840
|
+
},
|
|
10841
|
+
a11y: ["contrast"]
|
|
10842
|
+
},
|
|
10843
|
+
// ==========================================================================
|
|
10844
|
+
// SEMANTIC INTENTS
|
|
10845
|
+
// ==========================================================================
|
|
10846
|
+
"hero-section": {
|
|
10847
|
+
name: "hero-section",
|
|
10848
|
+
category: "semantic",
|
|
10849
|
+
description: "Full-width hero banner",
|
|
10850
|
+
semantics: [
|
|
10851
|
+
{ category: "spacing", intent: "generous" }
|
|
10852
|
+
],
|
|
10853
|
+
properties: {
|
|
10854
|
+
display: "flex",
|
|
10855
|
+
flexDirection: "column",
|
|
10856
|
+
justifyContent: "center",
|
|
10857
|
+
alignItems: "center",
|
|
10858
|
+
width: "100%",
|
|
10859
|
+
minHeight: "60vh",
|
|
10860
|
+
textAlign: "center"
|
|
10861
|
+
},
|
|
10862
|
+
responsive: {
|
|
10863
|
+
"mobile": { minHeight: "40vh", padding: "32px 16px" }
|
|
10864
|
+
}
|
|
10865
|
+
},
|
|
10866
|
+
"sticky-header": {
|
|
10867
|
+
name: "sticky-header",
|
|
10868
|
+
category: "semantic",
|
|
10869
|
+
description: "Sticky header with backdrop blur",
|
|
10870
|
+
semantics: [
|
|
10871
|
+
{ category: "elevation", intent: "sticky" },
|
|
10872
|
+
{ category: "spacing", intent: "compact" }
|
|
10873
|
+
],
|
|
10874
|
+
properties: {
|
|
10875
|
+
backgroundColor: "rgba(255,255,255,0.9)",
|
|
10876
|
+
backdropFilter: "blur(8px)",
|
|
10877
|
+
borderBottom: "1px solid rgba(0,0,0,0.05)"
|
|
10878
|
+
}
|
|
10879
|
+
},
|
|
10880
|
+
"call-to-action": {
|
|
10881
|
+
name: "call-to-action",
|
|
10882
|
+
category: "semantic",
|
|
10883
|
+
description: "Attention-grabbing CTA section",
|
|
10884
|
+
semantics: [
|
|
10885
|
+
{ category: "surface", intent: "interactive" },
|
|
10886
|
+
{ category: "spacing", intent: "spacious" }
|
|
10887
|
+
],
|
|
10888
|
+
properties: {
|
|
10889
|
+
textAlign: "center"
|
|
10890
|
+
}
|
|
10891
|
+
},
|
|
10892
|
+
"muted-text": {
|
|
10893
|
+
name: "muted-text",
|
|
10894
|
+
category: "semantic",
|
|
10895
|
+
description: "Secondary, less prominent text",
|
|
10896
|
+
semantics: [
|
|
10897
|
+
{ category: "text", intent: "muted" }
|
|
10898
|
+
]
|
|
10899
|
+
},
|
|
10900
|
+
"visually-hidden": {
|
|
10901
|
+
name: "visually-hidden",
|
|
10902
|
+
category: "semantic",
|
|
10903
|
+
description: "Visible only to screen readers",
|
|
10904
|
+
properties: {
|
|
10905
|
+
position: "absolute",
|
|
10906
|
+
width: "1px",
|
|
10907
|
+
height: "1px",
|
|
10908
|
+
padding: "0",
|
|
10909
|
+
margin: "-1px",
|
|
10910
|
+
overflow: "hidden",
|
|
10911
|
+
clip: "rect(0, 0, 0, 0)",
|
|
10912
|
+
whiteSpace: "nowrap",
|
|
10913
|
+
borderWidth: "0"
|
|
10914
|
+
}
|
|
10915
|
+
},
|
|
10916
|
+
// ==========================================================================
|
|
10917
|
+
// INTERACTION INTENTS
|
|
10918
|
+
// ==========================================================================
|
|
10919
|
+
"hover-lift": {
|
|
10920
|
+
name: "hover-lift",
|
|
10921
|
+
category: "interaction",
|
|
10922
|
+
description: "Subtle lift on hover",
|
|
10923
|
+
states: {
|
|
10924
|
+
hover: {
|
|
10925
|
+
transform: "translateY(-2px)",
|
|
10926
|
+
boxShadow: "0 8px 25px rgba(0,0,0,0.12)",
|
|
10927
|
+
transition: "all 0.2s ease"
|
|
10928
|
+
}
|
|
10929
|
+
},
|
|
10930
|
+
a11y: ["focus-visible"]
|
|
10931
|
+
},
|
|
10932
|
+
"focus-ring": {
|
|
10933
|
+
name: "focus-ring",
|
|
10934
|
+
category: "interaction",
|
|
10935
|
+
description: "Accessible focus indicator",
|
|
10936
|
+
states: {
|
|
10937
|
+
"focus-visible": {
|
|
10938
|
+
outline: "2px solid $colors.primary.500",
|
|
10939
|
+
outlineOffset: "2px"
|
|
10940
|
+
}
|
|
10941
|
+
}
|
|
10942
|
+
}
|
|
10943
|
+
};
|
|
10944
|
+
function resolveIntent(intentName, options) {
|
|
10945
|
+
const intent2 = INTENT_CATALOG[intentName];
|
|
10946
|
+
if (!intent2) return null;
|
|
10947
|
+
const properties = {};
|
|
10948
|
+
const states = {};
|
|
10949
|
+
const responsive = {};
|
|
10950
|
+
if (intent2.semantics) {
|
|
10951
|
+
for (const sem of intent2.semantics) {
|
|
10952
|
+
const resolved = resolveSemantic(sem.category, sem.intent, {
|
|
10953
|
+
mode: options?.theme || "light"
|
|
10954
|
+
});
|
|
10955
|
+
if (resolved) {
|
|
10956
|
+
for (const [prop, value] of Object.entries(resolved.properties)) {
|
|
10957
|
+
if (resolved.pseudoClass) {
|
|
10958
|
+
if (!states[resolved.pseudoClass]) states[resolved.pseudoClass] = {};
|
|
10959
|
+
states[resolved.pseudoClass][prop] = value;
|
|
10960
|
+
} else {
|
|
10961
|
+
properties[prop] = value;
|
|
10962
|
+
}
|
|
10963
|
+
}
|
|
10964
|
+
}
|
|
10965
|
+
}
|
|
10966
|
+
}
|
|
10967
|
+
if (intent2.properties) {
|
|
10968
|
+
Object.assign(properties, intent2.properties);
|
|
10969
|
+
}
|
|
10970
|
+
if (intent2.states) {
|
|
10971
|
+
for (const [state, props] of Object.entries(intent2.states)) {
|
|
10972
|
+
if (!states[state]) states[state] = {};
|
|
10973
|
+
Object.assign(states[state], props);
|
|
10974
|
+
}
|
|
10975
|
+
}
|
|
10976
|
+
if (intent2.responsive) {
|
|
10977
|
+
Object.assign(responsive, intent2.responsive);
|
|
10978
|
+
}
|
|
10979
|
+
return {
|
|
10980
|
+
properties,
|
|
10981
|
+
states,
|
|
10982
|
+
responsive,
|
|
10983
|
+
a11y: intent2.a11y || [],
|
|
10984
|
+
description: intent2.description
|
|
10985
|
+
};
|
|
10986
|
+
}
|
|
10987
|
+
function getAvailableIntents() {
|
|
10988
|
+
return Object.keys(INTENT_CATALOG);
|
|
10989
|
+
}
|
|
10990
|
+
function getIntentsByCategory(category) {
|
|
10991
|
+
return Object.entries(INTENT_CATALOG).filter(([, def]) => def.category === category).map(([name]) => name);
|
|
10992
|
+
}
|
|
10993
|
+
function getIntentDescription(intentName) {
|
|
10994
|
+
return INTENT_CATALOG[intentName]?.description || null;
|
|
10995
|
+
}
|
|
10996
|
+
var intentAPIPass = (ir) => {
|
|
10997
|
+
for (const rule of ir.rules) {
|
|
10998
|
+
const intentName = rule.meta._intent;
|
|
10999
|
+
if (!intentName) continue;
|
|
11000
|
+
const resolved = resolveIntent(intentName);
|
|
11001
|
+
if (!resolved) continue;
|
|
11002
|
+
for (const [prop, value] of Object.entries(resolved.properties)) {
|
|
11003
|
+
rule.declarations.push({
|
|
11004
|
+
id: "intent-prop-" + Date.now() + "-" + prop,
|
|
11005
|
+
property: prop,
|
|
11006
|
+
value,
|
|
11007
|
+
history: [{
|
|
11008
|
+
pass: "intent-api",
|
|
11009
|
+
action: "resolved-intent",
|
|
11010
|
+
timestamp: Date.now(),
|
|
11011
|
+
reason: 'intent("' + intentName + '") \u2192 ' + prop + ": " + value
|
|
11012
|
+
}],
|
|
11013
|
+
meta: { intent: intentName }
|
|
11014
|
+
});
|
|
11015
|
+
}
|
|
11016
|
+
for (const [stateName, stateProps] of Object.entries(resolved.states)) {
|
|
11017
|
+
rule.pseudoClasses.push({
|
|
11018
|
+
id: "intent-state-" + Date.now() + "-" + stateName,
|
|
11019
|
+
name: stateName,
|
|
11020
|
+
declarations: Object.entries(stateProps).map(([prop, value]) => ({
|
|
11021
|
+
id: "intent-decl-" + prop,
|
|
11022
|
+
property: prop,
|
|
11023
|
+
value,
|
|
11024
|
+
history: [{
|
|
11025
|
+
pass: "intent-api",
|
|
11026
|
+
action: "resolved-state",
|
|
11027
|
+
timestamp: Date.now(),
|
|
11028
|
+
reason: 'intent("' + intentName + '") state:' + stateName
|
|
11029
|
+
}],
|
|
11030
|
+
meta: {}
|
|
11031
|
+
})),
|
|
11032
|
+
source: rule.source,
|
|
11033
|
+
history: []
|
|
11034
|
+
});
|
|
11035
|
+
}
|
|
11036
|
+
if (Object.keys(resolved.responsive).length > 0) {
|
|
11037
|
+
rule.meta._responsiveIntents = resolved.responsive;
|
|
11038
|
+
}
|
|
11039
|
+
if (resolved.a11y.length > 0) {
|
|
11040
|
+
rule.meta._a11yRequirements = resolved.a11y;
|
|
11041
|
+
}
|
|
11042
|
+
}
|
|
11043
|
+
return ir;
|
|
11044
|
+
};
|
|
11045
|
+
var intentAPI = {
|
|
11046
|
+
resolve: resolveIntent,
|
|
11047
|
+
list: getAvailableIntents,
|
|
11048
|
+
byCategory: getIntentsByCategory,
|
|
11049
|
+
description: getIntentDescription,
|
|
11050
|
+
catalog: INTENT_CATALOG,
|
|
11051
|
+
pass: intentAPIPass
|
|
11052
|
+
};
|
|
11053
|
+
|
|
7643
11054
|
// src/index.ts
|
|
7644
11055
|
init_Chain();
|
|
7645
11056
|
|
|
@@ -8617,53 +12028,86 @@ export {
|
|
|
8617
12028
|
CacheManager,
|
|
8618
12029
|
ChainCSSCompiler,
|
|
8619
12030
|
ChainCSSPrefixer,
|
|
12031
|
+
DEFAULT_PIPELINE,
|
|
8620
12032
|
DesignTokens,
|
|
12033
|
+
PassManager,
|
|
8621
12034
|
PersistentCache,
|
|
8622
12035
|
SCROLL_PRESETS,
|
|
8623
12036
|
StyleAnalyzer,
|
|
8624
12037
|
StyleGraphCompiler,
|
|
8625
12038
|
Theme,
|
|
8626
12039
|
VERSION2 as VERSION,
|
|
12040
|
+
accessibilityEngine,
|
|
12041
|
+
accessibilityPass,
|
|
8627
12042
|
add,
|
|
12043
|
+
analyzeResponsive,
|
|
8628
12044
|
analyze as analyzeStyle,
|
|
8629
12045
|
animationPresets,
|
|
12046
|
+
applyPass,
|
|
12047
|
+
applyPasses,
|
|
12048
|
+
atomicExtractionPass,
|
|
12049
|
+
auditAccessibility,
|
|
8630
12050
|
auditContrast,
|
|
8631
12051
|
autoDetector,
|
|
12052
|
+
autoFixAll,
|
|
12053
|
+
autoFixIssue,
|
|
8632
12054
|
buildChain,
|
|
8633
12055
|
chain,
|
|
8634
12056
|
smartChain as chainV3,
|
|
8635
12057
|
checkContrast,
|
|
12058
|
+
checkRule,
|
|
8636
12059
|
clearTimeline,
|
|
8637
12060
|
compileChainCSS,
|
|
8638
12061
|
compileGraph,
|
|
8639
12062
|
compileScrollAnimation,
|
|
8640
12063
|
compileScrollAnimations,
|
|
12064
|
+
compileViaIR,
|
|
8641
12065
|
configureAtomic,
|
|
12066
|
+
constraintSolver,
|
|
12067
|
+
constraintSolverPass,
|
|
8642
12068
|
contrastRatio,
|
|
8643
12069
|
convert,
|
|
8644
12070
|
correct,
|
|
12071
|
+
countNodes,
|
|
8645
12072
|
createAnimation,
|
|
8646
12073
|
createContextualToken,
|
|
12074
|
+
createDeclaration,
|
|
8647
12075
|
createTokens as createDesignTokens,
|
|
12076
|
+
createIR,
|
|
12077
|
+
createRule,
|
|
8648
12078
|
createScrollAnimation,
|
|
8649
12079
|
createSmartComponent,
|
|
8650
12080
|
createTheme,
|
|
8651
12081
|
createThemeContract,
|
|
8652
12082
|
createTokens2 as createTokens,
|
|
12083
|
+
cssCompressionPass,
|
|
12084
|
+
cssIfTranspilePass,
|
|
12085
|
+
deadEliminationPass,
|
|
12086
|
+
debugIR,
|
|
8653
12087
|
index_default as default,
|
|
12088
|
+
diagnosticsExportPass,
|
|
8654
12089
|
divide,
|
|
8655
12090
|
enableDebug,
|
|
8656
12091
|
enableTimeline,
|
|
8657
12092
|
expandShorthand,
|
|
8658
12093
|
exportTimeline,
|
|
8659
12094
|
fluidType,
|
|
12095
|
+
generateCSS,
|
|
8660
12096
|
generateContextualCSS,
|
|
12097
|
+
generateResponsiveReport,
|
|
8661
12098
|
getAnimationPreset,
|
|
8662
12099
|
getAnimationPresetNames,
|
|
12100
|
+
getAvailableIntents,
|
|
8663
12101
|
getAvailableShorthands,
|
|
12102
|
+
getExtractionCandidates,
|
|
8664
12103
|
getIntent,
|
|
12104
|
+
getIntentDescription,
|
|
12105
|
+
getIntentsByCategory,
|
|
12106
|
+
getLayoutPatterns,
|
|
8665
12107
|
getPropertySuggestion,
|
|
8666
12108
|
getScrollPresets,
|
|
12109
|
+
getSemanticDescription,
|
|
12110
|
+
getSemanticIntents,
|
|
8667
12111
|
getShorthandSuggestion,
|
|
8668
12112
|
getStyleChanges,
|
|
8669
12113
|
getStyleDiff,
|
|
@@ -8676,25 +12120,56 @@ export {
|
|
|
8676
12120
|
helpers,
|
|
8677
12121
|
injectChainStyles,
|
|
8678
12122
|
intent,
|
|
12123
|
+
intentAPI,
|
|
12124
|
+
intentAPIPass,
|
|
12125
|
+
intentRecoveryPass,
|
|
8679
12126
|
isShorthand,
|
|
12127
|
+
layoutIntelligence,
|
|
12128
|
+
layoutIntelligencePass,
|
|
12129
|
+
learnPatterns,
|
|
8680
12130
|
macros,
|
|
8681
12131
|
math,
|
|
12132
|
+
mediaQueryPackingPass,
|
|
8682
12133
|
multiply,
|
|
12134
|
+
optimizeSource,
|
|
8683
12135
|
orchestrator,
|
|
12136
|
+
parseConstraint,
|
|
12137
|
+
parseIR,
|
|
12138
|
+
patternLearner,
|
|
12139
|
+
patternLearningPass,
|
|
8684
12140
|
recipe,
|
|
12141
|
+
recognizeLayout,
|
|
12142
|
+
resetIdCounter,
|
|
12143
|
+
resolveConstraint,
|
|
12144
|
+
resolveContainerQuery,
|
|
8685
12145
|
resolveContextual,
|
|
12146
|
+
resolveIntent,
|
|
12147
|
+
resolveSemantic,
|
|
12148
|
+
resolveStickyUntil,
|
|
12149
|
+
responsiveInference,
|
|
12150
|
+
responsiveInferencePass,
|
|
12151
|
+
runDefaultPipeline,
|
|
8686
12152
|
runtimeChain,
|
|
8687
12153
|
scale,
|
|
8688
12154
|
scrollTimeline,
|
|
12155
|
+
semanticTokens,
|
|
12156
|
+
semanticTokensPass,
|
|
8689
12157
|
setBreakpoints,
|
|
8690
12158
|
shorthandMap,
|
|
8691
12159
|
smartChain,
|
|
12160
|
+
sourceOptimizer,
|
|
12161
|
+
sourceOptimizerPass,
|
|
12162
|
+
specificitySortPass,
|
|
12163
|
+
styleIR,
|
|
8692
12164
|
subtract,
|
|
12165
|
+
suggestMacro,
|
|
8693
12166
|
toPx,
|
|
8694
12167
|
tokens2 as tokens,
|
|
12168
|
+
unitResolutionPass,
|
|
8695
12169
|
useSmartStyles,
|
|
8696
12170
|
validateTheme,
|
|
8697
12171
|
validateTokenRelationships,
|
|
8698
12172
|
validate as validateValue,
|
|
12173
|
+
validationPass,
|
|
8699
12174
|
withSmartStyles
|
|
8700
12175
|
};
|