orcasvn-react-diagrams 0.2.3 → 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 (32) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +15 -11
  3. package/dist/cjs/examples.js +857 -139
  4. package/dist/cjs/index.js +408 -41
  5. package/dist/cjs/types/api/types.d.ts +9 -0
  6. package/dist/cjs/types/displaybox/demos/labelStyleDemo.d.ts +2 -0
  7. package/dist/cjs/types/displaybox/demos/portPositionLimitsDemo.d.ts +2 -0
  8. package/dist/cjs/types/engine/DiagramEngine.d.ts +6 -0
  9. package/dist/cjs/types/engine/LinkRoutingService.d.ts +1 -1
  10. package/dist/cjs/types/renderer/konva/KonvaNodeFactory.d.ts +11 -0
  11. package/dist/cjs/types/renderer/konva/KonvaRenderer.d.ts +2 -0
  12. package/dist/cjs/types/strategies/ObstacleRouter.d.ts +2 -0
  13. package/dist/esm/examples.js +857 -139
  14. package/dist/esm/examples.js.map +1 -1
  15. package/dist/esm/index.js +408 -41
  16. package/dist/esm/index.js.map +1 -1
  17. package/dist/esm/types/api/types.d.ts +9 -0
  18. package/dist/esm/types/displaybox/demos/labelStyleDemo.d.ts +2 -0
  19. package/dist/esm/types/displaybox/demos/portPositionLimitsDemo.d.ts +2 -0
  20. package/dist/esm/types/engine/DiagramEngine.d.ts +6 -0
  21. package/dist/esm/types/engine/LinkRoutingService.d.ts +1 -1
  22. package/dist/esm/types/renderer/konva/KonvaNodeFactory.d.ts +11 -0
  23. package/dist/esm/types/renderer/konva/KonvaRenderer.d.ts +2 -0
  24. package/dist/esm/types/strategies/ObstacleRouter.d.ts +2 -0
  25. package/dist/examples.d.ts +9 -0
  26. package/dist/index.d.ts +10 -1
  27. package/package.json +11 -11
  28. package/src/displaybox/demos/ObstacleRoutingDemoTab.tsx +11 -10
  29. package/src/displaybox/demos/index.tsx +27 -13
  30. package/src/displaybox/demos/labelStyleDemo.ts +101 -0
  31. package/src/displaybox/demos/obstacleRoutingDemo.ts +212 -176
  32. package/src/displaybox/demos/portPositionLimitsDemo.ts +211 -0
@@ -1971,6 +1971,213 @@ var portConstraintsDemoConfig = ({
1971
1971
  ],
1972
1972
  });
1973
1973
 
1974
+ var createPortPositionLimitsState = function () { return ({
1975
+ elements: [
1976
+ {
1977
+ id: 'limit-left-host',
1978
+ position: { x: 60, y: 120 },
1979
+ size: { width: 220, height: 140 },
1980
+ shapeId: 'panel',
1981
+ portMovement: {
1982
+ moveMode: 'inside',
1983
+ positionLimits: {
1984
+ x: { max: 70 },
1985
+ },
1986
+ },
1987
+ },
1988
+ {
1989
+ id: 'limit-right-host',
1990
+ position: { x: 330, y: 120 },
1991
+ size: { width: 220, height: 140 },
1992
+ shapeId: 'panel',
1993
+ portMovement: {
1994
+ moveMode: 'inside',
1995
+ positionLimits: {
1996
+ x: { min: 150 },
1997
+ },
1998
+ },
1999
+ },
2000
+ {
2001
+ id: 'limit-top-host',
2002
+ position: { x: 600, y: 120 },
2003
+ size: { width: 220, height: 140 },
2004
+ shapeId: 'panel',
2005
+ portMovement: {
2006
+ moveMode: 'inside',
2007
+ positionLimits: {
2008
+ y: { max: 45 },
2009
+ },
2010
+ },
2011
+ },
2012
+ {
2013
+ id: 'limit-bottom-host',
2014
+ position: { x: 870, y: 120 },
2015
+ size: { width: 220, height: 140 },
2016
+ shapeId: 'panel',
2017
+ portMovement: {
2018
+ moveMode: 'inside',
2019
+ positionLimits: {
2020
+ y: { min: 90 },
2021
+ },
2022
+ },
2023
+ },
2024
+ {
2025
+ id: 'border-left-host',
2026
+ position: { x: 200, y: 350 },
2027
+ size: { width: 220, height: 140 },
2028
+ shapeId: 'panel',
2029
+ portMovement: {
2030
+ moveMode: 'border',
2031
+ positionLimits: {
2032
+ x: { max: 80 },
2033
+ },
2034
+ },
2035
+ },
2036
+ {
2037
+ id: 'border-right-host',
2038
+ position: { x: 500, y: 350 },
2039
+ size: { width: 220, height: 140 },
2040
+ shapeId: 'panel',
2041
+ portMovement: {
2042
+ moveMode: 'border',
2043
+ positionLimits: {
2044
+ x: { min: 140 },
2045
+ },
2046
+ },
2047
+ },
2048
+ {
2049
+ id: 'default-normalize-host',
2050
+ position: { x: 800, y: 350 },
2051
+ size: { width: 220, height: 140 },
2052
+ shapeId: 'panel',
2053
+ portMovement: {
2054
+ moveMode: 'inside',
2055
+ positionLimits: {
2056
+ x: { max: 60 },
2057
+ y: { max: 60 },
2058
+ },
2059
+ },
2060
+ },
2061
+ ],
2062
+ ports: [
2063
+ { id: 'limit-left-port', elementId: 'limit-left-host', position: { x: 40, y: 70 }, shapeId: 'port-circle', anchorCenter: true },
2064
+ { id: 'limit-right-port', elementId: 'limit-right-host', position: { x: 180, y: 70 }, shapeId: 'port-circle', anchorCenter: true },
2065
+ { id: 'limit-top-port', elementId: 'limit-top-host', position: { x: 110, y: 30 }, shapeId: 'port-circle', anchorCenter: true },
2066
+ { id: 'limit-bottom-port', elementId: 'limit-bottom-host', position: { x: 110, y: 110 }, shapeId: 'port-circle', anchorCenter: true },
2067
+ { id: 'border-left-port', elementId: 'border-left-host', position: { x: 80, y: 0 }, shapeId: 'port-circle', anchorCenter: true },
2068
+ { id: 'border-right-port', elementId: 'border-right-host', position: { x: 140, y: 140 }, shapeId: 'port-circle', anchorCenter: true },
2069
+ { id: 'reload-default-port', elementId: 'default-normalize-host', position: { x: 190, y: 130 }, shapeId: 'port-circle', anchorCenter: true },
2070
+ ],
2071
+ links: [],
2072
+ texts: [
2073
+ {
2074
+ id: 'limit-intro',
2075
+ content: 'Top row: threshold/limit hosts. Bottom row: border+limits hosts and default-position normalization checks for add/load flows.',
2076
+ position: { x: 60, y: 40 },
2077
+ },
2078
+ { id: 'limit-group-title', content: 'Threshold/limit-focused hosts', position: { x: 60, y: 85 } },
2079
+ { id: 'border-group-title', content: 'Border+limits + default-position normalization', position: { x: 200, y: 315 } },
2080
+ { id: 'limit-left-title', content: 'x.max = 70 (left of X)', position: { x: 8, y: -20 }, ownerId: 'limit-left-host' },
2081
+ { id: 'limit-right-title', content: 'x.min = 150 (right of X)', position: { x: 8, y: -20 }, ownerId: 'limit-right-host' },
2082
+ { id: 'limit-top-title', content: 'y.max = 45 (above Y)', position: { x: 8, y: -20 }, ownerId: 'limit-top-host' },
2083
+ { id: 'limit-bottom-title', content: 'y.min = 90 (below Y)', position: { x: 8, y: -20 }, ownerId: 'limit-bottom-host' },
2084
+ { id: 'border-left-title', content: 'moveMode=border + x.max = 80', position: { x: 8, y: -20 }, ownerId: 'border-left-host' },
2085
+ { id: 'border-right-title', content: 'moveMode=border + x.min = 140', position: { x: 8, y: -20 }, ownerId: 'border-right-host' },
2086
+ { id: 'default-normalize-title', content: 'default normalization host x/y.max = 60', position: { x: 8, y: -20 }, ownerId: 'default-normalize-host' },
2087
+ { id: 'limit-left-status', content: 'local: x=40, y=70', position: { x: 8, y: 110 }, ownerId: 'limit-left-host' },
2088
+ { id: 'limit-right-status', content: 'local: x=180, y=70', position: { x: 8, y: 110 }, ownerId: 'limit-right-host' },
2089
+ { id: 'limit-top-status', content: 'local: x=110, y=30', position: { x: 8, y: 110 }, ownerId: 'limit-top-host' },
2090
+ { id: 'limit-bottom-status', content: 'local: x=110, y=110', position: { x: 8, y: 110 }, ownerId: 'limit-bottom-host' },
2091
+ { id: 'border-left-status', content: 'local: x=80, y=0', position: { x: 8, y: 110 }, ownerId: 'border-left-host' },
2092
+ { id: 'border-right-status', content: 'local: x=140, y=140', position: { x: 8, y: 110 }, ownerId: 'border-right-host' },
2093
+ {
2094
+ id: 'add-default-status',
2095
+ content: 'add invalid default: not executed',
2096
+ position: { x: 8, y: 88 },
2097
+ ownerId: 'default-normalize-host',
2098
+ },
2099
+ {
2100
+ id: 'reload-default-status',
2101
+ content: 'reload invalid default: click "Read reload-normalized port"',
2102
+ position: { x: 8, y: 106 },
2103
+ ownerId: 'default-normalize-host',
2104
+ },
2105
+ ],
2106
+ }); };
2107
+ var portPositionLimitsDemoConfig = {
2108
+ id: 'port-position-limits',
2109
+ title: 'Port Position Limits',
2110
+ description: 'Unified restriction demo: threshold limits, border+limits, and add/load default-position normalization.',
2111
+ createState: createPortPositionLimitsState,
2112
+ elementShapes: baseElementShapes,
2113
+ portShapes: basePortShapes,
2114
+ defaultElementShapeId: 'default',
2115
+ defaultPortShapeId: 'port-circle',
2116
+ actions: [
2117
+ {
2118
+ id: 'force-limit-clamp',
2119
+ label: 'Force out-of-range moves',
2120
+ run: function (editor) {
2121
+ editor.movePortTo('limit-left-port', 1000, 1000);
2122
+ editor.movePortTo('limit-right-port', -1000, 1000);
2123
+ editor.movePortTo('limit-top-port', 1000, 1000);
2124
+ editor.movePortTo('limit-bottom-port', 1000, -1000);
2125
+ editor.movePortTo('border-left-port', 1000, 1000);
2126
+ editor.movePortTo('border-right-port', -1000, -1000);
2127
+ var state = editor.getState();
2128
+ var left = state.ports.find(function (item) { return item.id === 'limit-left-port'; });
2129
+ var right = state.ports.find(function (item) { return item.id === 'limit-right-port'; });
2130
+ var top = state.ports.find(function (item) { return item.id === 'limit-top-port'; });
2131
+ var bottom = state.ports.find(function (item) { return item.id === 'limit-bottom-port'; });
2132
+ var borderLeft = state.ports.find(function (item) { return item.id === 'border-left-port'; });
2133
+ var borderRight = state.ports.find(function (item) { return item.id === 'border-right-port'; });
2134
+ if (left)
2135
+ editor.updateText('limit-left-status', "local: x=".concat(left.position.x, ", y=").concat(left.position.y));
2136
+ if (right)
2137
+ editor.updateText('limit-right-status', "local: x=".concat(right.position.x, ", y=").concat(right.position.y));
2138
+ if (top)
2139
+ editor.updateText('limit-top-status', "local: x=".concat(top.position.x, ", y=").concat(top.position.y));
2140
+ if (bottom)
2141
+ editor.updateText('limit-bottom-status', "local: x=".concat(bottom.position.x, ", y=").concat(bottom.position.y));
2142
+ if (borderLeft)
2143
+ editor.updateText('border-left-status', "local: x=".concat(borderLeft.position.x, ", y=").concat(borderLeft.position.y));
2144
+ if (borderRight)
2145
+ editor.updateText('border-right-status', "local: x=".concat(borderRight.position.x, ", y=").concat(borderRight.position.y));
2146
+ },
2147
+ },
2148
+ {
2149
+ id: 'add-invalid-default-port',
2150
+ label: 'Add invalid default port',
2151
+ run: function (editor) {
2152
+ editor.removePort('added-default-port');
2153
+ editor.addPortToElement('default-normalize-host', {
2154
+ id: 'added-default-port',
2155
+ elementId: 'default-normalize-host',
2156
+ position: { x: 180, y: 120 },
2157
+ shapeId: 'port-circle',
2158
+ anchorCenter: true,
2159
+ });
2160
+ var state = editor.getState();
2161
+ var added = state.ports.find(function (item) { return item.id === 'added-default-port'; });
2162
+ if (added) {
2163
+ editor.updateText('add-default-status', "add invalid default: local x=".concat(added.position.x, ", y=").concat(added.position.y));
2164
+ }
2165
+ },
2166
+ },
2167
+ {
2168
+ id: 'read-reload-normalized-port',
2169
+ label: 'Read reload-normalized port',
2170
+ run: function (editor) {
2171
+ var state = editor.getState();
2172
+ var reloaded = state.ports.find(function (item) { return item.id === 'reload-default-port'; });
2173
+ if (reloaded) {
2174
+ editor.updateText('reload-default-status', "reload invalid default: local x=".concat(reloaded.position.x, ", y=").concat(reloaded.position.y));
2175
+ }
2176
+ },
2177
+ },
2178
+ ],
2179
+ };
2180
+
1974
2181
  var createPortBorderState = function () { return ({
1975
2182
  elements: [
1976
2183
  {
@@ -2125,6 +2332,104 @@ var childConstraintsDemoConfig = ({
2125
2332
  ],
2126
2333
  });
2127
2334
 
2335
+ var createLabelStyleState = function () { return ({
2336
+ elements: [
2337
+ {
2338
+ id: 'label-center-host',
2339
+ position: { x: 80, y: 160 },
2340
+ size: { width: 260, height: 120 },
2341
+ shapeId: 'panel',
2342
+ },
2343
+ {
2344
+ id: 'label-background-host',
2345
+ position: { x: 390, y: 160 },
2346
+ size: { width: 260, height: 120 },
2347
+ shapeId: 'panel',
2348
+ style: {
2349
+ cornerRadius: 16,
2350
+ stroke: '#1f4d99',
2351
+ strokeWidth: 2,
2352
+ fill: '#edf4ff',
2353
+ },
2354
+ },
2355
+ {
2356
+ id: 'label-baseline-host',
2357
+ position: { x: 700, y: 160 },
2358
+ size: { width: 260, height: 120 },
2359
+ shapeId: 'panel',
2360
+ },
2361
+ ],
2362
+ ports: [],
2363
+ links: [],
2364
+ texts: [
2365
+ {
2366
+ id: 'label-style-intro',
2367
+ content: 'Compare center alignment, background fill, and baseline label styling. Resize hosts to verify visual stability.',
2368
+ position: { x: 80, y: 80 },
2369
+ },
2370
+ {
2371
+ id: 'label-center-text',
2372
+ content: 'Center aligned owner-width label',
2373
+ ownerId: 'label-center-host',
2374
+ position: { x: 0, y: 44 },
2375
+ layout: { boundsMode: 'owner-width', wrap: 'none', overflow: 'ellipsis-end', padding: 8 },
2376
+ style: { align: 'center', fill: '#16324f', fontSize: 15 },
2377
+ },
2378
+ {
2379
+ id: 'label-background-text',
2380
+ content: 'Top Header Label',
2381
+ ownerId: 'label-background-host',
2382
+ position: { x: 0, y: 0 },
2383
+ layout: { boundsMode: 'owner-width', wrap: 'none', overflow: 'ellipsis-end', padding: 0 },
2384
+ style: {
2385
+ align: 'center',
2386
+ backgroundFill: '#cfe2ff',
2387
+ backgroundStroke: '#1f4d99',
2388
+ backgroundStrokeWidth: 1,
2389
+ fill: '#16324f',
2390
+ fontSize: 15,
2391
+ padding: 8,
2392
+ },
2393
+ },
2394
+ {
2395
+ id: 'label-baseline-text',
2396
+ content: 'Baseline label (no style keys)',
2397
+ ownerId: 'label-baseline-host',
2398
+ position: { x: 12, y: 44 },
2399
+ layout: { boundsMode: 'owner-width', wrap: 'none', overflow: 'ellipsis-end', padding: 8 },
2400
+ },
2401
+ ],
2402
+ }); };
2403
+ var labelStyleDemoConfig = {
2404
+ id: 'label-style',
2405
+ title: 'Label Style',
2406
+ description: 'Center text alignment and backgroundFill styling for labels.',
2407
+ createState: createLabelStyleState,
2408
+ elementShapes: baseElementShapes,
2409
+ portShapes: basePortShapes,
2410
+ defaultElementShapeId: 'default',
2411
+ defaultPortShapeId: 'port-circle',
2412
+ actions: [
2413
+ {
2414
+ id: 'resize-label-hosts',
2415
+ label: 'Resize hosts (style verification)',
2416
+ run: function (editor, state) {
2417
+ var _a, _b;
2418
+ var expanded = ((_b = (_a = state.elements.find(function (item) { return item.id === 'label-center-host'; })) === null || _a === void 0 ? void 0 : _a.size.width) !== null && _b !== void 0 ? _b : 0) > 260;
2419
+ if (expanded) {
2420
+ editor.resizeElement('label-center-host', 260, 120);
2421
+ editor.resizeElement('label-background-host', 260, 120);
2422
+ editor.resizeElement('label-baseline-host', 260, 120);
2423
+ return;
2424
+ }
2425
+ editor.resizeElement('label-center-host', 320, 140);
2426
+ editor.resizeElement('label-background-host', 320, 140);
2427
+ editor.resizeElement('label-baseline-host', 320, 140);
2428
+ },
2429
+ },
2430
+ ],
2431
+ };
2432
+
2128
2433
  var createGridOverlayState = function () { return ({
2129
2434
  elements: [
2130
2435
  {
@@ -2226,141 +2531,174 @@ var routingDemoConfig = ({
2226
2531
 
2227
2532
  var obstacleRoutingLinks = [
2228
2533
  {
2229
- id: 'block-link',
2230
- sourcePortId: 'block-source-port',
2231
- targetPortId: 'block-target-port',
2534
+ id: 'sibling-external-link',
2535
+ sourcePortId: 'sibling-a-port',
2536
+ targetPortId: 'sibling-b-port',
2232
2537
  points: [],
2233
2538
  routing: 'auto',
2234
2539
  },
2235
2540
  {
2236
- id: 'shared-sibling-link',
2237
- sourcePortId: 'shared-child-a-port',
2238
- targetPortId: 'shared-child-b-port',
2541
+ id: 'parent-to-child-link',
2542
+ sourcePortId: 'attach-parent-port',
2543
+ targetPortId: 'attach-child-a-port',
2239
2544
  points: [],
2240
2545
  routing: 'auto',
2241
2546
  },
2242
2547
  {
2243
- id: 'parent-child-link',
2244
- sourcePortId: 'parent-host-port',
2245
- targetPortId: 'edge-child-port',
2548
+ id: 'child-to-parent-link',
2549
+ sourcePortId: 'attach-child-b-port',
2550
+ targetPortId: 'attach-parent-port',
2246
2551
  points: [],
2247
2552
  routing: 'auto',
2248
2553
  },
2249
2554
  {
2250
- id: 'grandchild-link',
2251
- sourcePortId: 'grandchild-a-port',
2252
- targetPortId: 'grandchild-b-port',
2555
+ id: 'child-to-child-link',
2556
+ sourcePortId: 'attach-child-a-port',
2557
+ targetPortId: 'attach-child-b-port',
2253
2558
  points: [],
2254
2559
  routing: 'auto',
2255
2560
  },
2256
2561
  ];
2562
+ var obstacleRoutingShapes = __spreadArray(__spreadArray([], baseElementShapes, true), [
2563
+ {
2564
+ id: 'routing-ellipse',
2565
+ kind: 'ellipse',
2566
+ style: {
2567
+ fill: '#e8f8ff',
2568
+ stroke: '#1e6b8f',
2569
+ strokeWidth: 2,
2570
+ },
2571
+ },
2572
+ ], false);
2257
2573
  var createObstacleRoutingState = function () { return ({
2258
2574
  elements: [
2259
2575
  {
2260
- id: 'block-source',
2261
- position: { x: 60, y: 40 },
2262
- size: { width: 140, height: 90 },
2263
- shapeId: 'default',
2576
+ id: 'sibling-parent',
2577
+ position: { x: 40, y: 90 },
2578
+ size: { width: 540, height: 250 },
2579
+ shapeId: 'panel',
2580
+ style: { fill: '#fafafa' },
2264
2581
  },
2265
2582
  {
2266
- id: 'block-target',
2267
- position: { x: 380, y: 40 },
2268
- size: { width: 140, height: 90 },
2269
- shapeId: 'panel',
2583
+ id: 'sibling-a',
2584
+ position: { x: 70, y: 75 },
2585
+ size: { width: 140, height: 100 },
2586
+ shapeId: 'routing-ellipse',
2587
+ parentId: 'sibling-parent',
2588
+ portMovement: { moveMode: 'anchors', anchorConstraint: { preset: 'cardinal' } },
2270
2589
  },
2271
2590
  {
2272
- id: 'block-obstacle',
2273
- position: { x: 250, y: 30 },
2274
- size: { width: 80, height: 110 },
2275
- shapeId: 'default',
2591
+ id: 'sibling-b',
2592
+ position: { x: 310, y: 75 },
2593
+ size: { width: 140, height: 100 },
2594
+ shapeId: 'routing-ellipse',
2595
+ parentId: 'sibling-parent',
2596
+ portMovement: { moveMode: 'anchors', anchorConstraint: { preset: 'cardinal' } },
2276
2597
  },
2277
2598
  {
2278
- id: 'shared-parent',
2279
- position: { x: 40, y: 180 },
2280
- size: { width: 420, height: 220 },
2281
- shapeId: 'panel',
2282
- style: { fill: '#fafafa' },
2599
+ id: 'attach-parent',
2600
+ position: { x: 620, y: 90 },
2601
+ size: { width: 360, height: 250 },
2602
+ shapeId: 'routing-ellipse',
2603
+ portMovement: { moveMode: 'anchors', anchorConstraint: { preset: 'cardinal' } },
2283
2604
  },
2284
2605
  {
2285
- id: 'shared-child-a',
2286
- position: { x: 40, y: 40 },
2287
- size: { width: 140, height: 90 },
2288
- shapeId: 'default',
2289
- parentId: 'shared-parent',
2606
+ id: 'attach-child',
2607
+ position: { x: 40, y: 45 },
2608
+ size: { width: 140, height: 100 },
2609
+ shapeId: 'routing-ellipse',
2610
+ parentId: 'attach-parent',
2611
+ portMovement: { moveMode: 'anchors', anchorConstraint: { preset: 'cardinal' } },
2290
2612
  },
2291
2613
  {
2292
- id: 'shared-child-b',
2293
- position: { x: 220, y: 40 },
2294
- size: { width: 140, height: 90 },
2295
- shapeId: 'default',
2296
- parentId: 'shared-parent',
2614
+ id: 'attach-child-b',
2615
+ position: { x: 190, y: 120 },
2616
+ size: { width: 140, height: 100 },
2617
+ shapeId: 'routing-ellipse',
2618
+ parentId: 'attach-parent',
2619
+ portMovement: { moveMode: 'anchors', anchorConstraint: { preset: 'cardinal' } },
2297
2620
  },
2621
+ ],
2622
+ ports: [
2298
2623
  {
2299
- id: 'parent-host',
2300
- position: { x: 500, y: 180 },
2301
- size: { width: 320, height: 220 },
2302
- shapeId: 'panel',
2303
- style: { fill: '#fafafa' },
2624
+ id: 'sibling-a-port',
2625
+ elementId: 'sibling-a',
2626
+ position: { x: 0, y: 50 },
2627
+ shapeId: 'port-dark',
2628
+ anchorCenter: true,
2629
+ currentAnchorId: 'left',
2304
2630
  },
2305
2631
  {
2306
- id: 'edge-child',
2307
- position: { x: 40, y: 60 },
2308
- size: { width: 160, height: 90 },
2309
- shapeId: 'default',
2310
- parentId: 'parent-host',
2632
+ id: 'sibling-b-port',
2633
+ elementId: 'sibling-b',
2634
+ position: { x: 140, y: 50 },
2635
+ shapeId: 'port-dark',
2636
+ anchorCenter: true,
2637
+ currentAnchorId: 'right',
2311
2638
  },
2312
2639
  {
2313
- id: 'grandparent',
2314
- position: { x: 40, y: 380 },
2315
- size: { width: 780, height: 160 },
2316
- shapeId: 'panel',
2317
- style: { fill: '#fafafa' },
2640
+ id: 'attach-parent-port',
2641
+ elementId: 'attach-parent',
2642
+ position: { x: 20, y: 125 },
2643
+ shapeId: 'port-circle',
2644
+ anchorCenter: true,
2645
+ currentAnchorId: 'left',
2646
+ externalLinkAttachPoint: { x: -16, y: 0 },
2647
+ internalLinkAttachPoint: { x: 16, y: 0 },
2648
+ },
2649
+ {
2650
+ id: 'attach-child-a-port',
2651
+ elementId: 'attach-child',
2652
+ position: { x: 140, y: 50 },
2653
+ shapeId: 'port-circle',
2654
+ anchorCenter: true,
2655
+ currentAnchorId: 'right',
2656
+ externalLinkAttachPoint: { x: 12, y: 0 },
2657
+ internalLinkAttachPoint: { x: -12, y: 0 },
2658
+ },
2659
+ {
2660
+ id: 'attach-child-b-port',
2661
+ elementId: 'attach-child-b',
2662
+ position: { x: 0, y: 50 },
2663
+ shapeId: 'port-circle',
2664
+ anchorCenter: true,
2665
+ currentAnchorId: 'left',
2666
+ externalLinkAttachPoint: { x: 12, y: 0 },
2667
+ internalLinkAttachPoint: { x: -12, y: 0 },
2318
2668
  },
2669
+ ],
2670
+ links: obstacleRoutingLinks,
2671
+ texts: [
2319
2672
  {
2320
- id: 'mid-parent-a',
2673
+ id: 'obstacle-routing-instructions',
2674
+ content: 'Two checks in one canvas: (A) sibling external-anchor link avoids both child interiors; (B) parent/children hierarchy resolves internal vs external attach points per endpoint direction.',
2321
2675
  position: { x: 40, y: 30 },
2322
- size: { width: 300, height: 100 },
2323
- shapeId: 'panel',
2324
- parentId: 'grandparent',
2325
2676
  },
2326
2677
  {
2327
- id: 'mid-parent-b',
2328
- position: { x: 420, y: 30 },
2329
- size: { width: 300, height: 100 },
2330
- shapeId: 'panel',
2331
- parentId: 'grandparent',
2678
+ id: 'sibling-group-label',
2679
+ content: 'Scenario A: Same-parent children, external-facing anchors (left/right)',
2680
+ position: { x: 52, y: 66 },
2332
2681
  },
2333
2682
  {
2334
- id: 'grandchild-a',
2335
- position: { x: 40, y: 20 },
2336
- size: { width: 120, height: 60 },
2337
- shapeId: 'default',
2338
- parentId: 'mid-parent-a',
2683
+ id: 'attach-group-label',
2684
+ content: 'Scenario B: Directional attach semantics (parent internal, child/sibling external)',
2685
+ position: { x: 620, y: 66 },
2339
2686
  },
2340
2687
  {
2341
- id: 'grandchild-b',
2342
- position: { x: 40, y: 20 },
2343
- size: { width: 120, height: 60 },
2344
- shapeId: 'default',
2345
- parentId: 'mid-parent-b',
2688
+ id: 'attach-link-parent-child-label',
2689
+ content: 'parent->child-A: parent endpoint INTERNAL, child endpoint EXTERNAL',
2690
+ position: { x: 620, y: 350 },
2691
+ },
2692
+ {
2693
+ id: 'attach-link-child-parent-label',
2694
+ content: 'child-B->parent: child endpoint EXTERNAL, parent endpoint INTERNAL',
2695
+ position: { x: 620, y: 372 },
2696
+ },
2697
+ {
2698
+ id: 'attach-link-child-child-label',
2699
+ content: 'child-A->child-B: sibling endpoints EXTERNAL on both ends',
2700
+ position: { x: 620, y: 394 },
2346
2701
  },
2347
- ],
2348
- ports: [
2349
- { id: 'block-source-port', elementId: 'block-source', position: { x: 70, y: 45 }, shapeId: 'port-circle' },
2350
- { id: 'block-target-port', elementId: 'block-target', position: { x: 70, y: 45 }, shapeId: 'port-circle' },
2351
- { id: 'shared-child-a-port', elementId: 'shared-child-a', position: { x: 70, y: 45 }, shapeId: 'port-circle' },
2352
- { id: 'shared-child-b-port', elementId: 'shared-child-b', position: { x: 70, y: 45 }, shapeId: 'port-circle' },
2353
- { id: 'parent-host-port', elementId: 'parent-host', position: { x: 250, y: 110 }, shapeId: 'port-circle' },
2354
- { id: 'edge-child-port', elementId: 'edge-child', position: { x: 0, y: 45 }, shapeId: 'port-circle' },
2355
- { id: 'grandchild-a-port', elementId: 'grandchild-a', position: { x: 60, y: 30 }, shapeId: 'port-circle' },
2356
- { id: 'grandchild-b-port', elementId: 'grandchild-b', position: { x: 60, y: 30 }, shapeId: 'port-circle' },
2357
- ],
2358
- links: obstacleRoutingLinks,
2359
- texts: [
2360
- { id: 'block-label', content: 'Blocking obstacle', position: { x: 60, y: 16 } },
2361
- { id: 'shared-label', content: 'Shared parent (siblings)', position: { x: 40, y: 158 } },
2362
- { id: 'parent-label', content: 'Parent-child (stay inside)', position: { x: 500, y: 158 } },
2363
- { id: 'grand-label', content: 'Grandchildren (shared ancestors)', position: { x: 40, y: 358 } },
2364
2702
  ],
2365
2703
  }); };
2366
2704
  var addLinksAction = {
@@ -2382,17 +2720,17 @@ var rerouteAllAction = {
2382
2720
  editor.rerouteAllLinks();
2383
2721
  },
2384
2722
  };
2385
- var obstacleRoutingDemoConfig = ({
2723
+ var obstacleRoutingDemoConfig = {
2386
2724
  id: 'obstacle-routing',
2387
2725
  title: 'Obstacle Routing',
2388
- description: 'Auto routing avoids obstacles and respects ancestor/edge rules.',
2726
+ description: 'Nested multi-anchor checks: sibling interior-avoidance + directional internal/external parent-child attach semantics.',
2389
2727
  createState: createObstacleRoutingState,
2390
- elementShapes: baseElementShapes,
2728
+ elementShapes: obstacleRoutingShapes,
2391
2729
  portShapes: basePortShapes,
2392
2730
  defaultElementShapeId: 'default',
2393
2731
  defaultPortShapeId: 'port-circle',
2394
2732
  actions: [addLinksAction, rerouteAllAction],
2395
- });
2733
+ };
2396
2734
 
2397
2735
  var createLinkBendHandlesState = function () { return ({
2398
2736
  elements: [
@@ -4382,21 +4720,42 @@ var ObstacleRouter = /** @class */ (function () {
4382
4720
  ObstacleRouter.prototype.computeStubEndpoint = function (point, endpoint, bounds, obstacles) {
4383
4721
  if (!(endpoint === null || endpoint === void 0 ? void 0 : endpoint.onEdgeSide) || !endpoint.rect)
4384
4722
  return null;
4385
- var normal = this.getNormal(endpoint.onEdgeSide);
4723
+ var outwardNormal = this.getNormal(endpoint.onEdgeSide);
4724
+ var selectedNormal = outwardNormal;
4725
+ var maxLength = this.computeAvailableStubLength(point, outwardNormal, bounds, obstacles, endpoint.elementId);
4726
+ // If the outward normal is fully blocked by route bounds on the endpoint host, fall back inward
4727
+ // to preserve a visible perpendicular stub for bounded parent-child routes.
4728
+ if (maxLength <= this.tolerance && bounds && this.rectsEqual(bounds, endpoint.rect)) {
4729
+ var inwardNormal = { x: -outwardNormal.x, y: -outwardNormal.y };
4730
+ var inwardLength = this.computeAvailableStubLength(point, inwardNormal, bounds, obstacles, endpoint.elementId);
4731
+ if (inwardLength > maxLength) {
4732
+ selectedNormal = inwardNormal;
4733
+ maxLength = inwardLength;
4734
+ }
4735
+ }
4736
+ if (maxLength <= 0)
4737
+ return null;
4738
+ return {
4739
+ x: point.x + selectedNormal.x * maxLength,
4740
+ y: point.y + selectedNormal.y * maxLength,
4741
+ };
4742
+ };
4743
+ ObstacleRouter.prototype.computeAvailableStubLength = function (point, normal, bounds, obstacles, ignoreId) {
4386
4744
  var maxLength = this.stubLength;
4387
4745
  if (bounds) {
4388
4746
  maxLength = Math.min(maxLength, this.distanceToBounds(point, normal, bounds));
4389
4747
  }
4390
- var obstacleLimit = this.distanceToObstacles(point, normal, obstacles, endpoint.elementId);
4748
+ var obstacleLimit = this.distanceToObstacles(point, normal, obstacles, ignoreId);
4391
4749
  if (obstacleLimit !== null) {
4392
4750
  maxLength = Math.min(maxLength, obstacleLimit);
4393
4751
  }
4394
- if (maxLength <= 0)
4395
- return null;
4396
- return {
4397
- x: point.x + normal.x * maxLength,
4398
- y: point.y + normal.y * maxLength,
4399
- };
4752
+ return maxLength;
4753
+ };
4754
+ ObstacleRouter.prototype.rectsEqual = function (a, b) {
4755
+ return (Math.abs(a.x - b.x) <= this.tolerance &&
4756
+ Math.abs(a.y - b.y) <= this.tolerance &&
4757
+ Math.abs(a.width - b.width) <= this.tolerance &&
4758
+ Math.abs(a.height - b.height) <= this.tolerance);
4400
4759
  };
4401
4760
  ObstacleRouter.prototype.getNormal = function (side) {
4402
4761
  switch (side) {
@@ -4693,7 +5052,7 @@ var borderSideToInwardRotation = function (side) {
4693
5052
  return 0;
4694
5053
  };
4695
5054
 
4696
- var POSITION_EPSILON$1 = 1e-6;
5055
+ var POSITION_EPSILON$2 = 1e-6;
4697
5056
  var MIN_TRUNCATION_WIDTH_SAFETY_EPSILON = 0.5;
4698
5057
  var FONT_SIZE_TRUNCATION_SAFETY_RATIO = 1.4;
4699
5058
  var FONT_SIZE_TRUNCATION_SAFETY_BASE = 2;
@@ -4719,7 +5078,7 @@ var TextLayoutService = /** @class */ (function () {
4719
5078
  var textRenderPadding = this.resolveTextRenderPadding(text.style);
4720
5079
  var horizontalRenderInset = textRenderPadding * 2;
4721
5080
  var verticalRenderInset = textRenderPadding * 2;
4722
- var zeroPaddingInset = padding <= POSITION_EPSILON$1 && this.shouldTrackOwnerBoundLayout(text) ? 0.5 : 0;
5081
+ var zeroPaddingInset = padding <= POSITION_EPSILON$2 && this.shouldTrackOwnerBoundLayout(text) ? 0.5 : 0;
4723
5082
  var drawableWidth = maxWidth !== undefined ? Math.max(0, maxWidth - zeroPaddingInset * 2 - horizontalRenderInset) : undefined;
4724
5083
  var drawableHeight = maxHeight !== undefined ? Math.max(0, maxHeight - zeroPaddingInset * 2 - verticalRenderInset) : undefined;
4725
5084
  var lineHeight = this.textMeasurer.measure(__assign(__assign({}, text), { content: 'M' })).height;
@@ -5417,6 +5776,7 @@ var resolvePortWorldTransform = function (options) {
5417
5776
  };
5418
5777
 
5419
5778
  var EDGE_TOLERANCE = 0.5;
5779
+ var POSITION_EPSILON$1 = 1e-6;
5420
5780
  var LinkRoutingService = /** @class */ (function () {
5421
5781
  function LinkRoutingService(config) {
5422
5782
  this.model = config.model;
@@ -5525,11 +5885,27 @@ var LinkRoutingService = /** @class */ (function () {
5525
5885
  if (points.length < 2) {
5526
5886
  return [__assign({}, source), __assign({}, target)];
5527
5887
  }
5888
+ var sourceDelta = {
5889
+ x: source.x - points[0].x,
5890
+ y: source.y - points[0].y,
5891
+ };
5892
+ var targetDelta = {
5893
+ x: target.x - points[points.length - 1].x,
5894
+ y: target.y - points[points.length - 1].y,
5895
+ };
5896
+ var shouldTranslateAllPoints = Math.abs(sourceDelta.x - targetDelta.x) <= POSITION_EPSILON$1 &&
5897
+ Math.abs(sourceDelta.y - targetDelta.y) <= POSITION_EPSILON$1;
5528
5898
  return points.map(function (point, index) {
5529
5899
  if (index === 0)
5530
5900
  return __assign({}, source);
5531
5901
  if (index === points.length - 1)
5532
5902
  return __assign({}, target);
5903
+ if (shouldTranslateAllPoints) {
5904
+ return {
5905
+ x: point.x + sourceDelta.x,
5906
+ y: point.y + sourceDelta.y,
5907
+ };
5908
+ }
5533
5909
  return __assign({}, point);
5534
5910
  });
5535
5911
  };
@@ -5695,16 +6071,13 @@ var LinkRoutingService = /** @class */ (function () {
5695
6071
  var oppositeElementId = (_b = this.model.getPort(oppositePortId)) === null || _b === void 0 ? void 0 : _b.elementId;
5696
6072
  if (!elementId || !oppositeElementId)
5697
6073
  return 'external';
5698
- return this.hasAncestorRelation(elementId, oppositeElementId) ? 'internal' : 'external';
6074
+ return this.isAncestorOf(elementId, oppositeElementId) ? 'internal' : 'external';
5699
6075
  };
5700
- LinkRoutingService.prototype.hasAncestorRelation = function (sourceElementId, targetElementId) {
5701
- if (sourceElementId === targetElementId)
6076
+ LinkRoutingService.prototype.isAncestorOf = function (candidateAncestorId, candidateDescendantId) {
6077
+ if (candidateAncestorId === candidateDescendantId)
5702
6078
  return false;
5703
- var sourceChain = this.getAncestorChain(sourceElementId);
5704
- if (sourceChain.includes(targetElementId))
5705
- return true;
5706
- var targetChain = this.getAncestorChain(targetElementId);
5707
- return targetChain.includes(sourceElementId);
6079
+ var descendantChain = this.getAncestorChain(candidateDescendantId);
6080
+ return descendantChain.includes(candidateAncestorId);
5708
6081
  };
5709
6082
  LinkRoutingService.prototype.getAncestorChain = function (elementId) {
5710
6083
  var _a;
@@ -5864,11 +6237,17 @@ var DiagramEngine = /** @class */ (function () {
5864
6237
  DiagramEngine.prototype.load = function (state) {
5865
6238
  var _this = this;
5866
6239
  var patches = this.commandQueue.run(createLoadCommand(state), this.model);
5867
- var allPatches = this.mutationPipeline.run({
6240
+ var mutationPatches = this.mutationPipeline.run({
5868
6241
  basePatches: patches,
5869
6242
  layoutSteps: [function () { return _this.applyAllLayouts(); }],
5870
6243
  includeEmptyLinkRouting: true,
5871
6244
  });
6245
+ var normalizedPorts = this.normalizePortsForHostPolicies();
6246
+ var normalizedLinkPatches = normalizedPorts.movedPortIds.length > 0
6247
+ ? this.updateLinksForPorts(normalizedPorts.movedPortIds)
6248
+ : [];
6249
+ var textPresentationPatches = this.resolveAllTextPresentationPatches(false);
6250
+ var allPatches = __spreadArray(__spreadArray(__spreadArray(__spreadArray([], mutationPatches, true), normalizedPorts.patches, true), normalizedLinkPatches, true), textPresentationPatches, true);
5872
6251
  this.emitChange(allPatches);
5873
6252
  };
5874
6253
  DiagramEngine.prototype.getState = function () {
@@ -6460,7 +6839,7 @@ var DiagramEngine = /** @class */ (function () {
6460
6839
  if (!host)
6461
6840
  return;
6462
6841
  host.portIds.forEach(function (portId) {
6463
- var _a;
6842
+ var _a, _b, _c;
6464
6843
  var port = _this.model.getPort(portId);
6465
6844
  if (!port)
6466
6845
  return;
@@ -6483,11 +6862,16 @@ var DiagramEngine = /** @class */ (function () {
6483
6862
  return;
6484
6863
  }
6485
6864
  var projected = _this.resolveBorderPortResizeProjection(port.position, host.shapeId, sizeInfo, host.size);
6486
- var unchanged = Math.abs(projected.x - port.position.x) <= POSITION_EPSILON &&
6487
- Math.abs(projected.y - port.position.y) <= POSITION_EPSILON;
6865
+ var constrained = _this.resolveConstrainedPortRelativePosition(port, host, projected);
6866
+ var unchanged = Math.abs(constrained.position.x - port.position.x) <= POSITION_EPSILON &&
6867
+ Math.abs(constrained.position.y - port.position.y) <= POSITION_EPSILON &&
6868
+ ((_b = constrained.currentAnchorId) !== null && _b !== void 0 ? _b : null) === ((_c = port.currentAnchorId) !== null && _c !== void 0 ? _c : null);
6488
6869
  if (unchanged)
6489
6870
  return;
6490
- var patchesForPort = _this.commandQueue.run(createMovePortCommand(port.id, { position: projected, currentAnchorId: null }), _this.model);
6871
+ var patchesForPort = _this.commandQueue.run(createMovePortCommand(port.id, {
6872
+ position: constrained.position,
6873
+ currentAnchorId: constrained.currentAnchorId,
6874
+ }), _this.model);
6491
6875
  reprojectionPatches.push.apply(reprojectionPatches, patchesForPort);
6492
6876
  movedPortIds.add(port.id);
6493
6877
  });
@@ -6544,10 +6928,10 @@ var DiagramEngine = /** @class */ (function () {
6544
6928
  return this.projectPointToHostBorder(projectedTarget, shapeId, nextSize);
6545
6929
  };
6546
6930
  DiagramEngine.prototype.resolveConstrainedPortRelativePosition = function (port, element, requestedPosition) {
6547
- var _a, _b, _c, _d, _e;
6931
+ var _a, _b, _c, _d, _e, _f, _g;
6548
6932
  var position = __assign({}, requestedPosition);
6549
6933
  var effectiveMoveMode = this.resolveEffectivePortMoveMode(port, element);
6550
- if (effectiveMoveMode && effectiveMoveMode !== 'free' && effectiveMoveMode !== 'anchors') {
6934
+ if (effectiveMoveMode === 'inside' || effectiveMoveMode === 'border') {
6551
6935
  var widthLimit = Math.max(0, element.size.width - ((_b = (_a = port.size) === null || _a === void 0 ? void 0 : _a.width) !== null && _b !== void 0 ? _b : 0));
6552
6936
  var heightLimit = Math.max(0, element.size.height - ((_d = (_c = port.size) === null || _c === void 0 ? void 0 : _c.height) !== null && _d !== void 0 ? _d : 0));
6553
6937
  var bounds = {
@@ -6571,9 +6955,12 @@ var DiagramEngine = /** @class */ (function () {
6571
6955
  height: Math.max(0, element.size.height),
6572
6956
  };
6573
6957
  }
6574
- position = effectiveMoveMode === 'inside'
6575
- ? clampToRect(position, bounds)
6576
- : this.constrainPortToHostBorder(position, element);
6958
+ if (effectiveMoveMode === 'inside') {
6959
+ position = clampToRect(position, bounds);
6960
+ }
6961
+ else {
6962
+ position = this.constrainPortToHostBorder(position, element);
6963
+ }
6577
6964
  }
6578
6965
  var style = port.style;
6579
6966
  if ((style === null || style === void 0 ? void 0 : style.moveAxis) === 'horizontal') {
@@ -6587,18 +6974,134 @@ var DiagramEngine = /** @class */ (function () {
6587
6974
  position.x = clamp(position.x, bounds.x, bounds.x + bounds.width);
6588
6975
  position.y = clamp(position.y, bounds.y, bounds.y + bounds.height);
6589
6976
  }
6977
+ if (effectiveMoveMode === 'border') {
6978
+ var resolvedBorder = this.resolveBorderPositionWithLimits(position, element, (_e = element.portMovement) === null || _e === void 0 ? void 0 : _e.positionLimits);
6979
+ position = resolvedBorder !== null && resolvedBorder !== void 0 ? resolvedBorder : this.constrainPortToHostBorder(position, element);
6980
+ }
6981
+ else {
6982
+ position = this.applyPortPositionLimits(position, (_f = element.portMovement) === null || _f === void 0 ? void 0 : _f.positionLimits);
6983
+ }
6590
6984
  if (effectiveMoveMode === 'anchors') {
6591
- var anchor = this.resolveNearestPortAnchor(element, position);
6985
+ var anchor = this.resolveNearestPortAnchor(element, position, port.currentAnchorId);
6592
6986
  if (anchor) {
6593
6987
  return {
6594
6988
  position: __assign({}, anchor.position),
6595
6989
  currentAnchorId: anchor.id,
6596
6990
  };
6597
6991
  }
6598
- return { position: position, currentAnchorId: (_e = port.currentAnchorId) !== null && _e !== void 0 ? _e : null };
6992
+ return { position: __assign({}, port.position), currentAnchorId: (_g = port.currentAnchorId) !== null && _g !== void 0 ? _g : null };
6599
6993
  }
6600
6994
  return { position: position, currentAnchorId: null };
6601
6995
  };
6996
+ DiagramEngine.prototype.applyPortPositionLimits = function (position, limits) {
6997
+ if (!limits)
6998
+ return __assign({}, position);
6999
+ var x = position.x;
7000
+ var y = position.y;
7001
+ if (limits.x) {
7002
+ if (typeof limits.x.min === 'number') {
7003
+ x = Math.max(x, limits.x.min);
7004
+ }
7005
+ if (typeof limits.x.max === 'number') {
7006
+ x = Math.min(x, limits.x.max);
7007
+ }
7008
+ }
7009
+ if (limits.y) {
7010
+ if (typeof limits.y.min === 'number') {
7011
+ y = Math.max(y, limits.y.min);
7012
+ }
7013
+ if (typeof limits.y.max === 'number') {
7014
+ y = Math.min(y, limits.y.max);
7015
+ }
7016
+ }
7017
+ return { x: x, y: y };
7018
+ };
7019
+ DiagramEngine.prototype.resolveBorderPositionWithLimits = function (position, element, limits) {
7020
+ var _this = this;
7021
+ var _a, _b, _c, _d;
7022
+ var borderBase = this.constrainPortToHostBorder(position, element);
7023
+ if (!limits)
7024
+ return borderBase;
7025
+ if (this.isPointWithinPositionLimits(borderBase, limits))
7026
+ return borderBase;
7027
+ var target = this.applyPortPositionLimits(borderBase, limits);
7028
+ var xValues = new Set([borderBase.x, target.x, 0, Math.max(0, element.size.width)]);
7029
+ var yValues = new Set([borderBase.y, target.y, 0, Math.max(0, element.size.height)]);
7030
+ if (typeof ((_a = limits.x) === null || _a === void 0 ? void 0 : _a.min) === 'number')
7031
+ xValues.add(limits.x.min);
7032
+ if (typeof ((_b = limits.x) === null || _b === void 0 ? void 0 : _b.max) === 'number')
7033
+ xValues.add(limits.x.max);
7034
+ if (typeof ((_c = limits.y) === null || _c === void 0 ? void 0 : _c.min) === 'number')
7035
+ yValues.add(limits.y.min);
7036
+ if (typeof ((_d = limits.y) === null || _d === void 0 ? void 0 : _d.max) === 'number')
7037
+ yValues.add(limits.y.max);
7038
+ var seeds = [];
7039
+ xValues.forEach(function (x) { return yValues.forEach(function (y) { return seeds.push({ x: x, y: y }); }); });
7040
+ seeds.push({ x: borderBase.x, y: target.y }, { x: target.x, y: borderBase.y });
7041
+ var candidates = [];
7042
+ var seen = new Set();
7043
+ seeds.forEach(function (seed) {
7044
+ var candidate = _this.constrainPortToHostBorder(seed, element);
7045
+ var key = "".concat(candidate.x.toFixed(6), ":").concat(candidate.y.toFixed(6));
7046
+ if (seen.has(key))
7047
+ return;
7048
+ seen.add(key);
7049
+ candidates.push(candidate);
7050
+ });
7051
+ var valid = candidates.filter(function (candidate) { return _this.isPointWithinPositionLimits(candidate, limits); });
7052
+ if (valid.length === 0)
7053
+ return null;
7054
+ return valid.reduce(function (best, candidate) {
7055
+ var bestDistance = Math.pow((best.x - target.x), 2) + Math.pow((best.y - target.y), 2);
7056
+ var candidateDistance = Math.pow((candidate.x - target.x), 2) + Math.pow((candidate.y - target.y), 2);
7057
+ if (candidateDistance < bestDistance)
7058
+ return candidate;
7059
+ if (candidateDistance > bestDistance)
7060
+ return best;
7061
+ if (candidate.x < best.x)
7062
+ return candidate;
7063
+ if (candidate.x > best.x)
7064
+ return best;
7065
+ return candidate.y < best.y ? candidate : best;
7066
+ }, valid[0]);
7067
+ };
7068
+ DiagramEngine.prototype.isPointWithinPositionLimits = function (position, limits) {
7069
+ if (!limits)
7070
+ return true;
7071
+ if (limits.x) {
7072
+ if (typeof limits.x.min === 'number' && position.x < limits.x.min)
7073
+ return false;
7074
+ if (typeof limits.x.max === 'number' && position.x > limits.x.max)
7075
+ return false;
7076
+ }
7077
+ if (limits.y) {
7078
+ if (typeof limits.y.min === 'number' && position.y < limits.y.min)
7079
+ return false;
7080
+ if (typeof limits.y.max === 'number' && position.y > limits.y.max)
7081
+ return false;
7082
+ }
7083
+ return true;
7084
+ };
7085
+ DiagramEngine.prototype.filterAnchorsByPositionLimits = function (anchors, limits) {
7086
+ if (!limits)
7087
+ return anchors;
7088
+ return anchors.filter(function (anchor) {
7089
+ var position = anchor.position;
7090
+ if (limits.x) {
7091
+ if (typeof limits.x.min === 'number' && position.x < limits.x.min)
7092
+ return false;
7093
+ if (typeof limits.x.max === 'number' && position.x > limits.x.max)
7094
+ return false;
7095
+ }
7096
+ if (limits.y) {
7097
+ if (typeof limits.y.min === 'number' && position.y < limits.y.min)
7098
+ return false;
7099
+ if (typeof limits.y.max === 'number' && position.y > limits.y.max)
7100
+ return false;
7101
+ }
7102
+ return true;
7103
+ });
7104
+ };
6602
7105
  DiagramEngine.prototype.resolveEffectivePortMoveMode = function (port, element) {
6603
7106
  var _a, _b;
6604
7107
  return (_b = (_a = element.portMovement) === null || _a === void 0 ? void 0 : _a.moveMode) !== null && _b !== void 0 ? _b : port.moveMode;
@@ -6633,7 +7136,8 @@ var DiagramEngine = /** @class */ (function () {
6633
7136
  return [];
6634
7137
  };
6635
7138
  DiagramEngine.prototype.resolveNearestPortAnchor = function (element, target, preferredAnchorId) {
6636
- var anchors = this.resolvePortAnchorsForElement(element);
7139
+ var _a;
7140
+ var anchors = this.filterAnchorsByPositionLimits(this.resolvePortAnchorsForElement(element), (_a = element.portMovement) === null || _a === void 0 ? void 0 : _a.positionLimits);
6637
7141
  if (anchors.length === 0)
6638
7142
  return null;
6639
7143
  if (preferredAnchorId) {
@@ -6698,6 +7202,46 @@ var DiagramEngine = /** @class */ (function () {
6698
7202
  DiagramEngine.prototype.resolveTextPresentation = function (text) {
6699
7203
  return this.textLayoutService.resolveTextPresentation(text);
6700
7204
  };
7205
+ DiagramEngine.prototype.resolveAllTextPresentationPatches = function (emitTextUpdated) {
7206
+ var _this = this;
7207
+ var textPatches = [];
7208
+ this.model.texts.forEach(function (text) {
7209
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
7210
+ var current = text.toData();
7211
+ var resolved = _this.resolveTextPresentation(current);
7212
+ var currentDisplay = (_a = current.displayContent) !== null && _a !== void 0 ? _a : current.content;
7213
+ var sizeChanged = !current.size ||
7214
+ current.size.width !== resolved.size.width ||
7215
+ current.size.height !== resolved.size.height;
7216
+ var displayChanged = currentDisplay !== resolved.displayContent;
7217
+ var offsetChanged = ((_c = (_b = current.displayOffset) === null || _b === void 0 ? void 0 : _b.x) !== null && _c !== void 0 ? _c : 0) !== ((_e = (_d = resolved.displayOffset) === null || _d === void 0 ? void 0 : _d.x) !== null && _e !== void 0 ? _e : 0) ||
7218
+ ((_g = (_f = current.displayOffset) === null || _f === void 0 ? void 0 : _f.y) !== null && _g !== void 0 ? _g : 0) !== ((_j = (_h = resolved.displayOffset) === null || _h === void 0 ? void 0 : _h.y) !== null && _j !== void 0 ? _j : 0);
7219
+ var clipChanged = ((_l = (_k = current.displayClipSize) === null || _k === void 0 ? void 0 : _k.width) !== null && _l !== void 0 ? _l : 0) !== ((_o = (_m = resolved.displayClipSize) === null || _m === void 0 ? void 0 : _m.width) !== null && _o !== void 0 ? _o : 0) ||
7220
+ ((_q = (_p = current.displayClipSize) === null || _p === void 0 ? void 0 : _p.height) !== null && _q !== void 0 ? _q : 0) !== ((_s = (_r = resolved.displayClipSize) === null || _r === void 0 ? void 0 : _r.height) !== null && _s !== void 0 ? _s : 0);
7221
+ if (!sizeChanged && !displayChanged && !offsetChanged && !clipChanged)
7222
+ return;
7223
+ text.setSize(resolved.size);
7224
+ text.setDisplayContent(resolved.displayContent);
7225
+ text.setDisplayOffset(resolved.displayOffset);
7226
+ text.setDisplayClipSize(resolved.displayClipSize);
7227
+ textPatches.push(patchUpdate('text', text.id, {
7228
+ size: resolved.size,
7229
+ displayContent: resolved.displayContent,
7230
+ displayOffset: resolved.displayOffset,
7231
+ displayClipSize: resolved.displayClipSize,
7232
+ }));
7233
+ if (emitTextUpdated) {
7234
+ _this.events.emit('textUpdated', {
7235
+ textId: text.id,
7236
+ ownerId: text.ownerId,
7237
+ content: text.content,
7238
+ displayContent: resolved.displayContent,
7239
+ reason: 'layout',
7240
+ });
7241
+ }
7242
+ });
7243
+ return textPatches;
7244
+ };
6701
7245
  DiagramEngine.prototype.emitSelection = function () {
6702
7246
  var _this = this;
6703
7247
  var selectedIds = this.selection.get();
@@ -6787,6 +7331,40 @@ var DiagramEngine = /** @class */ (function () {
6787
7331
  DiagramEngine.prototype.routeLinksWithEmptyPoints = function () {
6788
7332
  return this.linkRoutingService.routeLinksWithEmptyPoints();
6789
7333
  };
7334
+ DiagramEngine.prototype.normalizePortsForHostPolicies = function (portIds) {
7335
+ var _this = this;
7336
+ var ids = portIds !== null && portIds !== void 0 ? portIds : Array.from(this.model.ports.keys());
7337
+ var normalizedPatches = [];
7338
+ var movedPortIds = new Set();
7339
+ ids.forEach(function (portId) {
7340
+ var _a, _b;
7341
+ var port = _this.model.getPort(portId);
7342
+ if (!port)
7343
+ return;
7344
+ var element = _this.model.getElement(port.elementId);
7345
+ if (!element)
7346
+ return;
7347
+ var constrained = _this.resolveConstrainedPortRelativePosition(port, element, port.position);
7348
+ var previousAnchorId = (_a = port.currentAnchorId) !== null && _a !== void 0 ? _a : null;
7349
+ var shouldPersistAnchorId = previousAnchorId !== null || constrained.currentAnchorId === null;
7350
+ var nextAnchorId = shouldPersistAnchorId ? ((_b = constrained.currentAnchorId) !== null && _b !== void 0 ? _b : null) : previousAnchorId;
7351
+ var unchanged = Math.abs(constrained.position.x - port.position.x) <= POSITION_EPSILON &&
7352
+ Math.abs(constrained.position.y - port.position.y) <= POSITION_EPSILON &&
7353
+ previousAnchorId === nextAnchorId;
7354
+ if (unchanged)
7355
+ return;
7356
+ var update = {
7357
+ position: constrained.position,
7358
+ };
7359
+ if (shouldPersistAnchorId) {
7360
+ update.currentAnchorId = constrained.currentAnchorId;
7361
+ }
7362
+ var patches = _this.commandQueue.run(createMovePortCommand(port.id, update), _this.model);
7363
+ normalizedPatches.push.apply(normalizedPatches, patches);
7364
+ movedPortIds.add(port.id);
7365
+ });
7366
+ return { patches: normalizedPatches, movedPortIds: Array.from(movedPortIds) };
7367
+ };
6790
7368
  DiagramEngine.prototype.computeRemovalDiff = function (before) {
6791
7369
  var after = this.model.toState();
6792
7370
  var removedPatches = [];
@@ -6997,6 +7575,21 @@ var KonvaNodeFactory = /** @class */ (function () {
6997
7575
  }
6998
7576
  return node;
6999
7577
  };
7578
+ KonvaNodeFactory.prototype.createTextBackgroundNode = function (config) {
7579
+ return new this.konva.Rect({
7580
+ id: config.id,
7581
+ x: config.x,
7582
+ y: config.y,
7583
+ width: config.width,
7584
+ height: config.height,
7585
+ fill: config.fill,
7586
+ stroke: config.stroke,
7587
+ strokeWidth: config.strokeWidth,
7588
+ cornerRadius: config.cornerRadius,
7589
+ name: 'text-background',
7590
+ listening: false,
7591
+ });
7592
+ };
7000
7593
  KonvaNodeFactory.prototype.createHandleNode = function (config) {
7001
7594
  return new this.konva.Rect({
7002
7595
  id: config.id,
@@ -7177,6 +7770,7 @@ var KonvaRenderer = /** @class */ (function () {
7177
7770
  this.portNodes = new Map();
7178
7771
  this.linkNodes = new Map();
7179
7772
  this.textNodes = new Map();
7773
+ this.textBackgroundNodes = new Map();
7180
7774
  this.selectedIds = new Set();
7181
7775
  this.tempLinkNode = null;
7182
7776
  this.tempPortNode = null;
@@ -7476,6 +8070,8 @@ var KonvaRenderer = /** @class */ (function () {
7476
8070
  this.elementNodes.clear();
7477
8071
  this.portNodes.clear();
7478
8072
  this.linkNodes.clear();
8073
+ this.textBackgroundNodes.forEach(function (node) { var _a; return (_a = node.destroy) === null || _a === void 0 ? void 0 : _a.call(node); });
8074
+ this.textBackgroundNodes.clear();
7479
8075
  this.textNodes.clear();
7480
8076
  this.resizeHandleNodes.clear();
7481
8077
  this.linkHandleNodes.clear();
@@ -7682,7 +8278,7 @@ var KonvaRenderer = /** @class */ (function () {
7682
8278
  var _this = this;
7683
8279
  var texts = Array.from(model.texts.values());
7684
8280
  texts.forEach(function (text) {
7685
- var _a, _b, _c, _d, _e, _f, _g, _h;
8281
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
7686
8282
  var node = _this.textNodes.get(text.id);
7687
8283
  if (!node) {
7688
8284
  node = _this.nodeFactory.createTextNode(text.toData());
@@ -7691,32 +8287,124 @@ var KonvaRenderer = /** @class */ (function () {
7691
8287
  }
7692
8288
  var position = (_c = model.getTextWorldPosition(text.id)) !== null && _c !== void 0 ? _c : text.position;
7693
8289
  var displayOffset = (_d = text.displayOffset) !== null && _d !== void 0 ? _d : { x: 0, y: 0 };
7694
- _this.updatePosition(node, {
8290
+ var textPosition = {
7695
8291
  x: position.x + displayOffset.x,
7696
8292
  y: position.y + displayOffset.y,
8293
+ };
8294
+ _this.updatePosition(node, {
8295
+ x: textPosition.x,
8296
+ y: textPosition.y,
7697
8297
  });
7698
8298
  if (node.setAttrs) {
7699
- var style = text.style;
7700
- var defaults = resolveTextStyleDefaults(style);
7701
- var baseFill = (_g = (_f = (_e = style === null || style === void 0 ? void 0 : style.fill) !== null && _e !== void 0 ? _e : _this.getNodeAttr(node, '__baseFill')) !== null && _f !== void 0 ? _f : _this.getNodeAttr(node, 'fill')) !== null && _g !== void 0 ? _g : 'black';
8299
+ var style_1 = text.style;
8300
+ var defaults = resolveTextStyleDefaults(style_1);
8301
+ var baseFill = (_g = (_f = (_e = style_1 === null || style_1 === void 0 ? void 0 : style_1.fill) !== null && _e !== void 0 ? _e : _this.getNodeAttr(node, '__baseFill')) !== null && _f !== void 0 ? _f : _this.getNodeAttr(node, 'fill')) !== null && _g !== void 0 ? _g : 'black';
7702
8302
  var fill = _this.selectedIds.has(text.id) ? '#ff7a00' : baseFill;
7703
- var clip = text.displayClipSize;
7704
- node.setAttrs(__assign(__assign({ text: (_h = text.displayContent) !== null && _h !== void 0 ? _h : text.content, fontSize: defaults.fontSize, fontFamily: defaults.fontFamily, lineHeight: defaults.lineHeight }, (style !== null && style !== void 0 ? style : {})), { width: clip === null || clip === void 0 ? void 0 : clip.width, height: clip === null || clip === void 0 ? void 0 : clip.height, wrap: clip ? 'none' : undefined, ellipsis: false, fill: fill, __baseFill: baseFill,
8303
+ var clip_1 = text.displayClipSize;
8304
+ node.setAttrs(__assign(__assign({ text: (_h = text.displayContent) !== null && _h !== void 0 ? _h : text.content, fontSize: defaults.fontSize, fontFamily: defaults.fontFamily, lineHeight: defaults.lineHeight }, (style_1 !== null && style_1 !== void 0 ? style_1 : {})), { width: clip_1 === null || clip_1 === void 0 ? void 0 : clip_1.width, height: clip_1 === null || clip_1 === void 0 ? void 0 : clip_1.height, wrap: clip_1 ? 'none' : undefined, ellipsis: false, fill: fill, __baseFill: baseFill,
7705
8305
  // Keep metadata for tests/debugging; Konva clip attrs do not apply on Text nodes.
7706
- clipX: clip ? 0 : undefined, clipY: clip ? 0 : undefined, clipWidth: clip === null || clip === void 0 ? void 0 : clip.width, clipHeight: clip === null || clip === void 0 ? void 0 : clip.height }));
8306
+ clipX: clip_1 ? 0 : undefined, clipY: clip_1 ? 0 : undefined, clipWidth: clip_1 === null || clip_1 === void 0 ? void 0 : clip_1.width, clipHeight: clip_1 === null || clip_1 === void 0 ? void 0 : clip_1.height }));
8307
+ }
8308
+ var style = text.style;
8309
+ var backgroundFill = style === null || style === void 0 ? void 0 : style.backgroundFill;
8310
+ var clip = text.displayClipSize;
8311
+ var backgroundWidth = (_j = clip === null || clip === void 0 ? void 0 : clip.width) !== null && _j !== void 0 ? _j : (_k = text.size) === null || _k === void 0 ? void 0 : _k.width;
8312
+ var backgroundHeight = (_l = clip === null || clip === void 0 ? void 0 : clip.height) !== null && _l !== void 0 ? _l : (_m = text.size) === null || _m === void 0 ? void 0 : _m.height;
8313
+ var backgroundMeta = _this.resolveTextBackgroundMeta(model, text.toData());
8314
+ var hasBackground = typeof backgroundFill === 'string' &&
8315
+ backgroundFill.length > 0 &&
8316
+ typeof backgroundWidth === 'number' &&
8317
+ typeof backgroundHeight === 'number' &&
8318
+ backgroundWidth > 0 &&
8319
+ backgroundHeight > 0;
8320
+ if (hasBackground) {
8321
+ var baseX = textPosition.x + backgroundMeta.inset;
8322
+ var baseY = textPosition.y + backgroundMeta.inset;
8323
+ var width = Math.max(0, backgroundWidth - backgroundMeta.inset * 2);
8324
+ var height = Math.max(0, backgroundHeight - backgroundMeta.inset);
8325
+ var backgroundStroke = typeof (style === null || style === void 0 ? void 0 : style.backgroundStroke) === 'string' && style.backgroundStroke.length > 0
8326
+ ? style.backgroundStroke
8327
+ : undefined;
8328
+ var backgroundStrokeWidth = typeof (style === null || style === void 0 ? void 0 : style.backgroundStrokeWidth) === 'number'
8329
+ ? Math.max(0, style.backgroundStrokeWidth)
8330
+ : undefined;
8331
+ var backgroundNode = _this.textBackgroundNodes.get(text.id);
8332
+ if (!backgroundNode) {
8333
+ backgroundNode = _this.nodeFactory.createTextBackgroundNode({
8334
+ id: "text-background:".concat(text.id),
8335
+ x: baseX,
8336
+ y: baseY,
8337
+ width: width,
8338
+ height: height,
8339
+ fill: backgroundFill,
8340
+ stroke: backgroundStroke,
8341
+ strokeWidth: backgroundStrokeWidth,
8342
+ cornerRadius: backgroundMeta.cornerRadius,
8343
+ });
8344
+ _this.textBackgroundNodes.set(text.id, backgroundNode);
8345
+ (_p = (_o = _this.layers.getLayer('texts')) === null || _o === void 0 ? void 0 : _o.add) === null || _p === void 0 ? void 0 : _p.call(_o, backgroundNode);
8346
+ }
8347
+ else {
8348
+ _this.updatePosition(backgroundNode, { x: baseX, y: baseY });
8349
+ (_q = backgroundNode.setAttrs) === null || _q === void 0 ? void 0 : _q.call(backgroundNode, {
8350
+ width: width,
8351
+ height: height,
8352
+ fill: backgroundFill,
8353
+ stroke: backgroundStroke,
8354
+ strokeWidth: backgroundStrokeWidth,
8355
+ cornerRadius: backgroundMeta.cornerRadius,
8356
+ listening: false,
8357
+ name: 'text-background',
8358
+ });
8359
+ }
8360
+ (_r = node.moveToTop) === null || _r === void 0 ? void 0 : _r.call(node);
8361
+ }
8362
+ else {
8363
+ var backgroundNode = _this.textBackgroundNodes.get(text.id);
8364
+ (_s = backgroundNode === null || backgroundNode === void 0 ? void 0 : backgroundNode.destroy) === null || _s === void 0 ? void 0 : _s.call(backgroundNode);
8365
+ _this.textBackgroundNodes.delete(text.id);
7707
8366
  }
7708
8367
  });
7709
8368
  Array.from(this.textNodes.keys()).forEach(function (id) {
7710
- var _a;
8369
+ var _a, _b;
7711
8370
  if (!model.texts.has(id)) {
8371
+ var backgroundNode = _this.textBackgroundNodes.get(id);
8372
+ (_a = backgroundNode === null || backgroundNode === void 0 ? void 0 : backgroundNode.destroy) === null || _a === void 0 ? void 0 : _a.call(backgroundNode);
8373
+ _this.textBackgroundNodes.delete(id);
7712
8374
  var node = _this.textNodes.get(id);
7713
- (_a = node === null || node === void 0 ? void 0 : node.destroy) === null || _a === void 0 ? void 0 : _a.call(node);
8375
+ (_b = node === null || node === void 0 ? void 0 : node.destroy) === null || _b === void 0 ? void 0 : _b.call(node);
7714
8376
  _this.textNodes.delete(id);
7715
8377
  _this.layers.markDirty('texts');
7716
8378
  }
7717
8379
  });
7718
8380
  this.layers.markDirty('texts');
7719
8381
  };
8382
+ KonvaRenderer.prototype.resolveTextBackgroundMeta = function (model, text) {
8383
+ var _a;
8384
+ var ownerId = (_a = text.ownerId) !== null && _a !== void 0 ? _a : null;
8385
+ if (!ownerId)
8386
+ return { inset: 0 };
8387
+ var owner = model.getElement(ownerId);
8388
+ if (!owner)
8389
+ return { inset: 0 };
8390
+ var ownerStyle = owner.style;
8391
+ var strokeWidth = typeof (ownerStyle === null || ownerStyle === void 0 ? void 0 : ownerStyle.strokeWidth) === 'number' ? Math.max(0, ownerStyle.strokeWidth) : 0;
8392
+ var inset = strokeWidth > 0 ? strokeWidth / 2 : 0;
8393
+ var rawCornerRadius = ownerStyle === null || ownerStyle === void 0 ? void 0 : ownerStyle.cornerRadius;
8394
+ var isTopLabel = text.position.y <= 1;
8395
+ if (!isTopLabel)
8396
+ return { inset: inset };
8397
+ if (typeof rawCornerRadius === 'number' && rawCornerRadius > 0) {
8398
+ var topRadius = Math.max(0, rawCornerRadius - inset);
8399
+ return { inset: inset, cornerRadius: [topRadius, topRadius, 0, 0] };
8400
+ }
8401
+ if (Array.isArray(rawCornerRadius) && rawCornerRadius.length === 4) {
8402
+ var tl = typeof rawCornerRadius[0] === 'number' ? Math.max(0, rawCornerRadius[0] - inset) : 0;
8403
+ var tr = typeof rawCornerRadius[1] === 'number' ? Math.max(0, rawCornerRadius[1] - inset) : 0;
8404
+ return { inset: inset, cornerRadius: [tl, tr, 0, 0] };
8405
+ }
8406
+ return { inset: inset };
8407
+ };
7720
8408
  KonvaRenderer.prototype.updatePosition = function (node, position) {
7721
8409
  if (node.position) {
7722
8410
  node.position({ x: position.x, y: position.y });
@@ -9731,14 +10419,19 @@ var KonvaInteraction = /** @class */ (function () {
9731
10419
  this.engine.setSelection(Array.from(selected));
9732
10420
  };
9733
10421
  KonvaInteraction.prototype.applyPortConstraints = function (portId, worldTarget) {
10422
+ var _a;
9734
10423
  var port = this.getPortById(portId);
9735
10424
  if (!port)
9736
10425
  return worldTarget;
10426
+ var element = this.getElementById(port.elementId);
10427
+ if (!element)
10428
+ return worldTarget;
9737
10429
  var elementPos = this.engine.getElementWorldPosition(port.elementId);
9738
10430
  if (!elementPos)
9739
10431
  return worldTarget;
9740
10432
  var relative = { x: worldTarget.x - elementPos.x, y: worldTarget.y - elementPos.y };
9741
10433
  var style = port.style;
10434
+ var limits = (_a = element.portMovement) === null || _a === void 0 ? void 0 : _a.positionLimits;
9742
10435
  var constrained = __assign({}, relative);
9743
10436
  if ((style === null || style === void 0 ? void 0 : style.moveAxis) === 'horizontal') {
9744
10437
  constrained.y = port.position.y;
@@ -9751,6 +10444,18 @@ var KonvaInteraction = /** @class */ (function () {
9751
10444
  constrained.x = Math.min(bounds.x + bounds.width, Math.max(bounds.x, constrained.x));
9752
10445
  constrained.y = Math.min(bounds.y + bounds.height, Math.max(bounds.y, constrained.y));
9753
10446
  }
10447
+ if (limits === null || limits === void 0 ? void 0 : limits.x) {
10448
+ if (typeof limits.x.min === 'number')
10449
+ constrained.x = Math.max(constrained.x, limits.x.min);
10450
+ if (typeof limits.x.max === 'number')
10451
+ constrained.x = Math.min(constrained.x, limits.x.max);
10452
+ }
10453
+ if (limits === null || limits === void 0 ? void 0 : limits.y) {
10454
+ if (typeof limits.y.min === 'number')
10455
+ constrained.y = Math.max(constrained.y, limits.y.min);
10456
+ if (typeof limits.y.max === 'number')
10457
+ constrained.y = Math.min(constrained.y, limits.y.max);
10458
+ }
9754
10459
  return { x: elementPos.x + constrained.x, y: elementPos.y + constrained.y };
9755
10460
  };
9756
10461
  KonvaInteraction.prototype.resolveLinkPreviewSource = function (sourcePortId, pointer, hit) {
@@ -12323,10 +13028,11 @@ var ObstacleRoutingDemo = function () { return (React.createElement(SimpleDemo,
12323
13028
  } },
12324
13029
  React.createElement("strong", null, "Expected routing behavior"),
12325
13030
  React.createElement("ul", { style: { margin: '6px 0 0 16px' } },
12326
- React.createElement("li", null, "Links between top nodes should route around the blocking element."),
12327
- React.createElement("li", null, "Sibling links inside a shared parent should ignore the parent as an obstacle."),
12328
- React.createElement("li", null, "Parent-child links should stay within the parent and avoid the child interior when ports are on edges."),
12329
- React.createElement("li", null, "Grandchild links should ignore shared ancestors (parent + grandparent containers)."))) })); };
13031
+ React.createElement("li", null, "Scenario A: the sibling link must not cross either child host interior."),
13032
+ React.createElement("li", null, "Scenario A: sibling host ports use anchor preset `cardinal` (left/right) for deterministic multi-anchor placement."),
13033
+ React.createElement("li", null, "Scenario B has one parent and two children with three links: parent->child, child->parent, and child->child."),
13034
+ React.createElement("li", null, "Scenario B: ancestor endpoints resolve to `internalLinkAttachPoint`; descendant/sibling endpoints resolve to `externalLinkAttachPoint`."),
13035
+ React.createElement("li", null, "Use `Reroute All Links` repeatedly; both routes should stay deterministic after each reroute."))) })); };
12330
13036
 
12331
13037
  var initialPayloads = {
12332
13038
  elementDeleted: null,
@@ -13314,6 +14020,18 @@ var demoTabs = [
13314
14020
  description: portConstraintsDemoConfig.description,
13315
14021
  Component: wrapSimpleDemo(portConstraintsDemoConfig),
13316
14022
  },
14023
+ {
14024
+ id: portPositionLimitsDemoConfig.id,
14025
+ title: portPositionLimitsDemoConfig.title,
14026
+ description: portPositionLimitsDemoConfig.description,
14027
+ Component: wrapSimpleDemo(portPositionLimitsDemoConfig),
14028
+ },
14029
+ {
14030
+ id: labelStyleDemoConfig.id,
14031
+ title: labelStyleDemoConfig.title,
14032
+ description: labelStyleDemoConfig.description,
14033
+ Component: wrapSimpleDemo(labelStyleDemoConfig),
14034
+ },
13317
14035
  {
13318
14036
  id: portBorderDemoConfig.id,
13319
14037
  title: portBorderDemoConfig.title,