orcasvn-react-diagrams 0.2.2 → 0.2.4

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 (83) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/README.md +15 -3
  3. package/dist/cjs/examples.js +2616 -291
  4. package/dist/cjs/index.js +1186 -153
  5. package/dist/cjs/types/api/createDiagramEditor.d.ts +19 -1
  6. package/dist/cjs/types/api/index.d.ts +1 -1
  7. package/dist/cjs/types/api/types.d.ts +41 -0
  8. package/dist/cjs/types/displaybox/DisplayBoxControls.d.ts +5 -1
  9. package/dist/cjs/types/displaybox/demos/AsymmetricPortMultiAnchorDemoTab.d.ts +3 -0
  10. package/dist/cjs/types/displaybox/demos/LayoutLabelReservedSpaceDemoTab.d.ts +3 -0
  11. package/dist/cjs/types/displaybox/demos/VertexControlLinkSessionDemoTab.d.ts +3 -0
  12. package/dist/cjs/types/displaybox/demos/asymmetricPortMultiAnchorDemo.d.ts +31 -0
  13. package/dist/cjs/types/displaybox/demos/labelStyleDemo.d.ts +2 -0
  14. package/dist/cjs/types/displaybox/demos/layoutLabelReservedSpaceDemo.d.ts +11 -0
  15. package/dist/cjs/types/displaybox/demos/portPositionLimitsDemo.d.ts +2 -0
  16. package/dist/cjs/types/displaybox/demos/vertexControlLinkSessionDemo.d.ts +12 -0
  17. package/dist/cjs/types/displaybox/useDemoControls.d.ts +4 -0
  18. package/dist/cjs/types/engine/AutoLayoutService.d.ts +2 -0
  19. package/dist/cjs/types/engine/DiagramEngine.d.ts +11 -0
  20. package/dist/cjs/types/engine/LinkRoutingService.d.ts +9 -1
  21. package/dist/cjs/types/models/PortModel.d.ts +5 -0
  22. package/dist/cjs/types/renderer/RenderTypes.d.ts +3 -1
  23. package/dist/cjs/types/renderer/konva/KonvaInteraction.d.ts +14 -0
  24. package/dist/cjs/types/renderer/konva/KonvaNodeFactory.d.ts +12 -0
  25. package/dist/cjs/types/renderer/konva/KonvaRenderer.d.ts +2 -1
  26. package/dist/cjs/types/shapes/BuiltInShapes.d.ts +3 -1
  27. package/dist/cjs/types/strategies/ObstacleRouter.d.ts +2 -0
  28. package/dist/cjs/types/utils/__tests__/portGeometry.test.d.ts +1 -0
  29. package/dist/cjs/types/utils/portGeometry.d.ts +44 -0
  30. package/dist/esm/examples.js +2617 -292
  31. package/dist/esm/examples.js.map +1 -1
  32. package/dist/esm/index.js +1186 -153
  33. package/dist/esm/index.js.map +1 -1
  34. package/dist/esm/types/api/createDiagramEditor.d.ts +19 -1
  35. package/dist/esm/types/api/index.d.ts +1 -1
  36. package/dist/esm/types/api/types.d.ts +41 -0
  37. package/dist/esm/types/displaybox/DisplayBoxControls.d.ts +5 -1
  38. package/dist/esm/types/displaybox/demos/AsymmetricPortMultiAnchorDemoTab.d.ts +3 -0
  39. package/dist/esm/types/displaybox/demos/LayoutLabelReservedSpaceDemoTab.d.ts +3 -0
  40. package/dist/esm/types/displaybox/demos/VertexControlLinkSessionDemoTab.d.ts +3 -0
  41. package/dist/esm/types/displaybox/demos/asymmetricPortMultiAnchorDemo.d.ts +31 -0
  42. package/dist/esm/types/displaybox/demos/labelStyleDemo.d.ts +2 -0
  43. package/dist/esm/types/displaybox/demos/layoutLabelReservedSpaceDemo.d.ts +11 -0
  44. package/dist/esm/types/displaybox/demos/portPositionLimitsDemo.d.ts +2 -0
  45. package/dist/esm/types/displaybox/demos/vertexControlLinkSessionDemo.d.ts +12 -0
  46. package/dist/esm/types/displaybox/useDemoControls.d.ts +4 -0
  47. package/dist/esm/types/engine/AutoLayoutService.d.ts +2 -0
  48. package/dist/esm/types/engine/DiagramEngine.d.ts +11 -0
  49. package/dist/esm/types/engine/LinkRoutingService.d.ts +9 -1
  50. package/dist/esm/types/models/PortModel.d.ts +5 -0
  51. package/dist/esm/types/renderer/RenderTypes.d.ts +3 -1
  52. package/dist/esm/types/renderer/konva/KonvaInteraction.d.ts +14 -0
  53. package/dist/esm/types/renderer/konva/KonvaNodeFactory.d.ts +12 -0
  54. package/dist/esm/types/renderer/konva/KonvaRenderer.d.ts +2 -1
  55. package/dist/esm/types/shapes/BuiltInShapes.d.ts +3 -1
  56. package/dist/esm/types/strategies/ObstacleRouter.d.ts +2 -0
  57. package/dist/esm/types/utils/__tests__/portGeometry.test.d.ts +1 -0
  58. package/dist/esm/types/utils/portGeometry.d.ts +44 -0
  59. package/dist/examples.d.ts +59 -0
  60. package/dist/index.d.ts +67 -1
  61. package/package.json +2 -1
  62. package/src/displaybox/demos/AsymmetricPortMultiAnchorDemoTab.tsx +269 -0
  63. package/src/displaybox/demos/AutoLayoutDemoTab.tsx +113 -11
  64. package/src/displaybox/demos/DeletionEventsDemoTab.tsx +6 -1
  65. package/src/displaybox/demos/EngineEventsDemoTab.tsx +5 -0
  66. package/src/displaybox/demos/EventHandlersDemoTab.tsx +5 -0
  67. package/src/displaybox/demos/ExternalDragDropDemoTab.tsx +5 -0
  68. package/src/displaybox/demos/LayoutLabelReservedSpaceDemoTab.tsx +291 -0
  69. package/src/displaybox/demos/LinkCancelDemoTab.tsx +5 -0
  70. package/src/displaybox/demos/ObstacleRoutingDemoTab.tsx +11 -10
  71. package/src/displaybox/demos/ShapeHoverControlsDemoTab.tsx +6 -1
  72. package/src/displaybox/demos/SimpleDemo.tsx +5 -0
  73. package/src/displaybox/demos/SvgPathDemoTab.tsx +5 -0
  74. package/src/displaybox/demos/TextLayoutDemoTab.tsx +6 -1
  75. package/src/displaybox/demos/VertexControlLinkSessionDemoTab.tsx +302 -0
  76. package/src/displaybox/demos/asymmetricPortMultiAnchorDemo.ts +357 -0
  77. package/src/displaybox/demos/autoLayoutDemo.ts +23 -5
  78. package/src/displaybox/demos/index.tsx +110 -80
  79. package/src/displaybox/demos/labelStyleDemo.ts +101 -0
  80. package/src/displaybox/demos/layoutLabelReservedSpaceDemo.ts +121 -0
  81. package/src/displaybox/demos/obstacleRoutingDemo.ts +212 -176
  82. package/src/displaybox/demos/portPositionLimitsDemo.ts +211 -0
  83. package/src/displaybox/demos/vertexControlLinkSessionDemo.ts +145 -0
@@ -1,4 +1,4 @@
1
- import React, { useMemo, useCallback, createContext, useContext, useRef, useState, useEffect } from 'react';
1
+ import React, { useMemo, useState, useCallback, createContext, useContext, useRef, useEffect } from 'react';
2
2
 
3
3
  var fallbackId = function () {
4
4
  return "id-".concat(Math.random().toString(36).slice(2, 10), "-").concat(Date.now().toString(36));
@@ -851,16 +851,34 @@ var createAutoLayoutState = function () { return ({
851
851
  ports: [],
852
852
  links: [],
853
853
  texts: [
854
- { id: 'label-layout-row', content: 'Row parent', position: { x: 8, y: -18 }, ownerId: 'layout-row' },
854
+ {
855
+ id: 'label-layout-row',
856
+ content: 'Row parent label lane',
857
+ position: { x: 8, y: 6 },
858
+ ownerId: 'layout-row',
859
+ layout: { boundsMode: 'owner-width', wrap: 'word', overflow: 'clip', padding: 0 },
860
+ },
855
861
  { id: 'label-row-a', content: 'row-a', position: { x: 6, y: -14 }, ownerId: 'row-a' },
856
862
  { id: 'label-row-b', content: 'row-b', position: { x: 6, y: -14 }, ownerId: 'row-b' },
857
863
  { id: 'label-row-c', content: 'row-c', position: { x: 6, y: -14 }, ownerId: 'row-c' },
858
864
  { id: 'label-row-nested-owner', content: 'owns children', position: { x: 6, y: -16 }, ownerId: 'row-nested-owner' },
859
- { id: 'label-layout-column', content: 'Column parent', position: { x: 8, y: -18 }, ownerId: 'layout-column' },
865
+ {
866
+ id: 'label-layout-column',
867
+ content: 'Column parent label lane',
868
+ position: { x: 8, y: 6 },
869
+ ownerId: 'layout-column',
870
+ layout: { boundsMode: 'owner-width', wrap: 'word', overflow: 'clip', padding: 0 },
871
+ },
860
872
  { id: 'label-col-a', content: 'col-a', position: { x: 6, y: -14 }, ownerId: 'col-a' },
861
873
  { id: 'label-col-b', content: 'col-b', position: { x: 6, y: -14 }, ownerId: 'col-b' },
862
874
  { id: 'label-col-c', content: 'col-c', position: { x: 6, y: -14 }, ownerId: 'col-c' },
863
- { id: 'label-layout-nested', content: 'Row + nested column', position: { x: 6, y: -18 }, ownerId: 'layout-nested' },
875
+ {
876
+ id: 'label-layout-nested',
877
+ content: 'Row + nested column lane',
878
+ position: { x: 6, y: 6 },
879
+ ownerId: 'layout-nested',
880
+ layout: { boundsMode: 'owner-width', wrap: 'word', overflow: 'clip', padding: 0 },
881
+ },
864
882
  { id: 'label-nested-stack', content: 'nested stack', position: { x: 6, y: -16 }, ownerId: 'nested-stack' },
865
883
  { id: 'label-stack-a', content: 'stack-a', position: { x: 6, y: -14 }, ownerId: 'stack-a' },
866
884
  { id: 'label-stack-b', content: 'stack-b', position: { x: 6, y: -14 }, ownerId: 'stack-b' },
@@ -873,8 +891,8 @@ var createAutoLayoutState = function () { return ({
873
891
  }); };
874
892
  var autoLayoutDemoConfig = ({
875
893
  id: 'auto-layout',
876
- title: 'Element Auto-Layout',
877
- description: 'Arrange children with padding/alignment plus fit options (main-axis distribute, cross-axis stretch).',
894
+ title: 'Element Auto-Layout + Label Lane',
895
+ description: 'Consolidated auto-layout verification with baseline/nested containers, fit options, and label reserved-space modes.',
878
896
  createState: createAutoLayoutState,
879
897
  elementShapes: baseElementShapes,
880
898
  portShapes: basePortShapes,
@@ -1486,6 +1504,302 @@ var multipleElementsDemoConfig = ({
1486
1504
  actions: [],
1487
1505
  });
1488
1506
 
1507
+ var asymmetricPortMultiAnchorDemoId = 'asymmetric-port-multi-anchor';
1508
+ var asymmetricPortDefaultVariantId = 'classic';
1509
+ var asymmetricPortShapeVariants = [
1510
+ {
1511
+ id: asymmetricPortDefaultVariantId,
1512
+ label: 'Classic arm',
1513
+ description: 'Baseline square-line-circle asymmetric glyph.',
1514
+ shapeId: 'displaybox-asymmetric-anchor-port-classic',
1515
+ svgPath: 'M2 6 H10 V14 H2 Z M10 10 H24 M28 10 m-4 0 a4 4 0 1 0 8 0 a4 4 0 1 0 -8 0',
1516
+ svgSize: { width: 36, height: 20 },
1517
+ placementPoint: { x: 6, y: 10 },
1518
+ externalLinkAttachPoint: { x: 28, y: 10 },
1519
+ internalLinkAttachPoint: { x: 6, y: 10 },
1520
+ rotationPivot: { x: 6, y: 10 },
1521
+ },
1522
+ {
1523
+ id: 'forked-arm',
1524
+ label: 'Forked arm',
1525
+ description: 'Connector splits before reaching the terminal circle.',
1526
+ shapeId: 'displaybox-asymmetric-anchor-port-forked',
1527
+ svgPath: 'M2 6 H10 V14 H2 Z M10 10 H18 M18 10 L24 6 M18 10 L24 14 M30 10 m-4 0 a4 4 0 1 0 8 0 a4 4 0 1 0 -8 0',
1528
+ svgSize: { width: 38, height: 20 },
1529
+ placementPoint: { x: 6, y: 10 },
1530
+ externalLinkAttachPoint: { x: 30, y: 10 },
1531
+ internalLinkAttachPoint: { x: 6, y: 10 },
1532
+ rotationPivot: { x: 6, y: 10 },
1533
+ },
1534
+ {
1535
+ id: 'zigzag-arm',
1536
+ label: 'Zigzag arm',
1537
+ description: 'Connector uses a zigzag body before the endpoint marker.',
1538
+ shapeId: 'displaybox-asymmetric-anchor-port-zigzag',
1539
+ svgPath: 'M2 6 H10 V14 H2 Z M10 10 L15 6 L20 14 L25 6 L28 10 M34 10 m-4 0 a4 4 0 1 0 8 0 a4 4 0 1 0 -8 0',
1540
+ svgSize: { width: 42, height: 20 },
1541
+ placementPoint: { x: 6, y: 10 },
1542
+ externalLinkAttachPoint: { x: 34, y: 10 },
1543
+ internalLinkAttachPoint: { x: 6, y: 10 },
1544
+ rotationPivot: { x: 6, y: 10 },
1545
+ },
1546
+ {
1547
+ id: 'dual-bar',
1548
+ label: 'Dual bar',
1549
+ description: 'Body includes dual bars between pivot and circle endpoint.',
1550
+ shapeId: 'displaybox-asymmetric-anchor-port-dual-bar',
1551
+ svgPath: 'M2 6 H10 V14 H2 Z M10 10 H18 M18 6 V14 M22 6 V14 M22 10 H26 M32 10 m-4 0 a4 4 0 1 0 8 0 a4 4 0 1 0 -8 0',
1552
+ svgSize: { width: 40, height: 20 },
1553
+ placementPoint: { x: 6, y: 10 },
1554
+ externalLinkAttachPoint: { x: 32, y: 10 },
1555
+ internalLinkAttachPoint: { x: 6, y: 10 },
1556
+ rotationPivot: { x: 6, y: 10 },
1557
+ },
1558
+ ];
1559
+ var resolveAsymmetricPortShapeVariant = function (variantId) {
1560
+ var _a;
1561
+ if (!variantId)
1562
+ return asymmetricPortShapeVariants[0];
1563
+ return (_a = asymmetricPortShapeVariants.find(function (variant) { return variant.id === variantId; })) !== null && _a !== void 0 ? _a : asymmetricPortShapeVariants[0];
1564
+ };
1565
+ resolveAsymmetricPortShapeVariant(asymmetricPortDefaultVariantId).shapeId;
1566
+ resolveAsymmetricPortShapeVariant(asymmetricPortDefaultVariantId).svgPath;
1567
+ resolveAsymmetricPortShapeVariant(asymmetricPortDefaultVariantId).svgSize;
1568
+ var multiAnchorHostId = 'multi-anchor-host';
1569
+ var multiAnchorExternalPortId = 'multi-anchor-port-external';
1570
+ var multiAnchorStroke = '#1f4d99';
1571
+ var legacyStroke = '#9b2c2c';
1572
+ var createAsymmetricPort = function (options) {
1573
+ var basePort = {
1574
+ id: options.id,
1575
+ elementId: options.elementId,
1576
+ position: options.position,
1577
+ shapeId: options.shapeVariant.shapeId,
1578
+ size: __assign({}, options.shapeVariant.svgSize),
1579
+ moveMode: 'border',
1580
+ anchorCenter: true,
1581
+ orientToHostBorder: true,
1582
+ style: {
1583
+ stroke: options.stroke,
1584
+ strokeWidth: 2,
1585
+ fill: 'transparent',
1586
+ },
1587
+ };
1588
+ if (options.mode === 'legacy') {
1589
+ return basePort;
1590
+ }
1591
+ return __assign(__assign({}, basePort), { placementPoint: __assign({}, options.shapeVariant.placementPoint), externalLinkAttachPoint: __assign({}, options.shapeVariant.externalLinkAttachPoint), internalLinkAttachPoint: __assign({}, options.shapeVariant.internalLinkAttachPoint), rotationPivot: __assign({}, options.shapeVariant.rotationPivot) });
1592
+ };
1593
+ var createComparisonRow = function (options) {
1594
+ var prefix = options.prefix, top = options.top, mode = options.mode, shapeVariant = options.shapeVariant;
1595
+ var hostId = "".concat(prefix, "-host");
1596
+ var peerId = "".concat(prefix, "-peer");
1597
+ var childId = "".concat(prefix, "-child");
1598
+ var externalPortId = "".concat(prefix, "-port-external");
1599
+ var topPortId = "".concat(prefix, "-port-top");
1600
+ var bottomPortId = "".concat(prefix, "-port-bottom");
1601
+ var internalPortId = "".concat(prefix, "-port-internal");
1602
+ var peerPortId = "".concat(prefix, "-peer-port");
1603
+ var childPortId = "".concat(prefix, "-child-port");
1604
+ var stroke = mode === 'multi-anchor' ? multiAnchorStroke : legacyStroke;
1605
+ var title = mode === 'multi-anchor' ? 'Multi-anchor geometry' : 'Legacy/default geometry';
1606
+ var subtitle = mode === 'multi-anchor'
1607
+ ? "".concat(shapeVariant.label, ": square pivot stays pinned to the border; right-side link starts at the circle endpoint.")
1608
+ : 'Same svgPath without placement/link/pivot anchors for quick comparison.';
1609
+ return {
1610
+ elements: [
1611
+ {
1612
+ id: hostId,
1613
+ position: { x: 80, y: top },
1614
+ size: { width: 280, height: 180 },
1615
+ shapeId: 'default',
1616
+ },
1617
+ {
1618
+ id: peerId,
1619
+ position: { x: 470, y: top + 36 },
1620
+ size: { width: 170, height: 108 },
1621
+ shapeId: 'panel',
1622
+ },
1623
+ {
1624
+ id: childId,
1625
+ position: { x: 88, y: 86 },
1626
+ size: { width: 122, height: 72 },
1627
+ shapeId: 'panel',
1628
+ parentId: hostId,
1629
+ },
1630
+ ],
1631
+ ports: [
1632
+ createAsymmetricPort({
1633
+ id: topPortId,
1634
+ elementId: hostId,
1635
+ position: { x: 112, y: 0 },
1636
+ stroke: stroke,
1637
+ mode: mode,
1638
+ shapeVariant: shapeVariant,
1639
+ }),
1640
+ createAsymmetricPort({
1641
+ id: externalPortId,
1642
+ elementId: hostId,
1643
+ position: { x: 280, y: 68 },
1644
+ stroke: stroke,
1645
+ mode: mode,
1646
+ shapeVariant: shapeVariant,
1647
+ }),
1648
+ createAsymmetricPort({
1649
+ id: bottomPortId,
1650
+ elementId: hostId,
1651
+ position: { x: 182, y: 180 },
1652
+ stroke: stroke,
1653
+ mode: mode,
1654
+ shapeVariant: shapeVariant,
1655
+ }),
1656
+ createAsymmetricPort({
1657
+ id: internalPortId,
1658
+ elementId: hostId,
1659
+ position: { x: 0, y: 130 },
1660
+ stroke: stroke,
1661
+ mode: mode,
1662
+ shapeVariant: shapeVariant,
1663
+ }),
1664
+ {
1665
+ id: peerPortId,
1666
+ elementId: peerId,
1667
+ position: { x: 0, y: 54 },
1668
+ shapeId: 'port-dark',
1669
+ moveMode: 'border',
1670
+ anchorCenter: true,
1671
+ },
1672
+ {
1673
+ id: childPortId,
1674
+ elementId: childId,
1675
+ position: { x: 0, y: 36 },
1676
+ shapeId: 'port-circle',
1677
+ moveMode: 'border',
1678
+ anchorCenter: true,
1679
+ },
1680
+ ],
1681
+ links: [
1682
+ {
1683
+ id: "".concat(prefix, "-link-external"),
1684
+ sourcePortId: externalPortId,
1685
+ targetPortId: peerPortId,
1686
+ points: [],
1687
+ style: {
1688
+ stroke: stroke,
1689
+ strokeWidth: 2,
1690
+ },
1691
+ },
1692
+ {
1693
+ id: "".concat(prefix, "-link-internal"),
1694
+ sourcePortId: internalPortId,
1695
+ targetPortId: childPortId,
1696
+ points: [],
1697
+ style: {
1698
+ stroke: mode === 'multi-anchor' ? '#2f7a3d' : '#7a5f2f',
1699
+ strokeWidth: 2,
1700
+ dash: mode === 'multi-anchor' ? [8, 4] : [4, 4],
1701
+ },
1702
+ },
1703
+ ],
1704
+ texts: [
1705
+ {
1706
+ id: "".concat(prefix, "-title"),
1707
+ content: title,
1708
+ position: { x: 74, y: top - 42 },
1709
+ style: { fontStyle: 'bold', fontSize: 20 },
1710
+ },
1711
+ {
1712
+ id: "".concat(prefix, "-subtitle"),
1713
+ content: subtitle,
1714
+ position: { x: 74, y: top - 18 },
1715
+ style: { fontSize: 13, fill: '#334155' },
1716
+ },
1717
+ {
1718
+ id: "".concat(prefix, "-host-label"),
1719
+ content: 'Drag border ports or use the side buttons below.',
1720
+ position: { x: 14, y: -18 },
1721
+ ownerId: hostId,
1722
+ style: { fontSize: 12, fill: '#334155' },
1723
+ },
1724
+ {
1725
+ id: "".concat(prefix, "-top-port-label"),
1726
+ content: 'top',
1727
+ position: { x: 10, y: -18 },
1728
+ ownerId: topPortId,
1729
+ style: { fontSize: 11 },
1730
+ },
1731
+ {
1732
+ id: "".concat(prefix, "-external-port-label"),
1733
+ content: 'external → circle',
1734
+ position: { x: 12, y: -18 },
1735
+ ownerId: externalPortId,
1736
+ style: { fontSize: 11 },
1737
+ },
1738
+ {
1739
+ id: "".concat(prefix, "-bottom-port-label"),
1740
+ content: 'bottom',
1741
+ position: { x: 12, y: 18 },
1742
+ ownerId: bottomPortId,
1743
+ style: { fontSize: 11 },
1744
+ },
1745
+ {
1746
+ id: "".concat(prefix, "-internal-port-label"),
1747
+ content: 'internal → square',
1748
+ position: { x: 12, y: -18 },
1749
+ ownerId: internalPortId,
1750
+ style: { fontSize: 11 },
1751
+ },
1752
+ {
1753
+ id: "".concat(prefix, "-peer-label"),
1754
+ content: 'External peer',
1755
+ position: { x: 12, y: -18 },
1756
+ ownerId: peerId,
1757
+ style: { fontSize: 12 },
1758
+ },
1759
+ {
1760
+ id: "".concat(prefix, "-child-label"),
1761
+ content: 'Child host for internal link',
1762
+ position: { x: 8, y: -18 },
1763
+ ownerId: childId,
1764
+ style: { fontSize: 11 },
1765
+ },
1766
+ ],
1767
+ };
1768
+ };
1769
+ var createAsymmetricPortMultiAnchorState = function (showLegacyComparison, variantId) {
1770
+ if (showLegacyComparison === void 0) { showLegacyComparison = true; }
1771
+ if (variantId === void 0) { variantId = asymmetricPortDefaultVariantId; }
1772
+ var shapeVariant = resolveAsymmetricPortShapeVariant(variantId);
1773
+ var primaryRow = createComparisonRow({ prefix: 'multi-anchor', top: 92, mode: 'multi-anchor', shapeVariant: shapeVariant });
1774
+ var legacyRow = showLegacyComparison
1775
+ ? createComparisonRow({ prefix: 'legacy', top: 356, mode: 'legacy', shapeVariant: shapeVariant })
1776
+ : { elements: [], ports: [], links: [], texts: [] };
1777
+ return {
1778
+ elements: __spreadArray(__spreadArray([], primaryRow.elements, true), legacyRow.elements, true),
1779
+ ports: __spreadArray(__spreadArray([], primaryRow.ports, true), legacyRow.ports, true),
1780
+ links: __spreadArray(__spreadArray([], primaryRow.links, true), legacyRow.links, true),
1781
+ texts: __spreadArray(__spreadArray([
1782
+ {
1783
+ id: 'multi-anchor-legend',
1784
+ content: 'Legend: square = placement/rotation pivot, line = glyph body, circle = external attach endpoint. Internal link uses the square anchor.',
1785
+ position: { x: 72, y: 28 },
1786
+ style: { fontSize: 13, fill: '#1f2937' },
1787
+ }
1788
+ ], primaryRow.texts, true), legacyRow.texts, true),
1789
+ };
1790
+ };
1791
+ var asymmetricPortMultiAnchorDemoConfig = {
1792
+ id: asymmetricPortMultiAnchorDemoId,
1793
+ title: 'Asymmetric Multi-Anchor Ports',
1794
+ description: 'Compares legacy/default origin-based svg glyph behavior with explicit placement, pivot, and external/internal link attach points.',
1795
+ createState: function () { return createAsymmetricPortMultiAnchorState(true, asymmetricPortDefaultVariantId); },
1796
+ elementShapes: baseElementShapes,
1797
+ portShapes: basePortShapes,
1798
+ defaultElementShapeId: 'default',
1799
+ defaultPortShapeId: 'port-circle',
1800
+ actions: [],
1801
+ };
1802
+
1489
1803
  var createPortConstraintState = function () { return ({
1490
1804
  elements: [
1491
1805
  {
@@ -1657,6 +1971,213 @@ var portConstraintsDemoConfig = ({
1657
1971
  ],
1658
1972
  });
1659
1973
 
1974
+ var createPortPositionLimitsState = function () { return ({
1975
+ elements: [
1976
+ {
1977
+ id: 'limit-left-host',
1978
+ position: { x: 60, y: 120 },
1979
+ size: { width: 220, height: 140 },
1980
+ shapeId: 'panel',
1981
+ portMovement: {
1982
+ moveMode: 'inside',
1983
+ positionLimits: {
1984
+ x: { max: 70 },
1985
+ },
1986
+ },
1987
+ },
1988
+ {
1989
+ id: 'limit-right-host',
1990
+ position: { x: 330, y: 120 },
1991
+ size: { width: 220, height: 140 },
1992
+ shapeId: 'panel',
1993
+ portMovement: {
1994
+ moveMode: 'inside',
1995
+ positionLimits: {
1996
+ x: { min: 150 },
1997
+ },
1998
+ },
1999
+ },
2000
+ {
2001
+ id: 'limit-top-host',
2002
+ position: { x: 600, y: 120 },
2003
+ size: { width: 220, height: 140 },
2004
+ shapeId: 'panel',
2005
+ portMovement: {
2006
+ moveMode: 'inside',
2007
+ positionLimits: {
2008
+ y: { max: 45 },
2009
+ },
2010
+ },
2011
+ },
2012
+ {
2013
+ id: 'limit-bottom-host',
2014
+ position: { x: 870, y: 120 },
2015
+ size: { width: 220, height: 140 },
2016
+ shapeId: 'panel',
2017
+ portMovement: {
2018
+ moveMode: 'inside',
2019
+ positionLimits: {
2020
+ y: { min: 90 },
2021
+ },
2022
+ },
2023
+ },
2024
+ {
2025
+ id: 'border-left-host',
2026
+ position: { x: 200, y: 350 },
2027
+ size: { width: 220, height: 140 },
2028
+ shapeId: 'panel',
2029
+ portMovement: {
2030
+ moveMode: 'border',
2031
+ positionLimits: {
2032
+ x: { max: 80 },
2033
+ },
2034
+ },
2035
+ },
2036
+ {
2037
+ id: 'border-right-host',
2038
+ position: { x: 500, y: 350 },
2039
+ size: { width: 220, height: 140 },
2040
+ shapeId: 'panel',
2041
+ portMovement: {
2042
+ moveMode: 'border',
2043
+ positionLimits: {
2044
+ x: { min: 140 },
2045
+ },
2046
+ },
2047
+ },
2048
+ {
2049
+ id: 'default-normalize-host',
2050
+ position: { x: 800, y: 350 },
2051
+ size: { width: 220, height: 140 },
2052
+ shapeId: 'panel',
2053
+ portMovement: {
2054
+ moveMode: 'inside',
2055
+ positionLimits: {
2056
+ x: { max: 60 },
2057
+ y: { max: 60 },
2058
+ },
2059
+ },
2060
+ },
2061
+ ],
2062
+ ports: [
2063
+ { id: 'limit-left-port', elementId: 'limit-left-host', position: { x: 40, y: 70 }, shapeId: 'port-circle', anchorCenter: true },
2064
+ { id: 'limit-right-port', elementId: 'limit-right-host', position: { x: 180, y: 70 }, shapeId: 'port-circle', anchorCenter: true },
2065
+ { id: 'limit-top-port', elementId: 'limit-top-host', position: { x: 110, y: 30 }, shapeId: 'port-circle', anchorCenter: true },
2066
+ { id: 'limit-bottom-port', elementId: 'limit-bottom-host', position: { x: 110, y: 110 }, shapeId: 'port-circle', anchorCenter: true },
2067
+ { id: 'border-left-port', elementId: 'border-left-host', position: { x: 80, y: 0 }, shapeId: 'port-circle', anchorCenter: true },
2068
+ { id: 'border-right-port', elementId: 'border-right-host', position: { x: 140, y: 140 }, shapeId: 'port-circle', anchorCenter: true },
2069
+ { id: 'reload-default-port', elementId: 'default-normalize-host', position: { x: 190, y: 130 }, shapeId: 'port-circle', anchorCenter: true },
2070
+ ],
2071
+ links: [],
2072
+ texts: [
2073
+ {
2074
+ id: 'limit-intro',
2075
+ content: 'Top row: threshold/limit hosts. Bottom row: border+limits hosts and default-position normalization checks for add/load flows.',
2076
+ position: { x: 60, y: 40 },
2077
+ },
2078
+ { id: 'limit-group-title', content: 'Threshold/limit-focused hosts', position: { x: 60, y: 85 } },
2079
+ { id: 'border-group-title', content: 'Border+limits + default-position normalization', position: { x: 200, y: 315 } },
2080
+ { id: 'limit-left-title', content: 'x.max = 70 (left of X)', position: { x: 8, y: -20 }, ownerId: 'limit-left-host' },
2081
+ { id: 'limit-right-title', content: 'x.min = 150 (right of X)', position: { x: 8, y: -20 }, ownerId: 'limit-right-host' },
2082
+ { id: 'limit-top-title', content: 'y.max = 45 (above Y)', position: { x: 8, y: -20 }, ownerId: 'limit-top-host' },
2083
+ { id: 'limit-bottom-title', content: 'y.min = 90 (below Y)', position: { x: 8, y: -20 }, ownerId: 'limit-bottom-host' },
2084
+ { id: 'border-left-title', content: 'moveMode=border + x.max = 80', position: { x: 8, y: -20 }, ownerId: 'border-left-host' },
2085
+ { id: 'border-right-title', content: 'moveMode=border + x.min = 140', position: { x: 8, y: -20 }, ownerId: 'border-right-host' },
2086
+ { id: 'default-normalize-title', content: 'default normalization host x/y.max = 60', position: { x: 8, y: -20 }, ownerId: 'default-normalize-host' },
2087
+ { id: 'limit-left-status', content: 'local: x=40, y=70', position: { x: 8, y: 110 }, ownerId: 'limit-left-host' },
2088
+ { id: 'limit-right-status', content: 'local: x=180, y=70', position: { x: 8, y: 110 }, ownerId: 'limit-right-host' },
2089
+ { id: 'limit-top-status', content: 'local: x=110, y=30', position: { x: 8, y: 110 }, ownerId: 'limit-top-host' },
2090
+ { id: 'limit-bottom-status', content: 'local: x=110, y=110', position: { x: 8, y: 110 }, ownerId: 'limit-bottom-host' },
2091
+ { id: 'border-left-status', content: 'local: x=80, y=0', position: { x: 8, y: 110 }, ownerId: 'border-left-host' },
2092
+ { id: 'border-right-status', content: 'local: x=140, y=140', position: { x: 8, y: 110 }, ownerId: 'border-right-host' },
2093
+ {
2094
+ id: 'add-default-status',
2095
+ content: 'add invalid default: not executed',
2096
+ position: { x: 8, y: 88 },
2097
+ ownerId: 'default-normalize-host',
2098
+ },
2099
+ {
2100
+ id: 'reload-default-status',
2101
+ content: 'reload invalid default: click "Read reload-normalized port"',
2102
+ position: { x: 8, y: 106 },
2103
+ ownerId: 'default-normalize-host',
2104
+ },
2105
+ ],
2106
+ }); };
2107
+ var portPositionLimitsDemoConfig = {
2108
+ id: 'port-position-limits',
2109
+ title: 'Port Position Limits',
2110
+ description: 'Unified restriction demo: threshold limits, border+limits, and add/load default-position normalization.',
2111
+ createState: createPortPositionLimitsState,
2112
+ elementShapes: baseElementShapes,
2113
+ portShapes: basePortShapes,
2114
+ defaultElementShapeId: 'default',
2115
+ defaultPortShapeId: 'port-circle',
2116
+ actions: [
2117
+ {
2118
+ id: 'force-limit-clamp',
2119
+ label: 'Force out-of-range moves',
2120
+ run: function (editor) {
2121
+ editor.movePortTo('limit-left-port', 1000, 1000);
2122
+ editor.movePortTo('limit-right-port', -1000, 1000);
2123
+ editor.movePortTo('limit-top-port', 1000, 1000);
2124
+ editor.movePortTo('limit-bottom-port', 1000, -1000);
2125
+ editor.movePortTo('border-left-port', 1000, 1000);
2126
+ editor.movePortTo('border-right-port', -1000, -1000);
2127
+ var state = editor.getState();
2128
+ var left = state.ports.find(function (item) { return item.id === 'limit-left-port'; });
2129
+ var right = state.ports.find(function (item) { return item.id === 'limit-right-port'; });
2130
+ var top = state.ports.find(function (item) { return item.id === 'limit-top-port'; });
2131
+ var bottom = state.ports.find(function (item) { return item.id === 'limit-bottom-port'; });
2132
+ var borderLeft = state.ports.find(function (item) { return item.id === 'border-left-port'; });
2133
+ var borderRight = state.ports.find(function (item) { return item.id === 'border-right-port'; });
2134
+ if (left)
2135
+ editor.updateText('limit-left-status', "local: x=".concat(left.position.x, ", y=").concat(left.position.y));
2136
+ if (right)
2137
+ editor.updateText('limit-right-status', "local: x=".concat(right.position.x, ", y=").concat(right.position.y));
2138
+ if (top)
2139
+ editor.updateText('limit-top-status', "local: x=".concat(top.position.x, ", y=").concat(top.position.y));
2140
+ if (bottom)
2141
+ editor.updateText('limit-bottom-status', "local: x=".concat(bottom.position.x, ", y=").concat(bottom.position.y));
2142
+ if (borderLeft)
2143
+ editor.updateText('border-left-status', "local: x=".concat(borderLeft.position.x, ", y=").concat(borderLeft.position.y));
2144
+ if (borderRight)
2145
+ editor.updateText('border-right-status', "local: x=".concat(borderRight.position.x, ", y=").concat(borderRight.position.y));
2146
+ },
2147
+ },
2148
+ {
2149
+ id: 'add-invalid-default-port',
2150
+ label: 'Add invalid default port',
2151
+ run: function (editor) {
2152
+ editor.removePort('added-default-port');
2153
+ editor.addPortToElement('default-normalize-host', {
2154
+ id: 'added-default-port',
2155
+ elementId: 'default-normalize-host',
2156
+ position: { x: 180, y: 120 },
2157
+ shapeId: 'port-circle',
2158
+ anchorCenter: true,
2159
+ });
2160
+ var state = editor.getState();
2161
+ var added = state.ports.find(function (item) { return item.id === 'added-default-port'; });
2162
+ if (added) {
2163
+ editor.updateText('add-default-status', "add invalid default: local x=".concat(added.position.x, ", y=").concat(added.position.y));
2164
+ }
2165
+ },
2166
+ },
2167
+ {
2168
+ id: 'read-reload-normalized-port',
2169
+ label: 'Read reload-normalized port',
2170
+ run: function (editor) {
2171
+ var state = editor.getState();
2172
+ var reloaded = state.ports.find(function (item) { return item.id === 'reload-default-port'; });
2173
+ if (reloaded) {
2174
+ editor.updateText('reload-default-status', "reload invalid default: local x=".concat(reloaded.position.x, ", y=").concat(reloaded.position.y));
2175
+ }
2176
+ },
2177
+ },
2178
+ ],
2179
+ };
2180
+
1660
2181
  var createPortBorderState = function () { return ({
1661
2182
  elements: [
1662
2183
  {
@@ -1811,6 +2332,104 @@ var childConstraintsDemoConfig = ({
1811
2332
  ],
1812
2333
  });
1813
2334
 
2335
+ var createLabelStyleState = function () { return ({
2336
+ elements: [
2337
+ {
2338
+ id: 'label-center-host',
2339
+ position: { x: 80, y: 160 },
2340
+ size: { width: 260, height: 120 },
2341
+ shapeId: 'panel',
2342
+ },
2343
+ {
2344
+ id: 'label-background-host',
2345
+ position: { x: 390, y: 160 },
2346
+ size: { width: 260, height: 120 },
2347
+ shapeId: 'panel',
2348
+ style: {
2349
+ cornerRadius: 16,
2350
+ stroke: '#1f4d99',
2351
+ strokeWidth: 2,
2352
+ fill: '#edf4ff',
2353
+ },
2354
+ },
2355
+ {
2356
+ id: 'label-baseline-host',
2357
+ position: { x: 700, y: 160 },
2358
+ size: { width: 260, height: 120 },
2359
+ shapeId: 'panel',
2360
+ },
2361
+ ],
2362
+ ports: [],
2363
+ links: [],
2364
+ texts: [
2365
+ {
2366
+ id: 'label-style-intro',
2367
+ content: 'Compare center alignment, background fill, and baseline label styling. Resize hosts to verify visual stability.',
2368
+ position: { x: 80, y: 80 },
2369
+ },
2370
+ {
2371
+ id: 'label-center-text',
2372
+ content: 'Center aligned owner-width label',
2373
+ ownerId: 'label-center-host',
2374
+ position: { x: 0, y: 44 },
2375
+ layout: { boundsMode: 'owner-width', wrap: 'none', overflow: 'ellipsis-end', padding: 8 },
2376
+ style: { align: 'center', fill: '#16324f', fontSize: 15 },
2377
+ },
2378
+ {
2379
+ id: 'label-background-text',
2380
+ content: 'Top Header Label',
2381
+ ownerId: 'label-background-host',
2382
+ position: { x: 0, y: 0 },
2383
+ layout: { boundsMode: 'owner-width', wrap: 'none', overflow: 'ellipsis-end', padding: 0 },
2384
+ style: {
2385
+ align: 'center',
2386
+ backgroundFill: '#cfe2ff',
2387
+ backgroundStroke: '#1f4d99',
2388
+ backgroundStrokeWidth: 1,
2389
+ fill: '#16324f',
2390
+ fontSize: 15,
2391
+ padding: 8,
2392
+ },
2393
+ },
2394
+ {
2395
+ id: 'label-baseline-text',
2396
+ content: 'Baseline label (no style keys)',
2397
+ ownerId: 'label-baseline-host',
2398
+ position: { x: 12, y: 44 },
2399
+ layout: { boundsMode: 'owner-width', wrap: 'none', overflow: 'ellipsis-end', padding: 8 },
2400
+ },
2401
+ ],
2402
+ }); };
2403
+ var labelStyleDemoConfig = {
2404
+ id: 'label-style',
2405
+ title: 'Label Style',
2406
+ description: 'Center text alignment and backgroundFill styling for labels.',
2407
+ createState: createLabelStyleState,
2408
+ elementShapes: baseElementShapes,
2409
+ portShapes: basePortShapes,
2410
+ defaultElementShapeId: 'default',
2411
+ defaultPortShapeId: 'port-circle',
2412
+ actions: [
2413
+ {
2414
+ id: 'resize-label-hosts',
2415
+ label: 'Resize hosts (style verification)',
2416
+ run: function (editor, state) {
2417
+ var _a, _b;
2418
+ var expanded = ((_b = (_a = state.elements.find(function (item) { return item.id === 'label-center-host'; })) === null || _a === void 0 ? void 0 : _a.size.width) !== null && _b !== void 0 ? _b : 0) > 260;
2419
+ if (expanded) {
2420
+ editor.resizeElement('label-center-host', 260, 120);
2421
+ editor.resizeElement('label-background-host', 260, 120);
2422
+ editor.resizeElement('label-baseline-host', 260, 120);
2423
+ return;
2424
+ }
2425
+ editor.resizeElement('label-center-host', 320, 140);
2426
+ editor.resizeElement('label-background-host', 320, 140);
2427
+ editor.resizeElement('label-baseline-host', 320, 140);
2428
+ },
2429
+ },
2430
+ ],
2431
+ };
2432
+
1814
2433
  var createGridOverlayState = function () { return ({
1815
2434
  elements: [
1816
2435
  {
@@ -1912,141 +2531,174 @@ var routingDemoConfig = ({
1912
2531
 
1913
2532
  var obstacleRoutingLinks = [
1914
2533
  {
1915
- id: 'block-link',
1916
- sourcePortId: 'block-source-port',
1917
- targetPortId: 'block-target-port',
2534
+ id: 'sibling-external-link',
2535
+ sourcePortId: 'sibling-a-port',
2536
+ targetPortId: 'sibling-b-port',
1918
2537
  points: [],
1919
2538
  routing: 'auto',
1920
2539
  },
1921
2540
  {
1922
- id: 'shared-sibling-link',
1923
- sourcePortId: 'shared-child-a-port',
1924
- targetPortId: 'shared-child-b-port',
2541
+ id: 'parent-to-child-link',
2542
+ sourcePortId: 'attach-parent-port',
2543
+ targetPortId: 'attach-child-a-port',
1925
2544
  points: [],
1926
2545
  routing: 'auto',
1927
2546
  },
1928
2547
  {
1929
- id: 'parent-child-link',
1930
- sourcePortId: 'parent-host-port',
1931
- targetPortId: 'edge-child-port',
2548
+ id: 'child-to-parent-link',
2549
+ sourcePortId: 'attach-child-b-port',
2550
+ targetPortId: 'attach-parent-port',
1932
2551
  points: [],
1933
2552
  routing: 'auto',
1934
2553
  },
1935
2554
  {
1936
- id: 'grandchild-link',
1937
- sourcePortId: 'grandchild-a-port',
1938
- targetPortId: 'grandchild-b-port',
2555
+ id: 'child-to-child-link',
2556
+ sourcePortId: 'attach-child-a-port',
2557
+ targetPortId: 'attach-child-b-port',
1939
2558
  points: [],
1940
2559
  routing: 'auto',
1941
2560
  },
1942
2561
  ];
2562
+ var obstacleRoutingShapes = __spreadArray(__spreadArray([], baseElementShapes, true), [
2563
+ {
2564
+ id: 'routing-ellipse',
2565
+ kind: 'ellipse',
2566
+ style: {
2567
+ fill: '#e8f8ff',
2568
+ stroke: '#1e6b8f',
2569
+ strokeWidth: 2,
2570
+ },
2571
+ },
2572
+ ], false);
1943
2573
  var createObstacleRoutingState = function () { return ({
1944
2574
  elements: [
1945
2575
  {
1946
- id: 'block-source',
1947
- position: { x: 60, y: 40 },
1948
- size: { width: 140, height: 90 },
1949
- shapeId: 'default',
2576
+ id: 'sibling-parent',
2577
+ position: { x: 40, y: 90 },
2578
+ size: { width: 540, height: 250 },
2579
+ shapeId: 'panel',
2580
+ style: { fill: '#fafafa' },
1950
2581
  },
1951
2582
  {
1952
- id: 'block-target',
1953
- position: { x: 380, y: 40 },
1954
- size: { width: 140, height: 90 },
1955
- shapeId: 'panel',
2583
+ id: 'sibling-a',
2584
+ position: { x: 70, y: 75 },
2585
+ size: { width: 140, height: 100 },
2586
+ shapeId: 'routing-ellipse',
2587
+ parentId: 'sibling-parent',
2588
+ portMovement: { moveMode: 'anchors', anchorConstraint: { preset: 'cardinal' } },
1956
2589
  },
1957
2590
  {
1958
- id: 'block-obstacle',
1959
- position: { x: 250, y: 30 },
1960
- size: { width: 80, height: 110 },
1961
- shapeId: 'default',
2591
+ id: 'sibling-b',
2592
+ position: { x: 310, y: 75 },
2593
+ size: { width: 140, height: 100 },
2594
+ shapeId: 'routing-ellipse',
2595
+ parentId: 'sibling-parent',
2596
+ portMovement: { moveMode: 'anchors', anchorConstraint: { preset: 'cardinal' } },
1962
2597
  },
1963
2598
  {
1964
- id: 'shared-parent',
1965
- position: { x: 40, y: 180 },
1966
- size: { width: 420, height: 220 },
1967
- shapeId: 'panel',
1968
- style: { fill: '#fafafa' },
2599
+ id: 'attach-parent',
2600
+ position: { x: 620, y: 90 },
2601
+ size: { width: 360, height: 250 },
2602
+ shapeId: 'routing-ellipse',
2603
+ portMovement: { moveMode: 'anchors', anchorConstraint: { preset: 'cardinal' } },
1969
2604
  },
1970
2605
  {
1971
- id: 'shared-child-a',
1972
- position: { x: 40, y: 40 },
1973
- size: { width: 140, height: 90 },
1974
- shapeId: 'default',
1975
- parentId: 'shared-parent',
2606
+ id: 'attach-child',
2607
+ position: { x: 40, y: 45 },
2608
+ size: { width: 140, height: 100 },
2609
+ shapeId: 'routing-ellipse',
2610
+ parentId: 'attach-parent',
2611
+ portMovement: { moveMode: 'anchors', anchorConstraint: { preset: 'cardinal' } },
1976
2612
  },
1977
2613
  {
1978
- id: 'shared-child-b',
1979
- position: { x: 220, y: 40 },
1980
- size: { width: 140, height: 90 },
1981
- shapeId: 'default',
1982
- parentId: 'shared-parent',
2614
+ id: 'attach-child-b',
2615
+ position: { x: 190, y: 120 },
2616
+ size: { width: 140, height: 100 },
2617
+ shapeId: 'routing-ellipse',
2618
+ parentId: 'attach-parent',
2619
+ portMovement: { moveMode: 'anchors', anchorConstraint: { preset: 'cardinal' } },
1983
2620
  },
2621
+ ],
2622
+ ports: [
1984
2623
  {
1985
- id: 'parent-host',
1986
- position: { x: 500, y: 180 },
1987
- size: { width: 320, height: 220 },
1988
- shapeId: 'panel',
1989
- style: { fill: '#fafafa' },
2624
+ id: 'sibling-a-port',
2625
+ elementId: 'sibling-a',
2626
+ position: { x: 0, y: 50 },
2627
+ shapeId: 'port-dark',
2628
+ anchorCenter: true,
2629
+ currentAnchorId: 'left',
1990
2630
  },
1991
2631
  {
1992
- id: 'edge-child',
1993
- position: { x: 40, y: 60 },
1994
- size: { width: 160, height: 90 },
1995
- shapeId: 'default',
1996
- parentId: 'parent-host',
2632
+ id: 'sibling-b-port',
2633
+ elementId: 'sibling-b',
2634
+ position: { x: 140, y: 50 },
2635
+ shapeId: 'port-dark',
2636
+ anchorCenter: true,
2637
+ currentAnchorId: 'right',
1997
2638
  },
1998
2639
  {
1999
- id: 'grandparent',
2000
- position: { x: 40, y: 380 },
2001
- size: { width: 780, height: 160 },
2002
- shapeId: 'panel',
2003
- style: { fill: '#fafafa' },
2640
+ id: 'attach-parent-port',
2641
+ elementId: 'attach-parent',
2642
+ position: { x: 20, y: 125 },
2643
+ shapeId: 'port-circle',
2644
+ anchorCenter: true,
2645
+ currentAnchorId: 'left',
2646
+ externalLinkAttachPoint: { x: -16, y: 0 },
2647
+ internalLinkAttachPoint: { x: 16, y: 0 },
2004
2648
  },
2005
2649
  {
2006
- id: 'mid-parent-a',
2650
+ id: 'attach-child-a-port',
2651
+ elementId: 'attach-child',
2652
+ position: { x: 140, y: 50 },
2653
+ shapeId: 'port-circle',
2654
+ anchorCenter: true,
2655
+ currentAnchorId: 'right',
2656
+ externalLinkAttachPoint: { x: 12, y: 0 },
2657
+ internalLinkAttachPoint: { x: -12, y: 0 },
2658
+ },
2659
+ {
2660
+ id: 'attach-child-b-port',
2661
+ elementId: 'attach-child-b',
2662
+ position: { x: 0, y: 50 },
2663
+ shapeId: 'port-circle',
2664
+ anchorCenter: true,
2665
+ currentAnchorId: 'left',
2666
+ externalLinkAttachPoint: { x: 12, y: 0 },
2667
+ internalLinkAttachPoint: { x: -12, y: 0 },
2668
+ },
2669
+ ],
2670
+ links: obstacleRoutingLinks,
2671
+ texts: [
2672
+ {
2673
+ id: 'obstacle-routing-instructions',
2674
+ content: 'Two checks in one canvas: (A) sibling external-anchor link avoids both child interiors; (B) parent/children hierarchy resolves internal vs external attach points per endpoint direction.',
2007
2675
  position: { x: 40, y: 30 },
2008
- size: { width: 300, height: 100 },
2009
- shapeId: 'panel',
2010
- parentId: 'grandparent',
2011
2676
  },
2012
2677
  {
2013
- id: 'mid-parent-b',
2014
- position: { x: 420, y: 30 },
2015
- size: { width: 300, height: 100 },
2016
- shapeId: 'panel',
2017
- parentId: 'grandparent',
2678
+ id: 'sibling-group-label',
2679
+ content: 'Scenario A: Same-parent children, external-facing anchors (left/right)',
2680
+ position: { x: 52, y: 66 },
2018
2681
  },
2019
2682
  {
2020
- id: 'grandchild-a',
2021
- position: { x: 40, y: 20 },
2022
- size: { width: 120, height: 60 },
2023
- shapeId: 'default',
2024
- parentId: 'mid-parent-a',
2683
+ id: 'attach-group-label',
2684
+ content: 'Scenario B: Directional attach semantics (parent internal, child/sibling external)',
2685
+ position: { x: 620, y: 66 },
2025
2686
  },
2026
2687
  {
2027
- id: 'grandchild-b',
2028
- position: { x: 40, y: 20 },
2029
- size: { width: 120, height: 60 },
2030
- shapeId: 'default',
2031
- parentId: 'mid-parent-b',
2688
+ id: 'attach-link-parent-child-label',
2689
+ content: 'parent->child-A: parent endpoint INTERNAL, child endpoint EXTERNAL',
2690
+ position: { x: 620, y: 350 },
2691
+ },
2692
+ {
2693
+ id: 'attach-link-child-parent-label',
2694
+ content: 'child-B->parent: child endpoint EXTERNAL, parent endpoint INTERNAL',
2695
+ position: { x: 620, y: 372 },
2696
+ },
2697
+ {
2698
+ id: 'attach-link-child-child-label',
2699
+ content: 'child-A->child-B: sibling endpoints EXTERNAL on both ends',
2700
+ position: { x: 620, y: 394 },
2032
2701
  },
2033
- ],
2034
- ports: [
2035
- { id: 'block-source-port', elementId: 'block-source', position: { x: 70, y: 45 }, shapeId: 'port-circle' },
2036
- { id: 'block-target-port', elementId: 'block-target', position: { x: 70, y: 45 }, shapeId: 'port-circle' },
2037
- { id: 'shared-child-a-port', elementId: 'shared-child-a', position: { x: 70, y: 45 }, shapeId: 'port-circle' },
2038
- { id: 'shared-child-b-port', elementId: 'shared-child-b', position: { x: 70, y: 45 }, shapeId: 'port-circle' },
2039
- { id: 'parent-host-port', elementId: 'parent-host', position: { x: 250, y: 110 }, shapeId: 'port-circle' },
2040
- { id: 'edge-child-port', elementId: 'edge-child', position: { x: 0, y: 45 }, shapeId: 'port-circle' },
2041
- { id: 'grandchild-a-port', elementId: 'grandchild-a', position: { x: 60, y: 30 }, shapeId: 'port-circle' },
2042
- { id: 'grandchild-b-port', elementId: 'grandchild-b', position: { x: 60, y: 30 }, shapeId: 'port-circle' },
2043
- ],
2044
- links: obstacleRoutingLinks,
2045
- texts: [
2046
- { id: 'block-label', content: 'Blocking obstacle', position: { x: 60, y: 16 } },
2047
- { id: 'shared-label', content: 'Shared parent (siblings)', position: { x: 40, y: 158 } },
2048
- { id: 'parent-label', content: 'Parent-child (stay inside)', position: { x: 500, y: 158 } },
2049
- { id: 'grand-label', content: 'Grandchildren (shared ancestors)', position: { x: 40, y: 358 } },
2050
2702
  ],
2051
2703
  }); };
2052
2704
  var addLinksAction = {
@@ -2068,17 +2720,17 @@ var rerouteAllAction = {
2068
2720
  editor.rerouteAllLinks();
2069
2721
  },
2070
2722
  };
2071
- var obstacleRoutingDemoConfig = ({
2723
+ var obstacleRoutingDemoConfig = {
2072
2724
  id: 'obstacle-routing',
2073
2725
  title: 'Obstacle Routing',
2074
- description: 'Auto routing avoids obstacles and respects ancestor/edge rules.',
2726
+ description: 'Nested multi-anchor checks: sibling interior-avoidance + directional internal/external parent-child attach semantics.',
2075
2727
  createState: createObstacleRoutingState,
2076
- elementShapes: baseElementShapes,
2728
+ elementShapes: obstacleRoutingShapes,
2077
2729
  portShapes: basePortShapes,
2078
2730
  defaultElementShapeId: 'default',
2079
2731
  defaultPortShapeId: 'port-circle',
2080
2732
  actions: [addLinksAction, rerouteAllAction],
2081
- });
2733
+ };
2082
2734
 
2083
2735
  var createLinkBendHandlesState = function () { return ({
2084
2736
  elements: [
@@ -2436,7 +3088,7 @@ var shapeHoverDemoIds = {
2436
3088
  circle: 'hover-circle-host',
2437
3089
  noopRect: 'hover-noop-rect',
2438
3090
  };
2439
- var elementShapes = __spreadArray(__spreadArray([], baseElementShapes, true), [
3091
+ var elementShapes$1 = __spreadArray(__spreadArray([], baseElementShapes, true), [
2440
3092
  {
2441
3093
  id: 'hover-diamond-shape',
2442
3094
  kind: 'diamond',
@@ -2570,7 +3222,7 @@ var shapeHoverControlsDemoConfig = {
2570
3222
  title: 'Shape Hover Controls',
2571
3223
  description: 'Host-configured edge/midpoint/vertex plus ellipse-midpoint controls with trigger modes, selectors, click actions, and drag event logging.',
2572
3224
  createState: createShapeHoverControlsState,
2573
- elementShapes: elementShapes,
3225
+ elementShapes: elementShapes$1,
2574
3226
  portShapes: basePortShapes,
2575
3227
  defaultElementShapeId: 'default',
2576
3228
  defaultPortShapeId: 'port-circle',
@@ -2592,22 +3244,168 @@ var shapeHoverControlsDemoConfig = {
2592
3244
  ],
2593
3245
  };
2594
3246
 
3247
+ var vertexSessionDemoIds = {
3248
+ source: 'vertex-session-source',
3249
+ portTarget: 'vertex-session-port-target',
3250
+ elementTarget: 'vertex-session-element-target',
3251
+ nativeSource: 'vertex-session-native-source',
3252
+ nativeTarget: 'vertex-session-native-target',
3253
+ existingTargetPort: 'vertex-session-existing-target-port',
3254
+ nativeSourcePort: 'vertex-session-native-source-port',
3255
+ nativeTargetPort: 'vertex-session-native-target-port',
3256
+ };
3257
+ var elementShapes = __spreadArray(__spreadArray([], baseElementShapes, true), [
3258
+ {
3259
+ id: 'vertex-session-diamond',
3260
+ kind: 'diamond',
3261
+ style: {
3262
+ fill: '#f5edff',
3263
+ stroke: '#6a3da3',
3264
+ strokeWidth: 2,
3265
+ },
3266
+ },
3267
+ {
3268
+ id: 'vertex-session-target',
3269
+ kind: 'rect',
3270
+ style: {
3271
+ cornerRadius: 10,
3272
+ fill: '#edf7ff',
3273
+ stroke: '#2f6d97',
3274
+ strokeWidth: 2,
3275
+ },
3276
+ },
3277
+ ], false);
3278
+ var createVertexSessionState = function () { return ({
3279
+ elements: [
3280
+ {
3281
+ id: vertexSessionDemoIds.source,
3282
+ position: { x: 80, y: 120 },
3283
+ size: { width: 170, height: 130 },
3284
+ shapeId: 'vertex-session-diamond',
3285
+ },
3286
+ {
3287
+ id: vertexSessionDemoIds.portTarget,
3288
+ position: { x: 420, y: 110 },
3289
+ size: { width: 210, height: 140 },
3290
+ shapeId: 'vertex-session-target',
3291
+ },
3292
+ {
3293
+ id: vertexSessionDemoIds.elementTarget,
3294
+ position: { x: 720, y: 120 },
3295
+ size: { width: 220, height: 160 },
3296
+ shapeId: 'vertex-session-target',
3297
+ },
3298
+ {
3299
+ id: vertexSessionDemoIds.nativeSource,
3300
+ position: { x: 200, y: 360 },
3301
+ size: { width: 180, height: 120 },
3302
+ shapeId: 'default',
3303
+ },
3304
+ {
3305
+ id: vertexSessionDemoIds.nativeTarget,
3306
+ position: { x: 560, y: 360 },
3307
+ size: { width: 180, height: 120 },
3308
+ shapeId: 'default',
3309
+ },
3310
+ ],
3311
+ ports: [
3312
+ {
3313
+ id: vertexSessionDemoIds.existingTargetPort,
3314
+ elementId: vertexSessionDemoIds.portTarget,
3315
+ position: { x: 0, y: 70 },
3316
+ shapeId: 'port-circle',
3317
+ moveMode: 'border',
3318
+ anchorCenter: true,
3319
+ orientToHostBorder: true,
3320
+ },
3321
+ {
3322
+ id: vertexSessionDemoIds.nativeSourcePort,
3323
+ elementId: vertexSessionDemoIds.nativeSource,
3324
+ position: { x: 180, y: 60 },
3325
+ shapeId: 'port-circle',
3326
+ moveMode: 'border',
3327
+ anchorCenter: true,
3328
+ orientToHostBorder: true,
3329
+ },
3330
+ {
3331
+ id: vertexSessionDemoIds.nativeTargetPort,
3332
+ elementId: vertexSessionDemoIds.nativeTarget,
3333
+ position: { x: 0, y: 60 },
3334
+ shapeId: 'port-circle',
3335
+ moveMode: 'border',
3336
+ anchorCenter: true,
3337
+ orientToHostBorder: true,
3338
+ },
3339
+ ],
3340
+ links: [],
3341
+ texts: [
3342
+ {
3343
+ id: 'vertex-session-source-label',
3344
+ ownerId: vertexSessionDemoIds.source,
3345
+ content: 'Drag the vertex control to start programmatic link session',
3346
+ position: { x: 8, y: -18 },
3347
+ style: { fontSize: 13, fontFamily: 'sans-serif' },
3348
+ },
3349
+ {
3350
+ id: 'vertex-session-target-port-label',
3351
+ ownerId: vertexSessionDemoIds.portTarget,
3352
+ content: 'Scenario 1 target: existing port',
3353
+ position: { x: 8, y: -16 },
3354
+ style: { fontSize: 13, fontFamily: 'sans-serif' },
3355
+ },
3356
+ {
3357
+ id: 'vertex-session-target-element-label',
3358
+ ownerId: vertexSessionDemoIds.elementTarget,
3359
+ content: 'Scenario 2 target: element body (auto create destination port)',
3360
+ position: { x: 8, y: -16 },
3361
+ style: { fontSize: 13, fontFamily: 'sans-serif' },
3362
+ },
3363
+ {
3364
+ id: 'vertex-session-native-label',
3365
+ ownerId: vertexSessionDemoIds.nativeSource,
3366
+ content: 'Scenario 5 baseline: native port drag still works',
3367
+ position: { x: 8, y: -16 },
3368
+ style: { fontSize: 12, fontFamily: 'sans-serif' },
3369
+ },
3370
+ ],
3371
+ }); };
3372
+ var vertexControlLinkSessionDemoConfig = {
3373
+ id: 'vertex-control-link-session',
3374
+ title: 'Vertex Control Link Session',
3375
+ description: 'Programmatic link lifecycle driven by vertex-control drag: start, preview update, complete-to-port, complete-to-element, and cancel paths with event log verification.',
3376
+ createState: createVertexSessionState,
3377
+ elementShapes: elementShapes,
3378
+ portShapes: basePortShapes,
3379
+ defaultElementShapeId: 'default',
3380
+ defaultPortShapeId: 'port-circle',
3381
+ actions: [],
3382
+ };
3383
+
2595
3384
  var DisplayBoxControls = function (_a) {
2596
- var actions = _a.actions, snapEnabled = _a.snapEnabled, selectedLinkRouting = _a.selectedLinkRouting, canToggleLinkRouting = _a.canToggleLinkRouting, onReload = _a.onReload, onZoomIn = _a.onZoomIn, onZoomOut = _a.onZoomOut, onResetViewport = _a.onResetViewport, onToggleSnap = _a.onToggleSnap, onManualRender = _a.onManualRender, onToggleLinkRouting = _a.onToggleLinkRouting, onAction = _a.onAction;
2597
- return (React.createElement("div", { style: { display: 'flex', flexWrap: 'wrap', gap: 8, marginBottom: 12 } },
2598
- React.createElement("button", { onClick: onReload, style: { padding: '8px 12px' } }, "Reload Demo State"),
2599
- React.createElement("button", { onClick: onZoomIn, style: { padding: '8px 12px' } }, "Zoom In"),
2600
- React.createElement("button", { onClick: onZoomOut, style: { padding: '8px 12px' } }, "Zoom Out"),
2601
- React.createElement("button", { onClick: onResetViewport, style: { padding: '8px 12px' } }, "Reset Viewport"),
2602
- React.createElement("button", { onClick: onToggleSnap, style: { padding: '8px 12px' } },
2603
- "Snap: ",
2604
- snapEnabled ? 'On' : 'Off'),
2605
- React.createElement("button", { onClick: onManualRender, style: { padding: '8px 12px' } }, "Manual Render"),
2606
- React.createElement("button", { onClick: onToggleLinkRouting, style: { padding: '8px 12px' }, disabled: !canToggleLinkRouting },
2607
- "Toggle Link Routing (",
2608
- selectedLinkRouting,
2609
- ")"),
2610
- actions.map(function (action) { return (React.createElement("button", { key: action.id, onClick: function () { return onAction(action); }, style: { padding: '8px 12px' } }, action.label)); })));
3385
+ var actions = _a.actions, snapEnabled = _a.snapEnabled, selectedLinkRouting = _a.selectedLinkRouting, canToggleLinkRouting = _a.canToggleLinkRouting, onReload = _a.onReload, onZoomIn = _a.onZoomIn, onZoomOut = _a.onZoomOut, onResetViewport = _a.onResetViewport, onToggleSnap = _a.onToggleSnap, onManualRender = _a.onManualRender, onToggleLinkRouting = _a.onToggleLinkRouting, onAction = _a.onAction, onExportImage = _a.onExportImage, onClearExportPreview = _a.onClearExportPreview, exportPreviewDataUrl = _a.exportPreviewDataUrl, exportError = _a.exportError;
3386
+ return (React.createElement("div", { style: { marginBottom: 12 } },
3387
+ React.createElement("div", { style: { display: 'flex', flexWrap: 'wrap', gap: 8 } },
3388
+ React.createElement("button", { onClick: onReload, style: { padding: '8px 12px' } }, "Reload Demo State"),
3389
+ React.createElement("button", { onClick: onZoomIn, style: { padding: '8px 12px' } }, "Zoom In"),
3390
+ React.createElement("button", { onClick: onZoomOut, style: { padding: '8px 12px' } }, "Zoom Out"),
3391
+ React.createElement("button", { onClick: onResetViewport, style: { padding: '8px 12px' } }, "Reset Viewport"),
3392
+ React.createElement("button", { onClick: onToggleSnap, style: { padding: '8px 12px' } },
3393
+ "Snap: ",
3394
+ snapEnabled ? 'On' : 'Off'),
3395
+ React.createElement("button", { onClick: onManualRender, style: { padding: '8px 12px' } }, "Manual Render"),
3396
+ React.createElement("button", { onClick: onToggleLinkRouting, style: { padding: '8px 12px' }, disabled: !canToggleLinkRouting },
3397
+ "Toggle Link Routing (",
3398
+ selectedLinkRouting,
3399
+ ")"),
3400
+ onExportImage ? (React.createElement("button", { onClick: onExportImage, style: { padding: '8px 12px' } }, "Export PNG (2x)")) : null,
3401
+ onClearExportPreview && (exportPreviewDataUrl || exportError) ? (React.createElement("button", { onClick: onClearExportPreview, style: { padding: '8px 12px' } }, "Clear Export")) : null,
3402
+ actions.map(function (action) { return (React.createElement("button", { key: action.id, onClick: function () { return onAction(action); }, style: { padding: '8px 12px' } }, action.label)); })),
3403
+ exportError ? (React.createElement("div", { style: { marginTop: 8, color: '#a10000', fontSize: 12 } },
3404
+ "Export failed: ",
3405
+ exportError)) : null,
3406
+ exportPreviewDataUrl ? (React.createElement("div", { style: { marginTop: 10 } },
3407
+ React.createElement("div", { style: { fontSize: 12, color: '#444', marginBottom: 6 } }, "Latest export preview:"),
3408
+ React.createElement("img", { src: exportPreviewDataUrl, alt: "Exported diagram preview", style: { maxWidth: 320, width: '100%', height: 'auto', border: '1px solid #d0d0d0', borderRadius: 6 } }))) : null));
2611
3409
  };
2612
3410
 
2613
3411
  var DisplayBoxStage = function (_a) {
@@ -2626,6 +3424,8 @@ var useDemoControls = function (_a) {
2626
3424
  }, [diagramState, selection]);
2627
3425
  var selectedLinkRouting = (_b = selectedLink === null || selectedLink === void 0 ? void 0 : selectedLink.routing) !== null && _b !== void 0 ? _b : 'auto';
2628
3426
  var canToggleLinkRouting = Boolean(selectedLink);
3427
+ var _c = useState(null), exportPreviewDataUrl = _c[0], setExportPreviewDataUrl = _c[1];
3428
+ var _d = useState(null), exportError = _d[0], setExportError = _d[1];
2629
3429
  var handleAction = useCallback(function (action) {
2630
3430
  var editor = editorRef.current;
2631
3431
  if (!editor)
@@ -2669,9 +3469,29 @@ var useDemoControls = function (_a) {
2669
3469
  var next = selectedLinkRouting === 'auto' ? 'manual' : 'auto';
2670
3470
  editor.setLinkRoutingMode(selectedLink.id, next);
2671
3471
  }, [editorRef, selectedLink, selectedLinkRouting]);
3472
+ var handleExportImage = useCallback(function () {
3473
+ var editor = editorRef.current;
3474
+ if (!editor)
3475
+ return;
3476
+ try {
3477
+ var dataUrl = editor.exportImage({ mimeType: 'image/png', pixelRatio: 2 });
3478
+ setExportPreviewDataUrl(dataUrl);
3479
+ setExportError(null);
3480
+ }
3481
+ catch (error) {
3482
+ setExportPreviewDataUrl(null);
3483
+ setExportError(error instanceof Error ? error.message : 'Failed to export image.');
3484
+ }
3485
+ }, [editorRef]);
3486
+ var handleClearExportPreview = useCallback(function () {
3487
+ setExportPreviewDataUrl(null);
3488
+ setExportError(null);
3489
+ }, []);
2672
3490
  return {
2673
3491
  selectedLinkRouting: selectedLinkRouting,
2674
3492
  canToggleLinkRouting: canToggleLinkRouting,
3493
+ exportPreviewDataUrl: exportPreviewDataUrl,
3494
+ exportError: exportError,
2675
3495
  handleAction: handleAction,
2676
3496
  handleReload: handleReload,
2677
3497
  handleZoomIn: function () { return handleZoom(1.1); },
@@ -2680,6 +3500,8 @@ var useDemoControls = function (_a) {
2680
3500
  handleToggleSnap: handleToggleSnap,
2681
3501
  handleManualRender: handleManualRender,
2682
3502
  handleToggleLinkRouting: handleToggleLinkRouting,
3503
+ handleExportImage: handleExportImage,
3504
+ handleClearExportPreview: handleClearExportPreview,
2683
3505
  snapEnabled: snapEnabled,
2684
3506
  };
2685
3507
  };
@@ -2832,6 +3654,11 @@ var PortModel = /** @class */ (function () {
2832
3654
  this.moveMode = data.moveMode;
2833
3655
  this.anchorCenter = (_a = data.anchorCenter) !== null && _a !== void 0 ? _a : true;
2834
3656
  this.orientToHostBorder = (_b = data.orientToHostBorder) !== null && _b !== void 0 ? _b : true;
3657
+ this.placementPoint = data.placementPoint ? __assign({}, data.placementPoint) : undefined;
3658
+ this.linkAttachPoint = data.linkAttachPoint ? __assign({}, data.linkAttachPoint) : undefined;
3659
+ this.externalLinkAttachPoint = data.externalLinkAttachPoint ? __assign({}, data.externalLinkAttachPoint) : undefined;
3660
+ this.internalLinkAttachPoint = data.internalLinkAttachPoint ? __assign({}, data.internalLinkAttachPoint) : undefined;
3661
+ this.rotationPivot = data.rotationPivot ? __assign({}, data.rotationPivot) : undefined;
2835
3662
  this.currentAnchorId = data.currentAnchorId;
2836
3663
  }
2837
3664
  PortModel.prototype.setPosition = function (position) {
@@ -2860,6 +3687,11 @@ var PortModel = /** @class */ (function () {
2860
3687
  moveMode: this.moveMode,
2861
3688
  anchorCenter: this.anchorCenter,
2862
3689
  orientToHostBorder: this.orientToHostBorder,
3690
+ placementPoint: this.placementPoint ? __assign({}, this.placementPoint) : undefined,
3691
+ linkAttachPoint: this.linkAttachPoint ? __assign({}, this.linkAttachPoint) : undefined,
3692
+ externalLinkAttachPoint: this.externalLinkAttachPoint ? __assign({}, this.externalLinkAttachPoint) : undefined,
3693
+ internalLinkAttachPoint: this.internalLinkAttachPoint ? __assign({}, this.internalLinkAttachPoint) : undefined,
3694
+ rotationPivot: this.rotationPivot ? __assign({}, this.rotationPivot) : undefined,
2863
3695
  currentAnchorId: this.currentAnchorId,
2864
3696
  };
2865
3697
  };
@@ -3888,21 +4720,42 @@ var ObstacleRouter = /** @class */ (function () {
3888
4720
  ObstacleRouter.prototype.computeStubEndpoint = function (point, endpoint, bounds, obstacles) {
3889
4721
  if (!(endpoint === null || endpoint === void 0 ? void 0 : endpoint.onEdgeSide) || !endpoint.rect)
3890
4722
  return null;
3891
- var normal = this.getNormal(endpoint.onEdgeSide);
4723
+ var outwardNormal = this.getNormal(endpoint.onEdgeSide);
4724
+ var selectedNormal = outwardNormal;
4725
+ var maxLength = this.computeAvailableStubLength(point, outwardNormal, bounds, obstacles, endpoint.elementId);
4726
+ // If the outward normal is fully blocked by route bounds on the endpoint host, fall back inward
4727
+ // to preserve a visible perpendicular stub for bounded parent-child routes.
4728
+ if (maxLength <= this.tolerance && bounds && this.rectsEqual(bounds, endpoint.rect)) {
4729
+ var inwardNormal = { x: -outwardNormal.x, y: -outwardNormal.y };
4730
+ var inwardLength = this.computeAvailableStubLength(point, inwardNormal, bounds, obstacles, endpoint.elementId);
4731
+ if (inwardLength > maxLength) {
4732
+ selectedNormal = inwardNormal;
4733
+ maxLength = inwardLength;
4734
+ }
4735
+ }
4736
+ if (maxLength <= 0)
4737
+ return null;
4738
+ return {
4739
+ x: point.x + selectedNormal.x * maxLength,
4740
+ y: point.y + selectedNormal.y * maxLength,
4741
+ };
4742
+ };
4743
+ ObstacleRouter.prototype.computeAvailableStubLength = function (point, normal, bounds, obstacles, ignoreId) {
3892
4744
  var maxLength = this.stubLength;
3893
4745
  if (bounds) {
3894
4746
  maxLength = Math.min(maxLength, this.distanceToBounds(point, normal, bounds));
3895
4747
  }
3896
- var obstacleLimit = this.distanceToObstacles(point, normal, obstacles, endpoint.elementId);
4748
+ var obstacleLimit = this.distanceToObstacles(point, normal, obstacles, ignoreId);
3897
4749
  if (obstacleLimit !== null) {
3898
4750
  maxLength = Math.min(maxLength, obstacleLimit);
3899
4751
  }
3900
- if (maxLength <= 0)
3901
- return null;
3902
- return {
3903
- x: point.x + normal.x * maxLength,
3904
- y: point.y + normal.y * maxLength,
3905
- };
4752
+ return maxLength;
4753
+ };
4754
+ ObstacleRouter.prototype.rectsEqual = function (a, b) {
4755
+ return (Math.abs(a.x - b.x) <= this.tolerance &&
4756
+ Math.abs(a.y - b.y) <= this.tolerance &&
4757
+ Math.abs(a.width - b.width) <= this.tolerance &&
4758
+ Math.abs(a.height - b.height) <= this.tolerance);
3906
4759
  };
3907
4760
  ObstacleRouter.prototype.getNormal = function (side) {
3908
4761
  switch (side) {
@@ -4199,7 +5052,7 @@ var borderSideToInwardRotation = function (side) {
4199
5052
  return 0;
4200
5053
  };
4201
5054
 
4202
- var POSITION_EPSILON$1 = 1e-6;
5055
+ var POSITION_EPSILON$2 = 1e-6;
4203
5056
  var MIN_TRUNCATION_WIDTH_SAFETY_EPSILON = 0.5;
4204
5057
  var FONT_SIZE_TRUNCATION_SAFETY_RATIO = 1.4;
4205
5058
  var FONT_SIZE_TRUNCATION_SAFETY_BASE = 2;
@@ -4225,7 +5078,7 @@ var TextLayoutService = /** @class */ (function () {
4225
5078
  var textRenderPadding = this.resolveTextRenderPadding(text.style);
4226
5079
  var horizontalRenderInset = textRenderPadding * 2;
4227
5080
  var verticalRenderInset = textRenderPadding * 2;
4228
- var zeroPaddingInset = padding <= POSITION_EPSILON$1 && this.shouldTrackOwnerBoundLayout(text) ? 0.5 : 0;
5081
+ var zeroPaddingInset = padding <= POSITION_EPSILON$2 && this.shouldTrackOwnerBoundLayout(text) ? 0.5 : 0;
4229
5082
  var drawableWidth = maxWidth !== undefined ? Math.max(0, maxWidth - zeroPaddingInset * 2 - horizontalRenderInset) : undefined;
4230
5083
  var drawableHeight = maxHeight !== undefined ? Math.max(0, maxHeight - zeroPaddingInset * 2 - verticalRenderInset) : undefined;
4231
5084
  var lineHeight = this.textMeasurer.measure(__assign(__assign({}, text), { content: 'M' })).height;
@@ -4542,6 +5395,7 @@ var AutoLayoutService = /** @class */ (function () {
4542
5395
  var childFitCrossAxis = (_e = layout.childFitCrossAxis) !== null && _e !== void 0 ? _e : 'none';
4543
5396
  var childFitMinSize = layout.childFitMinSize;
4544
5397
  var childFitMaxSize = layout.childFitMaxSize;
5398
+ var labelReservedTopLane = this.resolveLabelReservedTopLane(parentId, layout);
4545
5399
  var sorted = __spreadArray([], children, true).sort(function (a, b) {
4546
5400
  var aPos = axis === 'horizontal' ? a.position.x : a.position.y;
4547
5401
  var bPos = axis === 'horizontal' ? b.position.x : b.position.y;
@@ -4552,7 +5406,7 @@ var AutoLayoutService = /** @class */ (function () {
4552
5406
  var gapCount = Math.max(0, sorted.length - 1);
4553
5407
  var calculateFittedSizes = function (parentWidth, parentHeight) {
4554
5408
  var availableWidth = Math.max(0, parentWidth - padding.x * 2);
4555
- var availableHeight = Math.max(0, parentHeight - padding.y * 2);
5409
+ var availableHeight = Math.max(0, parentHeight - padding.y * 2 - labelReservedTopLane);
4556
5410
  var availableMain = axis === 'horizontal'
4557
5411
  ? Math.max(0, availableWidth - gap * gapCount)
4558
5412
  : Math.max(0, availableHeight - gap * gapCount);
@@ -4597,12 +5451,12 @@ var AutoLayoutService = /** @class */ (function () {
4597
5451
  if (axis === 'horizontal') {
4598
5452
  return {
4599
5453
  width: Math.max(parent.size.width, padding.x * 2 + totalWidth + gap * gapCount),
4600
- height: Math.max(parent.size.height, padding.y * 2 + maxHeight),
5454
+ height: Math.max(parent.size.height, padding.y * 2 + labelReservedTopLane + maxHeight),
4601
5455
  };
4602
5456
  }
4603
5457
  return {
4604
5458
  width: Math.max(parent.size.width, padding.x * 2 + maxWidth),
4605
- height: Math.max(parent.size.height, padding.y * 2 + totalHeight + gap * gapCount),
5459
+ height: Math.max(parent.size.height, padding.y * 2 + labelReservedTopLane + totalHeight + gap * gapCount),
4606
5460
  };
4607
5461
  };
4608
5462
  var fittedSizes = calculateFittedSizes(parent.size.width, parent.size.height);
@@ -4610,9 +5464,9 @@ var AutoLayoutService = /** @class */ (function () {
4610
5464
  fittedSizes = calculateFittedSizes(newParentWidth, newParentHeight);
4611
5465
  (_a = resolveParentSize(fittedSizes), newParentWidth = _a.width, newParentHeight = _a.height);
4612
5466
  var availableWidth = Math.max(0, newParentWidth - padding.x * 2);
4613
- var availableHeight = Math.max(0, newParentHeight - padding.y * 2);
5467
+ var availableHeight = Math.max(0, newParentHeight - padding.y * 2 - labelReservedTopLane);
4614
5468
  var cursorX = padding.x;
4615
- var cursorY = padding.y;
5469
+ var cursorY = padding.y + labelReservedTopLane;
4616
5470
  var desiredPositions = new Map();
4617
5471
  var desiredSizes = new Map();
4618
5472
  sorted.forEach(function (child, index) {
@@ -4620,10 +5474,10 @@ var AutoLayoutService = /** @class */ (function () {
4620
5474
  desiredSizes.set(child.id, fittedSize);
4621
5475
  if (axis === 'horizontal') {
4622
5476
  var y = align === 'start'
4623
- ? padding.y
5477
+ ? padding.y + labelReservedTopLane
4624
5478
  : align === 'end'
4625
5479
  ? newParentHeight - padding.y - fittedSize.height
4626
- : padding.y + Math.max(0, (availableHeight - fittedSize.height) / 2);
5480
+ : padding.y + labelReservedTopLane + Math.max(0, (availableHeight - fittedSize.height) / 2);
4627
5481
  desiredPositions.set(child.id, { x: cursorX, y: y });
4628
5482
  cursorX += fittedSize.width + (index < sorted.length - 1 ? gap : 0);
4629
5483
  }
@@ -4716,6 +5570,45 @@ var AutoLayoutService = /** @class */ (function () {
4716
5570
  y: (_c = padding.y) !== null && _c !== void 0 ? _c : 12,
4717
5571
  };
4718
5572
  };
5573
+ AutoLayoutService.prototype.resolveLabelReservedTopLane = function (parentId, layout) {
5574
+ var _a, _b, _c, _d;
5575
+ var policy = layout === null || layout === void 0 ? void 0 : layout.labelReservedSpace;
5576
+ if (!policy)
5577
+ return 0;
5578
+ var mode = (_a = policy.mode) !== null && _a !== void 0 ? _a : 'none';
5579
+ if (mode === 'none')
5580
+ return 0;
5581
+ if (((_b = policy.placement) !== null && _b !== void 0 ? _b : 'top') !== 'top')
5582
+ return 0;
5583
+ var minSize = Math.max(0, (_c = policy.minSize) !== null && _c !== void 0 ? _c : 0);
5584
+ var maxSize = typeof policy.maxSize === 'number' && Number.isFinite(policy.maxSize)
5585
+ ? Math.max(minSize, policy.maxSize)
5586
+ : Number.POSITIVE_INFINITY;
5587
+ var resolved = 0;
5588
+ if (mode === 'fixed') {
5589
+ resolved = Math.max(0, (_d = policy.size) !== null && _d !== void 0 ? _d : 0);
5590
+ }
5591
+ else {
5592
+ resolved = this.resolveFlexibleLabelLaneFromText(parentId);
5593
+ }
5594
+ return this.clampLayoutSize(resolved, minSize, maxSize);
5595
+ };
5596
+ AutoLayoutService.prototype.resolveFlexibleLabelLaneFromText = function (parentId) {
5597
+ var lane = 0;
5598
+ this.model.texts.forEach(function (text) {
5599
+ var _a, _b, _c;
5600
+ if (text.ownerId !== parentId)
5601
+ return;
5602
+ var offset = (_a = text.displayOffset) !== null && _a !== void 0 ? _a : { x: 0, y: 0 };
5603
+ var size = (_b = text.displayClipSize) !== null && _b !== void 0 ? _b : text.size;
5604
+ var height = (_c = size === null || size === void 0 ? void 0 : size.height) !== null && _c !== void 0 ? _c : 0;
5605
+ var bottom = text.position.y + offset.y + height;
5606
+ if (bottom > lane) {
5607
+ lane = bottom;
5608
+ }
5609
+ });
5610
+ return Math.max(0, lane);
5611
+ };
4719
5612
  AutoLayoutService.prototype.clampLayoutSize = function (value, min, max) {
4720
5613
  var minValue = min !== null && min !== void 0 ? min : 0;
4721
5614
  var maxValue = max !== null && max !== void 0 ? max : Number.POSITIVE_INFINITY;
@@ -4784,7 +5677,106 @@ var AutoLayoutService = /** @class */ (function () {
4784
5677
  return AutoLayoutService;
4785
5678
  }());
4786
5679
 
5680
+ var ZERO_POINT = { x: 0, y: 0 };
5681
+ var clonePoint = function (point) { return ({ x: point.x, y: point.y }); };
5682
+ var borderSideToNormal = function (side) {
5683
+ if (side === 'left')
5684
+ return { x: -1, y: 0 };
5685
+ if (side === 'right')
5686
+ return { x: 1, y: 0 };
5687
+ if (side === 'top')
5688
+ return { x: 0, y: -1 };
5689
+ return { x: 0, y: 1 };
5690
+ };
5691
+ var resolvePortGeometryPoints = function (port, attachMode) {
5692
+ var _a, _b, _c, _d, _e;
5693
+ var placementPoint = clonePoint((_a = port.placementPoint) !== null && _a !== void 0 ? _a : ZERO_POINT);
5694
+ var sharedLinkAttachPoint = clonePoint((_b = port.linkAttachPoint) !== null && _b !== void 0 ? _b : placementPoint);
5695
+ var effectiveLinkAttachPoint = clonePoint(attachMode === 'internal'
5696
+ ? (_c = port.internalLinkAttachPoint) !== null && _c !== void 0 ? _c : sharedLinkAttachPoint
5697
+ : (_d = port.externalLinkAttachPoint) !== null && _d !== void 0 ? _d : sharedLinkAttachPoint);
5698
+ var rotationPivot = clonePoint((_e = port.rotationPivot) !== null && _e !== void 0 ? _e : placementPoint);
5699
+ return {
5700
+ placementPoint: placementPoint,
5701
+ sharedLinkAttachPoint: sharedLinkAttachPoint,
5702
+ effectiveLinkAttachPoint: effectiveLinkAttachPoint,
5703
+ rotationPivot: rotationPivot,
5704
+ };
5705
+ };
5706
+ var resolvePortOrientationContext = function (options) {
5707
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
5708
+ var port = options.port, worldPlacement = options.worldPlacement, host = options.host, shapeRegistry = options.shapeRegistry, attachMode = options.attachMode;
5709
+ var portShape = port.shapeId ? shapeRegistry.get(port.shapeId) : undefined;
5710
+ var localRotation = ((_a = portShape === null || portShape === void 0 ? void 0 : portShape.baseRotation) !== null && _a !== void 0 ? _a : 0) +
5711
+ (typeof ((_b = port.style) === null || _b === void 0 ? void 0 : _b.rotation) === 'number' ? port.style.rotation : 0);
5712
+ var nodeAnchorPoint = port.anchorCenter && (portShape === null || portShape === void 0 ? void 0 : portShape.svgPath)
5713
+ ? {
5714
+ x: ((_f = (_d = (_c = portShape.svgSize) === null || _c === void 0 ? void 0 : _c.width) !== null && _d !== void 0 ? _d : (_e = port.size) === null || _e === void 0 ? void 0 : _e.width) !== null && _f !== void 0 ? _f : 0) / 2,
5715
+ y: ((_k = (_h = (_g = portShape.svgSize) === null || _g === void 0 ? void 0 : _g.height) !== null && _h !== void 0 ? _h : (_j = port.size) === null || _j === void 0 ? void 0 : _j.height) !== null && _k !== void 0 ? _k : 0) / 2,
5716
+ }
5717
+ : __assign({}, ZERO_POINT);
5718
+ if (port.moveMode !== 'border' || port.orientToHostBorder === false || !host) {
5719
+ return { localRotation: localRotation, rotation: 0, offset: __assign({}, ZERO_POINT), nodeAnchorPoint: nodeAnchorPoint };
5720
+ }
5721
+ var hostRect = {
5722
+ x: host.position.x,
5723
+ y: host.position.y,
5724
+ width: host.size.width,
5725
+ height: host.size.height,
5726
+ };
5727
+ var shape = shapeRegistry.get(host.shapeId);
5728
+ var side = (shape === null || shape === void 0 ? void 0 : shape.resolveBorderSide)
5729
+ ? shape.resolveBorderSide(worldPlacement, hostRect)
5730
+ : resolveBoundarySide(worldPlacement, hostRect, 'rect');
5731
+ var normal = (shape === null || shape === void 0 ? void 0 : shape.resolveBorderNormal)
5732
+ ? shape.resolveBorderNormal(worldPlacement, hostRect)
5733
+ : borderSideToNormal(side);
5734
+ var geometry = resolvePortGeometryPoints(port, attachMode);
5735
+ var hookContext = {
5736
+ side: side,
5737
+ normal: normal,
5738
+ hostRect: hostRect,
5739
+ attachMode: attachMode,
5740
+ effectiveLinkAttachPoint: geometry.effectiveLinkAttachPoint,
5741
+ placementPoint: geometry.placementPoint,
5742
+ rotationPivot: geometry.rotationPivot,
5743
+ portSize: port.size,
5744
+ };
5745
+ var hookResult = (_l = shape === null || shape === void 0 ? void 0 : shape.resolvePortBorderTransform) === null || _l === void 0 ? void 0 : _l.call(shape, hookContext);
5746
+ return {
5747
+ localRotation: localRotation,
5748
+ nodeAnchorPoint: nodeAnchorPoint,
5749
+ side: side,
5750
+ normal: normal,
5751
+ rotation: borderSideToInwardRotation(side) + ((_m = hookResult === null || hookResult === void 0 ? void 0 : hookResult.rotation) !== null && _m !== void 0 ? _m : 0),
5752
+ offset: (hookResult === null || hookResult === void 0 ? void 0 : hookResult.offset) ? clonePoint(hookResult.offset) : __assign({}, ZERO_POINT),
5753
+ };
5754
+ };
5755
+ var resolvePortWorldTransform = function (options) {
5756
+ var port = options.port, worldPlacement = options.worldPlacement, attachMode = options.attachMode, orientation = options.orientation;
5757
+ var geometry = resolvePortGeometryPoints(port, attachMode);
5758
+ var totalRotation = orientation.localRotation + orientation.rotation;
5759
+ var rotatedPlacement = rotatePoint({
5760
+ x: geometry.placementPoint.x - orientation.nodeAnchorPoint.x,
5761
+ y: geometry.placementPoint.y - orientation.nodeAnchorPoint.y,
5762
+ }, ZERO_POINT, totalRotation);
5763
+ var rotatedAttach = rotatePoint({
5764
+ x: geometry.effectiveLinkAttachPoint.x - orientation.nodeAnchorPoint.x,
5765
+ y: geometry.effectiveLinkAttachPoint.y - orientation.nodeAnchorPoint.y,
5766
+ }, ZERO_POINT, totalRotation);
5767
+ var nodePosition = {
5768
+ x: worldPlacement.x - rotatedPlacement.x + orientation.offset.x,
5769
+ y: worldPlacement.y - rotatedPlacement.y + orientation.offset.y,
5770
+ };
5771
+ var linkAttachWorld = {
5772
+ x: nodePosition.x + rotatedAttach.x,
5773
+ y: nodePosition.y + rotatedAttach.y,
5774
+ };
5775
+ return { nodePosition: nodePosition, linkAttachWorld: linkAttachWorld };
5776
+ };
5777
+
4787
5778
  var EDGE_TOLERANCE = 0.5;
5779
+ var POSITION_EPSILON$1 = 1e-6;
4788
5780
  var LinkRoutingService = /** @class */ (function () {
4789
5781
  function LinkRoutingService(config) {
4790
5782
  this.model = config.model;
@@ -4808,8 +5800,9 @@ var LinkRoutingService = /** @class */ (function () {
4808
5800
  }
4809
5801
  if (updatedLinks.has(link.id))
4810
5802
  return;
4811
- var source = _this.model.getPortWorldPosition(link.sourcePortId);
4812
- var target = _this.model.getPortWorldPosition(link.targetPortId);
5803
+ var endpoints = _this.resolveLinkEndpoints(link);
5804
+ var source = endpoints.source;
5805
+ var target = endpoints.target;
4813
5806
  if (!source || !target)
4814
5807
  return;
4815
5808
  var points = _this.resolveLinkPointsForUpdate(link, source, target);
@@ -4826,8 +5819,9 @@ var LinkRoutingService = /** @class */ (function () {
4826
5819
  var _a;
4827
5820
  if (link.points.length > 0)
4828
5821
  return;
4829
- var source = _this.model.getPortWorldPosition(link.sourcePortId);
4830
- var target = _this.model.getPortWorldPosition(link.targetPortId);
5822
+ var endpoints = _this.resolveLinkEndpoints(link);
5823
+ var source = endpoints.source;
5824
+ var target = endpoints.target;
4831
5825
  if (!source || !target)
4832
5826
  return;
4833
5827
  var points = ((_a = link.routing) !== null && _a !== void 0 ? _a : 'auto') === 'manual'
@@ -4838,6 +5832,31 @@ var LinkRoutingService = /** @class */ (function () {
4838
5832
  });
4839
5833
  return patches;
4840
5834
  };
5835
+ LinkRoutingService.prototype.getPortLinkWorldPosition = function (portId, options) {
5836
+ var _a;
5837
+ var port = this.model.getPort(portId);
5838
+ if (!port)
5839
+ return null;
5840
+ var worldPlacement = this.model.getPortWorldPosition(portId);
5841
+ if (!worldPlacement)
5842
+ return null;
5843
+ var host = this.resolveHostForPort(portId);
5844
+ var attachMode = (_a = options === null || options === void 0 ? void 0 : options.attachMode) !== null && _a !== void 0 ? _a : this.resolveAttachModeForPorts(portId, options === null || options === void 0 ? void 0 : options.oppositePortId);
5845
+ var orientation = resolvePortOrientationContext({
5846
+ port: port.toData(),
5847
+ worldPlacement: worldPlacement,
5848
+ host: host,
5849
+ shapeRegistry: this.shapeRegistry,
5850
+ attachMode: attachMode,
5851
+ });
5852
+ var transformed = resolvePortWorldTransform({
5853
+ port: port.toData(),
5854
+ worldPlacement: worldPlacement,
5855
+ attachMode: attachMode,
5856
+ orientation: orientation,
5857
+ });
5858
+ return transformed.linkAttachWorld;
5859
+ };
4841
5860
  LinkRoutingService.prototype.computeAutoRoute = function (link, source, target) {
4842
5861
  var routed = this.router.route(source, target, this.buildRouteContext(link));
4843
5862
  if (!routed || routed.length < 2) {
@@ -4856,15 +5875,37 @@ var LinkRoutingService = /** @class */ (function () {
4856
5875
  }
4857
5876
  return this.computeAutoRoute(link, source, target);
4858
5877
  };
5878
+ LinkRoutingService.prototype.resolveLinkEndpoints = function (link) {
5879
+ return {
5880
+ source: this.getPortLinkWorldPosition(link.sourcePortId, { oppositePortId: link.targetPortId }),
5881
+ target: this.getPortLinkWorldPosition(link.targetPortId, { oppositePortId: link.sourcePortId }),
5882
+ };
5883
+ };
4859
5884
  LinkRoutingService.prototype.updateManualRoute = function (points, source, target) {
4860
5885
  if (points.length < 2) {
4861
5886
  return [__assign({}, source), __assign({}, target)];
4862
5887
  }
5888
+ var sourceDelta = {
5889
+ x: source.x - points[0].x,
5890
+ y: source.y - points[0].y,
5891
+ };
5892
+ var targetDelta = {
5893
+ x: target.x - points[points.length - 1].x,
5894
+ y: target.y - points[points.length - 1].y,
5895
+ };
5896
+ var shouldTranslateAllPoints = Math.abs(sourceDelta.x - targetDelta.x) <= POSITION_EPSILON$1 &&
5897
+ Math.abs(sourceDelta.y - targetDelta.y) <= POSITION_EPSILON$1;
4863
5898
  return points.map(function (point, index) {
4864
5899
  if (index === 0)
4865
5900
  return __assign({}, source);
4866
5901
  if (index === points.length - 1)
4867
5902
  return __assign({}, target);
5903
+ if (shouldTranslateAllPoints) {
5904
+ return {
5905
+ x: point.x + sourceDelta.x,
5906
+ y: point.y + sourceDelta.y,
5907
+ };
5908
+ }
4868
5909
  return __assign({}, point);
4869
5910
  });
4870
5911
  };
@@ -4875,8 +5916,9 @@ var LinkRoutingService = /** @class */ (function () {
4875
5916
  var targetPort = this.model.getPort(link.targetPortId);
4876
5917
  var sourceElementId = sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.elementId;
4877
5918
  var targetElementId = targetPort === null || targetPort === void 0 ? void 0 : targetPort.elementId;
4878
- var sourcePosition = this.model.getPortWorldPosition(link.sourcePortId);
4879
- var targetPosition = this.model.getPortWorldPosition(link.targetPortId);
5919
+ var endpoints = this.resolveLinkEndpoints(link);
5920
+ var sourcePosition = endpoints.source;
5921
+ var targetPosition = endpoints.target;
4880
5922
  var sourceRect = sourceElementId ? this.getElementRect(sourceElementId) : null;
4881
5923
  var targetRect = targetElementId ? this.getElementRect(targetElementId) : null;
4882
5924
  var sourceEndpointGeometry = sourcePosition && sourceRect
@@ -5006,6 +6048,37 @@ var LinkRoutingService = /** @class */ (function () {
5006
6048
  height: element.size.height,
5007
6049
  };
5008
6050
  };
6051
+ LinkRoutingService.prototype.resolveHostForPort = function (portId) {
6052
+ var _a;
6053
+ var port = this.model.getPort(portId);
6054
+ if (!port)
6055
+ return null;
6056
+ var element = this.model.getElement(port.elementId);
6057
+ if (!element)
6058
+ return null;
6059
+ return {
6060
+ id: element.id,
6061
+ position: (_a = this.model.getElementWorldPosition(element.id)) !== null && _a !== void 0 ? _a : element.position,
6062
+ size: element.size,
6063
+ shapeId: element.shapeId,
6064
+ };
6065
+ };
6066
+ LinkRoutingService.prototype.resolveAttachModeForPorts = function (portId, oppositePortId) {
6067
+ var _a, _b;
6068
+ if (!oppositePortId)
6069
+ return 'external';
6070
+ var elementId = (_a = this.model.getPort(portId)) === null || _a === void 0 ? void 0 : _a.elementId;
6071
+ var oppositeElementId = (_b = this.model.getPort(oppositePortId)) === null || _b === void 0 ? void 0 : _b.elementId;
6072
+ if (!elementId || !oppositeElementId)
6073
+ return 'external';
6074
+ return this.isAncestorOf(elementId, oppositeElementId) ? 'internal' : 'external';
6075
+ };
6076
+ LinkRoutingService.prototype.isAncestorOf = function (candidateAncestorId, candidateDescendantId) {
6077
+ if (candidateAncestorId === candidateDescendantId)
6078
+ return false;
6079
+ var descendantChain = this.getAncestorChain(candidateDescendantId);
6080
+ return descendantChain.includes(candidateAncestorId);
6081
+ };
5009
6082
  LinkRoutingService.prototype.getAncestorChain = function (elementId) {
5010
6083
  var _a;
5011
6084
  var chain = [];
@@ -5164,11 +6237,17 @@ var DiagramEngine = /** @class */ (function () {
5164
6237
  DiagramEngine.prototype.load = function (state) {
5165
6238
  var _this = this;
5166
6239
  var patches = this.commandQueue.run(createLoadCommand(state), this.model);
5167
- var allPatches = this.mutationPipeline.run({
6240
+ var mutationPatches = this.mutationPipeline.run({
5168
6241
  basePatches: patches,
5169
6242
  layoutSteps: [function () { return _this.applyAllLayouts(); }],
5170
6243
  includeEmptyLinkRouting: true,
5171
6244
  });
6245
+ var normalizedPorts = this.normalizePortsForHostPolicies();
6246
+ var normalizedLinkPatches = normalizedPorts.movedPortIds.length > 0
6247
+ ? this.updateLinksForPorts(normalizedPorts.movedPortIds)
6248
+ : [];
6249
+ var textPresentationPatches = this.resolveAllTextPresentationPatches(false);
6250
+ var allPatches = __spreadArray(__spreadArray(__spreadArray(__spreadArray([], mutationPatches, true), normalizedPorts.patches, true), normalizedLinkPatches, true), textPresentationPatches, true);
5172
6251
  this.emitChange(allPatches);
5173
6252
  };
5174
6253
  DiagramEngine.prototype.getState = function () {
@@ -5383,8 +6462,12 @@ var DiagramEngine = /** @class */ (function () {
5383
6462
  };
5384
6463
  DiagramEngine.prototype.addLink = function (link) {
5385
6464
  var _a;
5386
- var source = this.model.getPortWorldPosition(link.sourcePortId);
5387
- var target = this.model.getPortWorldPosition(link.targetPortId);
6465
+ var source = this.linkRoutingService.getPortLinkWorldPosition(link.sourcePortId, {
6466
+ oppositePortId: link.targetPortId,
6467
+ });
6468
+ var target = this.linkRoutingService.getPortLinkWorldPosition(link.targetPortId, {
6469
+ oppositePortId: link.sourcePortId,
6470
+ });
5388
6471
  var routing = (_a = link.routing) !== null && _a !== void 0 ? _a : 'auto';
5389
6472
  var points = link.points;
5390
6473
  if (source && target) {
@@ -5408,8 +6491,12 @@ var DiagramEngine = /** @class */ (function () {
5408
6491
  return;
5409
6492
  var update = { routing: mode };
5410
6493
  if (mode === 'auto') {
5411
- var source = this.model.getPortWorldPosition(link.sourcePortId);
5412
- var target = this.model.getPortWorldPosition(link.targetPortId);
6494
+ var source = this.linkRoutingService.getPortLinkWorldPosition(link.sourcePortId, {
6495
+ oppositePortId: link.targetPortId,
6496
+ });
6497
+ var target = this.linkRoutingService.getPortLinkWorldPosition(link.targetPortId, {
6498
+ oppositePortId: link.sourcePortId,
6499
+ });
5413
6500
  if (source && target) {
5414
6501
  update.points = this.computeAutoRoute(link, source, target);
5415
6502
  }
@@ -5443,8 +6530,12 @@ var DiagramEngine = /** @class */ (function () {
5443
6530
  var routing = (_a = link.routing) !== null && _a !== void 0 ? _a : 'auto';
5444
6531
  if (routing === 'manual' && !includeManual)
5445
6532
  return;
5446
- var source = _this.model.getPortWorldPosition(link.sourcePortId);
5447
- var target = _this.model.getPortWorldPosition(link.targetPortId);
6533
+ var source = _this.linkRoutingService.getPortLinkWorldPosition(link.sourcePortId, {
6534
+ oppositePortId: link.targetPortId,
6535
+ });
6536
+ var target = _this.linkRoutingService.getPortLinkWorldPosition(link.targetPortId, {
6537
+ oppositePortId: link.sourcePortId,
6538
+ });
5448
6539
  if (!source || !target)
5449
6540
  return;
5450
6541
  var points = _this.computeAutoRoute(link, source, target);
@@ -5468,8 +6559,10 @@ var DiagramEngine = /** @class */ (function () {
5468
6559
  this.emitChange(patches);
5469
6560
  };
5470
6561
  DiagramEngine.prototype.updateText = function (id, content) {
6562
+ var _this = this;
5471
6563
  var patches = this.commandQueue.run(createUpdateTextCommand(id, content), this.model);
5472
6564
  var text = this.model.getText(id);
6565
+ var layoutOwnerId = (text === null || text === void 0 ? void 0 : text.ownerId) && this.hasFlexibleLabelReservedSpace(text.ownerId) ? text.ownerId : null;
5473
6566
  if (text) {
5474
6567
  var resolved = this.resolveTextPresentation(text.toData());
5475
6568
  text.setSize(resolved.size);
@@ -5490,7 +6583,18 @@ var DiagramEngine = /** @class */ (function () {
5490
6583
  reason: 'content',
5491
6584
  });
5492
6585
  }
5493
- this.emitChange(patches);
6586
+ if (!layoutOwnerId) {
6587
+ this.emitChange(patches);
6588
+ return;
6589
+ }
6590
+ var allPatches = this.mutationPipeline.run({
6591
+ basePatches: patches,
6592
+ layoutSteps: [
6593
+ function () { return _this.applyLayoutForParent(layoutOwnerId); },
6594
+ function () { var _a, _b; return _this.applyLayoutCascade((_b = (_a = _this.model.getElement(layoutOwnerId)) === null || _a === void 0 ? void 0 : _a.parentId) !== null && _b !== void 0 ? _b : null); },
6595
+ ],
6596
+ });
6597
+ this.emitChange(allPatches);
5494
6598
  };
5495
6599
  DiagramEngine.prototype.moveTextTo = function (id, x, y) {
5496
6600
  var text = this.model.getText(id);
@@ -5593,6 +6697,9 @@ var DiagramEngine = /** @class */ (function () {
5593
6697
  DiagramEngine.prototype.getPortWorldPosition = function (id) {
5594
6698
  return this.model.getPortWorldPosition(id);
5595
6699
  };
6700
+ DiagramEngine.prototype.getPortLinkWorldPosition = function (id, options) {
6701
+ return this.linkRoutingService.getPortLinkWorldPosition(id, options);
6702
+ };
5596
6703
  DiagramEngine.prototype.getTextWorldPosition = function (id) {
5597
6704
  return this.model.getTextWorldPosition(id);
5598
6705
  };
@@ -5732,7 +6839,7 @@ var DiagramEngine = /** @class */ (function () {
5732
6839
  if (!host)
5733
6840
  return;
5734
6841
  host.portIds.forEach(function (portId) {
5735
- var _a;
6842
+ var _a, _b, _c;
5736
6843
  var port = _this.model.getPort(portId);
5737
6844
  if (!port)
5738
6845
  return;
@@ -5755,11 +6862,16 @@ var DiagramEngine = /** @class */ (function () {
5755
6862
  return;
5756
6863
  }
5757
6864
  var projected = _this.resolveBorderPortResizeProjection(port.position, host.shapeId, sizeInfo, host.size);
5758
- var unchanged = Math.abs(projected.x - port.position.x) <= POSITION_EPSILON &&
5759
- Math.abs(projected.y - port.position.y) <= POSITION_EPSILON;
6865
+ var constrained = _this.resolveConstrainedPortRelativePosition(port, host, projected);
6866
+ var unchanged = Math.abs(constrained.position.x - port.position.x) <= POSITION_EPSILON &&
6867
+ Math.abs(constrained.position.y - port.position.y) <= POSITION_EPSILON &&
6868
+ ((_b = constrained.currentAnchorId) !== null && _b !== void 0 ? _b : null) === ((_c = port.currentAnchorId) !== null && _c !== void 0 ? _c : null);
5760
6869
  if (unchanged)
5761
6870
  return;
5762
- var patchesForPort = _this.commandQueue.run(createMovePortCommand(port.id, { position: projected, currentAnchorId: null }), _this.model);
6871
+ var patchesForPort = _this.commandQueue.run(createMovePortCommand(port.id, {
6872
+ position: constrained.position,
6873
+ currentAnchorId: constrained.currentAnchorId,
6874
+ }), _this.model);
5763
6875
  reprojectionPatches.push.apply(reprojectionPatches, patchesForPort);
5764
6876
  movedPortIds.add(port.id);
5765
6877
  });
@@ -5816,10 +6928,10 @@ var DiagramEngine = /** @class */ (function () {
5816
6928
  return this.projectPointToHostBorder(projectedTarget, shapeId, nextSize);
5817
6929
  };
5818
6930
  DiagramEngine.prototype.resolveConstrainedPortRelativePosition = function (port, element, requestedPosition) {
5819
- var _a, _b, _c, _d, _e;
6931
+ var _a, _b, _c, _d, _e, _f, _g;
5820
6932
  var position = __assign({}, requestedPosition);
5821
6933
  var effectiveMoveMode = this.resolveEffectivePortMoveMode(port, element);
5822
- if (effectiveMoveMode && effectiveMoveMode !== 'free' && effectiveMoveMode !== 'anchors') {
6934
+ if (effectiveMoveMode === 'inside' || effectiveMoveMode === 'border') {
5823
6935
  var widthLimit = Math.max(0, element.size.width - ((_b = (_a = port.size) === null || _a === void 0 ? void 0 : _a.width) !== null && _b !== void 0 ? _b : 0));
5824
6936
  var heightLimit = Math.max(0, element.size.height - ((_d = (_c = port.size) === null || _c === void 0 ? void 0 : _c.height) !== null && _d !== void 0 ? _d : 0));
5825
6937
  var bounds = {
@@ -5843,9 +6955,12 @@ var DiagramEngine = /** @class */ (function () {
5843
6955
  height: Math.max(0, element.size.height),
5844
6956
  };
5845
6957
  }
5846
- position = effectiveMoveMode === 'inside'
5847
- ? clampToRect(position, bounds)
5848
- : this.constrainPortToHostBorder(position, element);
6958
+ if (effectiveMoveMode === 'inside') {
6959
+ position = clampToRect(position, bounds);
6960
+ }
6961
+ else {
6962
+ position = this.constrainPortToHostBorder(position, element);
6963
+ }
5849
6964
  }
5850
6965
  var style = port.style;
5851
6966
  if ((style === null || style === void 0 ? void 0 : style.moveAxis) === 'horizontal') {
@@ -5859,18 +6974,134 @@ var DiagramEngine = /** @class */ (function () {
5859
6974
  position.x = clamp(position.x, bounds.x, bounds.x + bounds.width);
5860
6975
  position.y = clamp(position.y, bounds.y, bounds.y + bounds.height);
5861
6976
  }
6977
+ if (effectiveMoveMode === 'border') {
6978
+ var resolvedBorder = this.resolveBorderPositionWithLimits(position, element, (_e = element.portMovement) === null || _e === void 0 ? void 0 : _e.positionLimits);
6979
+ position = resolvedBorder !== null && resolvedBorder !== void 0 ? resolvedBorder : this.constrainPortToHostBorder(position, element);
6980
+ }
6981
+ else {
6982
+ position = this.applyPortPositionLimits(position, (_f = element.portMovement) === null || _f === void 0 ? void 0 : _f.positionLimits);
6983
+ }
5862
6984
  if (effectiveMoveMode === 'anchors') {
5863
- var anchor = this.resolveNearestPortAnchor(element, position);
6985
+ var anchor = this.resolveNearestPortAnchor(element, position, port.currentAnchorId);
5864
6986
  if (anchor) {
5865
6987
  return {
5866
6988
  position: __assign({}, anchor.position),
5867
6989
  currentAnchorId: anchor.id,
5868
6990
  };
5869
6991
  }
5870
- return { position: position, currentAnchorId: (_e = port.currentAnchorId) !== null && _e !== void 0 ? _e : null };
6992
+ return { position: __assign({}, port.position), currentAnchorId: (_g = port.currentAnchorId) !== null && _g !== void 0 ? _g : null };
5871
6993
  }
5872
6994
  return { position: position, currentAnchorId: null };
5873
6995
  };
6996
+ DiagramEngine.prototype.applyPortPositionLimits = function (position, limits) {
6997
+ if (!limits)
6998
+ return __assign({}, position);
6999
+ var x = position.x;
7000
+ var y = position.y;
7001
+ if (limits.x) {
7002
+ if (typeof limits.x.min === 'number') {
7003
+ x = Math.max(x, limits.x.min);
7004
+ }
7005
+ if (typeof limits.x.max === 'number') {
7006
+ x = Math.min(x, limits.x.max);
7007
+ }
7008
+ }
7009
+ if (limits.y) {
7010
+ if (typeof limits.y.min === 'number') {
7011
+ y = Math.max(y, limits.y.min);
7012
+ }
7013
+ if (typeof limits.y.max === 'number') {
7014
+ y = Math.min(y, limits.y.max);
7015
+ }
7016
+ }
7017
+ return { x: x, y: y };
7018
+ };
7019
+ DiagramEngine.prototype.resolveBorderPositionWithLimits = function (position, element, limits) {
7020
+ var _this = this;
7021
+ var _a, _b, _c, _d;
7022
+ var borderBase = this.constrainPortToHostBorder(position, element);
7023
+ if (!limits)
7024
+ return borderBase;
7025
+ if (this.isPointWithinPositionLimits(borderBase, limits))
7026
+ return borderBase;
7027
+ var target = this.applyPortPositionLimits(borderBase, limits);
7028
+ var xValues = new Set([borderBase.x, target.x, 0, Math.max(0, element.size.width)]);
7029
+ var yValues = new Set([borderBase.y, target.y, 0, Math.max(0, element.size.height)]);
7030
+ if (typeof ((_a = limits.x) === null || _a === void 0 ? void 0 : _a.min) === 'number')
7031
+ xValues.add(limits.x.min);
7032
+ if (typeof ((_b = limits.x) === null || _b === void 0 ? void 0 : _b.max) === 'number')
7033
+ xValues.add(limits.x.max);
7034
+ if (typeof ((_c = limits.y) === null || _c === void 0 ? void 0 : _c.min) === 'number')
7035
+ yValues.add(limits.y.min);
7036
+ if (typeof ((_d = limits.y) === null || _d === void 0 ? void 0 : _d.max) === 'number')
7037
+ yValues.add(limits.y.max);
7038
+ var seeds = [];
7039
+ xValues.forEach(function (x) { return yValues.forEach(function (y) { return seeds.push({ x: x, y: y }); }); });
7040
+ seeds.push({ x: borderBase.x, y: target.y }, { x: target.x, y: borderBase.y });
7041
+ var candidates = [];
7042
+ var seen = new Set();
7043
+ seeds.forEach(function (seed) {
7044
+ var candidate = _this.constrainPortToHostBorder(seed, element);
7045
+ var key = "".concat(candidate.x.toFixed(6), ":").concat(candidate.y.toFixed(6));
7046
+ if (seen.has(key))
7047
+ return;
7048
+ seen.add(key);
7049
+ candidates.push(candidate);
7050
+ });
7051
+ var valid = candidates.filter(function (candidate) { return _this.isPointWithinPositionLimits(candidate, limits); });
7052
+ if (valid.length === 0)
7053
+ return null;
7054
+ return valid.reduce(function (best, candidate) {
7055
+ var bestDistance = Math.pow((best.x - target.x), 2) + Math.pow((best.y - target.y), 2);
7056
+ var candidateDistance = Math.pow((candidate.x - target.x), 2) + Math.pow((candidate.y - target.y), 2);
7057
+ if (candidateDistance < bestDistance)
7058
+ return candidate;
7059
+ if (candidateDistance > bestDistance)
7060
+ return best;
7061
+ if (candidate.x < best.x)
7062
+ return candidate;
7063
+ if (candidate.x > best.x)
7064
+ return best;
7065
+ return candidate.y < best.y ? candidate : best;
7066
+ }, valid[0]);
7067
+ };
7068
+ DiagramEngine.prototype.isPointWithinPositionLimits = function (position, limits) {
7069
+ if (!limits)
7070
+ return true;
7071
+ if (limits.x) {
7072
+ if (typeof limits.x.min === 'number' && position.x < limits.x.min)
7073
+ return false;
7074
+ if (typeof limits.x.max === 'number' && position.x > limits.x.max)
7075
+ return false;
7076
+ }
7077
+ if (limits.y) {
7078
+ if (typeof limits.y.min === 'number' && position.y < limits.y.min)
7079
+ return false;
7080
+ if (typeof limits.y.max === 'number' && position.y > limits.y.max)
7081
+ return false;
7082
+ }
7083
+ return true;
7084
+ };
7085
+ DiagramEngine.prototype.filterAnchorsByPositionLimits = function (anchors, limits) {
7086
+ if (!limits)
7087
+ return anchors;
7088
+ return anchors.filter(function (anchor) {
7089
+ var position = anchor.position;
7090
+ if (limits.x) {
7091
+ if (typeof limits.x.min === 'number' && position.x < limits.x.min)
7092
+ return false;
7093
+ if (typeof limits.x.max === 'number' && position.x > limits.x.max)
7094
+ return false;
7095
+ }
7096
+ if (limits.y) {
7097
+ if (typeof limits.y.min === 'number' && position.y < limits.y.min)
7098
+ return false;
7099
+ if (typeof limits.y.max === 'number' && position.y > limits.y.max)
7100
+ return false;
7101
+ }
7102
+ return true;
7103
+ });
7104
+ };
5874
7105
  DiagramEngine.prototype.resolveEffectivePortMoveMode = function (port, element) {
5875
7106
  var _a, _b;
5876
7107
  return (_b = (_a = element.portMovement) === null || _a === void 0 ? void 0 : _a.moveMode) !== null && _b !== void 0 ? _b : port.moveMode;
@@ -5905,7 +7136,8 @@ var DiagramEngine = /** @class */ (function () {
5905
7136
  return [];
5906
7137
  };
5907
7138
  DiagramEngine.prototype.resolveNearestPortAnchor = function (element, target, preferredAnchorId) {
5908
- var anchors = this.resolvePortAnchorsForElement(element);
7139
+ var _a;
7140
+ var anchors = this.filterAnchorsByPositionLimits(this.resolvePortAnchorsForElement(element), (_a = element.portMovement) === null || _a === void 0 ? void 0 : _a.positionLimits);
5909
7141
  if (anchors.length === 0)
5910
7142
  return null;
5911
7143
  if (preferredAnchorId) {
@@ -5970,6 +7202,46 @@ var DiagramEngine = /** @class */ (function () {
5970
7202
  DiagramEngine.prototype.resolveTextPresentation = function (text) {
5971
7203
  return this.textLayoutService.resolveTextPresentation(text);
5972
7204
  };
7205
+ DiagramEngine.prototype.resolveAllTextPresentationPatches = function (emitTextUpdated) {
7206
+ var _this = this;
7207
+ var textPatches = [];
7208
+ this.model.texts.forEach(function (text) {
7209
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
7210
+ var current = text.toData();
7211
+ var resolved = _this.resolveTextPresentation(current);
7212
+ var currentDisplay = (_a = current.displayContent) !== null && _a !== void 0 ? _a : current.content;
7213
+ var sizeChanged = !current.size ||
7214
+ current.size.width !== resolved.size.width ||
7215
+ current.size.height !== resolved.size.height;
7216
+ var displayChanged = currentDisplay !== resolved.displayContent;
7217
+ var offsetChanged = ((_c = (_b = current.displayOffset) === null || _b === void 0 ? void 0 : _b.x) !== null && _c !== void 0 ? _c : 0) !== ((_e = (_d = resolved.displayOffset) === null || _d === void 0 ? void 0 : _d.x) !== null && _e !== void 0 ? _e : 0) ||
7218
+ ((_g = (_f = current.displayOffset) === null || _f === void 0 ? void 0 : _f.y) !== null && _g !== void 0 ? _g : 0) !== ((_j = (_h = resolved.displayOffset) === null || _h === void 0 ? void 0 : _h.y) !== null && _j !== void 0 ? _j : 0);
7219
+ var clipChanged = ((_l = (_k = current.displayClipSize) === null || _k === void 0 ? void 0 : _k.width) !== null && _l !== void 0 ? _l : 0) !== ((_o = (_m = resolved.displayClipSize) === null || _m === void 0 ? void 0 : _m.width) !== null && _o !== void 0 ? _o : 0) ||
7220
+ ((_q = (_p = current.displayClipSize) === null || _p === void 0 ? void 0 : _p.height) !== null && _q !== void 0 ? _q : 0) !== ((_s = (_r = resolved.displayClipSize) === null || _r === void 0 ? void 0 : _r.height) !== null && _s !== void 0 ? _s : 0);
7221
+ if (!sizeChanged && !displayChanged && !offsetChanged && !clipChanged)
7222
+ return;
7223
+ text.setSize(resolved.size);
7224
+ text.setDisplayContent(resolved.displayContent);
7225
+ text.setDisplayOffset(resolved.displayOffset);
7226
+ text.setDisplayClipSize(resolved.displayClipSize);
7227
+ textPatches.push(patchUpdate('text', text.id, {
7228
+ size: resolved.size,
7229
+ displayContent: resolved.displayContent,
7230
+ displayOffset: resolved.displayOffset,
7231
+ displayClipSize: resolved.displayClipSize,
7232
+ }));
7233
+ if (emitTextUpdated) {
7234
+ _this.events.emit('textUpdated', {
7235
+ textId: text.id,
7236
+ ownerId: text.ownerId,
7237
+ content: text.content,
7238
+ displayContent: resolved.displayContent,
7239
+ reason: 'layout',
7240
+ });
7241
+ }
7242
+ });
7243
+ return textPatches;
7244
+ };
5973
7245
  DiagramEngine.prototype.emitSelection = function () {
5974
7246
  var _this = this;
5975
7247
  var selectedIds = this.selection.get();
@@ -6001,6 +7273,12 @@ var DiagramEngine = /** @class */ (function () {
6001
7273
  DiagramEngine.prototype.applyAllLayouts = function () {
6002
7274
  return this.autoLayoutService.applyAllLayouts();
6003
7275
  };
7276
+ DiagramEngine.prototype.hasFlexibleLabelReservedSpace = function (elementId) {
7277
+ var _a, _b, _c;
7278
+ var element = this.model.getElement(elementId);
7279
+ var mode = (_c = (_b = (_a = element === null || element === void 0 ? void 0 : element.layout) === null || _a === void 0 ? void 0 : _a.labelReservedSpace) === null || _b === void 0 ? void 0 : _b.mode) !== null && _c !== void 0 ? _c : 'none';
7280
+ return mode === 'flexible';
7281
+ };
6004
7282
  DiagramEngine.prototype.updateLinksForPorts = function (portIds) {
6005
7283
  return this.linkRoutingService.updateLinksForPorts(portIds);
6006
7284
  };
@@ -6053,6 +7331,40 @@ var DiagramEngine = /** @class */ (function () {
6053
7331
  DiagramEngine.prototype.routeLinksWithEmptyPoints = function () {
6054
7332
  return this.linkRoutingService.routeLinksWithEmptyPoints();
6055
7333
  };
7334
+ DiagramEngine.prototype.normalizePortsForHostPolicies = function (portIds) {
7335
+ var _this = this;
7336
+ var ids = portIds !== null && portIds !== void 0 ? portIds : Array.from(this.model.ports.keys());
7337
+ var normalizedPatches = [];
7338
+ var movedPortIds = new Set();
7339
+ ids.forEach(function (portId) {
7340
+ var _a, _b;
7341
+ var port = _this.model.getPort(portId);
7342
+ if (!port)
7343
+ return;
7344
+ var element = _this.model.getElement(port.elementId);
7345
+ if (!element)
7346
+ return;
7347
+ var constrained = _this.resolveConstrainedPortRelativePosition(port, element, port.position);
7348
+ var previousAnchorId = (_a = port.currentAnchorId) !== null && _a !== void 0 ? _a : null;
7349
+ var shouldPersistAnchorId = previousAnchorId !== null || constrained.currentAnchorId === null;
7350
+ var nextAnchorId = shouldPersistAnchorId ? ((_b = constrained.currentAnchorId) !== null && _b !== void 0 ? _b : null) : previousAnchorId;
7351
+ var unchanged = Math.abs(constrained.position.x - port.position.x) <= POSITION_EPSILON &&
7352
+ Math.abs(constrained.position.y - port.position.y) <= POSITION_EPSILON &&
7353
+ previousAnchorId === nextAnchorId;
7354
+ if (unchanged)
7355
+ return;
7356
+ var update = {
7357
+ position: constrained.position,
7358
+ };
7359
+ if (shouldPersistAnchorId) {
7360
+ update.currentAnchorId = constrained.currentAnchorId;
7361
+ }
7362
+ var patches = _this.commandQueue.run(createMovePortCommand(port.id, update), _this.model);
7363
+ normalizedPatches.push.apply(normalizedPatches, patches);
7364
+ movedPortIds.add(port.id);
7365
+ });
7366
+ return { patches: normalizedPatches, movedPortIds: Array.from(movedPortIds) };
7367
+ };
6056
7368
  DiagramEngine.prototype.computeRemovalDiff = function (before) {
6057
7369
  var after = this.model.toState();
6058
7370
  var removedPatches = [];
@@ -6263,6 +7575,21 @@ var KonvaNodeFactory = /** @class */ (function () {
6263
7575
  }
6264
7576
  return node;
6265
7577
  };
7578
+ KonvaNodeFactory.prototype.createTextBackgroundNode = function (config) {
7579
+ return new this.konva.Rect({
7580
+ id: config.id,
7581
+ x: config.x,
7582
+ y: config.y,
7583
+ width: config.width,
7584
+ height: config.height,
7585
+ fill: config.fill,
7586
+ stroke: config.stroke,
7587
+ strokeWidth: config.strokeWidth,
7588
+ cornerRadius: config.cornerRadius,
7589
+ name: 'text-background',
7590
+ listening: false,
7591
+ });
7592
+ };
6266
7593
  KonvaNodeFactory.prototype.createHandleNode = function (config) {
6267
7594
  return new this.konva.Rect({
6268
7595
  id: config.id,
@@ -6342,7 +7669,8 @@ var KonvaNodeFactory = /** @class */ (function () {
6342
7669
  KonvaNodeFactory.prototype.createDrawNode = function (model, shape, config) {
6343
7670
  var _a, _b;
6344
7671
  var style = model.style;
6345
- var attrs = __assign(__assign({ id: model.id, x: model.position.x, y: model.position.y, name: config.name }, (style !== null && style !== void 0 ? style : {})), { __shapeDraw: true, __model: model, sceneFunc: function (ctx, node) {
7672
+ var _c = this.resolveShapeRotation(shape, style), resolvedStyle = _c.style, rotation = _c.rotation;
7673
+ var attrs = __assign(__assign({ id: model.id, x: model.position.x, y: model.position.y, name: config.name }, (resolvedStyle !== null && resolvedStyle !== void 0 ? resolvedStyle : {})), { __shapeDraw: true, __model: model, rotation: rotation, sceneFunc: function (ctx, node) {
6346
7674
  var _a, _b, _c;
6347
7675
  var resolvedModel = (_b = (node.getAttr ? node.getAttr('__model') : (_a = node.attrs) === null || _a === void 0 ? void 0 : _a.__model)) !== null && _b !== void 0 ? _b : model;
6348
7676
  (_c = shape.draw) === null || _c === void 0 ? void 0 : _c.call(shape, { ctx: ctx, model: resolvedModel });
@@ -6358,6 +7686,7 @@ var KonvaNodeFactory = /** @class */ (function () {
6358
7686
  KonvaNodeFactory.prototype.createSvgPathNode = function (model, shape, config) {
6359
7687
  var _a, _b, _c, _d, _e, _f, _g;
6360
7688
  var style = model.style;
7689
+ var _h = this.resolveShapeRotation(shape, style), resolvedStyle = _h.style, rotation = _h.rotation;
6361
7690
  var sizeUpdater = function (_a) {
6362
7691
  var _b, _c;
6363
7692
  var nextSize = _a.size, anchorCenter = _a.anchorCenter, updateOffsetX = _a.updateOffsetX, updateOffsetY = _a.updateOffsetY, getNodeAttr = _a.getNodeAttr;
@@ -6379,7 +7708,7 @@ var KonvaNodeFactory = /** @class */ (function () {
6379
7708
  return attrs;
6380
7709
  };
6381
7710
  var size = (_a = config.size) !== null && _a !== void 0 ? _a : { width: 0, height: 0 };
6382
- var node = new this.konva.Path(__assign(__assign({ id: model.id, x: model.position.x, y: model.position.y, data: shape.svgPath, name: config.name }, (style !== null && style !== void 0 ? style : {})), { shapeKind: 'svg-path', __autoOffsetX: true, __autoOffsetY: true }));
7711
+ var node = new this.konva.Path(__assign(__assign({ id: model.id, x: model.position.x, y: model.position.y, rotation: rotation, data: shape.svgPath, name: config.name }, (resolvedStyle !== null && resolvedStyle !== void 0 ? resolvedStyle : {})), { shapeKind: 'svg-path', __autoOffsetX: true, __autoOffsetY: true }));
6383
7712
  var rect = node.getClientRect ? node.getClientRect({ skipTransform: true }) : null;
6384
7713
  var baseWidth = (_d = (_c = (_b = shape.svgSize) === null || _b === void 0 ? void 0 : _b.width) !== null && _c !== void 0 ? _c : rect === null || rect === void 0 ? void 0 : rect.width) !== null && _d !== void 0 ? _d : size.width;
6385
7714
  var baseHeight = (_g = (_f = (_e = shape.svgSize) === null || _e === void 0 ? void 0 : _e.height) !== null && _f !== void 0 ? _f : rect === null || rect === void 0 ? void 0 : rect.height) !== null && _g !== void 0 ? _g : size.height;
@@ -6396,6 +7725,20 @@ var KonvaNodeFactory = /** @class */ (function () {
6396
7725
  }
6397
7726
  return node;
6398
7727
  };
7728
+ KonvaNodeFactory.prototype.resolveShapeRotation = function (shape, style) {
7729
+ var _a, _b;
7730
+ if (!style) {
7731
+ return {
7732
+ style: undefined,
7733
+ rotation: (_a = shape.baseRotation) !== null && _a !== void 0 ? _a : 0,
7734
+ };
7735
+ }
7736
+ var rotation = style.rotation, rest = __rest(style, ["rotation"]);
7737
+ return {
7738
+ style: rest,
7739
+ rotation: ((_b = shape.baseRotation) !== null && _b !== void 0 ? _b : 0) + (typeof rotation === 'number' ? rotation : 0),
7740
+ };
7741
+ };
6399
7742
  KonvaNodeFactory.prototype.applyShapeBehaviorAttrs = function (node, shape) {
6400
7743
  if (!node.setAttrs || !shape.sizeUpdater)
6401
7744
  return;
@@ -6427,6 +7770,7 @@ var KonvaRenderer = /** @class */ (function () {
6427
7770
  this.portNodes = new Map();
6428
7771
  this.linkNodes = new Map();
6429
7772
  this.textNodes = new Map();
7773
+ this.textBackgroundNodes = new Map();
6430
7774
  this.selectedIds = new Set();
6431
7775
  this.tempLinkNode = null;
6432
7776
  this.tempPortNode = null;
@@ -6596,7 +7940,7 @@ var KonvaRenderer = /** @class */ (function () {
6596
7940
  this.drawOverlays();
6597
7941
  };
6598
7942
  KonvaRenderer.prototype.renderPortPlaceholder = function (port, hostElement) {
6599
- var _a, _b, _c, _d;
7943
+ var _a, _b, _c, _d, _e, _f;
6600
7944
  var shapeId = port.shapeId;
6601
7945
  if (!this.tempPortNode || this.tempPortShapeId !== shapeId) {
6602
7946
  (_b = (_a = this.tempPortNode) === null || _a === void 0 ? void 0 : _a.destroy) === null || _b === void 0 ? void 0 : _b.call(_a);
@@ -6615,6 +7959,9 @@ var KonvaRenderer = /** @class */ (function () {
6615
7959
  }
6616
7960
  }
6617
7961
  if (this.tempPortNode) {
7962
+ this.updateSize(this.tempPortNode, (_e = port.size) !== null && _e !== void 0 ? _e : { width: 8, height: 8 }, {
7963
+ anchorCenter: (_f = port.anchorCenter) !== null && _f !== void 0 ? _f : true,
7964
+ });
6618
7965
  this.applyPortOrientation(this.tempPortNode, port, port.position, undefined, hostElement);
6619
7966
  }
6620
7967
  this.drawOverlays();
@@ -6723,6 +8070,8 @@ var KonvaRenderer = /** @class */ (function () {
6723
8070
  this.elementNodes.clear();
6724
8071
  this.portNodes.clear();
6725
8072
  this.linkNodes.clear();
8073
+ this.textBackgroundNodes.forEach(function (node) { var _a; return (_a = node.destroy) === null || _a === void 0 ? void 0 : _a.call(node); });
8074
+ this.textBackgroundNodes.clear();
6726
8075
  this.textNodes.clear();
6727
8076
  this.resizeHandleNodes.clear();
6728
8077
  this.linkHandleNodes.clear();
@@ -6874,7 +8223,7 @@ var KonvaRenderer = /** @class */ (function () {
6874
8223
  var _this = this;
6875
8224
  var ports = Array.from(model.ports.values());
6876
8225
  ports.forEach(function (port) {
6877
- var _a, _b, _c;
8226
+ var _a, _b, _c, _d, _e;
6878
8227
  var data = port.toData();
6879
8228
  var node = _this.portNodes.get(port.id);
6880
8229
  if (!node) {
@@ -6886,6 +8235,7 @@ var KonvaRenderer = /** @class */ (function () {
6886
8235
  node.setAttrs({ __model: data });
6887
8236
  }
6888
8237
  var position = (_c = model.getPortWorldPosition(port.id)) !== null && _c !== void 0 ? _c : port.position;
8238
+ _this.updateSize(node, (_d = data.size) !== null && _d !== void 0 ? _d : { width: 8, height: 8 }, { anchorCenter: (_e = data.anchorCenter) !== null && _e !== void 0 ? _e : true });
6889
8239
  _this.updatePosition(node, position);
6890
8240
  _this.applyPortOrientation(node, data, position, model);
6891
8241
  });
@@ -6928,7 +8278,7 @@ var KonvaRenderer = /** @class */ (function () {
6928
8278
  var _this = this;
6929
8279
  var texts = Array.from(model.texts.values());
6930
8280
  texts.forEach(function (text) {
6931
- var _a, _b, _c, _d, _e, _f, _g, _h;
8281
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
6932
8282
  var node = _this.textNodes.get(text.id);
6933
8283
  if (!node) {
6934
8284
  node = _this.nodeFactory.createTextNode(text.toData());
@@ -6937,32 +8287,124 @@ var KonvaRenderer = /** @class */ (function () {
6937
8287
  }
6938
8288
  var position = (_c = model.getTextWorldPosition(text.id)) !== null && _c !== void 0 ? _c : text.position;
6939
8289
  var displayOffset = (_d = text.displayOffset) !== null && _d !== void 0 ? _d : { x: 0, y: 0 };
6940
- _this.updatePosition(node, {
8290
+ var textPosition = {
6941
8291
  x: position.x + displayOffset.x,
6942
8292
  y: position.y + displayOffset.y,
8293
+ };
8294
+ _this.updatePosition(node, {
8295
+ x: textPosition.x,
8296
+ y: textPosition.y,
6943
8297
  });
6944
8298
  if (node.setAttrs) {
6945
- var style = text.style;
6946
- var defaults = resolveTextStyleDefaults(style);
6947
- var baseFill = (_g = (_f = (_e = style === null || style === void 0 ? void 0 : style.fill) !== null && _e !== void 0 ? _e : _this.getNodeAttr(node, '__baseFill')) !== null && _f !== void 0 ? _f : _this.getNodeAttr(node, 'fill')) !== null && _g !== void 0 ? _g : 'black';
8299
+ var style_1 = text.style;
8300
+ var defaults = resolveTextStyleDefaults(style_1);
8301
+ var baseFill = (_g = (_f = (_e = style_1 === null || style_1 === void 0 ? void 0 : style_1.fill) !== null && _e !== void 0 ? _e : _this.getNodeAttr(node, '__baseFill')) !== null && _f !== void 0 ? _f : _this.getNodeAttr(node, 'fill')) !== null && _g !== void 0 ? _g : 'black';
6948
8302
  var fill = _this.selectedIds.has(text.id) ? '#ff7a00' : baseFill;
6949
- var clip = text.displayClipSize;
6950
- node.setAttrs(__assign(__assign({ text: (_h = text.displayContent) !== null && _h !== void 0 ? _h : text.content, fontSize: defaults.fontSize, fontFamily: defaults.fontFamily, lineHeight: defaults.lineHeight }, (style !== null && style !== void 0 ? style : {})), { width: clip === null || clip === void 0 ? void 0 : clip.width, height: clip === null || clip === void 0 ? void 0 : clip.height, wrap: clip ? 'none' : undefined, ellipsis: false, fill: fill, __baseFill: baseFill,
8303
+ var clip_1 = text.displayClipSize;
8304
+ node.setAttrs(__assign(__assign({ text: (_h = text.displayContent) !== null && _h !== void 0 ? _h : text.content, fontSize: defaults.fontSize, fontFamily: defaults.fontFamily, lineHeight: defaults.lineHeight }, (style_1 !== null && style_1 !== void 0 ? style_1 : {})), { width: clip_1 === null || clip_1 === void 0 ? void 0 : clip_1.width, height: clip_1 === null || clip_1 === void 0 ? void 0 : clip_1.height, wrap: clip_1 ? 'none' : undefined, ellipsis: false, fill: fill, __baseFill: baseFill,
6951
8305
  // Keep metadata for tests/debugging; Konva clip attrs do not apply on Text nodes.
6952
- clipX: clip ? 0 : undefined, clipY: clip ? 0 : undefined, clipWidth: clip === null || clip === void 0 ? void 0 : clip.width, clipHeight: clip === null || clip === void 0 ? void 0 : clip.height }));
8306
+ clipX: clip_1 ? 0 : undefined, clipY: clip_1 ? 0 : undefined, clipWidth: clip_1 === null || clip_1 === void 0 ? void 0 : clip_1.width, clipHeight: clip_1 === null || clip_1 === void 0 ? void 0 : clip_1.height }));
8307
+ }
8308
+ var style = text.style;
8309
+ var backgroundFill = style === null || style === void 0 ? void 0 : style.backgroundFill;
8310
+ var clip = text.displayClipSize;
8311
+ var backgroundWidth = (_j = clip === null || clip === void 0 ? void 0 : clip.width) !== null && _j !== void 0 ? _j : (_k = text.size) === null || _k === void 0 ? void 0 : _k.width;
8312
+ var backgroundHeight = (_l = clip === null || clip === void 0 ? void 0 : clip.height) !== null && _l !== void 0 ? _l : (_m = text.size) === null || _m === void 0 ? void 0 : _m.height;
8313
+ var backgroundMeta = _this.resolveTextBackgroundMeta(model, text.toData());
8314
+ var hasBackground = typeof backgroundFill === 'string' &&
8315
+ backgroundFill.length > 0 &&
8316
+ typeof backgroundWidth === 'number' &&
8317
+ typeof backgroundHeight === 'number' &&
8318
+ backgroundWidth > 0 &&
8319
+ backgroundHeight > 0;
8320
+ if (hasBackground) {
8321
+ var baseX = textPosition.x + backgroundMeta.inset;
8322
+ var baseY = textPosition.y + backgroundMeta.inset;
8323
+ var width = Math.max(0, backgroundWidth - backgroundMeta.inset * 2);
8324
+ var height = Math.max(0, backgroundHeight - backgroundMeta.inset);
8325
+ var backgroundStroke = typeof (style === null || style === void 0 ? void 0 : style.backgroundStroke) === 'string' && style.backgroundStroke.length > 0
8326
+ ? style.backgroundStroke
8327
+ : undefined;
8328
+ var backgroundStrokeWidth = typeof (style === null || style === void 0 ? void 0 : style.backgroundStrokeWidth) === 'number'
8329
+ ? Math.max(0, style.backgroundStrokeWidth)
8330
+ : undefined;
8331
+ var backgroundNode = _this.textBackgroundNodes.get(text.id);
8332
+ if (!backgroundNode) {
8333
+ backgroundNode = _this.nodeFactory.createTextBackgroundNode({
8334
+ id: "text-background:".concat(text.id),
8335
+ x: baseX,
8336
+ y: baseY,
8337
+ width: width,
8338
+ height: height,
8339
+ fill: backgroundFill,
8340
+ stroke: backgroundStroke,
8341
+ strokeWidth: backgroundStrokeWidth,
8342
+ cornerRadius: backgroundMeta.cornerRadius,
8343
+ });
8344
+ _this.textBackgroundNodes.set(text.id, backgroundNode);
8345
+ (_p = (_o = _this.layers.getLayer('texts')) === null || _o === void 0 ? void 0 : _o.add) === null || _p === void 0 ? void 0 : _p.call(_o, backgroundNode);
8346
+ }
8347
+ else {
8348
+ _this.updatePosition(backgroundNode, { x: baseX, y: baseY });
8349
+ (_q = backgroundNode.setAttrs) === null || _q === void 0 ? void 0 : _q.call(backgroundNode, {
8350
+ width: width,
8351
+ height: height,
8352
+ fill: backgroundFill,
8353
+ stroke: backgroundStroke,
8354
+ strokeWidth: backgroundStrokeWidth,
8355
+ cornerRadius: backgroundMeta.cornerRadius,
8356
+ listening: false,
8357
+ name: 'text-background',
8358
+ });
8359
+ }
8360
+ (_r = node.moveToTop) === null || _r === void 0 ? void 0 : _r.call(node);
8361
+ }
8362
+ else {
8363
+ var backgroundNode = _this.textBackgroundNodes.get(text.id);
8364
+ (_s = backgroundNode === null || backgroundNode === void 0 ? void 0 : backgroundNode.destroy) === null || _s === void 0 ? void 0 : _s.call(backgroundNode);
8365
+ _this.textBackgroundNodes.delete(text.id);
6953
8366
  }
6954
8367
  });
6955
8368
  Array.from(this.textNodes.keys()).forEach(function (id) {
6956
- var _a;
8369
+ var _a, _b;
6957
8370
  if (!model.texts.has(id)) {
8371
+ var backgroundNode = _this.textBackgroundNodes.get(id);
8372
+ (_a = backgroundNode === null || backgroundNode === void 0 ? void 0 : backgroundNode.destroy) === null || _a === void 0 ? void 0 : _a.call(backgroundNode);
8373
+ _this.textBackgroundNodes.delete(id);
6958
8374
  var node = _this.textNodes.get(id);
6959
- (_a = node === null || node === void 0 ? void 0 : node.destroy) === null || _a === void 0 ? void 0 : _a.call(node);
8375
+ (_b = node === null || node === void 0 ? void 0 : node.destroy) === null || _b === void 0 ? void 0 : _b.call(node);
6960
8376
  _this.textNodes.delete(id);
6961
8377
  _this.layers.markDirty('texts');
6962
8378
  }
6963
8379
  });
6964
8380
  this.layers.markDirty('texts');
6965
8381
  };
8382
+ KonvaRenderer.prototype.resolveTextBackgroundMeta = function (model, text) {
8383
+ var _a;
8384
+ var ownerId = (_a = text.ownerId) !== null && _a !== void 0 ? _a : null;
8385
+ if (!ownerId)
8386
+ return { inset: 0 };
8387
+ var owner = model.getElement(ownerId);
8388
+ if (!owner)
8389
+ return { inset: 0 };
8390
+ var ownerStyle = owner.style;
8391
+ var strokeWidth = typeof (ownerStyle === null || ownerStyle === void 0 ? void 0 : ownerStyle.strokeWidth) === 'number' ? Math.max(0, ownerStyle.strokeWidth) : 0;
8392
+ var inset = strokeWidth > 0 ? strokeWidth / 2 : 0;
8393
+ var rawCornerRadius = ownerStyle === null || ownerStyle === void 0 ? void 0 : ownerStyle.cornerRadius;
8394
+ var isTopLabel = text.position.y <= 1;
8395
+ if (!isTopLabel)
8396
+ return { inset: inset };
8397
+ if (typeof rawCornerRadius === 'number' && rawCornerRadius > 0) {
8398
+ var topRadius = Math.max(0, rawCornerRadius - inset);
8399
+ return { inset: inset, cornerRadius: [topRadius, topRadius, 0, 0] };
8400
+ }
8401
+ if (Array.isArray(rawCornerRadius) && rawCornerRadius.length === 4) {
8402
+ var tl = typeof rawCornerRadius[0] === 'number' ? Math.max(0, rawCornerRadius[0] - inset) : 0;
8403
+ var tr = typeof rawCornerRadius[1] === 'number' ? Math.max(0, rawCornerRadius[1] - inset) : 0;
8404
+ return { inset: inset, cornerRadius: [tl, tr, 0, 0] };
8405
+ }
8406
+ return { inset: inset };
8407
+ };
6966
8408
  KonvaRenderer.prototype.updatePosition = function (node, position) {
6967
8409
  if (node.position) {
6968
8410
  node.position({ x: position.x, y: position.y });
@@ -6980,27 +8422,22 @@ var KonvaRenderer = /** @class */ (function () {
6980
8422
  if (this.getNodeAttr(node, '__baseRotation') === undefined) {
6981
8423
  node.setAttrs({ __baseRotation: baseRotation });
6982
8424
  }
6983
- var orientedRotation = this.resolvePortBorderRotation(port, worldPosition, model, hostElementOverride);
6984
- node.setAttrs({ rotation: baseRotation + (orientedRotation !== null && orientedRotation !== void 0 ? orientedRotation : 0) });
6985
- };
6986
- KonvaRenderer.prototype.resolvePortBorderRotation = function (port, worldPosition, model, hostElementOverride) {
6987
- if (port.moveMode !== 'border' || port.orientToHostBorder === false) {
6988
- return null;
6989
- }
6990
8425
  var host = hostElementOverride !== null && hostElementOverride !== void 0 ? hostElementOverride : this.resolveHostElement(port.elementId, model);
6991
- if (!host)
6992
- return null;
6993
- var hostRect = {
6994
- x: host.position.x,
6995
- y: host.position.y,
6996
- width: host.size.width,
6997
- height: host.size.height,
6998
- };
6999
- var shape = this.shapeRegistry.get(host.shapeId);
7000
- var side = (shape === null || shape === void 0 ? void 0 : shape.resolveBorderSide)
7001
- ? shape.resolveBorderSide(worldPosition, hostRect)
7002
- : resolveBoundarySide(worldPosition, hostRect, 'rect');
7003
- return borderSideToInwardRotation(side);
8426
+ var orientation = resolvePortOrientationContext({
8427
+ port: port,
8428
+ worldPlacement: worldPosition,
8429
+ host: host,
8430
+ shapeRegistry: this.shapeRegistry,
8431
+ attachMode: 'external',
8432
+ });
8433
+ var transform = resolvePortWorldTransform({
8434
+ port: port,
8435
+ worldPlacement: worldPosition,
8436
+ attachMode: 'external',
8437
+ orientation: orientation,
8438
+ });
8439
+ this.updatePosition(node, transform.nodePosition);
8440
+ node.setAttrs({ rotation: baseRotation + orientation.rotation });
7004
8441
  };
7005
8442
  KonvaRenderer.prototype.resolveHostElement = function (elementId, model) {
7006
8443
  var _a;
@@ -7091,6 +8528,7 @@ var KonvaInteraction = /** @class */ (function () {
7091
8528
  if (config === void 0) { config = {}; }
7092
8529
  var _a, _b;
7093
8530
  this.linkDragContext = null;
8531
+ this.programmaticLinkSession = null;
7094
8532
  this.bound = false;
7095
8533
  this.handlers = [];
7096
8534
  this.windowHandlers = [];
@@ -7105,6 +8543,8 @@ var KonvaInteraction = /** @class */ (function () {
7105
8543
  this.textEditor = null;
7106
8544
  this.dragThreshold = 4;
7107
8545
  this.panSpeed = 0.5;
8546
+ this.occupiedVertexTolerance = 2;
8547
+ this.emittingElementLinkEnded = false;
7108
8548
  this.engine = engine;
7109
8549
  this.stage = config.stage;
7110
8550
  this.hitTester = (_a = config.hitTester) !== null && _a !== void 0 ? _a : new KonvaHitTester();
@@ -7125,6 +8565,121 @@ var KonvaInteraction = /** @class */ (function () {
7125
8565
  this.updateShapeHoverControl(this.lastPointerPosition);
7126
8566
  }
7127
8567
  };
8568
+ KonvaInteraction.prototype.startLinkFromPort = function (sourcePortId, pointer) {
8569
+ var _a, _b, _c;
8570
+ var sourceElementId = this.engine.getPortElementId(sourcePortId);
8571
+ if (!sourceElementId)
8572
+ return;
8573
+ var sourcePoint = (_a = this.engine.getPortLinkWorldPosition(sourcePortId)) !== null && _a !== void 0 ? _a : this.engine.getPortWorldPosition(sourcePortId);
8574
+ if (!sourcePoint)
8575
+ return;
8576
+ if (((_b = this.dragState) === null || _b === void 0 ? void 0 : _b.mode) === 'link-drag' || this.programmaticLinkSession) {
8577
+ this.cancelLink();
8578
+ }
8579
+ if (this.dragState &&
8580
+ this.dragState.mode !== 'shape-control-drag' &&
8581
+ this.dragState.mode !== 'link-drag') {
8582
+ return;
8583
+ }
8584
+ var start = __assign({}, sourcePoint);
8585
+ if (((_c = this.dragState) === null || _c === void 0 ? void 0 : _c.mode) === 'shape-control-drag') {
8586
+ this.programmaticLinkSession = {
8587
+ sourcePortId: sourcePortId,
8588
+ sourceElementId: sourceElementId,
8589
+ start: start,
8590
+ current: start,
8591
+ };
8592
+ }
8593
+ else {
8594
+ this.dragState = {
8595
+ mode: 'link-drag',
8596
+ sourcePortId: sourcePortId,
8597
+ sourceElementId: sourceElementId,
8598
+ start: start,
8599
+ current: start,
8600
+ isMulti: false,
8601
+ hasMoved: false,
8602
+ completionBehavior: 'explicit',
8603
+ };
8604
+ }
8605
+ this.linkDragContext = { sourcePortId: sourcePortId, sourceElementId: sourceElementId };
8606
+ this.engine.emitElementLinkStarted({ sourcePortId: sourcePortId, sourceElementId: sourceElementId, startWorld: __assign({}, start) });
8607
+ this.setCursor('crosshair');
8608
+ if (pointer) {
8609
+ this.updateLinkPreview(pointer);
8610
+ }
8611
+ };
8612
+ KonvaInteraction.prototype.updateLinkPreview = function (pointer) {
8613
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
8614
+ if (this.programmaticLinkSession) {
8615
+ this.programmaticLinkSession.current = __assign({}, pointer);
8616
+ }
8617
+ else if (((_a = this.dragState) === null || _a === void 0 ? void 0 : _a.mode) === 'link-drag') {
8618
+ this.dragState.current = __assign({}, pointer);
8619
+ this.dragState.hasMoved = true;
8620
+ }
8621
+ else {
8622
+ return;
8623
+ }
8624
+ var sourcePortId = (_c = (_b = this.programmaticLinkSession) === null || _b === void 0 ? void 0 : _b.sourcePortId) !== null && _c !== void 0 ? _c : (((_d = this.dragState) === null || _d === void 0 ? void 0 : _d.mode) === 'link-drag' ? this.dragState.sourcePortId : null);
8625
+ if (!sourcePortId)
8626
+ return;
8627
+ var source = this.resolveLinkPreviewSource(sourcePortId, pointer);
8628
+ if (source) {
8629
+ (_e = this.renderer) === null || _e === void 0 ? void 0 : _e.renderTempLink([source, pointer]);
8630
+ }
8631
+ var hit = this.resolveHit(pointer);
8632
+ if ((hit === null || hit === void 0 ? void 0 : hit.type) === 'element') {
8633
+ var placeholder = this.createPlaceholderPort(hit.id, pointer, sourcePortId);
8634
+ if (placeholder) {
8635
+ var hostElement = this.getElementById(hit.id);
8636
+ if (hostElement) {
8637
+ var hostWorld = (_f = this.engine.getElementWorldPosition(hit.id)) !== null && _f !== void 0 ? _f : hostElement.position;
8638
+ (_h = (_g = this.renderer) === null || _g === void 0 ? void 0 : _g.renderPortPlaceholder) === null || _h === void 0 ? void 0 : _h.call(_g, placeholder, { id: hostElement.id, position: hostWorld, size: hostElement.size, shapeId: hostElement.shapeId });
8639
+ }
8640
+ else {
8641
+ (_k = (_j = this.renderer) === null || _j === void 0 ? void 0 : _j.renderPortPlaceholder) === null || _k === void 0 ? void 0 : _k.call(_j, placeholder);
8642
+ }
8643
+ }
8644
+ return;
8645
+ }
8646
+ (_m = (_l = this.renderer) === null || _l === void 0 ? void 0 : _l.clearPortPlaceholder) === null || _m === void 0 ? void 0 : _m.call(_l);
8647
+ };
8648
+ KonvaInteraction.prototype.completeLinkToPort = function (targetPortId) {
8649
+ var _a, _b;
8650
+ var session = this.resolveActiveLinkSession();
8651
+ if (!session)
8652
+ return;
8653
+ var completion = this.tryCreateLinkToPort(session.sourcePortId, session.sourceElementId, targetPortId);
8654
+ this.finishLinkDragSession({
8655
+ sourcePortId: session.sourcePortId,
8656
+ sourceElementId: session.sourceElementId,
8657
+ createdLinkId: (_a = completion.createdLinkId) !== null && _a !== void 0 ? _a : undefined,
8658
+ targetPortId: completion.targetPortId,
8659
+ targetElementId: (_b = completion.targetElementId) !== null && _b !== void 0 ? _b : undefined,
8660
+ cancelled: completion.createdLinkId === null,
8661
+ fromProgrammatic: session.fromProgrammatic,
8662
+ });
8663
+ };
8664
+ KonvaInteraction.prototype.completeLinkToElement = function (targetElementId, pointer) {
8665
+ var _a, _b, _c;
8666
+ var session = this.resolveActiveLinkSession();
8667
+ if (!session)
8668
+ return;
8669
+ var completion = this.tryCreateLinkToElement(session.sourcePortId, session.sourceElementId, targetElementId, pointer);
8670
+ this.finishLinkDragSession({
8671
+ sourcePortId: session.sourcePortId,
8672
+ sourceElementId: session.sourceElementId,
8673
+ createdLinkId: (_a = completion.createdLinkId) !== null && _a !== void 0 ? _a : undefined,
8674
+ targetPortId: (_b = completion.targetPortId) !== null && _b !== void 0 ? _b : undefined,
8675
+ targetElementId: (_c = completion.targetElementId) !== null && _c !== void 0 ? _c : undefined,
8676
+ cancelled: completion.createdLinkId === null,
8677
+ fromProgrammatic: session.fromProgrammatic,
8678
+ });
8679
+ };
8680
+ KonvaInteraction.prototype.cancelLink = function () {
8681
+ this.cancelLinkDrag();
8682
+ };
7128
8683
  KonvaInteraction.prototype.buildPointerInfo = function (world, nativeEvent) {
7129
8684
  var evt = nativeEvent !== null && nativeEvent !== void 0 ? nativeEvent : undefined;
7130
8685
  var client = evt ? { x: evt.clientX, y: evt.clientY } : { x: world.x, y: world.y };
@@ -7252,6 +8807,7 @@ var KonvaInteraction = /** @class */ (function () {
7252
8807
  current: point,
7253
8808
  isMulti: isMulti,
7254
8809
  hasMoved: false,
8810
+ completionBehavior: 'hover-or-release',
7255
8811
  };
7256
8812
  _this.linkDragContext = { sourcePortId: hit.id, sourceElementId: elementId };
7257
8813
  _this.engine.emitElementLinkStarted({ sourcePortId: hit.id, sourceElementId: elementId, startWorld: __assign({}, point) });
@@ -7326,22 +8882,29 @@ var KonvaInteraction = /** @class */ (function () {
7326
8882
  }
7327
8883
  _this.clearActiveShapeHoverControl();
7328
8884
  if (_this.dragState.mode === 'shape-control-drag') {
7329
- var delta = { x: point.x - _this.dragState.start.x, y: point.y - _this.dragState.start.y };
8885
+ var dragState = _this.dragState;
8886
+ var delta = { x: point.x - dragState.start.x, y: point.y - dragState.start.y };
7330
8887
  var moved = Math.hypot(delta.x, delta.y) >= _this.dragThreshold;
7331
- if (!_this.dragState.hasMoved && moved) {
7332
- _this.dragState.hasMoved = true;
7333
- _this.emitShapeHoverControlInteraction('drag-start', _this.dragState.control, point, nativeEvent, {
7334
- sessionId: _this.dragState.sessionId,
7335
- startPointer: _this.dragState.startPointer,
8888
+ if (!dragState.hasMoved && moved) {
8889
+ dragState.hasMoved = true;
8890
+ _this.emitShapeHoverControlInteraction('drag-start', dragState.control, point, nativeEvent, {
8891
+ sessionId: dragState.sessionId,
8892
+ startPointer: dragState.startPointer,
7336
8893
  delta: delta,
7337
8894
  });
8895
+ if (_this.dragState !== dragState) {
8896
+ return;
8897
+ }
7338
8898
  }
7339
- if (_this.dragState.hasMoved) {
7340
- _this.emitShapeHoverControlInteraction('drag-move', _this.dragState.control, point, nativeEvent, {
7341
- sessionId: _this.dragState.sessionId,
7342
- startPointer: _this.dragState.startPointer,
8899
+ if (dragState.hasMoved) {
8900
+ _this.emitShapeHoverControlInteraction('drag-move', dragState.control, point, nativeEvent, {
8901
+ sessionId: dragState.sessionId,
8902
+ startPointer: dragState.startPointer,
7343
8903
  delta: delta,
7344
8904
  });
8905
+ if (_this.dragState !== dragState) {
8906
+ return;
8907
+ }
7345
8908
  }
7346
8909
  }
7347
8910
  else if (_this.dragState.mode === 'move') {
@@ -7379,12 +8942,14 @@ var KonvaInteraction = /** @class */ (function () {
7379
8942
  _this.dragState.hasMoved = _this.dragState.hasMoved || moved;
7380
8943
  _this.dragState.current = point;
7381
8944
  if (_this.dragState.hasMoved) {
7382
- var source = _this.engine.getPortWorldPosition(_this.dragState.sourcePortId);
8945
+ var hit = _this.resolveHit(point);
8946
+ var source = _this.resolveLinkPreviewSource(_this.dragState.sourcePortId, point, hit);
7383
8947
  if (source) {
7384
8948
  (_b = _this.renderer) === null || _b === void 0 ? void 0 : _b.renderTempLink([source, point]);
7385
8949
  }
7386
- var hit = _this.resolveHit(point);
7387
- if ((hit === null || hit === void 0 ? void 0 : hit.type) === 'port' && hit.id !== linkDrag.sourcePortId) {
8950
+ if (linkDrag.completionBehavior === 'hover-or-release' &&
8951
+ (hit === null || hit === void 0 ? void 0 : hit.type) === 'port' &&
8952
+ hit.id !== linkDrag.sourcePortId) {
7388
8953
  var completion = _this.tryCreateLinkToPort(linkDrag.sourcePortId, linkDrag.sourceElementId, hit.id);
7389
8954
  if (completion.createdLinkId) {
7390
8955
  var pointerInfo = _this.buildPointerInfo(point, nativeEvent);
@@ -7393,7 +8958,9 @@ var KonvaInteraction = /** @class */ (function () {
7393
8958
  elementId: linkDrag.sourceElementId,
7394
8959
  pointer: pointerInfo,
7395
8960
  });
7396
- _this.engine.emitElementLinkEnded({
8961
+ _this.dragState = null;
8962
+ _this.linkDragContext = null;
8963
+ _this.emitElementLinkEndedSafely({
7397
8964
  sourcePortId: linkDrag.sourcePortId,
7398
8965
  sourceElementId: linkDrag.sourceElementId,
7399
8966
  linkId: completion.createdLinkId,
@@ -7403,8 +8970,6 @@ var KonvaInteraction = /** @class */ (function () {
7403
8970
  });
7404
8971
  (_d = _this.renderer) === null || _d === void 0 ? void 0 : _d.clearTempLink();
7405
8972
  (_f = (_e = _this.renderer) === null || _e === void 0 ? void 0 : _e.clearPortPlaceholder) === null || _f === void 0 ? void 0 : _f.call(_e);
7406
- _this.dragState = null;
7407
- _this.linkDragContext = null;
7408
8973
  _this.setCursor('default');
7409
8974
  return;
7410
8975
  }
@@ -7495,6 +9060,7 @@ var KonvaInteraction = /** @class */ (function () {
7495
9060
  var pointerPoint = _this.getPointerPosition();
7496
9061
  if (((_a = _this.dragState) === null || _a === void 0 ? void 0 : _a.mode) === 'shape-control-drag') {
7497
9062
  var dragState = _this.dragState;
9063
+ _this.dragState = null;
7498
9064
  var point = pointerPoint !== null && pointerPoint !== void 0 ? pointerPoint : dragState.start;
7499
9065
  var delta = { x: point.x - dragState.start.x, y: point.y - dragState.start.y };
7500
9066
  var dragContext = {
@@ -7509,7 +9075,6 @@ var KonvaInteraction = /** @class */ (function () {
7509
9075
  _this.emitShapeHoverControlInteraction('click', dragState.control, point, nativeEvent);
7510
9076
  _this.emitLegacyShapeHoverControlActivation(dragState.control, point, nativeEvent);
7511
9077
  }
7512
- _this.dragState = null;
7513
9078
  if (pointerPoint) {
7514
9079
  _this.lastPointerPosition = __assign({}, pointerPoint);
7515
9080
  _this.updateShapeHoverControl(pointerPoint);
@@ -7522,46 +9087,45 @@ var KonvaInteraction = /** @class */ (function () {
7522
9087
  return;
7523
9088
  }
7524
9089
  if (((_b = _this.dragState) === null || _b === void 0 ? void 0 : _b.mode) === 'link-drag') {
9090
+ var linkState = _this.dragState;
7525
9091
  var point = pointerPoint;
7526
- var pointerInfo = _this.buildPointerInfo(point !== null && point !== void 0 ? point : _this.dragState.start, nativeEvent);
9092
+ var pointerInfo = _this.buildPointerInfo(point !== null && point !== void 0 ? point : linkState.start, nativeEvent);
7527
9093
  var sourcePortPayload = {
7528
- portId: _this.dragState.sourcePortId,
7529
- elementId: _this.dragState.sourceElementId,
9094
+ portId: linkState.sourcePortId,
9095
+ elementId: linkState.sourceElementId,
7530
9096
  pointer: pointerInfo,
7531
9097
  };
7532
9098
  _this.engine.emitPortMouseUp(sourcePortPayload);
7533
9099
  var createdLinkId = null;
7534
9100
  var targetPortId = null;
7535
9101
  var targetElementId = null;
7536
- if (!_this.dragState.hasMoved) {
7537
- _this.handleSelection({ id: _this.dragState.sourcePortId, type: 'port' }, _this.dragState.isMulti);
9102
+ if (!linkState.hasMoved) {
9103
+ _this.handleSelection({ id: linkState.sourcePortId, type: 'port' }, linkState.isMulti);
7538
9104
  }
7539
9105
  else {
7540
9106
  var hit = point ? _this.resolveHit(point) : null;
7541
- if (hit && hit.type === 'port' && hit.id !== _this.dragState.sourcePortId) {
7542
- var completion = _this.tryCreateLinkToPort(_this.dragState.sourcePortId, _this.dragState.sourceElementId, hit.id);
9107
+ if (linkState.completionBehavior === 'hover-or-release' &&
9108
+ hit &&
9109
+ hit.type === 'port' &&
9110
+ hit.id !== linkState.sourcePortId) {
9111
+ var completion = _this.tryCreateLinkToPort(linkState.sourcePortId, linkState.sourceElementId, hit.id);
7543
9112
  createdLinkId = completion.createdLinkId;
7544
9113
  targetPortId = completion.targetPortId;
7545
9114
  targetElementId = completion.targetElementId;
7546
9115
  }
7547
- else if (hit && hit.type === 'element' && point) {
9116
+ else if (linkState.completionBehavior === 'hover-or-release' && hit && hit.type === 'element' && point) {
7548
9117
  targetElementId = hit.id;
7549
- var createdPortId = _this.createPortForLink(hit.id, point, _this.dragState.sourcePortId);
7550
- if (createdPortId) {
7551
- targetPortId = createdPortId;
7552
- createdLinkId = createId();
7553
- _this.engine.addLink({
7554
- id: createdLinkId,
7555
- sourcePortId: _this.dragState.sourcePortId,
7556
- targetPortId: createdPortId,
7557
- points: [],
7558
- });
7559
- }
9118
+ var completion = _this.tryCreateLinkToElement(linkState.sourcePortId, linkState.sourceElementId, hit.id, point);
9119
+ targetPortId = completion.targetPortId;
9120
+ createdLinkId = completion.createdLinkId;
9121
+ targetElementId = completion.targetElementId;
7560
9122
  }
7561
9123
  }
7562
- _this.engine.emitElementLinkEnded({
7563
- sourcePortId: _this.dragState.sourcePortId,
7564
- sourceElementId: _this.dragState.sourceElementId,
9124
+ _this.dragState = null;
9125
+ _this.linkDragContext = null;
9126
+ _this.emitElementLinkEndedSafely({
9127
+ sourcePortId: linkState.sourcePortId,
9128
+ sourceElementId: linkState.sourceElementId,
7565
9129
  linkId: createdLinkId !== null && createdLinkId !== void 0 ? createdLinkId : undefined,
7566
9130
  targetPortId: targetPortId !== null && targetPortId !== void 0 ? targetPortId : undefined,
7567
9131
  targetElementId: targetElementId !== null && targetElementId !== void 0 ? targetElementId : undefined,
@@ -7795,7 +9359,7 @@ var KonvaInteraction = /** @class */ (function () {
7795
9359
  addHit(this.findElementHit(point));
7796
9360
  if (hits.length === 0)
7797
9361
  return null;
7798
- var priority = ['resize-handle', 'link-handle', 'shape-hover-control', 'port', 'link', 'text', 'element'];
9362
+ var priority = ['resize-handle', 'link-handle', 'port', 'shape-hover-control', 'link', 'text', 'element'];
7799
9363
  var _loop_1 = function (i) {
7800
9364
  var candidates = hits.filter(function (hit) { return hit.type === priority[i]; });
7801
9365
  if (candidates.length === 0)
@@ -8241,10 +9805,10 @@ var KonvaInteraction = /** @class */ (function () {
8241
9805
  for (var index = 0; index < controls.length; index += 1) {
8242
9806
  var control = controls[index];
8243
9807
  var targetHoverCandidate = control.visibilityTriggers.includes('target-hover')
8244
- ? this.resolveTargetHoverCandidate(point, transformed, ellipseMidPoints, control)
9808
+ ? this.resolveTargetHoverCandidate(point, transformed, ellipseMidPoints, control, elementId)
8245
9809
  : null;
8246
9810
  var elementHoverCandidate = control.visibilityTriggers.includes('element-hover')
8247
- ? this.resolveElementHoverCandidate(point, transformed, ellipseMidPoints, control)
9811
+ ? this.resolveElementHoverCandidate(point, transformed, ellipseMidPoints, control, elementId)
8248
9812
  : null;
8249
9813
  var candidate = targetHoverCandidate !== null && targetHoverCandidate !== void 0 ? targetHoverCandidate : elementHoverCandidate;
8250
9814
  if (!candidate)
@@ -8260,7 +9824,7 @@ var KonvaInteraction = /** @class */ (function () {
8260
9824
  }
8261
9825
  return null;
8262
9826
  };
8263
- KonvaInteraction.prototype.resolveTargetHoverCandidate = function (point, geometry, ellipseMidPoints, control) {
9827
+ KonvaInteraction.prototype.resolveTargetHoverCandidate = function (point, geometry, ellipseMidPoints, control, elementId) {
8264
9828
  if (control.targetKind === 'ellipse-midpoint') {
8265
9829
  var targets = this.resolveEligibleEllipseMidPoints(control, ellipseMidPoints);
8266
9830
  if (!targets.length || control.tolerance <= 0)
@@ -8281,7 +9845,7 @@ var KonvaInteraction = /** @class */ (function () {
8281
9845
  }
8282
9846
  if (!geometry)
8283
9847
  return null;
8284
- var indices = this.resolveEligibleTargetIndices(control, geometry);
9848
+ var indices = this.resolveEligibleTargetIndices(control, geometry, elementId);
8285
9849
  if (!indices.length || control.tolerance <= 0)
8286
9850
  return null;
8287
9851
  if (control.targetKind === 'vertex') {
@@ -8331,7 +9895,7 @@ var KonvaInteraction = /** @class */ (function () {
8331
9895
  }
8332
9896
  return best ? { targetIndex: best.targetIndex, target: best.target } : null;
8333
9897
  };
8334
- KonvaInteraction.prototype.resolveElementHoverCandidate = function (point, geometry, ellipseMidPoints, control) {
9898
+ KonvaInteraction.prototype.resolveElementHoverCandidate = function (point, geometry, ellipseMidPoints, control, elementId) {
8335
9899
  if (control.targetKind === 'ellipse-midpoint') {
8336
9900
  var targets = this.resolveEligibleEllipseMidPoints(control, ellipseMidPoints);
8337
9901
  if (!targets.length)
@@ -8350,7 +9914,7 @@ var KonvaInteraction = /** @class */ (function () {
8350
9914
  }
8351
9915
  if (!geometry)
8352
9916
  return null;
8353
- var indices = this.resolveEligibleTargetIndices(control, geometry);
9917
+ var indices = this.resolveEligibleTargetIndices(control, geometry, elementId);
8354
9918
  if (!indices.length)
8355
9919
  return null;
8356
9920
  if (control.targetKind === 'vertex') {
@@ -8394,17 +9958,46 @@ var KonvaInteraction = /** @class */ (function () {
8394
9958
  }
8395
9959
  return best ? { targetIndex: best.targetIndex, target: best.target } : null;
8396
9960
  };
8397
- KonvaInteraction.prototype.resolveEligibleTargetIndices = function (control, geometry) {
9961
+ KonvaInteraction.prototype.resolveEligibleTargetIndices = function (control, geometry, elementId) {
8398
9962
  var _a;
8399
9963
  var source = control.targetKind === 'vertex' ? geometry.vertices : geometry.edges;
8400
9964
  if (!source.length)
8401
9965
  return [];
9966
+ var occupiedVertexIndices = this.resolveOccupiedVertexIndices(elementId, geometry);
8402
9967
  if (control.allowAllTargets === true) {
8403
- return Array.from({ length: source.length }, function (_, index) { return index; });
9968
+ return Array.from({ length: source.length }, function (_, index) { return index; }).filter(function (index) {
9969
+ return control.targetKind === 'vertex' ? !occupiedVertexIndices.has(index) : true;
9970
+ });
8404
9971
  }
8405
9972
  if (!((_a = control.targetIndices) === null || _a === void 0 ? void 0 : _a.length))
8406
9973
  return [];
8407
- return control.targetIndices.filter(function (targetIndex) { return targetIndex >= 0 && targetIndex < source.length; });
9974
+ return control.targetIndices.filter(function (targetIndex) {
9975
+ return targetIndex >= 0 &&
9976
+ targetIndex < source.length &&
9977
+ (control.targetKind !== 'vertex' || !occupiedVertexIndices.has(targetIndex));
9978
+ });
9979
+ };
9980
+ KonvaInteraction.prototype.resolveOccupiedVertexIndices = function (elementId, geometry) {
9981
+ var _this = this;
9982
+ if (!geometry.vertices.length)
9983
+ return new Set();
9984
+ var ports = this.engine.getState().ports.filter(function (port) { return port.elementId === elementId; });
9985
+ if (!ports.length)
9986
+ return new Set();
9987
+ var tolerance = this.occupiedVertexTolerance;
9988
+ var occupied = new Set();
9989
+ ports.forEach(function (port) {
9990
+ var world = _this.engine.getPortWorldPosition(port.id);
9991
+ if (!world)
9992
+ return;
9993
+ geometry.vertices.forEach(function (vertex, index) {
9994
+ if (Math.abs(vertex.position.x - world.x) <= tolerance &&
9995
+ Math.abs(vertex.position.y - world.y) <= tolerance) {
9996
+ occupied.add(index);
9997
+ }
9998
+ });
9999
+ });
10000
+ return occupied;
8408
10001
  };
8409
10002
  KonvaInteraction.prototype.resolveEligibleEllipseMidPoints = function (control, targets) {
8410
10003
  var _a;
@@ -8826,14 +10419,19 @@ var KonvaInteraction = /** @class */ (function () {
8826
10419
  this.engine.setSelection(Array.from(selected));
8827
10420
  };
8828
10421
  KonvaInteraction.prototype.applyPortConstraints = function (portId, worldTarget) {
10422
+ var _a;
8829
10423
  var port = this.getPortById(portId);
8830
10424
  if (!port)
8831
10425
  return worldTarget;
10426
+ var element = this.getElementById(port.elementId);
10427
+ if (!element)
10428
+ return worldTarget;
8832
10429
  var elementPos = this.engine.getElementWorldPosition(port.elementId);
8833
10430
  if (!elementPos)
8834
10431
  return worldTarget;
8835
10432
  var relative = { x: worldTarget.x - elementPos.x, y: worldTarget.y - elementPos.y };
8836
10433
  var style = port.style;
10434
+ var limits = (_a = element.portMovement) === null || _a === void 0 ? void 0 : _a.positionLimits;
8837
10435
  var constrained = __assign({}, relative);
8838
10436
  if ((style === null || style === void 0 ? void 0 : style.moveAxis) === 'horizontal') {
8839
10437
  constrained.y = port.position.y;
@@ -8841,12 +10439,82 @@ var KonvaInteraction = /** @class */ (function () {
8841
10439
  else if ((style === null || style === void 0 ? void 0 : style.moveAxis) === 'vertical') {
8842
10440
  constrained.x = port.position.x;
8843
10441
  }
8844
- if (style === null || style === void 0 ? void 0 : style.moveBounds) {
8845
- var bounds = style.moveBounds;
8846
- constrained.x = Math.min(bounds.x + bounds.width, Math.max(bounds.x, constrained.x));
8847
- constrained.y = Math.min(bounds.y + bounds.height, Math.max(bounds.y, constrained.y));
10442
+ if (style === null || style === void 0 ? void 0 : style.moveBounds) {
10443
+ var bounds = style.moveBounds;
10444
+ constrained.x = Math.min(bounds.x + bounds.width, Math.max(bounds.x, constrained.x));
10445
+ constrained.y = Math.min(bounds.y + bounds.height, Math.max(bounds.y, constrained.y));
10446
+ }
10447
+ if (limits === null || limits === void 0 ? void 0 : limits.x) {
10448
+ if (typeof limits.x.min === 'number')
10449
+ constrained.x = Math.max(constrained.x, limits.x.min);
10450
+ if (typeof limits.x.max === 'number')
10451
+ constrained.x = Math.min(constrained.x, limits.x.max);
10452
+ }
10453
+ if (limits === null || limits === void 0 ? void 0 : limits.y) {
10454
+ if (typeof limits.y.min === 'number')
10455
+ constrained.y = Math.max(constrained.y, limits.y.min);
10456
+ if (typeof limits.y.max === 'number')
10457
+ constrained.y = Math.min(constrained.y, limits.y.max);
10458
+ }
10459
+ return { x: elementPos.x + constrained.x, y: elementPos.y + constrained.y };
10460
+ };
10461
+ KonvaInteraction.prototype.resolveLinkPreviewSource = function (sourcePortId, pointer, hit) {
10462
+ var resolvedHit = hit !== null && hit !== void 0 ? hit : this.resolveHit(pointer);
10463
+ if ((resolvedHit === null || resolvedHit === void 0 ? void 0 : resolvedHit.type) === 'port' && resolvedHit.id !== sourcePortId) {
10464
+ return this.engine.getPortLinkWorldPosition(sourcePortId, { oppositePortId: resolvedHit.id });
10465
+ }
10466
+ return this.engine.getPortLinkWorldPosition(sourcePortId);
10467
+ };
10468
+ KonvaInteraction.prototype.resolveActiveLinkSession = function () {
10469
+ var _a;
10470
+ if (this.programmaticLinkSession) {
10471
+ return {
10472
+ sourcePortId: this.programmaticLinkSession.sourcePortId,
10473
+ sourceElementId: this.programmaticLinkSession.sourceElementId,
10474
+ fromProgrammatic: true,
10475
+ };
10476
+ }
10477
+ if (((_a = this.dragState) === null || _a === void 0 ? void 0 : _a.mode) === 'link-drag') {
10478
+ return {
10479
+ sourcePortId: this.dragState.sourcePortId,
10480
+ sourceElementId: this.dragState.sourceElementId,
10481
+ fromProgrammatic: false,
10482
+ };
10483
+ }
10484
+ return null;
10485
+ };
10486
+ KonvaInteraction.prototype.finishLinkDragSession = function (result) {
10487
+ var _a, _b, _c, _d;
10488
+ this.linkDragContext = null;
10489
+ if (result.fromProgrammatic) {
10490
+ this.programmaticLinkSession = null;
10491
+ }
10492
+ else if (((_a = this.dragState) === null || _a === void 0 ? void 0 : _a.mode) === 'link-drag') {
10493
+ this.dragState = null;
10494
+ }
10495
+ (_b = this.renderer) === null || _b === void 0 ? void 0 : _b.clearTempLink();
10496
+ (_d = (_c = this.renderer) === null || _c === void 0 ? void 0 : _c.clearPortPlaceholder) === null || _d === void 0 ? void 0 : _d.call(_c);
10497
+ this.clearActiveShapeHoverControl();
10498
+ this.setCursor('default');
10499
+ this.emitElementLinkEndedSafely({
10500
+ sourcePortId: result.sourcePortId,
10501
+ sourceElementId: result.sourceElementId,
10502
+ linkId: result.createdLinkId,
10503
+ targetPortId: result.targetPortId,
10504
+ targetElementId: result.targetElementId,
10505
+ cancelled: result.cancelled,
10506
+ });
10507
+ };
10508
+ KonvaInteraction.prototype.emitElementLinkEndedSafely = function (event) {
10509
+ if (this.emittingElementLinkEnded)
10510
+ return;
10511
+ this.emittingElementLinkEnded = true;
10512
+ try {
10513
+ this.engine.emitElementLinkEnded(event);
10514
+ }
10515
+ finally {
10516
+ this.emittingElementLinkEnded = false;
8848
10517
  }
8849
- return { x: elementPos.x + constrained.x, y: elementPos.y + constrained.y };
8850
10518
  };
8851
10519
  KonvaInteraction.prototype.tryCreateLinkToPort = function (sourcePortId, sourceElementId, targetPortId) {
8852
10520
  var targetElementId = this.engine.getPortElementId(targetPortId);
@@ -8871,6 +10539,33 @@ var KonvaInteraction = /** @class */ (function () {
8871
10539
  });
8872
10540
  return { createdLinkId: createdLinkId, targetPortId: targetPortId, targetElementId: targetElementId };
8873
10541
  };
10542
+ KonvaInteraction.prototype.tryCreateLinkToElement = function (sourcePortId, sourceElementId, targetElementId, worldPoint) {
10543
+ var targetPort = this.createPortForLink(targetElementId, worldPoint, sourcePortId);
10544
+ if (!targetPort) {
10545
+ return { createdLinkId: null, targetPortId: null, targetElementId: null };
10546
+ }
10547
+ var cancelledByHost = this.engine.emitElementLinkConnecting({
10548
+ sourcePortId: sourcePortId,
10549
+ sourceElementId: sourceElementId,
10550
+ targetPortId: targetPort.id,
10551
+ targetElementId: targetElementId,
10552
+ cancel: function () { },
10553
+ cancelled: false,
10554
+ });
10555
+ if (cancelledByHost) {
10556
+ return { createdLinkId: null, targetPortId: targetPort.id, targetElementId: targetElementId };
10557
+ }
10558
+ this.engine.addPortToElement(targetElementId, targetPort);
10559
+ this.engine.movePortTo(targetPort.id, worldPoint.x, worldPoint.y);
10560
+ var createdLinkId = createId();
10561
+ this.engine.addLink({
10562
+ id: createdLinkId,
10563
+ sourcePortId: sourcePortId,
10564
+ targetPortId: targetPort.id,
10565
+ points: [],
10566
+ });
10567
+ return { createdLinkId: createdLinkId, targetPortId: targetPort.id, targetElementId: targetElementId };
10568
+ };
8874
10569
  KonvaInteraction.prototype.createPortForLink = function (elementId, worldPoint, sourcePortId) {
8875
10570
  var _a, _b, _c, _d;
8876
10571
  var element = this.getElementById(elementId);
@@ -8885,9 +10580,8 @@ var KonvaInteraction = /** @class */ (function () {
8885
10580
  var destinationMoveMode = ((_b = element.portMovement) === null || _b === void 0 ? void 0 : _b.moveMode) && element.portMovement.moveMode !== 'anchors'
8886
10581
  ? element.portMovement.moveMode
8887
10582
  : undefined;
8888
- var portId = createId();
8889
- this.engine.addPortToElement(elementId, {
8890
- id: portId,
10583
+ return {
10584
+ id: createId(),
8891
10585
  elementId: elementId,
8892
10586
  position: relative,
8893
10587
  shapeId: sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.shapeId,
@@ -8896,9 +10590,12 @@ var KonvaInteraction = /** @class */ (function () {
8896
10590
  moveMode: destinationMoveMode !== null && destinationMoveMode !== void 0 ? destinationMoveMode : sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.moveMode,
8897
10591
  anchorCenter: (_c = sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.anchorCenter) !== null && _c !== void 0 ? _c : true,
8898
10592
  orientToHostBorder: (_d = sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.orientToHostBorder) !== null && _d !== void 0 ? _d : true,
8899
- });
8900
- this.engine.movePortTo(portId, worldPoint.x, worldPoint.y);
8901
- return portId;
10593
+ placementPoint: (sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.placementPoint) ? __assign({}, sourcePort.placementPoint) : undefined,
10594
+ linkAttachPoint: (sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.linkAttachPoint) ? __assign({}, sourcePort.linkAttachPoint) : undefined,
10595
+ externalLinkAttachPoint: (sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.externalLinkAttachPoint) ? __assign({}, sourcePort.externalLinkAttachPoint) : undefined,
10596
+ internalLinkAttachPoint: (sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.internalLinkAttachPoint) ? __assign({}, sourcePort.internalLinkAttachPoint) : undefined,
10597
+ rotationPivot: (sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.rotationPivot) ? __assign({}, sourcePort.rotationPivot) : undefined,
10598
+ };
8902
10599
  };
8903
10600
  KonvaInteraction.prototype.createPlaceholderPort = function (elementId, worldPoint, sourcePortId) {
8904
10601
  var _a, _b, _c;
@@ -8917,6 +10614,11 @@ var KonvaInteraction = /** @class */ (function () {
8917
10614
  moveMode: destinationMoveMode !== null && destinationMoveMode !== void 0 ? destinationMoveMode : sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.moveMode,
8918
10615
  anchorCenter: (_b = sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.anchorCenter) !== null && _b !== void 0 ? _b : true,
8919
10616
  orientToHostBorder: (_c = sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.orientToHostBorder) !== null && _c !== void 0 ? _c : true,
10617
+ placementPoint: (sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.placementPoint) ? __assign({}, sourcePort.placementPoint) : undefined,
10618
+ linkAttachPoint: (sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.linkAttachPoint) ? __assign({}, sourcePort.linkAttachPoint) : undefined,
10619
+ externalLinkAttachPoint: (sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.externalLinkAttachPoint) ? __assign({}, sourcePort.externalLinkAttachPoint) : undefined,
10620
+ internalLinkAttachPoint: (sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.internalLinkAttachPoint) ? __assign({}, sourcePort.internalLinkAttachPoint) : undefined,
10621
+ rotationPivot: (sourcePort === null || sourcePort === void 0 ? void 0 : sourcePort.rotationPivot) ? __assign({}, sourcePort.rotationPivot) : undefined,
8920
10622
  };
8921
10623
  };
8922
10624
  KonvaInteraction.prototype.getElementById = function (id) {
@@ -8960,47 +10662,77 @@ var KonvaInteraction = /** @class */ (function () {
8960
10662
  (_b = stageAny === null || stageAny === void 0 ? void 0 : stageAny.position) === null || _b === void 0 ? void 0 : _b.call(stageAny, this.pan);
8961
10663
  };
8962
10664
  KonvaInteraction.prototype.cancelLinkDrag = function () {
8963
- var _a, _b, _c, _d, _e, _f;
10665
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
8964
10666
  if (((_a = this.dragState) === null || _a === void 0 ? void 0 : _a.mode) === 'shape-control-drag') {
8965
- var point_1 = (_b = this.lastPointerPosition) !== null && _b !== void 0 ? _b : this.dragState.start;
8966
- if (this.dragState.hasMoved) {
8967
- this.emitShapeHoverControlInteraction('drag-end', this.dragState.control, point_1, undefined, {
8968
- sessionId: this.dragState.sessionId,
8969
- startPointer: this.dragState.startPointer,
10667
+ var dragState = this.dragState;
10668
+ var point_1 = (_b = this.lastPointerPosition) !== null && _b !== void 0 ? _b : dragState.start;
10669
+ this.dragState = null;
10670
+ if (dragState.hasMoved) {
10671
+ this.emitShapeHoverControlInteraction('drag-end', dragState.control, point_1, undefined, {
10672
+ sessionId: dragState.sessionId,
10673
+ startPointer: dragState.startPointer,
8970
10674
  delta: {
8971
- x: point_1.x - this.dragState.start.x,
8972
- y: point_1.y - this.dragState.start.y,
10675
+ x: point_1.x - dragState.start.x,
10676
+ y: point_1.y - dragState.start.y,
8973
10677
  },
8974
10678
  });
8975
10679
  }
8976
- this.dragState = null;
10680
+ if (this.programmaticLinkSession) {
10681
+ var session = this.programmaticLinkSession;
10682
+ this.programmaticLinkSession = null;
10683
+ this.linkDragContext = null;
10684
+ (_c = this.renderer) === null || _c === void 0 ? void 0 : _c.clearTempLink();
10685
+ (_e = (_d = this.renderer) === null || _d === void 0 ? void 0 : _d.clearPortPlaceholder) === null || _e === void 0 ? void 0 : _e.call(_d);
10686
+ this.emitElementLinkEndedSafely({
10687
+ sourcePortId: session.sourcePortId,
10688
+ sourceElementId: session.sourceElementId,
10689
+ cancelled: true,
10690
+ });
10691
+ }
10692
+ this.clearActiveShapeHoverControl();
10693
+ this.setCursor('default');
10694
+ return;
10695
+ }
10696
+ if (this.programmaticLinkSession) {
10697
+ var session = this.programmaticLinkSession;
10698
+ this.programmaticLinkSession = null;
10699
+ this.linkDragContext = null;
10700
+ (_f = this.renderer) === null || _f === void 0 ? void 0 : _f.clearTempLink();
10701
+ (_h = (_g = this.renderer) === null || _g === void 0 ? void 0 : _g.clearPortPlaceholder) === null || _h === void 0 ? void 0 : _h.call(_g);
10702
+ this.emitElementLinkEndedSafely({
10703
+ sourcePortId: session.sourcePortId,
10704
+ sourceElementId: session.sourceElementId,
10705
+ cancelled: true,
10706
+ });
8977
10707
  this.clearActiveShapeHoverControl();
8978
10708
  this.setCursor('default');
8979
10709
  return;
8980
10710
  }
8981
10711
  if (!this.dragState || this.dragState.mode !== 'link-drag') {
8982
10712
  if (this.linkDragContext) {
8983
- this.engine.emitElementLinkEnded(__assign(__assign({}, this.linkDragContext), { cancelled: true }));
10713
+ var context = this.linkDragContext;
8984
10714
  this.linkDragContext = null;
10715
+ this.emitElementLinkEndedSafely(__assign(__assign({}, context), { cancelled: true }));
8985
10716
  }
8986
10717
  return;
8987
10718
  }
8988
- var point = (_c = this.dragState.current) !== null && _c !== void 0 ? _c : this.dragState.start;
10719
+ var linkState = this.dragState;
10720
+ var point = (_j = linkState.current) !== null && _j !== void 0 ? _j : linkState.start;
8989
10721
  var pointerInfo = this.buildPointerInfo(point, null);
8990
10722
  this.engine.emitPortMouseUp({
8991
- portId: this.dragState.sourcePortId,
8992
- elementId: this.dragState.sourceElementId,
10723
+ portId: linkState.sourcePortId,
10724
+ elementId: linkState.sourceElementId,
8993
10725
  pointer: pointerInfo,
8994
10726
  });
8995
- this.engine.emitElementLinkEnded({
8996
- sourcePortId: this.dragState.sourcePortId,
8997
- sourceElementId: this.dragState.sourceElementId,
10727
+ this.dragState = null;
10728
+ this.linkDragContext = null;
10729
+ (_k = this.renderer) === null || _k === void 0 ? void 0 : _k.clearTempLink();
10730
+ (_m = (_l = this.renderer) === null || _l === void 0 ? void 0 : _l.clearPortPlaceholder) === null || _m === void 0 ? void 0 : _m.call(_l);
10731
+ this.emitElementLinkEndedSafely({
10732
+ sourcePortId: linkState.sourcePortId,
10733
+ sourceElementId: linkState.sourceElementId,
8998
10734
  cancelled: true,
8999
10735
  });
9000
- this.linkDragContext = null;
9001
- (_d = this.renderer) === null || _d === void 0 ? void 0 : _d.clearTempLink();
9002
- (_f = (_e = this.renderer) === null || _e === void 0 ? void 0 : _e.clearPortPlaceholder) === null || _f === void 0 ? void 0 : _f.call(_e);
9003
- this.dragState = null;
9004
10736
  this.clearActiveShapeHoverControl();
9005
10737
  this.setCursor('default');
9006
10738
  };
@@ -9224,6 +10956,15 @@ var normalizeVector = function (vector) {
9224
10956
  return { x: 0, y: -1 };
9225
10957
  return { x: vector.x / length, y: vector.y / length };
9226
10958
  };
10959
+ var sideToNormal = function (side) {
10960
+ if (side === 'left')
10961
+ return { x: -1, y: 0 };
10962
+ if (side === 'right')
10963
+ return { x: 1, y: 0 };
10964
+ if (side === 'top')
10965
+ return { x: 0, y: -1 };
10966
+ return { x: 0, y: 1 };
10967
+ };
9227
10968
  var resolveCardinalAnchors = function (rect) {
9228
10969
  var cx = rect.x + rect.width / 2;
9229
10970
  var cy = rect.y + rect.height / 2;
@@ -9303,9 +11044,15 @@ var BuiltInShape = /** @class */ (function () {
9303
11044
  BuiltInShape.prototype.resolveBorderSide = function (point, rect) {
9304
11045
  return resolveBoundarySide(point, rect, 'rect');
9305
11046
  };
11047
+ BuiltInShape.prototype.resolveBorderNormal = function (point, rect) {
11048
+ return sideToNormal(this.resolveBorderSide(point, rect));
11049
+ };
9306
11050
  BuiltInShape.prototype.resolvePortAnchors = function (_rect, _options) {
9307
11051
  return [];
9308
11052
  };
11053
+ BuiltInShape.prototype.resolvePortBorderTransform = function (_context) {
11054
+ return undefined;
11055
+ };
9309
11056
  BuiltInShape.prototype.resolveHoverGeometry = function (_rect) {
9310
11057
  return undefined;
9311
11058
  };
@@ -9535,6 +11282,78 @@ var getKonva = function () {
9535
11282
  var module = require('konva');
9536
11283
  return (_a = module.default) !== null && _a !== void 0 ? _a : module;
9537
11284
  };
11285
+ var createBounds = function () { return ({
11286
+ minX: Number.POSITIVE_INFINITY,
11287
+ minY: Number.POSITIVE_INFINITY,
11288
+ maxX: Number.NEGATIVE_INFINITY,
11289
+ maxY: Number.NEGATIVE_INFINITY,
11290
+ }); };
11291
+ var expandBounds = function (bounds, x, y) {
11292
+ if (x < bounds.minX)
11293
+ bounds.minX = x;
11294
+ if (y < bounds.minY)
11295
+ bounds.minY = y;
11296
+ if (x > bounds.maxX)
11297
+ bounds.maxX = x;
11298
+ if (y > bounds.maxY)
11299
+ bounds.maxY = y;
11300
+ };
11301
+ var includeRect = function (bounds, x, y, width, height) {
11302
+ var safeWidth = Math.max(0, width);
11303
+ var safeHeight = Math.max(0, height);
11304
+ expandBounds(bounds, x, y);
11305
+ expandBounds(bounds, x + safeWidth, y + safeHeight);
11306
+ };
11307
+ var hasBounds = function (bounds) {
11308
+ return Number.isFinite(bounds.minX) &&
11309
+ Number.isFinite(bounds.minY) &&
11310
+ Number.isFinite(bounds.maxX) &&
11311
+ Number.isFinite(bounds.maxY) &&
11312
+ bounds.maxX >= bounds.minX &&
11313
+ bounds.maxY >= bounds.minY;
11314
+ };
11315
+ var resolveStateWorldBounds = function (state) {
11316
+ var bounds = createBounds();
11317
+ state.elements.forEach(function (element) {
11318
+ includeRect(bounds, element.position.x, element.position.y, element.size.width, element.size.height);
11319
+ });
11320
+ state.ports.forEach(function (port) {
11321
+ var _a;
11322
+ var size = port.size;
11323
+ if (!size) {
11324
+ expandBounds(bounds, port.position.x, port.position.y);
11325
+ return;
11326
+ }
11327
+ var anchorCenter = (_a = port.anchorCenter) !== null && _a !== void 0 ? _a : true;
11328
+ var x = anchorCenter ? port.position.x - size.width / 2 : port.position.x;
11329
+ var y = anchorCenter ? port.position.y - size.height / 2 : port.position.y;
11330
+ includeRect(bounds, x, y, size.width, size.height);
11331
+ });
11332
+ state.links.forEach(function (link) {
11333
+ link.points.forEach(function (point) {
11334
+ expandBounds(bounds, point.x, point.y);
11335
+ });
11336
+ });
11337
+ state.texts.forEach(function (text) {
11338
+ var _a;
11339
+ var offset = (_a = text.displayOffset) !== null && _a !== void 0 ? _a : { x: 0, y: 0 };
11340
+ var position = { x: text.position.x + offset.x, y: text.position.y + offset.y };
11341
+ var clipSize = text.displayClipSize;
11342
+ var size = clipSize !== null && clipSize !== void 0 ? clipSize : text.size;
11343
+ if (size) {
11344
+ includeRect(bounds, position.x, position.y, size.width, size.height);
11345
+ return;
11346
+ }
11347
+ expandBounds(bounds, position.x, position.y);
11348
+ });
11349
+ return hasBounds(bounds) ? bounds : null;
11350
+ };
11351
+ var resolveFitToContentPadding = function (fitToContent) {
11352
+ if (typeof fitToContent === 'object' && typeof fitToContent.padding === 'number') {
11353
+ return Math.max(0, fitToContent.padding);
11354
+ }
11355
+ return 0;
11356
+ };
9538
11357
  var getNodeAttr = function (node, key) {
9539
11358
  if (node.getAttr) {
9540
11359
  return node.getAttr(key);
@@ -9573,7 +11392,13 @@ var registerSimpleShapes = function (registry, shapes, isPort) {
9573
11392
  : undefined,
9574
11393
  projectToBorder: function (point, rect) { return behavior.projectToBorder(point, rect); },
9575
11394
  resolveBorderSide: function (point, rect) { return behavior.resolveBorderSide(point, rect); },
11395
+ resolveBorderNormal: behavior.resolveBorderNormal
11396
+ ? function (point, rect) { return behavior.resolveBorderNormal(point, rect); }
11397
+ : undefined,
9576
11398
  resolvePortAnchors: function (rect, options) { return behavior.resolvePortAnchors(rect, options); },
11399
+ resolvePortBorderTransform: behavior.resolvePortBorderTransform
11400
+ ? function (context) { return behavior.resolvePortBorderTransform(context); }
11401
+ : undefined,
9577
11402
  resolveHoverGeometry: function (rect) { return behavior.resolveHoverGeometry(rect); },
9578
11403
  resolveEllipseMidPoints: function (rect) { return behavior.resolveEllipseMidPoints(rect); },
9579
11404
  createNode: function (model) {
@@ -9647,7 +11472,7 @@ var createDiagramEditor = function (config) {
9647
11472
  .filter(function (hit) { return Boolean(hit); });
9648
11473
  if (hits.length === 0)
9649
11474
  return { id: '', type: 'none' };
9650
- var priority = ['resize-handle', 'link-handle', 'shape-hover-control', 'port', 'link', 'text', 'element'];
11475
+ var priority = ['resize-handle', 'link-handle', 'port', 'shape-hover-control', 'link', 'text', 'element'];
9651
11476
  var _loop_1 = function (i) {
9652
11477
  var candidates = hits.filter(function (hit) { return hit.type === priority[i]; });
9653
11478
  if (candidates.length === 0)
@@ -9738,6 +11563,36 @@ var createDiagramEditor = function (config) {
9738
11563
  setSnapping: function (snapper) { return engine.setSnapping(snapper); },
9739
11564
  registerShape: function (shape) { return engine.registerShape(shape); },
9740
11565
  render: function () { return engine.render(); },
11566
+ startLinkFromPort: function (sourcePortId, pointer) { return interaction.startLinkFromPort(sourcePortId, pointer); },
11567
+ updateLinkPreview: function (pointer) { return interaction.updateLinkPreview(pointer); },
11568
+ completeLinkToPort: function (targetPortId) { return interaction.completeLinkToPort(targetPortId); },
11569
+ completeLinkToElement: function (targetElementId, pointer) { return interaction.completeLinkToElement(targetElementId, pointer); },
11570
+ cancelLink: function () { return interaction.cancelLink(); },
11571
+ exportImage: function (options) {
11572
+ engine.render();
11573
+ if (typeof stage.toDataURL !== 'function') {
11574
+ throw new Error('Diagram image export is not available on the current stage.');
11575
+ }
11576
+ if (!options) {
11577
+ return stage.toDataURL();
11578
+ }
11579
+ var fitToContent = options.fitToContent, baseOptions = __rest(options, ["fitToContent"]);
11580
+ if (!fitToContent) {
11581
+ return stage.toDataURL(baseOptions);
11582
+ }
11583
+ var padding = resolveFitToContentPadding(fitToContent);
11584
+ var worldBounds = resolveStateWorldBounds(engine.getState());
11585
+ if (!worldBounds) {
11586
+ return stage.toDataURL(baseOptions);
11587
+ }
11588
+ var viewport = engine.getViewport();
11589
+ var safeZoom = viewport.zoom === 0 ? 1 : viewport.zoom;
11590
+ var cropX = worldBounds.minX * safeZoom + viewport.pan.x - padding;
11591
+ var cropY = worldBounds.minY * safeZoom + viewport.pan.y - padding;
11592
+ var cropWidth = Math.max(1, (worldBounds.maxX - worldBounds.minX) * safeZoom + padding * 2);
11593
+ var cropHeight = Math.max(1, (worldBounds.maxY - worldBounds.minY) * safeZoom + padding * 2);
11594
+ return stage.toDataURL(__assign(__assign({}, baseOptions), { x: cropX, y: cropY, width: cropWidth, height: cropHeight }));
11595
+ },
9741
11596
  resize: function (width, height) {
9742
11597
  stage.width(width);
9743
11598
  stage.height(height);
@@ -9927,7 +11782,7 @@ var EventHandlersDemo = function () {
9927
11782
  React.createElement("div", { style: { marginBottom: 12 } },
9928
11783
  React.createElement("h2", { style: { marginTop: 0, marginBottom: 4 } }, demo.title),
9929
11784
  React.createElement("p", { style: { marginTop: 0 } }, demo.description)),
9930
- React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction }),
11785
+ React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction, onExportImage: controls.handleExportImage, onClearExportPreview: controls.handleClearExportPreview, exportPreviewDataUrl: controls.exportPreviewDataUrl, exportError: controls.exportError }),
9931
11786
  React.createElement("div", { style: { display: 'flex', gap: 12, alignItems: 'center', marginBottom: 12 } },
9932
11787
  React.createElement("label", { htmlFor: "event-handler-select", style: { fontWeight: 600 } }, "Event"),
9933
11788
  React.createElement("select", { id: "event-handler-select", value: eventDemoEvent, onChange: function (event) { return setEventDemoEvent(event.target.value); }, style: { padding: '6px 8px' } },
@@ -10005,7 +11860,7 @@ var EngineEventsDemo = function () {
10005
11860
  React.createElement("div", { style: { marginBottom: 12 } },
10006
11861
  React.createElement("h2", { style: { marginTop: 0, marginBottom: 4 } }, demo.title),
10007
11862
  React.createElement("p", { style: { marginTop: 0 } }, demo.description)),
10008
- React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction }),
11863
+ React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction, onExportImage: controls.handleExportImage, onClearExportPreview: controls.handleClearExportPreview, exportPreviewDataUrl: controls.exportPreviewDataUrl, exportError: controls.exportError }),
10009
11864
  React.createElement("div", { style: { display: 'grid', gap: 12, marginBottom: 12 } },
10010
11865
  React.createElement("div", { style: { display: 'flex', alignItems: 'center', gap: 12 } },
10011
11866
  React.createElement("label", { htmlFor: "engine-event-select", style: { fontWeight: 600 } }, "Engine Event"),
@@ -10113,7 +11968,7 @@ var LinkCancelDemo = function () {
10113
11968
  " and ",
10114
11969
  React.createElement("code", null, "elementLinkEnded"),
10115
11970
  " appear below.")),
10116
- React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction }),
11971
+ React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction, onExportImage: controls.handleExportImage, onClearExportPreview: controls.handleClearExportPreview, exportPreviewDataUrl: controls.exportPreviewDataUrl, exportError: controls.exportError }),
10117
11972
  React.createElement("div", { style: { display: 'grid', gap: 12, marginBottom: 12 } },
10118
11973
  React.createElement("div", { style: { display: 'flex', flexWrap: 'wrap', gap: 12, alignItems: 'center' } },
10119
11974
  React.createElement("span", { style: __assign(__assign({}, badgeStyle), { background: statusTone, color: statusColor, borderColor: statusColor }) },
@@ -10205,6 +12060,8 @@ var parentOptions = [
10205
12060
  { id: 'layout-nested', label: 'Nested layout' },
10206
12061
  { id: 'layout-manual', label: 'Manual (compare)' },
10207
12062
  ];
12063
+ var shortLabel = 'Parent label lane demo';
12064
+ var longLabel = 'Parent label lane demo with longer content to increase flexible reserved space and push children downward.';
10208
12065
  var AutoLayoutDemo = function () {
10209
12066
  var demo = autoLayoutDemoConfig;
10210
12067
  var _a = useDemoEditor({
@@ -10234,7 +12091,11 @@ var AutoLayoutDemo = function () {
10234
12091
  var _k = useState(''), childFitMinHeight = _k[0], setChildFitMinHeight = _k[1];
10235
12092
  var _l = useState(''), childFitMaxWidth = _l[0], setChildFitMaxWidth = _l[1];
10236
12093
  var _m = useState(''), childFitMaxHeight = _m[0], setChildFitMaxHeight = _m[1];
10237
- var _o = useState('None yet'), lastTrigger = _o[0], setLastTrigger = _o[1];
12094
+ var _o = useState('none'), labelReservedMode = _o[0], setLabelReservedMode = _o[1];
12095
+ var _p = useState(32), labelReservedFixedSize = _p[0], setLabelReservedFixedSize = _p[1];
12096
+ var _q = useState(''), labelReservedMinSize = _q[0], setLabelReservedMinSize = _q[1];
12097
+ var _r = useState(''), labelReservedMaxSize = _r[0], setLabelReservedMaxSize = _r[1];
12098
+ var _s = useState('None yet'), lastTrigger = _s[0], setLastTrigger = _s[1];
10238
12099
  useEffect(function () {
10239
12100
  var editor = editorRef.current;
10240
12101
  if (!editor)
@@ -10294,7 +12155,7 @@ var AutoLayoutDemo = function () {
10294
12155
  return options;
10295
12156
  }, [diagramState, targetElement]);
10296
12157
  useEffect(function () {
10297
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
12158
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
10298
12159
  var parent = diagramState === null || diagramState === void 0 ? void 0 : diagramState.elements.find(function (el) { return el.id === targetId; });
10299
12160
  if (!parent)
10300
12161
  return;
@@ -10314,6 +12175,10 @@ var AutoLayoutDemo = function () {
10314
12175
  setChildFitMinHeight((_l = (_k = layout === null || layout === void 0 ? void 0 : layout.childFitMinSize) === null || _k === void 0 ? void 0 : _k.height) !== null && _l !== void 0 ? _l : '');
10315
12176
  setChildFitMaxWidth((_o = (_m = layout === null || layout === void 0 ? void 0 : layout.childFitMaxSize) === null || _m === void 0 ? void 0 : _m.width) !== null && _o !== void 0 ? _o : '');
10316
12177
  setChildFitMaxHeight((_q = (_p = layout === null || layout === void 0 ? void 0 : layout.childFitMaxSize) === null || _p === void 0 ? void 0 : _p.height) !== null && _q !== void 0 ? _q : '');
12178
+ setLabelReservedMode((_s = (_r = layout === null || layout === void 0 ? void 0 : layout.labelReservedSpace) === null || _r === void 0 ? void 0 : _r.mode) !== null && _s !== void 0 ? _s : 'none');
12179
+ setLabelReservedFixedSize((_u = (_t = layout === null || layout === void 0 ? void 0 : layout.labelReservedSpace) === null || _t === void 0 ? void 0 : _t.size) !== null && _u !== void 0 ? _u : 32);
12180
+ setLabelReservedMinSize((_w = (_v = layout === null || layout === void 0 ? void 0 : layout.labelReservedSpace) === null || _v === void 0 ? void 0 : _v.minSize) !== null && _w !== void 0 ? _w : '');
12181
+ setLabelReservedMaxSize((_y = (_x = layout === null || layout === void 0 ? void 0 : layout.labelReservedSpace) === null || _x === void 0 ? void 0 : _x.maxSize) !== null && _y !== void 0 ? _y : '');
10317
12182
  }, [diagramState, targetId]);
10318
12183
  var childOrder = useMemo(function () {
10319
12184
  var _a;
@@ -10350,6 +12215,9 @@ var AutoLayoutDemo = function () {
10350
12215
  var childFitMaxSize = childFitMaxWidth === '' && childFitMaxHeight === ''
10351
12216
  ? undefined
10352
12217
  : __assign(__assign({}, (childFitMaxWidth === '' ? {} : { width: childFitMaxWidth })), (childFitMaxHeight === '' ? {} : { height: childFitMaxHeight }));
12218
+ var labelReservedSpace = mode === 'manual'
12219
+ ? undefined
12220
+ : __assign(__assign(__assign({ mode: labelReservedMode, placement: 'top' }, (labelReservedMode === 'fixed' ? { size: labelReservedFixedSize } : {})), (labelReservedMinSize === '' ? {} : { minSize: labelReservedMinSize })), (labelReservedMaxSize === '' ? {} : { maxSize: labelReservedMaxSize }));
10353
12221
  var layout = mode === 'manual'
10354
12222
  ? { mode: 'manual' }
10355
12223
  : {
@@ -10361,10 +12229,21 @@ var AutoLayoutDemo = function () {
10361
12229
  childFitCrossAxis: childFitCrossAxis,
10362
12230
  childFitMinSize: childFitMinSize,
10363
12231
  childFitMaxSize: childFitMaxSize,
12232
+ labelReservedSpace: labelReservedSpace,
10364
12233
  };
10365
12234
  editor.setElementLayout(targetElement.id, layout);
10366
12235
  setLastTrigger("layout applied (".concat(mode, ")"));
10367
12236
  };
12237
+ var handleSetLabelContent = function (content) {
12238
+ var editor = editorRef.current;
12239
+ if (!editor || !diagramState || !targetElement)
12240
+ return;
12241
+ var label = diagramState.texts.find(function (text) { return text.ownerId === targetElement.id; });
12242
+ if (!label)
12243
+ return;
12244
+ editor.updateText(label.id, content);
12245
+ setLastTrigger('label updated');
12246
+ };
10368
12247
  var handleAddChild = function () {
10369
12248
  var _a;
10370
12249
  var editor = editorRef.current;
@@ -10399,7 +12278,7 @@ var AutoLayoutDemo = function () {
10399
12278
  React.createElement("div", { style: { marginBottom: 12 } },
10400
12279
  React.createElement("h2", { style: { marginTop: 0, marginBottom: 4 } }, demo.title),
10401
12280
  React.createElement("p", { style: { marginTop: 0 } }, "Try horizontal, vertical, nested, and manual layout containers. Select a layout parent directly on the canvas or pick it from the list; the controls below always act on the current selection. Drag, resize, add, or remove children to see automatic reflow. Use fit controls to distribute/stretch child sizes with optional min/max guards. Nested column shows fit propagation boundaries to its parent row.")),
10402
- React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction }),
12281
+ React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction, onExportImage: controls.handleExportImage, onClearExportPreview: controls.handleClearExportPreview, exportPreviewDataUrl: controls.exportPreviewDataUrl, exportError: controls.exportError }),
10403
12282
  React.createElement("div", { style: { display: 'grid', gap: 12, marginBottom: 12 } },
10404
12283
  React.createElement("div", { style: { display: 'flex', flexWrap: 'wrap', gap: 12, alignItems: 'center' } },
10405
12284
  React.createElement("label", { htmlFor: "parent-select", style: { fontWeight: 600 } }, "Target element (follows selection)"),
@@ -10440,6 +12319,16 @@ var AutoLayoutDemo = function () {
10440
12319
  React.createElement("div", { style: { display: 'inline-flex', gap: 6 } },
10441
12320
  React.createElement("input", { id: "fit-max-width-input", type: "number", value: childFitMaxWidth, onChange: function (event) { return setChildFitMaxWidth(event.target.value === '' ? '' : Math.max(0, Number(event.target.value) || 0)); }, style: { width: 64, padding: '6px 8px' }, min: 0, placeholder: "w" }),
10442
12321
  React.createElement("input", { type: "number", value: childFitMaxHeight, onChange: function (event) { return setChildFitMaxHeight(event.target.value === '' ? '' : Math.max(0, Number(event.target.value) || 0)); }, style: { width: 64, padding: '6px 8px' }, min: 0, placeholder: "h" })),
12322
+ React.createElement("label", { htmlFor: "label-reserved-mode-select", style: { fontWeight: 600 } }, "Label lane"),
12323
+ React.createElement("select", { id: "label-reserved-mode-select", value: labelReservedMode, onChange: function (event) { return setLabelReservedMode(event.target.value); }, style: { padding: '6px 10px', minWidth: 120 }, disabled: mode === 'manual' },
12324
+ React.createElement("option", { value: "none" }, "None"),
12325
+ React.createElement("option", { value: "fixed" }, "Fixed"),
12326
+ React.createElement("option", { value: "flexible" }, "Flexible")),
12327
+ React.createElement("label", { htmlFor: "label-reserved-fixed-input", style: { fontWeight: 600 } }, "Lane fixed/min/max"),
12328
+ React.createElement("div", { style: { display: 'inline-flex', gap: 6 } },
12329
+ React.createElement("input", { id: "label-reserved-fixed-input", type: "number", value: labelReservedFixedSize, onChange: function (event) { return setLabelReservedFixedSize(Math.max(0, Number(event.target.value) || 0)); }, style: { width: 70, padding: '6px 8px' }, min: 0, placeholder: "fixed", disabled: mode === 'manual' }),
12330
+ React.createElement("input", { type: "number", value: labelReservedMinSize, onChange: function (event) { return setLabelReservedMinSize(event.target.value === '' ? '' : Math.max(0, Number(event.target.value) || 0)); }, style: { width: 64, padding: '6px 8px' }, min: 0, placeholder: "min", disabled: mode === 'manual' }),
12331
+ React.createElement("input", { type: "number", value: labelReservedMaxSize, onChange: function (event) { return setLabelReservedMaxSize(event.target.value === '' ? '' : Math.max(0, Number(event.target.value) || 0)); }, style: { width: 64, padding: '6px 8px' }, min: 0, placeholder: "max", disabled: mode === 'manual' })),
10443
12332
  React.createElement("button", { type: "button", onClick: handleApplyLayout, style: { padding: '6px 12px', fontWeight: 600 } }, "Apply layout"),
10444
12333
  React.createElement("button", { type: "button", onClick: handleAddChild, style: { padding: '6px 10px' }, disabled: !targetElement }, "Add child"),
10445
12334
  React.createElement("button", { type: "button", onClick: handleRemoveChild, style: { padding: '6px 10px' }, disabled: !targetElement }, "Remove last child"),
@@ -10457,7 +12346,9 @@ var AutoLayoutDemo = function () {
10457
12346
  targetElement ? targetElement.id : '—',
10458
12347
  React.createElement("span", { style: { width: 6, height: 6, borderRadius: '50%', background: '#1f4d99', display: 'inline-block' } }),
10459
12348
  "Last trigger: ",
10460
- React.createElement("strong", null, lastTrigger))),
12349
+ React.createElement("strong", null, lastTrigger)),
12350
+ React.createElement("button", { type: "button", onClick: function () { return handleSetLabelContent(shortLabel); }, style: { padding: '6px 10px' }, disabled: !targetElement || mode === 'manual' }, "Label short"),
12351
+ React.createElement("button", { type: "button", onClick: function () { return handleSetLabelContent(longLabel); }, style: { padding: '6px 10px' }, disabled: !targetElement || mode === 'manual' }, "Label long")),
10461
12352
  React.createElement("div", { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 } },
10462
12353
  React.createElement("div", null,
10463
12354
  React.createElement("div", { style: { fontWeight: 600, marginBottom: 6 } }, "Child order (based on layout axis)"),
@@ -10480,6 +12371,7 @@ var AutoLayoutDemo = function () {
10480
12371
  React.createElement("ul", { style: { marginTop: 0, paddingLeft: 18, fontSize: 13 } },
10481
12372
  React.createElement("li", null, "Horizontal: try center vs bottom alignment and larger padding."),
10482
12373
  React.createElement("li", null, "Vertical: see parent grow taller as children stack."),
12374
+ React.createElement("li", null, "Label lane: compare none/fixed/flexible and observe children start below the reserved lane."),
10483
12375
  React.createElement("li", null, "Fit main-axis distribute fills inner layout space across siblings."),
10484
12376
  React.createElement("li", null, "Fit cross-axis stretch extends children across inner cross axis."),
10485
12377
  React.createElement("li", null, "Min/max fit guards cap distributed or stretched child sizes."),
@@ -10660,7 +12552,7 @@ var ExternalDragDropDemo = function () {
10660
12552
  React.createElement("div", { style: { marginBottom: 12 } },
10661
12553
  React.createElement("h2", { style: { marginTop: 0, marginBottom: 4 } }, demo.title),
10662
12554
  React.createElement("p", { style: { marginTop: 0 } }, demo.description)),
10663
- React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction }),
12555
+ React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction, onExportImage: controls.handleExportImage, onClearExportPreview: controls.handleClearExportPreview, exportPreviewDataUrl: controls.exportPreviewDataUrl, exportError: controls.exportError }),
10664
12556
  React.createElement("div", { style: { display: 'flex', gap: 16, marginBottom: 12, alignItems: 'stretch' } },
10665
12557
  React.createElement("div", { style: {
10666
12558
  minWidth: 220,
@@ -10817,7 +12709,7 @@ var SvgPathDemo = function () {
10817
12709
  React.createElement("div", { style: { marginBottom: 12 } },
10818
12710
  React.createElement("h2", { style: { marginTop: 0, marginBottom: 4 } }, demo.title),
10819
12711
  React.createElement("p", { style: { marginTop: 0 } }, demo.description)),
10820
- React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction }),
12712
+ React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction, onExportImage: controls.handleExportImage, onClearExportPreview: controls.handleClearExportPreview, exportPreviewDataUrl: controls.exportPreviewDataUrl, exportError: controls.exportError }),
10821
12713
  React.createElement("div", { style: {
10822
12714
  display: 'grid',
10823
12715
  gridTemplateColumns: 'minmax(0, 1fr) 200px',
@@ -10998,7 +12890,7 @@ var TextLayoutDemo = function () {
10998
12890
  React.createElement("div", { style: { marginBottom: 12 } },
10999
12891
  React.createElement("h2", { style: { marginTop: 0, marginBottom: 4 } }, demo.title),
11000
12892
  React.createElement("p", { style: { marginTop: 0 } }, "Full text content is preserved in state. Overflow and ellipsis settings only change visual output (`displayContent`) for rendering.")),
11001
- React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction }),
12893
+ React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction, onExportImage: controls.handleExportImage, onClearExportPreview: controls.handleClearExportPreview, exportPreviewDataUrl: controls.exportPreviewDataUrl, exportError: controls.exportError }),
11002
12894
  React.createElement("div", { style: { display: 'grid', gap: 10, marginBottom: 12 } },
11003
12895
  React.createElement("div", { style: { display: 'flex', flexWrap: 'wrap', gap: 10, alignItems: 'center' } },
11004
12896
  React.createElement("label", { htmlFor: "text-target", style: { fontWeight: 600 } }, "Target"),
@@ -11122,7 +13014,7 @@ var SimpleDemo = function (_a) {
11122
13014
  React.createElement("div", { style: { marginBottom: 12 } },
11123
13015
  React.createElement("h2", { style: { marginTop: 0, marginBottom: 4 } }, demo.title),
11124
13016
  React.createElement("p", { style: { marginTop: 0 } }, demo.description)),
11125
- React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction }),
13017
+ React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction, onExportImage: controls.handleExportImage, onClearExportPreview: controls.handleClearExportPreview, exportPreviewDataUrl: controls.exportPreviewDataUrl, exportError: controls.exportError }),
11126
13018
  beforeStage,
11127
13019
  React.createElement(DisplayBoxStage, { containerRef: containerRef, onDragOver: stageHandlers === null || stageHandlers === void 0 ? void 0 : stageHandlers.onDragOver, onDragLeave: stageHandlers === null || stageHandlers === void 0 ? void 0 : stageHandlers.onDragLeave, onDrop: stageHandlers === null || stageHandlers === void 0 ? void 0 : stageHandlers.onDrop, stageStyle: demo.stageStyle })));
11128
13020
  };
@@ -11136,10 +13028,11 @@ var ObstacleRoutingDemo = function () { return (React.createElement(SimpleDemo,
11136
13028
  } },
11137
13029
  React.createElement("strong", null, "Expected routing behavior"),
11138
13030
  React.createElement("ul", { style: { margin: '6px 0 0 16px' } },
11139
- React.createElement("li", null, "Links between top nodes should route around the blocking element."),
11140
- React.createElement("li", null, "Sibling links inside a shared parent should ignore the parent as an obstacle."),
11141
- React.createElement("li", null, "Parent-child links should stay within the parent and avoid the child interior when ports are on edges."),
11142
- React.createElement("li", null, "Grandchild links should ignore shared ancestors (parent + grandparent containers)."))) })); };
13031
+ React.createElement("li", null, "Scenario A: the sibling link must not cross either child host interior."),
13032
+ React.createElement("li", null, "Scenario A: sibling host ports use anchor preset `cardinal` (left/right) for deterministic multi-anchor placement."),
13033
+ React.createElement("li", null, "Scenario B has one parent and two children with three links: parent->child, child->parent, and child->child."),
13034
+ React.createElement("li", null, "Scenario B: ancestor endpoints resolve to `internalLinkAttachPoint`; descendant/sibling endpoints resolve to `externalLinkAttachPoint`."),
13035
+ React.createElement("li", null, "Use `Reroute All Links` repeatedly; both routes should stay deterministic after each reroute."))) })); };
11143
13036
 
11144
13037
  var initialPayloads = {
11145
13038
  elementDeleted: null,
@@ -11204,7 +13097,7 @@ var DeletionEventsDemo = function () {
11204
13097
  "Use direct delete buttons for port/link/text, then delete ",
11205
13098
  React.createElement("code", null, "delete-source"),
11206
13099
  " to trigger cascade removals. Latest payloads are shown below for integration debugging.")),
11207
- React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction }),
13100
+ React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction, onExportImage: controls.handleExportImage, onClearExportPreview: controls.handleClearExportPreview, exportPreviewDataUrl: controls.exportPreviewDataUrl, exportError: controls.exportError }),
11208
13101
  React.createElement("div", { style: { display: 'grid', gap: 12, marginBottom: 12 } },
11209
13102
  React.createElement("div", { style: { display: 'flex', flexWrap: 'wrap', gap: 8 } },
11210
13103
  React.createElement("button", { type: "button", onClick: function () { var _a; return (_a = editorRef.current) === null || _a === void 0 ? void 0 : _a.removePort('delete-source-free-port'); }, disabled: !hasPort }, "Delete Port"),
@@ -11510,7 +13403,7 @@ var ShapeHoverControlsDemo = function () {
11510
13403
  React.createElement("div", { style: { marginBottom: 12 } },
11511
13404
  React.createElement("h2", { style: { marginTop: 0, marginBottom: 4 } }, demo.title),
11512
13405
  React.createElement("p", { style: { marginTop: 0 } }, demo.description)),
11513
- React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction }),
13406
+ React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction, onExportImage: controls.handleExportImage, onClearExportPreview: controls.handleClearExportPreview, exportPreviewDataUrl: controls.exportPreviewDataUrl, exportError: controls.exportError }),
11514
13407
  React.createElement("div", { style: {
11515
13408
  marginBottom: 12,
11516
13409
  padding: '10px 12px',
@@ -11597,6 +13490,414 @@ var EllipseMidPointControlSection = function (_a) {
11597
13490
  midPoint)); })))));
11598
13491
  };
11599
13492
 
13493
+ var sidePositions = {
13494
+ top: { x: 112, y: 0 },
13495
+ right: { x: 280, y: 68 },
13496
+ bottom: { x: 182, y: 180 },
13497
+ left: { x: 0, y: 130 },
13498
+ };
13499
+ var sideLabels = ['top', 'right', 'bottom', 'left'];
13500
+ var resolvePortSide = function (port, host) {
13501
+ if (Math.abs(port.position.x) <= 0.001)
13502
+ return 'left';
13503
+ if (Math.abs(port.position.x - host.size.width) <= 0.001)
13504
+ return 'right';
13505
+ if (Math.abs(port.position.y) <= 0.001)
13506
+ return 'top';
13507
+ if (Math.abs(port.position.y - host.size.height) <= 0.001)
13508
+ return 'bottom';
13509
+ return null;
13510
+ };
13511
+ var sideToVector = function (side) {
13512
+ if (side === 'left')
13513
+ return { x: -1, y: 0 };
13514
+ if (side === 'right')
13515
+ return { x: 1, y: 0 };
13516
+ if (side === 'top')
13517
+ return { x: 0, y: -1 };
13518
+ return { x: 0, y: 1 };
13519
+ };
13520
+ var AsymmetricPortMultiAnchorDemo = function () {
13521
+ var demo = asymmetricPortMultiAnchorDemoConfig;
13522
+ var _a = useState(true), showLegacyComparison = _a[0], setShowLegacyComparison = _a[1];
13523
+ var _b = useState(false), showExpectedAnchors = _b[0], setShowExpectedAnchors = _b[1];
13524
+ var _c = useState(asymmetricPortDefaultVariantId), selectedVariantId = _c[0], setSelectedVariantId = _c[1];
13525
+ var overlayHandlesRef = useRef([]);
13526
+ var selectedVariant = useMemo(function () { return resolveAsymmetricPortShapeVariant(selectedVariantId); }, [selectedVariantId]);
13527
+ var attachDistance = selectedVariant.externalLinkAttachPoint.x - selectedVariant.placementPoint.x;
13528
+ var createState = useCallback(function () { return createAsymmetricPortMultiAnchorState(showLegacyComparison, selectedVariantId); }, [showLegacyComparison, selectedVariantId]);
13529
+ var _d = useDemoEditor({
13530
+ createState: createState,
13531
+ elementShapes: demo.elementShapes,
13532
+ portShapes: demo.portShapes,
13533
+ }), containerRef = _d.containerRef, editorRef = _d.editorRef, diagramState = _d.diagramState, selection = _d.selection, snapEnabled = _d.snapEnabled, setSnapEnabled = _d.setSnapEnabled;
13534
+ var nextOffset = useOffsetSequence();
13535
+ var actionHelpers = useMemo(function () { return ({ nextOffset: nextOffset }); }, [nextOffset]);
13536
+ var controls = useDemoControls({
13537
+ demo: demo,
13538
+ editorRef: editorRef,
13539
+ diagramState: diagramState,
13540
+ selection: selection,
13541
+ snapEnabled: snapEnabled,
13542
+ setSnapEnabled: setSnapEnabled,
13543
+ actionHelpers: actionHelpers,
13544
+ });
13545
+ useEffect(function () {
13546
+ var editor = editorRef.current;
13547
+ if (!editor)
13548
+ return;
13549
+ asymmetricPortShapeVariants.forEach(function (variant) {
13550
+ editor.registerShape({
13551
+ id: variant.shapeId,
13552
+ baseRotation: 90,
13553
+ svgPath: variant.svgPath,
13554
+ svgSize: variant.svgSize,
13555
+ });
13556
+ });
13557
+ editor.rerouteAllLinks();
13558
+ }, [editorRef, showLegacyComparison, selectedVariantId]);
13559
+ useEffect(function () {
13560
+ var editor = editorRef.current;
13561
+ var state = diagramState;
13562
+ overlayHandlesRef.current.forEach(function (handle) { return handle.destroy(); });
13563
+ overlayHandlesRef.current = [];
13564
+ if (!showExpectedAnchors || !editor || !state)
13565
+ return;
13566
+ var hosts = new Map(state.elements.map(function (element) { return [element.id, element]; }));
13567
+ var debugPorts = state.ports.filter(function (port) { return port.id.startsWith('multi-anchor-port-'); });
13568
+ debugPorts.forEach(function (port) {
13569
+ var host = hosts.get(port.elementId);
13570
+ if (!host)
13571
+ return;
13572
+ var side = resolvePortSide(port, host);
13573
+ if (!side)
13574
+ return;
13575
+ var vector = sideToVector(side);
13576
+ var placementWorld = {
13577
+ x: host.position.x + port.position.x,
13578
+ y: host.position.y + port.position.y,
13579
+ };
13580
+ overlayHandlesRef.current.push(editor.createOverlayShape({
13581
+ shapeId: 'default',
13582
+ position: placementWorld,
13583
+ size: { width: 8, height: 8 },
13584
+ anchorCenter: true,
13585
+ style: {
13586
+ fill: '#ffffff',
13587
+ stroke: '#d97706',
13588
+ strokeWidth: 2,
13589
+ cornerRadius: 1,
13590
+ opacity: 0.9,
13591
+ },
13592
+ }));
13593
+ if (port.id === multiAnchorExternalPortId || port.id.endsWith('-port-top') || port.id.endsWith('-port-bottom')) {
13594
+ overlayHandlesRef.current.push(editor.createOverlayShape({
13595
+ shapeId: 'port-dark',
13596
+ position: {
13597
+ x: placementWorld.x + vector.x * attachDistance,
13598
+ y: placementWorld.y + vector.y * attachDistance,
13599
+ },
13600
+ size: { width: 8, height: 8 },
13601
+ anchorCenter: true,
13602
+ style: {
13603
+ fill: '#dc2626',
13604
+ stroke: '#dc2626',
13605
+ opacity: 0.85,
13606
+ },
13607
+ }));
13608
+ }
13609
+ });
13610
+ return function () {
13611
+ overlayHandlesRef.current.forEach(function (handle) { return handle.destroy(); });
13612
+ overlayHandlesRef.current = [];
13613
+ };
13614
+ }, [diagramState, editorRef, showExpectedAnchors]);
13615
+ var moveExternalPortToSide = function (side) {
13616
+ var editor = editorRef.current;
13617
+ var host = diagramState === null || diagramState === void 0 ? void 0 : diagramState.elements.find(function (element) { return element.id === multiAnchorHostId; });
13618
+ if (!editor || !host)
13619
+ return;
13620
+ var local = sidePositions[side];
13621
+ editor.movePortTo(multiAnchorExternalPortId, host.position.x + local.x, host.position.y + local.y);
13622
+ editor.setSelection([multiAnchorExternalPortId]);
13623
+ };
13624
+ return (React.createElement("section", null,
13625
+ React.createElement("div", { style: { marginBottom: 12 } },
13626
+ React.createElement("h2", { style: { marginTop: 0, marginBottom: 4 } }, demo.title),
13627
+ React.createElement("p", { style: { marginTop: 0, marginBottom: 8 } }, demo.description),
13628
+ React.createElement("p", { style: { marginTop: 0, marginBottom: 0, fontSize: 13, color: '#475569' } }, "The custom `svgPath` glyph is intentionally non-centered. The multi-anchor row keeps the square pivot pinned to the border while links use the circle for external targets and the square pivot for parent-child internal links."),
13629
+ React.createElement("p", { style: { marginTop: 8, marginBottom: 0, fontSize: 13, color: '#475569' } },
13630
+ "Active glyph variant: ",
13631
+ React.createElement("strong", null, selectedVariant.label),
13632
+ " (",
13633
+ selectedVariant.description,
13634
+ ")")),
13635
+ React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction, onExportImage: controls.handleExportImage, onClearExportPreview: controls.handleClearExportPreview, exportPreviewDataUrl: controls.exportPreviewDataUrl, exportError: controls.exportError }),
13636
+ React.createElement("div", { style: {
13637
+ display: 'grid',
13638
+ gridTemplateColumns: 'minmax(0, 1fr) auto',
13639
+ gap: 12,
13640
+ alignItems: 'start',
13641
+ marginBottom: 12,
13642
+ padding: 12,
13643
+ border: '1px solid #d9e2f0',
13644
+ borderRadius: 8,
13645
+ background: '#f8fbff',
13646
+ } },
13647
+ React.createElement("div", { style: { display: 'grid', gap: 8 } },
13648
+ React.createElement("label", { htmlFor: "legacy-comparison-toggle", style: { display: 'flex', alignItems: 'center', gap: 8, fontWeight: 600 } },
13649
+ React.createElement("input", { id: "legacy-comparison-toggle", type: "checkbox", checked: showLegacyComparison, onChange: function (event) { return setShowLegacyComparison(event.target.checked); } }),
13650
+ "Show legacy/default comparison"),
13651
+ React.createElement("label", { htmlFor: "expected-anchor-toggle", style: { display: 'flex', alignItems: 'center', gap: 8, fontWeight: 600 } },
13652
+ React.createElement("input", { id: "expected-anchor-toggle", type: "checkbox", checked: showExpectedAnchors, onChange: function (event) { return setShowExpectedAnchors(event.target.checked); } }),
13653
+ "Show expected anchor markers"),
13654
+ React.createElement("label", { htmlFor: "asymmetric-variant-select", style: { display: 'flex', alignItems: 'center', gap: 8, fontWeight: 600 } },
13655
+ "Port glyph variant",
13656
+ React.createElement("select", { id: "asymmetric-variant-select", value: selectedVariantId, onChange: function (event) { return setSelectedVariantId(event.target.value); } }, asymmetricPortShapeVariants.map(function (variant) { return (React.createElement("option", { key: variant.id, value: variant.id }, variant.label)); }))),
13657
+ React.createElement("div", { style: { fontSize: 13, color: '#334155' } }, "Drag any asymmetric port around the host border or use the side controls to verify top/right/bottom/left orientation."),
13658
+ React.createElement("div", { style: { fontSize: 13, color: '#334155' } }, "Quick visual check: the multi-anchor row should keep the square touching the border; the legacy row may shift because it has no explicit placement or pivot anchors."),
13659
+ React.createElement("div", { style: { fontSize: 13, color: '#334155' } }, "Debug markers: amber square = expected border placement point, red dot = expected external attach point.")),
13660
+ React.createElement("div", { style: { display: 'grid', gap: 8 } },
13661
+ React.createElement("div", { style: { fontSize: 12, fontWeight: 600, color: '#1e293b' } }, "Move the external demo port"),
13662
+ React.createElement("div", { style: { display: 'flex', flexWrap: 'wrap', gap: 8, justifyContent: 'flex-end' } }, sideLabels.map(function (side) { return (React.createElement("button", { key: side, type: "button", onClick: function () { return moveExternalPortToSide(side); }, style: { padding: '6px 10px' } },
13663
+ "External \u2192 ",
13664
+ side[0].toUpperCase() + side.slice(1))); })))),
13665
+ React.createElement(DisplayBoxStage, { containerRef: containerRef })));
13666
+ };
13667
+
13668
+ var VERTEX_CONTROL_ID = 'vertex-session-link';
13669
+ var VERTEX_CONTROL_SVG = 'M2 8 H14 M8 2 V14';
13670
+ var pointInRect = function (point, rect) {
13671
+ return point.x >= rect.x &&
13672
+ point.x <= rect.x + rect.width &&
13673
+ point.y >= rect.y &&
13674
+ point.y <= rect.y + rect.height;
13675
+ };
13676
+ var resolveTargetFromPointer = function (state, pointer, sourcePortId) {
13677
+ var closestPort = null;
13678
+ var _loop_1 = function (port) {
13679
+ if (port.id === sourcePortId)
13680
+ return "continue";
13681
+ var host = state.elements.find(function (element) { return element.id === port.elementId; });
13682
+ if (!host)
13683
+ return "continue";
13684
+ var world = {
13685
+ x: host.position.x + port.position.x,
13686
+ y: host.position.y + port.position.y,
13687
+ };
13688
+ var distance = Math.hypot(world.x - pointer.x, world.y - pointer.y);
13689
+ if (distance > 16)
13690
+ return "continue";
13691
+ if (!closestPort || distance < closestPort.distance) {
13692
+ closestPort = { id: port.id, distance: distance };
13693
+ }
13694
+ };
13695
+ for (var _i = 0, _a = state.ports; _i < _a.length; _i++) {
13696
+ var port = _a[_i];
13697
+ _loop_1(port);
13698
+ }
13699
+ if (closestPort) {
13700
+ return { type: 'port', id: closestPort.id };
13701
+ }
13702
+ var targetOrder = [vertexSessionDemoIds.elementTarget, vertexSessionDemoIds.portTarget];
13703
+ var _loop_2 = function (i) {
13704
+ var element = state.elements.find(function (item) { return item.id === targetOrder[i]; });
13705
+ if (!element)
13706
+ return "continue";
13707
+ if (pointInRect(pointer, {
13708
+ x: element.position.x,
13709
+ y: element.position.y,
13710
+ width: element.size.width,
13711
+ height: element.size.height,
13712
+ })) {
13713
+ return { value: { type: 'element', id: element.id } };
13714
+ }
13715
+ };
13716
+ for (var i = 0; i < targetOrder.length; i += 1) {
13717
+ var state_1 = _loop_2(i);
13718
+ if (typeof state_1 === "object")
13719
+ return state_1.value;
13720
+ }
13721
+ return { type: 'none' };
13722
+ };
13723
+ var createVertexSessionSourcePort = function (editor, sourcePoint) {
13724
+ var portId = "vertex-session-source-".concat(createId());
13725
+ var port = {
13726
+ id: portId,
13727
+ elementId: vertexSessionDemoIds.source,
13728
+ position: { x: 0, y: 0 },
13729
+ shapeId: 'port-dark',
13730
+ moveMode: 'border',
13731
+ anchorCenter: true,
13732
+ orientToHostBorder: true,
13733
+ };
13734
+ editor.addPortToElement(vertexSessionDemoIds.source, port);
13735
+ editor.movePortTo(portId, sourcePoint.x, sourcePoint.y);
13736
+ return portId;
13737
+ };
13738
+ var vertexControls = {
13739
+ controls: [
13740
+ {
13741
+ id: VERTEX_CONTROL_ID,
13742
+ targetKind: 'vertex',
13743
+ allowAllTargets: true,
13744
+ visibilityTriggers: ['target-hover'],
13745
+ tolerance: 14,
13746
+ icon: {
13747
+ svgPath: VERTEX_CONTROL_SVG,
13748
+ size: { width: 16, height: 16 },
13749
+ style: { fill: 'none', stroke: '#6a3da3', strokeWidth: 2, lineCap: 'round' },
13750
+ },
13751
+ },
13752
+ ],
13753
+ };
13754
+ var VertexControlLinkSessionDemo = function () {
13755
+ var _a, _b;
13756
+ var demo = vertexControlLinkSessionDemoConfig;
13757
+ var editorHandleRef = useRef(null);
13758
+ var activeSourcePortRef = useRef(null);
13759
+ var _c = useState(false), cancelOnConnect = _c[0], setCancelOnConnect = _c[1];
13760
+ var _d = useState([]), eventLog = _d[0], setEventLog = _d[1];
13761
+ var _e = useState('none'), lastResolution = _e[0], setLastResolution = _e[1];
13762
+ var _f = useState(false), isLogExpanded = _f[0], setIsLogExpanded = _f[1];
13763
+ var nextOffset = useOffsetSequence();
13764
+ var actionHelpers = useMemo(function () { return ({ nextOffset: nextOffset }); }, [nextOffset]);
13765
+ var onControlInteraction = useCallback(function (event) {
13766
+ var _a, _b;
13767
+ if (event.controlId !== VERTEX_CONTROL_ID || event.elementId !== vertexSessionDemoIds.source)
13768
+ return;
13769
+ var editor = editorHandleRef.current;
13770
+ if (!editor)
13771
+ return;
13772
+ if (event.eventType === 'drag-start') {
13773
+ var sourcePoint = (_b = (_a = event.vertex) === null || _a === void 0 ? void 0 : _a.position) !== null && _b !== void 0 ? _b : event.pointer.world;
13774
+ var sourcePortId = createVertexSessionSourcePort(editor, sourcePoint);
13775
+ activeSourcePortRef.current = sourcePortId;
13776
+ editor.startLinkFromPort(sourcePortId, sourcePoint);
13777
+ setLastResolution('started');
13778
+ return;
13779
+ }
13780
+ if (event.eventType === 'drag-move') {
13781
+ if (!activeSourcePortRef.current)
13782
+ return;
13783
+ editor.updateLinkPreview(event.pointer.world);
13784
+ return;
13785
+ }
13786
+ if (event.eventType === 'drag-end') {
13787
+ var sourcePortId = activeSourcePortRef.current;
13788
+ if (!sourcePortId)
13789
+ return;
13790
+ var state = editor.getState();
13791
+ var target = resolveTargetFromPointer(state, event.pointer.world, sourcePortId);
13792
+ if (target.type === 'port') {
13793
+ editor.completeLinkToPort(target.id);
13794
+ setLastResolution("completeLinkToPort(".concat(target.id, ")"));
13795
+ }
13796
+ else if (target.type === 'element') {
13797
+ editor.completeLinkToElement(target.id, event.pointer.world);
13798
+ setLastResolution("completeLinkToElement(".concat(target.id, ")"));
13799
+ }
13800
+ else {
13801
+ editor.cancelLink();
13802
+ setLastResolution('cancelLink()');
13803
+ }
13804
+ activeSourcePortRef.current = null;
13805
+ }
13806
+ }, []);
13807
+ var _g = useDemoEditor({
13808
+ createState: demo.createState,
13809
+ elementShapes: demo.elementShapes,
13810
+ portShapes: demo.portShapes,
13811
+ elementShapeHoverControls: vertexControls,
13812
+ onElementShapeHoverControlInteraction: onControlInteraction,
13813
+ }), containerRef = _g.containerRef, editorRef = _g.editorRef, diagramState = _g.diagramState, selection = _g.selection, snapEnabled = _g.snapEnabled, setSnapEnabled = _g.setSnapEnabled;
13814
+ editorHandleRef.current = editorRef.current;
13815
+ var controls = useDemoControls({
13816
+ demo: demo,
13817
+ editorRef: editorRef,
13818
+ diagramState: diagramState,
13819
+ selection: selection,
13820
+ snapEnabled: snapEnabled,
13821
+ setSnapEnabled: setSnapEnabled,
13822
+ actionHelpers: actionHelpers,
13823
+ });
13824
+ useEffect(function () {
13825
+ var editor = editorRef.current;
13826
+ if (!editor)
13827
+ return undefined;
13828
+ var append = function (entry) {
13829
+ setEventLog(function (prev) { return __spreadArray([entry], prev, true).slice(0, 16); });
13830
+ };
13831
+ var offStarted = editor.on('elementLinkStarted', function (payload) {
13832
+ append("elementLinkStarted source=".concat(payload.sourcePortId, " start=(").concat(Math.round(payload.startWorld.x), ",").concat(Math.round(payload.startWorld.y), ")"));
13833
+ });
13834
+ var offConnecting = editor.on('elementLinkConnecting', function (payload) {
13835
+ if (cancelOnConnect) {
13836
+ payload.cancel();
13837
+ }
13838
+ append("elementLinkConnecting source=".concat(payload.sourcePortId, " target=").concat(payload.targetPortId, " cancelled=").concat(payload.cancelled || cancelOnConnect));
13839
+ });
13840
+ var offEnded = editor.on('elementLinkEnded', function (payload) {
13841
+ var _a, _b;
13842
+ append("elementLinkEnded source=".concat(payload.sourcePortId, " targetPort=").concat((_a = payload.targetPortId) !== null && _a !== void 0 ? _a : '-', " targetElement=").concat((_b = payload.targetElementId) !== null && _b !== void 0 ? _b : '-', " cancelled=").concat(payload.cancelled));
13843
+ });
13844
+ return function () {
13845
+ offStarted();
13846
+ offConnecting();
13847
+ offEnded();
13848
+ };
13849
+ }, [editorRef, cancelOnConnect]);
13850
+ var linksCount = (_a = diagramState === null || diagramState === void 0 ? void 0 : diagramState.links.length) !== null && _a !== void 0 ? _a : 0;
13851
+ var portsCount = (_b = diagramState === null || diagramState === void 0 ? void 0 : diagramState.ports.length) !== null && _b !== void 0 ? _b : 0;
13852
+ var handleManualCancel = function () {
13853
+ var _a;
13854
+ (_a = editorRef.current) === null || _a === void 0 ? void 0 : _a.cancelLink();
13855
+ activeSourcePortRef.current = null;
13856
+ setLastResolution('cancelLink()');
13857
+ };
13858
+ return (React.createElement("section", null,
13859
+ React.createElement("div", { style: { marginBottom: 12 } },
13860
+ React.createElement("h2", { style: { marginTop: 0, marginBottom: 4 } }, demo.title),
13861
+ React.createElement("p", { style: { marginTop: 0 } }, demo.description)),
13862
+ React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction, onExportImage: controls.handleExportImage, onClearExportPreview: controls.handleClearExportPreview, exportPreviewDataUrl: controls.exportPreviewDataUrl, exportError: controls.exportError }),
13863
+ React.createElement("div", { style: { marginBottom: 12, padding: 12, border: '1px solid #d9e1ec', borderRadius: 8, background: '#fbfdff' } },
13864
+ React.createElement("div", { style: { fontWeight: 700, marginBottom: 6 } }, "Required scenarios"),
13865
+ React.createElement("ol", { style: { marginTop: 0, marginBottom: 10, paddingLeft: 20, fontSize: 13 } },
13866
+ React.createElement("li", null, "Drag source vertex control to the existing port on the left side of Scenario 1 target."),
13867
+ React.createElement("li", null, "Drag source vertex control to Scenario 2 target body (not its border port)."),
13868
+ React.createElement("li", null, "Enable host cancellation, then repeat a completion drag to verify cancelled end event."),
13869
+ React.createElement("li", null, "Start a drag and press Cancel Active Session to verify temp visuals clear and cancelled end event."),
13870
+ React.createElement("li", null, "Use baseline native ports (bottom row) and drag from one port to the other to verify no regression.")),
13871
+ React.createElement("div", { style: { display: 'flex', flexWrap: 'wrap', gap: 12, alignItems: 'center', marginBottom: 8 } },
13872
+ React.createElement("label", { style: { fontSize: 13 } },
13873
+ React.createElement("input", { type: "checkbox", checked: cancelOnConnect, onChange: function (event) { return setCancelOnConnect(event.target.checked); }, style: { marginRight: 6 } }),
13874
+ "Host cancels on elementLinkConnecting"),
13875
+ React.createElement("button", { type: "button", onClick: handleManualCancel, style: { padding: '6px 10px' } }, "Cancel Active Session"),
13876
+ React.createElement("button", { type: "button", onClick: function () { return setIsLogExpanded(function (prev) { return !prev; }); }, style: { padding: '6px 10px' } }, isLogExpanded ? 'Collapse Lifecycle Log' : 'Expand Lifecycle Log'),
13877
+ React.createElement("span", { style: { fontSize: 13 } },
13878
+ "Ports: ",
13879
+ React.createElement("strong", null, portsCount),
13880
+ " | Links: ",
13881
+ React.createElement("strong", null, linksCount),
13882
+ " | Last resolution: ",
13883
+ React.createElement("strong", null, lastResolution))),
13884
+ React.createElement("div", { style: { fontSize: 12, color: '#2d3a4d' } },
13885
+ React.createElement("strong", null, "Lifecycle log:"),
13886
+ " ",
13887
+ isLogExpanded ? 'expanded' : 'collapsed',
13888
+ isLogExpanded ? (React.createElement("pre", { style: {
13889
+ marginTop: 6,
13890
+ padding: 8,
13891
+ maxHeight: 170,
13892
+ overflow: 'auto',
13893
+ background: '#ffffff',
13894
+ border: '1px solid #d9e1ec',
13895
+ borderRadius: 6,
13896
+ whiteSpace: 'pre-wrap',
13897
+ } }, eventLog.length ? eventLog.join('\n') : 'No events yet')) : null)),
13898
+ React.createElement(DisplayBoxStage, { containerRef: containerRef, stageStyle: gridStageStyle })));
13899
+ };
13900
+
11600
13901
  var wrapSimpleDemo = function (demo) { return function () { return React.createElement(SimpleDemo, { demo: demo }); }; };
11601
13902
  var demoTabs = [
11602
13903
  {
@@ -11695,6 +13996,12 @@ var demoTabs = [
11695
13996
  description: svgPathDemoConfig.description,
11696
13997
  Component: SvgPathDemo,
11697
13998
  },
13999
+ {
14000
+ id: asymmetricPortMultiAnchorDemoConfig.id,
14001
+ title: asymmetricPortMultiAnchorDemoConfig.title,
14002
+ description: asymmetricPortMultiAnchorDemoConfig.description,
14003
+ Component: AsymmetricPortMultiAnchorDemo,
14004
+ },
11698
14005
  {
11699
14006
  id: multiLevelTreeDemoConfig.id,
11700
14007
  title: multiLevelTreeDemoConfig.title,
@@ -11713,6 +14020,18 @@ var demoTabs = [
11713
14020
  description: portConstraintsDemoConfig.description,
11714
14021
  Component: wrapSimpleDemo(portConstraintsDemoConfig),
11715
14022
  },
14023
+ {
14024
+ id: portPositionLimitsDemoConfig.id,
14025
+ title: portPositionLimitsDemoConfig.title,
14026
+ description: portPositionLimitsDemoConfig.description,
14027
+ Component: wrapSimpleDemo(portPositionLimitsDemoConfig),
14028
+ },
14029
+ {
14030
+ id: labelStyleDemoConfig.id,
14031
+ title: labelStyleDemoConfig.title,
14032
+ description: labelStyleDemoConfig.description,
14033
+ Component: wrapSimpleDemo(labelStyleDemoConfig),
14034
+ },
11716
14035
  {
11717
14036
  id: portBorderDemoConfig.id,
11718
14037
  title: portBorderDemoConfig.title,
@@ -11731,6 +14050,12 @@ var demoTabs = [
11731
14050
  description: shapeHoverControlsDemoConfig.description,
11732
14051
  Component: ShapeHoverControlsDemo,
11733
14052
  },
14053
+ {
14054
+ id: vertexControlLinkSessionDemoConfig.id,
14055
+ title: vertexControlLinkSessionDemoConfig.title,
14056
+ description: vertexControlLinkSessionDemoConfig.description,
14057
+ Component: VertexControlLinkSessionDemo,
14058
+ },
11734
14059
  {
11735
14060
  id: childConstraintsDemoConfig.id,
11736
14061
  title: childConstraintsDemoConfig.title,