Hyperscript examples: add Try it buttons, test stub VM continuation bug

- ~hyperscript/example component: shows "Try it" button with _= attr
  for all on-click examples, source pre wraps long lines
- Added CSS for .active/.light/.dark demo classes with !important
  to override Tailwind hover states
- Added #target div for the "put into" example
- Replaced broken examples (items, ~card, js-date-now) with
  self-contained ones that use available primitives
- Repeat example left in with note: continuation after loop pending
- New test suite io-suspension-continuation documenting the stub VM
  bug: outer do continuation lost after suspension/resume completes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-08 18:20:24 +00:00
parent 33e8788781
commit 71d1ac9ce4
2 changed files with 118 additions and 25 deletions

View File

@@ -85,3 +85,25 @@
(fn () (io "totally-unknown-op-xyz")) (fn () (io "totally-unknown-op-xyz"))
(fn (err) (set! caught true))) (fn (err) (set! caught true)))
(assert caught)))) (assert caught))))
(defsuite
"io-suspension-continuation"
(deftest
"code after non-suspending call executes"
(let
((result (list)))
(define f (fn () (set! result (append result "a"))))
(do (f) (set! result (append result "b")))
(assert= result (list "a" "b"))))
(deftest
"continuation after suspending lambda preserves outer do — BROWSER ONLY"
(let
((log (list)))
(define
non-suspending
(fn () (set! log (append log "a")) (set! log (append log "b"))))
(do (non-suspending) (set! log (append log "after-call")))
(assert= log (list "a" "b" "after-call"))
(assert
true
"passes without suspension — browser test needed for full verify: stub VM loses outer do continuation after cek_resume completes"))))

View File

@@ -2,22 +2,67 @@
;; Lives under Applications: /sx/(applications.(hyperscript)) ;; Lives under Applications: /sx/(applications.(hyperscript))
;; ── Compile result component (server-side rendered) ───────────── ;; ── Compile result component (server-side rendered) ─────────────
sx-serialize (defcomp
~hyperscript/compile-result
(&key source)
(if
(or (nil? source) (empty? source))
(p
(~tw :tokens "text-sm text-gray-400 italic")
"Enter some hyperscript and click Compile")
(let
((compiled (hs-to-sx-from-source source)))
(div
(~tw :tokens "space-y-4")
(div
(h4
(~tw
:tokens "text-xs font-semibold uppercase tracking-wider text-gray-500 mb-2")
"Compiled SX")
(pre
(~tw
:tokens "bg-gray-900 text-green-400 p-4 rounded-lg text-sm overflow-x-auto")
(sx-serialize compiled)))
(div
(h4
(~tw
:tokens "text-xs font-semibold uppercase tracking-wider text-gray-500 mb-2")
"Parse Tree")
(pre
(~tw
:tokens "bg-gray-900 text-amber-400 p-4 rounded-lg text-sm overflow-x-auto")
(sx-serialize (hs-compile source))))))))
;; ── Compile handler (POST endpoint) ───────────────────────────── ;; ── Compile handler (POST endpoint) ─────────────────────────────
(defcomp (defcomp
~hyperscript/example ~hyperscript/example
(&key source description) (&key source description)
(div (div
:class "border border-gray-200 rounded-lg p-4 space-y-3" (~tw :tokens "border border-gray-200 rounded-lg p-4 mb-4")
(when description (p :class "text-sm text-gray-600" description)) (when
description
(p (~tw :tokens "text-sm text-gray-600 mb-2") description))
(div (div
(h4 (~tw :tokens "flex gap-4 items-start mb-3")
:class "text-xs font-semibold uppercase tracking-wider text-gray-500 mb-1" (div
"Source") (~tw :tokens "flex-1")
(pre (h4
:class "bg-gray-50 text-gray-900 p-3 rounded text-sm font-mono" (~tw
(str "_=\"" source "\""))) :tokens "text-xs font-semibold uppercase tracking-wider text-gray-400 mb-1")
"Source")
(pre
(~tw
:tokens "bg-gray-50 text-gray-900 p-3 rounded text-sm font-mono whitespace-pre-wrap break-words")
(str "_=\"" source "\"")))
(when
(string-contains? source "on click")
(div
(~tw :tokens "flex-shrink-0 pt-5")
(button
(~tw
:tokens "px-4 py-2 border border-violet-300 rounded-lg text-sm font-medium text-violet-700 hover:bg-violet-50 transition-colors")
:_ source
"Try it"))))
(~hyperscript/compile-result :source source))) (~hyperscript/compile-result :source source)))
;; ── Pipeline example component ────────────────────────────────── ;; ── Pipeline example component ──────────────────────────────────
@@ -36,7 +81,8 @@ sx-serialize
"Hyperscript source") "Hyperscript source")
(textarea (textarea
:name "source" :name "source"
:class "w-full h-24 font-mono text-sm p-3 border border-gray-300 rounded-lg bg-white focus:ring-2 focus:ring-violet-500 focus:border-violet-500" (~tw
:tokens "w-full h-24 font-mono text-sm p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-violet-500 focus:border-transparent")
"on click add .active to me")) "on click add .active to me"))
(div (div
(~tw :tokens "mt-2") (~tw :tokens "mt-2")
@@ -54,25 +100,35 @@ sx-serialize
~hyperscript/live-demo ~hyperscript/live-demo
() ()
(div (div
:class "space-y-4" (~tw :tokens "space-y-4")
(style
"\n .bg-violet-600 { background-color: rgb(124 58 237); }\n .text-white { color: white; }\n .animate-bounce { animation: bounce 1s infinite; }\n @keyframes bounce {\n 0%, 100% { transform: translateY(-25%); animation-timing-function: cubic-bezier(0.8,0,1,1); }\n 50% { transform: none; animation-timing-function: cubic-bezier(0,0,0.2,1); }\n }\n ")
(p (p
:class "text-sm text-gray-600" (~tw :tokens "text-sm text-gray-600")
"These buttons have actual hyperscript compiled to SX. Click them to see the behavior.") "These buttons have "
(code "_=\"...\"")
" attributes — hyperscript compiled to SX and activated at boot.")
(div (div
:class "flex gap-3 items-center" (~tw :tokens "flex gap-3 items-center")
(button (button
:class "px-4 py-2 border border-gray-300 rounded-lg text-sm transition-colors" (~tw
:tokens "px-4 py-2 border border-gray-300 rounded-lg text-sm transition-colors")
:_ "on click toggle .bg-violet-600 on me then toggle .text-white on me" :_ "on click toggle .bg-violet-600 on me then toggle .text-white on me"
"Toggle Color") "Toggle Color")
(button (button
:class "px-4 py-2 border border-gray-300 rounded-lg text-sm" (~tw
:tokens "px-4 py-2 border border-gray-300 rounded-lg text-sm transition-colors")
:_ "on click add .animate-bounce to me then wait 1s then remove .animate-bounce from me" :_ "on click add .animate-bounce to me then wait 1s then remove .animate-bounce from me"
"Bounce") "Bounce")
(span :class "text-sm text-gray-500" :id "click-counter" "0 clicks")) (span
(~tw :tokens "text-sm text-gray-500")
:id "click-counter"
"0 clicks"))
(div (div
:class "mt-2" (~tw :tokens "mt-2")
(button (button
:class "px-4 py-2 border border-gray-300 rounded-lg text-sm" (~tw
:tokens "px-4 py-2 border border-gray-300 rounded-lg text-sm transition-colors")
:_ "on click increment @data-count on me then set #click-counter's innerHTML to my @data-count" :_ "on click increment @data-count on me then set #click-counter's innerHTML to my @data-count"
:data-count "0" :data-count "0"
"Count Clicks")))) "Count Clicks"))))
@@ -97,6 +153,14 @@ sx-serialize
(p (p
"Edit the hyperscript source and click Compile to see the tokenized, parsed, and compiled SX output.") "Edit the hyperscript source and click Compile to see the tokenized, parsed, and compiled SX output.")
(~hyperscript/playground)) (~hyperscript/playground))
(~docs/section
:title "Live Demo"
:id "live-demo"
(p
"These buttons have "
(code "_=\"...\"")
" attributes — hyperscript compiled to SX and activated at boot.")
(~hyperscript/live-demo))
(~docs/section (~docs/section
:title "Pipeline" :title "Pipeline"
:id "pipeline" :id "pipeline"
@@ -125,6 +189,8 @@ sx-serialize
(~docs/section (~docs/section
:title "Examples" :title "Examples"
:id "examples" :id "examples"
(style
"\n button.active { background-color: #7c3aed !important; color: white !important; border-color: #7c3aed !important; }\n button.active:hover { background-color: #6d28d9 !important; }\n button.light { background-color: #fef3c7 !important; color: #92400e !important; border-color: #f59e0b !important; }\n button.light:hover { background-color: #fde68a !important; }\n button.dark { background-color: #1e293b !important; color: #e2e8f0 !important; border-color: #475569 !important; }\n button.dark:hover { background-color: #334155 !important; }\n .animate-bounce { animation: bounce 1s; }\n @keyframes bounce {\n 0%, 100% { transform: translateY(0); }\n 50% { transform: translateY(-25%); }\n }")
(~hyperscript/example (~hyperscript/example
:source "on click add .active to me" :source "on click add .active to me"
:description "Event handler: click adds a CSS class") :description "Event handler: click adds a CSS class")
@@ -132,17 +198,22 @@ sx-serialize
:source "on click toggle between .light and .dark on me" :source "on click toggle between .light and .dark on me"
:description "Toggle between two states") :description "Toggle between two states")
(~hyperscript/example (~hyperscript/example
:source "on click set my innerHTML to eval (str \"Clicked at \" (js-date-now))" :source "on click set my innerHTML to eval (str \"Hello from \" (+ 2 3) \" worlds\")"
:description "SX escape: call SX functions from hyperscript, variables flow through") :description "SX escape: evaluate SX expressions from hyperscript")
(~hyperscript/example (~hyperscript/example
:source "on click render ~card :title 'Hello' into #target" :source "on click put \"<b>Rendered!</b>\" into #target"
:description "Render an SX component directly from a hyperscript handler") :description "Target another element by CSS selector")
(div
:id "target"
(~tw
:tokens "border border-dashed border-gray-300 rounded-lg p-3 min-h-[2rem] text-sm text-gray-400")
"← render target")
(~hyperscript/example (~hyperscript/example
:source "def double(n) return n + n end" :source "def double(n) return n + n end"
:description "Define reusable functions") :description "Define reusable functions")
(~hyperscript/example (~hyperscript/example
:source "on click for item in items log item end" :source "on click repeat 3 times add .active to me then wait 300ms then remove .active from me then wait 300ms end"
:description "Iteration over collections") :description "Iteration: repeat with timed animation (continuation after loop pending)")
(~hyperscript/example (~hyperscript/example
:source "behavior Draggable on mousedown add .dragging to me end on mouseup remove .dragging from me end end" :source "behavior Draggable on mousedown add .dragging to me end on mouseup remove .dragging from me end end"
:description "Reusable behaviors — install on any element")))) :description "Reusable behaviors — install on any element"))))