- ~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>
219 lines
9.0 KiB
Plaintext
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")))) |