@stocksharp/diagram 0.2.2 → 0.3.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 +12 -0
- package/dist/esm/canvas-renderer.js +28 -22
- package/dist/esm/canvas-renderer.js.map +1 -1
- package/dist/esm/core/action-registry.js +24 -0
- package/dist/esm/core/action-registry.js.map +1 -1
- package/dist/esm/core/document.js +85 -28
- package/dist/esm/core/document.js.map +1 -1
- package/dist/esm/core/json.js +17 -0
- package/dist/esm/core/json.js.map +1 -0
- package/dist/esm/core/model.js +4 -0
- package/dist/esm/core/model.js.map +1 -1
- package/dist/esm/core/state.js +16 -2
- package/dist/esm/core/state.js.map +1 -1
- package/dist/esm/diagram/catalog.js +17 -5
- package/dist/esm/diagram/catalog.js.map +1 -1
- package/dist/esm/diagram/event-emitter.js +10 -0
- package/dist/esm/diagram/event-emitter.js.map +1 -1
- package/dist/esm/diagram/stocksharp-diagram.js +69 -57
- package/dist/esm/diagram/stocksharp-diagram.js.map +1 -1
- package/dist/esm/diagram/types.js +2 -1
- package/dist/esm/diagram/types.js.map +1 -1
- package/dist/esm/embed.js +129 -39
- package/dist/esm/embed.js.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/ssdiagram.js +314 -147
- package/dist/ssdiagram.js.map +4 -4
- package/dist/types/canvas-renderer.d.ts +3 -5
- package/dist/types/canvas-renderer.d.ts.map +1 -1
- package/dist/types/core/action-registry.d.ts +7 -0
- package/dist/types/core/action-registry.d.ts.map +1 -1
- package/dist/types/core/document.d.ts.map +1 -1
- package/dist/types/core/json.d.ts +11 -0
- package/dist/types/core/json.d.ts.map +1 -0
- package/dist/types/core/model.d.ts +10 -1
- package/dist/types/core/model.d.ts.map +1 -1
- package/dist/types/core/state.d.ts +24 -1
- package/dist/types/core/state.d.ts.map +1 -1
- package/dist/types/diagram/api.d.ts +13 -6
- package/dist/types/diagram/api.d.ts.map +1 -1
- package/dist/types/diagram/catalog.d.ts +2 -1
- package/dist/types/diagram/catalog.d.ts.map +1 -1
- package/dist/types/diagram/event-emitter.d.ts +6 -1
- package/dist/types/diagram/event-emitter.d.ts.map +1 -1
- package/dist/types/diagram/palette.d.ts +1 -1
- package/dist/types/diagram/palette.d.ts.map +1 -1
- package/dist/types/diagram/stocksharp-diagram.d.ts +32 -21
- package/dist/types/diagram/stocksharp-diagram.d.ts.map +1 -1
- package/dist/types/diagram/types.d.ts +4 -4
- package/dist/types/diagram/types.d.ts.map +1 -1
- package/dist/types/embed.d.ts.map +1 -1
- package/dist/types/index.d.ts +3 -3
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/canvas-renderer.ts +26 -24
- package/src/core/action-registry.ts +25 -0
- package/src/core/document.ts +91 -31
- package/src/core/json.ts +16 -0
- package/src/core/model.ts +14 -1
- package/src/core/state.ts +41 -3
- package/src/diagram/api.ts +14 -5
- package/src/diagram/catalog.ts +18 -6
- package/src/diagram/event-emitter.ts +12 -1
- package/src/diagram/palette.ts +1 -1
- package/src/diagram/stocksharp-diagram.ts +91 -75
- package/src/diagram/types.ts +6 -5
- package/src/embed.ts +149 -40
- package/src/index.ts +4 -0
package/dist/ssdiagram.js
CHANGED
|
@@ -37,6 +37,7 @@ var SSDiagram = (() => {
|
|
|
37
37
|
StockSharpDiagram: () => StockSharpDiagram,
|
|
38
38
|
StockSharpPalette: () => StockSharpPalette,
|
|
39
39
|
cloneDiagramDocument: () => cloneDiagramDocument,
|
|
40
|
+
cloneDiagramNodeErrors: () => cloneDiagramNodeErrors,
|
|
40
41
|
cloneDiagramRuntimeState: () => cloneDiagramRuntimeState,
|
|
41
42
|
createDiagramDocument: () => createDiagramDocument,
|
|
42
43
|
createDiagramNodeRuntimeState: () => createDiagramNodeRuntimeState,
|
|
@@ -58,7 +59,19 @@ var SSDiagram = (() => {
|
|
|
58
59
|
serializeDiagramViewState: () => serializeDiagramViewState
|
|
59
60
|
});
|
|
60
61
|
|
|
62
|
+
// src/core/json.ts
|
|
63
|
+
function setJsonKey(target, key, value) {
|
|
64
|
+
if (key === "__proto__") {
|
|
65
|
+
Object.defineProperty(target, key, { value, enumerable: true, writable: true, configurable: true });
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
target[key] = value;
|
|
69
|
+
}
|
|
70
|
+
|
|
61
71
|
// src/core/model.ts
|
|
72
|
+
function toPortDynamicMode(value) {
|
|
73
|
+
return value === "manual" || value === "onConnect" ? value : "";
|
|
74
|
+
}
|
|
62
75
|
var DIAGRAM_DOCUMENT_VERSION = 1;
|
|
63
76
|
|
|
64
77
|
// src/core/document.ts
|
|
@@ -70,25 +83,29 @@ var SSDiagram = (() => {
|
|
|
70
83
|
}
|
|
71
84
|
};
|
|
72
85
|
function createDiagramDocument(input = {}) {
|
|
73
|
-
const
|
|
86
|
+
const rawNodes = mapElements(input.nodes, "$.nodes");
|
|
87
|
+
const rawLinks = mapElements(input.links, "$.links");
|
|
88
|
+
const nodes = rawNodes.map((node, index) => normalizeNode(node, `$.nodes[${index}]`));
|
|
74
89
|
const usedLinkIds = /* @__PURE__ */ new Set();
|
|
75
|
-
for (let index = 0; index <
|
|
76
|
-
const
|
|
77
|
-
if (
|
|
78
|
-
requireIdentifier(
|
|
90
|
+
for (let index = 0; index < rawLinks.length; index++) {
|
|
91
|
+
const raw = rawLinks[index].id;
|
|
92
|
+
if (raw === void 0) continue;
|
|
93
|
+
const id = requireIdentifier(raw, `$.links[${index}].id`);
|
|
79
94
|
if (usedLinkIds.has(id)) {
|
|
80
95
|
throw new DiagramDocumentError(`duplicate link id "${id}"`, `$.links[${index}].id`);
|
|
81
96
|
}
|
|
82
97
|
usedLinkIds.add(id);
|
|
83
98
|
}
|
|
84
99
|
let sequence = 1;
|
|
85
|
-
const links =
|
|
100
|
+
const links = rawLinks.map((link, index) => {
|
|
86
101
|
let id = link.id;
|
|
87
102
|
if (id === void 0) {
|
|
103
|
+
let generated;
|
|
88
104
|
do
|
|
89
|
-
|
|
90
|
-
while (usedLinkIds.has(
|
|
91
|
-
usedLinkIds.add(
|
|
105
|
+
generated = `link_${sequence++}`;
|
|
106
|
+
while (usedLinkIds.has(generated));
|
|
107
|
+
usedLinkIds.add(generated);
|
|
108
|
+
id = generated;
|
|
92
109
|
}
|
|
93
110
|
return normalizeLink({ ...link, id }, `$.links[${index}]`);
|
|
94
111
|
});
|
|
@@ -117,7 +134,7 @@ var SSDiagram = (() => {
|
|
|
117
134
|
throw new DiagramDocumentError(reason);
|
|
118
135
|
}
|
|
119
136
|
}
|
|
120
|
-
const root =
|
|
137
|
+
const root = requireStructure(value, "$");
|
|
121
138
|
const version = requireNumber(root.version, "$.version");
|
|
122
139
|
if (version !== DIAGRAM_DOCUMENT_VERSION) {
|
|
123
140
|
throw new DiagramDocumentError(`unsupported document version ${version}`, "$.version");
|
|
@@ -131,7 +148,8 @@ var SSDiagram = (() => {
|
|
|
131
148
|
validateDocument(document2);
|
|
132
149
|
return document2;
|
|
133
150
|
}
|
|
134
|
-
function normalizeNode(
|
|
151
|
+
function normalizeNode(value, path) {
|
|
152
|
+
const input = requireStructure(value, path);
|
|
135
153
|
const id = requireIdentifier(input.id, `${path}.id`);
|
|
136
154
|
return {
|
|
137
155
|
id,
|
|
@@ -146,28 +164,30 @@ var SSDiagram = (() => {
|
|
|
146
164
|
icon: requireString(input.icon ?? "", `${path}.icon`),
|
|
147
165
|
message: requireString(input.message ?? "", `${path}.message`),
|
|
148
166
|
openAction: requireString(input.openAction ?? "", `${path}.openAction`),
|
|
149
|
-
inPorts: (input.inPorts
|
|
150
|
-
outPorts: (input.outPorts
|
|
151
|
-
parameters: (input.parameters
|
|
167
|
+
inPorts: mapElements(input.inPorts, `${path}.inPorts`).map((port, index) => normalizePort(port, `${path}.inPorts[${index}]`)),
|
|
168
|
+
outPorts: mapElements(input.outPorts, `${path}.outPorts`).map((port, index) => normalizePort(port, `${path}.outPorts[${index}]`)),
|
|
169
|
+
parameters: mapElements(input.parameters, `${path}.parameters`).map((parameter, index) => normalizeParameter(parameter, `${path}.parameters[${index}]`)),
|
|
152
170
|
paramValues: cloneStringRecord(input.paramValues ?? {}, `${path}.paramValues`),
|
|
153
171
|
metadata: cloneJsonObject(input.metadata ?? {}, `${path}.metadata`)
|
|
154
172
|
};
|
|
155
173
|
}
|
|
156
|
-
function normalizePort(
|
|
174
|
+
function normalizePort(value, path) {
|
|
175
|
+
const input = requireStructure(value, path);
|
|
157
176
|
return {
|
|
158
177
|
id: requireIdentifier(input.id, `${path}.id`),
|
|
159
178
|
name: requireString(input.name, `${path}.name`),
|
|
160
179
|
description: requireString(input.description ?? "", `${path}.description`),
|
|
161
180
|
type: requireString(input.type ?? "", `${path}.type`),
|
|
162
181
|
maxLinks: requireNonNegativeInteger(input.maxLinks ?? 0, `${path}.maxLinks`),
|
|
163
|
-
availableTypes: (input.availableTypes ?? []).map((type, index) => requireString(type, `${path}.availableTypes[${index}]`)),
|
|
182
|
+
availableTypes: requireArray(input.availableTypes ?? [], `${path}.availableTypes`).map((type, index) => requireString(type, `${path}.availableTypes[${index}]`)),
|
|
164
183
|
isDynamic: requireBoolean(input.isDynamic ?? false, `${path}.isDynamic`),
|
|
165
|
-
dynamicMode:
|
|
184
|
+
dynamicMode: requirePortDynamicMode(input.dynamicMode ?? "", `${path}.dynamicMode`),
|
|
166
185
|
isSibling: requireBoolean(input.isSibling ?? false, `${path}.isSibling`),
|
|
167
186
|
metadata: cloneJsonObject(input.metadata ?? {}, `${path}.metadata`)
|
|
168
187
|
};
|
|
169
188
|
}
|
|
170
|
-
function normalizeParameter(
|
|
189
|
+
function normalizeParameter(value, path) {
|
|
190
|
+
const input = requireStructure(value, path);
|
|
171
191
|
return {
|
|
172
192
|
name: requireIdentifier(input.name, `${path}.name`),
|
|
173
193
|
displayName: requireString(input.displayName, `${path}.displayName`),
|
|
@@ -183,7 +203,8 @@ var SSDiagram = (() => {
|
|
|
183
203
|
editorType: requireString(input.editorType, `${path}.editorType`)
|
|
184
204
|
};
|
|
185
205
|
}
|
|
186
|
-
function normalizeLink(
|
|
206
|
+
function normalizeLink(value, path) {
|
|
207
|
+
const input = requireStructure(value, path);
|
|
187
208
|
return {
|
|
188
209
|
id: requireIdentifier(input.id, `${path}.id`),
|
|
189
210
|
from: normalizeEndpoint(input.from, `${path}.from`),
|
|
@@ -191,14 +212,15 @@ var SSDiagram = (() => {
|
|
|
191
212
|
metadata: cloneJsonObject(input.metadata ?? {}, `${path}.metadata`)
|
|
192
213
|
};
|
|
193
214
|
}
|
|
194
|
-
function normalizeEndpoint(
|
|
215
|
+
function normalizeEndpoint(value, path) {
|
|
216
|
+
const input = requireStructure(value, path);
|
|
195
217
|
return {
|
|
196
218
|
nodeId: requireIdentifier(input.nodeId, `${path}.nodeId`),
|
|
197
219
|
portId: requireIdentifier(input.portId, `${path}.portId`)
|
|
198
220
|
};
|
|
199
221
|
}
|
|
200
222
|
function parseNode(value, path) {
|
|
201
|
-
const node =
|
|
223
|
+
const node = requireStructure(value, path);
|
|
202
224
|
return normalizeNode({
|
|
203
225
|
id: requireString(node.id, `${path}.id`),
|
|
204
226
|
typeId: requireString(node.typeId, `${path}.typeId`),
|
|
@@ -220,7 +242,7 @@ var SSDiagram = (() => {
|
|
|
220
242
|
}, path);
|
|
221
243
|
}
|
|
222
244
|
function parsePort(value, path) {
|
|
223
|
-
const port =
|
|
245
|
+
const port = requireStructure(value, path);
|
|
224
246
|
return normalizePort({
|
|
225
247
|
id: requireString(port.id, `${path}.id`),
|
|
226
248
|
name: requireString(port.name, `${path}.name`),
|
|
@@ -229,13 +251,13 @@ var SSDiagram = (() => {
|
|
|
229
251
|
maxLinks: requireNumber(port.maxLinks, `${path}.maxLinks`),
|
|
230
252
|
availableTypes: requireArray(port.availableTypes, `${path}.availableTypes`).map((type, index) => requireString(type, `${path}.availableTypes[${index}]`)),
|
|
231
253
|
isDynamic: requireBoolean(port.isDynamic, `${path}.isDynamic`),
|
|
232
|
-
dynamicMode:
|
|
254
|
+
dynamicMode: requirePortDynamicMode(port.dynamicMode, `${path}.dynamicMode`),
|
|
233
255
|
isSibling: requireBoolean(port.isSibling, `${path}.isSibling`),
|
|
234
256
|
metadata: cloneJsonObject(port.metadata, `${path}.metadata`)
|
|
235
257
|
}, path);
|
|
236
258
|
}
|
|
237
259
|
function parseParameter(value, path) {
|
|
238
|
-
const parameter =
|
|
260
|
+
const parameter = requireStructure(value, path);
|
|
239
261
|
return normalizeParameter({
|
|
240
262
|
name: requireString(parameter.name, `${path}.name`),
|
|
241
263
|
displayName: requireString(parameter.displayName, `${path}.displayName`),
|
|
@@ -252,7 +274,7 @@ var SSDiagram = (() => {
|
|
|
252
274
|
}, path);
|
|
253
275
|
}
|
|
254
276
|
function parseLink(value, path) {
|
|
255
|
-
const link =
|
|
277
|
+
const link = requireStructure(value, path);
|
|
256
278
|
return normalizeLink({
|
|
257
279
|
id: requireString(link.id, `${path}.id`),
|
|
258
280
|
from: parseEndpoint(link.from, `${path}.from`),
|
|
@@ -261,7 +283,7 @@ var SSDiagram = (() => {
|
|
|
261
283
|
}, path);
|
|
262
284
|
}
|
|
263
285
|
function parseEndpoint(value, path) {
|
|
264
|
-
const endpoint =
|
|
286
|
+
const endpoint = requireStructure(value, path);
|
|
265
287
|
return {
|
|
266
288
|
nodeId: requireIdentifier(endpoint.nodeId, `${path}.nodeId`),
|
|
267
289
|
portId: requireIdentifier(endpoint.portId, `${path}.portId`)
|
|
@@ -309,7 +331,7 @@ var SSDiagram = (() => {
|
|
|
309
331
|
function cloneStringRecord(value, path) {
|
|
310
332
|
const object = requireObject(value, path);
|
|
311
333
|
const result = {};
|
|
312
|
-
for (const [key, item] of Object.entries(object)) result
|
|
334
|
+
for (const [key, item] of Object.entries(object)) setJsonKey(result, key, requireString(item, `${path}.${key}`));
|
|
313
335
|
return result;
|
|
314
336
|
}
|
|
315
337
|
function cloneJsonObject(value, path, ancestors = /* @__PURE__ */ new WeakSet()) {
|
|
@@ -319,7 +341,7 @@ var SSDiagram = (() => {
|
|
|
319
341
|
const result = {};
|
|
320
342
|
try {
|
|
321
343
|
for (const [key, item] of Object.entries(object)) {
|
|
322
|
-
result
|
|
344
|
+
setJsonKey(result, key, cloneJsonValue(item, `${path}.${key}`, ancestors));
|
|
323
345
|
}
|
|
324
346
|
} finally {
|
|
325
347
|
ancestors.delete(object);
|
|
@@ -341,6 +363,12 @@ var SSDiagram = (() => {
|
|
|
341
363
|
if (isObject(value)) return cloneJsonObject(value, path, ancestors);
|
|
342
364
|
throw new DiagramDocumentError("expected a JSON value", path);
|
|
343
365
|
}
|
|
366
|
+
function requireStructure(value, path) {
|
|
367
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
368
|
+
throw new DiagramDocumentError("expected an object", path);
|
|
369
|
+
}
|
|
370
|
+
return value;
|
|
371
|
+
}
|
|
344
372
|
function requireObject(value, path) {
|
|
345
373
|
if (!isObject(value)) throw new DiagramDocumentError("expected an object", path);
|
|
346
374
|
return value;
|
|
@@ -350,6 +378,13 @@ var SSDiagram = (() => {
|
|
|
350
378
|
const prototype = Object.getPrototypeOf(value);
|
|
351
379
|
return prototype === Object.prototype || prototype === null;
|
|
352
380
|
}
|
|
381
|
+
function mapElements(value, path) {
|
|
382
|
+
if (value === void 0 || value === null) return [];
|
|
383
|
+
return Array.from(
|
|
384
|
+
requireArray(value, path),
|
|
385
|
+
(element, index) => requireStructure(element, `${path}[${index}]`)
|
|
386
|
+
);
|
|
387
|
+
}
|
|
353
388
|
function requireArray(value, path) {
|
|
354
389
|
if (!Array.isArray(value)) throw new DiagramDocumentError("expected an array", path);
|
|
355
390
|
return value;
|
|
@@ -358,6 +393,10 @@ var SSDiagram = (() => {
|
|
|
358
393
|
if (typeof value !== "string") throw new DiagramDocumentError("expected a string", path);
|
|
359
394
|
return value;
|
|
360
395
|
}
|
|
396
|
+
function requirePortDynamicMode(value, path) {
|
|
397
|
+
const mode = requireString(value, path);
|
|
398
|
+
return toPortDynamicMode(mode);
|
|
399
|
+
}
|
|
361
400
|
function requireIdentifier(value, path) {
|
|
362
401
|
const id = requireString(value, path);
|
|
363
402
|
if (id.trim().length === 0) throw new DiagramDocumentError("identifier cannot be empty", path);
|
|
@@ -399,6 +438,24 @@ var SSDiagram = (() => {
|
|
|
399
438
|
if (this.actions.get(action.id) === action) this.actions.delete(action.id);
|
|
400
439
|
};
|
|
401
440
|
}
|
|
441
|
+
/**
|
|
442
|
+
* Registers one action per id from a table keyed by TId, in key order.
|
|
443
|
+
* Because the table is a total Record, adding a member to TId without adding
|
|
444
|
+
* its action stops compiling instead of producing a command that silently
|
|
445
|
+
* does nothing.
|
|
446
|
+
*/
|
|
447
|
+
registerAll(actions) {
|
|
448
|
+
const ids = Object.keys(actions);
|
|
449
|
+
const disposers = ids.map((id) => {
|
|
450
|
+
const action = actions[id];
|
|
451
|
+
return this.register({
|
|
452
|
+
id,
|
|
453
|
+
canExecute: (context) => action.canExecute(context),
|
|
454
|
+
execute: (context) => action.execute(context)
|
|
455
|
+
});
|
|
456
|
+
});
|
|
457
|
+
return () => disposers.forEach((dispose) => dispose());
|
|
458
|
+
}
|
|
402
459
|
get(id) {
|
|
403
460
|
return this.actions.get(id) ?? null;
|
|
404
461
|
}
|
|
@@ -554,7 +611,7 @@ var SSDiagram = (() => {
|
|
|
554
611
|
globalError: state.globalError === null ? null : { ...state.globalError },
|
|
555
612
|
nodes: Object.fromEntries(Object.entries(state.nodes).map(([nodeId, node]) => [nodeId, {
|
|
556
613
|
active: node.active,
|
|
557
|
-
|
|
614
|
+
errors: cloneDiagramNodeErrors(node.errors),
|
|
558
615
|
ports: {
|
|
559
616
|
in: Object.fromEntries(Object.entries(node.ports.in).map(([portId, port]) => [portId, { ...port }])),
|
|
560
617
|
out: Object.fromEntries(Object.entries(node.ports.out).map(([portId, port]) => [portId, { ...port }]))
|
|
@@ -611,10 +668,17 @@ var SSDiagram = (() => {
|
|
|
611
668
|
function createDiagramNodeRuntimeState() {
|
|
612
669
|
return {
|
|
613
670
|
active: false,
|
|
614
|
-
|
|
671
|
+
errors: {},
|
|
615
672
|
ports: { in: {}, out: {} }
|
|
616
673
|
};
|
|
617
674
|
}
|
|
675
|
+
function cloneDiagramNodeErrors(errors) {
|
|
676
|
+
const result = {};
|
|
677
|
+
if (errors === void 0 || errors === null) return result;
|
|
678
|
+
if (errors.runtime !== void 0) result.runtime = { ...errors.runtime };
|
|
679
|
+
if (errors.load !== void 0) result.load = { ...errors.load };
|
|
680
|
+
return result;
|
|
681
|
+
}
|
|
618
682
|
|
|
619
683
|
// src/core/view-state.ts
|
|
620
684
|
var DIAGRAM_VIEW_STATE_VERSION = 1;
|
|
@@ -699,7 +763,7 @@ var SSDiagram = (() => {
|
|
|
699
763
|
function copyJsonObject(value) {
|
|
700
764
|
if (value === void 0) return {};
|
|
701
765
|
const result = {};
|
|
702
|
-
for (const [key, item] of Object.entries(value)) result
|
|
766
|
+
for (const [key, item] of Object.entries(value)) setJsonKey(result, key, copyJsonValue(item));
|
|
703
767
|
return result;
|
|
704
768
|
}
|
|
705
769
|
function copyParameters(value) {
|
|
@@ -730,7 +794,7 @@ var SSDiagram = (() => {
|
|
|
730
794
|
this.maxLinks = typeof init.maxLinks === "number" ? init.maxLinks : 0;
|
|
731
795
|
this.availableTypes = [...init.availableTypes ?? []];
|
|
732
796
|
this.isDynamic = init.isDynamic ?? false;
|
|
733
|
-
this.dynamicMode = init.dynamicMode
|
|
797
|
+
this.dynamicMode = toPortDynamicMode(init.dynamicMode);
|
|
734
798
|
this.isSibling = init.isSibling ?? false;
|
|
735
799
|
this.metadata = copyJsonObject(init.metadata);
|
|
736
800
|
}
|
|
@@ -897,8 +961,6 @@ var SSDiagram = (() => {
|
|
|
897
961
|
this.dragStart = [];
|
|
898
962
|
// group-drag origin
|
|
899
963
|
this.dragAnchor = { wx: 0, wy: 0 };
|
|
900
|
-
this.dragDX = 0;
|
|
901
|
-
this.dragDY = 0;
|
|
902
964
|
this.panning = false;
|
|
903
965
|
this.panX = 0;
|
|
904
966
|
this.panY = 0;
|
|
@@ -1482,7 +1544,7 @@ var SSDiagram = (() => {
|
|
|
1482
1544
|
setNodeParamValue(nodeId, name, value) {
|
|
1483
1545
|
return this.updateNodeState(nodeId, "set node parameter", (node) => {
|
|
1484
1546
|
if (value === void 0) delete node.paramValues[name];
|
|
1485
|
-
else node.paramValues
|
|
1547
|
+
else setJsonKey(node.paramValues, name, value);
|
|
1486
1548
|
});
|
|
1487
1549
|
}
|
|
1488
1550
|
setShowNodeMessages(show) {
|
|
@@ -1565,7 +1627,7 @@ var SSDiagram = (() => {
|
|
|
1565
1627
|
for (const node of this.nodes) {
|
|
1566
1628
|
if (node.loadError.length === 0) continue;
|
|
1567
1629
|
const state = createDiagramNodeRuntimeState();
|
|
1568
|
-
state.
|
|
1630
|
+
state.errors.load = { kind: "load", message: node.loadError, pulse: ++this.runtimePulse };
|
|
1569
1631
|
this.runtimeState.nodes[node.id] = state;
|
|
1570
1632
|
}
|
|
1571
1633
|
if (Object.keys(this.runtimeState.nodes).length > 0) this.emitRuntimeStateChanged();
|
|
@@ -1765,15 +1827,16 @@ var SSDiagram = (() => {
|
|
|
1765
1827
|
this.runtimePulse = Math.max(
|
|
1766
1828
|
this.runtimePulse,
|
|
1767
1829
|
this.runtimeState.globalError?.pulse ?? 0,
|
|
1768
|
-
...Object.values(this.runtimeState.nodes).
|
|
1830
|
+
...Object.values(this.runtimeState.nodes).flatMap((node) => [node.errors.runtime?.pulse ?? 0, node.errors.load?.pulse ?? 0])
|
|
1769
1831
|
);
|
|
1770
1832
|
for (const node of this.nodes) {
|
|
1771
|
-
const
|
|
1772
|
-
const
|
|
1773
|
-
|
|
1774
|
-
node.
|
|
1775
|
-
|
|
1776
|
-
|
|
1833
|
+
const errors = this.runtimeState.nodes[node.id]?.errors;
|
|
1834
|
+
const previousRuntime = previous.nodes[node.id]?.errors.runtime;
|
|
1835
|
+
const runtimeError = errors?.runtime;
|
|
1836
|
+
node.runtimeError = runtimeError?.message ?? "";
|
|
1837
|
+
node.loadError = errors?.load?.message ?? "";
|
|
1838
|
+
if (runtimeError !== void 0) {
|
|
1839
|
+
if (previousRuntime === void 0 || previousRuntime.pulse !== runtimeError.pulse) {
|
|
1777
1840
|
node.errorFlashStart = performance.now();
|
|
1778
1841
|
}
|
|
1779
1842
|
} else {
|
|
@@ -1822,7 +1885,9 @@ var SSDiagram = (() => {
|
|
|
1822
1885
|
const state = this.getRuntimeState();
|
|
1823
1886
|
const nodeState = state.nodes[id] ?? createDiagramNodeRuntimeState();
|
|
1824
1887
|
state.nodes[id] = nodeState;
|
|
1825
|
-
|
|
1888
|
+
if (message.length === 0) delete nodeState.errors[kind];
|
|
1889
|
+
else if (kind === "load") nodeState.errors.load = { kind, message, pulse: ++this.runtimePulse };
|
|
1890
|
+
else nodeState.errors.runtime = { kind, message, pulse: ++this.runtimePulse };
|
|
1826
1891
|
this.runtimeState = state;
|
|
1827
1892
|
this.emitRuntimeStateChanged();
|
|
1828
1893
|
return true;
|
|
@@ -1837,8 +1902,9 @@ var SSDiagram = (() => {
|
|
|
1837
1902
|
if (kind === void 0 || kind === "load") node.loadError = "";
|
|
1838
1903
|
const state = this.getRuntimeState();
|
|
1839
1904
|
const nodeState = state.nodes[id];
|
|
1840
|
-
if (nodeState !== void 0
|
|
1841
|
-
|
|
1905
|
+
if (nodeState !== void 0) {
|
|
1906
|
+
if (kind === void 0 || kind === "runtime") delete nodeState.errors.runtime;
|
|
1907
|
+
if (kind === void 0 || kind === "load") delete nodeState.errors.load;
|
|
1842
1908
|
}
|
|
1843
1909
|
this.runtimeState = state;
|
|
1844
1910
|
this.emitRuntimeStateChanged();
|
|
@@ -3634,6 +3700,13 @@ ${runtime.error}`;
|
|
|
3634
3700
|
}
|
|
3635
3701
|
}
|
|
3636
3702
|
}
|
|
3703
|
+
/**
|
|
3704
|
+
* Whether anyone is listening. Lets a subject skip building a payload that
|
|
3705
|
+
* is expensive to produce and that nothing would read.
|
|
3706
|
+
*/
|
|
3707
|
+
hasHandlers(event) {
|
|
3708
|
+
return (this.handlers.get(event)?.size ?? 0) > 0;
|
|
3709
|
+
}
|
|
3637
3710
|
clearEventHandlers() {
|
|
3638
3711
|
this.handlers.clear();
|
|
3639
3712
|
}
|
|
@@ -3655,7 +3728,7 @@ ${runtime.error}`;
|
|
|
3655
3728
|
this.maxLinks = typeof init.maxLinks === "number" ? init.maxLinks : 0;
|
|
3656
3729
|
this.availableTypes = init.availableTypes ?? [];
|
|
3657
3730
|
this.isDynamic = init.isDynamic ?? false;
|
|
3658
|
-
this.dynamicMode = init.dynamicMode
|
|
3731
|
+
this.dynamicMode = toPortDynamicMode(init.dynamicMode);
|
|
3659
3732
|
this.isSibling = init.isSibling ?? false;
|
|
3660
3733
|
}
|
|
3661
3734
|
clone() {
|
|
@@ -3756,7 +3829,7 @@ ${runtime.error}`;
|
|
|
3756
3829
|
this.destroyed = false;
|
|
3757
3830
|
this.fullscreen = false;
|
|
3758
3831
|
this.fullscreenButtonVisible = true;
|
|
3759
|
-
this.
|
|
3832
|
+
this.fullscreenLabels = { enter: "Enter fullscreen", exit: "Exit fullscreen" };
|
|
3760
3833
|
this.contextActions = new DiagramActionRegistry();
|
|
3761
3834
|
this.div = options.div;
|
|
3762
3835
|
this.catalog = options.catalog;
|
|
@@ -3770,6 +3843,7 @@ ${runtime.error}`;
|
|
|
3770
3843
|
gridSize: options.gridSize
|
|
3771
3844
|
});
|
|
3772
3845
|
this.fullscreenButtonVisible = options.showFullscreenButton ?? true;
|
|
3846
|
+
this.fullscreenLabels = options.fullscreenLabels ?? this.fullscreenLabels;
|
|
3773
3847
|
this.prepareFullscreenButtonHost();
|
|
3774
3848
|
this.fullscreenButton = this.createFullscreenButton();
|
|
3775
3849
|
this.updateFullscreenButton();
|
|
@@ -3779,7 +3853,6 @@ ${runtime.error}`;
|
|
|
3779
3853
|
this.updateZoomLabel();
|
|
3780
3854
|
}
|
|
3781
3855
|
setLinkValidator(validator) {
|
|
3782
|
-
this.linkValidator = validator;
|
|
3783
3856
|
this.canvas.setLinkValidator(validator === null ? null : ({ fromNode, fromPort, toNode, toPort }) => validator({
|
|
3784
3857
|
fromNode: this.fromCanvasNode(fromNode),
|
|
3785
3858
|
fromPort: this.fromCanvasPort(fromPort),
|
|
@@ -3853,21 +3926,25 @@ ${runtime.error}`;
|
|
|
3853
3926
|
removeLink(link) {
|
|
3854
3927
|
this.canvas.removeLink(this.toCanvasLink(link));
|
|
3855
3928
|
}
|
|
3929
|
+
/** False when nothing changed: no such node or port, or the edit was a no-op. */
|
|
3856
3930
|
addPort(nodeId, direction, port) {
|
|
3857
|
-
this.canvas.addPort(nodeId, direction, this.toCanvasPort(port));
|
|
3931
|
+
return this.canvas.addPort(nodeId, direction, this.toCanvasPort(port));
|
|
3858
3932
|
}
|
|
3933
|
+
/** False when nothing changed: no such node or port, or the edit was a no-op. */
|
|
3859
3934
|
removePort(nodeId, direction, portId) {
|
|
3860
|
-
this.canvas.removePort(nodeId, direction, portId);
|
|
3935
|
+
return this.canvas.removePort(nodeId, direction, portId);
|
|
3861
3936
|
}
|
|
3937
|
+
/** False when nothing changed: no such node or port, or the edit was a no-op. */
|
|
3862
3938
|
updatePortType(nodeId, direction, portId, type) {
|
|
3863
|
-
this.canvas.updatePortType(nodeId, direction, portId, type);
|
|
3939
|
+
return this.canvas.updatePortType(nodeId, direction, portId, type);
|
|
3864
3940
|
}
|
|
3865
3941
|
updatePort(nodeId, direction, portId, patch) {
|
|
3866
3942
|
return this.canvas.updatePort(nodeId, direction, portId, patch);
|
|
3867
3943
|
}
|
|
3944
|
+
/** False when nothing changed: no such node or port, or the edit was a no-op. */
|
|
3868
3945
|
setNodePorts(nodeId, inPorts, outPorts) {
|
|
3869
3946
|
const current = this.canvas.findNode(nodeId);
|
|
3870
|
-
if (current === void 0) return;
|
|
3947
|
+
if (current === void 0) return false;
|
|
3871
3948
|
const convert = (port) => ({
|
|
3872
3949
|
id: port.key,
|
|
3873
3950
|
name: port.name,
|
|
@@ -3886,13 +3963,15 @@ ${runtime.error}`;
|
|
|
3886
3963
|
for (const sibling of current.outPorts.filter((port) => port.isSibling)) {
|
|
3887
3964
|
if (!nextOut.some((port) => port.id === sibling.id)) nextOut.push(sibling.toInit());
|
|
3888
3965
|
}
|
|
3889
|
-
this.canvas.setNodePorts(nodeId, nextIn, nextOut);
|
|
3966
|
+
return this.canvas.setNodePorts(nodeId, nextIn, nextOut);
|
|
3890
3967
|
}
|
|
3968
|
+
/** False when nothing changed: no such node or port, or the edit was a no-op. */
|
|
3891
3969
|
updateNode(nodeId, patch) {
|
|
3892
|
-
this.canvas.updateNode(nodeId, patch);
|
|
3970
|
+
return this.canvas.updateNode(nodeId, patch);
|
|
3893
3971
|
}
|
|
3972
|
+
/** False when nothing changed: no such node or port, or the edit was a no-op. */
|
|
3894
3973
|
setNodeMessage(nodeId, message) {
|
|
3895
|
-
this.canvas.updateNode(nodeId, { message });
|
|
3974
|
+
return this.canvas.updateNode(nodeId, { message });
|
|
3896
3975
|
}
|
|
3897
3976
|
setNodeError(nodeId, message, options = {}) {
|
|
3898
3977
|
return this.canvas.setNodeError(nodeId, message, options);
|
|
@@ -3918,11 +3997,13 @@ ${runtime.error}`;
|
|
|
3918
3997
|
setGlobalError(message, kind = "invalid") {
|
|
3919
3998
|
this.canvas.setGlobalError(message, kind);
|
|
3920
3999
|
}
|
|
4000
|
+
/** False when nothing changed: no such node or port, or the edit was a no-op. */
|
|
3921
4001
|
setNodeParamValue(nodeId, paramName, value) {
|
|
3922
|
-
this.canvas.setNodeParamValue(nodeId, paramName, value);
|
|
4002
|
+
return this.canvas.setNodeParamValue(nodeId, paramName, value);
|
|
3923
4003
|
}
|
|
4004
|
+
/** False when nothing changed: no such node or port, or the edit was a no-op. */
|
|
3924
4005
|
setNodeName(nodeId, value) {
|
|
3925
|
-
this.canvas.updateNode(nodeId, { name: value });
|
|
4006
|
+
return this.canvas.updateNode(nodeId, { name: value });
|
|
3926
4007
|
}
|
|
3927
4008
|
/** Groups host-driven document edits into one undo/redo operation. */
|
|
3928
4009
|
transaction(label, action) {
|
|
@@ -4001,6 +4082,14 @@ ${runtime.error}`;
|
|
|
4001
4082
|
isFullscreenButtonVisible() {
|
|
4002
4083
|
return this.fullscreenButtonVisible;
|
|
4003
4084
|
}
|
|
4085
|
+
/** Text of the fullscreen button, for a host that renders in another language. */
|
|
4086
|
+
setFullscreenLabels(labels) {
|
|
4087
|
+
this.fullscreenLabels = labels;
|
|
4088
|
+
this.updateFullscreenButton();
|
|
4089
|
+
}
|
|
4090
|
+
getFullscreenLabels() {
|
|
4091
|
+
return { ...this.fullscreenLabels };
|
|
4092
|
+
}
|
|
4004
4093
|
/** Updates only the control's state after the host changed its own layout. */
|
|
4005
4094
|
setFullscreenState(fullscreen) {
|
|
4006
4095
|
if (this.fullscreen === fullscreen) return;
|
|
@@ -4223,7 +4312,7 @@ ${runtime.error}`;
|
|
|
4223
4312
|
}
|
|
4224
4313
|
updateFullscreenButton() {
|
|
4225
4314
|
const fullscreen = this.fullscreen;
|
|
4226
|
-
const label = fullscreen ?
|
|
4315
|
+
const label = fullscreen ? this.fullscreenLabels.exit : this.fullscreenLabels.enter;
|
|
4227
4316
|
const path = fullscreen ? "M4 9h5V4M20 9h-5V4M4 15h5v5M20 15h-5v5" : "M9 4H4v5M15 4h5v5M9 20H4v-5M15 20h5v-5";
|
|
4228
4317
|
this.fullscreenButton.className = fullscreen ? "ssdiagram-fullscreen-button is-active" : "ssdiagram-fullscreen-button";
|
|
4229
4318
|
this.fullscreenButton.title = label;
|
|
@@ -4337,50 +4426,43 @@ ${runtime.error}`;
|
|
|
4337
4426
|
}
|
|
4338
4427
|
registerContextActions() {
|
|
4339
4428
|
const permissions = () => this.canvas.getInteractionPermissions();
|
|
4340
|
-
this.contextActions.
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
canExecute: ({ nodes }) => nodes.length > 0,
|
|
4378
|
-
execute: ({ nodes }) => this.emit("nodeProperties", { nodes })
|
|
4379
|
-
});
|
|
4380
|
-
this.contextActions.register({
|
|
4381
|
-
id: "help",
|
|
4382
|
-
canExecute: ({ nodes }) => this.helpEnabled && nodes.length > 0,
|
|
4383
|
-
execute: ({ nodes }) => this.emit("nodeHelp", { nodes })
|
|
4429
|
+
this.contextActions.registerAll({
|
|
4430
|
+
undo: {
|
|
4431
|
+
canExecute: () => this.canUndo(),
|
|
4432
|
+
execute: () => this.undo()
|
|
4433
|
+
},
|
|
4434
|
+
redo: {
|
|
4435
|
+
canExecute: () => this.canRedo(),
|
|
4436
|
+
execute: () => this.redo()
|
|
4437
|
+
},
|
|
4438
|
+
cut: {
|
|
4439
|
+
canExecute: ({ nodes }) => nodes.length > 0 && permissions().copy && permissions().deleteSelection,
|
|
4440
|
+
execute: () => this.cutSelection()
|
|
4441
|
+
},
|
|
4442
|
+
copy: {
|
|
4443
|
+
canExecute: ({ nodes }) => nodes.length > 0 && permissions().copy,
|
|
4444
|
+
execute: () => this.copySelection()
|
|
4445
|
+
},
|
|
4446
|
+
paste: {
|
|
4447
|
+
canExecute: () => this.canvas.hasClipboard() && permissions().paste,
|
|
4448
|
+
execute: () => this.pasteSelection()
|
|
4449
|
+
},
|
|
4450
|
+
open: {
|
|
4451
|
+
canExecute: ({ nodes }) => nodes.length === 1 && nodes[0].openAction.length > 0,
|
|
4452
|
+
execute: ({ nodes }) => this.emit("nodeOpen", { nodes })
|
|
4453
|
+
},
|
|
4454
|
+
delete: {
|
|
4455
|
+
canExecute: ({ selection }) => permissions().deleteSelection && (selection.nodeIds.length > 0 || selection.linkIds.length > 0),
|
|
4456
|
+
execute: () => this.canvas.deleteSelection()
|
|
4457
|
+
},
|
|
4458
|
+
properties: {
|
|
4459
|
+
canExecute: ({ nodes }) => nodes.length > 0,
|
|
4460
|
+
execute: ({ nodes }) => this.emit("nodeProperties", { nodes })
|
|
4461
|
+
},
|
|
4462
|
+
help: {
|
|
4463
|
+
canExecute: ({ nodes }) => this.helpEnabled && nodes.length > 0,
|
|
4464
|
+
execute: ({ nodes }) => this.emit("nodeHelp", { nodes })
|
|
4465
|
+
}
|
|
4384
4466
|
});
|
|
4385
4467
|
}
|
|
4386
4468
|
contextActionContext() {
|
|
@@ -4548,19 +4630,26 @@ ${runtime.error}`;
|
|
|
4548
4630
|
// imported node ("Element type is missing from the palette"). Node.id
|
|
4549
4631
|
// keeps its original casing so save/load round-trips unchanged.
|
|
4550
4632
|
addNodeType(node) {
|
|
4551
|
-
const n = node instanceof Node ? node : new Node(node);
|
|
4633
|
+
const n = (node instanceof Node ? node : new Node(node)).clone();
|
|
4552
4634
|
this.nodeTypes.set(n.id.toLowerCase(), n);
|
|
4553
|
-
this.
|
|
4635
|
+
this.emitNodeTypesChanged();
|
|
4554
4636
|
}
|
|
4555
4637
|
removeNodeType(id) {
|
|
4556
4638
|
this.nodeTypes.delete(id.toLowerCase());
|
|
4557
|
-
this.
|
|
4639
|
+
this.emitNodeTypesChanged();
|
|
4558
4640
|
}
|
|
4559
4641
|
getNodeType(id) {
|
|
4560
|
-
return this.nodeTypes.get(id.toLowerCase()) ?? null;
|
|
4642
|
+
return this.nodeTypes.get(id.toLowerCase())?.clone() ?? null;
|
|
4561
4643
|
}
|
|
4562
4644
|
getNodeTypes() {
|
|
4563
|
-
return Array.from(this.nodeTypes.values());
|
|
4645
|
+
return Array.from(this.nodeTypes.values(), (node) => node.clone());
|
|
4646
|
+
}
|
|
4647
|
+
// Node definitions are mutable, so the payload has to be a copy of each one.
|
|
4648
|
+
// Building a catalog is a loop of addNodeType calls, which would make that
|
|
4649
|
+
// quadratic; skipping it while nobody listens keeps catalog construction --
|
|
4650
|
+
// where no subscriber exists yet -- as cheap as it was.
|
|
4651
|
+
emitNodeTypesChanged() {
|
|
4652
|
+
if (this.hasHandlers("nodeTypesChanged")) this.emit("nodeTypesChanged", this.getNodeTypes());
|
|
4564
4653
|
}
|
|
4565
4654
|
};
|
|
4566
4655
|
|
|
@@ -4873,69 +4962,141 @@ ${runtime.error}`;
|
|
|
4873
4962
|
});
|
|
4874
4963
|
return { nodes, nodeErrors };
|
|
4875
4964
|
}
|
|
4965
|
+
function isRecord(value) {
|
|
4966
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4967
|
+
}
|
|
4968
|
+
function asRecord(value) {
|
|
4969
|
+
return isRecord(value) ? value : void 0;
|
|
4970
|
+
}
|
|
4971
|
+
function asArray(value) {
|
|
4972
|
+
return Array.isArray(value) ? value : [];
|
|
4973
|
+
}
|
|
4974
|
+
function asString(value) {
|
|
4975
|
+
return typeof value === "string" ? value : "";
|
|
4976
|
+
}
|
|
4977
|
+
function asFiniteNumber(value) {
|
|
4978
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
4979
|
+
}
|
|
4876
4980
|
function nodeName(node) {
|
|
4877
|
-
const
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
const
|
|
4881
|
-
|
|
4882
|
-
return nameVal;
|
|
4883
|
-
if (typeof node?.Figure === "string" && node.Figure.length > 0)
|
|
4884
|
-
return node.Figure;
|
|
4885
|
-
return typeof node?.Key === "string" ? node.Key : "";
|
|
4981
|
+
const named = asRecord(asRecord(asRecord(node.Settings)?.Parameters)?.Name)?.Value;
|
|
4982
|
+
if (typeof named === "string" && named.length > 0)
|
|
4983
|
+
return named;
|
|
4984
|
+
const figure = asString(node.Figure);
|
|
4985
|
+
return figure.length > 0 ? figure : asString(node.Key);
|
|
4886
4986
|
}
|
|
4887
4987
|
function parseRawScheme(raw) {
|
|
4888
4988
|
const nodes = [];
|
|
4889
4989
|
const links = [];
|
|
4890
|
-
const
|
|
4891
|
-
const
|
|
4892
|
-
|
|
4893
|
-
const scheme = value?.Scheme;
|
|
4894
|
-
const model = scheme?.Model;
|
|
4895
|
-
if (!model)
|
|
4990
|
+
const model = asRecord(asRecord(asRecord(asRecord(raw)?.Content)?.Value)?.Scheme)?.Model;
|
|
4991
|
+
const fields = asRecord(model);
|
|
4992
|
+
if (fields === void 0)
|
|
4896
4993
|
return { nodes, links };
|
|
4897
|
-
for (const
|
|
4898
|
-
const
|
|
4994
|
+
for (const entry of asArray(fields.Nodes)) {
|
|
4995
|
+
const node = asRecord(entry);
|
|
4996
|
+
if (node === void 0)
|
|
4997
|
+
continue;
|
|
4998
|
+
const key = node.Key;
|
|
4899
4999
|
if (typeof key !== "string" || key.length === 0)
|
|
4900
5000
|
continue;
|
|
4901
5001
|
nodes.push({
|
|
4902
5002
|
id: key,
|
|
4903
|
-
typeId:
|
|
4904
|
-
name: nodeName(
|
|
4905
|
-
x:
|
|
4906
|
-
y:
|
|
5003
|
+
typeId: asString(node.TypeId),
|
|
5004
|
+
name: nodeName(node),
|
|
5005
|
+
x: asFiniteNumber(node.X),
|
|
5006
|
+
y: asFiniteNumber(node.Y)
|
|
4907
5007
|
});
|
|
4908
5008
|
}
|
|
4909
|
-
for (const
|
|
4910
|
-
const
|
|
4911
|
-
|
|
5009
|
+
for (const entry of asArray(fields.Links)) {
|
|
5010
|
+
const link = asRecord(entry);
|
|
5011
|
+
if (link === void 0)
|
|
5012
|
+
continue;
|
|
5013
|
+
const from = link.From;
|
|
5014
|
+
const to = link.To;
|
|
4912
5015
|
if (typeof from !== "string" || typeof to !== "string")
|
|
4913
5016
|
continue;
|
|
4914
5017
|
links.push({
|
|
4915
5018
|
from,
|
|
4916
|
-
fromPort:
|
|
5019
|
+
fromPort: asString(link.FromPort),
|
|
4917
5020
|
to,
|
|
4918
|
-
toPort:
|
|
5021
|
+
toPort: asString(link.ToPort)
|
|
4919
5022
|
});
|
|
4920
5023
|
}
|
|
4921
5024
|
return { nodes, links };
|
|
4922
5025
|
}
|
|
5026
|
+
function parsePalettePorts(value) {
|
|
5027
|
+
const ports = [];
|
|
5028
|
+
for (const entry of asArray(value)) {
|
|
5029
|
+
const port = asRecord(entry);
|
|
5030
|
+
if (port === void 0)
|
|
5031
|
+
continue;
|
|
5032
|
+
const key = asString(port.key);
|
|
5033
|
+
if (key.length === 0)
|
|
5034
|
+
continue;
|
|
5035
|
+
ports.push({
|
|
5036
|
+
key,
|
|
5037
|
+
name: asString(port.name),
|
|
5038
|
+
type: asString(port.type),
|
|
5039
|
+
maxLinks: asFiniteNumber(port.maxLinks),
|
|
5040
|
+
availableTypes: asArray(port.availableTypes).filter((item) => typeof item === "string"),
|
|
5041
|
+
isDynamic: port.isDynamic === true,
|
|
5042
|
+
dynamicMode: toPortDynamicMode(port.dynamicMode)
|
|
5043
|
+
});
|
|
5044
|
+
}
|
|
5045
|
+
return ports;
|
|
5046
|
+
}
|
|
5047
|
+
function parsePalette(value) {
|
|
5048
|
+
const socketTypes = [];
|
|
5049
|
+
const elements = [];
|
|
5050
|
+
const root = asRecord(value);
|
|
5051
|
+
if (root === void 0)
|
|
5052
|
+
return { socketTypes, elements };
|
|
5053
|
+
for (const entry of asArray(root.socketTypes)) {
|
|
5054
|
+
const socket = asRecord(entry);
|
|
5055
|
+
const name = asString(socket?.name);
|
|
5056
|
+
if (name.length === 0)
|
|
5057
|
+
continue;
|
|
5058
|
+
socketTypes.push({ name, color: asString(socket?.color) });
|
|
5059
|
+
}
|
|
5060
|
+
for (const entry of asArray(root.elements)) {
|
|
5061
|
+
const element = asRecord(entry);
|
|
5062
|
+
const typeId = asString(element?.typeId);
|
|
5063
|
+
if (element === void 0 || typeId.length === 0)
|
|
5064
|
+
continue;
|
|
5065
|
+
elements.push({
|
|
5066
|
+
typeId,
|
|
5067
|
+
name: asString(element.name),
|
|
5068
|
+
groupName: asString(element.groupName),
|
|
5069
|
+
icon: asString(element.icon),
|
|
5070
|
+
inPorts: parsePalettePorts(element.inPorts),
|
|
5071
|
+
outPorts: parsePalettePorts(element.outPorts)
|
|
5072
|
+
});
|
|
5073
|
+
}
|
|
5074
|
+
return { socketTypes, elements };
|
|
5075
|
+
}
|
|
4923
5076
|
async function renderScheme(div, paletteUrl, scheme, options = {}) {
|
|
4924
5077
|
return renderSchemeAtRevision(div, paletteUrl, scheme, beginRender(div), options);
|
|
4925
5078
|
}
|
|
4926
5079
|
async function renderSchemeAtRevision(div, paletteUrl, scheme, revision, options) {
|
|
4927
5080
|
if (renderRevisions.get(div) !== revision)
|
|
4928
5081
|
return null;
|
|
4929
|
-
const
|
|
5082
|
+
const response = await fetch(paletteUrl);
|
|
5083
|
+
if (!response.ok)
|
|
5084
|
+
throw new Error(`Palette request to ${paletteUrl} failed with status ${response.status}.`);
|
|
5085
|
+
const palette = parsePalette(await response.json());
|
|
4930
5086
|
if (renderRevisions.get(div) !== revision)
|
|
4931
5087
|
return null;
|
|
4932
5088
|
const catalog = buildCatalog(palette);
|
|
4933
5089
|
disposeActiveRender(div);
|
|
4934
5090
|
div.classList.remove("ss-diagram-error");
|
|
4935
5091
|
div.replaceChildren();
|
|
5092
|
+
const [enterLabel, exitLabel] = (div.dataset.diagramFullscreen ?? "").split("|");
|
|
4936
5093
|
let diagram;
|
|
4937
5094
|
try {
|
|
4938
|
-
diagram = new StockSharpDiagram({
|
|
5095
|
+
diagram = new StockSharpDiagram({
|
|
5096
|
+
div,
|
|
5097
|
+
catalog,
|
|
5098
|
+
...enterLabel && exitLabel ? { fullscreenLabels: { enter: enterLabel, exit: exitLabel } } : {}
|
|
5099
|
+
});
|
|
4939
5100
|
} catch (error) {
|
|
4940
5101
|
div.replaceChildren();
|
|
4941
5102
|
throw error;
|
|
@@ -5085,6 +5246,14 @@ ${runtime.error}`;
|
|
|
5085
5246
|
disconnectedHostObserver.disconnect();
|
|
5086
5247
|
disconnectedHostObserver = null;
|
|
5087
5248
|
}
|
|
5249
|
+
function errorTexts(div) {
|
|
5250
|
+
const parts = (div.dataset.diagramErrors ?? "").split("|");
|
|
5251
|
+
return {
|
|
5252
|
+
load: parts[0] || "Diagram source could not be loaded.",
|
|
5253
|
+
empty: parts[1] || "Diagram is empty or malformed.",
|
|
5254
|
+
draw: parts[2] || "Diagram could not be rendered."
|
|
5255
|
+
};
|
|
5256
|
+
}
|
|
5088
5257
|
function note(div, message, revision) {
|
|
5089
5258
|
if (renderRevisions.get(div) !== revision) return;
|
|
5090
5259
|
disposeActiveRender(div);
|
|
@@ -5093,53 +5262,51 @@ ${runtime.error}`;
|
|
|
5093
5262
|
}
|
|
5094
5263
|
async function renderFromSource(div, paletteUrl, srcUrl, options = {}) {
|
|
5095
5264
|
const revision = beginRender(div);
|
|
5096
|
-
const errors = (div
|
|
5097
|
-
const [errLoad = "Diagram source could not be loaded.", errEmpty = "Diagram is empty or malformed.", errDraw = "Diagram could not be rendered."] = errors;
|
|
5265
|
+
const errors = errorTexts(div);
|
|
5098
5266
|
let raw;
|
|
5099
5267
|
try {
|
|
5100
5268
|
const resp = await fetch(srcUrl);
|
|
5101
5269
|
if (!resp.ok) {
|
|
5102
|
-
note(div,
|
|
5270
|
+
note(div, errors.load, revision);
|
|
5103
5271
|
return null;
|
|
5104
5272
|
}
|
|
5105
5273
|
const text = (await resp.text()).replace(/^/, "");
|
|
5106
5274
|
raw = JSON.parse(text);
|
|
5107
5275
|
} catch {
|
|
5108
|
-
note(div,
|
|
5276
|
+
note(div, errors.load, revision);
|
|
5109
5277
|
return null;
|
|
5110
5278
|
}
|
|
5111
5279
|
const scheme = parseRawScheme(raw);
|
|
5112
5280
|
if (scheme.nodes.length === 0) {
|
|
5113
|
-
note(div,
|
|
5281
|
+
note(div, errors.empty, revision);
|
|
5114
5282
|
return null;
|
|
5115
5283
|
}
|
|
5116
5284
|
try {
|
|
5117
5285
|
return await renderSchemeAtRevision(div, paletteUrl, scheme, revision, options);
|
|
5118
5286
|
} catch {
|
|
5119
|
-
note(div,
|
|
5287
|
+
note(div, errors.draw, revision);
|
|
5120
5288
|
return null;
|
|
5121
5289
|
}
|
|
5122
5290
|
}
|
|
5123
5291
|
async function renderFromInline(div, paletteUrl, json, options = {}) {
|
|
5124
5292
|
const revision = beginRender(div);
|
|
5125
|
-
const errors = (div
|
|
5126
|
-
const [, errEmpty = "Diagram is empty or malformed.", errDraw = "Diagram could not be rendered."] = errors;
|
|
5293
|
+
const errors = errorTexts(div);
|
|
5127
5294
|
let raw;
|
|
5128
5295
|
try {
|
|
5129
5296
|
raw = JSON.parse(json);
|
|
5130
5297
|
} catch {
|
|
5131
|
-
note(div,
|
|
5298
|
+
note(div, errors.empty, revision);
|
|
5132
5299
|
return null;
|
|
5133
5300
|
}
|
|
5134
5301
|
const scheme = parseRawScheme(raw);
|
|
5135
5302
|
if (scheme.nodes.length === 0) {
|
|
5136
|
-
note(div,
|
|
5303
|
+
note(div, errors.empty, revision);
|
|
5137
5304
|
return null;
|
|
5138
5305
|
}
|
|
5139
5306
|
try {
|
|
5140
5307
|
return await renderSchemeAtRevision(div, paletteUrl, scheme, revision, options);
|
|
5141
5308
|
} catch {
|
|
5142
|
-
note(div,
|
|
5309
|
+
note(div, errors.draw, revision);
|
|
5143
5310
|
return null;
|
|
5144
5311
|
}
|
|
5145
5312
|
}
|