markstream-angular 2.0.12 → 2.0.13
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.
|
@@ -3,8 +3,8 @@ import { CommonModule } from '@angular/common';
|
|
|
3
3
|
import * as i0 from '@angular/core';
|
|
4
4
|
import { Input, ChangeDetectionStrategy, Component, forwardRef, inject, ChangeDetectorRef, ViewChild, ViewContainerRef, ElementRef, Renderer2, Directive, HostListener, Injectable, InjectionToken, 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, createSmoothMarkdownStream } from 'markstream-core';
|
|
6
7
|
import { computePosition, offset, flip, shift, autoUpdate } from '@floating-ui/dom';
|
|
7
|
-
import { createSmoothMarkdownStream } from 'markstream-core';
|
|
8
8
|
|
|
9
9
|
const TAG_TOKEN_RE = /<\/?([A-Z][\w:-]*)(?:\s[^<>]*)?>/gi;
|
|
10
10
|
function hydrateCustomTagContent(nodes, source, customHtmlTags) {
|
|
@@ -4413,6 +4413,9 @@ class InfographicBlockNodeComponent {
|
|
|
4413
4413
|
this.showSource = false;
|
|
4414
4414
|
this.modalOpen = false;
|
|
4415
4415
|
this.zoom = 1;
|
|
4416
|
+
this.translateX = 0;
|
|
4417
|
+
this.translateY = 0;
|
|
4418
|
+
this.isDragging = false;
|
|
4416
4419
|
this.error = '';
|
|
4417
4420
|
this.containerMinHeight = '';
|
|
4418
4421
|
this.svgMarkup = '';
|
|
@@ -4422,6 +4425,31 @@ class InfographicBlockNodeComponent {
|
|
|
4422
4425
|
this.renderScheduled = false;
|
|
4423
4426
|
this.copyTimer = null;
|
|
4424
4427
|
this.infographicInstance = null;
|
|
4428
|
+
// Drag functionality. The gesture lifecycle (window listeners, pointer identity,
|
|
4429
|
+
// lost releases) lives in markstream-core so every package pans identically.
|
|
4430
|
+
this.panGesture = createPanGesture({
|
|
4431
|
+
getTranslate: () => ({ x: this.translateX, y: this.translateY }),
|
|
4432
|
+
setTranslate: (next) => {
|
|
4433
|
+
this.translateX = next.x;
|
|
4434
|
+
this.translateY = next.y;
|
|
4435
|
+
this.cdr.markForCheck();
|
|
4436
|
+
},
|
|
4437
|
+
canStart: (event) => {
|
|
4438
|
+
// Nothing to pan until the diagram exists. Without this the pending/error
|
|
4439
|
+
// source panel would be claimed by the gesture, and `preventDefault` would
|
|
4440
|
+
// take text selection away from it.
|
|
4441
|
+
if (!this.hasDiagramSvg())
|
|
4442
|
+
return false;
|
|
4443
|
+
// Touch pans only where the surface claims the gesture. Elsewhere the page
|
|
4444
|
+
// keeps the swipe for scrolling, and panning along with it would shift the
|
|
4445
|
+
// diagram by a few pixels before the browser takes the gesture over.
|
|
4446
|
+
return event.pointerType !== 'touch' || this.panSurfaceClaimsTouch;
|
|
4447
|
+
},
|
|
4448
|
+
onActiveChange: (active) => {
|
|
4449
|
+
this.isDragging = active;
|
|
4450
|
+
this.cdr.markForCheck();
|
|
4451
|
+
},
|
|
4452
|
+
});
|
|
4425
4453
|
}
|
|
4426
4454
|
get mergedProps() {
|
|
4427
4455
|
return {
|
|
@@ -4508,8 +4536,31 @@ class InfographicBlockNodeComponent {
|
|
|
4508
4536
|
get resolvedProgressiveRender() {
|
|
4509
4537
|
return this.mergedProps.progressiveRender === true;
|
|
4510
4538
|
}
|
|
4539
|
+
/**
|
|
4540
|
+
* The diagram is scaled to fit the preview box, so at fit zoom nothing is
|
|
4541
|
+
* off-screen and panning only shifts it inside the box. Touch therefore keeps
|
|
4542
|
+
* scrolling the page at that zoom (a chat is mostly scrolling) and only claims
|
|
4543
|
+
* the gesture once the diagram is zoomed in and actually overflows the box.
|
|
4544
|
+
* The fullscreen modal always claims it: the surface fills the viewport, panning
|
|
4545
|
+
* is the intended gesture there, and the page behind it is locked anyway.
|
|
4546
|
+
*/
|
|
4547
|
+
get panSurfaceClaimsTouch() {
|
|
4548
|
+
return this.modalOpen || this.zoom > 1;
|
|
4549
|
+
}
|
|
4550
|
+
get previewTransform() {
|
|
4551
|
+
return `translate(${this.translateX}px, ${this.translateY}px) scale(${this.zoom})`;
|
|
4552
|
+
}
|
|
4511
4553
|
get modalTransform() {
|
|
4512
|
-
return `scale(${this.zoom})`;
|
|
4554
|
+
return `translate(${this.translateX}px, ${this.translateY}px) scale(${this.zoom})`;
|
|
4555
|
+
}
|
|
4556
|
+
/**
|
|
4557
|
+
* Whether a pan surface holds a rendered diagram. The modal renders its own
|
|
4558
|
+
* copy of the SVG, so it is checked as well: the source panel may have taken
|
|
4559
|
+
* the inline preview's place while the modal is open.
|
|
4560
|
+
*/
|
|
4561
|
+
hasDiagramSvg() {
|
|
4562
|
+
return !!this.previewHost?.nativeElement?.querySelector('svg')
|
|
4563
|
+
|| !!this.modalHost?.nativeElement?.querySelector('svg');
|
|
4513
4564
|
}
|
|
4514
4565
|
ngAfterViewInit() {
|
|
4515
4566
|
this.viewReady = true;
|
|
@@ -4525,6 +4576,7 @@ class InfographicBlockNodeComponent {
|
|
|
4525
4576
|
this.destroyed = true;
|
|
4526
4577
|
this.renderToken += 1;
|
|
4527
4578
|
this.renderScheduled = false;
|
|
4579
|
+
this.panGesture.stop();
|
|
4528
4580
|
if (this.copyTimer != null && typeof window !== 'undefined')
|
|
4529
4581
|
window.clearTimeout(this.copyTimer);
|
|
4530
4582
|
this.destroyInfographic();
|
|
@@ -4591,8 +4643,13 @@ class InfographicBlockNodeComponent {
|
|
|
4591
4643
|
}
|
|
4592
4644
|
resetZoom() {
|
|
4593
4645
|
this.zoom = 1;
|
|
4646
|
+
this.translateX = 0;
|
|
4647
|
+
this.translateY = 0;
|
|
4594
4648
|
this.cdr.markForCheck();
|
|
4595
4649
|
}
|
|
4650
|
+
onPanStart(event) {
|
|
4651
|
+
this.panGesture.start(event);
|
|
4652
|
+
}
|
|
4596
4653
|
scheduleInfographicRender() {
|
|
4597
4654
|
if (this.destroyed || this.renderScheduled)
|
|
4598
4655
|
return;
|
|
@@ -4831,6 +4888,10 @@ class InfographicBlockNodeComponent {
|
|
|
4831
4888
|
#previewHost
|
|
4832
4889
|
class="infographic-render"
|
|
4833
4890
|
[style.minHeight]="resolvedContainerMinHeight"
|
|
4891
|
+
[style.transform]="previewTransform"
|
|
4892
|
+
[style.touch-action]="panSurfaceClaimsTouch ? 'none' : null"
|
|
4893
|
+
[style.cursor]="isDragging ? 'grabbing' : 'grab'"
|
|
4894
|
+
(pointerdown)="onPanStart($event)"
|
|
4834
4895
|
>
|
|
4835
4896
|
<pre *ngIf="!svgMarkup && !error" class="markstream-angular-enhanced-block__source infographic-pending-source"><code translate="no">{{ code }}</code></pre>
|
|
4836
4897
|
</div>
|
|
@@ -4860,7 +4921,13 @@ class InfographicBlockNodeComponent {
|
|
|
4860
4921
|
<button type="button" class="mermaid-modal-close" (click)="closeModal()">Close</button>
|
|
4861
4922
|
</div>
|
|
4862
4923
|
<div class="mermaid-modal-body">
|
|
4863
|
-
<div
|
|
4924
|
+
<div
|
|
4925
|
+
class="mermaid-modal-content"
|
|
4926
|
+
[style.transform]="modalTransform"
|
|
4927
|
+
[style.touch-action]="panSurfaceClaimsTouch ? 'none' : null"
|
|
4928
|
+
[style.cursor]="isDragging ? 'grabbing' : 'grab'"
|
|
4929
|
+
(pointerdown)="onPanStart($event)"
|
|
4930
|
+
>
|
|
4864
4931
|
<div #modalHost class="fullscreen"></div>
|
|
4865
4932
|
</div>
|
|
4866
4933
|
</div>
|
|
@@ -4966,6 +5033,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImpo
|
|
|
4966
5033
|
#previewHost
|
|
4967
5034
|
class="infographic-render"
|
|
4968
5035
|
[style.minHeight]="resolvedContainerMinHeight"
|
|
5036
|
+
[style.transform]="previewTransform"
|
|
5037
|
+
[style.touch-action]="panSurfaceClaimsTouch ? 'none' : null"
|
|
5038
|
+
[style.cursor]="isDragging ? 'grabbing' : 'grab'"
|
|
5039
|
+
(pointerdown)="onPanStart($event)"
|
|
4969
5040
|
>
|
|
4970
5041
|
<pre *ngIf="!svgMarkup && !error" class="markstream-angular-enhanced-block__source infographic-pending-source"><code translate="no">{{ code }}</code></pre>
|
|
4971
5042
|
</div>
|
|
@@ -4995,7 +5066,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImpo
|
|
|
4995
5066
|
<button type="button" class="mermaid-modal-close" (click)="closeModal()">Close</button>
|
|
4996
5067
|
</div>
|
|
4997
5068
|
<div class="mermaid-modal-body">
|
|
4998
|
-
<div
|
|
5069
|
+
<div
|
|
5070
|
+
class="mermaid-modal-content"
|
|
5071
|
+
[style.transform]="modalTransform"
|
|
5072
|
+
[style.touch-action]="panSurfaceClaimsTouch ? 'none' : null"
|
|
5073
|
+
[style.cursor]="isDragging ? 'grabbing' : 'grab'"
|
|
5074
|
+
(pointerdown)="onPanStart($event)"
|
|
5075
|
+
>
|
|
4999
5076
|
<div #modalHost class="fullscreen"></div>
|
|
5000
5077
|
</div>
|
|
5001
5078
|
</div>
|
|
@@ -6791,6 +6868,9 @@ class MermaidBlockNodeComponent {
|
|
|
6791
6868
|
this.showSource = false;
|
|
6792
6869
|
this.modalOpen = false;
|
|
6793
6870
|
this.zoom = 1;
|
|
6871
|
+
this.translateX = 0;
|
|
6872
|
+
this.translateY = 0;
|
|
6873
|
+
this.isDragging = false;
|
|
6794
6874
|
this.svgMarkup = '';
|
|
6795
6875
|
this.error = '';
|
|
6796
6876
|
this.viewReady = false;
|
|
@@ -6798,6 +6878,31 @@ class MermaidBlockNodeComponent {
|
|
|
6798
6878
|
this.renderToken = 0;
|
|
6799
6879
|
this.copyTimer = null;
|
|
6800
6880
|
this.lastMermaidBindFunctions = null;
|
|
6881
|
+
// Drag functionality. The gesture lifecycle (window listeners, pointer identity,
|
|
6882
|
+
// lost releases) lives in markstream-core so every package pans identically.
|
|
6883
|
+
this.panGesture = createPanGesture({
|
|
6884
|
+
getTranslate: () => ({ x: this.translateX, y: this.translateY }),
|
|
6885
|
+
setTranslate: (next) => {
|
|
6886
|
+
this.translateX = next.x;
|
|
6887
|
+
this.translateY = next.y;
|
|
6888
|
+
this.cdr.markForCheck();
|
|
6889
|
+
},
|
|
6890
|
+
canStart: (event) => {
|
|
6891
|
+
// Nothing to pan until the diagram exists. Without this the error message the
|
|
6892
|
+
// block renders in the preview would be claimed by the gesture, and
|
|
6893
|
+
// `preventDefault` would take text selection away from it.
|
|
6894
|
+
if (!this.hasDiagramSvg())
|
|
6895
|
+
return false;
|
|
6896
|
+
// Touch pans only where the surface claims the gesture. Elsewhere the page
|
|
6897
|
+
// keeps the swipe for scrolling, and panning along with it would shift the
|
|
6898
|
+
// diagram by a few pixels before the browser takes the gesture over.
|
|
6899
|
+
return event.pointerType !== 'touch' || this.panSurfaceClaimsTouch;
|
|
6900
|
+
},
|
|
6901
|
+
onActiveChange: (active) => {
|
|
6902
|
+
this.isDragging = active;
|
|
6903
|
+
this.cdr.markForCheck();
|
|
6904
|
+
},
|
|
6905
|
+
});
|
|
6801
6906
|
}
|
|
6802
6907
|
get mergedProps() {
|
|
6803
6908
|
return {
|
|
@@ -6888,11 +6993,31 @@ class MermaidBlockNodeComponent {
|
|
|
6888
6993
|
get resolvedProgressiveRender() {
|
|
6889
6994
|
return this.mergedProps.progressiveRender === true;
|
|
6890
6995
|
}
|
|
6996
|
+
/**
|
|
6997
|
+
* The diagram is scaled to fit the preview box, so at fit zoom nothing is
|
|
6998
|
+
* off-screen and panning only shifts it inside the box. Touch therefore keeps
|
|
6999
|
+
* scrolling the page at that zoom (a chat is mostly scrolling) and only claims
|
|
7000
|
+
* the gesture once the diagram is zoomed in and actually overflows the box.
|
|
7001
|
+
* The fullscreen modal always claims it: the surface fills the viewport, panning
|
|
7002
|
+
* is the intended gesture there, and the page behind it is locked anyway.
|
|
7003
|
+
*/
|
|
7004
|
+
get panSurfaceClaimsTouch() {
|
|
7005
|
+
return this.modalOpen || this.zoom > 1;
|
|
7006
|
+
}
|
|
6891
7007
|
get previewTransform() {
|
|
6892
|
-
return `scale(${this.zoom})`;
|
|
7008
|
+
return `translate(${this.translateX}px, ${this.translateY}px) scale(${this.zoom})`;
|
|
6893
7009
|
}
|
|
6894
7010
|
get modalTransform() {
|
|
6895
|
-
return `scale(${Math.max(1, this.zoom)})`;
|
|
7011
|
+
return `translate(${this.translateX}px, ${this.translateY}px) scale(${Math.max(1, this.zoom)})`;
|
|
7012
|
+
}
|
|
7013
|
+
/**
|
|
7014
|
+
* Whether a pan surface holds a rendered diagram. The modal renders its own
|
|
7015
|
+
* copy of the SVG, so it is checked as well: the source panel may have taken
|
|
7016
|
+
* the inline preview's place while the modal is open.
|
|
7017
|
+
*/
|
|
7018
|
+
hasDiagramSvg() {
|
|
7019
|
+
return !!this.previewHost?.nativeElement?.querySelector('svg')
|
|
7020
|
+
|| !!this.modalHost?.nativeElement?.querySelector('svg');
|
|
6896
7021
|
}
|
|
6897
7022
|
ngAfterViewInit() {
|
|
6898
7023
|
this.viewReady = true;
|
|
@@ -6906,6 +7031,7 @@ class MermaidBlockNodeComponent {
|
|
|
6906
7031
|
ngOnDestroy() {
|
|
6907
7032
|
this.destroyed = true;
|
|
6908
7033
|
this.renderToken += 1;
|
|
7034
|
+
this.panGesture.stop();
|
|
6909
7035
|
if (this.copyTimer != null && typeof window !== 'undefined')
|
|
6910
7036
|
window.clearTimeout(this.copyTimer);
|
|
6911
7037
|
}
|
|
@@ -6968,8 +7094,13 @@ class MermaidBlockNodeComponent {
|
|
|
6968
7094
|
}
|
|
6969
7095
|
resetZoom() {
|
|
6970
7096
|
this.zoom = 1;
|
|
7097
|
+
this.translateX = 0;
|
|
7098
|
+
this.translateY = 0;
|
|
6971
7099
|
this.cdr.markForCheck();
|
|
6972
7100
|
}
|
|
7101
|
+
onPanStart(event) {
|
|
7102
|
+
this.panGesture.start(event);
|
|
7103
|
+
}
|
|
6973
7104
|
handleModalWheel(event) {
|
|
6974
7105
|
if (this.mergedProps.enableWheelZoom !== true)
|
|
6975
7106
|
return;
|
|
@@ -7371,6 +7502,9 @@ class MermaidBlockNodeComponent {
|
|
|
7371
7502
|
[style.minHeight.px]="estimatedPreviewHeightPx"
|
|
7372
7503
|
[style.height.px]="estimatedPreviewHeightPx"
|
|
7373
7504
|
[style.transform]="previewTransform"
|
|
7505
|
+
[style.touch-action]="panSurfaceClaimsTouch ? 'none' : null"
|
|
7506
|
+
[style.cursor]="isDragging ? 'grabbing' : 'grab'"
|
|
7507
|
+
(pointerdown)="onPanStart($event)"
|
|
7374
7508
|
>
|
|
7375
7509
|
<div #previewHost class="markstream-angular-mermaid" [class.is-empty]="!svgMarkup"></div>
|
|
7376
7510
|
</div>
|
|
@@ -7403,6 +7537,9 @@ class MermaidBlockNodeComponent {
|
|
|
7403
7537
|
<div
|
|
7404
7538
|
class="mermaid-modal-content"
|
|
7405
7539
|
[style.transform]="modalTransform"
|
|
7540
|
+
[style.touch-action]="panSurfaceClaimsTouch ? 'none' : null"
|
|
7541
|
+
[style.cursor]="isDragging ? 'grabbing' : 'grab'"
|
|
7542
|
+
(pointerdown)="onPanStart($event)"
|
|
7406
7543
|
(wheel)="handleModalWheel($event)"
|
|
7407
7544
|
>
|
|
7408
7545
|
<div #modalHost class="markstream-angular-mermaid fullscreen"></div>
|
|
@@ -7516,6 +7653,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImpo
|
|
|
7516
7653
|
[style.minHeight.px]="estimatedPreviewHeightPx"
|
|
7517
7654
|
[style.height.px]="estimatedPreviewHeightPx"
|
|
7518
7655
|
[style.transform]="previewTransform"
|
|
7656
|
+
[style.touch-action]="panSurfaceClaimsTouch ? 'none' : null"
|
|
7657
|
+
[style.cursor]="isDragging ? 'grabbing' : 'grab'"
|
|
7658
|
+
(pointerdown)="onPanStart($event)"
|
|
7519
7659
|
>
|
|
7520
7660
|
<div #previewHost class="markstream-angular-mermaid" [class.is-empty]="!svgMarkup"></div>
|
|
7521
7661
|
</div>
|
|
@@ -7548,6 +7688,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.26", ngImpo
|
|
|
7548
7688
|
<div
|
|
7549
7689
|
class="mermaid-modal-content"
|
|
7550
7690
|
[style.transform]="modalTransform"
|
|
7691
|
+
[style.touch-action]="panSurfaceClaimsTouch ? 'none' : null"
|
|
7692
|
+
[style.cursor]="isDragging ? 'grabbing' : 'grab'"
|
|
7693
|
+
(pointerdown)="onPanStart($event)"
|
|
7551
7694
|
(wheel)="handleModalWheel($event)"
|
|
7552
7695
|
>
|
|
7553
7696
|
<div #modalHost class="markstream-angular-mermaid fullscreen"></div>
|