markstream-angular 2.0.23 → 2.0.24

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,9 +1,9 @@
1
1
  import * as i1 from '@angular/common';
2
2
  import { CommonModule } from '@angular/common';
3
3
  import * as i0 from '@angular/core';
4
- import { Input, ChangeDetectionStrategy, Component, forwardRef, inject, ChangeDetectorRef, ViewChild, ViewContainerRef, ElementRef, Renderer2, Directive, HostListener, Injectable, InjectionToken, EventEmitter, Output } from '@angular/core';
4
+ import { Input, ChangeDetectionStrategy, Component, forwardRef, inject, ChangeDetectorRef, ViewChild, ViewContainerRef, ElementRef, Renderer2, Directive, HostListener, InjectionToken, Optional, Inject, Injectable, EventEmitter, Output } from '@angular/core';
5
5
  import { normalizeCustomHtmlTags, normalizeCustomHtmlTagName, parseMarkdownToStructure, mergeCustomHtmlTags, getMarkdown, sanitizeHtmlAttrs, VOID_HTML_TAGS, BLOCKED_HTML_TAGS, isHtmlTagHardBlocked, isHtmlTagBlocked, isUnsafeHtmlUrl, shouldOpenLinkInNewTab, sanitizeImageSrc, NON_STRUCTURING_HTML_TAGS, toSafeMermaidSvgMarkup, getHtmlTagFromContent, stripCustomHtmlWrapper, STANDARD_HTML_TAGS, hasCompleteHtmlTagContent } from 'stream-markdown-parser';
6
- import { createPanGesture, applyMermaidThemeDirective, createSmoothMarkdownStream } from 'markstream-core';
6
+ import { createPanGesture, createStreamingTextScope, createStreamingTextDom, applyMermaidThemeDirective, createSmoothMarkdownStream, RENDERER_STREAM_DEFAULTS, createRendererStreamBridge } from 'markstream-core';
7
7
  import { computePosition, offset, flip, shift, autoUpdate } from '@floating-ui/dom';
8
8
 
9
9
  const TAG_TOKEN_RE = /<\/?([A-Z][\w:-]*)(?:\s[^<>]*)?>/gi;
@@ -5164,143 +5164,57 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImpo
5164
5164
  args: ['window:keydown', ['$event']]
5165
5165
  }] } });
5166
5166
 
5167
- function settleAndMergeSegments$1(segments, segmentId) {
5168
- return segments.reduce((result, segment) => {
5169
- const nextSegment = segment.id === segmentId
5170
- ? { ...segment, fading: false }
5171
- : segment;
5172
- const previousSegment = result[result.length - 1];
5173
- if (previousSegment && !previousSegment.fading && !nextSegment.fading) {
5174
- result[result.length - 1] = {
5175
- ...previousSegment,
5176
- content: previousSegment.content + nextSegment.content,
5177
- };
5178
- }
5179
- else {
5180
- result.push(nextSegment);
5181
- }
5182
- return result;
5183
- }, []);
5184
- }
5167
+ const MARKSTREAM_SMOOTH_STREAMING_SCOPE = new InjectionToken('MARKSTREAM_SMOOTH_STREAMING_SCOPE');
5168
+
5185
5169
  class InlineCodeNodeComponent {
5186
- constructor() {
5187
- this.segments = [];
5188
- this.renderedCode = '';
5189
- this.nextSegmentId = 0;
5170
+ constructor(scope) {
5171
+ this.scope = scope;
5172
+ this.initial = '';
5173
+ this.initialized = false;
5174
+ this.localScope = createStreamingTextScope(work => work());
5190
5175
  }
5191
5176
  ngOnChanges() {
5192
- const nextCode = getString$1(this.node?.code);
5193
- const streamStateKey = getString$1(this.indexKey).trim();
5194
- const textStreamState = this.context?.textStreamState;
5195
- const previousPersisted = streamStateKey
5196
- ? textStreamState?.get(streamStateKey)
5197
- : undefined;
5198
- const previousCode = previousPersisted ?? this.renderedCode;
5199
- const resumeFromPersistedCode = previousPersisted !== undefined
5200
- && previousPersisted !== this.renderedCode;
5201
- if (!this.fadeEnabled) {
5202
- this.setFullCode(nextCode);
5203
- }
5204
- else if (nextCode !== previousCode) {
5205
- if (previousCode && nextCode.startsWith(previousCode)) {
5206
- const appendedCode = nextCode.slice(previousCode.length);
5207
- const lastSegment = this.segments[this.segments.length - 1];
5208
- if (resumeFromPersistedCode) {
5209
- this.segments = [
5210
- { id: this.nextSegmentId++, content: previousCode, fading: false },
5211
- { id: this.nextSegmentId++, content: appendedCode, fading: true },
5212
- ];
5213
- }
5214
- else if (lastSegment?.fading) {
5215
- this.segments = [
5216
- ...this.segments.slice(0, -1),
5217
- { ...lastSegment, content: lastSegment.content + appendedCode },
5218
- ];
5219
- }
5220
- else {
5221
- this.segments = [
5222
- ...this.segments,
5223
- { id: this.nextSegmentId++, content: appendedCode, fading: true },
5224
- ];
5225
- }
5226
- }
5227
- else {
5228
- this.setFullCode(nextCode);
5229
- }
5177
+ if (!this.initialized) {
5178
+ this.initial = getString$1(this.node.code);
5179
+ this.initialized = true;
5230
5180
  }
5231
- this.renderedCode = nextCode;
5232
- if (streamStateKey)
5233
- textStreamState?.set(streamStateKey, nextCode);
5234
- }
5235
- get settledCode() {
5236
- return this.segments.filter(segment => !segment.fading).map(segment => segment.content).join('');
5237
- }
5238
- get streamedDelta() {
5239
- return this.segments.filter(segment => segment.fading).map(segment => segment.content).join('');
5240
- }
5241
- streamedDeltaClass(segment) {
5242
- return segment.id % 2 === 0
5243
- ? 'markstream-angular-text__stream-delta--a'
5244
- : 'markstream-angular-text__stream-delta--b';
5245
- }
5246
- get fadeEnabled() {
5247
- if (typeof this.fade === 'boolean')
5248
- return this.fade;
5249
- if (typeof this.context?.fade === 'boolean')
5250
- return this.context.fade;
5251
- return true;
5252
- }
5253
- get typewriterEnabled() {
5254
- if (typeof this.typewriter === 'boolean')
5255
- return this.typewriter;
5256
- if (typeof this.context?.typewriter === 'boolean')
5257
- return this.context.typewriter;
5258
- return false;
5259
- }
5260
- trackSegment(_index, segment) {
5261
- return segment.id;
5181
+ this.update();
5262
5182
  }
5263
- settleSegment(segmentId) {
5264
- if (!this.segments.some(segment => segment.id === segmentId && segment.fading))
5183
+ ngAfterViewInit() {
5184
+ if (typeof window === 'undefined')
5265
5185
  return;
5266
- this.segments = settleAndMergeSegments$1(this.segments, segmentId);
5186
+ this.binding = createStreamingTextDom(this.host.nativeElement, this.initial, this.scope?.textScope ?? this.localScope);
5187
+ this.update();
5267
5188
  }
5268
- setFullCode(content) {
5269
- if (this.segments.length === 1 && !this.segments[0]?.fading && this.segments[0].content === content)
5270
- return;
5271
- this.segments = content
5272
- ? [{ id: this.nextSegmentId++, content, fading: false }]
5273
- : [];
5189
+ ngOnDestroy() {
5190
+ this.binding?.dispose();
5274
5191
  }
5275
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: InlineCodeNodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
5276
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: InlineCodeNodeComponent, isStandalone: true, selector: "markstream-angular-inline-code-node", inputs: { node: "node", context: "context", indexKey: "indexKey", typewriter: "typewriter", fade: "fade" }, usesOnChanges: true, ngImport: i0, template: `
5277
- <code>
5278
- <span
5279
- *ngFor="let segment of segments; trackBy: trackSegment"
5280
- [class]="segment.fading ? 'markstream-angular-text__stream-delta ' + streamedDeltaClass(segment) : ''"
5281
- (animationend)="segment.fading && settleSegment(segment.id)"
5282
- >{{ segment.content }}</span>
5283
- </code>
5284
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
5192
+ update() {
5193
+ if (!this.scope?.textScope)
5194
+ this.localScope.setFinal(this.context?.final === true);
5195
+ const content = getString$1(this.node.code);
5196
+ const key = this.indexKey ?? '';
5197
+ this.binding?.update(content, this.fade ?? this.context?.fade ?? true, key ? this.context?.textStreamState?.get(key) : undefined);
5198
+ if (key)
5199
+ this.context?.textStreamState?.set(key, content);
5200
+ }
5201
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: InlineCodeNodeComponent, deps: [{ token: MARKSTREAM_SMOOTH_STREAMING_SCOPE, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
5202
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: InlineCodeNodeComponent, isStandalone: true, selector: "markstream-angular-inline-code-node", inputs: { node: "node", context: "context", indexKey: "indexKey", typewriter: "typewriter", fade: "fade" }, viewQueries: [{ propertyName: "host", first: true, predicate: ["host"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `<code #host><span>{{ initial }}</span></code>`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
5285
5203
  }
5286
5204
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: InlineCodeNodeComponent, decorators: [{
5287
5205
  type: Component,
5288
5206
  args: [{
5289
5207
  selector: 'markstream-angular-inline-code-node',
5290
5208
  standalone: true,
5291
- imports: [CommonModule],
5292
- template: `
5293
- <code>
5294
- <span
5295
- *ngFor="let segment of segments; trackBy: trackSegment"
5296
- [class]="segment.fading ? 'markstream-angular-text__stream-delta ' + streamedDeltaClass(segment) : ''"
5297
- (animationend)="segment.fading && settleSegment(segment.id)"
5298
- >{{ segment.content }}</span>
5299
- </code>
5300
- `,
5209
+ template: `<code #host><span>{{ initial }}</span></code>`,
5301
5210
  changeDetection: ChangeDetectionStrategy.OnPush,
5302
5211
  }]
5303
- }], propDecorators: { node: [{
5212
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
5213
+ type: Optional
5214
+ }, {
5215
+ type: Inject,
5216
+ args: [MARKSTREAM_SMOOTH_STREAMING_SCOPE]
5217
+ }] }], propDecorators: { node: [{
5304
5218
  type: Input,
5305
5219
  args: [{ required: true }]
5306
5220
  }], context: [{
@@ -5311,6 +5225,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImpo
5311
5225
  type: Input
5312
5226
  }], fade: [{
5313
5227
  type: Input
5228
+ }], host: [{
5229
+ type: ViewChild,
5230
+ args: ['host']
5314
5231
  }] } });
5315
5232
 
5316
5233
  class InsertNodeComponent {
@@ -5481,190 +5398,55 @@ function hideTooltip(immediate = false) {
5481
5398
  hideTimer = setTimeout(doHide, 120);
5482
5399
  }
5483
5400
 
5484
- function settleAndMergeSegments(segments, segmentId) {
5485
- return segments.reduce((result, segment) => {
5486
- const nextSegment = segmentId == null || segment.id === segmentId
5487
- ? { ...segment, fading: false }
5488
- : segment;
5489
- const previousSegment = result[result.length - 1];
5490
- if (previousSegment && !previousSegment.fading && !nextSegment.fading) {
5491
- result[result.length - 1] = {
5492
- ...previousSegment,
5493
- content: previousSegment.content + nextSegment.content,
5494
- };
5495
- }
5496
- else {
5497
- result.push(nextSegment);
5498
- }
5499
- return result;
5500
- }, []);
5501
- }
5502
5401
  class TextNodeComponent {
5503
- constructor() {
5504
- this.segments = [];
5505
- this.renderedText = '';
5506
- this.nextSegmentId = 0;
5402
+ constructor(scope) {
5403
+ this.scope = scope;
5404
+ this.initial = '';
5405
+ this.initialized = false;
5406
+ this.localScope = createStreamingTextScope(work => work());
5507
5407
  }
5508
5408
  ngOnChanges() {
5509
- const nextText = getString$1(this.node?.content);
5510
- const streamStateKey = getString$1(this.indexKey).trim();
5511
- const textStreamState = this.context?.textStreamState;
5512
- const streamRenderVersion = this.context?.streamRenderVersion;
5513
- const streamRenderVersionChanged = streamRenderVersion !== this.lastStreamRenderVersion;
5514
- const previousPersisted = streamStateKey
5515
- ? textStreamState?.get(streamStateKey)
5516
- : undefined;
5517
- const previousText = previousPersisted ?? this.renderedText;
5518
- const resumeFromPersistedText = previousPersisted !== undefined
5519
- && previousPersisted !== this.renderedText;
5520
- if (!this.fadeEnabled) {
5521
- this.setFullText(nextText);
5522
- this.renderedText = nextText;
5523
- if (streamStateKey)
5524
- textStreamState?.set(streamStateKey, nextText);
5525
- this.lastStreamRenderVersion = streamRenderVersion;
5526
- return;
5409
+ if (!this.initialized) {
5410
+ this.initial = getString$1(this.node.content);
5411
+ this.initialized = true;
5527
5412
  }
5528
- if (nextText === previousText) {
5529
- if (this.segments.some(segment => segment.fading) && streamRenderVersionChanged) {
5530
- this.settleFadingSegments();
5531
- }
5532
- else if (this.renderedText !== nextText) {
5533
- this.setFullText(nextText);
5534
- }
5535
- this.renderedText = nextText;
5536
- if (streamStateKey)
5537
- textStreamState?.set(streamStateKey, nextText);
5538
- this.lastStreamRenderVersion = streamRenderVersion;
5539
- return;
5540
- }
5541
- if (previousText && nextText.startsWith(previousText)) {
5542
- const appendedText = nextText.slice(previousText.length);
5543
- const lastSegment = this.segments[this.segments.length - 1];
5544
- if (resumeFromPersistedText) {
5545
- this.segments = [
5546
- { id: this.nextSegmentId++, content: previousText, fading: false },
5547
- { id: this.nextSegmentId++, content: appendedText, fading: true },
5548
- ];
5549
- }
5550
- else if (lastSegment?.fading) {
5551
- this.segments = [
5552
- ...this.segments.slice(0, -1),
5553
- { ...lastSegment, content: lastSegment.content + appendedText },
5554
- ];
5555
- }
5556
- else {
5557
- this.segments = [
5558
- ...this.segments,
5559
- { id: this.nextSegmentId++, content: appendedText, fading: true },
5560
- ];
5561
- }
5562
- }
5563
- else {
5564
- this.setFullText(nextText);
5565
- }
5566
- this.renderedText = nextText;
5567
- if (streamStateKey)
5568
- textStreamState?.set(streamStateKey, nextText);
5569
- this.lastStreamRenderVersion = streamRenderVersion;
5570
- }
5571
- get centered() {
5572
- return !!this.node?.center;
5573
- }
5574
- get settledText() {
5575
- return this.segments.filter(segment => !segment.fading).map(segment => segment.content).join('');
5576
- }
5577
- get streamedDelta() {
5578
- return this.segments.filter(segment => segment.fading).map(segment => segment.content).join('');
5579
- }
5580
- streamedDeltaClass(segment) {
5581
- return segment.id % 2 === 0
5582
- ? 'markstream-angular-text__stream-delta--a'
5583
- : 'markstream-angular-text__stream-delta--b';
5413
+ this.update();
5584
5414
  }
5585
- get fadeEnabled() {
5586
- if (typeof this.fade === 'boolean')
5587
- return this.fade;
5588
- if (typeof this.context?.fade === 'boolean')
5589
- return this.context.fade;
5590
- return true;
5591
- }
5592
- get typewriterEnabled() {
5593
- if (typeof this.typewriter === 'boolean')
5594
- return this.typewriter;
5595
- if (typeof this.context?.typewriter === 'boolean')
5596
- return this.context.typewriter;
5597
- return false;
5415
+ ngAfterViewInit() {
5416
+ if (typeof window === 'undefined')
5417
+ return;
5418
+ this.binding = createStreamingTextDom(this.host.nativeElement, this.initial, this.scope?.textScope ?? this.localScope);
5419
+ this.update();
5598
5420
  }
5599
- trackSegment(_index, segment) {
5600
- return segment.id;
5421
+ ngOnDestroy() {
5422
+ this.binding?.dispose();
5601
5423
  }
5602
- settleSegment(segmentId) {
5603
- if (!this.segments.some(segment => segment.id === segmentId && segment.fading))
5604
- return;
5605
- this.segments = settleAndMergeSegments(this.segments, segmentId);
5424
+ update() {
5425
+ if (!this.scope?.textScope)
5426
+ this.localScope.setFinal(this.context?.final === true);
5427
+ const content = getString$1(this.node.content);
5428
+ const key = this.indexKey ?? '';
5429
+ this.binding?.update(content, this.fade ?? this.context?.fade ?? true, key ? this.context?.textStreamState?.get(key) : undefined);
5430
+ if (key)
5431
+ this.context?.textStreamState?.set(key, content);
5606
5432
  }
5607
- setFullText(content) {
5608
- if (this.segments.length === 1 && !this.segments[0]?.fading && this.segments[0].content === content)
5609
- return;
5610
- this.segments = content
5611
- ? [{ id: this.nextSegmentId++, content, fading: false }]
5612
- : [];
5613
- }
5614
- settleFadingSegments() {
5615
- this.segments = settleAndMergeSegments(this.segments);
5616
- }
5617
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: TextNodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
5618
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: TextNodeComponent, isStandalone: true, selector: "markstream-angular-text-node", inputs: { node: "node", context: "context", indexKey: "indexKey", typewriter: "typewriter", fade: "fade" }, usesOnChanges: true, ngImport: i0, template: `
5619
- <ng-container *ngIf="!centered; else centeredText">
5620
- <span class="markstream-angular-text-node">
5621
- <span
5622
- *ngFor="let segment of segments; trackBy: trackSegment"
5623
- [class]="segment.fading ? 'markstream-angular-text__stream-delta ' + streamedDeltaClass(segment) : ''"
5624
- (animationend)="segment.fading && settleSegment(segment.id)"
5625
- >{{ segment.content }}</span>
5626
- </span>
5627
- </ng-container>
5628
- <ng-template #centeredText>
5629
- <span class="markstream-angular-text-node markstream-angular-text--centered">
5630
- <span
5631
- *ngFor="let segment of segments; trackBy: trackSegment"
5632
- [class]="segment.fading ? 'markstream-angular-text__stream-delta ' + streamedDeltaClass(segment) : ''"
5633
- (animationend)="segment.fading && settleSegment(segment.id)"
5634
- >{{ segment.content }}</span>
5635
- </span>
5636
- </ng-template>
5637
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
5433
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: TextNodeComponent, deps: [{ token: MARKSTREAM_SMOOTH_STREAMING_SCOPE, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
5434
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.26", type: TextNodeComponent, isStandalone: true, selector: "markstream-angular-text-node", inputs: { node: "node", context: "context", indexKey: "indexKey", typewriter: "typewriter", fade: "fade" }, viewQueries: [{ propertyName: "host", first: true, predicate: ["host"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `<span #host class="markstream-angular-text-node text-node" [class.markstream-angular-text--centered]="!!node.center"><span>{{ initial }}</span></span>`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
5638
5435
  }
5639
5436
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImport: i0, type: TextNodeComponent, decorators: [{
5640
5437
  type: Component,
5641
5438
  args: [{
5642
5439
  selector: 'markstream-angular-text-node',
5643
5440
  standalone: true,
5644
- imports: [CommonModule],
5645
- template: `
5646
- <ng-container *ngIf="!centered; else centeredText">
5647
- <span class="markstream-angular-text-node">
5648
- <span
5649
- *ngFor="let segment of segments; trackBy: trackSegment"
5650
- [class]="segment.fading ? 'markstream-angular-text__stream-delta ' + streamedDeltaClass(segment) : ''"
5651
- (animationend)="segment.fading && settleSegment(segment.id)"
5652
- >{{ segment.content }}</span>
5653
- </span>
5654
- </ng-container>
5655
- <ng-template #centeredText>
5656
- <span class="markstream-angular-text-node markstream-angular-text--centered">
5657
- <span
5658
- *ngFor="let segment of segments; trackBy: trackSegment"
5659
- [class]="segment.fading ? 'markstream-angular-text__stream-delta ' + streamedDeltaClass(segment) : ''"
5660
- (animationend)="segment.fading && settleSegment(segment.id)"
5661
- >{{ segment.content }}</span>
5662
- </span>
5663
- </ng-template>
5664
- `,
5441
+ template: `<span #host class="markstream-angular-text-node text-node" [class.markstream-angular-text--centered]="!!node.center"><span>{{ initial }}</span></span>`,
5665
5442
  changeDetection: ChangeDetectionStrategy.OnPush,
5666
5443
  }]
5667
- }], propDecorators: { node: [{
5444
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
5445
+ type: Optional
5446
+ }, {
5447
+ type: Inject,
5448
+ args: [MARKSTREAM_SMOOTH_STREAMING_SCOPE]
5449
+ }] }], propDecorators: { node: [{
5668
5450
  type: Input,
5669
5451
  args: [{ required: true }]
5670
5452
  }], context: [{
@@ -5675,6 +5457,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImpo
5675
5457
  type: Input
5676
5458
  }], fade: [{
5677
5459
  type: Input
5460
+ }], host: [{
5461
+ type: ViewChild,
5462
+ args: ['host']
5678
5463
  }] } });
5679
5464
 
5680
5465
  class LinkNodeComponent {
@@ -9829,6 +9614,7 @@ class SmoothMarkdownStreamService {
9829
9614
  final: false,
9830
9615
  };
9831
9616
  this.listeners = new Set();
9617
+ this.getSnapshot = () => this.snapshot;
9832
9618
  }
9833
9619
  init(options = {}) {
9834
9620
  if (this.controller)
@@ -9866,8 +9652,8 @@ class SmoothMarkdownStreamService {
9866
9652
  get pendingChars() { return this.snapshot.pendingChars; }
9867
9653
  enqueue(chunk) { this.controller?.enqueue(chunk); }
9868
9654
  finish(options) { this.controller?.finish(options); }
9869
- flush() { this.controller?.flush(); }
9870
- reset(initialMarkdown) { this.controller?.reset(initialMarkdown); }
9655
+ flush(options) { this.controller?.flush(options); }
9656
+ reset(initialMarkdown, options) { this.controller?.reset(initialMarkdown, options); }
9871
9657
  pause() { this.controller?.pause(); }
9872
9658
  resume() { this.controller?.resume(); }
9873
9659
  syncSnapshot() {
@@ -9945,15 +9731,15 @@ function computeLiveRange(total, focusIndex, maxLiveNodes, liveNodeBuffer) {
9945
9731
  };
9946
9732
  }
9947
9733
 
9948
- const MARKSTREAM_SMOOTH_STREAMING_SCOPE = new InjectionToken('MARKSTREAM_SMOOTH_STREAMING_SCOPE');
9949
-
9950
9734
  const SCROLL_PARENT_OVERFLOW_RE = /auto|scroll|overlay/i;
9951
9735
  class NodeRendererComponent {
9952
9736
  constructor() {
9953
9737
  this.cdr = inject(ChangeDetectorRef);
9954
9738
  this.hostRef = inject((ElementRef));
9955
9739
  this.smoothStream = new SmoothMarkdownStreamService();
9740
+ this.finalReady = false;
9956
9741
  this.parentSmoothStreamingScope = inject(MARKSTREAM_SMOOTH_STREAMING_SCOPE, { optional: true, skipSelf: true });
9742
+ this.ownTextScope = createStreamingTextScope(work => this.streamBridge.scheduleCleanup(work), this.parentSmoothStreamingScope?.textScope);
9957
9743
  this.content = '';
9958
9744
  this.htmlPolicy = 'safe';
9959
9745
  this.codeBlockStream = true;
@@ -10016,6 +9802,7 @@ class NodeRendererComponent {
10016
9802
  return `${this.resolvedIndexPrefix}-${entry.index}`;
10017
9803
  };
10018
9804
  }
9805
+ get textScope() { return this.ownTextScope; }
10019
9806
  static { this.TYPEWRITER_CURSOR_EXCLUDED_NODE_TYPES = new Set(['code_block', 'admonition', 'table', 'math_block', 'html_block', 'image']); }
10020
9807
  get hasNodes() {
10021
9808
  return Array.isArray(this.nodes);
@@ -10060,7 +9847,7 @@ class NodeRendererComponent {
10060
9847
  }
10061
9848
  get effectiveFinal() {
10062
9849
  if (this.smoothStreamingEnabled && this.requestedFinal != null)
10063
- return Boolean(this.requestedFinal && this.smoothSourceSynced && this.smoothStream.caughtUp);
9850
+ return Boolean(this.requestedFinal && this.finalReady && this.smoothSourceSynced && this.smoothStream.caughtUp);
10064
9851
  return this.requestedFinal;
10065
9852
  }
10066
9853
  get resolvedIndexPrefix() {
@@ -10135,7 +9922,7 @@ class NodeRendererComponent {
10135
9922
  this.rebuild();
10136
9923
  }
10137
9924
  ngOnInit() {
10138
- this.smoothStream.init(this.smoothStreamingOptions);
9925
+ this.smoothStream.init({ ...RENDERER_STREAM_DEFAULTS, ...this.smoothStreamingOptions, burstInitialContent: this.fade === false && !this.typewriter });
10139
9926
  this.unsubscribeSmoothStream = this.smoothStream.subscribe(() => {
10140
9927
  if (this.isSyncingSmoothStream)
10141
9928
  return;
@@ -10171,6 +9958,7 @@ class NodeRendererComponent {
10171
9958
  this.unsubscribeCustomComponents?.();
10172
9959
  this.unsubscribeCustomComponents = undefined;
10173
9960
  this.disconnectObserver();
9961
+ this.streamBridge?.dispose();
10174
9962
  this.smoothStream.ngOnDestroy();
10175
9963
  if (this.enhancementTimer != null && typeof window !== 'undefined') {
10176
9964
  window.clearTimeout(this.enhancementTimer);
@@ -10193,39 +9981,36 @@ class NodeRendererComponent {
10193
9981
  }
10194
9982
  ensureSmoothStreamInitialized() {
10195
9983
  if (this.smoothStream)
10196
- this.smoothStream.init(this.smoothStreamingOptions);
9984
+ this.smoothStream.init({ ...RENDERER_STREAM_DEFAULTS, ...this.smoothStreamingOptions, burstInitialContent: this.fade === false && !this.typewriter });
10197
9985
  }
10198
9986
  syncSmoothStream() {
10199
9987
  if (!this.smoothStream)
10200
9988
  return;
9989
+ this.ownTextScope.setFinal(this.requestedFinal === true);
10201
9990
  this.isSyncingSmoothStream = true;
10202
9991
  try {
10203
- const nextContent = this.content ?? '';
10204
- if (this.hasNodes) {
10205
- this.smoothStream.reset('');
10206
- return;
10207
- }
10208
- if (!this.smoothStreamingEnabled) {
10209
- this.smoothStream.reset(nextContent);
10210
- if (this.requestedFinal)
10211
- this.smoothStream.finish({ flush: true });
10212
- return;
10213
- }
10214
- const source = this.smoothStream.source;
10215
- if (!nextContent) {
10216
- this.smoothStream.reset('');
10217
- }
10218
- else if (nextContent === source) {
10219
- // no-op
10220
- }
10221
- else if (nextContent.startsWith(source)) {
10222
- this.smoothStream.enqueue(nextContent.slice(source.length));
10223
- }
10224
- else {
10225
- this.smoothStream.reset(nextContent);
10226
- }
10227
- if (this.requestedFinal)
10228
- this.smoothStream.finish();
9992
+ this.streamBridge ??= createRendererStreamBridge({
9993
+ getSnapshot: this.smoothStream.getSnapshot,
9994
+ enqueue: chunk => this.smoothStream.enqueue(chunk),
9995
+ finish: options => this.smoothStream.finish(options),
9996
+ flush: options => this.smoothStream.flush(options),
9997
+ reset: (content, options) => this.smoothStream.reset(content, options),
9998
+ pause: () => this.smoothStream.pause(),
9999
+ resume: () => this.smoothStream.resume(),
10000
+ }, {
10001
+ flushOnFinish: this.smoothStreamingOptions?.flushOnFinish,
10002
+ commit: (work) => {
10003
+ work();
10004
+ this.cdr.detectChanges();
10005
+ },
10006
+ measure: () => { void this.rootRef?.nativeElement.offsetHeight; },
10007
+ onReady: (value) => {
10008
+ this.finalReady = value;
10009
+ if (!this.isSyncingSmoothStream)
10010
+ this.rebuild();
10011
+ },
10012
+ });
10013
+ this.streamBridge.update(this.content ?? '', this.hasNodes, this.smoothStreamingEnabled, this.requestedFinal, this.fade !== false);
10229
10014
  }
10230
10015
  finally {
10231
10016
  this.isSyncingSmoothStream = false;
@@ -10771,6 +10556,7 @@ class NodeRendererComponent {
10771
10556
  ], viewQueries: [{ propertyName: "rootRef", first: true, predicate: ["root"], descendants: true }, { propertyName: "typewriterCursorRef", first: true, predicate: ["typewriterCursor"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `
10772
10557
  <div
10773
10558
  #root
10559
+ [attr.data-stream-final]="requestedFinal === true ? '' : null"
10774
10560
  class="markstream-angular markdown-renderer"
10775
10561
  [class.dark]="isDark"
10776
10562
  [class.virtualized]="virtualizationEnabled"
@@ -10841,6 +10627,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImpo
10841
10627
  template: `
10842
10628
  <div
10843
10629
  #root
10630
+ [attr.data-stream-final]="requestedFinal === true ? '' : null"
10844
10631
  class="markstream-angular markdown-renderer"
10845
10632
  [class.dark]="isDark"
10846
10633
  [class.virtualized]="virtualizationEnabled"