@crazyhappyone/auto-graph 0.0.1 → 0.0.2
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 +2 -2
- package/README.zh-CN.md +2 -2
- package/dist/cli/index.cjs +910 -66
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +909 -66
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +849 -73
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +87 -1
- package/dist/index.d.ts +87 -1
- package/dist/index.js +847 -74
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.cjs
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var module$1 = require('module');
|
|
3
4
|
var pretext = require('@chenglou/pretext');
|
|
4
5
|
var buffer = require('buffer');
|
|
5
6
|
var yaml = require('yaml');
|
|
6
7
|
var zod = require('zod');
|
|
7
8
|
var dagre = require('@dagrejs/dagre');
|
|
8
9
|
|
|
10
|
+
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
9
11
|
// src/geometry/boxes.ts
|
|
10
12
|
function normalizeInsets(input = 0) {
|
|
11
13
|
if (typeof input === "number") {
|
|
@@ -822,34 +824,34 @@ function assertFiniteNonNegative(value, label) {
|
|
|
822
824
|
throw new TypeError(`${label} must be a finite non-negative width`);
|
|
823
825
|
}
|
|
824
826
|
}
|
|
825
|
-
function validateTextStyle(
|
|
826
|
-
assertFinitePositive(
|
|
827
|
-
if (
|
|
828
|
-
assertFinitePositive(
|
|
827
|
+
function validateTextStyle(style2) {
|
|
828
|
+
assertFinitePositive(style2.fontSize, "fontSize");
|
|
829
|
+
if (style2.lineHeight !== void 0) {
|
|
830
|
+
assertFinitePositive(style2.lineHeight, "lineHeight");
|
|
829
831
|
}
|
|
830
|
-
if (
|
|
832
|
+
if (style2.letterSpacing !== void 0 && !Number.isFinite(style2.letterSpacing)) {
|
|
831
833
|
throw new TypeError("letterSpacing must be finite");
|
|
832
834
|
}
|
|
833
835
|
}
|
|
834
|
-
function resolveLineHeight(
|
|
835
|
-
validateTextStyle(
|
|
836
|
-
return
|
|
836
|
+
function resolveLineHeight(style2) {
|
|
837
|
+
validateTextStyle(style2);
|
|
838
|
+
return style2.lineHeight ?? style2.fontSize * 1.2;
|
|
837
839
|
}
|
|
838
|
-
function toCanvasFont(
|
|
839
|
-
validateTextStyle(
|
|
840
|
-
const fontStyle =
|
|
841
|
-
const fontWeight =
|
|
842
|
-
return `${fontStyle}${fontWeight} ${
|
|
840
|
+
function toCanvasFont(style2) {
|
|
841
|
+
validateTextStyle(style2);
|
|
842
|
+
const fontStyle = style2.fontStyle === "italic" ? "italic " : "";
|
|
843
|
+
const fontWeight = style2.fontWeight ?? 400;
|
|
844
|
+
return `${fontStyle}${fontWeight} ${style2.fontSize}px ${style2.fontFamily}`;
|
|
843
845
|
}
|
|
844
846
|
|
|
845
847
|
// src/text/fallback.ts
|
|
846
848
|
var DeterministicTextMeasurer = class {
|
|
847
|
-
prepare(text,
|
|
848
|
-
validateTextStyle(
|
|
849
|
+
prepare(text, style2) {
|
|
850
|
+
validateTextStyle(style2);
|
|
849
851
|
return {
|
|
850
852
|
text,
|
|
851
|
-
font: toCanvasFont(
|
|
852
|
-
style: { ...
|
|
853
|
+
font: toCanvasFont(style2),
|
|
854
|
+
style: { ...style2 },
|
|
853
855
|
backend: "deterministic"
|
|
854
856
|
};
|
|
855
857
|
}
|
|
@@ -908,9 +910,9 @@ var DeterministicTextMeasurer = class {
|
|
|
908
910
|
return output;
|
|
909
911
|
}
|
|
910
912
|
};
|
|
911
|
-
function getCharacterWidth(
|
|
912
|
-
const letterSpacing =
|
|
913
|
-
return Math.max(0,
|
|
913
|
+
function getCharacterWidth(style2) {
|
|
914
|
+
const letterSpacing = style2.letterSpacing ?? 0;
|
|
915
|
+
return Math.max(0, style2.fontSize * 0.6 + letterSpacing);
|
|
914
916
|
}
|
|
915
917
|
function createLine(text, width, segmentIndex, start, end) {
|
|
916
918
|
return {
|
|
@@ -931,27 +933,53 @@ function assertFinitePositiveLineHeight(lineHeight) {
|
|
|
931
933
|
throw new TypeError("lineHeight must be finite and positive");
|
|
932
934
|
}
|
|
933
935
|
}
|
|
936
|
+
var require2 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
|
|
937
|
+
function installNodeCanvasRuntime(loadNodeCanvasModule = loadDefaultNodeCanvasModule) {
|
|
938
|
+
if (typeof globalThis.OffscreenCanvas === "function") {
|
|
939
|
+
return true;
|
|
940
|
+
}
|
|
941
|
+
try {
|
|
942
|
+
const canvasModule = loadNodeCanvasModule();
|
|
943
|
+
const { createCanvas } = canvasModule;
|
|
944
|
+
const NodeOffscreenCanvas = class {
|
|
945
|
+
canvas;
|
|
946
|
+
constructor(width, height) {
|
|
947
|
+
this.canvas = createCanvas(width, height);
|
|
948
|
+
}
|
|
949
|
+
getContext(contextId) {
|
|
950
|
+
return contextId === "2d" ? this.canvas.getContext("2d") : null;
|
|
951
|
+
}
|
|
952
|
+
};
|
|
953
|
+
globalThis.OffscreenCanvas = NodeOffscreenCanvas;
|
|
954
|
+
return true;
|
|
955
|
+
} catch {
|
|
956
|
+
return false;
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
function loadDefaultNodeCanvasModule() {
|
|
960
|
+
return require2("@napi-rs/canvas");
|
|
961
|
+
}
|
|
934
962
|
var RUNTIME_UNAVAILABLE = "text.pretext.runtime-unavailable";
|
|
935
963
|
function isPretextRuntimeAvailable() {
|
|
936
964
|
return typeof Intl.Segmenter === "function" && typeof globalThis.OffscreenCanvas === "function";
|
|
937
965
|
}
|
|
938
966
|
var PretextTextMeasurer = class {
|
|
939
|
-
prepare(text,
|
|
967
|
+
prepare(text, style2) {
|
|
940
968
|
if (!isPretextRuntimeAvailable()) {
|
|
941
969
|
throw new TypeError(RUNTIME_UNAVAILABLE);
|
|
942
970
|
}
|
|
943
|
-
validateTextStyle(
|
|
944
|
-
const font = toCanvasFont(
|
|
971
|
+
validateTextStyle(style2);
|
|
972
|
+
const font = toCanvasFont(style2);
|
|
945
973
|
const options = {
|
|
946
|
-
...
|
|
947
|
-
...
|
|
948
|
-
...
|
|
974
|
+
...style2.whiteSpace === void 0 ? {} : { whiteSpace: style2.whiteSpace },
|
|
975
|
+
...style2.wordBreak === void 0 ? {} : { wordBreak: style2.wordBreak },
|
|
976
|
+
...style2.letterSpacing === void 0 ? {} : { letterSpacing: style2.letterSpacing }
|
|
949
977
|
};
|
|
950
978
|
const prepared = pretext.prepareWithSegments(text, font, options);
|
|
951
979
|
return {
|
|
952
980
|
text,
|
|
953
981
|
font,
|
|
954
|
-
style: { ...
|
|
982
|
+
style: { ...style2 },
|
|
955
983
|
backend: "pretext",
|
|
956
984
|
pretextPrepared: prepared
|
|
957
985
|
};
|
|
@@ -1001,6 +1029,13 @@ function toInternalPrepared(prepared) {
|
|
|
1001
1029
|
return prepared.pretextPrepared;
|
|
1002
1030
|
}
|
|
1003
1031
|
|
|
1032
|
+
// src/text/default.ts
|
|
1033
|
+
function createDefaultTextMeasurer(options = {}) {
|
|
1034
|
+
const installRuntime = options.installNodeCanvasRuntime ?? installNodeCanvasRuntime;
|
|
1035
|
+
installRuntime();
|
|
1036
|
+
return isPretextRuntimeAvailable() ? new PretextTextMeasurer() : new DeterministicTextMeasurer();
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1004
1039
|
// src/labels/fit.ts
|
|
1005
1040
|
function fitLabel(text, options, measurer) {
|
|
1006
1041
|
return computeLabelLayout(text, options, measurer);
|
|
@@ -1067,6 +1102,7 @@ function computeLabelLayout(text, options, measurer) {
|
|
|
1067
1102
|
fittedSize,
|
|
1068
1103
|
padding,
|
|
1069
1104
|
font: { ...options.font },
|
|
1105
|
+
textBackend: prepared.backend,
|
|
1070
1106
|
lineHeight,
|
|
1071
1107
|
lines: buildLines(textLayout, contentBox2, lineHeight),
|
|
1072
1108
|
overflow,
|
|
@@ -1161,8 +1197,9 @@ function normalizeDiagramDsl(dslValue, options = {}) {
|
|
|
1161
1197
|
...outputResult(dsl)
|
|
1162
1198
|
};
|
|
1163
1199
|
}
|
|
1164
|
-
const measurer = options.textMeasurer ??
|
|
1200
|
+
const measurer = options.textMeasurer ?? createDefaultTextMeasurer();
|
|
1165
1201
|
const routeKind = dsl.routing?.kind ?? "orthogonal";
|
|
1202
|
+
const portShifting = normalizePortShifting(dsl.routing?.portShifting);
|
|
1166
1203
|
const diagram = {
|
|
1167
1204
|
id: options.id ?? dsl.id ?? "diagram",
|
|
1168
1205
|
...dsl.title === void 0 ? {} : { title: dsl.title },
|
|
@@ -1170,9 +1207,14 @@ function normalizeDiagramDsl(dslValue, options = {}) {
|
|
|
1170
1207
|
nodes: normalizeNodes(dsl, measurer),
|
|
1171
1208
|
edges: normalizeEdges(dsl),
|
|
1172
1209
|
groups: normalizeGroups(dsl, measurer),
|
|
1210
|
+
swimlanes: normalizeSwimlanes(dsl),
|
|
1173
1211
|
constraints: normalizeConstraints(dsl),
|
|
1174
1212
|
diagnostics: [],
|
|
1175
|
-
|
|
1213
|
+
...dsl.frame === void 0 ? {} : { frame: normalizeFrame(dsl.frame) },
|
|
1214
|
+
metadata: {
|
|
1215
|
+
routeKind,
|
|
1216
|
+
...portShifting === void 0 ? {} : { portShifting }
|
|
1217
|
+
}
|
|
1176
1218
|
};
|
|
1177
1219
|
return {
|
|
1178
1220
|
diagram,
|
|
@@ -1180,6 +1222,15 @@ function normalizeDiagramDsl(dslValue, options = {}) {
|
|
|
1180
1222
|
...outputResult(dsl)
|
|
1181
1223
|
};
|
|
1182
1224
|
}
|
|
1225
|
+
function normalizePortShifting(portShifting) {
|
|
1226
|
+
if (portShifting === void 0) {
|
|
1227
|
+
return void 0;
|
|
1228
|
+
}
|
|
1229
|
+
return {
|
|
1230
|
+
...portShifting.enabled === void 0 ? {} : { enabled: portShifting.enabled },
|
|
1231
|
+
...portShifting.spacing === void 0 ? {} : { spacing: portShifting.spacing }
|
|
1232
|
+
};
|
|
1233
|
+
}
|
|
1183
1234
|
function outputResult(dsl) {
|
|
1184
1235
|
return dsl.output?.format === void 0 ? {} : { output: { format: dsl.output.format } };
|
|
1185
1236
|
}
|
|
@@ -1189,15 +1240,24 @@ function normalizeNodes(dsl, measurer) {
|
|
|
1189
1240
|
const label = toLabel(node?.label);
|
|
1190
1241
|
const labelLayout = label === void 0 ? void 0 : fitDslLabel(label, measurer);
|
|
1191
1242
|
const fittedSize = labelLayout?.fittedSize;
|
|
1243
|
+
const nodeCompartments = node?.compartments === void 0 ? void 0 : compartments(node.compartments);
|
|
1244
|
+
const compartmentWidth = nodeCompartments === void 0 ? 0 : compartmentNaturalWidth(id, label, nodeCompartments, measurer);
|
|
1192
1245
|
return {
|
|
1193
1246
|
id,
|
|
1194
1247
|
...label === void 0 ? {} : { label },
|
|
1195
1248
|
shape: node?.shape ?? "rectangle",
|
|
1196
1249
|
...node?.position === void 0 ? {} : { position: point(node.position) },
|
|
1250
|
+
...node?.style === void 0 ? {} : { style: style(node.style) },
|
|
1251
|
+
...node?.ports === void 0 ? {} : { ports: normalizePorts(node.ports) },
|
|
1252
|
+
...nodeCompartments === void 0 ? {} : { compartments: nodeCompartments },
|
|
1197
1253
|
size: {
|
|
1198
|
-
width: Math.max(
|
|
1254
|
+
width: Math.max(
|
|
1255
|
+
DEFAULT_NODE_MIN_SIZE.width,
|
|
1256
|
+
fittedSize?.width ?? 0,
|
|
1257
|
+
compartmentWidth
|
|
1258
|
+
),
|
|
1199
1259
|
height: Math.max(
|
|
1200
|
-
DEFAULT_NODE_MIN_SIZE.height,
|
|
1260
|
+
nodeCompartments === void 0 ? DEFAULT_NODE_MIN_SIZE.height : compartmentHeight(nodeCompartments),
|
|
1201
1261
|
fittedSize?.height ?? 0
|
|
1202
1262
|
)
|
|
1203
1263
|
},
|
|
@@ -1206,11 +1266,42 @@ function normalizeNodes(dsl, measurer) {
|
|
|
1206
1266
|
};
|
|
1207
1267
|
});
|
|
1208
1268
|
}
|
|
1269
|
+
function compartmentHeight(value) {
|
|
1270
|
+
const rowCount = (value.stereotype === void 0 ? 0 : 1) + 1 + (value.properties?.length ?? 0) + (value.constraints?.length ?? 0);
|
|
1271
|
+
const rowHeight = 16;
|
|
1272
|
+
const verticalPadding = 20;
|
|
1273
|
+
return Math.max(
|
|
1274
|
+
DEFAULT_NODE_MIN_SIZE.height,
|
|
1275
|
+
rowCount * rowHeight + verticalPadding
|
|
1276
|
+
);
|
|
1277
|
+
}
|
|
1278
|
+
function compartmentNaturalWidth(id, label, value, measurer) {
|
|
1279
|
+
const rows = compartmentRows(id, label, value);
|
|
1280
|
+
const maxRowWidth = rows.reduce((width, row) => {
|
|
1281
|
+
const prepared = measurer.prepare(row, DEFAULT_FONT);
|
|
1282
|
+
return Math.max(width, measurer.naturalWidth(prepared));
|
|
1283
|
+
}, 0);
|
|
1284
|
+
return Math.ceil(
|
|
1285
|
+
maxRowWidth + DEFAULT_NODE_PADDING.left + DEFAULT_NODE_PADDING.right
|
|
1286
|
+
);
|
|
1287
|
+
}
|
|
1288
|
+
function compartmentRows(id, label, value) {
|
|
1289
|
+
return [
|
|
1290
|
+
...value.stereotype === void 0 ? [] : [value.stereotype],
|
|
1291
|
+
value.name ?? label?.text ?? id,
|
|
1292
|
+
...value.properties ?? [],
|
|
1293
|
+
...value.constraints ?? []
|
|
1294
|
+
];
|
|
1295
|
+
}
|
|
1209
1296
|
function normalizeEdges(dsl) {
|
|
1210
1297
|
const counts = /* @__PURE__ */ new Map();
|
|
1211
1298
|
return (dsl.edges ?? []).map((edge) => {
|
|
1212
|
-
const
|
|
1213
|
-
const
|
|
1299
|
+
const source = typeof edge === "string" ? void 0 : edge.source;
|
|
1300
|
+
const target = typeof edge === "string" ? void 0 : edge.target;
|
|
1301
|
+
const sourceId = typeof edge === "string" ? "" : edge.sourceId ?? endpointNodeId(source) ?? "";
|
|
1302
|
+
const targetId = typeof edge === "string" ? "" : edge.targetId ?? endpointNodeId(target) ?? "";
|
|
1303
|
+
const sourceEndpoint = typeof edge === "string" ? { nodeId: sourceId } : endpoint(source, edge.sourceId);
|
|
1304
|
+
const targetEndpoint = typeof edge === "string" ? { nodeId: targetId } : endpoint(target, edge.targetId);
|
|
1214
1305
|
const baseId = `${sourceId}-${targetId}`;
|
|
1215
1306
|
const count = counts.get(baseId) ?? 0;
|
|
1216
1307
|
counts.set(baseId, count + 1);
|
|
@@ -1218,9 +1309,96 @@ function normalizeEdges(dsl) {
|
|
|
1218
1309
|
const label = typeof edge === "string" ? void 0 : toLabel(edge.label);
|
|
1219
1310
|
return {
|
|
1220
1311
|
id,
|
|
1221
|
-
source:
|
|
1222
|
-
target:
|
|
1223
|
-
...label === void 0 ? {} : { label }
|
|
1312
|
+
source: sourceEndpoint,
|
|
1313
|
+
target: targetEndpoint,
|
|
1314
|
+
...label === void 0 ? {} : { label },
|
|
1315
|
+
...typeof edge === "string" || edge.style === void 0 ? {} : { style: edge.style },
|
|
1316
|
+
...typeof edge === "string" || edge.arrowhead === void 0 ? {} : { arrowhead: edge.arrowhead }
|
|
1317
|
+
};
|
|
1318
|
+
});
|
|
1319
|
+
}
|
|
1320
|
+
function normalizePorts(ports) {
|
|
1321
|
+
return Object.keys(ports ?? {}).sort().map((id) => {
|
|
1322
|
+
const port = ports?.[id];
|
|
1323
|
+
const label = toLabel(port?.label);
|
|
1324
|
+
return {
|
|
1325
|
+
id,
|
|
1326
|
+
...label === void 0 ? {} : { label },
|
|
1327
|
+
side: port?.side ?? "right",
|
|
1328
|
+
kind: port?.kind ?? "proxy",
|
|
1329
|
+
...port?.order === void 0 ? {} : { order: port.order },
|
|
1330
|
+
...port?.style === void 0 ? {} : { style: style(port.style) }
|
|
1331
|
+
};
|
|
1332
|
+
});
|
|
1333
|
+
}
|
|
1334
|
+
function endpoint(value, nodeIdOverride) {
|
|
1335
|
+
if (nodeIdOverride !== void 0) {
|
|
1336
|
+
return {
|
|
1337
|
+
nodeId: nodeIdOverride,
|
|
1338
|
+
...typeof value === "object" && value.node === nodeIdOverride && value.port !== void 0 ? { portId: value.port } : {}
|
|
1339
|
+
};
|
|
1340
|
+
}
|
|
1341
|
+
if (value === void 0) {
|
|
1342
|
+
return { nodeId: "" };
|
|
1343
|
+
}
|
|
1344
|
+
if (typeof value === "string") {
|
|
1345
|
+
return { nodeId: value };
|
|
1346
|
+
}
|
|
1347
|
+
return {
|
|
1348
|
+
nodeId: value.node,
|
|
1349
|
+
...value.port === void 0 ? {} : { portId: value.port }
|
|
1350
|
+
};
|
|
1351
|
+
}
|
|
1352
|
+
function style(value) {
|
|
1353
|
+
return {
|
|
1354
|
+
...value.fill === void 0 ? {} : { fill: value.fill },
|
|
1355
|
+
...value.stroke === void 0 ? {} : { stroke: value.stroke }
|
|
1356
|
+
};
|
|
1357
|
+
}
|
|
1358
|
+
function compartments(value) {
|
|
1359
|
+
return {
|
|
1360
|
+
...value.stereotype === void 0 ? {} : { stereotype: value.stereotype },
|
|
1361
|
+
...value.name === void 0 ? {} : { name: value.name },
|
|
1362
|
+
...value.properties === void 0 ? {} : { properties: value.properties.map(formatCompartmentEntry) },
|
|
1363
|
+
...value.constraints === void 0 ? {} : { constraints: [...value.constraints] }
|
|
1364
|
+
};
|
|
1365
|
+
}
|
|
1366
|
+
function normalizeFrame(frame) {
|
|
1367
|
+
return {
|
|
1368
|
+
kind: frame.kind,
|
|
1369
|
+
...frame.context === void 0 ? {} : { context: frame.context },
|
|
1370
|
+
...frame.name === void 0 ? {} : { name: frame.name },
|
|
1371
|
+
titleTab: frame.titleTab,
|
|
1372
|
+
...frame.style === void 0 ? {} : { style: style(frame.style) }
|
|
1373
|
+
};
|
|
1374
|
+
}
|
|
1375
|
+
function formatCompartmentEntry(value) {
|
|
1376
|
+
if (typeof value === "string") {
|
|
1377
|
+
return value;
|
|
1378
|
+
}
|
|
1379
|
+
const [entry] = Object.entries(value);
|
|
1380
|
+
if (entry === void 0) {
|
|
1381
|
+
return "";
|
|
1382
|
+
}
|
|
1383
|
+
return `${entry[0]}: ${entry[1]}`;
|
|
1384
|
+
}
|
|
1385
|
+
function normalizeSwimlanes(dsl) {
|
|
1386
|
+
return Object.keys(dsl.swimlanes ?? {}).sort().map((id) => {
|
|
1387
|
+
const swimlane = dsl.swimlanes?.[id];
|
|
1388
|
+
const label = toLabel(swimlane?.label);
|
|
1389
|
+
return {
|
|
1390
|
+
id,
|
|
1391
|
+
...label === void 0 ? {} : { label },
|
|
1392
|
+
orientation: swimlane?.orientation ?? "vertical",
|
|
1393
|
+
lanes: Object.keys(swimlane?.lanes ?? {}).sort().map((laneId) => {
|
|
1394
|
+
const lane = swimlane?.lanes[laneId];
|
|
1395
|
+
const laneLabel = toLabel(lane?.label);
|
|
1396
|
+
return {
|
|
1397
|
+
id: laneId,
|
|
1398
|
+
...laneLabel === void 0 ? {} : { label: laneLabel },
|
|
1399
|
+
children: [...lane?.children ?? []]
|
|
1400
|
+
};
|
|
1401
|
+
})
|
|
1224
1402
|
};
|
|
1225
1403
|
});
|
|
1226
1404
|
}
|
|
@@ -1290,18 +1468,37 @@ function validateReferences(dsl) {
|
|
|
1290
1468
|
const diagnostics = [];
|
|
1291
1469
|
const nodeIds = new Set(Object.keys(dsl.nodes));
|
|
1292
1470
|
const groupIds = new Set(Object.keys(dsl.groups ?? {}));
|
|
1471
|
+
const swimlaneLaneIds = new Set(
|
|
1472
|
+
Object.entries(dsl.swimlanes ?? {}).flatMap(
|
|
1473
|
+
([swimlaneId, swimlane]) => Object.keys(swimlane.lanes).map((laneId) => `${swimlaneId}.${laneId}`)
|
|
1474
|
+
)
|
|
1475
|
+
);
|
|
1293
1476
|
(dsl.edges ?? []).forEach((edge, index) => {
|
|
1294
1477
|
if (typeof edge === "string") {
|
|
1295
1478
|
return;
|
|
1296
1479
|
}
|
|
1297
|
-
const sourceId = edge.sourceId ?? edge.source;
|
|
1298
|
-
const targetId = edge.targetId ?? edge.target;
|
|
1480
|
+
const sourceId = edge.sourceId ?? endpointNodeId(edge.source);
|
|
1481
|
+
const targetId = edge.targetId ?? endpointNodeId(edge.target);
|
|
1482
|
+
const sourceEndpoint = endpoint(edge.source, edge.sourceId);
|
|
1483
|
+
const targetEndpoint = endpoint(edge.target, edge.targetId);
|
|
1299
1484
|
if (sourceId !== void 0 && !nodeIds.has(sourceId)) {
|
|
1300
1485
|
diagnostics.push(referenceMissing(["edges", index, "source"], sourceId));
|
|
1301
1486
|
}
|
|
1302
1487
|
if (targetId !== void 0 && !nodeIds.has(targetId)) {
|
|
1303
1488
|
diagnostics.push(referenceMissing(["edges", index, "target"], targetId));
|
|
1304
1489
|
}
|
|
1490
|
+
validateEndpointPort(
|
|
1491
|
+
dsl,
|
|
1492
|
+
sourceEndpoint,
|
|
1493
|
+
["edges", index, "source"],
|
|
1494
|
+
diagnostics
|
|
1495
|
+
);
|
|
1496
|
+
validateEndpointPort(
|
|
1497
|
+
dsl,
|
|
1498
|
+
targetEndpoint,
|
|
1499
|
+
["edges", index, "target"],
|
|
1500
|
+
diagnostics
|
|
1501
|
+
);
|
|
1305
1502
|
});
|
|
1306
1503
|
for (const [groupId, group] of Object.entries(dsl.groups ?? {})) {
|
|
1307
1504
|
(group.nodes ?? []).forEach((nodeId, index) => {
|
|
@@ -1319,6 +1516,27 @@ function validateReferences(dsl) {
|
|
|
1319
1516
|
}
|
|
1320
1517
|
});
|
|
1321
1518
|
}
|
|
1519
|
+
for (const [swimlaneId, swimlane] of Object.entries(dsl.swimlanes ?? {})) {
|
|
1520
|
+
for (const [laneId, lane] of Object.entries(swimlane.lanes)) {
|
|
1521
|
+
(lane.children ?? []).forEach((child, childIndex) => {
|
|
1522
|
+
if (!nodeIds.has(child)) {
|
|
1523
|
+
diagnostics.push(
|
|
1524
|
+
referenceMissing(
|
|
1525
|
+
[
|
|
1526
|
+
"swimlanes",
|
|
1527
|
+
swimlaneId,
|
|
1528
|
+
"lanes",
|
|
1529
|
+
laneId,
|
|
1530
|
+
"children",
|
|
1531
|
+
childIndex
|
|
1532
|
+
],
|
|
1533
|
+
child
|
|
1534
|
+
)
|
|
1535
|
+
);
|
|
1536
|
+
}
|
|
1537
|
+
});
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1322
1540
|
(dsl.constraints ?? []).forEach((constraint, index) => {
|
|
1323
1541
|
switch (constraint.kind) {
|
|
1324
1542
|
case "exact-position": {
|
|
@@ -1362,7 +1580,7 @@ function validateReferences(dsl) {
|
|
|
1362
1580
|
break;
|
|
1363
1581
|
case "containment": {
|
|
1364
1582
|
const container = constraint.containerId ?? constraint.container;
|
|
1365
|
-
if (container !== void 0 && !hasNodeOrGroup(container, nodeIds, groupIds)) {
|
|
1583
|
+
if (container !== void 0 && !hasNodeOrGroup(container, nodeIds, groupIds, swimlaneLaneIds)) {
|
|
1366
1584
|
diagnostics.push(
|
|
1367
1585
|
referenceMissing(["constraints", index, "container"], container)
|
|
1368
1586
|
);
|
|
@@ -1395,8 +1613,23 @@ function referenceMissing(path, id) {
|
|
|
1395
1613
|
hint: "Define the referenced node or group id, or update this reference."
|
|
1396
1614
|
};
|
|
1397
1615
|
}
|
|
1398
|
-
function hasNodeOrGroup(id, nodeIds, groupIds) {
|
|
1399
|
-
return nodeIds.has(id) || groupIds.has(id);
|
|
1616
|
+
function hasNodeOrGroup(id, nodeIds, groupIds, swimlaneLaneIds = /* @__PURE__ */ new Set()) {
|
|
1617
|
+
return nodeIds.has(id) || groupIds.has(id) || swimlaneLaneIds.has(id);
|
|
1618
|
+
}
|
|
1619
|
+
function endpointNodeId(endpointValue) {
|
|
1620
|
+
if (typeof endpointValue === "string" || endpointValue === void 0) {
|
|
1621
|
+
return endpointValue;
|
|
1622
|
+
}
|
|
1623
|
+
return endpointValue.node;
|
|
1624
|
+
}
|
|
1625
|
+
function validateEndpointPort(dsl, endpointValue, path, diagnostics) {
|
|
1626
|
+
if (endpointValue.portId === void 0) {
|
|
1627
|
+
return;
|
|
1628
|
+
}
|
|
1629
|
+
const node = dsl.nodes[endpointValue.nodeId];
|
|
1630
|
+
if (node !== void 0 && node.ports?.[endpointValue.portId] === void 0) {
|
|
1631
|
+
diagnostics.push(referenceMissing([...path, "port"], endpointValue.portId));
|
|
1632
|
+
}
|
|
1400
1633
|
}
|
|
1401
1634
|
function toLabel(value) {
|
|
1402
1635
|
if (value === void 0) {
|
|
@@ -1425,6 +1658,8 @@ function point(value) {
|
|
|
1425
1658
|
var directionSchema = zod.z.enum(["TB", "LR", "BT", "RL"]);
|
|
1426
1659
|
var routeKindSchema = zod.z.enum(["orthogonal", "straight"]);
|
|
1427
1660
|
var outputFormatSchema = zod.z.enum(["svg", "excalidraw"]);
|
|
1661
|
+
var edgeStrokeStyleSchema = zod.z.enum(["solid", "dashed"]);
|
|
1662
|
+
var edgeArrowheadSchema = zod.z.enum(["triangle", "hollowTriangle"]);
|
|
1428
1663
|
var nodeShapeSchema = zod.z.enum([
|
|
1429
1664
|
"rectangle",
|
|
1430
1665
|
"rounded-rectangle",
|
|
@@ -1452,18 +1687,49 @@ var labelSchema = zod.z.union([
|
|
|
1452
1687
|
maxWidth: finiteNumberSchema.optional()
|
|
1453
1688
|
})
|
|
1454
1689
|
]);
|
|
1690
|
+
var styleSchema = zod.z.object({
|
|
1691
|
+
fill: zod.z.string().optional(),
|
|
1692
|
+
stroke: zod.z.string().optional()
|
|
1693
|
+
});
|
|
1694
|
+
var portSideSchema = zod.z.enum(["top", "right", "bottom", "left"]);
|
|
1695
|
+
var portKindSchema = zod.z.enum(["proxy", "flow"]);
|
|
1696
|
+
var portSchema = zod.z.object({
|
|
1697
|
+
label: labelSchema.optional(),
|
|
1698
|
+
side: portSideSchema,
|
|
1699
|
+
kind: portKindSchema.optional(),
|
|
1700
|
+
order: finiteNumberSchema.optional(),
|
|
1701
|
+
style: styleSchema.optional()
|
|
1702
|
+
});
|
|
1703
|
+
var compartmentsSchema = zod.z.object({
|
|
1704
|
+
stereotype: zod.z.string().optional(),
|
|
1705
|
+
name: zod.z.string().optional(),
|
|
1706
|
+
properties: zod.z.array(zod.z.record(zod.z.string(), zod.z.string()).or(zod.z.string())).optional(),
|
|
1707
|
+
constraints: zod.z.array(zod.z.string()).optional()
|
|
1708
|
+
});
|
|
1455
1709
|
var nodeSchema = zod.z.object({
|
|
1456
1710
|
label: labelSchema.optional(),
|
|
1457
1711
|
shape: nodeShapeSchema.optional(),
|
|
1458
|
-
position: pointSchema.optional()
|
|
1712
|
+
position: pointSchema.optional(),
|
|
1713
|
+
style: styleSchema.optional(),
|
|
1714
|
+
ports: zod.z.record(zod.z.string(), portSchema).optional(),
|
|
1715
|
+
compartments: compartmentsSchema.optional()
|
|
1459
1716
|
});
|
|
1717
|
+
var endpointSchema = zod.z.union([
|
|
1718
|
+
zod.z.string(),
|
|
1719
|
+
zod.z.object({
|
|
1720
|
+
node: zod.z.string(),
|
|
1721
|
+
port: zod.z.string().optional()
|
|
1722
|
+
})
|
|
1723
|
+
]);
|
|
1460
1724
|
var structuredEdgeSchema = zod.z.object({
|
|
1461
1725
|
id: zod.z.string().optional(),
|
|
1462
|
-
source:
|
|
1463
|
-
target:
|
|
1726
|
+
source: endpointSchema.optional(),
|
|
1727
|
+
target: endpointSchema.optional(),
|
|
1464
1728
|
sourceId: zod.z.string().optional(),
|
|
1465
1729
|
targetId: zod.z.string().optional(),
|
|
1466
|
-
label: labelSchema.optional()
|
|
1730
|
+
label: labelSchema.optional(),
|
|
1731
|
+
style: edgeStrokeStyleSchema.optional(),
|
|
1732
|
+
arrowhead: edgeArrowheadSchema.optional()
|
|
1467
1733
|
}).superRefine((edge, context) => {
|
|
1468
1734
|
if (edge.source === void 0 && edge.sourceId === void 0) {
|
|
1469
1735
|
context.addIssue({
|
|
@@ -1487,6 +1753,17 @@ var groupSchema = zod.z.object({
|
|
|
1487
1753
|
groups: zod.z.array(zod.z.string()).optional(),
|
|
1488
1754
|
padding: insetsSchema.optional()
|
|
1489
1755
|
});
|
|
1756
|
+
var swimlaneSchema = zod.z.object({
|
|
1757
|
+
label: labelSchema.optional(),
|
|
1758
|
+
orientation: zod.z.enum(["vertical", "horizontal"]).optional(),
|
|
1759
|
+
lanes: zod.z.record(
|
|
1760
|
+
zod.z.string(),
|
|
1761
|
+
zod.z.object({
|
|
1762
|
+
label: labelSchema.optional(),
|
|
1763
|
+
children: zod.z.array(zod.z.string()).optional()
|
|
1764
|
+
})
|
|
1765
|
+
)
|
|
1766
|
+
});
|
|
1490
1767
|
var exactPositionConstraintSchema = zod.z.object({
|
|
1491
1768
|
kind: zod.z.literal("exact-position"),
|
|
1492
1769
|
target: zod.z.string().optional(),
|
|
@@ -1547,12 +1824,24 @@ var diagramDslSchema = zod.z.object({
|
|
|
1547
1824
|
direction: directionSchema.optional()
|
|
1548
1825
|
}).optional(),
|
|
1549
1826
|
routing: zod.z.object({
|
|
1550
|
-
kind: routeKindSchema.optional()
|
|
1827
|
+
kind: routeKindSchema.optional(),
|
|
1828
|
+
portShifting: zod.z.object({
|
|
1829
|
+
enabled: zod.z.boolean().optional(),
|
|
1830
|
+
spacing: finiteNumberSchema.optional()
|
|
1831
|
+
}).optional()
|
|
1551
1832
|
}).optional(),
|
|
1552
1833
|
nodes: zod.z.record(zod.z.string(), nodeSchema),
|
|
1553
1834
|
edges: zod.z.array(edgeSchema).optional(),
|
|
1554
1835
|
groups: zod.z.record(zod.z.string(), groupSchema).optional(),
|
|
1836
|
+
swimlanes: zod.z.record(zod.z.string(), swimlaneSchema).optional(),
|
|
1555
1837
|
constraints: zod.z.array(constraintSchema).optional(),
|
|
1838
|
+
frame: zod.z.object({
|
|
1839
|
+
kind: zod.z.string(),
|
|
1840
|
+
context: zod.z.string().optional(),
|
|
1841
|
+
name: zod.z.string().optional(),
|
|
1842
|
+
titleTab: zod.z.string(),
|
|
1843
|
+
style: styleSchema.optional()
|
|
1844
|
+
}).optional(),
|
|
1556
1845
|
output: zod.z.object({
|
|
1557
1846
|
format: outputFormatSchema.optional()
|
|
1558
1847
|
}).optional()
|
|
@@ -1828,11 +2117,12 @@ function renderArrow(edge) {
|
|
|
1828
2117
|
height: box.height
|
|
1829
2118
|
}),
|
|
1830
2119
|
backgroundColor: "transparent",
|
|
2120
|
+
strokeStyle: edge.style ?? "solid",
|
|
1831
2121
|
points: relativePoints,
|
|
1832
2122
|
startBinding: { elementId: `node:${edge.source.nodeId}`, focus: 0, gap: 0 },
|
|
1833
2123
|
endBinding: { elementId: `node:${edge.target.nodeId}`, focus: 0, gap: 0 },
|
|
1834
2124
|
startArrowhead: null,
|
|
1835
|
-
endArrowhead:
|
|
2125
|
+
endArrowhead: mapArrowhead(edge.arrowhead)
|
|
1836
2126
|
};
|
|
1837
2127
|
}
|
|
1838
2128
|
function renderText(id, label, box, containerId, groupIds) {
|
|
@@ -1910,6 +2200,16 @@ function mapShape(shape) {
|
|
|
1910
2200
|
return "cylinder";
|
|
1911
2201
|
}
|
|
1912
2202
|
}
|
|
2203
|
+
function mapArrowhead(arrowhead) {
|
|
2204
|
+
switch (arrowhead) {
|
|
2205
|
+
case void 0:
|
|
2206
|
+
return "arrow";
|
|
2207
|
+
case "triangle":
|
|
2208
|
+
return "triangle";
|
|
2209
|
+
case "hollowTriangle":
|
|
2210
|
+
return "triangle_outline";
|
|
2211
|
+
}
|
|
2212
|
+
}
|
|
1913
2213
|
function createGroupMembership(groups) {
|
|
1914
2214
|
const membership = /* @__PURE__ */ new Map();
|
|
1915
2215
|
for (const group of groups) {
|
|
@@ -1976,19 +2276,28 @@ function exportSvg(diagram, options = {}) {
|
|
|
1976
2276
|
`<svg xmlns="http://www.w3.org/2000/svg" role="img" viewBox="${formatBoxViewBox(diagram.bounds)}">`,
|
|
1977
2277
|
...title === void 0 ? [] : [` <title>${escapeXml(title)}</title>`],
|
|
1978
2278
|
` <rect class="background" x="${formatNumber(diagram.bounds.x)}" y="${formatNumber(diagram.bounds.y)}" width="${formatNumber(diagram.bounds.width)}" height="${formatNumber(diagram.bounds.height)}" fill="#ffffff"/>`,
|
|
2279
|
+
...diagram.frame === void 0 ? [] : [indent(renderFrame(diagram.frame))],
|
|
2280
|
+
...(diagram.swimlanes ?? []).flatMap(
|
|
2281
|
+
(swimlane) => renderSwimlane(swimlane)
|
|
2282
|
+
),
|
|
1979
2283
|
...diagram.groups.map((group) => indent(renderGroup2(group))),
|
|
1980
2284
|
...diagram.edges.flatMap((edge) => {
|
|
1981
|
-
const path = renderEdgePath(edge
|
|
2285
|
+
const path = renderEdgePath(edge);
|
|
1982
2286
|
if (path === void 0) {
|
|
1983
2287
|
return [];
|
|
1984
2288
|
}
|
|
1985
|
-
return [indent(path), indent(renderArrowhead(edge
|
|
2289
|
+
return [indent(path), indent(renderArrowhead(edge))];
|
|
1986
2290
|
}),
|
|
1987
2291
|
...diagram.nodes.map((node) => indent(renderNode2(node))),
|
|
2292
|
+
...diagram.nodes.flatMap((node) => renderCompartments(node)),
|
|
2293
|
+
...diagram.nodes.flatMap((node) => renderPorts(node)),
|
|
1988
2294
|
...diagram.groups.flatMap(
|
|
1989
2295
|
(group) => renderLabel(group.label, group.box, group)
|
|
1990
2296
|
),
|
|
1991
|
-
...diagram.nodes.flatMap(
|
|
2297
|
+
...diagram.nodes.flatMap(
|
|
2298
|
+
(node) => node.compartments === void 0 ? renderLabel(node.label, node.box, node) : []
|
|
2299
|
+
),
|
|
2300
|
+
...diagram.edges.flatMap((edge) => renderEdgeLabel(edge)),
|
|
1992
2301
|
"</svg>"
|
|
1993
2302
|
];
|
|
1994
2303
|
return `${lines.join("\n")}
|
|
@@ -1998,7 +2307,9 @@ function renderGroup2(group) {
|
|
|
1998
2307
|
return `<rect class="group" data-id="${escapeAttribute(group.id)}" x="${formatNumber(group.box.x)}" y="${formatNumber(group.box.y)}" width="${formatNumber(group.box.width)}" height="${formatNumber(group.box.height)}" fill="${GROUP_FILL}" stroke="${STROKE}" stroke-dasharray="6 4"/>`;
|
|
1999
2308
|
}
|
|
2000
2309
|
function renderNode2(node) {
|
|
2001
|
-
const
|
|
2310
|
+
const fill = node.style?.fill ?? NODE_FILL;
|
|
2311
|
+
const stroke = node.style?.stroke ?? STROKE;
|
|
2312
|
+
const common = `class="node node-${node.shape}" data-id="${escapeAttribute(node.id)}" fill="${escapeAttribute(fill)}" stroke="${escapeAttribute(stroke)}"`;
|
|
2002
2313
|
switch (node.shape) {
|
|
2003
2314
|
case "rectangle":
|
|
2004
2315
|
return renderRect(node.box, common);
|
|
@@ -2014,16 +2325,111 @@ function renderNode2(node) {
|
|
|
2014
2325
|
return `<path ${common} d="${formatCylinderPath(node.box)}"/>`;
|
|
2015
2326
|
}
|
|
2016
2327
|
}
|
|
2328
|
+
function renderFrame(frame) {
|
|
2329
|
+
const stroke = frame.style?.stroke ?? "#6b7280";
|
|
2330
|
+
const fill = frame.style?.fill ?? "transparent";
|
|
2331
|
+
return [
|
|
2332
|
+
`<g class="sysml-frame" data-kind="${escapeAttribute(frame.kind)}">`,
|
|
2333
|
+
` <rect class="sysml-frame-border" x="${formatNumber(frame.box.x)}" y="${formatNumber(frame.box.y)}" width="${formatNumber(frame.box.width)}" height="${formatNumber(frame.box.height)}" fill="${escapeAttribute(fill)}" stroke="${escapeAttribute(stroke)}"/>`,
|
|
2334
|
+
` <path class="sysml-title-tab" d="M ${formatNumber(frame.titleBox.x)} ${formatNumber(frame.titleBox.y + frame.titleBox.height)} L ${formatNumber(frame.titleBox.x)} ${formatNumber(frame.titleBox.y)} L ${formatNumber(frame.titleBox.x + frame.titleBox.width - 16)} ${formatNumber(frame.titleBox.y)} L ${formatNumber(frame.titleBox.x + frame.titleBox.width)} ${formatNumber(frame.titleBox.y + frame.titleBox.height)} Z" fill="#f3f4f6" stroke="${escapeAttribute(stroke)}"/>`,
|
|
2335
|
+
` <text class="sysml-title-tab-label" x="${formatNumber(frame.titleBox.x + 8)}" y="${formatNumber(frame.titleBox.y + frame.titleBox.height / 2)}" dominant-baseline="middle" font-family="${FONT_FAMILY}" font-size="12" fill="#111827">${escapeXml(frame.titleTab)}</text>`,
|
|
2336
|
+
"</g>"
|
|
2337
|
+
].join("\n");
|
|
2338
|
+
}
|
|
2339
|
+
function renderSwimlane(swimlane) {
|
|
2340
|
+
if (swimlane.box === void 0) {
|
|
2341
|
+
return [];
|
|
2342
|
+
}
|
|
2343
|
+
const lines = [
|
|
2344
|
+
` <g class="swimlane" data-id="${escapeAttribute(swimlane.id)}">`,
|
|
2345
|
+
` <rect class="swimlane-frame" x="${formatNumber(swimlane.box.x)}" y="${formatNumber(swimlane.box.y)}" width="${formatNumber(swimlane.box.width)}" height="${formatNumber(swimlane.box.height)}" fill="#ffffff" stroke="${STROKE}"/>`
|
|
2346
|
+
];
|
|
2347
|
+
for (const lane of swimlane.lanes) {
|
|
2348
|
+
if (lane.box === void 0) {
|
|
2349
|
+
continue;
|
|
2350
|
+
}
|
|
2351
|
+
lines.push(
|
|
2352
|
+
` <rect class="swimlane-lane" data-lane="${escapeAttribute(`${swimlane.id}.${lane.id}`)}" x="${formatNumber(lane.box.x)}" y="${formatNumber(lane.box.y)}" width="${formatNumber(lane.box.width)}" height="${formatNumber(lane.box.height)}" fill="none" stroke="${STROKE}"/>`
|
|
2353
|
+
);
|
|
2354
|
+
if (lane.label?.text !== void 0) {
|
|
2355
|
+
lines.push(
|
|
2356
|
+
` <text class="swimlane-label" x="${formatNumber(lane.box.x + lane.box.width / 2)}" y="${formatNumber(lane.box.y + 16)}" text-anchor="middle" font-family="${FONT_FAMILY}" font-size="12" fill="#111827">${escapeXml(lane.label.text)}</text>`
|
|
2357
|
+
);
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
lines.push(" </g>");
|
|
2361
|
+
return lines;
|
|
2362
|
+
}
|
|
2363
|
+
function renderPorts(node) {
|
|
2364
|
+
return (node.ports ?? []).flatMap((port) => [
|
|
2365
|
+
` <rect class="port" data-kind="${escapeAttribute(port.kind)}" data-port="${escapeAttribute(`${node.id}.${port.id}`)}" x="${formatNumber(port.box.x)}" y="${formatNumber(port.box.y)}" width="${formatNumber(port.box.width)}" height="${formatNumber(port.box.height)}" fill="${escapeAttribute(port.style?.fill ?? "#d9ead3")}" stroke="${escapeAttribute(port.style?.stroke ?? STROKE)}"/>`,
|
|
2366
|
+
...port.label?.text === void 0 ? [] : [
|
|
2367
|
+
` <text class="port-label" data-for="${escapeAttribute(`${node.id}.${port.id}`)}" x="${formatNumber(portLabelX(port.anchor.x, port.side))}" y="${formatNumber(port.anchor.y - 8)}" text-anchor="${port.side === "left" ? "end" : "start"}" font-family="${FONT_FAMILY}" font-size="10" fill="#111827">${escapeXml(port.label.text)}</text>`
|
|
2368
|
+
]
|
|
2369
|
+
]);
|
|
2370
|
+
}
|
|
2371
|
+
function renderCompartments(node) {
|
|
2372
|
+
const compartments2 = node.compartments;
|
|
2373
|
+
if (compartments2 === void 0) {
|
|
2374
|
+
return [];
|
|
2375
|
+
}
|
|
2376
|
+
const rows = [
|
|
2377
|
+
...compartments2.stereotype === void 0 ? [] : [{ className: "stereotype", text: compartments2.stereotype }],
|
|
2378
|
+
{
|
|
2379
|
+
className: "name",
|
|
2380
|
+
text: compartments2.name ?? node.label?.text ?? node.id
|
|
2381
|
+
},
|
|
2382
|
+
...(compartments2.properties ?? []).map((text) => ({
|
|
2383
|
+
className: "properties",
|
|
2384
|
+
text
|
|
2385
|
+
})),
|
|
2386
|
+
...(compartments2.constraints ?? []).map((text) => ({
|
|
2387
|
+
className: "constraints",
|
|
2388
|
+
text
|
|
2389
|
+
}))
|
|
2390
|
+
];
|
|
2391
|
+
const lineHeight = 16;
|
|
2392
|
+
const lines = [
|
|
2393
|
+
` <g class="compartment" data-for="${escapeAttribute(node.id)}">`
|
|
2394
|
+
];
|
|
2395
|
+
for (let index = 0; index < rows.length; index += 1) {
|
|
2396
|
+
const row = rows[index];
|
|
2397
|
+
if (row === void 0) {
|
|
2398
|
+
continue;
|
|
2399
|
+
}
|
|
2400
|
+
const y = node.box.y + 18 + index * lineHeight;
|
|
2401
|
+
if (index > 1) {
|
|
2402
|
+
lines.push(
|
|
2403
|
+
` <line class="compartment-separator" x1="${formatNumber(node.box.x)}" y1="${formatNumber(y - 12)}" x2="${formatNumber(node.box.x + node.box.width)}" y2="${formatNumber(y - 12)}" stroke="${STROKE}"/>`
|
|
2404
|
+
);
|
|
2405
|
+
}
|
|
2406
|
+
lines.push(
|
|
2407
|
+
` <text class="compartment-${row.className}" x="${formatNumber(node.box.x + node.box.width / 2)}" y="${formatNumber(y)}" text-anchor="middle" font-family="${FONT_FAMILY}" font-size="11" fill="#111827">${escapeXml(row.text)}</text>`
|
|
2408
|
+
);
|
|
2409
|
+
}
|
|
2410
|
+
lines.push(" </g>");
|
|
2411
|
+
return lines;
|
|
2412
|
+
}
|
|
2413
|
+
function portLabelX(x, side) {
|
|
2414
|
+
if (side === "left") {
|
|
2415
|
+
return x - 8;
|
|
2416
|
+
}
|
|
2417
|
+
if (side === "right") {
|
|
2418
|
+
return x + 8;
|
|
2419
|
+
}
|
|
2420
|
+
return x + 8;
|
|
2421
|
+
}
|
|
2017
2422
|
function renderRect(box, attributes) {
|
|
2018
2423
|
return `<rect ${attributes} x="${formatNumber(box.x)}" y="${formatNumber(box.y)}" width="${formatNumber(box.width)}" height="${formatNumber(box.height)}"/>`;
|
|
2019
2424
|
}
|
|
2020
2425
|
function renderLabel(label, box, item) {
|
|
2021
2426
|
const labelLayout = item.labelLayout;
|
|
2022
2427
|
if (labelLayout?.lines !== void 0 && labelLayout.lines.length > 0) {
|
|
2428
|
+
const offset = isAbsoluteLabelLayout(labelLayout.box, box) ? { x: 0, y: 0 } : { x: box.x, y: box.y };
|
|
2023
2429
|
return [
|
|
2024
2430
|
` <text class="label" data-for="${escapeAttribute(item.id)}" font-family="${FONT_FAMILY}" font-size="${formatNumber(labelLayout.font.fontSize)}" fill="#111827">`,
|
|
2025
2431
|
...labelLayout.lines.map(
|
|
2026
|
-
(line) => ` <tspan x="${formatNumber(line.box.x)}" y="${formatNumber(line.baselineY)}">${escapeXml(line.text)}</tspan>`
|
|
2432
|
+
(line) => ` <tspan x="${formatNumber(offset.x + line.box.x)}" y="${formatNumber(offset.y + line.baselineY)}">${escapeXml(line.text)}</tspan>`
|
|
2027
2433
|
),
|
|
2028
2434
|
" </text>"
|
|
2029
2435
|
];
|
|
@@ -2035,15 +2441,91 @@ function renderLabel(label, box, item) {
|
|
|
2035
2441
|
` <text class="label" data-for="${escapeAttribute(item.id)}" x="${formatNumber(box.x + box.width / 2)}" y="${formatNumber(box.y + box.height / 2)}" text-anchor="middle" dominant-baseline="middle" font-family="${FONT_FAMILY}" font-size="14" fill="#111827">${escapeXml(label.text)}</text>`
|
|
2036
2442
|
];
|
|
2037
2443
|
}
|
|
2038
|
-
function renderEdgePath(
|
|
2039
|
-
if (points.length < 2) {
|
|
2444
|
+
function renderEdgePath(edge) {
|
|
2445
|
+
if (edge.points.length < 2) {
|
|
2040
2446
|
return void 0;
|
|
2041
2447
|
}
|
|
2042
|
-
|
|
2448
|
+
const dash = edge.style === "dashed" ? ' stroke-dasharray="6 4"' : "";
|
|
2449
|
+
return `<path class="edge" data-id="${escapeAttribute(edge.id)}" d="${formatPath(pathPointsBeforeArrowhead(edge.points))}" fill="none" stroke="${EDGE_STROKE}" stroke-width="1.5"${dash}/>`;
|
|
2043
2450
|
}
|
|
2044
|
-
function
|
|
2451
|
+
function renderEdgeLabel(edge) {
|
|
2452
|
+
if (edge.label?.text === void 0 || edge.points.length < 2) {
|
|
2453
|
+
return [];
|
|
2454
|
+
}
|
|
2455
|
+
const placement = labelPlacementOnPolyline(edge.points);
|
|
2456
|
+
if (placement === void 0) {
|
|
2457
|
+
return [];
|
|
2458
|
+
}
|
|
2459
|
+
return [
|
|
2460
|
+
` <text class="edge-label" data-for="${escapeAttribute(edge.id)}" x="${formatNumber(placement.x)}" y="${formatNumber(placement.y)}" text-anchor="middle" dominant-baseline="middle" font-family="${FONT_FAMILY}" font-size="12" fill="#111827">${escapeXml(edge.label.text)}</text>`
|
|
2461
|
+
];
|
|
2462
|
+
}
|
|
2463
|
+
function renderArrowhead(edge) {
|
|
2464
|
+
const arrowhead = computeArrowhead(edge.points);
|
|
2465
|
+
const fill = edge.arrowhead === "hollowTriangle" ? "none" : EDGE_STROKE;
|
|
2466
|
+
return `<polygon class="edge-arrowhead" data-edge="${escapeAttribute(edge.id)}" points="${formatPoints([arrowhead.tip, arrowhead.left, arrowhead.right])}" fill="${fill}" stroke="${EDGE_STROKE}"/>`;
|
|
2467
|
+
}
|
|
2468
|
+
function labelPlacementOnPolyline(points) {
|
|
2469
|
+
const segments = nonZeroSegments(points);
|
|
2470
|
+
const totalLength = segments.reduce(
|
|
2471
|
+
(sum, segment) => sum + segment.length,
|
|
2472
|
+
0
|
|
2473
|
+
);
|
|
2474
|
+
if (totalLength <= 0) {
|
|
2475
|
+
return void 0;
|
|
2476
|
+
}
|
|
2477
|
+
let remaining = totalLength / 2;
|
|
2478
|
+
for (const segment of segments) {
|
|
2479
|
+
if (remaining <= segment.length) {
|
|
2480
|
+
const ratio = remaining / segment.length;
|
|
2481
|
+
const x = segment.start.x + (segment.end.x - segment.start.x) * ratio;
|
|
2482
|
+
const y = segment.start.y + (segment.end.y - segment.start.y) * ratio;
|
|
2483
|
+
const offset2 = labelOffset(segment);
|
|
2484
|
+
return { x: x + offset2.x, y: y + offset2.y };
|
|
2485
|
+
}
|
|
2486
|
+
remaining -= segment.length;
|
|
2487
|
+
}
|
|
2488
|
+
const last = segments.at(-1);
|
|
2489
|
+
if (last === void 0) {
|
|
2490
|
+
return void 0;
|
|
2491
|
+
}
|
|
2492
|
+
const offset = labelOffset(last);
|
|
2493
|
+
return { x: last.end.x + offset.x, y: last.end.y + offset.y };
|
|
2494
|
+
}
|
|
2495
|
+
function nonZeroSegments(points) {
|
|
2496
|
+
const segments = [];
|
|
2497
|
+
for (let index = 0; index < points.length - 1; index += 1) {
|
|
2498
|
+
const start = points[index];
|
|
2499
|
+
const end = points[index + 1];
|
|
2500
|
+
if (start === void 0 || end === void 0) {
|
|
2501
|
+
continue;
|
|
2502
|
+
}
|
|
2503
|
+
const length = Math.hypot(end.x - start.x, end.y - start.y);
|
|
2504
|
+
if (length > 0) {
|
|
2505
|
+
segments.push({ start, end, length });
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
return segments;
|
|
2509
|
+
}
|
|
2510
|
+
function labelOffset(segment) {
|
|
2511
|
+
const offset = 10;
|
|
2512
|
+
const dx = segment.end.x - segment.start.x;
|
|
2513
|
+
const dy = segment.end.y - segment.start.y;
|
|
2514
|
+
return {
|
|
2515
|
+
x: -dy / segment.length * offset,
|
|
2516
|
+
y: dx / segment.length * offset
|
|
2517
|
+
};
|
|
2518
|
+
}
|
|
2519
|
+
function pathPointsBeforeArrowhead(points) {
|
|
2045
2520
|
const arrowhead = computeArrowhead(points);
|
|
2046
|
-
|
|
2521
|
+
const base = {
|
|
2522
|
+
x: (arrowhead.left.x + arrowhead.right.x) / 2,
|
|
2523
|
+
y: (arrowhead.left.y + arrowhead.right.y) / 2
|
|
2524
|
+
};
|
|
2525
|
+
return [...points.slice(0, -1), base];
|
|
2526
|
+
}
|
|
2527
|
+
function isAbsoluteLabelLayout(labelBox, itemBox) {
|
|
2528
|
+
return labelBox.x >= itemBox.x && labelBox.y >= itemBox.y && labelBox.x + labelBox.width <= itemBox.x + itemBox.width && labelBox.y + labelBox.height <= itemBox.y + itemBox.height;
|
|
2047
2529
|
}
|
|
2048
2530
|
function shapePoints(shape, box) {
|
|
2049
2531
|
const left = box.x;
|
|
@@ -2230,20 +2712,33 @@ function isValidDimension(value) {
|
|
|
2230
2712
|
// src/routing/routes.ts
|
|
2231
2713
|
function routeEdge(input) {
|
|
2232
2714
|
const diagnostics = [];
|
|
2715
|
+
const defaultAnchors = defaultAnchorsForGeometry(
|
|
2716
|
+
input.source.box,
|
|
2717
|
+
input.target.box,
|
|
2718
|
+
input.direction
|
|
2719
|
+
);
|
|
2233
2720
|
const source = getEdgePort(
|
|
2234
2721
|
input.source,
|
|
2235
2722
|
input.target.center,
|
|
2236
|
-
input.sourceAnchor
|
|
2723
|
+
input.sourceAnchor ?? defaultAnchors.sourceAnchor
|
|
2237
2724
|
);
|
|
2238
2725
|
const target = getEdgePort(
|
|
2239
2726
|
input.target,
|
|
2240
2727
|
input.source.center,
|
|
2241
|
-
input.targetAnchor
|
|
2728
|
+
input.targetAnchor ?? defaultAnchors.targetAnchor
|
|
2242
2729
|
);
|
|
2243
2730
|
if ((input.kind ?? "orthogonal") === "straight") {
|
|
2244
2731
|
return { points: simplifyRoute([source, target]), diagnostics };
|
|
2245
2732
|
}
|
|
2246
2733
|
const candidates = orthogonalCandidates(source, target, input.direction);
|
|
2734
|
+
candidates.push(
|
|
2735
|
+
...expandedObstacleCandidates(
|
|
2736
|
+
source,
|
|
2737
|
+
target,
|
|
2738
|
+
input.direction,
|
|
2739
|
+
input.obstacles ?? []
|
|
2740
|
+
)
|
|
2741
|
+
);
|
|
2247
2742
|
for (const candidate of candidates) {
|
|
2248
2743
|
if (!routeIntersectsObstacles(candidate, input.obstacles ?? [])) {
|
|
2249
2744
|
return { points: simplifyRoute(candidate), diagnostics };
|
|
@@ -2282,27 +2777,113 @@ function simplifyRoute(points) {
|
|
|
2282
2777
|
function orthogonalCandidates(source, target, direction) {
|
|
2283
2778
|
const midpointX = (source.x + target.x) / 2;
|
|
2284
2779
|
const midpointY = (source.y + target.y) / 2;
|
|
2285
|
-
const candidates = [
|
|
2286
|
-
[source, { x: target.x, y: source.y }, target],
|
|
2287
|
-
[source, { x: source.x, y: target.y }, target]
|
|
2288
|
-
];
|
|
2780
|
+
const candidates = [];
|
|
2289
2781
|
if (direction === "TB" || direction === "BT") {
|
|
2290
2782
|
candidates.push([
|
|
2291
2783
|
source,
|
|
2292
|
-
{ x:
|
|
2293
|
-
{ x:
|
|
2784
|
+
{ x: source.x, y: midpointY },
|
|
2785
|
+
{ x: target.x, y: midpointY },
|
|
2294
2786
|
target
|
|
2295
2787
|
]);
|
|
2296
2788
|
} else {
|
|
2297
2789
|
candidates.push([
|
|
2298
2790
|
source,
|
|
2299
|
-
{ x:
|
|
2300
|
-
{ x:
|
|
2791
|
+
{ x: midpointX, y: source.y },
|
|
2792
|
+
{ x: midpointX, y: target.y },
|
|
2301
2793
|
target
|
|
2302
2794
|
]);
|
|
2303
2795
|
}
|
|
2796
|
+
candidates.push(
|
|
2797
|
+
[source, { x: target.x, y: source.y }, target],
|
|
2798
|
+
[source, { x: source.x, y: target.y }, target]
|
|
2799
|
+
);
|
|
2304
2800
|
return candidates;
|
|
2305
2801
|
}
|
|
2802
|
+
function defaultSourceAnchor(direction) {
|
|
2803
|
+
switch (direction) {
|
|
2804
|
+
case "LR":
|
|
2805
|
+
return "right";
|
|
2806
|
+
case "RL":
|
|
2807
|
+
return "left";
|
|
2808
|
+
case "TB":
|
|
2809
|
+
return "bottom";
|
|
2810
|
+
case "BT":
|
|
2811
|
+
return "top";
|
|
2812
|
+
}
|
|
2813
|
+
}
|
|
2814
|
+
function defaultAnchorsForGeometry(source, target, direction) {
|
|
2815
|
+
const dx = target.x + target.width / 2 - (source.x + source.width / 2);
|
|
2816
|
+
const dy = target.y + target.height / 2 - (source.y + source.height / 2);
|
|
2817
|
+
if (Math.abs(dy) > Math.abs(dx)) {
|
|
2818
|
+
return dy >= 0 ? { sourceAnchor: "bottom", targetAnchor: "top" } : { sourceAnchor: "top", targetAnchor: "bottom" };
|
|
2819
|
+
}
|
|
2820
|
+
if (Math.abs(dx) > 0) {
|
|
2821
|
+
return dx >= 0 ? { sourceAnchor: "right", targetAnchor: "left" } : { sourceAnchor: "left", targetAnchor: "right" };
|
|
2822
|
+
}
|
|
2823
|
+
return {
|
|
2824
|
+
sourceAnchor: defaultSourceAnchor(direction),
|
|
2825
|
+
targetAnchor: defaultTargetAnchor(direction)
|
|
2826
|
+
};
|
|
2827
|
+
}
|
|
2828
|
+
function defaultTargetAnchor(direction) {
|
|
2829
|
+
switch (direction) {
|
|
2830
|
+
case "LR":
|
|
2831
|
+
return "left";
|
|
2832
|
+
case "RL":
|
|
2833
|
+
return "right";
|
|
2834
|
+
case "TB":
|
|
2835
|
+
return "top";
|
|
2836
|
+
case "BT":
|
|
2837
|
+
return "bottom";
|
|
2838
|
+
}
|
|
2839
|
+
}
|
|
2840
|
+
function expandedObstacleCandidates(source, target, direction, obstacles) {
|
|
2841
|
+
if (obstacles.length === 0) {
|
|
2842
|
+
return [];
|
|
2843
|
+
}
|
|
2844
|
+
const margin = 16;
|
|
2845
|
+
const candidates = [];
|
|
2846
|
+
if (direction === "TB" || direction === "BT") {
|
|
2847
|
+
const lanes = sortedUniqueLanes(
|
|
2848
|
+
obstacles.flatMap((obstacle) => [
|
|
2849
|
+
obstacle.x - margin,
|
|
2850
|
+
obstacle.x + obstacle.width + margin
|
|
2851
|
+
]),
|
|
2852
|
+
(source.x + target.x) / 2
|
|
2853
|
+
);
|
|
2854
|
+
for (const laneX of lanes) {
|
|
2855
|
+
candidates.push([
|
|
2856
|
+
source,
|
|
2857
|
+
{ x: laneX, y: source.y },
|
|
2858
|
+
{ x: laneX, y: target.y },
|
|
2859
|
+
target
|
|
2860
|
+
]);
|
|
2861
|
+
}
|
|
2862
|
+
} else {
|
|
2863
|
+
const lanes = sortedUniqueLanes(
|
|
2864
|
+
obstacles.flatMap((obstacle) => [
|
|
2865
|
+
obstacle.y - margin,
|
|
2866
|
+
obstacle.y + obstacle.height + margin
|
|
2867
|
+
]),
|
|
2868
|
+
(source.y + target.y) / 2
|
|
2869
|
+
);
|
|
2870
|
+
for (const laneY of lanes) {
|
|
2871
|
+
candidates.push([
|
|
2872
|
+
source,
|
|
2873
|
+
{ x: source.x, y: laneY },
|
|
2874
|
+
{ x: target.x, y: laneY },
|
|
2875
|
+
target
|
|
2876
|
+
]);
|
|
2877
|
+
}
|
|
2878
|
+
}
|
|
2879
|
+
return candidates;
|
|
2880
|
+
}
|
|
2881
|
+
function sortedUniqueLanes(lanes, midpoint) {
|
|
2882
|
+
return [...new Set(lanes)].filter((lane) => Number.isFinite(lane)).sort((left, right) => {
|
|
2883
|
+
const distance = Math.abs(left - midpoint) - Math.abs(right - midpoint);
|
|
2884
|
+
return distance === 0 ? left - right : distance;
|
|
2885
|
+
});
|
|
2886
|
+
}
|
|
2306
2887
|
function routeIntersectsObstacles(points, obstacles) {
|
|
2307
2888
|
for (let index = 0; index < points.length - 1; index += 1) {
|
|
2308
2889
|
const a = points[index];
|
|
@@ -2381,12 +2962,17 @@ function solveDiagram(diagram, options = {}) {
|
|
|
2381
2962
|
options,
|
|
2382
2963
|
diagnostics
|
|
2383
2964
|
);
|
|
2965
|
+
const coordinatedSwimlanes = coordinateSwimlanes(
|
|
2966
|
+
diagram.swimlanes ?? [],
|
|
2967
|
+
constrained.boxes
|
|
2968
|
+
);
|
|
2384
2969
|
const groupBoxes = new Map(
|
|
2385
2970
|
coordinatedGroups.map((group) => [group.id, group.box])
|
|
2386
2971
|
);
|
|
2387
2972
|
const coordinatedEdges = coordinateEdges(
|
|
2388
2973
|
edges,
|
|
2389
2974
|
nodeGeometryById,
|
|
2975
|
+
coordinatedNodes,
|
|
2390
2976
|
[...nodeGeometryById.values()].map((geometry) => geometry.obstacleBox),
|
|
2391
2977
|
diagram.direction,
|
|
2392
2978
|
options,
|
|
@@ -2394,8 +2980,18 @@ function solveDiagram(diagram, options = {}) {
|
|
|
2394
2980
|
);
|
|
2395
2981
|
const allBoxes = [
|
|
2396
2982
|
...coordinatedNodes.map((node) => node.box),
|
|
2397
|
-
...
|
|
2983
|
+
...coordinatedNodes.flatMap(
|
|
2984
|
+
(node) => (node.ports ?? []).flatMap(
|
|
2985
|
+
(port) => port.label === void 0 ? [port.box] : [port.box, portLabelBox(port)]
|
|
2986
|
+
)
|
|
2987
|
+
),
|
|
2988
|
+
...groupBoxes.values(),
|
|
2989
|
+
...coordinatedSwimlanes.flatMap(
|
|
2990
|
+
(swimlane) => swimlane.box === void 0 ? [] : [swimlane.box]
|
|
2991
|
+
)
|
|
2398
2992
|
];
|
|
2993
|
+
const contentBounds = allBoxes.length === 0 ? { x: 0, y: 0, width: 0, height: 0 } : unionBoxes(allBoxes);
|
|
2994
|
+
const frame = diagram.frame === void 0 ? void 0 : coordinateFrame(diagram.frame, contentBounds);
|
|
2399
2995
|
return {
|
|
2400
2996
|
id: diagram.id,
|
|
2401
2997
|
...diagram.title === void 0 ? {} : { title: diagram.title },
|
|
@@ -2403,8 +2999,10 @@ function solveDiagram(diagram, options = {}) {
|
|
|
2403
2999
|
nodes: coordinatedNodes,
|
|
2404
3000
|
edges: coordinatedEdges,
|
|
2405
3001
|
groups: coordinatedGroups,
|
|
3002
|
+
...coordinatedSwimlanes.length === 0 ? {} : { swimlanes: coordinatedSwimlanes },
|
|
2406
3003
|
diagnostics,
|
|
2407
|
-
bounds:
|
|
3004
|
+
bounds: frame === void 0 ? contentBounds : unionBoxes([contentBounds, frame.box, frame.titleBox]),
|
|
3005
|
+
...frame === void 0 ? {} : { frame },
|
|
2408
3006
|
...diagram.metadata === void 0 ? {} : { metadata: diagram.metadata }
|
|
2409
3007
|
};
|
|
2410
3008
|
}
|
|
@@ -2430,6 +3028,9 @@ function coordinateNodes(nodes, boxes, options, diagnostics) {
|
|
|
2430
3028
|
coordinated.push({
|
|
2431
3029
|
id: node.id,
|
|
2432
3030
|
...node.label === void 0 ? {} : { label: node.label },
|
|
3031
|
+
...node.style === void 0 ? {} : { style: node.style },
|
|
3032
|
+
...node.ports === void 0 ? {} : { ports: coordinatePorts(node, box, options.portShifting) },
|
|
3033
|
+
...node.compartments === void 0 ? {} : { compartments: node.compartments },
|
|
2433
3034
|
...node.labelLayout === void 0 ? {} : { labelLayout: node.labelLayout },
|
|
2434
3035
|
shape: node.shape,
|
|
2435
3036
|
...node.metadata === void 0 ? {} : { metadata: node.metadata },
|
|
@@ -2440,6 +3041,142 @@ function coordinateNodes(nodes, boxes, options, diagnostics) {
|
|
|
2440
3041
|
}
|
|
2441
3042
|
return coordinated;
|
|
2442
3043
|
}
|
|
3044
|
+
function coordinatePorts(node, nodeBox, portShifting) {
|
|
3045
|
+
const portsBySide = /* @__PURE__ */ new Map();
|
|
3046
|
+
for (const port of node.ports ?? []) {
|
|
3047
|
+
const ports = portsBySide.get(port.side) ?? [];
|
|
3048
|
+
ports.push(port);
|
|
3049
|
+
portsBySide.set(port.side, ports);
|
|
3050
|
+
}
|
|
3051
|
+
const coordinated = [];
|
|
3052
|
+
for (const [side, ports] of portsBySide) {
|
|
3053
|
+
const sorted = [...ports ?? []].sort((a, b) => {
|
|
3054
|
+
const order = (a.order ?? 0) - (b.order ?? 0);
|
|
3055
|
+
return order === 0 ? a.id.localeCompare(b.id) : order;
|
|
3056
|
+
});
|
|
3057
|
+
for (let index = 0; index < sorted.length; index += 1) {
|
|
3058
|
+
const port = sorted[index];
|
|
3059
|
+
if (port === void 0) {
|
|
3060
|
+
continue;
|
|
3061
|
+
}
|
|
3062
|
+
const anchor = portAnchor(
|
|
3063
|
+
nodeBox,
|
|
3064
|
+
side,
|
|
3065
|
+
index,
|
|
3066
|
+
sorted.length,
|
|
3067
|
+
portShifting
|
|
3068
|
+
);
|
|
3069
|
+
const box = portBox(anchor);
|
|
3070
|
+
coordinated.push({ ...port, box, anchor });
|
|
3071
|
+
}
|
|
3072
|
+
}
|
|
3073
|
+
return coordinated.sort((a, b) => a.id.localeCompare(b.id));
|
|
3074
|
+
}
|
|
3075
|
+
function portAnchor(nodeBox, side, index, count, portShifting) {
|
|
3076
|
+
const shiftingEnabled = portShifting?.enabled ?? true;
|
|
3077
|
+
const spacing = portShifting?.spacing ?? 24;
|
|
3078
|
+
const centeredOffset = shiftingEnabled ? (index - (count - 1) / 2) * spacing : 0;
|
|
3079
|
+
switch (side) {
|
|
3080
|
+
case "left":
|
|
3081
|
+
return {
|
|
3082
|
+
x: nodeBox.x,
|
|
3083
|
+
y: nodeBox.y + nodeBox.height / 2 + centeredOffset
|
|
3084
|
+
};
|
|
3085
|
+
case "right":
|
|
3086
|
+
return {
|
|
3087
|
+
x: nodeBox.x + nodeBox.width,
|
|
3088
|
+
y: nodeBox.y + nodeBox.height / 2 + centeredOffset
|
|
3089
|
+
};
|
|
3090
|
+
case "top":
|
|
3091
|
+
return {
|
|
3092
|
+
x: nodeBox.x + nodeBox.width / 2 + centeredOffset,
|
|
3093
|
+
y: nodeBox.y
|
|
3094
|
+
};
|
|
3095
|
+
case "bottom":
|
|
3096
|
+
return {
|
|
3097
|
+
x: nodeBox.x + nodeBox.width / 2 + centeredOffset,
|
|
3098
|
+
y: nodeBox.y + nodeBox.height
|
|
3099
|
+
};
|
|
3100
|
+
}
|
|
3101
|
+
}
|
|
3102
|
+
function portBox(anchor) {
|
|
3103
|
+
const size = 10;
|
|
3104
|
+
return {
|
|
3105
|
+
x: anchor.x - size / 2,
|
|
3106
|
+
y: anchor.y - size / 2,
|
|
3107
|
+
width: size,
|
|
3108
|
+
height: size
|
|
3109
|
+
};
|
|
3110
|
+
}
|
|
3111
|
+
function portLabelBox(port) {
|
|
3112
|
+
const textWidth = Math.max(0, (port.label?.text.length ?? 0) * 6);
|
|
3113
|
+
const height = 12;
|
|
3114
|
+
const gap = 8;
|
|
3115
|
+
const x = port.side === "left" ? port.anchor.x - gap - textWidth : port.anchor.x + gap;
|
|
3116
|
+
return {
|
|
3117
|
+
x,
|
|
3118
|
+
y: port.anchor.y - 8 - height,
|
|
3119
|
+
width: textWidth,
|
|
3120
|
+
height
|
|
3121
|
+
};
|
|
3122
|
+
}
|
|
3123
|
+
function coordinateSwimlanes(swimlanes, nodeBoxes) {
|
|
3124
|
+
const titleSize = 28;
|
|
3125
|
+
const padding = 16;
|
|
3126
|
+
return swimlanes.map((swimlane) => {
|
|
3127
|
+
const laneBoxes = swimlane.lanes.flatMap((lane) => {
|
|
3128
|
+
const childBoxes = lane.children.map((child) => nodeBoxes.get(child)).filter((box) => box !== void 0);
|
|
3129
|
+
return childBoxes.length === 0 ? [] : [unionBoxes(childBoxes)];
|
|
3130
|
+
});
|
|
3131
|
+
const laneUnion = laneBoxes.length === 0 ? { x: 0, y: 0, width: 120, height: 80 } : unionBoxes(laneBoxes);
|
|
3132
|
+
const outer = expand(laneUnion, padding, titleSize);
|
|
3133
|
+
const laneCount = Math.max(1, swimlane.lanes.length);
|
|
3134
|
+
const lanes = swimlane.lanes.map((lane, index) => {
|
|
3135
|
+
const box = swimlane.orientation === "vertical" ? {
|
|
3136
|
+
x: outer.x + outer.width / laneCount * index,
|
|
3137
|
+
y: outer.y,
|
|
3138
|
+
width: outer.width / laneCount,
|
|
3139
|
+
height: outer.height
|
|
3140
|
+
} : {
|
|
3141
|
+
x: outer.x,
|
|
3142
|
+
y: outer.y + outer.height / laneCount * index,
|
|
3143
|
+
width: outer.width,
|
|
3144
|
+
height: outer.height / laneCount
|
|
3145
|
+
};
|
|
3146
|
+
return { ...lane, box };
|
|
3147
|
+
});
|
|
3148
|
+
return { ...swimlane, lanes, box: outer };
|
|
3149
|
+
});
|
|
3150
|
+
}
|
|
3151
|
+
function coordinateFrame(frame, contentBounds) {
|
|
3152
|
+
const padding = 32;
|
|
3153
|
+
const titleHeight = 28;
|
|
3154
|
+
const titleWidth = Math.max(180, frame.titleTab.length * 7);
|
|
3155
|
+
const box = {
|
|
3156
|
+
x: contentBounds.x - padding,
|
|
3157
|
+
y: contentBounds.y - padding - titleHeight,
|
|
3158
|
+
width: contentBounds.width + padding * 2,
|
|
3159
|
+
height: contentBounds.height + padding * 2 + titleHeight
|
|
3160
|
+
};
|
|
3161
|
+
return {
|
|
3162
|
+
...frame,
|
|
3163
|
+
box,
|
|
3164
|
+
titleBox: {
|
|
3165
|
+
x: box.x,
|
|
3166
|
+
y: box.y,
|
|
3167
|
+
width: Math.min(titleWidth, box.width * 0.8),
|
|
3168
|
+
height: titleHeight
|
|
3169
|
+
}
|
|
3170
|
+
};
|
|
3171
|
+
}
|
|
3172
|
+
function expand(box, padding, titleSize) {
|
|
3173
|
+
return {
|
|
3174
|
+
x: box.x - padding,
|
|
3175
|
+
y: box.y - padding - titleSize,
|
|
3176
|
+
width: box.width + padding * 2,
|
|
3177
|
+
height: box.height + padding * 2 + titleSize
|
|
3178
|
+
};
|
|
3179
|
+
}
|
|
2443
3180
|
function coordinateGroups(groups, nodeBoxes, options, diagnostics) {
|
|
2444
3181
|
const coordinated = [];
|
|
2445
3182
|
const groupBoxes = /* @__PURE__ */ new Map();
|
|
@@ -2488,8 +3225,11 @@ function coordinateGroups(groups, nodeBoxes, options, diagnostics) {
|
|
|
2488
3225
|
}
|
|
2489
3226
|
return coordinated;
|
|
2490
3227
|
}
|
|
2491
|
-
function coordinateEdges(edges, nodes, obstacles, direction, options, diagnostics) {
|
|
3228
|
+
function coordinateEdges(edges, nodes, coordinatedNodes, obstacles, direction, options, diagnostics) {
|
|
2492
3229
|
const coordinated = [];
|
|
3230
|
+
const coordinatedNodeById = new Map(
|
|
3231
|
+
coordinatedNodes.map((node) => [node.id, node])
|
|
3232
|
+
);
|
|
2493
3233
|
for (const edge of edges) {
|
|
2494
3234
|
const source = nodes.get(edge.source.nodeId);
|
|
2495
3235
|
const target = nodes.get(edge.target.nodeId);
|
|
@@ -2507,11 +3247,13 @@ function coordinateEdges(edges, nodes, obstacles, direction, options, diagnostic
|
|
|
2507
3247
|
});
|
|
2508
3248
|
continue;
|
|
2509
3249
|
}
|
|
3250
|
+
const sourcePort = coordinatedNodeById.get(edge.source.nodeId)?.ports?.find((port) => port.id === edge.source.portId);
|
|
3251
|
+
const targetPort = coordinatedNodeById.get(edge.target.nodeId)?.ports?.find((port) => port.id === edge.target.portId);
|
|
2510
3252
|
const route = routeEdge({
|
|
2511
3253
|
kind: options.routeKind ?? "orthogonal",
|
|
2512
3254
|
direction,
|
|
2513
|
-
source,
|
|
2514
|
-
target,
|
|
3255
|
+
source: portGeometry(source, sourcePort),
|
|
3256
|
+
target: portGeometry(target, targetPort),
|
|
2515
3257
|
...edge.source.anchor === void 0 ? {} : { sourceAnchor: edge.source.anchor },
|
|
2516
3258
|
...edge.target.anchor === void 0 ? {} : { targetAnchor: edge.target.anchor },
|
|
2517
3259
|
obstacles: obstacles.filter(
|
|
@@ -2531,6 +3273,21 @@ function coordinateEdges(edges, nodes, obstacles, direction, options, diagnostic
|
|
|
2531
3273
|
}
|
|
2532
3274
|
return coordinated;
|
|
2533
3275
|
}
|
|
3276
|
+
function portGeometry(nodeGeometry, port) {
|
|
3277
|
+
if (port === void 0) {
|
|
3278
|
+
return nodeGeometry;
|
|
3279
|
+
}
|
|
3280
|
+
return {
|
|
3281
|
+
...nodeGeometry,
|
|
3282
|
+
box: port.box,
|
|
3283
|
+
center: port.anchor,
|
|
3284
|
+
anchors: nodeGeometry.anchors.map((anchor) => ({
|
|
3285
|
+
name: anchor.name,
|
|
3286
|
+
point: port.anchor
|
|
3287
|
+
})),
|
|
3288
|
+
obstacleBox: port.box
|
|
3289
|
+
};
|
|
3290
|
+
}
|
|
2534
3291
|
function stableById(items) {
|
|
2535
3292
|
return [...items].sort((a, b) => a.id.localeCompare(b.id));
|
|
2536
3293
|
}
|
|
@@ -2591,7 +3348,8 @@ function renderDiagramDsl(source, options = {}) {
|
|
|
2591
3348
|
return { diagnostics };
|
|
2592
3349
|
}
|
|
2593
3350
|
const solved = solveDiagram(normalized.diagram, {
|
|
2594
|
-
routeKind: normalized.diagram.metadata?.routeKind === "straight" ? "straight" : "orthogonal"
|
|
3351
|
+
routeKind: normalized.diagram.metadata?.routeKind === "straight" ? "straight" : "orthogonal",
|
|
3352
|
+
...solvePortShiftingOption(normalized.diagram.metadata?.portShifting)
|
|
2595
3353
|
});
|
|
2596
3354
|
const solveDiagnostics = solved.diagnostics.map(toSolveDiagnostic);
|
|
2597
3355
|
if (hasErrorDiagnostics2(solveDiagnostics)) {
|
|
@@ -2630,6 +3388,22 @@ function renderDiagramDsl(source, options = {}) {
|
|
|
2630
3388
|
function toSolveDiagnostic(diagnostic) {
|
|
2631
3389
|
return { ...diagnostic, layer: "solve" };
|
|
2632
3390
|
}
|
|
3391
|
+
function solvePortShiftingOption(value) {
|
|
3392
|
+
if (!isJsonObject(value)) {
|
|
3393
|
+
return {};
|
|
3394
|
+
}
|
|
3395
|
+
const portShifting = {};
|
|
3396
|
+
if (value.enabled === false) {
|
|
3397
|
+
portShifting.enabled = false;
|
|
3398
|
+
}
|
|
3399
|
+
if (typeof value.spacing === "number") {
|
|
3400
|
+
portShifting.spacing = value.spacing;
|
|
3401
|
+
}
|
|
3402
|
+
return { portShifting };
|
|
3403
|
+
}
|
|
3404
|
+
function isJsonObject(value) {
|
|
3405
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3406
|
+
}
|
|
2633
3407
|
function toExportDiagnostic(diagnostic) {
|
|
2634
3408
|
return { ...diagnostic, layer: "export" };
|
|
2635
3409
|
}
|
|
@@ -2776,11 +3550,13 @@ exports.canonicalize = canonicalize;
|
|
|
2776
3550
|
exports.computeArrowhead = computeArrowhead;
|
|
2777
3551
|
exports.computeContainerGeometry = computeContainerGeometry;
|
|
2778
3552
|
exports.computeShapeGeometry = computeShapeGeometry;
|
|
3553
|
+
exports.createDefaultTextMeasurer = createDefaultTextMeasurer;
|
|
2779
3554
|
exports.expandBox = expandBox;
|
|
2780
3555
|
exports.exportExcalidraw = exportExcalidraw;
|
|
2781
3556
|
exports.exportSvg = exportSvg;
|
|
2782
3557
|
exports.fitLabel = fitLabel;
|
|
2783
3558
|
exports.getEdgePort = getEdgePort;
|
|
3559
|
+
exports.installNodeCanvasRuntime = installNodeCanvasRuntime;
|
|
2784
3560
|
exports.intersectsAabb = intersectsAabb;
|
|
2785
3561
|
exports.isPretextRuntimeAvailable = isPretextRuntimeAvailable;
|
|
2786
3562
|
exports.normalizeDiagramDsl = normalizeDiagramDsl;
|