Stepper: steps-to-preview for isomorphic preview text (WIP)
steps-to-preview is a pure recursive descent function inside the island's letrec that builds an SX expression tree from steps[0..target-1]. The preview lake uses it to show partial text (e.g. "the joy of " at step 9). Still WIP: stepper island doesn't SSR because DOM-only code (effect, dom-query, dom-create-element) runs in the island body and fails on server. Need to guard client-only code so SSR can render the pure parts (code view, counter, preview). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
// =========================================================================
|
||||
|
||||
var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } });
|
||||
var SX_VERSION = "2026-03-24T04:10:36Z";
|
||||
var SX_VERSION = "2026-03-24T04:23:51Z";
|
||||
|
||||
function isNil(x) { return x === NIL || x === null || x === undefined; }
|
||||
function isSxTruthy(x) { return x !== false && !isNil(x); }
|
||||
|
||||
@@ -94,6 +94,52 @@
|
||||
(when is-tag
|
||||
(dict-set! step-ref "v" (+ (get step-ref "v") 1)))))
|
||||
:else nil)))
|
||||
(steps-to-preview (fn (all-steps target)
|
||||
;; Recursive descent: build SX expression tree from steps[0..target-1].
|
||||
;; "open" recurses for children; "close"/end-of-steps returns.
|
||||
;; Unclosed elements close naturally when steps run out.
|
||||
(if (or (empty? all-steps) (<= target 0))
|
||||
nil
|
||||
(let ((pos (dict "i" 0))
|
||||
(max-i (min target (len all-steps))))
|
||||
(letrec
|
||||
((build-children (fn ()
|
||||
(let ((children (list)))
|
||||
(let loop ()
|
||||
(if (>= (get pos "i") max-i)
|
||||
children
|
||||
(let ((step (nth all-steps (get pos "i")))
|
||||
(stype (get step "type")))
|
||||
(cond
|
||||
(= stype "open")
|
||||
(do
|
||||
(dict-set! pos "i" (+ (get pos "i") 1))
|
||||
(let ((tag (get step "tag"))
|
||||
(attrs (or (get step "attrs") (list)))
|
||||
(inner (build-children)))
|
||||
;; Skip spreads (~cssx/tw) — just structure + text
|
||||
(append! children
|
||||
(concat (list (make-symbol tag)) attrs inner)))
|
||||
(loop))
|
||||
(= stype "close")
|
||||
(do (dict-set! pos "i" (+ (get pos "i") 1))
|
||||
children)
|
||||
(= stype "leaf")
|
||||
(do (dict-set! pos "i" (+ (get pos "i") 1))
|
||||
(append! children (get step "expr"))
|
||||
(loop))
|
||||
(= stype "expr")
|
||||
(do (dict-set! pos "i" (+ (get pos "i") 1))
|
||||
(append! children (get step "expr"))
|
||||
(loop))
|
||||
:else
|
||||
(do (dict-set! pos "i" (+ (get pos "i") 1))
|
||||
(loop))))))))))
|
||||
(let ((root (build-children)))
|
||||
(cond
|
||||
(= (len root) 1) (first root)
|
||||
(empty? root) nil
|
||||
:else (concat (list (make-symbol "<>")) root))))))))
|
||||
(get-preview (fn () (dom-query "[data-sx-lake=\"home-preview\"]")))
|
||||
(get-code-view (fn () (dom-query "[data-code-view]")))
|
||||
(get-stack (fn () (deref dom-stack-sig)))
|
||||
@@ -250,14 +296,9 @@
|
||||
"text-violet-600 hover:text-violet-800 hover:bg-violet-50"
|
||||
"text-violet-300 cursor-not-allowed"))
|
||||
"\u25b6"))
|
||||
;; Live preview lake — client builds incrementally via do-step effect.
|
||||
;; SSR shows the full result; client effect replays 0→N for animation.
|
||||
;; Live preview — shows partial result up to current step.
|
||||
;; Same SX rendered by server (HTML) and client (DOM).
|
||||
(lake :id "home-preview"
|
||||
(div (~cssx/tw :tokens "text-center")
|
||||
(h1 (~cssx/tw :tokens "text-3xl font-bold mb-2")
|
||||
(span (~cssx/tw :tokens "text-rose-500") "the ")
|
||||
(span (~cssx/tw :tokens "text-amber-500") "joy ")
|
||||
(span (~cssx/tw :tokens "text-emerald-500") "of ")
|
||||
(span (~cssx/tw :tokens "text-violet-600 text-4xl") "sx")))))))))
|
||||
(steps-to-preview (deref steps) (deref step-idx))))))))
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user