package vdom import "encoding/json" // Schedule is installed by the wasm runtime at startup; a signal write // triggers a re-render. On the server it is nil (renders are one-shot). var Schedule func() // Signal holds state. On the client, writing it schedules a re-render. type Signal[T any] struct{ v T } func NewSignal[T any](initial T) *Signal[T] { s := &Signal[T]{v: initial} if active != nil { active.adopt(s) // server component round-trip: restore prior value, track for snapshot } return s } func (s *Signal[T]) Get() T { return s.v } func (s *Signal[T]) Set(v T) { s.v = v if Schedule != nil { Schedule() } } func (s *Signal[T]) Update(fn func(T) T) { s.Set(fn(s.v)) } func (s *Signal[T]) snapshot() []byte { b, _ := json.Marshal(s.v); return b } func (s *Signal[T]) restoreFrom(b []byte) { _ = json.Unmarshal(b, &s.v) } // ---- signal-state round-trip for server components ---- // // A server component is stateless on the server: its signal values are // serialized and round-tripped through the client. While a Collector is active // (during a server-component render on the server), NewSignal restores each // signal's value from the incoming snapshot by creation order (hook-order), and // remembers it so the new values can be snapshotted back out. type signalState interface { snapshot() []byte restoreFrom([]byte) } // Collector captures the signals created during a server render. type Collector struct { restore [][]byte idx int sigs []signalState } var active *Collector func (c *Collector) adopt(s signalState) { if c.idx < len(c.restore) { s.restoreFrom(c.restore[c.idx]) } c.idx++ c.sigs = append(c.sigs, s) } // BeginCollect starts collecting signals, restoring them from `restore` (which // may be nil for an initial mount). Call EndCollect when construction is done. func BeginCollect(restore [][]byte) *Collector { active = &Collector{restore: restore} return active } // EndCollect stops collecting (rendering/handlers may still read the signals). func EndCollect() { active = nil } // Snapshot returns the current values of the collected signals, in order. func (c *Collector) Snapshot() [][]byte { out := make([][]byte, len(c.sigs)) for i, s := range c.sigs { out[i] = s.snapshot() } return out }