sx-tools: WASM kernel updates, TW/CSSX rework, content refresh, new debugging tools

Build tooling: updated OCaml bootstrapper, compile-modules, bundle.sh, sx-build-all.
WASM browser: rebuilt sx_browser.bc.js/wasm, sx-platform-2.js, .sxbc bytecode files.
CSSX/Tailwind: reworked cssx.sx templates and tw-layout, added tw-type support.
Content: refreshed essays, plans, geography, reactive islands, docs, demos, handlers.
New tools: bisect_sxbc.sh, test-spa.js, render-trace.sx, morph playwright spec.
Tests: added test-match.sx, test-examples.sx, updated test-tw.sx and web tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-02 11:31:57 +00:00
parent 9ed1100ef6
commit d40a9c6796
178 changed files with 13591 additions and 9110 deletions

View File

@@ -24,7 +24,7 @@
// =========================================================================
var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } });
var SX_VERSION = "2026-03-31T23:22:57Z";
var SX_VERSION = "2026-04-01T20:24:51Z";
function isNil(x) { return x === NIL || x === null || x === undefined; }
function isSxTruthy(x) { return x !== false && !isNil(x); }
@@ -462,6 +462,8 @@
PRIMITIVES["dict-set!"] = function(d, k, v) { d[k] = v; return v; };
PRIMITIVES["has-key?"] = function(d, k) { return d !== null && d !== undefined && k in d; };
PRIMITIVES["into"] = function(target, coll) {
if (target === "list") return Array.isArray(coll) ? coll.slice() : Object.entries(coll).map(function(e) { return [e[0], e[1]]; });
if (target === "dict") { var r = {}; for (var i = 0; i < coll.length; i++) { var p = coll[i]; if (Array.isArray(p) && p.length >= 2) r[p[0]] = p[1]; } return r; }
if (Array.isArray(target)) return Array.isArray(coll) ? coll.slice() : Object.entries(coll);
var r = {}; for (var i = 0; i < coll.length; i++) { var p = coll[i]; if (Array.isArray(p) && p.length >= 2) r[p[0]] = p[1]; }
return r;
@@ -2864,10 +2866,20 @@ PRIMITIVES["render-html-form?"] = isRenderHtmlForm;
return (isSxTruthy(!isSxTruthy(sxEq(typeOf(head), "symbol"))) ? join("", map(function(x) { return renderValueToHtml(x, env); }, expr)) : (function() {
var name = symbolName(head);
var args = rest(expr);
return (isSxTruthy(sxEq(name, "<>")) ? join("", map(function(x) { return renderToHtml(x, env); }, args)) : (isSxTruthy(sxEq(name, "raw!")) ? join("", map(function(x) { return (String(trampoline(evalExpr(x, env)))); }, args)) : (isSxTruthy(sxEq(name, "lake")) ? renderHtmlLake(args, env) : (isSxTruthy(sxEq(name, "marsh")) ? renderHtmlMarsh(args, env) : (isSxTruthy(sxOr(sxEq(name, "portal"), sxEq(name, "error-boundary"), sxEq(name, "promise-delayed"))) ? join("", map(function(x) { return renderToHtml(x, env); }, args)) : (isSxTruthy(contains(HTML_TAGS, name)) ? renderHtmlElement(name, args, env) : (isSxTruthy((isSxTruthy(startsWith(name, "~")) && isSxTruthy(envHas(env, name)) && isIsland(envGet(env, name)))) ? renderHtmlIsland(envGet(env, name), args, env) : (isSxTruthy(startsWith(name, "~")) ? (function() {
return (isSxTruthy(sxEq(name, "<>")) ? join("", map(function(x) { return renderToHtml(x, env); }, args)) : (isSxTruthy(sxEq(name, "raw!")) ? join("", map(function(x) { return (String(trampoline(evalExpr(x, env)))); }, args)) : (isSxTruthy(sxEq(name, "lake")) ? renderHtmlLake(args, env) : (isSxTruthy(sxEq(name, "marsh")) ? renderHtmlMarsh(args, env) : (isSxTruthy(sxEq(name, "error-boundary")) ? (function() {
var hasFallback = (len(args) > 1);
return (function() {
var bodyExprs = (isSxTruthy(hasFallback) ? rest(args) : args);
var fallbackExpr = (isSxTruthy(hasFallback) ? first(args) : NIL);
return (String("<div data-sx-boundary=\"true\">") + String(tryCatch(function() { return join("", map(function(x) { return renderToHtml(x, env); }, bodyExprs)); }, function(err) { return (function() {
var safeErr = replace_(replace_((String(err)), "<", "&lt;"), ">", "&gt;");
return (isSxTruthy((isSxTruthy(fallbackExpr) && !isSxTruthy(isNil(fallbackExpr)))) ? tryCatch(function() { return renderToHtml([trampoline(evalExpr(fallbackExpr, env)), err, NIL], env); }, function(e2) { return (String("<div class=\"sx-render-error\" style=\"color:red;font-size:0.875rem;padding:0.5rem;border:1px solid red;border-radius:0.25rem;margin:0.5rem 0;\">Render error: ") + String(safeErr) + String("</div>")); }) : (String("<div class=\"sx-render-error\" style=\"color:red;font-size:0.875rem;padding:0.5rem;border:1px solid red;border-radius:0.25rem;margin:0.5rem 0;\">Render error: ") + String(safeErr) + String("</div>")));
})(); })) + String("</div>"));
})();
})() : (isSxTruthy(sxOr(sxEq(name, "portal"), sxEq(name, "promise-delayed"))) ? join("", map(function(x) { return renderToHtml(x, env); }, args)) : (isSxTruthy(contains(HTML_TAGS, name)) ? renderHtmlElement(name, args, env) : (isSxTruthy((isSxTruthy(startsWith(name, "~")) && isSxTruthy(envHas(env, name)) && isIsland(envGet(env, name)))) ? renderHtmlIsland(envGet(env, name), args, env) : (isSxTruthy(startsWith(name, "~")) ? (function() {
var val = envGet(env, name);
return (isSxTruthy(isComponent(val)) ? renderHtmlComponent(val, args, env) : (isSxTruthy(isMacro(val)) ? renderToHtml(expandMacro(val, args, env), env) : (String("<!-- unknown component: ") + String(name) + String(" -->"))));
})() : (isSxTruthy(isRenderHtmlForm(name)) ? dispatchHtmlForm(name, expr, env) : (isSxTruthy((isSxTruthy(envHas(env, name)) && isMacro(envGet(env, name)))) ? renderToHtml(expandMacro(envGet(env, name), args, env), env) : renderValueToHtml(trampoline(evalExpr(expr, env)), env)))))))))));
})() : (isSxTruthy(isRenderHtmlForm(name)) ? dispatchHtmlForm(name, expr, env) : (isSxTruthy((isSxTruthy(envHas(env, name)) && isMacro(envGet(env, name)))) ? renderToHtml(expandMacro(envGet(env, name), args, env), env) : renderValueToHtml(trampoline(evalExpr(expr, env)), env))))))))))));
})());
})()); };
PRIMITIVES["render-list-to-html"] = renderListToHtml;
@@ -3099,11 +3111,25 @@ PRIMITIVES["aser"] = aser;
var comp = (isSxTruthy(envHas(env, name)) ? envGet(env, name) : NIL);
var expandAll = (isSxTruthy(envHas(env, "expand-components?")) ? expandComponents_p() : false);
return (isSxTruthy((isSxTruthy(comp) && isMacro(comp))) ? aser(expandMacro(comp, args, env), env) : (isSxTruthy((isSxTruthy(comp) && isSxTruthy(isComponent(comp)) && isSxTruthy(!isSxTruthy(isIsland(comp))) && isSxTruthy(sxOr(expandAll, sxEq(componentAffinity(comp), "server"))) && !isSxTruthy(sxEq(componentAffinity(comp), "client")))) ? aserExpandComponent(comp, args, env) : aserCall(name, args, env)));
})() : (isSxTruthy(sxEq(name, "lake")) ? aserCall(name, args, env) : (isSxTruthy(sxEq(name, "marsh")) ? aserCall(name, args, env) : (isSxTruthy(contains(HTML_TAGS, name)) ? aserCall(name, args, env) : (isSxTruthy(sxOr(isSpecialForm(name), isHoForm(name))) ? aserSpecial(name, expr, env) : (isSxTruthy((isSxTruthy(envHas(env, name)) && isMacro(envGet(env, name)))) ? aser(expandMacro(envGet(env, name), args, env), env) : (function() {
})() : (isSxTruthy(sxEq(name, "lake")) ? aserCall(name, args, env) : (isSxTruthy(sxEq(name, "marsh")) ? aserCall(name, args, env) : (isSxTruthy(sxEq(name, "error-boundary")) ? (function() {
var hasFallback = (len(args) > 1);
return (function() {
var bodyExprs = (isSxTruthy(hasFallback) ? rest(args) : args);
var errStr = NIL;
return (function() {
var rendered = tryCatch(function() { return join("", map(function(x) { return (function() {
var v = aser(x, env);
return (isSxTruthy(sxEq(typeOf(v), "sx-expr")) ? sxExprSource(v) : (isSxTruthy(isNil(v)) ? "" : serialize(v)));
})(); }, bodyExprs)); }, function(err) { errStr = (String(err));
return NIL; });
return (isSxTruthy(rendered) ? makeSxExpr((String("(error-boundary ") + String(rendered) + String(")"))) : makeSxExpr((String("(div :data-sx-boundary \"true\" ") + String("(div :class \"sx-render-error\" ") + String(":style \"color:red;font-size:0.875rem;padding:0.5rem;border:1px solid red;border-radius:0.25rem;margin:0.5rem 0;\" ") + String("\"Render error: ") + String(replace_(replace_(errStr, "\"", "'"), "\\", "\\\\")) + String("\"))"))));
})();
})();
})() : (isSxTruthy(contains(HTML_TAGS, name)) ? aserCall(name, args, env) : (isSxTruthy(sxOr(isSpecialForm(name), isHoForm(name))) ? aserSpecial(name, expr, env) : (isSxTruthy((isSxTruthy(envHas(env, name)) && isMacro(envGet(env, name)))) ? aser(expandMacro(envGet(env, name), args, env), env) : (function() {
var f = trampoline(evalExpr(head, env));
var evaledArgs = map(function(a) { return trampoline(evalExpr(a, env)); }, args);
return (isSxTruthy((isSxTruthy(isCallable(f)) && isSxTruthy(!isSxTruthy(isLambda(f))) && isSxTruthy(!isSxTruthy(isComponent(f))) && !isSxTruthy(isIsland(f)))) ? apply(f, evaledArgs) : (isSxTruthy(isLambda(f)) ? trampoline(callLambda(f, evaledArgs, env)) : (isSxTruthy(isComponent(f)) ? aserCall((String("~") + String(componentName(f))), args, env) : (isSxTruthy(isIsland(f)) ? aserCall((String("~") + String(componentName(f))), args, env) : error((String("Not callable: ") + String(inspect(f))))))));
})()))))))));
})())))))))));
})());
})(); };
PRIMITIVES["aser-list"] = aserList;
@@ -4522,8 +4548,8 @@ PRIMITIVES["render-dom-portal"] = renderDomPortal;
// render-dom-error-boundary
var renderDomErrorBoundary = function(args, env, ns) { return (function() {
var fallbackExpr = first(args);
var bodyExprs = rest(args);
var fallbackExpr = (isSxTruthy((len(args) > 1)) ? first(args) : NIL);
var bodyExprs = (isSxTruthy((len(args) > 1)) ? rest(args) : args);
var container = domCreateElement("div", NIL);
var retryVersion = signal(0);
domSetAttr(container, "data-sx-boundary", "true");
@@ -4540,7 +4566,13 @@ return (function() {
var fallbackFn = trampoline(evalExpr(fallbackExpr, env));
var retryFn = function() { return swap_b(retryVersion, function(n) { return (n + 1); }); };
return (function() {
var fallbackDom = (isSxTruthy(isLambda(fallbackFn)) ? renderLambdaDom(fallbackFn, [err, retryFn], env, ns) : renderToDom(apply(fallbackFn, [err, retryFn]), env, ns));
var fallbackDom = (isSxTruthy(isNil(fallbackFn)) ? (function() {
var el = domCreateElement("div", NIL);
domSetAttr(el, "class", "sx-render-error");
domSetAttr(el, "style", "color:red;font-size:0.875rem;padding:0.5rem;border:1px solid red;border-radius:0.25rem;margin:0.5rem 0;");
domSetTextContent(el, (String("Render error: ") + String(err)));
return el;
})() : (isSxTruthy(isLambda(fallbackFn)) ? renderLambdaDom(fallbackFn, [err, retryFn], env, ns) : renderToDom(apply(fallbackFn, [err, retryFn]), env, ns)));
return domAppend(container, fallbackDom);
})();
})(); }); });
@@ -4578,7 +4610,7 @@ PRIMITIVES["parse-time"] = parseTime;
PRIMITIVES["parse-trigger-spec"] = parseTriggerSpec;
// default-trigger
var defaultTrigger = function(tagName) { return (isSxTruthy(sxEq(tagName, "FORM")) ? [{["event"]: "submit", ["modifiers"]: {}}] : (isSxTruthy(sxOr(sxEq(tagName, "INPUT"), sxEq(tagName, "SELECT"), sxEq(tagName, "TEXTAREA"))) ? [{["event"]: "change", ["modifiers"]: {}}] : [{["event"]: "click", ["modifiers"]: {}}])); };
var defaultTrigger = function(tagName) { return (isSxTruthy(sxEq(tagName, "form")) ? [{["event"]: "submit", ["modifiers"]: {}}] : (isSxTruthy(sxOr(sxEq(tagName, "input"), sxEq(tagName, "select"), sxEq(tagName, "textarea"))) ? [{["event"]: "change", ["modifiers"]: {}}] : [{["event"]: "click", ["modifiers"]: {}}])); };
PRIMITIVES["default-trigger"] = defaultTrigger;
// get-verb-info
@@ -5053,11 +5085,11 @@ PRIMITIVES["handle-fetch-success"] = handleFetchSuccess;
var container = domCreateElement("div", NIL);
domAppend(container, rendered);
processOobSwaps(container, function(t, oob, s) { disposeIslandsIn(t);
swapDomNodes(t, oob, s);
sxHydrate(t);
return processElements(t); });
swapDomNodes(t, (isSxTruthy(sxEq(s, "innerHTML")) ? childrenToFragment(oob) : oob), s);
return postSwap(t); });
return (function() {
var selectSel = domGetAttr(el, "sx-select");
return (function() {
var content = (isSxTruthy(selectSel) ? selectFromContainer(container, selectSel) : childrenToFragment(container));
disposeIslandsIn(target);
return withTransition(useTransition, function() { return (function() {
@@ -5065,6 +5097,7 @@ return processElements(t); });
return postSwap((isSxTruthy(sxEq(swapStyle, "outerHTML")) ? domParent(sxOr(swapResult, target)) : sxOr(swapResult, target)));
})(); });
})();
})();
})() : NIL);
})();
})();
@@ -5078,12 +5111,20 @@ PRIMITIVES["handle-sx-response"] = handleSxResponse;
var selectSel = domGetAttr(el, "sx-select");
disposeIslandsIn(target);
return (isSxTruthy(selectSel) ? (function() {
var html = selectHtmlFromDoc(doc, selectSel);
var container = domCreateElement("div", NIL);
domSetInnerHtml(container, domBodyInnerHtml(doc));
processOobSwaps(container, function(t, oob, s) { disposeIslandsIn(t);
swapDomNodes(t, oob, s);
return postSwap(t); });
hoistHeadElements(container);
return (function() {
var html = selectFromContainer(container, selectSel);
return withTransition(useTransition, function() { return (function() {
var swapRoot = swapHtmlString(target, html, swapStyle);
var swapRoot = swapDomNodes(target, html, swapStyle);
logInfo((String("swap-root: ") + String((isSxTruthy(swapRoot) ? domTagName(swapRoot) : "nil")) + String(" target: ") + String(domTagName(target))));
return postSwap(sxOr(swapRoot, target));
})(); });
})();
})() : (function() {
var container = domCreateElement("div", NIL);
domSetInnerHtml(container, domBodyInnerHtml(doc));
@@ -5119,7 +5160,10 @@ PRIMITIVES["handle-retry"] = handleRetry;
return forEach(function(trigger) { return (function() {
var kind = classifyTrigger(trigger);
var mods = get(trigger, "modifiers");
return (isSxTruthy(sxEq(kind, "poll")) ? setInterval_(function() { return executeRequest(el, NIL, NIL); }, get(mods, "interval")) : (isSxTruthy(sxEq(kind, "intersect")) ? observeIntersection(el, function() { return executeRequest(el, NIL, NIL); }, false, get(mods, "delay")) : (isSxTruthy(sxEq(kind, "load")) ? setTimeout_(function() { return executeRequest(el, NIL, NIL); }, sxOr(get(mods, "delay"), 0)) : (isSxTruthy(sxEq(kind, "revealed")) ? observeIntersection(el, function() { return executeRequest(el, NIL, NIL); }, true, get(mods, "delay")) : (isSxTruthy(sxEq(kind, "event")) ? bindEvent(el, get(trigger, "event"), mods, verbInfo) : NIL)))));
return (isSxTruthy(sxEq(kind, "poll")) ? (function() {
var intervalId = NIL;
return (intervalId = setInterval_(function() { return (isSxTruthy(hostGet(el, "isConnected")) ? executeRequest(el, NIL, NIL) : (clearInterval_(intervalId), logInfo("poll stopped: element removed"))); }, get(mods, "interval")));
})() : (isSxTruthy(sxEq(kind, "intersect")) ? observeIntersection(el, function() { return executeRequest(el, NIL, NIL); }, false, get(mods, "delay")) : (isSxTruthy(sxEq(kind, "load")) ? setTimeout_(function() { return executeRequest(el, NIL, NIL); }, sxOr(get(mods, "delay"), 0)) : (isSxTruthy(sxEq(kind, "revealed")) ? observeIntersection(el, function() { return executeRequest(el, NIL, NIL); }, true, get(mods, "delay")) : (isSxTruthy(sxEq(kind, "event")) ? bindEvent(el, get(trigger, "event"), mods, verbInfo) : NIL)))));
})(); }, triggers);
})(); };
PRIMITIVES["bind-triggers"] = bindTriggers;
@@ -5421,7 +5465,32 @@ PRIMITIVES["offline-aware-mutation"] = offlineAwareMutation;
PRIMITIVES["current-page-layout"] = currentPageLayout;
// swap-rendered-content
var swapRenderedContent = function(target, rendered, pathname) { return (disposeIslandsIn(target), domSetTextContent(target, ""), domAppend(target, rendered), hoistHeadElementsFull(target), processElements(target), sxHydrateElements(target), sxHydrateIslands(target), runPostRenderHooks(), domDispatch(target, "sx:clientRoute", {["pathname"]: pathname}), logInfo((String("sx:route client ") + String(pathname)))); };
var swapRenderedContent = function(target, rendered, pathname) { return (function() {
var container = domCreateElement("div", NIL);
domAppend(container, rendered);
processOobSwaps(container, function(t, oob, s) { disposeIslandsIn(t);
swapDomNodes(t, (isSxTruthy(sxEq(s, "innerHTML")) ? childrenToFragment(oob) : oob), s);
return postSwap(t); });
return (function() {
var targetId = domGetAttr(target, "id");
return (function() {
var inner = (isSxTruthy(targetId) ? domQuery(container, (String("#") + String(targetId))) : NIL);
return (function() {
var content = (isSxTruthy(inner) ? childrenToFragment(inner) : childrenToFragment(container));
disposeIslandsIn(target);
domSetTextContent(target, "");
domAppend(target, content);
hoistHeadElementsFull(target);
processElements(target);
sxHydrateElements(target);
sxHydrateIslands(target);
runPostRenderHooks();
domDispatch(target, "sx:clientRoute", {["pathname"]: pathname});
return logInfo((String("sx:route client ") + String(pathname)));
})();
})();
})();
})(); };
PRIMITIVES["swap-rendered-content"] = swapRenderedContent;
// resolve-route-target