Remove CSSX style dictionary infrastructure — styling is just components
The entire parallel CSS system (StyleValue type, style dictionary, keyword atom resolver, content-addressed class generation, runtime CSS injection, localStorage caching) was built but never adopted — the codebase already uses :class strings with defcomp components for all styling. Remove ~3,000 lines of unused infrastructure. Deleted: - cssx.sx spec module (317 lines) - style_dict.py (782 lines) and style_resolver.py (254 lines) - StyleValue type, defkeyframes special form, build-keyframes platform fn - Style dict JSON delivery (<script type="text/sx-styles">), cookies, localStorage - css/merge-styles primitives, inject-style-value, fnv1a-hash platform interface Simplified: - defstyle now binds any value (string, function) — no StyleValue type needed - render-attrs no longer special-cases :style StyleValue → class conversion - Boot sequence skips style dict init step Preserved: - tw.css parsing + CSS class delivery (SX-Css headers, <style id="sx-css">) - All component infrastructure (defcomp, caching, bundling, deps) - defstyle as a binding form for reusable class strings Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -59,15 +59,6 @@
|
|||||||
function RawHTML(html) { this.html = html; }
|
function RawHTML(html) { this.html = html; }
|
||||||
RawHTML.prototype._raw = true;
|
RawHTML.prototype._raw = true;
|
||||||
|
|
||||||
function StyleValue(className, declarations, mediaRules, pseudoRules, keyframes) {
|
|
||||||
this.className = className;
|
|
||||||
this.declarations = declarations || "";
|
|
||||||
this.mediaRules = mediaRules || [];
|
|
||||||
this.pseudoRules = pseudoRules || [];
|
|
||||||
this.keyframes = keyframes || [];
|
|
||||||
}
|
|
||||||
StyleValue.prototype._styleValue = true;
|
|
||||||
|
|
||||||
function isSym(x) { return x != null && x._sym === true; }
|
function isSym(x) { return x != null && x._sym === true; }
|
||||||
function isKw(x) { return x != null && x._kw === true; }
|
function isKw(x) { return x != null && x._kw === true; }
|
||||||
|
|
||||||
@@ -103,7 +94,6 @@
|
|||||||
if (x._component) return "component";
|
if (x._component) return "component";
|
||||||
if (x._macro) return "macro";
|
if (x._macro) return "macro";
|
||||||
if (x._raw) return "raw-html";
|
if (x._raw) return "raw-html";
|
||||||
if (x._styleValue) return "style-value";
|
|
||||||
if (typeof Node !== "undefined" && x instanceof Node) return "dom-node";
|
if (typeof Node !== "undefined" && x instanceof Node) return "dom-node";
|
||||||
if (Array.isArray(x)) return "list";
|
if (Array.isArray(x)) return "list";
|
||||||
if (typeof x === "object") return "dict";
|
if (typeof x === "object") return "dict";
|
||||||
@@ -150,27 +140,6 @@
|
|||||||
function isComponent(x) { return x != null && x._component === true; }
|
function isComponent(x) { return x != null && x._component === true; }
|
||||||
function isMacro(x) { return x != null && x._macro === true; }
|
function isMacro(x) { return x != null && x._macro === true; }
|
||||||
|
|
||||||
function isStyleValue(x) { return x != null && x._styleValue === true; }
|
|
||||||
function styleValueClass(x) { return x.className; }
|
|
||||||
function styleValue_p(x) { return x != null && x._styleValue === true; }
|
|
||||||
|
|
||||||
function buildKeyframes(kfName, steps, env) {
|
|
||||||
// Platform implementation of defkeyframes
|
|
||||||
var parts = [];
|
|
||||||
for (var i = 0; i < steps.length; i++) {
|
|
||||||
var step = steps[i];
|
|
||||||
var selector = isSym(step[0]) ? step[0].name : String(step[0]);
|
|
||||||
var body = trampoline(evalExpr(step[1], env));
|
|
||||||
var decls = isStyleValue(body) ? body.declarations : String(body);
|
|
||||||
parts.push(selector + "{" + decls + "}");
|
|
||||||
}
|
|
||||||
var kfRule = "@keyframes " + kfName + "{" + parts.join("") + "}";
|
|
||||||
var cn = "sx-ref-kf-" + kfName;
|
|
||||||
var sv = new StyleValue(cn, "animation-name:" + kfName, [], [], [[kfName, kfRule]]);
|
|
||||||
env[kfName] = sv;
|
|
||||||
return sv;
|
|
||||||
}
|
|
||||||
|
|
||||||
function envHas(env, name) { return name in env; }
|
function envHas(env, name) { return name in env; }
|
||||||
function envGet(env, name) { return env[name]; }
|
function envGet(env, name) { return env[name]; }
|
||||||
function envSet(env, name, val) { env[name] = val; }
|
function envSet(env, name, val) { env[name] = val; }
|
||||||
@@ -384,29 +353,6 @@
|
|||||||
PRIMITIVES["strip-tags"] = function(s) { return String(s).replace(/<[^>]+>/g, ""); };
|
PRIMITIVES["strip-tags"] = function(s) { return String(s).replace(/<[^>]+>/g, ""); };
|
||||||
|
|
||||||
|
|
||||||
// stdlib.style
|
|
||||||
PRIMITIVES["css"] = function() {
|
|
||||||
var atoms = [];
|
|
||||||
for (var i = 0; i < arguments.length; i++) {
|
|
||||||
var a = arguments[i];
|
|
||||||
if (isNil(a) || a === false) continue;
|
|
||||||
atoms.push(isKw(a) ? a.name : String(a));
|
|
||||||
}
|
|
||||||
if (!atoms.length) return NIL;
|
|
||||||
return new StyleValue("sx-" + atoms.join("-"), atoms.join(";"), [], [], []);
|
|
||||||
};
|
|
||||||
PRIMITIVES["merge-styles"] = function() {
|
|
||||||
var valid = [];
|
|
||||||
for (var i = 0; i < arguments.length; i++) {
|
|
||||||
if (isStyleValue(arguments[i])) valid.push(arguments[i]);
|
|
||||||
}
|
|
||||||
if (!valid.length) return NIL;
|
|
||||||
if (valid.length === 1) return valid[0];
|
|
||||||
var allDecls = valid.map(function(v) { return v.declarations; }).join(";");
|
|
||||||
return new StyleValue("sx-merged", allDecls, [], [], []);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// stdlib.debug
|
// stdlib.debug
|
||||||
PRIMITIVES["assert"] = function(cond, msg) {
|
PRIMITIVES["assert"] = function(cond, msg) {
|
||||||
if (!isSxTruthy(cond)) throw new Error("Assertion error: " + (msg || "Assertion failed"));
|
if (!isSxTruthy(cond)) throw new Error("Assertion error: " + (msg || "Assertion failed"));
|
||||||
@@ -493,7 +439,7 @@
|
|||||||
function isSpecialForm(n) { return n in {
|
function isSpecialForm(n) { return n in {
|
||||||
"if":1,"when":1,"cond":1,"case":1,"and":1,"or":1,"let":1,"let*":1,
|
"if":1,"when":1,"cond":1,"case":1,"and":1,"or":1,"let":1,"let*":1,
|
||||||
"lambda":1,"fn":1,"define":1,"defcomp":1,"defmacro":1,"defstyle":1,
|
"lambda":1,"fn":1,"define":1,"defcomp":1,"defmacro":1,"defstyle":1,
|
||||||
"defkeyframes":1,"defhandler":1,"begin":1,"do":1,
|
"defhandler":1,"begin":1,"do":1,
|
||||||
"quote":1,"quasiquote":1,"->":1,"set!":1
|
"quote":1,"quasiquote":1,"->":1,"set!":1
|
||||||
}; }
|
}; }
|
||||||
function isHoForm(n) { return n in {
|
function isHoForm(n) { return n in {
|
||||||
@@ -504,7 +450,7 @@
|
|||||||
|
|
||||||
function isDefinitionForm(name) {
|
function isDefinitionForm(name) {
|
||||||
return name === "define" || name === "defcomp" || name === "defmacro" ||
|
return name === "define" || name === "defcomp" || name === "defmacro" ||
|
||||||
name === "defstyle" || name === "defkeyframes" || name === "defhandler";
|
name === "defstyle" || name === "defhandler";
|
||||||
}
|
}
|
||||||
|
|
||||||
function indexOf_(s, ch) {
|
function indexOf_(s, ch) {
|
||||||
@@ -604,10 +550,10 @@
|
|||||||
var args = rest(expr);
|
var args = rest(expr);
|
||||||
return (isSxTruthy(!isSxTruthy(sxOr((typeOf(head) == "symbol"), (typeOf(head) == "lambda"), (typeOf(head) == "list")))) ? map(function(x) { return trampoline(evalExpr(x, env)); }, expr) : (isSxTruthy((typeOf(head) == "symbol")) ? (function() {
|
return (isSxTruthy(!isSxTruthy(sxOr((typeOf(head) == "symbol"), (typeOf(head) == "lambda"), (typeOf(head) == "list")))) ? map(function(x) { return trampoline(evalExpr(x, env)); }, expr) : (isSxTruthy((typeOf(head) == "symbol")) ? (function() {
|
||||||
var name = symbolName(head);
|
var name = symbolName(head);
|
||||||
return (isSxTruthy((name == "if")) ? sfIf(args, env) : (isSxTruthy((name == "when")) ? sfWhen(args, env) : (isSxTruthy((name == "cond")) ? sfCond(args, env) : (isSxTruthy((name == "case")) ? sfCase(args, env) : (isSxTruthy((name == "and")) ? sfAnd(args, env) : (isSxTruthy((name == "or")) ? sfOr(args, env) : (isSxTruthy((name == "let")) ? sfLet(args, env) : (isSxTruthy((name == "let*")) ? sfLet(args, env) : (isSxTruthy((name == "letrec")) ? sfLetrec(args, env) : (isSxTruthy((name == "lambda")) ? sfLambda(args, env) : (isSxTruthy((name == "fn")) ? sfLambda(args, env) : (isSxTruthy((name == "define")) ? sfDefine(args, env) : (isSxTruthy((name == "defcomp")) ? sfDefcomp(args, env) : (isSxTruthy((name == "defmacro")) ? sfDefmacro(args, env) : (isSxTruthy((name == "defstyle")) ? sfDefstyle(args, env) : (isSxTruthy((name == "defkeyframes")) ? sfDefkeyframes(args, env) : (isSxTruthy((name == "defhandler")) ? sfDefhandler(args, env) : (isSxTruthy((name == "defpage")) ? sfDefpage(args, env) : (isSxTruthy((name == "defquery")) ? sfDefquery(args, env) : (isSxTruthy((name == "defaction")) ? sfDefaction(args, env) : (isSxTruthy((name == "begin")) ? sfBegin(args, env) : (isSxTruthy((name == "do")) ? sfBegin(args, env) : (isSxTruthy((name == "quote")) ? sfQuote(args, env) : (isSxTruthy((name == "quasiquote")) ? sfQuasiquote(args, env) : (isSxTruthy((name == "->")) ? sfThreadFirst(args, env) : (isSxTruthy((name == "set!")) ? sfSetBang(args, env) : (isSxTruthy((name == "reset")) ? sfReset(args, env) : (isSxTruthy((name == "shift")) ? sfShift(args, env) : (isSxTruthy((name == "dynamic-wind")) ? sfDynamicWind(args, env) : (isSxTruthy((name == "map")) ? hoMap(args, env) : (isSxTruthy((name == "map-indexed")) ? hoMapIndexed(args, env) : (isSxTruthy((name == "filter")) ? hoFilter(args, env) : (isSxTruthy((name == "reduce")) ? hoReduce(args, env) : (isSxTruthy((name == "some")) ? hoSome(args, env) : (isSxTruthy((name == "every?")) ? hoEvery(args, env) : (isSxTruthy((name == "for-each")) ? hoForEach(args, env) : (isSxTruthy((isSxTruthy(envHas(env, name)) && isMacro(envGet(env, name)))) ? (function() {
|
return (isSxTruthy((name == "if")) ? sfIf(args, env) : (isSxTruthy((name == "when")) ? sfWhen(args, env) : (isSxTruthy((name == "cond")) ? sfCond(args, env) : (isSxTruthy((name == "case")) ? sfCase(args, env) : (isSxTruthy((name == "and")) ? sfAnd(args, env) : (isSxTruthy((name == "or")) ? sfOr(args, env) : (isSxTruthy((name == "let")) ? sfLet(args, env) : (isSxTruthy((name == "let*")) ? sfLet(args, env) : (isSxTruthy((name == "letrec")) ? sfLetrec(args, env) : (isSxTruthy((name == "lambda")) ? sfLambda(args, env) : (isSxTruthy((name == "fn")) ? sfLambda(args, env) : (isSxTruthy((name == "define")) ? sfDefine(args, env) : (isSxTruthy((name == "defcomp")) ? sfDefcomp(args, env) : (isSxTruthy((name == "defmacro")) ? sfDefmacro(args, env) : (isSxTruthy((name == "defstyle")) ? sfDefstyle(args, env) : (isSxTruthy((name == "defhandler")) ? sfDefhandler(args, env) : (isSxTruthy((name == "defpage")) ? sfDefpage(args, env) : (isSxTruthy((name == "defquery")) ? sfDefquery(args, env) : (isSxTruthy((name == "defaction")) ? sfDefaction(args, env) : (isSxTruthy((name == "begin")) ? sfBegin(args, env) : (isSxTruthy((name == "do")) ? sfBegin(args, env) : (isSxTruthy((name == "quote")) ? sfQuote(args, env) : (isSxTruthy((name == "quasiquote")) ? sfQuasiquote(args, env) : (isSxTruthy((name == "->")) ? sfThreadFirst(args, env) : (isSxTruthy((name == "set!")) ? sfSetBang(args, env) : (isSxTruthy((name == "reset")) ? sfReset(args, env) : (isSxTruthy((name == "shift")) ? sfShift(args, env) : (isSxTruthy((name == "dynamic-wind")) ? sfDynamicWind(args, env) : (isSxTruthy((name == "map")) ? hoMap(args, env) : (isSxTruthy((name == "map-indexed")) ? hoMapIndexed(args, env) : (isSxTruthy((name == "filter")) ? hoFilter(args, env) : (isSxTruthy((name == "reduce")) ? hoReduce(args, env) : (isSxTruthy((name == "some")) ? hoSome(args, env) : (isSxTruthy((name == "every?")) ? hoEvery(args, env) : (isSxTruthy((name == "for-each")) ? hoForEach(args, env) : (isSxTruthy((isSxTruthy(envHas(env, name)) && isMacro(envGet(env, name)))) ? (function() {
|
||||||
var mac = envGet(env, name);
|
var mac = envGet(env, name);
|
||||||
return makeThunk(expandMacro(mac, args, env), env);
|
return makeThunk(expandMacro(mac, args, env), env);
|
||||||
})() : (isSxTruthy(isRenderExpr(expr)) ? renderExpr(expr, env) : evalCall(head, args, env)))))))))))))))))))))))))))))))))))))));
|
})() : (isSxTruthy(isRenderExpr(expr)) ? renderExpr(expr, env) : evalCall(head, args, env))))))))))))))))))))))))))))))))))))));
|
||||||
})() : evalCall(head, args, env)));
|
})() : evalCall(head, args, env)));
|
||||||
})(); };
|
})(); };
|
||||||
|
|
||||||
@@ -828,13 +774,6 @@ return append_b(inits, nth(binding, 1)); }, bindings) : reduce(function(acc, pai
|
|||||||
return value;
|
return value;
|
||||||
})(); };
|
})(); };
|
||||||
|
|
||||||
// sf-defkeyframes
|
|
||||||
var sfDefkeyframes = function(args, env) { return (function() {
|
|
||||||
var kfName = symbolName(first(args));
|
|
||||||
var steps = rest(args);
|
|
||||||
return buildKeyframes(kfName, steps, env);
|
|
||||||
})(); };
|
|
||||||
|
|
||||||
// sf-begin
|
// sf-begin
|
||||||
var sfBegin = function(args, env) { return (isSxTruthy(isEmpty(args)) ? NIL : (forEach(function(e) { return trampoline(evalExpr(e, env)); }, slice(args, 0, (len(args) - 1))), makeThunk(last(args), env))); };
|
var sfBegin = function(args, env) { return (isSxTruthy(isEmpty(args)) ? NIL : (forEach(function(e) { return trampoline(evalExpr(e, env)); }, slice(args, 0, (len(args) - 1))), makeThunk(last(args), env))); };
|
||||||
|
|
||||||
@@ -994,7 +933,7 @@ return append_b(inits, nth(binding, 1)); }, bindings) : reduce(function(acc, pai
|
|||||||
var BOOLEAN_ATTRS = ["async", "autofocus", "autoplay", "checked", "controls", "default", "defer", "disabled", "formnovalidate", "hidden", "inert", "ismap", "loop", "multiple", "muted", "nomodule", "novalidate", "open", "playsinline", "readonly", "required", "reversed", "selected"];
|
var BOOLEAN_ATTRS = ["async", "autofocus", "autoplay", "checked", "controls", "default", "defer", "disabled", "formnovalidate", "hidden", "inert", "ismap", "loop", "multiple", "muted", "nomodule", "novalidate", "open", "playsinline", "readonly", "required", "reversed", "selected"];
|
||||||
|
|
||||||
// definition-form?
|
// definition-form?
|
||||||
var isDefinitionForm = function(name) { return sxOr((name == "define"), (name == "defcomp"), (name == "defmacro"), (name == "defstyle"), (name == "defkeyframes"), (name == "defhandler")); };
|
var isDefinitionForm = function(name) { return sxOr((name == "define"), (name == "defcomp"), (name == "defmacro"), (name == "defstyle"), (name == "defhandler")); };
|
||||||
|
|
||||||
// parse-element-args
|
// parse-element-args
|
||||||
var parseElementArgs = function(args, env) { return (function() {
|
var parseElementArgs = function(args, env) { return (function() {
|
||||||
@@ -1014,7 +953,7 @@ return append_b(inits, nth(binding, 1)); }, bindings) : reduce(function(acc, pai
|
|||||||
// render-attrs
|
// render-attrs
|
||||||
var renderAttrs = function(attrs) { return join("", map(function(key) { return (function() {
|
var renderAttrs = function(attrs) { return join("", map(function(key) { return (function() {
|
||||||
var val = dictGet(attrs, key);
|
var val = dictGet(attrs, key);
|
||||||
return (isSxTruthy((isSxTruthy(contains(BOOLEAN_ATTRS, key)) && val)) ? (String(" ") + String(key)) : (isSxTruthy((isSxTruthy(contains(BOOLEAN_ATTRS, key)) && !isSxTruthy(val))) ? "" : (isSxTruthy(isNil(val)) ? "" : (isSxTruthy((isSxTruthy((key == "style")) && isStyleValue(val))) ? (String(" class=\"") + String(styleValueClass(val)) + String("\"")) : (String(" ") + String(key) + String("=\"") + String(escapeAttr((String(val)))) + String("\""))))));
|
return (isSxTruthy((isSxTruthy(contains(BOOLEAN_ATTRS, key)) && val)) ? (String(" ") + String(key)) : (isSxTruthy((isSxTruthy(contains(BOOLEAN_ATTRS, key)) && !isSxTruthy(val))) ? "" : (isSxTruthy(isNil(val)) ? "" : (String(" ") + String(key) + String("=\"") + String(escapeAttr((String(val)))) + String("\"")))));
|
||||||
})(); }, keys(attrs))); };
|
})(); }, keys(attrs))); };
|
||||||
|
|
||||||
// eval-cond
|
// eval-cond
|
||||||
@@ -1165,10 +1104,10 @@ continue; } else { return NIL; } } };
|
|||||||
var renderToHtml = function(expr, env) { return (function() { var _m = typeOf(expr); if (_m == "nil") return ""; if (_m == "string") return escapeHtml(expr); if (_m == "number") return (String(expr)); if (_m == "boolean") return (isSxTruthy(expr) ? "true" : "false"); if (_m == "list") return (isSxTruthy(isEmpty(expr)) ? "" : renderListToHtml(expr, env)); if (_m == "symbol") return renderValueToHtml(trampoline(evalExpr(expr, env)), env); if (_m == "keyword") return escapeHtml(keywordName(expr)); if (_m == "raw-html") return rawHtmlContent(expr); return renderValueToHtml(trampoline(evalExpr(expr, env)), env); })(); };
|
var renderToHtml = function(expr, env) { return (function() { var _m = typeOf(expr); if (_m == "nil") return ""; if (_m == "string") return escapeHtml(expr); if (_m == "number") return (String(expr)); if (_m == "boolean") return (isSxTruthy(expr) ? "true" : "false"); if (_m == "list") return (isSxTruthy(isEmpty(expr)) ? "" : renderListToHtml(expr, env)); if (_m == "symbol") return renderValueToHtml(trampoline(evalExpr(expr, env)), env); if (_m == "keyword") return escapeHtml(keywordName(expr)); if (_m == "raw-html") return rawHtmlContent(expr); return renderValueToHtml(trampoline(evalExpr(expr, env)), env); })(); };
|
||||||
|
|
||||||
// render-value-to-html
|
// render-value-to-html
|
||||||
var renderValueToHtml = function(val, env) { return (function() { var _m = typeOf(val); if (_m == "nil") return ""; if (_m == "string") return escapeHtml(val); if (_m == "number") return (String(val)); if (_m == "boolean") return (isSxTruthy(val) ? "true" : "false"); if (_m == "list") return renderListToHtml(val, env); if (_m == "raw-html") return rawHtmlContent(val); if (_m == "style-value") return styleValueClass(val); return escapeHtml((String(val))); })(); };
|
var renderValueToHtml = function(val, env) { return (function() { var _m = typeOf(val); if (_m == "nil") return ""; if (_m == "string") return escapeHtml(val); if (_m == "number") return (String(val)); if (_m == "boolean") return (isSxTruthy(val) ? "true" : "false"); if (_m == "list") return renderListToHtml(val, env); if (_m == "raw-html") return rawHtmlContent(val); return escapeHtml((String(val))); })(); };
|
||||||
|
|
||||||
// RENDER_HTML_FORMS
|
// RENDER_HTML_FORMS
|
||||||
var RENDER_HTML_FORMS = ["if", "when", "cond", "case", "let", "let*", "begin", "do", "define", "defcomp", "defmacro", "defstyle", "defkeyframes", "defhandler", "map", "map-indexed", "filter", "for-each"];
|
var RENDER_HTML_FORMS = ["if", "when", "cond", "case", "let", "let*", "begin", "do", "define", "defcomp", "defmacro", "defstyle", "defhandler", "map", "map-indexed", "filter", "for-each"];
|
||||||
|
|
||||||
// render-html-form?
|
// render-html-form?
|
||||||
var isRenderHtmlForm = function(name) { return contains(RENDER_HTML_FORMS, name); };
|
var isRenderHtmlForm = function(name) { return contains(RENDER_HTML_FORMS, name); };
|
||||||
@@ -1316,7 +1255,7 @@ continue; } else { return NIL; } } };
|
|||||||
var MATH_NS = "http://www.w3.org/1998/Math/MathML";
|
var MATH_NS = "http://www.w3.org/1998/Math/MathML";
|
||||||
|
|
||||||
// render-to-dom
|
// render-to-dom
|
||||||
var renderToDom = function(expr, env, ns) { return (function() { var _m = typeOf(expr); if (_m == "nil") return createFragment(); if (_m == "boolean") return createFragment(); if (_m == "raw-html") return domParseHtml(rawHtmlContent(expr)); if (_m == "string") return createTextNode(expr); if (_m == "number") return createTextNode((String(expr))); if (_m == "symbol") return renderToDom(trampoline(evalExpr(expr, env)), env, ns); if (_m == "keyword") return createTextNode(keywordName(expr)); if (_m == "dom-node") return expr; if (_m == "dict") return createFragment(); if (_m == "list") return (isSxTruthy(isEmpty(expr)) ? createFragment() : renderDomList(expr, env, ns)); if (_m == "style-value") return createTextNode(styleValueClass(expr)); return createTextNode((String(expr))); })(); };
|
var renderToDom = function(expr, env, ns) { return (function() { var _m = typeOf(expr); if (_m == "nil") return createFragment(); if (_m == "boolean") return createFragment(); if (_m == "raw-html") return domParseHtml(rawHtmlContent(expr)); if (_m == "string") return createTextNode(expr); if (_m == "number") return createTextNode((String(expr))); if (_m == "symbol") return renderToDom(trampoline(evalExpr(expr, env)), env, ns); if (_m == "keyword") return createTextNode(keywordName(expr)); if (_m == "dom-node") return expr; if (_m == "dict") return createFragment(); if (_m == "list") return (isSxTruthy(isEmpty(expr)) ? createFragment() : renderDomList(expr, env, ns)); return createTextNode((String(expr))); })(); };
|
||||||
|
|
||||||
// render-dom-list
|
// render-dom-list
|
||||||
var renderDomList = function(expr, env, ns) { return (function() {
|
var renderDomList = function(expr, env, ns) { return (function() {
|
||||||
@@ -1339,22 +1278,15 @@ continue; } else { return NIL; } } };
|
|||||||
var renderDomElement = function(tag, args, env, ns) { return (function() {
|
var renderDomElement = function(tag, args, env, ns) { return (function() {
|
||||||
var newNs = (isSxTruthy((tag == "svg")) ? SVG_NS : (isSxTruthy((tag == "math")) ? MATH_NS : ns));
|
var newNs = (isSxTruthy((tag == "svg")) ? SVG_NS : (isSxTruthy((tag == "math")) ? MATH_NS : ns));
|
||||||
var el = domCreateElement(tag, newNs);
|
var el = domCreateElement(tag, newNs);
|
||||||
var extraClass = NIL;
|
|
||||||
reduce(function(state, arg) { return (function() {
|
reduce(function(state, arg) { return (function() {
|
||||||
var skip = get(state, "skip");
|
var skip = get(state, "skip");
|
||||||
return (isSxTruthy(skip) ? assoc(state, "skip", false, "i", (get(state, "i") + 1)) : (isSxTruthy((isSxTruthy((typeOf(arg) == "keyword")) && ((get(state, "i") + 1) < len(args)))) ? (function() {
|
return (isSxTruthy(skip) ? assoc(state, "skip", false, "i", (get(state, "i") + 1)) : (isSxTruthy((isSxTruthy((typeOf(arg) == "keyword")) && ((get(state, "i") + 1) < len(args)))) ? (function() {
|
||||||
var attrName = keywordName(arg);
|
var attrName = keywordName(arg);
|
||||||
var attrVal = trampoline(evalExpr(nth(args, (get(state, "i") + 1)), env));
|
var attrVal = trampoline(evalExpr(nth(args, (get(state, "i") + 1)), env));
|
||||||
(isSxTruthy(sxOr(isNil(attrVal), (attrVal == false))) ? NIL : (isSxTruthy((isSxTruthy((attrName == "style")) && isStyleValue(attrVal))) ? (extraClass = styleValueClass(attrVal)) : (isSxTruthy(contains(BOOLEAN_ATTRS, attrName)) ? (isSxTruthy(attrVal) ? domSetAttr(el, attrName, "") : NIL) : (isSxTruthy((attrVal == true)) ? domSetAttr(el, attrName, "") : domSetAttr(el, attrName, (String(attrVal)))))));
|
(isSxTruthy(sxOr(isNil(attrVal), (attrVal == false))) ? NIL : (isSxTruthy(contains(BOOLEAN_ATTRS, attrName)) ? (isSxTruthy(attrVal) ? domSetAttr(el, attrName, "") : NIL) : (isSxTruthy((attrVal == true)) ? domSetAttr(el, attrName, "") : domSetAttr(el, attrName, (String(attrVal))))));
|
||||||
return assoc(state, "skip", true, "i", (get(state, "i") + 1));
|
return assoc(state, "skip", true, "i", (get(state, "i") + 1));
|
||||||
})() : ((isSxTruthy(!isSxTruthy(contains(VOID_ELEMENTS, tag))) ? domAppend(el, renderToDom(arg, env, newNs)) : NIL), assoc(state, "i", (get(state, "i") + 1)))));
|
})() : ((isSxTruthy(!isSxTruthy(contains(VOID_ELEMENTS, tag))) ? domAppend(el, renderToDom(arg, env, newNs)) : NIL), assoc(state, "i", (get(state, "i") + 1)))));
|
||||||
})(); }, {["i"]: 0, ["skip"]: false}, args);
|
})(); }, {["i"]: 0, ["skip"]: false}, args);
|
||||||
if (isSxTruthy(extraClass)) {
|
|
||||||
(function() {
|
|
||||||
var existing = domGetAttr(el, "class");
|
|
||||||
return domSetAttr(el, "class", (isSxTruthy(existing) ? (String(existing) + String(" ") + String(extraClass)) : extraClass));
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
return el;
|
return el;
|
||||||
})(); };
|
})(); };
|
||||||
|
|
||||||
@@ -1405,7 +1337,7 @@ continue; } else { return NIL; } } };
|
|||||||
var renderDomUnknownComponent = function(name) { return error((String("Unknown component: ") + String(name))); };
|
var renderDomUnknownComponent = function(name) { return error((String("Unknown component: ") + String(name))); };
|
||||||
|
|
||||||
// RENDER_DOM_FORMS
|
// RENDER_DOM_FORMS
|
||||||
var RENDER_DOM_FORMS = ["if", "when", "cond", "case", "let", "let*", "begin", "do", "define", "defcomp", "defmacro", "defstyle", "defkeyframes", "defhandler", "map", "map-indexed", "filter", "for-each"];
|
var RENDER_DOM_FORMS = ["if", "when", "cond", "case", "let", "let*", "begin", "do", "define", "defcomp", "defmacro", "defstyle", "defhandler", "map", "map-indexed", "filter", "for-each"];
|
||||||
|
|
||||||
// render-dom-form?
|
// render-dom-form?
|
||||||
var isRenderDomForm = function(name) { return contains(RENDER_DOM_FORMS, name); };
|
var isRenderDomForm = function(name) { return contains(RENDER_DOM_FORMS, name); };
|
||||||
@@ -2172,180 +2104,6 @@ return bindInlineHandlers(root); };
|
|||||||
var engineInit = function() { return (initCssTracking(), sxProcessScripts(NIL), sxHydrate(NIL), processElements(NIL)); };
|
var engineInit = function() { return (initCssTracking(), sxProcessScripts(NIL), sxHydrate(NIL), processElements(NIL)); };
|
||||||
|
|
||||||
|
|
||||||
// === Transpiled from cssx ===
|
|
||||||
|
|
||||||
// _style-atoms
|
|
||||||
var _styleAtoms = {};
|
|
||||||
|
|
||||||
// _pseudo-variants
|
|
||||||
var _pseudoVariants = {};
|
|
||||||
|
|
||||||
// _responsive-breakpoints
|
|
||||||
var _responsiveBreakpoints = {};
|
|
||||||
|
|
||||||
// _style-keyframes
|
|
||||||
var _styleKeyframes = {};
|
|
||||||
|
|
||||||
// _arbitrary-patterns
|
|
||||||
var _arbitraryPatterns = [];
|
|
||||||
|
|
||||||
// _child-selector-prefixes
|
|
||||||
var _childSelectorPrefixes = [];
|
|
||||||
|
|
||||||
// _style-cache
|
|
||||||
var _styleCache = {};
|
|
||||||
|
|
||||||
// _injected-styles
|
|
||||||
var _injectedStyles = {};
|
|
||||||
|
|
||||||
// load-style-dict
|
|
||||||
var loadStyleDict = function(data) { _styleAtoms = sxOr(get(data, "a"), {});
|
|
||||||
_pseudoVariants = sxOr(get(data, "v"), {});
|
|
||||||
_responsiveBreakpoints = sxOr(get(data, "b"), {});
|
|
||||||
_styleKeyframes = sxOr(get(data, "k"), {});
|
|
||||||
_childSelectorPrefixes = sxOr(get(data, "c"), []);
|
|
||||||
_arbitraryPatterns = map(function(pair) { return {["re"]: compileRegex((String("^") + String(first(pair)) + String("$"))), ["tmpl"]: nth(pair, 1)}; }, sxOr(get(data, "p"), []));
|
|
||||||
return (_styleCache = {}); };
|
|
||||||
|
|
||||||
// split-variant
|
|
||||||
var splitVariant = function(atom) { return (function() {
|
|
||||||
var result = NIL;
|
|
||||||
{ var _c = keys(_responsiveBreakpoints); for (var _i = 0; _i < _c.length; _i++) { var bp = _c[_i]; if (isSxTruthy(isNil(result))) {
|
|
||||||
(function() {
|
|
||||||
var prefix = (String(bp) + String(":"));
|
|
||||||
return (isSxTruthy(startsWith(atom, prefix)) ? (function() {
|
|
||||||
var restAtom = slice(atom, len(prefix));
|
|
||||||
return (function() {
|
|
||||||
var innerMatch = NIL;
|
|
||||||
{ var _c = keys(_pseudoVariants); for (var _i = 0; _i < _c.length; _i++) { var pv = _c[_i]; if (isSxTruthy(isNil(innerMatch))) {
|
|
||||||
(function() {
|
|
||||||
var innerPrefix = (String(pv) + String(":"));
|
|
||||||
return (isSxTruthy(startsWith(restAtom, innerPrefix)) ? (innerMatch = [(String(bp) + String(":") + String(pv)), slice(restAtom, len(innerPrefix))]) : NIL);
|
|
||||||
})();
|
|
||||||
} } }
|
|
||||||
return (result = sxOr(innerMatch, [bp, restAtom]));
|
|
||||||
})();
|
|
||||||
})() : NIL);
|
|
||||||
})();
|
|
||||||
} } }
|
|
||||||
if (isSxTruthy(isNil(result))) {
|
|
||||||
{ var _c = keys(_pseudoVariants); for (var _i = 0; _i < _c.length; _i++) { var pv = _c[_i]; if (isSxTruthy(isNil(result))) {
|
|
||||||
(function() {
|
|
||||||
var prefix = (String(pv) + String(":"));
|
|
||||||
return (isSxTruthy(startsWith(atom, prefix)) ? (result = [pv, slice(atom, len(prefix))]) : NIL);
|
|
||||||
})();
|
|
||||||
} } }
|
|
||||||
}
|
|
||||||
return sxOr(result, [NIL, atom]);
|
|
||||||
})(); };
|
|
||||||
|
|
||||||
// resolve-atom
|
|
||||||
var resolveAtom = function(atom) { return (function() {
|
|
||||||
var decls = dictGet(_styleAtoms, atom);
|
|
||||||
return (isSxTruthy(!isSxTruthy(isNil(decls))) ? decls : (isSxTruthy(startsWith(atom, "animate-")) ? (function() {
|
|
||||||
var kfName = slice(atom, 8);
|
|
||||||
return (isSxTruthy(dictHas(_styleKeyframes, kfName)) ? (String("animation-name:") + String(kfName)) : NIL);
|
|
||||||
})() : (function() {
|
|
||||||
var matchResult = NIL;
|
|
||||||
{ var _c = _arbitraryPatterns; for (var _i = 0; _i < _c.length; _i++) { var pat = _c[_i]; if (isSxTruthy(isNil(matchResult))) {
|
|
||||||
(function() {
|
|
||||||
var m = regexMatch(get(pat, "re"), atom);
|
|
||||||
return (isSxTruthy(m) ? (matchResult = regexReplaceGroups(get(pat, "tmpl"), m)) : NIL);
|
|
||||||
})();
|
|
||||||
} } }
|
|
||||||
return matchResult;
|
|
||||||
})()));
|
|
||||||
})(); };
|
|
||||||
|
|
||||||
// is-child-selector-atom?
|
|
||||||
var isChildSelectorAtom = function(atom) { return some(function(prefix) { return startsWith(atom, prefix); }, _childSelectorPrefixes); };
|
|
||||||
|
|
||||||
// hash-style
|
|
||||||
var hashStyle = function(input) { return fnv1aHash(input); };
|
|
||||||
|
|
||||||
// resolve-style
|
|
||||||
var resolveStyle = function(atoms) { return (function() {
|
|
||||||
var key = join("\\0", atoms);
|
|
||||||
return (function() {
|
|
||||||
var cached = dictGet(_styleCache, key);
|
|
||||||
return (isSxTruthy(!isSxTruthy(isNil(cached))) ? cached : (function() {
|
|
||||||
var baseDecls = [];
|
|
||||||
var mediaRules = [];
|
|
||||||
var pseudoRules = [];
|
|
||||||
var kfNeeded = [];
|
|
||||||
{ var _c = atoms; for (var _i = 0; _i < _c.length; _i++) { var a = _c[_i]; if (isSxTruthy(a)) {
|
|
||||||
(function() {
|
|
||||||
var clean = (isSxTruthy(startsWith(a, ":")) ? slice(a, 1) : a);
|
|
||||||
return (function() {
|
|
||||||
var parts = splitVariant(clean);
|
|
||||||
return (function() {
|
|
||||||
var variant = first(parts);
|
|
||||||
var base = nth(parts, 1);
|
|
||||||
var decls = resolveAtom(base);
|
|
||||||
return (isSxTruthy(decls) ? ((isSxTruthy(startsWith(base, "animate-")) ? (function() {
|
|
||||||
var kfName = slice(base, 8);
|
|
||||||
return (isSxTruthy(dictHas(_styleKeyframes, kfName)) ? append_b(kfNeeded, [kfName, dictGet(_styleKeyframes, kfName)]) : NIL);
|
|
||||||
})() : NIL), (isSxTruthy(isNil(variant)) ? (isSxTruthy(isChildSelectorAtom(base)) ? append_b(pseudoRules, [">:not(:first-child)", decls]) : append_b(baseDecls, decls)) : (isSxTruthy(dictHas(_responsiveBreakpoints, variant)) ? append_b(mediaRules, [dictGet(_responsiveBreakpoints, variant), decls]) : (isSxTruthy(dictHas(_pseudoVariants, variant)) ? append_b(pseudoRules, [dictGet(_pseudoVariants, variant), decls]) : (function() {
|
|
||||||
var vparts = split(variant, ":");
|
|
||||||
var mediaPart = NIL;
|
|
||||||
var pseudoPart = NIL;
|
|
||||||
{ var _c = vparts; for (var _i = 0; _i < _c.length; _i++) { var vp = _c[_i]; (isSxTruthy(dictHas(_responsiveBreakpoints, vp)) ? (mediaPart = dictGet(_responsiveBreakpoints, vp)) : (isSxTruthy(dictHas(_pseudoVariants, vp)) ? (pseudoPart = dictGet(_pseudoVariants, vp)) : NIL)); } }
|
|
||||||
if (isSxTruthy(mediaPart)) {
|
|
||||||
mediaRules.push([mediaPart, decls]);
|
|
||||||
}
|
|
||||||
if (isSxTruthy(pseudoPart)) {
|
|
||||||
pseudoRules.push([pseudoPart, decls]);
|
|
||||||
}
|
|
||||||
return (isSxTruthy((isSxTruthy(isNil(mediaPart)) && isNil(pseudoPart))) ? append_b(baseDecls, decls) : NIL);
|
|
||||||
})())))) : NIL);
|
|
||||||
})();
|
|
||||||
})();
|
|
||||||
})();
|
|
||||||
} } }
|
|
||||||
return (function() {
|
|
||||||
var hashInput = join(";", baseDecls);
|
|
||||||
{ var _c = mediaRules; for (var _i = 0; _i < _c.length; _i++) { var mr = _c[_i]; hashInput = (String(hashInput) + String("@") + String(first(mr)) + String("{") + String(nth(mr, 1)) + String("}")); } }
|
|
||||||
{ var _c = pseudoRules; for (var _i = 0; _i < _c.length; _i++) { var pr = _c[_i]; hashInput = (String(hashInput) + String(first(pr)) + String("{") + String(nth(pr, 1)) + String("}")); } }
|
|
||||||
{ var _c = kfNeeded; for (var _i = 0; _i < _c.length; _i++) { var kf = _c[_i]; hashInput = (String(hashInput) + String(nth(kf, 1))); } }
|
|
||||||
return (function() {
|
|
||||||
var cn = (String("sx-") + String(hashStyle(hashInput)));
|
|
||||||
var sv = makeStyleValue_(cn, join(";", baseDecls), mediaRules, pseudoRules, kfNeeded);
|
|
||||||
_styleCache[key] = sv;
|
|
||||||
injectStyleValue(sv, atoms);
|
|
||||||
return sv;
|
|
||||||
})();
|
|
||||||
})();
|
|
||||||
})());
|
|
||||||
})();
|
|
||||||
})(); };
|
|
||||||
|
|
||||||
// merge-style-values
|
|
||||||
var mergeStyleValues = function(styles) { return (isSxTruthy((len(styles) == 1)) ? first(styles) : (function() {
|
|
||||||
var allDecls = [];
|
|
||||||
var allMedia = [];
|
|
||||||
var allPseudo = [];
|
|
||||||
var allKf = [];
|
|
||||||
{ var _c = styles; for (var _i = 0; _i < _c.length; _i++) { var sv = _c[_i]; if (isSxTruthy(styleValueDeclarations(sv))) {
|
|
||||||
allDecls.push(styleValueDeclarations(sv));
|
|
||||||
}
|
|
||||||
allMedia = concat(allMedia, styleValueMediaRules(sv));
|
|
||||||
allPseudo = concat(allPseudo, styleValuePseudoRules(sv));
|
|
||||||
allKf = concat(allKf, styleValueKeyframes_(sv)); } }
|
|
||||||
return (function() {
|
|
||||||
var hashInput = join(";", allDecls);
|
|
||||||
{ var _c = allMedia; for (var _i = 0; _i < _c.length; _i++) { var mr = _c[_i]; hashInput = (String(hashInput) + String("@") + String(first(mr)) + String("{") + String(nth(mr, 1)) + String("}")); } }
|
|
||||||
{ var _c = allPseudo; for (var _i = 0; _i < _c.length; _i++) { var pr = _c[_i]; hashInput = (String(hashInput) + String(first(pr)) + String("{") + String(nth(pr, 1)) + String("}")); } }
|
|
||||||
{ var _c = allKf; for (var _i = 0; _i < _c.length; _i++) { var kf = _c[_i]; hashInput = (String(hashInput) + String(nth(kf, 1))); } }
|
|
||||||
return (function() {
|
|
||||||
var cn = (String("sx-") + String(hashStyle(hashInput)));
|
|
||||||
var merged = makeStyleValue_(cn, join(";", allDecls), allMedia, allPseudo, allKf);
|
|
||||||
injectStyleValue(merged, []);
|
|
||||||
return merged;
|
|
||||||
})();
|
|
||||||
})();
|
|
||||||
})()); };
|
|
||||||
|
|
||||||
|
|
||||||
// === Transpiled from boot ===
|
// === Transpiled from boot ===
|
||||||
|
|
||||||
// HEAD_HOIST_SELECTOR
|
// HEAD_HOIST_SELECTOR
|
||||||
@@ -2461,26 +2219,6 @@ callExpr.push(dictGet(kwargs, k)); } }
|
|||||||
})();
|
})();
|
||||||
return setSxCompCookie(hash);
|
return setSxCompCookie(hash);
|
||||||
})());
|
})());
|
||||||
})(); };
|
|
||||||
|
|
||||||
// init-style-dict
|
|
||||||
var initStyleDict = function() { return (function() {
|
|
||||||
var scripts = queryStyleScripts();
|
|
||||||
return forEach(function(s) { return (isSxTruthy(!isSxTruthy(isProcessed(s, "styles"))) ? (markProcessed(s, "styles"), (function() {
|
|
||||||
var text = domTextContent(s);
|
|
||||||
var hash = domGetAttr(s, "data-hash");
|
|
||||||
return (isSxTruthy(isNil(hash)) ? (isSxTruthy((isSxTruthy(text) && !isSxTruthy(isEmpty(trim(text))))) ? parseAndLoadStyleDict(text) : NIL) : (function() {
|
|
||||||
var hasInline = (isSxTruthy(text) && !isSxTruthy(isEmpty(trim(text))));
|
|
||||||
(function() {
|
|
||||||
var cachedHash = localStorageGet("sx-styles-hash");
|
|
||||||
return (isSxTruthy((cachedHash == hash)) ? (isSxTruthy(hasInline) ? (localStorageSet("sx-styles-src", text), parseAndLoadStyleDict(text), logInfo("styles: downloaded (cookie stale)")) : (function() {
|
|
||||||
var cached = localStorageGet("sx-styles-src");
|
|
||||||
return (isSxTruthy(cached) ? (parseAndLoadStyleDict(cached), logInfo((String("styles: cached (") + String(hash) + String(")")))) : (clearSxStylesCookie(), browserReload()));
|
|
||||||
})()) : (isSxTruthy(hasInline) ? (localStorageSet("sx-styles-hash", hash), localStorageSet("sx-styles-src", text), parseAndLoadStyleDict(text), logInfo((String("styles: downloaded (") + String(hash) + String(")")))) : (localStorageRemove("sx-styles-hash"), localStorageRemove("sx-styles-src"), clearSxStylesCookie(), browserReload())));
|
|
||||||
})();
|
|
||||||
return setSxStylesCookie(hash);
|
|
||||||
})());
|
|
||||||
})()) : NIL); }, scripts);
|
|
||||||
})(); };
|
})(); };
|
||||||
|
|
||||||
// _page-routes
|
// _page-routes
|
||||||
@@ -2506,7 +2244,7 @@ callExpr.push(dictGet(kwargs, k)); } }
|
|||||||
})(); };
|
})(); };
|
||||||
|
|
||||||
// boot-init
|
// boot-init
|
||||||
var bootInit = function() { return (logInfo((String("sx-browser ") + String(SX_VERSION))), initCssTracking(), initStyleDict(), processPageScripts(), processSxScripts(NIL), sxHydrateElements(NIL), processElements(NIL)); };
|
var bootInit = function() { return (logInfo((String("sx-browser ") + String(SX_VERSION))), initCssTracking(), processPageScripts(), processSxScripts(NIL), sxHydrateElements(NIL), processElements(NIL)); };
|
||||||
|
|
||||||
|
|
||||||
// === Transpiled from router (client-side route matching) ===
|
// === Transpiled from router (client-side route matching) ===
|
||||||
@@ -2779,7 +2517,6 @@ callExpr.push(dictGet(kwargs, k)); } }
|
|||||||
renderDomElement = function(tag, args, env, ns) {
|
renderDomElement = function(tag, args, env, ns) {
|
||||||
var newNs = tag === "svg" ? SVG_NS : tag === "math" ? MATH_NS : ns;
|
var newNs = tag === "svg" ? SVG_NS : tag === "math" ? MATH_NS : ns;
|
||||||
var el = domCreateElement(tag, newNs);
|
var el = domCreateElement(tag, newNs);
|
||||||
var extraClasses = [];
|
|
||||||
var isVoid = contains(VOID_ELEMENTS, tag);
|
var isVoid = contains(VOID_ELEMENTS, tag);
|
||||||
for (var i = 0; i < args.length; i++) {
|
for (var i = 0; i < args.length; i++) {
|
||||||
var arg = args[i];
|
var arg = args[i];
|
||||||
@@ -2788,11 +2525,7 @@ callExpr.push(dictGet(kwargs, k)); } }
|
|||||||
var attrVal = trampoline(evalExpr(args[i + 1], env));
|
var attrVal = trampoline(evalExpr(args[i + 1], env));
|
||||||
i++; // skip value
|
i++; // skip value
|
||||||
if (isNil(attrVal) || attrVal === false) continue;
|
if (isNil(attrVal) || attrVal === false) continue;
|
||||||
if (attrName === "class" && attrVal && attrVal._styleValue) {
|
if (contains(BOOLEAN_ATTRS, attrName)) {
|
||||||
extraClasses.push(attrVal.className);
|
|
||||||
} else if (attrName === "style" && attrVal && attrVal._styleValue) {
|
|
||||||
extraClasses.push(attrVal.className);
|
|
||||||
} else if (contains(BOOLEAN_ATTRS, attrName)) {
|
|
||||||
if (isSxTruthy(attrVal)) el.setAttribute(attrName, "");
|
if (isSxTruthy(attrVal)) el.setAttribute(attrName, "");
|
||||||
} else if (attrVal === true) {
|
} else if (attrVal === true) {
|
||||||
el.setAttribute(attrName, "");
|
el.setAttribute(attrName, "");
|
||||||
@@ -2806,10 +2539,6 @@ callExpr.push(dictGet(kwargs, k)); } }
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (extraClasses.length) {
|
|
||||||
var existing = el.getAttribute("class") || "";
|
|
||||||
el.setAttribute("class", (existing ? existing + " " : "") + extraClasses.join(" "));
|
|
||||||
}
|
|
||||||
return el;
|
return el;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -3703,70 +3432,6 @@ callExpr.push(dictGet(kwargs, k)); } }
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeStyleValue_(cn, decls, media, pseudo, kf) {
|
|
||||||
return new StyleValue(cn, decls || "", media || [], pseudo || [], kf || []);
|
|
||||||
}
|
|
||||||
|
|
||||||
function styleValueDeclarations(sv) { return sv.declarations; }
|
|
||||||
function styleValueMediaRules(sv) { return sv.mediaRules; }
|
|
||||||
function styleValuePseudoRules(sv) { return sv.pseudoRules; }
|
|
||||||
function styleValueKeyframes_(sv) { return sv.keyframes; }
|
|
||||||
|
|
||||||
function injectStyleValue(sv, atoms) {
|
|
||||||
if (_injectedStyles[sv.className]) return;
|
|
||||||
_injectedStyles[sv.className] = true;
|
|
||||||
|
|
||||||
if (!_hasDom) return;
|
|
||||||
var cssTarget = document.getElementById("sx-css");
|
|
||||||
if (!cssTarget) return;
|
|
||||||
|
|
||||||
var rules = [];
|
|
||||||
// Child-selector atoms are now routed to pseudoRules by the resolver
|
|
||||||
// with selector ">:not(:first-child)", so base declarations are always
|
|
||||||
// applied directly to the class.
|
|
||||||
if (sv.declarations) {
|
|
||||||
rules.push("." + sv.className + "{" + sv.declarations + "}");
|
|
||||||
}
|
|
||||||
for (var pi = 0; pi < sv.pseudoRules.length; pi++) {
|
|
||||||
var sel = sv.pseudoRules[pi][0], decls = sv.pseudoRules[pi][1];
|
|
||||||
if (sel.indexOf("&") >= 0) {
|
|
||||||
rules.push(sel.replace(/&/g, "." + sv.className) + "{" + decls + "}");
|
|
||||||
} else {
|
|
||||||
rules.push("." + sv.className + sel + "{" + decls + "}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (var mi = 0; mi < sv.mediaRules.length; mi++) {
|
|
||||||
rules.push("@media " + sv.mediaRules[mi][0] + "{." + sv.className + "{" + sv.mediaRules[mi][1] + "}}");
|
|
||||||
}
|
|
||||||
for (var ki = 0; ki < sv.keyframes.length; ki++) {
|
|
||||||
rules.push(sv.keyframes[ki][1]);
|
|
||||||
}
|
|
||||||
cssTarget.textContent += rules.join("");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace stub css primitive with real CSSX implementation
|
|
||||||
PRIMITIVES["css"] = function() {
|
|
||||||
var atoms = [];
|
|
||||||
for (var i = 0; i < arguments.length; i++) {
|
|
||||||
var a = arguments[i];
|
|
||||||
if (isNil(a) || a === false) continue;
|
|
||||||
atoms.push(isKw(a) ? a.name : String(a));
|
|
||||||
}
|
|
||||||
if (!atoms.length) return NIL;
|
|
||||||
return resolveStyle(atoms);
|
|
||||||
};
|
|
||||||
|
|
||||||
PRIMITIVES["merge-styles"] = function() {
|
|
||||||
var valid = [];
|
|
||||||
for (var i = 0; i < arguments.length; i++) {
|
|
||||||
if (isStyleValue(arguments[i])) valid.push(arguments[i]);
|
|
||||||
}
|
|
||||||
if (!valid.length) return NIL;
|
|
||||||
if (valid.length === 1) return valid[0];
|
|
||||||
return mergeStyleValues(valid);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// Platform interface — Boot (mount, hydrate, scripts, cookies)
|
// Platform interface — Boot (mount, hydrate, scripts, cookies)
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
@@ -3823,12 +3488,6 @@ callExpr.push(dictGet(kwargs, k)); } }
|
|||||||
r.querySelectorAll('script[type="text/sx"]'));
|
r.querySelectorAll('script[type="text/sx"]'));
|
||||||
}
|
}
|
||||||
|
|
||||||
function queryStyleScripts() {
|
|
||||||
if (!_hasDom) return [];
|
|
||||||
return Array.prototype.slice.call(
|
|
||||||
document.querySelectorAll('script[type="text/sx-styles"]'));
|
|
||||||
}
|
|
||||||
|
|
||||||
function queryPageScripts() {
|
function queryPageScripts() {
|
||||||
if (!_hasDom) return [];
|
if (!_hasDom) return [];
|
||||||
return Array.prototype.slice.call(
|
return Array.prototype.slice.call(
|
||||||
@@ -3860,14 +3519,6 @@ callExpr.push(dictGet(kwargs, k)); } }
|
|||||||
if (_hasDom) document.cookie = "sx-comp-hash=;path=/;max-age=0;SameSite=Lax";
|
if (_hasDom) document.cookie = "sx-comp-hash=;path=/;max-age=0;SameSite=Lax";
|
||||||
}
|
}
|
||||||
|
|
||||||
function setSxStylesCookie(hash) {
|
|
||||||
if (_hasDom) document.cookie = "sx-styles-hash=" + hash + ";path=/;max-age=31536000;SameSite=Lax";
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearSxStylesCookie() {
|
|
||||||
if (_hasDom) document.cookie = "sx-styles-hash=;path=/;max-age=0;SameSite=Lax";
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Env helpers ---
|
// --- Env helpers ---
|
||||||
|
|
||||||
function parseEnvAttr(el) {
|
function parseEnvAttr(el) {
|
||||||
@@ -3916,12 +3567,6 @@ callExpr.push(dictGet(kwargs, k)); } }
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseAndLoadStyleDict(text) {
|
|
||||||
try { loadStyleDict(JSON.parse(text)); }
|
|
||||||
catch (e) { if (typeof console !== "undefined") console.warn("[sx-ref] style dict parse error", e); }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// Post-transpilation fixups
|
// Post-transpilation fixups
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
@@ -4082,7 +3727,7 @@ callExpr.push(dictGet(kwargs, k)); } }
|
|||||||
|
|
||||||
// define/defcomp/defmacro — eval for side effects
|
// define/defcomp/defmacro — eval for side effects
|
||||||
if (hname === "define" || hname === "defcomp" || hname === "defmacro" ||
|
if (hname === "define" || hname === "defcomp" || hname === "defmacro" ||
|
||||||
hname === "defstyle" || hname === "defkeyframes" || hname === "defhandler") {
|
hname === "defstyle" || hname === "defhandler") {
|
||||||
trampoline(evalExpr(expr, env));
|
trampoline(evalExpr(expr, env));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -4204,11 +3849,7 @@ callExpr.push(dictGet(kwargs, k)); } }
|
|||||||
})(attrName, attrVal);
|
})(attrName, attrVal);
|
||||||
} else {
|
} else {
|
||||||
if (!isNil(attrVal) && attrVal !== false) {
|
if (!isNil(attrVal) && attrVal !== false) {
|
||||||
if (attrName === "class" && attrVal && attrVal._styleValue) {
|
if (contains(BOOLEAN_ATTRS, attrName)) {
|
||||||
el.setAttribute("class", (el.getAttribute("class") || "") + " " + attrVal.className);
|
|
||||||
} else if (attrName === "style" && attrVal && attrVal._styleValue) {
|
|
||||||
el.setAttribute("class", (el.getAttribute("class") || "") + " " + attrVal.className);
|
|
||||||
} else if (contains(BOOLEAN_ATTRS, attrName)) {
|
|
||||||
if (isSxTruthy(attrVal)) el.setAttribute(attrName, "");
|
if (isSxTruthy(attrVal)) el.setAttribute(attrName, "");
|
||||||
} else if (attrVal === true) {
|
} else if (attrVal === true) {
|
||||||
el.setAttribute(attrName, "");
|
el.setAttribute(attrName, "");
|
||||||
@@ -4670,7 +4311,7 @@ callExpr.push(dictGet(kwargs, k)); } }
|
|||||||
registerIoDeps: typeof registerIoDeps === "function" ? registerIoDeps : null,
|
registerIoDeps: typeof registerIoDeps === "function" ? registerIoDeps : null,
|
||||||
asyncRender: typeof asyncSxRenderWithEnv === "function" ? asyncSxRenderWithEnv : null,
|
asyncRender: typeof asyncSxRenderWithEnv === "function" ? asyncSxRenderWithEnv : null,
|
||||||
asyncRenderToDom: typeof asyncRenderToDom === "function" ? asyncRenderToDom : null,
|
asyncRenderToDom: typeof asyncRenderToDom === "function" ? asyncRenderToDom : null,
|
||||||
_version: "ref-2.0 (boot+cssx+dom+engine+html+orchestration+parser+sx, bootstrap-compiled)"
|
_version: "ref-2.0 (boot+dom+engine+html+orchestration+parser+sx, bootstrap-compiled)"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -58,15 +58,6 @@
|
|||||||
function RawHTML(html) { this.html = html; }
|
function RawHTML(html) { this.html = html; }
|
||||||
RawHTML.prototype._raw = true;
|
RawHTML.prototype._raw = true;
|
||||||
|
|
||||||
function StyleValue(className, declarations, mediaRules, pseudoRules, keyframes) {
|
|
||||||
this.className = className;
|
|
||||||
this.declarations = declarations || "";
|
|
||||||
this.mediaRules = mediaRules || [];
|
|
||||||
this.pseudoRules = pseudoRules || [];
|
|
||||||
this.keyframes = keyframes || [];
|
|
||||||
}
|
|
||||||
StyleValue.prototype._styleValue = true;
|
|
||||||
|
|
||||||
function isSym(x) { return x != null && x._sym === true; }
|
function isSym(x) { return x != null && x._sym === true; }
|
||||||
function isKw(x) { return x != null && x._kw === true; }
|
function isKw(x) { return x != null && x._kw === true; }
|
||||||
|
|
||||||
@@ -102,7 +93,6 @@
|
|||||||
if (x._component) return "component";
|
if (x._component) return "component";
|
||||||
if (x._macro) return "macro";
|
if (x._macro) return "macro";
|
||||||
if (x._raw) return "raw-html";
|
if (x._raw) return "raw-html";
|
||||||
if (x._styleValue) return "style-value";
|
|
||||||
if (typeof Node !== "undefined" && x instanceof Node) return "dom-node";
|
if (typeof Node !== "undefined" && x instanceof Node) return "dom-node";
|
||||||
if (Array.isArray(x)) return "list";
|
if (Array.isArray(x)) return "list";
|
||||||
if (typeof x === "object") return "dict";
|
if (typeof x === "object") return "dict";
|
||||||
@@ -149,27 +139,6 @@
|
|||||||
function isComponent(x) { return x != null && x._component === true; }
|
function isComponent(x) { return x != null && x._component === true; }
|
||||||
function isMacro(x) { return x != null && x._macro === true; }
|
function isMacro(x) { return x != null && x._macro === true; }
|
||||||
|
|
||||||
function isStyleValue(x) { return x != null && x._styleValue === true; }
|
|
||||||
function styleValueClass(x) { return x.className; }
|
|
||||||
function styleValue_p(x) { return x != null && x._styleValue === true; }
|
|
||||||
|
|
||||||
function buildKeyframes(kfName, steps, env) {
|
|
||||||
// Platform implementation of defkeyframes
|
|
||||||
var parts = [];
|
|
||||||
for (var i = 0; i < steps.length; i++) {
|
|
||||||
var step = steps[i];
|
|
||||||
var selector = isSym(step[0]) ? step[0].name : String(step[0]);
|
|
||||||
var body = trampoline(evalExpr(step[1], env));
|
|
||||||
var decls = isStyleValue(body) ? body.declarations : String(body);
|
|
||||||
parts.push(selector + "{" + decls + "}");
|
|
||||||
}
|
|
||||||
var kfRule = "@keyframes " + kfName + "{" + parts.join("") + "}";
|
|
||||||
var cn = "sx-ref-kf-" + kfName;
|
|
||||||
var sv = new StyleValue(cn, "animation-name:" + kfName, [], [], [[kfName, kfRule]]);
|
|
||||||
env[kfName] = sv;
|
|
||||||
return sv;
|
|
||||||
}
|
|
||||||
|
|
||||||
function envHas(env, name) { return name in env; }
|
function envHas(env, name) { return name in env; }
|
||||||
function envGet(env, name) { return env[name]; }
|
function envGet(env, name) { return env[name]; }
|
||||||
function envSet(env, name, val) { env[name] = val; }
|
function envSet(env, name, val) { env[name] = val; }
|
||||||
@@ -377,29 +346,6 @@
|
|||||||
PRIMITIVES["strip-tags"] = function(s) { return String(s).replace(/<[^>]+>/g, ""); };
|
PRIMITIVES["strip-tags"] = function(s) { return String(s).replace(/<[^>]+>/g, ""); };
|
||||||
|
|
||||||
|
|
||||||
// stdlib.style
|
|
||||||
PRIMITIVES["css"] = function() {
|
|
||||||
var atoms = [];
|
|
||||||
for (var i = 0; i < arguments.length; i++) {
|
|
||||||
var a = arguments[i];
|
|
||||||
if (isNil(a) || a === false) continue;
|
|
||||||
atoms.push(isKw(a) ? a.name : String(a));
|
|
||||||
}
|
|
||||||
if (!atoms.length) return NIL;
|
|
||||||
return new StyleValue("sx-" + atoms.join("-"), atoms.join(";"), [], [], []);
|
|
||||||
};
|
|
||||||
PRIMITIVES["merge-styles"] = function() {
|
|
||||||
var valid = [];
|
|
||||||
for (var i = 0; i < arguments.length; i++) {
|
|
||||||
if (isStyleValue(arguments[i])) valid.push(arguments[i]);
|
|
||||||
}
|
|
||||||
if (!valid.length) return NIL;
|
|
||||||
if (valid.length === 1) return valid[0];
|
|
||||||
var allDecls = valid.map(function(v) { return v.declarations; }).join(";");
|
|
||||||
return new StyleValue("sx-merged", allDecls, [], [], []);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// stdlib.debug
|
// stdlib.debug
|
||||||
PRIMITIVES["assert"] = function(cond, msg) {
|
PRIMITIVES["assert"] = function(cond, msg) {
|
||||||
if (!isSxTruthy(cond)) throw new Error("Assertion error: " + (msg || "Assertion failed"));
|
if (!isSxTruthy(cond)) throw new Error("Assertion error: " + (msg || "Assertion failed"));
|
||||||
@@ -486,7 +432,7 @@
|
|||||||
function isSpecialForm(n) { return n in {
|
function isSpecialForm(n) { return n in {
|
||||||
"if":1,"when":1,"cond":1,"case":1,"and":1,"or":1,"let":1,"let*":1,
|
"if":1,"when":1,"cond":1,"case":1,"and":1,"or":1,"let":1,"let*":1,
|
||||||
"lambda":1,"fn":1,"define":1,"defcomp":1,"defmacro":1,"defstyle":1,
|
"lambda":1,"fn":1,"define":1,"defcomp":1,"defmacro":1,"defstyle":1,
|
||||||
"defkeyframes":1,"defhandler":1,"begin":1,"do":1,
|
"defhandler":1,"begin":1,"do":1,
|
||||||
"quote":1,"quasiquote":1,"->":1,"set!":1
|
"quote":1,"quasiquote":1,"->":1,"set!":1
|
||||||
}; }
|
}; }
|
||||||
function isHoForm(n) { return n in {
|
function isHoForm(n) { return n in {
|
||||||
@@ -517,7 +463,7 @@
|
|||||||
|
|
||||||
function isDefinitionForm(name) {
|
function isDefinitionForm(name) {
|
||||||
return name === "define" || name === "defcomp" || name === "defmacro" ||
|
return name === "define" || name === "defcomp" || name === "defmacro" ||
|
||||||
name === "defstyle" || name === "defkeyframes" || name === "defhandler";
|
name === "defstyle" || name === "defhandler";
|
||||||
}
|
}
|
||||||
|
|
||||||
function indexOf_(s, ch) {
|
function indexOf_(s, ch) {
|
||||||
@@ -657,10 +603,10 @@
|
|||||||
var args = rest(expr);
|
var args = rest(expr);
|
||||||
return (isSxTruthy(!sxOr((typeOf(head) == "symbol"), (typeOf(head) == "lambda"), (typeOf(head) == "list"))) ? map(function(x) { return trampoline(evalExpr(x, env)); }, expr) : (isSxTruthy((typeOf(head) == "symbol")) ? (function() {
|
return (isSxTruthy(!sxOr((typeOf(head) == "symbol"), (typeOf(head) == "lambda"), (typeOf(head) == "list"))) ? map(function(x) { return trampoline(evalExpr(x, env)); }, expr) : (isSxTruthy((typeOf(head) == "symbol")) ? (function() {
|
||||||
var name = symbolName(head);
|
var name = symbolName(head);
|
||||||
return (isSxTruthy((name == "if")) ? sfIf(args, env) : (isSxTruthy((name == "when")) ? sfWhen(args, env) : (isSxTruthy((name == "cond")) ? sfCond(args, env) : (isSxTruthy((name == "case")) ? sfCase(args, env) : (isSxTruthy((name == "and")) ? sfAnd(args, env) : (isSxTruthy((name == "or")) ? sfOr(args, env) : (isSxTruthy((name == "let")) ? sfLet(args, env) : (isSxTruthy((name == "let*")) ? sfLet(args, env) : (isSxTruthy((name == "letrec")) ? sfLetrec(args, env) : (isSxTruthy((name == "lambda")) ? sfLambda(args, env) : (isSxTruthy((name == "fn")) ? sfLambda(args, env) : (isSxTruthy((name == "define")) ? sfDefine(args, env) : (isSxTruthy((name == "defcomp")) ? sfDefcomp(args, env) : (isSxTruthy((name == "defmacro")) ? sfDefmacro(args, env) : (isSxTruthy((name == "defstyle")) ? sfDefstyle(args, env) : (isSxTruthy((name == "defkeyframes")) ? sfDefkeyframes(args, env) : (isSxTruthy((name == "defhandler")) ? sfDefhandler(args, env) : (isSxTruthy((name == "defpage")) ? sfDefpage(args, env) : (isSxTruthy((name == "defquery")) ? sfDefquery(args, env) : (isSxTruthy((name == "defaction")) ? sfDefaction(args, env) : (isSxTruthy((name == "begin")) ? sfBegin(args, env) : (isSxTruthy((name == "do")) ? sfBegin(args, env) : (isSxTruthy((name == "quote")) ? sfQuote(args, env) : (isSxTruthy((name == "quasiquote")) ? sfQuasiquote(args, env) : (isSxTruthy((name == "->")) ? sfThreadFirst(args, env) : (isSxTruthy((name == "set!")) ? sfSetBang(args, env) : (isSxTruthy((name == "reset")) ? sfReset(args, env) : (isSxTruthy((name == "shift")) ? sfShift(args, env) : (isSxTruthy((name == "dynamic-wind")) ? sfDynamicWind(args, env) : (isSxTruthy((name == "map")) ? hoMap(args, env) : (isSxTruthy((name == "map-indexed")) ? hoMapIndexed(args, env) : (isSxTruthy((name == "filter")) ? hoFilter(args, env) : (isSxTruthy((name == "reduce")) ? hoReduce(args, env) : (isSxTruthy((name == "some")) ? hoSome(args, env) : (isSxTruthy((name == "every?")) ? hoEvery(args, env) : (isSxTruthy((name == "for-each")) ? hoForEach(args, env) : (isSxTruthy((isSxTruthy(envHas(env, name)) && isMacro(envGet(env, name)))) ? (function() {
|
return (isSxTruthy((name == "if")) ? sfIf(args, env) : (isSxTruthy((name == "when")) ? sfWhen(args, env) : (isSxTruthy((name == "cond")) ? sfCond(args, env) : (isSxTruthy((name == "case")) ? sfCase(args, env) : (isSxTruthy((name == "and")) ? sfAnd(args, env) : (isSxTruthy((name == "or")) ? sfOr(args, env) : (isSxTruthy((name == "let")) ? sfLet(args, env) : (isSxTruthy((name == "let*")) ? sfLet(args, env) : (isSxTruthy((name == "letrec")) ? sfLetrec(args, env) : (isSxTruthy((name == "lambda")) ? sfLambda(args, env) : (isSxTruthy((name == "fn")) ? sfLambda(args, env) : (isSxTruthy((name == "define")) ? sfDefine(args, env) : (isSxTruthy((name == "defcomp")) ? sfDefcomp(args, env) : (isSxTruthy((name == "defmacro")) ? sfDefmacro(args, env) : (isSxTruthy((name == "defstyle")) ? sfDefstyle(args, env) : (isSxTruthy((name == "defhandler")) ? sfDefhandler(args, env) : (isSxTruthy((name == "defpage")) ? sfDefpage(args, env) : (isSxTruthy((name == "defquery")) ? sfDefquery(args, env) : (isSxTruthy((name == "defaction")) ? sfDefaction(args, env) : (isSxTruthy((name == "begin")) ? sfBegin(args, env) : (isSxTruthy((name == "do")) ? sfBegin(args, env) : (isSxTruthy((name == "quote")) ? sfQuote(args, env) : (isSxTruthy((name == "quasiquote")) ? sfQuasiquote(args, env) : (isSxTruthy((name == "->")) ? sfThreadFirst(args, env) : (isSxTruthy((name == "set!")) ? sfSetBang(args, env) : (isSxTruthy((name == "reset")) ? sfReset(args, env) : (isSxTruthy((name == "shift")) ? sfShift(args, env) : (isSxTruthy((name == "dynamic-wind")) ? sfDynamicWind(args, env) : (isSxTruthy((name == "map")) ? hoMap(args, env) : (isSxTruthy((name == "map-indexed")) ? hoMapIndexed(args, env) : (isSxTruthy((name == "filter")) ? hoFilter(args, env) : (isSxTruthy((name == "reduce")) ? hoReduce(args, env) : (isSxTruthy((name == "some")) ? hoSome(args, env) : (isSxTruthy((name == "every?")) ? hoEvery(args, env) : (isSxTruthy((name == "for-each")) ? hoForEach(args, env) : (isSxTruthy((isSxTruthy(envHas(env, name)) && isMacro(envGet(env, name)))) ? (function() {
|
||||||
var mac = envGet(env, name);
|
var mac = envGet(env, name);
|
||||||
return makeThunk(expandMacro(mac, args, env), env);
|
return makeThunk(expandMacro(mac, args, env), env);
|
||||||
})() : (isSxTruthy(isRenderExpr(expr)) ? renderExpr(expr, env) : evalCall(head, args, env)))))))))))))))))))))))))))))))))))))));
|
})() : (isSxTruthy(isRenderExpr(expr)) ? renderExpr(expr, env) : evalCall(head, args, env))))))))))))))))))))))))))))))))))))));
|
||||||
})() : evalCall(head, args, env)));
|
})() : evalCall(head, args, env)));
|
||||||
})(); };
|
})(); };
|
||||||
|
|
||||||
@@ -881,13 +827,6 @@ return append_b(inits, nth(binding, 1)); }, bindings) : reduce(function(acc, pai
|
|||||||
return value;
|
return value;
|
||||||
})(); };
|
})(); };
|
||||||
|
|
||||||
// sf-defkeyframes
|
|
||||||
var sfDefkeyframes = function(args, env) { return (function() {
|
|
||||||
var kfName = symbolName(first(args));
|
|
||||||
var steps = rest(args);
|
|
||||||
return buildKeyframes(kfName, steps, env);
|
|
||||||
})(); };
|
|
||||||
|
|
||||||
// sf-begin
|
// sf-begin
|
||||||
var sfBegin = function(args, env) { return (isSxTruthy(isEmpty(args)) ? NIL : (forEach(function(e) { return trampoline(evalExpr(e, env)); }, slice(args, 0, (len(args) - 1))), makeThunk(last(args), env))); };
|
var sfBegin = function(args, env) { return (isSxTruthy(isEmpty(args)) ? NIL : (forEach(function(e) { return trampoline(evalExpr(e, env)); }, slice(args, 0, (len(args) - 1))), makeThunk(last(args), env))); };
|
||||||
|
|
||||||
@@ -1047,7 +986,7 @@ return append_b(inits, nth(binding, 1)); }, bindings) : reduce(function(acc, pai
|
|||||||
var BOOLEAN_ATTRS = ["async", "autofocus", "autoplay", "checked", "controls", "default", "defer", "disabled", "formnovalidate", "hidden", "inert", "ismap", "loop", "multiple", "muted", "nomodule", "novalidate", "open", "playsinline", "readonly", "required", "reversed", "selected"];
|
var BOOLEAN_ATTRS = ["async", "autofocus", "autoplay", "checked", "controls", "default", "defer", "disabled", "formnovalidate", "hidden", "inert", "ismap", "loop", "multiple", "muted", "nomodule", "novalidate", "open", "playsinline", "readonly", "required", "reversed", "selected"];
|
||||||
|
|
||||||
// definition-form?
|
// definition-form?
|
||||||
var isDefinitionForm = function(name) { return sxOr((name == "define"), (name == "defcomp"), (name == "defmacro"), (name == "defstyle"), (name == "defkeyframes"), (name == "defhandler")); };
|
var isDefinitionForm = function(name) { return sxOr((name == "define"), (name == "defcomp"), (name == "defmacro"), (name == "defstyle"), (name == "defhandler")); };
|
||||||
|
|
||||||
// parse-element-args
|
// parse-element-args
|
||||||
var parseElementArgs = function(args, env) { return (function() {
|
var parseElementArgs = function(args, env) { return (function() {
|
||||||
@@ -1067,7 +1006,7 @@ return append_b(inits, nth(binding, 1)); }, bindings) : reduce(function(acc, pai
|
|||||||
// render-attrs
|
// render-attrs
|
||||||
var renderAttrs = function(attrs) { return join("", map(function(key) { return (function() {
|
var renderAttrs = function(attrs) { return join("", map(function(key) { return (function() {
|
||||||
var val = dictGet(attrs, key);
|
var val = dictGet(attrs, key);
|
||||||
return (isSxTruthy((isSxTruthy(contains(BOOLEAN_ATTRS, key)) && val)) ? (String(" ") + String(key)) : (isSxTruthy((isSxTruthy(contains(BOOLEAN_ATTRS, key)) && !val)) ? "" : (isSxTruthy(isNil(val)) ? "" : (isSxTruthy((isSxTruthy((key == "style")) && isStyleValue(val))) ? (String(" class=\"") + String(styleValueClass(val)) + String("\"")) : (String(" ") + String(key) + String("=\"") + String(escapeAttr((String(val)))) + String("\""))))));
|
return (isSxTruthy((isSxTruthy(contains(BOOLEAN_ATTRS, key)) && val)) ? (String(" ") + String(key)) : (isSxTruthy((isSxTruthy(contains(BOOLEAN_ATTRS, key)) && !val)) ? "" : (isSxTruthy(isNil(val)) ? "" : (String(" ") + String(key) + String("=\"") + String(escapeAttr((String(val)))) + String("\"")))));
|
||||||
})(); }, keys(attrs))); };
|
})(); }, keys(attrs))); };
|
||||||
|
|
||||||
|
|
||||||
@@ -1188,10 +1127,10 @@ continue; } else { return NIL; } } };
|
|||||||
var renderToHtml = function(expr, env) { return (function() { var _m = typeOf(expr); if (_m == "nil") return ""; if (_m == "string") return escapeHtml(expr); if (_m == "number") return (String(expr)); if (_m == "boolean") return (isSxTruthy(expr) ? "true" : "false"); if (_m == "list") return (isSxTruthy(isEmpty(expr)) ? "" : renderListToHtml(expr, env)); if (_m == "symbol") return renderValueToHtml(trampoline(evalExpr(expr, env)), env); if (_m == "keyword") return escapeHtml(keywordName(expr)); if (_m == "raw-html") return rawHtmlContent(expr); return renderValueToHtml(trampoline(evalExpr(expr, env)), env); })(); };
|
var renderToHtml = function(expr, env) { return (function() { var _m = typeOf(expr); if (_m == "nil") return ""; if (_m == "string") return escapeHtml(expr); if (_m == "number") return (String(expr)); if (_m == "boolean") return (isSxTruthy(expr) ? "true" : "false"); if (_m == "list") return (isSxTruthy(isEmpty(expr)) ? "" : renderListToHtml(expr, env)); if (_m == "symbol") return renderValueToHtml(trampoline(evalExpr(expr, env)), env); if (_m == "keyword") return escapeHtml(keywordName(expr)); if (_m == "raw-html") return rawHtmlContent(expr); return renderValueToHtml(trampoline(evalExpr(expr, env)), env); })(); };
|
||||||
|
|
||||||
// render-value-to-html
|
// render-value-to-html
|
||||||
var renderValueToHtml = function(val, env) { return (function() { var _m = typeOf(val); if (_m == "nil") return ""; if (_m == "string") return escapeHtml(val); if (_m == "number") return (String(val)); if (_m == "boolean") return (isSxTruthy(val) ? "true" : "false"); if (_m == "list") return renderListToHtml(val, env); if (_m == "raw-html") return rawHtmlContent(val); if (_m == "style-value") return styleValueClass(val); return escapeHtml((String(val))); })(); };
|
var renderValueToHtml = function(val, env) { return (function() { var _m = typeOf(val); if (_m == "nil") return ""; if (_m == "string") return escapeHtml(val); if (_m == "number") return (String(val)); if (_m == "boolean") return (isSxTruthy(val) ? "true" : "false"); if (_m == "list") return renderListToHtml(val, env); if (_m == "raw-html") return rawHtmlContent(val); return escapeHtml((String(val))); })(); };
|
||||||
|
|
||||||
// RENDER_HTML_FORMS
|
// RENDER_HTML_FORMS
|
||||||
var RENDER_HTML_FORMS = ["if", "when", "cond", "case", "let", "let*", "begin", "do", "define", "defcomp", "defmacro", "defstyle", "defkeyframes", "defhandler", "map", "map-indexed", "filter", "for-each"];
|
var RENDER_HTML_FORMS = ["if", "when", "cond", "case", "let", "let*", "begin", "do", "define", "defcomp", "defmacro", "defstyle", "defhandler", "map", "map-indexed", "filter", "for-each"];
|
||||||
|
|
||||||
// render-html-form?
|
// render-html-form?
|
||||||
var isRenderHtmlForm = function(name) { return contains(RENDER_HTML_FORMS, name); };
|
var isRenderHtmlForm = function(name) { return contains(RENDER_HTML_FORMS, name); };
|
||||||
@@ -1339,7 +1278,7 @@ continue; } else { return NIL; } } };
|
|||||||
var MATH_NS = "http://www.w3.org/1998/Math/MathML";
|
var MATH_NS = "http://www.w3.org/1998/Math/MathML";
|
||||||
|
|
||||||
// render-to-dom
|
// render-to-dom
|
||||||
var renderToDom = function(expr, env, ns) { return (function() { var _m = typeOf(expr); if (_m == "nil") return createFragment(); if (_m == "boolean") return createFragment(); if (_m == "raw-html") return domParseHtml(rawHtmlContent(expr)); if (_m == "string") return createTextNode(expr); if (_m == "number") return createTextNode((String(expr))); if (_m == "symbol") return renderToDom(trampoline(evalExpr(expr, env)), env, ns); if (_m == "keyword") return createTextNode(keywordName(expr)); if (_m == "dom-node") return expr; if (_m == "dict") return createFragment(); if (_m == "list") return (isSxTruthy(isEmpty(expr)) ? createFragment() : renderDomList(expr, env, ns)); if (_m == "style-value") return createTextNode(styleValueClass(expr)); return createTextNode((String(expr))); })(); };
|
var renderToDom = function(expr, env, ns) { return (function() { var _m = typeOf(expr); if (_m == "nil") return createFragment(); if (_m == "boolean") return createFragment(); if (_m == "raw-html") return domParseHtml(rawHtmlContent(expr)); if (_m == "string") return createTextNode(expr); if (_m == "number") return createTextNode((String(expr))); if (_m == "symbol") return renderToDom(trampoline(evalExpr(expr, env)), env, ns); if (_m == "keyword") return createTextNode(keywordName(expr)); if (_m == "dom-node") return expr; if (_m == "dict") return createFragment(); if (_m == "list") return (isSxTruthy(isEmpty(expr)) ? createFragment() : renderDomList(expr, env, ns)); return createTextNode((String(expr))); })(); };
|
||||||
|
|
||||||
// render-dom-list
|
// render-dom-list
|
||||||
var renderDomList = function(expr, env, ns) { return (function() {
|
var renderDomList = function(expr, env, ns) { return (function() {
|
||||||
@@ -1362,22 +1301,15 @@ continue; } else { return NIL; } } };
|
|||||||
var renderDomElement = function(tag, args, env, ns) { return (function() {
|
var renderDomElement = function(tag, args, env, ns) { return (function() {
|
||||||
var newNs = (isSxTruthy((tag == "svg")) ? SVG_NS : (isSxTruthy((tag == "math")) ? MATH_NS : ns));
|
var newNs = (isSxTruthy((tag == "svg")) ? SVG_NS : (isSxTruthy((tag == "math")) ? MATH_NS : ns));
|
||||||
var el = domCreateElement(tag, newNs);
|
var el = domCreateElement(tag, newNs);
|
||||||
var extraClass = NIL;
|
|
||||||
reduce(function(state, arg) { return (function() {
|
reduce(function(state, arg) { return (function() {
|
||||||
var skip = get(state, "skip");
|
var skip = get(state, "skip");
|
||||||
return (isSxTruthy(skip) ? assoc(state, "skip", false, "i", (get(state, "i") + 1)) : (isSxTruthy((isSxTruthy((typeOf(arg) == "keyword")) && ((get(state, "i") + 1) < len(args)))) ? (function() {
|
return (isSxTruthy(skip) ? assoc(state, "skip", false, "i", (get(state, "i") + 1)) : (isSxTruthy((isSxTruthy((typeOf(arg) == "keyword")) && ((get(state, "i") + 1) < len(args)))) ? (function() {
|
||||||
var attrName = keywordName(arg);
|
var attrName = keywordName(arg);
|
||||||
var attrVal = trampoline(evalExpr(nth(args, (get(state, "i") + 1)), env));
|
var attrVal = trampoline(evalExpr(nth(args, (get(state, "i") + 1)), env));
|
||||||
(isSxTruthy(sxOr(isNil(attrVal), (attrVal == false))) ? NIL : (isSxTruthy((isSxTruthy((attrName == "style")) && isStyleValue(attrVal))) ? (extraClass = styleValueClass(attrVal)) : (isSxTruthy(contains(BOOLEAN_ATTRS, attrName)) ? (isSxTruthy(attrVal) ? domSetAttr(el, attrName, "") : NIL) : (isSxTruthy((attrVal == true)) ? domSetAttr(el, attrName, "") : domSetAttr(el, attrName, (String(attrVal)))))));
|
(isSxTruthy(sxOr(isNil(attrVal), (attrVal == false))) ? NIL : (isSxTruthy(contains(BOOLEAN_ATTRS, attrName)) ? (isSxTruthy(attrVal) ? domSetAttr(el, attrName, "") : NIL) : (isSxTruthy((attrVal == true)) ? domSetAttr(el, attrName, "") : domSetAttr(el, attrName, (String(attrVal))))));
|
||||||
return assoc(state, "skip", true, "i", (get(state, "i") + 1));
|
return assoc(state, "skip", true, "i", (get(state, "i") + 1));
|
||||||
})() : ((isSxTruthy(!contains(VOID_ELEMENTS, tag)) ? domAppend(el, renderToDom(arg, env, newNs)) : NIL), assoc(state, "i", (get(state, "i") + 1)))));
|
})() : ((isSxTruthy(!contains(VOID_ELEMENTS, tag)) ? domAppend(el, renderToDom(arg, env, newNs)) : NIL), assoc(state, "i", (get(state, "i") + 1)))));
|
||||||
})(); }, {["i"]: 0, ["skip"]: false}, args);
|
})(); }, {["i"]: 0, ["skip"]: false}, args);
|
||||||
if (isSxTruthy(extraClass)) {
|
|
||||||
(function() {
|
|
||||||
var existing = domGetAttr(el, "class");
|
|
||||||
return domSetAttr(el, "class", (isSxTruthy(existing) ? (String(existing) + String(" ") + String(extraClass)) : extraClass));
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
return el;
|
return el;
|
||||||
})(); };
|
})(); };
|
||||||
|
|
||||||
@@ -1433,7 +1365,7 @@ continue; } else { return NIL; } } };
|
|||||||
})(); };
|
})(); };
|
||||||
|
|
||||||
// RENDER_DOM_FORMS
|
// RENDER_DOM_FORMS
|
||||||
var RENDER_DOM_FORMS = ["if", "when", "cond", "case", "let", "let*", "begin", "do", "define", "defcomp", "defmacro", "defstyle", "defkeyframes", "defhandler", "map", "map-indexed", "filter", "for-each"];
|
var RENDER_DOM_FORMS = ["if", "when", "cond", "case", "let", "let*", "begin", "do", "define", "defcomp", "defmacro", "defstyle", "defhandler", "map", "map-indexed", "filter", "for-each"];
|
||||||
|
|
||||||
// render-dom-form?
|
// render-dom-form?
|
||||||
var isRenderDomForm = function(name) { return contains(RENDER_DOM_FORMS, name); };
|
var isRenderDomForm = function(name) { return contains(RENDER_DOM_FORMS, name); };
|
||||||
@@ -2089,180 +2021,6 @@ return bindInlineHandlers(root); };
|
|||||||
var engineInit = function() { return (initCssTracking(), sxProcessScripts(NIL), sxHydrate(NIL), processElements(NIL)); };
|
var engineInit = function() { return (initCssTracking(), sxProcessScripts(NIL), sxHydrate(NIL), processElements(NIL)); };
|
||||||
|
|
||||||
|
|
||||||
// === Transpiled from cssx ===
|
|
||||||
|
|
||||||
// _style-atoms
|
|
||||||
var _styleAtoms = {};
|
|
||||||
|
|
||||||
// _pseudo-variants
|
|
||||||
var _pseudoVariants = {};
|
|
||||||
|
|
||||||
// _responsive-breakpoints
|
|
||||||
var _responsiveBreakpoints = {};
|
|
||||||
|
|
||||||
// _style-keyframes
|
|
||||||
var _styleKeyframes = {};
|
|
||||||
|
|
||||||
// _arbitrary-patterns
|
|
||||||
var _arbitraryPatterns = [];
|
|
||||||
|
|
||||||
// _child-selector-prefixes
|
|
||||||
var _childSelectorPrefixes = [];
|
|
||||||
|
|
||||||
// _style-cache
|
|
||||||
var _styleCache = {};
|
|
||||||
|
|
||||||
// _injected-styles
|
|
||||||
var _injectedStyles = {};
|
|
||||||
|
|
||||||
// load-style-dict
|
|
||||||
var loadStyleDict = function(data) { _styleAtoms = sxOr(get(data, "a"), {});
|
|
||||||
_pseudoVariants = sxOr(get(data, "v"), {});
|
|
||||||
_responsiveBreakpoints = sxOr(get(data, "b"), {});
|
|
||||||
_styleKeyframes = sxOr(get(data, "k"), {});
|
|
||||||
_childSelectorPrefixes = sxOr(get(data, "c"), []);
|
|
||||||
_arbitraryPatterns = map(function(pair) { return {["re"]: compileRegex((String("^") + String(first(pair)) + String("$"))), ["tmpl"]: nth(pair, 1)}; }, sxOr(get(data, "p"), []));
|
|
||||||
return (_styleCache = {}); };
|
|
||||||
|
|
||||||
// split-variant
|
|
||||||
var splitVariant = function(atom) { return (function() {
|
|
||||||
var result = NIL;
|
|
||||||
{ var _c = keys(_responsiveBreakpoints); for (var _i = 0; _i < _c.length; _i++) { var bp = _c[_i]; if (isSxTruthy(isNil(result))) {
|
|
||||||
(function() {
|
|
||||||
var prefix = (String(bp) + String(":"));
|
|
||||||
return (isSxTruthy(startsWith(atom, prefix)) ? (function() {
|
|
||||||
var restAtom = slice(atom, len(prefix));
|
|
||||||
return (function() {
|
|
||||||
var innerMatch = NIL;
|
|
||||||
{ var _c = keys(_pseudoVariants); for (var _i = 0; _i < _c.length; _i++) { var pv = _c[_i]; if (isSxTruthy(isNil(innerMatch))) {
|
|
||||||
(function() {
|
|
||||||
var innerPrefix = (String(pv) + String(":"));
|
|
||||||
return (isSxTruthy(startsWith(restAtom, innerPrefix)) ? (innerMatch = [(String(bp) + String(":") + String(pv)), slice(restAtom, len(innerPrefix))]) : NIL);
|
|
||||||
})();
|
|
||||||
} } }
|
|
||||||
return (result = sxOr(innerMatch, [bp, restAtom]));
|
|
||||||
})();
|
|
||||||
})() : NIL);
|
|
||||||
})();
|
|
||||||
} } }
|
|
||||||
if (isSxTruthy(isNil(result))) {
|
|
||||||
{ var _c = keys(_pseudoVariants); for (var _i = 0; _i < _c.length; _i++) { var pv = _c[_i]; if (isSxTruthy(isNil(result))) {
|
|
||||||
(function() {
|
|
||||||
var prefix = (String(pv) + String(":"));
|
|
||||||
return (isSxTruthy(startsWith(atom, prefix)) ? (result = [pv, slice(atom, len(prefix))]) : NIL);
|
|
||||||
})();
|
|
||||||
} } }
|
|
||||||
}
|
|
||||||
return sxOr(result, [NIL, atom]);
|
|
||||||
})(); };
|
|
||||||
|
|
||||||
// resolve-atom
|
|
||||||
var resolveAtom = function(atom) { return (function() {
|
|
||||||
var decls = dictGet(_styleAtoms, atom);
|
|
||||||
return (isSxTruthy(!isNil(decls)) ? decls : (isSxTruthy(startsWith(atom, "animate-")) ? (function() {
|
|
||||||
var kfName = slice(atom, 8);
|
|
||||||
return (isSxTruthy(dictHas(_styleKeyframes, kfName)) ? (String("animation-name:") + String(kfName)) : NIL);
|
|
||||||
})() : (function() {
|
|
||||||
var matchResult = NIL;
|
|
||||||
{ var _c = _arbitraryPatterns; for (var _i = 0; _i < _c.length; _i++) { var pat = _c[_i]; if (isSxTruthy(isNil(matchResult))) {
|
|
||||||
(function() {
|
|
||||||
var m = regexMatch(get(pat, "re"), atom);
|
|
||||||
return (isSxTruthy(m) ? (matchResult = regexReplaceGroups(get(pat, "tmpl"), m)) : NIL);
|
|
||||||
})();
|
|
||||||
} } }
|
|
||||||
return matchResult;
|
|
||||||
})()));
|
|
||||||
})(); };
|
|
||||||
|
|
||||||
// is-child-selector-atom?
|
|
||||||
var isChildSelectorAtom = function(atom) { return some(function(prefix) { return startsWith(atom, prefix); }, _childSelectorPrefixes); };
|
|
||||||
|
|
||||||
// hash-style
|
|
||||||
var hashStyle = function(input) { return fnv1aHash(input); };
|
|
||||||
|
|
||||||
// resolve-style
|
|
||||||
var resolveStyle = function(atoms) { return (function() {
|
|
||||||
var key = join("\\0", atoms);
|
|
||||||
return (function() {
|
|
||||||
var cached = dictGet(_styleCache, key);
|
|
||||||
return (isSxTruthy(!isNil(cached)) ? cached : (function() {
|
|
||||||
var baseDecls = [];
|
|
||||||
var mediaRules = [];
|
|
||||||
var pseudoRules = [];
|
|
||||||
var kfNeeded = [];
|
|
||||||
{ var _c = atoms; for (var _i = 0; _i < _c.length; _i++) { var a = _c[_i]; if (isSxTruthy(a)) {
|
|
||||||
(function() {
|
|
||||||
var clean = (isSxTruthy(startsWith(a, ":")) ? slice(a, 1) : a);
|
|
||||||
return (function() {
|
|
||||||
var parts = splitVariant(clean);
|
|
||||||
return (function() {
|
|
||||||
var variant = first(parts);
|
|
||||||
var base = nth(parts, 1);
|
|
||||||
var decls = resolveAtom(base);
|
|
||||||
return (isSxTruthy(decls) ? ((isSxTruthy(startsWith(base, "animate-")) ? (function() {
|
|
||||||
var kfName = slice(base, 8);
|
|
||||||
return (isSxTruthy(dictHas(_styleKeyframes, kfName)) ? append_b(kfNeeded, [kfName, dictGet(_styleKeyframes, kfName)]) : NIL);
|
|
||||||
})() : NIL), (isSxTruthy(isNil(variant)) ? (isSxTruthy(isChildSelectorAtom(base)) ? append_b(pseudoRules, [">:not(:first-child)", decls]) : append_b(baseDecls, decls)) : (isSxTruthy(dictHas(_responsiveBreakpoints, variant)) ? append_b(mediaRules, [dictGet(_responsiveBreakpoints, variant), decls]) : (isSxTruthy(dictHas(_pseudoVariants, variant)) ? append_b(pseudoRules, [dictGet(_pseudoVariants, variant), decls]) : (function() {
|
|
||||||
var vparts = split(variant, ":");
|
|
||||||
var mediaPart = NIL;
|
|
||||||
var pseudoPart = NIL;
|
|
||||||
{ var _c = vparts; for (var _i = 0; _i < _c.length; _i++) { var vp = _c[_i]; (isSxTruthy(dictHas(_responsiveBreakpoints, vp)) ? (mediaPart = dictGet(_responsiveBreakpoints, vp)) : (isSxTruthy(dictHas(_pseudoVariants, vp)) ? (pseudoPart = dictGet(_pseudoVariants, vp)) : NIL)); } }
|
|
||||||
if (isSxTruthy(mediaPart)) {
|
|
||||||
mediaRules.push([mediaPart, decls]);
|
|
||||||
}
|
|
||||||
if (isSxTruthy(pseudoPart)) {
|
|
||||||
pseudoRules.push([pseudoPart, decls]);
|
|
||||||
}
|
|
||||||
return (isSxTruthy((isSxTruthy(isNil(mediaPart)) && isNil(pseudoPart))) ? append_b(baseDecls, decls) : NIL);
|
|
||||||
})())))) : NIL);
|
|
||||||
})();
|
|
||||||
})();
|
|
||||||
})();
|
|
||||||
} } }
|
|
||||||
return (function() {
|
|
||||||
var hashInput = join(";", baseDecls);
|
|
||||||
{ var _c = mediaRules; for (var _i = 0; _i < _c.length; _i++) { var mr = _c[_i]; hashInput = (String(hashInput) + String("@") + String(first(mr)) + String("{") + String(nth(mr, 1)) + String("}")); } }
|
|
||||||
{ var _c = pseudoRules; for (var _i = 0; _i < _c.length; _i++) { var pr = _c[_i]; hashInput = (String(hashInput) + String(first(pr)) + String("{") + String(nth(pr, 1)) + String("}")); } }
|
|
||||||
{ var _c = kfNeeded; for (var _i = 0; _i < _c.length; _i++) { var kf = _c[_i]; hashInput = (String(hashInput) + String(nth(kf, 1))); } }
|
|
||||||
return (function() {
|
|
||||||
var cn = (String("sx-") + String(hashStyle(hashInput)));
|
|
||||||
var sv = makeStyleValue_(cn, join(";", baseDecls), mediaRules, pseudoRules, kfNeeded);
|
|
||||||
_styleCache[key] = sv;
|
|
||||||
injectStyleValue(sv, atoms);
|
|
||||||
return sv;
|
|
||||||
})();
|
|
||||||
})();
|
|
||||||
})());
|
|
||||||
})();
|
|
||||||
})(); };
|
|
||||||
|
|
||||||
// merge-style-values
|
|
||||||
var mergeStyleValues = function(styles) { return (isSxTruthy((len(styles) == 1)) ? first(styles) : (function() {
|
|
||||||
var allDecls = [];
|
|
||||||
var allMedia = [];
|
|
||||||
var allPseudo = [];
|
|
||||||
var allKf = [];
|
|
||||||
{ var _c = styles; for (var _i = 0; _i < _c.length; _i++) { var sv = _c[_i]; if (isSxTruthy(styleValueDeclarations(sv))) {
|
|
||||||
allDecls.push(styleValueDeclarations(sv));
|
|
||||||
}
|
|
||||||
allMedia = concat(allMedia, styleValueMediaRules(sv));
|
|
||||||
allPseudo = concat(allPseudo, styleValuePseudoRules(sv));
|
|
||||||
allKf = concat(allKf, styleValueKeyframes_(sv)); } }
|
|
||||||
return (function() {
|
|
||||||
var hashInput = join(";", allDecls);
|
|
||||||
{ var _c = allMedia; for (var _i = 0; _i < _c.length; _i++) { var mr = _c[_i]; hashInput = (String(hashInput) + String("@") + String(first(mr)) + String("{") + String(nth(mr, 1)) + String("}")); } }
|
|
||||||
{ var _c = allPseudo; for (var _i = 0; _i < _c.length; _i++) { var pr = _c[_i]; hashInput = (String(hashInput) + String(first(pr)) + String("{") + String(nth(pr, 1)) + String("}")); } }
|
|
||||||
{ var _c = allKf; for (var _i = 0; _i < _c.length; _i++) { var kf = _c[_i]; hashInput = (String(hashInput) + String(nth(kf, 1))); } }
|
|
||||||
return (function() {
|
|
||||||
var cn = (String("sx-") + String(hashStyle(hashInput)));
|
|
||||||
var merged = makeStyleValue_(cn, join(";", allDecls), allMedia, allPseudo, allKf);
|
|
||||||
injectStyleValue(merged, []);
|
|
||||||
return merged;
|
|
||||||
})();
|
|
||||||
})();
|
|
||||||
})()); };
|
|
||||||
|
|
||||||
|
|
||||||
// === Transpiled from boot ===
|
// === Transpiled from boot ===
|
||||||
|
|
||||||
// HEAD_HOIST_SELECTOR
|
// HEAD_HOIST_SELECTOR
|
||||||
@@ -2363,30 +2121,10 @@ callExpr.push(dictGet(kwargs, k)); } }
|
|||||||
})();
|
})();
|
||||||
return setSxCompCookie(hash);
|
return setSxCompCookie(hash);
|
||||||
})());
|
})());
|
||||||
})(); };
|
|
||||||
|
|
||||||
// init-style-dict
|
|
||||||
var initStyleDict = function() { return (function() {
|
|
||||||
var scripts = queryStyleScripts();
|
|
||||||
return forEach(function(s) { return (isSxTruthy(!isProcessed(s, "styles")) ? (markProcessed(s, "styles"), (function() {
|
|
||||||
var text = domTextContent(s);
|
|
||||||
var hash = domGetAttr(s, "data-hash");
|
|
||||||
return (isSxTruthy(isNil(hash)) ? (isSxTruthy((isSxTruthy(text) && !isEmpty(trim(text)))) ? parseAndLoadStyleDict(text) : NIL) : (function() {
|
|
||||||
var hasInline = (isSxTruthy(text) && !isEmpty(trim(text)));
|
|
||||||
(function() {
|
|
||||||
var cachedHash = localStorageGet("sx-styles-hash");
|
|
||||||
return (isSxTruthy((cachedHash == hash)) ? (isSxTruthy(hasInline) ? (localStorageSet("sx-styles-src", text), parseAndLoadStyleDict(text), logInfo("styles: downloaded (cookie stale)")) : (function() {
|
|
||||||
var cached = localStorageGet("sx-styles-src");
|
|
||||||
return (isSxTruthy(cached) ? (parseAndLoadStyleDict(cached), logInfo((String("styles: cached (") + String(hash) + String(")")))) : (clearSxStylesCookie(), browserReload()));
|
|
||||||
})()) : (isSxTruthy(hasInline) ? (localStorageSet("sx-styles-hash", hash), localStorageSet("sx-styles-src", text), parseAndLoadStyleDict(text), logInfo((String("styles: downloaded (") + String(hash) + String(")")))) : (localStorageRemove("sx-styles-hash"), localStorageRemove("sx-styles-src"), clearSxStylesCookie(), browserReload())));
|
|
||||||
})();
|
|
||||||
return setSxStylesCookie(hash);
|
|
||||||
})());
|
|
||||||
})()) : NIL); }, scripts);
|
|
||||||
})(); };
|
})(); };
|
||||||
|
|
||||||
// boot-init
|
// boot-init
|
||||||
var bootInit = function() { return (initCssTracking(), initStyleDict(), processSxScripts(NIL), sxHydrateElements(NIL), processElements(NIL)); };
|
var bootInit = function() { return (initCssTracking(), processSxScripts(NIL), sxHydrateElements(NIL), processElements(NIL)); };
|
||||||
|
|
||||||
|
|
||||||
// === Transpiled from deps (component dependency analysis) ===
|
// === Transpiled from deps (component dependency analysis) ===
|
||||||
@@ -3290,70 +3028,6 @@ callExpr.push(dictGet(kwargs, k)); } }
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeStyleValue_(cn, decls, media, pseudo, kf) {
|
|
||||||
return new StyleValue(cn, decls || "", media || [], pseudo || [], kf || []);
|
|
||||||
}
|
|
||||||
|
|
||||||
function styleValueDeclarations(sv) { return sv.declarations; }
|
|
||||||
function styleValueMediaRules(sv) { return sv.mediaRules; }
|
|
||||||
function styleValuePseudoRules(sv) { return sv.pseudoRules; }
|
|
||||||
function styleValueKeyframes_(sv) { return sv.keyframes; }
|
|
||||||
|
|
||||||
function injectStyleValue(sv, atoms) {
|
|
||||||
if (_injectedStyles[sv.className]) return;
|
|
||||||
_injectedStyles[sv.className] = true;
|
|
||||||
|
|
||||||
if (!_hasDom) return;
|
|
||||||
var cssTarget = document.getElementById("sx-css");
|
|
||||||
if (!cssTarget) return;
|
|
||||||
|
|
||||||
var rules = [];
|
|
||||||
// Child-selector atoms are now routed to pseudoRules by the resolver
|
|
||||||
// with selector ">:not(:first-child)", so base declarations are always
|
|
||||||
// applied directly to the class.
|
|
||||||
if (sv.declarations) {
|
|
||||||
rules.push("." + sv.className + "{" + sv.declarations + "}");
|
|
||||||
}
|
|
||||||
for (var pi = 0; pi < sv.pseudoRules.length; pi++) {
|
|
||||||
var sel = sv.pseudoRules[pi][0], decls = sv.pseudoRules[pi][1];
|
|
||||||
if (sel.indexOf("&") >= 0) {
|
|
||||||
rules.push(sel.replace(/&/g, "." + sv.className) + "{" + decls + "}");
|
|
||||||
} else {
|
|
||||||
rules.push("." + sv.className + sel + "{" + decls + "}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (var mi = 0; mi < sv.mediaRules.length; mi++) {
|
|
||||||
rules.push("@media " + sv.mediaRules[mi][0] + "{." + sv.className + "{" + sv.mediaRules[mi][1] + "}}");
|
|
||||||
}
|
|
||||||
for (var ki = 0; ki < sv.keyframes.length; ki++) {
|
|
||||||
rules.push(sv.keyframes[ki][1]);
|
|
||||||
}
|
|
||||||
cssTarget.textContent += rules.join("");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace stub css primitive with real CSSX implementation
|
|
||||||
PRIMITIVES["css"] = function() {
|
|
||||||
var atoms = [];
|
|
||||||
for (var i = 0; i < arguments.length; i++) {
|
|
||||||
var a = arguments[i];
|
|
||||||
if (isNil(a) || a === false) continue;
|
|
||||||
atoms.push(isKw(a) ? a.name : String(a));
|
|
||||||
}
|
|
||||||
if (!atoms.length) return NIL;
|
|
||||||
return resolveStyle(atoms);
|
|
||||||
};
|
|
||||||
|
|
||||||
PRIMITIVES["merge-styles"] = function() {
|
|
||||||
var valid = [];
|
|
||||||
for (var i = 0; i < arguments.length; i++) {
|
|
||||||
if (isStyleValue(arguments[i])) valid.push(arguments[i]);
|
|
||||||
}
|
|
||||||
if (!valid.length) return NIL;
|
|
||||||
if (valid.length === 1) return valid[0];
|
|
||||||
return mergeStyleValues(valid);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// Platform interface — Boot (mount, hydrate, scripts, cookies)
|
// Platform interface — Boot (mount, hydrate, scripts, cookies)
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
@@ -3410,12 +3084,6 @@ callExpr.push(dictGet(kwargs, k)); } }
|
|||||||
r.querySelectorAll('script[type="text/sx"]'));
|
r.querySelectorAll('script[type="text/sx"]'));
|
||||||
}
|
}
|
||||||
|
|
||||||
function queryStyleScripts() {
|
|
||||||
if (!_hasDom) return [];
|
|
||||||
return Array.prototype.slice.call(
|
|
||||||
document.querySelectorAll('script[type="text/sx-styles"]'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- localStorage ---
|
// --- localStorage ---
|
||||||
|
|
||||||
function localStorageGet(key) {
|
function localStorageGet(key) {
|
||||||
@@ -3441,14 +3109,6 @@ callExpr.push(dictGet(kwargs, k)); } }
|
|||||||
if (_hasDom) document.cookie = "sx-comp-hash=;path=/;max-age=0;SameSite=Lax";
|
if (_hasDom) document.cookie = "sx-comp-hash=;path=/;max-age=0;SameSite=Lax";
|
||||||
}
|
}
|
||||||
|
|
||||||
function setSxStylesCookie(hash) {
|
|
||||||
if (_hasDom) document.cookie = "sx-styles-hash=" + hash + ";path=/;max-age=31536000;SameSite=Lax";
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearSxStylesCookie() {
|
|
||||||
if (_hasDom) document.cookie = "sx-styles-hash=;path=/;max-age=0;SameSite=Lax";
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Env helpers ---
|
// --- Env helpers ---
|
||||||
|
|
||||||
function parseEnvAttr(el) {
|
function parseEnvAttr(el) {
|
||||||
@@ -3493,12 +3153,6 @@ callExpr.push(dictGet(kwargs, k)); } }
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseAndLoadStyleDict(text) {
|
|
||||||
try { loadStyleDict(JSON.parse(text)); }
|
|
||||||
catch (e) { if (typeof console !== "undefined") console.warn("[sx-ref] style dict parse error", e); }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// Post-transpilation fixups
|
// Post-transpilation fixups
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
@@ -3675,7 +3329,7 @@ callExpr.push(dictGet(kwargs, k)); } }
|
|||||||
transitiveIoRefs: transitiveIoRefs,
|
transitiveIoRefs: transitiveIoRefs,
|
||||||
computeAllIoRefs: computeAllIoRefs,
|
computeAllIoRefs: computeAllIoRefs,
|
||||||
componentPure_p: componentPure_p,
|
componentPure_p: componentPure_p,
|
||||||
_version: "ref-2.0 (boot+cssx+dom+engine+html+orchestration+parser+sx, bootstrap-compiled)"
|
_version: "ref-2.0 (boot+dom+engine+html+orchestration+parser+sx, bootstrap-compiled)"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -63,23 +63,12 @@
|
|||||||
function RawHTML(html) { this.html = html; }
|
function RawHTML(html) { this.html = html; }
|
||||||
RawHTML.prototype._raw = true;
|
RawHTML.prototype._raw = true;
|
||||||
|
|
||||||
/** CSSX StyleValue — generated CSS class with rules. */
|
|
||||||
function StyleValue(className, declarations, mediaRules, pseudoRules, keyframes) {
|
|
||||||
this.className = className;
|
|
||||||
this.declarations = declarations || "";
|
|
||||||
this.mediaRules = mediaRules || [];
|
|
||||||
this.pseudoRules = pseudoRules || [];
|
|
||||||
this.keyframes = keyframes || [];
|
|
||||||
}
|
|
||||||
StyleValue.prototype._styleValue = true;
|
|
||||||
|
|
||||||
function isSym(x) { return x && x._sym === true; }
|
function isSym(x) { return x && x._sym === true; }
|
||||||
function isKw(x) { return x && x._kw === true; }
|
function isKw(x) { return x && x._kw === true; }
|
||||||
function isLambda(x) { return x && x._lambda === true; }
|
function isLambda(x) { return x && x._lambda === true; }
|
||||||
function isComponent(x) { return x && x._component === true; }
|
function isComponent(x) { return x && x._component === true; }
|
||||||
function isMacro(x) { return x && x._macro === true; }
|
function isMacro(x) { return x && x._macro === true; }
|
||||||
function isRaw(x) { return x && x._raw === true; }
|
function isRaw(x) { return x && x._raw === true; }
|
||||||
function isStyleValue(x) { return x && x._styleValue === true; }
|
|
||||||
|
|
||||||
// --- Parser ---
|
// --- Parser ---
|
||||||
|
|
||||||
@@ -416,227 +405,6 @@
|
|||||||
return String(s).split(",").map(function(x) { return x.trim(); }).filter(function(x) { return x; });
|
return String(s).split(",").map(function(x) { return x.trim(); }).filter(function(x) { return x; });
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- CSSX Style Dictionary + Resolver ---
|
|
||||||
|
|
||||||
var _styleAtoms = {}; // atom → CSS declarations
|
|
||||||
var _pseudoVariants = {}; // variant → CSS pseudo-selector
|
|
||||||
var _responsiveBreakpoints = {}; // variant → media query
|
|
||||||
var _styleKeyframes = {}; // name → @keyframes rule
|
|
||||||
var _arbitraryPatterns = []; // [{re: RegExp, tmpl: string}, ...]
|
|
||||||
var _childSelectorPrefixes = []; // ["space-x-", "space-y-", ...]
|
|
||||||
var _styleCache = {}; // atoms-key → StyleValue
|
|
||||||
var _injectedStyles = {}; // className → true (already in <style>)
|
|
||||||
|
|
||||||
function _loadStyleDict(data) {
|
|
||||||
_styleAtoms = data.a || {};
|
|
||||||
_pseudoVariants = data.v || {};
|
|
||||||
_responsiveBreakpoints = data.b || {};
|
|
||||||
_styleKeyframes = data.k || {};
|
|
||||||
_childSelectorPrefixes = data.c || [];
|
|
||||||
_arbitraryPatterns = [];
|
|
||||||
var pats = data.p || [];
|
|
||||||
for (var i = 0; i < pats.length; i++) {
|
|
||||||
_arbitraryPatterns.push({ re: new RegExp("^" + pats[i][0] + "$"), tmpl: pats[i][1] });
|
|
||||||
}
|
|
||||||
_styleCache = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
function _splitVariant(atom) {
|
|
||||||
// Check responsive prefix first
|
|
||||||
for (var bp in _responsiveBreakpoints) {
|
|
||||||
var prefix = bp + ":";
|
|
||||||
if (atom.indexOf(prefix) === 0) {
|
|
||||||
var rest = atom.substring(prefix.length);
|
|
||||||
for (var pv in _pseudoVariants) {
|
|
||||||
var inner = pv + ":";
|
|
||||||
if (rest.indexOf(inner) === 0) return [bp + ":" + pv, rest.substring(inner.length)];
|
|
||||||
}
|
|
||||||
return [bp, rest];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (var pv2 in _pseudoVariants) {
|
|
||||||
var prefix2 = pv2 + ":";
|
|
||||||
if (atom.indexOf(prefix2) === 0) return [pv2, atom.substring(prefix2.length)];
|
|
||||||
}
|
|
||||||
return [null, atom];
|
|
||||||
}
|
|
||||||
|
|
||||||
function _resolveAtom(atom) {
|
|
||||||
var decls = _styleAtoms[atom];
|
|
||||||
if (decls !== undefined) return decls;
|
|
||||||
// Dynamic keyframes: animate-{name} → animation-name:{name}
|
|
||||||
if (atom.indexOf("animate-") === 0) {
|
|
||||||
var kfName = atom.substring(8);
|
|
||||||
if (_styleKeyframes[kfName]) return "animation-name:" + kfName;
|
|
||||||
}
|
|
||||||
for (var i = 0; i < _arbitraryPatterns.length; i++) {
|
|
||||||
var m = atom.match(_arbitraryPatterns[i].re);
|
|
||||||
if (m) {
|
|
||||||
var result = _arbitraryPatterns[i].tmpl;
|
|
||||||
for (var j = 1; j < m.length; j++) result = result.replace("{" + (j - 1) + "}", m[j]);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _isChildSelectorAtom(atom) {
|
|
||||||
for (var i = 0; i < _childSelectorPrefixes.length; i++) {
|
|
||||||
if (atom.indexOf(_childSelectorPrefixes[i]) === 0) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** SHA-256 hash (first 6 hex chars) for content-addressed class names. */
|
|
||||||
function _hashStyle(input) {
|
|
||||||
// Simple FNV-1a 32-bit hash — fast, deterministic, good distribution
|
|
||||||
var h = 0x811c9dc5;
|
|
||||||
for (var i = 0; i < input.length; i++) {
|
|
||||||
h ^= input.charCodeAt(i);
|
|
||||||
h = (h * 0x01000193) >>> 0;
|
|
||||||
}
|
|
||||||
return h.toString(16).padStart(8, "0").substring(0, 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _resolveStyle(atoms) {
|
|
||||||
var key = atoms.join("\0");
|
|
||||||
if (_styleCache[key]) return _styleCache[key];
|
|
||||||
|
|
||||||
var baseDecls = [], mediaRules = [], pseudoRules = [], kfNeeded = [];
|
|
||||||
for (var i = 0; i < atoms.length; i++) {
|
|
||||||
var a = atoms[i];
|
|
||||||
if (!a) continue;
|
|
||||||
if (a.charAt(0) === ":") a = a.substring(1);
|
|
||||||
|
|
||||||
var parts = _splitVariant(a);
|
|
||||||
var variant = parts[0], base = parts[1];
|
|
||||||
var decls = _resolveAtom(base);
|
|
||||||
if (!decls) continue;
|
|
||||||
|
|
||||||
// Check keyframes
|
|
||||||
if (base.indexOf("animate-") === 0) {
|
|
||||||
var kfName = base.substring(8);
|
|
||||||
if (_styleKeyframes[kfName]) kfNeeded.push([kfName, _styleKeyframes[kfName]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (variant === null) {
|
|
||||||
baseDecls.push(decls);
|
|
||||||
} else if (_responsiveBreakpoints[variant]) {
|
|
||||||
mediaRules.push([_responsiveBreakpoints[variant], decls]);
|
|
||||||
} else if (_pseudoVariants[variant]) {
|
|
||||||
pseudoRules.push([_pseudoVariants[variant], decls]);
|
|
||||||
} else {
|
|
||||||
// Compound variant: "sm:hover" → split
|
|
||||||
var vparts = variant.split(":");
|
|
||||||
var mediaPart = null, pseudoPart = null;
|
|
||||||
for (var vi = 0; vi < vparts.length; vi++) {
|
|
||||||
if (_responsiveBreakpoints[vparts[vi]]) mediaPart = _responsiveBreakpoints[vparts[vi]];
|
|
||||||
else if (_pseudoVariants[vparts[vi]]) pseudoPart = _pseudoVariants[vparts[vi]];
|
|
||||||
}
|
|
||||||
if (mediaPart) mediaRules.push([mediaPart, decls]);
|
|
||||||
if (pseudoPart) pseudoRules.push([pseudoPart, decls]);
|
|
||||||
if (!mediaPart && !pseudoPart) baseDecls.push(decls);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build hash input
|
|
||||||
var hashInput = baseDecls.join(";");
|
|
||||||
for (var mi = 0; mi < mediaRules.length; mi++) hashInput += "@" + mediaRules[mi][0] + "{" + mediaRules[mi][1] + "}";
|
|
||||||
for (var pi = 0; pi < pseudoRules.length; pi++) hashInput += pseudoRules[pi][0] + "{" + pseudoRules[pi][1] + "}";
|
|
||||||
for (var ki = 0; ki < kfNeeded.length; ki++) hashInput += kfNeeded[ki][1];
|
|
||||||
|
|
||||||
var cn = "sx-" + _hashStyle(hashInput);
|
|
||||||
var sv = new StyleValue(cn, baseDecls.join(";"), mediaRules, pseudoRules, kfNeeded);
|
|
||||||
_styleCache[key] = sv;
|
|
||||||
|
|
||||||
// Inject CSS rules into <style id="sx-css">
|
|
||||||
_injectStyleValue(sv, atoms);
|
|
||||||
return sv;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _injectStyleValue(sv, atoms) {
|
|
||||||
if (_injectedStyles[sv.className]) return;
|
|
||||||
_injectedStyles[sv.className] = true;
|
|
||||||
|
|
||||||
var cssTarget = document.getElementById("sx-css");
|
|
||||||
if (!cssTarget) return;
|
|
||||||
|
|
||||||
var rules = [];
|
|
||||||
if (sv.declarations) {
|
|
||||||
// Check if any atoms need child selectors
|
|
||||||
var hasChild = false;
|
|
||||||
if (atoms) {
|
|
||||||
for (var ai = 0; ai < atoms.length; ai++) {
|
|
||||||
if (_isChildSelectorAtom(atoms[ai])) { hasChild = true; break; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hasChild) {
|
|
||||||
rules.push("." + sv.className + ">:not(:first-child){" + sv.declarations + "}");
|
|
||||||
} else {
|
|
||||||
rules.push("." + sv.className + "{" + sv.declarations + "}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (var pi = 0; pi < sv.pseudoRules.length; pi++) {
|
|
||||||
var sel = sv.pseudoRules[pi][0], decls = sv.pseudoRules[pi][1];
|
|
||||||
if (sel.indexOf("&") >= 0) {
|
|
||||||
rules.push(sel.replace(/&/g, "." + sv.className) + "{" + decls + "}");
|
|
||||||
} else {
|
|
||||||
rules.push("." + sv.className + sel + "{" + decls + "}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (var mi = 0; mi < sv.mediaRules.length; mi++) {
|
|
||||||
rules.push("@media " + sv.mediaRules[mi][0] + "{." + sv.className + "{" + sv.mediaRules[mi][1] + "}}");
|
|
||||||
}
|
|
||||||
for (var ki = 0; ki < sv.keyframes.length; ki++) {
|
|
||||||
rules.push(sv.keyframes[ki][1]);
|
|
||||||
}
|
|
||||||
cssTarget.textContent += rules.join("");
|
|
||||||
}
|
|
||||||
|
|
||||||
function _mergeStyleValues(styles) {
|
|
||||||
if (styles.length === 1) return styles[0];
|
|
||||||
var allDecls = [], allMedia = [], allPseudo = [], allKf = [];
|
|
||||||
var allAtoms = [];
|
|
||||||
for (var i = 0; i < styles.length; i++) {
|
|
||||||
var sv = styles[i];
|
|
||||||
if (sv.declarations) allDecls.push(sv.declarations);
|
|
||||||
allMedia = allMedia.concat(sv.mediaRules);
|
|
||||||
allPseudo = allPseudo.concat(sv.pseudoRules);
|
|
||||||
allKf = allKf.concat(sv.keyframes);
|
|
||||||
}
|
|
||||||
var hashInput = allDecls.join(";");
|
|
||||||
for (var mi = 0; mi < allMedia.length; mi++) hashInput += "@" + allMedia[mi][0] + "{" + allMedia[mi][1] + "}";
|
|
||||||
for (var pi = 0; pi < allPseudo.length; pi++) hashInput += allPseudo[pi][0] + "{" + allPseudo[pi][1] + "}";
|
|
||||||
for (var ki = 0; ki < allKf.length; ki++) hashInput += allKf[ki][1];
|
|
||||||
var cn = "sx-" + _hashStyle(hashInput);
|
|
||||||
var merged = new StyleValue(cn, allDecls.join(";"), allMedia, allPseudo, allKf);
|
|
||||||
_injectStyleValue(merged, allAtoms);
|
|
||||||
return merged;
|
|
||||||
}
|
|
||||||
|
|
||||||
// css primitive: (css :flex :gap-4 :hover:bg-sky-200) → StyleValue
|
|
||||||
PRIMITIVES["css"] = function () {
|
|
||||||
var atoms = [];
|
|
||||||
for (var i = 0; i < arguments.length; i++) {
|
|
||||||
var a = arguments[i];
|
|
||||||
if (isNil(a) || a === false) continue;
|
|
||||||
atoms.push(isKw(a) ? a.name : String(a));
|
|
||||||
}
|
|
||||||
if (!atoms.length) return NIL;
|
|
||||||
return _resolveStyle(atoms);
|
|
||||||
};
|
|
||||||
|
|
||||||
// merge-styles: combine multiple StyleValues
|
|
||||||
PRIMITIVES["merge-styles"] = function () {
|
|
||||||
var valid = [];
|
|
||||||
for (var i = 0; i < arguments.length; i++) {
|
|
||||||
if (isStyleValue(arguments[i])) valid.push(arguments[i]);
|
|
||||||
}
|
|
||||||
if (!valid.length) return NIL;
|
|
||||||
if (valid.length === 1) return valid[0];
|
|
||||||
return _mergeStyleValues(valid);
|
|
||||||
};
|
|
||||||
|
|
||||||
// --- Evaluator ---
|
// --- Evaluator ---
|
||||||
|
|
||||||
/** Unwrap thunks by re-entering the evaluator until we get an actual value. */
|
/** Unwrap thunks by re-entering the evaluator until we get an actual value. */
|
||||||
@@ -892,30 +660,6 @@
|
|||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
|
|
||||||
SPECIAL_FORMS["defkeyframes"] = function (expr, env) {
|
|
||||||
var kfName = expr[1].name;
|
|
||||||
var steps = [];
|
|
||||||
for (var i = 2; i < expr.length; i++) {
|
|
||||||
var step = expr[i];
|
|
||||||
var selector = isSym(step[0]) ? step[0].name : String(step[0]);
|
|
||||||
var body = sxEval(step[1], env);
|
|
||||||
var decls = isStyleValue(body) ? body.declarations : String(body);
|
|
||||||
steps.push(selector + "{" + decls + "}");
|
|
||||||
}
|
|
||||||
var kfRule = "@keyframes " + kfName + "{" + steps.join("") + "}";
|
|
||||||
|
|
||||||
// Register in keyframes dict for animate-{name} lookup
|
|
||||||
_styleKeyframes[kfName] = kfRule;
|
|
||||||
_styleCache = {}; // Clear cache so new keyframes are picked up
|
|
||||||
|
|
||||||
// Build a StyleValue for the animation
|
|
||||||
var cn = "sx-" + _hashStyle(kfRule);
|
|
||||||
var sv = new StyleValue(cn, "animation-name:" + kfName, [], [], [[kfName, kfRule]]);
|
|
||||||
_injectStyleValue(sv, []);
|
|
||||||
env[kfName] = sv;
|
|
||||||
return sv;
|
|
||||||
};
|
|
||||||
|
|
||||||
SPECIAL_FORMS["defcomp"] = function (expr, env) {
|
SPECIAL_FORMS["defcomp"] = function (expr, env) {
|
||||||
var nameSym = expr[1];
|
var nameSym = expr[1];
|
||||||
var compName = nameSym.name.replace(/^~/, "");
|
var compName = nameSym.name.replace(/^~/, "");
|
||||||
@@ -1209,7 +953,6 @@
|
|||||||
|
|
||||||
RENDER_FORMS["define"] = function (expr, env) { sxEval(expr, env); return document.createDocumentFragment(); };
|
RENDER_FORMS["define"] = function (expr, env) { sxEval(expr, env); return document.createDocumentFragment(); };
|
||||||
RENDER_FORMS["defstyle"] = function (expr, env) { sxEval(expr, env); return document.createDocumentFragment(); };
|
RENDER_FORMS["defstyle"] = function (expr, env) { sxEval(expr, env); return document.createDocumentFragment(); };
|
||||||
RENDER_FORMS["defkeyframes"] = function (expr, env) { sxEval(expr, env); return document.createDocumentFragment(); };
|
|
||||||
RENDER_FORMS["defcomp"] = function (expr, env) { sxEval(expr, env); return document.createDocumentFragment(); };
|
RENDER_FORMS["defcomp"] = function (expr, env) { sxEval(expr, env); return document.createDocumentFragment(); };
|
||||||
RENDER_FORMS["defmacro"] = function (expr, env) { sxEval(expr, env); return document.createDocumentFragment(); };
|
RENDER_FORMS["defmacro"] = function (expr, env) { sxEval(expr, env); return document.createDocumentFragment(); };
|
||||||
RENDER_FORMS["defhandler"] = function (expr, env) { sxEval(expr, env); return document.createDocumentFragment(); };
|
RENDER_FORMS["defhandler"] = function (expr, env) { sxEval(expr, env); return document.createDocumentFragment(); };
|
||||||
@@ -1413,7 +1156,6 @@
|
|||||||
? document.createElementNS(ns, tag)
|
? document.createElementNS(ns, tag)
|
||||||
: (SVG_TAGS[tag] ? document.createElementNS(SVG_NS, tag) : document.createElement(tag));
|
: (SVG_TAGS[tag] ? document.createElementNS(SVG_NS, tag) : document.createElement(tag));
|
||||||
|
|
||||||
var extraClass = null;
|
|
||||||
var i = 0;
|
var i = 0;
|
||||||
while (i < args.length) {
|
while (i < args.length) {
|
||||||
var arg = args[i];
|
var arg = args[i];
|
||||||
@@ -1422,11 +1164,6 @@
|
|||||||
var attrVal = sxEval(args[i + 1], env);
|
var attrVal = sxEval(args[i + 1], env);
|
||||||
i += 2;
|
i += 2;
|
||||||
if (isNil(attrVal) || attrVal === false) continue;
|
if (isNil(attrVal) || attrVal === false) continue;
|
||||||
// :style StyleValue → convert to class
|
|
||||||
if (attrName === "style" && isStyleValue(attrVal)) {
|
|
||||||
extraClass = attrVal.className;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (BOOLEAN_ATTRS[attrName]) {
|
if (BOOLEAN_ATTRS[attrName]) {
|
||||||
if (attrVal) el.setAttribute(attrName, "");
|
if (attrVal) el.setAttribute(attrName, "");
|
||||||
} else if (attrVal === true) {
|
} else if (attrVal === true) {
|
||||||
@@ -1443,12 +1180,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge StyleValue class into element's class attribute
|
|
||||||
if (extraClass) {
|
|
||||||
var existing = el.getAttribute("class");
|
|
||||||
el.setAttribute("class", existing ? existing + " " + extraClass : extraClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
return el;
|
return el;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1770,7 +1501,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
// For testing / sx-test.js
|
// For testing / sx-test.js
|
||||||
_types: { NIL: NIL, Symbol: Symbol, Keyword: Keyword, Lambda: Lambda, Component: Component, RawHTML: RawHTML, StyleValue: StyleValue },
|
_types: { NIL: NIL, Symbol: Symbol, Keyword: Keyword, Lambda: Lambda, Component: Component, RawHTML: RawHTML },
|
||||||
_eval: sxEval,
|
_eval: sxEval,
|
||||||
_expandMacro: expandMacro,
|
_expandMacro: expandMacro,
|
||||||
_callLambda: function (fn, args, env) { return trampoline(callLambda(fn, args, env)); },
|
_callLambda: function (fn, args, env) { return trampoline(callLambda(fn, args, env)); },
|
||||||
@@ -3116,79 +2847,10 @@
|
|||||||
document.cookie = "sx-comp-hash=;path=/;max-age=0;SameSite=Lax";
|
document.cookie = "sx-comp-hash=;path=/;max-age=0;SameSite=Lax";
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- sx-styles-hash cookie helpers ---
|
|
||||||
|
|
||||||
function _setSxStylesCookie(hash) {
|
|
||||||
document.cookie = "sx-styles-hash=" + hash + ";path=/;max-age=31536000;SameSite=Lax";
|
|
||||||
}
|
|
||||||
|
|
||||||
function _clearSxStylesCookie() {
|
|
||||||
document.cookie = "sx-styles-hash=;path=/;max-age=0;SameSite=Lax";
|
|
||||||
}
|
|
||||||
|
|
||||||
function _initStyleDict() {
|
|
||||||
var scripts = document.querySelectorAll('script[type="text/sx-styles"]');
|
|
||||||
for (var i = 0; i < scripts.length; i++) {
|
|
||||||
var s = scripts[i];
|
|
||||||
if (s._sxProcessed) continue;
|
|
||||||
s._sxProcessed = true;
|
|
||||||
|
|
||||||
var text = s.textContent;
|
|
||||||
var hash = s.getAttribute("data-hash");
|
|
||||||
if (!hash) {
|
|
||||||
if (text && text.trim()) {
|
|
||||||
try { _loadStyleDict(JSON.parse(text)); } catch (e) { console.warn("[sx.js] style dict parse error", e); }
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var hasInline = text && text.trim();
|
|
||||||
try {
|
|
||||||
var cachedHash = localStorage.getItem("sx-styles-hash");
|
|
||||||
if (cachedHash === hash) {
|
|
||||||
if (hasInline) {
|
|
||||||
localStorage.setItem("sx-styles-src", text);
|
|
||||||
_loadStyleDict(JSON.parse(text));
|
|
||||||
console.log("[sx.js] styles: downloaded (cookie stale)");
|
|
||||||
} else {
|
|
||||||
var cached = localStorage.getItem("sx-styles-src");
|
|
||||||
if (cached) {
|
|
||||||
_loadStyleDict(JSON.parse(cached));
|
|
||||||
console.log("[sx.js] styles: cached (" + hash + ")");
|
|
||||||
} else {
|
|
||||||
_clearSxStylesCookie();
|
|
||||||
location.reload();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (hasInline) {
|
|
||||||
localStorage.setItem("sx-styles-hash", hash);
|
|
||||||
localStorage.setItem("sx-styles-src", text);
|
|
||||||
_loadStyleDict(JSON.parse(text));
|
|
||||||
console.log("[sx.js] styles: downloaded (" + hash + ")");
|
|
||||||
} else {
|
|
||||||
localStorage.removeItem("sx-styles-hash");
|
|
||||||
localStorage.removeItem("sx-styles-src");
|
|
||||||
_clearSxStylesCookie();
|
|
||||||
location.reload();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
if (hasInline) {
|
|
||||||
try { _loadStyleDict(JSON.parse(text)); } catch (e2) { console.warn("[sx.js] style dict parse error", e2); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_setSxStylesCookie(hash);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof document !== "undefined") {
|
if (typeof document !== "undefined") {
|
||||||
var init = function () {
|
var init = function () {
|
||||||
console.log("[sx.js] v" + Sx.VERSION + " init");
|
console.log("[sx.js] v" + Sx.VERSION + " init");
|
||||||
_initCssTracking();
|
_initCssTracking();
|
||||||
_initStyleDict();
|
|
||||||
Sx.processScripts();
|
Sx.processScripts();
|
||||||
Sx.hydrate();
|
Sx.hydrate();
|
||||||
SxEngine.process();
|
SxEngine.process();
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ import contextvars
|
|||||||
import inspect
|
import inspect
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from .types import Component, Keyword, Lambda, Macro, NIL, StyleValue, Symbol
|
from .types import Component, Keyword, Lambda, Macro, NIL, Symbol
|
||||||
|
|
||||||
# When True, _aser expands known components server-side instead of serializing
|
# When True, _aser expands known components server-side instead of serializing
|
||||||
# them for client rendering. Set during page slot evaluation so Python-only
|
# them for client rendering. Set during page slot evaluation so Python-only
|
||||||
@@ -425,11 +425,6 @@ async def _asf_defstyle(expr, env, ctx):
|
|||||||
return _sf_defstyle(expr, env)
|
return _sf_defstyle(expr, env)
|
||||||
|
|
||||||
|
|
||||||
async def _asf_defkeyframes(expr, env, ctx):
|
|
||||||
from .evaluator import _sf_defkeyframes
|
|
||||||
return _sf_defkeyframes(expr, env)
|
|
||||||
|
|
||||||
|
|
||||||
async def _asf_defmacro(expr, env, ctx):
|
async def _asf_defmacro(expr, env, ctx):
|
||||||
from .evaluator import _sf_defmacro
|
from .evaluator import _sf_defmacro
|
||||||
return _sf_defmacro(expr, env)
|
return _sf_defmacro(expr, env)
|
||||||
@@ -568,7 +563,6 @@ _ASYNC_SPECIAL_FORMS: dict[str, Any] = {
|
|||||||
"fn": _asf_lambda,
|
"fn": _asf_lambda,
|
||||||
"define": _asf_define,
|
"define": _asf_define,
|
||||||
"defstyle": _asf_defstyle,
|
"defstyle": _asf_defstyle,
|
||||||
"defkeyframes": _asf_defkeyframes,
|
|
||||||
"defcomp": _asf_defcomp,
|
"defcomp": _asf_defcomp,
|
||||||
"defmacro": _asf_defmacro,
|
"defmacro": _asf_defmacro,
|
||||||
"defhandler": _asf_defhandler,
|
"defhandler": _asf_defhandler,
|
||||||
@@ -884,18 +878,6 @@ async def _arender_element(
|
|||||||
children.append(arg)
|
children.append(arg)
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
# Handle :style StyleValue — convert to class and register CSS rule
|
|
||||||
style_val = attrs.get("style")
|
|
||||||
if isinstance(style_val, StyleValue):
|
|
||||||
from .css_registry import register_generated_rule
|
|
||||||
register_generated_rule(style_val)
|
|
||||||
existing_class = attrs.get("class")
|
|
||||||
if existing_class and existing_class is not NIL and existing_class is not False:
|
|
||||||
attrs["class"] = f"{existing_class} {style_val.class_name}"
|
|
||||||
else:
|
|
||||||
attrs["class"] = style_val.class_name
|
|
||||||
del attrs["style"]
|
|
||||||
|
|
||||||
class_val = attrs.get("class")
|
class_val = attrs.get("class")
|
||||||
if class_val is not None and class_val is not NIL and class_val is not False:
|
if class_val is not None and class_val is not NIL and class_val is not False:
|
||||||
collector = css_class_collector.get(None)
|
collector = css_class_collector.get(None)
|
||||||
@@ -1120,7 +1102,6 @@ _ASYNC_RENDER_FORMS: dict[str, Any] = {
|
|||||||
"do": _arsf_begin,
|
"do": _arsf_begin,
|
||||||
"define": _arsf_define,
|
"define": _arsf_define,
|
||||||
"defstyle": _arsf_define,
|
"defstyle": _arsf_define,
|
||||||
"defkeyframes": _arsf_define,
|
|
||||||
"defcomp": _arsf_define,
|
"defcomp": _arsf_define,
|
||||||
"defmacro": _arsf_define,
|
"defmacro": _arsf_define,
|
||||||
"defhandler": _arsf_define,
|
"defhandler": _arsf_define,
|
||||||
@@ -1441,19 +1422,13 @@ async def _aser_call(
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
parts = [name]
|
parts = [name]
|
||||||
extra_class: str | None = None # from :style StyleValue conversion
|
extra_class: str | None = None
|
||||||
i = 0
|
i = 0
|
||||||
while i < len(args):
|
while i < len(args):
|
||||||
arg = args[i]
|
arg = args[i]
|
||||||
if isinstance(arg, Keyword) and i + 1 < len(args):
|
if isinstance(arg, Keyword) and i + 1 < len(args):
|
||||||
val = await _aser(args[i + 1], env, ctx)
|
val = await _aser(args[i + 1], env, ctx)
|
||||||
if val is not NIL and val is not None:
|
if val is not NIL and val is not None:
|
||||||
# :style StyleValue → convert to :class and register CSS
|
|
||||||
if arg.name == "style" and isinstance(val, StyleValue):
|
|
||||||
from .css_registry import register_generated_rule
|
|
||||||
register_generated_rule(val)
|
|
||||||
extra_class = val.class_name
|
|
||||||
else:
|
|
||||||
parts.append(f":{arg.name}")
|
parts.append(f":{arg.name}")
|
||||||
# Plain list → serialize for the client.
|
# Plain list → serialize for the client.
|
||||||
# Rendered items (SxExpr) → wrap in (<> ...) fragment.
|
# Rendered items (SxExpr) → wrap in (<> ...) fragment.
|
||||||
@@ -1725,7 +1700,6 @@ _ASER_FORMS: dict[str, Any] = {
|
|||||||
"fn": _assf_lambda,
|
"fn": _assf_lambda,
|
||||||
"define": _assf_define,
|
"define": _assf_define,
|
||||||
"defstyle": _assf_define,
|
"defstyle": _assf_define,
|
||||||
"defkeyframes": _assf_define,
|
|
||||||
"defcomp": _assf_define,
|
"defcomp": _assf_define,
|
||||||
"defmacro": _assf_define,
|
"defmacro": _assf_define,
|
||||||
"defhandler": _assf_define,
|
"defhandler": _assf_define,
|
||||||
|
|||||||
@@ -94,10 +94,10 @@ def validate_helper(service: str, name: str) -> None:
|
|||||||
def validate_boundary_value(value: Any, context: str = "") -> None:
|
def validate_boundary_value(value: Any, context: str = "") -> None:
|
||||||
"""Validate that a value is an allowed SX boundary type.
|
"""Validate that a value is an allowed SX boundary type.
|
||||||
|
|
||||||
Allowed: int, float, str, bool, None/NIL, list, dict, SxExpr, StyleValue.
|
Allowed: int, float, str, bool, None/NIL, list, dict, SxExpr.
|
||||||
NOT allowed: datetime, ORM models, Quart objects, raw callables.
|
NOT allowed: datetime, ORM models, Quart objects, raw callables.
|
||||||
"""
|
"""
|
||||||
from .types import NIL, StyleValue
|
from .types import NIL
|
||||||
from .parser import SxExpr
|
from .parser import SxExpr
|
||||||
|
|
||||||
if value is None or value is NIL:
|
if value is None or value is NIL:
|
||||||
@@ -106,8 +106,6 @@ def validate_boundary_value(value: Any, context: str = "") -> None:
|
|||||||
return
|
return
|
||||||
if isinstance(value, SxExpr):
|
if isinstance(value, SxExpr):
|
||||||
return
|
return
|
||||||
if isinstance(value, StyleValue):
|
|
||||||
return
|
|
||||||
if isinstance(value, list):
|
if isinstance(value, list):
|
||||||
for item in value:
|
for item in value:
|
||||||
validate_boundary_value(item, context)
|
validate_boundary_value(item, context)
|
||||||
|
|||||||
@@ -147,48 +147,6 @@ def scan_classes_from_sx(source: str) -> set[str]:
|
|||||||
return classes
|
return classes
|
||||||
|
|
||||||
|
|
||||||
def register_generated_rule(style_val: Any) -> None:
|
|
||||||
"""Register a generated StyleValue's CSS rules in the registry.
|
|
||||||
|
|
||||||
This allows generated class names (``sx-a3f2c1``) to flow through
|
|
||||||
the existing ``lookup_rules()`` → ``SX-Css`` delta pipeline.
|
|
||||||
"""
|
|
||||||
from .style_dict import CHILD_SELECTOR_ATOMS
|
|
||||||
cn = style_val.class_name
|
|
||||||
if cn in _REGISTRY:
|
|
||||||
return # already registered
|
|
||||||
|
|
||||||
parts: list[str] = []
|
|
||||||
|
|
||||||
# Base declarations
|
|
||||||
if style_val.declarations:
|
|
||||||
parts.append(f".{cn}{{{style_val.declarations}}}")
|
|
||||||
|
|
||||||
# Pseudo-class rules
|
|
||||||
for sel, decls in style_val.pseudo_rules:
|
|
||||||
if sel.startswith("::"):
|
|
||||||
parts.append(f".{cn}{sel}{{{decls}}}")
|
|
||||||
elif "&" in sel:
|
|
||||||
# group-hover pattern: ":is(.group:hover) &" → .group:hover .sx-abc
|
|
||||||
expanded = sel.replace("&", f".{cn}")
|
|
||||||
parts.append(f"{expanded}{{{decls}}}")
|
|
||||||
else:
|
|
||||||
parts.append(f".{cn}{sel}{{{decls}}}")
|
|
||||||
|
|
||||||
# Media-query rules
|
|
||||||
for query, decls in style_val.media_rules:
|
|
||||||
parts.append(f"@media {query}{{.{cn}{{{decls}}}}}")
|
|
||||||
|
|
||||||
# Keyframes
|
|
||||||
for _name, kf_rule in style_val.keyframes:
|
|
||||||
parts.append(kf_rule)
|
|
||||||
|
|
||||||
rule_text = "".join(parts)
|
|
||||||
order = len(_RULE_ORDER) + 10000 # after all tw.css rules
|
|
||||||
_REGISTRY[cn] = rule_text
|
|
||||||
_RULE_ORDER[cn] = order
|
|
||||||
|
|
||||||
|
|
||||||
def registry_loaded() -> bool:
|
def registry_loaded() -> bool:
|
||||||
"""True if the registry has been populated."""
|
"""True if the registry has been populated."""
|
||||||
return bool(_REGISTRY)
|
return bool(_REGISTRY)
|
||||||
|
|||||||
@@ -493,9 +493,9 @@ def _sf_define(expr: list, env: dict) -> Any:
|
|||||||
|
|
||||||
|
|
||||||
def _sf_defstyle(expr: list, env: dict) -> Any:
|
def _sf_defstyle(expr: list, env: dict) -> Any:
|
||||||
"""``(defstyle card-base (css :rounded-xl :bg-white :shadow))``
|
"""``(defstyle card-base ...)``
|
||||||
|
|
||||||
Evaluates body → StyleValue, binds to name in env.
|
Evaluates body and binds to name in env.
|
||||||
"""
|
"""
|
||||||
if len(expr) < 3:
|
if len(expr) < 3:
|
||||||
raise EvalError("defstyle requires name and body")
|
raise EvalError("defstyle requires name and body")
|
||||||
@@ -507,63 +507,6 @@ def _sf_defstyle(expr: list, env: dict) -> Any:
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
def _sf_defkeyframes(expr: list, env: dict) -> Any:
|
|
||||||
"""``(defkeyframes fade-in (from (css :opacity-0)) (to (css :opacity-100)))``
|
|
||||||
|
|
||||||
Builds @keyframes rule from steps, registers it, and binds the animation.
|
|
||||||
"""
|
|
||||||
from .types import StyleValue
|
|
||||||
from .css_registry import register_generated_rule
|
|
||||||
from .style_dict import KEYFRAMES
|
|
||||||
|
|
||||||
if len(expr) < 3:
|
|
||||||
raise EvalError("defkeyframes requires name and at least one step")
|
|
||||||
name_sym = expr[1]
|
|
||||||
if not isinstance(name_sym, Symbol):
|
|
||||||
raise EvalError(f"defkeyframes name must be symbol, got {type(name_sym).__name__}")
|
|
||||||
|
|
||||||
kf_name = name_sym.name
|
|
||||||
|
|
||||||
# Build @keyframes rule from steps
|
|
||||||
steps: list[str] = []
|
|
||||||
for step_expr in expr[2:]:
|
|
||||||
if not isinstance(step_expr, list) or len(step_expr) < 2:
|
|
||||||
raise EvalError("defkeyframes step must be (selector (css ...))")
|
|
||||||
selector = step_expr[0]
|
|
||||||
if isinstance(selector, Symbol):
|
|
||||||
selector = selector.name
|
|
||||||
else:
|
|
||||||
selector = str(selector)
|
|
||||||
body = _trampoline(_eval(step_expr[1], env))
|
|
||||||
if isinstance(body, StyleValue):
|
|
||||||
decls = body.declarations
|
|
||||||
elif isinstance(body, str):
|
|
||||||
decls = body
|
|
||||||
else:
|
|
||||||
raise EvalError(f"defkeyframes step body must be css/string, got {type(body).__name__}")
|
|
||||||
steps.append(f"{selector}{{{decls}}}")
|
|
||||||
|
|
||||||
kf_rule = f"@keyframes {kf_name}{{{' '.join(steps)}}}"
|
|
||||||
|
|
||||||
# Register in KEYFRAMES so animate-{name} works
|
|
||||||
KEYFRAMES[kf_name] = kf_rule
|
|
||||||
# Clear resolver cache so new keyframes are picked up
|
|
||||||
from .style_resolver import _resolve_cached
|
|
||||||
_resolve_cached.cache_clear()
|
|
||||||
|
|
||||||
# Create a StyleValue for the animation property
|
|
||||||
import hashlib
|
|
||||||
h = hashlib.sha256(kf_rule.encode()).hexdigest()[:6]
|
|
||||||
sv = StyleValue(
|
|
||||||
class_name=f"sx-{h}",
|
|
||||||
declarations=f"animation-name:{kf_name}",
|
|
||||||
keyframes=((kf_name, kf_rule),),
|
|
||||||
)
|
|
||||||
register_generated_rule(sv)
|
|
||||||
env[kf_name] = sv
|
|
||||||
return sv
|
|
||||||
|
|
||||||
|
|
||||||
def _sf_defcomp(expr: list, env: dict) -> Component:
|
def _sf_defcomp(expr: list, env: dict) -> Component:
|
||||||
"""``(defcomp ~name (&key param1 param2 &rest children) body)``"""
|
"""``(defcomp ~name (&key param1 param2 &rest children) body)``"""
|
||||||
if len(expr) < 4:
|
if len(expr) < 4:
|
||||||
@@ -1060,7 +1003,6 @@ _SPECIAL_FORMS: dict[str, Any] = {
|
|||||||
"fn": _sf_lambda,
|
"fn": _sf_lambda,
|
||||||
"define": _sf_define,
|
"define": _sf_define,
|
||||||
"defstyle": _sf_defstyle,
|
"defstyle": _sf_defstyle,
|
||||||
"defkeyframes": _sf_defkeyframes,
|
|
||||||
"defcomp": _sf_defcomp,
|
"defcomp": _sf_defcomp,
|
||||||
"defrelation": _sf_defrelation,
|
"defrelation": _sf_defrelation,
|
||||||
"begin": _sf_begin,
|
"begin": _sf_begin,
|
||||||
|
|||||||
@@ -645,7 +645,6 @@ details.group{{overflow:hidden}}details.group>summary{{list-style:none}}details.
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-stone-50 text-stone-900">
|
<body class="bg-stone-50 text-stone-900">
|
||||||
<script type="text/sx-styles" data-hash="{styles_hash}">{styles_json}</script>
|
|
||||||
<script type="text/sx" data-components data-hash="{component_hash}">{component_defs}</script>
|
<script type="text/sx" data-components data-hash="{component_hash}">{component_defs}</script>
|
||||||
<script type="text/sx-pages">{pages_sx}</script>
|
<script type="text/sx-pages">{pages_sx}</script>
|
||||||
<script type="text/sx" data-mount="body">{page_sx}</script>
|
<script type="text/sx" data-mount="body">{page_sx}</script>
|
||||||
@@ -820,14 +819,6 @@ def sx_page(ctx: dict, page_sx: str, *,
|
|||||||
import logging
|
import logging
|
||||||
logging.getLogger("sx").warning("Pretty-print page_sx failed: %s", e)
|
logging.getLogger("sx").warning("Pretty-print page_sx failed: %s", e)
|
||||||
|
|
||||||
# Style dictionary for client-side css primitive
|
|
||||||
styles_hash = _get_style_dict_hash()
|
|
||||||
client_styles_hash = _get_sx_styles_cookie()
|
|
||||||
if not _is_dev_mode() and client_styles_hash and client_styles_hash == styles_hash:
|
|
||||||
styles_json = "" # Client has cached version
|
|
||||||
else:
|
|
||||||
styles_json = _build_style_dict_json()
|
|
||||||
|
|
||||||
# Page registry for client-side routing
|
# Page registry for client-side routing
|
||||||
import logging
|
import logging
|
||||||
_plog = logging.getLogger("sx.pages")
|
_plog = logging.getLogger("sx.pages")
|
||||||
@@ -844,8 +835,6 @@ def sx_page(ctx: dict, page_sx: str, *,
|
|||||||
csrf=_html_escape(csrf),
|
csrf=_html_escape(csrf),
|
||||||
component_hash=component_hash,
|
component_hash=component_hash,
|
||||||
component_defs=component_defs,
|
component_defs=component_defs,
|
||||||
styles_hash=styles_hash,
|
|
||||||
styles_json=styles_json,
|
|
||||||
pages_sx=pages_sx,
|
pages_sx=pages_sx,
|
||||||
page_sx=page_sx,
|
page_sx=page_sx,
|
||||||
sx_css=sx_css,
|
sx_css=sx_css,
|
||||||
@@ -907,10 +896,6 @@ def sx_page_streaming_parts(ctx: dict, page_html: str, *,
|
|||||||
title = ctx.get("base_title", "Rose Ash")
|
title = ctx.get("base_title", "Rose Ash")
|
||||||
csrf = _get_csrf_token()
|
csrf = _get_csrf_token()
|
||||||
|
|
||||||
styles_hash = _get_style_dict_hash()
|
|
||||||
client_styles_hash = _get_sx_styles_cookie()
|
|
||||||
styles_json = "" if (not _is_dev_mode() and client_styles_hash == styles_hash) else _build_style_dict_json()
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from quart import current_app
|
from quart import current_app
|
||||||
pages_sx = _build_pages_sx(current_app.name)
|
pages_sx = _build_pages_sx(current_app.name)
|
||||||
@@ -953,7 +938,6 @@ def sx_page_streaming_parts(ctx: dict, page_html: str, *,
|
|||||||
'</style>\n'
|
'</style>\n'
|
||||||
'</head>\n'
|
'</head>\n'
|
||||||
'<body class="bg-stone-50 text-stone-900">\n'
|
'<body class="bg-stone-50 text-stone-900">\n'
|
||||||
f'<script type="text/sx-styles" data-hash="{styles_hash}">{styles_json}</script>\n'
|
|
||||||
f'<script type="text/sx" data-components data-hash="{component_hash}">{component_defs}</script>\n'
|
f'<script type="text/sx" data-components data-hash="{component_hash}">{component_defs}</script>\n'
|
||||||
f'<script type="text/sx-pages">{pages_sx}</script>\n'
|
f'<script type="text/sx-pages">{pages_sx}</script>\n'
|
||||||
# Server-rendered HTML — suspense placeholders are real DOM elements
|
# Server-rendered HTML — suspense placeholders are real DOM elements
|
||||||
@@ -989,58 +973,6 @@ def sx_streaming_resolve_script(suspension_id: str, sx_source: str,
|
|||||||
|
|
||||||
|
|
||||||
_SCRIPT_HASH_CACHE: dict[str, str] = {}
|
_SCRIPT_HASH_CACHE: dict[str, str] = {}
|
||||||
_STYLE_DICT_JSON: str = ""
|
|
||||||
_STYLE_DICT_HASH: str = ""
|
|
||||||
|
|
||||||
|
|
||||||
def _build_style_dict_json() -> str:
|
|
||||||
"""Build compact JSON style dictionary for client-side css primitive."""
|
|
||||||
global _STYLE_DICT_JSON, _STYLE_DICT_HASH
|
|
||||||
if _STYLE_DICT_JSON:
|
|
||||||
return _STYLE_DICT_JSON
|
|
||||||
|
|
||||||
import json
|
|
||||||
from .style_dict import (
|
|
||||||
STYLE_ATOMS, PSEUDO_VARIANTS, RESPONSIVE_BREAKPOINTS,
|
|
||||||
KEYFRAMES, ARBITRARY_PATTERNS, CHILD_SELECTOR_ATOMS,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Derive child selector prefixes from CHILD_SELECTOR_ATOMS
|
|
||||||
prefixes = set()
|
|
||||||
for atom in CHILD_SELECTOR_ATOMS:
|
|
||||||
# "space-y-4" → "space-y-", "divide-y" → "divide-"
|
|
||||||
for sep in ("space-x-", "space-y-", "divide-x", "divide-y"):
|
|
||||||
if atom.startswith(sep):
|
|
||||||
prefixes.add(sep)
|
|
||||||
break
|
|
||||||
|
|
||||||
data = {
|
|
||||||
"a": STYLE_ATOMS,
|
|
||||||
"v": PSEUDO_VARIANTS,
|
|
||||||
"b": RESPONSIVE_BREAKPOINTS,
|
|
||||||
"k": KEYFRAMES,
|
|
||||||
"p": ARBITRARY_PATTERNS,
|
|
||||||
"c": sorted(prefixes),
|
|
||||||
}
|
|
||||||
_STYLE_DICT_JSON = json.dumps(data, separators=(",", ":"))
|
|
||||||
_STYLE_DICT_HASH = hashlib.md5(_STYLE_DICT_JSON.encode()).hexdigest()[:8]
|
|
||||||
return _STYLE_DICT_JSON
|
|
||||||
|
|
||||||
|
|
||||||
def _get_style_dict_hash() -> str:
|
|
||||||
"""Get the hash of the style dictionary JSON."""
|
|
||||||
if not _STYLE_DICT_HASH:
|
|
||||||
_build_style_dict_json()
|
|
||||||
return _STYLE_DICT_HASH
|
|
||||||
|
|
||||||
|
|
||||||
def _get_sx_styles_cookie() -> str:
|
|
||||||
"""Read the sx-styles-hash cookie from the current request."""
|
|
||||||
try:
|
|
||||||
from quart import request
|
|
||||||
return request.cookies.get("sx-styles-hash", "")
|
|
||||||
except RuntimeError:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
|
|
||||||
def _script_hash(filename: str) -> str:
|
def _script_hash(filename: str) -> str:
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ from __future__ import annotations
|
|||||||
import contextvars
|
import contextvars
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from .types import Component, Keyword, Lambda, Macro, NIL, StyleValue, Symbol
|
from .types import Component, Keyword, Lambda, Macro, NIL, Symbol
|
||||||
from .evaluator import _eval as _raw_eval, _call_component as _raw_call_component, _expand_macro, _trampoline
|
from .evaluator import _eval as _raw_eval, _call_component as _raw_call_component, _expand_macro, _trampoline
|
||||||
|
|
||||||
def _eval(expr, env):
|
def _eval(expr, env):
|
||||||
@@ -510,19 +510,6 @@ def _render_element(tag: str, args: list, env: dict[str, Any]) -> str:
|
|||||||
children.append(arg)
|
children.append(arg)
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
# Handle :style StyleValue — convert to class and register CSS rule
|
|
||||||
style_val = attrs.get("style")
|
|
||||||
if isinstance(style_val, StyleValue):
|
|
||||||
from .css_registry import register_generated_rule
|
|
||||||
register_generated_rule(style_val)
|
|
||||||
# Merge into :class
|
|
||||||
existing_class = attrs.get("class")
|
|
||||||
if existing_class and existing_class is not NIL and existing_class is not False:
|
|
||||||
attrs["class"] = f"{existing_class} {style_val.class_name}"
|
|
||||||
else:
|
|
||||||
attrs["class"] = style_val.class_name
|
|
||||||
del attrs["style"]
|
|
||||||
|
|
||||||
# Collect CSS classes if collector is active
|
# Collect CSS classes if collector is active
|
||||||
class_val = attrs.get("class")
|
class_val = attrs.get("class")
|
||||||
if class_val is not None and class_val is not NIL and class_val is not False:
|
if class_val is not None and class_val is not NIL and class_val is not False:
|
||||||
|
|||||||
@@ -380,11 +380,6 @@ def serialize(expr: Any, indent: int = 0, pretty: bool = False) -> str:
|
|||||||
items.append(serialize(v, indent, pretty))
|
items.append(serialize(v, indent, pretty))
|
||||||
return "{" + " ".join(items) + "}"
|
return "{" + " ".join(items) + "}"
|
||||||
|
|
||||||
# StyleValue — serialize as class name string
|
|
||||||
from .types import StyleValue
|
|
||||||
if isinstance(expr, StyleValue):
|
|
||||||
return f'"{expr.class_name}"'
|
|
||||||
|
|
||||||
# _RawHTML — pre-rendered HTML; wrap as (raw! "...") for SX wire format
|
# _RawHTML — pre-rendered HTML; wrap as (raw! "...") for SX wire format
|
||||||
from .html import _RawHTML
|
from .html import _RawHTML
|
||||||
if isinstance(expr, _RawHTML):
|
if isinstance(expr, _RawHTML):
|
||||||
|
|||||||
@@ -89,37 +89,6 @@ def prim_strip_tags(s: str) -> str:
|
|||||||
return re.sub(r"<[^>]+>", "", s)
|
return re.sub(r"<[^>]+>", "", s)
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# stdlib.style
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
@register_primitive("css")
|
|
||||||
def prim_css(*args: Any) -> Any:
|
|
||||||
"""``(css :flex :gap-4 :hover:bg-sky-200)`` → StyleValue."""
|
|
||||||
from .types import Keyword
|
|
||||||
from .style_resolver import resolve_style
|
|
||||||
atoms = tuple(
|
|
||||||
(a.name if isinstance(a, Keyword) else str(a))
|
|
||||||
for a in args if a is not None and a is not NIL and a is not False
|
|
||||||
)
|
|
||||||
if not atoms:
|
|
||||||
return NIL
|
|
||||||
return resolve_style(atoms)
|
|
||||||
|
|
||||||
|
|
||||||
@register_primitive("merge-styles")
|
|
||||||
def prim_merge_styles(*styles: Any) -> Any:
|
|
||||||
"""``(merge-styles style1 style2)`` → merged StyleValue."""
|
|
||||||
from .types import StyleValue
|
|
||||||
from .style_resolver import merge_styles
|
|
||||||
valid = [s for s in styles if isinstance(s, StyleValue)]
|
|
||||||
if not valid:
|
|
||||||
return NIL
|
|
||||||
if len(valid) == 1:
|
|
||||||
return valid[0]
|
|
||||||
return merge_styles(valid)
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# stdlib.debug
|
# stdlib.debug
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ Only these types may cross the host-SX boundary:
|
|||||||
| list | `list` | `Array` | `Vec<SxValue>` |
|
| list | `list` | `Array` | `Vec<SxValue>` |
|
||||||
| dict | `dict` | `Object` / `Map` | `HashMap<String, SxValue>` |
|
| dict | `dict` | `Object` / `Map` | `HashMap<String, SxValue>` |
|
||||||
| sx-source | `SxExpr` wrapper | `string` | `String` |
|
| sx-source | `SxExpr` wrapper | `string` | `String` |
|
||||||
| style-value | `StyleValue` | `StyleValue` | `StyleValue` |
|
|
||||||
|
|
||||||
**NOT allowed:** ORM models, datetime objects, request objects, raw callables, framework types. Convert at the edge before crossing.
|
**NOT allowed:** ORM models, datetime objects, request objects, raw callables, framework types. Convert at the edge before crossing.
|
||||||
|
|
||||||
|
|||||||
@@ -52,9 +52,6 @@
|
|||||||
(create-fragment)
|
(create-fragment)
|
||||||
(render-dom-list expr env ns))
|
(render-dom-list expr env ns))
|
||||||
|
|
||||||
;; Style value → text of class name
|
|
||||||
"style-value" (create-text-node (style-value-class expr))
|
|
||||||
|
|
||||||
;; Fallback
|
;; Fallback
|
||||||
:else (create-text-node (str expr)))))
|
:else (create-text-node (str expr)))))
|
||||||
|
|
||||||
@@ -147,8 +144,7 @@
|
|||||||
(let ((new-ns (cond (= tag "svg") SVG_NS
|
(let ((new-ns (cond (= tag "svg") SVG_NS
|
||||||
(= tag "math") MATH_NS
|
(= tag "math") MATH_NS
|
||||||
:else ns))
|
:else ns))
|
||||||
(el (dom-create-element tag new-ns))
|
(el (dom-create-element tag new-ns)))
|
||||||
(extra-class nil))
|
|
||||||
|
|
||||||
;; Process args: keywords → attrs, others → children
|
;; Process args: keywords → attrs, others → children
|
||||||
(reduce
|
(reduce
|
||||||
@@ -168,9 +164,6 @@
|
|||||||
;; nil or false → skip
|
;; nil or false → skip
|
||||||
(or (nil? attr-val) (= attr-val false))
|
(or (nil? attr-val) (= attr-val false))
|
||||||
nil
|
nil
|
||||||
;; :style StyleValue → convert to class
|
|
||||||
(and (= attr-name "style") (style-value? attr-val))
|
|
||||||
(set! extra-class (style-value-class attr-val))
|
|
||||||
;; Boolean attr
|
;; Boolean attr
|
||||||
(contains? BOOLEAN_ATTRS attr-name)
|
(contains? BOOLEAN_ATTRS attr-name)
|
||||||
(when attr-val (dom-set-attr el attr-name ""))
|
(when attr-val (dom-set-attr el attr-name ""))
|
||||||
@@ -190,12 +183,6 @@
|
|||||||
(dict "i" 0 "skip" false)
|
(dict "i" 0 "skip" false)
|
||||||
args)
|
args)
|
||||||
|
|
||||||
;; Merge StyleValue class
|
|
||||||
(when extra-class
|
|
||||||
(let ((existing (dom-get-attr el "class")))
|
|
||||||
(dom-set-attr el "class"
|
|
||||||
(if existing (str existing " " extra-class) extra-class))))
|
|
||||||
|
|
||||||
el)))
|
el)))
|
||||||
|
|
||||||
|
|
||||||
@@ -297,7 +284,7 @@
|
|||||||
|
|
||||||
(define RENDER_DOM_FORMS
|
(define RENDER_DOM_FORMS
|
||||||
(list "if" "when" "cond" "case" "let" "let*" "begin" "do"
|
(list "if" "when" "cond" "case" "let" "let*" "begin" "do"
|
||||||
"define" "defcomp" "defmacro" "defstyle" "defkeyframes" "defhandler"
|
"define" "defcomp" "defmacro" "defstyle" "defhandler"
|
||||||
"map" "map-indexed" "filter" "for-each"))
|
"map" "map-indexed" "filter" "for-each"))
|
||||||
|
|
||||||
(define render-dom-form?
|
(define render-dom-form?
|
||||||
@@ -450,7 +437,6 @@
|
|||||||
;;
|
;;
|
||||||
;; From render.sx:
|
;; From render.sx:
|
||||||
;; HTML_TAGS, VOID_ELEMENTS, BOOLEAN_ATTRS, definition-form?
|
;; HTML_TAGS, VOID_ELEMENTS, BOOLEAN_ATTRS, definition-form?
|
||||||
;; style-value?, style-value-class
|
|
||||||
;;
|
;;
|
||||||
;; From eval.sx:
|
;; From eval.sx:
|
||||||
;; eval-expr, trampoline, expand-macro, process-bindings, eval-cond
|
;; eval-expr, trampoline, expand-macro, process-bindings, eval-cond
|
||||||
|
|||||||
@@ -41,7 +41,6 @@
|
|||||||
"boolean" (if val "true" "false")
|
"boolean" (if val "true" "false")
|
||||||
"list" (render-list-to-html val env)
|
"list" (render-list-to-html val env)
|
||||||
"raw-html" (raw-html-content val)
|
"raw-html" (raw-html-content val)
|
||||||
"style-value" (style-value-class val)
|
|
||||||
:else (escape-html (str val)))))
|
:else (escape-html (str val)))))
|
||||||
|
|
||||||
|
|
||||||
@@ -51,7 +50,7 @@
|
|||||||
|
|
||||||
(define RENDER_HTML_FORMS
|
(define RENDER_HTML_FORMS
|
||||||
(list "if" "when" "cond" "case" "let" "let*" "begin" "do"
|
(list "if" "when" "cond" "case" "let" "let*" "begin" "do"
|
||||||
"define" "defcomp" "defmacro" "defstyle" "defkeyframes" "defhandler"
|
"define" "defcomp" "defmacro" "defstyle" "defhandler"
|
||||||
"map" "map-indexed" "filter" "for-each"))
|
"map" "map-indexed" "filter" "for-each"))
|
||||||
|
|
||||||
(define render-html-form?
|
(define render-html-form?
|
||||||
@@ -293,7 +292,7 @@
|
|||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
;;
|
;;
|
||||||
;; Inherited from render.sx:
|
;; Inherited from render.sx:
|
||||||
;; escape-html, escape-attr, raw-html-content, style-value?, style-value-class
|
;; escape-html, escape-attr, raw-html-content
|
||||||
;;
|
;;
|
||||||
;; From eval.sx:
|
;; From eval.sx:
|
||||||
;; eval-expr, trampoline, expand-macro, process-bindings, eval-cond
|
;; eval-expr, trampoline, expand-macro, process-bindings, eval-cond
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import contextvars
|
|||||||
import inspect
|
import inspect
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from ..types import Component, Keyword, Lambda, Macro, NIL, StyleValue, Symbol
|
from ..types import Component, Keyword, Lambda, Macro, NIL, Symbol
|
||||||
from ..parser import SxExpr, serialize
|
from ..parser import SxExpr, serialize
|
||||||
from ..primitives_io import IO_PRIMITIVES, RequestContext, execute_io
|
from ..primitives_io import IO_PRIMITIVES, RequestContext, execute_io
|
||||||
from ..html import (
|
from ..html import (
|
||||||
@@ -250,18 +250,6 @@ async def _arender_element(tag, args, env, ctx):
|
|||||||
children.append(arg)
|
children.append(arg)
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
# StyleValue → class
|
|
||||||
style_val = attrs.get("style")
|
|
||||||
if isinstance(style_val, StyleValue):
|
|
||||||
from ..css_registry import register_generated_rule
|
|
||||||
register_generated_rule(style_val)
|
|
||||||
existing = attrs.get("class")
|
|
||||||
if existing and existing is not NIL and existing is not False:
|
|
||||||
attrs["class"] = f"{existing} {style_val.class_name}"
|
|
||||||
else:
|
|
||||||
attrs["class"] = style_val.class_name
|
|
||||||
del attrs["style"]
|
|
||||||
|
|
||||||
class_val = attrs.get("class")
|
class_val = attrs.get("class")
|
||||||
if class_val is not None and class_val is not NIL and class_val is not False:
|
if class_val is not None and class_val is not NIL and class_val is not False:
|
||||||
collector = css_class_collector.get(None)
|
collector = css_class_collector.get(None)
|
||||||
@@ -464,7 +452,6 @@ _ASYNC_RENDER_FORMS = {
|
|||||||
"do": _arsf_begin,
|
"do": _arsf_begin,
|
||||||
"define": _arsf_define,
|
"define": _arsf_define,
|
||||||
"defstyle": _arsf_define,
|
"defstyle": _arsf_define,
|
||||||
"defkeyframes": _arsf_define,
|
|
||||||
"defcomp": _arsf_define,
|
"defcomp": _arsf_define,
|
||||||
"defmacro": _arsf_define,
|
"defmacro": _arsf_define,
|
||||||
"defhandler": _arsf_define,
|
"defhandler": _arsf_define,
|
||||||
@@ -716,11 +703,6 @@ async def _aser_call(name, args, env, ctx):
|
|||||||
if isinstance(arg, Keyword) and i + 1 < len(args):
|
if isinstance(arg, Keyword) and i + 1 < len(args):
|
||||||
val = await _aser(args[i + 1], env, ctx)
|
val = await _aser(args[i + 1], env, ctx)
|
||||||
if val is not NIL and val is not None:
|
if val is not NIL and val is not None:
|
||||||
if arg.name == "style" and isinstance(val, StyleValue):
|
|
||||||
from ..css_registry import register_generated_rule
|
|
||||||
register_generated_rule(val)
|
|
||||||
extra_class = val.class_name
|
|
||||||
else:
|
|
||||||
parts.append(f":{arg.name}")
|
parts.append(f":{arg.name}")
|
||||||
if isinstance(val, list):
|
if isinstance(val, list):
|
||||||
live = [v for v in val if v is not NIL and v is not None]
|
live = [v for v in val if v is not NIL and v is not None]
|
||||||
@@ -996,7 +978,6 @@ _ASER_FORMS = {
|
|||||||
"fn": _assf_lambda,
|
"fn": _assf_lambda,
|
||||||
"define": _assf_define,
|
"define": _assf_define,
|
||||||
"defstyle": _assf_define,
|
"defstyle": _assf_define,
|
||||||
"defkeyframes": _assf_define,
|
|
||||||
"defcomp": _assf_define,
|
"defcomp": _assf_define,
|
||||||
"defmacro": _assf_define,
|
"defmacro": _assf_define,
|
||||||
"defhandler": _assf_define,
|
"defhandler": _assf_define,
|
||||||
|
|||||||
@@ -3,16 +3,14 @@
|
|||||||
;;
|
;;
|
||||||
;; Handles the browser startup lifecycle:
|
;; Handles the browser startup lifecycle:
|
||||||
;; 1. CSS tracking init
|
;; 1. CSS tracking init
|
||||||
;; 2. Style dictionary loading (from <script type="text/sx-styles">)
|
;; 2. Component script processing (from <script type="text/sx">)
|
||||||
;; 3. Component script processing (from <script type="text/sx">)
|
;; 3. Hydration of [data-sx] elements
|
||||||
;; 4. Hydration of [data-sx] elements
|
;; 4. Engine element processing
|
||||||
;; 5. Engine element processing
|
|
||||||
;;
|
;;
|
||||||
;; Also provides the public mounting/hydration API:
|
;; Also provides the public mounting/hydration API:
|
||||||
;; mount, hydrate, update, render-component
|
;; mount, hydrate, update, render-component
|
||||||
;;
|
;;
|
||||||
;; Depends on:
|
;; Depends on:
|
||||||
;; cssx.sx — load-style-dict
|
|
||||||
;; orchestration.sx — process-elements, engine-init
|
;; orchestration.sx — process-elements, engine-init
|
||||||
;; adapter-dom.sx — render-to-dom
|
;; adapter-dom.sx — render-to-dom
|
||||||
;; render.sx — shared registries
|
;; render.sx — shared registries
|
||||||
@@ -275,58 +273,6 @@
|
|||||||
(set-sx-comp-cookie hash))))))
|
(set-sx-comp-cookie hash))))))
|
||||||
|
|
||||||
|
|
||||||
;; --------------------------------------------------------------------------
|
|
||||||
;; Style dictionary initialization
|
|
||||||
;; --------------------------------------------------------------------------
|
|
||||||
|
|
||||||
(define init-style-dict
|
|
||||||
(fn ()
|
|
||||||
;; Process <script type="text/sx-styles"> tags with caching.
|
|
||||||
(let ((scripts (query-style-scripts)))
|
|
||||||
(for-each
|
|
||||||
(fn (s)
|
|
||||||
(when (not (is-processed? s "styles"))
|
|
||||||
(mark-processed! s "styles")
|
|
||||||
(let ((text (dom-text-content s))
|
|
||||||
(hash (dom-get-attr s "data-hash")))
|
|
||||||
(if (nil? hash)
|
|
||||||
;; No hash — just parse inline
|
|
||||||
(when (and text (not (empty? (trim text))))
|
|
||||||
(parse-and-load-style-dict text))
|
|
||||||
;; Hash-based caching
|
|
||||||
(let ((has-inline (and text (not (empty? (trim text))))))
|
|
||||||
(let ((cached-hash (local-storage-get "sx-styles-hash")))
|
|
||||||
(if (= cached-hash hash)
|
|
||||||
;; Cache hit
|
|
||||||
(if has-inline
|
|
||||||
(do
|
|
||||||
(local-storage-set "sx-styles-src" text)
|
|
||||||
(parse-and-load-style-dict text)
|
|
||||||
(log-info "styles: downloaded (cookie stale)"))
|
|
||||||
(let ((cached (local-storage-get "sx-styles-src")))
|
|
||||||
(if cached
|
|
||||||
(do
|
|
||||||
(parse-and-load-style-dict cached)
|
|
||||||
(log-info (str "styles: cached (" hash ")")))
|
|
||||||
(do
|
|
||||||
(clear-sx-styles-cookie)
|
|
||||||
(browser-reload)))))
|
|
||||||
;; Cache miss
|
|
||||||
(if has-inline
|
|
||||||
(do
|
|
||||||
(local-storage-set "sx-styles-hash" hash)
|
|
||||||
(local-storage-set "sx-styles-src" text)
|
|
||||||
(parse-and-load-style-dict text)
|
|
||||||
(log-info (str "styles: downloaded (" hash ")")))
|
|
||||||
(do
|
|
||||||
(local-storage-remove "sx-styles-hash")
|
|
||||||
(local-storage-remove "sx-styles-src")
|
|
||||||
(clear-sx-styles-cookie)
|
|
||||||
(browser-reload)))))
|
|
||||||
(set-sx-styles-cookie hash))))))
|
|
||||||
scripts))))
|
|
||||||
|
|
||||||
|
|
||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
;; Page registry for client-side routing
|
;; Page registry for client-side routing
|
||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
@@ -375,7 +321,6 @@
|
|||||||
(do
|
(do
|
||||||
(log-info (str "sx-browser " SX_VERSION))
|
(log-info (str "sx-browser " SX_VERSION))
|
||||||
(init-css-tracking)
|
(init-css-tracking)
|
||||||
(init-style-dict)
|
|
||||||
(process-page-scripts)
|
(process-page-scripts)
|
||||||
(process-sx-scripts nil)
|
(process-sx-scripts nil)
|
||||||
(sx-hydrate-elements nil)
|
(sx-hydrate-elements nil)
|
||||||
@@ -389,9 +334,6 @@
|
|||||||
;; From orchestration.sx:
|
;; From orchestration.sx:
|
||||||
;; process-elements, init-css-tracking
|
;; process-elements, init-css-tracking
|
||||||
;;
|
;;
|
||||||
;; From cssx.sx:
|
|
||||||
;; load-style-dict
|
|
||||||
;;
|
|
||||||
;; === DOM / Render ===
|
;; === DOM / Render ===
|
||||||
;; (resolve-mount-target target) → Element (string → querySelector, else identity)
|
;; (resolve-mount-target target) → Element (string → querySelector, else identity)
|
||||||
;; (sx-render-with-env source extra-env) → DOM node (parse + render with componentEnv + extra)
|
;; (sx-render-with-env source extra-env) → DOM node (parse + render with componentEnv + extra)
|
||||||
@@ -420,7 +362,6 @@
|
|||||||
;;
|
;;
|
||||||
;; === Script queries ===
|
;; === Script queries ===
|
||||||
;; (query-sx-scripts root) → list of <script type="text/sx"> elements
|
;; (query-sx-scripts root) → list of <script type="text/sx"> elements
|
||||||
;; (query-style-scripts) → list of <script type="text/sx-styles"> elements
|
|
||||||
;; (query-page-scripts) → list of <script type="text/sx-pages"> elements
|
;; (query-page-scripts) → list of <script type="text/sx-pages"> elements
|
||||||
;;
|
;;
|
||||||
;; === localStorage ===
|
;; === localStorage ===
|
||||||
@@ -431,8 +372,6 @@
|
|||||||
;; === Cookies ===
|
;; === Cookies ===
|
||||||
;; (set-sx-comp-cookie hash) → void
|
;; (set-sx-comp-cookie hash) → void
|
||||||
;; (clear-sx-comp-cookie) → void
|
;; (clear-sx-comp-cookie) → void
|
||||||
;; (set-sx-styles-cookie hash) → void
|
|
||||||
;; (clear-sx-styles-cookie) → void
|
|
||||||
;;
|
;;
|
||||||
;; === Env ===
|
;; === Env ===
|
||||||
;; (parse-env-attr el) → dict (parse data-sx-env JSON attr)
|
;; (parse-env-attr el) → dict (parse data-sx-env JSON attr)
|
||||||
@@ -444,8 +383,6 @@
|
|||||||
;; (log-parse-error label text err) → void (diagnostic parse error)
|
;; (log-parse-error label text err) → void (diagnostic parse error)
|
||||||
;;
|
;;
|
||||||
;; === JSON parsing ===
|
;; === JSON parsing ===
|
||||||
;; (parse-and-load-style-dict text) → void (JSON.parse + load-style-dict)
|
|
||||||
;;
|
|
||||||
;; === Processing markers ===
|
;; === Processing markers ===
|
||||||
;; (mark-processed! el key) → void
|
;; (mark-processed! el key) → void
|
||||||
;; (is-processed? el key) → boolean
|
;; (is-processed? el key) → boolean
|
||||||
|
|||||||
@@ -191,10 +191,6 @@ class JSEmitter:
|
|||||||
"ho-every": "hoEvery",
|
"ho-every": "hoEvery",
|
||||||
"ho-for-each": "hoForEach",
|
"ho-for-each": "hoForEach",
|
||||||
"sf-defstyle": "sfDefstyle",
|
"sf-defstyle": "sfDefstyle",
|
||||||
"sf-defkeyframes": "sfDefkeyframes",
|
|
||||||
"build-keyframes": "buildKeyframes",
|
|
||||||
"style-value?": "isStyleValue",
|
|
||||||
"style-value-class": "styleValueClass",
|
|
||||||
"kf-name": "kfName",
|
"kf-name": "kfName",
|
||||||
"special-form?": "isSpecialForm",
|
"special-form?": "isSpecialForm",
|
||||||
"ho-form?": "isHoForm",
|
"ho-form?": "isHoForm",
|
||||||
@@ -443,32 +439,6 @@ class JSEmitter:
|
|||||||
"format-date": "formatDate",
|
"format-date": "formatDate",
|
||||||
"format-decimal": "formatDecimal",
|
"format-decimal": "formatDecimal",
|
||||||
"parse-int": "parseInt_",
|
"parse-int": "parseInt_",
|
||||||
# cssx.sx
|
|
||||||
"_style-atoms": "_styleAtoms",
|
|
||||||
"_pseudo-variants": "_pseudoVariants",
|
|
||||||
"_responsive-breakpoints": "_responsiveBreakpoints",
|
|
||||||
"_style-keyframes": "_styleKeyframes",
|
|
||||||
"_arbitrary-patterns": "_arbitraryPatterns",
|
|
||||||
"_child-selector-prefixes": "_childSelectorPrefixes",
|
|
||||||
"_style-cache": "_styleCache",
|
|
||||||
"_injected-styles": "_injectedStyles",
|
|
||||||
"load-style-dict": "loadStyleDict",
|
|
||||||
"split-variant": "splitVariant",
|
|
||||||
"resolve-atom": "resolveAtom",
|
|
||||||
"is-child-selector-atom?": "isChildSelectorAtom",
|
|
||||||
"hash-style": "hashStyle",
|
|
||||||
"resolve-style": "resolveStyle",
|
|
||||||
"merge-style-values": "mergeStyleValues",
|
|
||||||
"fnv1a-hash": "fnv1aHash",
|
|
||||||
"compile-regex": "compileRegex",
|
|
||||||
"regex-match": "regexMatch",
|
|
||||||
"regex-replace-groups": "regexReplaceGroups",
|
|
||||||
"make-style-value": "makeStyleValue_",
|
|
||||||
"style-value-declarations": "styleValueDeclarations",
|
|
||||||
"style-value-media-rules": "styleValueMediaRules",
|
|
||||||
"style-value-pseudo-rules": "styleValuePseudoRules",
|
|
||||||
"style-value-keyframes": "styleValueKeyframes_",
|
|
||||||
"inject-style-value": "injectStyleValue",
|
|
||||||
# boot.sx
|
# boot.sx
|
||||||
"HEAD_HOIST_SELECTOR": "HEAD_HOIST_SELECTOR",
|
"HEAD_HOIST_SELECTOR": "HEAD_HOIST_SELECTOR",
|
||||||
"hoist-head-elements-full": "hoistHeadElementsFull",
|
"hoist-head-elements-full": "hoistHeadElementsFull",
|
||||||
@@ -478,7 +448,6 @@ class JSEmitter:
|
|||||||
"sx-render-component": "sxRenderComponent",
|
"sx-render-component": "sxRenderComponent",
|
||||||
"process-sx-scripts": "processSxScripts",
|
"process-sx-scripts": "processSxScripts",
|
||||||
"process-component-script": "processComponentScript",
|
"process-component-script": "processComponentScript",
|
||||||
"init-style-dict": "initStyleDict",
|
|
||||||
"SX_VERSION": "SX_VERSION",
|
"SX_VERSION": "SX_VERSION",
|
||||||
"boot-init": "bootInit",
|
"boot-init": "bootInit",
|
||||||
"resolve-suspense": "resolveSuspense",
|
"resolve-suspense": "resolveSuspense",
|
||||||
@@ -490,21 +459,17 @@ class JSEmitter:
|
|||||||
"set-document-title": "setDocumentTitle",
|
"set-document-title": "setDocumentTitle",
|
||||||
"remove-head-element": "removeHeadElement",
|
"remove-head-element": "removeHeadElement",
|
||||||
"query-sx-scripts": "querySxScripts",
|
"query-sx-scripts": "querySxScripts",
|
||||||
"query-style-scripts": "queryStyleScripts",
|
|
||||||
"local-storage-get": "localStorageGet",
|
"local-storage-get": "localStorageGet",
|
||||||
"local-storage-set": "localStorageSet",
|
"local-storage-set": "localStorageSet",
|
||||||
"local-storage-remove": "localStorageRemove",
|
"local-storage-remove": "localStorageRemove",
|
||||||
"set-sx-comp-cookie": "setSxCompCookie",
|
"set-sx-comp-cookie": "setSxCompCookie",
|
||||||
"clear-sx-comp-cookie": "clearSxCompCookie",
|
"clear-sx-comp-cookie": "clearSxCompCookie",
|
||||||
"set-sx-styles-cookie": "setSxStylesCookie",
|
|
||||||
"clear-sx-styles-cookie": "clearSxStylesCookie",
|
|
||||||
"parse-env-attr": "parseEnvAttr",
|
"parse-env-attr": "parseEnvAttr",
|
||||||
"store-env-attr": "storeEnvAttr",
|
"store-env-attr": "storeEnvAttr",
|
||||||
"to-kebab": "toKebab",
|
"to-kebab": "toKebab",
|
||||||
"log-info": "logInfo",
|
"log-info": "logInfo",
|
||||||
"log-warn": "logWarn",
|
"log-warn": "logWarn",
|
||||||
"log-parse-error": "logParseError",
|
"log-parse-error": "logParseError",
|
||||||
"parse-and-load-style-dict": "parseAndLoadStyleDict",
|
|
||||||
"_page-routes": "_pageRoutes",
|
"_page-routes": "_pageRoutes",
|
||||||
"process-page-scripts": "processPageScripts",
|
"process-page-scripts": "processPageScripts",
|
||||||
"query-page-scripts": "queryPageScripts",
|
"query-page-scripts": "queryPageScripts",
|
||||||
@@ -1044,7 +1009,6 @@ ADAPTER_FILES = {
|
|||||||
"dom": ("adapter-dom.sx", "adapter-dom"),
|
"dom": ("adapter-dom.sx", "adapter-dom"),
|
||||||
"engine": ("engine.sx", "engine"),
|
"engine": ("engine.sx", "engine"),
|
||||||
"orchestration": ("orchestration.sx","orchestration"),
|
"orchestration": ("orchestration.sx","orchestration"),
|
||||||
"cssx": ("cssx.sx", "cssx"),
|
|
||||||
"boot": ("boot.sx", "boot"),
|
"boot": ("boot.sx", "boot"),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1052,8 +1016,7 @@ ADAPTER_FILES = {
|
|||||||
ADAPTER_DEPS = {
|
ADAPTER_DEPS = {
|
||||||
"engine": ["dom"],
|
"engine": ["dom"],
|
||||||
"orchestration": ["engine", "dom"],
|
"orchestration": ["engine", "dom"],
|
||||||
"cssx": [],
|
"boot": ["dom", "engine", "orchestration", "parser"],
|
||||||
"boot": ["dom", "engine", "orchestration", "cssx", "parser"],
|
|
||||||
"parser": [],
|
"parser": [],
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1292,7 +1255,7 @@ ASYNC_IO_JS = '''
|
|||||||
|
|
||||||
// define/defcomp/defmacro — eval for side effects
|
// define/defcomp/defmacro — eval for side effects
|
||||||
if (hname === "define" || hname === "defcomp" || hname === "defmacro" ||
|
if (hname === "define" || hname === "defcomp" || hname === "defmacro" ||
|
||||||
hname === "defstyle" || hname === "defkeyframes" || hname === "defhandler") {
|
hname === "defstyle" || hname === "defhandler") {
|
||||||
trampoline(evalExpr(expr, env));
|
trampoline(evalExpr(expr, env));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -1414,11 +1377,7 @@ ASYNC_IO_JS = '''
|
|||||||
})(attrName, attrVal);
|
})(attrName, attrVal);
|
||||||
} else {
|
} else {
|
||||||
if (!isNil(attrVal) && attrVal !== false) {
|
if (!isNil(attrVal) && attrVal !== false) {
|
||||||
if (attrName === "class" && attrVal && attrVal._styleValue) {
|
if (contains(BOOLEAN_ATTRS, attrName)) {
|
||||||
el.setAttribute("class", (el.getAttribute("class") || "") + " " + attrVal.className);
|
|
||||||
} else if (attrName === "style" && attrVal && attrVal._styleValue) {
|
|
||||||
el.setAttribute("class", (el.getAttribute("class") || "") + " " + attrVal.className);
|
|
||||||
} else if (contains(BOOLEAN_ATTRS, attrName)) {
|
|
||||||
if (isSxTruthy(attrVal)) el.setAttribute(attrName, "");
|
if (isSxTruthy(attrVal)) el.setAttribute(attrName, "");
|
||||||
} else if (attrVal === true) {
|
} else if (attrVal === true) {
|
||||||
el.setAttribute(attrName, "");
|
el.setAttribute(attrName, "");
|
||||||
@@ -1829,7 +1788,6 @@ def compile_ref_to_js(
|
|||||||
"dom": PLATFORM_DOM_JS,
|
"dom": PLATFORM_DOM_JS,
|
||||||
"engine": PLATFORM_ENGINE_PURE_JS,
|
"engine": PLATFORM_ENGINE_PURE_JS,
|
||||||
"orchestration": PLATFORM_ORCHESTRATION_JS,
|
"orchestration": PLATFORM_ORCHESTRATION_JS,
|
||||||
"cssx": PLATFORM_CSSX_JS,
|
|
||||||
"boot": PLATFORM_BOOT_JS,
|
"boot": PLATFORM_BOOT_JS,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1864,7 +1822,7 @@ def compile_ref_to_js(
|
|||||||
("eval.sx", "eval"),
|
("eval.sx", "eval"),
|
||||||
("render.sx", "render (core)"),
|
("render.sx", "render (core)"),
|
||||||
]
|
]
|
||||||
for name in ("parser", "html", "sx", "dom", "engine", "orchestration", "cssx", "boot"):
|
for name in ("parser", "html", "sx", "dom", "engine", "orchestration", "boot"):
|
||||||
if name in adapter_set:
|
if name in adapter_set:
|
||||||
sx_files.append(ADAPTER_FILES[name])
|
sx_files.append(ADAPTER_FILES[name])
|
||||||
for name in sorted(spec_mod_set):
|
for name in sorted(spec_mod_set):
|
||||||
@@ -1895,7 +1853,6 @@ def compile_ref_to_js(
|
|||||||
has_dom = "dom" in adapter_set
|
has_dom = "dom" in adapter_set
|
||||||
has_engine = "engine" in adapter_set
|
has_engine = "engine" in adapter_set
|
||||||
has_orch = "orchestration" in adapter_set
|
has_orch = "orchestration" in adapter_set
|
||||||
has_cssx = "cssx" in adapter_set
|
|
||||||
has_boot = "boot" in adapter_set
|
has_boot = "boot" in adapter_set
|
||||||
has_parser = "parser" in adapter_set
|
has_parser = "parser" in adapter_set
|
||||||
adapter_label = "+".join(sorted(adapter_set)) if adapter_set else "core-only"
|
adapter_label = "+".join(sorted(adapter_set)) if adapter_set else "core-only"
|
||||||
@@ -1937,7 +1894,7 @@ def compile_ref_to_js(
|
|||||||
# Platform JS for selected adapters
|
# Platform JS for selected adapters
|
||||||
if not has_dom:
|
if not has_dom:
|
||||||
parts.append("\n var _hasDom = false;\n")
|
parts.append("\n var _hasDom = false;\n")
|
||||||
for name in ("dom", "engine", "orchestration", "cssx", "boot"):
|
for name in ("dom", "engine", "orchestration", "boot"):
|
||||||
if name in adapter_set and name in adapter_platform:
|
if name in adapter_set and name in adapter_platform:
|
||||||
parts.append(adapter_platform[name])
|
parts.append(adapter_platform[name])
|
||||||
|
|
||||||
@@ -1946,7 +1903,7 @@ def compile_ref_to_js(
|
|||||||
parts.append(CONTINUATIONS_JS)
|
parts.append(CONTINUATIONS_JS)
|
||||||
if has_dom:
|
if has_dom:
|
||||||
parts.append(ASYNC_IO_JS)
|
parts.append(ASYNC_IO_JS)
|
||||||
parts.append(public_api_js(has_html, has_sx, has_dom, has_engine, has_orch, has_cssx, has_boot, has_parser, adapter_label, has_deps, has_router))
|
parts.append(public_api_js(has_html, has_sx, has_dom, has_engine, has_orch, has_boot, has_parser, adapter_label, has_deps, has_router))
|
||||||
parts.append(EPILOGUE)
|
parts.append(EPILOGUE)
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
build_ts = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
build_ts = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||||
@@ -2019,15 +1976,6 @@ PREAMBLE = '''\
|
|||||||
function RawHTML(html) { this.html = html; }
|
function RawHTML(html) { this.html = html; }
|
||||||
RawHTML.prototype._raw = true;
|
RawHTML.prototype._raw = true;
|
||||||
|
|
||||||
function StyleValue(className, declarations, mediaRules, pseudoRules, keyframes) {
|
|
||||||
this.className = className;
|
|
||||||
this.declarations = declarations || "";
|
|
||||||
this.mediaRules = mediaRules || [];
|
|
||||||
this.pseudoRules = pseudoRules || [];
|
|
||||||
this.keyframes = keyframes || [];
|
|
||||||
}
|
|
||||||
StyleValue.prototype._styleValue = true;
|
|
||||||
|
|
||||||
function isSym(x) { return x != null && x._sym === true; }
|
function isSym(x) { return x != null && x._sym === true; }
|
||||||
function isKw(x) { return x != null && x._kw === true; }
|
function isKw(x) { return x != null && x._kw === true; }
|
||||||
|
|
||||||
@@ -2224,30 +2172,6 @@ PRIMITIVES_JS_MODULES: dict[str, str] = {
|
|||||||
PRIMITIVES["strip-tags"] = function(s) { return String(s).replace(/<[^>]+>/g, ""); };
|
PRIMITIVES["strip-tags"] = function(s) { return String(s).replace(/<[^>]+>/g, ""); };
|
||||||
''',
|
''',
|
||||||
|
|
||||||
"stdlib.style": '''
|
|
||||||
// stdlib.style
|
|
||||||
PRIMITIVES["css"] = function() {
|
|
||||||
var atoms = [];
|
|
||||||
for (var i = 0; i < arguments.length; i++) {
|
|
||||||
var a = arguments[i];
|
|
||||||
if (isNil(a) || a === false) continue;
|
|
||||||
atoms.push(isKw(a) ? a.name : String(a));
|
|
||||||
}
|
|
||||||
if (!atoms.length) return NIL;
|
|
||||||
return new StyleValue("sx-" + atoms.join("-"), atoms.join(";"), [], [], []);
|
|
||||||
};
|
|
||||||
PRIMITIVES["merge-styles"] = function() {
|
|
||||||
var valid = [];
|
|
||||||
for (var i = 0; i < arguments.length; i++) {
|
|
||||||
if (isStyleValue(arguments[i])) valid.push(arguments[i]);
|
|
||||||
}
|
|
||||||
if (!valid.length) return NIL;
|
|
||||||
if (valid.length === 1) return valid[0];
|
|
||||||
var allDecls = valid.map(function(v) { return v.declarations; }).join(";");
|
|
||||||
return new StyleValue("sx-merged", allDecls, [], [], []);
|
|
||||||
};
|
|
||||||
''',
|
|
||||||
|
|
||||||
"stdlib.debug": '''
|
"stdlib.debug": '''
|
||||||
// stdlib.debug
|
// stdlib.debug
|
||||||
PRIMITIVES["assert"] = function(cond, msg) {
|
PRIMITIVES["assert"] = function(cond, msg) {
|
||||||
@@ -2295,7 +2219,6 @@ PLATFORM_JS_PRE = '''
|
|||||||
if (x._component) return "component";
|
if (x._component) return "component";
|
||||||
if (x._macro) return "macro";
|
if (x._macro) return "macro";
|
||||||
if (x._raw) return "raw-html";
|
if (x._raw) return "raw-html";
|
||||||
if (x._styleValue) return "style-value";
|
|
||||||
if (typeof Node !== "undefined" && x instanceof Node) return "dom-node";
|
if (typeof Node !== "undefined" && x instanceof Node) return "dom-node";
|
||||||
if (Array.isArray(x)) return "list";
|
if (Array.isArray(x)) return "list";
|
||||||
if (typeof x === "object") return "dict";
|
if (typeof x === "object") return "dict";
|
||||||
@@ -2342,27 +2265,6 @@ PLATFORM_JS_PRE = '''
|
|||||||
function isComponent(x) { return x != null && x._component === true; }
|
function isComponent(x) { return x != null && x._component === true; }
|
||||||
function isMacro(x) { return x != null && x._macro === true; }
|
function isMacro(x) { return x != null && x._macro === true; }
|
||||||
|
|
||||||
function isStyleValue(x) { return x != null && x._styleValue === true; }
|
|
||||||
function styleValueClass(x) { return x.className; }
|
|
||||||
function styleValue_p(x) { return x != null && x._styleValue === true; }
|
|
||||||
|
|
||||||
function buildKeyframes(kfName, steps, env) {
|
|
||||||
// Platform implementation of defkeyframes
|
|
||||||
var parts = [];
|
|
||||||
for (var i = 0; i < steps.length; i++) {
|
|
||||||
var step = steps[i];
|
|
||||||
var selector = isSym(step[0]) ? step[0].name : String(step[0]);
|
|
||||||
var body = trampoline(evalExpr(step[1], env));
|
|
||||||
var decls = isStyleValue(body) ? body.declarations : String(body);
|
|
||||||
parts.push(selector + "{" + decls + "}");
|
|
||||||
}
|
|
||||||
var kfRule = "@keyframes " + kfName + "{" + parts.join("") + "}";
|
|
||||||
var cn = "sx-ref-kf-" + kfName;
|
|
||||||
var sv = new StyleValue(cn, "animation-name:" + kfName, [], [], [[kfName, kfRule]]);
|
|
||||||
env[kfName] = sv;
|
|
||||||
return sv;
|
|
||||||
}
|
|
||||||
|
|
||||||
function envHas(env, name) { return name in env; }
|
function envHas(env, name) { return name in env; }
|
||||||
function envGet(env, name) { return env[name]; }
|
function envGet(env, name) { return env[name]; }
|
||||||
function envSet(env, name, val) { env[name] = val; }
|
function envSet(env, name, val) { env[name] = val; }
|
||||||
@@ -2488,7 +2390,7 @@ PLATFORM_JS_POST = '''
|
|||||||
function isSpecialForm(n) { return n in {
|
function isSpecialForm(n) { return n in {
|
||||||
"if":1,"when":1,"cond":1,"case":1,"and":1,"or":1,"let":1,"let*":1,
|
"if":1,"when":1,"cond":1,"case":1,"and":1,"or":1,"let":1,"let*":1,
|
||||||
"lambda":1,"fn":1,"define":1,"defcomp":1,"defmacro":1,"defstyle":1,
|
"lambda":1,"fn":1,"define":1,"defcomp":1,"defmacro":1,"defstyle":1,
|
||||||
"defkeyframes":1,"defhandler":1,"begin":1,"do":1,
|
"defhandler":1,"begin":1,"do":1,
|
||||||
"quote":1,"quasiquote":1,"->":1,"set!":1
|
"quote":1,"quasiquote":1,"->":1,"set!":1
|
||||||
}; }
|
}; }
|
||||||
function isHoForm(n) { return n in {
|
function isHoForm(n) { return n in {
|
||||||
@@ -2499,7 +2401,7 @@ PLATFORM_JS_POST = '''
|
|||||||
|
|
||||||
function isDefinitionForm(name) {
|
function isDefinitionForm(name) {
|
||||||
return name === "define" || name === "defcomp" || name === "defmacro" ||
|
return name === "define" || name === "defcomp" || name === "defmacro" ||
|
||||||
name === "defstyle" || name === "defkeyframes" || name === "defhandler";
|
name === "defstyle" || name === "defhandler";
|
||||||
}
|
}
|
||||||
|
|
||||||
function indexOf_(s, ch) {
|
function indexOf_(s, ch) {
|
||||||
@@ -2877,11 +2779,7 @@ PLATFORM_DOM_JS = """
|
|||||||
var attrVal = trampoline(evalExpr(args[i + 1], env));
|
var attrVal = trampoline(evalExpr(args[i + 1], env));
|
||||||
i++; // skip value
|
i++; // skip value
|
||||||
if (isNil(attrVal) || attrVal === false) continue;
|
if (isNil(attrVal) || attrVal === false) continue;
|
||||||
if (attrName === "class" && attrVal && attrVal._styleValue) {
|
if (contains(BOOLEAN_ATTRS, attrName)) {
|
||||||
extraClasses.push(attrVal.className);
|
|
||||||
} else if (attrName === "style" && attrVal && attrVal._styleValue) {
|
|
||||||
extraClasses.push(attrVal.className);
|
|
||||||
} else if (contains(BOOLEAN_ATTRS, attrName)) {
|
|
||||||
if (isSxTruthy(attrVal)) el.setAttribute(attrName, "");
|
if (isSxTruthy(attrVal)) el.setAttribute(attrName, "");
|
||||||
} else if (attrVal === true) {
|
} else if (attrVal === true) {
|
||||||
el.setAttribute(attrName, "");
|
el.setAttribute(attrName, "");
|
||||||
@@ -3763,102 +3661,6 @@ PLATFORM_ORCHESTRATION_JS = """
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
PLATFORM_CSSX_JS = """
|
|
||||||
// =========================================================================
|
|
||||||
// Platform interface — CSSX (style dictionary)
|
|
||||||
// =========================================================================
|
|
||||||
|
|
||||||
function fnv1aHash(input) {
|
|
||||||
var h = 0x811c9dc5;
|
|
||||||
for (var i = 0; i < input.length; i++) {
|
|
||||||
h ^= input.charCodeAt(i);
|
|
||||||
h = (h * 0x01000193) >>> 0;
|
|
||||||
}
|
|
||||||
return h.toString(16).padStart(8, "0").substring(0, 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
function compileRegex(pattern) {
|
|
||||||
try { return new RegExp(pattern); } catch (e) { return null; }
|
|
||||||
}
|
|
||||||
|
|
||||||
function regexMatch(re, s) {
|
|
||||||
if (!re) return NIL;
|
|
||||||
var m = s.match(re);
|
|
||||||
return m ? Array.prototype.slice.call(m) : NIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
function regexReplaceGroups(tmpl, match) {
|
|
||||||
var result = tmpl;
|
|
||||||
for (var j = 1; j < match.length; j++) {
|
|
||||||
result = result.split("{" + (j - 1) + "}").join(match[j]);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeStyleValue_(cn, decls, media, pseudo, kf) {
|
|
||||||
return new StyleValue(cn, decls || "", media || [], pseudo || [], kf || []);
|
|
||||||
}
|
|
||||||
|
|
||||||
function styleValueDeclarations(sv) { return sv.declarations; }
|
|
||||||
function styleValueMediaRules(sv) { return sv.mediaRules; }
|
|
||||||
function styleValuePseudoRules(sv) { return sv.pseudoRules; }
|
|
||||||
function styleValueKeyframes_(sv) { return sv.keyframes; }
|
|
||||||
|
|
||||||
function injectStyleValue(sv, atoms) {
|
|
||||||
if (_injectedStyles[sv.className]) return;
|
|
||||||
_injectedStyles[sv.className] = true;
|
|
||||||
|
|
||||||
if (!_hasDom) return;
|
|
||||||
var cssTarget = document.getElementById("sx-css");
|
|
||||||
if (!cssTarget) return;
|
|
||||||
|
|
||||||
var rules = [];
|
|
||||||
// Child-selector atoms are now routed to pseudoRules by the resolver
|
|
||||||
// with selector ">:not(:first-child)", so base declarations are always
|
|
||||||
// applied directly to the class.
|
|
||||||
if (sv.declarations) {
|
|
||||||
rules.push("." + sv.className + "{" + sv.declarations + "}");
|
|
||||||
}
|
|
||||||
for (var pi = 0; pi < sv.pseudoRules.length; pi++) {
|
|
||||||
var sel = sv.pseudoRules[pi][0], decls = sv.pseudoRules[pi][1];
|
|
||||||
if (sel.indexOf("&") >= 0) {
|
|
||||||
rules.push(sel.replace(/&/g, "." + sv.className) + "{" + decls + "}");
|
|
||||||
} else {
|
|
||||||
rules.push("." + sv.className + sel + "{" + decls + "}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (var mi = 0; mi < sv.mediaRules.length; mi++) {
|
|
||||||
rules.push("@media " + sv.mediaRules[mi][0] + "{." + sv.className + "{" + sv.mediaRules[mi][1] + "}}");
|
|
||||||
}
|
|
||||||
for (var ki = 0; ki < sv.keyframes.length; ki++) {
|
|
||||||
rules.push(sv.keyframes[ki][1]);
|
|
||||||
}
|
|
||||||
cssTarget.textContent += rules.join("");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace stub css primitive with real CSSX implementation
|
|
||||||
PRIMITIVES["css"] = function() {
|
|
||||||
var atoms = [];
|
|
||||||
for (var i = 0; i < arguments.length; i++) {
|
|
||||||
var a = arguments[i];
|
|
||||||
if (isNil(a) || a === false) continue;
|
|
||||||
atoms.push(isKw(a) ? a.name : String(a));
|
|
||||||
}
|
|
||||||
if (!atoms.length) return NIL;
|
|
||||||
return resolveStyle(atoms);
|
|
||||||
};
|
|
||||||
|
|
||||||
PRIMITIVES["merge-styles"] = function() {
|
|
||||||
var valid = [];
|
|
||||||
for (var i = 0; i < arguments.length; i++) {
|
|
||||||
if (isStyleValue(arguments[i])) valid.push(arguments[i]);
|
|
||||||
}
|
|
||||||
if (!valid.length) return NIL;
|
|
||||||
if (valid.length === 1) return valid[0];
|
|
||||||
return mergeStyleValues(valid);
|
|
||||||
};
|
|
||||||
"""
|
|
||||||
|
|
||||||
PLATFORM_BOOT_JS = """
|
PLATFORM_BOOT_JS = """
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// Platform interface — Boot (mount, hydrate, scripts, cookies)
|
// Platform interface — Boot (mount, hydrate, scripts, cookies)
|
||||||
@@ -3916,12 +3718,6 @@ PLATFORM_BOOT_JS = """
|
|||||||
r.querySelectorAll('script[type="text/sx"]'));
|
r.querySelectorAll('script[type="text/sx"]'));
|
||||||
}
|
}
|
||||||
|
|
||||||
function queryStyleScripts() {
|
|
||||||
if (!_hasDom) return [];
|
|
||||||
return Array.prototype.slice.call(
|
|
||||||
document.querySelectorAll('script[type="text/sx-styles"]'));
|
|
||||||
}
|
|
||||||
|
|
||||||
function queryPageScripts() {
|
function queryPageScripts() {
|
||||||
if (!_hasDom) return [];
|
if (!_hasDom) return [];
|
||||||
return Array.prototype.slice.call(
|
return Array.prototype.slice.call(
|
||||||
@@ -3953,14 +3749,6 @@ PLATFORM_BOOT_JS = """
|
|||||||
if (_hasDom) document.cookie = "sx-comp-hash=;path=/;max-age=0;SameSite=Lax";
|
if (_hasDom) document.cookie = "sx-comp-hash=;path=/;max-age=0;SameSite=Lax";
|
||||||
}
|
}
|
||||||
|
|
||||||
function setSxStylesCookie(hash) {
|
|
||||||
if (_hasDom) document.cookie = "sx-styles-hash=" + hash + ";path=/;max-age=31536000;SameSite=Lax";
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearSxStylesCookie() {
|
|
||||||
if (_hasDom) document.cookie = "sx-styles-hash=;path=/;max-age=0;SameSite=Lax";
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Env helpers ---
|
// --- Env helpers ---
|
||||||
|
|
||||||
function parseEnvAttr(el) {
|
function parseEnvAttr(el) {
|
||||||
@@ -4009,10 +3797,6 @@ PLATFORM_BOOT_JS = """
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseAndLoadStyleDict(text) {
|
|
||||||
try { loadStyleDict(JSON.parse(text)); }
|
|
||||||
catch (e) { if (typeof console !== "undefined") console.warn("[sx-ref] style dict parse error", e); }
|
|
||||||
}
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def fixups_js(has_html, has_sx, has_dom):
|
def fixups_js(has_html, has_sx, has_dom):
|
||||||
@@ -4039,7 +3823,7 @@ def fixups_js(has_html, has_sx, has_dom):
|
|||||||
return "\n".join(lines)
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
def public_api_js(has_html, has_sx, has_dom, has_engine, has_orch, has_cssx, has_boot, has_parser, adapter_label, has_deps=False, has_router=False):
|
def public_api_js(has_html, has_sx, has_dom, has_engine, has_orch, has_boot, has_parser, adapter_label, has_deps=False, has_router=False):
|
||||||
# Parser: use compiled sxParse from parser.sx, or inline a minimal fallback
|
# Parser: use compiled sxParse from parser.sx, or inline a minimal fallback
|
||||||
if has_parser:
|
if has_parser:
|
||||||
parser = '''
|
parser = '''
|
||||||
|
|||||||
@@ -199,11 +199,6 @@ class PyEmitter:
|
|||||||
"ho-every": "ho_every",
|
"ho-every": "ho_every",
|
||||||
"ho-for-each": "ho_for_each",
|
"ho-for-each": "ho_for_each",
|
||||||
"sf-defstyle": "sf_defstyle",
|
"sf-defstyle": "sf_defstyle",
|
||||||
"sf-defkeyframes": "sf_defkeyframes",
|
|
||||||
"build-keyframes": "build_keyframes",
|
|
||||||
"style-value?": "is_style_value",
|
|
||||||
"style-value-class": "style_value_class",
|
|
||||||
"kf-name": "kf_name",
|
|
||||||
"special-form?": "is_special_form",
|
"special-form?": "is_special_form",
|
||||||
"ho-form?": "is_ho_form",
|
"ho-form?": "is_ho_form",
|
||||||
"strip-prefix": "strip_prefix",
|
"strip-prefix": "strip_prefix",
|
||||||
@@ -1080,7 +1075,7 @@ from typing import Any
|
|||||||
# =========================================================================
|
# =========================================================================
|
||||||
|
|
||||||
from shared.sx.types import (
|
from shared.sx.types import (
|
||||||
NIL, Symbol, Keyword, Lambda, Component, Continuation, Macro, StyleValue,
|
NIL, Symbol, Keyword, Lambda, Component, Continuation, Macro,
|
||||||
HandlerDef, QueryDef, ActionDef, PageDef, _ShiftSignal,
|
HandlerDef, QueryDef, ActionDef, PageDef, _ShiftSignal,
|
||||||
)
|
)
|
||||||
from shared.sx.parser import SxExpr
|
from shared.sx.parser import SxExpr
|
||||||
@@ -1189,8 +1184,6 @@ def type_of(x):
|
|||||||
return "macro"
|
return "macro"
|
||||||
if isinstance(x, _RawHTML):
|
if isinstance(x, _RawHTML):
|
||||||
return "raw-html"
|
return "raw-html"
|
||||||
if isinstance(x, StyleValue):
|
|
||||||
return "style-value"
|
|
||||||
if isinstance(x, Continuation):
|
if isinstance(x, Continuation):
|
||||||
return "continuation"
|
return "continuation"
|
||||||
if isinstance(x, list):
|
if isinstance(x, list):
|
||||||
@@ -1355,14 +1348,6 @@ def is_macro(x):
|
|||||||
return isinstance(x, Macro)
|
return isinstance(x, Macro)
|
||||||
|
|
||||||
|
|
||||||
def is_style_value(x):
|
|
||||||
return isinstance(x, StyleValue)
|
|
||||||
|
|
||||||
|
|
||||||
def style_value_class(x):
|
|
||||||
return x.class_name
|
|
||||||
|
|
||||||
|
|
||||||
def env_has(env, name):
|
def env_has(env, name):
|
||||||
return name in env
|
return name in env
|
||||||
|
|
||||||
@@ -1513,8 +1498,6 @@ def serialize(val):
|
|||||||
if t == "raw-html":
|
if t == "raw-html":
|
||||||
escaped = escape_string(raw_html_content(val))
|
escaped = escape_string(raw_html_content(val))
|
||||||
return '(raw! "' + escaped + '")'
|
return '(raw! "' + escaped + '")'
|
||||||
if t == "style-value":
|
|
||||||
return '"' + style_value_class(val) + '"'
|
|
||||||
if t == "list":
|
if t == "list":
|
||||||
if not val:
|
if not val:
|
||||||
return "()"
|
return "()"
|
||||||
@@ -1534,7 +1517,7 @@ def serialize(val):
|
|||||||
_SPECIAL_FORM_NAMES = frozenset([
|
_SPECIAL_FORM_NAMES = frozenset([
|
||||||
"if", "when", "cond", "case", "and", "or",
|
"if", "when", "cond", "case", "and", "or",
|
||||||
"let", "let*", "lambda", "fn",
|
"let", "let*", "lambda", "fn",
|
||||||
"define", "defcomp", "defmacro", "defstyle", "defkeyframes",
|
"define", "defcomp", "defmacro", "defstyle",
|
||||||
"defhandler", "defpage", "defquery", "defaction", "defrelation",
|
"defhandler", "defpage", "defquery", "defaction", "defrelation",
|
||||||
"begin", "do", "quote", "quasiquote",
|
"begin", "do", "quote", "quasiquote",
|
||||||
"->", "set!",
|
"->", "set!",
|
||||||
@@ -1696,7 +1679,7 @@ def aser_special(name, expr, env):
|
|||||||
fn(item)
|
fn(item)
|
||||||
return results if results else NIL
|
return results if results else NIL
|
||||||
# Definition forms — evaluate for side effects
|
# Definition forms — evaluate for side effects
|
||||||
if name in ("define", "defcomp", "defmacro", "defstyle", "defkeyframes",
|
if name in ("define", "defcomp", "defmacro", "defstyle",
|
||||||
"defhandler", "defpage", "defquery", "defaction", "defrelation"):
|
"defhandler", "defpage", "defquery", "defaction", "defrelation"):
|
||||||
trampoline(eval_expr(expr, env))
|
trampoline(eval_expr(expr, env))
|
||||||
return NIL
|
return NIL
|
||||||
|
|||||||
@@ -123,4 +123,4 @@
|
|||||||
|
|
||||||
(define-boundary-types
|
(define-boundary-types
|
||||||
(list "number" "string" "boolean" "nil" "keyword"
|
(list "number" "string" "boolean" "nil" "keyword"
|
||||||
"list" "dict" "sx-source" "style-value"))
|
"list" "dict" "sx-source"))
|
||||||
|
|||||||
@@ -1,317 +0,0 @@
|
|||||||
;; ==========================================================================
|
|
||||||
;; cssx.sx — On-demand CSS style dictionary
|
|
||||||
;;
|
|
||||||
;; Resolves keyword atoms (e.g. :flex, :gap-4, :hover:bg-sky-200) into
|
|
||||||
;; StyleValue objects with content-addressed class names. CSS rules are
|
|
||||||
;; injected into the document on first use.
|
|
||||||
;;
|
|
||||||
;; The style dictionary is loaded from a JSON blob (typically served
|
|
||||||
;; inline in a <script type="text/sx-styles"> tag) containing:
|
|
||||||
;; a — atom → CSS declarations map
|
|
||||||
;; v — pseudo-variant → CSS pseudo-selector map
|
|
||||||
;; b — responsive breakpoint → media query map
|
|
||||||
;; k — keyframe name → @keyframes rule map
|
|
||||||
;; p — arbitrary patterns: [[regex, template], ...]
|
|
||||||
;; c — child selector prefixes: ["space-x-", "space-y-", ...]
|
|
||||||
;;
|
|
||||||
;; Depends on:
|
|
||||||
;; render.sx — StyleValue type
|
|
||||||
;; ==========================================================================
|
|
||||||
|
|
||||||
|
|
||||||
;; --------------------------------------------------------------------------
|
|
||||||
;; State — populated by load-style-dict
|
|
||||||
;; --------------------------------------------------------------------------
|
|
||||||
|
|
||||||
(define _style-atoms (dict))
|
|
||||||
(define _pseudo-variants (dict))
|
|
||||||
(define _responsive-breakpoints (dict))
|
|
||||||
(define _style-keyframes (dict))
|
|
||||||
(define _arbitrary-patterns (list))
|
|
||||||
(define _child-selector-prefixes (list))
|
|
||||||
(define _style-cache (dict))
|
|
||||||
(define _injected-styles (dict))
|
|
||||||
|
|
||||||
|
|
||||||
;; --------------------------------------------------------------------------
|
|
||||||
;; Load style dictionary from parsed JSON data
|
|
||||||
;; --------------------------------------------------------------------------
|
|
||||||
|
|
||||||
(define load-style-dict
|
|
||||||
(fn (data)
|
|
||||||
(set! _style-atoms (or (get data "a") (dict)))
|
|
||||||
(set! _pseudo-variants (or (get data "v") (dict)))
|
|
||||||
(set! _responsive-breakpoints (or (get data "b") (dict)))
|
|
||||||
(set! _style-keyframes (or (get data "k") (dict)))
|
|
||||||
(set! _child-selector-prefixes (or (get data "c") (list)))
|
|
||||||
;; Compile arbitrary patterns from [regex, template] pairs
|
|
||||||
(set! _arbitrary-patterns
|
|
||||||
(map
|
|
||||||
(fn (pair)
|
|
||||||
(dict "re" (compile-regex (str "^" (first pair) "$"))
|
|
||||||
"tmpl" (nth pair 1)))
|
|
||||||
(or (get data "p") (list))))
|
|
||||||
;; Clear cache on reload
|
|
||||||
(set! _style-cache (dict))))
|
|
||||||
|
|
||||||
|
|
||||||
;; --------------------------------------------------------------------------
|
|
||||||
;; Variant splitting
|
|
||||||
;; --------------------------------------------------------------------------
|
|
||||||
|
|
||||||
(define split-variant
|
|
||||||
(fn (atom)
|
|
||||||
;; Parse variant prefixes: "sm:hover:bg-sky-200" → ["sm:hover", "bg-sky-200"]
|
|
||||||
;; Returns [variant, base] where variant is nil for no variant.
|
|
||||||
|
|
||||||
;; Check responsive prefix first
|
|
||||||
(let ((result nil))
|
|
||||||
(for-each
|
|
||||||
(fn (bp)
|
|
||||||
(when (nil? result)
|
|
||||||
(let ((prefix (str bp ":")))
|
|
||||||
(when (starts-with? atom prefix)
|
|
||||||
(let ((rest-atom (slice atom (len prefix))))
|
|
||||||
;; Check for compound variant (sm:hover:...)
|
|
||||||
(let ((inner-match nil))
|
|
||||||
(for-each
|
|
||||||
(fn (pv)
|
|
||||||
(when (nil? inner-match)
|
|
||||||
(let ((inner-prefix (str pv ":")))
|
|
||||||
(when (starts-with? rest-atom inner-prefix)
|
|
||||||
(set! inner-match
|
|
||||||
(list (str bp ":" pv)
|
|
||||||
(slice rest-atom (len inner-prefix))))))))
|
|
||||||
(keys _pseudo-variants))
|
|
||||||
(set! result
|
|
||||||
(or inner-match (list bp rest-atom)))))))))
|
|
||||||
(keys _responsive-breakpoints))
|
|
||||||
|
|
||||||
(when (nil? result)
|
|
||||||
;; Check pseudo variants
|
|
||||||
(for-each
|
|
||||||
(fn (pv)
|
|
||||||
(when (nil? result)
|
|
||||||
(let ((prefix (str pv ":")))
|
|
||||||
(when (starts-with? atom prefix)
|
|
||||||
(set! result (list pv (slice atom (len prefix))))))))
|
|
||||||
(keys _pseudo-variants)))
|
|
||||||
|
|
||||||
(or result (list nil atom)))))
|
|
||||||
|
|
||||||
|
|
||||||
;; --------------------------------------------------------------------------
|
|
||||||
;; Atom resolution
|
|
||||||
;; --------------------------------------------------------------------------
|
|
||||||
|
|
||||||
(define resolve-atom
|
|
||||||
(fn (atom)
|
|
||||||
;; Look up atom → CSS declarations string, or nil
|
|
||||||
(let ((decls (dict-get _style-atoms atom)))
|
|
||||||
(if (not (nil? decls))
|
|
||||||
decls
|
|
||||||
;; Dynamic keyframes: animate-{name}
|
|
||||||
(if (starts-with? atom "animate-")
|
|
||||||
(let ((kf-name (slice atom 8)))
|
|
||||||
(if (dict-has? _style-keyframes kf-name)
|
|
||||||
(str "animation-name:" kf-name)
|
|
||||||
nil))
|
|
||||||
;; Try arbitrary patterns
|
|
||||||
(let ((match-result nil))
|
|
||||||
(for-each
|
|
||||||
(fn (pat)
|
|
||||||
(when (nil? match-result)
|
|
||||||
(let ((m (regex-match (get pat "re") atom)))
|
|
||||||
(when m
|
|
||||||
(set! match-result
|
|
||||||
(regex-replace-groups (get pat "tmpl") m))))))
|
|
||||||
_arbitrary-patterns)
|
|
||||||
match-result))))))
|
|
||||||
|
|
||||||
|
|
||||||
;; --------------------------------------------------------------------------
|
|
||||||
;; Child selector detection
|
|
||||||
;; --------------------------------------------------------------------------
|
|
||||||
|
|
||||||
(define is-child-selector-atom?
|
|
||||||
(fn (atom)
|
|
||||||
(some
|
|
||||||
(fn (prefix) (starts-with? atom prefix))
|
|
||||||
_child-selector-prefixes)))
|
|
||||||
|
|
||||||
|
|
||||||
;; --------------------------------------------------------------------------
|
|
||||||
;; FNV-1a 32-bit hash → 6 hex chars
|
|
||||||
;; --------------------------------------------------------------------------
|
|
||||||
|
|
||||||
(define hash-style
|
|
||||||
(fn (input)
|
|
||||||
;; FNV-1a 32-bit hash for content-addressed class names
|
|
||||||
(fnv1a-hash input)))
|
|
||||||
|
|
||||||
|
|
||||||
;; --------------------------------------------------------------------------
|
|
||||||
;; Full style resolution pipeline
|
|
||||||
;; --------------------------------------------------------------------------
|
|
||||||
|
|
||||||
(define resolve-style
|
|
||||||
(fn (atoms)
|
|
||||||
;; Resolve a list of atom strings into a StyleValue.
|
|
||||||
;; Uses content-addressed caching.
|
|
||||||
(let ((key (join "\0" atoms)))
|
|
||||||
(let ((cached (dict-get _style-cache key)))
|
|
||||||
(if (not (nil? cached))
|
|
||||||
cached
|
|
||||||
;; Resolve each atom
|
|
||||||
(let ((base-decls (list))
|
|
||||||
(media-rules (list))
|
|
||||||
(pseudo-rules (list))
|
|
||||||
(kf-needed (list)))
|
|
||||||
(for-each
|
|
||||||
(fn (a)
|
|
||||||
(when a
|
|
||||||
(let ((clean (if (starts-with? a ":") (slice a 1) a)))
|
|
||||||
(let ((parts (split-variant clean)))
|
|
||||||
(let ((variant (first parts))
|
|
||||||
(base (nth parts 1))
|
|
||||||
(decls (resolve-atom base)))
|
|
||||||
(when decls
|
|
||||||
;; Check keyframes
|
|
||||||
(when (starts-with? base "animate-")
|
|
||||||
(let ((kf-name (slice base 8)))
|
|
||||||
(when (dict-has? _style-keyframes kf-name)
|
|
||||||
(append! kf-needed
|
|
||||||
(list kf-name (dict-get _style-keyframes kf-name))))))
|
|
||||||
|
|
||||||
(cond
|
|
||||||
(nil? variant)
|
|
||||||
(if (is-child-selector-atom? base)
|
|
||||||
(append! pseudo-rules
|
|
||||||
(list ">:not(:first-child)" decls))
|
|
||||||
(append! base-decls decls))
|
|
||||||
|
|
||||||
(dict-has? _responsive-breakpoints variant)
|
|
||||||
(append! media-rules
|
|
||||||
(list (dict-get _responsive-breakpoints variant) decls))
|
|
||||||
|
|
||||||
(dict-has? _pseudo-variants variant)
|
|
||||||
(append! pseudo-rules
|
|
||||||
(list (dict-get _pseudo-variants variant) decls))
|
|
||||||
|
|
||||||
;; Compound variant: "sm:hover"
|
|
||||||
:else
|
|
||||||
(let ((vparts (split variant ":"))
|
|
||||||
(media-part nil)
|
|
||||||
(pseudo-part nil))
|
|
||||||
(for-each
|
|
||||||
(fn (vp)
|
|
||||||
(cond
|
|
||||||
(dict-has? _responsive-breakpoints vp)
|
|
||||||
(set! media-part (dict-get _responsive-breakpoints vp))
|
|
||||||
(dict-has? _pseudo-variants vp)
|
|
||||||
(set! pseudo-part (dict-get _pseudo-variants vp))))
|
|
||||||
vparts)
|
|
||||||
(when media-part
|
|
||||||
(append! media-rules (list media-part decls)))
|
|
||||||
(when pseudo-part
|
|
||||||
(append! pseudo-rules (list pseudo-part decls)))
|
|
||||||
(when (and (nil? media-part) (nil? pseudo-part))
|
|
||||||
(append! base-decls decls))))))))))
|
|
||||||
atoms)
|
|
||||||
|
|
||||||
;; Build hash input
|
|
||||||
(let ((hash-input (join ";" base-decls)))
|
|
||||||
(for-each
|
|
||||||
(fn (mr)
|
|
||||||
(set! hash-input
|
|
||||||
(str hash-input "@" (first mr) "{" (nth mr 1) "}")))
|
|
||||||
media-rules)
|
|
||||||
(for-each
|
|
||||||
(fn (pr)
|
|
||||||
(set! hash-input
|
|
||||||
(str hash-input (first pr) "{" (nth pr 1) "}")))
|
|
||||||
pseudo-rules)
|
|
||||||
(for-each
|
|
||||||
(fn (kf)
|
|
||||||
(set! hash-input (str hash-input (nth kf 1))))
|
|
||||||
kf-needed)
|
|
||||||
|
|
||||||
(let ((cn (str "sx-" (hash-style hash-input)))
|
|
||||||
(sv (make-style-value cn
|
|
||||||
(join ";" base-decls)
|
|
||||||
media-rules
|
|
||||||
pseudo-rules
|
|
||||||
kf-needed)))
|
|
||||||
(dict-set! _style-cache key sv)
|
|
||||||
;; Inject CSS rules
|
|
||||||
(inject-style-value sv atoms)
|
|
||||||
sv))))))))
|
|
||||||
|
|
||||||
|
|
||||||
;; --------------------------------------------------------------------------
|
|
||||||
;; Merge multiple StyleValues
|
|
||||||
;; --------------------------------------------------------------------------
|
|
||||||
|
|
||||||
(define merge-style-values
|
|
||||||
(fn (styles)
|
|
||||||
(if (= (len styles) 1)
|
|
||||||
(first styles)
|
|
||||||
(let ((all-decls (list))
|
|
||||||
(all-media (list))
|
|
||||||
(all-pseudo (list))
|
|
||||||
(all-kf (list)))
|
|
||||||
(for-each
|
|
||||||
(fn (sv)
|
|
||||||
(when (style-value-declarations sv)
|
|
||||||
(append! all-decls (style-value-declarations sv)))
|
|
||||||
(set! all-media (concat all-media (style-value-media-rules sv)))
|
|
||||||
(set! all-pseudo (concat all-pseudo (style-value-pseudo-rules sv)))
|
|
||||||
(set! all-kf (concat all-kf (style-value-keyframes sv))))
|
|
||||||
styles)
|
|
||||||
|
|
||||||
(let ((hash-input (join ";" all-decls)))
|
|
||||||
(for-each
|
|
||||||
(fn (mr)
|
|
||||||
(set! hash-input
|
|
||||||
(str hash-input "@" (first mr) "{" (nth mr 1) "}")))
|
|
||||||
all-media)
|
|
||||||
(for-each
|
|
||||||
(fn (pr)
|
|
||||||
(set! hash-input
|
|
||||||
(str hash-input (first pr) "{" (nth pr 1) "}")))
|
|
||||||
all-pseudo)
|
|
||||||
(for-each
|
|
||||||
(fn (kf)
|
|
||||||
(set! hash-input (str hash-input (nth kf 1))))
|
|
||||||
all-kf)
|
|
||||||
|
|
||||||
(let ((cn (str "sx-" (hash-style hash-input)))
|
|
||||||
(merged (make-style-value cn
|
|
||||||
(join ";" all-decls)
|
|
||||||
all-media all-pseudo all-kf)))
|
|
||||||
(inject-style-value merged (list))
|
|
||||||
merged))))))
|
|
||||||
|
|
||||||
|
|
||||||
;; --------------------------------------------------------------------------
|
|
||||||
;; Platform interface — CSSX
|
|
||||||
;; --------------------------------------------------------------------------
|
|
||||||
;;
|
|
||||||
;; Hash:
|
|
||||||
;; (fnv1a-hash input) → 6-char hex string (FNV-1a 32-bit)
|
|
||||||
;;
|
|
||||||
;; Regex:
|
|
||||||
;; (compile-regex pattern) → compiled regex object
|
|
||||||
;; (regex-match re str) → match array or nil
|
|
||||||
;; (regex-replace-groups tmpl match) → string with {0},{1},... replaced
|
|
||||||
;;
|
|
||||||
;; StyleValue construction:
|
|
||||||
;; (make-style-value cn decls media pseudo kf) → StyleValue object
|
|
||||||
;; (style-value-declarations sv) → declarations string
|
|
||||||
;; (style-value-media-rules sv) → list of [query, decls] pairs
|
|
||||||
;; (style-value-pseudo-rules sv) → list of [selector, decls] pairs
|
|
||||||
;; (style-value-keyframes sv) → list of [name, rule] pairs
|
|
||||||
;;
|
|
||||||
;; CSS injection:
|
|
||||||
;; (inject-style-value sv atoms) → void (append CSS rules to <style id="sx-css">)
|
|
||||||
;; --------------------------------------------------------------------------
|
|
||||||
@@ -143,7 +143,6 @@
|
|||||||
(= name "defcomp") (sf-defcomp args env)
|
(= name "defcomp") (sf-defcomp args env)
|
||||||
(= name "defmacro") (sf-defmacro args env)
|
(= name "defmacro") (sf-defmacro args env)
|
||||||
(= name "defstyle") (sf-defstyle args env)
|
(= name "defstyle") (sf-defstyle args env)
|
||||||
(= name "defkeyframes") (sf-defkeyframes args env)
|
|
||||||
(= name "defhandler") (sf-defhandler args env)
|
(= name "defhandler") (sf-defhandler args env)
|
||||||
(= name "defpage") (sf-defpage args env)
|
(= name "defpage") (sf-defpage args env)
|
||||||
(= name "defquery") (sf-defquery args env)
|
(= name "defquery") (sf-defquery args env)
|
||||||
@@ -559,23 +558,13 @@
|
|||||||
|
|
||||||
(define sf-defstyle
|
(define sf-defstyle
|
||||||
(fn (args env)
|
(fn (args env)
|
||||||
;; (defstyle name expr) — bind name to evaluated expr (typically a StyleValue)
|
;; (defstyle name expr) — bind name to evaluated expr (string, function, etc.)
|
||||||
(let ((name-sym (first args))
|
(let ((name-sym (first args))
|
||||||
(value (trampoline (eval-expr (nth args 1) env))))
|
(value (trampoline (eval-expr (nth args 1) env))))
|
||||||
(env-set! env (symbol-name name-sym) value)
|
(env-set! env (symbol-name name-sym) value)
|
||||||
value)))
|
value)))
|
||||||
|
|
||||||
|
|
||||||
(define sf-defkeyframes
|
|
||||||
(fn (args env)
|
|
||||||
;; (defkeyframes name (selector body) ...) — build @keyframes rule,
|
|
||||||
;; register in keyframes dict, return StyleValue.
|
|
||||||
;; Delegates to platform: build-keyframes returns a StyleValue.
|
|
||||||
(let ((kf-name (symbol-name (first args)))
|
|
||||||
(steps (rest args)))
|
|
||||||
(build-keyframes kf-name steps env))))
|
|
||||||
|
|
||||||
|
|
||||||
(define sf-begin
|
(define sf-begin
|
||||||
(fn (args env)
|
(fn (args env)
|
||||||
(if (empty? args)
|
(if (empty? args)
|
||||||
@@ -931,9 +920,6 @@
|
|||||||
;; (zip lists...) → list of tuples
|
;; (zip lists...) → list of tuples
|
||||||
;;
|
;;
|
||||||
;;
|
;;
|
||||||
;; CSSX (style system):
|
|
||||||
;; (build-keyframes name steps env) → StyleValue (platform builds @keyframes)
|
|
||||||
;;
|
|
||||||
;; Dynamic wind (for dynamic-wind):
|
;; Dynamic wind (for dynamic-wind):
|
||||||
;; (push-wind! before after) → void (push wind record onto stack)
|
;; (push-wind! before after) → void (push wind record onto stack)
|
||||||
;; (pop-wind!) → void (pop wind record from stack)
|
;; (pop-wind!) → void (pop wind record from stack)
|
||||||
|
|||||||
@@ -541,18 +541,6 @@
|
|||||||
;; Stdlib — Style
|
;; Stdlib — Style
|
||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(define-module :stdlib.style)
|
|
||||||
|
|
||||||
(define-primitive "css"
|
|
||||||
:params (&rest atoms)
|
|
||||||
:returns "style-value"
|
|
||||||
:doc "Resolve style atoms to a StyleValue with className and CSS declarations.
|
|
||||||
Atoms are keywords or strings: (css :flex :gap-4 :hover:bg-sky-200).")
|
|
||||||
|
|
||||||
(define-primitive "merge-styles"
|
|
||||||
:params (&rest styles)
|
|
||||||
:returns "style-value"
|
|
||||||
:doc "Merge multiple StyleValues into one combined StyleValue.")
|
|
||||||
|
|
||||||
|
|
||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -74,7 +74,7 @@
|
|||||||
(define definition-form?
|
(define definition-form?
|
||||||
(fn (name)
|
(fn (name)
|
||||||
(or (= name "define") (= name "defcomp") (= name "defmacro")
|
(or (= name "define") (= name "defcomp") (= name "defmacro")
|
||||||
(= name "defstyle") (= name "defkeyframes") (= name "defhandler"))))
|
(= name "defstyle") (= name "defhandler"))))
|
||||||
|
|
||||||
|
|
||||||
(define parse-element-args
|
(define parse-element-args
|
||||||
@@ -116,9 +116,6 @@
|
|||||||
""
|
""
|
||||||
;; Nil values — skip
|
;; Nil values — skip
|
||||||
(nil? val) ""
|
(nil? val) ""
|
||||||
;; StyleValue on :style → emit as class
|
|
||||||
(and (= key "style") (style-value? val))
|
|
||||||
(str " class=\"" (style-value-class val) "\"")
|
|
||||||
;; Normal attr
|
;; Normal attr
|
||||||
:else (str " " key "=\"" (escape-attr (str val)) "\""))))
|
:else (str " " key "=\"" (escape-attr (str val)) "\""))))
|
||||||
(keys attrs)))))
|
(keys attrs)))))
|
||||||
@@ -202,10 +199,6 @@
|
|||||||
;; (escape-attr s) → attribute-value-escaped string
|
;; (escape-attr s) → attribute-value-escaped string
|
||||||
;; (raw-html-content r) → unwrap RawHTML marker to string
|
;; (raw-html-content r) → unwrap RawHTML marker to string
|
||||||
;;
|
;;
|
||||||
;; StyleValue:
|
|
||||||
;; (style-value? x) → boolean (is x a StyleValue?)
|
|
||||||
;; (style-value-class sv) → string (CSS class name)
|
|
||||||
;;
|
|
||||||
;; Serialization:
|
;; Serialization:
|
||||||
;; (serialize val) → SX source string representation of val
|
;; (serialize val) → SX source string representation of val
|
||||||
;;
|
;;
|
||||||
|
|||||||
@@ -363,20 +363,12 @@
|
|||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(define-special-form "defstyle"
|
(define-special-form "defstyle"
|
||||||
:syntax (defstyle name atoms ...)
|
:syntax (defstyle name expr)
|
||||||
:doc "Define a named style. Evaluates atoms to a StyleValue and binds
|
:doc "Define a named style value. Evaluates expr and binds the result
|
||||||
it to name in the environment."
|
to name in the environment. The value is typically a class string
|
||||||
|
or a function that returns class strings."
|
||||||
:tail-position "none"
|
:tail-position "none"
|
||||||
:example "(defstyle card-style :rounded-lg :shadow-md :p-4 :bg-white)")
|
:example "(defstyle card-style \"rounded-lg shadow-md p-4 bg-white\")")
|
||||||
|
|
||||||
(define-special-form "defkeyframes"
|
|
||||||
:syntax (defkeyframes name steps ...)
|
|
||||||
:doc "Define a CSS @keyframes animation. Steps are (percentage properties ...)
|
|
||||||
pairs. Produces a StyleValue with the animation name and keyframe rules."
|
|
||||||
:tail-position "none"
|
|
||||||
:example "(defkeyframes fade-in
|
|
||||||
(0 :opacity-0)
|
|
||||||
(100 :opacity-100))")
|
|
||||||
|
|
||||||
(define-special-form "defhandler"
|
(define-special-form "defhandler"
|
||||||
:syntax (defhandler name (&key params ...) body)
|
:syntax (defhandler name (&key params ...) body)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ from typing import Any
|
|||||||
# =========================================================================
|
# =========================================================================
|
||||||
|
|
||||||
from shared.sx.types import (
|
from shared.sx.types import (
|
||||||
NIL, Symbol, Keyword, Lambda, Component, Continuation, Macro, StyleValue,
|
NIL, Symbol, Keyword, Lambda, Component, Continuation, Macro,
|
||||||
HandlerDef, QueryDef, ActionDef, PageDef, _ShiftSignal,
|
HandlerDef, QueryDef, ActionDef, PageDef, _ShiftSignal,
|
||||||
)
|
)
|
||||||
from shared.sx.parser import SxExpr
|
from shared.sx.parser import SxExpr
|
||||||
@@ -126,8 +126,6 @@ def type_of(x):
|
|||||||
return "macro"
|
return "macro"
|
||||||
if isinstance(x, _RawHTML):
|
if isinstance(x, _RawHTML):
|
||||||
return "raw-html"
|
return "raw-html"
|
||||||
if isinstance(x, StyleValue):
|
|
||||||
return "style-value"
|
|
||||||
if isinstance(x, Continuation):
|
if isinstance(x, Continuation):
|
||||||
return "continuation"
|
return "continuation"
|
||||||
if isinstance(x, list):
|
if isinstance(x, list):
|
||||||
@@ -292,14 +290,6 @@ def is_macro(x):
|
|||||||
return isinstance(x, Macro)
|
return isinstance(x, Macro)
|
||||||
|
|
||||||
|
|
||||||
def is_style_value(x):
|
|
||||||
return isinstance(x, StyleValue)
|
|
||||||
|
|
||||||
|
|
||||||
def style_value_class(x):
|
|
||||||
return x.class_name
|
|
||||||
|
|
||||||
|
|
||||||
def env_has(env, name):
|
def env_has(env, name):
|
||||||
return name in env
|
return name in env
|
||||||
|
|
||||||
@@ -450,8 +440,6 @@ def serialize(val):
|
|||||||
if t == "raw-html":
|
if t == "raw-html":
|
||||||
escaped = escape_string(raw_html_content(val))
|
escaped = escape_string(raw_html_content(val))
|
||||||
return '(raw! "' + escaped + '")'
|
return '(raw! "' + escaped + '")'
|
||||||
if t == "style-value":
|
|
||||||
return '"' + style_value_class(val) + '"'
|
|
||||||
if t == "list":
|
if t == "list":
|
||||||
if not val:
|
if not val:
|
||||||
return "()"
|
return "()"
|
||||||
@@ -471,7 +459,7 @@ def serialize(val):
|
|||||||
_SPECIAL_FORM_NAMES = frozenset([
|
_SPECIAL_FORM_NAMES = frozenset([
|
||||||
"if", "when", "cond", "case", "and", "or",
|
"if", "when", "cond", "case", "and", "or",
|
||||||
"let", "let*", "lambda", "fn",
|
"let", "let*", "lambda", "fn",
|
||||||
"define", "defcomp", "defmacro", "defstyle", "defkeyframes",
|
"define", "defcomp", "defmacro", "defstyle",
|
||||||
"defhandler", "defpage", "defquery", "defaction", "defrelation",
|
"defhandler", "defpage", "defquery", "defaction", "defrelation",
|
||||||
"begin", "do", "quote", "quasiquote",
|
"begin", "do", "quote", "quasiquote",
|
||||||
"->", "set!",
|
"->", "set!",
|
||||||
@@ -633,7 +621,7 @@ def aser_special(name, expr, env):
|
|||||||
fn(item)
|
fn(item)
|
||||||
return results if results else NIL
|
return results if results else NIL
|
||||||
# Definition forms — evaluate for side effects
|
# Definition forms — evaluate for side effects
|
||||||
if name in ("define", "defcomp", "defmacro", "defstyle", "defkeyframes",
|
if name in ("define", "defcomp", "defmacro", "defstyle",
|
||||||
"defhandler", "defpage", "defquery", "defaction", "defrelation"):
|
"defhandler", "defpage", "defquery", "defaction", "defrelation"):
|
||||||
trampoline(eval_expr(expr, env))
|
trampoline(eval_expr(expr, env))
|
||||||
return NIL
|
return NIL
|
||||||
@@ -955,7 +943,7 @@ trampoline = lambda val: (lambda result: (trampoline(eval_expr(thunk_expr(result
|
|||||||
eval_expr = lambda expr, env: _sx_case(type_of(expr), [('number', lambda: expr), ('string', lambda: expr), ('boolean', lambda: expr), ('nil', lambda: NIL), ('symbol', lambda: (lambda name: (env_get(env, name) if sx_truthy(env_has(env, name)) else (get_primitive(name) if sx_truthy(is_primitive(name)) else (True if sx_truthy((name == 'true')) else (False if sx_truthy((name == 'false')) else (NIL if sx_truthy((name == 'nil')) else error(sx_str('Undefined symbol: ', name))))))))(symbol_name(expr))), ('keyword', lambda: keyword_name(expr)), ('dict', lambda: map_dict(lambda k, v: trampoline(eval_expr(v, env)), expr)), ('list', lambda: ([] if sx_truthy(empty_p(expr)) else eval_list(expr, env))), (None, lambda: expr)])
|
eval_expr = lambda expr, env: _sx_case(type_of(expr), [('number', lambda: expr), ('string', lambda: expr), ('boolean', lambda: expr), ('nil', lambda: NIL), ('symbol', lambda: (lambda name: (env_get(env, name) if sx_truthy(env_has(env, name)) else (get_primitive(name) if sx_truthy(is_primitive(name)) else (True if sx_truthy((name == 'true')) else (False if sx_truthy((name == 'false')) else (NIL if sx_truthy((name == 'nil')) else error(sx_str('Undefined symbol: ', name))))))))(symbol_name(expr))), ('keyword', lambda: keyword_name(expr)), ('dict', lambda: map_dict(lambda k, v: trampoline(eval_expr(v, env)), expr)), ('list', lambda: ([] if sx_truthy(empty_p(expr)) else eval_list(expr, env))), (None, lambda: expr)])
|
||||||
|
|
||||||
# eval-list
|
# eval-list
|
||||||
eval_list = lambda expr, env: (lambda head: (lambda args: (map(lambda x: trampoline(eval_expr(x, env)), expr) if sx_truthy((not sx_truthy(((type_of(head) == 'symbol') if sx_truthy((type_of(head) == 'symbol')) else ((type_of(head) == 'lambda') if sx_truthy((type_of(head) == 'lambda')) else (type_of(head) == 'list')))))) else ((lambda name: (sf_if(args, env) if sx_truthy((name == 'if')) else (sf_when(args, env) if sx_truthy((name == 'when')) else (sf_cond(args, env) if sx_truthy((name == 'cond')) else (sf_case(args, env) if sx_truthy((name == 'case')) else (sf_and(args, env) if sx_truthy((name == 'and')) else (sf_or(args, env) if sx_truthy((name == 'or')) else (sf_let(args, env) if sx_truthy((name == 'let')) else (sf_let(args, env) if sx_truthy((name == 'let*')) else (sf_letrec(args, env) if sx_truthy((name == 'letrec')) else (sf_lambda(args, env) if sx_truthy((name == 'lambda')) else (sf_lambda(args, env) if sx_truthy((name == 'fn')) else (sf_define(args, env) if sx_truthy((name == 'define')) else (sf_defcomp(args, env) if sx_truthy((name == 'defcomp')) else (sf_defmacro(args, env) if sx_truthy((name == 'defmacro')) else (sf_defstyle(args, env) if sx_truthy((name == 'defstyle')) else (sf_defkeyframes(args, env) if sx_truthy((name == 'defkeyframes')) else (sf_defhandler(args, env) if sx_truthy((name == 'defhandler')) else (sf_defpage(args, env) if sx_truthy((name == 'defpage')) else (sf_defquery(args, env) if sx_truthy((name == 'defquery')) else (sf_defaction(args, env) if sx_truthy((name == 'defaction')) else (sf_begin(args, env) if sx_truthy((name == 'begin')) else (sf_begin(args, env) if sx_truthy((name == 'do')) else (sf_quote(args, env) if sx_truthy((name == 'quote')) else (sf_quasiquote(args, env) if sx_truthy((name == 'quasiquote')) else (sf_thread_first(args, env) if sx_truthy((name == '->')) else (sf_set_bang(args, env) if sx_truthy((name == 'set!')) else (sf_reset(args, env) if sx_truthy((name == 'reset')) else (sf_shift(args, env) if sx_truthy((name == 'shift')) else (sf_dynamic_wind(args, env) if sx_truthy((name == 'dynamic-wind')) else (ho_map(args, env) if sx_truthy((name == 'map')) else (ho_map_indexed(args, env) if sx_truthy((name == 'map-indexed')) else (ho_filter(args, env) if sx_truthy((name == 'filter')) else (ho_reduce(args, env) if sx_truthy((name == 'reduce')) else (ho_some(args, env) if sx_truthy((name == 'some')) else (ho_every(args, env) if sx_truthy((name == 'every?')) else (ho_for_each(args, env) if sx_truthy((name == 'for-each')) else ((lambda mac: make_thunk(expand_macro(mac, args, env), env))(env_get(env, name)) if sx_truthy((env_has(env, name) if not sx_truthy(env_has(env, name)) else is_macro(env_get(env, name)))) else (render_expr(expr, env) if sx_truthy(is_render_expr(expr)) else eval_call(head, args, env))))))))))))))))))))))))))))))))))))))))(symbol_name(head)) if sx_truthy((type_of(head) == 'symbol')) else eval_call(head, args, env))))(rest(expr)))(first(expr))
|
eval_list = lambda expr, env: (lambda head: (lambda args: (map(lambda x: trampoline(eval_expr(x, env)), expr) if sx_truthy((not sx_truthy(((type_of(head) == 'symbol') if sx_truthy((type_of(head) == 'symbol')) else ((type_of(head) == 'lambda') if sx_truthy((type_of(head) == 'lambda')) else (type_of(head) == 'list')))))) else ((lambda name: (sf_if(args, env) if sx_truthy((name == 'if')) else (sf_when(args, env) if sx_truthy((name == 'when')) else (sf_cond(args, env) if sx_truthy((name == 'cond')) else (sf_case(args, env) if sx_truthy((name == 'case')) else (sf_and(args, env) if sx_truthy((name == 'and')) else (sf_or(args, env) if sx_truthy((name == 'or')) else (sf_let(args, env) if sx_truthy((name == 'let')) else (sf_let(args, env) if sx_truthy((name == 'let*')) else (sf_letrec(args, env) if sx_truthy((name == 'letrec')) else (sf_lambda(args, env) if sx_truthy((name == 'lambda')) else (sf_lambda(args, env) if sx_truthy((name == 'fn')) else (sf_define(args, env) if sx_truthy((name == 'define')) else (sf_defcomp(args, env) if sx_truthy((name == 'defcomp')) else (sf_defmacro(args, env) if sx_truthy((name == 'defmacro')) else (sf_defstyle(args, env) if sx_truthy((name == 'defstyle')) else (sf_defhandler(args, env) if sx_truthy((name == 'defhandler')) else (sf_defpage(args, env) if sx_truthy((name == 'defpage')) else (sf_defquery(args, env) if sx_truthy((name == 'defquery')) else (sf_defaction(args, env) if sx_truthy((name == 'defaction')) else (sf_begin(args, env) if sx_truthy((name == 'begin')) else (sf_begin(args, env) if sx_truthy((name == 'do')) else (sf_quote(args, env) if sx_truthy((name == 'quote')) else (sf_quasiquote(args, env) if sx_truthy((name == 'quasiquote')) else (sf_thread_first(args, env) if sx_truthy((name == '->')) else (sf_set_bang(args, env) if sx_truthy((name == 'set!')) else (sf_reset(args, env) if sx_truthy((name == 'reset')) else (sf_shift(args, env) if sx_truthy((name == 'shift')) else (sf_dynamic_wind(args, env) if sx_truthy((name == 'dynamic-wind')) else (ho_map(args, env) if sx_truthy((name == 'map')) else (ho_map_indexed(args, env) if sx_truthy((name == 'map-indexed')) else (ho_filter(args, env) if sx_truthy((name == 'filter')) else (ho_reduce(args, env) if sx_truthy((name == 'reduce')) else (ho_some(args, env) if sx_truthy((name == 'some')) else (ho_every(args, env) if sx_truthy((name == 'every?')) else (ho_for_each(args, env) if sx_truthy((name == 'for-each')) else ((lambda mac: make_thunk(expand_macro(mac, args, env), env))(env_get(env, name)) if sx_truthy((env_has(env, name) if not sx_truthy(env_has(env, name)) else is_macro(env_get(env, name)))) else (render_expr(expr, env) if sx_truthy(is_render_expr(expr)) else eval_call(head, args, env)))))))))))))))))))))))))))))))))))))))(symbol_name(head)) if sx_truthy((type_of(head) == 'symbol')) else eval_call(head, args, env))))(rest(expr)))(first(expr))
|
||||||
|
|
||||||
# eval-call
|
# eval-call
|
||||||
eval_call = lambda head, args, env: (lambda f: (lambda evaluated_args: (apply(f, evaluated_args) if sx_truthy((is_callable(f) if not sx_truthy(is_callable(f)) else ((not sx_truthy(is_lambda(f))) if not sx_truthy((not sx_truthy(is_lambda(f)))) else (not sx_truthy(is_component(f)))))) else (call_lambda(f, evaluated_args, env) if sx_truthy(is_lambda(f)) else (call_component(f, args, env) if sx_truthy(is_component(f)) else error(sx_str('Not callable: ', inspect(f)))))))(map(lambda a: trampoline(eval_expr(a, env)), args)))(trampoline(eval_expr(head, env)))
|
eval_call = lambda head, args, env: (lambda f: (lambda evaluated_args: (apply(f, evaluated_args) if sx_truthy((is_callable(f) if not sx_truthy(is_callable(f)) else ((not sx_truthy(is_lambda(f))) if not sx_truthy((not sx_truthy(is_lambda(f)))) else (not sx_truthy(is_component(f)))))) else (call_lambda(f, evaluated_args, env) if sx_truthy(is_lambda(f)) else (call_component(f, args, env) if sx_truthy(is_component(f)) else error(sx_str('Not callable: ', inspect(f)))))))(map(lambda a: trampoline(eval_expr(a, env)), args)))(trampoline(eval_expr(head, env)))
|
||||||
@@ -1051,9 +1039,6 @@ def parse_macro_params(params_expr):
|
|||||||
# sf-defstyle
|
# sf-defstyle
|
||||||
sf_defstyle = lambda args, env: (lambda name_sym: (lambda value: _sx_begin(_sx_dict_set(env, symbol_name(name_sym), value), value))(trampoline(eval_expr(nth(args, 1), env))))(first(args))
|
sf_defstyle = lambda args, env: (lambda name_sym: (lambda value: _sx_begin(_sx_dict_set(env, symbol_name(name_sym), value), value))(trampoline(eval_expr(nth(args, 1), env))))(first(args))
|
||||||
|
|
||||||
# sf-defkeyframes
|
|
||||||
sf_defkeyframes = lambda args, env: (lambda kf_name: (lambda steps: build_keyframes(kf_name, steps, env))(rest(args)))(symbol_name(first(args)))
|
|
||||||
|
|
||||||
# sf-begin
|
# sf-begin
|
||||||
sf_begin = lambda args, env: (NIL if sx_truthy(empty_p(args)) else _sx_begin(for_each(lambda e: trampoline(eval_expr(e, env)), slice(args, 0, (len(args) - 1))), make_thunk(last(args), env)))
|
sf_begin = lambda args, env: (NIL if sx_truthy(empty_p(args)) else _sx_begin(for_each(lambda e: trampoline(eval_expr(e, env)), slice(args, 0, (len(args) - 1))), make_thunk(last(args), env)))
|
||||||
|
|
||||||
@@ -1164,13 +1149,13 @@ VOID_ELEMENTS = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'li
|
|||||||
BOOLEAN_ATTRS = ['async', 'autofocus', 'autoplay', 'checked', 'controls', 'default', 'defer', 'disabled', 'formnovalidate', 'hidden', 'inert', 'ismap', 'loop', 'multiple', 'muted', 'nomodule', 'novalidate', 'open', 'playsinline', 'readonly', 'required', 'reversed', 'selected']
|
BOOLEAN_ATTRS = ['async', 'autofocus', 'autoplay', 'checked', 'controls', 'default', 'defer', 'disabled', 'formnovalidate', 'hidden', 'inert', 'ismap', 'loop', 'multiple', 'muted', 'nomodule', 'novalidate', 'open', 'playsinline', 'readonly', 'required', 'reversed', 'selected']
|
||||||
|
|
||||||
# definition-form?
|
# definition-form?
|
||||||
is_definition_form = lambda name: ((name == 'define') if sx_truthy((name == 'define')) else ((name == 'defcomp') if sx_truthy((name == 'defcomp')) else ((name == 'defmacro') if sx_truthy((name == 'defmacro')) else ((name == 'defstyle') if sx_truthy((name == 'defstyle')) else ((name == 'defkeyframes') if sx_truthy((name == 'defkeyframes')) else (name == 'defhandler'))))))
|
is_definition_form = lambda name: ((name == 'define') if sx_truthy((name == 'define')) else ((name == 'defcomp') if sx_truthy((name == 'defcomp')) else ((name == 'defmacro') if sx_truthy((name == 'defmacro')) else ((name == 'defstyle') if sx_truthy((name == 'defstyle')) else (name == 'defhandler')))))
|
||||||
|
|
||||||
# parse-element-args
|
# parse-element-args
|
||||||
parse_element_args = lambda args, env: (lambda attrs: (lambda children: _sx_begin(reduce(lambda state, arg: (lambda skip: (assoc(state, 'skip', False, 'i', (get(state, 'i') + 1)) if sx_truthy(skip) else ((lambda val: _sx_begin(_sx_dict_set(attrs, keyword_name(arg), val), assoc(state, 'skip', True, 'i', (get(state, 'i') + 1))))(trampoline(eval_expr(nth(args, (get(state, 'i') + 1)), env))) if sx_truthy(((type_of(arg) == 'keyword') if not sx_truthy((type_of(arg) == 'keyword')) else ((get(state, 'i') + 1) < len(args)))) else _sx_begin(_sx_append(children, arg), assoc(state, 'i', (get(state, 'i') + 1))))))(get(state, 'skip')), {'i': 0, 'skip': False}, args), [attrs, children]))([]))({})
|
parse_element_args = lambda args, env: (lambda attrs: (lambda children: _sx_begin(reduce(lambda state, arg: (lambda skip: (assoc(state, 'skip', False, 'i', (get(state, 'i') + 1)) if sx_truthy(skip) else ((lambda val: _sx_begin(_sx_dict_set(attrs, keyword_name(arg), val), assoc(state, 'skip', True, 'i', (get(state, 'i') + 1))))(trampoline(eval_expr(nth(args, (get(state, 'i') + 1)), env))) if sx_truthy(((type_of(arg) == 'keyword') if not sx_truthy((type_of(arg) == 'keyword')) else ((get(state, 'i') + 1) < len(args)))) else _sx_begin(_sx_append(children, arg), assoc(state, 'i', (get(state, 'i') + 1))))))(get(state, 'skip')), {'i': 0, 'skip': False}, args), [attrs, children]))([]))({})
|
||||||
|
|
||||||
# render-attrs
|
# render-attrs
|
||||||
render_attrs = lambda attrs: join('', map(lambda key: (lambda val: (sx_str(' ', key) if sx_truthy((contains_p(BOOLEAN_ATTRS, key) if not sx_truthy(contains_p(BOOLEAN_ATTRS, key)) else val)) else ('' if sx_truthy((contains_p(BOOLEAN_ATTRS, key) if not sx_truthy(contains_p(BOOLEAN_ATTRS, key)) else (not sx_truthy(val)))) else ('' if sx_truthy(is_nil(val)) else (sx_str(' class="', style_value_class(val), '"') if sx_truthy(((key == 'style') if not sx_truthy((key == 'style')) else is_style_value(val))) else sx_str(' ', key, '="', escape_attr(sx_str(val)), '"'))))))(dict_get(attrs, key)), keys(attrs)))
|
render_attrs = lambda attrs: join('', map(lambda key: (lambda val: (sx_str(' ', key) if sx_truthy((contains_p(BOOLEAN_ATTRS, key) if not sx_truthy(contains_p(BOOLEAN_ATTRS, key)) else val)) else ('' if sx_truthy((contains_p(BOOLEAN_ATTRS, key) if not sx_truthy(contains_p(BOOLEAN_ATTRS, key)) else (not sx_truthy(val)))) else ('' if sx_truthy(is_nil(val)) else sx_str(' ', key, '="', escape_attr(sx_str(val)), '"')))))(dict_get(attrs, key)), keys(attrs)))
|
||||||
|
|
||||||
# eval-cond
|
# eval-cond
|
||||||
eval_cond = lambda clauses, env: (eval_cond_scheme(clauses, env) if sx_truthy(((not sx_truthy(empty_p(clauses))) if not sx_truthy((not sx_truthy(empty_p(clauses)))) else ((type_of(first(clauses)) == 'list') if not sx_truthy((type_of(first(clauses)) == 'list')) else (len(first(clauses)) == 2)))) else eval_cond_clojure(clauses, env))
|
eval_cond = lambda clauses, env: (eval_cond_scheme(clauses, env) if sx_truthy(((not sx_truthy(empty_p(clauses))) if not sx_truthy((not sx_truthy(empty_p(clauses)))) else ((type_of(first(clauses)) == 'list') if not sx_truthy((type_of(first(clauses)) == 'list')) else (len(first(clauses)) == 2)))) else eval_cond_clojure(clauses, env))
|
||||||
@@ -1191,10 +1176,10 @@ process_bindings = lambda bindings, env: (lambda local: _sx_begin(for_each(lambd
|
|||||||
render_to_html = lambda expr, env: _sx_case(type_of(expr), [('nil', lambda: ''), ('string', lambda: escape_html(expr)), ('number', lambda: sx_str(expr)), ('boolean', lambda: ('true' if sx_truthy(expr) else 'false')), ('list', lambda: ('' if sx_truthy(empty_p(expr)) else render_list_to_html(expr, env))), ('symbol', lambda: render_value_to_html(trampoline(eval_expr(expr, env)), env)), ('keyword', lambda: escape_html(keyword_name(expr))), ('raw-html', lambda: raw_html_content(expr)), (None, lambda: render_value_to_html(trampoline(eval_expr(expr, env)), env))])
|
render_to_html = lambda expr, env: _sx_case(type_of(expr), [('nil', lambda: ''), ('string', lambda: escape_html(expr)), ('number', lambda: sx_str(expr)), ('boolean', lambda: ('true' if sx_truthy(expr) else 'false')), ('list', lambda: ('' if sx_truthy(empty_p(expr)) else render_list_to_html(expr, env))), ('symbol', lambda: render_value_to_html(trampoline(eval_expr(expr, env)), env)), ('keyword', lambda: escape_html(keyword_name(expr))), ('raw-html', lambda: raw_html_content(expr)), (None, lambda: render_value_to_html(trampoline(eval_expr(expr, env)), env))])
|
||||||
|
|
||||||
# render-value-to-html
|
# render-value-to-html
|
||||||
render_value_to_html = lambda val, env: _sx_case(type_of(val), [('nil', lambda: ''), ('string', lambda: escape_html(val)), ('number', lambda: sx_str(val)), ('boolean', lambda: ('true' if sx_truthy(val) else 'false')), ('list', lambda: render_list_to_html(val, env)), ('raw-html', lambda: raw_html_content(val)), ('style-value', lambda: style_value_class(val)), (None, lambda: escape_html(sx_str(val)))])
|
render_value_to_html = lambda val, env: _sx_case(type_of(val), [('nil', lambda: ''), ('string', lambda: escape_html(val)), ('number', lambda: sx_str(val)), ('boolean', lambda: ('true' if sx_truthy(val) else 'false')), ('list', lambda: render_list_to_html(val, env)), ('raw-html', lambda: raw_html_content(val)), (None, lambda: escape_html(sx_str(val)))])
|
||||||
|
|
||||||
# RENDER_HTML_FORMS
|
# RENDER_HTML_FORMS
|
||||||
RENDER_HTML_FORMS = ['if', 'when', 'cond', 'case', 'let', 'let*', 'begin', 'do', 'define', 'defcomp', 'defmacro', 'defstyle', 'defkeyframes', 'defhandler', 'map', 'map-indexed', 'filter', 'for-each']
|
RENDER_HTML_FORMS = ['if', 'when', 'cond', 'case', 'let', 'let*', 'begin', 'do', 'define', 'defcomp', 'defmacro', 'defstyle', 'defhandler', 'map', 'map-indexed', 'filter', 'for-each']
|
||||||
|
|
||||||
# render-html-form?
|
# render-html-form?
|
||||||
is_render_html_form = lambda name: contains_p(RENDER_HTML_FORMS, name)
|
is_render_html_form = lambda name: contains_p(RENDER_HTML_FORMS, name)
|
||||||
|
|||||||
@@ -1,782 +0,0 @@
|
|||||||
"""
|
|
||||||
Style dictionary — maps keyword atoms to CSS declarations.
|
|
||||||
|
|
||||||
Pure data. Each key is a Tailwind-compatible class name (used as an sx keyword
|
|
||||||
atom in ``(css :flex :gap-4 :p-2)``), and each value is the CSS declaration(s)
|
|
||||||
that class produces. Declarations are self-contained — no ``--tw-*`` custom
|
|
||||||
properties needed.
|
|
||||||
|
|
||||||
Generated from the codebase's tw.css via ``css_registry.py`` then simplified
|
|
||||||
to remove Tailwind v3 variable indirection.
|
|
||||||
|
|
||||||
Used by:
|
|
||||||
- ``style_resolver.py`` (server) — resolves ``(css ...)`` to StyleValue
|
|
||||||
- ``sx.js`` (client) — same resolution, cached in localStorage
|
|
||||||
"""
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════════
|
|
||||||
# Base atoms — keyword → CSS declarations
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════════
|
|
||||||
#
|
|
||||||
# ~466 atoms covering all utilities used across the codebase.
|
|
||||||
# Variants (hover:*, sm:*, focus:*, etc.) are NOT stored here — the
|
|
||||||
# resolver splits "hover:bg-sky-200" into variant="hover" + atom="bg-sky-200"
|
|
||||||
# and wraps the declaration in the appropriate pseudo/media rule.
|
|
||||||
|
|
||||||
STYLE_ATOMS: dict[str, str] = {
|
|
||||||
# ── Display ──────────────────────────────────────────────────────────
|
|
||||||
"block": "display:block",
|
|
||||||
"inline-block": "display:inline-block",
|
|
||||||
"inline": "display:inline",
|
|
||||||
"flex": "display:flex",
|
|
||||||
"inline-flex": "display:inline-flex",
|
|
||||||
"table": "display:table",
|
|
||||||
"table-row": "display:table-row",
|
|
||||||
"grid": "display:grid",
|
|
||||||
"contents": "display:contents",
|
|
||||||
"hidden": "display:none",
|
|
||||||
|
|
||||||
# ── Position ─────────────────────────────────────────────────────────
|
|
||||||
"static": "position:static",
|
|
||||||
"fixed": "position:fixed",
|
|
||||||
"absolute": "position:absolute",
|
|
||||||
"relative": "position:relative",
|
|
||||||
"inset-0": "inset:0",
|
|
||||||
"top-0": "top:0",
|
|
||||||
"top-1/2": "top:50%",
|
|
||||||
"top-2": "top:.5rem",
|
|
||||||
"top-20": "top:5rem",
|
|
||||||
"top-[8px]": "top:8px",
|
|
||||||
"top-full": "top:100%",
|
|
||||||
"right-2": "right:.5rem",
|
|
||||||
"right-[8px]": "right:8px",
|
|
||||||
"bottom-full": "bottom:100%",
|
|
||||||
"left-1/2": "left:50%",
|
|
||||||
"left-2": "left:.5rem",
|
|
||||||
"-right-2": "right:-.5rem",
|
|
||||||
"-right-3": "right:-.75rem",
|
|
||||||
"-top-1.5": "top:-.375rem",
|
|
||||||
"-top-2": "top:-.5rem",
|
|
||||||
|
|
||||||
# ── Z-Index ──────────────────────────────────────────────────────────
|
|
||||||
"z-10": "z-index:10",
|
|
||||||
"z-40": "z-index:40",
|
|
||||||
"z-50": "z-index:50",
|
|
||||||
|
|
||||||
# ── Grid ─────────────────────────────────────────────────────────────
|
|
||||||
"grid-cols-1": "grid-template-columns:repeat(1,minmax(0,1fr))",
|
|
||||||
"grid-cols-2": "grid-template-columns:repeat(2,minmax(0,1fr))",
|
|
||||||
"grid-cols-3": "grid-template-columns:repeat(3,minmax(0,1fr))",
|
|
||||||
"grid-cols-4": "grid-template-columns:repeat(4,minmax(0,1fr))",
|
|
||||||
"grid-cols-5": "grid-template-columns:repeat(5,minmax(0,1fr))",
|
|
||||||
"grid-cols-6": "grid-template-columns:repeat(6,minmax(0,1fr))",
|
|
||||||
"grid-cols-7": "grid-template-columns:repeat(7,minmax(0,1fr))",
|
|
||||||
"grid-cols-12": "grid-template-columns:repeat(12,minmax(0,1fr))",
|
|
||||||
"col-span-2": "grid-column:span 2/span 2",
|
|
||||||
"col-span-3": "grid-column:span 3/span 3",
|
|
||||||
"col-span-4": "grid-column:span 4/span 4",
|
|
||||||
"col-span-5": "grid-column:span 5/span 5",
|
|
||||||
"col-span-12": "grid-column:span 12/span 12",
|
|
||||||
"col-span-full": "grid-column:1/-1",
|
|
||||||
|
|
||||||
# ── Flexbox ──────────────────────────────────────────────────────────
|
|
||||||
"flex-row": "flex-direction:row",
|
|
||||||
"flex-col": "flex-direction:column",
|
|
||||||
"flex-wrap": "flex-wrap:wrap",
|
|
||||||
"flex-0": "flex:0",
|
|
||||||
"flex-1": "flex:1 1 0%",
|
|
||||||
"flex-shrink-0": "flex-shrink:0",
|
|
||||||
"shrink-0": "flex-shrink:0",
|
|
||||||
"flex-shrink": "flex-shrink:1",
|
|
||||||
|
|
||||||
# ── Alignment ────────────────────────────────────────────────────────
|
|
||||||
"items-start": "align-items:flex-start",
|
|
||||||
"items-end": "align-items:flex-end",
|
|
||||||
"items-center": "align-items:center",
|
|
||||||
"items-baseline": "align-items:baseline",
|
|
||||||
"justify-start": "justify-content:flex-start",
|
|
||||||
"justify-end": "justify-content:flex-end",
|
|
||||||
"justify-center": "justify-content:center",
|
|
||||||
"justify-between": "justify-content:space-between",
|
|
||||||
"self-start": "align-self:flex-start",
|
|
||||||
"self-center": "align-self:center",
|
|
||||||
"place-items-center": "place-items:center",
|
|
||||||
|
|
||||||
# ── Gap ───────────────────────────────────────────────────────────────
|
|
||||||
"gap-px": "gap:1px",
|
|
||||||
"gap-0.5": "gap:.125rem",
|
|
||||||
"gap-1": "gap:.25rem",
|
|
||||||
"gap-1.5": "gap:.375rem",
|
|
||||||
"gap-2": "gap:.5rem",
|
|
||||||
"gap-3": "gap:.75rem",
|
|
||||||
"gap-4": "gap:1rem",
|
|
||||||
"gap-5": "gap:1.25rem",
|
|
||||||
"gap-6": "gap:1.5rem",
|
|
||||||
"gap-8": "gap:2rem",
|
|
||||||
"gap-[4px]": "gap:4px",
|
|
||||||
"gap-[8px]": "gap:8px",
|
|
||||||
"gap-[16px]": "gap:16px",
|
|
||||||
"gap-x-3": "column-gap:.75rem",
|
|
||||||
"gap-y-1": "row-gap:.25rem",
|
|
||||||
|
|
||||||
# ── Margin ───────────────────────────────────────────────────────────
|
|
||||||
"m-0": "margin:0",
|
|
||||||
"m-2": "margin:.5rem",
|
|
||||||
"mx-1": "margin-left:.25rem;margin-right:.25rem",
|
|
||||||
"mx-2": "margin-left:.5rem;margin-right:.5rem",
|
|
||||||
"mx-4": "margin-left:1rem;margin-right:1rem",
|
|
||||||
"mx-auto": "margin-left:auto;margin-right:auto",
|
|
||||||
"my-3": "margin-top:.75rem;margin-bottom:.75rem",
|
|
||||||
"-mb-px": "margin-bottom:-1px",
|
|
||||||
"mb-1": "margin-bottom:.25rem",
|
|
||||||
"mb-2": "margin-bottom:.5rem",
|
|
||||||
"mb-3": "margin-bottom:.75rem",
|
|
||||||
"mb-4": "margin-bottom:1rem",
|
|
||||||
"mb-6": "margin-bottom:1.5rem",
|
|
||||||
"mb-8": "margin-bottom:2rem",
|
|
||||||
"mb-12": "margin-bottom:3rem",
|
|
||||||
"mb-[8px]": "margin-bottom:8px",
|
|
||||||
"mb-[24px]": "margin-bottom:24px",
|
|
||||||
"ml-1": "margin-left:.25rem",
|
|
||||||
"ml-2": "margin-left:.5rem",
|
|
||||||
"ml-4": "margin-left:1rem",
|
|
||||||
"ml-auto": "margin-left:auto",
|
|
||||||
"mr-1": "margin-right:.25rem",
|
|
||||||
"mr-2": "margin-right:.5rem",
|
|
||||||
"mr-3": "margin-right:.75rem",
|
|
||||||
"mt-0.5": "margin-top:.125rem",
|
|
||||||
"mt-1": "margin-top:.25rem",
|
|
||||||
"mt-2": "margin-top:.5rem",
|
|
||||||
"mt-3": "margin-top:.75rem",
|
|
||||||
"mt-4": "margin-top:1rem",
|
|
||||||
"mt-5": "margin-top:1.25rem",
|
|
||||||
"mt-6": "margin-top:1.5rem",
|
|
||||||
"mt-8": "margin-top:2rem",
|
|
||||||
"mt-[8px]": "margin-top:8px",
|
|
||||||
"mt-[16px]": "margin-top:16px",
|
|
||||||
"mt-[32px]": "margin-top:32px",
|
|
||||||
|
|
||||||
# ── Padding ──────────────────────────────────────────────────────────
|
|
||||||
"p-0": "padding:0",
|
|
||||||
"p-1": "padding:.25rem",
|
|
||||||
"p-1.5": "padding:.375rem",
|
|
||||||
"p-2": "padding:.5rem",
|
|
||||||
"p-3": "padding:.75rem",
|
|
||||||
"p-4": "padding:1rem",
|
|
||||||
"p-5": "padding:1.25rem",
|
|
||||||
"p-6": "padding:1.5rem",
|
|
||||||
"p-8": "padding:2rem",
|
|
||||||
"px-1": "padding-left:.25rem;padding-right:.25rem",
|
|
||||||
"px-1.5": "padding-left:.375rem;padding-right:.375rem",
|
|
||||||
"px-2": "padding-left:.5rem;padding-right:.5rem",
|
|
||||||
"px-2.5": "padding-left:.625rem;padding-right:.625rem",
|
|
||||||
"px-3": "padding-left:.75rem;padding-right:.75rem",
|
|
||||||
"px-4": "padding-left:1rem;padding-right:1rem",
|
|
||||||
"px-6": "padding-left:1.5rem;padding-right:1.5rem",
|
|
||||||
"px-[8px]": "padding-left:8px;padding-right:8px",
|
|
||||||
"px-[12px]": "padding-left:12px;padding-right:12px",
|
|
||||||
"px-[16px]": "padding-left:16px;padding-right:16px",
|
|
||||||
"px-[20px]": "padding-left:20px;padding-right:20px",
|
|
||||||
"py-0.5": "padding-top:.125rem;padding-bottom:.125rem",
|
|
||||||
"py-1": "padding-top:.25rem;padding-bottom:.25rem",
|
|
||||||
"py-1.5": "padding-top:.375rem;padding-bottom:.375rem",
|
|
||||||
"py-2": "padding-top:.5rem;padding-bottom:.5rem",
|
|
||||||
"py-3": "padding-top:.75rem;padding-bottom:.75rem",
|
|
||||||
"py-4": "padding-top:1rem;padding-bottom:1rem",
|
|
||||||
"py-6": "padding-top:1.5rem;padding-bottom:1.5rem",
|
|
||||||
"py-8": "padding-top:2rem;padding-bottom:2rem",
|
|
||||||
"py-12": "padding-top:3rem;padding-bottom:3rem",
|
|
||||||
"py-16": "padding-top:4rem;padding-bottom:4rem",
|
|
||||||
"py-[6px]": "padding-top:6px;padding-bottom:6px",
|
|
||||||
"py-[12px]": "padding-top:12px;padding-bottom:12px",
|
|
||||||
"pb-1": "padding-bottom:.25rem",
|
|
||||||
"pb-2": "padding-bottom:.5rem",
|
|
||||||
"pb-3": "padding-bottom:.75rem",
|
|
||||||
"pb-4": "padding-bottom:1rem",
|
|
||||||
"pb-6": "padding-bottom:1.5rem",
|
|
||||||
"pb-8": "padding-bottom:2rem",
|
|
||||||
"pb-[48px]": "padding-bottom:48px",
|
|
||||||
"pl-2": "padding-left:.5rem",
|
|
||||||
"pl-3": "padding-left:.75rem",
|
|
||||||
"pl-5": "padding-left:1.25rem",
|
|
||||||
"pl-6": "padding-left:1.5rem",
|
|
||||||
"pr-1": "padding-right:.25rem",
|
|
||||||
"pr-2": "padding-right:.5rem",
|
|
||||||
"pr-4": "padding-right:1rem",
|
|
||||||
"pt-2": "padding-top:.5rem",
|
|
||||||
"pt-3": "padding-top:.75rem",
|
|
||||||
"pt-4": "padding-top:1rem",
|
|
||||||
"pt-[16px]": "padding-top:16px",
|
|
||||||
|
|
||||||
# ── Width ────────────────────────────────────────────────────────────
|
|
||||||
"w-1": "width:.25rem",
|
|
||||||
"w-2": "width:.5rem",
|
|
||||||
"w-4": "width:1rem",
|
|
||||||
"w-5": "width:1.25rem",
|
|
||||||
"w-6": "width:1.5rem",
|
|
||||||
"w-8": "width:2rem",
|
|
||||||
"w-10": "width:2.5rem",
|
|
||||||
"w-11": "width:2.75rem",
|
|
||||||
"w-12": "width:3rem",
|
|
||||||
"w-14": "width:3.5rem",
|
|
||||||
"w-16": "width:4rem",
|
|
||||||
"w-20": "width:5rem",
|
|
||||||
"w-24": "width:6rem",
|
|
||||||
"w-28": "width:7rem",
|
|
||||||
"w-32": "width:8rem",
|
|
||||||
"w-40": "width:10rem",
|
|
||||||
"w-48": "width:12rem",
|
|
||||||
"w-56": "width:14rem",
|
|
||||||
"w-1/2": "width:50%",
|
|
||||||
"w-1/3": "width:33.333333%",
|
|
||||||
"w-1/4": "width:25%",
|
|
||||||
"w-1/6": "width:16.666667%",
|
|
||||||
"w-2/6": "width:33.333333%",
|
|
||||||
"w-3/4": "width:75%",
|
|
||||||
"w-full": "width:100%",
|
|
||||||
"w-auto": "width:auto",
|
|
||||||
"w-[1em]": "width:1em",
|
|
||||||
"w-[32px]": "width:32px",
|
|
||||||
|
|
||||||
# ── Height ───────────────────────────────────────────────────────────
|
|
||||||
"h-2": "height:.5rem",
|
|
||||||
"h-4": "height:1rem",
|
|
||||||
"h-5": "height:1.25rem",
|
|
||||||
"h-6": "height:1.5rem",
|
|
||||||
"h-8": "height:2rem",
|
|
||||||
"h-10": "height:2.5rem",
|
|
||||||
"h-12": "height:3rem",
|
|
||||||
"h-14": "height:3.5rem",
|
|
||||||
"h-14": "height:3.5rem",
|
|
||||||
"h-16": "height:4rem",
|
|
||||||
"h-24": "height:6rem",
|
|
||||||
"h-28": "height:7rem",
|
|
||||||
"h-48": "height:12rem",
|
|
||||||
"h-64": "height:16rem",
|
|
||||||
"h-full": "height:100%",
|
|
||||||
"h-[1em]": "height:1em",
|
|
||||||
"h-[30vh]": "height:30vh",
|
|
||||||
"h-[32px]": "height:32px",
|
|
||||||
"h-[60vh]": "height:60vh",
|
|
||||||
|
|
||||||
# ── Min/Max Dimensions ───────────────────────────────────────────────
|
|
||||||
"min-w-0": "min-width:0",
|
|
||||||
"min-w-full": "min-width:100%",
|
|
||||||
"min-w-[1.25rem]": "min-width:1.25rem",
|
|
||||||
"min-w-[180px]": "min-width:180px",
|
|
||||||
"min-h-0": "min-height:0",
|
|
||||||
"min-h-20": "min-height:5rem",
|
|
||||||
"min-h-[3rem]": "min-height:3rem",
|
|
||||||
"min-h-[50vh]": "min-height:50vh",
|
|
||||||
"max-w-xs": "max-width:20rem",
|
|
||||||
"max-w-md": "max-width:28rem",
|
|
||||||
"max-w-lg": "max-width:32rem",
|
|
||||||
"max-w-2xl": "max-width:42rem",
|
|
||||||
"max-w-3xl": "max-width:48rem",
|
|
||||||
"max-w-4xl": "max-width:56rem",
|
|
||||||
"max-w-full": "max-width:100%",
|
|
||||||
"max-w-0": "max-width:0",
|
|
||||||
"max-w-none": "max-width:none",
|
|
||||||
"max-w-screen-2xl": "max-width:1536px",
|
|
||||||
"max-w-[360px]": "max-width:360px",
|
|
||||||
"max-w-[768px]": "max-width:768px",
|
|
||||||
"max-w-[640px]": "max-width:640px",
|
|
||||||
"max-h-32": "max-height:8rem",
|
|
||||||
"max-h-64": "max-height:16rem",
|
|
||||||
"max-h-72": "max-height:18rem",
|
|
||||||
"max-h-96": "max-height:24rem",
|
|
||||||
"max-h-none": "max-height:none",
|
|
||||||
"max-h-[448px]": "max-height:448px",
|
|
||||||
"max-h-[50vh]": "max-height:50vh",
|
|
||||||
|
|
||||||
# ── Typography ───────────────────────────────────────────────────────
|
|
||||||
"text-xs": "font-size:.75rem;line-height:1rem",
|
|
||||||
"text-sm": "font-size:.875rem;line-height:1.25rem",
|
|
||||||
"text-base": "font-size:1rem;line-height:1.5rem",
|
|
||||||
"text-md": "font-size:1rem;line-height:1.5rem", # alias for text-base
|
|
||||||
"text-lg": "font-size:1.125rem;line-height:1.75rem",
|
|
||||||
"text-xl": "font-size:1.25rem;line-height:1.75rem",
|
|
||||||
"text-2xl": "font-size:1.5rem;line-height:2rem",
|
|
||||||
"text-3xl": "font-size:1.875rem;line-height:2.25rem",
|
|
||||||
"text-4xl": "font-size:2.25rem;line-height:2.5rem",
|
|
||||||
"text-5xl": "font-size:3rem;line-height:1",
|
|
||||||
"text-6xl": "font-size:3.75rem;line-height:1",
|
|
||||||
"text-8xl": "font-size:6rem;line-height:1",
|
|
||||||
"text-[8px]": "font-size:8px",
|
|
||||||
"text-[9px]": "font-size:9px",
|
|
||||||
"text-[10px]": "font-size:10px",
|
|
||||||
"text-[11px]": "font-size:11px",
|
|
||||||
"text-[13px]": "font-size:13px",
|
|
||||||
"text-[14px]": "font-size:14px",
|
|
||||||
"text-[16px]": "font-size:16px",
|
|
||||||
"text-[18px]": "font-size:18px",
|
|
||||||
"text-[36px]": "font-size:36px",
|
|
||||||
"text-[40px]": "font-size:40px",
|
|
||||||
"text-[0.6rem]": "font-size:.6rem",
|
|
||||||
"text-[0.65rem]": "font-size:.65rem",
|
|
||||||
"text-[0.7rem]": "font-size:.7rem",
|
|
||||||
"font-normal": "font-weight:400",
|
|
||||||
"font-medium": "font-weight:500",
|
|
||||||
"font-semibold": "font-weight:600",
|
|
||||||
"font-bold": "font-weight:700",
|
|
||||||
"font-mono": "font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace",
|
|
||||||
"italic": "font-style:italic",
|
|
||||||
"uppercase": "text-transform:uppercase",
|
|
||||||
"capitalize": "text-transform:capitalize",
|
|
||||||
"tabular-nums": "font-variant-numeric:tabular-nums",
|
|
||||||
"leading-none": "line-height:1",
|
|
||||||
"leading-tight": "line-height:1.25",
|
|
||||||
"leading-snug": "line-height:1.375",
|
|
||||||
"leading-relaxed": "line-height:1.625",
|
|
||||||
"tracking-tight": "letter-spacing:-.025em",
|
|
||||||
"tracking-wide": "letter-spacing:.025em",
|
|
||||||
"tracking-widest": "letter-spacing:.1em",
|
|
||||||
"text-left": "text-align:left",
|
|
||||||
"text-center": "text-align:center",
|
|
||||||
"text-right": "text-align:right",
|
|
||||||
"align-top": "vertical-align:top",
|
|
||||||
|
|
||||||
# ── Text Colors ──────────────────────────────────────────────────────
|
|
||||||
"text-white": "color:rgb(255 255 255)",
|
|
||||||
"text-white/80": "color:rgba(255,255,255,.8)",
|
|
||||||
"text-black": "color:rgb(0 0 0)",
|
|
||||||
"text-stone-300": "color:rgb(214 211 209)",
|
|
||||||
"text-stone-400": "color:rgb(168 162 158)",
|
|
||||||
"text-stone-500": "color:rgb(120 113 108)",
|
|
||||||
"text-stone-600": "color:rgb(87 83 78)",
|
|
||||||
"text-stone-700": "color:rgb(68 64 60)",
|
|
||||||
"text-stone-800": "color:rgb(41 37 36)",
|
|
||||||
"text-stone-900": "color:rgb(28 25 23)",
|
|
||||||
"text-slate-400": "color:rgb(148 163 184)",
|
|
||||||
"text-gray-500": "color:rgb(107 114 128)",
|
|
||||||
"text-gray-600": "color:rgb(75 85 99)",
|
|
||||||
"text-red-500": "color:rgb(239 68 68)",
|
|
||||||
"text-red-600": "color:rgb(220 38 38)",
|
|
||||||
"text-red-700": "color:rgb(185 28 28)",
|
|
||||||
"text-red-800": "color:rgb(153 27 27)",
|
|
||||||
"text-rose-500": "color:rgb(244 63 94)",
|
|
||||||
"text-rose-600": "color:rgb(225 29 72)",
|
|
||||||
"text-rose-700": "color:rgb(190 18 60)",
|
|
||||||
"text-rose-800": "color:rgb(159 18 57)",
|
|
||||||
"text-rose-800/80": "color:rgba(159,18,57,.8)",
|
|
||||||
"text-rose-900": "color:rgb(136 19 55)",
|
|
||||||
"text-orange-600": "color:rgb(234 88 12)",
|
|
||||||
"text-amber-500": "color:rgb(245 158 11)",
|
|
||||||
"text-amber-600": "color:rgb(217 119 6)",
|
|
||||||
"text-amber-700": "color:rgb(180 83 9)",
|
|
||||||
"text-amber-800": "color:rgb(146 64 14)",
|
|
||||||
"text-yellow-700": "color:rgb(161 98 7)",
|
|
||||||
"text-green-600": "color:rgb(22 163 74)",
|
|
||||||
"text-green-800": "color:rgb(22 101 52)",
|
|
||||||
"text-green-900": "color:rgb(20 83 45)",
|
|
||||||
"text-neutral-400": "color:rgb(163 163 163)",
|
|
||||||
"text-neutral-500": "color:rgb(115 115 115)",
|
|
||||||
"text-neutral-600": "color:rgb(82 82 82)",
|
|
||||||
"text-emerald-500": "color:rgb(16 185 129)",
|
|
||||||
"text-emerald-600": "color:rgb(5 150 105)",
|
|
||||||
"text-emerald-700": "color:rgb(4 120 87)",
|
|
||||||
"text-emerald-800": "color:rgb(6 95 70)",
|
|
||||||
"text-emerald-900": "color:rgb(6 78 59)",
|
|
||||||
"text-sky-600": "color:rgb(2 132 199)",
|
|
||||||
"text-sky-700": "color:rgb(3 105 161)",
|
|
||||||
"text-sky-800": "color:rgb(7 89 133)",
|
|
||||||
"text-blue-500": "color:rgb(59 130 246)",
|
|
||||||
"text-blue-600": "color:rgb(37 99 235)",
|
|
||||||
"text-blue-700": "color:rgb(29 78 216)",
|
|
||||||
"text-blue-800": "color:rgb(30 64 175)",
|
|
||||||
"text-purple-600": "color:rgb(147 51 234)",
|
|
||||||
"text-violet-600": "color:rgb(124 58 237)",
|
|
||||||
"text-violet-700": "color:rgb(109 40 217)",
|
|
||||||
"text-violet-800": "color:rgb(91 33 182)",
|
|
||||||
"text-violet-900": "color:rgb(76 29 149)",
|
|
||||||
|
|
||||||
# ── Background Colors ────────────────────────────────────────────────
|
|
||||||
"bg-transparent": "background-color:transparent",
|
|
||||||
"bg-white": "background-color:rgb(255 255 255)",
|
|
||||||
"bg-white/60": "background-color:rgba(255,255,255,.6)",
|
|
||||||
"bg-white/70": "background-color:rgba(255,255,255,.7)",
|
|
||||||
"bg-white/80": "background-color:rgba(255,255,255,.8)",
|
|
||||||
"bg-white/90": "background-color:rgba(255,255,255,.9)",
|
|
||||||
"bg-black": "background-color:rgb(0 0 0)",
|
|
||||||
"bg-black/50": "background-color:rgba(0,0,0,.5)",
|
|
||||||
"bg-stone-50": "background-color:rgb(250 250 249)",
|
|
||||||
"bg-stone-100": "background-color:rgb(245 245 244)",
|
|
||||||
"bg-stone-200": "background-color:rgb(231 229 228)",
|
|
||||||
"bg-stone-300": "background-color:rgb(214 211 209)",
|
|
||||||
"bg-stone-400": "background-color:rgb(168 162 158)",
|
|
||||||
"bg-stone-500": "background-color:rgb(120 113 108)",
|
|
||||||
"bg-stone-600": "background-color:rgb(87 83 78)",
|
|
||||||
"bg-stone-700": "background-color:rgb(68 64 60)",
|
|
||||||
"bg-stone-800": "background-color:rgb(41 37 36)",
|
|
||||||
"bg-stone-900": "background-color:rgb(28 25 23)",
|
|
||||||
"bg-slate-100": "background-color:rgb(241 245 249)",
|
|
||||||
"bg-slate-200": "background-color:rgb(226 232 240)",
|
|
||||||
"bg-gray-100": "background-color:rgb(243 244 246)",
|
|
||||||
"bg-red-50": "background-color:rgb(254 242 242)",
|
|
||||||
"bg-red-100": "background-color:rgb(254 226 226)",
|
|
||||||
"bg-red-200": "background-color:rgb(254 202 202)",
|
|
||||||
"bg-red-500": "background-color:rgb(239 68 68)",
|
|
||||||
"bg-red-600": "background-color:rgb(220 38 38)",
|
|
||||||
"bg-rose-50": "background-color:rgb(255 241 242)",
|
|
||||||
"bg-rose-50/80": "background-color:rgba(255,241,242,.8)",
|
|
||||||
"bg-orange-100": "background-color:rgb(255 237 213)",
|
|
||||||
"bg-amber-50": "background-color:rgb(255 251 235)",
|
|
||||||
"bg-amber-50/60": "background-color:rgba(255,251,235,.6)",
|
|
||||||
"bg-amber-100": "background-color:rgb(254 243 199)",
|
|
||||||
"bg-amber-500": "background-color:rgb(245 158 11)",
|
|
||||||
"bg-amber-600": "background-color:rgb(217 119 6)",
|
|
||||||
"bg-yellow-50": "background-color:rgb(254 252 232)",
|
|
||||||
"bg-yellow-100": "background-color:rgb(254 249 195)",
|
|
||||||
"bg-yellow-200": "background-color:rgb(254 240 138)",
|
|
||||||
"bg-yellow-300": "background-color:rgb(253 224 71)",
|
|
||||||
"bg-green-50": "background-color:rgb(240 253 244)",
|
|
||||||
"bg-green-100": "background-color:rgb(220 252 231)",
|
|
||||||
"bg-green-200": "background-color:rgb(187 247 208)",
|
|
||||||
"bg-neutral-50/70": "background-color:rgba(250,250,250,.7)",
|
|
||||||
"bg-black/70": "background-color:rgba(0,0,0,.7)",
|
|
||||||
"bg-emerald-50": "background-color:rgb(236 253 245)",
|
|
||||||
"bg-emerald-50/80": "background-color:rgba(236,253,245,.8)",
|
|
||||||
"bg-emerald-100": "background-color:rgb(209 250 229)",
|
|
||||||
"bg-emerald-200": "background-color:rgb(167 243 208)",
|
|
||||||
"bg-emerald-500": "background-color:rgb(16 185 129)",
|
|
||||||
"bg-emerald-600": "background-color:rgb(5 150 105)",
|
|
||||||
"bg-sky-100": "background-color:rgb(224 242 254)",
|
|
||||||
"bg-sky-200": "background-color:rgb(186 230 253)",
|
|
||||||
"bg-sky-300": "background-color:rgb(125 211 252)",
|
|
||||||
"bg-sky-400": "background-color:rgb(56 189 248)",
|
|
||||||
"bg-sky-500": "background-color:rgb(14 165 233)",
|
|
||||||
"bg-blue-50": "background-color:rgb(239 246 255)",
|
|
||||||
"bg-blue-100": "background-color:rgb(219 234 254)",
|
|
||||||
"bg-blue-600": "background-color:rgb(37 99 235)",
|
|
||||||
"bg-purple-600": "background-color:rgb(147 51 234)",
|
|
||||||
"bg-violet-50": "background-color:rgb(245 243 255)",
|
|
||||||
"bg-violet-100": "background-color:rgb(237 233 254)",
|
|
||||||
"bg-violet-200": "background-color:rgb(221 214 254)",
|
|
||||||
"bg-violet-300": "background-color:rgb(196 181 253)",
|
|
||||||
"bg-violet-400": "background-color:rgb(167 139 250)",
|
|
||||||
"bg-violet-500": "background-color:rgb(139 92 246)",
|
|
||||||
"bg-violet-600": "background-color:rgb(124 58 237)",
|
|
||||||
"bg-violet-700": "background-color:rgb(109 40 217)",
|
|
||||||
"bg-amber-200": "background-color:rgb(253 230 138)",
|
|
||||||
"bg-blue-700": "background-color:rgb(29 78 216)",
|
|
||||||
"bg-emerald-700": "background-color:rgb(4 120 87)",
|
|
||||||
"bg-purple-700": "background-color:rgb(126 34 206)",
|
|
||||||
"bg-stone-50/60": "background-color:rgba(250,250,249,.6)",
|
|
||||||
|
|
||||||
# ── Border ───────────────────────────────────────────────────────────
|
|
||||||
"border": "border-width:1px",
|
|
||||||
"border-2": "border-width:2px",
|
|
||||||
"border-4": "border-width:4px",
|
|
||||||
"border-t": "border-top-width:1px",
|
|
||||||
"border-t-0": "border-top-width:0",
|
|
||||||
"border-b": "border-bottom-width:1px",
|
|
||||||
"border-b-2": "border-bottom-width:2px",
|
|
||||||
"border-r": "border-right-width:1px",
|
|
||||||
"border-l": "border-left-width:1px",
|
|
||||||
"border-l-4": "border-left-width:4px",
|
|
||||||
"border-dashed": "border-style:dashed",
|
|
||||||
"border-none": "border-style:none",
|
|
||||||
"border-transparent": "border-color:transparent",
|
|
||||||
"border-white": "border-color:rgb(255 255 255)",
|
|
||||||
"border-white/30": "border-color:rgba(255,255,255,.3)",
|
|
||||||
"border-stone-100": "border-color:rgb(245 245 244)",
|
|
||||||
"border-stone-200": "border-color:rgb(231 229 228)",
|
|
||||||
"border-stone-300": "border-color:rgb(214 211 209)",
|
|
||||||
"border-stone-700": "border-color:rgb(68 64 60)",
|
|
||||||
"border-red-200": "border-color:rgb(254 202 202)",
|
|
||||||
"border-red-300": "border-color:rgb(252 165 165)",
|
|
||||||
"border-rose-200": "border-color:rgb(254 205 211)",
|
|
||||||
"border-rose-300": "border-color:rgb(253 164 175)",
|
|
||||||
"border-amber-200": "border-color:rgb(253 230 138)",
|
|
||||||
"border-amber-300": "border-color:rgb(252 211 77)",
|
|
||||||
"border-yellow-200": "border-color:rgb(254 240 138)",
|
|
||||||
"border-green-300": "border-color:rgb(134 239 172)",
|
|
||||||
"border-emerald-100": "border-color:rgb(209 250 229)",
|
|
||||||
"border-emerald-200": "border-color:rgb(167 243 208)",
|
|
||||||
"border-emerald-300": "border-color:rgb(110 231 183)",
|
|
||||||
"border-emerald-600": "border-color:rgb(5 150 105)",
|
|
||||||
"border-blue-200": "border-color:rgb(191 219 254)",
|
|
||||||
"border-blue-300": "border-color:rgb(147 197 253)",
|
|
||||||
"border-violet-200": "border-color:rgb(221 214 254)",
|
|
||||||
"border-violet-300": "border-color:rgb(196 181 253)",
|
|
||||||
"border-violet-400": "border-color:rgb(167 139 250)",
|
|
||||||
"border-neutral-200": "border-color:rgb(229 229 229)",
|
|
||||||
"border-red-400": "border-color:rgb(248 113 113)",
|
|
||||||
"border-stone-400": "border-color:rgb(168 162 158)",
|
|
||||||
"border-t-white": "border-top-color:rgb(255 255 255)",
|
|
||||||
"border-t-stone-600": "border-top-color:rgb(87 83 78)",
|
|
||||||
"border-l-stone-400": "border-left-color:rgb(168 162 158)",
|
|
||||||
|
|
||||||
# ── Border Radius ────────────────────────────────────────────────────
|
|
||||||
"rounded": "border-radius:.25rem",
|
|
||||||
"rounded-md": "border-radius:.375rem",
|
|
||||||
"rounded-lg": "border-radius:.5rem",
|
|
||||||
"rounded-xl": "border-radius:.75rem",
|
|
||||||
"rounded-2xl": "border-radius:1rem",
|
|
||||||
"rounded-full": "border-radius:9999px",
|
|
||||||
"rounded-t": "border-top-left-radius:.25rem;border-top-right-radius:.25rem",
|
|
||||||
"rounded-b": "border-bottom-left-radius:.25rem;border-bottom-right-radius:.25rem",
|
|
||||||
"rounded-[4px]": "border-radius:4px",
|
|
||||||
"rounded-[8px]": "border-radius:8px",
|
|
||||||
|
|
||||||
# ── Shadow ───────────────────────────────────────────────────────────
|
|
||||||
"shadow-sm": "box-shadow:0 1px 2px 0 rgba(0,0,0,.05)",
|
|
||||||
"shadow": "box-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1)",
|
|
||||||
"shadow-md": "box-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1)",
|
|
||||||
"shadow-lg": "box-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1)",
|
|
||||||
"shadow-xl": "box-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1)",
|
|
||||||
|
|
||||||
# ── Opacity ──────────────────────────────────────────────────────────
|
|
||||||
"opacity-0": "opacity:0",
|
|
||||||
"opacity-40": "opacity:.4",
|
|
||||||
"opacity-50": "opacity:.5",
|
|
||||||
"opacity-90": "opacity:.9",
|
|
||||||
"opacity-100": "opacity:1",
|
|
||||||
|
|
||||||
# ── Ring / Outline ───────────────────────────────────────────────────
|
|
||||||
"outline-none": "outline:2px solid transparent;outline-offset:2px",
|
|
||||||
"ring-2": "box-shadow:0 0 0 2px var(--tw-ring-color,rgb(59 130 246))",
|
|
||||||
"ring-offset-2": "box-shadow:0 0 0 2px rgb(255 255 255),0 0 0 4px var(--tw-ring-color,rgb(59 130 246))",
|
|
||||||
"ring-stone-300": "--tw-ring-color:rgb(214 211 209)",
|
|
||||||
"ring-stone-500": "--tw-ring-color:rgb(120 113 108)",
|
|
||||||
"ring-violet-500": "--tw-ring-color:rgb(139 92 246)",
|
|
||||||
"ring-blue-500": "--tw-ring-color:rgb(59 130 246)",
|
|
||||||
"ring-green-500": "--tw-ring-color:rgb(22 163 74)",
|
|
||||||
"ring-purple-500": "--tw-ring-color:rgb(147 51 234)",
|
|
||||||
|
|
||||||
# ── Overflow ─────────────────────────────────────────────────────────
|
|
||||||
"overflow-hidden": "overflow:hidden",
|
|
||||||
"overflow-x-auto": "overflow-x:auto",
|
|
||||||
"overflow-y-auto": "overflow-y:auto",
|
|
||||||
"overflow-visible": "overflow:visible",
|
|
||||||
"overflow-y-visible": "overflow-y:visible",
|
|
||||||
"overscroll-contain": "overscroll-behavior:contain",
|
|
||||||
|
|
||||||
# ── Text Decoration ──────────────────────────────────────────────────
|
|
||||||
"underline": "text-decoration-line:underline",
|
|
||||||
"line-through": "text-decoration-line:line-through",
|
|
||||||
"no-underline": "text-decoration-line:none",
|
|
||||||
|
|
||||||
# ── Text Overflow ────────────────────────────────────────────────────
|
|
||||||
"truncate": "overflow:hidden;text-overflow:ellipsis;white-space:nowrap",
|
|
||||||
"line-clamp-2": "display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden",
|
|
||||||
"line-clamp-3": "display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden",
|
|
||||||
|
|
||||||
# ── Whitespace / Word Break ──────────────────────────────────────────
|
|
||||||
"whitespace-normal": "white-space:normal",
|
|
||||||
"whitespace-nowrap": "white-space:nowrap",
|
|
||||||
"whitespace-pre-line": "white-space:pre-line",
|
|
||||||
"whitespace-pre-wrap": "white-space:pre-wrap",
|
|
||||||
"break-words": "overflow-wrap:break-word",
|
|
||||||
"break-all": "word-break:break-all",
|
|
||||||
|
|
||||||
# ── Transform ────────────────────────────────────────────────────────
|
|
||||||
"rotate-180": "transform:rotate(180deg)",
|
|
||||||
"-translate-x-1/2": "transform:translateX(-50%)",
|
|
||||||
"-translate-y-1/2": "transform:translateY(-50%)",
|
|
||||||
|
|
||||||
# ── Transition ───────────────────────────────────────────────────────
|
|
||||||
"transition": "transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s",
|
|
||||||
"transition-all": "transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s",
|
|
||||||
"transition-colors": "transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s",
|
|
||||||
"transition-opacity": "transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s",
|
|
||||||
"transition-transform": "transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s",
|
|
||||||
"duration-75": "transition-duration:75ms",
|
|
||||||
"duration-100": "transition-duration:100ms",
|
|
||||||
"duration-150": "transition-duration:150ms",
|
|
||||||
"duration-200": "transition-duration:200ms",
|
|
||||||
"duration-300": "transition-duration:300ms",
|
|
||||||
"duration-500": "transition-duration:500ms",
|
|
||||||
"duration-700": "transition-duration:700ms",
|
|
||||||
|
|
||||||
# ── Animation ────────────────────────────────────────────────────────
|
|
||||||
"animate-spin": "animation:spin 1s linear infinite",
|
|
||||||
"animate-ping": "animation:ping 1s cubic-bezier(0,0,0.2,1) infinite",
|
|
||||||
"animate-pulse": "animation:pulse 2s cubic-bezier(0.4,0,0.6,1) infinite",
|
|
||||||
"animate-bounce": "animation:bounce 1s infinite",
|
|
||||||
"animate-none": "animation:none",
|
|
||||||
|
|
||||||
# ── Aspect Ratio ─────────────────────────────────────────────────────
|
|
||||||
"aspect-square": "aspect-ratio:1/1",
|
|
||||||
"aspect-video": "aspect-ratio:16/9",
|
|
||||||
|
|
||||||
# ── Object Fit / Position ────────────────────────────────────────────
|
|
||||||
"object-contain": "object-fit:contain",
|
|
||||||
"object-cover": "object-fit:cover",
|
|
||||||
"object-center": "object-position:center",
|
|
||||||
"object-top": "object-position:top",
|
|
||||||
|
|
||||||
# ── Cursor ───────────────────────────────────────────────────────────
|
|
||||||
"cursor-pointer": "cursor:pointer",
|
|
||||||
"cursor-move": "cursor:move",
|
|
||||||
|
|
||||||
# ── User Select ──────────────────────────────────────────────────────
|
|
||||||
"select-none": "user-select:none",
|
|
||||||
"select-all": "user-select:all",
|
|
||||||
|
|
||||||
# ── Pointer Events ───────────────────────────────────────────────────
|
|
||||||
"pointer-events-none": "pointer-events:none",
|
|
||||||
|
|
||||||
# ── Resize ───────────────────────────────────────────────────────────
|
|
||||||
"resize": "resize:both",
|
|
||||||
"resize-none": "resize:none",
|
|
||||||
|
|
||||||
# ── Scroll Snap ──────────────────────────────────────────────────────
|
|
||||||
"snap-y": "scroll-snap-type:y mandatory",
|
|
||||||
"snap-start": "scroll-snap-align:start",
|
|
||||||
"snap-mandatory": "scroll-snap-type:y mandatory",
|
|
||||||
|
|
||||||
# ── List Style ───────────────────────────────────────────────────────
|
|
||||||
"list-disc": "list-style-type:disc",
|
|
||||||
"list-decimal": "list-style-type:decimal",
|
|
||||||
"list-inside": "list-style-position:inside",
|
|
||||||
|
|
||||||
# ── Table ────────────────────────────────────────────────────────────
|
|
||||||
"table-fixed": "table-layout:fixed",
|
|
||||||
|
|
||||||
# ── Backdrop ─────────────────────────────────────────────────────────
|
|
||||||
"backdrop-blur": "backdrop-filter:blur(8px)",
|
|
||||||
"backdrop-blur-sm": "backdrop-filter:blur(4px)",
|
|
||||||
"backdrop-blur-md": "backdrop-filter:blur(12px)",
|
|
||||||
|
|
||||||
# ── Filter ───────────────────────────────────────────────────────────
|
|
||||||
"saturate-0": "filter:saturate(0)",
|
|
||||||
|
|
||||||
# ── Space Between (child selector atoms) ─────────────────────────────
|
|
||||||
# These generate `.atom > :not(:first-child)` rules
|
|
||||||
"space-y-0": "margin-top:0",
|
|
||||||
"space-y-0.5": "margin-top:.125rem",
|
|
||||||
"space-y-1": "margin-top:.25rem",
|
|
||||||
"space-y-2": "margin-top:.5rem",
|
|
||||||
"space-y-3": "margin-top:.75rem",
|
|
||||||
"space-y-4": "margin-top:1rem",
|
|
||||||
"space-y-6": "margin-top:1.5rem",
|
|
||||||
"space-y-8": "margin-top:2rem",
|
|
||||||
"space-y-10": "margin-top:2.5rem",
|
|
||||||
"space-x-1": "margin-left:.25rem",
|
|
||||||
"space-x-2": "margin-left:.5rem",
|
|
||||||
|
|
||||||
# ── Divide (child selector atoms) ────────────────────────────────────
|
|
||||||
# These generate `.atom > :not(:first-child)` rules
|
|
||||||
"divide-y": "border-top-width:1px",
|
|
||||||
"divide-stone-100": "border-color:rgb(245 245 244)",
|
|
||||||
"divide-stone-200": "border-color:rgb(231 229 228)",
|
|
||||||
|
|
||||||
# ── Important modifiers ──────────────────────────────────────────────
|
|
||||||
"!bg-stone-500": "background-color:rgb(120 113 108)!important",
|
|
||||||
"!text-white": "color:rgb(255 255 255)!important",
|
|
||||||
}
|
|
||||||
|
|
||||||
# Atoms that need a child selector: `.atom > :not(:first-child)` instead of `.atom`
|
|
||||||
CHILD_SELECTOR_ATOMS: frozenset[str] = frozenset({
|
|
||||||
k for k in STYLE_ATOMS
|
|
||||||
if k.startswith(("space-x-", "space-y-", "divide-y", "divide-x"))
|
|
||||||
and not k.startswith("divide-stone")
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════════
|
|
||||||
# Pseudo-class / pseudo-element variants
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
PSEUDO_VARIANTS: dict[str, str] = {
|
|
||||||
"hover": ":hover",
|
|
||||||
"focus": ":focus",
|
|
||||||
"focus-within": ":focus-within",
|
|
||||||
"focus-visible": ":focus-visible",
|
|
||||||
"active": ":active",
|
|
||||||
"disabled": ":disabled",
|
|
||||||
"first": ":first-child",
|
|
||||||
"last": ":last-child",
|
|
||||||
"odd": ":nth-child(odd)",
|
|
||||||
"even": ":nth-child(even)",
|
|
||||||
"empty": ":empty",
|
|
||||||
"open": "[open]",
|
|
||||||
"placeholder": "::placeholder",
|
|
||||||
"file": "::file-selector-button",
|
|
||||||
"aria-selected": "[aria-selected=true]",
|
|
||||||
"invalid": ":invalid",
|
|
||||||
"placeholder-shown": ":placeholder-shown",
|
|
||||||
"group-hover": ":is(.group:hover) &",
|
|
||||||
"group-open": ":is(.group[open]) &",
|
|
||||||
"group-open/cat": ":is(.group\\/cat[open]) &",
|
|
||||||
"group-open/filter": ":is(.group\\/filter[open]) &",
|
|
||||||
"group-open/root": ":is(.group\\/root[open]) &",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════════
|
|
||||||
# Responsive breakpoints
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
RESPONSIVE_BREAKPOINTS: dict[str, str] = {
|
|
||||||
"sm": "(min-width:640px)",
|
|
||||||
"md": "(min-width:768px)",
|
|
||||||
"lg": "(min-width:1024px)",
|
|
||||||
"xl": "(min-width:1280px)",
|
|
||||||
"2xl": "(min-width:1536px)",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════════
|
|
||||||
# Keyframes — built-in animation definitions
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
KEYFRAMES: dict[str, str] = {
|
|
||||||
"spin": "@keyframes spin{to{transform:rotate(360deg)}}",
|
|
||||||
"ping": "@keyframes ping{75%,100%{transform:scale(2);opacity:0}}",
|
|
||||||
"pulse": "@keyframes pulse{50%{opacity:.5}}",
|
|
||||||
"bounce": "@keyframes bounce{0%,100%{transform:translateY(-25%);animation-timing-function:cubic-bezier(0.8,0,1,1)}50%{transform:none;animation-timing-function:cubic-bezier(0,0,0.2,1)}}",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════════
|
|
||||||
# Arbitrary value patterns — fallback when atom not in STYLE_ATOMS
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════════
|
|
||||||
#
|
|
||||||
# Each tuple is (regex_pattern, css_template).
|
|
||||||
# The regex captures value groups; the template uses {0}, {1}, etc.
|
|
||||||
|
|
||||||
ARBITRARY_PATTERNS: list[tuple[str, str]] = [
|
|
||||||
# Width / Height
|
|
||||||
(r"w-\[(.+)\]", "width:{0}"),
|
|
||||||
(r"h-\[(.+)\]", "height:{0}"),
|
|
||||||
(r"min-w-\[(.+)\]", "min-width:{0}"),
|
|
||||||
(r"min-h-\[(.+)\]", "min-height:{0}"),
|
|
||||||
(r"max-w-\[(.+)\]", "max-width:{0}"),
|
|
||||||
(r"max-h-\[(.+)\]", "max-height:{0}"),
|
|
||||||
# Spacing
|
|
||||||
(r"p-\[(.+)\]", "padding:{0}"),
|
|
||||||
(r"px-\[(.+)\]", "padding-left:{0};padding-right:{0}"),
|
|
||||||
(r"py-\[(.+)\]", "padding-top:{0};padding-bottom:{0}"),
|
|
||||||
(r"pt-\[(.+)\]", "padding-top:{0}"),
|
|
||||||
(r"pb-\[(.+)\]", "padding-bottom:{0}"),
|
|
||||||
(r"pl-\[(.+)\]", "padding-left:{0}"),
|
|
||||||
(r"pr-\[(.+)\]", "padding-right:{0}"),
|
|
||||||
(r"m-\[(.+)\]", "margin:{0}"),
|
|
||||||
(r"mx-\[(.+)\]", "margin-left:{0};margin-right:{0}"),
|
|
||||||
(r"my-\[(.+)\]", "margin-top:{0};margin-bottom:{0}"),
|
|
||||||
(r"mt-\[(.+)\]", "margin-top:{0}"),
|
|
||||||
(r"mb-\[(.+)\]", "margin-bottom:{0}"),
|
|
||||||
(r"ml-\[(.+)\]", "margin-left:{0}"),
|
|
||||||
(r"mr-\[(.+)\]", "margin-right:{0}"),
|
|
||||||
# Gap
|
|
||||||
(r"gap-\[(.+)\]", "gap:{0}"),
|
|
||||||
(r"gap-x-\[(.+)\]", "column-gap:{0}"),
|
|
||||||
(r"gap-y-\[(.+)\]", "row-gap:{0}"),
|
|
||||||
# Position
|
|
||||||
(r"top-\[(.+)\]", "top:{0}"),
|
|
||||||
(r"right-\[(.+)\]", "right:{0}"),
|
|
||||||
(r"bottom-\[(.+)\]", "bottom:{0}"),
|
|
||||||
(r"left-\[(.+)\]", "left:{0}"),
|
|
||||||
# Border radius
|
|
||||||
(r"rounded-\[(.+)\]", "border-radius:{0}"),
|
|
||||||
# Background / Text color
|
|
||||||
(r"bg-\[(.+)\]", "background-color:{0}"),
|
|
||||||
(r"text-\[(.+)\]", "font-size:{0}"),
|
|
||||||
# Grid
|
|
||||||
(r"grid-cols-\[(.+)\]", "grid-template-columns:{0}"),
|
|
||||||
(r"col-span-(\d+)", "grid-column:span {0}/span {0}"),
|
|
||||||
]
|
|
||||||
@@ -1,254 +0,0 @@
|
|||||||
"""
|
|
||||||
Style resolver — ``(css :flex :gap-4 :hover:bg-sky-200)`` → StyleValue.
|
|
||||||
|
|
||||||
Resolves a tuple of atom strings into a ``StyleValue`` with:
|
|
||||||
- A content-addressed class name (``sx-{hash[:6]}``)
|
|
||||||
- Base CSS declarations
|
|
||||||
- Pseudo-class rules (hover, focus, etc.)
|
|
||||||
- Media-query rules (responsive breakpoints)
|
|
||||||
- Referenced @keyframes definitions
|
|
||||||
|
|
||||||
Resolution order per atom:
|
|
||||||
1. Dictionary lookup in ``STYLE_ATOMS``
|
|
||||||
2. Arbitrary value pattern match (``w-[347px]`` → ``width:347px``)
|
|
||||||
3. Ignored (unknown atoms are silently skipped)
|
|
||||||
|
|
||||||
Results are memoized by input tuple for zero-cost repeat calls.
|
|
||||||
"""
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import hashlib
|
|
||||||
import re
|
|
||||||
from functools import lru_cache
|
|
||||||
from typing import Sequence
|
|
||||||
|
|
||||||
from .style_dict import (
|
|
||||||
ARBITRARY_PATTERNS,
|
|
||||||
CHILD_SELECTOR_ATOMS,
|
|
||||||
KEYFRAMES,
|
|
||||||
PSEUDO_VARIANTS,
|
|
||||||
RESPONSIVE_BREAKPOINTS,
|
|
||||||
STYLE_ATOMS,
|
|
||||||
)
|
|
||||||
from .types import StyleValue
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Compiled arbitrary-value patterns
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
_COMPILED_PATTERNS: list[tuple[re.Pattern, str]] = [
|
|
||||||
(re.compile(f"^{pat}$"), tmpl)
|
|
||||||
for pat, tmpl in ARBITRARY_PATTERNS
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Public API
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def resolve_style(atoms: tuple[str, ...]) -> StyleValue:
|
|
||||||
"""Resolve a tuple of keyword atoms into a StyleValue.
|
|
||||||
|
|
||||||
Each atom is a Tailwind-compatible keyword (``flex``, ``gap-4``,
|
|
||||||
``hover:bg-sky-200``, ``sm:flex-row``, etc.). Both keywords
|
|
||||||
(without leading colon) and runtime strings are accepted.
|
|
||||||
"""
|
|
||||||
return _resolve_cached(atoms)
|
|
||||||
|
|
||||||
|
|
||||||
def merge_styles(styles: Sequence[StyleValue]) -> StyleValue:
|
|
||||||
"""Merge multiple StyleValues into one.
|
|
||||||
|
|
||||||
Later declarations win for the same CSS property. Class name is
|
|
||||||
recomputed from the merged declarations.
|
|
||||||
"""
|
|
||||||
if len(styles) == 1:
|
|
||||||
return styles[0]
|
|
||||||
|
|
||||||
all_decls: list[str] = []
|
|
||||||
all_media: list[tuple[str, str]] = []
|
|
||||||
all_pseudo: list[tuple[str, str]] = []
|
|
||||||
all_kf: list[tuple[str, str]] = []
|
|
||||||
|
|
||||||
for sv in styles:
|
|
||||||
if sv.declarations:
|
|
||||||
all_decls.append(sv.declarations)
|
|
||||||
all_media.extend(sv.media_rules)
|
|
||||||
all_pseudo.extend(sv.pseudo_rules)
|
|
||||||
all_kf.extend(sv.keyframes)
|
|
||||||
|
|
||||||
merged_decls = ";".join(all_decls)
|
|
||||||
return _build_style_value(
|
|
||||||
merged_decls,
|
|
||||||
tuple(all_media),
|
|
||||||
tuple(all_pseudo),
|
|
||||||
tuple(dict(all_kf).items()), # dedupe keyframes by name
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Internal resolution
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
@lru_cache(maxsize=4096)
|
|
||||||
def _resolve_cached(atoms: tuple[str, ...]) -> StyleValue:
|
|
||||||
"""Memoized resolver."""
|
|
||||||
base_decls: list[str] = []
|
|
||||||
media_rules: list[tuple[str, str]] = [] # (query, decls)
|
|
||||||
pseudo_rules: list[tuple[str, str]] = [] # (selector_suffix, decls)
|
|
||||||
keyframes_needed: list[tuple[str, str]] = []
|
|
||||||
|
|
||||||
for atom in atoms:
|
|
||||||
if not atom:
|
|
||||||
continue
|
|
||||||
# Strip leading colon if keyword form (":flex" → "flex")
|
|
||||||
a = atom.lstrip(":")
|
|
||||||
|
|
||||||
# Split variant prefix(es): "hover:bg-sky-200" → ["hover", "bg-sky-200"]
|
|
||||||
# "sm:hover:bg-sky-200" → ["sm", "hover", "bg-sky-200"]
|
|
||||||
variant, base = _split_variant(a)
|
|
||||||
|
|
||||||
# Resolve the base atom to CSS declarations
|
|
||||||
decls = _resolve_atom(base)
|
|
||||||
if not decls:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Check if this atom references a keyframe
|
|
||||||
_check_keyframes(base, keyframes_needed)
|
|
||||||
|
|
||||||
# Route to the appropriate bucket
|
|
||||||
if variant is None:
|
|
||||||
base_decls.append(decls)
|
|
||||||
elif variant in RESPONSIVE_BREAKPOINTS:
|
|
||||||
query = RESPONSIVE_BREAKPOINTS[variant]
|
|
||||||
media_rules.append((query, decls))
|
|
||||||
elif variant in PSEUDO_VARIANTS:
|
|
||||||
pseudo_sel = PSEUDO_VARIANTS[variant]
|
|
||||||
pseudo_rules.append((pseudo_sel, decls))
|
|
||||||
else:
|
|
||||||
# Compound variant: "sm:hover:..." → media + pseudo
|
|
||||||
parts = variant.split(":")
|
|
||||||
media_part = None
|
|
||||||
pseudo_part = None
|
|
||||||
for p in parts:
|
|
||||||
if p in RESPONSIVE_BREAKPOINTS:
|
|
||||||
media_part = RESPONSIVE_BREAKPOINTS[p]
|
|
||||||
elif p in PSEUDO_VARIANTS:
|
|
||||||
pseudo_part = PSEUDO_VARIANTS[p]
|
|
||||||
if media_part and pseudo_part:
|
|
||||||
# Both media and pseudo — store as pseudo within media
|
|
||||||
# For now, put in pseudo_rules with media annotation
|
|
||||||
pseudo_rules.append((pseudo_part, decls))
|
|
||||||
media_rules.append((media_part, decls))
|
|
||||||
elif media_part:
|
|
||||||
media_rules.append((media_part, decls))
|
|
||||||
elif pseudo_part:
|
|
||||||
pseudo_rules.append((pseudo_part, decls))
|
|
||||||
else:
|
|
||||||
# Unknown variant — treat as base
|
|
||||||
base_decls.append(decls)
|
|
||||||
|
|
||||||
return _build_style_value(
|
|
||||||
";".join(base_decls),
|
|
||||||
tuple(media_rules),
|
|
||||||
tuple(pseudo_rules),
|
|
||||||
tuple(keyframes_needed),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _split_variant(atom: str) -> tuple[str | None, str]:
|
|
||||||
"""Split a potentially variant-prefixed atom.
|
|
||||||
|
|
||||||
Returns (variant, base) where variant is None for non-prefixed atoms.
|
|
||||||
Examples:
|
|
||||||
"flex" → (None, "flex")
|
|
||||||
"hover:bg-sky-200" → ("hover", "bg-sky-200")
|
|
||||||
"sm:flex-row" → ("sm", "flex-row")
|
|
||||||
"sm:hover:bg-sky-200" → ("sm:hover", "bg-sky-200")
|
|
||||||
"""
|
|
||||||
# Check for responsive prefix first (always outermost)
|
|
||||||
for bp in RESPONSIVE_BREAKPOINTS:
|
|
||||||
prefix = bp + ":"
|
|
||||||
if atom.startswith(prefix):
|
|
||||||
rest = atom[len(prefix):]
|
|
||||||
# Check for nested pseudo variant
|
|
||||||
for pv in PSEUDO_VARIANTS:
|
|
||||||
inner_prefix = pv + ":"
|
|
||||||
if rest.startswith(inner_prefix):
|
|
||||||
return (bp + ":" + pv, rest[len(inner_prefix):])
|
|
||||||
return (bp, rest)
|
|
||||||
|
|
||||||
# Check for pseudo variant
|
|
||||||
for pv in PSEUDO_VARIANTS:
|
|
||||||
prefix = pv + ":"
|
|
||||||
if atom.startswith(prefix):
|
|
||||||
return (pv, atom[len(prefix):])
|
|
||||||
|
|
||||||
return (None, atom)
|
|
||||||
|
|
||||||
|
|
||||||
def _resolve_atom(atom: str) -> str | None:
|
|
||||||
"""Look up CSS declarations for a single base atom.
|
|
||||||
|
|
||||||
Returns None if the atom is unknown.
|
|
||||||
"""
|
|
||||||
# 1. Dictionary lookup
|
|
||||||
decls = STYLE_ATOMS.get(atom)
|
|
||||||
if decls is not None:
|
|
||||||
return decls
|
|
||||||
|
|
||||||
# 2. Dynamic keyframes: animate-{name} → animation-name:{name}
|
|
||||||
if atom.startswith("animate-"):
|
|
||||||
name = atom[len("animate-"):]
|
|
||||||
if name in KEYFRAMES:
|
|
||||||
return f"animation-name:{name}"
|
|
||||||
|
|
||||||
# 3. Arbitrary value pattern match
|
|
||||||
for pattern, template in _COMPILED_PATTERNS:
|
|
||||||
m = pattern.match(atom)
|
|
||||||
if m:
|
|
||||||
groups = m.groups()
|
|
||||||
result = template
|
|
||||||
for i, g in enumerate(groups):
|
|
||||||
result = result.replace(f"{{{i}}}", g)
|
|
||||||
return result
|
|
||||||
|
|
||||||
# 4. Unknown atom — silently skip
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def _check_keyframes(atom: str, kf_list: list[tuple[str, str]]) -> None:
|
|
||||||
"""If the atom references a built-in animation, add its @keyframes."""
|
|
||||||
if atom.startswith("animate-"):
|
|
||||||
name = atom[len("animate-"):]
|
|
||||||
if name in KEYFRAMES:
|
|
||||||
kf_list.append((name, KEYFRAMES[name]))
|
|
||||||
|
|
||||||
|
|
||||||
def _build_style_value(
|
|
||||||
declarations: str,
|
|
||||||
media_rules: tuple,
|
|
||||||
pseudo_rules: tuple,
|
|
||||||
keyframes: tuple,
|
|
||||||
) -> StyleValue:
|
|
||||||
"""Build a StyleValue with a content-addressed class name."""
|
|
||||||
# Build hash from all rules for deterministic class name
|
|
||||||
hash_input = declarations
|
|
||||||
for query, decls in media_rules:
|
|
||||||
hash_input += f"@{query}{{{decls}}}"
|
|
||||||
for sel, decls in pseudo_rules:
|
|
||||||
hash_input += f"{sel}{{{decls}}}"
|
|
||||||
for name, rule in keyframes:
|
|
||||||
hash_input += rule
|
|
||||||
|
|
||||||
h = hashlib.sha256(hash_input.encode()).hexdigest()[:6]
|
|
||||||
class_name = f"sx-{h}"
|
|
||||||
|
|
||||||
return StyleValue(
|
|
||||||
class_name=class_name,
|
|
||||||
declarations=declarations,
|
|
||||||
media_rules=media_rules,
|
|
||||||
pseudo_rules=pseudo_rules,
|
|
||||||
keyframes=keyframes,
|
|
||||||
)
|
|
||||||
@@ -303,30 +303,6 @@ class ActionDef:
|
|||||||
return f"<action:{self.name}({', '.join(self.params)})>"
|
return f"<action:{self.name}({', '.join(self.params)})>"
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# StyleValue
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
|
||||||
class StyleValue:
|
|
||||||
"""A resolved CSS style produced by ``(css :flex :gap-4 :hover:bg-sky-200)``.
|
|
||||||
|
|
||||||
Generated by the style resolver. The renderer emits ``class_name`` as a
|
|
||||||
CSS class and registers the CSS rule for on-demand delivery.
|
|
||||||
"""
|
|
||||||
class_name: str # "sx-a3f2c1"
|
|
||||||
declarations: str # "display:flex;gap:1rem"
|
|
||||||
media_rules: tuple = () # ((query, decls), ...)
|
|
||||||
pseudo_rules: tuple = () # ((selector, decls), ...)
|
|
||||||
keyframes: tuple = () # (("spin", "@keyframes spin{...}"), ...)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return f"<StyleValue {self.class_name}>"
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.class_name
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Continuation
|
# Continuation
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -368,4 +344,4 @@ class _ShiftSignal(BaseException):
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
# An s-expression value after evaluation
|
# An s-expression value after evaluation
|
||||||
SExp = int | float | str | bool | Symbol | Keyword | Lambda | Macro | Component | Continuation | HandlerDef | RelationDef | PageDef | QueryDef | ActionDef | StyleValue | list | dict | _Nil | None
|
SExp = int | float | str | bool | Symbol | Keyword | Lambda | Macro | Component | Continuation | HandlerDef | RelationDef | PageDef | QueryDef | ActionDef | list | dict | _Nil | None
|
||||||
|
|||||||
@@ -198,10 +198,7 @@
|
|||||||
(define browser-spec-items (list
|
(define browser-spec-items (list
|
||||||
(dict :slug "boot" :filename "boot.sx" :title "Boot"
|
(dict :slug "boot" :filename "boot.sx" :title "Boot"
|
||||||
:desc "Browser startup lifecycle: mount, hydrate, script processing."
|
:desc "Browser startup lifecycle: mount, hydrate, script processing."
|
||||||
:prose "Boot handles the browser startup sequence and provides the public API for mounting SX content. On page load it: (1) initializes CSS tracking, (2) loads the style dictionary from inline JSON, (3) processes <script type=\"text/sx\"> tags (component definitions and mount directives), (4) hydrates [data-sx] elements, and (5) activates the engine on all elements. It also provides the public mount/hydrate/update/render-component API, and the head element hoisting logic that moves <meta>, <title>, and <link> tags from rendered content into <head>.")
|
:prose "Boot handles the browser startup sequence and provides the public API for mounting SX content. On page load it: (1) initializes CSS tracking, (2) processes <script type=\"text/sx\"> tags (component definitions and mount directives), (3) hydrates [data-sx] elements, and (4) activates the engine on all elements. It also provides the public mount/hydrate/update/render-component API, and the head element hoisting logic that moves <meta>, <title>, and <link> tags from rendered content into <head>.")))
|
||||||
(dict :slug "cssx" :filename "cssx.sx" :title "CSSX"
|
|
||||||
:desc "On-demand CSS: style dictionary, keyword resolution, rule injection."
|
|
||||||
:prose "CSSX is the on-demand CSS system. It resolves keyword atoms (:flex, :gap-4, :hover:bg-sky-200) into StyleValue objects with content-addressed class names, injecting CSS rules into the document on first use. The style dictionary is a JSON blob containing: atoms (keyword to CSS declarations), pseudo-variants (hover:, focus:, etc.), responsive breakpoints (md:, lg:, etc.), keyframe animations, arbitrary value patterns, and child selector prefixes (space-x-, space-y-). Classes are only emitted when used, keeping the CSS payload minimal. The dictionary is typically served inline in a <script type=\"text/sx-styles\"> tag.")))
|
|
||||||
|
|
||||||
(define extension-spec-items (list
|
(define extension-spec-items (list
|
||||||
(dict :slug "continuations" :filename "continuations.sx" :title "Continuations"
|
(dict :slug "continuations" :filename "continuations.sx" :title "Continuations"
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ def _special_forms_data() -> dict:
|
|||||||
"filter": "Higher-Order Forms", "reduce": "Higher-Order Forms",
|
"filter": "Higher-Order Forms", "reduce": "Higher-Order Forms",
|
||||||
"some": "Higher-Order Forms", "every?": "Higher-Order Forms",
|
"some": "Higher-Order Forms", "every?": "Higher-Order Forms",
|
||||||
"for-each": "Higher-Order Forms",
|
"for-each": "Higher-Order Forms",
|
||||||
"defstyle": "Domain Definitions", "defkeyframes": "Domain Definitions",
|
"defstyle": "Domain Definitions",
|
||||||
"defhandler": "Domain Definitions", "defpage": "Domain Definitions",
|
"defhandler": "Domain Definitions", "defpage": "Domain Definitions",
|
||||||
"defquery": "Domain Definitions", "defaction": "Domain Definitions",
|
"defquery": "Domain Definitions", "defaction": "Domain Definitions",
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user