Files
rose-ash/sx/sx/hyperscript.sx
giles 71d1ac9ce4 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>
2026-04-08 18:20:24 +00:00

219 lines
9.0 KiB
Plaintext

;; _hyperscript Playground
;; Lives under Applications: /sx/(applications.(hyperscript))
;; ── Compile result component (server-side rendered) ─────────────
(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) ─────────────────────────────
(defcomp
~hyperscript/example
(&key source description)
(div
(~tw :tokens "border border-gray-200 rounded-lg p-4 mb-4")
(when
description
(p (~tw :tokens "text-sm text-gray-600 mb-2") description))
(div
(~tw :tokens "flex gap-4 items-start mb-3")
(div
(~tw :tokens "flex-1")
(h4
(~tw
: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)))
;; ── Pipeline example component ──────────────────────────────────
(defcomp
~hyperscript/playground
()
(div
(~tw :tokens "space-y-4")
(form
:sx-post "/sx/(applications.(hyperscript.(api.compile)))"
:sx-target "#hs-playground-result"
:sx-swap "innerHTML"
(div
(label
(~tw :tokens "block text-sm font-medium text-gray-700 mb-1")
"Hyperscript source")
(textarea
:name "source"
(~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"))
(div
(~tw :tokens "mt-2")
(button
:type "submit"
(~tw
:tokens "px-4 py-2 bg-violet-600 text-white rounded-lg hover:bg-violet-700 text-sm font-medium")
"Compile")))
(div
:id "hs-playground-result"
(~hyperscript/compile-result :source "on click add .active to me"))))
;; ── Playground island ───────────────────────────────────────────
(defcomp
~hyperscript/live-demo
()
(div
(~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
(~tw :tokens "text-sm text-gray-600")
"These buttons have "
(code "_=\"...\"")
" attributes — hyperscript compiled to SX and activated at boot.")
(div
(~tw :tokens "flex gap-3 items-center")
(button
(~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"
"Toggle Color")
(button
(~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"
"Bounce")
(span
(~tw :tokens "text-sm text-gray-500")
:id "click-counter"
"0 clicks"))
(div
(~tw :tokens "mt-2")
(button
(~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"
:data-count "0"
"Count Clicks"))))
;; ── Live demo: actual hyperscript running ───────────────────────
(defcomp
~hyperscript/playground-content
()
(~docs/page
:title "_hyperscript Playground"
(p
:class "text-lg text-gray-600 mb-6"
"Compile "
(code "_hyperscript")
" to SX expressions. "
"The same "
(code "_=\"...\"")
" syntax, compiled to cached bytecode instead of re-parsed every page load.")
(~docs/section
:title "Interactive Playground"
:id "playground"
(p
"Edit the hyperscript source and click Compile to see the tokenized, parsed, and compiled SX output.")
(~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
:title "Pipeline"
:id "pipeline"
(p "Every hyperscript string passes through three stages:")
(ol
:class "list-decimal list-inside space-y-1 text-sm text-gray-700 mb-4"
(li
(strong "Tokenize")
" — source string to typed token stream (keywords, classes, ids, operators, strings)")
(li
(strong "Parse")
" — token stream to AST (commands, expressions, features)")
(li
(strong "Compile")
" — AST to SX expressions targeting "
(code "web/lib/dom.sx")
" primitives"))
(p
"The compiled SX is wrapped in "
(code "(fn (me) ...)")
" and evaluated with "
(code "me")
" bound to the element. Hyperscript variables are visible to "
(code "eval (sx-expr)")
" escapes."))
(~docs/section
:title "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
:source "on click add .active to me"
:description "Event handler: click adds a CSS class")
(~hyperscript/example
:source "on click toggle between .light and .dark on me"
:description "Toggle between two states")
(~hyperscript/example
:source "on click set my innerHTML to eval (str \"Hello from \" (+ 2 3) \" worlds\")"
:description "SX escape: evaluate SX expressions from hyperscript")
(~hyperscript/example
:source "on click put \"<b>Rendered!</b>\" into #target"
: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
:source "def double(n) return n + n end"
:description "Define reusable functions")
(~hyperscript/example
:source "on click repeat 3 times add .active to me then wait 300ms then remove .active from me then wait 300ms end"
:description "Iteration: repeat with timed animation (continuation after loop pending)")
(~hyperscript/example
: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"))))