diff --git a/shared/static/scripts/sexp.js b/shared/static/scripts/sexp.js
index 3537af6..292028b 100644
--- a/shared/static/scripts/sexp.js
+++ b/shared/static/scripts/sexp.js
@@ -1189,6 +1189,49 @@
}
},
+ /**
+ * Bind client-side sexp rendering to elements with data-sexp-* attrs.
+ *
+ * Pattern:
+ *
+ *
+ *
+ *
+ * Call Sexp.update(el, {title: "New"}) to re-render with new data.
+ */
+ update: function (target, newEnv) {
+ var el = typeof target === "string" ? document.querySelector(target) : target;
+ if (!el) return;
+ var source = el.getAttribute("data-sexp");
+ if (!source) return;
+ var baseEnv = {};
+ var envAttr = el.getAttribute("data-sexp-env");
+ if (envAttr) {
+ try { baseEnv = JSON.parse(envAttr); } catch (e) { /* ignore */ }
+ }
+ var env = merge({}, _componentEnv, baseEnv, newEnv || {});
+ var node = renderDOM(parse(source), env);
+ el.textContent = "";
+ el.appendChild(node);
+ if (newEnv) {
+ merge(baseEnv, newEnv);
+ el.setAttribute("data-sexp-env", JSON.stringify(baseEnv));
+ }
+ },
+
+ /**
+ * Find all [data-sexp] elements within root and render them.
+ * Useful after HTMX swaps bring in new sexp-enabled elements.
+ */
+ hydrate: function (root) {
+ var els = (root || document).querySelectorAll("[data-sexp]");
+ for (var i = 0; i < els.length; i++) {
+ if (els[i]._sexpHydrated) continue;
+ els[i]._sexpHydrated = true;
+ Sexp.update(els[i]);
+ }
+ },
+
// For testing
_types: { NIL: NIL, Symbol: Symbol, Keyword: Keyword, Lambda: Lambda, Component: Component, RawHTML: RawHTML },
_eval: sexpEval,
@@ -1203,17 +1246,20 @@
// =========================================================================
if (typeof document !== "undefined") {
- // Process sexp scripts on DOMContentLoaded
- function init() { Sexp.processScripts(); }
+ var init = function () {
+ Sexp.processScripts();
+ Sexp.hydrate();
+ };
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}
- // Re-process after HTMX swaps (new sexp scripts may have arrived)
+ // Re-process after HTMX swaps
document.addEventListener("htmx:afterSwap", function (e) {
Sexp.processScripts(e.detail.target);
+ Sexp.hydrate(e.detail.target);
});
}