Root cause: sx_insert_near placed break-lines-greedy, pretext-position-line, pretext-layout-lines OUTSIDE the define-library begin block. The bytecode compiler only compiles forms inside begin as STORE_GLOBAL — forms outside are invisible to the browser VM. Fix: moved all function definitions inside (begin ...) of (define-library). Bytecode now includes all 17 functions (11K compiled, was 9K). Browser load-sxbc: simplified VmSuspended handling — just catch and continue, since STORE_GLOBAL ops already ran before the import OP_PERFORM. sync_vm_to_env copies them to global_env. Island now calls break-lines and pretext-layout-lines from bytecode-compiled library — runs on VM, not CEK interpreter. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
122 lines
5.0 KiB
Plaintext
122 lines
5.0 KiB
Plaintext
;; Pretext island — client-side text layout with live controls
|
|
;; Uses bytecode-compiled break-lines from text-layout library.
|
|
|
|
(defisland
|
|
~pretext-demo/live
|
|
()
|
|
(let
|
|
((text "In the beginning was the Word, and the Word was with God, and the Word was God. The same was in the beginning with God. All things were made by him; and without him was not any thing made that was made. In him was life; and the life was the light of men.")
|
|
(words (split text " "))
|
|
(max-w (signal 500))
|
|
(font-size (signal 16))
|
|
(use-optimal (signal true))
|
|
(doc (host-global "document"))
|
|
(canvas (host-call doc "createElement" "canvas"))
|
|
(ctx (host-call canvas "getContext" "2d")))
|
|
(let
|
|
((mw (fn (word sz) (do (host-set! ctx "font" (str sz "px 'Pretext Serif', DejaVu Serif, serif")) (host-get (host-call ctx "measureText" word) "width")))))
|
|
(let
|
|
((layout (computed (fn () (let ((sz (deref font-size)) (mxw (deref max-w)) (opt (deref use-optimal))) (let ((widths (map (fn (w) (mw w sz)) words)) (spw (mw " " sz)) (lh (* sz 1.5))) (let ((ranges (if opt (break-lines widths spw mxw) (break-lines-greedy widths spw mxw)))) (pretext-layout-lines words widths ranges spw mxw lh))))))))
|
|
(div
|
|
(~tw :tokens "space-y-4")
|
|
(div
|
|
(~tw :tokens "flex flex-wrap gap-4 items-end")
|
|
(div
|
|
(label
|
|
(~tw :tokens "block text-xs text-stone-500 mb-1")
|
|
"Width")
|
|
(input
|
|
:type "range"
|
|
:min "200"
|
|
:max "700"
|
|
:value (deref max-w)
|
|
(~tw :tokens "w-32")
|
|
:on-input (fn
|
|
(e)
|
|
(reset!
|
|
max-w
|
|
(parse-number (host-get (host-get e "target") "value"))))))
|
|
(div
|
|
(label
|
|
(~tw :tokens "block text-xs text-stone-500 mb-1")
|
|
"Font size")
|
|
(input
|
|
:type "range"
|
|
:min "10"
|
|
:max "24"
|
|
:value (deref font-size)
|
|
(~tw :tokens "w-24")
|
|
:on-input (fn
|
|
(e)
|
|
(reset!
|
|
font-size
|
|
(parse-number (host-get (host-get e "target") "value"))))))
|
|
(div
|
|
(label
|
|
(~tw :tokens "block text-xs text-stone-500 mb-1")
|
|
"Algorithm")
|
|
(button
|
|
(~tw
|
|
:tokens "px-3 py-1 rounded border text-sm transition-colors")
|
|
:class (if
|
|
(deref use-optimal)
|
|
"bg-violet-600 text-white border-violet-600"
|
|
"bg-white text-stone-600 border-stone-300")
|
|
:on-click (fn (e) (reset! use-optimal (not (deref use-optimal))))
|
|
(if (deref use-optimal) "Knuth-Plass" "Greedy")))
|
|
(div
|
|
(~tw :tokens "text-xs text-stone-400")
|
|
(str (deref max-w) "px / " (deref font-size) "px")))
|
|
(let
|
|
((lines (deref layout))
|
|
(lh (* (deref font-size) 1.5))
|
|
(mxw (deref max-w))
|
|
(sz (deref font-size)))
|
|
(div
|
|
:class "relative rounded-lg border border-stone-200 bg-white overflow-hidden"
|
|
(div
|
|
:class "px-4 pt-3 pb-1"
|
|
(span
|
|
:class "text-xs font-medium uppercase tracking-wide text-stone-400"
|
|
(str
|
|
"Client-side — "
|
|
(len lines)
|
|
" lines, "
|
|
(len words)
|
|
" words")))
|
|
(div
|
|
:style (str
|
|
"position:relative;height:"
|
|
(* (len lines) lh)
|
|
"px;padding:12px 16px;")
|
|
(map
|
|
(fn
|
|
(line)
|
|
(let
|
|
((y (get line :y)))
|
|
(map
|
|
(fn
|
|
(pw)
|
|
(span
|
|
:style (str
|
|
"position:absolute;left:"
|
|
(+ (get pw :x) 16)
|
|
"px;top:"
|
|
(+ y 12)
|
|
"px;font-family:'Pretext Serif',serif;font-size:"
|
|
sz
|
|
"px;line-height:"
|
|
lh
|
|
"px;white-space:nowrap;")
|
|
(get pw :word)))
|
|
(get line :words))))
|
|
lines))
|
|
(div
|
|
:class "px-4 py-2 border-t border-stone-100 bg-stone-50 flex justify-between"
|
|
(span
|
|
:class "text-xs text-stone-400"
|
|
(str (len lines) " lines"))
|
|
(span
|
|
:class "text-xs text-stone-400"
|
|
(str "width: " mxw "px"))))))))))
|