From 8819d7cbd1d84900591c8d94990da0c18b74dcfb Mon Sep 17 00:00:00 2001 From: giles Date: Thu, 16 Apr 2026 13:44:59 +0000 Subject: [PATCH] HS fixes: multi-property transition, take attr with-val, empty form, css-value parsing - Parser: multi-property transition (width from 0px to 100px height from...) with collect-transitions loop. CSS value parsing uses parse-atom + manual number+unit concat to avoid greedy string-postfix chaining. - Compiler: take! passes attr-val and with-val (restored from revert) - Runtime: hs-empty-target! handles FORM by iterating child inputs, hs-starts-with-ic/hs-ends-with-ic for case-insensitive comparison Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/hyperscript/compiler.sx | 16 +++++++- lib/hyperscript/parser.sx | 57 +++++++++++++++++++--------- lib/hyperscript/runtime.sx | 4 ++ shared/static/wasm/sx/hs-compiler.sx | 16 +++++++- shared/static/wasm/sx/hs-parser.sx | 57 +++++++++++++++++++--------- shared/static/wasm/sx/hs-runtime.sx | 4 ++ 6 files changed, 116 insertions(+), 38 deletions(-) diff --git a/lib/hyperscript/compiler.sx b/lib/hyperscript/compiler.sx index ee9ee518..d8eadd64 100644 --- a/lib/hyperscript/compiler.sx +++ b/lib/hyperscript/compiler.sx @@ -1063,7 +1063,9 @@ ((kind (nth ast 1)) (name (nth ast 2)) (from-sel (if (> (len ast) 3) (nth ast 3) nil)) - (for-tgt (if (> (len ast) 4) (nth ast 4) nil))) + (for-tgt (if (> (len ast) 4) (nth ast 4) nil)) + (attr-val (if (> (len ast) 5) (nth ast 5) nil)) + (with-val (if (> (len ast) 6) (nth ast 6) nil))) (let ((target (if for-tgt (hs-to-sx for-tgt) (quote me))) (scope @@ -1072,7 +1074,17 @@ ((and (list? from-sel) (= (first from-sel) (quote query))) (list (quote hs-query-all) (nth from-sel 1))) (true (hs-to-sx from-sel))))) - (list (quote hs-take!) target kind name scope)))) + (if + (and (= kind "attr") (or attr-val with-val)) + (list + (quote hs-take!) + target + kind + name + scope + attr-val + (if with-val (hs-to-sx with-val) nil)) + (list (quote hs-take!) target kind name scope))))) ((= head (quote make)) (emit-make ast)) ((= head (quote install)) (cons (quote hs-install) (map hs-to-sx (rest ast)))) diff --git a/lib/hyperscript/parser.sx b/lib/hyperscript/parser.sx index c59cdd5f..d00235dc 100644 --- a/lib/hyperscript/parser.sx +++ b/lib/hyperscript/parser.sx @@ -1186,25 +1186,48 @@ () (let ((tgt (cond ((and (= (tp-type) "ident") (= (tp-val) "element")) (do (adv!) (parse-atom))) ((= (tp-type) "id") (parse-atom)) ((= (tp-type) "class") (parse-atom)) ((= (tp-type) "selector") (parse-atom)) (true nil)))) - (let - ((prop (cond ((= (tp-type) "style") (get (adv!) "value")) ((= (tp-val) "my") (do (adv!) (if (= (tp-type) "style") (get (adv!) "value") (get (adv!) "value")))) (true (get (adv!) "value"))))) - (let - ((from-val (if (match-kw "from") (parse-expr) nil))) - (expect-kw! "to") + (define + parse-one-transition + (fn + () (let - ((value (parse-expr))) + ((prop (cond ((= (tp-type) "style") (get (adv!) "value")) ((= (tp-val) "my") (do (adv!) (if (= (tp-type) "style") (get (adv!) "value") (get (adv!) "value")))) (true (get (adv!) "value"))))) (let - ((dur (if (match-kw "over") (parse-expr) nil))) - (if - from-val - (list - (quote transition-from) - prop - from-val - value - dur - tgt) - (list (quote transition) prop value dur tgt))))))))) + ((from-val (if (match-kw "from") (let ((v (parse-atom))) (if (and (number? v) (= (tp-type) "ident") (not (hs-keyword? (tp-val)))) (let ((unit (get (adv!) "value"))) (list (quote string-postfix) v unit)) v)) nil))) + (expect-kw! "to") + (let + ((value (let ((v (parse-atom))) (if (and (number? v) (= (tp-type) "ident") (not (hs-keyword? (tp-val)))) (let ((unit (get (adv!) "value"))) (list (quote string-postfix) v unit)) v)))) + (let + ((dur (if (match-kw "over") (let ((v (parse-atom))) (if (and (number? v) (= (tp-type) "ident") (not (hs-keyword? (tp-val)))) (let ((unit (get (adv!) "value"))) (list (quote string-postfix) v unit)) v)) nil))) + (let + ((using-val (if (match-kw "using") (parse-expr) nil))) + (if + from-val + (list + (quote transition-from) + prop + from-val + value + dur + tgt) + (list (quote transition) prop value dur tgt))))))))) + (let + ((first-t (parse-one-transition))) + (define + collect-transitions + (fn + (acc) + (if + (and + (not (at-end?)) + (= (tp-type) "ident") + (not (hs-keyword? (tp-val)))) + (collect-transitions + (append acc (list (parse-one-transition)))) + acc))) + (let + ((all (collect-transitions (list first-t)))) + (if (= (len all) 1) (first all) (cons (quote do) all))))))) (define parse-repeat-cmd (fn diff --git a/lib/hyperscript/runtime.sx b/lib/hyperscript/runtime.sx index ee3ab575..7687e6df 100644 --- a/lib/hyperscript/runtime.sx +++ b/lib/hyperscript/runtime.sx @@ -542,6 +542,10 @@ (dom-set-prop target "checked" false) (dom-set-prop target "value" "")))) ((= tag "FORM") (dom-set-inner-html target "")) + ((= tag "FORM") + (let + ((children (host-call target "querySelectorAll" "input, textarea, select"))) + (for-each (fn (el) (hs-empty-target! el)) children))) (true (dom-set-inner-html target "")))))))) ;; Collection: joined by (define diff --git a/shared/static/wasm/sx/hs-compiler.sx b/shared/static/wasm/sx/hs-compiler.sx index ee9ee518..d8eadd64 100644 --- a/shared/static/wasm/sx/hs-compiler.sx +++ b/shared/static/wasm/sx/hs-compiler.sx @@ -1063,7 +1063,9 @@ ((kind (nth ast 1)) (name (nth ast 2)) (from-sel (if (> (len ast) 3) (nth ast 3) nil)) - (for-tgt (if (> (len ast) 4) (nth ast 4) nil))) + (for-tgt (if (> (len ast) 4) (nth ast 4) nil)) + (attr-val (if (> (len ast) 5) (nth ast 5) nil)) + (with-val (if (> (len ast) 6) (nth ast 6) nil))) (let ((target (if for-tgt (hs-to-sx for-tgt) (quote me))) (scope @@ -1072,7 +1074,17 @@ ((and (list? from-sel) (= (first from-sel) (quote query))) (list (quote hs-query-all) (nth from-sel 1))) (true (hs-to-sx from-sel))))) - (list (quote hs-take!) target kind name scope)))) + (if + (and (= kind "attr") (or attr-val with-val)) + (list + (quote hs-take!) + target + kind + name + scope + attr-val + (if with-val (hs-to-sx with-val) nil)) + (list (quote hs-take!) target kind name scope))))) ((= head (quote make)) (emit-make ast)) ((= head (quote install)) (cons (quote hs-install) (map hs-to-sx (rest ast)))) diff --git a/shared/static/wasm/sx/hs-parser.sx b/shared/static/wasm/sx/hs-parser.sx index c59cdd5f..d00235dc 100644 --- a/shared/static/wasm/sx/hs-parser.sx +++ b/shared/static/wasm/sx/hs-parser.sx @@ -1186,25 +1186,48 @@ () (let ((tgt (cond ((and (= (tp-type) "ident") (= (tp-val) "element")) (do (adv!) (parse-atom))) ((= (tp-type) "id") (parse-atom)) ((= (tp-type) "class") (parse-atom)) ((= (tp-type) "selector") (parse-atom)) (true nil)))) - (let - ((prop (cond ((= (tp-type) "style") (get (adv!) "value")) ((= (tp-val) "my") (do (adv!) (if (= (tp-type) "style") (get (adv!) "value") (get (adv!) "value")))) (true (get (adv!) "value"))))) - (let - ((from-val (if (match-kw "from") (parse-expr) nil))) - (expect-kw! "to") + (define + parse-one-transition + (fn + () (let - ((value (parse-expr))) + ((prop (cond ((= (tp-type) "style") (get (adv!) "value")) ((= (tp-val) "my") (do (adv!) (if (= (tp-type) "style") (get (adv!) "value") (get (adv!) "value")))) (true (get (adv!) "value"))))) (let - ((dur (if (match-kw "over") (parse-expr) nil))) - (if - from-val - (list - (quote transition-from) - prop - from-val - value - dur - tgt) - (list (quote transition) prop value dur tgt))))))))) + ((from-val (if (match-kw "from") (let ((v (parse-atom))) (if (and (number? v) (= (tp-type) "ident") (not (hs-keyword? (tp-val)))) (let ((unit (get (adv!) "value"))) (list (quote string-postfix) v unit)) v)) nil))) + (expect-kw! "to") + (let + ((value (let ((v (parse-atom))) (if (and (number? v) (= (tp-type) "ident") (not (hs-keyword? (tp-val)))) (let ((unit (get (adv!) "value"))) (list (quote string-postfix) v unit)) v)))) + (let + ((dur (if (match-kw "over") (let ((v (parse-atom))) (if (and (number? v) (= (tp-type) "ident") (not (hs-keyword? (tp-val)))) (let ((unit (get (adv!) "value"))) (list (quote string-postfix) v unit)) v)) nil))) + (let + ((using-val (if (match-kw "using") (parse-expr) nil))) + (if + from-val + (list + (quote transition-from) + prop + from-val + value + dur + tgt) + (list (quote transition) prop value dur tgt))))))))) + (let + ((first-t (parse-one-transition))) + (define + collect-transitions + (fn + (acc) + (if + (and + (not (at-end?)) + (= (tp-type) "ident") + (not (hs-keyword? (tp-val)))) + (collect-transitions + (append acc (list (parse-one-transition)))) + acc))) + (let + ((all (collect-transitions (list first-t)))) + (if (= (len all) 1) (first all) (cons (quote do) all))))))) (define parse-repeat-cmd (fn diff --git a/shared/static/wasm/sx/hs-runtime.sx b/shared/static/wasm/sx/hs-runtime.sx index ee3ab575..7687e6df 100644 --- a/shared/static/wasm/sx/hs-runtime.sx +++ b/shared/static/wasm/sx/hs-runtime.sx @@ -542,6 +542,10 @@ (dom-set-prop target "checked" false) (dom-set-prop target "value" "")))) ((= tag "FORM") (dom-set-inner-html target "")) + ((= tag "FORM") + (let + ((children (host-call target "querySelectorAll" "input, textarea, select"))) + (for-each (fn (el) (hs-empty-target! el)) children))) (true (dom-set-inner-html target "")))))))) ;; Collection: joined by (define