@aranzatech/diagrams-bpmn 0.3.5 → 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-JFYWN6QH.js → chunk-EMTO53AN.js} +52 -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 +73 -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 +194 -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 +1146 -872
  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 +560 -89
  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-FFWJA5BV.js +0 -163
  50. package/dist/chunk-FFWJA5BV.js.map +0 -1
  51. package/dist/chunk-JFYWN6QH.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,28 +721,67 @@ 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
787
  const laneColumnLayouts = /* @__PURE__ */ new Map();
@@ -524,7 +794,7 @@ function layoutPool(pool, lanes, content, allEdges) {
524
794
  for (const col of usedCols) colW.set(col, 0);
525
795
  for (const node of laneNodes) {
526
796
  const col = columns.get(node.id) ?? 0;
527
- colW.set(col, Math.max(colW.get(col) ?? 0, layoutW(node)));
797
+ colW.set(col, Math.max(colW.get(col) ?? 0, columnW(node)));
528
798
  }
529
799
  const colX = /* @__PURE__ */ new Map();
530
800
  let laneCumX = 0;
@@ -571,9 +841,8 @@ function layoutPool(pool, lanes, content, allEdges) {
571
841
  const lw = layoutW(node);
572
842
  const lh = layoutH(node);
573
843
  const colOffset = (laneLayout?.colX.get(c) ?? 0) + (laneLayout?.colW.get(c) ?? 120) / 2 - lw / 2;
574
- const rowOffset = (r - stat.minRow) * (ROW_HEIGHT + ROW_GAP) + ROW_HEIGHT / 2 - lh / 2;
575
- const contentH_stat = stat.rowCount * ROW_HEIGHT + Math.max(0, stat.rowCount - 1) * ROW_GAP;
576
- const vertTopOffset = Math.max(LANE_V_PAD, (stat.height - contentH_stat) / 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);
577
846
  const isLaneChild = hasLanes && !!node.parentId && node.parentId !== pool.id;
578
847
  const x = isLaneChild ? LANE_LABEL_W + LANE_H_PAD + colOffset : POOL_H_PAD + colOffset;
579
848
  const y = isLaneChild ? vertTopOffset + rowOffset : lYOff + vertTopOffset + rowOffset;
@@ -709,6 +978,17 @@ function layoutPool(pool, lanes, content, allEdges) {
709
978
  height: poolH
710
979
  };
711
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
+ }
712
992
  function absolutePos(nodeId, byId, cache) {
713
993
  const cached = cache.get(nodeId);
714
994
  if (cached) return cached;
@@ -789,9 +1069,75 @@ function routeEdges(edges, layoutNodes, backEdgeIds, laneIds, poolIds) {
789
1069
  const cache = /* @__PURE__ */ new Map();
790
1070
  const abs = (id) => absolutePos(id, byId, cache);
791
1071
  const sortedLanes = [...laneIds].map((id) => byId.get(id)).filter((n) => !!n).sort((a, b) => abs(a.id).y - abs(b.id).y);
792
- 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) => {
793
1133
  const edgeType = edge.data?.edgeType ?? edge.type;
794
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
+ }
795
1141
  const d = { ...edge.data };
796
1142
  delete d.routingPoints;
797
1143
  return { ...edge, data: d };
@@ -816,7 +1162,16 @@ function routeEdges(edges, layoutNodes, backEdgeIds, laneIds, poolIds) {
816
1162
  }
817
1163
  let routingPoints;
818
1164
  if (backEdgeIds.has(edge.id)) {
819
- 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;
820
1175
  routingPoints = [
821
1176
  { x: sAbs.x + sW, y: sCY },
822
1177
  { x: sAbs.x + sW + EDGE_ROUTE_PAD, y: sCY },
@@ -830,7 +1185,16 @@ function routeEdges(edges, layoutNodes, backEdgeIds, laneIds, poolIds) {
830
1185
  const tgtLane = byId.get(laneOf(tgt, laneIds, byId) ?? "");
831
1186
  if (srcLane && tgtLane && laneIds.has(srcLane.id) && laneIds.has(tgtLane.id)) {
832
1187
  const goingDown = tAbs.y > sAbs.y;
833
- 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
+ );
834
1198
  const srcLaneIdx = sortedLanes.findIndex((l) => l.id === srcLane.id);
835
1199
  const tgtLaneIdx = sortedLanes.findIndex((l) => l.id === tgtLane.id);
836
1200
  const pts = [];
@@ -865,7 +1229,16 @@ function routeEdges(edges, layoutNodes, backEdgeIds, laneIds, poolIds) {
865
1229
  }
866
1230
  routingPoints = pts;
867
1231
  } else {
868
- 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
+ );
869
1242
  routingPoints = [
870
1243
  { x: sAbs.x + sW, y: sCY },
871
1244
  { x: sharedX, y: sCY },
@@ -895,7 +1268,16 @@ function routeEdges(edges, layoutNodes, backEdgeIds, laneIds, poolIds) {
895
1268
  const midX = routeMidX(sAbs, sW, tAbs, tW);
896
1269
  const exitX = sAbs.x + sW;
897
1270
  const entryX = tAbs.x;
898
- 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
+ );
899
1281
  routingPoints = [
900
1282
  { x: exitX, y: sCY },
901
1283
  { x: corridorX, y: sCY },
@@ -906,9 +1288,81 @@ function routeEdges(edges, layoutNodes, backEdgeIds, laneIds, poolIds) {
906
1288
  }
907
1289
  return { ...edge, data: { ...edge.data, routingPoints } };
908
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
+ });
909
1362
  }
910
1363
  var ARTIFACT_ABOVE_GAP = 16;
911
1364
  var ARTIFACT_H_SPACING = 12;
1365
+ var ARTIFACT_EDGE_PAD = 4;
912
1366
  function positionArtifacts(artifacts, resultNodes, edges, originalNodes) {
913
1367
  if (artifacts.length === 0) return [];
914
1368
  const byId = new Map(resultNodes.map((n) => [n.id, n]));
@@ -949,11 +1403,21 @@ function positionArtifacts(artifacts, resultNodes, edges, originalNodes) {
949
1403
  let desiredAbsX = connAbsP.x + nW(connNode) / 2 - totalW / 2;
950
1404
  for (const artifact of arts) {
951
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
+ }
952
1416
  positioned.push({
953
1417
  ...artifact,
954
1418
  position: {
955
1419
  x: desiredAbsX - parentAbsP.x,
956
- y: desiredAbsY - parentAbsP.y
1420
+ y: relY
957
1421
  }
958
1422
  });
959
1423
  desiredAbsX += nW(artifact) + ARTIFACT_H_SPACING;
@@ -1018,9 +1482,17 @@ async function bpmnCustomLayout(nodes, edges) {
1018
1482
  const mainNodes = nodes.filter((n) => !LAYOUT_ARTIFACT_TYPES.has(n.data.elementType));
1019
1483
  const pools = mainNodes.filter((n) => n.data.elementType === "Pool");
1020
1484
  const lanes = mainNodes.filter((n) => n.data.elementType === "Lane");
1021
- const expandedSubProcesses = mainNodes.filter(
1022
- (n) => COLLAPSED_SUBPROCESS_TYPES.has(n.data.elementType) && n.data.isExpanded
1023
- );
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));
1024
1496
  const subProcessLayouts = /* @__PURE__ */ new Map();
1025
1497
  const workingMainNodes = [...mainNodes];
1026
1498
  for (const sp of expandedSubProcesses) {
@@ -1038,15 +1510,30 @@ async function bpmnCustomLayout(nodes, edges) {
1038
1510
  }
1039
1511
  }
1040
1512
  const content = workingMainNodes.filter((n) => {
1513
+ if (n.data.elementType === "BoundaryEvent") return false;
1041
1514
  if (!LAYOUT_CONTAINER_TYPES.has(n.data.elementType)) return true;
1042
1515
  if (COLLAPSED_SUBPROCESS_TYPES.has(n.data.elementType)) return true;
1043
1516
  return false;
1044
1517
  });
1045
1518
  if (pools.length === 0) {
1046
- const { bpmnElkLayout: bpmnElkLayout2 } = await import('../elk-QT7H4252.js');
1047
- const elkResult = await bpmnElkLayout2(nodes.filter((n) => !LAYOUT_ARTIFACT_TYPES.has(n.data.elementType)), edges);
1048
- const posArtifacts = positionArtifacts(artifacts, elkResult.nodes, edges, nodes);
1049
- 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
+ };
1050
1537
  }
1051
1538
  const poolIds = new Set(pools.map((p) => p.id));
1052
1539
  const allLaneIds = new Set(lanes.map((l) => l.id));
@@ -1089,30 +1576,14 @@ async function bpmnCustomLayout(nodes, edges) {
1089
1576
  const layoutted = new Set(resultNodes.map((n) => n.id));
1090
1577
  const freeNodes = workingMainNodes.filter((n) => !layoutted.has(n.id));
1091
1578
  if (freeNodes.length > 0) {
1092
- const freeNodeIds = new Set(freeNodes.map((n) => n.id));
1093
- const freeEdges = edges.filter(
1094
- (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)
1095
1581
  );
1096
- try {
1097
- const { bpmnElkLayout: bpmnElkLayout2 } = await import('../elk-QT7H4252.js');
1098
- const elkFree = await bpmnElkLayout2(freeNodes, freeEdges);
1099
- const offsetY = stackY;
1100
- for (const node of elkFree.nodes) {
1101
- resultNodes.push({
1102
- ...node,
1103
- position: { x: node.position.x, y: node.position.y + offsetY }
1104
- });
1105
- }
1106
- const freeEdgeIds = new Set(freeEdges.map((e) => e.id));
1107
- const elkEdgeMap = new Map(elkFree.edges.map((e) => [e.id, e]));
1108
- for (let i = 0; i < edges.length; i++) {
1109
- if (freeEdgeIds.has(edges[i].id) && elkEdgeMap.has(edges[i].id)) {
1110
- edges[i] = elkEdgeMap.get(edges[i].id);
1111
- }
1112
- }
1113
- } catch {
1114
- 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);
1115
1585
  }
1586
+ for (const node of freeRest) resultNodes.push(node);
1116
1587
  }
1117
1588
  const routedEdges = routeEdges(edges, resultNodes, allBackEdgeIds, allLaneIds, poolIds);
1118
1589
  const positionedArtifacts = positionArtifacts(artifacts, resultNodes, edges, nodes);
@@ -1133,6 +1604,6 @@ function applyBpmnLayoutResult(state, result) {
1133
1604
  return applyLayoutResultToDiagram(state, result);
1134
1605
  }
1135
1606
 
1136
- export { applyBpmnLayoutResult, bpmnCustomLayout, bpmnDagreLayout };
1607
+ export { applyBpmnLayoutResult, bpmnCustomLayout, bpmnDagreLayout, bpmnElkLayout };
1137
1608
  //# sourceMappingURL=index.js.map
1138
1609
  //# sourceMappingURL=index.js.map