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

@@ -166,6 +166,22 @@
document.cookie = args[0] + "=" + encodeURIComponent(args[1] || "") + ";path=/;max-age=31536000;SameSite=Lax";
});
// IntersectionObserver — native JS to avoid bytecode callback issues
K.registerNative("observe-intersection", function(args) {
var el = args[0], callback = args[1], once = args[2], delay = args[3];
var obs = new IntersectionObserver(function(entries) {
for (var i = 0; i < entries.length; i++) {
if (entries[i].isIntersecting) {
var d = (delay && delay !== null) ? delay : 0;
setTimeout(function() { K.callFn(callback, []); }, d);
if (once) obs.unobserve(el);
}
}
});
obs.observe(el);
return obs;
});
// ================================================================
// Load SX web libraries and adapters
// ================================================================
@@ -398,68 +414,6 @@
"children:", islands[j].children.length);
}
console.log("[sx] boot done");
// sx-on: inline event handlers — bind from JS because the WASM
// CSS selector [sx-on\:] doesn't match. Uses MutationObserver to
// also catch elements added after boot (e.g. from swaps).
function _bindSxOn(root) {
var all = (root || document).querySelectorAll('*');
for (var k = 0; k < all.length; k++) {
var el = all[k];
if (el._sxOnBound) continue;
var attrs = el.attributes;
var hasSxOn = false;
for (var a = 0; a < attrs.length; a++) {
var aname = attrs[a].name;
if (aname.indexOf('sx-on:') === 0) {
hasSxOn = true;
var evtName = aname.slice(6);
// HTML lowercases attrs: afterSwap → afterswap.
// Engine dispatches camelCase: sx:afterSwap.
// Listen for both forms.
var evtName2 = null;
if (evtName.indexOf('after') === 0 || evtName.indexOf('before') === 0) {
evtName2 = 'sx:' + evtName; // lowercase form
// Also try camelCase form
var camel = evtName.replace(/swap|request|settle/gi, function(m) {
return m.charAt(0).toUpperCase() + m.slice(1);
});
evtName = 'sx:' + camel;
}
(function(el2, evt, evt2, code) {
var handler = function(e) {
try { new Function('event', code).call(el2, e); }
catch(err) { console.warn('[sx] sx-on:' + evt + ' error:', err); }
};
el2.addEventListener(evt, handler);
if (evt2) el2.addEventListener(evt2, handler);
})(el, evtName, evtName2, attrs[a].value);
}
}
if (hasSxOn) el._sxOnBound = true;
}
}
_bindSxOn(document);
// Re-bind after swaps
document.addEventListener('sx:afterSwap', function(e) {
if (e.target) _bindSxOn(e.target);
});
// Global keyboard shortcut dispatch — WASM host-callbacks on
// document/body don't fire, so handle from:body keyboard
// triggers in JS and call execute-request via the SX engine.
document.addEventListener("keyup", function(e) {
if (e.target && e.target.matches && e.target.matches("input,textarea,select")) return;
var sel = '[sx-trigger*="key==\'' + e.key + '\'"]';
var els = document.querySelectorAll(sel);
for (var i = 0; i < els.length; i++) {
var el = els[i];
if (!el.id) el.id = "_sx_kbd_" + Math.random().toString(36).slice(2);
try {
K.eval('(execute-request (dom-query-by-id "' + el.id + '") nil nil)');
} catch(err) { console.warn("[sx] keyboard dispatch error:", err); }
}
});
}
}
};