diff --git a/lib/hyperscript/compiler.sx b/lib/hyperscript/compiler.sx index 6dc0430d..1e22f874 100644 --- a/lib/hyperscript/compiler.sx +++ b/lib/hyperscript/compiler.sx @@ -1757,6 +1757,39 @@ (list (quote hs-settle) (quote me))) ((= head (quote go)) (list (quote hs-navigate!) (hs-to-sx (nth ast 1)))) + ((= head (quote ask)) + (let + ((val (list (quote hs-ask) (hs-to-sx (nth ast 1))))) + (list + (quote let) + (list (list (quote __hs-a) val)) + (list + (quote begin) + (list (quote set!) (quote the-result) (quote __hs-a)) + (list (quote set!) (quote it) (quote __hs-a)) + (quote __hs-a))))) + ((= head (quote answer)) + (let + ((val (list (quote hs-answer) (hs-to-sx (nth ast 1)) (hs-to-sx (nth ast 2)) (hs-to-sx (nth ast 3))))) + (list + (quote let) + (list (list (quote __hs-a) val)) + (list + (quote begin) + (list (quote set!) (quote the-result) (quote __hs-a)) + (list (quote set!) (quote it) (quote __hs-a)) + (quote __hs-a))))) + ((= head (quote answer-alert)) + (let + ((val (list (quote hs-answer-alert) (hs-to-sx (nth ast 1))))) + (list + (quote let) + (list (list (quote __hs-a) val)) + (list + (quote begin) + (list (quote set!) (quote the-result) (quote __hs-a)) + (list (quote set!) (quote it) (quote __hs-a)) + (quote __hs-a))))) ((= head (quote __get-cmd)) (let ((val (hs-to-sx (nth ast 1)))) diff --git a/lib/hyperscript/parser.sx b/lib/hyperscript/parser.sx index e3f3b761..d52dca2d 100644 --- a/lib/hyperscript/parser.sx +++ b/lib/hyperscript/parser.sx @@ -1717,6 +1717,23 @@ (ca-collect (append acc (list arg))))))) (ca-collect (list)))) (define parse-call-cmd (fn () (parse-expr))) + (define parse-ask-cmd (fn () (list (quote ask) (parse-expr)))) + (define + parse-answer-cmd + (fn + () + (let + ((msg (parse-atom))) + (if + (match-kw "with") + (let + ((yes (parse-atom))) + (begin + (match-kw "or") + (let + ((no-val (parse-atom))) + (list (quote answer) msg yes no-val)))) + (list (quote answer-alert) msg))))) (define parse-get-cmd (fn () (list (quote __get-cmd) (parse-expr)))) (define parse-take-cmd @@ -2399,6 +2416,10 @@ (do (adv!) (parse-take-cmd))) ((and (= typ "keyword") (= val "pick")) (do (adv!) (parse-pick-cmd))) + ((and (= typ "keyword") (= val "ask")) + (do (adv!) (parse-ask-cmd))) + ((and (= typ "keyword") (= val "answer")) + (do (adv!) (parse-answer-cmd))) ((and (= typ "keyword") (= val "settle")) (do (adv!) (list (quote settle)))) ((and (= typ "keyword") (= val "go")) @@ -2504,7 +2525,9 @@ (= v "morph") (= v "open") (= v "close") - (= v "pick")))) + (= v "pick") + (= v "ask") + (= v "answer")))) (define cl-collect (fn diff --git a/lib/hyperscript/runtime.sx b/lib/hyperscript/runtime.sx index c419cf05..41b98706 100644 --- a/lib/hyperscript/runtime.sx +++ b/lib/hyperscript/runtime.sx @@ -468,6 +468,35 @@ ;; `window.getSelection().toString()`. In the mock test runner, a test ;; setup stashes the desired selection text at `window.__test_selection` ;; and the fallback path returns that so tests can assert on the result. +(define + hs-ask + (fn + (msg) + (let + ((w (host-global "window"))) + (if w (host-call w "prompt" msg) nil)))) + + +;; ── Transition ────────────────────────────────────────────────── + +;; Transition a CSS property to a value, optionally with duration. +;; (hs-transition target prop value duration) +(define + hs-answer + (fn + (msg yes-val no-val) + (let + ((w (host-global "window"))) + (if w (if (host-call w "confirm" msg) yes-val no-val) no-val)))) + +(define + hs-answer-alert + (fn + (msg) + (let + ((w (host-global "window"))) + (if w (begin (host-call w "alert" msg) nil) nil)))) + (define hs-scroll! (fn @@ -480,11 +509,6 @@ ((= position "bottom") (dict :block "end")) (true (dict :block "start"))))))) - -;; ── Transition ────────────────────────────────────────────────── - -;; Transition a CSS property to a value, optionally with duration. -;; (hs-transition target prop value duration) (define hs-halt! (fn @@ -619,6 +643,10 @@ (hs-query-all sel) (host-call target "querySelectorAll" sel)))) + + + + (define hs-list-set (fn @@ -628,15 +656,12 @@ (define hs-to-number (fn (v) (if (number? v) v (or (parse-number (str v)) 0)))) - +;; ── Sandbox/test runtime additions ────────────────────────────── +;; Property access — dot notation and .length (define hs-query-first (fn (sel) (host-call (host-global "document") "querySelector" sel))) - - - - - +;; DOM query stub — sandbox returns empty list (define hs-query-last (fn @@ -644,10 +669,11 @@ (let ((all (dom-query-all (dom-body) sel))) (if (> (len all) 0) (nth all (- (len all) 1)) nil)))) - +;; Method dispatch — obj.method(args) (define hs-first (fn (scope sel) (dom-query-all scope sel))) -;; ── Sandbox/test runtime additions ────────────────────────────── -;; Property access — dot notation and .length + +;; ── 0.9.90 features ───────────────────────────────────────────── +;; beep! — debug logging, returns value unchanged (define hs-last (fn @@ -655,7 +681,7 @@ (let ((all (dom-query-all scope sel))) (if (> (len all) 0) (nth all (- (len all) 1)) nil)))) -;; DOM query stub — sandbox returns empty list +;; Property-based is — check obj.key truthiness (define hs-repeat-times (fn @@ -673,7 +699,7 @@ ((= signal "hs-continue") (do-repeat (+ i 1))) (true (do-repeat (+ i 1)))))))) (do-repeat 0))) -;; Method dispatch — obj.method(args) +;; Array slicing (inclusive both ends) (define hs-repeat-forever (fn @@ -689,9 +715,7 @@ ((= signal "hs-continue") (do-forever)) (true (do-forever)))))) (do-forever))) - -;; ── 0.9.90 features ───────────────────────────────────────────── -;; beep! — debug logging, returns value unchanged +;; Collection: sorted by (define hs-repeat-while (fn @@ -704,7 +728,7 @@ ((= signal "hs-break") nil) ((= signal "hs-continue") (hs-repeat-while cond-fn thunk)) (true (hs-repeat-while cond-fn thunk))))))) -;; Property-based is — check obj.key truthiness +;; Collection: sorted by descending (define hs-repeat-until (fn @@ -716,7 +740,7 @@ ((= signal "hs-continue") (if (cond-fn) nil (hs-repeat-until cond-fn thunk))) (true (if (cond-fn) nil (hs-repeat-until cond-fn thunk))))))) -;; Array slicing (inclusive both ends) +;; Collection: split by (define hs-for-each (fn @@ -736,7 +760,7 @@ ((= signal "hs-continue") (do-loop (rest remaining))) (true (do-loop (rest remaining)))))))) (do-loop items)))) -;; Collection: sorted by +;; Collection: joined by (begin (define hs-append @@ -764,7 +788,7 @@ ((hs-element? target) (dom-insert-adjacent-html target "beforeend" (str value))) (true nil))))) -;; Collection: sorted by descending + (define hs-sender (fn @@ -772,7 +796,7 @@ (let ((detail (host-get event "detail"))) (if detail (host-get detail "sender") nil)))) -;; Collection: split by + (define hs-host-to-sx (fn @@ -826,7 +850,7 @@ (dict-set! out k (hs-host-to-sx (host-get v k)))) (host-call (host-global "Object") "keys" v)) out))))))))))) -;; Collection: joined by + (define hs-fetch (fn diff --git a/lib/hyperscript/tokenizer.sx b/lib/hyperscript/tokenizer.sx index a40d9e60..2483ea8c 100644 --- a/lib/hyperscript/tokenizer.sx +++ b/lib/hyperscript/tokenizer.sx @@ -185,7 +185,9 @@ "dom" "morph" "using" - "giving")) + "giving" + "ask" + "answer")) (define hs-keyword? (fn (word) (some (fn (k) (= k word)) hs-keywords))) diff --git a/plans/hs-conformance-scoreboard.md b/plans/hs-conformance-scoreboard.md index 51e53a6b..606ee35a 100644 --- a/plans/hs-conformance-scoreboard.md +++ b/plans/hs-conformance-scoreboard.md @@ -4,10 +4,10 @@ Live tally for `plans/hs-conformance-to-100.md`. Update after every cluster comm ``` Baseline: 1213/1496 (81.1%) -Merged: 1260/1496 (84.2%) delta +47 +Merged: 1264/1496 (84.5%) delta +51 Worktree: all landed Target: 1496/1496 (100.0%) -Remaining: ~236 tests +Remaining: ~232 tests ``` ## Cluster ledger @@ -53,7 +53,7 @@ Remaining: ~236 tests |---|---------|--------|---|--------| | 26 | resize observer mock + `on resize` | done | +3 | 304a52d2 | | 27 | intersection observer mock + `on intersection` | done | +3 | 0c31dd27 | -| 28 | `ask`/`answer` + prompt/confirm mock | pending | (+4 est) | — | +| 28 | `ask`/`answer` + prompt/confirm mock | done | +4 | SHA-PENDING | | 29 | `hyperscript:before:init` / `:after:init` / `:parse-error` | pending | (+4–6 est) | — | | 30 | `logAll` config | done | +1 | 64bcefff | @@ -87,7 +87,7 @@ Defer until A–D drain. Estimated ~25 recoverable tests. |--------|-----:|--------:|--------:|--------:|--------:|------------:|------:| | A | 12 | 4 | 0 | 0 | 1 | — | 17 | | B | 5 | 0 | 0 | 1 | 1 | — | 7 | -| C | 3 | 0 | 0 | 2 | 0 | — | 5 | +| C | 4 | 0 | 0 | 1 | 0 | — | 5 | | D | 0 | 0 | 0 | 5 | 0 | — | 5 | | E | 0 | 0 | 0 | 0 | 0 | 5 | 5 | | F | — | — | — | ~10 | — | — | ~10 | diff --git a/plans/hs-conformance-to-100.md b/plans/hs-conformance-to-100.md index e154234b..79cbc6b1 100644 --- a/plans/hs-conformance-to-100.md +++ b/plans/hs-conformance-to-100.md @@ -107,7 +107,7 @@ Orchestrator cherry-picks worktree commits onto `architecture` one at a time; re 27. **[done (+3)] intersection observer mock + `on intersection`** — 3 tests. Mock `IntersectionObserver`; compile `on intersection` with margin/threshold modifiers. Expected: +3. -28. **[pending] `ask`/`answer` + prompt/confirm mock** — `askAnswer` 4 tests. **Requires test-name-keyed mock**: first test wants `confirm → true`, second `confirm → false`, third `prompt → "Alice"`, fourth `prompt → null`. Keyed via `_current-test-name` in the runner. Expected: +4. +28. **[done (+4)] `ask`/`answer` + prompt/confirm mock** — `askAnswer` 4 tests. **Requires test-name-keyed mock**: first test wants `confirm → true`, second `confirm → false`, third `prompt → "Alice"`, fourth `prompt → null`. Keyed via `_current-test-name` in the runner. Expected: +4. 29. **[pending] `hyperscript:before:init` / `:after:init` / `:parse-error` events** — 6 tests in `bootstrap` + `parser`. Fire DOM events at activation boundaries. Expected: +4-6. @@ -177,6 +177,9 @@ Many tests are `SKIP (untranslated)` because `tests/playwright/generate-sx-tests (Reverse chronological — newest at top.) +### 2026-04-24 — cluster 28 ask/answer + prompt/confirm mock +- **SHA-PENDING** — `HS: ask/answer + prompt/confirm mock (+4 tests)`. Five-part change: (a) `tokenizer.sx` registers `ask` and `answer` as hs-keywords. (b) `parser.sx` — `cmd-kw?` gains both, `parse-cmd` gains cond branches dispatching to new `parse-ask-cmd` (emits `(ask MSG)`) and `parse-answer-cmd` which reads `answer MSG [with YES or NO]` with `parse-atom` for the choices (using `parse-expr` there collapsed `"Yes" or "No"` into `(or "Yes" "No")` before `match-kw "or"` could fire — parse-atom skips the logical layer). No-`with` form emits `(answer-alert MSG)`. (c) `compiler.sx` — three new cond branches (`ask`, `answer`, `answer-alert`) compile to `(let ((__hs-a (hs-XXX ...))) (begin (set! the-result __hs-a) (set! it __hs-a) __hs-a))` so `then put it into …` works. (d) `runtime.sx` — `hs-ask`, `hs-answer`, `hs-answer-alert` call `window.prompt`/`confirm`/`alert` via `host-call (host-global "window") …`. (e) `tests/hs-run-filtered.js` — test-name-keyed stubs for `globalThis.{alert,confirm,prompt}`, `__currentHsTestName` updated before each test. One extra tweak: `host-set!` innerHTML/textContent now coerces JS `null` → the string `"null"` (matching browser behaviour) so `prompt` returning null → `put it into #out` renders literal `"null"` text — the fourth test depends on exactly this. Suite hs-upstream-askAnswer: 1/5 → 5/5. Smoke 0-195: 166/195 → 170/195. + ### 2026-04-24 — cluster 25 parenthesized commands and features - **d7a88d85** — `HS: parenthesized commands and features (+1 test)`. Parser-only fix in `lib/hyperscript/parser.sx`. Three additions: (a) `parse-feat` gets a new cond branch — on `paren-open`, advance, recurse `parse-feat`, consume `paren-close`; lets features like `(on click ...)` be grouped. (b) `parse-cmd` gets two new cond branches — on `paren-close` return nil (so `cl-collect` terminates when the outer paren group ends), and on `paren-open` advance+recurse+close (parenthesized single commands like `(log me)`). (c) The key missing piece: `cl-collect` previously only recursed when the next token was a recognised command keyword (`cmd-kw?`), so after the first `(log me)` the next `(trigger foo)` would end the body. Extended the recursion predicate to also fire when the next token is `paren-open`. Result: `on click (log me) (trigger foo)` now emits both commands inside the handler body, not the second as a sibling top-level feature. Suite hs-upstream-core/parser: 9/14 → 10/14. Smoke 0-195: 165/195 → 166/195. diff --git a/shared/static/wasm/sx/hs-compiler.sx b/shared/static/wasm/sx/hs-compiler.sx index 6dc0430d..1e22f874 100644 --- a/shared/static/wasm/sx/hs-compiler.sx +++ b/shared/static/wasm/sx/hs-compiler.sx @@ -1757,6 +1757,39 @@ (list (quote hs-settle) (quote me))) ((= head (quote go)) (list (quote hs-navigate!) (hs-to-sx (nth ast 1)))) + ((= head (quote ask)) + (let + ((val (list (quote hs-ask) (hs-to-sx (nth ast 1))))) + (list + (quote let) + (list (list (quote __hs-a) val)) + (list + (quote begin) + (list (quote set!) (quote the-result) (quote __hs-a)) + (list (quote set!) (quote it) (quote __hs-a)) + (quote __hs-a))))) + ((= head (quote answer)) + (let + ((val (list (quote hs-answer) (hs-to-sx (nth ast 1)) (hs-to-sx (nth ast 2)) (hs-to-sx (nth ast 3))))) + (list + (quote let) + (list (list (quote __hs-a) val)) + (list + (quote begin) + (list (quote set!) (quote the-result) (quote __hs-a)) + (list (quote set!) (quote it) (quote __hs-a)) + (quote __hs-a))))) + ((= head (quote answer-alert)) + (let + ((val (list (quote hs-answer-alert) (hs-to-sx (nth ast 1))))) + (list + (quote let) + (list (list (quote __hs-a) val)) + (list + (quote begin) + (list (quote set!) (quote the-result) (quote __hs-a)) + (list (quote set!) (quote it) (quote __hs-a)) + (quote __hs-a))))) ((= head (quote __get-cmd)) (let ((val (hs-to-sx (nth ast 1)))) diff --git a/shared/static/wasm/sx/hs-parser.sx b/shared/static/wasm/sx/hs-parser.sx index e3f3b761..d52dca2d 100644 --- a/shared/static/wasm/sx/hs-parser.sx +++ b/shared/static/wasm/sx/hs-parser.sx @@ -1717,6 +1717,23 @@ (ca-collect (append acc (list arg))))))) (ca-collect (list)))) (define parse-call-cmd (fn () (parse-expr))) + (define parse-ask-cmd (fn () (list (quote ask) (parse-expr)))) + (define + parse-answer-cmd + (fn + () + (let + ((msg (parse-atom))) + (if + (match-kw "with") + (let + ((yes (parse-atom))) + (begin + (match-kw "or") + (let + ((no-val (parse-atom))) + (list (quote answer) msg yes no-val)))) + (list (quote answer-alert) msg))))) (define parse-get-cmd (fn () (list (quote __get-cmd) (parse-expr)))) (define parse-take-cmd @@ -2399,6 +2416,10 @@ (do (adv!) (parse-take-cmd))) ((and (= typ "keyword") (= val "pick")) (do (adv!) (parse-pick-cmd))) + ((and (= typ "keyword") (= val "ask")) + (do (adv!) (parse-ask-cmd))) + ((and (= typ "keyword") (= val "answer")) + (do (adv!) (parse-answer-cmd))) ((and (= typ "keyword") (= val "settle")) (do (adv!) (list (quote settle)))) ((and (= typ "keyword") (= val "go")) @@ -2504,7 +2525,9 @@ (= v "morph") (= v "open") (= v "close") - (= v "pick")))) + (= v "pick") + (= v "ask") + (= v "answer")))) (define cl-collect (fn diff --git a/shared/static/wasm/sx/hs-runtime.sx b/shared/static/wasm/sx/hs-runtime.sx index c419cf05..41b98706 100644 --- a/shared/static/wasm/sx/hs-runtime.sx +++ b/shared/static/wasm/sx/hs-runtime.sx @@ -468,6 +468,35 @@ ;; `window.getSelection().toString()`. In the mock test runner, a test ;; setup stashes the desired selection text at `window.__test_selection` ;; and the fallback path returns that so tests can assert on the result. +(define + hs-ask + (fn + (msg) + (let + ((w (host-global "window"))) + (if w (host-call w "prompt" msg) nil)))) + + +;; ── Transition ────────────────────────────────────────────────── + +;; Transition a CSS property to a value, optionally with duration. +;; (hs-transition target prop value duration) +(define + hs-answer + (fn + (msg yes-val no-val) + (let + ((w (host-global "window"))) + (if w (if (host-call w "confirm" msg) yes-val no-val) no-val)))) + +(define + hs-answer-alert + (fn + (msg) + (let + ((w (host-global "window"))) + (if w (begin (host-call w "alert" msg) nil) nil)))) + (define hs-scroll! (fn @@ -480,11 +509,6 @@ ((= position "bottom") (dict :block "end")) (true (dict :block "start"))))))) - -;; ── Transition ────────────────────────────────────────────────── - -;; Transition a CSS property to a value, optionally with duration. -;; (hs-transition target prop value duration) (define hs-halt! (fn @@ -619,6 +643,10 @@ (hs-query-all sel) (host-call target "querySelectorAll" sel)))) + + + + (define hs-list-set (fn @@ -628,15 +656,12 @@ (define hs-to-number (fn (v) (if (number? v) v (or (parse-number (str v)) 0)))) - +;; ── Sandbox/test runtime additions ────────────────────────────── +;; Property access — dot notation and .length (define hs-query-first (fn (sel) (host-call (host-global "document") "querySelector" sel))) - - - - - +;; DOM query stub — sandbox returns empty list (define hs-query-last (fn @@ -644,10 +669,11 @@ (let ((all (dom-query-all (dom-body) sel))) (if (> (len all) 0) (nth all (- (len all) 1)) nil)))) - +;; Method dispatch — obj.method(args) (define hs-first (fn (scope sel) (dom-query-all scope sel))) -;; ── Sandbox/test runtime additions ────────────────────────────── -;; Property access — dot notation and .length + +;; ── 0.9.90 features ───────────────────────────────────────────── +;; beep! — debug logging, returns value unchanged (define hs-last (fn @@ -655,7 +681,7 @@ (let ((all (dom-query-all scope sel))) (if (> (len all) 0) (nth all (- (len all) 1)) nil)))) -;; DOM query stub — sandbox returns empty list +;; Property-based is — check obj.key truthiness (define hs-repeat-times (fn @@ -673,7 +699,7 @@ ((= signal "hs-continue") (do-repeat (+ i 1))) (true (do-repeat (+ i 1)))))))) (do-repeat 0))) -;; Method dispatch — obj.method(args) +;; Array slicing (inclusive both ends) (define hs-repeat-forever (fn @@ -689,9 +715,7 @@ ((= signal "hs-continue") (do-forever)) (true (do-forever)))))) (do-forever))) - -;; ── 0.9.90 features ───────────────────────────────────────────── -;; beep! — debug logging, returns value unchanged +;; Collection: sorted by (define hs-repeat-while (fn @@ -704,7 +728,7 @@ ((= signal "hs-break") nil) ((= signal "hs-continue") (hs-repeat-while cond-fn thunk)) (true (hs-repeat-while cond-fn thunk))))))) -;; Property-based is — check obj.key truthiness +;; Collection: sorted by descending (define hs-repeat-until (fn @@ -716,7 +740,7 @@ ((= signal "hs-continue") (if (cond-fn) nil (hs-repeat-until cond-fn thunk))) (true (if (cond-fn) nil (hs-repeat-until cond-fn thunk))))))) -;; Array slicing (inclusive both ends) +;; Collection: split by (define hs-for-each (fn @@ -736,7 +760,7 @@ ((= signal "hs-continue") (do-loop (rest remaining))) (true (do-loop (rest remaining)))))))) (do-loop items)))) -;; Collection: sorted by +;; Collection: joined by (begin (define hs-append @@ -764,7 +788,7 @@ ((hs-element? target) (dom-insert-adjacent-html target "beforeend" (str value))) (true nil))))) -;; Collection: sorted by descending + (define hs-sender (fn @@ -772,7 +796,7 @@ (let ((detail (host-get event "detail"))) (if detail (host-get detail "sender") nil)))) -;; Collection: split by + (define hs-host-to-sx (fn @@ -826,7 +850,7 @@ (dict-set! out k (hs-host-to-sx (host-get v k)))) (host-call (host-global "Object") "keys" v)) out))))))))))) -;; Collection: joined by + (define hs-fetch (fn diff --git a/shared/static/wasm/sx/hs-tokenizer.sx b/shared/static/wasm/sx/hs-tokenizer.sx index a40d9e60..2483ea8c 100644 --- a/shared/static/wasm/sx/hs-tokenizer.sx +++ b/shared/static/wasm/sx/hs-tokenizer.sx @@ -185,7 +185,9 @@ "dom" "morph" "using" - "giving")) + "giving" + "ask" + "answer")) (define hs-keyword? (fn (word) (some (fn (k) (= k word)) hs-keywords))) diff --git a/tests/hs-run-filtered.js b/tests/hs-run-filtered.js index de8bff65..845f535e 100755 --- a/tests/hs-run-filtered.js +++ b/tests/hs-run-filtered.js @@ -327,6 +327,22 @@ const document = { createEvent(t){return new Ev(t);}, addEventListener(){}, removeEventListener(){}, }; globalThis.document=document; globalThis.window=globalThis; globalThis.HTMLElement=El; globalThis.Element=El; +// cluster-28: test-name-keyed confirm/prompt/alert mocks. The upstream +// ask/answer tests each expect a deterministic return value. Keyed on +// globalThis.__currentHsTestName which the test loop sets before each test. +globalThis.alert = function(){}; +globalThis.confirm = function(_msg){ + const n = globalThis.__currentHsTestName || ''; + if (n === 'confirm returns first choice on OK') return true; + if (n === 'confirm returns second choice on cancel') return false; + return true; +}; +globalThis.prompt = function(_msg){ + const n = globalThis.__currentHsTestName || ''; + if (n === 'prompts and puts result in it') return 'Alice'; + if (n === 'returns null on cancel') return null; + return ''; +}; globalThis.Event=Ev; globalThis.CustomEvent=Ev; globalThis.NodeList=Array; globalThis.HTMLCollection=Array; globalThis.getComputedStyle=(e)=>e?e.style:{}; globalThis.requestAnimationFrame=(f)=>{f();return 0;}; globalThis.cancelAnimationFrame=()=>{}; globalThis.MutationObserver=class{observe(){}disconnect(){}}; @@ -397,7 +413,7 @@ K.registerNative('host-get',a=>{ if((a[1]==='innerHTML'||a[1]==='textContent'||a[1]==='value'||a[1]==='className')&&typeof v!=='string')v=String(v!=null?v:''); return v; }); -K.registerNative('host-set!',a=>{if(a[0]!=null){const v=a[2]; if(a[1]==='innerHTML'&&a[0] instanceof El){const s=String(v!=null?v:'');a[0]._setInnerHTML(s);a[0][a[1]]=a[0].innerHTML;} else if(a[1]==='textContent'&&a[0] instanceof El){const s=String(v!=null?v:'');a[0].textContent=s;a[0].innerHTML=s;for(const c of a[0].children){c.parentElement=null;c.parentNode=null;}a[0].children=[];a[0].childNodes=[];} else{a[0][a[1]]=v;}} return a[2];}); +K.registerNative('host-set!',a=>{if(a[0]!=null){const v=a[2]; if(a[1]==='innerHTML'&&a[0] instanceof El){const s=v===null?'null':v===undefined?'':String(v);a[0]._setInnerHTML(s);a[0][a[1]]=a[0].innerHTML;} else if(a[1]==='textContent'&&a[0] instanceof El){const s=v===null?'null':v===undefined?'':String(v);a[0].textContent=s;a[0].innerHTML=s;for(const c of a[0].children){c.parentElement=null;c.parentNode=null;}a[0].children=[];a[0].childNodes=[];} else{a[0][a[1]]=v;}} return a[2];}); K.registerNative('host-call',a=>{if(_testDeadline&&Date.now()>_testDeadline)throw new Error('TIMEOUT: wall clock exceeded');const[o,m,...r]=a;if(o==null){const f=globalThis[m];return typeof f==='function'?f.apply(null,r):null;}if(o&&typeof o[m]==='function'){try{const v=o[m].apply(o,r);return v===undefined?null:v;}catch(e){return null;}}return null;}); K.registerNative('host-new',a=>{const C=typeof a[0]==='string'?globalThis[a[0]]:a[0];return typeof C==='function'?new C(...a.slice(1)):null;}); K.registerNative('host-callback',a=>{const fn=a[0];if(typeof fn==='function'&&fn.__sx_handle===undefined)return fn;if(fn&&fn.__sx_handle!==undefined)return function(){const r=K.callFn(fn,Array.from(arguments));if(globalThis._driveAsync)globalThis._driveAsync(r);return r;};return function(){};}); @@ -524,6 +540,7 @@ for(let i=startTest;i