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:
@@ -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
|
||||
|
||||
@@ -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))")))
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
306
sx/sx/pretext-demo.sx
Normal 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
Reference in New Issue
Block a user