@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.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createRequire } from 'module';
|
|
1
2
|
import { prepareWithSegments, layoutWithLines, measureNaturalWidth } from '@chenglou/pretext';
|
|
2
3
|
import { Buffer } from 'buffer';
|
|
3
4
|
import { parseDocument } from 'yaml';
|
|
@@ -820,34 +821,34 @@ function assertFiniteNonNegative(value, label) {
|
|
|
820
821
|
throw new TypeError(`${label} must be a finite non-negative width`);
|
|
821
822
|
}
|
|
822
823
|
}
|
|
823
|
-
function validateTextStyle(
|
|
824
|
-
assertFinitePositive(
|
|
825
|
-
if (
|
|
826
|
-
assertFinitePositive(
|
|
824
|
+
function validateTextStyle(style2) {
|
|
825
|
+
assertFinitePositive(style2.fontSize, "fontSize");
|
|
826
|
+
if (style2.lineHeight !== void 0) {
|
|
827
|
+
assertFinitePositive(style2.lineHeight, "lineHeight");
|
|
827
828
|
}
|
|
828
|
-
if (
|
|
829
|
+
if (style2.letterSpacing !== void 0 && !Number.isFinite(style2.letterSpacing)) {
|
|
829
830
|
throw new TypeError("letterSpacing must be finite");
|
|
830
831
|
}
|
|
831
832
|
}
|
|
832
|
-
function resolveLineHeight(
|
|
833
|
-
validateTextStyle(
|
|
834
|
-
return
|
|
833
|
+
function resolveLineHeight(style2) {
|
|
834
|
+
validateTextStyle(style2);
|
|
835
|
+
return style2.lineHeight ?? style2.fontSize * 1.2;
|
|
835
836
|
}
|
|
836
|
-
function toCanvasFont(
|
|
837
|
-
validateTextStyle(
|
|
838
|
-
const fontStyle =
|
|
839
|
-
const fontWeight =
|
|
840
|
-
return `${fontStyle}${fontWeight} ${
|
|
837
|
+
function toCanvasFont(style2) {
|
|
838
|
+
validateTextStyle(style2);
|
|
839
|
+
const fontStyle = style2.fontStyle === "italic" ? "italic " : "";
|
|
840
|
+
const fontWeight = style2.fontWeight ?? 400;
|
|
841
|
+
return `${fontStyle}${fontWeight} ${style2.fontSize}px ${style2.fontFamily}`;
|
|
841
842
|
}
|
|
842
843
|
|
|
843
844
|
// src/text/fallback.ts
|
|
844
845
|
var DeterministicTextMeasurer = class {
|
|
845
|
-
prepare(text,
|
|
846
|
-
validateTextStyle(
|
|
846
|
+
prepare(text, style2) {
|
|
847
|
+
validateTextStyle(style2);
|
|
847
848
|
return {
|
|
848
849
|
text,
|
|
849
|
-
font: toCanvasFont(
|
|
850
|
-
style: { ...
|
|
850
|
+
font: toCanvasFont(style2),
|
|
851
|
+
style: { ...style2 },
|
|
851
852
|
backend: "deterministic"
|
|
852
853
|
};
|
|
853
854
|
}
|
|
@@ -906,9 +907,9 @@ var DeterministicTextMeasurer = class {
|
|
|
906
907
|
return output;
|
|
907
908
|
}
|
|
908
909
|
};
|
|
909
|
-
function getCharacterWidth(
|
|
910
|
-
const letterSpacing =
|
|
911
|
-
return Math.max(0,
|
|
910
|
+
function getCharacterWidth(style2) {
|
|
911
|
+
const letterSpacing = style2.letterSpacing ?? 0;
|
|
912
|
+
return Math.max(0, style2.fontSize * 0.6 + letterSpacing);
|
|
912
913
|
}
|
|
913
914
|
function createLine(text, width, segmentIndex, start, end) {
|
|
914
915
|
return {
|
|
@@ -929,27 +930,53 @@ function assertFinitePositiveLineHeight(lineHeight) {
|
|
|
929
930
|
throw new TypeError("lineHeight must be finite and positive");
|
|
930
931
|
}
|
|
931
932
|
}
|
|
933
|
+
var require2 = createRequire(import.meta.url);
|
|
934
|
+
function installNodeCanvasRuntime(loadNodeCanvasModule = loadDefaultNodeCanvasModule) {
|
|
935
|
+
if (typeof globalThis.OffscreenCanvas === "function") {
|
|
936
|
+
return true;
|
|
937
|
+
}
|
|
938
|
+
try {
|
|
939
|
+
const canvasModule = loadNodeCanvasModule();
|
|
940
|
+
const { createCanvas } = canvasModule;
|
|
941
|
+
const NodeOffscreenCanvas = class {
|
|
942
|
+
canvas;
|
|
943
|
+
constructor(width, height) {
|
|
944
|
+
this.canvas = createCanvas(width, height);
|
|
945
|
+
}
|
|
946
|
+
getContext(contextId) {
|
|
947
|
+
return contextId === "2d" ? this.canvas.getContext("2d") : null;
|
|
948
|
+
}
|
|
949
|
+
};
|
|
950
|
+
globalThis.OffscreenCanvas = NodeOffscreenCanvas;
|
|
951
|
+
return true;
|
|
952
|
+
} catch {
|
|
953
|
+
return false;
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
function loadDefaultNodeCanvasModule() {
|
|
957
|
+
return require2("@napi-rs/canvas");
|
|
958
|
+
}
|
|
932
959
|
var RUNTIME_UNAVAILABLE = "text.pretext.runtime-unavailable";
|
|
933
960
|
function isPretextRuntimeAvailable() {
|
|
934
961
|
return typeof Intl.Segmenter === "function" && typeof globalThis.OffscreenCanvas === "function";
|
|
935
962
|
}
|
|
936
963
|
var PretextTextMeasurer = class {
|
|
937
|
-
prepare(text,
|
|
964
|
+
prepare(text, style2) {
|
|
938
965
|
if (!isPretextRuntimeAvailable()) {
|
|
939
966
|
throw new TypeError(RUNTIME_UNAVAILABLE);
|
|
940
967
|
}
|
|
941
|
-
validateTextStyle(
|
|
942
|
-
const font = toCanvasFont(
|
|
968
|
+
validateTextStyle(style2);
|
|
969
|
+
const font = toCanvasFont(style2);
|
|
943
970
|
const options = {
|
|
944
|
-
...
|
|
945
|
-
...
|
|
946
|
-
...
|
|
971
|
+
...style2.whiteSpace === void 0 ? {} : { whiteSpace: style2.whiteSpace },
|
|
972
|
+
...style2.wordBreak === void 0 ? {} : { wordBreak: style2.wordBreak },
|
|
973
|
+
...style2.letterSpacing === void 0 ? {} : { letterSpacing: style2.letterSpacing }
|
|
947
974
|
};
|
|
948
975
|
const prepared = prepareWithSegments(text, font, options);
|
|
949
976
|
return {
|
|
950
977
|
text,
|
|
951
978
|
font,
|
|
952
|
-
style: { ...
|
|
979
|
+
style: { ...style2 },
|
|
953
980
|
backend: "pretext",
|
|
954
981
|
pretextPrepared: prepared
|
|
955
982
|
};
|
|
@@ -999,6 +1026,13 @@ function toInternalPrepared(prepared) {
|
|
|
999
1026
|
return prepared.pretextPrepared;
|
|
1000
1027
|
}
|
|
1001
1028
|
|
|
1029
|
+
// src/text/default.ts
|
|
1030
|
+
function createDefaultTextMeasurer(options = {}) {
|
|
1031
|
+
const installRuntime = options.installNodeCanvasRuntime ?? installNodeCanvasRuntime;
|
|
1032
|
+
installRuntime();
|
|
1033
|
+
return isPretextRuntimeAvailable() ? new PretextTextMeasurer() : new DeterministicTextMeasurer();
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1002
1036
|
// src/labels/fit.ts
|
|
1003
1037
|
function fitLabel(text, options, measurer) {
|
|
1004
1038
|
return computeLabelLayout(text, options, measurer);
|
|
@@ -1065,6 +1099,7 @@ function computeLabelLayout(text, options, measurer) {
|
|
|
1065
1099
|
fittedSize,
|
|
1066
1100
|
padding,
|
|
1067
1101
|
font: { ...options.font },
|
|
1102
|
+
textBackend: prepared.backend,
|
|
1068
1103
|
lineHeight,
|
|
1069
1104
|
lines: buildLines(textLayout, contentBox2, lineHeight),
|
|
1070
1105
|
overflow,
|
|
@@ -1159,8 +1194,9 @@ function normalizeDiagramDsl(dslValue, options = {}) {
|
|
|
1159
1194
|
...outputResult(dsl)
|
|
1160
1195
|
};
|
|
1161
1196
|
}
|
|
1162
|
-
const measurer = options.textMeasurer ??
|
|
1197
|
+
const measurer = options.textMeasurer ?? createDefaultTextMeasurer();
|
|
1163
1198
|
const routeKind = dsl.routing?.kind ?? "orthogonal";
|
|
1199
|
+
const portShifting = normalizePortShifting(dsl.routing?.portShifting);
|
|
1164
1200
|
const diagram = {
|
|
1165
1201
|
id: options.id ?? dsl.id ?? "diagram",
|
|
1166
1202
|
...dsl.title === void 0 ? {} : { title: dsl.title },
|
|
@@ -1168,9 +1204,14 @@ function normalizeDiagramDsl(dslValue, options = {}) {
|
|
|
1168
1204
|
nodes: normalizeNodes(dsl, measurer),
|
|
1169
1205
|
edges: normalizeEdges(dsl),
|
|
1170
1206
|
groups: normalizeGroups(dsl, measurer),
|
|
1207
|
+
swimlanes: normalizeSwimlanes(dsl),
|
|
1171
1208
|
constraints: normalizeConstraints(dsl),
|
|
1172
1209
|
diagnostics: [],
|
|
1173
|
-
|
|
1210
|
+
...dsl.frame === void 0 ? {} : { frame: normalizeFrame(dsl.frame) },
|
|
1211
|
+
metadata: {
|
|
1212
|
+
routeKind,
|
|
1213
|
+
...portShifting === void 0 ? {} : { portShifting }
|
|
1214
|
+
}
|
|
1174
1215
|
};
|
|
1175
1216
|
return {
|
|
1176
1217
|
diagram,
|
|
@@ -1178,6 +1219,15 @@ function normalizeDiagramDsl(dslValue, options = {}) {
|
|
|
1178
1219
|
...outputResult(dsl)
|
|
1179
1220
|
};
|
|
1180
1221
|
}
|
|
1222
|
+
function normalizePortShifting(portShifting) {
|
|
1223
|
+
if (portShifting === void 0) {
|
|
1224
|
+
return void 0;
|
|
1225
|
+
}
|
|
1226
|
+
return {
|
|
1227
|
+
...portShifting.enabled === void 0 ? {} : { enabled: portShifting.enabled },
|
|
1228
|
+
...portShifting.spacing === void 0 ? {} : { spacing: portShifting.spacing }
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
1181
1231
|
function outputResult(dsl) {
|
|
1182
1232
|
return dsl.output?.format === void 0 ? {} : { output: { format: dsl.output.format } };
|
|
1183
1233
|
}
|
|
@@ -1187,15 +1237,24 @@ function normalizeNodes(dsl, measurer) {
|
|
|
1187
1237
|
const label = toLabel(node?.label);
|
|
1188
1238
|
const labelLayout = label === void 0 ? void 0 : fitDslLabel(label, measurer);
|
|
1189
1239
|
const fittedSize = labelLayout?.fittedSize;
|
|
1240
|
+
const nodeCompartments = node?.compartments === void 0 ? void 0 : compartments(node.compartments);
|
|
1241
|
+
const compartmentWidth = nodeCompartments === void 0 ? 0 : compartmentNaturalWidth(id, label, nodeCompartments, measurer);
|
|
1190
1242
|
return {
|
|
1191
1243
|
id,
|
|
1192
1244
|
...label === void 0 ? {} : { label },
|
|
1193
1245
|
shape: node?.shape ?? "rectangle",
|
|
1194
1246
|
...node?.position === void 0 ? {} : { position: point(node.position) },
|
|
1247
|
+
...node?.style === void 0 ? {} : { style: style(node.style) },
|
|
1248
|
+
...node?.ports === void 0 ? {} : { ports: normalizePorts(node.ports) },
|
|
1249
|
+
...nodeCompartments === void 0 ? {} : { compartments: nodeCompartments },
|
|
1195
1250
|
size: {
|
|
1196
|
-
width: Math.max(
|
|
1251
|
+
width: Math.max(
|
|
1252
|
+
DEFAULT_NODE_MIN_SIZE.width,
|
|
1253
|
+
fittedSize?.width ?? 0,
|
|
1254
|
+
compartmentWidth
|
|
1255
|
+
),
|
|
1197
1256
|
height: Math.max(
|
|
1198
|
-
DEFAULT_NODE_MIN_SIZE.height,
|
|
1257
|
+
nodeCompartments === void 0 ? DEFAULT_NODE_MIN_SIZE.height : compartmentHeight(nodeCompartments),
|
|
1199
1258
|
fittedSize?.height ?? 0
|
|
1200
1259
|
)
|
|
1201
1260
|
},
|
|
@@ -1204,11 +1263,42 @@ function normalizeNodes(dsl, measurer) {
|
|
|
1204
1263
|
};
|
|
1205
1264
|
});
|
|
1206
1265
|
}
|
|
1266
|
+
function compartmentHeight(value) {
|
|
1267
|
+
const rowCount = (value.stereotype === void 0 ? 0 : 1) + 1 + (value.properties?.length ?? 0) + (value.constraints?.length ?? 0);
|
|
1268
|
+
const rowHeight = 16;
|
|
1269
|
+
const verticalPadding = 20;
|
|
1270
|
+
return Math.max(
|
|
1271
|
+
DEFAULT_NODE_MIN_SIZE.height,
|
|
1272
|
+
rowCount * rowHeight + verticalPadding
|
|
1273
|
+
);
|
|
1274
|
+
}
|
|
1275
|
+
function compartmentNaturalWidth(id, label, value, measurer) {
|
|
1276
|
+
const rows = compartmentRows(id, label, value);
|
|
1277
|
+
const maxRowWidth = rows.reduce((width, row) => {
|
|
1278
|
+
const prepared = measurer.prepare(row, DEFAULT_FONT);
|
|
1279
|
+
return Math.max(width, measurer.naturalWidth(prepared));
|
|
1280
|
+
}, 0);
|
|
1281
|
+
return Math.ceil(
|
|
1282
|
+
maxRowWidth + DEFAULT_NODE_PADDING.left + DEFAULT_NODE_PADDING.right
|
|
1283
|
+
);
|
|
1284
|
+
}
|
|
1285
|
+
function compartmentRows(id, label, value) {
|
|
1286
|
+
return [
|
|
1287
|
+
...value.stereotype === void 0 ? [] : [value.stereotype],
|
|
1288
|
+
value.name ?? label?.text ?? id,
|
|
1289
|
+
...value.properties ?? [],
|
|
1290
|
+
...value.constraints ?? []
|
|
1291
|
+
];
|
|
1292
|
+
}
|
|
1207
1293
|
function normalizeEdges(dsl) {
|
|
1208
1294
|
const counts = /* @__PURE__ */ new Map();
|
|
1209
1295
|
return (dsl.edges ?? []).map((edge) => {
|
|
1210
|
-
const
|
|
1211
|
-
const
|
|
1296
|
+
const source = typeof edge === "string" ? void 0 : edge.source;
|
|
1297
|
+
const target = typeof edge === "string" ? void 0 : edge.target;
|
|
1298
|
+
const sourceId = typeof edge === "string" ? "" : edge.sourceId ?? endpointNodeId(source) ?? "";
|
|
1299
|
+
const targetId = typeof edge === "string" ? "" : edge.targetId ?? endpointNodeId(target) ?? "";
|
|
1300
|
+
const sourceEndpoint = typeof edge === "string" ? { nodeId: sourceId } : endpoint(source, edge.sourceId);
|
|
1301
|
+
const targetEndpoint = typeof edge === "string" ? { nodeId: targetId } : endpoint(target, edge.targetId);
|
|
1212
1302
|
const baseId = `${sourceId}-${targetId}`;
|
|
1213
1303
|
const count = counts.get(baseId) ?? 0;
|
|
1214
1304
|
counts.set(baseId, count + 1);
|
|
@@ -1216,9 +1306,96 @@ function normalizeEdges(dsl) {
|
|
|
1216
1306
|
const label = typeof edge === "string" ? void 0 : toLabel(edge.label);
|
|
1217
1307
|
return {
|
|
1218
1308
|
id,
|
|
1219
|
-
source:
|
|
1220
|
-
target:
|
|
1221
|
-
...label === void 0 ? {} : { label }
|
|
1309
|
+
source: sourceEndpoint,
|
|
1310
|
+
target: targetEndpoint,
|
|
1311
|
+
...label === void 0 ? {} : { label },
|
|
1312
|
+
...typeof edge === "string" || edge.style === void 0 ? {} : { style: edge.style },
|
|
1313
|
+
...typeof edge === "string" || edge.arrowhead === void 0 ? {} : { arrowhead: edge.arrowhead }
|
|
1314
|
+
};
|
|
1315
|
+
});
|
|
1316
|
+
}
|
|
1317
|
+
function normalizePorts(ports) {
|
|
1318
|
+
return Object.keys(ports ?? {}).sort().map((id) => {
|
|
1319
|
+
const port = ports?.[id];
|
|
1320
|
+
const label = toLabel(port?.label);
|
|
1321
|
+
return {
|
|
1322
|
+
id,
|
|
1323
|
+
...label === void 0 ? {} : { label },
|
|
1324
|
+
side: port?.side ?? "right",
|
|
1325
|
+
kind: port?.kind ?? "proxy",
|
|
1326
|
+
...port?.order === void 0 ? {} : { order: port.order },
|
|
1327
|
+
...port?.style === void 0 ? {} : { style: style(port.style) }
|
|
1328
|
+
};
|
|
1329
|
+
});
|
|
1330
|
+
}
|
|
1331
|
+
function endpoint(value, nodeIdOverride) {
|
|
1332
|
+
if (nodeIdOverride !== void 0) {
|
|
1333
|
+
return {
|
|
1334
|
+
nodeId: nodeIdOverride,
|
|
1335
|
+
...typeof value === "object" && value.node === nodeIdOverride && value.port !== void 0 ? { portId: value.port } : {}
|
|
1336
|
+
};
|
|
1337
|
+
}
|
|
1338
|
+
if (value === void 0) {
|
|
1339
|
+
return { nodeId: "" };
|
|
1340
|
+
}
|
|
1341
|
+
if (typeof value === "string") {
|
|
1342
|
+
return { nodeId: value };
|
|
1343
|
+
}
|
|
1344
|
+
return {
|
|
1345
|
+
nodeId: value.node,
|
|
1346
|
+
...value.port === void 0 ? {} : { portId: value.port }
|
|
1347
|
+
};
|
|
1348
|
+
}
|
|
1349
|
+
function style(value) {
|
|
1350
|
+
return {
|
|
1351
|
+
...value.fill === void 0 ? {} : { fill: value.fill },
|
|
1352
|
+
...value.stroke === void 0 ? {} : { stroke: value.stroke }
|
|
1353
|
+
};
|
|
1354
|
+
}
|
|
1355
|
+
function compartments(value) {
|
|
1356
|
+
return {
|
|
1357
|
+
...value.stereotype === void 0 ? {} : { stereotype: value.stereotype },
|
|
1358
|
+
...value.name === void 0 ? {} : { name: value.name },
|
|
1359
|
+
...value.properties === void 0 ? {} : { properties: value.properties.map(formatCompartmentEntry) },
|
|
1360
|
+
...value.constraints === void 0 ? {} : { constraints: [...value.constraints] }
|
|
1361
|
+
};
|
|
1362
|
+
}
|
|
1363
|
+
function normalizeFrame(frame) {
|
|
1364
|
+
return {
|
|
1365
|
+
kind: frame.kind,
|
|
1366
|
+
...frame.context === void 0 ? {} : { context: frame.context },
|
|
1367
|
+
...frame.name === void 0 ? {} : { name: frame.name },
|
|
1368
|
+
titleTab: frame.titleTab,
|
|
1369
|
+
...frame.style === void 0 ? {} : { style: style(frame.style) }
|
|
1370
|
+
};
|
|
1371
|
+
}
|
|
1372
|
+
function formatCompartmentEntry(value) {
|
|
1373
|
+
if (typeof value === "string") {
|
|
1374
|
+
return value;
|
|
1375
|
+
}
|
|
1376
|
+
const [entry] = Object.entries(value);
|
|
1377
|
+
if (entry === void 0) {
|
|
1378
|
+
return "";
|
|
1379
|
+
}
|
|
1380
|
+
return `${entry[0]}: ${entry[1]}`;
|
|
1381
|
+
}
|
|
1382
|
+
function normalizeSwimlanes(dsl) {
|
|
1383
|
+
return Object.keys(dsl.swimlanes ?? {}).sort().map((id) => {
|
|
1384
|
+
const swimlane = dsl.swimlanes?.[id];
|
|
1385
|
+
const label = toLabel(swimlane?.label);
|
|
1386
|
+
return {
|
|
1387
|
+
id,
|
|
1388
|
+
...label === void 0 ? {} : { label },
|
|
1389
|
+
orientation: swimlane?.orientation ?? "vertical",
|
|
1390
|
+
lanes: Object.keys(swimlane?.lanes ?? {}).sort().map((laneId) => {
|
|
1391
|
+
const lane = swimlane?.lanes[laneId];
|
|
1392
|
+
const laneLabel = toLabel(lane?.label);
|
|
1393
|
+
return {
|
|
1394
|
+
id: laneId,
|
|
1395
|
+
...laneLabel === void 0 ? {} : { label: laneLabel },
|
|
1396
|
+
children: [...lane?.children ?? []]
|
|
1397
|
+
};
|
|
1398
|
+
})
|
|
1222
1399
|
};
|
|
1223
1400
|
});
|
|
1224
1401
|
}
|
|
@@ -1292,14 +1469,28 @@ function validateReferences(dsl) {
|
|
|
1292
1469
|
if (typeof edge === "string") {
|
|
1293
1470
|
return;
|
|
1294
1471
|
}
|
|
1295
|
-
const sourceId = edge.sourceId ?? edge.source;
|
|
1296
|
-
const targetId = edge.targetId ?? edge.target;
|
|
1472
|
+
const sourceId = edge.sourceId ?? endpointNodeId(edge.source);
|
|
1473
|
+
const targetId = edge.targetId ?? endpointNodeId(edge.target);
|
|
1474
|
+
const sourceEndpoint = endpoint(edge.source, edge.sourceId);
|
|
1475
|
+
const targetEndpoint = endpoint(edge.target, edge.targetId);
|
|
1297
1476
|
if (sourceId !== void 0 && !nodeIds.has(sourceId)) {
|
|
1298
1477
|
diagnostics.push(referenceMissing(["edges", index, "source"], sourceId));
|
|
1299
1478
|
}
|
|
1300
1479
|
if (targetId !== void 0 && !nodeIds.has(targetId)) {
|
|
1301
1480
|
diagnostics.push(referenceMissing(["edges", index, "target"], targetId));
|
|
1302
1481
|
}
|
|
1482
|
+
validateEndpointPort(
|
|
1483
|
+
dsl,
|
|
1484
|
+
sourceEndpoint,
|
|
1485
|
+
["edges", index, "source"],
|
|
1486
|
+
diagnostics
|
|
1487
|
+
);
|
|
1488
|
+
validateEndpointPort(
|
|
1489
|
+
dsl,
|
|
1490
|
+
targetEndpoint,
|
|
1491
|
+
["edges", index, "target"],
|
|
1492
|
+
diagnostics
|
|
1493
|
+
);
|
|
1303
1494
|
});
|
|
1304
1495
|
for (const [groupId, group] of Object.entries(dsl.groups ?? {})) {
|
|
1305
1496
|
(group.nodes ?? []).forEach((nodeId, index) => {
|
|
@@ -1317,6 +1508,27 @@ function validateReferences(dsl) {
|
|
|
1317
1508
|
}
|
|
1318
1509
|
});
|
|
1319
1510
|
}
|
|
1511
|
+
for (const [swimlaneId, swimlane] of Object.entries(dsl.swimlanes ?? {})) {
|
|
1512
|
+
for (const [laneId, lane] of Object.entries(swimlane.lanes)) {
|
|
1513
|
+
(lane.children ?? []).forEach((child, childIndex) => {
|
|
1514
|
+
if (!nodeIds.has(child)) {
|
|
1515
|
+
diagnostics.push(
|
|
1516
|
+
referenceMissing(
|
|
1517
|
+
[
|
|
1518
|
+
"swimlanes",
|
|
1519
|
+
swimlaneId,
|
|
1520
|
+
"lanes",
|
|
1521
|
+
laneId,
|
|
1522
|
+
"children",
|
|
1523
|
+
childIndex
|
|
1524
|
+
],
|
|
1525
|
+
child
|
|
1526
|
+
)
|
|
1527
|
+
);
|
|
1528
|
+
}
|
|
1529
|
+
});
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1320
1532
|
(dsl.constraints ?? []).forEach((constraint, index) => {
|
|
1321
1533
|
switch (constraint.kind) {
|
|
1322
1534
|
case "exact-position": {
|
|
@@ -1360,10 +1572,12 @@ function validateReferences(dsl) {
|
|
|
1360
1572
|
break;
|
|
1361
1573
|
case "containment": {
|
|
1362
1574
|
const container = constraint.containerId ?? constraint.container;
|
|
1363
|
-
if (container !== void 0
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1575
|
+
if (container !== void 0) {
|
|
1576
|
+
if (!nodeIds.has(container)) {
|
|
1577
|
+
diagnostics.push(
|
|
1578
|
+
referenceMissing(["constraints", index, "container"], container)
|
|
1579
|
+
);
|
|
1580
|
+
}
|
|
1367
1581
|
}
|
|
1368
1582
|
(constraint.childIds ?? constraint.children ?? []).forEach(
|
|
1369
1583
|
(child, childIndex) => {
|
|
@@ -1393,8 +1607,23 @@ function referenceMissing(path, id) {
|
|
|
1393
1607
|
hint: "Define the referenced node or group id, or update this reference."
|
|
1394
1608
|
};
|
|
1395
1609
|
}
|
|
1396
|
-
function hasNodeOrGroup(id, nodeIds, groupIds) {
|
|
1397
|
-
return nodeIds.has(id) || groupIds.has(id);
|
|
1610
|
+
function hasNodeOrGroup(id, nodeIds, groupIds, swimlaneLaneIds = /* @__PURE__ */ new Set()) {
|
|
1611
|
+
return nodeIds.has(id) || groupIds.has(id) || swimlaneLaneIds.has(id);
|
|
1612
|
+
}
|
|
1613
|
+
function endpointNodeId(endpointValue) {
|
|
1614
|
+
if (typeof endpointValue === "string" || endpointValue === void 0) {
|
|
1615
|
+
return endpointValue;
|
|
1616
|
+
}
|
|
1617
|
+
return endpointValue.node;
|
|
1618
|
+
}
|
|
1619
|
+
function validateEndpointPort(dsl, endpointValue, path, diagnostics) {
|
|
1620
|
+
if (endpointValue.portId === void 0) {
|
|
1621
|
+
return;
|
|
1622
|
+
}
|
|
1623
|
+
const node = dsl.nodes[endpointValue.nodeId];
|
|
1624
|
+
if (node !== void 0 && node.ports?.[endpointValue.portId] === void 0) {
|
|
1625
|
+
diagnostics.push(referenceMissing([...path, "port"], endpointValue.portId));
|
|
1626
|
+
}
|
|
1398
1627
|
}
|
|
1399
1628
|
function toLabel(value) {
|
|
1400
1629
|
if (value === void 0) {
|
|
@@ -1423,6 +1652,8 @@ function point(value) {
|
|
|
1423
1652
|
var directionSchema = z.enum(["TB", "LR", "BT", "RL"]);
|
|
1424
1653
|
var routeKindSchema = z.enum(["orthogonal", "straight"]);
|
|
1425
1654
|
var outputFormatSchema = z.enum(["svg", "excalidraw"]);
|
|
1655
|
+
var edgeStrokeStyleSchema = z.enum(["solid", "dashed"]);
|
|
1656
|
+
var edgeArrowheadSchema = z.enum(["triangle", "hollowTriangle"]);
|
|
1426
1657
|
var nodeShapeSchema = z.enum([
|
|
1427
1658
|
"rectangle",
|
|
1428
1659
|
"rounded-rectangle",
|
|
@@ -1450,18 +1681,49 @@ var labelSchema = z.union([
|
|
|
1450
1681
|
maxWidth: finiteNumberSchema.optional()
|
|
1451
1682
|
})
|
|
1452
1683
|
]);
|
|
1684
|
+
var styleSchema = z.object({
|
|
1685
|
+
fill: z.string().optional(),
|
|
1686
|
+
stroke: z.string().optional()
|
|
1687
|
+
});
|
|
1688
|
+
var portSideSchema = z.enum(["top", "right", "bottom", "left"]);
|
|
1689
|
+
var portKindSchema = z.enum(["proxy", "flow"]);
|
|
1690
|
+
var portSchema = z.object({
|
|
1691
|
+
label: labelSchema.optional(),
|
|
1692
|
+
side: portSideSchema,
|
|
1693
|
+
kind: portKindSchema.optional(),
|
|
1694
|
+
order: finiteNumberSchema.optional(),
|
|
1695
|
+
style: styleSchema.optional()
|
|
1696
|
+
});
|
|
1697
|
+
var compartmentsSchema = z.object({
|
|
1698
|
+
stereotype: z.string().optional(),
|
|
1699
|
+
name: z.string().optional(),
|
|
1700
|
+
properties: z.array(z.record(z.string(), z.string()).or(z.string())).optional(),
|
|
1701
|
+
constraints: z.array(z.string()).optional()
|
|
1702
|
+
});
|
|
1453
1703
|
var nodeSchema = z.object({
|
|
1454
1704
|
label: labelSchema.optional(),
|
|
1455
1705
|
shape: nodeShapeSchema.optional(),
|
|
1456
|
-
position: pointSchema.optional()
|
|
1706
|
+
position: pointSchema.optional(),
|
|
1707
|
+
style: styleSchema.optional(),
|
|
1708
|
+
ports: z.record(z.string(), portSchema).optional(),
|
|
1709
|
+
compartments: compartmentsSchema.optional()
|
|
1457
1710
|
});
|
|
1711
|
+
var endpointSchema = z.union([
|
|
1712
|
+
z.string(),
|
|
1713
|
+
z.object({
|
|
1714
|
+
node: z.string(),
|
|
1715
|
+
port: z.string().optional()
|
|
1716
|
+
})
|
|
1717
|
+
]);
|
|
1458
1718
|
var structuredEdgeSchema = z.object({
|
|
1459
1719
|
id: z.string().optional(),
|
|
1460
|
-
source:
|
|
1461
|
-
target:
|
|
1720
|
+
source: endpointSchema.optional(),
|
|
1721
|
+
target: endpointSchema.optional(),
|
|
1462
1722
|
sourceId: z.string().optional(),
|
|
1463
1723
|
targetId: z.string().optional(),
|
|
1464
|
-
label: labelSchema.optional()
|
|
1724
|
+
label: labelSchema.optional(),
|
|
1725
|
+
style: edgeStrokeStyleSchema.optional(),
|
|
1726
|
+
arrowhead: edgeArrowheadSchema.optional()
|
|
1465
1727
|
}).superRefine((edge, context) => {
|
|
1466
1728
|
if (edge.source === void 0 && edge.sourceId === void 0) {
|
|
1467
1729
|
context.addIssue({
|
|
@@ -1485,6 +1747,17 @@ var groupSchema = z.object({
|
|
|
1485
1747
|
groups: z.array(z.string()).optional(),
|
|
1486
1748
|
padding: insetsSchema.optional()
|
|
1487
1749
|
});
|
|
1750
|
+
var swimlaneSchema = z.object({
|
|
1751
|
+
label: labelSchema.optional(),
|
|
1752
|
+
orientation: z.enum(["vertical", "horizontal"]).optional(),
|
|
1753
|
+
lanes: z.record(
|
|
1754
|
+
z.string(),
|
|
1755
|
+
z.object({
|
|
1756
|
+
label: labelSchema.optional(),
|
|
1757
|
+
children: z.array(z.string()).optional()
|
|
1758
|
+
})
|
|
1759
|
+
)
|
|
1760
|
+
});
|
|
1488
1761
|
var exactPositionConstraintSchema = z.object({
|
|
1489
1762
|
kind: z.literal("exact-position"),
|
|
1490
1763
|
target: z.string().optional(),
|
|
@@ -1545,12 +1818,24 @@ var diagramDslSchema = z.object({
|
|
|
1545
1818
|
direction: directionSchema.optional()
|
|
1546
1819
|
}).optional(),
|
|
1547
1820
|
routing: z.object({
|
|
1548
|
-
kind: routeKindSchema.optional()
|
|
1821
|
+
kind: routeKindSchema.optional(),
|
|
1822
|
+
portShifting: z.object({
|
|
1823
|
+
enabled: z.boolean().optional(),
|
|
1824
|
+
spacing: finiteNumberSchema.optional()
|
|
1825
|
+
}).optional()
|
|
1549
1826
|
}).optional(),
|
|
1550
1827
|
nodes: z.record(z.string(), nodeSchema),
|
|
1551
1828
|
edges: z.array(edgeSchema).optional(),
|
|
1552
1829
|
groups: z.record(z.string(), groupSchema).optional(),
|
|
1830
|
+
swimlanes: z.record(z.string(), swimlaneSchema).optional(),
|
|
1553
1831
|
constraints: z.array(constraintSchema).optional(),
|
|
1832
|
+
frame: z.object({
|
|
1833
|
+
kind: z.string(),
|
|
1834
|
+
context: z.string().optional(),
|
|
1835
|
+
name: z.string().optional(),
|
|
1836
|
+
titleTab: z.string(),
|
|
1837
|
+
style: styleSchema.optional()
|
|
1838
|
+
}).optional(),
|
|
1554
1839
|
output: z.object({
|
|
1555
1840
|
format: outputFormatSchema.optional()
|
|
1556
1841
|
}).optional()
|
|
@@ -1826,11 +2111,12 @@ function renderArrow(edge) {
|
|
|
1826
2111
|
height: box.height
|
|
1827
2112
|
}),
|
|
1828
2113
|
backgroundColor: "transparent",
|
|
2114
|
+
strokeStyle: edge.style ?? "solid",
|
|
1829
2115
|
points: relativePoints,
|
|
1830
2116
|
startBinding: { elementId: `node:${edge.source.nodeId}`, focus: 0, gap: 0 },
|
|
1831
2117
|
endBinding: { elementId: `node:${edge.target.nodeId}`, focus: 0, gap: 0 },
|
|
1832
2118
|
startArrowhead: null,
|
|
1833
|
-
endArrowhead:
|
|
2119
|
+
endArrowhead: mapArrowhead(edge.arrowhead)
|
|
1834
2120
|
};
|
|
1835
2121
|
}
|
|
1836
2122
|
function renderText(id, label, box, containerId, groupIds) {
|
|
@@ -1908,6 +2194,16 @@ function mapShape(shape) {
|
|
|
1908
2194
|
return "cylinder";
|
|
1909
2195
|
}
|
|
1910
2196
|
}
|
|
2197
|
+
function mapArrowhead(arrowhead) {
|
|
2198
|
+
switch (arrowhead) {
|
|
2199
|
+
case void 0:
|
|
2200
|
+
return "arrow";
|
|
2201
|
+
case "triangle":
|
|
2202
|
+
return "triangle";
|
|
2203
|
+
case "hollowTriangle":
|
|
2204
|
+
return "triangle_outline";
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
1911
2207
|
function createGroupMembership(groups) {
|
|
1912
2208
|
const membership = /* @__PURE__ */ new Map();
|
|
1913
2209
|
for (const group of groups) {
|
|
@@ -1974,19 +2270,28 @@ function exportSvg(diagram, options = {}) {
|
|
|
1974
2270
|
`<svg xmlns="http://www.w3.org/2000/svg" role="img" viewBox="${formatBoxViewBox(diagram.bounds)}">`,
|
|
1975
2271
|
...title === void 0 ? [] : [` <title>${escapeXml(title)}</title>`],
|
|
1976
2272
|
` <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"/>`,
|
|
2273
|
+
...diagram.frame === void 0 ? [] : [indent(renderFrame(diagram.frame))],
|
|
2274
|
+
...(diagram.swimlanes ?? []).flatMap(
|
|
2275
|
+
(swimlane) => renderSwimlane(swimlane)
|
|
2276
|
+
),
|
|
1977
2277
|
...diagram.groups.map((group) => indent(renderGroup2(group))),
|
|
1978
2278
|
...diagram.edges.flatMap((edge) => {
|
|
1979
|
-
const path = renderEdgePath(edge
|
|
2279
|
+
const path = renderEdgePath(edge);
|
|
1980
2280
|
if (path === void 0) {
|
|
1981
2281
|
return [];
|
|
1982
2282
|
}
|
|
1983
|
-
return [indent(path), indent(renderArrowhead(edge
|
|
2283
|
+
return [indent(path), indent(renderArrowhead(edge))];
|
|
1984
2284
|
}),
|
|
1985
2285
|
...diagram.nodes.map((node) => indent(renderNode2(node))),
|
|
2286
|
+
...diagram.nodes.flatMap((node) => renderCompartments(node)),
|
|
2287
|
+
...diagram.nodes.flatMap((node) => renderPorts(node)),
|
|
1986
2288
|
...diagram.groups.flatMap(
|
|
1987
2289
|
(group) => renderLabel(group.label, group.box, group)
|
|
1988
2290
|
),
|
|
1989
|
-
...diagram.nodes.flatMap(
|
|
2291
|
+
...diagram.nodes.flatMap(
|
|
2292
|
+
(node) => node.compartments === void 0 ? renderLabel(node.label, node.box, node) : []
|
|
2293
|
+
),
|
|
2294
|
+
...diagram.edges.flatMap((edge) => renderEdgeLabel(edge)),
|
|
1990
2295
|
"</svg>"
|
|
1991
2296
|
];
|
|
1992
2297
|
return `${lines.join("\n")}
|
|
@@ -1996,7 +2301,9 @@ function renderGroup2(group) {
|
|
|
1996
2301
|
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"/>`;
|
|
1997
2302
|
}
|
|
1998
2303
|
function renderNode2(node) {
|
|
1999
|
-
const
|
|
2304
|
+
const fill = node.style?.fill ?? NODE_FILL;
|
|
2305
|
+
const stroke = node.style?.stroke ?? STROKE;
|
|
2306
|
+
const common = `class="node node-${node.shape}" data-id="${escapeAttribute(node.id)}" fill="${escapeAttribute(fill)}" stroke="${escapeAttribute(stroke)}"`;
|
|
2000
2307
|
switch (node.shape) {
|
|
2001
2308
|
case "rectangle":
|
|
2002
2309
|
return renderRect(node.box, common);
|
|
@@ -2012,16 +2319,111 @@ function renderNode2(node) {
|
|
|
2012
2319
|
return `<path ${common} d="${formatCylinderPath(node.box)}"/>`;
|
|
2013
2320
|
}
|
|
2014
2321
|
}
|
|
2322
|
+
function renderFrame(frame) {
|
|
2323
|
+
const stroke = frame.style?.stroke ?? "#6b7280";
|
|
2324
|
+
const fill = frame.style?.fill ?? "transparent";
|
|
2325
|
+
return [
|
|
2326
|
+
`<g class="sysml-frame" data-kind="${escapeAttribute(frame.kind)}">`,
|
|
2327
|
+
` <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)}"/>`,
|
|
2328
|
+
` <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)}"/>`,
|
|
2329
|
+
` <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>`,
|
|
2330
|
+
"</g>"
|
|
2331
|
+
].join("\n");
|
|
2332
|
+
}
|
|
2333
|
+
function renderSwimlane(swimlane) {
|
|
2334
|
+
if (swimlane.box === void 0) {
|
|
2335
|
+
return [];
|
|
2336
|
+
}
|
|
2337
|
+
const lines = [
|
|
2338
|
+
` <g class="swimlane" data-id="${escapeAttribute(swimlane.id)}">`,
|
|
2339
|
+
` <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}"/>`
|
|
2340
|
+
];
|
|
2341
|
+
for (const lane of swimlane.lanes) {
|
|
2342
|
+
if (lane.box === void 0) {
|
|
2343
|
+
continue;
|
|
2344
|
+
}
|
|
2345
|
+
lines.push(
|
|
2346
|
+
` <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}"/>`
|
|
2347
|
+
);
|
|
2348
|
+
if (lane.label?.text !== void 0) {
|
|
2349
|
+
lines.push(
|
|
2350
|
+
` <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>`
|
|
2351
|
+
);
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
lines.push(" </g>");
|
|
2355
|
+
return lines;
|
|
2356
|
+
}
|
|
2357
|
+
function renderPorts(node) {
|
|
2358
|
+
return (node.ports ?? []).flatMap((port) => [
|
|
2359
|
+
` <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)}"/>`,
|
|
2360
|
+
...port.label?.text === void 0 ? [] : [
|
|
2361
|
+
` <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>`
|
|
2362
|
+
]
|
|
2363
|
+
]);
|
|
2364
|
+
}
|
|
2365
|
+
function renderCompartments(node) {
|
|
2366
|
+
const compartments2 = node.compartments;
|
|
2367
|
+
if (compartments2 === void 0) {
|
|
2368
|
+
return [];
|
|
2369
|
+
}
|
|
2370
|
+
const rows = [
|
|
2371
|
+
...compartments2.stereotype === void 0 ? [] : [{ className: "stereotype", text: compartments2.stereotype }],
|
|
2372
|
+
{
|
|
2373
|
+
className: "name",
|
|
2374
|
+
text: compartments2.name ?? node.label?.text ?? node.id
|
|
2375
|
+
},
|
|
2376
|
+
...(compartments2.properties ?? []).map((text) => ({
|
|
2377
|
+
className: "properties",
|
|
2378
|
+
text
|
|
2379
|
+
})),
|
|
2380
|
+
...(compartments2.constraints ?? []).map((text) => ({
|
|
2381
|
+
className: "constraints",
|
|
2382
|
+
text
|
|
2383
|
+
}))
|
|
2384
|
+
];
|
|
2385
|
+
const lineHeight = 16;
|
|
2386
|
+
const lines = [
|
|
2387
|
+
` <g class="compartment" data-for="${escapeAttribute(node.id)}">`
|
|
2388
|
+
];
|
|
2389
|
+
for (let index = 0; index < rows.length; index += 1) {
|
|
2390
|
+
const row = rows[index];
|
|
2391
|
+
if (row === void 0) {
|
|
2392
|
+
continue;
|
|
2393
|
+
}
|
|
2394
|
+
const y = node.box.y + 18 + index * lineHeight;
|
|
2395
|
+
if (index > 1) {
|
|
2396
|
+
lines.push(
|
|
2397
|
+
` <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}"/>`
|
|
2398
|
+
);
|
|
2399
|
+
}
|
|
2400
|
+
lines.push(
|
|
2401
|
+
` <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>`
|
|
2402
|
+
);
|
|
2403
|
+
}
|
|
2404
|
+
lines.push(" </g>");
|
|
2405
|
+
return lines;
|
|
2406
|
+
}
|
|
2407
|
+
function portLabelX(x, side) {
|
|
2408
|
+
if (side === "left") {
|
|
2409
|
+
return x - 8;
|
|
2410
|
+
}
|
|
2411
|
+
if (side === "right") {
|
|
2412
|
+
return x + 8;
|
|
2413
|
+
}
|
|
2414
|
+
return x + 8;
|
|
2415
|
+
}
|
|
2015
2416
|
function renderRect(box, attributes) {
|
|
2016
2417
|
return `<rect ${attributes} x="${formatNumber(box.x)}" y="${formatNumber(box.y)}" width="${formatNumber(box.width)}" height="${formatNumber(box.height)}"/>`;
|
|
2017
2418
|
}
|
|
2018
2419
|
function renderLabel(label, box, item) {
|
|
2019
2420
|
const labelLayout = item.labelLayout;
|
|
2020
2421
|
if (labelLayout?.lines !== void 0 && labelLayout.lines.length > 0) {
|
|
2422
|
+
const offset = { x: box.x, y: box.y };
|
|
2021
2423
|
return [
|
|
2022
2424
|
` <text class="label" data-for="${escapeAttribute(item.id)}" font-family="${FONT_FAMILY}" font-size="${formatNumber(labelLayout.font.fontSize)}" fill="#111827">`,
|
|
2023
2425
|
...labelLayout.lines.map(
|
|
2024
|
-
(line) => ` <tspan x="${formatNumber(line.box.x)}" y="${formatNumber(line.baselineY)}">${escapeXml(line.text)}</tspan>`
|
|
2426
|
+
(line) => ` <tspan x="${formatNumber(offset.x + line.box.x)}" y="${formatNumber(offset.y + line.baselineY)}">${escapeXml(line.text)}</tspan>`
|
|
2025
2427
|
),
|
|
2026
2428
|
" </text>"
|
|
2027
2429
|
];
|
|
@@ -2033,15 +2435,88 @@ function renderLabel(label, box, item) {
|
|
|
2033
2435
|
` <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>`
|
|
2034
2436
|
];
|
|
2035
2437
|
}
|
|
2036
|
-
function renderEdgePath(
|
|
2037
|
-
if (points.length < 2) {
|
|
2438
|
+
function renderEdgePath(edge) {
|
|
2439
|
+
if (edge.points.length < 2) {
|
|
2038
2440
|
return void 0;
|
|
2039
2441
|
}
|
|
2040
|
-
|
|
2442
|
+
const dash = edge.style === "dashed" ? ' stroke-dasharray="6 4"' : "";
|
|
2443
|
+
return `<path class="edge" data-id="${escapeAttribute(edge.id)}" d="${formatPath(pathPointsBeforeArrowhead(edge.points))}" fill="none" stroke="${EDGE_STROKE}" stroke-width="1.5"${dash}/>`;
|
|
2041
2444
|
}
|
|
2042
|
-
function
|
|
2445
|
+
function renderEdgeLabel(edge) {
|
|
2446
|
+
if (edge.label?.text === void 0 || edge.points.length < 2) {
|
|
2447
|
+
return [];
|
|
2448
|
+
}
|
|
2449
|
+
const placement = labelPlacementOnPolyline(edge.points);
|
|
2450
|
+
if (placement === void 0) {
|
|
2451
|
+
return [];
|
|
2452
|
+
}
|
|
2453
|
+
return [
|
|
2454
|
+
` <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>`
|
|
2455
|
+
];
|
|
2456
|
+
}
|
|
2457
|
+
function renderArrowhead(edge) {
|
|
2458
|
+
const arrowhead = computeArrowhead(edge.points);
|
|
2459
|
+
const fill = edge.arrowhead === "hollowTriangle" ? "none" : EDGE_STROKE;
|
|
2460
|
+
return `<polygon class="edge-arrowhead" data-edge="${escapeAttribute(edge.id)}" points="${formatPoints([arrowhead.tip, arrowhead.left, arrowhead.right])}" fill="${fill}" stroke="${EDGE_STROKE}"/>`;
|
|
2461
|
+
}
|
|
2462
|
+
function labelPlacementOnPolyline(points) {
|
|
2463
|
+
const segments = nonZeroSegments(points);
|
|
2464
|
+
const totalLength = segments.reduce(
|
|
2465
|
+
(sum, segment) => sum + segment.length,
|
|
2466
|
+
0
|
|
2467
|
+
);
|
|
2468
|
+
if (totalLength <= 0) {
|
|
2469
|
+
return void 0;
|
|
2470
|
+
}
|
|
2471
|
+
let remaining = totalLength / 2;
|
|
2472
|
+
for (const segment of segments) {
|
|
2473
|
+
if (remaining <= segment.length) {
|
|
2474
|
+
const ratio = remaining / segment.length;
|
|
2475
|
+
const x = segment.start.x + (segment.end.x - segment.start.x) * ratio;
|
|
2476
|
+
const y = segment.start.y + (segment.end.y - segment.start.y) * ratio;
|
|
2477
|
+
const offset2 = labelOffset(segment);
|
|
2478
|
+
return { x: x + offset2.x, y: y + offset2.y };
|
|
2479
|
+
}
|
|
2480
|
+
remaining -= segment.length;
|
|
2481
|
+
}
|
|
2482
|
+
const last = segments.at(-1);
|
|
2483
|
+
if (last === void 0) {
|
|
2484
|
+
return void 0;
|
|
2485
|
+
}
|
|
2486
|
+
const offset = labelOffset(last);
|
|
2487
|
+
return { x: last.end.x + offset.x, y: last.end.y + offset.y };
|
|
2488
|
+
}
|
|
2489
|
+
function nonZeroSegments(points) {
|
|
2490
|
+
const segments = [];
|
|
2491
|
+
for (let index = 0; index < points.length - 1; index += 1) {
|
|
2492
|
+
const start = points[index];
|
|
2493
|
+
const end = points[index + 1];
|
|
2494
|
+
if (start === void 0 || end === void 0) {
|
|
2495
|
+
continue;
|
|
2496
|
+
}
|
|
2497
|
+
const length = Math.hypot(end.x - start.x, end.y - start.y);
|
|
2498
|
+
if (length > 0) {
|
|
2499
|
+
segments.push({ start, end, length });
|
|
2500
|
+
}
|
|
2501
|
+
}
|
|
2502
|
+
return segments;
|
|
2503
|
+
}
|
|
2504
|
+
function labelOffset(segment) {
|
|
2505
|
+
const offset = 10;
|
|
2506
|
+
const dx = segment.end.x - segment.start.x;
|
|
2507
|
+
const dy = segment.end.y - segment.start.y;
|
|
2508
|
+
return {
|
|
2509
|
+
x: -dy / segment.length * offset,
|
|
2510
|
+
y: dx / segment.length * offset
|
|
2511
|
+
};
|
|
2512
|
+
}
|
|
2513
|
+
function pathPointsBeforeArrowhead(points) {
|
|
2043
2514
|
const arrowhead = computeArrowhead(points);
|
|
2044
|
-
|
|
2515
|
+
const base = {
|
|
2516
|
+
x: (arrowhead.left.x + arrowhead.right.x) / 2,
|
|
2517
|
+
y: (arrowhead.left.y + arrowhead.right.y) / 2
|
|
2518
|
+
};
|
|
2519
|
+
return [...points.slice(0, -1), base];
|
|
2045
2520
|
}
|
|
2046
2521
|
function shapePoints(shape, box) {
|
|
2047
2522
|
const left = box.x;
|
|
@@ -2228,20 +2703,33 @@ function isValidDimension(value) {
|
|
|
2228
2703
|
// src/routing/routes.ts
|
|
2229
2704
|
function routeEdge(input) {
|
|
2230
2705
|
const diagnostics = [];
|
|
2706
|
+
const defaultAnchors = defaultAnchorsForGeometry(
|
|
2707
|
+
input.source.box,
|
|
2708
|
+
input.target.box,
|
|
2709
|
+
input.direction
|
|
2710
|
+
);
|
|
2231
2711
|
const source = getEdgePort(
|
|
2232
2712
|
input.source,
|
|
2233
2713
|
input.target.center,
|
|
2234
|
-
input.sourceAnchor
|
|
2714
|
+
input.sourceAnchor ?? defaultAnchors.sourceAnchor
|
|
2235
2715
|
);
|
|
2236
2716
|
const target = getEdgePort(
|
|
2237
2717
|
input.target,
|
|
2238
2718
|
input.source.center,
|
|
2239
|
-
input.targetAnchor
|
|
2719
|
+
input.targetAnchor ?? defaultAnchors.targetAnchor
|
|
2240
2720
|
);
|
|
2241
2721
|
if ((input.kind ?? "orthogonal") === "straight") {
|
|
2242
2722
|
return { points: simplifyRoute([source, target]), diagnostics };
|
|
2243
2723
|
}
|
|
2244
2724
|
const candidates = orthogonalCandidates(source, target, input.direction);
|
|
2725
|
+
candidates.push(
|
|
2726
|
+
...expandedObstacleCandidates(
|
|
2727
|
+
source,
|
|
2728
|
+
target,
|
|
2729
|
+
input.direction,
|
|
2730
|
+
input.obstacles ?? []
|
|
2731
|
+
)
|
|
2732
|
+
);
|
|
2245
2733
|
for (const candidate of candidates) {
|
|
2246
2734
|
if (!routeIntersectsObstacles(candidate, input.obstacles ?? [])) {
|
|
2247
2735
|
return { points: simplifyRoute(candidate), diagnostics };
|
|
@@ -2280,27 +2768,113 @@ function simplifyRoute(points) {
|
|
|
2280
2768
|
function orthogonalCandidates(source, target, direction) {
|
|
2281
2769
|
const midpointX = (source.x + target.x) / 2;
|
|
2282
2770
|
const midpointY = (source.y + target.y) / 2;
|
|
2283
|
-
const candidates = [
|
|
2284
|
-
[source, { x: target.x, y: source.y }, target],
|
|
2285
|
-
[source, { x: source.x, y: target.y }, target]
|
|
2286
|
-
];
|
|
2771
|
+
const candidates = [];
|
|
2287
2772
|
if (direction === "TB" || direction === "BT") {
|
|
2288
2773
|
candidates.push([
|
|
2289
2774
|
source,
|
|
2290
|
-
{ x:
|
|
2291
|
-
{ x:
|
|
2775
|
+
{ x: source.x, y: midpointY },
|
|
2776
|
+
{ x: target.x, y: midpointY },
|
|
2292
2777
|
target
|
|
2293
2778
|
]);
|
|
2294
2779
|
} else {
|
|
2295
2780
|
candidates.push([
|
|
2296
2781
|
source,
|
|
2297
|
-
{ x:
|
|
2298
|
-
{ x:
|
|
2782
|
+
{ x: midpointX, y: source.y },
|
|
2783
|
+
{ x: midpointX, y: target.y },
|
|
2299
2784
|
target
|
|
2300
2785
|
]);
|
|
2301
2786
|
}
|
|
2787
|
+
candidates.push(
|
|
2788
|
+
[source, { x: target.x, y: source.y }, target],
|
|
2789
|
+
[source, { x: source.x, y: target.y }, target]
|
|
2790
|
+
);
|
|
2302
2791
|
return candidates;
|
|
2303
2792
|
}
|
|
2793
|
+
function defaultSourceAnchor(direction) {
|
|
2794
|
+
switch (direction) {
|
|
2795
|
+
case "LR":
|
|
2796
|
+
return "right";
|
|
2797
|
+
case "RL":
|
|
2798
|
+
return "left";
|
|
2799
|
+
case "TB":
|
|
2800
|
+
return "bottom";
|
|
2801
|
+
case "BT":
|
|
2802
|
+
return "top";
|
|
2803
|
+
}
|
|
2804
|
+
}
|
|
2805
|
+
function defaultAnchorsForGeometry(source, target, direction) {
|
|
2806
|
+
const dx = target.x + target.width / 2 - (source.x + source.width / 2);
|
|
2807
|
+
const dy = target.y + target.height / 2 - (source.y + source.height / 2);
|
|
2808
|
+
if (Math.abs(dy) > Math.abs(dx)) {
|
|
2809
|
+
return dy >= 0 ? { sourceAnchor: "bottom", targetAnchor: "top" } : { sourceAnchor: "top", targetAnchor: "bottom" };
|
|
2810
|
+
}
|
|
2811
|
+
if (Math.abs(dx) > 0) {
|
|
2812
|
+
return dx >= 0 ? { sourceAnchor: "right", targetAnchor: "left" } : { sourceAnchor: "left", targetAnchor: "right" };
|
|
2813
|
+
}
|
|
2814
|
+
return {
|
|
2815
|
+
sourceAnchor: defaultSourceAnchor(direction),
|
|
2816
|
+
targetAnchor: defaultTargetAnchor(direction)
|
|
2817
|
+
};
|
|
2818
|
+
}
|
|
2819
|
+
function defaultTargetAnchor(direction) {
|
|
2820
|
+
switch (direction) {
|
|
2821
|
+
case "LR":
|
|
2822
|
+
return "left";
|
|
2823
|
+
case "RL":
|
|
2824
|
+
return "right";
|
|
2825
|
+
case "TB":
|
|
2826
|
+
return "top";
|
|
2827
|
+
case "BT":
|
|
2828
|
+
return "bottom";
|
|
2829
|
+
}
|
|
2830
|
+
}
|
|
2831
|
+
function expandedObstacleCandidates(source, target, direction, obstacles) {
|
|
2832
|
+
if (obstacles.length === 0) {
|
|
2833
|
+
return [];
|
|
2834
|
+
}
|
|
2835
|
+
const margin = 16;
|
|
2836
|
+
const candidates = [];
|
|
2837
|
+
if (direction === "TB" || direction === "BT") {
|
|
2838
|
+
const lanes = sortedUniqueLanes(
|
|
2839
|
+
obstacles.flatMap((obstacle) => [
|
|
2840
|
+
obstacle.x - margin,
|
|
2841
|
+
obstacle.x + obstacle.width + margin
|
|
2842
|
+
]),
|
|
2843
|
+
(source.x + target.x) / 2
|
|
2844
|
+
);
|
|
2845
|
+
for (const laneX of lanes) {
|
|
2846
|
+
candidates.push([
|
|
2847
|
+
source,
|
|
2848
|
+
{ x: laneX, y: source.y },
|
|
2849
|
+
{ x: laneX, y: target.y },
|
|
2850
|
+
target
|
|
2851
|
+
]);
|
|
2852
|
+
}
|
|
2853
|
+
} else {
|
|
2854
|
+
const lanes = sortedUniqueLanes(
|
|
2855
|
+
obstacles.flatMap((obstacle) => [
|
|
2856
|
+
obstacle.y - margin,
|
|
2857
|
+
obstacle.y + obstacle.height + margin
|
|
2858
|
+
]),
|
|
2859
|
+
(source.y + target.y) / 2
|
|
2860
|
+
);
|
|
2861
|
+
for (const laneY of lanes) {
|
|
2862
|
+
candidates.push([
|
|
2863
|
+
source,
|
|
2864
|
+
{ x: source.x, y: laneY },
|
|
2865
|
+
{ x: target.x, y: laneY },
|
|
2866
|
+
target
|
|
2867
|
+
]);
|
|
2868
|
+
}
|
|
2869
|
+
}
|
|
2870
|
+
return candidates;
|
|
2871
|
+
}
|
|
2872
|
+
function sortedUniqueLanes(lanes, midpoint) {
|
|
2873
|
+
return [...new Set(lanes)].filter((lane) => Number.isFinite(lane)).sort((left, right) => {
|
|
2874
|
+
const distance = Math.abs(left - midpoint) - Math.abs(right - midpoint);
|
|
2875
|
+
return distance === 0 ? left - right : distance;
|
|
2876
|
+
});
|
|
2877
|
+
}
|
|
2304
2878
|
function routeIntersectsObstacles(points, obstacles) {
|
|
2305
2879
|
for (let index = 0; index < points.length - 1; index += 1) {
|
|
2306
2880
|
const a = points[index];
|
|
@@ -2379,12 +2953,17 @@ function solveDiagram(diagram, options = {}) {
|
|
|
2379
2953
|
options,
|
|
2380
2954
|
diagnostics
|
|
2381
2955
|
);
|
|
2956
|
+
const coordinatedSwimlanes = coordinateSwimlanes(
|
|
2957
|
+
diagram.swimlanes ?? [],
|
|
2958
|
+
constrained.boxes
|
|
2959
|
+
);
|
|
2382
2960
|
const groupBoxes = new Map(
|
|
2383
2961
|
coordinatedGroups.map((group) => [group.id, group.box])
|
|
2384
2962
|
);
|
|
2385
2963
|
const coordinatedEdges = coordinateEdges(
|
|
2386
2964
|
edges,
|
|
2387
2965
|
nodeGeometryById,
|
|
2966
|
+
coordinatedNodes,
|
|
2388
2967
|
[...nodeGeometryById.values()].map((geometry) => geometry.obstacleBox),
|
|
2389
2968
|
diagram.direction,
|
|
2390
2969
|
options,
|
|
@@ -2392,8 +2971,18 @@ function solveDiagram(diagram, options = {}) {
|
|
|
2392
2971
|
);
|
|
2393
2972
|
const allBoxes = [
|
|
2394
2973
|
...coordinatedNodes.map((node) => node.box),
|
|
2395
|
-
...
|
|
2974
|
+
...coordinatedNodes.flatMap(
|
|
2975
|
+
(node) => (node.ports ?? []).flatMap(
|
|
2976
|
+
(port) => port.label === void 0 ? [port.box] : [port.box, portLabelBox(port)]
|
|
2977
|
+
)
|
|
2978
|
+
),
|
|
2979
|
+
...groupBoxes.values(),
|
|
2980
|
+
...coordinatedSwimlanes.flatMap(
|
|
2981
|
+
(swimlane) => swimlane.box === void 0 ? [] : [swimlane.box]
|
|
2982
|
+
)
|
|
2396
2983
|
];
|
|
2984
|
+
const contentBounds = allBoxes.length === 0 ? { x: 0, y: 0, width: 0, height: 0 } : unionBoxes(allBoxes);
|
|
2985
|
+
const frame = diagram.frame === void 0 ? void 0 : coordinateFrame(diagram.frame, contentBounds);
|
|
2397
2986
|
return {
|
|
2398
2987
|
id: diagram.id,
|
|
2399
2988
|
...diagram.title === void 0 ? {} : { title: diagram.title },
|
|
@@ -2401,8 +2990,10 @@ function solveDiagram(diagram, options = {}) {
|
|
|
2401
2990
|
nodes: coordinatedNodes,
|
|
2402
2991
|
edges: coordinatedEdges,
|
|
2403
2992
|
groups: coordinatedGroups,
|
|
2993
|
+
...coordinatedSwimlanes.length === 0 ? {} : { swimlanes: coordinatedSwimlanes },
|
|
2404
2994
|
diagnostics,
|
|
2405
|
-
bounds:
|
|
2995
|
+
bounds: frame === void 0 ? contentBounds : unionBoxes([contentBounds, frame.box, frame.titleBox]),
|
|
2996
|
+
...frame === void 0 ? {} : { frame },
|
|
2406
2997
|
...diagram.metadata === void 0 ? {} : { metadata: diagram.metadata }
|
|
2407
2998
|
};
|
|
2408
2999
|
}
|
|
@@ -2428,6 +3019,9 @@ function coordinateNodes(nodes, boxes, options, diagnostics) {
|
|
|
2428
3019
|
coordinated.push({
|
|
2429
3020
|
id: node.id,
|
|
2430
3021
|
...node.label === void 0 ? {} : { label: node.label },
|
|
3022
|
+
...node.style === void 0 ? {} : { style: node.style },
|
|
3023
|
+
...node.ports === void 0 ? {} : { ports: coordinatePorts(node, box, options.portShifting) },
|
|
3024
|
+
...node.compartments === void 0 ? {} : { compartments: node.compartments },
|
|
2431
3025
|
...node.labelLayout === void 0 ? {} : { labelLayout: node.labelLayout },
|
|
2432
3026
|
shape: node.shape,
|
|
2433
3027
|
...node.metadata === void 0 ? {} : { metadata: node.metadata },
|
|
@@ -2438,6 +3032,142 @@ function coordinateNodes(nodes, boxes, options, diagnostics) {
|
|
|
2438
3032
|
}
|
|
2439
3033
|
return coordinated;
|
|
2440
3034
|
}
|
|
3035
|
+
function coordinatePorts(node, nodeBox, portShifting) {
|
|
3036
|
+
const portsBySide = /* @__PURE__ */ new Map();
|
|
3037
|
+
for (const port of node.ports ?? []) {
|
|
3038
|
+
const ports = portsBySide.get(port.side) ?? [];
|
|
3039
|
+
ports.push(port);
|
|
3040
|
+
portsBySide.set(port.side, ports);
|
|
3041
|
+
}
|
|
3042
|
+
const coordinated = [];
|
|
3043
|
+
for (const [side, ports] of portsBySide) {
|
|
3044
|
+
const sorted = [...ports ?? []].sort((a, b) => {
|
|
3045
|
+
const order = (a.order ?? 0) - (b.order ?? 0);
|
|
3046
|
+
return order === 0 ? a.id.localeCompare(b.id) : order;
|
|
3047
|
+
});
|
|
3048
|
+
for (let index = 0; index < sorted.length; index += 1) {
|
|
3049
|
+
const port = sorted[index];
|
|
3050
|
+
if (port === void 0) {
|
|
3051
|
+
continue;
|
|
3052
|
+
}
|
|
3053
|
+
const anchor = portAnchor(
|
|
3054
|
+
nodeBox,
|
|
3055
|
+
side,
|
|
3056
|
+
index,
|
|
3057
|
+
sorted.length,
|
|
3058
|
+
portShifting
|
|
3059
|
+
);
|
|
3060
|
+
const box = portBox(anchor);
|
|
3061
|
+
coordinated.push({ ...port, box, anchor });
|
|
3062
|
+
}
|
|
3063
|
+
}
|
|
3064
|
+
return coordinated.sort((a, b) => a.id.localeCompare(b.id));
|
|
3065
|
+
}
|
|
3066
|
+
function portAnchor(nodeBox, side, index, count, portShifting) {
|
|
3067
|
+
const shiftingEnabled = portShifting?.enabled ?? true;
|
|
3068
|
+
const spacing = portShifting?.spacing ?? 24;
|
|
3069
|
+
const centeredOffset = shiftingEnabled ? (index - (count - 1) / 2) * spacing : 0;
|
|
3070
|
+
switch (side) {
|
|
3071
|
+
case "left":
|
|
3072
|
+
return {
|
|
3073
|
+
x: nodeBox.x,
|
|
3074
|
+
y: nodeBox.y + nodeBox.height / 2 + centeredOffset
|
|
3075
|
+
};
|
|
3076
|
+
case "right":
|
|
3077
|
+
return {
|
|
3078
|
+
x: nodeBox.x + nodeBox.width,
|
|
3079
|
+
y: nodeBox.y + nodeBox.height / 2 + centeredOffset
|
|
3080
|
+
};
|
|
3081
|
+
case "top":
|
|
3082
|
+
return {
|
|
3083
|
+
x: nodeBox.x + nodeBox.width / 2 + centeredOffset,
|
|
3084
|
+
y: nodeBox.y
|
|
3085
|
+
};
|
|
3086
|
+
case "bottom":
|
|
3087
|
+
return {
|
|
3088
|
+
x: nodeBox.x + nodeBox.width / 2 + centeredOffset,
|
|
3089
|
+
y: nodeBox.y + nodeBox.height
|
|
3090
|
+
};
|
|
3091
|
+
}
|
|
3092
|
+
}
|
|
3093
|
+
function portBox(anchor) {
|
|
3094
|
+
const size = 10;
|
|
3095
|
+
return {
|
|
3096
|
+
x: anchor.x - size / 2,
|
|
3097
|
+
y: anchor.y - size / 2,
|
|
3098
|
+
width: size,
|
|
3099
|
+
height: size
|
|
3100
|
+
};
|
|
3101
|
+
}
|
|
3102
|
+
function portLabelBox(port) {
|
|
3103
|
+
const textWidth = Math.max(0, (port.label?.text.length ?? 0) * 6);
|
|
3104
|
+
const height = 12;
|
|
3105
|
+
const gap = 8;
|
|
3106
|
+
const x = port.side === "left" ? port.anchor.x - gap - textWidth : port.anchor.x + gap;
|
|
3107
|
+
return {
|
|
3108
|
+
x,
|
|
3109
|
+
y: port.anchor.y - 8 - height,
|
|
3110
|
+
width: textWidth,
|
|
3111
|
+
height
|
|
3112
|
+
};
|
|
3113
|
+
}
|
|
3114
|
+
function coordinateSwimlanes(swimlanes, nodeBoxes) {
|
|
3115
|
+
const titleSize = 28;
|
|
3116
|
+
const padding = 16;
|
|
3117
|
+
return swimlanes.map((swimlane) => {
|
|
3118
|
+
const laneBoxes = swimlane.lanes.flatMap((lane) => {
|
|
3119
|
+
const childBoxes = lane.children.map((child) => nodeBoxes.get(child)).filter((box) => box !== void 0);
|
|
3120
|
+
return childBoxes.length === 0 ? [] : [unionBoxes(childBoxes)];
|
|
3121
|
+
});
|
|
3122
|
+
const laneUnion = laneBoxes.length === 0 ? { x: 0, y: 0, width: 120, height: 80 } : unionBoxes(laneBoxes);
|
|
3123
|
+
const outer = expand(laneUnion, padding, titleSize);
|
|
3124
|
+
const laneCount = Math.max(1, swimlane.lanes.length);
|
|
3125
|
+
const lanes = swimlane.lanes.map((lane, index) => {
|
|
3126
|
+
const box = swimlane.orientation === "vertical" ? {
|
|
3127
|
+
x: outer.x + outer.width / laneCount * index,
|
|
3128
|
+
y: outer.y,
|
|
3129
|
+
width: outer.width / laneCount,
|
|
3130
|
+
height: outer.height
|
|
3131
|
+
} : {
|
|
3132
|
+
x: outer.x,
|
|
3133
|
+
y: outer.y + outer.height / laneCount * index,
|
|
3134
|
+
width: outer.width,
|
|
3135
|
+
height: outer.height / laneCount
|
|
3136
|
+
};
|
|
3137
|
+
return { ...lane, box };
|
|
3138
|
+
});
|
|
3139
|
+
return { ...swimlane, lanes, box: outer };
|
|
3140
|
+
});
|
|
3141
|
+
}
|
|
3142
|
+
function coordinateFrame(frame, contentBounds) {
|
|
3143
|
+
const padding = 32;
|
|
3144
|
+
const titleHeight = 28;
|
|
3145
|
+
const titleWidth = Math.max(180, frame.titleTab.length * 7);
|
|
3146
|
+
const box = {
|
|
3147
|
+
x: contentBounds.x - padding,
|
|
3148
|
+
y: contentBounds.y - padding - titleHeight,
|
|
3149
|
+
width: contentBounds.width + padding * 2,
|
|
3150
|
+
height: contentBounds.height + padding * 2 + titleHeight
|
|
3151
|
+
};
|
|
3152
|
+
return {
|
|
3153
|
+
...frame,
|
|
3154
|
+
box,
|
|
3155
|
+
titleBox: {
|
|
3156
|
+
x: box.x,
|
|
3157
|
+
y: box.y,
|
|
3158
|
+
width: Math.min(titleWidth, box.width * 0.8),
|
|
3159
|
+
height: titleHeight
|
|
3160
|
+
}
|
|
3161
|
+
};
|
|
3162
|
+
}
|
|
3163
|
+
function expand(box, padding, titleSize) {
|
|
3164
|
+
return {
|
|
3165
|
+
x: box.x - padding,
|
|
3166
|
+
y: box.y - padding - titleSize,
|
|
3167
|
+
width: box.width + padding * 2,
|
|
3168
|
+
height: box.height + padding * 2 + titleSize
|
|
3169
|
+
};
|
|
3170
|
+
}
|
|
2441
3171
|
function coordinateGroups(groups, nodeBoxes, options, diagnostics) {
|
|
2442
3172
|
const coordinated = [];
|
|
2443
3173
|
const groupBoxes = /* @__PURE__ */ new Map();
|
|
@@ -2486,8 +3216,11 @@ function coordinateGroups(groups, nodeBoxes, options, diagnostics) {
|
|
|
2486
3216
|
}
|
|
2487
3217
|
return coordinated;
|
|
2488
3218
|
}
|
|
2489
|
-
function coordinateEdges(edges, nodes, obstacles, direction, options, diagnostics) {
|
|
3219
|
+
function coordinateEdges(edges, nodes, coordinatedNodes, obstacles, direction, options, diagnostics) {
|
|
2490
3220
|
const coordinated = [];
|
|
3221
|
+
const coordinatedNodeById = new Map(
|
|
3222
|
+
coordinatedNodes.map((node) => [node.id, node])
|
|
3223
|
+
);
|
|
2491
3224
|
for (const edge of edges) {
|
|
2492
3225
|
const source = nodes.get(edge.source.nodeId);
|
|
2493
3226
|
const target = nodes.get(edge.target.nodeId);
|
|
@@ -2505,11 +3238,13 @@ function coordinateEdges(edges, nodes, obstacles, direction, options, diagnostic
|
|
|
2505
3238
|
});
|
|
2506
3239
|
continue;
|
|
2507
3240
|
}
|
|
3241
|
+
const sourcePort = coordinatedNodeById.get(edge.source.nodeId)?.ports?.find((port) => port.id === edge.source.portId);
|
|
3242
|
+
const targetPort = coordinatedNodeById.get(edge.target.nodeId)?.ports?.find((port) => port.id === edge.target.portId);
|
|
2508
3243
|
const route = routeEdge({
|
|
2509
3244
|
kind: options.routeKind ?? "orthogonal",
|
|
2510
3245
|
direction,
|
|
2511
|
-
source,
|
|
2512
|
-
target,
|
|
3246
|
+
source: portGeometry(source, sourcePort),
|
|
3247
|
+
target: portGeometry(target, targetPort),
|
|
2513
3248
|
...edge.source.anchor === void 0 ? {} : { sourceAnchor: edge.source.anchor },
|
|
2514
3249
|
...edge.target.anchor === void 0 ? {} : { targetAnchor: edge.target.anchor },
|
|
2515
3250
|
obstacles: obstacles.filter(
|
|
@@ -2529,6 +3264,21 @@ function coordinateEdges(edges, nodes, obstacles, direction, options, diagnostic
|
|
|
2529
3264
|
}
|
|
2530
3265
|
return coordinated;
|
|
2531
3266
|
}
|
|
3267
|
+
function portGeometry(nodeGeometry, port) {
|
|
3268
|
+
if (port === void 0) {
|
|
3269
|
+
return nodeGeometry;
|
|
3270
|
+
}
|
|
3271
|
+
return {
|
|
3272
|
+
...nodeGeometry,
|
|
3273
|
+
box: port.box,
|
|
3274
|
+
center: port.anchor,
|
|
3275
|
+
anchors: nodeGeometry.anchors.map((anchor) => ({
|
|
3276
|
+
name: anchor.name,
|
|
3277
|
+
point: port.anchor
|
|
3278
|
+
})),
|
|
3279
|
+
obstacleBox: port.box
|
|
3280
|
+
};
|
|
3281
|
+
}
|
|
2532
3282
|
function stableById(items) {
|
|
2533
3283
|
return [...items].sort((a, b) => a.id.localeCompare(b.id));
|
|
2534
3284
|
}
|
|
@@ -2589,7 +3339,8 @@ function renderDiagramDsl(source, options = {}) {
|
|
|
2589
3339
|
return { diagnostics };
|
|
2590
3340
|
}
|
|
2591
3341
|
const solved = solveDiagram(normalized.diagram, {
|
|
2592
|
-
routeKind: normalized.diagram.metadata?.routeKind === "straight" ? "straight" : "orthogonal"
|
|
3342
|
+
routeKind: normalized.diagram.metadata?.routeKind === "straight" ? "straight" : "orthogonal",
|
|
3343
|
+
...solvePortShiftingOption(normalized.diagram.metadata?.portShifting)
|
|
2593
3344
|
});
|
|
2594
3345
|
const solveDiagnostics = solved.diagnostics.map(toSolveDiagnostic);
|
|
2595
3346
|
if (hasErrorDiagnostics2(solveDiagnostics)) {
|
|
@@ -2628,6 +3379,22 @@ function renderDiagramDsl(source, options = {}) {
|
|
|
2628
3379
|
function toSolveDiagnostic(diagnostic) {
|
|
2629
3380
|
return { ...diagnostic, layer: "solve" };
|
|
2630
3381
|
}
|
|
3382
|
+
function solvePortShiftingOption(value) {
|
|
3383
|
+
if (!isJsonObject(value)) {
|
|
3384
|
+
return {};
|
|
3385
|
+
}
|
|
3386
|
+
const portShifting = {};
|
|
3387
|
+
if (value.enabled === false) {
|
|
3388
|
+
portShifting.enabled = false;
|
|
3389
|
+
}
|
|
3390
|
+
if (typeof value.spacing === "number") {
|
|
3391
|
+
portShifting.spacing = value.spacing;
|
|
3392
|
+
}
|
|
3393
|
+
return { portShifting };
|
|
3394
|
+
}
|
|
3395
|
+
function isJsonObject(value) {
|
|
3396
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3397
|
+
}
|
|
2631
3398
|
function toExportDiagnostic(diagnostic) {
|
|
2632
3399
|
return { ...diagnostic, layer: "export" };
|
|
2633
3400
|
}
|
|
@@ -2761,6 +3528,6 @@ function isPointLikeRecord(value) {
|
|
|
2761
3528
|
return isPlainObject(value) && typeof value.x === "number" && typeof value.y === "number";
|
|
2762
3529
|
}
|
|
2763
3530
|
|
|
2764
|
-
export { DEFAULT_CANONICAL_PRECISION, DEFAULT_DSL_MAX_BYTES, DeterministicTextMeasurer, LabelFitter, PretextTextMeasurer, applyLayoutConstraints, assertFiniteNonNegative, assertFinitePositive, boxCenter, canonicalize, computeArrowhead, computeContainerGeometry, computeShapeGeometry, expandBox, exportExcalidraw, exportSvg, fitLabel, getEdgePort, intersectsAabb, isPretextRuntimeAvailable, normalizeDiagramDsl, normalizeInsets, parseDiagramDsl, parseEdgeShorthand, renderDiagramDsl, resolveLineHeight, resolveOutputFormat, routeEdge, runDagreInitialLayout, simplifyRoute, solveDiagram, sortDslDiagnostics, stringifyCanonical, toCanvasFont, unionBoxes, validateBox, validateTextStyle };
|
|
3531
|
+
export { DEFAULT_CANONICAL_PRECISION, DEFAULT_DSL_MAX_BYTES, DeterministicTextMeasurer, LabelFitter, PretextTextMeasurer, applyLayoutConstraints, assertFiniteNonNegative, assertFinitePositive, boxCenter, canonicalize, computeArrowhead, computeContainerGeometry, computeShapeGeometry, createDefaultTextMeasurer, expandBox, exportExcalidraw, exportSvg, fitLabel, getEdgePort, installNodeCanvasRuntime, intersectsAabb, isPretextRuntimeAvailable, normalizeDiagramDsl, normalizeInsets, parseDiagramDsl, parseEdgeShorthand, renderDiagramDsl, resolveLineHeight, resolveOutputFormat, routeEdge, runDagreInitialLayout, simplifyRoute, solveDiagram, sortDslDiagnostics, stringifyCanonical, toCanvasFont, unionBoxes, validateBox, validateTextStyle };
|
|
2765
3532
|
//# sourceMappingURL=index.js.map
|
|
2766
3533
|
//# sourceMappingURL=index.js.map
|