@stocksharp/diagram 0.6.0 → 0.7.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/dist/esm/canvas-renderer.js +185 -34
- package/dist/esm/canvas-renderer.js.map +1 -1
- package/dist/esm/core/document.js +24 -12
- package/dist/esm/core/document.js.map +1 -1
- package/dist/esm/core/history.js +12 -3
- package/dist/esm/core/history.js.map +1 -1
- package/dist/esm/core/json.js +10 -0
- package/dist/esm/core/json.js.map +1 -1
- package/dist/esm/diagram/context-menu.js +28 -10
- package/dist/esm/diagram/context-menu.js.map +1 -1
- package/dist/esm/diagram/event-emitter.js +12 -1
- package/dist/esm/diagram/event-emitter.js.map +1 -1
- package/dist/esm/diagram/palette.js +9 -1
- package/dist/esm/diagram/palette.js.map +1 -1
- package/dist/esm/diagram/stocksharp-diagram.js +8 -0
- package/dist/esm/diagram/stocksharp-diagram.js.map +1 -1
- package/dist/esm/diagram/types.js +29 -2
- package/dist/esm/diagram/types.js.map +1 -1
- package/dist/esm/embed.js +63 -10
- package/dist/esm/embed.js.map +1 -1
- package/dist/esm/i18n.js.map +1 -1
- package/dist/esm/svg-surface.js +10 -2
- package/dist/esm/svg-surface.js.map +1 -1
- package/dist/ssdiagram.js +253 -74
- package/dist/ssdiagram.js.map +3 -3
- package/dist/types/canvas-renderer.d.ts +19 -1
- package/dist/types/canvas-renderer.d.ts.map +1 -1
- package/dist/types/core/document.d.ts.map +1 -1
- package/dist/types/core/history.d.ts +1 -0
- package/dist/types/core/history.d.ts.map +1 -1
- package/dist/types/core/json.d.ts +8 -0
- package/dist/types/core/json.d.ts.map +1 -1
- package/dist/types/diagram/context-menu.d.ts +6 -0
- package/dist/types/diagram/context-menu.d.ts.map +1 -1
- package/dist/types/diagram/event-emitter.d.ts.map +1 -1
- package/dist/types/diagram/palette.d.ts.map +1 -1
- package/dist/types/diagram/stocksharp-diagram.d.ts.map +1 -1
- package/dist/types/diagram/types.d.ts +5 -0
- package/dist/types/diagram/types.d.ts.map +1 -1
- package/dist/types/embed.d.ts.map +1 -1
- package/dist/types/i18n.d.ts +2 -0
- package/dist/types/i18n.d.ts.map +1 -1
- package/dist/types/svg-surface.d.ts +1 -1
- package/dist/types/svg-surface.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/canvas-renderer.ts +173 -34
- package/src/core/document.ts +25 -12
- package/src/core/history.ts +13 -3
- package/src/core/json.ts +11 -0
- package/src/diagram/context-menu.ts +30 -10
- package/src/diagram/event-emitter.ts +10 -1
- package/src/diagram/palette.ts +9 -2
- package/src/diagram/stocksharp-diagram.ts +5 -0
- package/src/diagram/types.ts +29 -3
- package/src/embed.ts +71 -10
- package/src/i18n.ts +2 -0
- package/src/svg-surface.ts +11 -2
package/dist/ssdiagram.js
CHANGED
|
@@ -67,6 +67,9 @@ var SSDiagram = (() => {
|
|
|
67
67
|
}
|
|
68
68
|
target[key] = value;
|
|
69
69
|
}
|
|
70
|
+
function getJsonKey(target, key) {
|
|
71
|
+
return Object.prototype.hasOwnProperty.call(target, key) ? target[key] : void 0;
|
|
72
|
+
}
|
|
70
73
|
|
|
71
74
|
// src/core/model.ts
|
|
72
75
|
function toPortDynamicMode(value) {
|
|
@@ -110,15 +113,8 @@ var SSDiagram = (() => {
|
|
|
110
113
|
return normalizeLink({ ...link, id }, `$.links[${index}]`);
|
|
111
114
|
});
|
|
112
115
|
const rawZones = mapElements(input.zones, "$.zones");
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
const normalized = normalizeZone(zone, `$.zones[${index}]`);
|
|
116
|
-
if (usedZoneIds.has(normalized.id)) {
|
|
117
|
-
throw new DiagramDocumentError(`duplicate zone id "${normalized.id}"`, `$.zones[${index}].id`);
|
|
118
|
-
}
|
|
119
|
-
usedZoneIds.add(normalized.id);
|
|
120
|
-
return normalized;
|
|
121
|
-
});
|
|
116
|
+
const zones = rawZones.map((zone, index) => normalizeZone(zone, `$.zones[${index}]`));
|
|
117
|
+
validateUniqueZones(zones);
|
|
122
118
|
const document2 = {
|
|
123
119
|
version: DIAGRAM_DOCUMENT_VERSION,
|
|
124
120
|
nodes,
|
|
@@ -239,8 +235,9 @@ var SSDiagram = (() => {
|
|
|
239
235
|
return {
|
|
240
236
|
id: requireIdentifier(input.id, `${path}.id`),
|
|
241
237
|
name: requireString(input.name, `${path}.name`),
|
|
242
|
-
|
|
243
|
-
|
|
238
|
+
// Finite, like node coordinates: NaN survives every arithmetic check and writes as null.
|
|
239
|
+
x: requireFiniteNumber(input.x, `${path}.x`),
|
|
240
|
+
y: requireFiniteNumber(input.y, `${path}.y`),
|
|
244
241
|
width: requirePositiveSize(input.width, `${path}.width`),
|
|
245
242
|
height: requirePositiveSize(input.height, `${path}.height`),
|
|
246
243
|
color: input.color === void 0 || input.color === null ? "" : requireString(input.color, `${path}.color`),
|
|
@@ -248,7 +245,7 @@ var SSDiagram = (() => {
|
|
|
248
245
|
};
|
|
249
246
|
}
|
|
250
247
|
function requirePositiveSize(value, path) {
|
|
251
|
-
const size =
|
|
248
|
+
const size = requireFiniteNumber(value, path);
|
|
252
249
|
if (!(size > 0)) throw new DiagramDocumentError("size must be greater than zero", path);
|
|
253
250
|
return size;
|
|
254
251
|
}
|
|
@@ -330,6 +327,14 @@ var SSDiagram = (() => {
|
|
|
330
327
|
portId: requireIdentifier(endpoint.portId, `${path}.portId`)
|
|
331
328
|
};
|
|
332
329
|
}
|
|
330
|
+
function validateUniqueZones(zones) {
|
|
331
|
+
const seen = /* @__PURE__ */ new Set();
|
|
332
|
+
for (let index = 0; index < zones.length; index++) {
|
|
333
|
+
const zone = zones[index];
|
|
334
|
+
if (seen.has(zone.id)) throw new DiagramDocumentError(`duplicate zone id "${zone.id}"`, `$.zones[${index}].id`);
|
|
335
|
+
seen.add(zone.id);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
333
338
|
function validateDocument(document2) {
|
|
334
339
|
const nodes = /* @__PURE__ */ new Map();
|
|
335
340
|
for (let index = 0; index < document2.nodes.length; index++) {
|
|
@@ -360,6 +365,7 @@ var SSDiagram = (() => {
|
|
|
360
365
|
if (endpoints.has(key)) throw new DiagramDocumentError("duplicate link endpoints", path);
|
|
361
366
|
endpoints.add(key);
|
|
362
367
|
}
|
|
368
|
+
validateUniqueZones(document2.zones);
|
|
363
369
|
}
|
|
364
370
|
function validateUniquePorts(ports, path) {
|
|
365
371
|
const ids = /* @__PURE__ */ new Set();
|
|
@@ -573,7 +579,13 @@ var SSDiagram = (() => {
|
|
|
573
579
|
throw error;
|
|
574
580
|
}
|
|
575
581
|
}
|
|
582
|
+
assertNoOpenTransaction(operation) {
|
|
583
|
+
if (this.transactions.length > 0) {
|
|
584
|
+
throw new Error(`Cannot ${operation} command history inside a transaction.`);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
576
587
|
undo() {
|
|
588
|
+
this.assertNoOpenTransaction("undo");
|
|
577
589
|
const command = this.undoCommands.pop();
|
|
578
590
|
if (command === void 0) return false;
|
|
579
591
|
try {
|
|
@@ -587,6 +599,7 @@ var SSDiagram = (() => {
|
|
|
587
599
|
return true;
|
|
588
600
|
}
|
|
589
601
|
redo() {
|
|
602
|
+
this.assertNoOpenTransaction("redo");
|
|
590
603
|
const command = this.redoCommands.pop();
|
|
591
604
|
if (command === void 0) return false;
|
|
592
605
|
try {
|
|
@@ -600,9 +613,7 @@ var SSDiagram = (() => {
|
|
|
600
613
|
return true;
|
|
601
614
|
}
|
|
602
615
|
clear() {
|
|
603
|
-
|
|
604
|
-
throw new Error("Cannot clear command history inside a transaction.");
|
|
605
|
-
}
|
|
616
|
+
this.assertNoOpenTransaction("clear");
|
|
606
617
|
this.undoCommands.length = 0;
|
|
607
618
|
this.redoCommands.length = 0;
|
|
608
619
|
this.notify();
|
|
@@ -1045,12 +1056,16 @@ var SSDiagram = (() => {
|
|
|
1045
1056
|
this.emit(`<rect x="${n(x)}" y="${n(y)}" width="${n(w)}" height="${n(h)}" fill="none" ${this.strokeAttrs()}/>`);
|
|
1046
1057
|
}
|
|
1047
1058
|
// ===== text ==========================================================
|
|
1048
|
-
fillText(text, x, y) {
|
|
1059
|
+
fillText(text, x, y, maxWidth) {
|
|
1049
1060
|
if (text === "") return;
|
|
1050
1061
|
const anchor = ANCHOR[this.state.textAlign] ?? "start";
|
|
1051
1062
|
const baseline = BASELINE[this.state.textBaseline] ?? "alphabetic";
|
|
1063
|
+
let constraint = "";
|
|
1064
|
+
if (maxWidth !== void 0 && maxWidth > 0 && this.measureText(text).width > maxWidth) {
|
|
1065
|
+
constraint = ` textLength="${n(maxWidth)}" lengthAdjust="spacingAndGlyphs"`;
|
|
1066
|
+
}
|
|
1052
1067
|
this.emit(
|
|
1053
|
-
`<text x="${n(x)}" y="${n(y)}" style="font:${escapeXml(this.state.font)}" text-anchor="${anchor}" dominant-baseline="${baseline}" ${this.fillAttrs()}>${escapeXml(text)}</text>`
|
|
1068
|
+
`<text x="${n(x)}" y="${n(y)}" style="font:${escapeXml(this.state.font)}" text-anchor="${anchor}" dominant-baseline="${baseline}"${constraint} ${this.fillAttrs()}>${escapeXml(text)}</text>`
|
|
1054
1069
|
);
|
|
1055
1070
|
}
|
|
1056
1071
|
measureText(text) {
|
|
@@ -1358,15 +1373,20 @@ var SSDiagram = (() => {
|
|
|
1358
1373
|
this.introStart = null;
|
|
1359
1374
|
this.overviewVisible = true;
|
|
1360
1375
|
this.ovDragging = false;
|
|
1376
|
+
this.historyShortcut = (direction) => {
|
|
1377
|
+
if (direction === "undo") this.undo();
|
|
1378
|
+
else this.redo();
|
|
1379
|
+
};
|
|
1361
1380
|
this.ovGeo = null;
|
|
1362
1381
|
this.validator = null;
|
|
1363
1382
|
this.handlers = /* @__PURE__ */ new Map();
|
|
1364
1383
|
this.opts = opts;
|
|
1384
|
+
if (opts.typeColors !== void 0) this.setTypeColorsInternal(opts.typeColors);
|
|
1365
1385
|
this.host = opts.host;
|
|
1366
1386
|
this.gridSnapEnabled = opts.gridSnap ?? false;
|
|
1367
1387
|
this.gridSize = this.normalizeGridSize(opts.gridSize ?? DEFAULT_GRID_SIZE);
|
|
1368
|
-
this.history = new DiagramCommandHistory((
|
|
1369
|
-
this.emit("undoStackChanged", { canUndo, canRedo });
|
|
1388
|
+
this.history = new DiagramCommandHistory(() => {
|
|
1389
|
+
this.emit("undoStackChanged", { canUndo: this.canUndo(), canRedo: this.canRedo() });
|
|
1370
1390
|
});
|
|
1371
1391
|
this.canvas = document.createElement("canvas");
|
|
1372
1392
|
this.canvas.style.display = "block";
|
|
@@ -1749,6 +1769,8 @@ var SSDiagram = (() => {
|
|
|
1749
1769
|
clear() {
|
|
1750
1770
|
this.nodes = [];
|
|
1751
1771
|
this.links = [];
|
|
1772
|
+
this.zones = [];
|
|
1773
|
+
const hadSelection = this.selectedNode !== null || this.selectedNodes.size > 0 || this.selectedLink !== null || this.selectedPort !== null;
|
|
1752
1774
|
this.selectedNode = null;
|
|
1753
1775
|
this.selectedNodes.clear();
|
|
1754
1776
|
this.selectedLink = null;
|
|
@@ -1758,6 +1780,7 @@ var SSDiagram = (() => {
|
|
|
1758
1780
|
this.runtimePulse = 0;
|
|
1759
1781
|
this.globalErrorFlashStart = null;
|
|
1760
1782
|
this.history.clear();
|
|
1783
|
+
if (hadSelection) this.emitSelectionChanged();
|
|
1761
1784
|
this.emitRuntimeStateChanged();
|
|
1762
1785
|
}
|
|
1763
1786
|
relink(linkId, next) {
|
|
@@ -1976,6 +1999,20 @@ var SSDiagram = (() => {
|
|
|
1976
1999
|
this.clear();
|
|
1977
2000
|
this.idSeq = 1;
|
|
1978
2001
|
this.linkSeq = 1;
|
|
2002
|
+
const seenNodeIds = /* @__PURE__ */ new Set();
|
|
2003
|
+
for (const node of nodes) {
|
|
2004
|
+
const id = node.id ?? "";
|
|
2005
|
+
if (id === "") continue;
|
|
2006
|
+
if (seenNodeIds.has(id)) throw new Error(`ssdiagram: duplicate node id "${id}"`);
|
|
2007
|
+
seenNodeIds.add(id);
|
|
2008
|
+
}
|
|
2009
|
+
const seenLinkIds = /* @__PURE__ */ new Set();
|
|
2010
|
+
for (const link of links) {
|
|
2011
|
+
const id = link.id ?? "";
|
|
2012
|
+
if (id === "") continue;
|
|
2013
|
+
if (seenLinkIds.has(id)) throw new Error(`ssdiagram: duplicate link id "${id}"`);
|
|
2014
|
+
seenLinkIds.add(id);
|
|
2015
|
+
}
|
|
1979
2016
|
for (const node of nodes) {
|
|
1980
2017
|
const id = node.id ?? this.nextNodeId();
|
|
1981
2018
|
this.doAddNode({ ...node, id });
|
|
@@ -1992,7 +2029,7 @@ var SSDiagram = (() => {
|
|
|
1992
2029
|
if (node.loadError.length === 0) continue;
|
|
1993
2030
|
const state = createDiagramNodeRuntimeState();
|
|
1994
2031
|
state.errors.load = { kind: "load", message: node.loadError, pulse: ++this.runtimePulse };
|
|
1995
|
-
this.runtimeState.nodes
|
|
2032
|
+
setJsonKey(this.runtimeState.nodes, node.id, state);
|
|
1996
2033
|
}
|
|
1997
2034
|
if (Object.keys(this.runtimeState.nodes).length > 0) this.emitRuntimeStateChanged();
|
|
1998
2035
|
this.history.clear();
|
|
@@ -2003,7 +2040,15 @@ var SSDiagram = (() => {
|
|
|
2003
2040
|
save() {
|
|
2004
2041
|
return {
|
|
2005
2042
|
nodes: this.nodes.map((node) => node.toInit(false)),
|
|
2006
|
-
links: this.links.map((l) => ({
|
|
2043
|
+
links: this.links.map((l) => ({
|
|
2044
|
+
id: l.id,
|
|
2045
|
+
from: l.from,
|
|
2046
|
+
fromPort: l.fromPort,
|
|
2047
|
+
to: l.to,
|
|
2048
|
+
toPort: l.toPort,
|
|
2049
|
+
style: l.style,
|
|
2050
|
+
metadata: copyJsonObject(l.metadata)
|
|
2051
|
+
}))
|
|
2007
2052
|
};
|
|
2008
2053
|
}
|
|
2009
2054
|
loadDocument(source) {
|
|
@@ -2073,7 +2118,20 @@ var SSDiagram = (() => {
|
|
|
2073
2118
|
overviewVisible: this.overviewVisible
|
|
2074
2119
|
};
|
|
2075
2120
|
}
|
|
2121
|
+
/**
|
|
2122
|
+
* Replaces what Ctrl+Z / Ctrl+Y do. Passing null restores the built-in behaviour, which is
|
|
2123
|
+
* plain undo()/redo() -- so a renderer used on its own keeps its keyboard history.
|
|
2124
|
+
*/
|
|
2125
|
+
setHistoryShortcutHandler(handler) {
|
|
2126
|
+
this.historyShortcut = handler ?? ((direction) => {
|
|
2127
|
+
if (direction === "undo") this.undo();
|
|
2128
|
+
else this.redo();
|
|
2129
|
+
});
|
|
2130
|
+
}
|
|
2076
2131
|
setViewState(state) {
|
|
2132
|
+
if (!Number.isFinite(state.zoom) || !Number.isFinite(state.panX) || !Number.isFinite(state.panY)) {
|
|
2133
|
+
throw new RangeError("ssdiagram: view state zoom and pan must be finite numbers");
|
|
2134
|
+
}
|
|
2077
2135
|
this.scale = clamp(state.zoom, ZOOM_MIN, ZOOM_MAX);
|
|
2078
2136
|
this.offX = state.panX;
|
|
2079
2137
|
this.offY = state.panY;
|
|
@@ -2101,6 +2159,7 @@ var SSDiagram = (() => {
|
|
|
2101
2159
|
throw new RangeError(`ssdiagram: unsupported screenshot scope "${String(scope)}"`);
|
|
2102
2160
|
}
|
|
2103
2161
|
if (scope === "viewport" && Object.keys(options).length === 0) {
|
|
2162
|
+
if (this.drawScheduled) this.draw();
|
|
2104
2163
|
const copy = document.createElement("canvas");
|
|
2105
2164
|
copy.width = this.canvas.width;
|
|
2106
2165
|
copy.height = this.canvas.height;
|
|
@@ -2127,7 +2186,10 @@ var SSDiagram = (() => {
|
|
|
2127
2186
|
offX: this.offX,
|
|
2128
2187
|
offY: this.offY,
|
|
2129
2188
|
overviewVisible: this.overviewVisible,
|
|
2130
|
-
background: this.opts.background
|
|
2189
|
+
background: this.opts.background,
|
|
2190
|
+
// Drawing the overview recomputes this from the export frame. Left behind, the next
|
|
2191
|
+
// click on the on-screen minimap panned to a point in the export's coordinates.
|
|
2192
|
+
ovGeo: this.ovGeo
|
|
2131
2193
|
};
|
|
2132
2194
|
try {
|
|
2133
2195
|
this.ctx = outputContext;
|
|
@@ -2157,6 +2219,7 @@ var SSDiagram = (() => {
|
|
|
2157
2219
|
this.offY = previous.offY;
|
|
2158
2220
|
this.overviewVisible = previous.overviewVisible;
|
|
2159
2221
|
this.opts.background = previous.background;
|
|
2222
|
+
this.ovGeo = previous.ovGeo;
|
|
2160
2223
|
}
|
|
2161
2224
|
return output;
|
|
2162
2225
|
}
|
|
@@ -2210,10 +2273,15 @@ var SSDiagram = (() => {
|
|
|
2210
2273
|
scale: this.scale,
|
|
2211
2274
|
offX: this.offX,
|
|
2212
2275
|
offY: this.offY,
|
|
2213
|
-
overviewVisible: this.overviewVisible
|
|
2276
|
+
overviewVisible: this.overviewVisible,
|
|
2277
|
+
background: this.opts.background,
|
|
2278
|
+
// Drawing the overview recomputes this from the export frame. Left behind, the next
|
|
2279
|
+
// click on the on-screen minimap panned to a point in the export's coordinates.
|
|
2280
|
+
ovGeo: this.ovGeo
|
|
2214
2281
|
};
|
|
2215
2282
|
try {
|
|
2216
2283
|
this.ctx = surface;
|
|
2284
|
+
if (options.background !== void 0) this.opts.background = options.background;
|
|
2217
2285
|
this.width = width;
|
|
2218
2286
|
this.height = height;
|
|
2219
2287
|
this.dpr = 1;
|
|
@@ -2237,6 +2305,8 @@ var SSDiagram = (() => {
|
|
|
2237
2305
|
this.offX = previous.offX;
|
|
2238
2306
|
this.offY = previous.offY;
|
|
2239
2307
|
this.overviewVisible = previous.overviewVisible;
|
|
2308
|
+
this.opts.background = previous.background;
|
|
2309
|
+
this.ovGeo = previous.ovGeo;
|
|
2240
2310
|
}
|
|
2241
2311
|
return surface.toSvg();
|
|
2242
2312
|
}
|
|
@@ -2269,8 +2339,8 @@ var SSDiagram = (() => {
|
|
|
2269
2339
|
...Object.values(this.runtimeState.nodes).flatMap((node) => [node.errors.runtime?.pulse ?? 0, node.errors.load?.pulse ?? 0])
|
|
2270
2340
|
);
|
|
2271
2341
|
for (const node of this.nodes) {
|
|
2272
|
-
const errors = this.runtimeState.nodes
|
|
2273
|
-
const previousRuntime = previous.nodes
|
|
2342
|
+
const errors = getJsonKey(this.runtimeState.nodes, node.id)?.errors;
|
|
2343
|
+
const previousRuntime = getJsonKey(previous.nodes, node.id)?.errors.runtime;
|
|
2274
2344
|
const runtimeError = errors?.runtime;
|
|
2275
2345
|
node.runtimeError = runtimeError?.message ?? "";
|
|
2276
2346
|
node.loadError = errors?.load?.message ?? "";
|
|
@@ -2299,8 +2369,8 @@ var SSDiagram = (() => {
|
|
|
2299
2369
|
const port = direction === "in" ? node?.inPorts.find((candidate) => candidate.id === portId) : node?.outPorts.find((candidate) => candidate.id === portId);
|
|
2300
2370
|
if (node === void 0 || port === void 0) return false;
|
|
2301
2371
|
const state = this.getRuntimeState();
|
|
2302
|
-
const nodeState = state.nodes
|
|
2303
|
-
state.nodes
|
|
2372
|
+
const nodeState = getJsonKey(state.nodes, nodeId) ?? createDiagramNodeRuntimeState();
|
|
2373
|
+
setJsonKey(state.nodes, nodeId, nodeState);
|
|
2304
2374
|
const current = nodeState.ports[direction][portId] ?? createDiagramPortRuntimeState();
|
|
2305
2375
|
nodeState.ports[direction][portId] = { ...current, ...patch };
|
|
2306
2376
|
this.setRuntimeState(state);
|
|
@@ -2322,8 +2392,8 @@ var SSDiagram = (() => {
|
|
|
2322
2392
|
node.errorFlashStart = message.length > 0 && options.animate !== false ? performance.now() : null;
|
|
2323
2393
|
}
|
|
2324
2394
|
const state = this.getRuntimeState();
|
|
2325
|
-
const nodeState = state.nodes
|
|
2326
|
-
state.nodes
|
|
2395
|
+
const nodeState = getJsonKey(state.nodes, id) ?? createDiagramNodeRuntimeState();
|
|
2396
|
+
setJsonKey(state.nodes, id, nodeState);
|
|
2327
2397
|
if (message.length === 0) delete nodeState.errors[kind];
|
|
2328
2398
|
else if (kind === "load") nodeState.errors.load = { kind, message, pulse: ++this.runtimePulse };
|
|
2329
2399
|
else nodeState.errors.runtime = { kind, message, pulse: ++this.runtimePulse };
|
|
@@ -2340,7 +2410,7 @@ var SSDiagram = (() => {
|
|
|
2340
2410
|
}
|
|
2341
2411
|
if (kind === void 0 || kind === "load") node.loadError = "";
|
|
2342
2412
|
const state = this.getRuntimeState();
|
|
2343
|
-
const nodeState = state.nodes
|
|
2413
|
+
const nodeState = getJsonKey(state.nodes, id);
|
|
2344
2414
|
if (nodeState !== void 0) {
|
|
2345
2415
|
if (kind === void 0 || kind === "runtime") delete nodeState.errors.runtime;
|
|
2346
2416
|
if (kind === void 0 || kind === "load") delete nodeState.errors.load;
|
|
@@ -2364,6 +2434,7 @@ var SSDiagram = (() => {
|
|
|
2364
2434
|
this.selectNode(node);
|
|
2365
2435
|
}
|
|
2366
2436
|
setZoom(scale) {
|
|
2437
|
+
if (!Number.isFinite(scale)) throw new RangeError("ssdiagram: zoom must be a finite number");
|
|
2367
2438
|
const cx = this.width / 2;
|
|
2368
2439
|
const cy = this.height / 2;
|
|
2369
2440
|
const wx = (cx - this.offX) / this.scale;
|
|
@@ -2405,9 +2476,21 @@ var SSDiagram = (() => {
|
|
|
2405
2476
|
this.scheduleDraw();
|
|
2406
2477
|
}
|
|
2407
2478
|
setTypeColors(colors) {
|
|
2408
|
-
this.
|
|
2479
|
+
this.setTypeColorsInternal(colors);
|
|
2409
2480
|
this.scheduleDraw();
|
|
2410
2481
|
}
|
|
2482
|
+
/**
|
|
2483
|
+
* Keyed the way port types are compared everywhere else. Connection compatibility is case- and
|
|
2484
|
+
* space-insensitive, so an imported scheme spelling a socket "candle " connects to "Candle" and
|
|
2485
|
+
* then fell through to a hash colour, showing one logical type in two colours in one diagram.
|
|
2486
|
+
*/
|
|
2487
|
+
setTypeColorsInternal(colors) {
|
|
2488
|
+
const normalized = {};
|
|
2489
|
+
for (const [type, color] of Object.entries(colors)) {
|
|
2490
|
+
setJsonKey(normalized, normalizePortType(type), color);
|
|
2491
|
+
}
|
|
2492
|
+
this.opts.typeColors = normalized;
|
|
2493
|
+
}
|
|
2411
2494
|
setTheme(t2) {
|
|
2412
2495
|
if (t2.background !== void 0) this.opts.background = t2.background;
|
|
2413
2496
|
if (t2.gridColor !== void 0) this.opts.gridColor = t2.gridColor;
|
|
@@ -2509,7 +2592,8 @@ var SSDiagram = (() => {
|
|
|
2509
2592
|
const light = maxL !== void 0 && maxL < 0.5;
|
|
2510
2593
|
if (type === "") return light ? "#585c62" : "#9aa0a6";
|
|
2511
2594
|
const map = this.opts.typeColors ?? {};
|
|
2512
|
-
const
|
|
2595
|
+
const normalized = normalizePortType(type);
|
|
2596
|
+
const base = getJsonKey(map, normalized) ?? `hsl(${hashHue(normalized)}, 62%, 58%)`;
|
|
2513
2597
|
return maxL !== void 0 ? clampLightness(base, maxL) : base;
|
|
2514
2598
|
}
|
|
2515
2599
|
// Selection/hover accent. The dark-canvas blue washes out on a light canvas, so a light theme
|
|
@@ -2529,6 +2613,7 @@ var SSDiagram = (() => {
|
|
|
2529
2613
|
for (const p of [...n2.inPorts, ...n2.outPorts]) {
|
|
2530
2614
|
if ((wx - p.cx) ** 2 + (wy - p.cy) ** 2 <= (PORT_R + 4) ** 2) return { node: n2, port: p };
|
|
2531
2615
|
}
|
|
2616
|
+
if (wx >= n2.x && wx <= n2.x + n2.w && wy >= n2.y && wy <= n2.y + n2.h) return null;
|
|
2532
2617
|
}
|
|
2533
2618
|
return null;
|
|
2534
2619
|
}
|
|
@@ -2807,6 +2892,9 @@ var SSDiagram = (() => {
|
|
|
2807
2892
|
fromPort: l.from.portId,
|
|
2808
2893
|
to: t2,
|
|
2809
2894
|
toPort: l.to.portId,
|
|
2895
|
+
// The clipboard is built from saveDocument(), so it carries the style;
|
|
2896
|
+
// rebuilding without it silently turned every pasted dashed link solid.
|
|
2897
|
+
style: l.style,
|
|
2810
2898
|
metadata: l.metadata
|
|
2811
2899
|
});
|
|
2812
2900
|
}
|
|
@@ -2883,14 +2971,16 @@ var SSDiagram = (() => {
|
|
|
2883
2971
|
}
|
|
2884
2972
|
this.cancelLongPress();
|
|
2885
2973
|
this.relinkCandidate = null;
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
this.lpTimer =
|
|
2892
|
-
|
|
2893
|
-
|
|
2974
|
+
if (e.pointerType !== "mouse") {
|
|
2975
|
+
this.lpStart = { sx: e.clientX, sy: e.clientY, px: e.clientX, py: e.clientY };
|
|
2976
|
+
const downSx = e.clientX, downSy = e.clientY;
|
|
2977
|
+
const r0 = this.canvas.getBoundingClientRect();
|
|
2978
|
+
const localSx = downSx - r0.left, localSy = downSy - r0.top;
|
|
2979
|
+
this.lpTimer = setTimeout(() => {
|
|
2980
|
+
this.lpTimer = null;
|
|
2981
|
+
this.fireContextMenu(localSx, localSy, downSx, downSy);
|
|
2982
|
+
}, this.lpDelayMs);
|
|
2983
|
+
}
|
|
2894
2984
|
this.canvas.focus();
|
|
2895
2985
|
const [sx, sy] = localXY(e);
|
|
2896
2986
|
if (this.ovHit(sx, sy)) {
|
|
@@ -2960,13 +3050,13 @@ var SSDiagram = (() => {
|
|
|
2960
3050
|
if (node !== null) {
|
|
2961
3051
|
const add = e.shiftKey || e.ctrlKey || e.metaKey;
|
|
2962
3052
|
if (this.permissions.select) {
|
|
2963
|
-
if (add) {
|
|
3053
|
+
if (add && e.button === 0) {
|
|
2964
3054
|
this.toggleSelect(node);
|
|
2965
3055
|
return;
|
|
2966
3056
|
}
|
|
2967
3057
|
if (!this.selectedNodes.has(node)) this.selectNode(node);
|
|
2968
3058
|
}
|
|
2969
|
-
if (this.permissions.moveNodes) {
|
|
3059
|
+
if (this.permissions.moveNodes && e.button === 0) {
|
|
2970
3060
|
this.dragNode = node;
|
|
2971
3061
|
this.dragAnchor = { wx, wy };
|
|
2972
3062
|
const moving = this.selectedNodes.has(node) ? [...this.selectedNodes] : [node];
|
|
@@ -2981,6 +3071,7 @@ var SSDiagram = (() => {
|
|
|
2981
3071
|
if (this.permissions.select) this.selectLink(link);
|
|
2982
3072
|
return;
|
|
2983
3073
|
}
|
|
3074
|
+
if (e.button !== 0 && e.button !== 1) return;
|
|
2984
3075
|
const wantPan = !this.permissions.select || e.button === 1 || e.ctrlKey || e.altKey || e.pointerType === "touch";
|
|
2985
3076
|
if (wantPan) {
|
|
2986
3077
|
if (this.permissions.select && !e.shiftKey) this.selectNode(null);
|
|
@@ -3104,7 +3195,10 @@ var SSDiagram = (() => {
|
|
|
3104
3195
|
this.relinkCandidate = null;
|
|
3105
3196
|
if (this.dragNode !== null) {
|
|
3106
3197
|
const moves = this.dragStart.map((it) => ({ id: it.n.id, fromX: it.x, fromY: it.y, toX: it.n.x, toY: it.n.y })).filter((m) => m.fromX !== m.toX || m.fromY !== m.toY);
|
|
3107
|
-
|
|
3198
|
+
const moved = new Set(moves.map((m) => m.id));
|
|
3199
|
+
for (const it of this.dragStart) {
|
|
3200
|
+
if (moved.has(it.n.id)) this.emit("nodeMoved", { node: it.n });
|
|
3201
|
+
}
|
|
3108
3202
|
this.dragNode = null;
|
|
3109
3203
|
this.dragStart = [];
|
|
3110
3204
|
if (moves.length > 0) {
|
|
@@ -3191,6 +3285,7 @@ var SSDiagram = (() => {
|
|
|
3191
3285
|
const [sx, sy] = localXY(e);
|
|
3192
3286
|
const [wx, wy] = this.toWorld(sx, sy);
|
|
3193
3287
|
if (this.portAt(wx, wy) !== null) return;
|
|
3288
|
+
if (this.linkAt(wx, wy) !== null) return;
|
|
3194
3289
|
const node = this.nodeAt(wx, wy);
|
|
3195
3290
|
if (node !== null) {
|
|
3196
3291
|
if (this.permissions.inspect && node.openAction.length > 0) {
|
|
@@ -3233,6 +3328,8 @@ var SSDiagram = (() => {
|
|
|
3233
3328
|
this.linking = null;
|
|
3234
3329
|
this.relinking = null;
|
|
3235
3330
|
this.relinkCandidate = null;
|
|
3331
|
+
this.ovDragging = false;
|
|
3332
|
+
this.linkSnap = null;
|
|
3236
3333
|
}
|
|
3237
3334
|
}, { passive: false });
|
|
3238
3335
|
this.listen(this.canvas, "touchmove", (e) => {
|
|
@@ -3293,12 +3390,12 @@ var SSDiagram = (() => {
|
|
|
3293
3390
|
}
|
|
3294
3391
|
if (mod && this.permissions.history && e.code === "KeyZ" && !e.shiftKey) {
|
|
3295
3392
|
e.preventDefault();
|
|
3296
|
-
this.undo
|
|
3393
|
+
this.historyShortcut("undo");
|
|
3297
3394
|
return;
|
|
3298
3395
|
}
|
|
3299
3396
|
if (mod && this.permissions.history && (e.code === "KeyY" || e.code === "KeyZ" && e.shiftKey)) {
|
|
3300
3397
|
e.preventDefault();
|
|
3301
|
-
this.redo
|
|
3398
|
+
this.historyShortcut("redo");
|
|
3302
3399
|
return;
|
|
3303
3400
|
}
|
|
3304
3401
|
});
|
|
@@ -3376,17 +3473,23 @@ var SSDiagram = (() => {
|
|
|
3376
3473
|
}
|
|
3377
3474
|
ctx.restore();
|
|
3378
3475
|
if (options.overview) this.drawOverview();
|
|
3379
|
-
if (options.runtime) this.drawGlobalError();
|
|
3476
|
+
if (options.runtime) this.drawGlobalError(options.transient);
|
|
3380
3477
|
if (options.transient) this.drawTooltip();
|
|
3381
3478
|
if (options.transient && (this.introStart !== null || this.globalErrorFlashStart !== null || this.nodes.some((n2) => n2.errorFlashStart !== null)))
|
|
3382
3479
|
this.scheduleDraw();
|
|
3383
3480
|
}
|
|
3384
|
-
|
|
3481
|
+
/**
|
|
3482
|
+
* `transient` gates the flash exactly as it gates every other animation, which an export must
|
|
3483
|
+
* not see: it would bake in whatever point of the pulse it caught, so the same export taken
|
|
3484
|
+
* twice differed in opacity. The gate also keeps the export from consuming the animation --
|
|
3485
|
+
* clearing globalErrorFlashStart from an export cut the flash short on screen.
|
|
3486
|
+
*/
|
|
3487
|
+
drawGlobalError(transient) {
|
|
3385
3488
|
const error = this.runtimeState.globalError;
|
|
3386
3489
|
if (error === null) return;
|
|
3387
3490
|
const ctx = this.ctx;
|
|
3388
3491
|
let flashAlpha = 1;
|
|
3389
|
-
if (this.globalErrorFlashStart !== null) {
|
|
3492
|
+
if (transient && this.globalErrorFlashStart !== null) {
|
|
3390
3493
|
const elapsed = performance.now() - this.globalErrorFlashStart;
|
|
3391
3494
|
if (elapsed >= ERROR_FLASH_MS) {
|
|
3392
3495
|
this.globalErrorFlashStart = null;
|
|
@@ -3569,7 +3672,7 @@ ${runtime.error}`;
|
|
|
3569
3672
|
const ctx = this.ctx;
|
|
3570
3673
|
const hasLoadError = options.runtime && n2.loadError.length > 0;
|
|
3571
3674
|
const hasRuntimeError = options.runtime && n2.runtimeError.length > 0;
|
|
3572
|
-
const runtime = options.runtime ? this.runtimeState.nodes
|
|
3675
|
+
const runtime = options.runtime ? getJsonKey(this.runtimeState.nodes, n2.id) : void 0;
|
|
3573
3676
|
const active = options.runtime && (this.runtimeState.activeNodeId === n2.id || runtime?.active === true);
|
|
3574
3677
|
roundRect(ctx, n2.x, n2.y, n2.w, n2.h, 6);
|
|
3575
3678
|
ctx.fillStyle = hasLoadError ? ERROR_BACKGROUND : active ? "#ffd1dc" : n2.color;
|
|
@@ -3657,7 +3760,7 @@ ${runtime.error}`;
|
|
|
3657
3760
|
}
|
|
3658
3761
|
}
|
|
3659
3762
|
portRuntimeState(node, port) {
|
|
3660
|
-
return this.runtimeState.nodes
|
|
3763
|
+
return getJsonKey(this.runtimeState.nodes, node.id)?.ports[port.direction][port.id] ?? null;
|
|
3661
3764
|
}
|
|
3662
3765
|
// Symmetric jump-over: any segment of THIS link hops the perpendicular
|
|
3663
3766
|
// segments of links drawn before it (H over earlier V, V over earlier
|
|
@@ -4245,6 +4348,9 @@ ${runtime.error}`;
|
|
|
4245
4348
|
const id = "group" in item ? item.group : item.command;
|
|
4246
4349
|
if (SEPARATE_AFTER.has(id) && index < items.length - 1) root.appendChild(this.separator(owner));
|
|
4247
4350
|
});
|
|
4351
|
+
this.on(root, "contextmenu", (event) => {
|
|
4352
|
+
event.preventDefault();
|
|
4353
|
+
});
|
|
4248
4354
|
this.container.appendChild(root);
|
|
4249
4355
|
this.root = root;
|
|
4250
4356
|
this.place(root, x, y);
|
|
@@ -4374,6 +4480,16 @@ ${runtime.error}`;
|
|
|
4374
4480
|
this.submenuTriggers.get(this.submenu)?.setAttribute("aria-expanded", "false");
|
|
4375
4481
|
this.submenu = null;
|
|
4376
4482
|
}
|
|
4483
|
+
/**
|
|
4484
|
+
* The window the menu actually lives in. A component created from one document and mounted
|
|
4485
|
+
* into another -- a same-origin iframe, a popup -- must be measured and dismissed against
|
|
4486
|
+
* that document's window, not the one this module happened to be evaluated in.
|
|
4487
|
+
*/
|
|
4488
|
+
get view() {
|
|
4489
|
+
const owner = this.container.ownerDocument ?? (typeof document === "undefined" ? null : document);
|
|
4490
|
+
const view = owner?.defaultView ?? (typeof window === "undefined" ? null : window);
|
|
4491
|
+
return view ?? null;
|
|
4492
|
+
}
|
|
4377
4493
|
// Reads the resolved writing direction so the submenu's side and its arrow agree with it.
|
|
4378
4494
|
rtl() {
|
|
4379
4495
|
if (typeof getComputedStyle !== "function") return false;
|
|
@@ -4385,13 +4501,14 @@ ${runtime.error}`;
|
|
|
4385
4501
|
}
|
|
4386
4502
|
/** Keeps the panel inside the viewport, flipping it left when it would run off the edge. */
|
|
4387
4503
|
keepOnScreen(panel) {
|
|
4388
|
-
|
|
4504
|
+
const view = this.view;
|
|
4505
|
+
if (typeof panel.getBoundingClientRect !== "function" || view === null) return;
|
|
4389
4506
|
const box = panel.getBoundingClientRect();
|
|
4390
|
-
if (box.right >
|
|
4507
|
+
if (box.right > view.innerWidth - 4 || box.left < 4) {
|
|
4391
4508
|
panel.style.insetInlineStart = "auto";
|
|
4392
4509
|
panel.style.insetInlineEnd = "100%";
|
|
4393
4510
|
}
|
|
4394
|
-
if (box.bottom >
|
|
4511
|
+
if (box.bottom > view.innerHeight - 4) {
|
|
4395
4512
|
panel.style.top = "auto";
|
|
4396
4513
|
panel.style.bottom = "-5px";
|
|
4397
4514
|
}
|
|
@@ -4399,10 +4516,11 @@ ${runtime.error}`;
|
|
|
4399
4516
|
place(root, x, y) {
|
|
4400
4517
|
root.style.left = `${x}px`;
|
|
4401
4518
|
root.style.top = `${y}px`;
|
|
4402
|
-
|
|
4519
|
+
const view = this.view;
|
|
4520
|
+
if (typeof root.getBoundingClientRect !== "function" || view === null) return;
|
|
4403
4521
|
const box = root.getBoundingClientRect();
|
|
4404
|
-
if (box.right >
|
|
4405
|
-
if (box.bottom >
|
|
4522
|
+
if (box.right > view.innerWidth - 4) root.style.left = `${Math.max(4, x - box.width)}px`;
|
|
4523
|
+
if (box.bottom > view.innerHeight - 4) root.style.top = `${Math.max(4, y - box.height)}px`;
|
|
4406
4524
|
}
|
|
4407
4525
|
watchForDismissal(owner) {
|
|
4408
4526
|
const dismiss = () => this.hide();
|
|
@@ -4416,10 +4534,11 @@ ${runtime.error}`;
|
|
|
4416
4534
|
this.on(owner, "keydown", (event) => {
|
|
4417
4535
|
if (event.key === "Escape") this.hide();
|
|
4418
4536
|
});
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
this.on(
|
|
4422
|
-
this.on(
|
|
4537
|
+
const view = this.view;
|
|
4538
|
+
if (view !== null) {
|
|
4539
|
+
this.on(view, "blur", dismiss);
|
|
4540
|
+
this.on(view, "resize", dismiss);
|
|
4541
|
+
this.on(view, "scroll", dismiss, true);
|
|
4423
4542
|
}
|
|
4424
4543
|
}
|
|
4425
4544
|
on(target, type, listener, capture = false) {
|
|
@@ -4458,7 +4577,8 @@ ${runtime.error}`;
|
|
|
4458
4577
|
if (set === void 0) {
|
|
4459
4578
|
return;
|
|
4460
4579
|
}
|
|
4461
|
-
for (const handler of set) {
|
|
4580
|
+
for (const handler of [...set]) {
|
|
4581
|
+
if (!set.has(handler)) continue;
|
|
4462
4582
|
try {
|
|
4463
4583
|
handler(payload);
|
|
4464
4584
|
} catch (err) {
|
|
@@ -4474,11 +4594,23 @@ ${runtime.error}`;
|
|
|
4474
4594
|
return (this.handlers.get(event)?.size ?? 0) > 0;
|
|
4475
4595
|
}
|
|
4476
4596
|
clearEventHandlers() {
|
|
4597
|
+
for (const set of this.handlers.values()) set.clear();
|
|
4477
4598
|
this.handlers.clear();
|
|
4478
4599
|
}
|
|
4479
4600
|
};
|
|
4480
4601
|
|
|
4481
4602
|
// src/diagram/types.ts
|
|
4603
|
+
function copyJsonValue2(value) {
|
|
4604
|
+
if (Array.isArray(value)) return value.map(copyJsonValue2);
|
|
4605
|
+
if (value !== null && typeof value === "object") return copyJsonObject2(value);
|
|
4606
|
+
return value;
|
|
4607
|
+
}
|
|
4608
|
+
function copyJsonObject2(value) {
|
|
4609
|
+
if (value === void 0) return {};
|
|
4610
|
+
const result = {};
|
|
4611
|
+
for (const [key, item] of Object.entries(value)) setJsonKey(result, key, copyJsonValue2(item));
|
|
4612
|
+
return result;
|
|
4613
|
+
}
|
|
4482
4614
|
var PortType = class {
|
|
4483
4615
|
constructor(init) {
|
|
4484
4616
|
this.name = init.name;
|
|
@@ -4516,7 +4648,7 @@ ${runtime.error}`;
|
|
|
4516
4648
|
this.id = init.id;
|
|
4517
4649
|
this.name = init.name;
|
|
4518
4650
|
this.description = init.description ?? "";
|
|
4519
|
-
this.groupName = init.groupName ??
|
|
4651
|
+
this.groupName = init.groupName ?? DEFAULT_GROUP_NAME;
|
|
4520
4652
|
this.inPorts = (init.inPorts ?? []).map((p) => p instanceof Port ? p : new Port(p));
|
|
4521
4653
|
this.outPorts = (init.outPorts ?? []).map((p) => p instanceof Port ? p : new Port(p));
|
|
4522
4654
|
this.icon = init.icon ?? "";
|
|
@@ -4571,6 +4703,7 @@ ${runtime.error}`;
|
|
|
4571
4703
|
});
|
|
4572
4704
|
}
|
|
4573
4705
|
};
|
|
4706
|
+
var DEFAULT_GROUP_NAME = "Common";
|
|
4574
4707
|
var Link = class {
|
|
4575
4708
|
constructor(init) {
|
|
4576
4709
|
this.id = init.id ?? "";
|
|
@@ -4578,7 +4711,7 @@ ${runtime.error}`;
|
|
|
4578
4711
|
this.outPort = init.outPort;
|
|
4579
4712
|
this.inNode = init.inNode;
|
|
4580
4713
|
this.inPort = init.inPort;
|
|
4581
|
-
this.metadata = init.metadata
|
|
4714
|
+
this.metadata = copyJsonObject2(init.metadata);
|
|
4582
4715
|
}
|
|
4583
4716
|
};
|
|
4584
4717
|
|
|
@@ -4634,6 +4767,10 @@ ${runtime.error}`;
|
|
|
4634
4767
|
this.fullscreenButton = this.createFullscreenButton();
|
|
4635
4768
|
this.updateFullscreenButton();
|
|
4636
4769
|
this.setContextMenuEnabled(options.showContextMenu ?? true);
|
|
4770
|
+
this.canvas.setHistoryShortcutHandler((direction) => {
|
|
4771
|
+
if (direction === "undo") this.undo();
|
|
4772
|
+
else this.redo();
|
|
4773
|
+
});
|
|
4637
4774
|
this.registerContextActions();
|
|
4638
4775
|
this.bindCanvasEvents();
|
|
4639
4776
|
this.disposables.push(this.catalog.on("portTypesChanged", () => this.applySocketTheme()));
|
|
@@ -5744,7 +5881,8 @@ ${runtime.error}`;
|
|
|
5744
5881
|
}
|
|
5745
5882
|
};
|
|
5746
5883
|
function groupNameOf(node) {
|
|
5747
|
-
|
|
5884
|
+
const group = node.groupName;
|
|
5885
|
+
return group.length > 0 && group !== DEFAULT_GROUP_NAME ? group : t("paletteCommonGroup", DEFAULT_GROUP_NAME);
|
|
5748
5886
|
}
|
|
5749
5887
|
function normalizeTypeId(typeId) {
|
|
5750
5888
|
return typeId.toLowerCase();
|
|
@@ -5762,12 +5900,43 @@ ${runtime.error}`;
|
|
|
5762
5900
|
function iconUrl(name) {
|
|
5763
5901
|
return name ? `/diagram-icons/${name}.svg` : "";
|
|
5764
5902
|
}
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5903
|
+
var CSS_COLOR_KEYWORDS = {
|
|
5904
|
+
white: [255, 255, 255],
|
|
5905
|
+
black: [0, 0, 0],
|
|
5906
|
+
transparent: [255, 255, 255]
|
|
5907
|
+
};
|
|
5908
|
+
function luminance(color) {
|
|
5909
|
+
const value = color.trim().toLowerCase();
|
|
5910
|
+
if (value === "")
|
|
5911
|
+
return -1;
|
|
5912
|
+
const keyword = CSS_COLOR_KEYWORDS[value];
|
|
5913
|
+
if (keyword !== void 0)
|
|
5914
|
+
return 0.299 * keyword[0] + 0.587 * keyword[1] + 0.114 * keyword[2];
|
|
5915
|
+
const long = value.match(/^#?([0-9a-f]{6})$/);
|
|
5916
|
+
if (long !== null) {
|
|
5917
|
+
const n2 = parseInt(long[1], 16);
|
|
5918
|
+
return 0.299 * (n2 >> 16 & 255) + 0.587 * (n2 >> 8 & 255) + 0.114 * (n2 & 255);
|
|
5919
|
+
}
|
|
5920
|
+
const short = value.match(/^#?([0-9a-f]{3})([0-9a-f])?$/);
|
|
5921
|
+
if (short !== null) {
|
|
5922
|
+
const [r, g, b] = [...short[1]].map((digit) => parseInt(digit + digit, 16));
|
|
5923
|
+
return 0.299 * r + 0.587 * g + 0.114 * b;
|
|
5924
|
+
}
|
|
5925
|
+
const long8 = value.match(/^#?([0-9a-f]{6})[0-9a-f]{2}$/);
|
|
5926
|
+
if (long8 !== null) {
|
|
5927
|
+
const n2 = parseInt(long8[1], 16);
|
|
5928
|
+
return 0.299 * (n2 >> 16 & 255) + 0.587 * (n2 >> 8 & 255) + 0.114 * (n2 & 255);
|
|
5929
|
+
}
|
|
5930
|
+
const rgb = value.match(/^rgba?\(([^)]+)\)$/);
|
|
5931
|
+
if (rgb !== null) {
|
|
5932
|
+
const parts = rgb[1].split(/[\s,/]+/).filter((part) => part !== "");
|
|
5933
|
+
if (parts.length >= 3) {
|
|
5934
|
+
const [r, g, b] = parts.slice(0, 3).map((part) => part.endsWith("%") ? parseFloat(part) / 100 * 255 : parseFloat(part));
|
|
5935
|
+
if ([r, g, b].every((channel) => Number.isFinite(channel)))
|
|
5936
|
+
return 0.299 * r + 0.587 * g + 0.114 * b;
|
|
5937
|
+
}
|
|
5938
|
+
}
|
|
5939
|
+
return -1;
|
|
5771
5940
|
}
|
|
5772
5941
|
function makePort(p) {
|
|
5773
5942
|
return new Port({
|
|
@@ -6022,6 +6191,7 @@ ${runtime.error}`;
|
|
|
6022
6191
|
diagram.setTheme({
|
|
6023
6192
|
diagramBackground: bg,
|
|
6024
6193
|
gridColor,
|
|
6194
|
+
// Unreadable colours keep the dark default rather than being treated as black.
|
|
6025
6195
|
linkMaxLightness: luminance(bg) > 140 ? 0.42 : 1
|
|
6026
6196
|
});
|
|
6027
6197
|
};
|
|
@@ -6182,16 +6352,25 @@ ${runtime.error}`;
|
|
|
6182
6352
|
root.querySelectorAll(".ss-diagram-host").forEach((host) => {
|
|
6183
6353
|
if (host.dataset.rendered === "1")
|
|
6184
6354
|
return;
|
|
6355
|
+
const started = (run) => {
|
|
6356
|
+
host.dataset.rendered = "1";
|
|
6357
|
+
void run.then(
|
|
6358
|
+
(handle) => {
|
|
6359
|
+
if (handle === null) delete host.dataset.rendered;
|
|
6360
|
+
},
|
|
6361
|
+
() => {
|
|
6362
|
+
delete host.dataset.rendered;
|
|
6363
|
+
}
|
|
6364
|
+
);
|
|
6365
|
+
};
|
|
6185
6366
|
const src = host.dataset.diagramSrc;
|
|
6186
6367
|
if (src) {
|
|
6187
|
-
host
|
|
6188
|
-
void renderFromSource(host, PALETTE_URL, src, options);
|
|
6368
|
+
started(renderFromSource(host, PALETTE_URL, src, options));
|
|
6189
6369
|
return;
|
|
6190
6370
|
}
|
|
6191
6371
|
const inline = host.querySelector('script[type="application/json"]');
|
|
6192
6372
|
if (inline) {
|
|
6193
|
-
host.
|
|
6194
|
-
void renderFromInline(host, PALETTE_URL, inline.textContent ?? "", options);
|
|
6373
|
+
started(renderFromInline(host, PALETTE_URL, inline.textContent ?? "", options));
|
|
6195
6374
|
}
|
|
6196
6375
|
});
|
|
6197
6376
|
}
|