Hyperscript examples working: toggle, bounce, count clicks

- sx_browser.ml: restore VmSuspended handler in api_call_fn with
  make_js_callFn_suspension for IO suspension chains (wait, fetch)
- runtime.sx: delete host-get stub that shadowed platform native —
  hs-toggle-class! now uses real FFI host-get for classList access

All three live demo examples work:
  Toggle Color — classList.toggle on click
  Bounce — add .animate-bounce, wait 1s suspend, remove
  Count Clicks — increment @data-count, put into innerHTML

4/4 bytecode regression tests pass (was 0/4 without VmSuspended).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-09 21:04:45 +00:00
parent c6df054957
commit de9ab4ca07
7 changed files with 253 additions and 117 deletions

View File

@@ -49,9 +49,7 @@
;; Toggle a single class on an element.
(define
hs-toggle-class!
(fn
(target cls)
(host-call (hs-host-get target "classList") "toggle" cls)))
(fn (target cls) (host-call (host-get target "classList") "toggle" cls)))
;; Toggle between two classes — exactly one is active at a time.
(define
@@ -468,67 +466,107 @@
d))))
;; ── Sandbox/test runtime additions ──────────────────────────────
;; Property access — dot notation and .length
(define
hs-host-get
(fn (obj key) (if (= key "length") (len obj) (get obj key))))
;; DOM query stub — sandbox returns empty list
(define hs-dom-query (fn (selector) (list)))
;; DOM query stub — sandbox returns empty list
(define
hs-method-call
(fn
(obj method &rest args)
(cond
((= method "map") (map (first args) obj))
((= method "push") (do (append! obj (first args)) obj))
((= method "filter") (filter (first args) obj))
((= method "join") (join obj (first args)))
((= method "indexOf")
(let
((item (first args)))
(define
idx-loop
(fn
(lst i)
(if
(= (len lst) 0)
-1
(if (= (first lst) item) i (idx-loop (rest lst) (+ i 1))))))
(idx-loop obj 0)))
(true nil))))
;; Method dispatch — obj.method(args)
(define hs-method-call (fn (obj method &rest args)
(cond
((= method "map") (map (first args) obj))
((= method "push") (do (append! obj (first args)) obj))
((= method "filter") (filter (first args) obj))
((= method "join") (join obj (first args)))
((= method "indexOf")
(let ((item (first args)))
(define idx-loop (fn (lst i)
(if (= (len lst) 0) -1
(if (= (first lst) item) i (idx-loop (rest lst) (+ i 1))))))
(idx-loop obj 0)))
(true nil))))
(define hs-beep (fn (v) v))
;; ── 0.9.90 features ─────────────────────────────────────────────
;; beep! — debug logging, returns value unchanged
(define hs-beep (fn (v) v))
(define hs-prop-is (fn (obj key) (not (hs-falsy? (host-get obj key)))))
;; Property-based is — check obj.key truthiness
(define hs-prop-is (fn (obj key) (not (hs-falsy? (hs-host-get obj key)))))
;; Array slicing (inclusive both ends)
(define hs-slice (fn (col start end)
(let ((s (if (nil? start) 0 start))
(define
hs-slice
(fn
(col start end)
(let
((s (if (nil? start) 0 start))
(e (if (nil? end) (len col) (+ end 1))))
(slice col s e))))
(slice col s e))))
;; Array slicing (inclusive both ends)
(define
hs-sorted-by
(fn
(col key-fn)
(let
((pairs (map (fn (item) (list (key-fn item) item)) col)))
(map
(fn (p) (nth p 1))
(sort (fn (a b) (if (< (first a) (first b)) true false)) pairs)))))
;; Collection: sorted by
(define hs-sorted-by (fn (col key-fn)
(let ((pairs (map (fn (item) (list (key-fn item) item)) col)))
(map (fn (p) (nth p 1))
(sort (fn (a b) (if (< (first a) (first b)) true false)) pairs)))))
(define
hs-sorted-by-desc
(fn
(col key-fn)
(let
((pairs (map (fn (item) (list (key-fn item) item)) col)))
(map
(fn (p) (nth p 1))
(sort (fn (a b) (if (> (first a) (first b)) true false)) pairs)))))
;; Collection: sorted by descending
(define hs-sorted-by-desc (fn (col key-fn)
(let ((pairs (map (fn (item) (list (key-fn item) item)) col)))
(map (fn (p) (nth p 1))
(sort (fn (a b) (if (> (first a) (first b)) true false)) pairs)))))
;; Collection: split by
(define hs-split-by (fn (s sep) (split s sep)))
;; Collection: joined by
;; Collection: split by
(define hs-joined-by (fn (col sep) (join sep col)))
;; Collection: joined by
(define
hs-sorted-by
(fn
(col key-fn)
(let
((decorated (map (fn (item) (list (key-fn item) item)) col)))
(let
((sorted-dec (sort (map first decorated))))
(define
reorder
(fn
(keys acc remaining)
(if
(= (len keys) 0)
acc
(let
((k (first keys)))
(define
find-item
(fn
(lst)
(if
(= (len lst) 0)
nil
(if
(= (first (first lst)) k)
(first lst)
(find-item (rest lst))))))
(let
((found (find-item remaining)))
(reorder
(rest keys)
(append acc (list (nth found 1)))
(filter (fn (x) (not (= x found))) remaining)))))))
(reorder sorted-dec (list) decorated)))))
;; Override sorted-by — use decorate-sort-undecorate (no comparator arg to sort)
(define hs-sorted-by (fn (col key-fn)
(let ((decorated (map (fn (item) (list (key-fn item) item)) col)))
(let ((sorted-dec (sort (map first decorated))))
(define reorder (fn (keys acc remaining)
(if (= (len keys) 0) acc
(let ((k (first keys)))
(define find-item (fn (lst)
(if (= (len lst) 0) nil
(if (= (first (first lst)) k) (first lst)
(find-item (rest lst))))))
(let ((found (find-item remaining)))
(reorder (rest keys)
(append acc (list (nth found 1)))
(filter (fn (x) (not (= x found))) remaining)))))))
(reorder sorted-dec (list) decorated)))))
(define hs-sorted-by-desc (fn (col key-fn)
(reverse (hs-sorted-by col key-fn))))
(define
hs-sorted-by-desc
(fn (col key-fn) (reverse (hs-sorted-by col key-fn))))