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:
@@ -38,7 +38,6 @@ Only these types may cross the host-SX boundary:
|
||||
| list | `list` | `Array` | `Vec<SxValue>` |
|
||||
| dict | `dict` | `Object` / `Map` | `HashMap<String, SxValue>` |
|
||||
| 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.
|
||||
|
||||
|
||||
@@ -52,9 +52,6 @@
|
||||
(create-fragment)
|
||||
(render-dom-list expr env ns))
|
||||
|
||||
;; Style value → text of class name
|
||||
"style-value" (create-text-node (style-value-class expr))
|
||||
|
||||
;; Fallback
|
||||
:else (create-text-node (str expr)))))
|
||||
|
||||
@@ -147,8 +144,7 @@
|
||||
(let ((new-ns (cond (= tag "svg") SVG_NS
|
||||
(= tag "math") MATH_NS
|
||||
:else ns))
|
||||
(el (dom-create-element tag new-ns))
|
||||
(extra-class nil))
|
||||
(el (dom-create-element tag new-ns)))
|
||||
|
||||
;; Process args: keywords → attrs, others → children
|
||||
(reduce
|
||||
@@ -168,9 +164,6 @@
|
||||
;; nil or false → skip
|
||||
(or (nil? attr-val) (= attr-val false))
|
||||
nil
|
||||
;; :style StyleValue → convert to class
|
||||
(and (= attr-name "style") (style-value? attr-val))
|
||||
(set! extra-class (style-value-class attr-val))
|
||||
;; Boolean attr
|
||||
(contains? BOOLEAN_ATTRS attr-name)
|
||||
(when attr-val (dom-set-attr el attr-name ""))
|
||||
@@ -190,12 +183,6 @@
|
||||
(dict "i" 0 "skip" false)
|
||||
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)))
|
||||
|
||||
|
||||
@@ -297,7 +284,7 @@
|
||||
|
||||
(define RENDER_DOM_FORMS
|
||||
(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"))
|
||||
|
||||
(define render-dom-form?
|
||||
@@ -450,7 +437,6 @@
|
||||
;;
|
||||
;; From render.sx:
|
||||
;; HTML_TAGS, VOID_ELEMENTS, BOOLEAN_ATTRS, definition-form?
|
||||
;; style-value?, style-value-class
|
||||
;;
|
||||
;; From eval.sx:
|
||||
;; eval-expr, trampoline, expand-macro, process-bindings, eval-cond
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
"boolean" (if val "true" "false")
|
||||
"list" (render-list-to-html val env)
|
||||
"raw-html" (raw-html-content val)
|
||||
"style-value" (style-value-class val)
|
||||
:else (escape-html (str val)))))
|
||||
|
||||
|
||||
@@ -51,7 +50,7 @@
|
||||
|
||||
(define RENDER_HTML_FORMS
|
||||
(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"))
|
||||
|
||||
(define render-html-form?
|
||||
@@ -293,7 +292,7 @@
|
||||
;; --------------------------------------------------------------------------
|
||||
;;
|
||||
;; 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:
|
||||
;; eval-expr, trampoline, expand-macro, process-bindings, eval-cond
|
||||
|
||||
@@ -26,7 +26,7 @@ import contextvars
|
||||
import inspect
|
||||
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 ..primitives_io import IO_PRIMITIVES, RequestContext, execute_io
|
||||
from ..html import (
|
||||
@@ -250,18 +250,6 @@ async def _arender_element(tag, args, env, ctx):
|
||||
children.append(arg)
|
||||
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")
|
||||
if class_val is not None and class_val is not NIL and class_val is not False:
|
||||
collector = css_class_collector.get(None)
|
||||
@@ -464,7 +452,6 @@ _ASYNC_RENDER_FORMS = {
|
||||
"do": _arsf_begin,
|
||||
"define": _arsf_define,
|
||||
"defstyle": _arsf_define,
|
||||
"defkeyframes": _arsf_define,
|
||||
"defcomp": _arsf_define,
|
||||
"defmacro": _arsf_define,
|
||||
"defhandler": _arsf_define,
|
||||
@@ -716,23 +703,18 @@ async def _aser_call(name, args, env, ctx):
|
||||
if isinstance(arg, Keyword) and i + 1 < len(args):
|
||||
val = await _aser(args[i + 1], env, ctx)
|
||||
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}")
|
||||
if isinstance(val, list):
|
||||
live = [v for v in val if v is not NIL and v is not None]
|
||||
items = [serialize(v) for v in live]
|
||||
if not items:
|
||||
parts.append("nil")
|
||||
elif any(isinstance(v, SxExpr) for v in live):
|
||||
parts.append("(<> " + " ".join(items) + ")")
|
||||
else:
|
||||
parts.append("(list " + " ".join(items) + ")")
|
||||
parts.append(f":{arg.name}")
|
||||
if isinstance(val, list):
|
||||
live = [v for v in val if v is not NIL and v is not None]
|
||||
items = [serialize(v) for v in live]
|
||||
if not items:
|
||||
parts.append("nil")
|
||||
elif any(isinstance(v, SxExpr) for v in live):
|
||||
parts.append("(<> " + " ".join(items) + ")")
|
||||
else:
|
||||
parts.append(serialize(val))
|
||||
parts.append("(list " + " ".join(items) + ")")
|
||||
else:
|
||||
parts.append(serialize(val))
|
||||
i += 2
|
||||
else:
|
||||
result = await _aser(arg, env, ctx)
|
||||
@@ -996,7 +978,6 @@ _ASER_FORMS = {
|
||||
"fn": _assf_lambda,
|
||||
"define": _assf_define,
|
||||
"defstyle": _assf_define,
|
||||
"defkeyframes": _assf_define,
|
||||
"defcomp": _assf_define,
|
||||
"defmacro": _assf_define,
|
||||
"defhandler": _assf_define,
|
||||
|
||||
@@ -3,16 +3,14 @@
|
||||
;;
|
||||
;; Handles the browser startup lifecycle:
|
||||
;; 1. CSS tracking init
|
||||
;; 2. Style dictionary loading (from <script type="text/sx-styles">)
|
||||
;; 3. Component script processing (from <script type="text/sx">)
|
||||
;; 4. Hydration of [data-sx] elements
|
||||
;; 5. Engine element processing
|
||||
;; 2. Component script processing (from <script type="text/sx">)
|
||||
;; 3. Hydration of [data-sx] elements
|
||||
;; 4. Engine element processing
|
||||
;;
|
||||
;; Also provides the public mounting/hydration API:
|
||||
;; mount, hydrate, update, render-component
|
||||
;;
|
||||
;; Depends on:
|
||||
;; cssx.sx — load-style-dict
|
||||
;; orchestration.sx — process-elements, engine-init
|
||||
;; adapter-dom.sx — render-to-dom
|
||||
;; render.sx — shared registries
|
||||
@@ -275,58 +273,6 @@
|
||||
(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
|
||||
;; --------------------------------------------------------------------------
|
||||
@@ -375,7 +321,6 @@
|
||||
(do
|
||||
(log-info (str "sx-browser " SX_VERSION))
|
||||
(init-css-tracking)
|
||||
(init-style-dict)
|
||||
(process-page-scripts)
|
||||
(process-sx-scripts nil)
|
||||
(sx-hydrate-elements nil)
|
||||
@@ -389,9 +334,6 @@
|
||||
;; From orchestration.sx:
|
||||
;; process-elements, init-css-tracking
|
||||
;;
|
||||
;; From cssx.sx:
|
||||
;; load-style-dict
|
||||
;;
|
||||
;; === DOM / Render ===
|
||||
;; (resolve-mount-target target) → Element (string → querySelector, else identity)
|
||||
;; (sx-render-with-env source extra-env) → DOM node (parse + render with componentEnv + extra)
|
||||
@@ -420,7 +362,6 @@
|
||||
;;
|
||||
;; === Script queries ===
|
||||
;; (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
|
||||
;;
|
||||
;; === localStorage ===
|
||||
@@ -431,8 +372,6 @@
|
||||
;; === Cookies ===
|
||||
;; (set-sx-comp-cookie hash) → void
|
||||
;; (clear-sx-comp-cookie) → void
|
||||
;; (set-sx-styles-cookie hash) → void
|
||||
;; (clear-sx-styles-cookie) → void
|
||||
;;
|
||||
;; === Env ===
|
||||
;; (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)
|
||||
;;
|
||||
;; === JSON parsing ===
|
||||
;; (parse-and-load-style-dict text) → void (JSON.parse + load-style-dict)
|
||||
;;
|
||||
;; === Processing markers ===
|
||||
;; (mark-processed! el key) → void
|
||||
;; (is-processed? el key) → boolean
|
||||
|
||||
@@ -191,10 +191,6 @@ class JSEmitter:
|
||||
"ho-every": "hoEvery",
|
||||
"ho-for-each": "hoForEach",
|
||||
"sf-defstyle": "sfDefstyle",
|
||||
"sf-defkeyframes": "sfDefkeyframes",
|
||||
"build-keyframes": "buildKeyframes",
|
||||
"style-value?": "isStyleValue",
|
||||
"style-value-class": "styleValueClass",
|
||||
"kf-name": "kfName",
|
||||
"special-form?": "isSpecialForm",
|
||||
"ho-form?": "isHoForm",
|
||||
@@ -443,32 +439,6 @@ class JSEmitter:
|
||||
"format-date": "formatDate",
|
||||
"format-decimal": "formatDecimal",
|
||||
"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
|
||||
"HEAD_HOIST_SELECTOR": "HEAD_HOIST_SELECTOR",
|
||||
"hoist-head-elements-full": "hoistHeadElementsFull",
|
||||
@@ -478,7 +448,6 @@ class JSEmitter:
|
||||
"sx-render-component": "sxRenderComponent",
|
||||
"process-sx-scripts": "processSxScripts",
|
||||
"process-component-script": "processComponentScript",
|
||||
"init-style-dict": "initStyleDict",
|
||||
"SX_VERSION": "SX_VERSION",
|
||||
"boot-init": "bootInit",
|
||||
"resolve-suspense": "resolveSuspense",
|
||||
@@ -490,21 +459,17 @@ class JSEmitter:
|
||||
"set-document-title": "setDocumentTitle",
|
||||
"remove-head-element": "removeHeadElement",
|
||||
"query-sx-scripts": "querySxScripts",
|
||||
"query-style-scripts": "queryStyleScripts",
|
||||
"local-storage-get": "localStorageGet",
|
||||
"local-storage-set": "localStorageSet",
|
||||
"local-storage-remove": "localStorageRemove",
|
||||
"set-sx-comp-cookie": "setSxCompCookie",
|
||||
"clear-sx-comp-cookie": "clearSxCompCookie",
|
||||
"set-sx-styles-cookie": "setSxStylesCookie",
|
||||
"clear-sx-styles-cookie": "clearSxStylesCookie",
|
||||
"parse-env-attr": "parseEnvAttr",
|
||||
"store-env-attr": "storeEnvAttr",
|
||||
"to-kebab": "toKebab",
|
||||
"log-info": "logInfo",
|
||||
"log-warn": "logWarn",
|
||||
"log-parse-error": "logParseError",
|
||||
"parse-and-load-style-dict": "parseAndLoadStyleDict",
|
||||
"_page-routes": "_pageRoutes",
|
||||
"process-page-scripts": "processPageScripts",
|
||||
"query-page-scripts": "queryPageScripts",
|
||||
@@ -1044,7 +1009,6 @@ ADAPTER_FILES = {
|
||||
"dom": ("adapter-dom.sx", "adapter-dom"),
|
||||
"engine": ("engine.sx", "engine"),
|
||||
"orchestration": ("orchestration.sx","orchestration"),
|
||||
"cssx": ("cssx.sx", "cssx"),
|
||||
"boot": ("boot.sx", "boot"),
|
||||
}
|
||||
|
||||
@@ -1052,8 +1016,7 @@ ADAPTER_FILES = {
|
||||
ADAPTER_DEPS = {
|
||||
"engine": ["dom"],
|
||||
"orchestration": ["engine", "dom"],
|
||||
"cssx": [],
|
||||
"boot": ["dom", "engine", "orchestration", "cssx", "parser"],
|
||||
"boot": ["dom", "engine", "orchestration", "parser"],
|
||||
"parser": [],
|
||||
}
|
||||
|
||||
@@ -1292,7 +1255,7 @@ ASYNC_IO_JS = '''
|
||||
|
||||
// define/defcomp/defmacro — eval for side effects
|
||||
if (hname === "define" || hname === "defcomp" || hname === "defmacro" ||
|
||||
hname === "defstyle" || hname === "defkeyframes" || hname === "defhandler") {
|
||||
hname === "defstyle" || hname === "defhandler") {
|
||||
trampoline(evalExpr(expr, env));
|
||||
return null;
|
||||
}
|
||||
@@ -1414,11 +1377,7 @@ ASYNC_IO_JS = '''
|
||||
})(attrName, attrVal);
|
||||
} else {
|
||||
if (!isNil(attrVal) && attrVal !== false) {
|
||||
if (attrName === "class" && attrVal && attrVal._styleValue) {
|
||||
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 (contains(BOOLEAN_ATTRS, attrName)) {
|
||||
if (isSxTruthy(attrVal)) el.setAttribute(attrName, "");
|
||||
} else if (attrVal === true) {
|
||||
el.setAttribute(attrName, "");
|
||||
@@ -1829,7 +1788,6 @@ def compile_ref_to_js(
|
||||
"dom": PLATFORM_DOM_JS,
|
||||
"engine": PLATFORM_ENGINE_PURE_JS,
|
||||
"orchestration": PLATFORM_ORCHESTRATION_JS,
|
||||
"cssx": PLATFORM_CSSX_JS,
|
||||
"boot": PLATFORM_BOOT_JS,
|
||||
}
|
||||
|
||||
@@ -1864,7 +1822,7 @@ def compile_ref_to_js(
|
||||
("eval.sx", "eval"),
|
||||
("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:
|
||||
sx_files.append(ADAPTER_FILES[name])
|
||||
for name in sorted(spec_mod_set):
|
||||
@@ -1895,7 +1853,6 @@ def compile_ref_to_js(
|
||||
has_dom = "dom" in adapter_set
|
||||
has_engine = "engine" in adapter_set
|
||||
has_orch = "orchestration" in adapter_set
|
||||
has_cssx = "cssx" in adapter_set
|
||||
has_boot = "boot" in adapter_set
|
||||
has_parser = "parser" in adapter_set
|
||||
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
|
||||
if not has_dom:
|
||||
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:
|
||||
parts.append(adapter_platform[name])
|
||||
|
||||
@@ -1946,7 +1903,7 @@ def compile_ref_to_js(
|
||||
parts.append(CONTINUATIONS_JS)
|
||||
if has_dom:
|
||||
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)
|
||||
from datetime import datetime, timezone
|
||||
build_ts = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
@@ -2019,15 +1976,6 @@ PREAMBLE = '''\
|
||||
function RawHTML(html) { this.html = html; }
|
||||
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 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, ""); };
|
||||
''',
|
||||
|
||||
"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
|
||||
PRIMITIVES["assert"] = function(cond, msg) {
|
||||
@@ -2295,7 +2219,6 @@ PLATFORM_JS_PRE = '''
|
||||
if (x._component) return "component";
|
||||
if (x._macro) return "macro";
|
||||
if (x._raw) return "raw-html";
|
||||
if (x._styleValue) return "style-value";
|
||||
if (typeof Node !== "undefined" && x instanceof Node) return "dom-node";
|
||||
if (Array.isArray(x)) return "list";
|
||||
if (typeof x === "object") return "dict";
|
||||
@@ -2342,27 +2265,6 @@ PLATFORM_JS_PRE = '''
|
||||
function isComponent(x) { return x != null && x._component === 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 envGet(env, name) { return env[name]; }
|
||||
function envSet(env, name, val) { env[name] = val; }
|
||||
@@ -2488,7 +2390,7 @@ PLATFORM_JS_POST = '''
|
||||
function isSpecialForm(n) { return n in {
|
||||
"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,
|
||||
"defkeyframes":1,"defhandler":1,"begin":1,"do":1,
|
||||
"defhandler":1,"begin":1,"do":1,
|
||||
"quote":1,"quasiquote":1,"->":1,"set!":1
|
||||
}; }
|
||||
function isHoForm(n) { return n in {
|
||||
@@ -2499,7 +2401,7 @@ PLATFORM_JS_POST = '''
|
||||
|
||||
function isDefinitionForm(name) {
|
||||
return name === "define" || name === "defcomp" || name === "defmacro" ||
|
||||
name === "defstyle" || name === "defkeyframes" || name === "defhandler";
|
||||
name === "defstyle" || name === "defhandler";
|
||||
}
|
||||
|
||||
function indexOf_(s, ch) {
|
||||
@@ -2877,11 +2779,7 @@ PLATFORM_DOM_JS = """
|
||||
var attrVal = trampoline(evalExpr(args[i + 1], env));
|
||||
i++; // skip value
|
||||
if (isNil(attrVal) || attrVal === false) continue;
|
||||
if (attrName === "class" && attrVal && attrVal._styleValue) {
|
||||
extraClasses.push(attrVal.className);
|
||||
} else if (attrName === "style" && attrVal && attrVal._styleValue) {
|
||||
extraClasses.push(attrVal.className);
|
||||
} else if (contains(BOOLEAN_ATTRS, attrName)) {
|
||||
if (contains(BOOLEAN_ATTRS, attrName)) {
|
||||
if (isSxTruthy(attrVal)) el.setAttribute(attrName, "");
|
||||
} else if (attrVal === true) {
|
||||
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 interface — Boot (mount, hydrate, scripts, cookies)
|
||||
@@ -3916,12 +3718,6 @@ PLATFORM_BOOT_JS = """
|
||||
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() {
|
||||
if (!_hasDom) return [];
|
||||
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";
|
||||
}
|
||||
|
||||
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 ---
|
||||
|
||||
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):
|
||||
@@ -4039,7 +3823,7 @@ def fixups_js(has_html, has_sx, has_dom):
|
||||
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
|
||||
if has_parser:
|
||||
parser = '''
|
||||
|
||||
@@ -199,11 +199,6 @@ class PyEmitter:
|
||||
"ho-every": "ho_every",
|
||||
"ho-for-each": "ho_for_each",
|
||||
"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",
|
||||
"ho-form?": "is_ho_form",
|
||||
"strip-prefix": "strip_prefix",
|
||||
@@ -1080,7 +1075,7 @@ from typing import Any
|
||||
# =========================================================================
|
||||
|
||||
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,
|
||||
)
|
||||
from shared.sx.parser import SxExpr
|
||||
@@ -1189,8 +1184,6 @@ def type_of(x):
|
||||
return "macro"
|
||||
if isinstance(x, _RawHTML):
|
||||
return "raw-html"
|
||||
if isinstance(x, StyleValue):
|
||||
return "style-value"
|
||||
if isinstance(x, Continuation):
|
||||
return "continuation"
|
||||
if isinstance(x, list):
|
||||
@@ -1355,14 +1348,6 @@ def is_macro(x):
|
||||
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):
|
||||
return name in env
|
||||
|
||||
@@ -1513,8 +1498,6 @@ def serialize(val):
|
||||
if t == "raw-html":
|
||||
escaped = escape_string(raw_html_content(val))
|
||||
return '(raw! "' + escaped + '")'
|
||||
if t == "style-value":
|
||||
return '"' + style_value_class(val) + '"'
|
||||
if t == "list":
|
||||
if not val:
|
||||
return "()"
|
||||
@@ -1534,7 +1517,7 @@ def serialize(val):
|
||||
_SPECIAL_FORM_NAMES = frozenset([
|
||||
"if", "when", "cond", "case", "and", "or",
|
||||
"let", "let*", "lambda", "fn",
|
||||
"define", "defcomp", "defmacro", "defstyle", "defkeyframes",
|
||||
"define", "defcomp", "defmacro", "defstyle",
|
||||
"defhandler", "defpage", "defquery", "defaction", "defrelation",
|
||||
"begin", "do", "quote", "quasiquote",
|
||||
"->", "set!",
|
||||
@@ -1696,7 +1679,7 @@ def aser_special(name, expr, env):
|
||||
fn(item)
|
||||
return results if results else NIL
|
||||
# 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"):
|
||||
trampoline(eval_expr(expr, env))
|
||||
return NIL
|
||||
|
||||
@@ -123,4 +123,4 @@
|
||||
|
||||
(define-boundary-types
|
||||
(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 "defmacro") (sf-defmacro args env)
|
||||
(= name "defstyle") (sf-defstyle args env)
|
||||
(= name "defkeyframes") (sf-defkeyframes args env)
|
||||
(= name "defhandler") (sf-defhandler args env)
|
||||
(= name "defpage") (sf-defpage args env)
|
||||
(= name "defquery") (sf-defquery args env)
|
||||
@@ -559,23 +558,13 @@
|
||||
|
||||
(define sf-defstyle
|
||||
(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))
|
||||
(value (trampoline (eval-expr (nth args 1) env))))
|
||||
(env-set! env (symbol-name name-sym) 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
|
||||
(fn (args env)
|
||||
(if (empty? args)
|
||||
@@ -931,9 +920,6 @@
|
||||
;; (zip lists...) → list of tuples
|
||||
;;
|
||||
;;
|
||||
;; CSSX (style system):
|
||||
;; (build-keyframes name steps env) → StyleValue (platform builds @keyframes)
|
||||
;;
|
||||
;; Dynamic wind (for dynamic-wind):
|
||||
;; (push-wind! before after) → void (push wind record onto stack)
|
||||
;; (pop-wind!) → void (pop wind record from stack)
|
||||
|
||||
@@ -541,18 +541,6 @@
|
||||
;; 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?
|
||||
(fn (name)
|
||||
(or (= name "define") (= name "defcomp") (= name "defmacro")
|
||||
(= name "defstyle") (= name "defkeyframes") (= name "defhandler"))))
|
||||
(= name "defstyle") (= name "defhandler"))))
|
||||
|
||||
|
||||
(define parse-element-args
|
||||
@@ -116,9 +116,6 @@
|
||||
""
|
||||
;; Nil values — skip
|
||||
(nil? val) ""
|
||||
;; StyleValue on :style → emit as class
|
||||
(and (= key "style") (style-value? val))
|
||||
(str " class=\"" (style-value-class val) "\"")
|
||||
;; Normal attr
|
||||
:else (str " " key "=\"" (escape-attr (str val)) "\""))))
|
||||
(keys attrs)))))
|
||||
@@ -202,10 +199,6 @@
|
||||
;; (escape-attr s) → attribute-value-escaped 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:
|
||||
;; (serialize val) → SX source string representation of val
|
||||
;;
|
||||
|
||||
@@ -363,20 +363,12 @@
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(define-special-form "defstyle"
|
||||
:syntax (defstyle name atoms ...)
|
||||
:doc "Define a named style. Evaluates atoms to a StyleValue and binds
|
||||
it to name in the environment."
|
||||
:syntax (defstyle name expr)
|
||||
:doc "Define a named style value. Evaluates expr and binds the result
|
||||
to name in the environment. The value is typically a class string
|
||||
or a function that returns class strings."
|
||||
:tail-position "none"
|
||||
: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))")
|
||||
:example "(defstyle card-style \"rounded-lg shadow-md p-4 bg-white\")")
|
||||
|
||||
(define-special-form "defhandler"
|
||||
:syntax (defhandler name (&key params ...) body)
|
||||
|
||||
@@ -18,7 +18,7 @@ from typing import Any
|
||||
# =========================================================================
|
||||
|
||||
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,
|
||||
)
|
||||
from shared.sx.parser import SxExpr
|
||||
@@ -126,8 +126,6 @@ def type_of(x):
|
||||
return "macro"
|
||||
if isinstance(x, _RawHTML):
|
||||
return "raw-html"
|
||||
if isinstance(x, StyleValue):
|
||||
return "style-value"
|
||||
if isinstance(x, Continuation):
|
||||
return "continuation"
|
||||
if isinstance(x, list):
|
||||
@@ -292,14 +290,6 @@ def is_macro(x):
|
||||
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):
|
||||
return name in env
|
||||
|
||||
@@ -450,8 +440,6 @@ def serialize(val):
|
||||
if t == "raw-html":
|
||||
escaped = escape_string(raw_html_content(val))
|
||||
return '(raw! "' + escaped + '")'
|
||||
if t == "style-value":
|
||||
return '"' + style_value_class(val) + '"'
|
||||
if t == "list":
|
||||
if not val:
|
||||
return "()"
|
||||
@@ -471,7 +459,7 @@ def serialize(val):
|
||||
_SPECIAL_FORM_NAMES = frozenset([
|
||||
"if", "when", "cond", "case", "and", "or",
|
||||
"let", "let*", "lambda", "fn",
|
||||
"define", "defcomp", "defmacro", "defstyle", "defkeyframes",
|
||||
"define", "defcomp", "defmacro", "defstyle",
|
||||
"defhandler", "defpage", "defquery", "defaction", "defrelation",
|
||||
"begin", "do", "quote", "quasiquote",
|
||||
"->", "set!",
|
||||
@@ -633,7 +621,7 @@ def aser_special(name, expr, env):
|
||||
fn(item)
|
||||
return results if results else NIL
|
||||
# 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"):
|
||||
trampoline(eval_expr(expr, env))
|
||||
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-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 = 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 = 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 = 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']
|
||||
|
||||
# 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 = 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 = 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 = 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-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 = ['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?
|
||||
is_render_html_form = lambda name: contains_p(RENDER_HTML_FORMS, name)
|
||||
|
||||
Reference in New Issue
Block a user