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