@aranzatech/diagrams-bpmn 0.2.15 → 0.3.1

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.
Files changed (75) hide show
  1. package/README.md +34 -4
  2. package/dist/{catalog-xOMF2ifW.d.cts → catalog-DAGDhO-D.d.cts} +1 -1
  3. package/dist/{catalog-CK3_4cOb.d.ts → catalog-Q1QmKLDD.d.ts} +1 -1
  4. package/dist/{chunk-YUE5EM3W.js → chunk-334WN4JZ.js} +276 -107
  5. package/dist/chunk-334WN4JZ.js.map +1 -0
  6. package/dist/chunk-77L6O76M.js +3 -0
  7. package/dist/chunk-77L6O76M.js.map +1 -0
  8. package/dist/{chunk-QSMP34CT.js → chunk-CPFUQM6H.js} +80 -44
  9. package/dist/chunk-CPFUQM6H.js.map +1 -0
  10. package/dist/{chunk-XMVV7FRZ.js → chunk-FFWJA5BV.js} +3 -3
  11. package/dist/{chunk-XMVV7FRZ.js.map → chunk-FFWJA5BV.js.map} +1 -1
  12. package/dist/{chunk-FBTGIYZS.js → chunk-JEGYVEJO.js} +80 -3
  13. package/dist/{chunk-FBTGIYZS.js.map → chunk-JEGYVEJO.js.map} +1 -1
  14. package/dist/chunk-TB6V4S5N.js +104 -0
  15. package/dist/chunk-TB6V4S5N.js.map +1 -0
  16. package/dist/{chunk-HOWK3ZOO.js → chunk-YAYZW45I.js} +379 -16
  17. package/dist/chunk-YAYZW45I.js.map +1 -0
  18. package/dist/edges/index.cjs +78 -42
  19. package/dist/edges/index.cjs.map +1 -1
  20. package/dist/edges/index.js +1 -1
  21. package/dist/elements/index.cjs +78 -0
  22. package/dist/elements/index.cjs.map +1 -1
  23. package/dist/elements/index.d.cts +24 -5
  24. package/dist/elements/index.d.ts +24 -5
  25. package/dist/elements/index.js +1 -1
  26. package/dist/elk-QT7H4252.js +6 -0
  27. package/dist/elk-QT7H4252.js.map +1 -0
  28. package/dist/extensions/index.cjs +108 -0
  29. package/dist/extensions/index.cjs.map +1 -0
  30. package/dist/extensions/index.d.cts +145 -0
  31. package/dist/extensions/index.d.ts +145 -0
  32. package/dist/extensions/index.js +4 -0
  33. package/dist/extensions/index.js.map +1 -0
  34. package/dist/index.cjs +922 -160
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.d.cts +7 -5
  37. package/dist/index.d.ts +7 -5
  38. package/dist/index.js +6 -4
  39. package/dist/index.js.map +1 -1
  40. package/dist/layout/index.cjs +366 -90
  41. package/dist/layout/index.cjs.map +1 -1
  42. package/dist/layout/index.d.cts +4 -3
  43. package/dist/layout/index.d.ts +4 -3
  44. package/dist/layout/index.js +358 -92
  45. package/dist/layout/index.js.map +1 -1
  46. package/dist/modeling/index.cjs +387 -13
  47. package/dist/modeling/index.cjs.map +1 -1
  48. package/dist/modeling/index.d.cts +62 -4
  49. package/dist/modeling/index.d.ts +62 -4
  50. package/dist/modeling/index.js +1 -1
  51. package/dist/types-BX_o95GC.d.cts +40 -0
  52. package/dist/{types-y-ZbX-ff.d.ts → types-BYN4Zuee.d.cts} +15 -1
  53. package/dist/{types-y-ZbX-ff.d.cts → types-BYN4Zuee.d.ts} +15 -1
  54. package/dist/{types-jIDz306Y.d.cts → types-CggktCqr.d.cts} +4 -1
  55. package/dist/types-D7zel9dq.d.ts +40 -0
  56. package/dist/{types-DG5yPKld.d.ts → types-DmDODKlh.d.ts} +4 -1
  57. package/dist/validation/index.cjs +81 -125
  58. package/dist/validation/index.cjs.map +1 -1
  59. package/dist/validation/index.d.cts +22 -5
  60. package/dist/validation/index.d.ts +22 -5
  61. package/dist/validation/index.js +82 -126
  62. package/dist/validation/index.js.map +1 -1
  63. package/dist/xml/index.cjs +319 -49
  64. package/dist/xml/index.cjs.map +1 -1
  65. package/dist/xml/index.d.cts +5 -3
  66. package/dist/xml/index.d.ts +5 -3
  67. package/dist/xml/index.js +2 -1
  68. package/package.json +6 -1
  69. package/dist/chunk-HOWK3ZOO.js.map +0 -1
  70. package/dist/chunk-QSMP34CT.js.map +0 -1
  71. package/dist/chunk-YUE5EM3W.js.map +0 -1
  72. package/dist/elk-FSFIEL6O.js +0 -6
  73. package/dist/elk-FSFIEL6O.js.map +0 -1
  74. package/dist/guards-C70uIY_O.d.cts +0 -16
  75. package/dist/guards-foB6XIfZ.d.ts +0 -16
@@ -706,7 +706,9 @@ function inferBpmnEdgeType(state, sourceId, targetId) {
706
706
  if (source.data.elementType === "Conversation" || source.data.elementType === "SubConversation" || source.data.elementType === "CallConversation" || target.data.elementType === "Conversation" || target.data.elementType === "SubConversation" || target.data.elementType === "CallConversation") {
707
707
  return "conversationLink";
708
708
  }
709
- if (source.parentId && target.parentId && source.parentId !== target.parentId) {
709
+ const sourcePoolId = getAncestorContainerId(state, source, "Pool");
710
+ const targetPoolId = getAncestorContainerId(state, target, "Pool");
711
+ if (sourcePoolId && targetPoolId && sourcePoolId !== targetPoolId) {
710
712
  return "messageFlow";
711
713
  }
712
714
  return "sequenceFlow";
@@ -744,6 +746,14 @@ function canContainBpmnElement(parentType, childType) {
744
746
  }
745
747
  return `${parentType} cannot contain BPMN child elements.`;
746
748
  }
749
+ function getAncestorContainerId(state, node, elementType) {
750
+ let current = node;
751
+ while (current) {
752
+ if (current.data.elementType === elementType) return current.id;
753
+ current = current.parentId ? diagramsCore.getNode(state, current.parentId) : void 0;
754
+ }
755
+ return void 0;
756
+ }
747
757
  function getBpmnNodeSize(node) {
748
758
  return diagramsCore.getNodeSize(node, getBpmnElementSize(node.data.elementType));
749
759
  }
@@ -1301,63 +1311,197 @@ function assignRows(nodes, forwardEdges, columns, gatewayPairs) {
1301
1311
 
1302
1312
  // src/layout/bpmn-custom-layout.ts
1303
1313
  var LANE_LABEL_W = 28;
1304
- var LANE_H_PAD = 20;
1305
- var COL_GAP = 80;
1306
- var ROW_HEIGHT = 80;
1307
- var ROW_GAP = 60;
1308
- var LANE_V_PAD = 50;
1309
- var POOL_H_PAD = 60;
1310
- var POOL_V_GAP = 50;
1311
- var LANE_MIN_H = 160;
1312
- var POOL_MIN_W = 720;
1313
- var POOL_INNER_PAD = 8;
1314
+ var LANE_H_PAD = 40;
1315
+ var COL_GAP = 100;
1316
+ var ROW_HEIGHT = 100;
1317
+ var ROW_GAP = 80;
1318
+ var LANE_V_PAD = 60;
1319
+ var POOL_H_PAD = 80;
1320
+ var POOL_V_GAP = 60;
1321
+ var LANE_MIN_H = 200;
1322
+ var POOL_MIN_W = 840;
1323
+ var POOL_INNER_PAD = 10;
1324
+ var BACK_EDGE_CLEARANCE = 70;
1325
+ var LAYOUT_ARTIFACT_TYPES = /* @__PURE__ */ new Set([
1326
+ "DataObject",
1327
+ "DataObjectReference",
1328
+ "DataInput",
1329
+ "DataOutput",
1330
+ "DataStore",
1331
+ "DataStoreReference",
1332
+ "Annotation",
1333
+ "Group"
1334
+ ]);
1335
+ var COLLAPSED_SUBPROCESS_TYPES = /* @__PURE__ */ new Set([
1336
+ "SubProcess",
1337
+ "Transaction",
1338
+ "EventSubProcess",
1339
+ "AdHocSubProcess"
1340
+ ]);
1314
1341
  function nW(node) {
1315
1342
  return node.width ?? node.measured?.width ?? 120;
1316
1343
  }
1317
1344
  function nH(node) {
1318
1345
  return node.height ?? node.measured?.height ?? 60;
1319
1346
  }
1347
+ var BOUNDARY_SPACING = 10;
1348
+ function repositionBoundaryEvents(boundaryEvents, positionedContent) {
1349
+ if (boundaryEvents.length === 0) return [];
1350
+ const hostById = new Map(positionedContent.map((n) => [n.id, n]));
1351
+ const byHost = /* @__PURE__ */ new Map();
1352
+ for (const be of boundaryEvents) {
1353
+ const hostId = be.data.attachedToRef;
1354
+ if (!hostId) continue;
1355
+ if (!byHost.has(hostId)) byHost.set(hostId, []);
1356
+ byHost.get(hostId).push(be);
1357
+ }
1358
+ const result = [];
1359
+ for (const be of boundaryEvents) {
1360
+ const hostId = be.data.attachedToRef;
1361
+ if (!hostId) {
1362
+ result.push(be);
1363
+ continue;
1364
+ }
1365
+ const host = hostById.get(hostId);
1366
+ if (!host) {
1367
+ result.push(be);
1368
+ continue;
1369
+ }
1370
+ const hostGroup = byHost.get(hostId);
1371
+ const siblingIdx = hostGroup.findIndex((n) => n.id === be.id);
1372
+ const hostW = nW(host);
1373
+ const hostH = nH(host);
1374
+ const beH = nH(be);
1375
+ const totalGroupW = hostGroup.reduce((s, b) => s + nW(b), 0) + (hostGroup.length - 1) * BOUNDARY_SPACING;
1376
+ const groupStartX = host.position.x + hostW / 2 - totalGroupW / 2;
1377
+ const offsetX = hostGroup.slice(0, siblingIdx).reduce((s, b) => s + nW(b) + BOUNDARY_SPACING, 0);
1378
+ result.push({
1379
+ ...be,
1380
+ position: {
1381
+ x: groupStartX + offsetX,
1382
+ y: host.position.y + hostH - beH / 2
1383
+ }
1384
+ });
1385
+ }
1386
+ return result;
1387
+ }
1388
+ var SP_PAD = 48;
1389
+ function layoutSubProcess(children, edges) {
1390
+ if (children.length === 0) {
1391
+ return { children: [], width: 280, height: 160 };
1392
+ }
1393
+ const boundaryEvents = children.filter((n) => n.data.elementType === "BoundaryEvent");
1394
+ const mainChildren = children.filter((n) => n.data.elementType !== "BoundaryEvent");
1395
+ if (mainChildren.length === 0) {
1396
+ return {
1397
+ children: repositionBoundaryEvents(boundaryEvents, []),
1398
+ width: 280,
1399
+ height: 160
1400
+ };
1401
+ }
1402
+ const contentIds = new Set(mainChildren.map((n) => n.id));
1403
+ const seqEdges = extractSeqEdges(edges, contentIds);
1404
+ const backIds = detectBackEdges([...contentIds], seqEdges);
1405
+ const fwdEdges = seqEdges.filter((e) => !backIds.has(e.id));
1406
+ const columns = assignColumns([...contentIds], fwdEdges);
1407
+ const pairs = detectGatewayPairs(mainChildren, fwdEdges);
1408
+ const rows = assignRows(mainChildren, fwdEdges, columns, pairs);
1409
+ const maxCol = Math.max(0, ...[...columns.values()]);
1410
+ const colW = /* @__PURE__ */ new Map();
1411
+ for (let c = 0; c <= maxCol; c++) colW.set(c, 0);
1412
+ for (const node of mainChildren) {
1413
+ const c = columns.get(node.id) ?? 0;
1414
+ colW.set(c, Math.max(colW.get(c) ?? 0, nW(node)));
1415
+ }
1416
+ const colX = /* @__PURE__ */ new Map();
1417
+ let cumX = 0;
1418
+ for (let c = 0; c <= maxCol; c++) {
1419
+ colX.set(c, cumX);
1420
+ cumX += (colW.get(c) ?? 120) + COL_GAP;
1421
+ }
1422
+ const contentW = cumX - COL_GAP;
1423
+ const rowVals = [...rows.values()];
1424
+ const minRow = Math.min(0, ...rowVals);
1425
+ const maxRow = Math.max(0, ...rowVals);
1426
+ const rowCount = maxRow - minRow + 1;
1427
+ const contentH = rowCount * ROW_HEIGHT + Math.max(0, rowCount - 1) * ROW_GAP;
1428
+ const spW = Math.max(280, SP_PAD * 2 + contentW);
1429
+ const spH = Math.max(160, SP_PAD * 2 + contentH);
1430
+ const positionedChildren = mainChildren.map((node) => {
1431
+ const c = columns.get(node.id) ?? 0;
1432
+ const r = rows.get(node.id) ?? 0;
1433
+ const colOffset = (colX.get(c) ?? 0) + (colW.get(c) ?? 120) / 2 - nW(node) / 2;
1434
+ const rowOffset = (r - minRow) * (ROW_HEIGHT + ROW_GAP) + ROW_HEIGHT / 2 - nH(node) / 2;
1435
+ return {
1436
+ ...node,
1437
+ position: { x: SP_PAD + colOffset, y: SP_PAD + rowOffset }
1438
+ };
1439
+ });
1440
+ const positionedBoundaries = repositionBoundaryEvents(boundaryEvents, positionedChildren);
1441
+ return {
1442
+ children: [...positionedChildren, ...positionedBoundaries],
1443
+ width: spW,
1444
+ height: spH
1445
+ };
1446
+ }
1320
1447
  function layoutPool(pool, lanes, content, allEdges) {
1321
- if (content.length === 0) {
1448
+ const boundaryEvents = content.filter((n) => n.data.elementType === "BoundaryEvent");
1449
+ const mainContent = content.filter((n) => n.data.elementType !== "BoundaryEvent");
1450
+ if (mainContent.length === 0) {
1451
+ const positionedBoundaries2 = repositionBoundaryEvents(boundaryEvents, []);
1322
1452
  if (lanes.length === 0) {
1323
- return { nodes: [], width: 240, height: 120 };
1453
+ return { nodes: positionedBoundaries2, width: 300, height: 140 };
1324
1454
  }
1325
1455
  const laneH = LANE_MIN_H;
1326
1456
  const h = POOL_INNER_PAD * 2 + lanes.length * laneH + Math.max(0, lanes.length - 1) * POOL_INNER_PAD;
1327
- const w = POOL_MIN_W;
1328
1457
  return {
1329
- nodes: lanes.map((lane, i) => ({
1330
- ...lane,
1331
- position: { x: POOL_INNER_PAD, y: POOL_INNER_PAD + i * (laneH + POOL_INNER_PAD) },
1332
- width: w - POOL_INNER_PAD * 2,
1333
- height: laneH
1334
- })),
1335
- width: w,
1458
+ nodes: [
1459
+ ...positionedBoundaries2,
1460
+ ...lanes.map((lane, i) => ({
1461
+ ...lane,
1462
+ position: { x: POOL_INNER_PAD, y: POOL_INNER_PAD + i * (laneH + POOL_INNER_PAD) },
1463
+ width: POOL_MIN_W - POOL_INNER_PAD * 2,
1464
+ height: laneH
1465
+ }))
1466
+ ],
1467
+ width: POOL_MIN_W,
1336
1468
  height: h
1337
1469
  };
1338
1470
  }
1339
- const contentIds = new Set(content.map((n) => n.id));
1340
- const seqEdges = extractSeqEdges(allEdges, contentIds);
1341
- const backIds = detectBackEdges([...contentIds], seqEdges);
1342
- const fwdEdges = seqEdges.filter((e) => !backIds.has(e.id));
1343
- const columns = assignColumns([...contentIds], fwdEdges);
1344
- const pairs = detectGatewayPairs(content, fwdEdges);
1345
- const rows = assignRows(content, fwdEdges, columns, pairs);
1346
1471
  const lanePositionsDistinct = lanes.some((l) => Math.abs(l.position.y) > 10);
1347
1472
  const sortedLanes = lanePositionsDistinct ? [...lanes].sort((a, b) => a.position.y - b.position.y) : [...lanes];
1348
1473
  const hasLanes = sortedLanes.length > 0;
1474
+ const laneIdSet = new Set(sortedLanes.map((l) => l.id));
1349
1475
  const nodeLaneId = /* @__PURE__ */ new Map();
1350
- for (const node of content) {
1351
- if (hasLanes && node.parentId && node.parentId !== pool.id) {
1352
- nodeLaneId.set(node.id, node.parentId);
1353
- } else {
1354
- nodeLaneId.set(node.id, "_pool_");
1355
- }
1476
+ for (const node of mainContent) {
1477
+ const lId = hasLanes && node.parentId && laneIdSet.has(node.parentId) ? node.parentId : "_pool_";
1478
+ nodeLaneId.set(node.id, lId);
1479
+ }
1480
+ const contentIds = new Set(mainContent.map((n) => n.id));
1481
+ const seqEdges = extractSeqEdges(allEdges, contentIds);
1482
+ const backIds = detectBackEdges([...contentIds], seqEdges);
1483
+ const fwdEdges = seqEdges.filter((e) => !backIds.has(e.id));
1484
+ const columns = assignColumns([...contentIds], fwdEdges);
1485
+ const rows = /* @__PURE__ */ new Map();
1486
+ const contentByLane = /* @__PURE__ */ new Map();
1487
+ for (const node of mainContent) {
1488
+ const lId = nodeLaneId.get(node.id) ?? "_pool_";
1489
+ if (!contentByLane.has(lId)) contentByLane.set(lId, []);
1490
+ contentByLane.get(lId).push(node);
1491
+ }
1492
+ for (const [, laneNodes] of contentByLane) {
1493
+ const laneNodeIds = new Set(laneNodes.map((n) => n.id));
1494
+ const intraEdges = fwdEdges.filter(
1495
+ (e) => laneNodeIds.has(e.source) && laneNodeIds.has(e.target)
1496
+ );
1497
+ const lanePairs = detectGatewayPairs(laneNodes, intraEdges);
1498
+ const laneRows = assignRows(laneNodes, intraEdges, columns, lanePairs);
1499
+ for (const [id, row] of laneRows) rows.set(id, row);
1356
1500
  }
1357
1501
  const laneIds = hasLanes ? sortedLanes.map((l) => l.id) : ["_pool_"];
1358
1502
  const laneStats = /* @__PURE__ */ new Map();
1359
1503
  for (const laneId of laneIds) {
1360
- const laneRows = content.filter((n) => nodeLaneId.get(n.id) === laneId).map((n) => rows.get(n.id) ?? 0);
1504
+ const laneRows = mainContent.filter((n) => nodeLaneId.get(n.id) === laneId).map((n) => rows.get(n.id) ?? 0);
1361
1505
  if (laneRows.length === 0) {
1362
1506
  laneStats.set(laneId, { minRow: 0, maxRow: 0, rowCount: 1, height: LANE_MIN_H });
1363
1507
  } else {
@@ -1372,7 +1516,7 @@ function layoutPool(pool, lanes, content, allEdges) {
1372
1516
  const maxCol = Math.max(0, ...[...columns.values()]);
1373
1517
  const colW = /* @__PURE__ */ new Map();
1374
1518
  for (let c = 0; c <= maxCol; c++) colW.set(c, 0);
1375
- for (const node of content) {
1519
+ for (const node of mainContent) {
1376
1520
  const c = columns.get(node.id) ?? 0;
1377
1521
  colW.set(c, Math.max(colW.get(c) ?? 0, nW(node)));
1378
1522
  }
@@ -1395,7 +1539,7 @@ function layoutPool(pool, lanes, content, allEdges) {
1395
1539
  if (i < laneIds.length - 1) cumY += POOL_INNER_PAD;
1396
1540
  }
1397
1541
  const poolH = cumY + POOL_INNER_PAD;
1398
- const positionedContent = content.map((node) => {
1542
+ const positionedContent = mainContent.map((node) => {
1399
1543
  const c = columns.get(node.id) ?? 0;
1400
1544
  const r = rows.get(node.id) ?? 0;
1401
1545
  const laneId = nodeLaneId.get(node.id) ?? "_pool_";
@@ -1403,14 +1547,14 @@ function layoutPool(pool, lanes, content, allEdges) {
1403
1547
  const lYOff = laneY.get(laneId) ?? 0;
1404
1548
  const colOffset = (colX.get(c) ?? 0) + (colW.get(c) ?? 120) / 2 - nW(node) / 2;
1405
1549
  const rowOffset = (r - stat.minRow) * (ROW_HEIGHT + ROW_GAP) + ROW_HEIGHT / 2 - nH(node) / 2;
1406
- const isLaneChild = hasLanes && !!node.parentId && node.parentId !== pool.id;
1407
1550
  const contentH_stat = stat.rowCount * ROW_HEIGHT + Math.max(0, stat.rowCount - 1) * ROW_GAP;
1408
1551
  const vertTopOffset = Math.max(LANE_V_PAD, (stat.height - contentH_stat) / 2);
1552
+ const isLaneChild = hasLanes && !!node.parentId && node.parentId !== pool.id;
1409
1553
  const x = isLaneChild ? LANE_LABEL_W + LANE_H_PAD + colOffset : POOL_H_PAD + colOffset;
1410
1554
  const y = isLaneChild ? vertTopOffset + rowOffset : lYOff + vertTopOffset + rowOffset;
1411
1555
  return { ...node, position: { x, y } };
1412
1556
  });
1413
- const NODE_MIN_GAP = 20;
1557
+ const NODE_MIN_GAP = 24;
1414
1558
  const resolvedContent = [...positionedContent];
1415
1559
  for (const laneId of laneIds) {
1416
1560
  const laneNodeIndices = resolvedContent.map((n, i) => ({ n, i })).filter(({ n }) => (nodeLaneId.get(n.id) ?? "_pool_") === laneId).sort((a, b) => a.n.position.x - b.n.position.x);
@@ -1427,30 +1571,24 @@ function layoutPool(pool, lanes, content, allEdges) {
1427
1571
  if (prevRight + NODE_MIN_GAP > curr.position.x) {
1428
1572
  resolvedContent[laneNodeIndices[k].i] = {
1429
1573
  ...curr,
1430
- position: {
1431
- x: prevRight + NODE_MIN_GAP,
1432
- y: curr.position.y
1433
- }
1574
+ position: { x: prevRight + NODE_MIN_GAP, y: curr.position.y }
1434
1575
  };
1435
1576
  }
1436
1577
  }
1437
1578
  }
1579
+ const positionedBoundaries = repositionBoundaryEvents(boundaryEvents, resolvedContent);
1438
1580
  const positionedLanes = hasLanes ? sortedLanes.map((lane) => ({
1439
1581
  ...lane,
1440
- // x: after pool label strip + left inner padding
1441
- // y: laneY already includes top POOL_INNER_PAD offset
1442
1582
  position: { x: POOL_INNER_PAD, y: laneY.get(lane.id) ?? POOL_INNER_PAD },
1443
1583
  width: laneW,
1444
1584
  height: laneStats.get(lane.id)?.height ?? LANE_MIN_H
1445
1585
  })) : [];
1446
1586
  return {
1447
- nodes: [...resolvedContent, ...positionedLanes],
1587
+ nodes: [...resolvedContent, ...positionedBoundaries, ...positionedLanes],
1448
1588
  width: poolW,
1449
1589
  height: poolH
1450
1590
  };
1451
1591
  }
1452
- var BACK_EDGE_CLEARANCE = 50;
1453
- var SAME_ROW_THRESHOLD = 15;
1454
1592
  function absolutePos(nodeId, byId, cache) {
1455
1593
  const cached = cache.get(nodeId);
1456
1594
  if (cached) return cached;
@@ -1469,10 +1607,17 @@ function absolutePos(nodeId, byId, cache) {
1469
1607
  function laneOf(node, laneIds) {
1470
1608
  return node.parentId && laneIds.has(node.parentId) ? node.parentId : void 0;
1471
1609
  }
1610
+ function gapMidX(sAbs, sW, tAbs, tW) {
1611
+ const leftRightEdge = Math.min(sAbs.x + sW, tAbs.x + tW);
1612
+ const rightLeftEdge = Math.max(sAbs.x, tAbs.x);
1613
+ return leftRightEdge < rightLeftEdge ? (leftRightEdge + rightLeftEdge) / 2 : sAbs.x + sW / 2;
1614
+ }
1615
+ var SAME_ROW_THRESHOLD = 15;
1472
1616
  function routeEdges(edges, layoutNodes, backEdgeIds, laneIds, poolIds) {
1473
1617
  const byId = new Map(layoutNodes.map((n) => [n.id, n]));
1474
1618
  const cache = /* @__PURE__ */ new Map();
1475
1619
  const abs = (id) => absolutePos(id, byId, cache);
1620
+ const sortedLanes = [...laneIds].map((id) => byId.get(id)).filter((n) => !!n).sort((a, b) => abs(a.id).y - abs(b.id).y);
1476
1621
  return edges.map((edge) => {
1477
1622
  const edgeType = edge.data?.edgeType ?? edge.type;
1478
1623
  if (edgeType !== "sequenceFlow") {
@@ -1487,11 +1632,22 @@ function routeEdges(edges, layoutNodes, backEdgeIds, laneIds, poolIds) {
1487
1632
  const tAbs = abs(tgt.id);
1488
1633
  const sW = nW(src), sH = nH(src);
1489
1634
  const tW = nW(tgt), tH = nH(tgt);
1490
- const sCX = sAbs.x + sW / 2, sCY = sAbs.y + sH / 2;
1491
- const tCX = tAbs.x + tW / 2, tCY = tAbs.y + tH / 2;
1492
- const srcPool = poolIds.has(src.parentId ?? "") ? src.parentId : poolIds.has(byId.get(src.parentId ?? "")?.parentId ?? "") ? byId.get(src.parentId ?? "")?.parentId : src.parentId;
1493
- const tgtPool = poolIds.has(tgt.parentId ?? "") ? tgt.parentId : poolIds.has(byId.get(tgt.parentId ?? "")?.parentId ?? "") ? byId.get(tgt.parentId ?? "")?.parentId : tgt.parentId;
1494
- if (srcPool !== tgtPool) {
1635
+ const sCX = sAbs.x + sW / 2;
1636
+ const tCX = tAbs.x + tW / 2;
1637
+ const sCY = sAbs.y + sH / 2;
1638
+ const tCY = tAbs.y + tH / 2;
1639
+ const getPool = (nodeId) => {
1640
+ const node = byId.get(nodeId);
1641
+ if (!node) return null;
1642
+ if (poolIds.has(nodeId)) return nodeId;
1643
+ if (node.parentId && poolIds.has(node.parentId)) return node.parentId;
1644
+ if (node.parentId) {
1645
+ const gp = byId.get(node.parentId)?.parentId ?? null;
1646
+ if (gp && poolIds.has(gp)) return gp;
1647
+ }
1648
+ return node.parentId ?? null;
1649
+ };
1650
+ if (getPool(edge.source) !== getPool(edge.target)) {
1495
1651
  const d = { ...edge.data };
1496
1652
  delete d.routingPoints;
1497
1653
  return { ...edge, data: d };
@@ -1501,33 +1657,49 @@ function routeEdges(edges, layoutNodes, backEdgeIds, laneIds, poolIds) {
1501
1657
  const topY = Math.min(sAbs.y, tAbs.y) - BACK_EDGE_CLEARANCE;
1502
1658
  routingPoints = [
1503
1659
  { x: sCX, y: sAbs.y },
1504
- // [0] discarded
1505
1660
  { x: sCX, y: topY },
1506
- // [1] go up
1507
1661
  { x: tCX, y: topY },
1508
- // [2] go left/right
1509
- { x: tCX, y: tAbs.y }
1510
- // [3] discarded
1662
+ { x: tCX, y: tAbs.y + tH }
1511
1663
  ];
1512
1664
  } else if (laneOf(src, laneIds) !== laneOf(tgt, laneIds)) {
1513
- const goingDown = tAbs.y > sAbs.y;
1514
- const sharedX = Math.abs(sCX - tCX) < 10 ? sCX : (sCX + tCX) / 2;
1515
1665
  const srcLane = byId.get(src.parentId ?? "");
1516
1666
  const tgtLane = byId.get(tgt.parentId ?? "");
1517
1667
  if (srcLane && tgtLane && laneIds.has(srcLane.id) && laneIds.has(tgtLane.id)) {
1518
- const srcLaneAbs = abs(srcLane.id);
1519
- const borderY = goingDown ? srcLaneAbs.y + (srcLane.height ?? 160) : srcLaneAbs.y;
1520
- routingPoints = [
1521
- { x: sCX, y: goingDown ? sAbs.y + sH : sAbs.y },
1522
- // [0] discarded
1523
- { x: sharedX, y: goingDown ? sAbs.y + sH : sAbs.y },
1524
- // [1] bend: horizontal exit
1525
- { x: sharedX, y: borderY },
1526
- // [2] bend: lane border
1527
- { x: sharedX, y: goingDown ? tAbs.y : tAbs.y + tH }
1528
- // [3] discarded
1529
- ];
1668
+ const goingDown = tAbs.y > sAbs.y;
1669
+ const sharedX = gapMidX(sAbs, sW, tAbs, tW);
1670
+ const srcLaneIdx = sortedLanes.findIndex((l) => l.id === srcLane.id);
1671
+ const tgtLaneIdx = sortedLanes.findIndex((l) => l.id === tgtLane.id);
1672
+ const pts = [];
1673
+ if (goingDown) {
1674
+ pts.push({ x: sCX, y: sAbs.y + sH });
1675
+ pts.push({ x: sharedX, y: sAbs.y + sH });
1676
+ const fromIdx = Math.min(srcLaneIdx, tgtLaneIdx);
1677
+ const toIdx = Math.max(srcLaneIdx, tgtLaneIdx);
1678
+ for (let i = fromIdx; i < toIdx; i++) {
1679
+ const lane = sortedLanes[i];
1680
+ const laneBot = abs(lane.id).y + (lane.height ?? LANE_MIN_H);
1681
+ pts.push({ x: sharedX, y: laneBot });
1682
+ }
1683
+ const lastY = pts[pts.length - 1].y;
1684
+ pts.push({ x: tCX, y: lastY });
1685
+ pts.push({ x: tCX, y: tAbs.y + tH });
1686
+ } else {
1687
+ pts.push({ x: sCX, y: sAbs.y });
1688
+ pts.push({ x: sharedX, y: sAbs.y });
1689
+ const fromIdx = Math.min(srcLaneIdx, tgtLaneIdx);
1690
+ const toIdx = Math.max(srcLaneIdx, tgtLaneIdx);
1691
+ for (let i = toIdx; i > fromIdx; i--) {
1692
+ const lane = sortedLanes[i];
1693
+ const laneTop = abs(lane.id).y;
1694
+ pts.push({ x: sharedX, y: laneTop });
1695
+ }
1696
+ const lastY = pts[pts.length - 1].y;
1697
+ pts.push({ x: tCX, y: lastY });
1698
+ pts.push({ x: tCX, y: tAbs.y });
1699
+ }
1700
+ routingPoints = pts;
1530
1701
  } else {
1702
+ const sharedX = gapMidX(sAbs, sW, tAbs, tW);
1531
1703
  routingPoints = [
1532
1704
  { x: sCX, y: sCY },
1533
1705
  { x: sharedX, y: sCY },
@@ -1546,39 +1718,108 @@ function routeEdges(edges, layoutNodes, backEdgeIds, laneIds, poolIds) {
1546
1718
  if (exitsTop || exitsBottom) {
1547
1719
  routingPoints = [
1548
1720
  { x: sCX, y: exitsTop ? sAbs.y : sAbs.y + sH },
1549
- // [0] discarded
1550
1721
  { x: sCX, y: tCY },
1551
- // [1] vertical to target row
1552
1722
  { x: tCX, y: tCY },
1553
- // [2] horizontal to target
1554
1723
  { x: tCX, y: exitsTop ? tAbs.y + tH : tAbs.y }
1555
- // [3] discarded
1556
1724
  ];
1557
1725
  } else {
1558
- const goingRight = tAbs.x >= sAbs.x;
1559
- const midX = goingRight ? sAbs.x + sW + COL_GAP / 2 : sAbs.x - COL_GAP / 2;
1726
+ const midX = gapMidX(sAbs, sW, tAbs, tW);
1560
1727
  routingPoints = [
1561
1728
  { x: sAbs.x + sW, y: sCY },
1562
- // [0] discarded
1563
1729
  { x: midX, y: sCY },
1564
- // [1] horizontal exit
1565
1730
  { x: midX, y: tCY },
1566
- // [2] vertical to target row
1567
1731
  { x: tAbs.x, y: tCY }
1568
- // [3] discarded
1569
1732
  ];
1570
1733
  }
1571
1734
  }
1572
1735
  return { ...edge, data: { ...edge.data, routingPoints } };
1573
1736
  });
1574
1737
  }
1738
+ var ARTIFACT_ABOVE_GAP = 16;
1739
+ var ARTIFACT_H_SPACING = 12;
1740
+ function positionArtifacts(artifacts, resultNodes, edges) {
1741
+ if (artifacts.length === 0) return [];
1742
+ const byId = new Map(resultNodes.map((n) => [n.id, n]));
1743
+ const cache = /* @__PURE__ */ new Map();
1744
+ const absPos = (id) => absolutePos(id, byId, cache);
1745
+ const artifactsByNode = /* @__PURE__ */ new Map();
1746
+ const ungrouped = [];
1747
+ for (const artifact of artifacts) {
1748
+ if (artifact.data.elementType === "Group") {
1749
+ ungrouped.push(artifact);
1750
+ continue;
1751
+ }
1752
+ const connEdge = edges.find((e) => {
1753
+ const t = e.data?.edgeType ?? e.type;
1754
+ return (t === "association" || t === "dataAssociation") && (e.source === artifact.id || e.target === artifact.id);
1755
+ });
1756
+ if (!connEdge) {
1757
+ ungrouped.push(artifact);
1758
+ continue;
1759
+ }
1760
+ const connId = connEdge.source === artifact.id ? connEdge.target : connEdge.source;
1761
+ if (!byId.has(connId)) {
1762
+ ungrouped.push(artifact);
1763
+ continue;
1764
+ }
1765
+ if (!artifactsByNode.has(connId)) artifactsByNode.set(connId, []);
1766
+ artifactsByNode.get(connId).push(artifact);
1767
+ }
1768
+ const positioned = [...ungrouped];
1769
+ for (const [connId, arts] of artifactsByNode) {
1770
+ const connNode = byId.get(connId);
1771
+ const connAbsP = absPos(connId);
1772
+ const totalW = arts.reduce((s, a) => s + nW(a), 0) + (arts.length - 1) * ARTIFACT_H_SPACING;
1773
+ const desiredAbsY = connAbsP.y - ARTIFACT_ABOVE_GAP - Math.max(...arts.map(nH));
1774
+ let desiredAbsX = connAbsP.x + nW(connNode) / 2 - totalW / 2;
1775
+ for (const artifact of arts) {
1776
+ const parentAbsP = artifact.parentId ? absPos(artifact.parentId) : { x: 0, y: 0 };
1777
+ positioned.push({
1778
+ ...artifact,
1779
+ position: {
1780
+ x: desiredAbsX - parentAbsP.x,
1781
+ y: desiredAbsY - parentAbsP.y
1782
+ }
1783
+ });
1784
+ desiredAbsX += nW(artifact) + ARTIFACT_H_SPACING;
1785
+ }
1786
+ }
1787
+ return positioned;
1788
+ }
1575
1789
  async function bpmnCustomLayout(nodes, edges) {
1576
- const pools = nodes.filter((n) => n.data.elementType === "Pool");
1577
- const lanes = nodes.filter((n) => n.data.elementType === "Lane");
1578
- const content = nodes.filter((n) => !LAYOUT_CONTAINER_TYPES.has(n.data.elementType));
1790
+ const artifacts = nodes.filter((n) => LAYOUT_ARTIFACT_TYPES.has(n.data.elementType));
1791
+ const mainNodes = nodes.filter((n) => !LAYOUT_ARTIFACT_TYPES.has(n.data.elementType));
1792
+ const pools = mainNodes.filter((n) => n.data.elementType === "Pool");
1793
+ const lanes = mainNodes.filter((n) => n.data.elementType === "Lane");
1794
+ const expandedSubProcesses = mainNodes.filter(
1795
+ (n) => COLLAPSED_SUBPROCESS_TYPES.has(n.data.elementType) && n.data.isExpanded
1796
+ );
1797
+ const subProcessLayouts = /* @__PURE__ */ new Map();
1798
+ const workingMainNodes = [...mainNodes];
1799
+ for (const sp of expandedSubProcesses) {
1800
+ const spChildren = workingMainNodes.filter((n) => n.parentId === sp.id);
1801
+ const result = layoutSubProcess(spChildren, edges);
1802
+ subProcessLayouts.set(sp.id, result);
1803
+ const spIdx = workingMainNodes.findIndex((n) => n.id === sp.id);
1804
+ if (spIdx >= 0) {
1805
+ workingMainNodes[spIdx] = {
1806
+ ...workingMainNodes[spIdx],
1807
+ width: result.width,
1808
+ height: result.height,
1809
+ measured: { width: result.width, height: result.height }
1810
+ };
1811
+ }
1812
+ }
1813
+ const content = workingMainNodes.filter((n) => {
1814
+ if (!LAYOUT_CONTAINER_TYPES.has(n.data.elementType)) return true;
1815
+ if (COLLAPSED_SUBPROCESS_TYPES.has(n.data.elementType)) return true;
1816
+ return false;
1817
+ });
1579
1818
  if (pools.length === 0) {
1580
1819
  const { bpmnElkLayout: bpmnElkLayout2 } = await Promise.resolve().then(() => (init_elk(), elk_exports));
1581
- return bpmnElkLayout2(nodes, edges);
1820
+ const elkResult = await bpmnElkLayout2(nodes.filter((n) => !LAYOUT_ARTIFACT_TYPES.has(n.data.elementType)), edges);
1821
+ const posArtifacts = positionArtifacts(artifacts, elkResult.nodes, edges);
1822
+ return { nodes: [...elkResult.nodes, ...posArtifacts], edges: elkResult.edges };
1582
1823
  }
1583
1824
  const poolIds = new Set(pools.map((p) => p.id));
1584
1825
  const allLaneIds = new Set(lanes.map((l) => l.id));
@@ -1598,7 +1839,10 @@ async function bpmnCustomLayout(nodes, edges) {
1598
1839
  const poolContent = content.filter(
1599
1840
  (n) => n.parentId === pool.id || n.parentId != null && laneIds.has(n.parentId)
1600
1841
  );
1601
- const result = layoutPool(pool, poolLanes, poolContent, edges);
1842
+ const poolBoundaries = workingMainNodes.filter(
1843
+ (n) => n.data.elementType === "BoundaryEvent" && (n.parentId === pool.id || n.parentId != null && laneIds.has(n.parentId))
1844
+ );
1845
+ const result = layoutPool(pool, poolLanes, [...poolContent, ...poolBoundaries], edges);
1602
1846
  resultNodes.push({
1603
1847
  ...pool,
1604
1848
  position: { x: 0, y: stackY },
@@ -1610,12 +1854,44 @@ async function bpmnCustomLayout(nodes, edges) {
1610
1854
  }
1611
1855
  stackY += result.height + POOL_V_GAP;
1612
1856
  }
1857
+ for (const [, spLayout] of subProcessLayouts) {
1858
+ for (const child of spLayout.children) {
1859
+ resultNodes.push(child);
1860
+ }
1861
+ }
1613
1862
  const layoutted = new Set(resultNodes.map((n) => n.id));
1614
- for (const node of nodes) {
1615
- if (!layoutted.has(node.id)) resultNodes.push(node);
1863
+ const freeNodes = workingMainNodes.filter((n) => !layoutted.has(n.id));
1864
+ if (freeNodes.length > 0) {
1865
+ const freeEdges = edges.filter(
1866
+ (e) => freeNodes.some((n) => n.id === e.source || n.id === e.target)
1867
+ );
1868
+ try {
1869
+ const { bpmnElkLayout: bpmnElkLayout2 } = await Promise.resolve().then(() => (init_elk(), elk_exports));
1870
+ const elkFree = await bpmnElkLayout2(freeNodes, freeEdges);
1871
+ const offsetY = stackY;
1872
+ for (const node of elkFree.nodes) {
1873
+ resultNodes.push({
1874
+ ...node,
1875
+ position: { x: node.position.x, y: node.position.y + offsetY }
1876
+ });
1877
+ }
1878
+ const freeEdgeIds = new Set(freeEdges.map((e) => e.id));
1879
+ const elkEdgeMap = new Map(elkFree.edges.map((e) => [e.id, e]));
1880
+ for (let i = 0; i < edges.length; i++) {
1881
+ if (freeEdgeIds.has(edges[i].id) && elkEdgeMap.has(edges[i].id)) {
1882
+ edges[i] = elkEdgeMap.get(edges[i].id);
1883
+ }
1884
+ }
1885
+ } catch {
1886
+ for (const node of freeNodes) resultNodes.push(node);
1887
+ }
1616
1888
  }
1617
1889
  const routedEdges = routeEdges(edges, resultNodes, allBackEdgeIds, allLaneIds, poolIds);
1618
- return { nodes: resultNodes, edges: routedEdges };
1890
+ const positionedArtifacts = positionArtifacts(artifacts, resultNodes, edges);
1891
+ return {
1892
+ nodes: [...resultNodes, ...positionedArtifacts],
1893
+ edges: routedEdges
1894
+ };
1619
1895
  }
1620
1896
 
1621
1897
  // src/layout/index.ts