@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/cli/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { CommanderError, Command } from 'commander';
|
|
3
3
|
import { Graph, layout } from '@dagrejs/dagre';
|
|
4
|
+
import { createRequire } from 'module';
|
|
5
|
+
import { prepareWithSegments, layoutWithLines, measureNaturalWidth } from '@chenglou/pretext';
|
|
4
6
|
import { Buffer as Buffer$1 } from 'buffer';
|
|
5
7
|
import { parseDocument } from 'yaml';
|
|
6
8
|
import { z } from 'zod';
|
|
@@ -194,11 +196,12 @@ function renderArrow(edge) {
|
|
|
194
196
|
height: box.height
|
|
195
197
|
}),
|
|
196
198
|
backgroundColor: "transparent",
|
|
199
|
+
strokeStyle: edge.style ?? "solid",
|
|
197
200
|
points: relativePoints,
|
|
198
201
|
startBinding: { elementId: `node:${edge.source.nodeId}`, focus: 0, gap: 0 },
|
|
199
202
|
endBinding: { elementId: `node:${edge.target.nodeId}`, focus: 0, gap: 0 },
|
|
200
203
|
startArrowhead: null,
|
|
201
|
-
endArrowhead:
|
|
204
|
+
endArrowhead: mapArrowhead(edge.arrowhead)
|
|
202
205
|
};
|
|
203
206
|
}
|
|
204
207
|
function renderText(id, label, box, containerId, groupIds) {
|
|
@@ -276,6 +279,16 @@ function mapShape(shape) {
|
|
|
276
279
|
return "cylinder";
|
|
277
280
|
}
|
|
278
281
|
}
|
|
282
|
+
function mapArrowhead(arrowhead) {
|
|
283
|
+
switch (arrowhead) {
|
|
284
|
+
case void 0:
|
|
285
|
+
return "arrow";
|
|
286
|
+
case "triangle":
|
|
287
|
+
return "triangle";
|
|
288
|
+
case "hollowTriangle":
|
|
289
|
+
return "triangle_outline";
|
|
290
|
+
}
|
|
291
|
+
}
|
|
279
292
|
function createGroupMembership(groups) {
|
|
280
293
|
const membership = /* @__PURE__ */ new Map();
|
|
281
294
|
for (const group of groups) {
|
|
@@ -342,19 +355,28 @@ function exportSvg(diagram, options = {}) {
|
|
|
342
355
|
`<svg xmlns="http://www.w3.org/2000/svg" role="img" viewBox="${formatBoxViewBox(diagram.bounds)}">`,
|
|
343
356
|
...title === void 0 ? [] : [` <title>${escapeXml(title)}</title>`],
|
|
344
357
|
` <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"/>`,
|
|
358
|
+
...diagram.frame === void 0 ? [] : [indent(renderFrame(diagram.frame))],
|
|
359
|
+
...(diagram.swimlanes ?? []).flatMap(
|
|
360
|
+
(swimlane) => renderSwimlane(swimlane)
|
|
361
|
+
),
|
|
345
362
|
...diagram.groups.map((group) => indent(renderGroup2(group))),
|
|
346
363
|
...diagram.edges.flatMap((edge) => {
|
|
347
|
-
const path = renderEdgePath(edge
|
|
364
|
+
const path = renderEdgePath(edge);
|
|
348
365
|
if (path === void 0) {
|
|
349
366
|
return [];
|
|
350
367
|
}
|
|
351
|
-
return [indent(path), indent(renderArrowhead(edge
|
|
368
|
+
return [indent(path), indent(renderArrowhead(edge))];
|
|
352
369
|
}),
|
|
353
370
|
...diagram.nodes.map((node) => indent(renderNode2(node))),
|
|
371
|
+
...diagram.nodes.flatMap((node) => renderCompartments(node)),
|
|
372
|
+
...diagram.nodes.flatMap((node) => renderPorts(node)),
|
|
354
373
|
...diagram.groups.flatMap(
|
|
355
374
|
(group) => renderLabel(group.label, group.box, group)
|
|
356
375
|
),
|
|
357
|
-
...diagram.nodes.flatMap(
|
|
376
|
+
...diagram.nodes.flatMap(
|
|
377
|
+
(node) => node.compartments === void 0 ? renderLabel(node.label, node.box, node) : []
|
|
378
|
+
),
|
|
379
|
+
...diagram.edges.flatMap((edge) => renderEdgeLabel(edge)),
|
|
358
380
|
"</svg>"
|
|
359
381
|
];
|
|
360
382
|
return `${lines.join("\n")}
|
|
@@ -364,7 +386,9 @@ function renderGroup2(group) {
|
|
|
364
386
|
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"/>`;
|
|
365
387
|
}
|
|
366
388
|
function renderNode2(node) {
|
|
367
|
-
const
|
|
389
|
+
const fill = node.style?.fill ?? NODE_FILL;
|
|
390
|
+
const stroke = node.style?.stroke ?? STROKE;
|
|
391
|
+
const common = `class="node node-${node.shape}" data-id="${escapeAttribute(node.id)}" fill="${escapeAttribute(fill)}" stroke="${escapeAttribute(stroke)}"`;
|
|
368
392
|
switch (node.shape) {
|
|
369
393
|
case "rectangle":
|
|
370
394
|
return renderRect(node.box, common);
|
|
@@ -380,16 +404,111 @@ function renderNode2(node) {
|
|
|
380
404
|
return `<path ${common} d="${formatCylinderPath(node.box)}"/>`;
|
|
381
405
|
}
|
|
382
406
|
}
|
|
407
|
+
function renderFrame(frame) {
|
|
408
|
+
const stroke = frame.style?.stroke ?? "#6b7280";
|
|
409
|
+
const fill = frame.style?.fill ?? "transparent";
|
|
410
|
+
return [
|
|
411
|
+
`<g class="sysml-frame" data-kind="${escapeAttribute(frame.kind)}">`,
|
|
412
|
+
` <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)}"/>`,
|
|
413
|
+
` <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)}"/>`,
|
|
414
|
+
` <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>`,
|
|
415
|
+
"</g>"
|
|
416
|
+
].join("\n");
|
|
417
|
+
}
|
|
418
|
+
function renderSwimlane(swimlane) {
|
|
419
|
+
if (swimlane.box === void 0) {
|
|
420
|
+
return [];
|
|
421
|
+
}
|
|
422
|
+
const lines = [
|
|
423
|
+
` <g class="swimlane" data-id="${escapeAttribute(swimlane.id)}">`,
|
|
424
|
+
` <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}"/>`
|
|
425
|
+
];
|
|
426
|
+
for (const lane of swimlane.lanes) {
|
|
427
|
+
if (lane.box === void 0) {
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
lines.push(
|
|
431
|
+
` <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}"/>`
|
|
432
|
+
);
|
|
433
|
+
if (lane.label?.text !== void 0) {
|
|
434
|
+
lines.push(
|
|
435
|
+
` <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>`
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
lines.push(" </g>");
|
|
440
|
+
return lines;
|
|
441
|
+
}
|
|
442
|
+
function renderPorts(node) {
|
|
443
|
+
return (node.ports ?? []).flatMap((port) => [
|
|
444
|
+
` <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)}"/>`,
|
|
445
|
+
...port.label?.text === void 0 ? [] : [
|
|
446
|
+
` <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>`
|
|
447
|
+
]
|
|
448
|
+
]);
|
|
449
|
+
}
|
|
450
|
+
function renderCompartments(node) {
|
|
451
|
+
const compartments2 = node.compartments;
|
|
452
|
+
if (compartments2 === void 0) {
|
|
453
|
+
return [];
|
|
454
|
+
}
|
|
455
|
+
const rows = [
|
|
456
|
+
...compartments2.stereotype === void 0 ? [] : [{ className: "stereotype", text: compartments2.stereotype }],
|
|
457
|
+
{
|
|
458
|
+
className: "name",
|
|
459
|
+
text: compartments2.name ?? node.label?.text ?? node.id
|
|
460
|
+
},
|
|
461
|
+
...(compartments2.properties ?? []).map((text) => ({
|
|
462
|
+
className: "properties",
|
|
463
|
+
text
|
|
464
|
+
})),
|
|
465
|
+
...(compartments2.constraints ?? []).map((text) => ({
|
|
466
|
+
className: "constraints",
|
|
467
|
+
text
|
|
468
|
+
}))
|
|
469
|
+
];
|
|
470
|
+
const lineHeight = 16;
|
|
471
|
+
const lines = [
|
|
472
|
+
` <g class="compartment" data-for="${escapeAttribute(node.id)}">`
|
|
473
|
+
];
|
|
474
|
+
for (let index = 0; index < rows.length; index += 1) {
|
|
475
|
+
const row = rows[index];
|
|
476
|
+
if (row === void 0) {
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
479
|
+
const y = node.box.y + 18 + index * lineHeight;
|
|
480
|
+
if (index > 1) {
|
|
481
|
+
lines.push(
|
|
482
|
+
` <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}"/>`
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
lines.push(
|
|
486
|
+
` <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>`
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
lines.push(" </g>");
|
|
490
|
+
return lines;
|
|
491
|
+
}
|
|
492
|
+
function portLabelX(x, side) {
|
|
493
|
+
if (side === "left") {
|
|
494
|
+
return x - 8;
|
|
495
|
+
}
|
|
496
|
+
if (side === "right") {
|
|
497
|
+
return x + 8;
|
|
498
|
+
}
|
|
499
|
+
return x + 8;
|
|
500
|
+
}
|
|
383
501
|
function renderRect(box, attributes) {
|
|
384
502
|
return `<rect ${attributes} x="${formatNumber(box.x)}" y="${formatNumber(box.y)}" width="${formatNumber(box.width)}" height="${formatNumber(box.height)}"/>`;
|
|
385
503
|
}
|
|
386
504
|
function renderLabel(label, box, item) {
|
|
387
505
|
const labelLayout = item.labelLayout;
|
|
388
506
|
if (labelLayout?.lines !== void 0 && labelLayout.lines.length > 0) {
|
|
507
|
+
const offset = isAbsoluteLabelLayout(labelLayout.box, box) ? { x: 0, y: 0 } : { x: box.x, y: box.y };
|
|
389
508
|
return [
|
|
390
509
|
` <text class="label" data-for="${escapeAttribute(item.id)}" font-family="${FONT_FAMILY}" font-size="${formatNumber(labelLayout.font.fontSize)}" fill="#111827">`,
|
|
391
510
|
...labelLayout.lines.map(
|
|
392
|
-
(line) => ` <tspan x="${formatNumber(line.box.x)}" y="${formatNumber(line.baselineY)}">${escapeXml(line.text)}</tspan>`
|
|
511
|
+
(line) => ` <tspan x="${formatNumber(offset.x + line.box.x)}" y="${formatNumber(offset.y + line.baselineY)}">${escapeXml(line.text)}</tspan>`
|
|
393
512
|
),
|
|
394
513
|
" </text>"
|
|
395
514
|
];
|
|
@@ -401,15 +520,91 @@ function renderLabel(label, box, item) {
|
|
|
401
520
|
` <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>`
|
|
402
521
|
];
|
|
403
522
|
}
|
|
404
|
-
function renderEdgePath(
|
|
405
|
-
if (points.length < 2) {
|
|
523
|
+
function renderEdgePath(edge) {
|
|
524
|
+
if (edge.points.length < 2) {
|
|
525
|
+
return void 0;
|
|
526
|
+
}
|
|
527
|
+
const dash = edge.style === "dashed" ? ' stroke-dasharray="6 4"' : "";
|
|
528
|
+
return `<path class="edge" data-id="${escapeAttribute(edge.id)}" d="${formatPath(pathPointsBeforeArrowhead(edge.points))}" fill="none" stroke="${EDGE_STROKE}" stroke-width="1.5"${dash}/>`;
|
|
529
|
+
}
|
|
530
|
+
function renderEdgeLabel(edge) {
|
|
531
|
+
if (edge.label?.text === void 0 || edge.points.length < 2) {
|
|
532
|
+
return [];
|
|
533
|
+
}
|
|
534
|
+
const placement = labelPlacementOnPolyline(edge.points);
|
|
535
|
+
if (placement === void 0) {
|
|
536
|
+
return [];
|
|
537
|
+
}
|
|
538
|
+
return [
|
|
539
|
+
` <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>`
|
|
540
|
+
];
|
|
541
|
+
}
|
|
542
|
+
function renderArrowhead(edge) {
|
|
543
|
+
const arrowhead = computeArrowhead(edge.points);
|
|
544
|
+
const fill = edge.arrowhead === "hollowTriangle" ? "none" : EDGE_STROKE;
|
|
545
|
+
return `<polygon class="edge-arrowhead" data-edge="${escapeAttribute(edge.id)}" points="${formatPoints([arrowhead.tip, arrowhead.left, arrowhead.right])}" fill="${fill}" stroke="${EDGE_STROKE}"/>`;
|
|
546
|
+
}
|
|
547
|
+
function labelPlacementOnPolyline(points) {
|
|
548
|
+
const segments = nonZeroSegments(points);
|
|
549
|
+
const totalLength = segments.reduce(
|
|
550
|
+
(sum, segment) => sum + segment.length,
|
|
551
|
+
0
|
|
552
|
+
);
|
|
553
|
+
if (totalLength <= 0) {
|
|
406
554
|
return void 0;
|
|
407
555
|
}
|
|
408
|
-
|
|
556
|
+
let remaining = totalLength / 2;
|
|
557
|
+
for (const segment of segments) {
|
|
558
|
+
if (remaining <= segment.length) {
|
|
559
|
+
const ratio = remaining / segment.length;
|
|
560
|
+
const x = segment.start.x + (segment.end.x - segment.start.x) * ratio;
|
|
561
|
+
const y = segment.start.y + (segment.end.y - segment.start.y) * ratio;
|
|
562
|
+
const offset2 = labelOffset(segment);
|
|
563
|
+
return { x: x + offset2.x, y: y + offset2.y };
|
|
564
|
+
}
|
|
565
|
+
remaining -= segment.length;
|
|
566
|
+
}
|
|
567
|
+
const last = segments.at(-1);
|
|
568
|
+
if (last === void 0) {
|
|
569
|
+
return void 0;
|
|
570
|
+
}
|
|
571
|
+
const offset = labelOffset(last);
|
|
572
|
+
return { x: last.end.x + offset.x, y: last.end.y + offset.y };
|
|
573
|
+
}
|
|
574
|
+
function nonZeroSegments(points) {
|
|
575
|
+
const segments = [];
|
|
576
|
+
for (let index = 0; index < points.length - 1; index += 1) {
|
|
577
|
+
const start = points[index];
|
|
578
|
+
const end = points[index + 1];
|
|
579
|
+
if (start === void 0 || end === void 0) {
|
|
580
|
+
continue;
|
|
581
|
+
}
|
|
582
|
+
const length = Math.hypot(end.x - start.x, end.y - start.y);
|
|
583
|
+
if (length > 0) {
|
|
584
|
+
segments.push({ start, end, length });
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
return segments;
|
|
409
588
|
}
|
|
410
|
-
function
|
|
589
|
+
function labelOffset(segment) {
|
|
590
|
+
const offset = 10;
|
|
591
|
+
const dx = segment.end.x - segment.start.x;
|
|
592
|
+
const dy = segment.end.y - segment.start.y;
|
|
593
|
+
return {
|
|
594
|
+
x: -dy / segment.length * offset,
|
|
595
|
+
y: dx / segment.length * offset
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
function pathPointsBeforeArrowhead(points) {
|
|
411
599
|
const arrowhead = computeArrowhead(points);
|
|
412
|
-
|
|
600
|
+
const base = {
|
|
601
|
+
x: (arrowhead.left.x + arrowhead.right.x) / 2,
|
|
602
|
+
y: (arrowhead.left.y + arrowhead.right.y) / 2
|
|
603
|
+
};
|
|
604
|
+
return [...points.slice(0, -1), base];
|
|
605
|
+
}
|
|
606
|
+
function isAbsoluteLabelLayout(labelBox, itemBox) {
|
|
607
|
+
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;
|
|
413
608
|
}
|
|
414
609
|
function shapePoints(shape, box) {
|
|
415
610
|
const left = box.x;
|
|
@@ -1294,20 +1489,33 @@ function isValidDimension(value) {
|
|
|
1294
1489
|
// src/routing/routes.ts
|
|
1295
1490
|
function routeEdge(input) {
|
|
1296
1491
|
const diagnostics = [];
|
|
1492
|
+
const defaultAnchors = defaultAnchorsForGeometry(
|
|
1493
|
+
input.source.box,
|
|
1494
|
+
input.target.box,
|
|
1495
|
+
input.direction
|
|
1496
|
+
);
|
|
1297
1497
|
const source = getEdgePort(
|
|
1298
1498
|
input.source,
|
|
1299
1499
|
input.target.center,
|
|
1300
|
-
input.sourceAnchor
|
|
1500
|
+
input.sourceAnchor ?? defaultAnchors.sourceAnchor
|
|
1301
1501
|
);
|
|
1302
1502
|
const target = getEdgePort(
|
|
1303
1503
|
input.target,
|
|
1304
1504
|
input.source.center,
|
|
1305
|
-
input.targetAnchor
|
|
1505
|
+
input.targetAnchor ?? defaultAnchors.targetAnchor
|
|
1306
1506
|
);
|
|
1307
1507
|
if ((input.kind ?? "orthogonal") === "straight") {
|
|
1308
1508
|
return { points: simplifyRoute([source, target]), diagnostics };
|
|
1309
1509
|
}
|
|
1310
1510
|
const candidates = orthogonalCandidates(source, target, input.direction);
|
|
1511
|
+
candidates.push(
|
|
1512
|
+
...expandedObstacleCandidates(
|
|
1513
|
+
source,
|
|
1514
|
+
target,
|
|
1515
|
+
input.direction,
|
|
1516
|
+
input.obstacles ?? []
|
|
1517
|
+
)
|
|
1518
|
+
);
|
|
1311
1519
|
for (const candidate of candidates) {
|
|
1312
1520
|
if (!routeIntersectsObstacles(candidate, input.obstacles ?? [])) {
|
|
1313
1521
|
return { points: simplifyRoute(candidate), diagnostics };
|
|
@@ -1346,27 +1554,113 @@ function simplifyRoute(points) {
|
|
|
1346
1554
|
function orthogonalCandidates(source, target, direction) {
|
|
1347
1555
|
const midpointX = (source.x + target.x) / 2;
|
|
1348
1556
|
const midpointY = (source.y + target.y) / 2;
|
|
1349
|
-
const candidates = [
|
|
1350
|
-
[source, { x: target.x, y: source.y }, target],
|
|
1351
|
-
[source, { x: source.x, y: target.y }, target]
|
|
1352
|
-
];
|
|
1557
|
+
const candidates = [];
|
|
1353
1558
|
if (direction === "TB" || direction === "BT") {
|
|
1354
1559
|
candidates.push([
|
|
1355
1560
|
source,
|
|
1356
|
-
{ x:
|
|
1357
|
-
{ x:
|
|
1561
|
+
{ x: source.x, y: midpointY },
|
|
1562
|
+
{ x: target.x, y: midpointY },
|
|
1358
1563
|
target
|
|
1359
1564
|
]);
|
|
1360
1565
|
} else {
|
|
1361
1566
|
candidates.push([
|
|
1362
1567
|
source,
|
|
1363
|
-
{ x:
|
|
1364
|
-
{ x:
|
|
1568
|
+
{ x: midpointX, y: source.y },
|
|
1569
|
+
{ x: midpointX, y: target.y },
|
|
1365
1570
|
target
|
|
1366
1571
|
]);
|
|
1367
1572
|
}
|
|
1573
|
+
candidates.push(
|
|
1574
|
+
[source, { x: target.x, y: source.y }, target],
|
|
1575
|
+
[source, { x: source.x, y: target.y }, target]
|
|
1576
|
+
);
|
|
1577
|
+
return candidates;
|
|
1578
|
+
}
|
|
1579
|
+
function defaultSourceAnchor(direction) {
|
|
1580
|
+
switch (direction) {
|
|
1581
|
+
case "LR":
|
|
1582
|
+
return "right";
|
|
1583
|
+
case "RL":
|
|
1584
|
+
return "left";
|
|
1585
|
+
case "TB":
|
|
1586
|
+
return "bottom";
|
|
1587
|
+
case "BT":
|
|
1588
|
+
return "top";
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
function defaultAnchorsForGeometry(source, target, direction) {
|
|
1592
|
+
const dx = target.x + target.width / 2 - (source.x + source.width / 2);
|
|
1593
|
+
const dy = target.y + target.height / 2 - (source.y + source.height / 2);
|
|
1594
|
+
if (Math.abs(dy) > Math.abs(dx)) {
|
|
1595
|
+
return dy >= 0 ? { sourceAnchor: "bottom", targetAnchor: "top" } : { sourceAnchor: "top", targetAnchor: "bottom" };
|
|
1596
|
+
}
|
|
1597
|
+
if (Math.abs(dx) > 0) {
|
|
1598
|
+
return dx >= 0 ? { sourceAnchor: "right", targetAnchor: "left" } : { sourceAnchor: "left", targetAnchor: "right" };
|
|
1599
|
+
}
|
|
1600
|
+
return {
|
|
1601
|
+
sourceAnchor: defaultSourceAnchor(direction),
|
|
1602
|
+
targetAnchor: defaultTargetAnchor(direction)
|
|
1603
|
+
};
|
|
1604
|
+
}
|
|
1605
|
+
function defaultTargetAnchor(direction) {
|
|
1606
|
+
switch (direction) {
|
|
1607
|
+
case "LR":
|
|
1608
|
+
return "left";
|
|
1609
|
+
case "RL":
|
|
1610
|
+
return "right";
|
|
1611
|
+
case "TB":
|
|
1612
|
+
return "top";
|
|
1613
|
+
case "BT":
|
|
1614
|
+
return "bottom";
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
function expandedObstacleCandidates(source, target, direction, obstacles) {
|
|
1618
|
+
if (obstacles.length === 0) {
|
|
1619
|
+
return [];
|
|
1620
|
+
}
|
|
1621
|
+
const margin = 16;
|
|
1622
|
+
const candidates = [];
|
|
1623
|
+
if (direction === "TB" || direction === "BT") {
|
|
1624
|
+
const lanes = sortedUniqueLanes(
|
|
1625
|
+
obstacles.flatMap((obstacle) => [
|
|
1626
|
+
obstacle.x - margin,
|
|
1627
|
+
obstacle.x + obstacle.width + margin
|
|
1628
|
+
]),
|
|
1629
|
+
(source.x + target.x) / 2
|
|
1630
|
+
);
|
|
1631
|
+
for (const laneX of lanes) {
|
|
1632
|
+
candidates.push([
|
|
1633
|
+
source,
|
|
1634
|
+
{ x: laneX, y: source.y },
|
|
1635
|
+
{ x: laneX, y: target.y },
|
|
1636
|
+
target
|
|
1637
|
+
]);
|
|
1638
|
+
}
|
|
1639
|
+
} else {
|
|
1640
|
+
const lanes = sortedUniqueLanes(
|
|
1641
|
+
obstacles.flatMap((obstacle) => [
|
|
1642
|
+
obstacle.y - margin,
|
|
1643
|
+
obstacle.y + obstacle.height + margin
|
|
1644
|
+
]),
|
|
1645
|
+
(source.y + target.y) / 2
|
|
1646
|
+
);
|
|
1647
|
+
for (const laneY of lanes) {
|
|
1648
|
+
candidates.push([
|
|
1649
|
+
source,
|
|
1650
|
+
{ x: source.x, y: laneY },
|
|
1651
|
+
{ x: target.x, y: laneY },
|
|
1652
|
+
target
|
|
1653
|
+
]);
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1368
1656
|
return candidates;
|
|
1369
1657
|
}
|
|
1658
|
+
function sortedUniqueLanes(lanes, midpoint) {
|
|
1659
|
+
return [...new Set(lanes)].filter((lane) => Number.isFinite(lane)).sort((left, right) => {
|
|
1660
|
+
const distance = Math.abs(left - midpoint) - Math.abs(right - midpoint);
|
|
1661
|
+
return distance === 0 ? left - right : distance;
|
|
1662
|
+
});
|
|
1663
|
+
}
|
|
1370
1664
|
function routeIntersectsObstacles(points, obstacles) {
|
|
1371
1665
|
for (let index = 0; index < points.length - 1; index += 1) {
|
|
1372
1666
|
const a = points[index];
|
|
@@ -1445,12 +1739,17 @@ function solveDiagram(diagram, options = {}) {
|
|
|
1445
1739
|
options,
|
|
1446
1740
|
diagnostics
|
|
1447
1741
|
);
|
|
1742
|
+
const coordinatedSwimlanes = coordinateSwimlanes(
|
|
1743
|
+
diagram.swimlanes ?? [],
|
|
1744
|
+
constrained.boxes
|
|
1745
|
+
);
|
|
1448
1746
|
const groupBoxes = new Map(
|
|
1449
1747
|
coordinatedGroups.map((group) => [group.id, group.box])
|
|
1450
1748
|
);
|
|
1451
1749
|
const coordinatedEdges = coordinateEdges(
|
|
1452
1750
|
edges,
|
|
1453
1751
|
nodeGeometryById,
|
|
1752
|
+
coordinatedNodes,
|
|
1454
1753
|
[...nodeGeometryById.values()].map((geometry) => geometry.obstacleBox),
|
|
1455
1754
|
diagram.direction,
|
|
1456
1755
|
options,
|
|
@@ -1458,8 +1757,18 @@ function solveDiagram(diagram, options = {}) {
|
|
|
1458
1757
|
);
|
|
1459
1758
|
const allBoxes = [
|
|
1460
1759
|
...coordinatedNodes.map((node) => node.box),
|
|
1461
|
-
...
|
|
1760
|
+
...coordinatedNodes.flatMap(
|
|
1761
|
+
(node) => (node.ports ?? []).flatMap(
|
|
1762
|
+
(port) => port.label === void 0 ? [port.box] : [port.box, portLabelBox(port)]
|
|
1763
|
+
)
|
|
1764
|
+
),
|
|
1765
|
+
...groupBoxes.values(),
|
|
1766
|
+
...coordinatedSwimlanes.flatMap(
|
|
1767
|
+
(swimlane) => swimlane.box === void 0 ? [] : [swimlane.box]
|
|
1768
|
+
)
|
|
1462
1769
|
];
|
|
1770
|
+
const contentBounds = allBoxes.length === 0 ? { x: 0, y: 0, width: 0, height: 0 } : unionBoxes(allBoxes);
|
|
1771
|
+
const frame = diagram.frame === void 0 ? void 0 : coordinateFrame(diagram.frame, contentBounds);
|
|
1463
1772
|
return {
|
|
1464
1773
|
id: diagram.id,
|
|
1465
1774
|
...diagram.title === void 0 ? {} : { title: diagram.title },
|
|
@@ -1467,8 +1776,10 @@ function solveDiagram(diagram, options = {}) {
|
|
|
1467
1776
|
nodes: coordinatedNodes,
|
|
1468
1777
|
edges: coordinatedEdges,
|
|
1469
1778
|
groups: coordinatedGroups,
|
|
1779
|
+
...coordinatedSwimlanes.length === 0 ? {} : { swimlanes: coordinatedSwimlanes },
|
|
1470
1780
|
diagnostics,
|
|
1471
|
-
bounds:
|
|
1781
|
+
bounds: frame === void 0 ? contentBounds : unionBoxes([contentBounds, frame.box, frame.titleBox]),
|
|
1782
|
+
...frame === void 0 ? {} : { frame },
|
|
1472
1783
|
...diagram.metadata === void 0 ? {} : { metadata: diagram.metadata }
|
|
1473
1784
|
};
|
|
1474
1785
|
}
|
|
@@ -1494,6 +1805,9 @@ function coordinateNodes(nodes, boxes, options, diagnostics) {
|
|
|
1494
1805
|
coordinated.push({
|
|
1495
1806
|
id: node.id,
|
|
1496
1807
|
...node.label === void 0 ? {} : { label: node.label },
|
|
1808
|
+
...node.style === void 0 ? {} : { style: node.style },
|
|
1809
|
+
...node.ports === void 0 ? {} : { ports: coordinatePorts(node, box, options.portShifting) },
|
|
1810
|
+
...node.compartments === void 0 ? {} : { compartments: node.compartments },
|
|
1497
1811
|
...node.labelLayout === void 0 ? {} : { labelLayout: node.labelLayout },
|
|
1498
1812
|
shape: node.shape,
|
|
1499
1813
|
...node.metadata === void 0 ? {} : { metadata: node.metadata },
|
|
@@ -1504,6 +1818,142 @@ function coordinateNodes(nodes, boxes, options, diagnostics) {
|
|
|
1504
1818
|
}
|
|
1505
1819
|
return coordinated;
|
|
1506
1820
|
}
|
|
1821
|
+
function coordinatePorts(node, nodeBox, portShifting) {
|
|
1822
|
+
const portsBySide = /* @__PURE__ */ new Map();
|
|
1823
|
+
for (const port of node.ports ?? []) {
|
|
1824
|
+
const ports = portsBySide.get(port.side) ?? [];
|
|
1825
|
+
ports.push(port);
|
|
1826
|
+
portsBySide.set(port.side, ports);
|
|
1827
|
+
}
|
|
1828
|
+
const coordinated = [];
|
|
1829
|
+
for (const [side, ports] of portsBySide) {
|
|
1830
|
+
const sorted = [...ports ?? []].sort((a, b) => {
|
|
1831
|
+
const order = (a.order ?? 0) - (b.order ?? 0);
|
|
1832
|
+
return order === 0 ? a.id.localeCompare(b.id) : order;
|
|
1833
|
+
});
|
|
1834
|
+
for (let index = 0; index < sorted.length; index += 1) {
|
|
1835
|
+
const port = sorted[index];
|
|
1836
|
+
if (port === void 0) {
|
|
1837
|
+
continue;
|
|
1838
|
+
}
|
|
1839
|
+
const anchor = portAnchor(
|
|
1840
|
+
nodeBox,
|
|
1841
|
+
side,
|
|
1842
|
+
index,
|
|
1843
|
+
sorted.length,
|
|
1844
|
+
portShifting
|
|
1845
|
+
);
|
|
1846
|
+
const box = portBox(anchor);
|
|
1847
|
+
coordinated.push({ ...port, box, anchor });
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
return coordinated.sort((a, b) => a.id.localeCompare(b.id));
|
|
1851
|
+
}
|
|
1852
|
+
function portAnchor(nodeBox, side, index, count, portShifting) {
|
|
1853
|
+
const shiftingEnabled = portShifting?.enabled ?? true;
|
|
1854
|
+
const spacing = portShifting?.spacing ?? 24;
|
|
1855
|
+
const centeredOffset = shiftingEnabled ? (index - (count - 1) / 2) * spacing : 0;
|
|
1856
|
+
switch (side) {
|
|
1857
|
+
case "left":
|
|
1858
|
+
return {
|
|
1859
|
+
x: nodeBox.x,
|
|
1860
|
+
y: nodeBox.y + nodeBox.height / 2 + centeredOffset
|
|
1861
|
+
};
|
|
1862
|
+
case "right":
|
|
1863
|
+
return {
|
|
1864
|
+
x: nodeBox.x + nodeBox.width,
|
|
1865
|
+
y: nodeBox.y + nodeBox.height / 2 + centeredOffset
|
|
1866
|
+
};
|
|
1867
|
+
case "top":
|
|
1868
|
+
return {
|
|
1869
|
+
x: nodeBox.x + nodeBox.width / 2 + centeredOffset,
|
|
1870
|
+
y: nodeBox.y
|
|
1871
|
+
};
|
|
1872
|
+
case "bottom":
|
|
1873
|
+
return {
|
|
1874
|
+
x: nodeBox.x + nodeBox.width / 2 + centeredOffset,
|
|
1875
|
+
y: nodeBox.y + nodeBox.height
|
|
1876
|
+
};
|
|
1877
|
+
}
|
|
1878
|
+
}
|
|
1879
|
+
function portBox(anchor) {
|
|
1880
|
+
const size = 10;
|
|
1881
|
+
return {
|
|
1882
|
+
x: anchor.x - size / 2,
|
|
1883
|
+
y: anchor.y - size / 2,
|
|
1884
|
+
width: size,
|
|
1885
|
+
height: size
|
|
1886
|
+
};
|
|
1887
|
+
}
|
|
1888
|
+
function portLabelBox(port) {
|
|
1889
|
+
const textWidth = Math.max(0, (port.label?.text.length ?? 0) * 6);
|
|
1890
|
+
const height = 12;
|
|
1891
|
+
const gap = 8;
|
|
1892
|
+
const x = port.side === "left" ? port.anchor.x - gap - textWidth : port.anchor.x + gap;
|
|
1893
|
+
return {
|
|
1894
|
+
x,
|
|
1895
|
+
y: port.anchor.y - 8 - height,
|
|
1896
|
+
width: textWidth,
|
|
1897
|
+
height
|
|
1898
|
+
};
|
|
1899
|
+
}
|
|
1900
|
+
function coordinateSwimlanes(swimlanes, nodeBoxes) {
|
|
1901
|
+
const titleSize = 28;
|
|
1902
|
+
const padding = 16;
|
|
1903
|
+
return swimlanes.map((swimlane) => {
|
|
1904
|
+
const laneBoxes = swimlane.lanes.flatMap((lane) => {
|
|
1905
|
+
const childBoxes = lane.children.map((child) => nodeBoxes.get(child)).filter((box) => box !== void 0);
|
|
1906
|
+
return childBoxes.length === 0 ? [] : [unionBoxes(childBoxes)];
|
|
1907
|
+
});
|
|
1908
|
+
const laneUnion = laneBoxes.length === 0 ? { x: 0, y: 0, width: 120, height: 80 } : unionBoxes(laneBoxes);
|
|
1909
|
+
const outer = expand(laneUnion, padding, titleSize);
|
|
1910
|
+
const laneCount = Math.max(1, swimlane.lanes.length);
|
|
1911
|
+
const lanes = swimlane.lanes.map((lane, index) => {
|
|
1912
|
+
const box = swimlane.orientation === "vertical" ? {
|
|
1913
|
+
x: outer.x + outer.width / laneCount * index,
|
|
1914
|
+
y: outer.y,
|
|
1915
|
+
width: outer.width / laneCount,
|
|
1916
|
+
height: outer.height
|
|
1917
|
+
} : {
|
|
1918
|
+
x: outer.x,
|
|
1919
|
+
y: outer.y + outer.height / laneCount * index,
|
|
1920
|
+
width: outer.width,
|
|
1921
|
+
height: outer.height / laneCount
|
|
1922
|
+
};
|
|
1923
|
+
return { ...lane, box };
|
|
1924
|
+
});
|
|
1925
|
+
return { ...swimlane, lanes, box: outer };
|
|
1926
|
+
});
|
|
1927
|
+
}
|
|
1928
|
+
function coordinateFrame(frame, contentBounds) {
|
|
1929
|
+
const padding = 32;
|
|
1930
|
+
const titleHeight = 28;
|
|
1931
|
+
const titleWidth = Math.max(180, frame.titleTab.length * 7);
|
|
1932
|
+
const box = {
|
|
1933
|
+
x: contentBounds.x - padding,
|
|
1934
|
+
y: contentBounds.y - padding - titleHeight,
|
|
1935
|
+
width: contentBounds.width + padding * 2,
|
|
1936
|
+
height: contentBounds.height + padding * 2 + titleHeight
|
|
1937
|
+
};
|
|
1938
|
+
return {
|
|
1939
|
+
...frame,
|
|
1940
|
+
box,
|
|
1941
|
+
titleBox: {
|
|
1942
|
+
x: box.x,
|
|
1943
|
+
y: box.y,
|
|
1944
|
+
width: Math.min(titleWidth, box.width * 0.8),
|
|
1945
|
+
height: titleHeight
|
|
1946
|
+
}
|
|
1947
|
+
};
|
|
1948
|
+
}
|
|
1949
|
+
function expand(box, padding, titleSize) {
|
|
1950
|
+
return {
|
|
1951
|
+
x: box.x - padding,
|
|
1952
|
+
y: box.y - padding - titleSize,
|
|
1953
|
+
width: box.width + padding * 2,
|
|
1954
|
+
height: box.height + padding * 2 + titleSize
|
|
1955
|
+
};
|
|
1956
|
+
}
|
|
1507
1957
|
function coordinateGroups(groups, nodeBoxes, options, diagnostics) {
|
|
1508
1958
|
const coordinated = [];
|
|
1509
1959
|
const groupBoxes = /* @__PURE__ */ new Map();
|
|
@@ -1552,8 +2002,11 @@ function coordinateGroups(groups, nodeBoxes, options, diagnostics) {
|
|
|
1552
2002
|
}
|
|
1553
2003
|
return coordinated;
|
|
1554
2004
|
}
|
|
1555
|
-
function coordinateEdges(edges, nodes, obstacles, direction, options, diagnostics) {
|
|
2005
|
+
function coordinateEdges(edges, nodes, coordinatedNodes, obstacles, direction, options, diagnostics) {
|
|
1556
2006
|
const coordinated = [];
|
|
2007
|
+
const coordinatedNodeById = new Map(
|
|
2008
|
+
coordinatedNodes.map((node) => [node.id, node])
|
|
2009
|
+
);
|
|
1557
2010
|
for (const edge of edges) {
|
|
1558
2011
|
const source = nodes.get(edge.source.nodeId);
|
|
1559
2012
|
const target = nodes.get(edge.target.nodeId);
|
|
@@ -1571,11 +2024,13 @@ function coordinateEdges(edges, nodes, obstacles, direction, options, diagnostic
|
|
|
1571
2024
|
});
|
|
1572
2025
|
continue;
|
|
1573
2026
|
}
|
|
2027
|
+
const sourcePort = coordinatedNodeById.get(edge.source.nodeId)?.ports?.find((port) => port.id === edge.source.portId);
|
|
2028
|
+
const targetPort = coordinatedNodeById.get(edge.target.nodeId)?.ports?.find((port) => port.id === edge.target.portId);
|
|
1574
2029
|
const route = routeEdge({
|
|
1575
2030
|
kind: options.routeKind ?? "orthogonal",
|
|
1576
2031
|
direction,
|
|
1577
|
-
source,
|
|
1578
|
-
target,
|
|
2032
|
+
source: portGeometry(source, sourcePort),
|
|
2033
|
+
target: portGeometry(target, targetPort),
|
|
1579
2034
|
...edge.source.anchor === void 0 ? {} : { sourceAnchor: edge.source.anchor },
|
|
1580
2035
|
...edge.target.anchor === void 0 ? {} : { targetAnchor: edge.target.anchor },
|
|
1581
2036
|
obstacles: obstacles.filter(
|
|
@@ -1595,6 +2050,21 @@ function coordinateEdges(edges, nodes, obstacles, direction, options, diagnostic
|
|
|
1595
2050
|
}
|
|
1596
2051
|
return coordinated;
|
|
1597
2052
|
}
|
|
2053
|
+
function portGeometry(nodeGeometry, port) {
|
|
2054
|
+
if (port === void 0) {
|
|
2055
|
+
return nodeGeometry;
|
|
2056
|
+
}
|
|
2057
|
+
return {
|
|
2058
|
+
...nodeGeometry,
|
|
2059
|
+
box: port.box,
|
|
2060
|
+
center: port.anchor,
|
|
2061
|
+
anchors: nodeGeometry.anchors.map((anchor) => ({
|
|
2062
|
+
name: anchor.name,
|
|
2063
|
+
point: port.anchor
|
|
2064
|
+
})),
|
|
2065
|
+
obstacleBox: port.box
|
|
2066
|
+
};
|
|
2067
|
+
}
|
|
1598
2068
|
function stableById(items) {
|
|
1599
2069
|
return [...items].sort((a, b) => a.id.localeCompare(b.id));
|
|
1600
2070
|
}
|
|
@@ -1624,34 +2094,34 @@ function assertFiniteNonNegative(value, label) {
|
|
|
1624
2094
|
throw new TypeError(`${label} must be a finite non-negative width`);
|
|
1625
2095
|
}
|
|
1626
2096
|
}
|
|
1627
|
-
function validateTextStyle(
|
|
1628
|
-
assertFinitePositive(
|
|
1629
|
-
if (
|
|
1630
|
-
assertFinitePositive(
|
|
2097
|
+
function validateTextStyle(style2) {
|
|
2098
|
+
assertFinitePositive(style2.fontSize, "fontSize");
|
|
2099
|
+
if (style2.lineHeight !== void 0) {
|
|
2100
|
+
assertFinitePositive(style2.lineHeight, "lineHeight");
|
|
1631
2101
|
}
|
|
1632
|
-
if (
|
|
2102
|
+
if (style2.letterSpacing !== void 0 && !Number.isFinite(style2.letterSpacing)) {
|
|
1633
2103
|
throw new TypeError("letterSpacing must be finite");
|
|
1634
2104
|
}
|
|
1635
2105
|
}
|
|
1636
|
-
function resolveLineHeight(
|
|
1637
|
-
validateTextStyle(
|
|
1638
|
-
return
|
|
2106
|
+
function resolveLineHeight(style2) {
|
|
2107
|
+
validateTextStyle(style2);
|
|
2108
|
+
return style2.lineHeight ?? style2.fontSize * 1.2;
|
|
1639
2109
|
}
|
|
1640
|
-
function toCanvasFont(
|
|
1641
|
-
validateTextStyle(
|
|
1642
|
-
const fontStyle =
|
|
1643
|
-
const fontWeight =
|
|
1644
|
-
return `${fontStyle}${fontWeight} ${
|
|
2110
|
+
function toCanvasFont(style2) {
|
|
2111
|
+
validateTextStyle(style2);
|
|
2112
|
+
const fontStyle = style2.fontStyle === "italic" ? "italic " : "";
|
|
2113
|
+
const fontWeight = style2.fontWeight ?? 400;
|
|
2114
|
+
return `${fontStyle}${fontWeight} ${style2.fontSize}px ${style2.fontFamily}`;
|
|
1645
2115
|
}
|
|
1646
2116
|
|
|
1647
2117
|
// src/text/fallback.ts
|
|
1648
2118
|
var DeterministicTextMeasurer = class {
|
|
1649
|
-
prepare(text,
|
|
1650
|
-
validateTextStyle(
|
|
2119
|
+
prepare(text, style2) {
|
|
2120
|
+
validateTextStyle(style2);
|
|
1651
2121
|
return {
|
|
1652
2122
|
text,
|
|
1653
|
-
font: toCanvasFont(
|
|
1654
|
-
style: { ...
|
|
2123
|
+
font: toCanvasFont(style2),
|
|
2124
|
+
style: { ...style2 },
|
|
1655
2125
|
backend: "deterministic"
|
|
1656
2126
|
};
|
|
1657
2127
|
}
|
|
@@ -1710,9 +2180,9 @@ var DeterministicTextMeasurer = class {
|
|
|
1710
2180
|
return output;
|
|
1711
2181
|
}
|
|
1712
2182
|
};
|
|
1713
|
-
function getCharacterWidth(
|
|
1714
|
-
const letterSpacing =
|
|
1715
|
-
return Math.max(0,
|
|
2183
|
+
function getCharacterWidth(style2) {
|
|
2184
|
+
const letterSpacing = style2.letterSpacing ?? 0;
|
|
2185
|
+
return Math.max(0, style2.fontSize * 0.6 + letterSpacing);
|
|
1716
2186
|
}
|
|
1717
2187
|
function createLine(text, width, segmentIndex, start, end) {
|
|
1718
2188
|
return {
|
|
@@ -1733,6 +2203,108 @@ function assertFinitePositiveLineHeight(lineHeight) {
|
|
|
1733
2203
|
throw new TypeError("lineHeight must be finite and positive");
|
|
1734
2204
|
}
|
|
1735
2205
|
}
|
|
2206
|
+
var require2 = createRequire(import.meta.url);
|
|
2207
|
+
function installNodeCanvasRuntime(loadNodeCanvasModule = loadDefaultNodeCanvasModule) {
|
|
2208
|
+
if (typeof globalThis.OffscreenCanvas === "function") {
|
|
2209
|
+
return true;
|
|
2210
|
+
}
|
|
2211
|
+
try {
|
|
2212
|
+
const canvasModule = loadNodeCanvasModule();
|
|
2213
|
+
const { createCanvas } = canvasModule;
|
|
2214
|
+
const NodeOffscreenCanvas = class {
|
|
2215
|
+
canvas;
|
|
2216
|
+
constructor(width, height) {
|
|
2217
|
+
this.canvas = createCanvas(width, height);
|
|
2218
|
+
}
|
|
2219
|
+
getContext(contextId) {
|
|
2220
|
+
return contextId === "2d" ? this.canvas.getContext("2d") : null;
|
|
2221
|
+
}
|
|
2222
|
+
};
|
|
2223
|
+
globalThis.OffscreenCanvas = NodeOffscreenCanvas;
|
|
2224
|
+
return true;
|
|
2225
|
+
} catch {
|
|
2226
|
+
return false;
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
function loadDefaultNodeCanvasModule() {
|
|
2230
|
+
return require2("@napi-rs/canvas");
|
|
2231
|
+
}
|
|
2232
|
+
var RUNTIME_UNAVAILABLE = "text.pretext.runtime-unavailable";
|
|
2233
|
+
function isPretextRuntimeAvailable() {
|
|
2234
|
+
return typeof Intl.Segmenter === "function" && typeof globalThis.OffscreenCanvas === "function";
|
|
2235
|
+
}
|
|
2236
|
+
var PretextTextMeasurer = class {
|
|
2237
|
+
prepare(text, style2) {
|
|
2238
|
+
if (!isPretextRuntimeAvailable()) {
|
|
2239
|
+
throw new TypeError(RUNTIME_UNAVAILABLE);
|
|
2240
|
+
}
|
|
2241
|
+
validateTextStyle(style2);
|
|
2242
|
+
const font = toCanvasFont(style2);
|
|
2243
|
+
const options = {
|
|
2244
|
+
...style2.whiteSpace === void 0 ? {} : { whiteSpace: style2.whiteSpace },
|
|
2245
|
+
...style2.wordBreak === void 0 ? {} : { wordBreak: style2.wordBreak },
|
|
2246
|
+
...style2.letterSpacing === void 0 ? {} : { letterSpacing: style2.letterSpacing }
|
|
2247
|
+
};
|
|
2248
|
+
const prepared = prepareWithSegments(text, font, options);
|
|
2249
|
+
return {
|
|
2250
|
+
text,
|
|
2251
|
+
font,
|
|
2252
|
+
style: { ...style2 },
|
|
2253
|
+
backend: "pretext",
|
|
2254
|
+
pretextPrepared: prepared
|
|
2255
|
+
};
|
|
2256
|
+
}
|
|
2257
|
+
layout(prepared, maxWidth, lineHeight = resolveLineHeight(prepared.style)) {
|
|
2258
|
+
assertFiniteNonNegative(maxWidth, "maxWidth");
|
|
2259
|
+
if (!Number.isFinite(lineHeight) || lineHeight <= 0) {
|
|
2260
|
+
throw new TypeError("lineHeight must be finite and positive");
|
|
2261
|
+
}
|
|
2262
|
+
const result = layoutWithLines(
|
|
2263
|
+
toInternalPrepared(prepared),
|
|
2264
|
+
maxWidth,
|
|
2265
|
+
lineHeight
|
|
2266
|
+
);
|
|
2267
|
+
const width = result.lines.reduce(
|
|
2268
|
+
(current, line) => Math.max(current, line.width),
|
|
2269
|
+
0
|
|
2270
|
+
);
|
|
2271
|
+
return {
|
|
2272
|
+
width,
|
|
2273
|
+
height: result.height,
|
|
2274
|
+
lineHeight,
|
|
2275
|
+
lineCount: result.lineCount,
|
|
2276
|
+
lines: result.lines.map((line) => ({
|
|
2277
|
+
text: line.text,
|
|
2278
|
+
width: line.width,
|
|
2279
|
+
start: {
|
|
2280
|
+
segmentIndex: line.start.segmentIndex,
|
|
2281
|
+
graphemeIndex: line.start.graphemeIndex
|
|
2282
|
+
},
|
|
2283
|
+
end: {
|
|
2284
|
+
segmentIndex: line.end.segmentIndex,
|
|
2285
|
+
graphemeIndex: line.end.graphemeIndex
|
|
2286
|
+
}
|
|
2287
|
+
})),
|
|
2288
|
+
diagnostics: []
|
|
2289
|
+
};
|
|
2290
|
+
}
|
|
2291
|
+
naturalWidth(prepared) {
|
|
2292
|
+
return measureNaturalWidth(toInternalPrepared(prepared));
|
|
2293
|
+
}
|
|
2294
|
+
};
|
|
2295
|
+
function toInternalPrepared(prepared) {
|
|
2296
|
+
if (prepared.backend !== "pretext" || !("pretextPrepared" in prepared)) {
|
|
2297
|
+
throw new TypeError("prepared text was not created by PretextTextMeasurer");
|
|
2298
|
+
}
|
|
2299
|
+
return prepared.pretextPrepared;
|
|
2300
|
+
}
|
|
2301
|
+
|
|
2302
|
+
// src/text/default.ts
|
|
2303
|
+
function createDefaultTextMeasurer(options = {}) {
|
|
2304
|
+
const installRuntime = options.installNodeCanvasRuntime ?? installNodeCanvasRuntime;
|
|
2305
|
+
installRuntime();
|
|
2306
|
+
return isPretextRuntimeAvailable() ? new PretextTextMeasurer() : new DeterministicTextMeasurer();
|
|
2307
|
+
}
|
|
1736
2308
|
|
|
1737
2309
|
// src/labels/fit.ts
|
|
1738
2310
|
function fitLabel(text, options, measurer) {
|
|
@@ -1791,6 +2363,7 @@ function computeLabelLayout(text, options, measurer) {
|
|
|
1791
2363
|
fittedSize,
|
|
1792
2364
|
padding,
|
|
1793
2365
|
font: { ...options.font },
|
|
2366
|
+
textBackend: prepared.backend,
|
|
1794
2367
|
lineHeight,
|
|
1795
2368
|
lines: buildLines(textLayout, contentBox2, lineHeight),
|
|
1796
2369
|
overflow,
|
|
@@ -1885,8 +2458,9 @@ function normalizeDiagramDsl(dslValue, options = {}) {
|
|
|
1885
2458
|
...outputResult(dsl)
|
|
1886
2459
|
};
|
|
1887
2460
|
}
|
|
1888
|
-
const measurer = options.textMeasurer ??
|
|
2461
|
+
const measurer = options.textMeasurer ?? createDefaultTextMeasurer();
|
|
1889
2462
|
const routeKind = dsl.routing?.kind ?? "orthogonal";
|
|
2463
|
+
const portShifting = normalizePortShifting(dsl.routing?.portShifting);
|
|
1890
2464
|
const diagram = {
|
|
1891
2465
|
id: options.id ?? dsl.id ?? "diagram",
|
|
1892
2466
|
...dsl.title === void 0 ? {} : { title: dsl.title },
|
|
@@ -1894,9 +2468,14 @@ function normalizeDiagramDsl(dslValue, options = {}) {
|
|
|
1894
2468
|
nodes: normalizeNodes(dsl, measurer),
|
|
1895
2469
|
edges: normalizeEdges(dsl),
|
|
1896
2470
|
groups: normalizeGroups(dsl, measurer),
|
|
2471
|
+
swimlanes: normalizeSwimlanes(dsl),
|
|
1897
2472
|
constraints: normalizeConstraints(dsl),
|
|
1898
2473
|
diagnostics: [],
|
|
1899
|
-
|
|
2474
|
+
...dsl.frame === void 0 ? {} : { frame: normalizeFrame(dsl.frame) },
|
|
2475
|
+
metadata: {
|
|
2476
|
+
routeKind,
|
|
2477
|
+
...portShifting === void 0 ? {} : { portShifting }
|
|
2478
|
+
}
|
|
1900
2479
|
};
|
|
1901
2480
|
return {
|
|
1902
2481
|
diagram,
|
|
@@ -1904,6 +2483,15 @@ function normalizeDiagramDsl(dslValue, options = {}) {
|
|
|
1904
2483
|
...outputResult(dsl)
|
|
1905
2484
|
};
|
|
1906
2485
|
}
|
|
2486
|
+
function normalizePortShifting(portShifting) {
|
|
2487
|
+
if (portShifting === void 0) {
|
|
2488
|
+
return void 0;
|
|
2489
|
+
}
|
|
2490
|
+
return {
|
|
2491
|
+
...portShifting.enabled === void 0 ? {} : { enabled: portShifting.enabled },
|
|
2492
|
+
...portShifting.spacing === void 0 ? {} : { spacing: portShifting.spacing }
|
|
2493
|
+
};
|
|
2494
|
+
}
|
|
1907
2495
|
function outputResult(dsl) {
|
|
1908
2496
|
return dsl.output?.format === void 0 ? {} : { output: { format: dsl.output.format } };
|
|
1909
2497
|
}
|
|
@@ -1913,15 +2501,24 @@ function normalizeNodes(dsl, measurer) {
|
|
|
1913
2501
|
const label = toLabel(node?.label);
|
|
1914
2502
|
const labelLayout = label === void 0 ? void 0 : fitDslLabel(label, measurer);
|
|
1915
2503
|
const fittedSize = labelLayout?.fittedSize;
|
|
2504
|
+
const nodeCompartments = node?.compartments === void 0 ? void 0 : compartments(node.compartments);
|
|
2505
|
+
const compartmentWidth = nodeCompartments === void 0 ? 0 : compartmentNaturalWidth(id, label, nodeCompartments, measurer);
|
|
1916
2506
|
return {
|
|
1917
2507
|
id,
|
|
1918
2508
|
...label === void 0 ? {} : { label },
|
|
1919
2509
|
shape: node?.shape ?? "rectangle",
|
|
1920
2510
|
...node?.position === void 0 ? {} : { position: point(node.position) },
|
|
2511
|
+
...node?.style === void 0 ? {} : { style: style(node.style) },
|
|
2512
|
+
...node?.ports === void 0 ? {} : { ports: normalizePorts(node.ports) },
|
|
2513
|
+
...nodeCompartments === void 0 ? {} : { compartments: nodeCompartments },
|
|
1921
2514
|
size: {
|
|
1922
|
-
width: Math.max(
|
|
2515
|
+
width: Math.max(
|
|
2516
|
+
DEFAULT_NODE_MIN_SIZE.width,
|
|
2517
|
+
fittedSize?.width ?? 0,
|
|
2518
|
+
compartmentWidth
|
|
2519
|
+
),
|
|
1923
2520
|
height: Math.max(
|
|
1924
|
-
DEFAULT_NODE_MIN_SIZE.height,
|
|
2521
|
+
nodeCompartments === void 0 ? DEFAULT_NODE_MIN_SIZE.height : compartmentHeight(nodeCompartments),
|
|
1925
2522
|
fittedSize?.height ?? 0
|
|
1926
2523
|
)
|
|
1927
2524
|
},
|
|
@@ -1930,11 +2527,42 @@ function normalizeNodes(dsl, measurer) {
|
|
|
1930
2527
|
};
|
|
1931
2528
|
});
|
|
1932
2529
|
}
|
|
2530
|
+
function compartmentHeight(value) {
|
|
2531
|
+
const rowCount = (value.stereotype === void 0 ? 0 : 1) + 1 + (value.properties?.length ?? 0) + (value.constraints?.length ?? 0);
|
|
2532
|
+
const rowHeight = 16;
|
|
2533
|
+
const verticalPadding = 20;
|
|
2534
|
+
return Math.max(
|
|
2535
|
+
DEFAULT_NODE_MIN_SIZE.height,
|
|
2536
|
+
rowCount * rowHeight + verticalPadding
|
|
2537
|
+
);
|
|
2538
|
+
}
|
|
2539
|
+
function compartmentNaturalWidth(id, label, value, measurer) {
|
|
2540
|
+
const rows = compartmentRows(id, label, value);
|
|
2541
|
+
const maxRowWidth = rows.reduce((width, row) => {
|
|
2542
|
+
const prepared = measurer.prepare(row, DEFAULT_FONT);
|
|
2543
|
+
return Math.max(width, measurer.naturalWidth(prepared));
|
|
2544
|
+
}, 0);
|
|
2545
|
+
return Math.ceil(
|
|
2546
|
+
maxRowWidth + DEFAULT_NODE_PADDING.left + DEFAULT_NODE_PADDING.right
|
|
2547
|
+
);
|
|
2548
|
+
}
|
|
2549
|
+
function compartmentRows(id, label, value) {
|
|
2550
|
+
return [
|
|
2551
|
+
...value.stereotype === void 0 ? [] : [value.stereotype],
|
|
2552
|
+
value.name ?? label?.text ?? id,
|
|
2553
|
+
...value.properties ?? [],
|
|
2554
|
+
...value.constraints ?? []
|
|
2555
|
+
];
|
|
2556
|
+
}
|
|
1933
2557
|
function normalizeEdges(dsl) {
|
|
1934
2558
|
const counts = /* @__PURE__ */ new Map();
|
|
1935
2559
|
return (dsl.edges ?? []).map((edge) => {
|
|
1936
|
-
const
|
|
1937
|
-
const
|
|
2560
|
+
const source = typeof edge === "string" ? void 0 : edge.source;
|
|
2561
|
+
const target = typeof edge === "string" ? void 0 : edge.target;
|
|
2562
|
+
const sourceId = typeof edge === "string" ? "" : edge.sourceId ?? endpointNodeId(source) ?? "";
|
|
2563
|
+
const targetId = typeof edge === "string" ? "" : edge.targetId ?? endpointNodeId(target) ?? "";
|
|
2564
|
+
const sourceEndpoint = typeof edge === "string" ? { nodeId: sourceId } : endpoint(source, edge.sourceId);
|
|
2565
|
+
const targetEndpoint = typeof edge === "string" ? { nodeId: targetId } : endpoint(target, edge.targetId);
|
|
1938
2566
|
const baseId = `${sourceId}-${targetId}`;
|
|
1939
2567
|
const count = counts.get(baseId) ?? 0;
|
|
1940
2568
|
counts.set(baseId, count + 1);
|
|
@@ -1942,9 +2570,96 @@ function normalizeEdges(dsl) {
|
|
|
1942
2570
|
const label = typeof edge === "string" ? void 0 : toLabel(edge.label);
|
|
1943
2571
|
return {
|
|
1944
2572
|
id,
|
|
1945
|
-
source:
|
|
1946
|
-
target:
|
|
1947
|
-
...label === void 0 ? {} : { label }
|
|
2573
|
+
source: sourceEndpoint,
|
|
2574
|
+
target: targetEndpoint,
|
|
2575
|
+
...label === void 0 ? {} : { label },
|
|
2576
|
+
...typeof edge === "string" || edge.style === void 0 ? {} : { style: edge.style },
|
|
2577
|
+
...typeof edge === "string" || edge.arrowhead === void 0 ? {} : { arrowhead: edge.arrowhead }
|
|
2578
|
+
};
|
|
2579
|
+
});
|
|
2580
|
+
}
|
|
2581
|
+
function normalizePorts(ports) {
|
|
2582
|
+
return Object.keys(ports ?? {}).sort().map((id) => {
|
|
2583
|
+
const port = ports?.[id];
|
|
2584
|
+
const label = toLabel(port?.label);
|
|
2585
|
+
return {
|
|
2586
|
+
id,
|
|
2587
|
+
...label === void 0 ? {} : { label },
|
|
2588
|
+
side: port?.side ?? "right",
|
|
2589
|
+
kind: port?.kind ?? "proxy",
|
|
2590
|
+
...port?.order === void 0 ? {} : { order: port.order },
|
|
2591
|
+
...port?.style === void 0 ? {} : { style: style(port.style) }
|
|
2592
|
+
};
|
|
2593
|
+
});
|
|
2594
|
+
}
|
|
2595
|
+
function endpoint(value, nodeIdOverride) {
|
|
2596
|
+
if (nodeIdOverride !== void 0) {
|
|
2597
|
+
return {
|
|
2598
|
+
nodeId: nodeIdOverride,
|
|
2599
|
+
...typeof value === "object" && value.node === nodeIdOverride && value.port !== void 0 ? { portId: value.port } : {}
|
|
2600
|
+
};
|
|
2601
|
+
}
|
|
2602
|
+
if (value === void 0) {
|
|
2603
|
+
return { nodeId: "" };
|
|
2604
|
+
}
|
|
2605
|
+
if (typeof value === "string") {
|
|
2606
|
+
return { nodeId: value };
|
|
2607
|
+
}
|
|
2608
|
+
return {
|
|
2609
|
+
nodeId: value.node,
|
|
2610
|
+
...value.port === void 0 ? {} : { portId: value.port }
|
|
2611
|
+
};
|
|
2612
|
+
}
|
|
2613
|
+
function style(value) {
|
|
2614
|
+
return {
|
|
2615
|
+
...value.fill === void 0 ? {} : { fill: value.fill },
|
|
2616
|
+
...value.stroke === void 0 ? {} : { stroke: value.stroke }
|
|
2617
|
+
};
|
|
2618
|
+
}
|
|
2619
|
+
function compartments(value) {
|
|
2620
|
+
return {
|
|
2621
|
+
...value.stereotype === void 0 ? {} : { stereotype: value.stereotype },
|
|
2622
|
+
...value.name === void 0 ? {} : { name: value.name },
|
|
2623
|
+
...value.properties === void 0 ? {} : { properties: value.properties.map(formatCompartmentEntry) },
|
|
2624
|
+
...value.constraints === void 0 ? {} : { constraints: [...value.constraints] }
|
|
2625
|
+
};
|
|
2626
|
+
}
|
|
2627
|
+
function normalizeFrame(frame) {
|
|
2628
|
+
return {
|
|
2629
|
+
kind: frame.kind,
|
|
2630
|
+
...frame.context === void 0 ? {} : { context: frame.context },
|
|
2631
|
+
...frame.name === void 0 ? {} : { name: frame.name },
|
|
2632
|
+
titleTab: frame.titleTab,
|
|
2633
|
+
...frame.style === void 0 ? {} : { style: style(frame.style) }
|
|
2634
|
+
};
|
|
2635
|
+
}
|
|
2636
|
+
function formatCompartmentEntry(value) {
|
|
2637
|
+
if (typeof value === "string") {
|
|
2638
|
+
return value;
|
|
2639
|
+
}
|
|
2640
|
+
const [entry] = Object.entries(value);
|
|
2641
|
+
if (entry === void 0) {
|
|
2642
|
+
return "";
|
|
2643
|
+
}
|
|
2644
|
+
return `${entry[0]}: ${entry[1]}`;
|
|
2645
|
+
}
|
|
2646
|
+
function normalizeSwimlanes(dsl) {
|
|
2647
|
+
return Object.keys(dsl.swimlanes ?? {}).sort().map((id) => {
|
|
2648
|
+
const swimlane = dsl.swimlanes?.[id];
|
|
2649
|
+
const label = toLabel(swimlane?.label);
|
|
2650
|
+
return {
|
|
2651
|
+
id,
|
|
2652
|
+
...label === void 0 ? {} : { label },
|
|
2653
|
+
orientation: swimlane?.orientation ?? "vertical",
|
|
2654
|
+
lanes: Object.keys(swimlane?.lanes ?? {}).sort().map((laneId) => {
|
|
2655
|
+
const lane = swimlane?.lanes[laneId];
|
|
2656
|
+
const laneLabel = toLabel(lane?.label);
|
|
2657
|
+
return {
|
|
2658
|
+
id: laneId,
|
|
2659
|
+
...laneLabel === void 0 ? {} : { label: laneLabel },
|
|
2660
|
+
children: [...lane?.children ?? []]
|
|
2661
|
+
};
|
|
2662
|
+
})
|
|
1948
2663
|
};
|
|
1949
2664
|
});
|
|
1950
2665
|
}
|
|
@@ -2014,18 +2729,37 @@ function validateReferences(dsl) {
|
|
|
2014
2729
|
const diagnostics = [];
|
|
2015
2730
|
const nodeIds = new Set(Object.keys(dsl.nodes));
|
|
2016
2731
|
const groupIds = new Set(Object.keys(dsl.groups ?? {}));
|
|
2732
|
+
const swimlaneLaneIds = new Set(
|
|
2733
|
+
Object.entries(dsl.swimlanes ?? {}).flatMap(
|
|
2734
|
+
([swimlaneId, swimlane]) => Object.keys(swimlane.lanes).map((laneId) => `${swimlaneId}.${laneId}`)
|
|
2735
|
+
)
|
|
2736
|
+
);
|
|
2017
2737
|
(dsl.edges ?? []).forEach((edge, index) => {
|
|
2018
2738
|
if (typeof edge === "string") {
|
|
2019
2739
|
return;
|
|
2020
2740
|
}
|
|
2021
|
-
const sourceId = edge.sourceId ?? edge.source;
|
|
2022
|
-
const targetId = edge.targetId ?? edge.target;
|
|
2741
|
+
const sourceId = edge.sourceId ?? endpointNodeId(edge.source);
|
|
2742
|
+
const targetId = edge.targetId ?? endpointNodeId(edge.target);
|
|
2743
|
+
const sourceEndpoint = endpoint(edge.source, edge.sourceId);
|
|
2744
|
+
const targetEndpoint = endpoint(edge.target, edge.targetId);
|
|
2023
2745
|
if (sourceId !== void 0 && !nodeIds.has(sourceId)) {
|
|
2024
2746
|
diagnostics.push(referenceMissing(["edges", index, "source"], sourceId));
|
|
2025
2747
|
}
|
|
2026
2748
|
if (targetId !== void 0 && !nodeIds.has(targetId)) {
|
|
2027
2749
|
diagnostics.push(referenceMissing(["edges", index, "target"], targetId));
|
|
2028
2750
|
}
|
|
2751
|
+
validateEndpointPort(
|
|
2752
|
+
dsl,
|
|
2753
|
+
sourceEndpoint,
|
|
2754
|
+
["edges", index, "source"],
|
|
2755
|
+
diagnostics
|
|
2756
|
+
);
|
|
2757
|
+
validateEndpointPort(
|
|
2758
|
+
dsl,
|
|
2759
|
+
targetEndpoint,
|
|
2760
|
+
["edges", index, "target"],
|
|
2761
|
+
diagnostics
|
|
2762
|
+
);
|
|
2029
2763
|
});
|
|
2030
2764
|
for (const [groupId, group] of Object.entries(dsl.groups ?? {})) {
|
|
2031
2765
|
(group.nodes ?? []).forEach((nodeId, index) => {
|
|
@@ -2043,6 +2777,27 @@ function validateReferences(dsl) {
|
|
|
2043
2777
|
}
|
|
2044
2778
|
});
|
|
2045
2779
|
}
|
|
2780
|
+
for (const [swimlaneId, swimlane] of Object.entries(dsl.swimlanes ?? {})) {
|
|
2781
|
+
for (const [laneId, lane] of Object.entries(swimlane.lanes)) {
|
|
2782
|
+
(lane.children ?? []).forEach((child, childIndex) => {
|
|
2783
|
+
if (!nodeIds.has(child)) {
|
|
2784
|
+
diagnostics.push(
|
|
2785
|
+
referenceMissing(
|
|
2786
|
+
[
|
|
2787
|
+
"swimlanes",
|
|
2788
|
+
swimlaneId,
|
|
2789
|
+
"lanes",
|
|
2790
|
+
laneId,
|
|
2791
|
+
"children",
|
|
2792
|
+
childIndex
|
|
2793
|
+
],
|
|
2794
|
+
child
|
|
2795
|
+
)
|
|
2796
|
+
);
|
|
2797
|
+
}
|
|
2798
|
+
});
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2046
2801
|
(dsl.constraints ?? []).forEach((constraint, index) => {
|
|
2047
2802
|
switch (constraint.kind) {
|
|
2048
2803
|
case "exact-position": {
|
|
@@ -2086,7 +2841,7 @@ function validateReferences(dsl) {
|
|
|
2086
2841
|
break;
|
|
2087
2842
|
case "containment": {
|
|
2088
2843
|
const container = constraint.containerId ?? constraint.container;
|
|
2089
|
-
if (container !== void 0 && !hasNodeOrGroup(container, nodeIds, groupIds)) {
|
|
2844
|
+
if (container !== void 0 && !hasNodeOrGroup(container, nodeIds, groupIds, swimlaneLaneIds)) {
|
|
2090
2845
|
diagnostics.push(
|
|
2091
2846
|
referenceMissing(["constraints", index, "container"], container)
|
|
2092
2847
|
);
|
|
@@ -2119,8 +2874,23 @@ function referenceMissing(path, id) {
|
|
|
2119
2874
|
hint: "Define the referenced node or group id, or update this reference."
|
|
2120
2875
|
};
|
|
2121
2876
|
}
|
|
2122
|
-
function hasNodeOrGroup(id, nodeIds, groupIds) {
|
|
2123
|
-
return nodeIds.has(id) || groupIds.has(id);
|
|
2877
|
+
function hasNodeOrGroup(id, nodeIds, groupIds, swimlaneLaneIds = /* @__PURE__ */ new Set()) {
|
|
2878
|
+
return nodeIds.has(id) || groupIds.has(id) || swimlaneLaneIds.has(id);
|
|
2879
|
+
}
|
|
2880
|
+
function endpointNodeId(endpointValue) {
|
|
2881
|
+
if (typeof endpointValue === "string" || endpointValue === void 0) {
|
|
2882
|
+
return endpointValue;
|
|
2883
|
+
}
|
|
2884
|
+
return endpointValue.node;
|
|
2885
|
+
}
|
|
2886
|
+
function validateEndpointPort(dsl, endpointValue, path, diagnostics) {
|
|
2887
|
+
if (endpointValue.portId === void 0) {
|
|
2888
|
+
return;
|
|
2889
|
+
}
|
|
2890
|
+
const node = dsl.nodes[endpointValue.nodeId];
|
|
2891
|
+
if (node !== void 0 && node.ports?.[endpointValue.portId] === void 0) {
|
|
2892
|
+
diagnostics.push(referenceMissing([...path, "port"], endpointValue.portId));
|
|
2893
|
+
}
|
|
2124
2894
|
}
|
|
2125
2895
|
function toLabel(value) {
|
|
2126
2896
|
if (value === void 0) {
|
|
@@ -2190,6 +2960,8 @@ function isValidEdgeId(value) {
|
|
|
2190
2960
|
var directionSchema = z.enum(["TB", "LR", "BT", "RL"]);
|
|
2191
2961
|
var routeKindSchema = z.enum(["orthogonal", "straight"]);
|
|
2192
2962
|
var outputFormatSchema = z.enum(["svg", "excalidraw"]);
|
|
2963
|
+
var edgeStrokeStyleSchema = z.enum(["solid", "dashed"]);
|
|
2964
|
+
var edgeArrowheadSchema = z.enum(["triangle", "hollowTriangle"]);
|
|
2193
2965
|
var nodeShapeSchema = z.enum([
|
|
2194
2966
|
"rectangle",
|
|
2195
2967
|
"rounded-rectangle",
|
|
@@ -2217,18 +2989,49 @@ var labelSchema = z.union([
|
|
|
2217
2989
|
maxWidth: finiteNumberSchema.optional()
|
|
2218
2990
|
})
|
|
2219
2991
|
]);
|
|
2992
|
+
var styleSchema = z.object({
|
|
2993
|
+
fill: z.string().optional(),
|
|
2994
|
+
stroke: z.string().optional()
|
|
2995
|
+
});
|
|
2996
|
+
var portSideSchema = z.enum(["top", "right", "bottom", "left"]);
|
|
2997
|
+
var portKindSchema = z.enum(["proxy", "flow"]);
|
|
2998
|
+
var portSchema = z.object({
|
|
2999
|
+
label: labelSchema.optional(),
|
|
3000
|
+
side: portSideSchema,
|
|
3001
|
+
kind: portKindSchema.optional(),
|
|
3002
|
+
order: finiteNumberSchema.optional(),
|
|
3003
|
+
style: styleSchema.optional()
|
|
3004
|
+
});
|
|
3005
|
+
var compartmentsSchema = z.object({
|
|
3006
|
+
stereotype: z.string().optional(),
|
|
3007
|
+
name: z.string().optional(),
|
|
3008
|
+
properties: z.array(z.record(z.string(), z.string()).or(z.string())).optional(),
|
|
3009
|
+
constraints: z.array(z.string()).optional()
|
|
3010
|
+
});
|
|
2220
3011
|
var nodeSchema = z.object({
|
|
2221
3012
|
label: labelSchema.optional(),
|
|
2222
3013
|
shape: nodeShapeSchema.optional(),
|
|
2223
|
-
position: pointSchema.optional()
|
|
3014
|
+
position: pointSchema.optional(),
|
|
3015
|
+
style: styleSchema.optional(),
|
|
3016
|
+
ports: z.record(z.string(), portSchema).optional(),
|
|
3017
|
+
compartments: compartmentsSchema.optional()
|
|
2224
3018
|
});
|
|
3019
|
+
var endpointSchema = z.union([
|
|
3020
|
+
z.string(),
|
|
3021
|
+
z.object({
|
|
3022
|
+
node: z.string(),
|
|
3023
|
+
port: z.string().optional()
|
|
3024
|
+
})
|
|
3025
|
+
]);
|
|
2225
3026
|
var structuredEdgeSchema = z.object({
|
|
2226
3027
|
id: z.string().optional(),
|
|
2227
|
-
source:
|
|
2228
|
-
target:
|
|
3028
|
+
source: endpointSchema.optional(),
|
|
3029
|
+
target: endpointSchema.optional(),
|
|
2229
3030
|
sourceId: z.string().optional(),
|
|
2230
3031
|
targetId: z.string().optional(),
|
|
2231
|
-
label: labelSchema.optional()
|
|
3032
|
+
label: labelSchema.optional(),
|
|
3033
|
+
style: edgeStrokeStyleSchema.optional(),
|
|
3034
|
+
arrowhead: edgeArrowheadSchema.optional()
|
|
2232
3035
|
}).superRefine((edge, context) => {
|
|
2233
3036
|
if (edge.source === void 0 && edge.sourceId === void 0) {
|
|
2234
3037
|
context.addIssue({
|
|
@@ -2252,6 +3055,17 @@ var groupSchema = z.object({
|
|
|
2252
3055
|
groups: z.array(z.string()).optional(),
|
|
2253
3056
|
padding: insetsSchema.optional()
|
|
2254
3057
|
});
|
|
3058
|
+
var swimlaneSchema = z.object({
|
|
3059
|
+
label: labelSchema.optional(),
|
|
3060
|
+
orientation: z.enum(["vertical", "horizontal"]).optional(),
|
|
3061
|
+
lanes: z.record(
|
|
3062
|
+
z.string(),
|
|
3063
|
+
z.object({
|
|
3064
|
+
label: labelSchema.optional(),
|
|
3065
|
+
children: z.array(z.string()).optional()
|
|
3066
|
+
})
|
|
3067
|
+
)
|
|
3068
|
+
});
|
|
2255
3069
|
var exactPositionConstraintSchema = z.object({
|
|
2256
3070
|
kind: z.literal("exact-position"),
|
|
2257
3071
|
target: z.string().optional(),
|
|
@@ -2312,12 +3126,24 @@ var diagramDslSchema = z.object({
|
|
|
2312
3126
|
direction: directionSchema.optional()
|
|
2313
3127
|
}).optional(),
|
|
2314
3128
|
routing: z.object({
|
|
2315
|
-
kind: routeKindSchema.optional()
|
|
3129
|
+
kind: routeKindSchema.optional(),
|
|
3130
|
+
portShifting: z.object({
|
|
3131
|
+
enabled: z.boolean().optional(),
|
|
3132
|
+
spacing: finiteNumberSchema.optional()
|
|
3133
|
+
}).optional()
|
|
2316
3134
|
}).optional(),
|
|
2317
3135
|
nodes: z.record(z.string(), nodeSchema),
|
|
2318
3136
|
edges: z.array(edgeSchema).optional(),
|
|
2319
3137
|
groups: z.record(z.string(), groupSchema).optional(),
|
|
3138
|
+
swimlanes: z.record(z.string(), swimlaneSchema).optional(),
|
|
2320
3139
|
constraints: z.array(constraintSchema).optional(),
|
|
3140
|
+
frame: z.object({
|
|
3141
|
+
kind: z.string(),
|
|
3142
|
+
context: z.string().optional(),
|
|
3143
|
+
name: z.string().optional(),
|
|
3144
|
+
titleTab: z.string(),
|
|
3145
|
+
style: styleSchema.optional()
|
|
3146
|
+
}).optional(),
|
|
2321
3147
|
output: z.object({
|
|
2322
3148
|
format: outputFormatSchema.optional()
|
|
2323
3149
|
}).optional()
|
|
@@ -2514,7 +3340,8 @@ function renderDiagramDsl(source, options = {}) {
|
|
|
2514
3340
|
return { diagnostics };
|
|
2515
3341
|
}
|
|
2516
3342
|
const solved = solveDiagram(normalized.diagram, {
|
|
2517
|
-
routeKind: normalized.diagram.metadata?.routeKind === "straight" ? "straight" : "orthogonal"
|
|
3343
|
+
routeKind: normalized.diagram.metadata?.routeKind === "straight" ? "straight" : "orthogonal",
|
|
3344
|
+
...solvePortShiftingOption(normalized.diagram.metadata?.portShifting)
|
|
2518
3345
|
});
|
|
2519
3346
|
const solveDiagnostics = solved.diagnostics.map(toSolveDiagnostic);
|
|
2520
3347
|
if (hasErrorDiagnostics2(solveDiagnostics)) {
|
|
@@ -2553,6 +3380,22 @@ function renderDiagramDsl(source, options = {}) {
|
|
|
2553
3380
|
function toSolveDiagnostic(diagnostic) {
|
|
2554
3381
|
return { ...diagnostic, layer: "solve" };
|
|
2555
3382
|
}
|
|
3383
|
+
function solvePortShiftingOption(value) {
|
|
3384
|
+
if (!isJsonObject(value)) {
|
|
3385
|
+
return {};
|
|
3386
|
+
}
|
|
3387
|
+
const portShifting = {};
|
|
3388
|
+
if (value.enabled === false) {
|
|
3389
|
+
portShifting.enabled = false;
|
|
3390
|
+
}
|
|
3391
|
+
if (typeof value.spacing === "number") {
|
|
3392
|
+
portShifting.spacing = value.spacing;
|
|
3393
|
+
}
|
|
3394
|
+
return { portShifting };
|
|
3395
|
+
}
|
|
3396
|
+
function isJsonObject(value) {
|
|
3397
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3398
|
+
}
|
|
2556
3399
|
function toExportDiagnostic(diagnostic) {
|
|
2557
3400
|
return { ...diagnostic, layer: "export" };
|
|
2558
3401
|
}
|