@xmachines/play-dom 2.0.0-alpha.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,39 +1,42 @@
1
1
  /**
2
- * PlayRenderer.ts — XMachines wrapper around @xmachines/json-render-dom.
2
+ * PlayRenderer.ts — the XMachines wrapper of @xmachines/json-render-dom.
3
3
  *
4
- * Bridges actor.currentView (TC39 Signal) to the inner DOM renderer using watchSignal.
5
- * Exposes connect() / disconnect() methods following the same pattern as
6
- * play-react, play-vue, and play-solid PlayRenderer classes.
4
+ * It connects actor.currentView, a TC39 Signal, to the DOM renderer below it, with
5
+ * watchSignal. It gives you the connect() method and the disconnect() method, in
6
+ * the same pattern as the PlayRenderer class of play-react, of play-vue, and of
7
+ * play-solid.
7
8
  *
8
- * State store: uses external `store` option if provided (controlled mode); otherwise
9
- * creates a fresh @xstate/store atom per view transition seeded from spec.state.
9
+ * The state store: the renderer uses the external `store` option when the caller
10
+ * gives one, and this is the controlled mode. Without that option, it makes a new
11
+ * @xstate/store atom for each view transition, with the values of spec.state.
10
12
  */
11
13
  import { watchSignal } from "@xmachines/play-signals";
12
14
  import { createAtom } from "@xstate/store";
13
15
  import { xstateStoreStateStore } from "@xmachines/json-render-xstate";
14
- import { toAtomState } from "@xmachines/play-actor";
16
+ import { createViewStoreLifecycle, refreshContextSubtree } from "@xmachines/play-actor";
15
17
  import { createIncrementalRenderer } from "@xmachines/json-render-dom";
16
18
  /**
17
- * PlayRenderer connects an actor's `currentView` signal to the DOM renderer.
19
+ * PlayRenderer connects the `currentView` signal of an actor to the DOM renderer.
18
20
  *
19
- * Watches `actor.currentView` via TC39 Signals and renders `DomComponentRenderer`
20
- * functions into `container` on every view transition. Cleared on `disconnect()`.
21
+ * It watches `actor.currentView` through the TC39 Signals. It renders each
22
+ * `DomComponentRenderer` function into `container` on every view transition.
23
+ * `disconnect()` clears the container.
21
24
  *
22
- * Options from `PlayDomOptions` (which extends `UIProviderOptions`) are all forwarded
23
- * into `DomRenderContext` on every render pass:
24
- * - `functions` — named compute functions for `{ $computed: "name" }` prop expressions
25
- * - `directives` — custom `$`-prefixed dynamic values (from `defineDirective`), resolved during prop resolution
26
- * - `validationFunctions` — custom validation functions, available at `ctx.ctx.validationFunctions`
27
- * - `navigate` — navigation callback, invoked on `onSuccess: { navigate: "..." }` action bindings
28
- * - `onRenderError` — called with `(error, name)` for component render errors and action handler rejections
25
+ * Every option of `PlayDomOptions`, which extends `UIProviderOptions`, goes into
26
+ * `DomRenderContext` on each render pass:
27
+ * - `functions` — the named compute functions of a `{ $computed: "name" }` prop expression
28
+ * - `directives` — your own dynamic values with a `$` prefix, from `defineDirective`. The renderer resolves them with the props
29
+ * - `validationFunctions` — your own check functions. They are available at `ctx.ctx.validationFunctions`
30
+ * - `navigate` — the navigation callback. The renderer calls it for an `onSuccess: { navigate: "..." }` action binding
31
+ * - `onRenderError` — the renderer calls it with `(error, name)` for a component render error and for an action handler rejection
29
32
  *
30
- * **Preferred usage — via `registryResult`:**
33
+ * **The preferred use, with `registryResult`:**
31
34
  * ```typescript
32
35
  * import { PlayRenderer, defineRegistry } from "@xmachines/play-dom";
33
36
  *
34
37
  * const registryResult = defineRegistry(catalog, { components, actions });
35
38
  * const renderer = new PlayRenderer(container, actor, registryResult.registry, {
36
- * registryResult, // wires setState/getState from xstate store automatically
39
+ * registryResult, // it connects setState and getState of the xstate store for you
37
40
  * navigate: (path) => myRouter.push(path),
38
41
  * functions: { fullName: (args) => `${args.first} ${args.last}` },
39
42
  * });
@@ -42,7 +45,7 @@ import { createIncrementalRenderer } from "@xmachines/json-render-dom";
42
45
  * renderer.disconnect();
43
46
  * ```
44
47
  *
45
- * **Controlled store mode** — bring your own `StateStore`:
48
+ * **The controlled store mode** — you give your own `StateStore`:
46
49
  * ```typescript
47
50
  * import { createAtom } from "@xstate/store";
48
51
  * import { xstateStoreStateStore } from "@xmachines/json-render-xstate";
@@ -53,8 +56,9 @@ import { createIncrementalRenderer } from "@xmachines/json-render-dom";
53
56
  * renderer.connect();
54
57
  * ```
55
58
  *
56
- * Double `connect()` is safe — calling `connect()` while already connected
57
- * automatically disconnects first, preventing double-render subscriptions.
59
+ * A second `connect()` is safe. A `connect()` call on a connected renderer
60
+ * disconnects it first. Therefore the renderer holds no second render
61
+ * subscription.
58
62
  */
59
63
  export class PlayRenderer {
60
64
  container;
@@ -64,35 +68,56 @@ export class PlayRenderer {
64
68
  unwatch = null;
65
69
  storeUnsubscribe = null;
66
70
  /**
67
- * Live watch-subscription cleanups. A `Set` (not an array) so that each
68
- * registered cleanup can delete ITSELF on invocation: the incremental
69
- * renderer releases a watcher when its element unmounts, and the registered
70
- * wrapper is what it invokes — so the Set shrinks in lock-step with live
71
- * subscriptions instead of accumulating one spent closure per historical
72
- * mount over a long-lived view.
71
+ * The cleanup functions of the live watch subscriptions. This field is a `Set`,
72
+ * and not an array, so that each registered cleanup function can delete ITSELF on
73
+ * its call: the incremental renderer releases a watcher when its element unmounts,
74
+ * and it calls the registered wrapper. The Set therefore shrinks with the live
75
+ * subscriptions. It does not collect one spent closure for each historical mount of
76
+ * a view with a long life.
73
77
  */
74
78
  watchCleanups = new Set();
75
79
  /**
76
- * Set to `false` in `disconnect()` before calling `storeUnsub()`.
77
- * The `rerender` closure checks this flag at entry so that any synchronous
78
- * callback fired by the `StateStore` implementation within its own
79
- * `unsubscribe()` call (an edge-case but valid contract) does not mutate a
80
- * detached DOM tree.
80
+ * The viewKey of the view on the screen now, and the store behind it, without the
81
+ * guard. An emission with the same key means that only the /context projection
82
+ * moved: the fast path for a slice in render() refreshes the store in place, and it
83
+ * does not remove the tree. The ephemeral view state, such as a draft or a toggle,
84
+ * therefore stays. A null emission, from a state with no meta.view, is a GAP, and
85
+ * not a new view: the DOM goes away, but the identity and the store stay. A return
86
+ * to the same view therefore builds again around the ephemeral state that survived,
87
+ * in the same way as the framework providers. `disconnect()` resets both fields,
88
+ * because a store that stays alive must not outlive the connection.
89
+ */
90
+ lastViewKey = undefined;
91
+ currentStore = null;
92
+ /**
93
+ * The shared coordinator of the store lifecycle, from @xmachines/play-actor. It
94
+ * seeds the store again on a change of the viewKey, it refreshes /context in place
95
+ * in every other case, and it guards the identity cache. The two fields above,
96
+ * lastViewKey and currentStore, stay as the DOM-side record for the fast path of a
97
+ * slice. That path is about the live TREE, which is the spec and the subscription,
98
+ * and not about the store.
99
+ */
100
+ storeLifecycle = createViewStoreLifecycle((seed) => xstateStoreStateStore({ atom: createAtom(seed) }));
101
+ /**
102
+ * `disconnect()` sets this field to `false` before it calls `storeUnsub()`.
103
+ * The `rerender` closure reads the flag at its start. Therefore a synchronous
104
+ * callback from the `StateStore` implementation inside its own `unsubscribe()`
105
+ * call, which is rare but correct, changes no detached DOM tree.
81
106
  */
82
107
  alive = true;
83
108
  /**
84
- * @param container - `HTMLElement` to render into. Cleared and repopulated on every view transition.
85
- * @param actor - Actor providing the `currentView` signal (must implement `Viewable`).
86
- * @param registry - Component renderer map — typically `registryResult.registry` from `defineRegistry`.
87
- * @param options - Configuration (see {@link PlayDomOptions}):
88
- * - `registryResult` — auto-wires `setState`/`state` from the xstate store.
89
- * - `store` — external `StateStore` (controlled mode; overrides `spec.state` seeding).
90
- * - `loading` — streaming mode flag; suppresses missing-child warnings.
91
- * - `functions` — named compute functions for `$computed` prop expressions.
92
- * - `directives` — custom `$`-prefixed dynamic values resolved during prop resolution.
93
- * - `validationFunctions` — custom validation functions; available at `ctx.ctx.validationFunctions`.
94
- * - `navigate` — navigation callback; invoked on `onSuccess: { navigate: "..." }`.
95
- * - `onRenderError` — `(error, name)` handler for component render errors and action handler rejections; suppresses `console.error` fallback.
109
+ * @param container - The `HTMLElement` to render into. Each view transition clears it and fills it again.
110
+ * @param actor - The actor with the `currentView` signal. It must implement `Viewable`.
111
+ * @param registry - The map of the component renderers, usually `registryResult.registry` from `defineRegistry`.
112
+ * @param options - The configuration. See {@link PlayDomOptions}:
113
+ * - `registryResult` — it connects `setState` and `state` of the xstate store for you.
114
+ * - `store` — an external `StateStore`, which is the controlled mode. It replaces the values of `spec.state`.
115
+ * - `loading` — the flag of the streaming mode. It stops each warning about an absent child.
116
+ * - `functions` — the named compute functions of a `$computed` prop expression.
117
+ * - `directives` — your own dynamic values with a `$` prefix. The renderer resolves them with the props.
118
+ * - `validationFunctions` — your own check functions. They are available at `ctx.ctx.validationFunctions`.
119
+ * - `navigate` — the navigation callback. The renderer calls it for `onSuccess: { navigate: "..." }`.
120
+ * - `onRenderError` — the `(error, name)` handler of a component render error and of an action handler rejection. It stops the `console.error` fallback.
96
121
  */
97
122
  constructor(container, actor, registry, options = {}) {
98
123
  this.container = container;
@@ -101,13 +126,13 @@ export class PlayRenderer {
101
126
  this.options = options;
102
127
  }
103
128
  /**
104
- * Start watching actor.currentView and render to container.
105
- * Renders the initial view synchronously, then subscribes to signal changes.
129
+ * Starts the watch of actor.currentView, and renders into the container.
130
+ * It renders the first view synchronously, then it subscribes to the signal changes.
106
131
  *
107
- * Calling `connect()` on an already-connected renderer (where a previous
108
- * `connect()` was never followed by `disconnect()`) would silently install a
109
- * second `watchSignal` subscription, causing double-renders on every view
110
- * change. Guard against this by auto-disconnecting first.
132
+ * A `connect()` call on a connected renderer, where a `disconnect()` call did not
133
+ * follow the previous `connect()`, installs a second `watchSignal` subscription.
134
+ * Each view change then renders two times. Therefore this method disconnects the
135
+ * renderer first.
111
136
  */
112
137
  connect() {
113
138
  if (this.unwatch !== null)
@@ -116,83 +141,150 @@ export class PlayRenderer {
116
141
  this.unwatch = watchSignal(this.actor.currentView, (view) => this.render(view));
117
142
  }
118
143
  /**
119
- * Stop watching and clear the container.
144
+ * Stops the watch and clears the container.
120
145
  */
121
146
  disconnect() {
122
147
  this.unwatch?.();
123
148
  this.unwatch = null;
124
- // Null the refs first so any in-flight rerender callback sees null and skips.
125
- // Capture the arrays/functions before clearing so we call the correct cleanups
126
- // even if unsubscribing the store triggers a synchronous rerender that would
127
- // otherwise overwrite watchCleanups before we iterate them.
149
+ // Set the refs to null first. A rerender callback that is still in flight therefore
150
+ // sees null, and it stops.
151
+ // Capture each array and each function before the code clears them. Therefore the
152
+ // code calls the correct cleanup functions, also when the cancellation of the store
153
+ // subscription starts a synchronous rerender. Without the capture, that rerender
154
+ // overwrites watchCleanups before the loop over them.
128
155
  const storeUnsub = this.storeUnsubscribe;
129
156
  const watchCleanups = [...this.watchCleanups];
130
157
  this.storeUnsubscribe = null;
131
158
  this.watchCleanups = new Set();
132
- // Set alive = false before calling storeUnsub() so that any synchronous
133
- // rerender callback triggered within unsubscribe() exits immediately.
159
+ // Set alive = false before the storeUnsub() call. A synchronous rerender callback
160
+ // from inside unsubscribe() therefore returns at once.
134
161
  this.alive = false;
135
162
  storeUnsub?.();
136
163
  for (const cleanup of watchCleanups)
137
164
  cleanup();
138
165
  this.container.replaceChildren();
166
+ // The rule of the lifetime: the store that stays alive dies with the connection.
167
+ this.lastViewKey = undefined;
168
+ this.currentStore = null;
169
+ this.storeLifecycle.reset();
139
170
  }
140
171
  /**
141
- * Number of live watch-subscription cleanups currently retained.
172
+ * The number of live watch-subscription cleanup functions now.
142
173
  *
143
- * @internal Test-only accessor for asserting that the watch-cleanup
144
- * collection tracks live registrations (no per-mount leak). Not part of the
145
- * public API and may change without notice.
174
+ * @internal This accessor is for a test only. A test asserts with it that the
175
+ * collection of the watch cleanups holds the live registrations, and that no mount
176
+ * leaks. It is not part of the public API, and it can change without a notice.
146
177
  */
147
178
  get watchCleanupCount() {
148
179
  return this.watchCleanups.size;
149
180
  }
150
181
  render(view) {
182
+ // ── The fast path of a slice ────────────────────────────────────────────
183
+ // The viewKey of the live tree is the same → only the /context projection changed.
184
+ // deriveCurrentView uses the selected meta entry as the key of each emission, and the
185
+ // authored spec of a state is static. Refresh the subtree of the store in place: the
186
+ // store subscription below calls incremental.update() already, and that function
187
+ // compares the snapshots and patches the elements of the change only. Skip the
188
+ // teardown completely, so that the ephemeral view state stays.
189
+ // createIncrementalRenderer binds the SPEC at its construction. Therefore this path
190
+ // is safe only because the spec is the same. A real view change, which means a
191
+ // different viewKey or no viewKey for a comparison, still rebuilds the tree below, as
192
+ // the discard-on-view-transition contract of the incremental renderer says.
193
+ if (view !== null &&
194
+ view.viewKey !== undefined &&
195
+ this.lastViewKey === view.viewKey &&
196
+ this.currentStore !== null &&
197
+ this.storeUnsubscribe !== null) {
198
+ refreshContextSubtree(this.currentStore, view);
199
+ return;
200
+ }
151
201
  this.alive = true;
152
- // Capture and clear before calling — prevents the synchronous callback fired
153
- // by some store implementations during unsubscribe from seeing a live
154
- // storeUnsubscribe and re-entering the rerender path with stale view/store.
155
- // Mirrors the capture-then-clear pattern used in disconnect().
202
+ // Capture the values and clear them before the call. Some store implementations
203
+ // fire a synchronous callback during their unsubscribe(). Without this order, that
204
+ // callback sees a live storeUnsubscribe, and it enters the rerender path again with
205
+ // an old view and an old store.
206
+ // disconnect() uses the same capture-then-clear pattern.
156
207
  const oldUnsub = this.storeUnsubscribe;
157
208
  this.storeUnsubscribe = null;
158
209
  oldUnsub?.();
159
- // Release watch cleanups from the previous view's initial render.
160
- // Without this, watch subscriptions from the prior view remain active
161
- // between a view transition and the first store update in the new view,
162
- // holding references to stale DOM elements and preventing GC.
210
+ // Release the watch cleanups of the first render of the previous view.
211
+ // Without this step, the watch subscriptions of the previous view stay active between
212
+ // a view transition and the first store update of the new view. They then hold a
213
+ // reference to a DOM element that is gone, and the garbage collector cannot take
214
+ // it.
163
215
  const prevWatchCleanups = [...this.watchCleanups];
164
216
  this.watchCleanups = new Set();
165
217
  for (const cleanup of prevWatchCleanups)
166
218
  cleanup();
167
219
  this.container.replaceChildren();
220
+ // A null emission is a gap between two views, and not a new view: keep lastViewKey
221
+ // and currentStore. A return to the same identity therefore uses the store below
222
+ // again. disconnect() stays the boundary of the lifetime.
168
223
  if (!view)
169
224
  return;
170
- // Resolve the store: external (controlled) or fresh per-view atom.
171
- // `spec.state` carries the machine's initial state for this view.
172
- // Guard that it is a plain object before seeding createAtom — a non-object
173
- // value (null, string, number) would silently produce a broken store.
174
- const store = this.options.store
175
- ? this.options.store
176
- : xstateStoreStateStore({
177
- atom: createAtom(toAtomState(view.state)),
178
- });
225
+ // Resolve the store through the shared lifecycle. There are three results: the
226
+ // external store, which is the controlled mode, and the coordinator refreshes its
227
+ // /context, because the machinery owns /context in both modes; the store that the
228
+ // coordinator kept, when this emission returns to the live identity (a view, then
229
+ // null, then the same view — the fast path could not take this case, because the DOM
230
+ // is gone, and the coordinator refreshes /context in place, because the context can
231
+ // move during the gap); or a new store of this view, with the values of the composed
232
+ // state.
233
+ const resolution = this.storeLifecycle.resolve(this.actor, view, this.options.store);
234
+ this.lastViewKey = view.viewKey;
235
+ // The code keeps the store without a guard, for the refresh of the fast path of a slice.
236
+ this.currentStore = resolution.store;
237
+ try {
238
+ this.renderView(view, resolution.guardedStore);
239
+ }
240
+ catch (error) {
241
+ this.resetFailedRebuild();
242
+ throw error;
243
+ }
244
+ }
245
+ /**
246
+ * A rebuild that fails must not leave the guard of the fast path armed on a dead
247
+ * tree, because the container is empty already. Therefore this code resets the view
248
+ * identity. The next emission, also one with the same viewKey, then makes a
249
+ * complete render again. It does not patch a store that nothing shows.
250
+ */
251
+ resetFailedRebuild() {
252
+ const failedUnsub = this.storeUnsubscribe;
253
+ this.storeUnsubscribe = null;
254
+ failedUnsub?.();
255
+ this.lastViewKey = undefined;
256
+ this.currentStore = null;
257
+ this.storeLifecycle.reset();
258
+ }
259
+ /**
260
+ * The tail of a complete rebuild, which can throw. See the catch block of render().
261
+ *
262
+ * @param guardedStore - Everything that the spec can write through, which includes
263
+ * $bindState, setState, and a chained set, receives the GUARDED store: /context is
264
+ * read-only to the spec, because the machine context changes through an event
265
+ * only.
266
+ */
267
+ renderView(view, guardedStore) {
179
268
  const send = this.actor.send.bind(this.actor);
180
- // Build a SetState function backed by the xstate store.
181
- // Called lazily at action-invocation time (not at handlers() call time)
182
- // so actions always see the latest store state.
269
+ // Build a SetState function on the xstate store.
270
+ // The code calls it late, at the moment of an action, and not at the moment of the
271
+ // handlers() call. Therefore each action sees the newest state of the store.
183
272
  const setState = (updater) => {
184
- const prev = store.getSnapshot();
273
+ const prev = guardedStore.getSnapshot();
185
274
  const next = updater(prev);
186
- // store.update() is called with a full state snapshot (not a patch map).
187
- // This is compatible with createStoreAdapter/createStateStore where update()
188
- // performs a shallow merge of the provided top-level keys, effectively
189
- // replacing the entire state model. Do not change this to a path-keyed
190
- // patch unless the StateStore contract is updated to match.
191
- store.update(next);
275
+ // The code calls store.update() with a complete snapshot of the state, and not with
276
+ // a map of a patch. This works with createStoreAdapter and with createStateStore,
277
+ // where update() merges the given top-level keys in a shallow way, and therefore
278
+ // replaces the complete model of the state. Change this call to a patch with a path
279
+ // as its key only after a change of the StateStore contract.
280
+ // The store with the guard lets the context key of this read-modify-write round trip
281
+ // pass, because the code touches that key not. A real mutation of /context
282
+ // throws.
283
+ guardedStore.update(next);
192
284
  };
193
- // Resolve handlers from registryResult (wires setState/getState from store).
285
+ // Resolve the handlers from registryResult. This connects setState and getState of the store.
194
286
  const handlers = this.options.registryResult
195
- ? this.options.registryResult.handlers(() => setState, () => store.getSnapshot())
287
+ ? this.options.registryResult.handlers(() => setState, () => guardedStore.getSnapshot())
196
288
  : {};
197
289
  const loading = this.options.loading;
198
290
  const functions = this.options.functions;
@@ -201,28 +293,29 @@ export class PlayRenderer {
201
293
  const navigate = this.options.navigate;
202
294
  const onConfirm = this.options.onConfirm;
203
295
  const onRenderError = this.options.onRenderError;
204
- // ── Watch lifecycle ─────────────────────────────────────────────────
205
- // Watch subscriptions follow element MOUNTS, managed by the incremental
206
- // renderer's per-instance registry: elements mounted on the initial
207
- // render register then, and elements that only appear later (visibility
208
- // flipping on, repeat items being appended) register at that moment with
209
- // the same skip-on-mount baseline semantics. Subscriptions are released
210
- // when their element unmounts (visibility off, repeat item removed), on
211
- // view change (top of this method), or on disconnect().
296
+ // ── The lifecycle of a watch ─────────────────────────────────────────────
297
+ // A watch subscription follows the MOUNT of an element, and the registry of each
298
+ // instance in the incremental renderer manages it: an element of the first render
299
+ // registers then, and an element that appears later, from a visibility that becomes
300
+ // true or from a repeat item that the code appends, registers at that moment, with
301
+ // the same skip-on-mount baseline. The renderer releases a subscription when its
302
+ // element unmounts, from a visibility that becomes false or from a repeat item that
303
+ // the code removes. It also releases each subscription on a view change, at the top of
304
+ // this method, and on a disconnect().
212
305
  //
213
- // Stable elements are NOT rewired per-rerender: the update subscription
214
- // below is registered with the store before renderSpec's watch
215
- // subscriptions, and @xstate/store notifies in insertion order. If
216
- // in-place re-renders tore watchers down and recreated them, the old
217
- // watchers would die queued-but-unfired and the new ones would baseline
218
- // against the already-updated snapshot — watch actions would never
219
- // fire. The registry therefore preserves the ORIGINAL subscription
220
- // (baseline and notification position intact) whenever an instance is
221
- // merely re-rendered, and only registers genuinely new mounts.
306
+ // A stable element does NOT receive a new watch on each rerender. The code registers
307
+ // the update subscription below with the store before the watch subscriptions of
308
+ // renderSpec, and @xstate/store notifies in the order of the insertion. If a render
309
+ // in place removed the watchers and made them again, each old watcher would die in
310
+ // the queue, before it fires, and each new watcher would take the snapshot after the
311
+ // update as its baseline. A watch action would then fire never. Therefore the registry
312
+ // keeps the ORIGINAL subscription, with its baseline and its position in the
313
+ // notification, whenever the code only renders an instance again. It registers a
314
+ // real new mount only.
222
315
  //
223
- // Every registered cleanup is idempotent and also collected in
224
- // watchCleanups, so calling it again here after the incremental
225
- // renderer already released it is safe.
316
+ // Each registered cleanup is idempotent, and the Set watchCleanups also holds it.
317
+ // Therefore a second call here, after the incremental renderer released it already, is
318
+ // safe.
226
319
  const renderOptions = {
227
320
  send,
228
321
  handlers,
@@ -231,11 +324,11 @@ export class PlayRenderer {
231
324
  ...(directives !== undefined ? { directives } : {}),
232
325
  ...(loading !== undefined ? { loading } : {}),
233
326
  onWatchSetup: (cleanup) => {
234
- // Wrap so that invoking the cleanup — from disconnect()/view change
235
- // here, OR from the incremental renderer when the owning element
236
- // unmounts — also drops it from the Set. Returned so the incremental
237
- // renderer registers THIS wrapper and calls it on release, keeping
238
- // the Set in step with live subscriptions.
327
+ // Wrap each cleanup. A call of the wrapper, from the disconnect() here or from the
328
+ // view change here, OR from the incremental renderer when the element of the cleanup
329
+ // unmounts, also removes the cleanup from the Set. The code returns the wrapper.
330
+ // Therefore the incremental renderer registers THIS wrapper and calls it on a
331
+ // release, and the Set stays in step with the live subscriptions.
239
332
  const entry = () => {
240
333
  this.watchCleanups.delete(entry);
241
334
  cleanup();
@@ -247,18 +340,20 @@ export class PlayRenderer {
247
340
  ...(navigate !== undefined ? { navigate } : {}),
248
341
  ...(onConfirm !== undefined ? { onConfirm } : {}),
249
342
  };
250
- // ── Incremental updates ─────────────────────────────────────────────
251
- // Store updates no longer rebuild the whole tree: the incremental
252
- // renderer diffs the previous vs next snapshot on each element's
253
- // tracked $state paths and re-renders only the affected elements in
254
- // place. Elements without state bindings keep their DOM node identity
255
- // (focus, selection, scroll survive unrelated updates). View
256
- // transitions still perform a full rebuild — this method runs anew.
257
- const incremental = createIncrementalRenderer(view, store, this.registry, this.container, renderOptions);
258
- this.storeUnsubscribe = store.subscribe(() => {
259
- // Guard: skip if PlayRenderer has been disconnected.
260
- // Handles the edge case where a StateStore implementation fires its
261
- // subscribed callbacks synchronously within its own unsubscribe() call.
343
+ // ── The incremental updates ─────────────────────────────────────────────
344
+ // A store update rebuilds the complete tree no longer: the incremental renderer
345
+ // compares the previous snapshot and the next snapshot on the tracked $state paths of
346
+ // each element, and it renders the elements of the change again, in place. An element
347
+ // without a state binding keeps the identity of its DOM node. Therefore the focus, the
348
+ // selection, and the scroll position survive an update of another element. A view
349
+ // transition still rebuilds everything, because this method runs again.
350
+ const incremental = createIncrementalRenderer(view, guardedStore, this.registry, this.container, renderOptions);
351
+ // subscribe passes through the guard without a change, and it keeps the same function
352
+ // reference. The guard wraps set and update only.
353
+ this.storeUnsubscribe = guardedStore.subscribe(() => {
354
+ // The guard: stop when a caller disconnected the PlayRenderer.
355
+ // This handles the case of a StateStore implementation that fires its subscribed
356
+ // callbacks synchronously inside its own unsubscribe() call.
262
357
  if (!this.alive)
263
358
  return;
264
359
  incremental.update();
@@ -1 +1 @@
1
- {"version":3,"file":"PlayRenderer.js","sourceRoot":"","sources":["../src/PlayRenderer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"PlayRenderer.js","sourceRoot":"","sources":["../src/PlayRenderer.ts"],"names":[],"mappings":"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"}