@crazyhappyone/auto-graph 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/README.zh-CN.md +2 -2
- package/dist/cli/index.cjs +910 -66
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +909 -66
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +849 -73
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +87 -1
- package/dist/index.d.ts +87 -1
- package/dist/index.js +847 -74
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.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
|
}
|
|
@@ -1288,18 +1465,37 @@ function validateReferences(dsl) {
|
|
|
1288
1465
|
const diagnostics = [];
|
|
1289
1466
|
const nodeIds = new Set(Object.keys(dsl.nodes));
|
|
1290
1467
|
const groupIds = new Set(Object.keys(dsl.groups ?? {}));
|
|
1468
|
+
const swimlaneLaneIds = new Set(
|
|
1469
|
+
Object.entries(dsl.swimlanes ?? {}).flatMap(
|
|
1470
|
+
([swimlaneId, swimlane]) => Object.keys(swimlane.lanes).map((laneId) => `${swimlaneId}.${laneId}`)
|
|
1471
|
+
)
|
|
1472
|
+
);
|
|
1291
1473
|
(dsl.edges ?? []).forEach((edge, index) => {
|
|
1292
1474
|
if (typeof edge === "string") {
|
|
1293
1475
|
return;
|
|
1294
1476
|
}
|
|
1295
|
-
const sourceId = edge.sourceId ?? edge.source;
|
|
1296
|
-
const targetId = edge.targetId ?? edge.target;
|
|
1477
|
+
const sourceId = edge.sourceId ?? endpointNodeId(edge.source);
|
|
1478
|
+
const targetId = edge.targetId ?? endpointNodeId(edge.target);
|
|
1479
|
+
const sourceEndpoint = endpoint(edge.source, edge.sourceId);
|
|
1480
|
+
const targetEndpoint = endpoint(edge.target, edge.targetId);
|
|
1297
1481
|
if (sourceId !== void 0 && !nodeIds.has(sourceId)) {
|
|
1298
1482
|
diagnostics.push(referenceMissing(["edges", index, "source"], sourceId));
|
|
1299
1483
|
}
|
|
1300
1484
|
if (targetId !== void 0 && !nodeIds.has(targetId)) {
|
|
1301
1485
|
diagnostics.push(referenceMissing(["edges", index, "target"], targetId));
|
|
1302
1486
|
}
|
|
1487
|
+
validateEndpointPort(
|
|
1488
|
+
dsl,
|
|
1489
|
+
sourceEndpoint,
|
|
1490
|
+
["edges", index, "source"],
|
|
1491
|
+
diagnostics
|
|
1492
|
+
);
|
|
1493
|
+
validateEndpointPort(
|
|
1494
|
+
dsl,
|
|
1495
|
+
targetEndpoint,
|
|
1496
|
+
["edges", index, "target"],
|
|
1497
|
+
diagnostics
|
|
1498
|
+
);
|
|
1303
1499
|
});
|
|
1304
1500
|
for (const [groupId, group] of Object.entries(dsl.groups ?? {})) {
|
|
1305
1501
|
(group.nodes ?? []).forEach((nodeId, index) => {
|
|
@@ -1317,6 +1513,27 @@ function validateReferences(dsl) {
|
|
|
1317
1513
|
}
|
|
1318
1514
|
});
|
|
1319
1515
|
}
|
|
1516
|
+
for (const [swimlaneId, swimlane] of Object.entries(dsl.swimlanes ?? {})) {
|
|
1517
|
+
for (const [laneId, lane] of Object.entries(swimlane.lanes)) {
|
|
1518
|
+
(lane.children ?? []).forEach((child, childIndex) => {
|
|
1519
|
+
if (!nodeIds.has(child)) {
|
|
1520
|
+
diagnostics.push(
|
|
1521
|
+
referenceMissing(
|
|
1522
|
+
[
|
|
1523
|
+
"swimlanes",
|
|
1524
|
+
swimlaneId,
|
|
1525
|
+
"lanes",
|
|
1526
|
+
laneId,
|
|
1527
|
+
"children",
|
|
1528
|
+
childIndex
|
|
1529
|
+
],
|
|
1530
|
+
child
|
|
1531
|
+
)
|
|
1532
|
+
);
|
|
1533
|
+
}
|
|
1534
|
+
});
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1320
1537
|
(dsl.constraints ?? []).forEach((constraint, index) => {
|
|
1321
1538
|
switch (constraint.kind) {
|
|
1322
1539
|
case "exact-position": {
|
|
@@ -1360,7 +1577,7 @@ function validateReferences(dsl) {
|
|
|
1360
1577
|
break;
|
|
1361
1578
|
case "containment": {
|
|
1362
1579
|
const container = constraint.containerId ?? constraint.container;
|
|
1363
|
-
if (container !== void 0 && !hasNodeOrGroup(container, nodeIds, groupIds)) {
|
|
1580
|
+
if (container !== void 0 && !hasNodeOrGroup(container, nodeIds, groupIds, swimlaneLaneIds)) {
|
|
1364
1581
|
diagnostics.push(
|
|
1365
1582
|
referenceMissing(["constraints", index, "container"], container)
|
|
1366
1583
|
);
|
|
@@ -1393,8 +1610,23 @@ function referenceMissing(path, id) {
|
|
|
1393
1610
|
hint: "Define the referenced node or group id, or update this reference."
|
|
1394
1611
|
};
|
|
1395
1612
|
}
|
|
1396
|
-
function hasNodeOrGroup(id, nodeIds, groupIds) {
|
|
1397
|
-
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
|
+
}
|
|
1398
1630
|
}
|
|
1399
1631
|
function toLabel(value) {
|
|
1400
1632
|
if (value === void 0) {
|
|
@@ -1423,6 +1655,8 @@ function point(value) {
|
|
|
1423
1655
|
var directionSchema = z.enum(["TB", "LR", "BT", "RL"]);
|
|
1424
1656
|
var routeKindSchema = z.enum(["orthogonal", "straight"]);
|
|
1425
1657
|
var outputFormatSchema = z.enum(["svg", "excalidraw"]);
|
|
1658
|
+
var edgeStrokeStyleSchema = z.enum(["solid", "dashed"]);
|
|
1659
|
+
var edgeArrowheadSchema = z.enum(["triangle", "hollowTriangle"]);
|
|
1426
1660
|
var nodeShapeSchema = z.enum([
|
|
1427
1661
|
"rectangle",
|
|
1428
1662
|
"rounded-rectangle",
|
|
@@ -1450,18 +1684,49 @@ var labelSchema = z.union([
|
|
|
1450
1684
|
maxWidth: finiteNumberSchema.optional()
|
|
1451
1685
|
})
|
|
1452
1686
|
]);
|
|
1687
|
+
var styleSchema = z.object({
|
|
1688
|
+
fill: z.string().optional(),
|
|
1689
|
+
stroke: z.string().optional()
|
|
1690
|
+
});
|
|
1691
|
+
var portSideSchema = z.enum(["top", "right", "bottom", "left"]);
|
|
1692
|
+
var portKindSchema = z.enum(["proxy", "flow"]);
|
|
1693
|
+
var portSchema = 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 = z.object({
|
|
1701
|
+
stereotype: z.string().optional(),
|
|
1702
|
+
name: z.string().optional(),
|
|
1703
|
+
properties: z.array(z.record(z.string(), z.string()).or(z.string())).optional(),
|
|
1704
|
+
constraints: z.array(z.string()).optional()
|
|
1705
|
+
});
|
|
1453
1706
|
var nodeSchema = z.object({
|
|
1454
1707
|
label: labelSchema.optional(),
|
|
1455
1708
|
shape: nodeShapeSchema.optional(),
|
|
1456
|
-
position: pointSchema.optional()
|
|
1709
|
+
position: pointSchema.optional(),
|
|
1710
|
+
style: styleSchema.optional(),
|
|
1711
|
+
ports: z.record(z.string(), portSchema).optional(),
|
|
1712
|
+
compartments: compartmentsSchema.optional()
|
|
1457
1713
|
});
|
|
1714
|
+
var endpointSchema = z.union([
|
|
1715
|
+
z.string(),
|
|
1716
|
+
z.object({
|
|
1717
|
+
node: z.string(),
|
|
1718
|
+
port: z.string().optional()
|
|
1719
|
+
})
|
|
1720
|
+
]);
|
|
1458
1721
|
var structuredEdgeSchema = z.object({
|
|
1459
1722
|
id: z.string().optional(),
|
|
1460
|
-
source:
|
|
1461
|
-
target:
|
|
1723
|
+
source: endpointSchema.optional(),
|
|
1724
|
+
target: endpointSchema.optional(),
|
|
1462
1725
|
sourceId: z.string().optional(),
|
|
1463
1726
|
targetId: z.string().optional(),
|
|
1464
|
-
label: labelSchema.optional()
|
|
1727
|
+
label: labelSchema.optional(),
|
|
1728
|
+
style: edgeStrokeStyleSchema.optional(),
|
|
1729
|
+
arrowhead: edgeArrowheadSchema.optional()
|
|
1465
1730
|
}).superRefine((edge, context) => {
|
|
1466
1731
|
if (edge.source === void 0 && edge.sourceId === void 0) {
|
|
1467
1732
|
context.addIssue({
|
|
@@ -1485,6 +1750,17 @@ var groupSchema = z.object({
|
|
|
1485
1750
|
groups: z.array(z.string()).optional(),
|
|
1486
1751
|
padding: insetsSchema.optional()
|
|
1487
1752
|
});
|
|
1753
|
+
var swimlaneSchema = z.object({
|
|
1754
|
+
label: labelSchema.optional(),
|
|
1755
|
+
orientation: z.enum(["vertical", "horizontal"]).optional(),
|
|
1756
|
+
lanes: z.record(
|
|
1757
|
+
z.string(),
|
|
1758
|
+
z.object({
|
|
1759
|
+
label: labelSchema.optional(),
|
|
1760
|
+
children: z.array(z.string()).optional()
|
|
1761
|
+
})
|
|
1762
|
+
)
|
|
1763
|
+
});
|
|
1488
1764
|
var exactPositionConstraintSchema = z.object({
|
|
1489
1765
|
kind: z.literal("exact-position"),
|
|
1490
1766
|
target: z.string().optional(),
|
|
@@ -1545,12 +1821,24 @@ var diagramDslSchema = z.object({
|
|
|
1545
1821
|
direction: directionSchema.optional()
|
|
1546
1822
|
}).optional(),
|
|
1547
1823
|
routing: z.object({
|
|
1548
|
-
kind: routeKindSchema.optional()
|
|
1824
|
+
kind: routeKindSchema.optional(),
|
|
1825
|
+
portShifting: z.object({
|
|
1826
|
+
enabled: z.boolean().optional(),
|
|
1827
|
+
spacing: finiteNumberSchema.optional()
|
|
1828
|
+
}).optional()
|
|
1549
1829
|
}).optional(),
|
|
1550
1830
|
nodes: z.record(z.string(), nodeSchema),
|
|
1551
1831
|
edges: z.array(edgeSchema).optional(),
|
|
1552
1832
|
groups: z.record(z.string(), groupSchema).optional(),
|
|
1833
|
+
swimlanes: z.record(z.string(), swimlaneSchema).optional(),
|
|
1553
1834
|
constraints: z.array(constraintSchema).optional(),
|
|
1835
|
+
frame: z.object({
|
|
1836
|
+
kind: z.string(),
|
|
1837
|
+
context: z.string().optional(),
|
|
1838
|
+
name: z.string().optional(),
|
|
1839
|
+
titleTab: z.string(),
|
|
1840
|
+
style: styleSchema.optional()
|
|
1841
|
+
}).optional(),
|
|
1554
1842
|
output: z.object({
|
|
1555
1843
|
format: outputFormatSchema.optional()
|
|
1556
1844
|
}).optional()
|
|
@@ -1826,11 +2114,12 @@ function renderArrow(edge) {
|
|
|
1826
2114
|
height: box.height
|
|
1827
2115
|
}),
|
|
1828
2116
|
backgroundColor: "transparent",
|
|
2117
|
+
strokeStyle: edge.style ?? "solid",
|
|
1829
2118
|
points: relativePoints,
|
|
1830
2119
|
startBinding: { elementId: `node:${edge.source.nodeId}`, focus: 0, gap: 0 },
|
|
1831
2120
|
endBinding: { elementId: `node:${edge.target.nodeId}`, focus: 0, gap: 0 },
|
|
1832
2121
|
startArrowhead: null,
|
|
1833
|
-
endArrowhead:
|
|
2122
|
+
endArrowhead: mapArrowhead(edge.arrowhead)
|
|
1834
2123
|
};
|
|
1835
2124
|
}
|
|
1836
2125
|
function renderText(id, label, box, containerId, groupIds) {
|
|
@@ -1908,6 +2197,16 @@ function mapShape(shape) {
|
|
|
1908
2197
|
return "cylinder";
|
|
1909
2198
|
}
|
|
1910
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
|
+
}
|
|
1911
2210
|
function createGroupMembership(groups) {
|
|
1912
2211
|
const membership = /* @__PURE__ */ new Map();
|
|
1913
2212
|
for (const group of groups) {
|
|
@@ -1974,19 +2273,28 @@ function exportSvg(diagram, options = {}) {
|
|
|
1974
2273
|
`<svg xmlns="http://www.w3.org/2000/svg" role="img" viewBox="${formatBoxViewBox(diagram.bounds)}">`,
|
|
1975
2274
|
...title === void 0 ? [] : [` <title>${escapeXml(title)}</title>`],
|
|
1976
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
|
+
),
|
|
1977
2280
|
...diagram.groups.map((group) => indent(renderGroup2(group))),
|
|
1978
2281
|
...diagram.edges.flatMap((edge) => {
|
|
1979
|
-
const path = renderEdgePath(edge
|
|
2282
|
+
const path = renderEdgePath(edge);
|
|
1980
2283
|
if (path === void 0) {
|
|
1981
2284
|
return [];
|
|
1982
2285
|
}
|
|
1983
|
-
return [indent(path), indent(renderArrowhead(edge
|
|
2286
|
+
return [indent(path), indent(renderArrowhead(edge))];
|
|
1984
2287
|
}),
|
|
1985
2288
|
...diagram.nodes.map((node) => indent(renderNode2(node))),
|
|
2289
|
+
...diagram.nodes.flatMap((node) => renderCompartments(node)),
|
|
2290
|
+
...diagram.nodes.flatMap((node) => renderPorts(node)),
|
|
1986
2291
|
...diagram.groups.flatMap(
|
|
1987
2292
|
(group) => renderLabel(group.label, group.box, group)
|
|
1988
2293
|
),
|
|
1989
|
-
...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)),
|
|
1990
2298
|
"</svg>"
|
|
1991
2299
|
];
|
|
1992
2300
|
return `${lines.join("\n")}
|
|
@@ -1996,7 +2304,9 @@ function renderGroup2(group) {
|
|
|
1996
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"/>`;
|
|
1997
2305
|
}
|
|
1998
2306
|
function renderNode2(node) {
|
|
1999
|
-
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)}"`;
|
|
2000
2310
|
switch (node.shape) {
|
|
2001
2311
|
case "rectangle":
|
|
2002
2312
|
return renderRect(node.box, common);
|
|
@@ -2012,16 +2322,111 @@ function renderNode2(node) {
|
|
|
2012
2322
|
return `<path ${common} d="${formatCylinderPath(node.box)}"/>`;
|
|
2013
2323
|
}
|
|
2014
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
|
+
}
|
|
2015
2419
|
function renderRect(box, attributes) {
|
|
2016
2420
|
return `<rect ${attributes} x="${formatNumber(box.x)}" y="${formatNumber(box.y)}" width="${formatNumber(box.width)}" height="${formatNumber(box.height)}"/>`;
|
|
2017
2421
|
}
|
|
2018
2422
|
function renderLabel(label, box, item) {
|
|
2019
2423
|
const labelLayout = item.labelLayout;
|
|
2020
2424
|
if (labelLayout?.lines !== void 0 && labelLayout.lines.length > 0) {
|
|
2425
|
+
const offset = isAbsoluteLabelLayout(labelLayout.box, box) ? { x: 0, y: 0 } : { x: box.x, y: box.y };
|
|
2021
2426
|
return [
|
|
2022
2427
|
` <text class="label" data-for="${escapeAttribute(item.id)}" font-family="${FONT_FAMILY}" font-size="${formatNumber(labelLayout.font.fontSize)}" fill="#111827">`,
|
|
2023
2428
|
...labelLayout.lines.map(
|
|
2024
|
-
(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>`
|
|
2025
2430
|
),
|
|
2026
2431
|
" </text>"
|
|
2027
2432
|
];
|
|
@@ -2033,15 +2438,91 @@ function renderLabel(label, box, item) {
|
|
|
2033
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>`
|
|
2034
2439
|
];
|
|
2035
2440
|
}
|
|
2036
|
-
function renderEdgePath(
|
|
2037
|
-
if (points.length < 2) {
|
|
2441
|
+
function renderEdgePath(edge) {
|
|
2442
|
+
if (edge.points.length < 2) {
|
|
2038
2443
|
return void 0;
|
|
2039
2444
|
}
|
|
2040
|
-
|
|
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}/>`;
|
|
2041
2447
|
}
|
|
2042
|
-
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) {
|
|
2043
2517
|
const arrowhead = computeArrowhead(points);
|
|
2044
|
-
|
|
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];
|
|
2523
|
+
}
|
|
2524
|
+
function isAbsoluteLabelLayout(labelBox, itemBox) {
|
|
2525
|
+
return labelBox.x >= itemBox.x && labelBox.y >= itemBox.y && labelBox.x + labelBox.width <= itemBox.x + itemBox.width && labelBox.y + labelBox.height <= itemBox.y + itemBox.height;
|
|
2045
2526
|
}
|
|
2046
2527
|
function shapePoints(shape, box) {
|
|
2047
2528
|
const left = box.x;
|
|
@@ -2228,20 +2709,33 @@ function isValidDimension(value) {
|
|
|
2228
2709
|
// src/routing/routes.ts
|
|
2229
2710
|
function routeEdge(input) {
|
|
2230
2711
|
const diagnostics = [];
|
|
2712
|
+
const defaultAnchors = defaultAnchorsForGeometry(
|
|
2713
|
+
input.source.box,
|
|
2714
|
+
input.target.box,
|
|
2715
|
+
input.direction
|
|
2716
|
+
);
|
|
2231
2717
|
const source = getEdgePort(
|
|
2232
2718
|
input.source,
|
|
2233
2719
|
input.target.center,
|
|
2234
|
-
input.sourceAnchor
|
|
2720
|
+
input.sourceAnchor ?? defaultAnchors.sourceAnchor
|
|
2235
2721
|
);
|
|
2236
2722
|
const target = getEdgePort(
|
|
2237
2723
|
input.target,
|
|
2238
2724
|
input.source.center,
|
|
2239
|
-
input.targetAnchor
|
|
2725
|
+
input.targetAnchor ?? defaultAnchors.targetAnchor
|
|
2240
2726
|
);
|
|
2241
2727
|
if ((input.kind ?? "orthogonal") === "straight") {
|
|
2242
2728
|
return { points: simplifyRoute([source, target]), diagnostics };
|
|
2243
2729
|
}
|
|
2244
2730
|
const candidates = orthogonalCandidates(source, target, input.direction);
|
|
2731
|
+
candidates.push(
|
|
2732
|
+
...expandedObstacleCandidates(
|
|
2733
|
+
source,
|
|
2734
|
+
target,
|
|
2735
|
+
input.direction,
|
|
2736
|
+
input.obstacles ?? []
|
|
2737
|
+
)
|
|
2738
|
+
);
|
|
2245
2739
|
for (const candidate of candidates) {
|
|
2246
2740
|
if (!routeIntersectsObstacles(candidate, input.obstacles ?? [])) {
|
|
2247
2741
|
return { points: simplifyRoute(candidate), diagnostics };
|
|
@@ -2280,27 +2774,113 @@ function simplifyRoute(points) {
|
|
|
2280
2774
|
function orthogonalCandidates(source, target, direction) {
|
|
2281
2775
|
const midpointX = (source.x + target.x) / 2;
|
|
2282
2776
|
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
|
-
];
|
|
2777
|
+
const candidates = [];
|
|
2287
2778
|
if (direction === "TB" || direction === "BT") {
|
|
2288
2779
|
candidates.push([
|
|
2289
2780
|
source,
|
|
2290
|
-
{ x:
|
|
2291
|
-
{ x:
|
|
2781
|
+
{ x: source.x, y: midpointY },
|
|
2782
|
+
{ x: target.x, y: midpointY },
|
|
2292
2783
|
target
|
|
2293
2784
|
]);
|
|
2294
2785
|
} else {
|
|
2295
2786
|
candidates.push([
|
|
2296
2787
|
source,
|
|
2297
|
-
{ x:
|
|
2298
|
-
{ x:
|
|
2788
|
+
{ x: midpointX, y: source.y },
|
|
2789
|
+
{ x: midpointX, y: target.y },
|
|
2299
2790
|
target
|
|
2300
2791
|
]);
|
|
2301
2792
|
}
|
|
2793
|
+
candidates.push(
|
|
2794
|
+
[source, { x: target.x, y: source.y }, target],
|
|
2795
|
+
[source, { x: source.x, y: target.y }, target]
|
|
2796
|
+
);
|
|
2302
2797
|
return candidates;
|
|
2303
2798
|
}
|
|
2799
|
+
function defaultSourceAnchor(direction) {
|
|
2800
|
+
switch (direction) {
|
|
2801
|
+
case "LR":
|
|
2802
|
+
return "right";
|
|
2803
|
+
case "RL":
|
|
2804
|
+
return "left";
|
|
2805
|
+
case "TB":
|
|
2806
|
+
return "bottom";
|
|
2807
|
+
case "BT":
|
|
2808
|
+
return "top";
|
|
2809
|
+
}
|
|
2810
|
+
}
|
|
2811
|
+
function defaultAnchorsForGeometry(source, target, direction) {
|
|
2812
|
+
const dx = target.x + target.width / 2 - (source.x + source.width / 2);
|
|
2813
|
+
const dy = target.y + target.height / 2 - (source.y + source.height / 2);
|
|
2814
|
+
if (Math.abs(dy) > Math.abs(dx)) {
|
|
2815
|
+
return dy >= 0 ? { sourceAnchor: "bottom", targetAnchor: "top" } : { sourceAnchor: "top", targetAnchor: "bottom" };
|
|
2816
|
+
}
|
|
2817
|
+
if (Math.abs(dx) > 0) {
|
|
2818
|
+
return dx >= 0 ? { sourceAnchor: "right", targetAnchor: "left" } : { sourceAnchor: "left", targetAnchor: "right" };
|
|
2819
|
+
}
|
|
2820
|
+
return {
|
|
2821
|
+
sourceAnchor: defaultSourceAnchor(direction),
|
|
2822
|
+
targetAnchor: defaultTargetAnchor(direction)
|
|
2823
|
+
};
|
|
2824
|
+
}
|
|
2825
|
+
function defaultTargetAnchor(direction) {
|
|
2826
|
+
switch (direction) {
|
|
2827
|
+
case "LR":
|
|
2828
|
+
return "left";
|
|
2829
|
+
case "RL":
|
|
2830
|
+
return "right";
|
|
2831
|
+
case "TB":
|
|
2832
|
+
return "top";
|
|
2833
|
+
case "BT":
|
|
2834
|
+
return "bottom";
|
|
2835
|
+
}
|
|
2836
|
+
}
|
|
2837
|
+
function expandedObstacleCandidates(source, target, direction, obstacles) {
|
|
2838
|
+
if (obstacles.length === 0) {
|
|
2839
|
+
return [];
|
|
2840
|
+
}
|
|
2841
|
+
const margin = 16;
|
|
2842
|
+
const candidates = [];
|
|
2843
|
+
if (direction === "TB" || direction === "BT") {
|
|
2844
|
+
const lanes = sortedUniqueLanes(
|
|
2845
|
+
obstacles.flatMap((obstacle) => [
|
|
2846
|
+
obstacle.x - margin,
|
|
2847
|
+
obstacle.x + obstacle.width + margin
|
|
2848
|
+
]),
|
|
2849
|
+
(source.x + target.x) / 2
|
|
2850
|
+
);
|
|
2851
|
+
for (const laneX of lanes) {
|
|
2852
|
+
candidates.push([
|
|
2853
|
+
source,
|
|
2854
|
+
{ x: laneX, y: source.y },
|
|
2855
|
+
{ x: laneX, y: target.y },
|
|
2856
|
+
target
|
|
2857
|
+
]);
|
|
2858
|
+
}
|
|
2859
|
+
} else {
|
|
2860
|
+
const lanes = sortedUniqueLanes(
|
|
2861
|
+
obstacles.flatMap((obstacle) => [
|
|
2862
|
+
obstacle.y - margin,
|
|
2863
|
+
obstacle.y + obstacle.height + margin
|
|
2864
|
+
]),
|
|
2865
|
+
(source.y + target.y) / 2
|
|
2866
|
+
);
|
|
2867
|
+
for (const laneY of lanes) {
|
|
2868
|
+
candidates.push([
|
|
2869
|
+
source,
|
|
2870
|
+
{ x: source.x, y: laneY },
|
|
2871
|
+
{ x: target.x, y: laneY },
|
|
2872
|
+
target
|
|
2873
|
+
]);
|
|
2874
|
+
}
|
|
2875
|
+
}
|
|
2876
|
+
return candidates;
|
|
2877
|
+
}
|
|
2878
|
+
function sortedUniqueLanes(lanes, midpoint) {
|
|
2879
|
+
return [...new Set(lanes)].filter((lane) => Number.isFinite(lane)).sort((left, right) => {
|
|
2880
|
+
const distance = Math.abs(left - midpoint) - Math.abs(right - midpoint);
|
|
2881
|
+
return distance === 0 ? left - right : distance;
|
|
2882
|
+
});
|
|
2883
|
+
}
|
|
2304
2884
|
function routeIntersectsObstacles(points, obstacles) {
|
|
2305
2885
|
for (let index = 0; index < points.length - 1; index += 1) {
|
|
2306
2886
|
const a = points[index];
|
|
@@ -2379,12 +2959,17 @@ function solveDiagram(diagram, options = {}) {
|
|
|
2379
2959
|
options,
|
|
2380
2960
|
diagnostics
|
|
2381
2961
|
);
|
|
2962
|
+
const coordinatedSwimlanes = coordinateSwimlanes(
|
|
2963
|
+
diagram.swimlanes ?? [],
|
|
2964
|
+
constrained.boxes
|
|
2965
|
+
);
|
|
2382
2966
|
const groupBoxes = new Map(
|
|
2383
2967
|
coordinatedGroups.map((group) => [group.id, group.box])
|
|
2384
2968
|
);
|
|
2385
2969
|
const coordinatedEdges = coordinateEdges(
|
|
2386
2970
|
edges,
|
|
2387
2971
|
nodeGeometryById,
|
|
2972
|
+
coordinatedNodes,
|
|
2388
2973
|
[...nodeGeometryById.values()].map((geometry) => geometry.obstacleBox),
|
|
2389
2974
|
diagram.direction,
|
|
2390
2975
|
options,
|
|
@@ -2392,8 +2977,18 @@ function solveDiagram(diagram, options = {}) {
|
|
|
2392
2977
|
);
|
|
2393
2978
|
const allBoxes = [
|
|
2394
2979
|
...coordinatedNodes.map((node) => node.box),
|
|
2395
|
-
...
|
|
2980
|
+
...coordinatedNodes.flatMap(
|
|
2981
|
+
(node) => (node.ports ?? []).flatMap(
|
|
2982
|
+
(port) => port.label === void 0 ? [port.box] : [port.box, portLabelBox(port)]
|
|
2983
|
+
)
|
|
2984
|
+
),
|
|
2985
|
+
...groupBoxes.values(),
|
|
2986
|
+
...coordinatedSwimlanes.flatMap(
|
|
2987
|
+
(swimlane) => swimlane.box === void 0 ? [] : [swimlane.box]
|
|
2988
|
+
)
|
|
2396
2989
|
];
|
|
2990
|
+
const contentBounds = allBoxes.length === 0 ? { x: 0, y: 0, width: 0, height: 0 } : unionBoxes(allBoxes);
|
|
2991
|
+
const frame = diagram.frame === void 0 ? void 0 : coordinateFrame(diagram.frame, contentBounds);
|
|
2397
2992
|
return {
|
|
2398
2993
|
id: diagram.id,
|
|
2399
2994
|
...diagram.title === void 0 ? {} : { title: diagram.title },
|
|
@@ -2401,8 +2996,10 @@ function solveDiagram(diagram, options = {}) {
|
|
|
2401
2996
|
nodes: coordinatedNodes,
|
|
2402
2997
|
edges: coordinatedEdges,
|
|
2403
2998
|
groups: coordinatedGroups,
|
|
2999
|
+
...coordinatedSwimlanes.length === 0 ? {} : { swimlanes: coordinatedSwimlanes },
|
|
2404
3000
|
diagnostics,
|
|
2405
|
-
bounds:
|
|
3001
|
+
bounds: frame === void 0 ? contentBounds : unionBoxes([contentBounds, frame.box, frame.titleBox]),
|
|
3002
|
+
...frame === void 0 ? {} : { frame },
|
|
2406
3003
|
...diagram.metadata === void 0 ? {} : { metadata: diagram.metadata }
|
|
2407
3004
|
};
|
|
2408
3005
|
}
|
|
@@ -2428,6 +3025,9 @@ function coordinateNodes(nodes, boxes, options, diagnostics) {
|
|
|
2428
3025
|
coordinated.push({
|
|
2429
3026
|
id: node.id,
|
|
2430
3027
|
...node.label === void 0 ? {} : { label: node.label },
|
|
3028
|
+
...node.style === void 0 ? {} : { style: node.style },
|
|
3029
|
+
...node.ports === void 0 ? {} : { ports: coordinatePorts(node, box, options.portShifting) },
|
|
3030
|
+
...node.compartments === void 0 ? {} : { compartments: node.compartments },
|
|
2431
3031
|
...node.labelLayout === void 0 ? {} : { labelLayout: node.labelLayout },
|
|
2432
3032
|
shape: node.shape,
|
|
2433
3033
|
...node.metadata === void 0 ? {} : { metadata: node.metadata },
|
|
@@ -2438,6 +3038,142 @@ function coordinateNodes(nodes, boxes, options, diagnostics) {
|
|
|
2438
3038
|
}
|
|
2439
3039
|
return coordinated;
|
|
2440
3040
|
}
|
|
3041
|
+
function coordinatePorts(node, nodeBox, portShifting) {
|
|
3042
|
+
const portsBySide = /* @__PURE__ */ new Map();
|
|
3043
|
+
for (const port of node.ports ?? []) {
|
|
3044
|
+
const ports = portsBySide.get(port.side) ?? [];
|
|
3045
|
+
ports.push(port);
|
|
3046
|
+
portsBySide.set(port.side, ports);
|
|
3047
|
+
}
|
|
3048
|
+
const coordinated = [];
|
|
3049
|
+
for (const [side, ports] of portsBySide) {
|
|
3050
|
+
const sorted = [...ports ?? []].sort((a, b) => {
|
|
3051
|
+
const order = (a.order ?? 0) - (b.order ?? 0);
|
|
3052
|
+
return order === 0 ? a.id.localeCompare(b.id) : order;
|
|
3053
|
+
});
|
|
3054
|
+
for (let index = 0; index < sorted.length; index += 1) {
|
|
3055
|
+
const port = sorted[index];
|
|
3056
|
+
if (port === void 0) {
|
|
3057
|
+
continue;
|
|
3058
|
+
}
|
|
3059
|
+
const anchor = portAnchor(
|
|
3060
|
+
nodeBox,
|
|
3061
|
+
side,
|
|
3062
|
+
index,
|
|
3063
|
+
sorted.length,
|
|
3064
|
+
portShifting
|
|
3065
|
+
);
|
|
3066
|
+
const box = portBox(anchor);
|
|
3067
|
+
coordinated.push({ ...port, box, anchor });
|
|
3068
|
+
}
|
|
3069
|
+
}
|
|
3070
|
+
return coordinated.sort((a, b) => a.id.localeCompare(b.id));
|
|
3071
|
+
}
|
|
3072
|
+
function portAnchor(nodeBox, side, index, count, portShifting) {
|
|
3073
|
+
const shiftingEnabled = portShifting?.enabled ?? true;
|
|
3074
|
+
const spacing = portShifting?.spacing ?? 24;
|
|
3075
|
+
const centeredOffset = shiftingEnabled ? (index - (count - 1) / 2) * spacing : 0;
|
|
3076
|
+
switch (side) {
|
|
3077
|
+
case "left":
|
|
3078
|
+
return {
|
|
3079
|
+
x: nodeBox.x,
|
|
3080
|
+
y: nodeBox.y + nodeBox.height / 2 + centeredOffset
|
|
3081
|
+
};
|
|
3082
|
+
case "right":
|
|
3083
|
+
return {
|
|
3084
|
+
x: nodeBox.x + nodeBox.width,
|
|
3085
|
+
y: nodeBox.y + nodeBox.height / 2 + centeredOffset
|
|
3086
|
+
};
|
|
3087
|
+
case "top":
|
|
3088
|
+
return {
|
|
3089
|
+
x: nodeBox.x + nodeBox.width / 2 + centeredOffset,
|
|
3090
|
+
y: nodeBox.y
|
|
3091
|
+
};
|
|
3092
|
+
case "bottom":
|
|
3093
|
+
return {
|
|
3094
|
+
x: nodeBox.x + nodeBox.width / 2 + centeredOffset,
|
|
3095
|
+
y: nodeBox.y + nodeBox.height
|
|
3096
|
+
};
|
|
3097
|
+
}
|
|
3098
|
+
}
|
|
3099
|
+
function portBox(anchor) {
|
|
3100
|
+
const size = 10;
|
|
3101
|
+
return {
|
|
3102
|
+
x: anchor.x - size / 2,
|
|
3103
|
+
y: anchor.y - size / 2,
|
|
3104
|
+
width: size,
|
|
3105
|
+
height: size
|
|
3106
|
+
};
|
|
3107
|
+
}
|
|
3108
|
+
function portLabelBox(port) {
|
|
3109
|
+
const textWidth = Math.max(0, (port.label?.text.length ?? 0) * 6);
|
|
3110
|
+
const height = 12;
|
|
3111
|
+
const gap = 8;
|
|
3112
|
+
const x = port.side === "left" ? port.anchor.x - gap - textWidth : port.anchor.x + gap;
|
|
3113
|
+
return {
|
|
3114
|
+
x,
|
|
3115
|
+
y: port.anchor.y - 8 - height,
|
|
3116
|
+
width: textWidth,
|
|
3117
|
+
height
|
|
3118
|
+
};
|
|
3119
|
+
}
|
|
3120
|
+
function coordinateSwimlanes(swimlanes, nodeBoxes) {
|
|
3121
|
+
const titleSize = 28;
|
|
3122
|
+
const padding = 16;
|
|
3123
|
+
return swimlanes.map((swimlane) => {
|
|
3124
|
+
const laneBoxes = swimlane.lanes.flatMap((lane) => {
|
|
3125
|
+
const childBoxes = lane.children.map((child) => nodeBoxes.get(child)).filter((box) => box !== void 0);
|
|
3126
|
+
return childBoxes.length === 0 ? [] : [unionBoxes(childBoxes)];
|
|
3127
|
+
});
|
|
3128
|
+
const laneUnion = laneBoxes.length === 0 ? { x: 0, y: 0, width: 120, height: 80 } : unionBoxes(laneBoxes);
|
|
3129
|
+
const outer = expand(laneUnion, padding, titleSize);
|
|
3130
|
+
const laneCount = Math.max(1, swimlane.lanes.length);
|
|
3131
|
+
const lanes = swimlane.lanes.map((lane, index) => {
|
|
3132
|
+
const box = swimlane.orientation === "vertical" ? {
|
|
3133
|
+
x: outer.x + outer.width / laneCount * index,
|
|
3134
|
+
y: outer.y,
|
|
3135
|
+
width: outer.width / laneCount,
|
|
3136
|
+
height: outer.height
|
|
3137
|
+
} : {
|
|
3138
|
+
x: outer.x,
|
|
3139
|
+
y: outer.y + outer.height / laneCount * index,
|
|
3140
|
+
width: outer.width,
|
|
3141
|
+
height: outer.height / laneCount
|
|
3142
|
+
};
|
|
3143
|
+
return { ...lane, box };
|
|
3144
|
+
});
|
|
3145
|
+
return { ...swimlane, lanes, box: outer };
|
|
3146
|
+
});
|
|
3147
|
+
}
|
|
3148
|
+
function coordinateFrame(frame, contentBounds) {
|
|
3149
|
+
const padding = 32;
|
|
3150
|
+
const titleHeight = 28;
|
|
3151
|
+
const titleWidth = Math.max(180, frame.titleTab.length * 7);
|
|
3152
|
+
const box = {
|
|
3153
|
+
x: contentBounds.x - padding,
|
|
3154
|
+
y: contentBounds.y - padding - titleHeight,
|
|
3155
|
+
width: contentBounds.width + padding * 2,
|
|
3156
|
+
height: contentBounds.height + padding * 2 + titleHeight
|
|
3157
|
+
};
|
|
3158
|
+
return {
|
|
3159
|
+
...frame,
|
|
3160
|
+
box,
|
|
3161
|
+
titleBox: {
|
|
3162
|
+
x: box.x,
|
|
3163
|
+
y: box.y,
|
|
3164
|
+
width: Math.min(titleWidth, box.width * 0.8),
|
|
3165
|
+
height: titleHeight
|
|
3166
|
+
}
|
|
3167
|
+
};
|
|
3168
|
+
}
|
|
3169
|
+
function expand(box, padding, titleSize) {
|
|
3170
|
+
return {
|
|
3171
|
+
x: box.x - padding,
|
|
3172
|
+
y: box.y - padding - titleSize,
|
|
3173
|
+
width: box.width + padding * 2,
|
|
3174
|
+
height: box.height + padding * 2 + titleSize
|
|
3175
|
+
};
|
|
3176
|
+
}
|
|
2441
3177
|
function coordinateGroups(groups, nodeBoxes, options, diagnostics) {
|
|
2442
3178
|
const coordinated = [];
|
|
2443
3179
|
const groupBoxes = /* @__PURE__ */ new Map();
|
|
@@ -2486,8 +3222,11 @@ function coordinateGroups(groups, nodeBoxes, options, diagnostics) {
|
|
|
2486
3222
|
}
|
|
2487
3223
|
return coordinated;
|
|
2488
3224
|
}
|
|
2489
|
-
function coordinateEdges(edges, nodes, obstacles, direction, options, diagnostics) {
|
|
3225
|
+
function coordinateEdges(edges, nodes, coordinatedNodes, obstacles, direction, options, diagnostics) {
|
|
2490
3226
|
const coordinated = [];
|
|
3227
|
+
const coordinatedNodeById = new Map(
|
|
3228
|
+
coordinatedNodes.map((node) => [node.id, node])
|
|
3229
|
+
);
|
|
2491
3230
|
for (const edge of edges) {
|
|
2492
3231
|
const source = nodes.get(edge.source.nodeId);
|
|
2493
3232
|
const target = nodes.get(edge.target.nodeId);
|
|
@@ -2505,11 +3244,13 @@ function coordinateEdges(edges, nodes, obstacles, direction, options, diagnostic
|
|
|
2505
3244
|
});
|
|
2506
3245
|
continue;
|
|
2507
3246
|
}
|
|
3247
|
+
const sourcePort = coordinatedNodeById.get(edge.source.nodeId)?.ports?.find((port) => port.id === edge.source.portId);
|
|
3248
|
+
const targetPort = coordinatedNodeById.get(edge.target.nodeId)?.ports?.find((port) => port.id === edge.target.portId);
|
|
2508
3249
|
const route = routeEdge({
|
|
2509
3250
|
kind: options.routeKind ?? "orthogonal",
|
|
2510
3251
|
direction,
|
|
2511
|
-
source,
|
|
2512
|
-
target,
|
|
3252
|
+
source: portGeometry(source, sourcePort),
|
|
3253
|
+
target: portGeometry(target, targetPort),
|
|
2513
3254
|
...edge.source.anchor === void 0 ? {} : { sourceAnchor: edge.source.anchor },
|
|
2514
3255
|
...edge.target.anchor === void 0 ? {} : { targetAnchor: edge.target.anchor },
|
|
2515
3256
|
obstacles: obstacles.filter(
|
|
@@ -2529,6 +3270,21 @@ function coordinateEdges(edges, nodes, obstacles, direction, options, diagnostic
|
|
|
2529
3270
|
}
|
|
2530
3271
|
return coordinated;
|
|
2531
3272
|
}
|
|
3273
|
+
function portGeometry(nodeGeometry, port) {
|
|
3274
|
+
if (port === void 0) {
|
|
3275
|
+
return nodeGeometry;
|
|
3276
|
+
}
|
|
3277
|
+
return {
|
|
3278
|
+
...nodeGeometry,
|
|
3279
|
+
box: port.box,
|
|
3280
|
+
center: port.anchor,
|
|
3281
|
+
anchors: nodeGeometry.anchors.map((anchor) => ({
|
|
3282
|
+
name: anchor.name,
|
|
3283
|
+
point: port.anchor
|
|
3284
|
+
})),
|
|
3285
|
+
obstacleBox: port.box
|
|
3286
|
+
};
|
|
3287
|
+
}
|
|
2532
3288
|
function stableById(items) {
|
|
2533
3289
|
return [...items].sort((a, b) => a.id.localeCompare(b.id));
|
|
2534
3290
|
}
|
|
@@ -2589,7 +3345,8 @@ function renderDiagramDsl(source, options = {}) {
|
|
|
2589
3345
|
return { diagnostics };
|
|
2590
3346
|
}
|
|
2591
3347
|
const solved = solveDiagram(normalized.diagram, {
|
|
2592
|
-
routeKind: normalized.diagram.metadata?.routeKind === "straight" ? "straight" : "orthogonal"
|
|
3348
|
+
routeKind: normalized.diagram.metadata?.routeKind === "straight" ? "straight" : "orthogonal",
|
|
3349
|
+
...solvePortShiftingOption(normalized.diagram.metadata?.portShifting)
|
|
2593
3350
|
});
|
|
2594
3351
|
const solveDiagnostics = solved.diagnostics.map(toSolveDiagnostic);
|
|
2595
3352
|
if (hasErrorDiagnostics2(solveDiagnostics)) {
|
|
@@ -2628,6 +3385,22 @@ function renderDiagramDsl(source, options = {}) {
|
|
|
2628
3385
|
function toSolveDiagnostic(diagnostic) {
|
|
2629
3386
|
return { ...diagnostic, layer: "solve" };
|
|
2630
3387
|
}
|
|
3388
|
+
function solvePortShiftingOption(value) {
|
|
3389
|
+
if (!isJsonObject(value)) {
|
|
3390
|
+
return {};
|
|
3391
|
+
}
|
|
3392
|
+
const portShifting = {};
|
|
3393
|
+
if (value.enabled === false) {
|
|
3394
|
+
portShifting.enabled = false;
|
|
3395
|
+
}
|
|
3396
|
+
if (typeof value.spacing === "number") {
|
|
3397
|
+
portShifting.spacing = value.spacing;
|
|
3398
|
+
}
|
|
3399
|
+
return { portShifting };
|
|
3400
|
+
}
|
|
3401
|
+
function isJsonObject(value) {
|
|
3402
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3403
|
+
}
|
|
2631
3404
|
function toExportDiagnostic(diagnostic) {
|
|
2632
3405
|
return { ...diagnostic, layer: "export" };
|
|
2633
3406
|
}
|
|
@@ -2761,6 +3534,6 @@ function isPointLikeRecord(value) {
|
|
|
2761
3534
|
return isPlainObject(value) && typeof value.x === "number" && typeof value.y === "number";
|
|
2762
3535
|
}
|
|
2763
3536
|
|
|
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 };
|
|
3537
|
+
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
3538
|
//# sourceMappingURL=index.js.map
|
|
2766
3539
|
//# sourceMappingURL=index.js.map
|