ngx-json-render 0.8.1 → 0.8.2

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.
package/README.md CHANGED
@@ -8,7 +8,7 @@ Built on `@json-render/core` (the same spec format, expressions, state store, ac
8
8
 
9
9
  This is an Angular **adapter over the official core**, not a second implementation of it — `@json-render/core` is a peer dependency, and the spec your model emits is the same one the React, Vue, Solid and Svelte renderers consume. A catalog and a spec written here move to another framework unchanged.
10
10
 
11
- **[Live demo](https://shteynu.github.io/ngx-json-render/)** — interactive spec (bindings, repeat, confirm, watch), a SpecStream rendering progressively that you can stop mid-generation, and a chat where prose and UI patches arrive in one reply; both streaming tabs replay a recording, or call a real model on your own key ([source](https://github.com/shteynu/ngx-json-render/tree/main/projects/demo)) — or [![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/shteynu/ngx-json-render)
11
+ **[Live demo](https://shteynu.github.io/ngx-json-render/)** — opens on a sales dashboard streaming in patch by patch, which you can stop mid-generation; plus a playground, an interactive spec (bindings, repeat, confirm, watch), and a chat where prose and UI patches arrive in one reply. Both streaming tabs replay a recording, or call a real model on your own key ([source](https://github.com/shteynu/ngx-json-render/tree/main/projects/demo)) — or [![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/shteynu/ngx-json-render)
12
12
 
13
13
  ![A SpecStream of RFC 6902 patches rendering progressively into an Angular dashboard](https://raw.githubusercontent.com/shteynu/ngx-json-render/main/docs/streaming.gif)
14
14
 
@@ -3166,6 +3166,39 @@ function notifyStreamObservers(event, ...args) {
3166
3166
  }
3167
3167
  }
3168
3168
  }
3169
+ /**
3170
+ * The start and end of one hook's generations, as observers see them.
3171
+ *
3172
+ * A superseded or stopped request only settles once its abort has gone
3173
+ * through, after the next generation may already have started. Reporting its
3174
+ * end from there would close the new generation instead of the old one, so a
3175
+ * generation ends here the moment it is superseded or stopped, and its own
3176
+ * late end is dropped.
3177
+ */
3178
+ function createObservedRuns() {
3179
+ let endOpen = null;
3180
+ return {
3181
+ start() {
3182
+ endOpen?.();
3183
+ let ended = false;
3184
+ const end = (ok) => {
3185
+ if (ended)
3186
+ return;
3187
+ ended = true;
3188
+ if (endOpen === stop)
3189
+ endOpen = null;
3190
+ notifyStreamObservers('onEnd', ok);
3191
+ };
3192
+ const stop = () => end(true);
3193
+ endOpen = stop;
3194
+ notifyStreamObservers('onStart');
3195
+ return end;
3196
+ },
3197
+ stop() {
3198
+ endOpen?.();
3199
+ },
3200
+ };
3201
+ }
3169
3202
 
3170
3203
  /**
3171
3204
  * Convert a flat element list to a Spec.
@@ -3463,9 +3496,11 @@ function injectUIStream(options) {
3463
3496
  catalog: options.catalog ?? null,
3464
3497
  };
3465
3498
  const session = createStreamSession();
3499
+ const runs = createObservedRuns();
3466
3500
  inject(DestroyRef).onDestroy(() => session.cancel());
3467
3501
  const stop = () => {
3468
3502
  session.cancel();
3503
+ runs.stop();
3469
3504
  isStreaming.set(false);
3470
3505
  };
3471
3506
  const clear = () => {
@@ -3490,7 +3525,7 @@ function injectUIStream(options) {
3490
3525
  ? { ...previousSpec, elements: { ...previousSpec.elements } }
3491
3526
  : { root: '', elements: {} };
3492
3527
  spec.set(currentSpec);
3493
- notifyStreamObservers('onStart');
3528
+ const end = runs.start();
3494
3529
  let failed = false;
3495
3530
  const handleLine = (line) => {
3496
3531
  const parsed = parseLine(line);
@@ -3556,7 +3591,7 @@ function injectUIStream(options) {
3556
3591
  }
3557
3592
  finally {
3558
3593
  gate.commit(() => isStreaming.set(false));
3559
- notifyStreamObservers('onEnd', !failed);
3594
+ end(!failed);
3560
3595
  }
3561
3596
  };
3562
3597
  return {
@@ -3600,9 +3635,11 @@ function injectChatUI(options) {
3600
3635
  catalog: options.catalog ?? null,
3601
3636
  };
3602
3637
  const session = createStreamSession();
3638
+ const runs = createObservedRuns();
3603
3639
  inject(DestroyRef).onDestroy(() => session.cancel());
3604
3640
  const stop = () => {
3605
3641
  session.cancel();
3642
+ runs.stop();
3606
3643
  isStreaming.set(false);
3607
3644
  };
3608
3645
  const clear = () => {
@@ -3657,7 +3694,7 @@ function injectChatUI(options) {
3657
3694
  messages.update((prev) => [...prev, userMessage, assistantMessage]);
3658
3695
  isStreaming.set(true);
3659
3696
  error.set(null);
3660
- notifyStreamObservers('onStart');
3697
+ const end = runs.start();
3661
3698
  let failed = false;
3662
3699
  // Mutable state for accumulating the assistant response
3663
3700
  let accumulatedText = '';
@@ -3740,7 +3777,7 @@ function injectChatUI(options) {
3740
3777
  }
3741
3778
  finally {
3742
3779
  gate.commit(() => isStreaming.set(false));
3743
- notifyStreamObservers('onEnd', !failed);
3780
+ end(!failed);
3744
3781
  }
3745
3782
  };
3746
3783
  return {