@aranzatech/diagrams-bpmn 0.3.4 → 0.3.6

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 (55) hide show
  1. package/dist/{catalog-DNIyjHbl.d.ts → catalog-CYZHikuU.d.ts} +1 -1
  2. package/dist/{catalog-DG-sz0VM.d.cts → catalog-C_S9hyyn.d.cts} +1 -1
  3. package/dist/{chunk-CPFUQM6H.js → chunk-EMTO53AN.js} +54 -73
  4. package/dist/chunk-EMTO53AN.js.map +1 -0
  5. package/dist/chunk-G22XQD6H.js +64 -0
  6. package/dist/chunk-G22XQD6H.js.map +1 -0
  7. package/dist/{chunk-NYIYQUGX.js → chunk-OFOTX3LA.js} +124 -20
  8. package/dist/chunk-OFOTX3LA.js.map +1 -0
  9. package/dist/{chunk-YAYZW45I.js → chunk-SRUWPELT.js} +80 -3
  10. package/dist/chunk-SRUWPELT.js.map +1 -0
  11. package/dist/edges/index.cjs +75 -35
  12. package/dist/edges/index.cjs.map +1 -1
  13. package/dist/edges/index.js +2 -1
  14. package/dist/elements/index.d.cts +3 -3
  15. package/dist/elements/index.d.ts +3 -3
  16. package/dist/extensions/index.d.cts +2 -2
  17. package/dist/extensions/index.d.ts +2 -2
  18. package/dist/index.cjs +196 -53
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +4 -4
  21. package/dist/index.d.ts +4 -4
  22. package/dist/index.js +4 -3
  23. package/dist/index.js.map +1 -1
  24. package/dist/layout/index.cjs +1280 -889
  25. package/dist/layout/index.cjs.map +1 -1
  26. package/dist/layout/index.d.cts +4 -4
  27. package/dist/layout/index.d.ts +4 -4
  28. package/dist/layout/index.js +694 -106
  29. package/dist/layout/index.js.map +1 -1
  30. package/dist/modeling/index.cjs +78 -0
  31. package/dist/modeling/index.cjs.map +1 -1
  32. package/dist/modeling/index.d.cts +10 -5
  33. package/dist/modeling/index.d.ts +10 -5
  34. package/dist/modeling/index.js +1 -1
  35. package/dist/{types-nvF59RGF.d.cts → types--x9aoecw.d.cts} +5 -0
  36. package/dist/{types-nvF59RGF.d.ts → types--x9aoecw.d.ts} +5 -0
  37. package/dist/{types-dQUuSnV5.d.cts → types-CrFDTGo9.d.cts} +1 -1
  38. package/dist/{types-CuDL2YGL.d.ts → types-DoPv3m7u.d.ts} +2 -2
  39. package/dist/{types-X5FyP8oS.d.ts → types-DteJykQG.d.ts} +1 -1
  40. package/dist/{types-CDp9kWQ4.d.cts → types-YZ4sj3Ih.d.cts} +2 -2
  41. package/dist/validation/index.d.cts +3 -3
  42. package/dist/validation/index.d.ts +3 -3
  43. package/dist/xml/index.cjs +162 -18
  44. package/dist/xml/index.cjs.map +1 -1
  45. package/dist/xml/index.d.cts +4 -4
  46. package/dist/xml/index.d.ts +4 -4
  47. package/dist/xml/index.js +2 -1
  48. package/package.json +1 -1
  49. package/dist/chunk-CPFUQM6H.js.map +0 -1
  50. package/dist/chunk-FFWJA5BV.js +0 -163
  51. package/dist/chunk-FFWJA5BV.js.map +0 -1
  52. package/dist/chunk-NYIYQUGX.js.map +0 -1
  53. package/dist/chunk-YAYZW45I.js.map +0 -1
  54. package/dist/elk-QT7H4252.js +0 -6
  55. package/dist/elk-QT7H4252.js.map +0 -1
@@ -1,10 +1,166 @@
1
- export { bpmnElkLayout } from '../chunk-FFWJA5BV.js';
2
- import { getBpmnNodeSize } from '../chunk-YAYZW45I.js';
1
+ import { getBpmnNodeSize } from '../chunk-SRUWPELT.js';
3
2
  import '../chunk-RLAJNRF2.js';
4
3
  import '../chunk-L5Z22RLX.js';
5
- import { dagreLayout, applyLayoutResultToDiagram } from '@aranzatech/diagrams-core/layout';
4
+ import { elkLayout, dagreLayout, applyLayoutResultToDiagram } from '@aranzatech/diagrams-core/layout';
6
5
  export { applyLayoutResultToDiagram } from '@aranzatech/diagrams-core/layout';
7
6
 
7
+ var BPMN_CONTAINER_TYPES = /* @__PURE__ */ new Set([
8
+ "Pool",
9
+ "Lane",
10
+ "SubProcess",
11
+ "Transaction",
12
+ "EventSubProcess",
13
+ "AdHocSubProcess"
14
+ ]);
15
+ var CONTAINER_MIN_SIZE = {
16
+ Pool: { w: 720, h: 200 },
17
+ Lane: { w: 600, h: 160 },
18
+ SubProcess: { w: 240, h: 140 },
19
+ Transaction: { w: 240, h: 140 },
20
+ EventSubProcess: { w: 240, h: 140 },
21
+ AdHocSubProcess: { w: 240, h: 140 }
22
+ };
23
+ var CONTAINER_PADDING = {
24
+ Pool: "[top=30,left=60,bottom=30,right=50]",
25
+ Lane: "[top=40,left=70,bottom=40,right=50]",
26
+ SubProcess: "[top=30,left=40,bottom=30,right=40]",
27
+ Transaction: "[top=30,left=40,bottom=30,right=40]",
28
+ EventSubProcess: "[top=30,left=40,bottom=30,right=40]",
29
+ AdHocSubProcess: "[top=30,left=40,bottom=30,right=40]"
30
+ };
31
+ async function bpmnElkLayout(nodes, edges) {
32
+ const poolsWithLanes = /* @__PURE__ */ new Set();
33
+ for (const node of nodes) {
34
+ if (node.data.elementType === "Lane" && node.parentId) {
35
+ poolsWithLanes.add(node.parentId);
36
+ }
37
+ }
38
+ const result = await elkLayout(
39
+ nodes,
40
+ edges,
41
+ {
42
+ direction: "LR",
43
+ getNodeSize: (node) => {
44
+ const bNode = node;
45
+ if (BPMN_CONTAINER_TYPES.has(bNode.data.elementType)) return void 0;
46
+ return getBpmnNodeSize(bNode);
47
+ },
48
+ isContainerNode: (node) => BPMN_CONTAINER_TYPES.has(node.data.elementType),
49
+ getNodeLayoutOptions: (node) => {
50
+ const bNode = node;
51
+ const type = bNode.data.elementType;
52
+ if (!BPMN_CONTAINER_TYPES.has(type)) return void 0;
53
+ const min = CONTAINER_MIN_SIZE[type];
54
+ const isPoolWithLanes = type === "Pool" && poolsWithLanes.has(node.id);
55
+ const dir = isPoolWithLanes ? "DOWN" : "RIGHT";
56
+ return {
57
+ "elk.direction": dir,
58
+ "elk.nodeSize.constraints": "MINIMUM_SIZE",
59
+ // Each container handles its own layout pass so cross-lane edges
60
+ // participate in the internal layout without the global direction
61
+ // forcing Lane containers to align sideways.
62
+ "elk.hierarchyHandling": "INCLUDE_CHILDREN",
63
+ // Lanes within a Pool share a border — zero gap between them.
64
+ // Other containers keep the tighter inner spacing.
65
+ ...isPoolWithLanes ? { "elk.spacing.nodeNode": "0", "elk.spacing.componentComponent": "0" } : {
66
+ "elk.layered.spacing.nodeNodeBetweenLayers": "100",
67
+ "elk.layered.spacing.edgeNodeBetweenLayers": "45",
68
+ "elk.spacing.nodeNode": "50"
69
+ },
70
+ ...min ? { "elk.nodeSize.minimum": `[w=${min.w},h=${min.h}]` } : {},
71
+ ...CONTAINER_PADDING[type] ? { "elk.padding": CONTAINER_PADDING[type] } : {}
72
+ };
73
+ },
74
+ elk: {
75
+ "elk.algorithm": "layered",
76
+ "elk.direction": "RIGHT",
77
+ // INHERIT: each container handles its own INCLUDE_CHILDREN pass so
78
+ // the global RIGHT direction does not force Lane containers sideways.
79
+ "elk.hierarchyHandling": "INHERIT",
80
+ "elk.edgeRouting": "ORTHOGONAL",
81
+ // Global spacing — generous values for visual breathing room.
82
+ "elk.layered.spacing.nodeNodeBetweenLayers": "120",
83
+ "elk.layered.spacing.edgeNodeBetweenLayers": "50",
84
+ "elk.spacing.nodeNode": "70",
85
+ "elk.spacing.componentComponent": "100"
86
+ }
87
+ }
88
+ );
89
+ const nodeMap = new Map(result.nodes.map((n) => [n.id, n]));
90
+ const edgeMap = new Map(result.edges.map((e) => [e.id, e]));
91
+ const COMPONENT_GAP = 100;
92
+ const rootNodeOrder = nodes.filter((n) => !n.parentId).map((n) => n.id);
93
+ const rootHeights = /* @__PURE__ */ new Map();
94
+ for (const id of rootNodeOrder) {
95
+ const laid = nodeMap.get(id);
96
+ const orig = nodes.find((n) => n.id === id);
97
+ rootHeights.set(id, laid?.height ?? orig?.height ?? 200);
98
+ }
99
+ let stackY = 0;
100
+ const rootY = /* @__PURE__ */ new Map();
101
+ for (const id of rootNodeOrder) {
102
+ rootY.set(id, stackY);
103
+ stackY += (rootHeights.get(id) ?? 200) + COMPONENT_GAP;
104
+ }
105
+ const updatedNodes = nodes.map((node) => {
106
+ const laid = nodeMap.get(node.id);
107
+ if (!laid) return node;
108
+ const pos = node.parentId ? laid.position : { x: laid.position.x, y: rootY.get(node.id) ?? laid.position.y };
109
+ return { ...node, position: pos, width: laid.width, height: laid.height };
110
+ });
111
+ const lanesByPool = /* @__PURE__ */ new Map();
112
+ for (const node of updatedNodes) {
113
+ if (node.data.elementType === "Lane" && node.parentId) {
114
+ const arr = lanesByPool.get(node.parentId) ?? [];
115
+ arr.push(node.id);
116
+ lanesByPool.set(node.parentId, arr);
117
+ }
118
+ }
119
+ const nodeIndex = new Map(updatedNodes.map((n, i) => [n.id, i]));
120
+ for (const laneIds of lanesByPool.values()) {
121
+ const widths = laneIds.map((id) => updatedNodes[nodeIndex.get(id)]?.width ?? 0);
122
+ const maxWidth = Math.max(...widths);
123
+ if (maxWidth <= 0) continue;
124
+ for (const id of laneIds) {
125
+ const idx = nodeIndex.get(id);
126
+ if (idx !== void 0) {
127
+ updatedNodes[idx] = { ...updatedNodes[idx], width: maxWidth };
128
+ }
129
+ }
130
+ }
131
+ const nodeRootContainer = /* @__PURE__ */ new Map();
132
+ for (const node of updatedNodes) {
133
+ nodeRootContainer.set(node.id, node.parentId ?? null);
134
+ }
135
+ const getRootContainer = (nodeId) => {
136
+ let current = nodeId;
137
+ const visited = /* @__PURE__ */ new Set();
138
+ while (current) {
139
+ if (visited.has(current)) break;
140
+ visited.add(current);
141
+ const parent = nodeRootContainer.get(current);
142
+ if (!parent) return current;
143
+ current = parent;
144
+ }
145
+ return null;
146
+ };
147
+ const updatedEdges = edges.map((edge) => {
148
+ const laid = edgeMap.get(edge.id);
149
+ if (!laid) return edge;
150
+ const nextData = { ...edge.data };
151
+ const sourceRoot = getRootContainer(edge.source);
152
+ const targetRoot = getRootContainer(edge.target);
153
+ const isCrossPool = sourceRoot !== targetRoot;
154
+ if (!isCrossPool && laid.points && laid.points.length > 0) {
155
+ nextData.routingPoints = laid.points;
156
+ } else {
157
+ delete nextData.routingPoints;
158
+ }
159
+ return { ...edge, data: nextData };
160
+ });
161
+ return { nodes: updatedNodes, edges: updatedEdges };
162
+ }
163
+
8
164
  // src/layout/bpmn-layout-graph.ts
9
165
  var LAYOUT_CONTAINER_TYPES = /* @__PURE__ */ new Set([
10
166
  "Pool",
@@ -22,15 +178,21 @@ var LAYOUT_GATEWAY_TYPES = /* @__PURE__ */ new Set([
22
178
  "ComplexGateway"
23
179
  ]);
24
180
  function detectBackEdges(nodeIds, seqEdges) {
181
+ const sortedIds = [...nodeIds].sort();
25
182
  const backEdgeIds = /* @__PURE__ */ new Set();
26
183
  const state = /* @__PURE__ */ new Map();
27
- for (const id of nodeIds) state.set(id, 0);
184
+ for (const id of sortedIds) state.set(id, 0);
28
185
  const adj = /* @__PURE__ */ new Map();
29
- for (const id of nodeIds) adj.set(id, []);
186
+ for (const id of sortedIds) adj.set(id, []);
30
187
  for (const e of seqEdges) {
31
188
  adj.get(e.source)?.push({ target: e.target, edgeId: e.id });
32
189
  }
33
- for (const startId of nodeIds) {
190
+ for (const list of adj.values()) {
191
+ list.sort(
192
+ (a, b) => a.target < b.target ? -1 : a.target > b.target ? 1 : a.edgeId < b.edgeId ? -1 : a.edgeId > b.edgeId ? 1 : 0
193
+ );
194
+ }
195
+ for (const startId of sortedIds) {
34
196
  if (state.get(startId) !== 0) continue;
35
197
  const stack = [{ id: startId, idx: 0 }];
36
198
  state.set(startId, 1);
@@ -107,9 +269,7 @@ function detectGatewayPairs(nodes, forwardEdges) {
107
269
  }
108
270
  const order = topologicalSort(nodeIds, forwardEdges);
109
271
  const topoPos = new Map(order.map((id, i) => [id, i]));
110
- const splits = nodes.filter(
111
- (n) => LAYOUT_GATEWAY_TYPES.has(n.data.elementType) && (succs.get(n.id)?.length ?? 0) > 1
112
- );
272
+ const splits = nodes.filter((n) => (succs.get(n.id)?.length ?? 0) > 1);
113
273
  for (const split of splits) {
114
274
  const branches = succs.get(split.id) ?? [];
115
275
  if (branches.length < 2) continue;
@@ -161,12 +321,18 @@ function buildHandleMap(edges) {
161
321
  }
162
322
  return map;
163
323
  }
324
+ function branchPullBias(nodeIds, nodeBias) {
325
+ if (!nodeBias) return null;
326
+ let sum = 0;
327
+ for (const id of nodeIds) sum += nodeBias.get(id) ?? 0;
328
+ if (sum === 0) return null;
329
+ return sum > 0 ? 1 : -1;
330
+ }
164
331
  function pickMainBranch(branchData) {
165
332
  if (branchData.length === 0) return void 0;
166
333
  return [...branchData].sort((a, b) => {
167
- const aRight = a.bias === 0 ? 1 : 0;
168
- const bRight = b.bias === 0 ? 1 : 0;
169
- if (aRight !== bRight) return bRight - aRight;
334
+ const rank = (branch) => branch.bias === 0 ? 2 : branch.bias === null ? 1 : 0;
335
+ if (rank(a) !== rank(b)) return rank(b) - rank(a);
170
336
  if (a.nodeIds.length !== b.nodeIds.length) return b.nodeIds.length - a.nodeIds.length;
171
337
  return a.start.localeCompare(b.start);
172
338
  })[0]?.start;
@@ -207,7 +373,7 @@ function assignBranchRowsAroundMain(branchData, splitRow) {
207
373
  }
208
374
  return assigned;
209
375
  }
210
- function assignRows(nodes, forwardEdges, columns, gatewayPairs) {
376
+ function assignRows(nodes, forwardEdges, columns, gatewayPairs, nodeBias) {
211
377
  const rows = new Map(nodes.map((n) => [n.id, 0]));
212
378
  const succs = new Map(nodes.map((n) => [n.id, []]));
213
379
  for (const e of forwardEdges) {
@@ -215,7 +381,7 @@ function assignRows(nodes, forwardEdges, columns, gatewayPairs) {
215
381
  }
216
382
  const handleMap = buildHandleMap(forwardEdges);
217
383
  const sortedPairs = [...gatewayPairs.entries()].sort(
218
- (a, b) => (columns.get(b[0]) ?? 0) - (columns.get(a[0]) ?? 0)
384
+ (a, b) => (columns.get(a[0]) ?? 0) - (columns.get(b[0]) ?? 0)
219
385
  );
220
386
  for (const [splitId, mergeId] of sortedPairs) {
221
387
  const branches = succs.get(splitId) ?? [];
@@ -235,11 +401,14 @@ function assignRows(nodes, forwardEdges, columns, gatewayPairs) {
235
401
  return result;
236
402
  };
237
403
  const splitHandles = handleMap.get(splitId);
238
- const branchData = branches.map((start) => ({
239
- start,
240
- nodeIds: getBranchNodes(start),
241
- bias: handleToRowBias(splitHandles?.get(start) ?? null)
242
- }));
404
+ const branchData = branches.map((start) => {
405
+ const nodeIds = getBranchNodes(start);
406
+ return {
407
+ start,
408
+ nodeIds,
409
+ bias: handleToRowBias(splitHandles?.get(start) ?? null) ?? branchPullBias(nodeIds, nodeBias)
410
+ };
411
+ });
243
412
  const assigned = assignBranchRowsAroundMain(branchData, splitRow);
244
413
  for (const b of branchData) {
245
414
  const row = assigned.get(b.start) ?? splitRow;
@@ -251,7 +420,7 @@ function assignRows(nodes, forwardEdges, columns, gatewayPairs) {
251
420
  }
252
421
  const pairedSplits = new Set(gatewayPairs.keys());
253
422
  const unpairedSplits = nodes.filter(
254
- (n) => LAYOUT_GATEWAY_TYPES.has(n.data.elementType) && !pairedSplits.has(n.id) && (succs.get(n.id)?.length ?? 0) > 1
423
+ (n) => !pairedSplits.has(n.id) && (succs.get(n.id)?.length ?? 0) > 1
255
424
  );
256
425
  for (const split of unpairedSplits) {
257
426
  const branches = succs.get(split.id) ?? [];
@@ -271,11 +440,14 @@ function assignRows(nodes, forwardEdges, columns, gatewayPairs) {
271
440
  return result;
272
441
  };
273
442
  const splitHandles2 = handleMap.get(split.id);
274
- const branchData = branches.map((start) => ({
275
- start,
276
- nodeIds: getAllReachable(start),
277
- bias: handleToRowBias(splitHandles2?.get(start) ?? null)
278
- }));
443
+ const branchData = branches.map((start) => {
444
+ const nodeIds = getAllReachable(start);
445
+ return {
446
+ start,
447
+ nodeIds,
448
+ bias: handleToRowBias(splitHandles2?.get(start) ?? null) ?? branchPullBias(nodeIds, nodeBias)
449
+ };
450
+ });
279
451
  const assigned2 = assignBranchRowsAroundMain(branchData, splitRow);
280
452
  for (const b of branchData) {
281
453
  const row = assigned2.get(b.start) ?? splitRow;
@@ -328,6 +500,17 @@ var LAYOUT_TASK_TYPES = /* @__PURE__ */ new Set([
328
500
  ]);
329
501
  var TASK_LAYOUT_MIN_W = 130;
330
502
  var TASK_LAYOUT_MIN_H = 80;
503
+ var SUBPROCESS_LAYOUT_MIN_W = 160;
504
+ var SUBPROCESS_LAYOUT_MIN_H = 80;
505
+ function layoutMinSize(node) {
506
+ if (LAYOUT_TASK_TYPES.has(node.data.elementType)) {
507
+ return { w: TASK_LAYOUT_MIN_W, h: TASK_LAYOUT_MIN_H };
508
+ }
509
+ if (COLLAPSED_SUBPROCESS_TYPES.has(node.data.elementType) && !node.data.isExpanded) {
510
+ return { w: SUBPROCESS_LAYOUT_MIN_W, h: SUBPROCESS_LAYOUT_MIN_H };
511
+ }
512
+ return null;
513
+ }
331
514
  function nW(node) {
332
515
  return node.width ?? node.measured?.width ?? 120;
333
516
  }
@@ -335,18 +518,68 @@ function nH(node) {
335
518
  return node.height ?? node.measured?.height ?? 60;
336
519
  }
337
520
  function layoutW(node) {
338
- return LAYOUT_TASK_TYPES.has(node.data.elementType) ? Math.max(nW(node), TASK_LAYOUT_MIN_W) : nW(node);
521
+ const min = layoutMinSize(node);
522
+ return min ? Math.max(nW(node), min.w) : nW(node);
339
523
  }
340
524
  function layoutH(node) {
341
- return LAYOUT_TASK_TYPES.has(node.data.elementType) ? Math.max(nH(node), TASK_LAYOUT_MIN_H) : nH(node);
525
+ const min = layoutMinSize(node);
526
+ return min ? Math.max(nH(node), min.h) : nH(node);
527
+ }
528
+ var LABEL_RESERVE_TYPES = /* @__PURE__ */ new Set([
529
+ ...LAYOUT_GATEWAY_TYPES,
530
+ "StartEvent",
531
+ "EndEvent",
532
+ "IntermediateCatchEvent",
533
+ "IntermediateThrowEvent"
534
+ ]);
535
+ var LABEL_CHAR_W = 6.5;
536
+ var LABEL_RESERVE_MAX = 150;
537
+ function columnW(node) {
538
+ const base = layoutW(node);
539
+ if (!LABEL_RESERVE_TYPES.has(node.data.elementType)) return base;
540
+ const label = typeof node.data.label === "string" ? node.data.label : "";
541
+ if (!label) return base;
542
+ const reserve = Math.min(Math.ceil(label.length * LABEL_CHAR_W), LABEL_RESERVE_MAX);
543
+ return Math.max(base, reserve);
342
544
  }
343
545
  function applyLayoutMinSize(node) {
344
- if (!LAYOUT_TASK_TYPES.has(node.data.elementType)) return node;
546
+ if (!layoutMinSize(node)) return node;
345
547
  const w = layoutW(node);
346
548
  const h = layoutH(node);
347
549
  if (w === nW(node) && h === nH(node)) return node;
348
550
  return { ...node, width: w, height: h, measured: { width: w, height: h } };
349
551
  }
552
+ function computeRowMetrics(contextNodes, rows, includeRowZero) {
553
+ const vals = contextNodes.map((n) => rows.get(n.id) ?? 0);
554
+ const base = includeRowZero ? [0, ...vals] : vals;
555
+ const minRow = Math.min(...base);
556
+ const maxRow = Math.max(...base);
557
+ const rowH = /* @__PURE__ */ new Map();
558
+ for (let r = minRow; r <= maxRow; r++) rowH.set(r, ROW_HEIGHT);
559
+ for (const node of contextNodes) {
560
+ const r = rows.get(node.id) ?? 0;
561
+ rowH.set(r, Math.max(rowH.get(r) ?? ROW_HEIGHT, layoutH(node)));
562
+ }
563
+ const rowY = /* @__PURE__ */ new Map();
564
+ let y = 0;
565
+ for (let r = minRow; r <= maxRow; r++) {
566
+ rowY.set(r, y);
567
+ y += (rowH.get(r) ?? ROW_HEIGHT) + ROW_GAP;
568
+ }
569
+ return { minRow, maxRow, rowH, rowY, contentH: y - ROW_GAP };
570
+ }
571
+ function reanchorBoundaryEdges(edges, boundaryEvents, contentIds) {
572
+ if (boundaryEvents.length === 0) return edges;
573
+ const hostByBoundaryId = /* @__PURE__ */ new Map();
574
+ for (const be of boundaryEvents) {
575
+ if (be.data.attachedToRef) hostByBoundaryId.set(be.id, be.data.attachedToRef);
576
+ }
577
+ if (hostByBoundaryId.size === 0) return edges;
578
+ return edges.map((edge) => {
579
+ const host = hostByBoundaryId.get(edge.source);
580
+ return host && contentIds.has(host) ? { ...edge, source: host, sourceHandle: "source-bottom" } : edge;
581
+ });
582
+ }
350
583
  var BOUNDARY_SPACING = 10;
351
584
  function repositionBoundaryEvents(boundaryEvents, positionedContent) {
352
585
  if (boundaryEvents.length === 0) return [];
@@ -403,7 +636,8 @@ function layoutSubProcess(children, edges) {
403
636
  };
404
637
  }
405
638
  const contentIds = new Set(mainChildren.map((n) => n.id));
406
- const seqEdges = extractSeqEdges(edges, contentIds);
639
+ const graphEdges = reanchorBoundaryEdges(edges, boundaryEvents, contentIds);
640
+ const seqEdges = extractSeqEdges(graphEdges, contentIds);
407
641
  const backIds = detectBackEdges([...contentIds], seqEdges);
408
642
  const fwdEdges = seqEdges.filter((e) => !backIds.has(e.id));
409
643
  const columns = assignColumns([...contentIds], fwdEdges);
@@ -414,7 +648,7 @@ function layoutSubProcess(children, edges) {
414
648
  for (let c = 0; c <= maxCol; c++) colW.set(c, 0);
415
649
  for (const node of mainChildren) {
416
650
  const c = columns.get(node.id) ?? 0;
417
- colW.set(c, Math.max(colW.get(c) ?? 0, layoutW(node)));
651
+ colW.set(c, Math.max(colW.get(c) ?? 0, columnW(node)));
418
652
  }
419
653
  const colX = /* @__PURE__ */ new Map();
420
654
  let cumX = 0;
@@ -423,20 +657,16 @@ function layoutSubProcess(children, edges) {
423
657
  cumX += (colW.get(c) ?? 120) + COL_GAP;
424
658
  }
425
659
  const contentW = cumX - COL_GAP;
426
- const rowVals = [...rows.values()];
427
- const minRow = Math.min(0, ...rowVals);
428
- const maxRow = Math.max(0, ...rowVals);
429
- const rowCount = maxRow - minRow + 1;
430
- const contentH = rowCount * ROW_HEIGHT + Math.max(0, rowCount - 1) * ROW_GAP;
660
+ const rowMetrics = computeRowMetrics(mainChildren, rows, true);
431
661
  const spW = Math.max(280, SP_PAD * 2 + contentW);
432
- const spH = Math.max(160, SP_PAD * 2 + contentH);
662
+ const spH = Math.max(160, SP_PAD * 2 + rowMetrics.contentH);
433
663
  const positionedChildren = mainChildren.map((node) => {
434
664
  const c = columns.get(node.id) ?? 0;
435
665
  const r = rows.get(node.id) ?? 0;
436
666
  const lw = layoutW(node);
437
667
  const lh = layoutH(node);
438
668
  const colOffset = (colX.get(c) ?? 0) + (colW.get(c) ?? 120) / 2 - lw / 2;
439
- const rowOffset = (r - minRow) * (ROW_HEIGHT + ROW_GAP) + ROW_HEIGHT / 2 - lh / 2;
669
+ const rowOffset = (rowMetrics.rowY.get(r) ?? 0) + (rowMetrics.rowH.get(r) ?? ROW_HEIGHT) / 2 - lh / 2;
440
670
  return applyLayoutMinSize({ ...node, position: { x: SP_PAD + colOffset, y: SP_PAD + rowOffset } });
441
671
  });
442
672
  const positionedBoundaries = repositionBoundaryEvents(boundaryEvents, positionedChildren);
@@ -479,7 +709,8 @@ function layoutPool(pool, lanes, content, allEdges) {
479
709
  nodeLaneId.set(node.id, lId);
480
710
  }
481
711
  const contentIds = new Set(mainContent.map((n) => n.id));
482
- const seqEdges = extractSeqEdges(allEdges, contentIds);
712
+ const graphEdges = reanchorBoundaryEdges(allEdges, boundaryEvents, contentIds);
713
+ const seqEdges = extractSeqEdges(graphEdges, contentIds);
483
714
  const backIds = detectBackEdges([...contentIds], seqEdges);
484
715
  const fwdEdges = seqEdges.filter((e) => !backIds.has(e.id));
485
716
  const columns = assignColumns([...contentIds], fwdEdges);
@@ -490,47 +721,107 @@ function layoutPool(pool, lanes, content, allEdges) {
490
721
  if (!contentByLane.has(lId)) contentByLane.set(lId, []);
491
722
  contentByLane.get(lId).push(node);
492
723
  }
724
+ const laneStackIndex = /* @__PURE__ */ new Map();
725
+ sortedLanes.forEach((laneNode, index) => laneStackIndex.set(laneNode.id, index));
726
+ laneStackIndex.set("_pool_", sortedLanes.length);
727
+ const crossLanePull = /* @__PURE__ */ new Map();
728
+ for (const e of fwdEdges) {
729
+ const sLane = nodeLaneId.get(e.source) ?? "_pool_";
730
+ const tLane = nodeLaneId.get(e.target) ?? "_pool_";
731
+ if (sLane === tLane) continue;
732
+ const dir = Math.sign((laneStackIndex.get(tLane) ?? 0) - (laneStackIndex.get(sLane) ?? 0));
733
+ crossLanePull.set(e.source, (crossLanePull.get(e.source) ?? 0) + dir);
734
+ crossLanePull.set(e.target, (crossLanePull.get(e.target) ?? 0) - dir);
735
+ }
493
736
  for (const [, laneNodes] of contentByLane) {
494
737
  const laneNodeIds = new Set(laneNodes.map((n) => n.id));
495
738
  const intraEdges = fwdEdges.filter(
496
739
  (e) => laneNodeIds.has(e.source) && laneNodeIds.has(e.target)
497
740
  );
498
741
  const lanePairs = detectGatewayPairs(laneNodes, intraEdges);
499
- const laneRows = assignRows(laneNodes, intraEdges, columns, lanePairs);
742
+ const laneRows = assignRows(laneNodes, intraEdges, columns, lanePairs, crossLanePull);
500
743
  for (const [id, row] of laneRows) rows.set(id, row);
501
744
  }
502
- const laneIds = hasLanes ? sortedLanes.map((l) => l.id) : ["_pool_"];
745
+ const seqTouched = /* @__PURE__ */ new Set();
746
+ for (const e of seqEdges) {
747
+ seqTouched.add(e.source);
748
+ seqTouched.add(e.target);
749
+ }
750
+ for (const [, laneNodes] of contentByLane) {
751
+ const eventSPs = laneNodes.filter(
752
+ (n) => n.data.elementType === "EventSubProcess" && !seqTouched.has(n.id)
753
+ );
754
+ if (eventSPs.length === 0) continue;
755
+ const eventSpIds = new Set(eventSPs.map((n) => n.id));
756
+ const otherRows = laneNodes.filter((n) => !eventSpIds.has(n.id)).map((n) => rows.get(n.id) ?? 0);
757
+ const laneMaxRow = otherRows.length > 0 ? Math.max(...otherRows) : -1;
758
+ for (const sp of eventSPs) rows.set(sp.id, laneMaxRow + 1);
759
+ }
760
+ const hasPoolDirectContent = [...nodeLaneId.values()].includes("_pool_");
761
+ const laneIds = hasLanes ? [...sortedLanes.map((l) => l.id), ...hasPoolDirectContent ? ["_pool_"] : []] : ["_pool_"];
503
762
  const laneStats = /* @__PURE__ */ new Map();
504
763
  for (const laneId of laneIds) {
505
- const laneRows = mainContent.filter((n) => nodeLaneId.get(n.id) === laneId).map((n) => rows.get(n.id) ?? 0);
506
- if (laneRows.length === 0) {
507
- laneStats.set(laneId, { minRow: 0, maxRow: 0, rowCount: 1, height: LANE_MIN_H });
764
+ const laneNodes = mainContent.filter((n) => nodeLaneId.get(n.id) === laneId);
765
+ if (laneNodes.length === 0) {
766
+ laneStats.set(laneId, {
767
+ minRow: 0,
768
+ maxRow: 0,
769
+ height: LANE_MIN_H,
770
+ contentH: ROW_HEIGHT,
771
+ rowH: /* @__PURE__ */ new Map([[0, ROW_HEIGHT]]),
772
+ rowY: /* @__PURE__ */ new Map([[0, 0]])
773
+ });
508
774
  } else {
509
- const minRow = Math.min(...laneRows);
510
- const maxRow = Math.max(...laneRows);
511
- const rowCount = maxRow - minRow + 1;
512
- const contentH = rowCount * ROW_HEIGHT + Math.max(0, rowCount - 1) * ROW_GAP;
513
- const height = Math.max(LANE_MIN_H, LANE_V_PAD * 2 + contentH);
514
- laneStats.set(laneId, { minRow, maxRow, rowCount, height });
775
+ const m = computeRowMetrics(laneNodes, rows, false);
776
+ const height = Math.max(LANE_MIN_H, LANE_V_PAD * 2 + m.contentH);
777
+ laneStats.set(laneId, {
778
+ minRow: m.minRow,
779
+ maxRow: m.maxRow,
780
+ height,
781
+ contentH: m.contentH,
782
+ rowH: m.rowH,
783
+ rowY: m.rowY
784
+ });
515
785
  }
516
786
  }
517
- const maxCol = Math.max(0, ...[...columns.values()]);
518
- const colW = /* @__PURE__ */ new Map();
519
- for (let c = 0; c <= maxCol; c++) colW.set(c, 0);
520
- for (const node of mainContent) {
521
- const c = columns.get(node.id) ?? 0;
522
- colW.set(c, Math.max(colW.get(c) ?? 0, layoutW(node)));
523
- }
524
- const colX = /* @__PURE__ */ new Map();
525
- let cumX = 0;
526
- for (let c = 0; c <= maxCol; c++) {
527
- colX.set(c, cumX);
528
- cumX += (colW.get(c) ?? 120) + COL_GAP;
787
+ const laneColumnLayouts = /* @__PURE__ */ new Map();
788
+ for (const laneId of laneIds) {
789
+ const laneNodes = mainContent.filter(
790
+ (node) => (nodeLaneId.get(node.id) ?? "_pool_") === laneId
791
+ );
792
+ const usedCols = [...new Set(laneNodes.map((node) => columns.get(node.id) ?? 0))].sort((a, b) => a - b);
793
+ const colW = /* @__PURE__ */ new Map();
794
+ for (const col of usedCols) colW.set(col, 0);
795
+ for (const node of laneNodes) {
796
+ const col = columns.get(node.id) ?? 0;
797
+ colW.set(col, Math.max(colW.get(col) ?? 0, columnW(node)));
798
+ }
799
+ const colX = /* @__PURE__ */ new Map();
800
+ let laneCumX = 0;
801
+ for (const col of usedCols) {
802
+ colX.set(col, laneCumX);
803
+ laneCumX += (colW.get(col) ?? 120) + COL_GAP;
804
+ }
805
+ laneColumnLayouts.set(laneId, {
806
+ colW,
807
+ colX,
808
+ contentW: usedCols.length > 0 ? laneCumX - COL_GAP : 0
809
+ });
529
810
  }
530
- const contentW = cumX - COL_GAP;
531
- const innerW = LANE_LABEL_W + LANE_H_PAD + contentW + LANE_H_PAD;
811
+ const baseLaneInnerW = Math.max(
812
+ 0,
813
+ ...laneIds.filter((laneId) => laneId !== "_pool_").map((laneId) => {
814
+ const contentW = laneColumnLayouts.get(laneId)?.contentW ?? 0;
815
+ return LANE_LABEL_W + LANE_H_PAD + contentW + LANE_H_PAD;
816
+ })
817
+ );
818
+ const basePoolInnerW = Math.max(
819
+ 0,
820
+ ...laneIds.filter((laneId) => laneId === "_pool_").map((laneId) => (laneColumnLayouts.get(laneId)?.contentW ?? 0) + POOL_H_PAD * 2)
821
+ );
822
+ const innerW = Math.max(baseLaneInnerW, basePoolInnerW);
532
823
  const poolW = Math.max(POOL_MIN_W, innerW + POOL_INNER_PAD * 2);
533
- const laneW = poolW - POOL_INNER_PAD * 2;
824
+ let laneW = poolW - POOL_INNER_PAD * 2;
534
825
  const laneY = /* @__PURE__ */ new Map();
535
826
  let cumY = POOL_INNER_PAD;
536
827
  for (let i = 0; i < laneIds.length; i++) {
@@ -545,20 +836,106 @@ function layoutPool(pool, lanes, content, allEdges) {
545
836
  const r = rows.get(node.id) ?? 0;
546
837
  const laneId = nodeLaneId.get(node.id) ?? "_pool_";
547
838
  const stat = laneStats.get(laneId);
839
+ const laneLayout = laneColumnLayouts.get(laneId);
548
840
  const lYOff = laneY.get(laneId) ?? 0;
549
841
  const lw = layoutW(node);
550
842
  const lh = layoutH(node);
551
- const colOffset = (colX.get(c) ?? 0) + (colW.get(c) ?? 120) / 2 - lw / 2;
552
- const rowOffset = (r - stat.minRow) * (ROW_HEIGHT + ROW_GAP) + ROW_HEIGHT / 2 - lh / 2;
553
- const contentH_stat = stat.rowCount * ROW_HEIGHT + Math.max(0, stat.rowCount - 1) * ROW_GAP;
554
- const vertTopOffset = Math.max(LANE_V_PAD, (stat.height - contentH_stat) / 2);
843
+ const colOffset = (laneLayout?.colX.get(c) ?? 0) + (laneLayout?.colW.get(c) ?? 120) / 2 - lw / 2;
844
+ const rowOffset = (stat.rowY.get(r) ?? 0) + (stat.rowH.get(r) ?? ROW_HEIGHT) / 2 - lh / 2;
845
+ const vertTopOffset = Math.max(LANE_V_PAD, (stat.height - stat.contentH) / 2);
555
846
  const isLaneChild = hasLanes && !!node.parentId && node.parentId !== pool.id;
556
847
  const x = isLaneChild ? LANE_LABEL_W + LANE_H_PAD + colOffset : POOL_H_PAD + colOffset;
557
848
  const y = isLaneChild ? vertTopOffset + rowOffset : lYOff + vertTopOffset + rowOffset;
558
849
  return applyLayoutMinSize({ ...node, position: { x, y } });
559
850
  });
851
+ const handoffAdjustedContent = [...positionedContent];
852
+ const nodeIndexById = new Map(handoffAdjustedContent.map((node, index) => [node.id, index]));
853
+ const outgoingBySource = /* @__PURE__ */ new Map();
854
+ for (const edge of fwdEdges) {
855
+ if (!outgoingBySource.has(edge.source)) outgoingBySource.set(edge.source, []);
856
+ outgoingBySource.get(edge.source).push(edge);
857
+ }
858
+ const crossLaneEdges = fwdEdges.filter((edge) => {
859
+ const srcLane = nodeLaneId.get(edge.source) ?? "_pool_";
860
+ const tgtLane = nodeLaneId.get(edge.target) ?? "_pool_";
861
+ return srcLane !== tgtLane;
862
+ }).sort(
863
+ (a, b) => (columns.get(a.target) ?? 0) - (columns.get(b.target) ?? 0) || (columns.get(a.source) ?? 0) - (columns.get(b.source) ?? 0)
864
+ );
865
+ for (const edge of crossLaneEdges) {
866
+ const srcIndex = nodeIndexById.get(edge.source);
867
+ const tgtIndex = nodeIndexById.get(edge.target);
868
+ if (srcIndex == null || tgtIndex == null) continue;
869
+ const srcNode = handoffAdjustedContent[srcIndex];
870
+ const tgtNode = handoffAdjustedContent[tgtIndex];
871
+ const tgtLaneId = nodeLaneId.get(tgtNode.id) ?? "_pool_";
872
+ const tgtCol = columns.get(tgtNode.id) ?? 0;
873
+ const siblingTargets = (outgoingBySource.get(edge.source) ?? []).filter((candidate) => (columns.get(candidate.target) ?? 0) === tgtCol).map((candidate) => handoffAdjustedContent[nodeIndexById.get(candidate.target) ?? -1]).filter((node) => !!node);
874
+ const siblingAlignedX = siblingTargets.reduce(
875
+ (max, node) => Math.max(max, node.position.x),
876
+ Number.NEGATIVE_INFINITY
877
+ );
878
+ const minTargetX = Math.max(
879
+ siblingAlignedX,
880
+ srcNode.position.x + layoutW(srcNode) / 2 + layoutW(tgtNode) / 2 + COL_GAP
881
+ );
882
+ if (tgtNode.position.x >= minTargetX) continue;
883
+ const delta = minTargetX - tgtNode.position.x;
884
+ for (let i = 0; i < handoffAdjustedContent.length; i++) {
885
+ const candidate = handoffAdjustedContent[i];
886
+ if ((nodeLaneId.get(candidate.id) ?? "_pool_") !== tgtLaneId) continue;
887
+ if ((columns.get(candidate.id) ?? 0) < tgtCol) continue;
888
+ handoffAdjustedContent[i] = {
889
+ ...candidate,
890
+ position: {
891
+ x: candidate.position.x + delta,
892
+ y: candidate.position.y
893
+ }
894
+ };
895
+ }
896
+ }
897
+ for (const [sourceId, outgoing] of outgoingBySource) {
898
+ if (outgoing.length < 2) continue;
899
+ const hasCrossLaneSibling = outgoing.some((edge) => {
900
+ const srcLane = nodeLaneId.get(edge.source) ?? "_pool_";
901
+ const tgtLane = nodeLaneId.get(edge.target) ?? "_pool_";
902
+ return srcLane !== tgtLane;
903
+ });
904
+ if (!hasCrossLaneSibling) continue;
905
+ const siblingsByCol = /* @__PURE__ */ new Map();
906
+ for (const edge of outgoing) {
907
+ const col = columns.get(edge.target) ?? 0;
908
+ const node = handoffAdjustedContent[nodeIndexById.get(edge.target) ?? -1];
909
+ if (!node) continue;
910
+ if (!siblingsByCol.has(col)) siblingsByCol.set(col, []);
911
+ siblingsByCol.get(col).push(node);
912
+ }
913
+ for (const [col, siblings] of siblingsByCol) {
914
+ if (siblings.length < 2) continue;
915
+ const alignedX = Math.max(...siblings.map((node) => node.position.x));
916
+ for (let i = 0; i < handoffAdjustedContent.length; i++) {
917
+ const candidate = handoffAdjustedContent[i];
918
+ if (!siblings.some((node) => node.id === candidate.id)) continue;
919
+ const laneId = nodeLaneId.get(candidate.id) ?? "_pool_";
920
+ for (let j = 0; j < handoffAdjustedContent.length; j++) {
921
+ const maybeShift = handoffAdjustedContent[j];
922
+ if ((nodeLaneId.get(maybeShift.id) ?? "_pool_") !== laneId) continue;
923
+ if ((columns.get(maybeShift.id) ?? 0) < col) continue;
924
+ const delta = alignedX - candidate.position.x;
925
+ if (delta <= 0) continue;
926
+ handoffAdjustedContent[j] = {
927
+ ...maybeShift,
928
+ position: {
929
+ x: maybeShift.position.x + delta,
930
+ y: maybeShift.position.y
931
+ }
932
+ };
933
+ }
934
+ }
935
+ }
936
+ }
560
937
  const NODE_MIN_GAP = 24;
561
- const resolvedContent = [...positionedContent];
938
+ const resolvedContent = [...handoffAdjustedContent];
562
939
  for (const laneId of laneIds) {
563
940
  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);
564
941
  for (let k = 1; k < laneNodeIndices.length; k++) {
@@ -580,6 +957,15 @@ function layoutPool(pool, lanes, content, allEdges) {
580
957
  }
581
958
  }
582
959
  const positionedBoundaries = repositionBoundaryEvents(boundaryEvents, resolvedContent);
960
+ const laneChildRight = resolvedContent.filter((node) => hasLanes && !!node.parentId && node.parentId !== pool.id).reduce((max, node) => Math.max(max, node.position.x + nW(node) + LANE_H_PAD), 0);
961
+ const poolChildRight = resolvedContent.filter((node) => !hasLanes || !node.parentId || node.parentId === pool.id).reduce((max, node) => Math.max(max, node.position.x + nW(node) + POOL_H_PAD), 0);
962
+ const boundaryRight = positionedBoundaries.reduce(
963
+ (max, node) => Math.max(max, node.position.x + nW(node) + (hasLanes ? LANE_H_PAD : POOL_H_PAD)),
964
+ 0
965
+ );
966
+ const finalInnerW = Math.max(innerW, laneChildRight, poolChildRight, boundaryRight);
967
+ const finalPoolW = Math.max(POOL_MIN_W, finalInnerW + POOL_INNER_PAD * 2);
968
+ laneW = finalPoolW - POOL_INNER_PAD * 2;
583
969
  const positionedLanes = hasLanes ? sortedLanes.map((lane) => ({
584
970
  ...lane,
585
971
  position: { x: POOL_INNER_PAD, y: laneY.get(lane.id) ?? POOL_INNER_PAD },
@@ -588,10 +974,21 @@ function layoutPool(pool, lanes, content, allEdges) {
588
974
  })) : [];
589
975
  return {
590
976
  nodes: [...resolvedContent, ...positionedBoundaries, ...positionedLanes],
591
- width: poolW,
977
+ width: finalPoolW,
592
978
  height: poolH
593
979
  };
594
980
  }
981
+ function layoutVirtualContext(contextNodes, allEdges, offsetY) {
982
+ if (contextNodes.length === 0) return [];
983
+ const virtualPool = {
984
+ id: "__bpmn_virtual_pool__"};
985
+ const result = layoutPool(virtualPool, [], contextNodes, allEdges);
986
+ if (offsetY === 0) return result.nodes;
987
+ return result.nodes.map((n) => ({
988
+ ...n,
989
+ position: { x: n.position.x, y: n.position.y + offsetY }
990
+ }));
991
+ }
595
992
  function absolutePos(nodeId, byId, cache) {
596
993
  const cached = cache.get(nodeId);
597
994
  if (cached) return cached;
@@ -672,9 +1069,75 @@ function routeEdges(edges, layoutNodes, backEdgeIds, laneIds, poolIds) {
672
1069
  const cache = /* @__PURE__ */ new Map();
673
1070
  const abs = (id) => absolutePos(id, byId, cache);
674
1071
  const sortedLanes = [...laneIds].map((id) => byId.get(id)).filter((n) => !!n).sort((a, b) => abs(a.id).y - abs(b.id).y);
675
- return edges.map((edge) => {
1072
+ const contentInfos = layoutNodes.filter((n) => !poolIds.has(n.id) && !laneIds.has(n.id)).map((n) => {
1073
+ const p = abs(n.id);
1074
+ return { id: n.id, x: p.x, y: p.y, w: nW(n), h: nH(n), poolId: poolOf(n, poolIds, byId) ?? null };
1075
+ });
1076
+ const findClearX = (desiredX, y0, y1, minX, maxX, excludeA, excludeB, poolId) => {
1077
+ if (minX > maxX) return desiredX;
1078
+ const clampX = (x) => Math.min(maxX, Math.max(minX, x));
1079
+ const blockers = contentInfos.filter(
1080
+ (c) => c.poolId === poolId && c.id !== excludeA && c.id !== excludeB && c.y < y1 && c.y + c.h > y0
1081
+ );
1082
+ const isFree = (x) => blockers.every((c) => x < c.x - 4 || x > c.x + c.w + 4);
1083
+ const clamped = clampX(desiredX);
1084
+ if (isFree(clamped)) return clamped;
1085
+ const candidates = [];
1086
+ for (const c of blockers) candidates.push(c.x - 8, c.x + c.w + 8);
1087
+ const free = candidates.map(clampX).filter(isFree).sort((a, b) => Math.abs(a - desiredX) - Math.abs(b - desiredX));
1088
+ return free.length > 0 ? free[0] : clamped;
1089
+ };
1090
+ const backEdgeOrder = /* @__PURE__ */ new Map();
1091
+ for (const edge of [...edges].sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0)) {
1092
+ if (backEdgeIds.has(edge.id)) backEdgeOrder.set(edge.id, backEdgeOrder.size);
1093
+ }
1094
+ const routeMessageFlow = (edge) => {
1095
+ const src = byId.get(edge.source);
1096
+ const tgt = byId.get(edge.target);
1097
+ if (!src || !tgt) return null;
1098
+ const srcPoolId = poolOf(src, poolIds, byId);
1099
+ const tgtPoolId = poolOf(tgt, poolIds, byId);
1100
+ if (!srcPoolId || !tgtPoolId || srcPoolId === tgtPoolId) return null;
1101
+ const srcPoolNode = byId.get(srcPoolId);
1102
+ const tgtPoolNode = byId.get(tgtPoolId);
1103
+ if (!srcPoolNode || !tgtPoolNode) return null;
1104
+ const sAbs = abs(src.id);
1105
+ const tAbs = abs(tgt.id);
1106
+ const sCX = sAbs.x + nW(src) / 2;
1107
+ const tCX = tAbs.x + nW(tgt) / 2;
1108
+ const srcPoolAbs = abs(srcPoolId);
1109
+ const tgtPoolAbs = abs(tgtPoolId);
1110
+ const srcPoolBottom = srcPoolAbs.y + nH(srcPoolNode);
1111
+ const tgtPoolBottom = tgtPoolAbs.y + nH(tgtPoolNode);
1112
+ if (tgtPoolAbs.y >= srcPoolBottom) {
1113
+ const corridorY = (srcPoolBottom + tgtPoolAbs.y) / 2;
1114
+ return [
1115
+ { x: sCX, y: sAbs.y + nH(src) },
1116
+ { x: sCX, y: corridorY },
1117
+ { x: tCX, y: corridorY },
1118
+ { x: tCX, y: tAbs.y }
1119
+ ];
1120
+ }
1121
+ if (srcPoolAbs.y >= tgtPoolBottom) {
1122
+ const corridorY = (tgtPoolBottom + srcPoolAbs.y) / 2;
1123
+ return [
1124
+ { x: sCX, y: sAbs.y },
1125
+ { x: sCX, y: corridorY },
1126
+ { x: tCX, y: corridorY },
1127
+ { x: tCX, y: tAbs.y + nH(tgt) }
1128
+ ];
1129
+ }
1130
+ return null;
1131
+ };
1132
+ const routed = edges.map((edge) => {
676
1133
  const edgeType = edge.data?.edgeType ?? edge.type;
677
1134
  if (edgeType !== "sequenceFlow") {
1135
+ if (edgeType === "messageFlow") {
1136
+ const pts = routeMessageFlow(edge);
1137
+ if (pts) {
1138
+ return { ...edge, data: { ...edge.data, routingPoints: pts } };
1139
+ }
1140
+ }
678
1141
  const d = { ...edge.data };
679
1142
  delete d.routingPoints;
680
1143
  return { ...edge, data: d };
@@ -699,7 +1162,16 @@ function routeEdges(edges, layoutNodes, backEdgeIds, laneIds, poolIds) {
699
1162
  }
700
1163
  let routingPoints;
701
1164
  if (backEdgeIds.has(edge.id)) {
702
- const topY = Math.min(sAbs.y, tAbs.y) - BACK_EDGE_CLEARANCE;
1165
+ const spanMinX = Math.min(tAbs.x, sAbs.x) - EDGE_ROUTE_PAD;
1166
+ const spanMaxX = Math.max(tAbs.x + tW, sAbs.x + sW) + EDGE_ROUTE_PAD;
1167
+ let topMost = Math.min(sAbs.y, tAbs.y);
1168
+ for (const cand of contentInfos) {
1169
+ if (cand.poolId !== srcPool) continue;
1170
+ if (cand.x + cand.w < spanMinX || cand.x > spanMaxX) continue;
1171
+ topMost = Math.min(topMost, cand.y);
1172
+ }
1173
+ const stagger = (backEdgeOrder.get(edge.id) ?? 0) * 14;
1174
+ const topY = topMost - BACK_EDGE_CLEARANCE - stagger;
703
1175
  routingPoints = [
704
1176
  { x: sAbs.x + sW, y: sCY },
705
1177
  { x: sAbs.x + sW + EDGE_ROUTE_PAD, y: sCY },
@@ -713,7 +1185,16 @@ function routeEdges(edges, layoutNodes, backEdgeIds, laneIds, poolIds) {
713
1185
  const tgtLane = byId.get(laneOf(tgt, laneIds, byId) ?? "");
714
1186
  if (srcLane && tgtLane && laneIds.has(srcLane.id) && laneIds.has(tgtLane.id)) {
715
1187
  const goingDown = tAbs.y > sAbs.y;
716
- const sharedX = gapMidX(sAbs, sW, tAbs, tW);
1188
+ const sharedX = findClearX(
1189
+ gapMidX(sAbs, sW, tAbs, tW),
1190
+ Math.min(sCY, tCY),
1191
+ Math.max(sCY, tCY),
1192
+ sAbs.x + sW + 6,
1193
+ tAbs.x - EDGE_ROUTE_PAD - 4,
1194
+ src.id,
1195
+ tgt.id,
1196
+ srcPool
1197
+ );
717
1198
  const srcLaneIdx = sortedLanes.findIndex((l) => l.id === srcLane.id);
718
1199
  const tgtLaneIdx = sortedLanes.findIndex((l) => l.id === tgtLane.id);
719
1200
  const pts = [];
@@ -748,7 +1229,16 @@ function routeEdges(edges, layoutNodes, backEdgeIds, laneIds, poolIds) {
748
1229
  }
749
1230
  routingPoints = pts;
750
1231
  } else {
751
- const sharedX = gapMidX(sAbs, sW, tAbs, tW);
1232
+ const sharedX = findClearX(
1233
+ gapMidX(sAbs, sW, tAbs, tW),
1234
+ Math.min(sCY, tCY),
1235
+ Math.max(sCY, tCY),
1236
+ sAbs.x + sW + 6,
1237
+ tAbs.x - 6,
1238
+ src.id,
1239
+ tgt.id,
1240
+ srcPool
1241
+ );
752
1242
  routingPoints = [
753
1243
  { x: sAbs.x + sW, y: sCY },
754
1244
  { x: sharedX, y: sCY },
@@ -778,7 +1268,16 @@ function routeEdges(edges, layoutNodes, backEdgeIds, laneIds, poolIds) {
778
1268
  const midX = routeMidX(sAbs, sW, tAbs, tW);
779
1269
  const exitX = sAbs.x + sW;
780
1270
  const entryX = tAbs.x;
781
- const corridorX = Math.max(midX, exitX + EDGE_ROUTE_PAD);
1271
+ const corridorX = findClearX(
1272
+ Math.max(midX, exitX + EDGE_ROUTE_PAD),
1273
+ Math.min(sCY, tCY),
1274
+ Math.max(sCY, tCY),
1275
+ exitX + 6,
1276
+ entryX - 6,
1277
+ src.id,
1278
+ tgt.id,
1279
+ srcPool
1280
+ );
782
1281
  routingPoints = [
783
1282
  { x: exitX, y: sCY },
784
1283
  { x: corridorX, y: sCY },
@@ -789,9 +1288,81 @@ function routeEdges(edges, layoutNodes, backEdgeIds, laneIds, poolIds) {
789
1288
  }
790
1289
  return { ...edge, data: { ...edge.data, routingPoints } };
791
1290
  });
1291
+ return spreadVerticalChannels(routed);
1292
+ }
1293
+ var CHANNEL_GAP = 10;
1294
+ var CHANNEL_CLUSTER_TOL = 8;
1295
+ function spreadVerticalChannels(edges) {
1296
+ const runs = [];
1297
+ edges.forEach((edge, edgeIdx) => {
1298
+ const pts = edge.data?.routingPoints;
1299
+ if (!pts || pts.length < 4) return;
1300
+ let i = 1;
1301
+ while (i < pts.length) {
1302
+ let j = i;
1303
+ while (j < pts.length && Math.abs(pts[j].x - pts[j - 1].x) <= 0.5) j++;
1304
+ if (j > i) {
1305
+ const fromIdx = i - 1;
1306
+ const toIdx = j - 1;
1307
+ if (fromIdx > 0 && toIdx < pts.length - 1) {
1308
+ const ys = pts.slice(fromIdx, toIdx + 1).map((p) => p.y);
1309
+ const y0 = Math.min(...ys);
1310
+ const y1 = Math.max(...ys);
1311
+ if (y1 - y0 >= 4) {
1312
+ runs.push({ edgeIdx, fromIdx, toIdx, x: pts[fromIdx].x, y0, y1 });
1313
+ }
1314
+ }
1315
+ i = j + 1;
1316
+ } else {
1317
+ i++;
1318
+ }
1319
+ }
1320
+ });
1321
+ if (runs.length < 2) return edges;
1322
+ runs.sort((a, b) => a.x - b.x);
1323
+ const clusters = [];
1324
+ for (const run of runs) {
1325
+ const last = clusters[clusters.length - 1];
1326
+ if (last && run.x - last[0].x <= CHANNEL_CLUSTER_TOL) last.push(run);
1327
+ else clusters.push([run]);
1328
+ }
1329
+ const offsets = /* @__PURE__ */ new Map();
1330
+ for (const cluster of clusters) {
1331
+ if (cluster.length < 2) continue;
1332
+ const colliding = cluster.filter(
1333
+ (run) => cluster.some((other) => other !== run && run.y0 < other.y1 && other.y0 < run.y1)
1334
+ );
1335
+ if (colliding.length < 2) continue;
1336
+ const ordered = [...colliding].sort((a, b) => {
1337
+ const idA = edges[a.edgeIdx].id;
1338
+ const idB = edges[b.edgeIdx].id;
1339
+ return idA < idB ? -1 : idA > idB ? 1 : a.fromIdx - b.fromIdx;
1340
+ });
1341
+ ordered.forEach((run, k) => {
1342
+ const off = Math.round((k - (ordered.length - 1) / 2) * CHANNEL_GAP);
1343
+ if (off !== 0) offsets.set(run, off);
1344
+ });
1345
+ }
1346
+ if (offsets.size === 0) return edges;
1347
+ const runsByEdge = /* @__PURE__ */ new Map();
1348
+ for (const [run, off] of offsets) {
1349
+ if (!runsByEdge.has(run.edgeIdx)) runsByEdge.set(run.edgeIdx, []);
1350
+ runsByEdge.get(run.edgeIdx).push(run);
1351
+ }
1352
+ return edges.map((edge, edgeIdx) => {
1353
+ const edgeRuns = runsByEdge.get(edgeIdx);
1354
+ if (!edgeRuns) return edge;
1355
+ const pts = (edge.data?.routingPoints).map((p) => ({ ...p }));
1356
+ for (const run of edgeRuns) {
1357
+ const off = offsets.get(run);
1358
+ for (let p = run.fromIdx; p <= run.toIdx; p++) pts[p].x += off;
1359
+ }
1360
+ return { ...edge, data: { ...edge.data, routingPoints: pts } };
1361
+ });
792
1362
  }
793
1363
  var ARTIFACT_ABOVE_GAP = 16;
794
1364
  var ARTIFACT_H_SPACING = 12;
1365
+ var ARTIFACT_EDGE_PAD = 4;
795
1366
  function positionArtifacts(artifacts, resultNodes, edges, originalNodes) {
796
1367
  if (artifacts.length === 0) return [];
797
1368
  const byId = new Map(resultNodes.map((n) => [n.id, n]));
@@ -832,11 +1403,21 @@ function positionArtifacts(artifacts, resultNodes, edges, originalNodes) {
832
1403
  let desiredAbsX = connAbsP.x + nW(connNode) / 2 - totalW / 2;
833
1404
  for (const artifact of arts) {
834
1405
  const parentAbsP = artifact.parentId ? absPos(artifact.parentId) : { x: 0, y: 0 };
1406
+ let relY = desiredAbsY - parentAbsP.y;
1407
+ const parentNode = artifact.parentId ? byId.get(artifact.parentId) : void 0;
1408
+ if (parentNode) {
1409
+ if (relY < ARTIFACT_EDGE_PAD) {
1410
+ const belowAbsY = connAbsP.y + nH(connNode) + ARTIFACT_ABOVE_GAP;
1411
+ relY = belowAbsY - parentAbsP.y;
1412
+ }
1413
+ relY = Math.min(relY, nH(parentNode) - nH(artifact) - ARTIFACT_EDGE_PAD);
1414
+ relY = Math.max(relY, ARTIFACT_EDGE_PAD);
1415
+ }
835
1416
  positioned.push({
836
1417
  ...artifact,
837
1418
  position: {
838
1419
  x: desiredAbsX - parentAbsP.x,
839
- y: desiredAbsY - parentAbsP.y
1420
+ y: relY
840
1421
  }
841
1422
  });
842
1423
  desiredAbsX += nW(artifact) + ARTIFACT_H_SPACING;
@@ -901,9 +1482,17 @@ async function bpmnCustomLayout(nodes, edges) {
901
1482
  const mainNodes = nodes.filter((n) => !LAYOUT_ARTIFACT_TYPES.has(n.data.elementType));
902
1483
  const pools = mainNodes.filter((n) => n.data.elementType === "Pool");
903
1484
  const lanes = mainNodes.filter((n) => n.data.elementType === "Lane");
904
- const expandedSubProcesses = mainNodes.filter(
905
- (n) => COLLAPSED_SUBPROCESS_TYPES.has(n.data.elementType) && n.data.isExpanded
906
- );
1485
+ const nodeDepthById = /* @__PURE__ */ new Map();
1486
+ const mainById = new Map(mainNodes.map((n) => [n.id, n]));
1487
+ const depthOf = (node) => {
1488
+ const cached = nodeDepthById.get(node.id);
1489
+ if (cached != null) return cached;
1490
+ const parent = node.parentId ? mainById.get(node.parentId) : void 0;
1491
+ const depth = parent ? depthOf(parent) + 1 : 0;
1492
+ nodeDepthById.set(node.id, depth);
1493
+ return depth;
1494
+ };
1495
+ const expandedSubProcesses = mainNodes.filter((n) => COLLAPSED_SUBPROCESS_TYPES.has(n.data.elementType) && n.data.isExpanded).sort((a, b) => depthOf(b) - depthOf(a));
907
1496
  const subProcessLayouts = /* @__PURE__ */ new Map();
908
1497
  const workingMainNodes = [...mainNodes];
909
1498
  for (const sp of expandedSubProcesses) {
@@ -921,15 +1510,30 @@ async function bpmnCustomLayout(nodes, edges) {
921
1510
  }
922
1511
  }
923
1512
  const content = workingMainNodes.filter((n) => {
1513
+ if (n.data.elementType === "BoundaryEvent") return false;
924
1514
  if (!LAYOUT_CONTAINER_TYPES.has(n.data.elementType)) return true;
925
1515
  if (COLLAPSED_SUBPROCESS_TYPES.has(n.data.elementType)) return true;
926
1516
  return false;
927
1517
  });
928
1518
  if (pools.length === 0) {
929
- const { bpmnElkLayout: bpmnElkLayout2 } = await import('../elk-QT7H4252.js');
930
- const elkResult = await bpmnElkLayout2(nodes.filter((n) => !LAYOUT_ARTIFACT_TYPES.has(n.data.elementType)), edges);
931
- const posArtifacts = positionArtifacts(artifacts, elkResult.nodes, edges, nodes);
932
- return { nodes: sortNodesParentFirst([...elkResult.nodes, ...posArtifacts]), edges: elkResult.edges };
1519
+ const isSpChild = (n) => !!n.parentId && subProcessLayouts.has(n.parentId);
1520
+ const freeContent = content.filter((n) => !isSpChild(n));
1521
+ const freeBoundaries = workingMainNodes.filter(
1522
+ (n) => n.data.elementType === "BoundaryEvent" && !isSpChild(n)
1523
+ );
1524
+ const resultNodes2 = layoutVirtualContext([...freeContent, ...freeBoundaries], edges, 0);
1525
+ for (const [, spLayout] of subProcessLayouts) {
1526
+ for (const child of spLayout.children) resultNodes2.push(child);
1527
+ }
1528
+ const contentIds = new Set(freeContent.map((n) => n.id));
1529
+ const seqEdges = extractSeqEdges(edges, contentIds);
1530
+ const backIds = detectBackEdges([...contentIds], seqEdges);
1531
+ const routedEdges2 = routeEdges(edges, resultNodes2, backIds, /* @__PURE__ */ new Set(), /* @__PURE__ */ new Set());
1532
+ const posArtifacts = positionArtifacts(artifacts, resultNodes2, edges, nodes);
1533
+ return {
1534
+ nodes: sortNodesParentFirst([...resultNodes2, ...posArtifacts]),
1535
+ edges: routedEdges2
1536
+ };
933
1537
  }
934
1538
  const poolIds = new Set(pools.map((p) => p.id));
935
1539
  const allLaneIds = new Set(lanes.map((l) => l.id));
@@ -972,30 +1576,14 @@ async function bpmnCustomLayout(nodes, edges) {
972
1576
  const layoutted = new Set(resultNodes.map((n) => n.id));
973
1577
  const freeNodes = workingMainNodes.filter((n) => !layoutted.has(n.id));
974
1578
  if (freeNodes.length > 0) {
975
- const freeNodeIds = new Set(freeNodes.map((n) => n.id));
976
- const freeEdges = edges.filter(
977
- (e) => freeNodeIds.has(e.source) && freeNodeIds.has(e.target)
1579
+ const freeFlow = freeNodes.filter(
1580
+ (n) => n.data.elementType === "BoundaryEvent" || !LAYOUT_CONTAINER_TYPES.has(n.data.elementType) || COLLAPSED_SUBPROCESS_TYPES.has(n.data.elementType)
978
1581
  );
979
- try {
980
- const { bpmnElkLayout: bpmnElkLayout2 } = await import('../elk-QT7H4252.js');
981
- const elkFree = await bpmnElkLayout2(freeNodes, freeEdges);
982
- const offsetY = stackY;
983
- for (const node of elkFree.nodes) {
984
- resultNodes.push({
985
- ...node,
986
- position: { x: node.position.x, y: node.position.y + offsetY }
987
- });
988
- }
989
- const freeEdgeIds = new Set(freeEdges.map((e) => e.id));
990
- const elkEdgeMap = new Map(elkFree.edges.map((e) => [e.id, e]));
991
- for (let i = 0; i < edges.length; i++) {
992
- if (freeEdgeIds.has(edges[i].id) && elkEdgeMap.has(edges[i].id)) {
993
- edges[i] = elkEdgeMap.get(edges[i].id);
994
- }
995
- }
996
- } catch {
997
- for (const node of freeNodes) resultNodes.push(node);
1582
+ const freeRest = freeNodes.filter((n) => !freeFlow.includes(n));
1583
+ for (const node of layoutVirtualContext(freeFlow, edges, stackY)) {
1584
+ resultNodes.push(node);
998
1585
  }
1586
+ for (const node of freeRest) resultNodes.push(node);
999
1587
  }
1000
1588
  const routedEdges = routeEdges(edges, resultNodes, allBackEdgeIds, allLaneIds, poolIds);
1001
1589
  const positionedArtifacts = positionArtifacts(artifacts, resultNodes, edges, nodes);
@@ -1016,6 +1604,6 @@ function applyBpmnLayoutResult(state, result) {
1016
1604
  return applyLayoutResultToDiagram(state, result);
1017
1605
  }
1018
1606
 
1019
- export { applyBpmnLayoutResult, bpmnCustomLayout, bpmnDagreLayout };
1607
+ export { applyBpmnLayoutResult, bpmnCustomLayout, bpmnDagreLayout, bpmnElkLayout };
1020
1608
  //# sourceMappingURL=index.js.map
1021
1609
  //# sourceMappingURL=index.js.map