add WASM blazor-like thing

This commit is contained in:
2026-07-13 00:40:08 -04:00
parent 98978e4930
commit 3c494605ba
38 changed files with 3070 additions and 1 deletions

View File

@@ -0,0 +1,68 @@
//go:build js && wasm
package wasmruntime
import (
"syscall/js"
"kjol/vdom"
)
// State preservation across an in-place hot swap (see the dev server's
// livereload script). A fresh wasm instance has fresh Go memory, so signal
// values would reset on every rebuild. To keep them, the outgoing instance
// snapshots its signals (by creation order, via a vdom.Collector) into
// sessionStorage on dispose, and the incoming instance restores them on boot.
//
// It's best-effort: if an edit adds/removes/reorders signals, the by-order match
// shifts and unmatched signals fall back to their initial value — the same
// hook-order caveat as server components.
const stateKey = "__gowasm_hmr_state"
var stateCollector *vdom.Collector
// PreserveState registers the collector whose signals are snapshotted to
// sessionStorage when this instance is disposed for a hot swap.
func PreserveState(c *vdom.Collector) { stateCollector = c }
// RestoreState returns the signal snapshot saved by a prior instance before a
// hot swap, or nil if there is none. It is one-shot: the entry is cleared so a
// manual page refresh starts from initial state.
func RestoreState() [][]byte {
ss := js.Global().Get("sessionStorage")
if !ss.Truthy() {
return nil
}
raw := ss.Call("getItem", stateKey)
if !raw.Truthy() {
return nil
}
ss.Call("removeItem", stateKey)
arr := js.Global().Get("JSON").Call("parse", raw)
if arr.Type() != js.TypeObject {
return nil
}
out := make([][]byte, arr.Length())
for i := range out {
out[i] = []byte(arr.Index(i).String())
}
return out
}
// saveState writes the preserved collector's current signal values to
// sessionStorage. Called from the dispose hook before the DOM is torn down.
func saveState() {
if stateCollector == nil {
return
}
ss := js.Global().Get("sessionStorage")
if !ss.Truthy() {
return
}
arr := js.Global().Get("Array").New()
for _, b := range stateCollector.Snapshot() {
arr.Call("push", string(b))
}
ss.Call("setItem", stateKey, js.Global().Get("JSON").Call("stringify", arr))
}