@ngroznykh/papirus 0.6.5 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/papirus.js CHANGED
@@ -1047,19 +1047,21 @@ var DragManager = class extends EventEmitter {
1047
1047
  const nodeBindings = /* @__PURE__ */ new Map();
1048
1048
  for (const edge of this.renderer.edges.values()) {
1049
1049
  if (!edge.hasEditableControlPoints()) continue;
1050
- const isFromDragged = draggedIds.has(edge.from.nodeId);
1051
- const isToDragged = draggedIds.has(edge.to.nodeId);
1050
+ const fromId = edge.from.nodeId;
1051
+ const toId = edge.to.nodeId;
1052
+ const isFromDragged = !!fromId && draggedIds.has(fromId);
1053
+ const isToDragged = !!toId && draggedIds.has(toId);
1052
1054
  if (isFromDragged && isToDragged) {
1053
1055
  this.editablePolylineFullyConnectedEdges.add(edge.id);
1054
1056
  this.initialControlPointsForFullyConnected.set(edge.id, clonePoints(edge.controlPoints));
1055
1057
  continue;
1056
1058
  }
1057
1059
  for (const node of this.draggedNodes) {
1058
- const isFrom = edge.from.nodeId === node.id;
1059
- const isTo = edge.to.nodeId === node.id;
1060
+ const isFrom = fromId === node.id;
1061
+ const isTo = toId === node.id;
1060
1062
  if (!isFrom && !isTo) continue;
1061
- const oppositeNodeId = isFrom ? edge.to.nodeId : edge.from.nodeId;
1062
- if (draggedIds.has(oppositeNodeId)) continue;
1063
+ const oppositeNodeId = isFrom ? toId : fromId;
1064
+ if (!oppositeNodeId || draggedIds.has(oppositeNodeId)) continue;
1063
1065
  const controlPointIndex = isFrom ? 0 : edge.controlPoints.length - 1;
1064
1066
  const nearestPoint = edge.controlPoints[controlPointIndex];
1065
1067
  const endpoint = isFrom ? edge.startPoint : edge.endPoint;
@@ -1734,6 +1736,8 @@ var ConnectionManager = class extends EventEmitter {
1734
1736
  this.sourceOutlineParam = null;
1735
1737
  this.previewTargetOutlineParam = null;
1736
1738
  this.previewTargetNodeId = null;
1739
+ this.previewTargetEdgeId = null;
1740
+ this.previewTargetPathParam = null;
1737
1741
  this.isReconnecting = false;
1738
1742
  this.reconnectingEdge = null;
1739
1743
  this.reconnectingEndpoint = null;
@@ -1912,8 +1916,8 @@ var ConnectionManager = class extends EventEmitter {
1912
1916
  updateReconnectingEdge(point) {
1913
1917
  if (!this.reconnectingEdge) return;
1914
1918
  const edge = this.reconnectingEdge;
1915
- const fromNode = this.renderer.getNode(edge.from.nodeId);
1916
- const toNode = this.renderer.getNode(edge.to.nodeId);
1919
+ const fromNode = edge.from.nodeId ? this.renderer.getNode(edge.from.nodeId) : void 0;
1920
+ const toNode = edge.to.nodeId ? this.renderer.getNode(edge.to.nodeId) : void 0;
1917
1921
  let snappedPoint = point;
1918
1922
  let snappedDir;
1919
1923
  let targetNode = this.getNodeAtPoint(point, true);
@@ -2056,6 +2060,8 @@ var ConnectionManager = class extends EventEmitter {
2056
2060
  this.previewTargetAnchorId = null;
2057
2061
  this.previewTargetOutlineParam = null;
2058
2062
  this.previewTargetNodeId = null;
2063
+ this.previewTargetEdgeId = null;
2064
+ this.previewTargetPathParam = null;
2059
2065
  let targetNode = this.getNodeAtPoint(cursorPoint, false);
2060
2066
  if (this.attachToOutline) {
2061
2067
  const tolerance = 40 / Math.max(this.renderer.zoom, 1e-4);
@@ -2099,6 +2105,24 @@ var ConnectionManager = class extends EventEmitter {
2099
2105
  }
2100
2106
  }
2101
2107
  }
2108
+ const edgeDrop = this.findEdgeDropTarget(cursorPoint);
2109
+ if (edgeDrop) {
2110
+ let nodeDistance = Infinity;
2111
+ if (targetNode) {
2112
+ const dx = cursorPoint.x - snappedPoint.x;
2113
+ const dy = cursorPoint.y - snappedPoint.y;
2114
+ nodeDistance = Math.sqrt(dx * dx + dy * dy);
2115
+ }
2116
+ if (!targetNode || edgeDrop.distance < nodeDistance) {
2117
+ targetNode = null;
2118
+ this.previewTargetNodeId = null;
2119
+ this.previewTargetOutlineParam = null;
2120
+ this.previewTargetAnchorId = null;
2121
+ snappedPoint = edgeDrop.point;
2122
+ this.previewTargetEdgeId = edgeDrop.edge.id;
2123
+ this.previewTargetPathParam = edgeDrop.pathParam;
2124
+ }
2125
+ }
2102
2126
  if (targetNode) {
2103
2127
  const forbidden = this._connectionValidator && this.sourceNode && !this._connectionValidator(this.sourceNode.id, targetNode.id);
2104
2128
  this.setCursor(forbidden ? "not-allowed" : "crosshair");
@@ -2199,9 +2223,29 @@ var ConnectionManager = class extends EventEmitter {
2199
2223
  y: event.worldY
2200
2224
  };
2201
2225
  let createdEdge = null;
2226
+ const edgeDrop = this.previewTargetEdgeId != null && this.previewTargetPathParam != null ? {
2227
+ edgeId: this.previewTargetEdgeId,
2228
+ pathParam: this.previewTargetPathParam,
2229
+ point
2230
+ } : this.findEdgeDropTarget(point);
2202
2231
  let targetNode = this.getNodeAtPoint(point, false);
2203
2232
  if (!targetNode && this.attachToOutline && this.previewTargetNodeId) targetNode = this.renderer.getNode(this.previewTargetNodeId) ?? null;
2204
- if (targetNode) {
2233
+ if (edgeDrop && this.sourceNode && (!targetNode || this.previewTargetEdgeId != null)) {
2234
+ const from = this.attachToOutline ? {
2235
+ nodeId: this.sourceNode.id,
2236
+ outlineParam: this.sourceOutlineParam ?? void 0
2237
+ } : {
2238
+ nodeId: this.sourceNode.id,
2239
+ portId: this.sourceAnchorId ? `${ANCHOR_PORT_PREFIX}${this.sourceAnchorId}` : void 0
2240
+ };
2241
+ const to = {
2242
+ edgeId: edgeDrop.edgeId,
2243
+ pathParam: edgeDrop.pathParam
2244
+ };
2245
+ createdEdge = this.createEdge(from, to);
2246
+ this.addEdge(createdEdge);
2247
+ this.emit("connect", createdEdge);
2248
+ } else if (targetNode) {
2205
2249
  if (!this._connectionValidator || this._connectionValidator(this.sourceNode.id, targetNode.id)) {
2206
2250
  let from;
2207
2251
  let to;
@@ -2338,8 +2382,36 @@ var ConnectionManager = class extends EventEmitter {
2338
2382
  this.sourceOutlineParam = null;
2339
2383
  this.previewTargetOutlineParam = null;
2340
2384
  this.previewTargetNodeId = null;
2385
+ this.previewTargetEdgeId = null;
2386
+ this.previewTargetPathParam = null;
2341
2387
  this.setCursor("");
2342
2388
  }
2389
+ /**
2390
+ * Find the nearest edge under the cursor for junction / note→relation drops.
2391
+ */
2392
+ findEdgeDropTarget(point) {
2393
+ const tolerance = Math.max(14, 20 / Math.max(this.renderer.zoom, 1e-4));
2394
+ let best = null;
2395
+ for (const edge of this.renderer.edges.values()) {
2396
+ if (!edge.visible || edge.path.length < 2) continue;
2397
+ const closest = edge.getClosestPointOnPath(point);
2398
+ if (!closest || closest.distance > tolerance) continue;
2399
+ if (!best || closest.distance < best.distance) best = {
2400
+ edge,
2401
+ pathParam: closest.pathParam,
2402
+ point: closest.point,
2403
+ distance: closest.distance
2404
+ };
2405
+ }
2406
+ if (!best) return null;
2407
+ return {
2408
+ edge: best.edge,
2409
+ edgeId: best.edge.id,
2410
+ pathParam: best.pathParam,
2411
+ point: best.point,
2412
+ distance: best.distance
2413
+ };
2414
+ }
2343
2415
  resetReconnection() {
2344
2416
  this.isReconnecting = false;
2345
2417
  if (this.reconnectingEdge) this.reconnectingEdge.autoUpdateEndpoints = true;
@@ -3588,6 +3660,109 @@ var Element = class {
3588
3660
  }
3589
3661
  };
3590
3662
  //#endregion
3663
+ //#region src/utils/edgePath.ts
3664
+ /**
3665
+ * Point and tangent angle at a normalized position along an edge path (0..1 by length).
3666
+ */
3667
+ function getPathPointAt(path, type, position) {
3668
+ if (type !== "bezier" || path.length < 4) return getPointAlongPolyline(path, position);
3669
+ const samples = [];
3670
+ const steps = 20;
3671
+ for (let i = 1; i + 2 < path.length; i += 3) {
3672
+ const start = path[i - 1];
3673
+ const control1 = path[i];
3674
+ const control2 = path[i + 1];
3675
+ const end = path[i + 2];
3676
+ for (let sample = 0; sample <= steps; sample++) {
3677
+ const t = sample / steps;
3678
+ if (samples.length > 0 && t === 0) continue;
3679
+ samples.push(bezierPoint(start, control1, control2, end, t));
3680
+ }
3681
+ }
3682
+ return getPointAlongPolyline(samples, position);
3683
+ }
3684
+ function getPointAlongPolyline(path, position) {
3685
+ if (path.length === 0) return {
3686
+ point: {
3687
+ x: 0,
3688
+ y: 0
3689
+ },
3690
+ angle: 0
3691
+ };
3692
+ if (path.length === 1) return {
3693
+ point: path[0],
3694
+ angle: 0
3695
+ };
3696
+ let totalLength = 0;
3697
+ const segments = [];
3698
+ for (let i = 1; i < path.length; i++) {
3699
+ const start = path[i - 1];
3700
+ const end = path[i];
3701
+ const length = Math.hypot(end.x - start.x, end.y - start.y);
3702
+ segments.push({
3703
+ start,
3704
+ end,
3705
+ length
3706
+ });
3707
+ totalLength += length;
3708
+ }
3709
+ const targetLength = totalLength * Math.max(0, Math.min(1, position));
3710
+ let accumulated = 0;
3711
+ for (const segment of segments) {
3712
+ if (accumulated + segment.length >= targetLength) {
3713
+ const segmentPosition = segment.length > 0 ? (targetLength - accumulated) / segment.length : 0;
3714
+ return {
3715
+ point: {
3716
+ x: segment.start.x + segmentPosition * (segment.end.x - segment.start.x),
3717
+ y: segment.start.y + segmentPosition * (segment.end.y - segment.start.y)
3718
+ },
3719
+ angle: Math.atan2(segment.end.y - segment.start.y, segment.end.x - segment.start.x)
3720
+ };
3721
+ }
3722
+ accumulated += segment.length;
3723
+ }
3724
+ const lastSegment = segments[segments.length - 1];
3725
+ return {
3726
+ point: lastSegment.end,
3727
+ angle: Math.atan2(lastSegment.end.y - lastSegment.start.y, lastSegment.end.x - lastSegment.start.x)
3728
+ };
3729
+ }
3730
+ /** Map tangent angle to a cardinal direction used by polyline routing. */
3731
+ function directionFromAngle(angle) {
3732
+ const twoPi = Math.PI * 2;
3733
+ const a = (angle % twoPi + twoPi) % twoPi;
3734
+ if (a >= Math.PI * .25 && a < Math.PI * .75) return "bottom";
3735
+ if (a >= Math.PI * .75 && a < Math.PI * 1.25) return "left";
3736
+ if (a >= Math.PI * 1.25 && a < Math.PI * 1.75) return "top";
3737
+ return "right";
3738
+ }
3739
+ /**
3740
+ * Nearest point on an edge path to `point`, by sampling along normalized length.
3741
+ */
3742
+ function getClosestPointOnPath(path, type, point, samples = 48) {
3743
+ if (path.length < 2) return null;
3744
+ let bestPoint = getPathPointAt(path, type, 0).point;
3745
+ let bestParam = 0;
3746
+ let bestDistSq = Infinity;
3747
+ for (let i = 0; i <= samples; i++) {
3748
+ const pathParam = i / samples;
3749
+ const at = getPathPointAt(path, type, pathParam).point;
3750
+ const dx = at.x - point.x;
3751
+ const dy = at.y - point.y;
3752
+ const distSq = dx * dx + dy * dy;
3753
+ if (distSq < bestDistSq) {
3754
+ bestDistSq = distSq;
3755
+ bestPoint = at;
3756
+ bestParam = pathParam;
3757
+ }
3758
+ }
3759
+ return {
3760
+ point: bestPoint,
3761
+ pathParam: bestParam,
3762
+ distance: Math.sqrt(bestDistSq)
3763
+ };
3764
+ }
3765
+ //#endregion
3591
3766
  //#region src/elements/paths/StraightPathStrategy.ts
3592
3767
  /**
3593
3768
  * Straight line path strategy
@@ -4696,69 +4871,6 @@ function getEdgeLabelRotation(options) {
4696
4871
  if (angle < -Math.PI / 2) angle += Math.PI;
4697
4872
  return angle;
4698
4873
  }
4699
- function getPathPointAt(path, type, position) {
4700
- if (type !== "bezier" || path.length < 4) return getPointAlongPolyline(path, position);
4701
- const samples = [];
4702
- const steps = 20;
4703
- for (let i = 1; i + 2 < path.length; i += 3) {
4704
- const start = path[i - 1];
4705
- const control1 = path[i];
4706
- const control2 = path[i + 1];
4707
- const end = path[i + 2];
4708
- for (let sample = 0; sample <= steps; sample++) {
4709
- const t = sample / steps;
4710
- if (samples.length > 0 && t === 0) continue;
4711
- samples.push(bezierPoint(start, control1, control2, end, t));
4712
- }
4713
- }
4714
- return getPointAlongPolyline(samples, position);
4715
- }
4716
- function getPointAlongPolyline(path, position) {
4717
- if (path.length === 0) return {
4718
- point: {
4719
- x: 0,
4720
- y: 0
4721
- },
4722
- angle: 0
4723
- };
4724
- if (path.length === 1) return {
4725
- point: path[0],
4726
- angle: 0
4727
- };
4728
- let totalLength = 0;
4729
- const segments = [];
4730
- for (let i = 1; i < path.length; i++) {
4731
- const start = path[i - 1];
4732
- const end = path[i];
4733
- const length = Math.hypot(end.x - start.x, end.y - start.y);
4734
- segments.push({
4735
- start,
4736
- end,
4737
- length
4738
- });
4739
- totalLength += length;
4740
- }
4741
- const targetLength = totalLength * Math.max(0, Math.min(1, position));
4742
- let accumulated = 0;
4743
- for (const segment of segments) {
4744
- if (accumulated + segment.length >= targetLength) {
4745
- const segmentPosition = segment.length > 0 ? (targetLength - accumulated) / segment.length : 0;
4746
- return {
4747
- point: {
4748
- x: segment.start.x + segmentPosition * (segment.end.x - segment.start.x),
4749
- y: segment.start.y + segmentPosition * (segment.end.y - segment.start.y)
4750
- },
4751
- angle: Math.atan2(segment.end.y - segment.start.y, segment.end.x - segment.start.x)
4752
- };
4753
- }
4754
- accumulated += segment.length;
4755
- }
4756
- const lastSegment = segments[segments.length - 1];
4757
- return {
4758
- point: lastSegment.end,
4759
- angle: Math.atan2(lastSegment.end.y - lastSegment.start.y, lastSegment.end.x - lastSegment.start.x)
4760
- };
4761
- }
4762
4874
  //#endregion
4763
4875
  //#region src/elements/Edge.ts
4764
4876
  var DEFAULT_EDGE_STYLE = {
@@ -4825,6 +4937,7 @@ var Edge = class extends Element {
4825
4937
  }
4826
4938
  set from(value) {
4827
4939
  this._from = value;
4940
+ this._bindingListener?.();
4828
4941
  this.markDirty();
4829
4942
  }
4830
4943
  /**
@@ -4835,9 +4948,17 @@ var Edge = class extends Element {
4835
4948
  }
4836
4949
  set to(value) {
4837
4950
  this._to = value;
4951
+ this._bindingListener?.();
4838
4952
  this.markDirty();
4839
4953
  }
4840
4954
  /**
4955
+ * Notified when from/to binding changes (ports / outline params / node ids).
4956
+ * Used by DiagramRenderer to resync endpoints without doing so on every pan frame.
4957
+ */
4958
+ setBindingListener(listener) {
4959
+ this._bindingListener = listener;
4960
+ }
4961
+ /**
4841
4962
  * Edge type
4842
4963
  */
4843
4964
  get type() {
@@ -5102,6 +5223,10 @@ var Edge = class extends Element {
5102
5223
  * @param toDir Direction at end point (top, right, bottom, left)
5103
5224
  */
5104
5225
  updateEndpoints(fromPoint, toPoint, fromDir, toDir, options) {
5226
+ if (this._path.length > 0 && this._fromPoint.x === fromPoint.x && this._fromPoint.y === fromPoint.y && this._toPoint.x === toPoint.x && this._toPoint.y === toPoint.y && this._fromDir === fromDir && this._toDir === toDir) {
5227
+ this._pathOptions = options;
5228
+ return;
5229
+ }
5105
5230
  this._fromPoint = fromPoint;
5106
5231
  this._toPoint = toPoint;
5107
5232
  this._fromDir = fromDir;
@@ -5398,6 +5523,19 @@ var Edge = class extends Element {
5398
5523
  });
5399
5524
  }
5400
5525
  /**
5526
+ * Point and tangent on the current path at a normalized length position (0..1).
5527
+ */
5528
+ getPointAt(pathParam = .5) {
5529
+ if (this._path.length < 2) return null;
5530
+ return getPathPointAt(this._path, this._type, pathParam);
5531
+ }
5532
+ /**
5533
+ * Nearest point on this edge's path to `point` (world coordinates).
5534
+ */
5535
+ getClosestPointOnPath(point) {
5536
+ return getClosestPointOnPath(this._path, this._type, point);
5537
+ }
5538
+ /**
5401
5539
  * Get world position of label center along path.
5402
5540
  */
5403
5541
  getLabelPosition() {
@@ -5592,22 +5730,27 @@ var ClipboardManager = class {
5592
5730
  nodeIds.add(node.id);
5593
5731
  }
5594
5732
  }
5595
- for (const edge of this.renderer.edges.values()) if (nodeIds.has(edge.from.nodeId) && nodeIds.has(edge.to.nodeId)) edges.push({
5596
- id: edge.id,
5597
- from: edge.from,
5598
- to: edge.to,
5599
- type: edge.type,
5600
- controlPoints: edge.controlPoints,
5601
- arrowType: edge.arrowType,
5602
- style: edge.style,
5603
- styleClass: edge.styleClass,
5604
- label: edge.label?.text,
5605
- labelStyleClass: edge.label?.styleClass,
5606
- labelOffset: edge.labelOffset !== 0 ? edge.labelOffset : void 0,
5607
- labelBackground: edge.labelBackground,
5608
- labelLineGap: edge.labelLineGap ? true : void 0,
5609
- data: Object.keys(edge.data).length > 0 ? edge.data : void 0
5610
- });
5733
+ for (const edge of this.renderer.edges.values()) {
5734
+ const fromId = edge.from.nodeId;
5735
+ const toId = edge.to.nodeId;
5736
+ if (!fromId || !toId || !nodeIds.has(fromId) || !nodeIds.has(toId)) continue;
5737
+ edges.push({
5738
+ id: edge.id,
5739
+ from: edge.from,
5740
+ to: edge.to,
5741
+ type: edge.type,
5742
+ controlPoints: edge.controlPoints,
5743
+ arrowType: edge.arrowType,
5744
+ style: edge.style,
5745
+ styleClass: edge.styleClass,
5746
+ label: edge.label?.text,
5747
+ labelStyleClass: edge.label?.styleClass,
5748
+ labelOffset: edge.labelOffset !== 0 ? edge.labelOffset : void 0,
5749
+ labelBackground: edge.labelBackground,
5750
+ labelLineGap: edge.labelLineGap ? true : void 0,
5751
+ data: Object.keys(edge.data).length > 0 ? edge.data : void 0
5752
+ });
5753
+ }
5611
5754
  this.clipboard = {
5612
5755
  nodes,
5613
5756
  edges
@@ -5634,8 +5777,11 @@ var ClipboardManager = class {
5634
5777
  newNodes.push(this.nodeFactory(copyData));
5635
5778
  }
5636
5779
  for (const edgeData of this.clipboard.edges) {
5637
- const fromNodeId = idMap.get(edgeData.from.nodeId);
5638
- const toNodeId = idMap.get(edgeData.to.nodeId);
5780
+ const fromKey = edgeData.from.nodeId;
5781
+ const toKey = edgeData.to.nodeId;
5782
+ if (!fromKey || !toKey) continue;
5783
+ const fromNodeId = idMap.get(fromKey);
5784
+ const toNodeId = idMap.get(toKey);
5639
5785
  if (!fromNodeId || !toNodeId) continue;
5640
5786
  const newId = `${edgeData.id}_copy_${Date.now()}`;
5641
5787
  const copyEdge = {
@@ -5837,7 +5983,9 @@ var InteractionManager = class {
5837
5983
  const idSet = new Set(nodeIds);
5838
5984
  for (const edge of this.renderer.edges.values()) {
5839
5985
  if (!edge.hasEditableControlPoints()) continue;
5840
- if (!idSet.has(edge.from.nodeId) && !idSet.has(edge.to.nodeId)) continue;
5986
+ const fromId = edge.from.nodeId;
5987
+ const toId = edge.to.nodeId;
5988
+ if ((!fromId || !idSet.has(fromId)) && (!toId || !idSet.has(toId))) continue;
5841
5989
  if (this.dragStartEditablePolylinePoints.has(edge.id)) continue;
5842
5990
  this.dragStartEditablePolylinePoints.set(edge.id, clonePoints(edge.controlPoints));
5843
5991
  }
@@ -6385,10 +6533,14 @@ var InteractionManager = class {
6385
6533
  nodeGroupIds.set(child.id, list);
6386
6534
  }
6387
6535
  }
6388
- for (const edge of this.renderer.edges.values()) if (nodeIds.has(edge.from.nodeId) || nodeIds.has(edge.to.nodeId)) {
6389
- if (!edgeIds.has(edge.id)) {
6390
- edgeIds.add(edge.id);
6391
- edges.push(edge);
6536
+ for (const edge of this.renderer.edges.values()) {
6537
+ const fromId = edge.from.nodeId;
6538
+ const toId = edge.to.nodeId;
6539
+ if (fromId && nodeIds.has(fromId) || toId && nodeIds.has(toId)) {
6540
+ if (!edgeIds.has(edge.id)) {
6541
+ edgeIds.add(edge.id);
6542
+ edges.push(edge);
6543
+ }
6392
6544
  }
6393
6545
  }
6394
6546
  return new CompositeCommand([{
@@ -6411,8 +6563,15 @@ var InteractionManager = class {
6411
6563
  }]);
6412
6564
  }
6413
6565
  endpointsEqual(a, b) {
6414
- if (a.nodeId !== b.nodeId) return false;
6566
+ if ((a.edgeId ?? null) !== (b.edgeId ?? null)) return false;
6567
+ if ((a.nodeId ?? null) !== (b.nodeId ?? null)) return false;
6415
6568
  if ((a.portId ?? null) !== (b.portId ?? null)) return false;
6569
+ const aPath = a.pathParam;
6570
+ const bPath = b.pathParam;
6571
+ if (aPath !== void 0 || bPath !== void 0) {
6572
+ if (aPath === void 0 || bPath === void 0) return false;
6573
+ if (Math.abs(aPath - bPath) >= 1e-9) return false;
6574
+ }
6416
6575
  const aParam = a.outlineParam;
6417
6576
  const bParam = b.outlineParam;
6418
6577
  if (aParam === void 0 && bParam === void 0) return true;
@@ -6812,7 +6971,16 @@ var ContextMenuManager = class extends EventEmitter {
6812
6971
  }
6813
6972
  };
6814
6973
  //#endregion
6974
+ //#region src/types.ts
6975
+ function isNodeEdgeEndpoint(endpoint) {
6976
+ return typeof endpoint.nodeId === "string" && endpoint.nodeId.length > 0;
6977
+ }
6978
+ function isEdgeEdgeEndpoint(endpoint) {
6979
+ return typeof endpoint.edgeId === "string" && endpoint.edgeId.length > 0;
6980
+ }
6981
+ //#endregion
6815
6982
  //#region src/core/EdgeEndpointUpdater.ts
6983
+ var EDGE_ATTACH_MAX_PASSES = 3;
6816
6984
  var EdgeEndpointUpdater = class {
6817
6985
  constructor(host) {
6818
6986
  this.host = host;
@@ -6826,26 +6994,73 @@ var EdgeEndpointUpdater = class {
6826
6994
  update(includeRoutingObstacles) {
6827
6995
  const obstacles = includeRoutingObstacles ? this.host.getNodeObstacles() : void 0;
6828
6996
  const nodes = this.host.getNodes();
6829
- for (const edge of this.host.getEdges().values()) {
6997
+ const edges = this.host.getEdges();
6998
+ const nodeNode = [];
6999
+ const edgeAttached = [];
7000
+ for (const edge of edges.values()) {
6830
7001
  if (!edge.autoUpdateEndpoints) continue;
6831
- const fromNode = nodes.get(edge.from.nodeId);
6832
- const toNode = nodes.get(edge.to.nodeId);
6833
- if (!fromNode || !toNode) continue;
6834
- if (includeRoutingObstacles) this.lockAnchors(edge, fromNode, toNode);
6835
- const from = this.resolveEndpoint(fromNode, edge.from.portId, edge.from.outlineParam);
6836
- const to = this.resolveEndpoint(toNode, edge.to.portId, edge.to.outlineParam);
6837
- edge.updateEndpoints(from.point, to.point, from.direction, to.direction, obstacles ? { obstacles } : void 0);
7002
+ if (isEdgeEdgeEndpoint(edge.from) || isEdgeEdgeEndpoint(edge.to)) edgeAttached.push(edge);
7003
+ else nodeNode.push(edge);
7004
+ }
7005
+ for (const edge of nodeNode) this.updateNodeNodeEdge(edge, nodes, includeRoutingObstacles, obstacles);
7006
+ for (let pass = 0; pass < EDGE_ATTACH_MAX_PASSES; pass++) {
7007
+ let changed = false;
7008
+ for (const edge of edgeAttached) if (this.updateEdgeAttachedEdge(edge, nodes, edges, obstacles)) changed = true;
7009
+ if (!changed) break;
7010
+ }
7011
+ }
7012
+ updateNodeNodeEdge(edge, nodes, includeRoutingObstacles, obstacles) {
7013
+ if (!isNodeEdgeEndpoint(edge.from) || !isNodeEdgeEndpoint(edge.to)) return;
7014
+ const fromNode = nodes.get(edge.from.nodeId);
7015
+ const toNode = nodes.get(edge.to.nodeId);
7016
+ if (!fromNode || !toNode) return;
7017
+ if (includeRoutingObstacles) this.lockAnchors(edge, fromNode, toNode);
7018
+ const from = this.resolveNodeEndpoint(fromNode, edge.from);
7019
+ const to = this.resolveNodeEndpoint(toNode, edge.to);
7020
+ edge.updateEndpoints(from.point, to.point, from.direction, to.direction, obstacles ? { obstacles } : void 0);
7021
+ }
7022
+ /**
7023
+ * @returns true when the resolved geometry changed enough to warrant another pass
7024
+ */
7025
+ updateEdgeAttachedEdge(edge, nodes, edges, obstacles) {
7026
+ const from = this.resolveAnyEndpoint(edge.from, nodes, edges);
7027
+ const to = this.resolveAnyEndpoint(edge.to, nodes, edges);
7028
+ if (!from || !to) return false;
7029
+ const before = edge.path;
7030
+ const beforeKey = before.length >= 2 ? `${before[0].x},${before[0].y}:${before[before.length - 1].x},${before[before.length - 1].y}` : "";
7031
+ edge.updateEndpoints(from.point, to.point, from.direction, to.direction, obstacles ? { obstacles } : void 0);
7032
+ const after = edge.path;
7033
+ return beforeKey !== (after.length >= 2 ? `${after[0].x},${after[0].y}:${after[after.length - 1].x},${after[after.length - 1].y}` : "");
7034
+ }
7035
+ resolveAnyEndpoint(endpoint, nodes, edges) {
7036
+ if (isEdgeEdgeEndpoint(endpoint)) return this.resolveEdgeEndpoint(endpoint, edges);
7037
+ if (isNodeEdgeEndpoint(endpoint)) {
7038
+ const node = nodes.get(endpoint.nodeId);
7039
+ if (!node) return null;
7040
+ return this.resolveNodeEndpoint(node, endpoint);
6838
7041
  }
7042
+ return null;
7043
+ }
7044
+ resolveEdgeEndpoint(endpoint, edges) {
7045
+ const host = edges.get(endpoint.edgeId);
7046
+ if (!host) return null;
7047
+ const pathParam = endpoint.pathParam ?? .5;
7048
+ const at = host.getPointAt(pathParam);
7049
+ if (!at) return null;
7050
+ return {
7051
+ point: at.point,
7052
+ direction: directionFromAngle(at.angle)
7053
+ };
6839
7054
  }
6840
7055
  lockAnchors(edge, fromNode, toNode) {
6841
- if (edge.lockAnchors && edge.from.outlineParam === void 0 && !edge.from.portId) {
7056
+ if (isNodeEdgeEndpoint(edge.from) && edge.lockAnchors && edge.from.outlineParam === void 0 && !edge.from.portId) {
6842
7057
  const anchor = fromNode.getNearestAnchor(toNode.getCenter());
6843
7058
  if (anchor) edge.from = {
6844
7059
  ...edge.from,
6845
7060
  portId: `${ANCHOR_PORT_PREFIX}${anchor.id}`
6846
7061
  };
6847
7062
  }
6848
- if (edge.lockAnchors && edge.to.outlineParam === void 0 && !edge.to.portId) {
7063
+ if (isNodeEdgeEndpoint(edge.to) && edge.lockAnchors && edge.to.outlineParam === void 0 && !edge.to.portId) {
6849
7064
  const anchor = toNode.getNearestAnchor(fromNode.getCenter());
6850
7065
  if (anchor) edge.to = {
6851
7066
  ...edge.to,
@@ -6853,18 +7068,18 @@ var EdgeEndpointUpdater = class {
6853
7068
  };
6854
7069
  }
6855
7070
  }
6856
- resolveEndpoint(node, portId, outlineParam) {
6857
- if (portId) {
6858
- const anchorId = portId.slice(ANCHOR_PORT_PREFIX.length);
7071
+ resolveNodeEndpoint(node, endpoint) {
7072
+ if (endpoint.portId) {
7073
+ const anchorId = endpoint.portId.slice(ANCHOR_PORT_PREFIX.length);
6859
7074
  const point = node.getAnchorPointById(anchorId);
6860
7075
  return point ? {
6861
7076
  point,
6862
7077
  direction: anchorId.split(":")[0]
6863
7078
  } : { point: node.getCenter() };
6864
7079
  }
6865
- if (outlineParam !== void 0) return {
6866
- point: node.getConnectionPointAtOutlineParam(outlineParam),
6867
- direction: getDirectionFromOutlineParam(outlineParam)
7080
+ if (endpoint.outlineParam !== void 0) return {
7081
+ point: node.getConnectionPointAtOutlineParam(endpoint.outlineParam),
7082
+ direction: getDirectionFromOutlineParam(endpoint.outlineParam)
6868
7083
  };
6869
7084
  return { point: node.getCenter() };
6870
7085
  }
@@ -7232,6 +7447,7 @@ var DiagramRenderer = class extends EventEmitter {
7232
7447
  this._offsetY = 0;
7233
7448
  this._dirty = true;
7234
7449
  this._styleDirty = true;
7450
+ this._contentDirty = true;
7235
7451
  this._destroyed = false;
7236
7452
  this._canvasRect = null;
7237
7453
  this._nodeObstaclesCache = null;
@@ -7536,12 +7752,11 @@ var DiagramRenderer = class extends EventEmitter {
7536
7752
  * Add a node to the diagram
7537
7753
  */
7538
7754
  addNode(node) {
7539
- node.setDirtyListener(() => this.markDirty());
7755
+ node.setDirtyListener(() => this.markContentDirty());
7540
7756
  node.setAttachToOutlineGetter(() => this._attachToOutline);
7541
7757
  this._nodes.set(node.id, node);
7542
- this._nodeObstaclesCache = null;
7543
7758
  this.animationManager.registerEnter(node.id);
7544
- this.markDirty();
7759
+ this.markContentDirty();
7545
7760
  this.emit("nodeAdd", node);
7546
7761
  }
7547
7762
  /**
@@ -7572,11 +7787,12 @@ var DiagramRenderer = class extends EventEmitter {
7572
7787
  */
7573
7788
  addEdge(edge) {
7574
7789
  edge.setDirtyListener(() => this.markDirty());
7790
+ edge.setBindingListener(() => this.markContentDirty());
7575
7791
  this._edges.set(edge.id, edge);
7576
- this._updateEdgeIndex(edge.id, edge.from.nodeId, true);
7577
- this._updateEdgeIndex(edge.id, edge.to.nodeId, true);
7792
+ if (edge.from.nodeId) this._updateEdgeIndex(edge.id, edge.from.nodeId, true);
7793
+ if (edge.to.nodeId) this._updateEdgeIndex(edge.id, edge.to.nodeId, true);
7578
7794
  this.animationManager.registerEnter(edge.id);
7579
- this.markDirty();
7795
+ this.markContentDirty();
7580
7796
  this.emit("edgeAdd", edge);
7581
7797
  }
7582
7798
  /**
@@ -7615,21 +7831,27 @@ var DiagramRenderer = class extends EventEmitter {
7615
7831
  if (edgesToRemove) for (const edgeId of Array.from(edgesToRemove)) this.removeEdgeImmediate(edgeId);
7616
7832
  for (const group of this._groups.values()) group.removeChild(nodeId);
7617
7833
  this._nodes.delete(nodeId);
7618
- this._nodeObstaclesCache = null;
7619
7834
  node.setDirtyListener(void 0);
7620
7835
  node.setAttachToOutlineGetter(void 0);
7621
- this.markDirty();
7836
+ this.markContentDirty();
7622
7837
  this.emit("nodeRemove", node);
7623
7838
  return true;
7624
7839
  }
7625
7840
  removeEdgeImmediate(edgeId) {
7626
7841
  const edge = this._edges.get(edgeId);
7627
7842
  if (edge === void 0) return false;
7628
- this._updateEdgeIndex(edgeId, edge.from.nodeId, false);
7629
- this._updateEdgeIndex(edgeId, edge.to.nodeId, false);
7843
+ const dependents = [];
7844
+ for (const other of this._edges.values()) {
7845
+ if (other.id === edgeId) continue;
7846
+ if (other.from.edgeId === edgeId || other.to.edgeId === edgeId) dependents.push(other.id);
7847
+ }
7848
+ for (const dependentId of dependents) this.removeEdgeImmediate(dependentId);
7849
+ if (edge.from.nodeId) this._updateEdgeIndex(edgeId, edge.from.nodeId, false);
7850
+ if (edge.to.nodeId) this._updateEdgeIndex(edgeId, edge.to.nodeId, false);
7630
7851
  this._edges.delete(edgeId);
7631
7852
  edge.setDirtyListener(void 0);
7632
- this.markDirty();
7853
+ edge.setBindingListener(void 0);
7854
+ this.markContentDirty();
7633
7855
  this.emit("edgeRemove", edge);
7634
7856
  return true;
7635
7857
  }
@@ -7709,12 +7931,22 @@ var DiagramRenderer = class extends EventEmitter {
7709
7931
  for (const n of this._nodes.values()) n.setBadgeHover(n.id === hoveredNodeId ? hoveredIndex : -1);
7710
7932
  }
7711
7933
  /**
7712
- * Mark the diagram as needing re-render
7934
+ * Mark the diagram as needing re-render (viewport / cosmetics).
7935
+ * Does not resync edge endpoints — use {@link markContentDirty} after layout changes.
7713
7936
  */
7714
7937
  markDirty() {
7715
7938
  this._dirty = true;
7716
7939
  }
7717
7940
  /**
7941
+ * Mark that node/edge layout changed and edge endpoints must be recalculated.
7942
+ * Pan/zoom should call {@link markDirty} only so path routing is not redone every frame.
7943
+ */
7944
+ markContentDirty() {
7945
+ this._contentDirty = true;
7946
+ this._nodeObstaclesCache = null;
7947
+ this._dirty = true;
7948
+ }
7949
+ /**
7718
7950
  * Force an immediate render
7719
7951
  */
7720
7952
  render() {
@@ -7792,7 +8024,6 @@ var DiagramRenderer = class extends EventEmitter {
7792
8024
  }
7793
8025
  renderFrame(now) {
7794
8026
  this.frameTime = now;
7795
- this._nodeObstaclesCache = null;
7796
8027
  const ctx = this.ctx;
7797
8028
  const { width, height, backgroundColor } = this.options;
7798
8029
  ctx.setTransform(this.devicePixelRatio, 0, 0, this.devicePixelRatio, 0, 0);
@@ -7810,7 +8041,10 @@ var DiagramRenderer = class extends EventEmitter {
7810
8041
  this.renderElementWithAnimation(ctx, group, () => group.render(ctx));
7811
8042
  group.clearDirty();
7812
8043
  }
7813
- this.edgeEndpointUpdater.updateAll();
8044
+ if (this._contentDirty) {
8045
+ this.edgeEndpointUpdater.updateAll();
8046
+ this._contentDirty = false;
8047
+ }
7814
8048
  for (const node of this._nodes.values()) if (node.visible) {
7815
8049
  this.renderElementWithAnimation(ctx, node, () => node.render(ctx));
7816
8050
  node.clearDirty();
@@ -8054,8 +8288,10 @@ var SearchManager = class {
8054
8288
  for (const edge of this.renderer.edges.values()) {
8055
8289
  if (scope === "nodes") continue;
8056
8290
  const matchesEdge = this.matchesEdge(edge, options);
8057
- const fromVisible = this.renderer.getNode(edge.from.nodeId)?.visible ?? true;
8058
- const toVisible = this.renderer.getNode(edge.to.nodeId)?.visible ?? true;
8291
+ const fromId = edge.from.nodeId;
8292
+ const toId = edge.to.nodeId;
8293
+ const fromVisible = fromId ? this.renderer.getNode(fromId)?.visible ?? true : true;
8294
+ const toVisible = toId ? this.renderer.getNode(toId)?.visible ?? true : true;
8059
8295
  edge.visible = matchesEdge && fromVisible && toVisible;
8060
8296
  }
8061
8297
  this.renderer.markDirty();
@@ -8414,7 +8650,7 @@ var svgTextCache = new LRUCache(100);
8414
8650
  function fetchSvgText(url) {
8415
8651
  let promise = svgTextCache.get(url);
8416
8652
  if (!promise) {
8417
- promise = fetch(url).then((r) => r.ok ? r.text() : "");
8653
+ promise = fetch(url).then((r) => r.ok ? r.text() : "").catch(() => "");
8418
8654
  svgTextCache.set(url, promise);
8419
8655
  }
8420
8656
  return promise;
@@ -8432,6 +8668,32 @@ function readSvgFromUrlSync(url) {
8432
8668
  } catch {}
8433
8669
  return null;
8434
8670
  }
8671
+ /**
8672
+ * Resolve a source string to an href suitable for Image/SVG (async, with tint).
8673
+ */
8674
+ async function resolveTintedSvgDataUrl(source, options = {}) {
8675
+ const { strokeColor, fillColor } = options;
8676
+ if (isSvgMarkup(source)) return svgToDataUrl(tintSvg(source, strokeColor, fillColor));
8677
+ if (isSvgUrl(source) || source.toLowerCase().endsWith(".svg")) {
8678
+ const svgText = await fetchSvgText(source);
8679
+ if (!svgText) return null;
8680
+ return svgToDataUrl(tintSvg(svgText, strokeColor, fillColor));
8681
+ }
8682
+ return null;
8683
+ }
8684
+ /**
8685
+ * Resolve a source to a tinted data URL synchronously (SvgExporter path).
8686
+ */
8687
+ function resolveTintedSvgDataUrlSync(source, options = {}) {
8688
+ const { strokeColor, fillColor } = options;
8689
+ if (isSvgMarkup(source)) return svgToDataUrl(tintSvg(source, strokeColor, fillColor));
8690
+ if (source.toLowerCase().endsWith(".svg")) {
8691
+ const svgText = readSvgFromUrlSync(source);
8692
+ if (!svgText) return null;
8693
+ return svgToDataUrl(tintSvg(svgText, strokeColor, fillColor));
8694
+ }
8695
+ return null;
8696
+ }
8435
8697
  //#endregion
8436
8698
  //#region src/utils/iconLayout.ts
8437
8699
  /**
@@ -8616,22 +8878,20 @@ var NodeImage = class {
8616
8878
  if (source instanceof HTMLImageElement) return;
8617
8879
  const version = ++this._sourceVersion;
8618
8880
  if (isSvgMarkup(source)) {
8619
- const tinted = tintSvg(source, this._options.strokeColor, this._options.fillColor);
8881
+ const resolvedSource = resolveTintedSvgDataUrlSync(source, {
8882
+ strokeColor: this._options.strokeColor,
8883
+ fillColor: this._options.fillColor
8884
+ });
8620
8885
  if (version !== this._sourceVersion) return;
8621
- this._image.src = svgToDataUrl(tinted);
8886
+ this._image.src = resolvedSource ?? source;
8622
8887
  return;
8623
8888
  }
8624
- if ((!!this._options.strokeColor || !!this._options.fillColor) && source.toLowerCase().endsWith(".svg")) {
8625
- const svgText = await fetchSvgText(source);
8626
- if (version !== this._sourceVersion) return;
8627
- if (svgText) {
8628
- const tinted = tintSvg(svgText, this._options.strokeColor, this._options.fillColor);
8629
- this._image.src = svgToDataUrl(tinted);
8630
- return;
8631
- }
8632
- }
8889
+ const resolvedSource = await resolveTintedSvgDataUrl(source, {
8890
+ strokeColor: this._options.strokeColor,
8891
+ fillColor: this._options.fillColor
8892
+ });
8633
8893
  if (version !== this._sourceVersion) return;
8634
- this._image.src = source;
8894
+ this._image.src = resolvedSource ?? source;
8635
8895
  }
8636
8896
  attachHandlers() {
8637
8897
  if (this._image.complete && this._image.naturalWidth > 0) {
@@ -10703,16 +10963,8 @@ var CIcon = class extends CompositeComponentBase {
10703
10963
  }
10704
10964
  }
10705
10965
  async loadSource(source) {
10706
- if (isSvgMarkup(source)) {
10707
- const tinted = this._fillColor ? tintSvg(source, void 0, this._fillColor) : source;
10708
- this._image.src = svgToDataUrl(tinted);
10709
- } else if (isSvgUrl(source) && this._fillColor) try {
10710
- const tinted = tintSvg(await fetchSvgText(source), void 0, this._fillColor);
10711
- this._image.src = svgToDataUrl(tinted);
10712
- } catch {
10713
- this._image.src = source;
10714
- }
10715
- else this._image.src = source;
10966
+ const resolvedSource = await resolveTintedSvgDataUrl(source, { fillColor: this._fillColor });
10967
+ this._image.src = resolvedSource ?? source;
10716
10968
  }
10717
10969
  measure(_ctx) {
10718
10970
  return {
@@ -10761,7 +11013,8 @@ var CIcon = class extends CompositeComponentBase {
10761
11013
  const drawHeight = Math.min(this._height, bounds.height);
10762
11014
  const dx = bounds.x + (bounds.width - drawWidth) / 2;
10763
11015
  const dy = bounds.y + (bounds.height - drawHeight) / 2;
10764
- svg += `<image href="${escapeXmlAttr(this._source)}" x="${dx}" y="${dy}" width="${drawWidth}" height="${drawHeight}" />`;
11016
+ const source = resolveTintedSvgDataUrlSync(this._source, { fillColor: this._fillColor }) ?? this._source;
11017
+ svg += `<image href="${escapeXmlAttr(source)}" x="${dx}" y="${dy}" width="${drawWidth}" height="${drawHeight}" />`;
10765
11018
  return svg;
10766
11019
  }
10767
11020
  };
@@ -12180,9 +12433,14 @@ var Serializer = class {
12180
12433
  if (!Array.isArray(data.nodes)) throw new SerializerValidationError("Invalid or missing nodes array");
12181
12434
  if (!Array.isArray(data.edges)) throw new SerializerValidationError("Invalid or missing edges array");
12182
12435
  const nodeIds = new Set(data.nodes.map((n) => n.id));
12436
+ const edgeIds = new Set(data.edges.map((e) => e.id));
12183
12437
  for (const edge of data.edges) {
12184
- if (!nodeIds.has(edge.from.nodeId)) throw new SerializerValidationError(`Edge "${edge.id}" references non-existent source node "${edge.from.nodeId}"`);
12185
- if (!nodeIds.has(edge.to.nodeId)) throw new SerializerValidationError(`Edge "${edge.id}" references non-existent target node "${edge.to.nodeId}"`);
12438
+ if (edge.from.edgeId) {
12439
+ if (!edgeIds.has(edge.from.edgeId)) throw new SerializerValidationError(`Edge "${edge.id}" references non-existent host edge "${edge.from.edgeId}"`);
12440
+ } else if (!edge.from.nodeId || !nodeIds.has(edge.from.nodeId)) throw new SerializerValidationError(`Edge "${edge.id}" references non-existent source node "${edge.from.nodeId}"`);
12441
+ if (edge.to.edgeId) {
12442
+ if (!edgeIds.has(edge.to.edgeId)) throw new SerializerValidationError(`Edge "${edge.id}" references non-existent host edge "${edge.to.edgeId}"`);
12443
+ } else if (!edge.to.nodeId || !nodeIds.has(edge.to.nodeId)) throw new SerializerValidationError(`Edge "${edge.id}" references non-existent target node "${edge.to.nodeId}"`);
12186
12444
  }
12187
12445
  }
12188
12446
  /**
@@ -12965,12 +13223,10 @@ var SvgExporter = class {
12965
13223
  const source = opts.source;
12966
13224
  if (source instanceof HTMLImageElement) return source.src;
12967
13225
  if (!source) return "";
12968
- if (isSvgMarkup(source)) return svgToDataUrl(tintSvg(source, opts.strokeColor, opts.fillColor));
12969
- if (source.toLowerCase().endsWith(".svg")) {
12970
- const svgText = readSvgFromUrlSync(source);
12971
- if (svgText) return svgToDataUrl(tintSvg(svgText, opts.strokeColor, opts.fillColor));
12972
- }
12973
- return source;
13226
+ return resolveTintedSvgDataUrlSync(source, {
13227
+ strokeColor: opts.strokeColor,
13228
+ fillColor: opts.fillColor
13229
+ }) ?? source;
12974
13230
  }
12975
13231
  escapeAttribute(value) {
12976
13232
  return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
@@ -13027,8 +13283,11 @@ var AutoRouting = class {
13027
13283
  const type = options.type ?? "polyline";
13028
13284
  for (const edge of renderer.edges.values()) {
13029
13285
  edge.type = type;
13030
- const fromNode = renderer.getNode(edge.from.nodeId);
13031
- const toNode = renderer.getNode(edge.to.nodeId);
13286
+ const fromId = edge.from.nodeId;
13287
+ const toId = edge.to.nodeId;
13288
+ if (!fromId || !toId) continue;
13289
+ const fromNode = renderer.getNode(fromId);
13290
+ const toNode = renderer.getNode(toId);
13032
13291
  if (!fromNode || !toNode) continue;
13033
13292
  const fromPoint = edge.from.portId !== void 0 ? fromNode.getPortPosition(edge.from.portId) ?? fromNode.getCenter() : fromNode.getCenter();
13034
13293
  const toPoint = edge.to.portId !== void 0 ? toNode.getPortPosition(edge.to.portId) ?? toNode.getCenter() : toNode.getCenter();
@@ -13503,6 +13762,6 @@ function applyEdgeStyle(ctx, style) {
13503
13762
  return baseOpacity * strokeOpacity;
13504
13763
  }
13505
13764
  //#endregion
13506
- export { AddNodeCommand, AnimationManager, AutoLayout, AutoRouting, BezierPathStrategy, CContainer, CDivider, CIcon, CShape, CText, ChangeEdgePropertiesCommand, ChangeEditablePolylineControlPointsCommand, ChangeGroupPropertiesCommand, ChangeNodePropertiesCommand, CircleNode, ClipboardManager, CompositeCommand, CompositeNode, ConnectionManager, ContextMenuManager, CustomShapeNode, DARK_THEME, DEFAULT_THEME, DiagramRenderer, DiamondNode, DragManager, Edge, Element, EventEmitter, GridOverlay, Group, GuidesOverlay, HistoryManager, ImageExporter, InputHandler, InteractionManager, MiniMap, MoveNodesCommand, NavigationManager, Node, NodeImage, PolylinePathStrategy, Port, PropertyChangeBatcher, RectangleNode, RemoveNodeCommand, ResizeManager, RulersOverlay, SearchManager, SelectionManager, Serializer, ShapeFactories, StraightPathStrategy, StyleManager, SvgExporter, TextLabel, alignNodes, angle, applyEdgeStyle, applyNodeStyle, bezierPoint, boundsCenter, calculateBezierControlPoints, clamp, clonePoints, container, deserializeCComponent, distance, distanceToSegment, distributeNodes, divider, drawRoundedRectPath, expandBounds, flexLayout, generateId, icon, isCornerPlacement, lerp, mergeBounds, normalizeSides, pointInEllipse, pointInRect, rectIntersection, rectUnion, rectsIntersect, renderFillAndStroke, resetIdCounter, resetPortIdCounter, rotatePoint, segmentRectIntersections, shape, snapPointToGrid, snapToGrid, text };
13765
+ export { AddNodeCommand, AnimationManager, AutoLayout, AutoRouting, BezierPathStrategy, CContainer, CDivider, CIcon, CShape, CText, ChangeEdgePropertiesCommand, ChangeEditablePolylineControlPointsCommand, ChangeGroupPropertiesCommand, ChangeNodePropertiesCommand, CircleNode, ClipboardManager, CompositeCommand, CompositeNode, ConnectionManager, ContextMenuManager, CustomShapeNode, DARK_THEME, DEFAULT_THEME, DiagramRenderer, DiamondNode, DragManager, Edge, Element, EventEmitter, GridOverlay, Group, GuidesOverlay, HistoryManager, ImageExporter, InputHandler, InteractionManager, MiniMap, MoveNodesCommand, NavigationManager, Node, NodeImage, PolylinePathStrategy, Port, PropertyChangeBatcher, RectangleNode, RemoveNodeCommand, ResizeManager, RulersOverlay, SearchManager, SelectionManager, Serializer, ShapeFactories, StraightPathStrategy, StyleManager, SvgExporter, TextLabel, alignNodes, angle, applyEdgeStyle, applyNodeStyle, bezierPoint, boundsCenter, calculateBezierControlPoints, clamp, clonePoints, container, deserializeCComponent, directionFromAngle, distance, distanceToSegment, distributeNodes, divider, drawRoundedRectPath, expandBounds, flexLayout, generateId, getClosestPointOnPath, getPathPointAt, icon, isCornerPlacement, isEdgeEdgeEndpoint, isNodeEdgeEndpoint, lerp, mergeBounds, normalizeSides, pointInEllipse, pointInRect, rectIntersection, rectUnion, rectsIntersect, renderFillAndStroke, resetIdCounter, resetPortIdCounter, rotatePoint, segmentRectIntersections, shape, snapPointToGrid, snapToGrid, text };
13507
13766
 
13508
13767
  //# sourceMappingURL=papirus.js.map