Persist home stepper state to localStorage via freeze scope
Some checks failed
Build and Deploy / build-and-deploy (push) Has been cancelled
Some checks failed
Build and Deploy / build-and-deploy (push) Has been cancelled
- freeze-scope "home-stepper" captures step-idx signal - Each step/back saves to localStorage via freeze-to-sx - On mount, restores from localStorage via thaw-from-sx - Invalid state resets to default (step 9) - Clear preview lake before replay to prevent duplicates - Register local-storage-get/set/remove as primitives - Arrows 3x bigger 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 NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } });
|
||||||
var SX_VERSION = "2026-03-14T22:48:39Z";
|
var SX_VERSION = "2026-03-15T00:04:32Z";
|
||||||
|
|
||||||
function isNil(x) { return x === NIL || x === null || x === undefined; }
|
function isNil(x) { return x === NIL || x === null || x === undefined; }
|
||||||
function isSxTruthy(x) { return x !== false && !isNil(x); }
|
function isSxTruthy(x) { return x !== false && !isNil(x); }
|
||||||
@@ -5757,6 +5757,20 @@ PRIMITIVES["resource"] = resource;
|
|||||||
PRIMITIVES["is-html-tag?"] = function(n) { return HTML_TAGS.indexOf(n) >= 0; };
|
PRIMITIVES["is-html-tag?"] = function(n) { return HTML_TAGS.indexOf(n) >= 0; };
|
||||||
PRIMITIVES["make-env"] = function() { return merge(componentEnv, PRIMITIVES); };
|
PRIMITIVES["make-env"] = function() { return merge(componentEnv, PRIMITIVES); };
|
||||||
|
|
||||||
|
// localStorage — defined here (before boot) so islands can use at hydration
|
||||||
|
PRIMITIVES["local-storage-get"] = function(key) {
|
||||||
|
try { var v = localStorage.getItem(key); return v === null ? NIL : v; }
|
||||||
|
catch (e) { return NIL; }
|
||||||
|
};
|
||||||
|
PRIMITIVES["local-storage-set"] = function(key, val) {
|
||||||
|
try { localStorage.setItem(key, val); } catch (e) {}
|
||||||
|
return NIL;
|
||||||
|
};
|
||||||
|
PRIMITIVES["local-storage-remove"] = function(key) {
|
||||||
|
try { localStorage.removeItem(key); } catch (e) {}
|
||||||
|
return NIL;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// Platform interface — DOM adapter (browser-only)
|
// Platform interface — DOM adapter (browser-only)
|
||||||
@@ -7019,6 +7033,7 @@ PRIMITIVES["resource"] = resource;
|
|||||||
function localStorageRemove(key) {
|
function localStorageRemove(key) {
|
||||||
try { localStorage.removeItem(key); } catch (e) {}
|
try { localStorage.removeItem(key); } catch (e) {}
|
||||||
}
|
}
|
||||||
|
// localStorage primitives registered in CEK_FIXUPS_JS for ordering
|
||||||
|
|
||||||
// --- Cookies ---
|
// --- Cookies ---
|
||||||
|
|
||||||
|
|||||||
@@ -1531,6 +1531,20 @@ CEK_FIXUPS_JS = '''
|
|||||||
PRIMITIVES["make-symbol"] = function(n) { return new Symbol(n); };
|
PRIMITIVES["make-symbol"] = function(n) { return new Symbol(n); };
|
||||||
PRIMITIVES["is-html-tag?"] = function(n) { return HTML_TAGS.indexOf(n) >= 0; };
|
PRIMITIVES["is-html-tag?"] = function(n) { return HTML_TAGS.indexOf(n) >= 0; };
|
||||||
PRIMITIVES["make-env"] = function() { return merge(componentEnv, PRIMITIVES); };
|
PRIMITIVES["make-env"] = function() { return merge(componentEnv, PRIMITIVES); };
|
||||||
|
|
||||||
|
// localStorage — defined here (before boot) so islands can use at hydration
|
||||||
|
PRIMITIVES["local-storage-get"] = function(key) {
|
||||||
|
try { var v = localStorage.getItem(key); return v === null ? NIL : v; }
|
||||||
|
catch (e) { return NIL; }
|
||||||
|
};
|
||||||
|
PRIMITIVES["local-storage-set"] = function(key, val) {
|
||||||
|
try { localStorage.setItem(key, val); } catch (e) {}
|
||||||
|
return NIL;
|
||||||
|
};
|
||||||
|
PRIMITIVES["local-storage-remove"] = function(key) {
|
||||||
|
try { localStorage.removeItem(key); } catch (e) {}
|
||||||
|
return NIL;
|
||||||
|
};
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
@@ -2912,6 +2926,7 @@ PLATFORM_BOOT_JS = """
|
|||||||
function localStorageRemove(key) {
|
function localStorageRemove(key) {
|
||||||
try { localStorage.removeItem(key); } catch (e) {}
|
try { localStorage.removeItem(key); } catch (e) {}
|
||||||
}
|
}
|
||||||
|
// localStorage primitives registered in CEK_FIXUPS_JS for ordering
|
||||||
|
|
||||||
// --- Cookies ---
|
// --- Cookies ---
|
||||||
|
|
||||||
|
|||||||
@@ -174,7 +174,8 @@
|
|||||||
(when (and parent rendered)
|
(when (and parent rendered)
|
||||||
(dom-append parent rendered)))))
|
(dom-append parent rendered)))))
|
||||||
(swap! step-idx inc)
|
(swap! step-idx inc)
|
||||||
(update-code-highlight))))
|
(update-code-highlight)
|
||||||
|
(local-storage-set "sx-home-stepper" (freeze-to-sx "home-stepper")))))
|
||||||
(do-back (fn ()
|
(do-back (fn ()
|
||||||
(when (> (deref step-idx) 0)
|
(when (> (deref step-idx) 0)
|
||||||
(let ((target (- (deref step-idx) 1))
|
(let ((target (- (deref step-idx) 1))
|
||||||
@@ -182,7 +183,18 @@
|
|||||||
(when container (dom-set-prop container "innerHTML" ""))
|
(when container (dom-set-prop container "innerHTML" ""))
|
||||||
(set-stack (list (get-preview)))
|
(set-stack (list (get-preview)))
|
||||||
(reset! step-idx 0)
|
(reset! step-idx 0)
|
||||||
(for-each (fn (_) (do-step)) (slice (deref steps) 0 target)))))))
|
(for-each (fn (_) (do-step)) (slice (deref steps) 0 target))
|
||||||
|
(local-storage-set "sx-home-stepper" (freeze-to-sx "home-stepper")))))))
|
||||||
|
;; Freeze scope for persistence
|
||||||
|
(freeze-scope "home-stepper" (fn ()
|
||||||
|
(freeze-signal "step" step-idx)))
|
||||||
|
;; Restore from localStorage on mount
|
||||||
|
(let ((saved (local-storage-get "sx-home-stepper")))
|
||||||
|
(when saved
|
||||||
|
(thaw-from-sx saved)
|
||||||
|
;; Validate — reset to default if out of range
|
||||||
|
(when (or (< (deref step-idx) 0) (> (deref step-idx) 16))
|
||||||
|
(reset! step-idx 9))))
|
||||||
;; Auto-parse via effect
|
;; Auto-parse via effect
|
||||||
(effect (fn ()
|
(effect (fn ()
|
||||||
(let ((parsed (sx-parse source)))
|
(let ((parsed (sx-parse source)))
|
||||||
@@ -198,7 +210,9 @@
|
|||||||
;; Defer code DOM build until lake exists
|
;; Defer code DOM build until lake exists
|
||||||
(schedule-idle (fn ()
|
(schedule-idle (fn ()
|
||||||
(build-code-dom)
|
(build-code-dom)
|
||||||
;; Replay to initial step-idx
|
;; Clear preview and replay to initial step-idx
|
||||||
|
(let ((preview (get-preview)))
|
||||||
|
(when preview (dom-set-prop preview "innerHTML" "")))
|
||||||
(let ((target (deref step-idx)))
|
(let ((target (deref step-idx)))
|
||||||
(reset! step-idx 0)
|
(reset! step-idx 0)
|
||||||
(set-stack (list (get-preview)))
|
(set-stack (list (get-preview)))
|
||||||
@@ -213,7 +227,7 @@
|
|||||||
;; Controls
|
;; Controls
|
||||||
(div :class "flex items-center justify-center gap-2 md:gap-3"
|
(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))
|
||||||
:class (str "px-2 py-1 rounded text-lg "
|
:class (str "px-2 py-1 rounded text-3xl "
|
||||||
(if (> (deref step-idx) 0)
|
(if (> (deref step-idx) 0)
|
||||||
"text-stone-600 hover:text-stone-800 hover:bg-stone-100"
|
"text-stone-600 hover:text-stone-800 hover:bg-stone-100"
|
||||||
"text-stone-300 cursor-not-allowed"))
|
"text-stone-300 cursor-not-allowed"))
|
||||||
@@ -221,7 +235,7 @@
|
|||||||
(span :class "text-sm text-stone-500 font-mono tabular-nums"
|
(span :class "text-sm text-stone-500 font-mono tabular-nums"
|
||||||
(deref step-idx) " / " (len (deref steps)))
|
(deref step-idx) " / " (len (deref steps)))
|
||||||
(button :on-click (fn (e) (do-step))
|
(button :on-click (fn (e) (do-step))
|
||||||
:class (str "px-2 py-1 rounded text-lg "
|
:class (str "px-2 py-1 rounded text-3xl "
|
||||||
(if (< (deref step-idx) (len (deref steps)))
|
(if (< (deref step-idx) (len (deref steps)))
|
||||||
"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"))
|
||||||
|
|||||||
Reference in New Issue
Block a user