@stocksharp/diagram 1.0.1 → 1.2.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.
- package/README.md +8 -0
- package/dist/esm/diagram/stocksharp-diagram.js +76 -0
- package/dist/esm/diagram/stocksharp-diagram.js.map +1 -1
- package/dist/esm/embed.js +10 -0
- package/dist/esm/embed.js.map +1 -1
- package/dist/esm/headless.js +111 -0
- package/dist/esm/headless.js.map +1 -0
- package/dist/esm/i18n.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/ssdiagram-headless.js +4176 -0
- package/dist/ssdiagram-headless.js.map +7 -0
- package/dist/ssdiagram.js +516 -329
- package/dist/ssdiagram.js.map +4 -4
- package/dist/types/diagram/api.d.ts +7 -0
- package/dist/types/diagram/api.d.ts.map +1 -1
- package/dist/types/diagram/stocksharp-diagram.d.ts +10 -0
- package/dist/types/diagram/stocksharp-diagram.d.ts.map +1 -1
- package/dist/types/embed.d.ts +6 -1
- package/dist/types/embed.d.ts.map +1 -1
- package/dist/types/headless.d.ts +24 -0
- package/dist/types/headless.d.ts.map +1 -0
- package/dist/types/i18n.d.ts +1 -0
- package/dist/types/i18n.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/diagram/api.ts +7 -0
- package/src/diagram/stocksharp-diagram.ts +83 -0
- package/src/embed.ts +20 -1
- package/src/headless.ts +139 -0
- package/src/i18n.ts +1 -0
- package/src/index.ts +3 -0
package/dist/ssdiagram.js
CHANGED
|
@@ -52,6 +52,7 @@ var SSDiagram = (() => {
|
|
|
52
52
|
parseDiagramDocument: () => parseDiagramDocument,
|
|
53
53
|
parseDiagramViewState: () => parseDiagramViewState,
|
|
54
54
|
renderAll: () => renderAll,
|
|
55
|
+
renderDiagramSvg: () => renderDiagramSvg,
|
|
55
56
|
renderFromInline: () => renderFromInline,
|
|
56
57
|
renderFromSource: () => renderFromSource,
|
|
57
58
|
renderScheme: () => renderScheme,
|
|
@@ -471,334 +472,6 @@ var SSDiagram = (() => {
|
|
|
471
472
|
return value;
|
|
472
473
|
}
|
|
473
474
|
|
|
474
|
-
// src/core/action-registry.ts
|
|
475
|
-
var DiagramActionRegistry = class {
|
|
476
|
-
constructor() {
|
|
477
|
-
this.actions = /* @__PURE__ */ new Map();
|
|
478
|
-
}
|
|
479
|
-
register(action) {
|
|
480
|
-
if (this.actions.has(action.id)) {
|
|
481
|
-
throw new Error(`Diagram action "${action.id}" is already registered.`);
|
|
482
|
-
}
|
|
483
|
-
this.actions.set(action.id, action);
|
|
484
|
-
return () => {
|
|
485
|
-
if (this.actions.get(action.id) === action) this.actions.delete(action.id);
|
|
486
|
-
};
|
|
487
|
-
}
|
|
488
|
-
/**
|
|
489
|
-
* Registers one action per id from a table keyed by TId, in key order.
|
|
490
|
-
* Because the table is a total Record, adding a member to TId without adding
|
|
491
|
-
* its action stops compiling instead of producing a command that silently
|
|
492
|
-
* does nothing.
|
|
493
|
-
*/
|
|
494
|
-
registerAll(actions) {
|
|
495
|
-
const ids = Object.keys(actions);
|
|
496
|
-
const disposers = ids.map((id) => {
|
|
497
|
-
const action = actions[id];
|
|
498
|
-
return this.register({
|
|
499
|
-
id,
|
|
500
|
-
canExecute: (context) => action.canExecute(context),
|
|
501
|
-
execute: (context) => action.execute(context)
|
|
502
|
-
});
|
|
503
|
-
});
|
|
504
|
-
return () => disposers.forEach((dispose) => dispose());
|
|
505
|
-
}
|
|
506
|
-
get(id) {
|
|
507
|
-
return this.actions.get(id) ?? null;
|
|
508
|
-
}
|
|
509
|
-
states(context) {
|
|
510
|
-
return [...this.actions.values()].map((action) => ({
|
|
511
|
-
id: action.id,
|
|
512
|
-
enabled: action.canExecute(context)
|
|
513
|
-
}));
|
|
514
|
-
}
|
|
515
|
-
canExecute(id, context) {
|
|
516
|
-
return this.actions.get(id)?.canExecute(context) ?? false;
|
|
517
|
-
}
|
|
518
|
-
execute(id, context) {
|
|
519
|
-
const action = this.actions.get(id);
|
|
520
|
-
if (action === void 0 || !action.canExecute(context)) return false;
|
|
521
|
-
action.execute(context);
|
|
522
|
-
return true;
|
|
523
|
-
}
|
|
524
|
-
};
|
|
525
|
-
|
|
526
|
-
// src/core/history.ts
|
|
527
|
-
var DiagramCommandHistory = class {
|
|
528
|
-
constructor(listener) {
|
|
529
|
-
this.listener = listener;
|
|
530
|
-
this.undoCommands = [];
|
|
531
|
-
this.redoCommands = [];
|
|
532
|
-
this.transactions = [];
|
|
533
|
-
this.replaying = false;
|
|
534
|
-
}
|
|
535
|
-
get state() {
|
|
536
|
-
return {
|
|
537
|
-
canUndo: this.undoCommands.length > 0,
|
|
538
|
-
canRedo: this.redoCommands.length > 0,
|
|
539
|
-
undoDepth: this.undoCommands.length,
|
|
540
|
-
redoDepth: this.redoCommands.length,
|
|
541
|
-
undoLabel: this.undoCommands[this.undoCommands.length - 1]?.label ?? null,
|
|
542
|
-
redoLabel: this.redoCommands[this.redoCommands.length - 1]?.label ?? null
|
|
543
|
-
};
|
|
544
|
-
}
|
|
545
|
-
execute(command) {
|
|
546
|
-
if (this.replaying) {
|
|
547
|
-
command.execute();
|
|
548
|
-
return;
|
|
549
|
-
}
|
|
550
|
-
command.execute();
|
|
551
|
-
this.recordApplied(command);
|
|
552
|
-
}
|
|
553
|
-
/** Records a gesture that already changed the document, such as pointer drag. */
|
|
554
|
-
recordApplied(command) {
|
|
555
|
-
if (this.replaying) return;
|
|
556
|
-
const transaction = this.transactions[this.transactions.length - 1];
|
|
557
|
-
if (transaction !== void 0) {
|
|
558
|
-
transaction.commands.push(command);
|
|
559
|
-
return;
|
|
560
|
-
}
|
|
561
|
-
this.undoCommands.push(command);
|
|
562
|
-
this.redoCommands.length = 0;
|
|
563
|
-
this.notify();
|
|
564
|
-
}
|
|
565
|
-
transaction(label2, action) {
|
|
566
|
-
const frame = { label: label2, commands: [] };
|
|
567
|
-
this.transactions.push(frame);
|
|
568
|
-
try {
|
|
569
|
-
const result = action();
|
|
570
|
-
this.transactions.pop();
|
|
571
|
-
const command = compose(frame);
|
|
572
|
-
if (command !== null) this.recordApplied(command);
|
|
573
|
-
return result;
|
|
574
|
-
} catch (error) {
|
|
575
|
-
this.transactions.pop();
|
|
576
|
-
this.replay(() => {
|
|
577
|
-
for (const command of frame.commands.slice().reverse()) command.undo();
|
|
578
|
-
});
|
|
579
|
-
throw error;
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
assertNoOpenTransaction(operation) {
|
|
583
|
-
if (this.transactions.length > 0) {
|
|
584
|
-
throw new Error(`Cannot ${operation} command history inside a transaction.`);
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
undo() {
|
|
588
|
-
this.assertNoOpenTransaction("undo");
|
|
589
|
-
const command = this.undoCommands.pop();
|
|
590
|
-
if (command === void 0) return false;
|
|
591
|
-
try {
|
|
592
|
-
this.replay(() => command.undo());
|
|
593
|
-
} catch (error) {
|
|
594
|
-
this.undoCommands.push(command);
|
|
595
|
-
throw error;
|
|
596
|
-
}
|
|
597
|
-
this.redoCommands.push(command);
|
|
598
|
-
this.notify();
|
|
599
|
-
return true;
|
|
600
|
-
}
|
|
601
|
-
redo() {
|
|
602
|
-
this.assertNoOpenTransaction("redo");
|
|
603
|
-
const command = this.redoCommands.pop();
|
|
604
|
-
if (command === void 0) return false;
|
|
605
|
-
try {
|
|
606
|
-
this.replay(() => command.execute());
|
|
607
|
-
} catch (error) {
|
|
608
|
-
this.redoCommands.push(command);
|
|
609
|
-
throw error;
|
|
610
|
-
}
|
|
611
|
-
this.undoCommands.push(command);
|
|
612
|
-
this.notify();
|
|
613
|
-
return true;
|
|
614
|
-
}
|
|
615
|
-
clear() {
|
|
616
|
-
this.assertNoOpenTransaction("clear");
|
|
617
|
-
this.undoCommands.length = 0;
|
|
618
|
-
this.redoCommands.length = 0;
|
|
619
|
-
this.notify();
|
|
620
|
-
}
|
|
621
|
-
replay(action) {
|
|
622
|
-
const previous = this.replaying;
|
|
623
|
-
this.replaying = true;
|
|
624
|
-
try {
|
|
625
|
-
action();
|
|
626
|
-
} finally {
|
|
627
|
-
this.replaying = previous;
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
notify() {
|
|
631
|
-
this.listener?.(this.state);
|
|
632
|
-
}
|
|
633
|
-
};
|
|
634
|
-
function compose(frame) {
|
|
635
|
-
if (frame.commands.length === 0) return null;
|
|
636
|
-
if (frame.commands.length === 1) {
|
|
637
|
-
const command = frame.commands[0];
|
|
638
|
-
return frame.label === command.label ? command : {
|
|
639
|
-
label: frame.label,
|
|
640
|
-
execute: () => command.execute(),
|
|
641
|
-
undo: () => command.undo()
|
|
642
|
-
};
|
|
643
|
-
}
|
|
644
|
-
const commands = [...frame.commands];
|
|
645
|
-
return {
|
|
646
|
-
label: frame.label,
|
|
647
|
-
execute: () => {
|
|
648
|
-
for (const command of commands) command.execute();
|
|
649
|
-
},
|
|
650
|
-
undo: () => {
|
|
651
|
-
for (const command of commands.slice().reverse()) command.undo();
|
|
652
|
-
}
|
|
653
|
-
};
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
// src/core/state.ts
|
|
657
|
-
function createDiagramRuntimeState() {
|
|
658
|
-
return { activeNodeId: null, nodes: {}, globalError: null };
|
|
659
|
-
}
|
|
660
|
-
function cloneDiagramRuntimeState(state) {
|
|
661
|
-
return {
|
|
662
|
-
activeNodeId: state.activeNodeId,
|
|
663
|
-
globalError: state.globalError === null ? null : { ...state.globalError },
|
|
664
|
-
nodes: Object.fromEntries(Object.entries(state.nodes).map(([nodeId, node]) => [nodeId, {
|
|
665
|
-
active: node.active,
|
|
666
|
-
errors: cloneDiagramNodeErrors(node.errors),
|
|
667
|
-
ports: {
|
|
668
|
-
in: Object.fromEntries(Object.entries(node.ports.in).map(([portId, port]) => [portId, { ...port }])),
|
|
669
|
-
out: Object.fromEntries(Object.entries(node.ports.out).map(([portId, port]) => [portId, { ...port }]))
|
|
670
|
-
}
|
|
671
|
-
}]))
|
|
672
|
-
};
|
|
673
|
-
}
|
|
674
|
-
function createDiagramViewState() {
|
|
675
|
-
return { zoom: 1, panX: 0, panY: 0, overviewVisible: true };
|
|
676
|
-
}
|
|
677
|
-
function createDiagramSelection() {
|
|
678
|
-
return {
|
|
679
|
-
nodeIds: [],
|
|
680
|
-
linkIds: [],
|
|
681
|
-
port: null,
|
|
682
|
-
primaryNodeId: null,
|
|
683
|
-
primaryLinkId: null
|
|
684
|
-
};
|
|
685
|
-
}
|
|
686
|
-
function createDiagramPortRuntimeState() {
|
|
687
|
-
return {
|
|
688
|
-
active: false,
|
|
689
|
-
selected: false,
|
|
690
|
-
breakpoint: false,
|
|
691
|
-
breakpointActive: false,
|
|
692
|
-
value: null,
|
|
693
|
-
error: null
|
|
694
|
-
};
|
|
695
|
-
}
|
|
696
|
-
function createEditableDiagramPermissions() {
|
|
697
|
-
return {
|
|
698
|
-
select: true,
|
|
699
|
-
inspect: true,
|
|
700
|
-
copy: true,
|
|
701
|
-
moveNodes: true,
|
|
702
|
-
createLinks: true,
|
|
703
|
-
deleteSelection: true,
|
|
704
|
-
paste: true,
|
|
705
|
-
history: true
|
|
706
|
-
};
|
|
707
|
-
}
|
|
708
|
-
function createReadOnlyDiagramPermissions() {
|
|
709
|
-
return {
|
|
710
|
-
select: true,
|
|
711
|
-
inspect: true,
|
|
712
|
-
copy: true,
|
|
713
|
-
moveNodes: false,
|
|
714
|
-
createLinks: false,
|
|
715
|
-
deleteSelection: false,
|
|
716
|
-
paste: false,
|
|
717
|
-
history: false
|
|
718
|
-
};
|
|
719
|
-
}
|
|
720
|
-
function createDiagramNodeRuntimeState() {
|
|
721
|
-
return {
|
|
722
|
-
active: false,
|
|
723
|
-
errors: {},
|
|
724
|
-
ports: { in: {}, out: {} }
|
|
725
|
-
};
|
|
726
|
-
}
|
|
727
|
-
function cloneDiagramNodeErrors(errors) {
|
|
728
|
-
const result = {};
|
|
729
|
-
if (errors === void 0 || errors === null) return result;
|
|
730
|
-
if (errors.runtime !== void 0) result.runtime = { ...errors.runtime };
|
|
731
|
-
if (errors.load !== void 0) result.load = { ...errors.load };
|
|
732
|
-
return result;
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
// src/core/view-state.ts
|
|
736
|
-
var DIAGRAM_VIEW_STATE_VERSION = 1;
|
|
737
|
-
var DiagramViewStateError = class extends Error {
|
|
738
|
-
constructor(message, path = "$") {
|
|
739
|
-
super(`${path}: ${message}`);
|
|
740
|
-
this.path = path;
|
|
741
|
-
this.name = "DiagramViewStateError";
|
|
742
|
-
}
|
|
743
|
-
};
|
|
744
|
-
function createDiagramViewStateDocument(view = createDiagramViewState()) {
|
|
745
|
-
return {
|
|
746
|
-
version: DIAGRAM_VIEW_STATE_VERSION,
|
|
747
|
-
view: normalizeView(view)
|
|
748
|
-
};
|
|
749
|
-
}
|
|
750
|
-
function parseDiagramViewState(source) {
|
|
751
|
-
let value = source;
|
|
752
|
-
if (typeof source === "string") {
|
|
753
|
-
try {
|
|
754
|
-
value = JSON.parse(source);
|
|
755
|
-
} catch (error) {
|
|
756
|
-
const reason = error instanceof Error ? error.message : "invalid JSON";
|
|
757
|
-
throw new DiagramViewStateError(reason);
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
const root = requireObject2(value, "$");
|
|
761
|
-
const version = requireFiniteNumber2(root.version, "$.version");
|
|
762
|
-
if (version !== DIAGRAM_VIEW_STATE_VERSION) {
|
|
763
|
-
throw new DiagramViewStateError(`unsupported view-state version ${version}`, "$.version");
|
|
764
|
-
}
|
|
765
|
-
return normalizeView(requireObject2(root.view, "$.view"));
|
|
766
|
-
}
|
|
767
|
-
function serializeDiagramViewState(view, space) {
|
|
768
|
-
return JSON.stringify(createDiagramViewStateDocument(view), null, space);
|
|
769
|
-
}
|
|
770
|
-
function normalizeView(value) {
|
|
771
|
-
const view = requireObject2(value, "$.view");
|
|
772
|
-
const zoom = requireFiniteNumber2(view.zoom, "$.view.zoom");
|
|
773
|
-
if (zoom <= 0) throw new DiagramViewStateError("expected a positive number", "$.view.zoom");
|
|
774
|
-
return {
|
|
775
|
-
zoom,
|
|
776
|
-
panX: requireFiniteNumber2(view.panX, "$.view.panX"),
|
|
777
|
-
panY: requireFiniteNumber2(view.panY, "$.view.panY"),
|
|
778
|
-
overviewVisible: requireBoolean2(view.overviewVisible, "$.view.overviewVisible")
|
|
779
|
-
};
|
|
780
|
-
}
|
|
781
|
-
function requireObject2(value, path) {
|
|
782
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
783
|
-
throw new DiagramViewStateError("expected an object", path);
|
|
784
|
-
}
|
|
785
|
-
const prototype = Object.getPrototypeOf(value);
|
|
786
|
-
if (prototype !== Object.prototype && prototype !== null) {
|
|
787
|
-
throw new DiagramViewStateError("expected a plain object", path);
|
|
788
|
-
}
|
|
789
|
-
return value;
|
|
790
|
-
}
|
|
791
|
-
function requireFiniteNumber2(value, path) {
|
|
792
|
-
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
793
|
-
throw new DiagramViewStateError("expected a finite number", path);
|
|
794
|
-
}
|
|
795
|
-
return value;
|
|
796
|
-
}
|
|
797
|
-
function requireBoolean2(value, path) {
|
|
798
|
-
if (typeof value !== "boolean") throw new DiagramViewStateError("expected a boolean", path);
|
|
799
|
-
return value;
|
|
800
|
-
}
|
|
801
|
-
|
|
802
475
|
// src/svg-surface.ts
|
|
803
476
|
var IDENTITY = [1, 0, 0, 1, 0, 0];
|
|
804
477
|
function multiply(m, n2) {
|
|
@@ -1123,6 +796,215 @@ var SSDiagram = (() => {
|
|
|
1123
796
|
}
|
|
1124
797
|
};
|
|
1125
798
|
|
|
799
|
+
// src/core/history.ts
|
|
800
|
+
var DiagramCommandHistory = class {
|
|
801
|
+
constructor(listener) {
|
|
802
|
+
this.listener = listener;
|
|
803
|
+
this.undoCommands = [];
|
|
804
|
+
this.redoCommands = [];
|
|
805
|
+
this.transactions = [];
|
|
806
|
+
this.replaying = false;
|
|
807
|
+
}
|
|
808
|
+
get state() {
|
|
809
|
+
return {
|
|
810
|
+
canUndo: this.undoCommands.length > 0,
|
|
811
|
+
canRedo: this.redoCommands.length > 0,
|
|
812
|
+
undoDepth: this.undoCommands.length,
|
|
813
|
+
redoDepth: this.redoCommands.length,
|
|
814
|
+
undoLabel: this.undoCommands[this.undoCommands.length - 1]?.label ?? null,
|
|
815
|
+
redoLabel: this.redoCommands[this.redoCommands.length - 1]?.label ?? null
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
execute(command) {
|
|
819
|
+
if (this.replaying) {
|
|
820
|
+
command.execute();
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
command.execute();
|
|
824
|
+
this.recordApplied(command);
|
|
825
|
+
}
|
|
826
|
+
/** Records a gesture that already changed the document, such as pointer drag. */
|
|
827
|
+
recordApplied(command) {
|
|
828
|
+
if (this.replaying) return;
|
|
829
|
+
const transaction = this.transactions[this.transactions.length - 1];
|
|
830
|
+
if (transaction !== void 0) {
|
|
831
|
+
transaction.commands.push(command);
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
834
|
+
this.undoCommands.push(command);
|
|
835
|
+
this.redoCommands.length = 0;
|
|
836
|
+
this.notify();
|
|
837
|
+
}
|
|
838
|
+
transaction(label2, action) {
|
|
839
|
+
const frame = { label: label2, commands: [] };
|
|
840
|
+
this.transactions.push(frame);
|
|
841
|
+
try {
|
|
842
|
+
const result = action();
|
|
843
|
+
this.transactions.pop();
|
|
844
|
+
const command = compose(frame);
|
|
845
|
+
if (command !== null) this.recordApplied(command);
|
|
846
|
+
return result;
|
|
847
|
+
} catch (error) {
|
|
848
|
+
this.transactions.pop();
|
|
849
|
+
this.replay(() => {
|
|
850
|
+
for (const command of frame.commands.slice().reverse()) command.undo();
|
|
851
|
+
});
|
|
852
|
+
throw error;
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
assertNoOpenTransaction(operation) {
|
|
856
|
+
if (this.transactions.length > 0) {
|
|
857
|
+
throw new Error(`Cannot ${operation} command history inside a transaction.`);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
undo() {
|
|
861
|
+
this.assertNoOpenTransaction("undo");
|
|
862
|
+
const command = this.undoCommands.pop();
|
|
863
|
+
if (command === void 0) return false;
|
|
864
|
+
try {
|
|
865
|
+
this.replay(() => command.undo());
|
|
866
|
+
} catch (error) {
|
|
867
|
+
this.undoCommands.push(command);
|
|
868
|
+
throw error;
|
|
869
|
+
}
|
|
870
|
+
this.redoCommands.push(command);
|
|
871
|
+
this.notify();
|
|
872
|
+
return true;
|
|
873
|
+
}
|
|
874
|
+
redo() {
|
|
875
|
+
this.assertNoOpenTransaction("redo");
|
|
876
|
+
const command = this.redoCommands.pop();
|
|
877
|
+
if (command === void 0) return false;
|
|
878
|
+
try {
|
|
879
|
+
this.replay(() => command.execute());
|
|
880
|
+
} catch (error) {
|
|
881
|
+
this.redoCommands.push(command);
|
|
882
|
+
throw error;
|
|
883
|
+
}
|
|
884
|
+
this.undoCommands.push(command);
|
|
885
|
+
this.notify();
|
|
886
|
+
return true;
|
|
887
|
+
}
|
|
888
|
+
clear() {
|
|
889
|
+
this.assertNoOpenTransaction("clear");
|
|
890
|
+
this.undoCommands.length = 0;
|
|
891
|
+
this.redoCommands.length = 0;
|
|
892
|
+
this.notify();
|
|
893
|
+
}
|
|
894
|
+
replay(action) {
|
|
895
|
+
const previous = this.replaying;
|
|
896
|
+
this.replaying = true;
|
|
897
|
+
try {
|
|
898
|
+
action();
|
|
899
|
+
} finally {
|
|
900
|
+
this.replaying = previous;
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
notify() {
|
|
904
|
+
this.listener?.(this.state);
|
|
905
|
+
}
|
|
906
|
+
};
|
|
907
|
+
function compose(frame) {
|
|
908
|
+
if (frame.commands.length === 0) return null;
|
|
909
|
+
if (frame.commands.length === 1) {
|
|
910
|
+
const command = frame.commands[0];
|
|
911
|
+
return frame.label === command.label ? command : {
|
|
912
|
+
label: frame.label,
|
|
913
|
+
execute: () => command.execute(),
|
|
914
|
+
undo: () => command.undo()
|
|
915
|
+
};
|
|
916
|
+
}
|
|
917
|
+
const commands = [...frame.commands];
|
|
918
|
+
return {
|
|
919
|
+
label: frame.label,
|
|
920
|
+
execute: () => {
|
|
921
|
+
for (const command of commands) command.execute();
|
|
922
|
+
},
|
|
923
|
+
undo: () => {
|
|
924
|
+
for (const command of commands.slice().reverse()) command.undo();
|
|
925
|
+
}
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
// src/core/state.ts
|
|
930
|
+
function createDiagramRuntimeState() {
|
|
931
|
+
return { activeNodeId: null, nodes: {}, globalError: null };
|
|
932
|
+
}
|
|
933
|
+
function cloneDiagramRuntimeState(state) {
|
|
934
|
+
return {
|
|
935
|
+
activeNodeId: state.activeNodeId,
|
|
936
|
+
globalError: state.globalError === null ? null : { ...state.globalError },
|
|
937
|
+
nodes: Object.fromEntries(Object.entries(state.nodes).map(([nodeId, node]) => [nodeId, {
|
|
938
|
+
active: node.active,
|
|
939
|
+
errors: cloneDiagramNodeErrors(node.errors),
|
|
940
|
+
ports: {
|
|
941
|
+
in: Object.fromEntries(Object.entries(node.ports.in).map(([portId, port]) => [portId, { ...port }])),
|
|
942
|
+
out: Object.fromEntries(Object.entries(node.ports.out).map(([portId, port]) => [portId, { ...port }]))
|
|
943
|
+
}
|
|
944
|
+
}]))
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
function createDiagramViewState() {
|
|
948
|
+
return { zoom: 1, panX: 0, panY: 0, overviewVisible: true };
|
|
949
|
+
}
|
|
950
|
+
function createDiagramSelection() {
|
|
951
|
+
return {
|
|
952
|
+
nodeIds: [],
|
|
953
|
+
linkIds: [],
|
|
954
|
+
port: null,
|
|
955
|
+
primaryNodeId: null,
|
|
956
|
+
primaryLinkId: null
|
|
957
|
+
};
|
|
958
|
+
}
|
|
959
|
+
function createDiagramPortRuntimeState() {
|
|
960
|
+
return {
|
|
961
|
+
active: false,
|
|
962
|
+
selected: false,
|
|
963
|
+
breakpoint: false,
|
|
964
|
+
breakpointActive: false,
|
|
965
|
+
value: null,
|
|
966
|
+
error: null
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
function createEditableDiagramPermissions() {
|
|
970
|
+
return {
|
|
971
|
+
select: true,
|
|
972
|
+
inspect: true,
|
|
973
|
+
copy: true,
|
|
974
|
+
moveNodes: true,
|
|
975
|
+
createLinks: true,
|
|
976
|
+
deleteSelection: true,
|
|
977
|
+
paste: true,
|
|
978
|
+
history: true
|
|
979
|
+
};
|
|
980
|
+
}
|
|
981
|
+
function createReadOnlyDiagramPermissions() {
|
|
982
|
+
return {
|
|
983
|
+
select: true,
|
|
984
|
+
inspect: true,
|
|
985
|
+
copy: true,
|
|
986
|
+
moveNodes: false,
|
|
987
|
+
createLinks: false,
|
|
988
|
+
deleteSelection: false,
|
|
989
|
+
paste: false,
|
|
990
|
+
history: false
|
|
991
|
+
};
|
|
992
|
+
}
|
|
993
|
+
function createDiagramNodeRuntimeState() {
|
|
994
|
+
return {
|
|
995
|
+
active: false,
|
|
996
|
+
errors: {},
|
|
997
|
+
ports: { in: {}, out: {} }
|
|
998
|
+
};
|
|
999
|
+
}
|
|
1000
|
+
function cloneDiagramNodeErrors(errors) {
|
|
1001
|
+
const result = {};
|
|
1002
|
+
if (errors === void 0 || errors === null) return result;
|
|
1003
|
+
if (errors.runtime !== void 0) result.runtime = { ...errors.runtime };
|
|
1004
|
+
if (errors.load !== void 0) result.load = { ...errors.load };
|
|
1005
|
+
return result;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1126
1008
|
// src/canvas-renderer.ts
|
|
1127
1009
|
var SCREEN_RENDER_OPTIONS = {
|
|
1128
1010
|
grid: true,
|
|
@@ -4237,6 +4119,232 @@ ${runtime.error}`;
|
|
|
4237
4119
|
return Math.hypot(px - cx, py - cy);
|
|
4238
4120
|
}
|
|
4239
4121
|
|
|
4122
|
+
// src/headless.ts
|
|
4123
|
+
function createContext() {
|
|
4124
|
+
const own = {
|
|
4125
|
+
globalAlpha: 1,
|
|
4126
|
+
measureText: (text) => ({ width: text.length * 7 })
|
|
4127
|
+
};
|
|
4128
|
+
return new Proxy(own, {
|
|
4129
|
+
get: (target, property) => property in target ? target[property] : () => void 0,
|
|
4130
|
+
set: (target, property, value) => {
|
|
4131
|
+
target[property] = value;
|
|
4132
|
+
return true;
|
|
4133
|
+
}
|
|
4134
|
+
});
|
|
4135
|
+
}
|
|
4136
|
+
var StubElement = class {
|
|
4137
|
+
constructor() {
|
|
4138
|
+
this.style = {};
|
|
4139
|
+
this.className = "";
|
|
4140
|
+
this.tabIndex = 0;
|
|
4141
|
+
this.width = 0;
|
|
4142
|
+
this.height = 0;
|
|
4143
|
+
this.textContent = "";
|
|
4144
|
+
this.children = [];
|
|
4145
|
+
this.classList = { toggle: () => false, add: () => void 0, remove: () => void 0 };
|
|
4146
|
+
this.context = createContext();
|
|
4147
|
+
}
|
|
4148
|
+
appendChild(child) {
|
|
4149
|
+
this.children.push(child);
|
|
4150
|
+
return child;
|
|
4151
|
+
}
|
|
4152
|
+
removeChild(child) {
|
|
4153
|
+
const at = this.children.indexOf(child);
|
|
4154
|
+
if (at >= 0) this.children.splice(at, 1);
|
|
4155
|
+
return child;
|
|
4156
|
+
}
|
|
4157
|
+
remove() {
|
|
4158
|
+
}
|
|
4159
|
+
setAttribute() {
|
|
4160
|
+
}
|
|
4161
|
+
focus() {
|
|
4162
|
+
}
|
|
4163
|
+
addEventListener() {
|
|
4164
|
+
}
|
|
4165
|
+
removeEventListener() {
|
|
4166
|
+
}
|
|
4167
|
+
getContext() {
|
|
4168
|
+
return this.context;
|
|
4169
|
+
}
|
|
4170
|
+
getBoundingClientRect() {
|
|
4171
|
+
return { left: 0, top: 0, right: this.width, bottom: this.height, width: this.width, height: this.height, x: 0, y: 0, toJSON: () => ({}) };
|
|
4172
|
+
}
|
|
4173
|
+
get clientWidth() {
|
|
4174
|
+
return this.width;
|
|
4175
|
+
}
|
|
4176
|
+
get clientHeight() {
|
|
4177
|
+
return this.height;
|
|
4178
|
+
}
|
|
4179
|
+
};
|
|
4180
|
+
var GLOBAL_KEYS = ["window", "document", "requestAnimationFrame", "cancelAnimationFrame", "Image", "getComputedStyle", "performance"];
|
|
4181
|
+
function installGlobals() {
|
|
4182
|
+
const target = globalThis;
|
|
4183
|
+
if (target.document !== void 0)
|
|
4184
|
+
return () => void 0;
|
|
4185
|
+
const before = /* @__PURE__ */ new Map();
|
|
4186
|
+
for (const key of GLOBAL_KEYS) before.set(key, target[key]);
|
|
4187
|
+
const stubDocument = Object.assign(new StubElement(), {
|
|
4188
|
+
documentElement: new StubElement(),
|
|
4189
|
+
createElement: () => new StubElement()
|
|
4190
|
+
});
|
|
4191
|
+
Object.assign(target, {
|
|
4192
|
+
window: Object.assign(new StubElement(), { devicePixelRatio: 1 }),
|
|
4193
|
+
document: stubDocument,
|
|
4194
|
+
requestAnimationFrame: () => 1,
|
|
4195
|
+
cancelAnimationFrame: () => void 0,
|
|
4196
|
+
Image: class {
|
|
4197
|
+
},
|
|
4198
|
+
getComputedStyle: () => ({ getPropertyValue: () => "" }),
|
|
4199
|
+
// A host without a browser may have no clock either -- an embedded JS engine has none. The drawing
|
|
4200
|
+
// reads it for animation, and a still picture has no animation to advance.
|
|
4201
|
+
performance: globalThis.performance ?? { now: () => 0 }
|
|
4202
|
+
});
|
|
4203
|
+
return () => {
|
|
4204
|
+
for (const [key, value] of before) {
|
|
4205
|
+
if (value === void 0) delete target[key];
|
|
4206
|
+
else target[key] = value;
|
|
4207
|
+
}
|
|
4208
|
+
};
|
|
4209
|
+
}
|
|
4210
|
+
function renderDiagramSvg(source, options = {}) {
|
|
4211
|
+
const { hostWidth = 1200, hostHeight = 800, ...screenshot } = options;
|
|
4212
|
+
const restore = installGlobals();
|
|
4213
|
+
try {
|
|
4214
|
+
const host = new StubElement();
|
|
4215
|
+
host.width = hostWidth;
|
|
4216
|
+
host.height = hostHeight;
|
|
4217
|
+
const diagram = new Diagram({ host });
|
|
4218
|
+
try {
|
|
4219
|
+
diagram.loadDocument(parseDiagramDocument(source));
|
|
4220
|
+
return diagram.takeSvg({ scope: "content", ...screenshot });
|
|
4221
|
+
} finally {
|
|
4222
|
+
diagram.destroy();
|
|
4223
|
+
}
|
|
4224
|
+
} finally {
|
|
4225
|
+
restore();
|
|
4226
|
+
}
|
|
4227
|
+
}
|
|
4228
|
+
|
|
4229
|
+
// src/core/action-registry.ts
|
|
4230
|
+
var DiagramActionRegistry = class {
|
|
4231
|
+
constructor() {
|
|
4232
|
+
this.actions = /* @__PURE__ */ new Map();
|
|
4233
|
+
}
|
|
4234
|
+
register(action) {
|
|
4235
|
+
if (this.actions.has(action.id)) {
|
|
4236
|
+
throw new Error(`Diagram action "${action.id}" is already registered.`);
|
|
4237
|
+
}
|
|
4238
|
+
this.actions.set(action.id, action);
|
|
4239
|
+
return () => {
|
|
4240
|
+
if (this.actions.get(action.id) === action) this.actions.delete(action.id);
|
|
4241
|
+
};
|
|
4242
|
+
}
|
|
4243
|
+
/**
|
|
4244
|
+
* Registers one action per id from a table keyed by TId, in key order.
|
|
4245
|
+
* Because the table is a total Record, adding a member to TId without adding
|
|
4246
|
+
* its action stops compiling instead of producing a command that silently
|
|
4247
|
+
* does nothing.
|
|
4248
|
+
*/
|
|
4249
|
+
registerAll(actions) {
|
|
4250
|
+
const ids = Object.keys(actions);
|
|
4251
|
+
const disposers = ids.map((id) => {
|
|
4252
|
+
const action = actions[id];
|
|
4253
|
+
return this.register({
|
|
4254
|
+
id,
|
|
4255
|
+
canExecute: (context) => action.canExecute(context),
|
|
4256
|
+
execute: (context) => action.execute(context)
|
|
4257
|
+
});
|
|
4258
|
+
});
|
|
4259
|
+
return () => disposers.forEach((dispose) => dispose());
|
|
4260
|
+
}
|
|
4261
|
+
get(id) {
|
|
4262
|
+
return this.actions.get(id) ?? null;
|
|
4263
|
+
}
|
|
4264
|
+
states(context) {
|
|
4265
|
+
return [...this.actions.values()].map((action) => ({
|
|
4266
|
+
id: action.id,
|
|
4267
|
+
enabled: action.canExecute(context)
|
|
4268
|
+
}));
|
|
4269
|
+
}
|
|
4270
|
+
canExecute(id, context) {
|
|
4271
|
+
return this.actions.get(id)?.canExecute(context) ?? false;
|
|
4272
|
+
}
|
|
4273
|
+
execute(id, context) {
|
|
4274
|
+
const action = this.actions.get(id);
|
|
4275
|
+
if (action === void 0 || !action.canExecute(context)) return false;
|
|
4276
|
+
action.execute(context);
|
|
4277
|
+
return true;
|
|
4278
|
+
}
|
|
4279
|
+
};
|
|
4280
|
+
|
|
4281
|
+
// src/core/view-state.ts
|
|
4282
|
+
var DIAGRAM_VIEW_STATE_VERSION = 1;
|
|
4283
|
+
var DiagramViewStateError = class extends Error {
|
|
4284
|
+
constructor(message, path = "$") {
|
|
4285
|
+
super(`${path}: ${message}`);
|
|
4286
|
+
this.path = path;
|
|
4287
|
+
this.name = "DiagramViewStateError";
|
|
4288
|
+
}
|
|
4289
|
+
};
|
|
4290
|
+
function createDiagramViewStateDocument(view = createDiagramViewState()) {
|
|
4291
|
+
return {
|
|
4292
|
+
version: DIAGRAM_VIEW_STATE_VERSION,
|
|
4293
|
+
view: normalizeView(view)
|
|
4294
|
+
};
|
|
4295
|
+
}
|
|
4296
|
+
function parseDiagramViewState(source) {
|
|
4297
|
+
let value = source;
|
|
4298
|
+
if (typeof source === "string") {
|
|
4299
|
+
try {
|
|
4300
|
+
value = JSON.parse(source);
|
|
4301
|
+
} catch (error) {
|
|
4302
|
+
const reason = error instanceof Error ? error.message : "invalid JSON";
|
|
4303
|
+
throw new DiagramViewStateError(reason);
|
|
4304
|
+
}
|
|
4305
|
+
}
|
|
4306
|
+
const root = requireObject2(value, "$");
|
|
4307
|
+
const version = requireFiniteNumber2(root.version, "$.version");
|
|
4308
|
+
if (version !== DIAGRAM_VIEW_STATE_VERSION) {
|
|
4309
|
+
throw new DiagramViewStateError(`unsupported view-state version ${version}`, "$.version");
|
|
4310
|
+
}
|
|
4311
|
+
return normalizeView(requireObject2(root.view, "$.view"));
|
|
4312
|
+
}
|
|
4313
|
+
function serializeDiagramViewState(view, space) {
|
|
4314
|
+
return JSON.stringify(createDiagramViewStateDocument(view), null, space);
|
|
4315
|
+
}
|
|
4316
|
+
function normalizeView(value) {
|
|
4317
|
+
const view = requireObject2(value, "$.view");
|
|
4318
|
+
const zoom = requireFiniteNumber2(view.zoom, "$.view.zoom");
|
|
4319
|
+
if (zoom <= 0) throw new DiagramViewStateError("expected a positive number", "$.view.zoom");
|
|
4320
|
+
return {
|
|
4321
|
+
zoom,
|
|
4322
|
+
panX: requireFiniteNumber2(view.panX, "$.view.panX"),
|
|
4323
|
+
panY: requireFiniteNumber2(view.panY, "$.view.panY"),
|
|
4324
|
+
overviewVisible: requireBoolean2(view.overviewVisible, "$.view.overviewVisible")
|
|
4325
|
+
};
|
|
4326
|
+
}
|
|
4327
|
+
function requireObject2(value, path) {
|
|
4328
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
4329
|
+
throw new DiagramViewStateError("expected an object", path);
|
|
4330
|
+
}
|
|
4331
|
+
const prototype = Object.getPrototypeOf(value);
|
|
4332
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
4333
|
+
throw new DiagramViewStateError("expected a plain object", path);
|
|
4334
|
+
}
|
|
4335
|
+
return value;
|
|
4336
|
+
}
|
|
4337
|
+
function requireFiniteNumber2(value, path) {
|
|
4338
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
4339
|
+
throw new DiagramViewStateError("expected a finite number", path);
|
|
4340
|
+
}
|
|
4341
|
+
return value;
|
|
4342
|
+
}
|
|
4343
|
+
function requireBoolean2(value, path) {
|
|
4344
|
+
if (typeof value !== "boolean") throw new DiagramViewStateError("expected a boolean", path);
|
|
4345
|
+
return value;
|
|
4346
|
+
}
|
|
4347
|
+
|
|
4240
4348
|
// src/i18n.ts
|
|
4241
4349
|
function t(key, fallback) {
|
|
4242
4350
|
if (typeof window === "undefined") return fallback;
|
|
@@ -4735,6 +4843,8 @@ ${runtime.error}`;
|
|
|
4735
4843
|
var StockSharpDiagram = class extends EventEmitter {
|
|
4736
4844
|
constructor(options) {
|
|
4737
4845
|
super();
|
|
4846
|
+
// Built only where a host asked for it: a control nobody asked to save gets no button at all.
|
|
4847
|
+
this.downloadButton = null;
|
|
4738
4848
|
this.disposables = [];
|
|
4739
4849
|
this.idCounter = 1;
|
|
4740
4850
|
this.undoEnabled = true;
|
|
@@ -4744,6 +4854,8 @@ ${runtime.error}`;
|
|
|
4744
4854
|
this.destroyed = false;
|
|
4745
4855
|
this.fullscreen = false;
|
|
4746
4856
|
this.fullscreenButtonVisible = true;
|
|
4857
|
+
this.downloadButtonVisible = false;
|
|
4858
|
+
this.downloadLabel = null;
|
|
4747
4859
|
// null means the host never named the button, so its wording comes from the shared bundle.
|
|
4748
4860
|
// Keeping the two apart is what lets an explicit label win while an untouched control still
|
|
4749
4861
|
// follows the page's language instead of being stuck in English.
|
|
@@ -4766,6 +4878,8 @@ ${runtime.error}`;
|
|
|
4766
4878
|
this.prepareFullscreenButtonHost();
|
|
4767
4879
|
this.fullscreenButton = this.createFullscreenButton();
|
|
4768
4880
|
this.updateFullscreenButton();
|
|
4881
|
+
this.downloadLabel = options.downloadLabel ?? null;
|
|
4882
|
+
this.setDownloadButtonVisible(options.showDownloadButton ?? false);
|
|
4769
4883
|
this.setContextMenuEnabled(options.showContextMenu ?? true);
|
|
4770
4884
|
this.canvas.setHistoryShortcutHandler((direction) => {
|
|
4771
4885
|
if (direction === "undo") this.undo();
|
|
@@ -5002,6 +5116,28 @@ ${runtime.error}`;
|
|
|
5002
5116
|
isFullscreen() {
|
|
5003
5117
|
return this.fullscreen;
|
|
5004
5118
|
}
|
|
5119
|
+
/** Shows or hides the button that asks the host to save the diagram. */
|
|
5120
|
+
setDownloadButtonVisible(visible) {
|
|
5121
|
+
this.downloadButtonVisible = visible;
|
|
5122
|
+
if (!visible) {
|
|
5123
|
+
if (this.downloadButton === null) return;
|
|
5124
|
+
this.downloadButton.hidden = true;
|
|
5125
|
+
this.downloadButton.style.display = "none";
|
|
5126
|
+
return;
|
|
5127
|
+
}
|
|
5128
|
+
this.downloadButton ?? (this.downloadButton = this.createDownloadButton());
|
|
5129
|
+
this.updateDownloadButton();
|
|
5130
|
+
this.downloadButton.hidden = false;
|
|
5131
|
+
this.downloadButton.style.display = "inline-flex";
|
|
5132
|
+
}
|
|
5133
|
+
isDownloadButtonVisible() {
|
|
5134
|
+
return this.downloadButtonVisible;
|
|
5135
|
+
}
|
|
5136
|
+
/** Text of the download button, for a host that renders in another language. */
|
|
5137
|
+
setDownloadLabel(label2) {
|
|
5138
|
+
this.downloadLabel = label2;
|
|
5139
|
+
this.updateDownloadButton();
|
|
5140
|
+
}
|
|
5005
5141
|
setFullscreenButtonVisible(visible) {
|
|
5006
5142
|
this.fullscreenButtonVisible = visible;
|
|
5007
5143
|
this.fullscreenButton.hidden = !visible;
|
|
@@ -5287,6 +5423,49 @@ ${runtime.error}`;
|
|
|
5287
5423
|
button.style.display = this.fullscreenButtonVisible ? "inline-flex" : "none";
|
|
5288
5424
|
return button;
|
|
5289
5425
|
}
|
|
5426
|
+
createDownloadButton() {
|
|
5427
|
+
const owner = this.div.ownerDocument ?? document;
|
|
5428
|
+
const button = owner.createElement("button");
|
|
5429
|
+
button.type = "button";
|
|
5430
|
+
button.className = "ssdiagram-download-button";
|
|
5431
|
+
button.setAttribute("data-ssdiagram-download-button", "");
|
|
5432
|
+
Object.assign(button.style, {
|
|
5433
|
+
position: "absolute",
|
|
5434
|
+
top: "8px",
|
|
5435
|
+
right: "46px",
|
|
5436
|
+
zIndex: "4",
|
|
5437
|
+
display: "inline-flex",
|
|
5438
|
+
alignItems: "center",
|
|
5439
|
+
justifyContent: "center",
|
|
5440
|
+
width: "30px",
|
|
5441
|
+
height: "30px",
|
|
5442
|
+
padding: "0",
|
|
5443
|
+
border: "1px solid var(--ssdiagram-control-border, var(--t-border, #3a4250))",
|
|
5444
|
+
borderRadius: "5px",
|
|
5445
|
+
background: "var(--ssdiagram-control-background, var(--t-surface, rgba(18, 21, 28, 0.9)))",
|
|
5446
|
+
color: "var(--ssdiagram-control-color, var(--t-text, #eaecef))",
|
|
5447
|
+
boxSizing: "border-box",
|
|
5448
|
+
cursor: "pointer",
|
|
5449
|
+
opacity: "0.9"
|
|
5450
|
+
});
|
|
5451
|
+
const click = () => {
|
|
5452
|
+
this.emit("exportRequested", { format: "document" });
|
|
5453
|
+
};
|
|
5454
|
+
button.addEventListener("click", click);
|
|
5455
|
+
this.div.appendChild(button);
|
|
5456
|
+
this.disposables.push(() => {
|
|
5457
|
+
button.removeEventListener("click", click);
|
|
5458
|
+
button.remove();
|
|
5459
|
+
});
|
|
5460
|
+
return button;
|
|
5461
|
+
}
|
|
5462
|
+
updateDownloadButton() {
|
|
5463
|
+
if (this.downloadButton === null) return;
|
|
5464
|
+
const label2 = this.downloadLabel ?? t("download", "Download");
|
|
5465
|
+
this.downloadButton.title = label2;
|
|
5466
|
+
this.downloadButton.setAttribute("aria-label", label2);
|
|
5467
|
+
this.downloadButton.innerHTML = '<svg viewBox="0 0 24 24" width="16" height="16" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M12 4v11M8 11l4 4 4-4M5 19h14"/></svg>';
|
|
5468
|
+
}
|
|
5290
5469
|
updateFullscreenButton() {
|
|
5291
5470
|
const fullscreen = this.fullscreen;
|
|
5292
5471
|
const labels = this.resolveFullscreenLabels();
|
|
@@ -6127,12 +6306,16 @@ ${runtime.error}`;
|
|
|
6127
6306
|
div.classList.remove("ss-diagram-error");
|
|
6128
6307
|
div.replaceChildren();
|
|
6129
6308
|
const [enterLabel, exitLabel] = (div.dataset.diagramFullscreen ?? "").split("|");
|
|
6309
|
+
const downloadLabel = div.dataset.diagramDownload;
|
|
6310
|
+
const showDownloadButton = options.onExportRequested !== void 0;
|
|
6130
6311
|
let diagram;
|
|
6131
6312
|
try {
|
|
6132
6313
|
diagram = new StockSharpDiagram({
|
|
6133
6314
|
div,
|
|
6134
6315
|
catalog,
|
|
6135
|
-
...enterLabel && exitLabel ? { fullscreenLabels: { enter: enterLabel, exit: exitLabel } } : {}
|
|
6316
|
+
...enterLabel && exitLabel ? { fullscreenLabels: { enter: enterLabel, exit: exitLabel } } : {},
|
|
6317
|
+
...showDownloadButton ? { showDownloadButton: true } : {},
|
|
6318
|
+
...downloadLabel ? { downloadLabel } : {}
|
|
6136
6319
|
});
|
|
6137
6320
|
} catch (error) {
|
|
6138
6321
|
div.replaceChildren();
|
|
@@ -6179,6 +6362,10 @@ ${runtime.error}`;
|
|
|
6179
6362
|
activeRenders.set(div, handle);
|
|
6180
6363
|
connectedRenders.add(handle);
|
|
6181
6364
|
ensureDisconnectedHostObserver();
|
|
6365
|
+
if (options.onExportRequested !== void 0) {
|
|
6366
|
+
const notifyHost = options.onExportRequested;
|
|
6367
|
+
cleanups.push(diagram.on("exportRequested", (request) => notifyHost(request, handle)));
|
|
6368
|
+
}
|
|
6182
6369
|
if (options.onFullscreenRequested !== void 0) {
|
|
6183
6370
|
const notifyHost = options.onFullscreenRequested;
|
|
6184
6371
|
cleanups.push(diagram.on("fullscreenRequested", (request) => notifyHost(request, handle)));
|