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:
@@ -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)), "<", "<"), ">", ">");
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user