Stepper: isomorphic code highlighting + steps-to-preview (WIP)
Code view: SSR now uses same highlighting logic as client update-code-highlight (bg-amber-100 for current step, font-bold for active, opacity-40 for future). steps-to-preview: pure function that replays step machine as SX expression tree — intended for isomorphic preview rendering. Currently working for simple cases but needs fix for partial step counts (close-loop issue). Close steps now carry open-attrs/open-spreads for steps-to-preview. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,55 @@
|
|||||||
|
;; steps-to-preview — pure function: replay step machine as SX expression tree.
|
||||||
|
;; Given a list of steps and a target index, build the SX that represents
|
||||||
|
;; the partial render at that step. Works on both server and client.
|
||||||
|
(define steps-to-preview
|
||||||
|
(fn (all-steps target)
|
||||||
|
(if (or (empty? all-steps) (<= target 0))
|
||||||
|
nil
|
||||||
|
;; Use mutable lists for the stacks so append!/init work correctly
|
||||||
|
(let ((stack (list (list)))
|
||||||
|
(tag-stack (list)))
|
||||||
|
;; Replay steps 0..target-1
|
||||||
|
(for-each (fn (step)
|
||||||
|
(let ((step-type (get step "type")))
|
||||||
|
(cond
|
||||||
|
(= step-type "open")
|
||||||
|
(do (append! stack (list))
|
||||||
|
(append! tag-stack (get step "tag")))
|
||||||
|
(= step-type "close")
|
||||||
|
(when (> (len stack) 1)
|
||||||
|
(let ((children (last stack))
|
||||||
|
(tag (last tag-stack))
|
||||||
|
(attrs (or (get step "open-attrs") (list)))
|
||||||
|
(spreads (or (get step "open-spreads") (list)))
|
||||||
|
(expr (concat (list (make-symbol tag)) attrs spreads children)))
|
||||||
|
;; Pop stack: remove last, append expr to new last
|
||||||
|
(set! stack (init stack))
|
||||||
|
(set! tag-stack (init tag-stack))
|
||||||
|
(append! (last stack) expr)))
|
||||||
|
(= step-type "leaf")
|
||||||
|
(when (not (empty? stack))
|
||||||
|
(append! (last stack) (get step "expr")))
|
||||||
|
(= step-type "expr")
|
||||||
|
(when (not (empty? stack))
|
||||||
|
(append! (last stack) (get step "expr"))))))
|
||||||
|
(slice all-steps 0 (min target (len all-steps))))
|
||||||
|
;; Close any unclosed elements
|
||||||
|
(let close-loop ()
|
||||||
|
(when (> (len stack) 1)
|
||||||
|
(let ((children (last stack))
|
||||||
|
(tag (last tag-stack))
|
||||||
|
(expr (concat (list (make-symbol tag)) children)))
|
||||||
|
(set! stack (init stack))
|
||||||
|
(set! tag-stack (init tag-stack))
|
||||||
|
(append! (last stack) expr)
|
||||||
|
(close-loop))))
|
||||||
|
;; Return root content
|
||||||
|
(let ((root (first stack)))
|
||||||
|
(cond
|
||||||
|
(= (len root) 1) (first root)
|
||||||
|
(empty? root) nil
|
||||||
|
:else (concat (list (make-symbol "<>")) root)))))))
|
||||||
|
|
||||||
(defisland ~home/stepper ()
|
(defisland ~home/stepper ()
|
||||||
(let ((source "(div (~cssx/tw :tokens \"text-center\")\n (h1 (~cssx/tw :tokens \"text-3xl font-bold mb-2\")\n (span (~cssx/tw :tokens \"text-rose-500\") \"the \")\n (span (~cssx/tw :tokens \"text-amber-500\") \"joy \")\n (span (~cssx/tw :tokens \"text-emerald-500\") \"of \")\n (span (~cssx/tw :tokens \"text-violet-600 text-4xl\") \"sx\")))")
|
(let ((source "(div (~cssx/tw :tokens \"text-center\")\n (h1 (~cssx/tw :tokens \"text-3xl font-bold mb-2\")\n (span (~cssx/tw :tokens \"text-rose-500\") \"the \")\n (span (~cssx/tw :tokens \"text-amber-500\") \"joy \")\n (span (~cssx/tw :tokens \"text-emerald-500\") \"of \")\n (span (~cssx/tw :tokens \"text-violet-600 text-4xl\") \"sx\")))")
|
||||||
(steps (signal (list)))
|
(steps (signal (list)))
|
||||||
@@ -32,7 +84,7 @@
|
|||||||
cargs)
|
cargs)
|
||||||
(append! result {"type" "open" "tag" ctag "attrs" cat "spreads" spreads})
|
(append! result {"type" "open" "tag" ctag "attrs" cat "spreads" spreads})
|
||||||
(for-each (fn (c) (split-tag c result)) cch)
|
(for-each (fn (c) (split-tag c result)) cch)
|
||||||
(append! result {"type" "close" "tag" ctag}))
|
(append! result {"type" "close" "tag" ctag "open-attrs" cat "open-spreads" spreads}))
|
||||||
:else
|
:else
|
||||||
(append! result {"type" "expr" "expr" expr}))))
|
(append! result {"type" "expr" "expr" expr}))))
|
||||||
(build-code-tokens (fn (expr tokens step-ref indent)
|
(build-code-tokens (fn (expr tokens step-ref indent)
|
||||||
@@ -226,13 +278,15 @@
|
|||||||
(map (fn (tok)
|
(map (fn (tok)
|
||||||
(let ((step (get tok "step"))
|
(let ((step (get tok "step"))
|
||||||
(cur (deref step-idx))
|
(cur (deref step-idx))
|
||||||
(active (<= step cur))
|
(is-spread (get tok "spread"))
|
||||||
(cls (str (get tok "cls")
|
(cls (str (get tok "cls")
|
||||||
(if (and active (>= step 0) (not (get tok "spread")))
|
(cond
|
||||||
" font-bold text-xs"
|
(= step -1) ""
|
||||||
(if (and (not active) (>= step 0) (not (get tok "spread")))
|
(and (= step cur) is-spread) " opacity-60"
|
||||||
" opacity-40"
|
(= step cur) " bg-amber-100 rounded px-0.5 font-bold text-sm"
|
||||||
"")))))
|
(and (< step cur) is-spread) " opacity-60"
|
||||||
|
(< step cur) " font-bold text-xs"
|
||||||
|
:else " opacity-40"))))
|
||||||
(span :class cls (get tok "text"))))
|
(span :class cls (get tok "text"))))
|
||||||
(deref code-tokens))))
|
(deref code-tokens))))
|
||||||
;; Controls
|
;; Controls
|
||||||
@@ -251,12 +305,8 @@
|
|||||||
"text-violet-600 hover:text-violet-800 hover:bg-violet-50"
|
"text-violet-600 hover:text-violet-800 hover:bg-violet-50"
|
||||||
"text-violet-300 cursor-not-allowed"))
|
"text-violet-300 cursor-not-allowed"))
|
||||||
"\u25b6"))
|
"\u25b6"))
|
||||||
;; Live preview lake — SSR shows the final rendered result
|
;; Live preview — declarative: same SX rendered by server (HTML) and client (DOM).
|
||||||
|
;; steps-to-preview replays the stack machine as SX expressions.
|
||||||
(lake :id "home-preview"
|
(lake :id "home-preview"
|
||||||
(div (~cssx/tw :tokens "text-center")
|
(steps-to-preview (deref steps) (deref step-idx))))))))
|
||||||
(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")))))))))
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user