;; _hyperscript integration — wire _="..." attributes to compiled SX ;; ;; Entry points: ;; (hs-handler src) — compile source to callable (fn (me) ...) ;; (hs-activate! el) — activate hyperscript on a single element ;; (hs-boot!) — scan DOM, activate all _="..." elements ;; (hs-boot-subtree! root) — activate within a subtree (for HTMX swaps) ;; ── Compile source to a handler function ──────────────────────── ;; Returns a function (fn (me) ...) that can be called with a DOM element. ;; Uses eval-expr-cek to turn the SX data structure into a live closure. (begin (define hs-collect-vars (fn (sx) (define vars (list)) (define reserved (list (quote me) (quote it) (quote event) (quote you) (quote yourself))) (define walk (fn (node) (when (list? node) (when (and (> (len node) 1) (= (first node) (quote set!)) (symbol? (nth node 1))) (let ((name (nth node 1))) (when (and (not (some (fn (v) (= v name)) vars)) (not (some (fn (v) (= v name)) reserved))) (set! vars (cons name vars))))) (for-each walk node)))) (walk sx) vars)) (define hs-handler (fn (src) (let ((sx (hs-to-sx-from-source src))) (let ((extra-vars (hs-collect-vars sx))) (do (for-each (fn (v) (eval-expr-cek (list (quote define) v nil))) extra-vars) (let ((guarded (list (quote guard) (list (quote _e) (list (quote true) (list (quote if) (list (quote and) (list (quote list?) (quote _e)) (list (quote =) (list (quote first) (quote _e)) "hs-return")) (list (quote nth) (quote _e) 1) (list (quote raise) (quote _e))))) sx))) (eval-expr-cek (list (quote fn) (list (quote me)) (list (quote let) (list (list (quote it) nil) (list (quote event) nil)) guarded)))))))))) ;; ── Activate a single element ─────────────────────────────────── ;; Reads the _="..." attribute, compiles, and executes with me=element. ;; Marks the element to avoid double-activation. (define hs-activate! (fn (el) (let ((src (dom-get-attr el "_")) (prev (dom-get-data el "hs-script"))) (when (and src (not (= src prev))) (hs-log-event! "hyperscript:init") (dom-set-data el "hs-script" src) (dom-set-data el "hs-active" true) (dom-set-attr el "data-hyperscript-powered" "true") (let ((handler (hs-handler src))) (handler el)))))) ;; ── Boot: scan entire document ────────────────────────────────── ;; Called once at page load. Finds all elements with _ attribute, ;; compiles their hyperscript, and activates them. (define hs-deactivate! (fn (el) (let ((unlisteners (or (dom-get-data el "hs-unlisteners") (list)))) (for-each (fn (u) (when u (u))) unlisteners) (dom-set-data el "hs-unlisteners" (list)) (dom-set-data el "hs-active" false) (dom-set-data el "hs-script" nil)))) ;; ── Boot subtree: for dynamic content ─────────────────────────── ;; Called after HTMX swaps or dynamic DOM insertion. ;; Only activates elements within the given root. (define hs-boot! (fn () (let ((elements (dom-query-all (host-get (host-global "document") "body") "[_]"))) (for-each (fn (el) (hs-activate! el)) elements)))) (define hs-boot-subtree! (fn (root) (let ((elements (dom-query-all root "[_]"))) (for-each (fn (el) (hs-activate! el)) elements)) (when (dom-get-attr root "_") (hs-activate! root))))