Hyperscript conformance: 372→373 — hide/show strategy, generator toEqual

Parser: hide/show handle `with opacity/visibility/display` strategy,
target detection for then-less chaining (add/remove/set/put as boundary).
Generator: inline run().toEqual([...]) pattern for eval-only tests.
Compiler: hide/show emit correct CSS property per strategy.

373/831 (45%)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-12 11:42:28 +00:00
parent 3dbbe7e1d1
commit 854ed9c027
24 changed files with 1665 additions and 469 deletions

View File

@@ -343,9 +343,27 @@
((tokens (list)))
(dict-set! step-ref "v" 0)
(build-code-tokens (first parsed) tokens step-ref 0)
(reset! code-tokens tokens)))))
(reset! code-tokens tokens)
(when
(client?)
(set-timeout
(fn
()
(let
((cv (dom-query "[data-code-view]")))
(when
cv
(host-set!
cv
"innerHTML"
"<span class=\"test\">FIXED</span>")
(log-info
(str
"DIRECT innerHTML kids="
(len (dom-child-nodes cv)))))))
0))))))
(let
((_eff (let ((first-run (signal true))) (effect (fn () (let ((cur (deref step-idx))) (if (deref first-run) (do (reset! first-run false) (host-call (host-global "queueMicrotask") (host-callback (fn () (rebuild-preview cur) (run-post-render-hooks))))) (schedule-idle (fn () (build-code-dom) (rebuild-preview cur) (update-code-highlight) (run-post-render-hooks))))))))))
((_eff (let ((first-run (signal true))) (effect (fn () (let ((cur (deref step-idx))) (if (deref first-run) (do (reset! first-run false) (host-call (host-global "queueMicrotask") (host-callback (fn () (update-code-highlight) (rebuild-preview cur) (run-post-render-hooks))))) (schedule-idle (fn () (build-code-dom) (rebuild-preview cur) (update-code-highlight) (run-post-render-hooks))))))))))
(div
(~tw :tokens "space-y-4 text-center")
(div
@@ -353,26 +371,28 @@
(~tw
:tokens "font-mono bg-stone-50 rounded p-2 overflow-x-auto leading-relaxed whitespace-pre-wrap")
:style "font-size:0.85rem"
(map
(fn
(tok)
(let
((step (get tok "step"))
(cur (deref step-idx))
(is-spread (get tok "spread"))
(cls
(str
(get tok "cls")
(cond
(= step -1)
""
(= step cur)
" bg-amber-100 rounded px-0.5 font-bold text-sm"
(< step cur)
" font-bold text-xs"
:else " opacity-40"))))
(span :class cls (get tok "text"))))
(deref code-tokens)))
(when
(not (client?))
(map
(fn
(tok)
(let
((step (get tok "step"))
(cur (deref step-idx))
(is-spread (get tok "spread"))
(cls
(str
(get tok "cls")
(cond
(= step -1)
""
(= step cur)
" bg-amber-100 rounded px-0.5 font-bold text-sm"
(< step cur)
" font-bold text-xs"
:else " opacity-40"))))
(span :class cls (get tok "text"))))
(deref code-tokens))))
(div
(~tw :tokens "flex items-center justify-center gap-2 md:gap-3")
(button

View File

@@ -823,3 +823,7 @@
(define
sxtp-nav-items
(list (dict :label "SXTP Protocol" :href "/sx/(applications.(sxtp))")))
(define
pretext-nav-items
(list (dict :label "Pretext" :href "/sx/(applications.(pretext))")))

View File

@@ -123,6 +123,7 @@
:href "/sx/(applications.(native-browser))"
:label "Native Browser")
(dict :href "/sx/(applications.(sxtp))" :label "SXTP Protocol")
(dict :href "/sx/(applications.(pretext))" :label "Pretext")
(dict
:href "/sx/(applications.(hyperscript))"
:label "_hyperscript"

View File

@@ -723,3 +723,7 @@
"~applications/sxtp/"
nil
"-content"))
(define
pretext
(make-page-fn "~pretext-demo/content" "~pretext-demo/" nil "-content"))

306
sx/sx/pretext-demo.sx Normal file
View File

@@ -0,0 +1,306 @@
;; Pretext demo — DOM-free text layout
;;
;; Shows Knuth-Plass optimal line breaking and text positioning,
;; computed entirely in pure SX with one IO primitive (text-measure).
;; Server renders with monospace approximation; browser uses canvas.measureText.
(defcomp
~pretext-demo/content
()
(div
(~tw :tokens "space-y-8")
(div
(~tw :tokens "border-b border-stone-200 pb-6")
(h1
(~tw :tokens "text-2xl font-bold text-stone-900")
"Pretext: DOM-free Text Layout")
(p
(~tw :tokens "mt-2 text-stone-600")
"Pure arithmetic text layout — one "
(code
(~tw :tokens "bg-stone-100 px-1 rounded text-violet-700")
"perform")
" for glyph measurement, everything else is deterministic SX functions over numbers. "
"Knuth-Plass optimal line breaking. Liang's hyphenation. No DOM reflow."))
(div
(~tw
:tokens "rounded-lg border border-blue-200 bg-blue-50 p-6 space-y-4")
(h2
(~tw :tokens "text-lg font-semibold text-blue-900")
"Architecture: one IO boundary")
(div
(~tw :tokens "grid grid-cols-1 md:grid-cols-2 gap-4")
(div
(~tw :tokens "rounded border border-blue-200 bg-white p-4")
(h3
(~tw
:tokens "text-sm font-medium text-blue-600 uppercase tracking-wide mb-2")
"IO (platform-resolved)")
(p
(~tw :tokens "text-sm text-blue-800 font-mono")
"(perform (text-measure font size text))")
(p
(~tw :tokens "text-xs text-blue-600 mt-2")
"Server: OCaml monospace approximation (otfm font tables later). "
"Browser: canvas.measureText on offscreen canvas."))
(div
(~tw :tokens "rounded border border-blue-200 bg-white p-4")
(h3
(~tw
:tokens "text-sm font-medium text-blue-600 uppercase tracking-wide mb-2")
"Pure SX (no IO)")
(ul
(~tw :tokens "text-sm text-blue-800 space-y-1")
(li "Knuth-Plass line breaking (DP over break candidates)")
(li "Liang's hyphenation (trie over character patterns)")
(li "Position calculation (running x/y sums)")
(li "Badness/demerits (cubic deviation penalty)")))))
(div
(~tw :tokens "space-y-4")
(h2
(~tw :tokens "text-lg font-semibold text-stone-800")
"Line breaking with fixed widths")
(p
(~tw :tokens "text-sm text-stone-600")
"These examples use fixed glyph widths to demonstrate the Knuth-Plass algorithm. "
"No IO — pure functions over numbers.")
(let
((widths (list 30 30 30 30 30 30 30 30))
(words
(list "The" "quick" "brown" "fox" "jumps" "over" "the" "dog"))
(space-width 5)
(max-width 75))
(let
((ranges (break-lines widths space-width max-width))
(positioned
(position-lines
words
widths
(break-lines widths space-width max-width)
space-width
24
0
0)))
(div
(~tw :tokens "rounded-lg border border-stone-200 bg-white p-6")
(h3
(~tw
:tokens "text-sm font-medium text-stone-500 uppercase tracking-wide mb-3")
"8 words × 30px, space 5px, max-width 75px")
(div
(~tw :tokens "space-y-1")
(map-indexed
(fn
(line-idx line)
(div
(~tw :tokens "flex items-baseline gap-1")
(span
(~tw :tokens "text-xs text-stone-400 w-6 shrink-0")
(str "L" (str (+ line-idx 1))))
(div
(~tw :tokens "flex gap-1")
(map
(fn
(word-info)
(span
(~tw
:tokens "inline-block bg-violet-100 text-violet-800 px-2 py-0.5 rounded text-sm font-mono")
(get word-info :word)))
line))))
positioned))
(p
(~tw :tokens "text-xs text-stone-500 mt-3")
(str
(len ranges)
" lines, "
(len words)
" words. "
"Break points: "
(join
", "
(map (fn (r) (str (first r) "→" (nth r 1))) ranges))))))))
(let
((widths (list 80 20 50 30 60 40 70 25 55 35))
(words
(list
"Typesetting"
"is"
"about"
"the"
"optimal"
"line"
"breaking"
"of"
"words"
"into"))
(space-width 6)
(max-width 120))
(let
((ranges (break-lines widths space-width max-width))
(positioned
(position-lines
words
widths
(break-lines widths space-width max-width)
space-width
24
0
0)))
(div
(~tw :tokens "rounded-lg border border-stone-200 bg-white p-6")
(h3
(~tw
:tokens "text-sm font-medium text-stone-500 uppercase tracking-wide mb-3")
"10 words, varying widths, max-width 120px")
(div
(~tw :tokens "space-y-1")
(map-indexed
(fn
(line-idx line)
(div
(~tw :tokens "flex items-baseline gap-1")
(span
(~tw :tokens "text-xs text-stone-400 w-6 shrink-0")
(str "L" (str (+ line-idx 1))))
(div
(~tw :tokens "flex gap-1")
(map
(fn
(word-info)
(let
((w (get word-info :width)))
(span
:style (str "min-width:" w "px")
(~tw
:tokens "inline-block bg-emerald-100 text-emerald-800 px-2 py-0.5 rounded text-sm font-mono")
(get word-info :word))))
line))))
positioned))
(p
(~tw :tokens "text-xs text-stone-500 mt-3")
(str
(len ranges)
" lines. Break points: "
(join
", "
(map (fn (r) (str (first r) "→" (nth r 1))) ranges)))))))
(div
(~tw :tokens "rounded-lg border border-stone-200 bg-white p-6")
(h3
(~tw
:tokens "text-sm font-medium text-stone-500 uppercase tracking-wide mb-3")
"Badness function: how lines are scored")
(p
(~tw :tokens "text-sm text-stone-600 mb-4")
"Badness grows cubically with slack. Exact fit = 0. "
"Lines over max-width get penalty 100,000.")
(div
(~tw :tokens "grid grid-cols-2 md:grid-cols-4 gap-3")
(map
(fn
(used)
(let
((bad (line-badness used 100)))
(div
(~tw :tokens "rounded border border-stone-200 p-3 text-center")
(div
(~tw :tokens "text-2xl font-mono font-bold")
(if
(>= bad 100000)
(span (~tw :tokens "text-red-600") "∞")
(span (~tw :tokens "text-stone-800") (str bad))))
(div
(~tw :tokens "text-xs text-stone-500 mt-1")
(str "used=" used "/100")))))
(list 100 90 80 70 60 50 110 120))))
(div
(~tw :tokens "rounded-lg border border-stone-200 bg-white p-6")
(h3
(~tw
:tokens "text-sm font-medium text-stone-500 uppercase tracking-wide mb-3")
"Demerits: (1 + badness)² + penalty²")
(div
(~tw :tokens "grid grid-cols-3 md:grid-cols-5 gap-3")
(map
(fn
(pair)
(let
((bad (first pair)) (pen (nth pair 1)))
(div
(~tw :tokens "rounded border border-stone-200 p-3 text-center")
(div
(~tw :tokens "text-xl font-mono font-bold text-stone-800")
(str (compute-demerits bad pen)))
(div
(~tw :tokens "text-xs text-stone-500 mt-1")
(str "b=" bad " p=" pen)))))
(list (list 0 0) (list 5 0) (list 10 0) (list 0 5) (list 10 5)))))
(div
(~tw :tokens "space-y-4")
(h2
(~tw :tokens "text-lg font-semibold text-stone-800")
"Hyphenation (Liang's algorithm)")
(p
(~tw :tokens "text-sm text-stone-600")
"Trie-based pattern matching. Digit patterns encode hyphenation levels — "
"odd levels allow breaks. Patterns like "
(code (~tw :tokens "bg-stone-100 px-1 rounded") "hy1p")
" mean: after 'y' in 'hyp...' insert a level-1 break point.")
(let
((trie (make-hyphenation-trie (list "hy1p" "he2n" "hen3at" "hena4t" "1na" "n2at" "1tio" "2io" "o2i" "1tic" "1mo" "4m1p" "1pu" "put1" "1er" "pro1g" "1gram" "2gra" "program5" "pro3"))))
(div
(~tw :tokens "rounded-lg border border-stone-200 bg-white p-6")
(h3
(~tw
:tokens "text-sm font-medium text-stone-500 uppercase tracking-wide mb-3")
"Syllable decomposition")
(div
(~tw :tokens "grid grid-cols-1 md:grid-cols-3 gap-4")
(map
(fn
(word)
(let
((syllables (hyphenate-word trie word))
(points (find-hyphenation-points trie word)))
(div
(~tw :tokens "rounded border border-stone-200 p-4")
(div
(~tw
:tokens "text-lg font-mono font-bold text-stone-800 mb-2")
(join "·" syllables))
(div
(~tw :tokens "text-xs text-stone-500")
(str
"Break points: "
(if
(empty? points)
"none"
(join ", " (map str points))))))))
(list "hyphen" "computation" "programming"))))))
(div
(~tw :tokens "rounded-lg border border-amber-200 bg-amber-50 p-6")
(h2 (~tw :tokens "text-lg font-semibold text-amber-900") "How it works")
(ol
(~tw
:tokens "list-decimal list-inside text-amber-800 space-y-2 text-sm")
(li
(code "measure-text")
" calls "
(code "(perform (text-measure ...))")
" — the only IO")
(li
(code "break-lines")
" runs Knuth-Plass DP over word widths to find optimal breaks")
(li
(code "position-lines")
" converts breaks + widths into x/y coordinates (pure arithmetic)")
(li
(code "hyphenate-word")
" uses Liang's trie algorithm to find syllable boundaries")
(li
"All layout is "
(strong "deterministic")
" — same input widths → same output positions, every time")
(li
"Server renders with monospace approximation; browser uses "
(code "canvas.measureText"))))))

File diff suppressed because it is too large Load Diff