From 9f2f0dacaf8caf7a360099d9dfdd63ba87ae2513 Mon Sep 17 00:00:00 2001 From: giles Date: Sat, 28 Feb 2026 23:40:14 +0000 Subject: [PATCH] Add update/hydrate methods and browser auto-init to sexp.js Adds Sexp.update() for re-rendering data-sexp elements with new data, Sexp.hydrate() for finding and rendering all [data-sexp] elements, and auto-init on DOMContentLoaded + htmx:afterSwap integration. Co-Authored-By: Claude Opus 4.6 --- shared/static/scripts/sexp.js | 52 +++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 3 deletions(-) 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); }); }