diff --git a/shared/static/scripts/sx-browser.js b/shared/static/scripts/sx-browser.js index 5d45290..b0da85b 100644 --- a/shared/static/scripts/sx-browser.js +++ b/shared/static/scripts/sx-browser.js @@ -14,7 +14,7 @@ // ========================================================================= var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } }); - var SX_VERSION = "2026-03-13T02:54:01Z"; + var SX_VERSION = "2026-03-13T03:23:01Z"; function isNil(x) { return x === NIL || x === null || x === undefined; } function isSxTruthy(x) { return x !== false && !isNil(x); } @@ -1898,7 +1898,10 @@ return (function() { var _m = typeOf(expr); if (_m == "nil") return createFragme })() : renderToDom(trampoline(evalExpr(expr, env)), env, ns)))))))))))))); })() : (isSxTruthy(sxOr(isLambda(head), (typeOf(head) == "list"))) ? renderToDom(trampoline(evalExpr(expr, env)), env, ns) : (function() { var frag = createFragment(); - { var _c = expr; for (var _i = 0; _i < _c.length; _i++) { var x = _c[_i]; domAppend(frag, renderToDom(x, env, ns)); } } + { var _c = expr; for (var _i = 0; _i < _c.length; _i++) { var x = _c[_i]; (function() { + var result = renderToDom(x, env, ns); + return (isSxTruthy(!isSxTruthy(isSpread(result))) ? domAppend(frag, result) : NIL); +})(); } } return frag; })())); })(); }; @@ -1964,7 +1967,10 @@ return (function() { var _m = typeOf(expr); if (_m == "nil") return createFragme if (isSxTruthy(componentHasChildren(comp))) { (function() { var childFrag = createFragment(); - { var _c = children; for (var _i = 0; _i < _c.length; _i++) { var c = _c[_i]; domAppend(childFrag, renderToDom(c, env, ns)); } } + { var _c = children; for (var _i = 0; _i < _c.length; _i++) { var c = _c[_i]; (function() { + var result = renderToDom(c, env, ns); + return (isSxTruthy(!isSxTruthy(isSpread(result))) ? domAppend(childFrag, result) : NIL); +})(); } } return envSet(local, "children", childFrag); })(); } @@ -1975,7 +1981,10 @@ return (function() { var _m = typeOf(expr); if (_m == "nil") return createFragme // render-dom-fragment var renderDomFragment = function(args, env, ns) { return (function() { var frag = createFragment(); - { var _c = args; for (var _i = 0; _i < _c.length; _i++) { var x = _c[_i]; domAppend(frag, renderToDom(x, env, ns)); } } + { var _c = args; for (var _i = 0; _i < _c.length; _i++) { var x = _c[_i]; (function() { + var result = renderToDom(x, env, ns); + return (isSxTruthy(!isSxTruthy(isSpread(result))) ? domAppend(frag, result) : NIL); +})(); } } return frag; })(); }; @@ -2078,14 +2087,22 @@ return (function() { var _m = typeOf(expr); if (_m == "nil") return createFragme return (isSxTruthy(branch) ? renderToDom(branch, env, ns) : createFragment()); })()) : (isSxTruthy((name == "case")) ? renderToDom(trampoline(evalExpr(expr, env)), env, ns) : (isSxTruthy(sxOr((name == "let"), (name == "let*"))) ? (function() { var local = processBindings(nth(expr, 1), env); + return (isSxTruthy((len(expr) == 3)) ? renderToDom(nth(expr, 2), local, ns) : (function() { var frag = createFragment(); - { var _c = range(2, len(expr)); for (var _i = 0; _i < _c.length; _i++) { var i = _c[_i]; domAppend(frag, renderToDom(nth(expr, i), local, ns)); } } + { var _c = range(2, len(expr)); for (var _i = 0; _i < _c.length; _i++) { var i = _c[_i]; (function() { + var result = renderToDom(nth(expr, i), local, ns); + return (isSxTruthy(!isSxTruthy(isSpread(result))) ? domAppend(frag, result) : NIL); +})(); } } return frag; -})() : (isSxTruthy(sxOr((name == "begin"), (name == "do"))) ? (function() { +})()); +})() : (isSxTruthy(sxOr((name == "begin"), (name == "do"))) ? (isSxTruthy((len(expr) == 2)) ? renderToDom(nth(expr, 1), env, ns) : (function() { var frag = createFragment(); - { var _c = range(1, len(expr)); for (var _i = 0; _i < _c.length; _i++) { var i = _c[_i]; domAppend(frag, renderToDom(nth(expr, i), env, ns)); } } + { var _c = range(1, len(expr)); for (var _i = 0; _i < _c.length; _i++) { var i = _c[_i]; (function() { + var result = renderToDom(nth(expr, i), env, ns); + return (isSxTruthy(!isSxTruthy(isSpread(result))) ? domAppend(frag, result) : NIL); +})(); } } return frag; -})() : (isSxTruthy(isDefinitionForm(name)) ? (trampoline(evalExpr(expr, env)), createFragment()) : (isSxTruthy((name == "map")) ? (function() { +})()) : (isSxTruthy(isDefinitionForm(name)) ? (trampoline(evalExpr(expr, env)), createFragment()) : (isSxTruthy((name == "map")) ? (function() { var collExpr = nth(expr, 2); return (isSxTruthy((isSxTruthy(_islandScope) && isSxTruthy((typeOf(collExpr) == "list")) && isSxTruthy((len(collExpr) > 1)) && isSxTruthy((typeOf(first(collExpr)) == "symbol")) && (symbolName(first(collExpr)) == "deref"))) ? (function() { var f = trampoline(evalExpr(nth(expr, 1), env)); diff --git a/shared/sx/ref/adapter-dom.sx b/shared/sx/ref/adapter-dom.sx index 5c956a8..7c17907 100644 --- a/shared/sx/ref/adapter-dom.sx +++ b/shared/sx/ref/adapter-dom.sx @@ -160,7 +160,11 @@ ;; Data list :else (let ((frag (create-fragment))) - (for-each (fn (x) (dom-append frag (render-to-dom x env ns))) expr) + (for-each (fn (x) + (let ((result (render-to-dom x env ns))) + (when (not (spread? result)) + (dom-append frag result)))) + expr) frag))))) @@ -296,10 +300,14 @@ (component-params comp)) ;; If component accepts children, pre-render them to a fragment + ;; Spread values are filtered out (no parent element to merge onto) (when (component-has-children? comp) (let ((child-frag (create-fragment))) (for-each - (fn (c) (dom-append child-frag (render-to-dom c env ns))) + (fn (c) + (let ((result (render-to-dom c env ns))) + (when (not (spread? result)) + (dom-append child-frag result)))) children) (env-set! local "children" child-frag))) @@ -314,7 +322,10 @@ (fn ((args :as list) (env :as dict) (ns :as string)) (let ((frag (create-fragment))) (for-each - (fn (x) (dom-append frag (render-to-dom x env ns))) + (fn (x) + (let ((result (render-to-dom x env ns))) + (when (not (spread? result)) + (dom-append frag result)))) args) frag))) @@ -498,24 +509,32 @@ (= name "case") (render-to-dom (trampoline (eval-expr expr env)) env ns) - ;; let / let* + ;; let / let* — single body: pass through (spread propagates). Multi: fragment. (or (= name "let") (= name "let*")) - (let ((local (process-bindings (nth expr 1) env)) - (frag (create-fragment))) - (for-each - (fn (i) - (dom-append frag (render-to-dom (nth expr i) local ns))) - (range 2 (len expr))) - frag) + (let ((local (process-bindings (nth expr 1) env))) + (if (= (len expr) 3) + (render-to-dom (nth expr 2) local ns) + (let ((frag (create-fragment))) + (for-each + (fn (i) + (let ((result (render-to-dom (nth expr i) local ns))) + (when (not (spread? result)) + (dom-append frag result)))) + (range 2 (len expr))) + frag))) - ;; begin / do + ;; begin / do — single body: pass through. Multi: fragment. (or (= name "begin") (= name "do")) - (let ((frag (create-fragment))) - (for-each - (fn (i) - (dom-append frag (render-to-dom (nth expr i) env ns))) - (range 1 (len expr))) - frag) + (if (= (len expr) 2) + (render-to-dom (nth expr 1) env ns) + (let ((frag (create-fragment))) + (for-each + (fn (i) + (let ((result (render-to-dom (nth expr i) env ns))) + (when (not (spread? result)) + (dom-append frag result)))) + (range 1 (len expr))) + frag)) ;; Definition forms — eval for side effects (definition-form? name)