@crazyhappyone/auto-graph 0.0.1 → 0.0.21
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 +907 -69
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +906 -69
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +846 -76
- 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 +844 -77
- 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
|
}
|
|
@@ -1294,14 +1472,28 @@ function validateReferences(dsl) {
|
|
|
1294
1472
|
if (typeof edge === "string") {
|
|
1295
1473
|
return;
|
|
1296
1474
|
}
|
|
1297
|
-
const sourceId = edge.sourceId ?? edge.source;
|
|
1298
|
-
const targetId = edge.targetId ?? edge.target;
|
|
1475
|
+
const sourceId = edge.sourceId ?? endpointNodeId(edge.source);
|
|
1476
|
+
const targetId = edge.targetId ?? endpointNodeId(edge.target);
|
|
1477
|
+
const sourceEndpoint = endpoint(edge.source, edge.sourceId);
|
|
1478
|
+
const targetEndpoint = endpoint(edge.target, edge.targetId);
|
|
1299
1479
|
if (sourceId !== void 0 && !nodeIds.has(sourceId)) {
|
|
1300
1480
|
diagnostics.push(referenceMissing(["edges", index, "source"], sourceId));
|
|
1301
1481
|
}
|
|
1302
1482
|
if (targetId !== void 0 && !nodeIds.has(targetId)) {
|
|
1303
1483
|
diagnostics.push(referenceMissing(["edges", index, "target"], targetId));
|
|
1304
1484
|
}
|
|
1485
|
+
validateEndpointPort(
|
|
1486
|
+
dsl,
|
|
1487
|
+
sourceEndpoint,
|
|
1488
|
+
["edges", index, "source"],
|
|
1489
|
+
diagnostics
|
|
1490
|
+
);
|
|
1491
|
+
validateEndpointPort(
|
|
1492
|
+
dsl,
|
|
1493
|
+
targetEndpoint,
|
|
1494
|
+
["edges", index, "target"],
|
|
1495
|
+
diagnostics
|
|
1496
|
+
);
|
|
1305
1497
|
});
|
|
1306
1498
|
for (const [groupId, group] of Object.entries(dsl.groups ?? {})) {
|
|
1307
1499
|
(group.nodes ?? []).forEach((nodeId, index) => {
|
|
@@ -1319,6 +1511,27 @@ function validateReferences(dsl) {
|
|
|
1319
1511
|
}
|
|
1320
1512
|
});
|
|
1321
1513
|
}
|
|
1514
|
+
for (const [swimlaneId, swimlane] of Object.entries(dsl.swimlanes ?? {})) {
|
|
1515
|
+
for (const [laneId, lane] of Object.entries(swimlane.lanes)) {
|
|
1516
|
+
(lane.children ?? []).forEach((child, childIndex) => {
|
|
1517
|
+
if (!nodeIds.has(child)) {
|
|
1518
|
+
diagnostics.push(
|
|
1519
|
+
referenceMissing(
|
|
1520
|
+
[
|
|
1521
|
+
"swimlanes",
|
|
1522
|
+
swimlaneId,
|
|
1523
|
+
"lanes",
|
|
1524
|
+
laneId,
|
|
1525
|
+
"children",
|
|
1526
|
+
childIndex
|
|
1527
|
+
],
|
|
1528
|
+
child
|
|
1529
|
+
)
|
|
1530
|
+
);
|
|
1531
|
+
}
|
|
1532
|
+
});
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1322
1535
|
(dsl.constraints ?? []).forEach((constraint, index) => {
|
|
1323
1536
|
switch (constraint.kind) {
|
|
1324
1537
|
case "exact-position": {
|
|
@@ -1362,10 +1575,12 @@ function validateReferences(dsl) {
|
|
|
1362
1575
|
break;
|
|
1363
1576
|
case "containment": {
|
|
1364
1577
|
const container = constraint.containerId ?? constraint.container;
|
|
1365
|
-
if (container !== void 0
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1578
|
+
if (container !== void 0) {
|
|
1579
|
+
if (!nodeIds.has(container)) {
|
|
1580
|
+
diagnostics.push(
|
|
1581
|
+
referenceMissing(["constraints", index, "container"], container)
|
|
1582
|
+
);
|
|
1583
|
+
}
|
|
1369
1584
|
}
|
|
1370
1585
|
(constraint.childIds ?? constraint.children ?? []).forEach(
|
|
1371
1586
|
(child, childIndex) => {
|
|
@@ -1395,8 +1610,23 @@ function referenceMissing(path, id) {
|
|
|
1395
1610
|
hint: "Define the referenced node or group id, or update this reference."
|
|
1396
1611
|
};
|
|
1397
1612
|
}
|
|
1398
|
-
function hasNodeOrGroup(id, nodeIds, groupIds) {
|
|
1399
|
-
return nodeIds.has(id) || groupIds.has(id);
|
|
1613
|
+
function hasNodeOrGroup(id, nodeIds, groupIds, swimlaneLaneIds = /* @__PURE__ */ new Set()) {
|
|
1614
|
+
return nodeIds.has(id) || groupIds.has(id) || swimlaneLaneIds.has(id);
|
|
1615
|
+
}
|
|
1616
|
+
function endpointNodeId(endpointValue) {
|
|
1617
|
+
if (typeof endpointValue === "string" || endpointValue === void 0) {
|
|
1618
|
+
return endpointValue;
|
|
1619
|
+
}
|
|
1620
|
+
return endpointValue.node;
|
|
1621
|
+
}
|
|
1622
|
+
function validateEndpointPort(dsl, endpointValue, path, diagnostics) {
|
|
1623
|
+
if (endpointValue.portId === void 0) {
|
|
1624
|
+
return;
|
|
1625
|
+
}
|
|
1626
|
+
const node = dsl.nodes[endpointValue.nodeId];
|
|
1627
|
+
if (node !== void 0 && node.ports?.[endpointValue.portId] === void 0) {
|
|
1628
|
+
diagnostics.push(referenceMissing([...path, "port"], endpointValue.portId));
|
|
1629
|
+
}
|
|
1400
1630
|
}
|
|
1401
1631
|
function toLabel(value) {
|
|
1402
1632
|
if (value === void 0) {
|
|
@@ -1425,6 +1655,8 @@ function point(value) {
|
|
|
1425
1655
|
var directionSchema = zod.z.enum(["TB", "LR", "BT", "RL"]);
|
|
1426
1656
|
var routeKindSchema = zod.z.enum(["orthogonal", "straight"]);
|
|
1427
1657
|
var outputFormatSchema = zod.z.enum(["svg", "excalidraw"]);
|
|
1658
|
+
var edgeStrokeStyleSchema = zod.z.enum(["solid", "dashed"]);
|
|
1659
|
+
var edgeArrowheadSchema = zod.z.enum(["triangle", "hollowTriangle"]);
|
|
1428
1660
|
var nodeShapeSchema = zod.z.enum([
|
|
1429
1661
|
"rectangle",
|
|
1430
1662
|
"rounded-rectangle",
|
|
@@ -1452,18 +1684,49 @@ var labelSchema = zod.z.union([
|
|
|
1452
1684
|
maxWidth: finiteNumberSchema.optional()
|
|
1453
1685
|
})
|
|
1454
1686
|
]);
|
|
1687
|
+
var styleSchema = zod.z.object({
|
|
1688
|
+
fill: zod.z.string().optional(),
|
|
1689
|
+
stroke: zod.z.string().optional()
|
|
1690
|
+
});
|
|
1691
|
+
var portSideSchema = zod.z.enum(["top", "right", "bottom", "left"]);
|
|
1692
|
+
var portKindSchema = zod.z.enum(["proxy", "flow"]);
|
|
1693
|
+
var portSchema = zod.z.object({
|
|
1694
|
+
label: labelSchema.optional(),
|
|
1695
|
+
side: portSideSchema,
|
|
1696
|
+
kind: portKindSchema.optional(),
|
|
1697
|
+
order: finiteNumberSchema.optional(),
|
|
1698
|
+
style: styleSchema.optional()
|
|
1699
|
+
});
|
|
1700
|
+
var compartmentsSchema = zod.z.object({
|
|
1701
|
+
stereotype: zod.z.string().optional(),
|
|
1702
|
+
name: zod.z.string().optional(),
|
|
1703
|
+
properties: zod.z.array(zod.z.record(zod.z.string(), zod.z.string()).or(zod.z.string())).optional(),
|
|
1704
|
+
constraints: zod.z.array(zod.z.string()).optional()
|
|
1705
|
+
});
|
|
1455
1706
|
var nodeSchema = zod.z.object({
|
|
1456
1707
|
label: labelSchema.optional(),
|
|
1457
1708
|
shape: nodeShapeSchema.optional(),
|
|
1458
|
-
position: pointSchema.optional()
|
|
1709
|
+
position: pointSchema.optional(),
|
|
1710
|
+
style: styleSchema.optional(),
|
|
1711
|
+
ports: zod.z.record(zod.z.string(), portSchema).optional(),
|
|
1712
|
+
compartments: compartmentsSchema.optional()
|
|
1459
1713
|
});
|
|
1714
|
+
var endpointSchema = zod.z.union([
|
|
1715
|
+
zod.z.string(),
|
|
1716
|
+
zod.z.object({
|
|
1717
|
+
node: zod.z.string(),
|
|
1718
|
+
port: zod.z.string().optional()
|
|
1719
|
+
})
|
|
1720
|
+
]);
|
|
1460
1721
|
var structuredEdgeSchema = zod.z.object({
|
|
1461
1722
|
id: zod.z.string().optional(),
|
|
1462
|
-
source:
|
|
1463
|
-
target:
|
|
1723
|
+
source: endpointSchema.optional(),
|
|
1724
|
+
target: endpointSchema.optional(),
|
|
1464
1725
|
sourceId: zod.z.string().optional(),
|
|
1465
1726
|
targetId: zod.z.string().optional(),
|
|
1466
|
-
label: labelSchema.optional()
|
|
1727
|
+
label: labelSchema.optional(),
|
|
1728
|
+
style: edgeStrokeStyleSchema.optional(),
|
|
1729
|
+
arrowhead: edgeArrowheadSchema.optional()
|
|
1467
1730
|
}).superRefine((edge, context) => {
|
|
1468
1731
|
if (edge.source === void 0 && edge.sourceId === void 0) {
|
|
1469
1732
|
context.addIssue({
|
|
@@ -1487,6 +1750,17 @@ var groupSchema = zod.z.object({
|
|
|
1487
1750
|
groups: zod.z.array(zod.z.string()).optional(),
|
|
1488
1751
|
padding: insetsSchema.optional()
|
|
1489
1752
|
});
|
|
1753
|
+
var swimlaneSchema = zod.z.object({
|
|
1754
|
+
label: labelSchema.optional(),
|
|
1755
|
+
orientation: zod.z.enum(["vertical", "horizontal"]).optional(),
|
|
1756
|
+
lanes: zod.z.record(
|
|
1757
|
+
zod.z.string(),
|
|
1758
|
+
zod.z.object({
|
|
1759
|
+
label: labelSchema.optional(),
|
|
1760
|
+
children: zod.z.array(zod.z.string()).optional()
|
|
1761
|
+
})
|
|
1762
|
+
)
|
|
1763
|
+
});
|
|
1490
1764
|
var exactPositionConstraintSchema = zod.z.object({
|
|
1491
1765
|
kind: zod.z.literal("exact-position"),
|
|
1492
1766
|
target: zod.z.string().optional(),
|
|
@@ -1547,12 +1821,24 @@ var diagramDslSchema = zod.z.object({
|
|
|
1547
1821
|
direction: directionSchema.optional()
|
|
1548
1822
|
}).optional(),
|
|
1549
1823
|
routing: zod.z.object({
|
|
1550
|
-
kind: routeKindSchema.optional()
|
|
1824
|
+
kind: routeKindSchema.optional(),
|
|
1825
|
+
portShifting: zod.z.object({
|
|
1826
|
+
enabled: zod.z.boolean().optional(),
|
|
1827
|
+
spacing: finiteNumberSchema.optional()
|
|
1828
|
+
}).optional()
|
|
1551
1829
|
}).optional(),
|
|
1552
1830
|
nodes: zod.z.record(zod.z.string(), nodeSchema),
|
|
1553
1831
|
edges: zod.z.array(edgeSchema).optional(),
|
|
1554
1832
|
groups: zod.z.record(zod.z.string(), groupSchema).optional(),
|
|
1833
|
+
swimlanes: zod.z.record(zod.z.string(), swimlaneSchema).optional(),
|
|
1555
1834
|
constraints: zod.z.array(constraintSchema).optional(),
|
|
1835
|
+
frame: zod.z.object({
|
|
1836
|
+
kind: zod.z.string(),
|
|
1837
|
+
context: zod.z.string().optional(),
|
|
1838
|
+
name: zod.z.string().optional(),
|
|
1839
|
+
titleTab: zod.z.string(),
|
|
1840
|
+
style: styleSchema.optional()
|
|
1841
|
+
}).optional(),
|
|
1556
1842
|
output: zod.z.object({
|
|
1557
1843
|
format: outputFormatSchema.optional()
|
|
1558
1844
|
}).optional()
|
|
@@ -1828,11 +2114,12 @@ function renderArrow(edge) {
|
|
|
1828
2114
|
height: box.height
|
|
1829
2115
|
}),
|
|
1830
2116
|
backgroundColor: "transparent",
|
|
2117
|
+
strokeStyle: edge.style ?? "solid",
|
|
1831
2118
|
points: relativePoints,
|
|
1832
2119
|
startBinding: { elementId: `node:${edge.source.nodeId}`, focus: 0, gap: 0 },
|
|
1833
2120
|
endBinding: { elementId: `node:${edge.target.nodeId}`, focus: 0, gap: 0 },
|
|
1834
2121
|
startArrowhead: null,
|
|
1835
|
-
endArrowhead:
|
|
2122
|
+
endArrowhead: mapArrowhead(edge.arrowhead)
|
|
1836
2123
|
};
|
|
1837
2124
|
}
|
|
1838
2125
|
function renderText(id, label, box, containerId, groupIds) {
|
|
@@ -1910,6 +2197,16 @@ function mapShape(shape) {
|
|
|
1910
2197
|
return "cylinder";
|
|
1911
2198
|
}
|
|
1912
2199
|
}
|
|
2200
|
+
function mapArrowhead(arrowhead) {
|
|
2201
|
+
switch (arrowhead) {
|
|
2202
|
+
case void 0:
|
|
2203
|
+
return "arrow";
|
|
2204
|
+
case "triangle":
|
|
2205
|
+
return "triangle";
|
|
2206
|
+
case "hollowTriangle":
|
|
2207
|
+
return "triangle_outline";
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
1913
2210
|
function createGroupMembership(groups) {
|
|
1914
2211
|
const membership = /* @__PURE__ */ new Map();
|
|
1915
2212
|
for (const group of groups) {
|
|
@@ -1976,19 +2273,28 @@ function exportSvg(diagram, options = {}) {
|
|
|
1976
2273
|
`<svg xmlns="http://www.w3.org/2000/svg" role="img" viewBox="${formatBoxViewBox(diagram.bounds)}">`,
|
|
1977
2274
|
...title === void 0 ? [] : [` <title>${escapeXml(title)}</title>`],
|
|
1978
2275
|
` <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"/>`,
|
|
2276
|
+
...diagram.frame === void 0 ? [] : [indent(renderFrame(diagram.frame))],
|
|
2277
|
+
...(diagram.swimlanes ?? []).flatMap(
|
|
2278
|
+
(swimlane) => renderSwimlane(swimlane)
|
|
2279
|
+
),
|
|
1979
2280
|
...diagram.groups.map((group) => indent(renderGroup2(group))),
|
|
1980
2281
|
...diagram.edges.flatMap((edge) => {
|
|
1981
|
-
const path = renderEdgePath(edge
|
|
2282
|
+
const path = renderEdgePath(edge);
|
|
1982
2283
|
if (path === void 0) {
|
|
1983
2284
|
return [];
|
|
1984
2285
|
}
|
|
1985
|
-
return [indent(path), indent(renderArrowhead(edge
|
|
2286
|
+
return [indent(path), indent(renderArrowhead(edge))];
|
|
1986
2287
|
}),
|
|
1987
2288
|
...diagram.nodes.map((node) => indent(renderNode2(node))),
|
|
2289
|
+
...diagram.nodes.flatMap((node) => renderCompartments(node)),
|
|
2290
|
+
...diagram.nodes.flatMap((node) => renderPorts(node)),
|
|
1988
2291
|
...diagram.groups.flatMap(
|
|
1989
2292
|
(group) => renderLabel(group.label, group.box, group)
|
|
1990
2293
|
),
|
|
1991
|
-
...diagram.nodes.flatMap(
|
|
2294
|
+
...diagram.nodes.flatMap(
|
|
2295
|
+
(node) => node.compartments === void 0 ? renderLabel(node.label, node.box, node) : []
|
|
2296
|
+
),
|
|
2297
|
+
...diagram.edges.flatMap((edge) => renderEdgeLabel(edge)),
|
|
1992
2298
|
"</svg>"
|
|
1993
2299
|
];
|
|
1994
2300
|
return `${lines.join("\n")}
|
|
@@ -1998,7 +2304,9 @@ function renderGroup2(group) {
|
|
|
1998
2304
|
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
2305
|
}
|
|
2000
2306
|
function renderNode2(node) {
|
|
2001
|
-
const
|
|
2307
|
+
const fill = node.style?.fill ?? NODE_FILL;
|
|
2308
|
+
const stroke = node.style?.stroke ?? STROKE;
|
|
2309
|
+
const common = `class="node node-${node.shape}" data-id="${escapeAttribute(node.id)}" fill="${escapeAttribute(fill)}" stroke="${escapeAttribute(stroke)}"`;
|
|
2002
2310
|
switch (node.shape) {
|
|
2003
2311
|
case "rectangle":
|
|
2004
2312
|
return renderRect(node.box, common);
|
|
@@ -2014,16 +2322,111 @@ function renderNode2(node) {
|
|
|
2014
2322
|
return `<path ${common} d="${formatCylinderPath(node.box)}"/>`;
|
|
2015
2323
|
}
|
|
2016
2324
|
}
|
|
2325
|
+
function renderFrame(frame) {
|
|
2326
|
+
const stroke = frame.style?.stroke ?? "#6b7280";
|
|
2327
|
+
const fill = frame.style?.fill ?? "transparent";
|
|
2328
|
+
return [
|
|
2329
|
+
`<g class="sysml-frame" data-kind="${escapeAttribute(frame.kind)}">`,
|
|
2330
|
+
` <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)}"/>`,
|
|
2331
|
+
` <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)}"/>`,
|
|
2332
|
+
` <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>`,
|
|
2333
|
+
"</g>"
|
|
2334
|
+
].join("\n");
|
|
2335
|
+
}
|
|
2336
|
+
function renderSwimlane(swimlane) {
|
|
2337
|
+
if (swimlane.box === void 0) {
|
|
2338
|
+
return [];
|
|
2339
|
+
}
|
|
2340
|
+
const lines = [
|
|
2341
|
+
` <g class="swimlane" data-id="${escapeAttribute(swimlane.id)}">`,
|
|
2342
|
+
` <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}"/>`
|
|
2343
|
+
];
|
|
2344
|
+
for (const lane of swimlane.lanes) {
|
|
2345
|
+
if (lane.box === void 0) {
|
|
2346
|
+
continue;
|
|
2347
|
+
}
|
|
2348
|
+
lines.push(
|
|
2349
|
+
` <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}"/>`
|
|
2350
|
+
);
|
|
2351
|
+
if (lane.label?.text !== void 0) {
|
|
2352
|
+
lines.push(
|
|
2353
|
+
` <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>`
|
|
2354
|
+
);
|
|
2355
|
+
}
|
|
2356
|
+
}
|
|
2357
|
+
lines.push(" </g>");
|
|
2358
|
+
return lines;
|
|
2359
|
+
}
|
|
2360
|
+
function renderPorts(node) {
|
|
2361
|
+
return (node.ports ?? []).flatMap((port) => [
|
|
2362
|
+
` <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)}"/>`,
|
|
2363
|
+
...port.label?.text === void 0 ? [] : [
|
|
2364
|
+
` <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>`
|
|
2365
|
+
]
|
|
2366
|
+
]);
|
|
2367
|
+
}
|
|
2368
|
+
function renderCompartments(node) {
|
|
2369
|
+
const compartments2 = node.compartments;
|
|
2370
|
+
if (compartments2 === void 0) {
|
|
2371
|
+
return [];
|
|
2372
|
+
}
|
|
2373
|
+
const rows = [
|
|
2374
|
+
...compartments2.stereotype === void 0 ? [] : [{ className: "stereotype", text: compartments2.stereotype }],
|
|
2375
|
+
{
|
|
2376
|
+
className: "name",
|
|
2377
|
+
text: compartments2.name ?? node.label?.text ?? node.id
|
|
2378
|
+
},
|
|
2379
|
+
...(compartments2.properties ?? []).map((text) => ({
|
|
2380
|
+
className: "properties",
|
|
2381
|
+
text
|
|
2382
|
+
})),
|
|
2383
|
+
...(compartments2.constraints ?? []).map((text) => ({
|
|
2384
|
+
className: "constraints",
|
|
2385
|
+
text
|
|
2386
|
+
}))
|
|
2387
|
+
];
|
|
2388
|
+
const lineHeight = 16;
|
|
2389
|
+
const lines = [
|
|
2390
|
+
` <g class="compartment" data-for="${escapeAttribute(node.id)}">`
|
|
2391
|
+
];
|
|
2392
|
+
for (let index = 0; index < rows.length; index += 1) {
|
|
2393
|
+
const row = rows[index];
|
|
2394
|
+
if (row === void 0) {
|
|
2395
|
+
continue;
|
|
2396
|
+
}
|
|
2397
|
+
const y = node.box.y + 18 + index * lineHeight;
|
|
2398
|
+
if (index > 1) {
|
|
2399
|
+
lines.push(
|
|
2400
|
+
` <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}"/>`
|
|
2401
|
+
);
|
|
2402
|
+
}
|
|
2403
|
+
lines.push(
|
|
2404
|
+
` <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>`
|
|
2405
|
+
);
|
|
2406
|
+
}
|
|
2407
|
+
lines.push(" </g>");
|
|
2408
|
+
return lines;
|
|
2409
|
+
}
|
|
2410
|
+
function portLabelX(x, side) {
|
|
2411
|
+
if (side === "left") {
|
|
2412
|
+
return x - 8;
|
|
2413
|
+
}
|
|
2414
|
+
if (side === "right") {
|
|
2415
|
+
return x + 8;
|
|
2416
|
+
}
|
|
2417
|
+
return x + 8;
|
|
2418
|
+
}
|
|
2017
2419
|
function renderRect(box, attributes) {
|
|
2018
2420
|
return `<rect ${attributes} x="${formatNumber(box.x)}" y="${formatNumber(box.y)}" width="${formatNumber(box.width)}" height="${formatNumber(box.height)}"/>`;
|
|
2019
2421
|
}
|
|
2020
2422
|
function renderLabel(label, box, item) {
|
|
2021
2423
|
const labelLayout = item.labelLayout;
|
|
2022
2424
|
if (labelLayout?.lines !== void 0 && labelLayout.lines.length > 0) {
|
|
2425
|
+
const offset = { x: box.x, y: box.y };
|
|
2023
2426
|
return [
|
|
2024
2427
|
` <text class="label" data-for="${escapeAttribute(item.id)}" font-family="${FONT_FAMILY}" font-size="${formatNumber(labelLayout.font.fontSize)}" fill="#111827">`,
|
|
2025
2428
|
...labelLayout.lines.map(
|
|
2026
|
-
(line) => ` <tspan x="${formatNumber(line.box.x)}" y="${formatNumber(line.baselineY)}">${escapeXml(line.text)}</tspan>`
|
|
2429
|
+
(line) => ` <tspan x="${formatNumber(offset.x + line.box.x)}" y="${formatNumber(offset.y + line.baselineY)}">${escapeXml(line.text)}</tspan>`
|
|
2027
2430
|
),
|
|
2028
2431
|
" </text>"
|
|
2029
2432
|
];
|
|
@@ -2035,15 +2438,88 @@ function renderLabel(label, box, item) {
|
|
|
2035
2438
|
` <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
2439
|
];
|
|
2037
2440
|
}
|
|
2038
|
-
function renderEdgePath(
|
|
2039
|
-
if (points.length < 2) {
|
|
2441
|
+
function renderEdgePath(edge) {
|
|
2442
|
+
if (edge.points.length < 2) {
|
|
2040
2443
|
return void 0;
|
|
2041
2444
|
}
|
|
2042
|
-
|
|
2445
|
+
const dash = edge.style === "dashed" ? ' stroke-dasharray="6 4"' : "";
|
|
2446
|
+
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
2447
|
}
|
|
2044
|
-
function
|
|
2448
|
+
function renderEdgeLabel(edge) {
|
|
2449
|
+
if (edge.label?.text === void 0 || edge.points.length < 2) {
|
|
2450
|
+
return [];
|
|
2451
|
+
}
|
|
2452
|
+
const placement = labelPlacementOnPolyline(edge.points);
|
|
2453
|
+
if (placement === void 0) {
|
|
2454
|
+
return [];
|
|
2455
|
+
}
|
|
2456
|
+
return [
|
|
2457
|
+
` <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>`
|
|
2458
|
+
];
|
|
2459
|
+
}
|
|
2460
|
+
function renderArrowhead(edge) {
|
|
2461
|
+
const arrowhead = computeArrowhead(edge.points);
|
|
2462
|
+
const fill = edge.arrowhead === "hollowTriangle" ? "none" : EDGE_STROKE;
|
|
2463
|
+
return `<polygon class="edge-arrowhead" data-edge="${escapeAttribute(edge.id)}" points="${formatPoints([arrowhead.tip, arrowhead.left, arrowhead.right])}" fill="${fill}" stroke="${EDGE_STROKE}"/>`;
|
|
2464
|
+
}
|
|
2465
|
+
function labelPlacementOnPolyline(points) {
|
|
2466
|
+
const segments = nonZeroSegments(points);
|
|
2467
|
+
const totalLength = segments.reduce(
|
|
2468
|
+
(sum, segment) => sum + segment.length,
|
|
2469
|
+
0
|
|
2470
|
+
);
|
|
2471
|
+
if (totalLength <= 0) {
|
|
2472
|
+
return void 0;
|
|
2473
|
+
}
|
|
2474
|
+
let remaining = totalLength / 2;
|
|
2475
|
+
for (const segment of segments) {
|
|
2476
|
+
if (remaining <= segment.length) {
|
|
2477
|
+
const ratio = remaining / segment.length;
|
|
2478
|
+
const x = segment.start.x + (segment.end.x - segment.start.x) * ratio;
|
|
2479
|
+
const y = segment.start.y + (segment.end.y - segment.start.y) * ratio;
|
|
2480
|
+
const offset2 = labelOffset(segment);
|
|
2481
|
+
return { x: x + offset2.x, y: y + offset2.y };
|
|
2482
|
+
}
|
|
2483
|
+
remaining -= segment.length;
|
|
2484
|
+
}
|
|
2485
|
+
const last = segments.at(-1);
|
|
2486
|
+
if (last === void 0) {
|
|
2487
|
+
return void 0;
|
|
2488
|
+
}
|
|
2489
|
+
const offset = labelOffset(last);
|
|
2490
|
+
return { x: last.end.x + offset.x, y: last.end.y + offset.y };
|
|
2491
|
+
}
|
|
2492
|
+
function nonZeroSegments(points) {
|
|
2493
|
+
const segments = [];
|
|
2494
|
+
for (let index = 0; index < points.length - 1; index += 1) {
|
|
2495
|
+
const start = points[index];
|
|
2496
|
+
const end = points[index + 1];
|
|
2497
|
+
if (start === void 0 || end === void 0) {
|
|
2498
|
+
continue;
|
|
2499
|
+
}
|
|
2500
|
+
const length = Math.hypot(end.x - start.x, end.y - start.y);
|
|
2501
|
+
if (length > 0) {
|
|
2502
|
+
segments.push({ start, end, length });
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
return segments;
|
|
2506
|
+
}
|
|
2507
|
+
function labelOffset(segment) {
|
|
2508
|
+
const offset = 10;
|
|
2509
|
+
const dx = segment.end.x - segment.start.x;
|
|
2510
|
+
const dy = segment.end.y - segment.start.y;
|
|
2511
|
+
return {
|
|
2512
|
+
x: -dy / segment.length * offset,
|
|
2513
|
+
y: dx / segment.length * offset
|
|
2514
|
+
};
|
|
2515
|
+
}
|
|
2516
|
+
function pathPointsBeforeArrowhead(points) {
|
|
2045
2517
|
const arrowhead = computeArrowhead(points);
|
|
2046
|
-
|
|
2518
|
+
const base = {
|
|
2519
|
+
x: (arrowhead.left.x + arrowhead.right.x) / 2,
|
|
2520
|
+
y: (arrowhead.left.y + arrowhead.right.y) / 2
|
|
2521
|
+
};
|
|
2522
|
+
return [...points.slice(0, -1), base];
|
|
2047
2523
|
}
|
|
2048
2524
|
function shapePoints(shape, box) {
|
|
2049
2525
|
const left = box.x;
|
|
@@ -2230,20 +2706,33 @@ function isValidDimension(value) {
|
|
|
2230
2706
|
// src/routing/routes.ts
|
|
2231
2707
|
function routeEdge(input) {
|
|
2232
2708
|
const diagnostics = [];
|
|
2709
|
+
const defaultAnchors = defaultAnchorsForGeometry(
|
|
2710
|
+
input.source.box,
|
|
2711
|
+
input.target.box,
|
|
2712
|
+
input.direction
|
|
2713
|
+
);
|
|
2233
2714
|
const source = getEdgePort(
|
|
2234
2715
|
input.source,
|
|
2235
2716
|
input.target.center,
|
|
2236
|
-
input.sourceAnchor
|
|
2717
|
+
input.sourceAnchor ?? defaultAnchors.sourceAnchor
|
|
2237
2718
|
);
|
|
2238
2719
|
const target = getEdgePort(
|
|
2239
2720
|
input.target,
|
|
2240
2721
|
input.source.center,
|
|
2241
|
-
input.targetAnchor
|
|
2722
|
+
input.targetAnchor ?? defaultAnchors.targetAnchor
|
|
2242
2723
|
);
|
|
2243
2724
|
if ((input.kind ?? "orthogonal") === "straight") {
|
|
2244
2725
|
return { points: simplifyRoute([source, target]), diagnostics };
|
|
2245
2726
|
}
|
|
2246
2727
|
const candidates = orthogonalCandidates(source, target, input.direction);
|
|
2728
|
+
candidates.push(
|
|
2729
|
+
...expandedObstacleCandidates(
|
|
2730
|
+
source,
|
|
2731
|
+
target,
|
|
2732
|
+
input.direction,
|
|
2733
|
+
input.obstacles ?? []
|
|
2734
|
+
)
|
|
2735
|
+
);
|
|
2247
2736
|
for (const candidate of candidates) {
|
|
2248
2737
|
if (!routeIntersectsObstacles(candidate, input.obstacles ?? [])) {
|
|
2249
2738
|
return { points: simplifyRoute(candidate), diagnostics };
|
|
@@ -2282,27 +2771,113 @@ function simplifyRoute(points) {
|
|
|
2282
2771
|
function orthogonalCandidates(source, target, direction) {
|
|
2283
2772
|
const midpointX = (source.x + target.x) / 2;
|
|
2284
2773
|
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
|
-
];
|
|
2774
|
+
const candidates = [];
|
|
2289
2775
|
if (direction === "TB" || direction === "BT") {
|
|
2290
2776
|
candidates.push([
|
|
2291
2777
|
source,
|
|
2292
|
-
{ x:
|
|
2293
|
-
{ x:
|
|
2778
|
+
{ x: source.x, y: midpointY },
|
|
2779
|
+
{ x: target.x, y: midpointY },
|
|
2294
2780
|
target
|
|
2295
2781
|
]);
|
|
2296
2782
|
} else {
|
|
2297
2783
|
candidates.push([
|
|
2298
2784
|
source,
|
|
2299
|
-
{ x:
|
|
2300
|
-
{ x:
|
|
2785
|
+
{ x: midpointX, y: source.y },
|
|
2786
|
+
{ x: midpointX, y: target.y },
|
|
2301
2787
|
target
|
|
2302
2788
|
]);
|
|
2303
2789
|
}
|
|
2790
|
+
candidates.push(
|
|
2791
|
+
[source, { x: target.x, y: source.y }, target],
|
|
2792
|
+
[source, { x: source.x, y: target.y }, target]
|
|
2793
|
+
);
|
|
2304
2794
|
return candidates;
|
|
2305
2795
|
}
|
|
2796
|
+
function defaultSourceAnchor(direction) {
|
|
2797
|
+
switch (direction) {
|
|
2798
|
+
case "LR":
|
|
2799
|
+
return "right";
|
|
2800
|
+
case "RL":
|
|
2801
|
+
return "left";
|
|
2802
|
+
case "TB":
|
|
2803
|
+
return "bottom";
|
|
2804
|
+
case "BT":
|
|
2805
|
+
return "top";
|
|
2806
|
+
}
|
|
2807
|
+
}
|
|
2808
|
+
function defaultAnchorsForGeometry(source, target, direction) {
|
|
2809
|
+
const dx = target.x + target.width / 2 - (source.x + source.width / 2);
|
|
2810
|
+
const dy = target.y + target.height / 2 - (source.y + source.height / 2);
|
|
2811
|
+
if (Math.abs(dy) > Math.abs(dx)) {
|
|
2812
|
+
return dy >= 0 ? { sourceAnchor: "bottom", targetAnchor: "top" } : { sourceAnchor: "top", targetAnchor: "bottom" };
|
|
2813
|
+
}
|
|
2814
|
+
if (Math.abs(dx) > 0) {
|
|
2815
|
+
return dx >= 0 ? { sourceAnchor: "right", targetAnchor: "left" } : { sourceAnchor: "left", targetAnchor: "right" };
|
|
2816
|
+
}
|
|
2817
|
+
return {
|
|
2818
|
+
sourceAnchor: defaultSourceAnchor(direction),
|
|
2819
|
+
targetAnchor: defaultTargetAnchor(direction)
|
|
2820
|
+
};
|
|
2821
|
+
}
|
|
2822
|
+
function defaultTargetAnchor(direction) {
|
|
2823
|
+
switch (direction) {
|
|
2824
|
+
case "LR":
|
|
2825
|
+
return "left";
|
|
2826
|
+
case "RL":
|
|
2827
|
+
return "right";
|
|
2828
|
+
case "TB":
|
|
2829
|
+
return "top";
|
|
2830
|
+
case "BT":
|
|
2831
|
+
return "bottom";
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
function expandedObstacleCandidates(source, target, direction, obstacles) {
|
|
2835
|
+
if (obstacles.length === 0) {
|
|
2836
|
+
return [];
|
|
2837
|
+
}
|
|
2838
|
+
const margin = 16;
|
|
2839
|
+
const candidates = [];
|
|
2840
|
+
if (direction === "TB" || direction === "BT") {
|
|
2841
|
+
const lanes = sortedUniqueLanes(
|
|
2842
|
+
obstacles.flatMap((obstacle) => [
|
|
2843
|
+
obstacle.x - margin,
|
|
2844
|
+
obstacle.x + obstacle.width + margin
|
|
2845
|
+
]),
|
|
2846
|
+
(source.x + target.x) / 2
|
|
2847
|
+
);
|
|
2848
|
+
for (const laneX of lanes) {
|
|
2849
|
+
candidates.push([
|
|
2850
|
+
source,
|
|
2851
|
+
{ x: laneX, y: source.y },
|
|
2852
|
+
{ x: laneX, y: target.y },
|
|
2853
|
+
target
|
|
2854
|
+
]);
|
|
2855
|
+
}
|
|
2856
|
+
} else {
|
|
2857
|
+
const lanes = sortedUniqueLanes(
|
|
2858
|
+
obstacles.flatMap((obstacle) => [
|
|
2859
|
+
obstacle.y - margin,
|
|
2860
|
+
obstacle.y + obstacle.height + margin
|
|
2861
|
+
]),
|
|
2862
|
+
(source.y + target.y) / 2
|
|
2863
|
+
);
|
|
2864
|
+
for (const laneY of lanes) {
|
|
2865
|
+
candidates.push([
|
|
2866
|
+
source,
|
|
2867
|
+
{ x: source.x, y: laneY },
|
|
2868
|
+
{ x: target.x, y: laneY },
|
|
2869
|
+
target
|
|
2870
|
+
]);
|
|
2871
|
+
}
|
|
2872
|
+
}
|
|
2873
|
+
return candidates;
|
|
2874
|
+
}
|
|
2875
|
+
function sortedUniqueLanes(lanes, midpoint) {
|
|
2876
|
+
return [...new Set(lanes)].filter((lane) => Number.isFinite(lane)).sort((left, right) => {
|
|
2877
|
+
const distance = Math.abs(left - midpoint) - Math.abs(right - midpoint);
|
|
2878
|
+
return distance === 0 ? left - right : distance;
|
|
2879
|
+
});
|
|
2880
|
+
}
|
|
2306
2881
|
function routeIntersectsObstacles(points, obstacles) {
|
|
2307
2882
|
for (let index = 0; index < points.length - 1; index += 1) {
|
|
2308
2883
|
const a = points[index];
|
|
@@ -2381,12 +2956,17 @@ function solveDiagram(diagram, options = {}) {
|
|
|
2381
2956
|
options,
|
|
2382
2957
|
diagnostics
|
|
2383
2958
|
);
|
|
2959
|
+
const coordinatedSwimlanes = coordinateSwimlanes(
|
|
2960
|
+
diagram.swimlanes ?? [],
|
|
2961
|
+
constrained.boxes
|
|
2962
|
+
);
|
|
2384
2963
|
const groupBoxes = new Map(
|
|
2385
2964
|
coordinatedGroups.map((group) => [group.id, group.box])
|
|
2386
2965
|
);
|
|
2387
2966
|
const coordinatedEdges = coordinateEdges(
|
|
2388
2967
|
edges,
|
|
2389
2968
|
nodeGeometryById,
|
|
2969
|
+
coordinatedNodes,
|
|
2390
2970
|
[...nodeGeometryById.values()].map((geometry) => geometry.obstacleBox),
|
|
2391
2971
|
diagram.direction,
|
|
2392
2972
|
options,
|
|
@@ -2394,8 +2974,18 @@ function solveDiagram(diagram, options = {}) {
|
|
|
2394
2974
|
);
|
|
2395
2975
|
const allBoxes = [
|
|
2396
2976
|
...coordinatedNodes.map((node) => node.box),
|
|
2397
|
-
...
|
|
2977
|
+
...coordinatedNodes.flatMap(
|
|
2978
|
+
(node) => (node.ports ?? []).flatMap(
|
|
2979
|
+
(port) => port.label === void 0 ? [port.box] : [port.box, portLabelBox(port)]
|
|
2980
|
+
)
|
|
2981
|
+
),
|
|
2982
|
+
...groupBoxes.values(),
|
|
2983
|
+
...coordinatedSwimlanes.flatMap(
|
|
2984
|
+
(swimlane) => swimlane.box === void 0 ? [] : [swimlane.box]
|
|
2985
|
+
)
|
|
2398
2986
|
];
|
|
2987
|
+
const contentBounds = allBoxes.length === 0 ? { x: 0, y: 0, width: 0, height: 0 } : unionBoxes(allBoxes);
|
|
2988
|
+
const frame = diagram.frame === void 0 ? void 0 : coordinateFrame(diagram.frame, contentBounds);
|
|
2399
2989
|
return {
|
|
2400
2990
|
id: diagram.id,
|
|
2401
2991
|
...diagram.title === void 0 ? {} : { title: diagram.title },
|
|
@@ -2403,8 +2993,10 @@ function solveDiagram(diagram, options = {}) {
|
|
|
2403
2993
|
nodes: coordinatedNodes,
|
|
2404
2994
|
edges: coordinatedEdges,
|
|
2405
2995
|
groups: coordinatedGroups,
|
|
2996
|
+
...coordinatedSwimlanes.length === 0 ? {} : { swimlanes: coordinatedSwimlanes },
|
|
2406
2997
|
diagnostics,
|
|
2407
|
-
bounds:
|
|
2998
|
+
bounds: frame === void 0 ? contentBounds : unionBoxes([contentBounds, frame.box, frame.titleBox]),
|
|
2999
|
+
...frame === void 0 ? {} : { frame },
|
|
2408
3000
|
...diagram.metadata === void 0 ? {} : { metadata: diagram.metadata }
|
|
2409
3001
|
};
|
|
2410
3002
|
}
|
|
@@ -2430,6 +3022,9 @@ function coordinateNodes(nodes, boxes, options, diagnostics) {
|
|
|
2430
3022
|
coordinated.push({
|
|
2431
3023
|
id: node.id,
|
|
2432
3024
|
...node.label === void 0 ? {} : { label: node.label },
|
|
3025
|
+
...node.style === void 0 ? {} : { style: node.style },
|
|
3026
|
+
...node.ports === void 0 ? {} : { ports: coordinatePorts(node, box, options.portShifting) },
|
|
3027
|
+
...node.compartments === void 0 ? {} : { compartments: node.compartments },
|
|
2433
3028
|
...node.labelLayout === void 0 ? {} : { labelLayout: node.labelLayout },
|
|
2434
3029
|
shape: node.shape,
|
|
2435
3030
|
...node.metadata === void 0 ? {} : { metadata: node.metadata },
|
|
@@ -2440,6 +3035,142 @@ function coordinateNodes(nodes, boxes, options, diagnostics) {
|
|
|
2440
3035
|
}
|
|
2441
3036
|
return coordinated;
|
|
2442
3037
|
}
|
|
3038
|
+
function coordinatePorts(node, nodeBox, portShifting) {
|
|
3039
|
+
const portsBySide = /* @__PURE__ */ new Map();
|
|
3040
|
+
for (const port of node.ports ?? []) {
|
|
3041
|
+
const ports = portsBySide.get(port.side) ?? [];
|
|
3042
|
+
ports.push(port);
|
|
3043
|
+
portsBySide.set(port.side, ports);
|
|
3044
|
+
}
|
|
3045
|
+
const coordinated = [];
|
|
3046
|
+
for (const [side, ports] of portsBySide) {
|
|
3047
|
+
const sorted = [...ports ?? []].sort((a, b) => {
|
|
3048
|
+
const order = (a.order ?? 0) - (b.order ?? 0);
|
|
3049
|
+
return order === 0 ? a.id.localeCompare(b.id) : order;
|
|
3050
|
+
});
|
|
3051
|
+
for (let index = 0; index < sorted.length; index += 1) {
|
|
3052
|
+
const port = sorted[index];
|
|
3053
|
+
if (port === void 0) {
|
|
3054
|
+
continue;
|
|
3055
|
+
}
|
|
3056
|
+
const anchor = portAnchor(
|
|
3057
|
+
nodeBox,
|
|
3058
|
+
side,
|
|
3059
|
+
index,
|
|
3060
|
+
sorted.length,
|
|
3061
|
+
portShifting
|
|
3062
|
+
);
|
|
3063
|
+
const box = portBox(anchor);
|
|
3064
|
+
coordinated.push({ ...port, box, anchor });
|
|
3065
|
+
}
|
|
3066
|
+
}
|
|
3067
|
+
return coordinated.sort((a, b) => a.id.localeCompare(b.id));
|
|
3068
|
+
}
|
|
3069
|
+
function portAnchor(nodeBox, side, index, count, portShifting) {
|
|
3070
|
+
const shiftingEnabled = portShifting?.enabled ?? true;
|
|
3071
|
+
const spacing = portShifting?.spacing ?? 24;
|
|
3072
|
+
const centeredOffset = shiftingEnabled ? (index - (count - 1) / 2) * spacing : 0;
|
|
3073
|
+
switch (side) {
|
|
3074
|
+
case "left":
|
|
3075
|
+
return {
|
|
3076
|
+
x: nodeBox.x,
|
|
3077
|
+
y: nodeBox.y + nodeBox.height / 2 + centeredOffset
|
|
3078
|
+
};
|
|
3079
|
+
case "right":
|
|
3080
|
+
return {
|
|
3081
|
+
x: nodeBox.x + nodeBox.width,
|
|
3082
|
+
y: nodeBox.y + nodeBox.height / 2 + centeredOffset
|
|
3083
|
+
};
|
|
3084
|
+
case "top":
|
|
3085
|
+
return {
|
|
3086
|
+
x: nodeBox.x + nodeBox.width / 2 + centeredOffset,
|
|
3087
|
+
y: nodeBox.y
|
|
3088
|
+
};
|
|
3089
|
+
case "bottom":
|
|
3090
|
+
return {
|
|
3091
|
+
x: nodeBox.x + nodeBox.width / 2 + centeredOffset,
|
|
3092
|
+
y: nodeBox.y + nodeBox.height
|
|
3093
|
+
};
|
|
3094
|
+
}
|
|
3095
|
+
}
|
|
3096
|
+
function portBox(anchor) {
|
|
3097
|
+
const size = 10;
|
|
3098
|
+
return {
|
|
3099
|
+
x: anchor.x - size / 2,
|
|
3100
|
+
y: anchor.y - size / 2,
|
|
3101
|
+
width: size,
|
|
3102
|
+
height: size
|
|
3103
|
+
};
|
|
3104
|
+
}
|
|
3105
|
+
function portLabelBox(port) {
|
|
3106
|
+
const textWidth = Math.max(0, (port.label?.text.length ?? 0) * 6);
|
|
3107
|
+
const height = 12;
|
|
3108
|
+
const gap = 8;
|
|
3109
|
+
const x = port.side === "left" ? port.anchor.x - gap - textWidth : port.anchor.x + gap;
|
|
3110
|
+
return {
|
|
3111
|
+
x,
|
|
3112
|
+
y: port.anchor.y - 8 - height,
|
|
3113
|
+
width: textWidth,
|
|
3114
|
+
height
|
|
3115
|
+
};
|
|
3116
|
+
}
|
|
3117
|
+
function coordinateSwimlanes(swimlanes, nodeBoxes) {
|
|
3118
|
+
const titleSize = 28;
|
|
3119
|
+
const padding = 16;
|
|
3120
|
+
return swimlanes.map((swimlane) => {
|
|
3121
|
+
const laneBoxes = swimlane.lanes.flatMap((lane) => {
|
|
3122
|
+
const childBoxes = lane.children.map((child) => nodeBoxes.get(child)).filter((box) => box !== void 0);
|
|
3123
|
+
return childBoxes.length === 0 ? [] : [unionBoxes(childBoxes)];
|
|
3124
|
+
});
|
|
3125
|
+
const laneUnion = laneBoxes.length === 0 ? { x: 0, y: 0, width: 120, height: 80 } : unionBoxes(laneBoxes);
|
|
3126
|
+
const outer = expand(laneUnion, padding, titleSize);
|
|
3127
|
+
const laneCount = Math.max(1, swimlane.lanes.length);
|
|
3128
|
+
const lanes = swimlane.lanes.map((lane, index) => {
|
|
3129
|
+
const box = swimlane.orientation === "vertical" ? {
|
|
3130
|
+
x: outer.x + outer.width / laneCount * index,
|
|
3131
|
+
y: outer.y,
|
|
3132
|
+
width: outer.width / laneCount,
|
|
3133
|
+
height: outer.height
|
|
3134
|
+
} : {
|
|
3135
|
+
x: outer.x,
|
|
3136
|
+
y: outer.y + outer.height / laneCount * index,
|
|
3137
|
+
width: outer.width,
|
|
3138
|
+
height: outer.height / laneCount
|
|
3139
|
+
};
|
|
3140
|
+
return { ...lane, box };
|
|
3141
|
+
});
|
|
3142
|
+
return { ...swimlane, lanes, box: outer };
|
|
3143
|
+
});
|
|
3144
|
+
}
|
|
3145
|
+
function coordinateFrame(frame, contentBounds) {
|
|
3146
|
+
const padding = 32;
|
|
3147
|
+
const titleHeight = 28;
|
|
3148
|
+
const titleWidth = Math.max(180, frame.titleTab.length * 7);
|
|
3149
|
+
const box = {
|
|
3150
|
+
x: contentBounds.x - padding,
|
|
3151
|
+
y: contentBounds.y - padding - titleHeight,
|
|
3152
|
+
width: contentBounds.width + padding * 2,
|
|
3153
|
+
height: contentBounds.height + padding * 2 + titleHeight
|
|
3154
|
+
};
|
|
3155
|
+
return {
|
|
3156
|
+
...frame,
|
|
3157
|
+
box,
|
|
3158
|
+
titleBox: {
|
|
3159
|
+
x: box.x,
|
|
3160
|
+
y: box.y,
|
|
3161
|
+
width: Math.min(titleWidth, box.width * 0.8),
|
|
3162
|
+
height: titleHeight
|
|
3163
|
+
}
|
|
3164
|
+
};
|
|
3165
|
+
}
|
|
3166
|
+
function expand(box, padding, titleSize) {
|
|
3167
|
+
return {
|
|
3168
|
+
x: box.x - padding,
|
|
3169
|
+
y: box.y - padding - titleSize,
|
|
3170
|
+
width: box.width + padding * 2,
|
|
3171
|
+
height: box.height + padding * 2 + titleSize
|
|
3172
|
+
};
|
|
3173
|
+
}
|
|
2443
3174
|
function coordinateGroups(groups, nodeBoxes, options, diagnostics) {
|
|
2444
3175
|
const coordinated = [];
|
|
2445
3176
|
const groupBoxes = /* @__PURE__ */ new Map();
|
|
@@ -2488,8 +3219,11 @@ function coordinateGroups(groups, nodeBoxes, options, diagnostics) {
|
|
|
2488
3219
|
}
|
|
2489
3220
|
return coordinated;
|
|
2490
3221
|
}
|
|
2491
|
-
function coordinateEdges(edges, nodes, obstacles, direction, options, diagnostics) {
|
|
3222
|
+
function coordinateEdges(edges, nodes, coordinatedNodes, obstacles, direction, options, diagnostics) {
|
|
2492
3223
|
const coordinated = [];
|
|
3224
|
+
const coordinatedNodeById = new Map(
|
|
3225
|
+
coordinatedNodes.map((node) => [node.id, node])
|
|
3226
|
+
);
|
|
2493
3227
|
for (const edge of edges) {
|
|
2494
3228
|
const source = nodes.get(edge.source.nodeId);
|
|
2495
3229
|
const target = nodes.get(edge.target.nodeId);
|
|
@@ -2507,11 +3241,13 @@ function coordinateEdges(edges, nodes, obstacles, direction, options, diagnostic
|
|
|
2507
3241
|
});
|
|
2508
3242
|
continue;
|
|
2509
3243
|
}
|
|
3244
|
+
const sourcePort = coordinatedNodeById.get(edge.source.nodeId)?.ports?.find((port) => port.id === edge.source.portId);
|
|
3245
|
+
const targetPort = coordinatedNodeById.get(edge.target.nodeId)?.ports?.find((port) => port.id === edge.target.portId);
|
|
2510
3246
|
const route = routeEdge({
|
|
2511
3247
|
kind: options.routeKind ?? "orthogonal",
|
|
2512
3248
|
direction,
|
|
2513
|
-
source,
|
|
2514
|
-
target,
|
|
3249
|
+
source: portGeometry(source, sourcePort),
|
|
3250
|
+
target: portGeometry(target, targetPort),
|
|
2515
3251
|
...edge.source.anchor === void 0 ? {} : { sourceAnchor: edge.source.anchor },
|
|
2516
3252
|
...edge.target.anchor === void 0 ? {} : { targetAnchor: edge.target.anchor },
|
|
2517
3253
|
obstacles: obstacles.filter(
|
|
@@ -2531,6 +3267,21 @@ function coordinateEdges(edges, nodes, obstacles, direction, options, diagnostic
|
|
|
2531
3267
|
}
|
|
2532
3268
|
return coordinated;
|
|
2533
3269
|
}
|
|
3270
|
+
function portGeometry(nodeGeometry, port) {
|
|
3271
|
+
if (port === void 0) {
|
|
3272
|
+
return nodeGeometry;
|
|
3273
|
+
}
|
|
3274
|
+
return {
|
|
3275
|
+
...nodeGeometry,
|
|
3276
|
+
box: port.box,
|
|
3277
|
+
center: port.anchor,
|
|
3278
|
+
anchors: nodeGeometry.anchors.map((anchor) => ({
|
|
3279
|
+
name: anchor.name,
|
|
3280
|
+
point: port.anchor
|
|
3281
|
+
})),
|
|
3282
|
+
obstacleBox: port.box
|
|
3283
|
+
};
|
|
3284
|
+
}
|
|
2534
3285
|
function stableById(items) {
|
|
2535
3286
|
return [...items].sort((a, b) => a.id.localeCompare(b.id));
|
|
2536
3287
|
}
|
|
@@ -2591,7 +3342,8 @@ function renderDiagramDsl(source, options = {}) {
|
|
|
2591
3342
|
return { diagnostics };
|
|
2592
3343
|
}
|
|
2593
3344
|
const solved = solveDiagram(normalized.diagram, {
|
|
2594
|
-
routeKind: normalized.diagram.metadata?.routeKind === "straight" ? "straight" : "orthogonal"
|
|
3345
|
+
routeKind: normalized.diagram.metadata?.routeKind === "straight" ? "straight" : "orthogonal",
|
|
3346
|
+
...solvePortShiftingOption(normalized.diagram.metadata?.portShifting)
|
|
2595
3347
|
});
|
|
2596
3348
|
const solveDiagnostics = solved.diagnostics.map(toSolveDiagnostic);
|
|
2597
3349
|
if (hasErrorDiagnostics2(solveDiagnostics)) {
|
|
@@ -2630,6 +3382,22 @@ function renderDiagramDsl(source, options = {}) {
|
|
|
2630
3382
|
function toSolveDiagnostic(diagnostic) {
|
|
2631
3383
|
return { ...diagnostic, layer: "solve" };
|
|
2632
3384
|
}
|
|
3385
|
+
function solvePortShiftingOption(value) {
|
|
3386
|
+
if (!isJsonObject(value)) {
|
|
3387
|
+
return {};
|
|
3388
|
+
}
|
|
3389
|
+
const portShifting = {};
|
|
3390
|
+
if (value.enabled === false) {
|
|
3391
|
+
portShifting.enabled = false;
|
|
3392
|
+
}
|
|
3393
|
+
if (typeof value.spacing === "number") {
|
|
3394
|
+
portShifting.spacing = value.spacing;
|
|
3395
|
+
}
|
|
3396
|
+
return { portShifting };
|
|
3397
|
+
}
|
|
3398
|
+
function isJsonObject(value) {
|
|
3399
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3400
|
+
}
|
|
2633
3401
|
function toExportDiagnostic(diagnostic) {
|
|
2634
3402
|
return { ...diagnostic, layer: "export" };
|
|
2635
3403
|
}
|
|
@@ -2776,11 +3544,13 @@ exports.canonicalize = canonicalize;
|
|
|
2776
3544
|
exports.computeArrowhead = computeArrowhead;
|
|
2777
3545
|
exports.computeContainerGeometry = computeContainerGeometry;
|
|
2778
3546
|
exports.computeShapeGeometry = computeShapeGeometry;
|
|
3547
|
+
exports.createDefaultTextMeasurer = createDefaultTextMeasurer;
|
|
2779
3548
|
exports.expandBox = expandBox;
|
|
2780
3549
|
exports.exportExcalidraw = exportExcalidraw;
|
|
2781
3550
|
exports.exportSvg = exportSvg;
|
|
2782
3551
|
exports.fitLabel = fitLabel;
|
|
2783
3552
|
exports.getEdgePort = getEdgePort;
|
|
3553
|
+
exports.installNodeCanvasRuntime = installNodeCanvasRuntime;
|
|
2784
3554
|
exports.intersectsAabb = intersectsAabb;
|
|
2785
3555
|
exports.isPretextRuntimeAvailable = isPretextRuntimeAvailable;
|
|
2786
3556
|
exports.normalizeDiagramDsl = normalizeDiagramDsl;
|