From c62e7319cf40f34ee30dc03c3934165275506111 Mon Sep 17 00:00:00 2001 From: giles Date: Wed, 25 Mar 2026 16:20:13 +0000 Subject: [PATCH] Optimize stepper: rebuild-preview replaces step replay - do-back: uses steps-to-preview + render-to-dom (O(1) render) instead of replaying every do-step from 0 (O(n) DOM ops + cssx) - Hydration effect: same rebuild-preview instead of step replay - Cookie save moved to button on-click only (not per-step) Co-Authored-By: Claude Opus 4.6 (1M context) --- sx/sx/home-stepper.sx | 44 ++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/sx/sx/home-stepper.sx b/sx/sx/home-stepper.sx index 939e8461..57b25cb3 100644 --- a/sx/sx/home-stepper.sx +++ b/sx/sx/home-stepper.sx @@ -219,17 +219,25 @@ ;; Component expressions handled by lake's reactive render nil)) (swap! step-idx inc) - (update-code-highlight) - (set-cookie "sx-home-stepper" (freeze-to-sx "home-stepper"))))) + (update-code-highlight))))) + (rebuild-preview (fn (target) + ;; Rebuild preview in one shot from steps-to-preview (pure SX→DOM) + (let ((container (get-preview))) + (when container + (dom-set-prop container "innerHTML" "") + (let ((expr (steps-to-preview (deref steps) target))) + (when expr + (let ((dom (render-to-dom expr (get-render-env nil) nil))) + (when dom + (dom-append container dom))))) + (set-stack (list container)))))) (do-back (fn () (when (> (deref step-idx) 0) - (let ((target (- (deref step-idx) 1)) - (container (get-preview))) - (when container (dom-set-prop container "innerHTML" "")) - (set-stack (list (get-preview))) - (reset! step-idx 0) - (for-each (fn (_) (do-step)) (slice (deref steps) 0 target)) - (set-cookie "sx-home-stepper" (freeze-to-sx "home-stepper"))))))) + (let ((target (- (deref step-idx) 1))) + (rebuild-preview target) + (reset! step-idx target) + (update-code-highlight) + (set-cookie "sx-home-stepper" (freeze-to-sx "home-stepper")))))))) ;; Freeze scope for persistence — same mechanism, cookie storage (freeze-scope "home-stepper" (fn () (freeze-signal "step" step-idx))) @@ -254,13 +262,7 @@ (let ((_eff (effect (fn () (schedule-idle (fn () (build-code-dom) - (let ((preview (get-preview))) - (when preview (dom-set-prop preview "innerHTML" ""))) - (batch (fn () - (let ((target (deref step-idx))) - (reset! step-idx 0) - (set-stack (list (get-preview))) - (for-each (fn (_) (do-step)) (slice (deref steps) 0 target))))) + (rebuild-preview (deref step-idx)) (update-code-highlight) (run-post-render-hooks))))))) (div :class "space-y-4" @@ -282,7 +284,9 @@ (deref code-tokens))) ;; Controls (div :class "flex items-center justify-center gap-2 md:gap-3" - (button :on-click (fn (e) (do-back)) + (button :on-click (fn (e) + (do-back) + (set-cookie "sx-home-stepper" (freeze-to-sx "home-stepper"))) :class (str "px-2 py-1 rounded text-3xl " (if (> (deref step-idx) 0) "text-stone-600 hover:text-stone-800 hover:bg-stone-100" @@ -290,7 +294,9 @@ "\u25c0") (span :class "text-sm text-stone-500 font-mono tabular-nums" (deref step-idx) " / " (len (deref steps))) - (button :on-click (fn (e) (do-step)) + (button :on-click (fn (e) + (do-step) + (set-cookie "sx-home-stepper" (freeze-to-sx "home-stepper"))) :class (str "px-2 py-1 rounded text-3xl " (if (< (deref step-idx) (len (deref steps))) "text-violet-600 hover:text-violet-800 hover:bg-violet-50" @@ -299,6 +305,6 @@ ;; Live preview — shows partial result up to current step. ;; Same SX rendered by server (HTML) and client (DOM). (lake :id "home-preview" - (steps-to-preview (deref steps) (deref step-idx)))))))) + (steps-to-preview (deref steps) (deref step-idx))))))