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
@@ -1973,6 +1973,213 @@ var portConstraintsDemoConfig = ({
1973
1973
  ],
1974
1974
  });
1975
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
+
1976
2183
  var createPortBorderState = function () { return ({
1977
2184
  elements: [
1978
2185
  {
@@ -2127,6 +2334,104 @@ var childConstraintsDemoConfig = ({
2127
2334
  ],
2128
2335
  });
2129
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
+
2130
2435
  var createGridOverlayState = function () { return ({
2131
2436
  elements: [
2132
2437
  {
@@ -2228,141 +2533,174 @@ var routingDemoConfig = ({
2228
2533
 
2229
2534
  var obstacleRoutingLinks = [
2230
2535
  {
2231
- id: 'block-link',
2232
- sourcePortId: 'block-source-port',
2233
- targetPortId: 'block-target-port',
2536
+ id: 'sibling-external-link',
2537
+ sourcePortId: 'sibling-a-port',
2538
+ targetPortId: 'sibling-b-port',
2234
2539
  points: [],
2235
2540
  routing: 'auto',
2236
2541
  },
2237
2542
  {
2238
- id: 'shared-sibling-link',
2239
- sourcePortId: 'shared-child-a-port',
2240
- targetPortId: 'shared-child-b-port',
2543
+ id: 'parent-to-child-link',
2544
+ sourcePortId: 'attach-parent-port',
2545
+ targetPortId: 'attach-child-a-port',
2241
2546
  points: [],
2242
2547
  routing: 'auto',
2243
2548
  },
2244
2549
  {
2245
- id: 'parent-child-link',
2246
- sourcePortId: 'parent-host-port',
2247
- targetPortId: 'edge-child-port',
2550
+ id: 'child-to-parent-link',
2551
+ sourcePortId: 'attach-child-b-port',
2552
+ targetPortId: 'attach-parent-port',
2248
2553
  points: [],
2249
2554
  routing: 'auto',
2250
2555
  },
2251
2556
  {
2252
- id: 'grandchild-link',
2253
- sourcePortId: 'grandchild-a-port',
2254
- targetPortId: 'grandchild-b-port',
2557
+ id: 'child-to-child-link',
2558
+ sourcePortId: 'attach-child-a-port',
2559
+ targetPortId: 'attach-child-b-port',
2255
2560
  points: [],
2256
2561
  routing: 'auto',
2257
2562
  },
2258
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);
2259
2575
  var createObstacleRoutingState = function () { return ({
2260
2576
  elements: [
2261
2577
  {
2262
- id: 'block-source',
2263
- position: { x: 60, y: 40 },
2264
- size: { width: 140, height: 90 },
2265
- 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' },
2266
2583
  },
2267
2584
  {
2268
- id: 'block-target',
2269
- position: { x: 380, y: 40 },
2270
- size: { width: 140, height: 90 },
2271
- 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' } },
2272
2591
  },
2273
2592
  {
2274
- id: 'block-obstacle',
2275
- position: { x: 250, y: 30 },
2276
- size: { width: 80, height: 110 },
2277
- 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' } },
2278
2599
  },
2279
2600
  {
2280
- id: 'shared-parent',
2281
- position: { x: 40, y: 180 },
2282
- size: { width: 420, height: 220 },
2283
- shapeId: 'panel',
2284
- 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' } },
2285
2606
  },
2286
2607
  {
2287
- id: 'shared-child-a',
2288
- position: { x: 40, y: 40 },
2289
- size: { width: 140, height: 90 },
2290
- shapeId: 'default',
2291
- 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' } },
2292
2614
  },
2293
2615
  {
2294
- id: 'shared-child-b',
2295
- position: { x: 220, y: 40 },
2296
- size: { width: 140, height: 90 },
2297
- shapeId: 'default',
2298
- 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' } },
2299
2622
  },
2623
+ ],
2624
+ ports: [
2300
2625
  {
2301
- id: 'parent-host',
2302
- position: { x: 500, y: 180 },
2303
- size: { width: 320, height: 220 },
2304
- shapeId: 'panel',
2305
- 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',
2306
2632
  },
2307
2633
  {
2308
- id: 'edge-child',
2309
- position: { x: 40, y: 60 },
2310
- size: { width: 160, height: 90 },
2311
- shapeId: 'default',
2312
- 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',
2313
2640
  },
2314
2641
  {
2315
- id: 'grandparent',
2316
- position: { x: 40, y: 380 },
2317
- size: { width: 780, height: 160 },
2318
- shapeId: 'panel',
2319
- 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 },
2650
+ },
2651
+ {
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 },
2320
2670
  },
2671
+ ],
2672
+ links: obstacleRoutingLinks,
2673
+ texts: [
2321
2674
  {
2322
- id: 'mid-parent-a',
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.',
2323
2677
  position: { x: 40, y: 30 },
2324
- size: { width: 300, height: 100 },
2325
- shapeId: 'panel',
2326
- parentId: 'grandparent',
2327
2678
  },
2328
2679
  {
2329
- id: 'mid-parent-b',
2330
- position: { x: 420, y: 30 },
2331
- size: { width: 300, height: 100 },
2332
- shapeId: 'panel',
2333
- 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 },
2334
2683
  },
2335
2684
  {
2336
- id: 'grandchild-a',
2337
- position: { x: 40, y: 20 },
2338
- size: { width: 120, height: 60 },
2339
- shapeId: 'default',
2340
- 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 },
2341
2688
  },
2342
2689
  {
2343
- id: 'grandchild-b',
2344
- position: { x: 40, y: 20 },
2345
- size: { width: 120, height: 60 },
2346
- shapeId: 'default',
2347
- 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 },
2348
2703
  },
2349
- ],
2350
- ports: [
2351
- { id: 'block-source-port', elementId: 'block-source', position: { x: 70, y: 45 }, shapeId: 'port-circle' },
2352
- { id: 'block-target-port', elementId: 'block-target', position: { x: 70, y: 45 }, shapeId: 'port-circle' },
2353
- { id: 'shared-child-a-port', elementId: 'shared-child-a', position: { x: 70, y: 45 }, shapeId: 'port-circle' },
2354
- { id: 'shared-child-b-port', elementId: 'shared-child-b', position: { x: 70, y: 45 }, shapeId: 'port-circle' },
2355
- { id: 'parent-host-port', elementId: 'parent-host', position: { x: 250, y: 110 }, shapeId: 'port-circle' },
2356
- { id: 'edge-child-port', elementId: 'edge-child', position: { x: 0, y: 45 }, shapeId: 'port-circle' },
2357
- { id: 'grandchild-a-port', elementId: 'grandchild-a', position: { x: 60, y: 30 }, shapeId: 'port-circle' },
2358
- { id: 'grandchild-b-port', elementId: 'grandchild-b', position: { x: 60, y: 30 }, shapeId: 'port-circle' },
2359
- ],
2360
- links: obstacleRoutingLinks,
2361
- texts: [
2362
- { id: 'block-label', content: 'Blocking obstacle', position: { x: 60, y: 16 } },
2363
- { id: 'shared-label', content: 'Shared parent (siblings)', position: { x: 40, y: 158 } },
2364
- { id: 'parent-label', content: 'Parent-child (stay inside)', position: { x: 500, y: 158 } },
2365
- { id: 'grand-label', content: 'Grandchildren (shared ancestors)', position: { x: 40, y: 358 } },
2366
2704
  ],
2367
2705
  }); };
2368
2706
  var addLinksAction = {
@@ -2384,17 +2722,17 @@ var rerouteAllAction = {
2384
2722
  editor.rerouteAllLinks();
2385
2723
  },
2386
2724
  };
2387
- var obstacleRoutingDemoConfig = ({
2725
+ var obstacleRoutingDemoConfig = {
2388
2726
  id: 'obstacle-routing',
2389
2727
  title: 'Obstacle Routing',
2390
- 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.',
2391
2729
  createState: createObstacleRoutingState,
2392
- elementShapes: baseElementShapes,
2730
+ elementShapes: obstacleRoutingShapes,
2393
2731
  portShapes: basePortShapes,
2394
2732
  defaultElementShapeId: 'default',
2395
2733
  defaultPortShapeId: 'port-circle',
2396
2734
  actions: [addLinksAction, rerouteAllAction],
2397
- });
2735
+ };
2398
2736
 
2399
2737
  var createLinkBendHandlesState = function () { return ({
2400
2738
  elements: [
@@ -4384,21 +4722,42 @@ var ObstacleRouter = /** @class */ (function () {
4384
4722
  ObstacleRouter.prototype.computeStubEndpoint = function (point, endpoint, bounds, obstacles) {
4385
4723
  if (!(endpoint === null || endpoint === void 0 ? void 0 : endpoint.onEdgeSide) || !endpoint.rect)
4386
4724
  return null;
4387
- 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) {
4388
4746
  var maxLength = this.stubLength;
4389
4747
  if (bounds) {
4390
4748
  maxLength = Math.min(maxLength, this.distanceToBounds(point, normal, bounds));
4391
4749
  }
4392
- var obstacleLimit = this.distanceToObstacles(point, normal, obstacles, endpoint.elementId);
4750
+ var obstacleLimit = this.distanceToObstacles(point, normal, obstacles, ignoreId);
4393
4751
  if (obstacleLimit !== null) {
4394
4752
  maxLength = Math.min(maxLength, obstacleLimit);
4395
4753
  }
4396
- if (maxLength <= 0)
4397
- return null;
4398
- return {
4399
- x: point.x + normal.x * maxLength,
4400
- y: point.y + normal.y * maxLength,
4401
- };
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);
4402
4761
  };
4403
4762
  ObstacleRouter.prototype.getNormal = function (side) {
4404
4763
  switch (side) {
@@ -4695,7 +5054,7 @@ var borderSideToInwardRotation = function (side) {
4695
5054
  return 0;
4696
5055
  };
4697
5056
 
4698
- var POSITION_EPSILON$1 = 1e-6;
5057
+ var POSITION_EPSILON$2 = 1e-6;
4699
5058
  var MIN_TRUNCATION_WIDTH_SAFETY_EPSILON = 0.5;
4700
5059
  var FONT_SIZE_TRUNCATION_SAFETY_RATIO = 1.4;
4701
5060
  var FONT_SIZE_TRUNCATION_SAFETY_BASE = 2;
@@ -4721,7 +5080,7 @@ var TextLayoutService = /** @class */ (function () {
4721
5080
  var textRenderPadding = this.resolveTextRenderPadding(text.style);
4722
5081
  var horizontalRenderInset = textRenderPadding * 2;
4723
5082
  var verticalRenderInset = textRenderPadding * 2;
4724
- 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;
4725
5084
  var drawableWidth = maxWidth !== undefined ? Math.max(0, maxWidth - zeroPaddingInset * 2 - horizontalRenderInset) : undefined;
4726
5085
  var drawableHeight = maxHeight !== undefined ? Math.max(0, maxHeight - zeroPaddingInset * 2 - verticalRenderInset) : undefined;
4727
5086
  var lineHeight = this.textMeasurer.measure(__assign(__assign({}, text), { content: 'M' })).height;
@@ -5419,6 +5778,7 @@ var resolvePortWorldTransform = function (options) {
5419
5778
  };
5420
5779
 
5421
5780
  var EDGE_TOLERANCE = 0.5;
5781
+ var POSITION_EPSILON$1 = 1e-6;
5422
5782
  var LinkRoutingService = /** @class */ (function () {
5423
5783
  function LinkRoutingService(config) {
5424
5784
  this.model = config.model;
@@ -5527,11 +5887,27 @@ var LinkRoutingService = /** @class */ (function () {
5527
5887
  if (points.length < 2) {
5528
5888
  return [__assign({}, source), __assign({}, target)];
5529
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;
5530
5900
  return points.map(function (point, index) {
5531
5901
  if (index === 0)
5532
5902
  return __assign({}, source);
5533
5903
  if (index === points.length - 1)
5534
5904
  return __assign({}, target);
5905
+ if (shouldTranslateAllPoints) {
5906
+ return {
5907
+ x: point.x + sourceDelta.x,
5908
+ y: point.y + sourceDelta.y,
5909
+ };
5910
+ }
5535
5911
  return __assign({}, point);
5536
5912
  });
5537
5913
  };
@@ -5697,16 +6073,13 @@ var LinkRoutingService = /** @class */ (function () {
5697
6073
  var oppositeElementId = (_b = this.model.getPort(oppositePortId)) === null || _b === void 0 ? void 0 : _b.elementId;
5698
6074
  if (!elementId || !oppositeElementId)
5699
6075
  return 'external';
5700
- return this.hasAncestorRelation(elementId, oppositeElementId) ? 'internal' : 'external';
6076
+ return this.isAncestorOf(elementId, oppositeElementId) ? 'internal' : 'external';
5701
6077
  };
5702
- LinkRoutingService.prototype.hasAncestorRelation = function (sourceElementId, targetElementId) {
5703
- if (sourceElementId === targetElementId)
6078
+ LinkRoutingService.prototype.isAncestorOf = function (candidateAncestorId, candidateDescendantId) {
6079
+ if (candidateAncestorId === candidateDescendantId)
5704
6080
  return false;
5705
- var sourceChain = this.getAncestorChain(sourceElementId);
5706
- if (sourceChain.includes(targetElementId))
5707
- return true;
5708
- var targetChain = this.getAncestorChain(targetElementId);
5709
- return targetChain.includes(sourceElementId);
6081
+ var descendantChain = this.getAncestorChain(candidateDescendantId);
6082
+ return descendantChain.includes(candidateAncestorId);
5710
6083
  };
5711
6084
  LinkRoutingService.prototype.getAncestorChain = function (elementId) {
5712
6085
  var _a;
@@ -5866,11 +6239,17 @@ var DiagramEngine = /** @class */ (function () {
5866
6239
  DiagramEngine.prototype.load = function (state) {
5867
6240
  var _this = this;
5868
6241
  var patches = this.commandQueue.run(createLoadCommand(state), this.model);
5869
- var allPatches = this.mutationPipeline.run({
6242
+ var mutationPatches = this.mutationPipeline.run({
5870
6243
  basePatches: patches,
5871
6244
  layoutSteps: [function () { return _this.applyAllLayouts(); }],
5872
6245
  includeEmptyLinkRouting: true,
5873
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);
5874
6253
  this.emitChange(allPatches);
5875
6254
  };
5876
6255
  DiagramEngine.prototype.getState = function () {
@@ -6462,7 +6841,7 @@ var DiagramEngine = /** @class */ (function () {
6462
6841
  if (!host)
6463
6842
  return;
6464
6843
  host.portIds.forEach(function (portId) {
6465
- var _a;
6844
+ var _a, _b, _c;
6466
6845
  var port = _this.model.getPort(portId);
6467
6846
  if (!port)
6468
6847
  return;
@@ -6485,11 +6864,16 @@ var DiagramEngine = /** @class */ (function () {
6485
6864
  return;
6486
6865
  }
6487
6866
  var projected = _this.resolveBorderPortResizeProjection(port.position, host.shapeId, sizeInfo, host.size);
6488
- var unchanged = Math.abs(projected.x - port.position.x) <= POSITION_EPSILON &&
6489
- 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);
6490
6871
  if (unchanged)
6491
6872
  return;
6492
- 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);
6493
6877
  reprojectionPatches.push.apply(reprojectionPatches, patchesForPort);
6494
6878
  movedPortIds.add(port.id);
6495
6879
  });
@@ -6546,10 +6930,10 @@ var DiagramEngine = /** @class */ (function () {
6546
6930
  return this.projectPointToHostBorder(projectedTarget, shapeId, nextSize);
6547
6931
  };
6548
6932
  DiagramEngine.prototype.resolveConstrainedPortRelativePosition = function (port, element, requestedPosition) {
6549
- var _a, _b, _c, _d, _e;
6933
+ var _a, _b, _c, _d, _e, _f, _g;
6550
6934
  var position = __assign({}, requestedPosition);
6551
6935
  var effectiveMoveMode = this.resolveEffectivePortMoveMode(port, element);
6552
- if (effectiveMoveMode && effectiveMoveMode !== 'free' && effectiveMoveMode !== 'anchors') {
6936
+ if (effectiveMoveMode === 'inside' || effectiveMoveMode === 'border') {
6553
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));
6554
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));
6555
6939
  var bounds = {
@@ -6573,9 +6957,12 @@ var DiagramEngine = /** @class */ (function () {
6573
6957
  height: Math.max(0, element.size.height),
6574
6958
  };
6575
6959
  }
6576
- position = effectiveMoveMode === 'inside'
6577
- ? clampToRect(position, bounds)
6578
- : this.constrainPortToHostBorder(position, element);
6960
+ if (effectiveMoveMode === 'inside') {
6961
+ position = clampToRect(position, bounds);
6962
+ }
6963
+ else {
6964
+ position = this.constrainPortToHostBorder(position, element);
6965
+ }
6579
6966
  }
6580
6967
  var style = port.style;
6581
6968
  if ((style === null || style === void 0 ? void 0 : style.moveAxis) === 'horizontal') {
@@ -6589,18 +6976,134 @@ var DiagramEngine = /** @class */ (function () {
6589
6976
  position.x = clamp(position.x, bounds.x, bounds.x + bounds.width);
6590
6977
  position.y = clamp(position.y, bounds.y, bounds.y + bounds.height);
6591
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
+ }
6592
6986
  if (effectiveMoveMode === 'anchors') {
6593
- var anchor = this.resolveNearestPortAnchor(element, position);
6987
+ var anchor = this.resolveNearestPortAnchor(element, position, port.currentAnchorId);
6594
6988
  if (anchor) {
6595
6989
  return {
6596
6990
  position: __assign({}, anchor.position),
6597
6991
  currentAnchorId: anchor.id,
6598
6992
  };
6599
6993
  }
6600
- 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 };
6601
6995
  }
6602
6996
  return { position: position, currentAnchorId: null };
6603
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
+ };
6604
7107
  DiagramEngine.prototype.resolveEffectivePortMoveMode = function (port, element) {
6605
7108
  var _a, _b;
6606
7109
  return (_b = (_a = element.portMovement) === null || _a === void 0 ? void 0 : _a.moveMode) !== null && _b !== void 0 ? _b : port.moveMode;
@@ -6635,7 +7138,8 @@ var DiagramEngine = /** @class */ (function () {
6635
7138
  return [];
6636
7139
  };
6637
7140
  DiagramEngine.prototype.resolveNearestPortAnchor = function (element, target, preferredAnchorId) {
6638
- 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);
6639
7143
  if (anchors.length === 0)
6640
7144
  return null;
6641
7145
  if (preferredAnchorId) {
@@ -6700,6 +7204,46 @@ var DiagramEngine = /** @class */ (function () {
6700
7204
  DiagramEngine.prototype.resolveTextPresentation = function (text) {
6701
7205
  return this.textLayoutService.resolveTextPresentation(text);
6702
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
+ };
6703
7247
  DiagramEngine.prototype.emitSelection = function () {
6704
7248
  var _this = this;
6705
7249
  var selectedIds = this.selection.get();
@@ -6789,6 +7333,40 @@ var DiagramEngine = /** @class */ (function () {
6789
7333
  DiagramEngine.prototype.routeLinksWithEmptyPoints = function () {
6790
7334
  return this.linkRoutingService.routeLinksWithEmptyPoints();
6791
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
+ };
6792
7370
  DiagramEngine.prototype.computeRemovalDiff = function (before) {
6793
7371
  var after = this.model.toState();
6794
7372
  var removedPatches = [];
@@ -6999,6 +7577,21 @@ var KonvaNodeFactory = /** @class */ (function () {
6999
7577
  }
7000
7578
  return node;
7001
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
+ };
7002
7595
  KonvaNodeFactory.prototype.createHandleNode = function (config) {
7003
7596
  return new this.konva.Rect({
7004
7597
  id: config.id,
@@ -7179,6 +7772,7 @@ var KonvaRenderer = /** @class */ (function () {
7179
7772
  this.portNodes = new Map();
7180
7773
  this.linkNodes = new Map();
7181
7774
  this.textNodes = new Map();
7775
+ this.textBackgroundNodes = new Map();
7182
7776
  this.selectedIds = new Set();
7183
7777
  this.tempLinkNode = null;
7184
7778
  this.tempPortNode = null;
@@ -7478,6 +8072,8 @@ var KonvaRenderer = /** @class */ (function () {
7478
8072
  this.elementNodes.clear();
7479
8073
  this.portNodes.clear();
7480
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();
7481
8077
  this.textNodes.clear();
7482
8078
  this.resizeHandleNodes.clear();
7483
8079
  this.linkHandleNodes.clear();
@@ -7684,7 +8280,7 @@ var KonvaRenderer = /** @class */ (function () {
7684
8280
  var _this = this;
7685
8281
  var texts = Array.from(model.texts.values());
7686
8282
  texts.forEach(function (text) {
7687
- 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;
7688
8284
  var node = _this.textNodes.get(text.id);
7689
8285
  if (!node) {
7690
8286
  node = _this.nodeFactory.createTextNode(text.toData());
@@ -7693,32 +8289,124 @@ var KonvaRenderer = /** @class */ (function () {
7693
8289
  }
7694
8290
  var position = (_c = model.getTextWorldPosition(text.id)) !== null && _c !== void 0 ? _c : text.position;
7695
8291
  var displayOffset = (_d = text.displayOffset) !== null && _d !== void 0 ? _d : { x: 0, y: 0 };
7696
- _this.updatePosition(node, {
8292
+ var textPosition = {
7697
8293
  x: position.x + displayOffset.x,
7698
8294
  y: position.y + displayOffset.y,
8295
+ };
8296
+ _this.updatePosition(node, {
8297
+ x: textPosition.x,
8298
+ y: textPosition.y,
7699
8299
  });
7700
8300
  if (node.setAttrs) {
7701
- var style = text.style;
7702
- var defaults = resolveTextStyleDefaults(style);
7703
- 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';
7704
8304
  var fill = _this.selectedIds.has(text.id) ? '#ff7a00' : baseFill;
7705
- var clip = text.displayClipSize;
7706
- 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,
7707
8307
  // Keep metadata for tests/debugging; Konva clip attrs do not apply on Text nodes.
7708
- 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);
7709
8368
  }
7710
8369
  });
7711
8370
  Array.from(this.textNodes.keys()).forEach(function (id) {
7712
- var _a;
8371
+ var _a, _b;
7713
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);
7714
8376
  var node = _this.textNodes.get(id);
7715
- (_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);
7716
8378
  _this.textNodes.delete(id);
7717
8379
  _this.layers.markDirty('texts');
7718
8380
  }
7719
8381
  });
7720
8382
  this.layers.markDirty('texts');
7721
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
+ };
7722
8410
  KonvaRenderer.prototype.updatePosition = function (node, position) {
7723
8411
  if (node.position) {
7724
8412
  node.position({ x: position.x, y: position.y });
@@ -9733,14 +10421,19 @@ var KonvaInteraction = /** @class */ (function () {
9733
10421
  this.engine.setSelection(Array.from(selected));
9734
10422
  };
9735
10423
  KonvaInteraction.prototype.applyPortConstraints = function (portId, worldTarget) {
10424
+ var _a;
9736
10425
  var port = this.getPortById(portId);
9737
10426
  if (!port)
9738
10427
  return worldTarget;
10428
+ var element = this.getElementById(port.elementId);
10429
+ if (!element)
10430
+ return worldTarget;
9739
10431
  var elementPos = this.engine.getElementWorldPosition(port.elementId);
9740
10432
  if (!elementPos)
9741
10433
  return worldTarget;
9742
10434
  var relative = { x: worldTarget.x - elementPos.x, y: worldTarget.y - elementPos.y };
9743
10435
  var style = port.style;
10436
+ var limits = (_a = element.portMovement) === null || _a === void 0 ? void 0 : _a.positionLimits;
9744
10437
  var constrained = __assign({}, relative);
9745
10438
  if ((style === null || style === void 0 ? void 0 : style.moveAxis) === 'horizontal') {
9746
10439
  constrained.y = port.position.y;
@@ -9753,6 +10446,18 @@ var KonvaInteraction = /** @class */ (function () {
9753
10446
  constrained.x = Math.min(bounds.x + bounds.width, Math.max(bounds.x, constrained.x));
9754
10447
  constrained.y = Math.min(bounds.y + bounds.height, Math.max(bounds.y, constrained.y));
9755
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
+ }
9756
10461
  return { x: elementPos.x + constrained.x, y: elementPos.y + constrained.y };
9757
10462
  };
9758
10463
  KonvaInteraction.prototype.resolveLinkPreviewSource = function (sourcePortId, pointer, hit) {
@@ -12325,10 +13030,11 @@ var ObstacleRoutingDemo = function () { return (React.createElement(SimpleDemo,
12325
13030
  } },
12326
13031
  React.createElement("strong", null, "Expected routing behavior"),
12327
13032
  React.createElement("ul", { style: { margin: '6px 0 0 16px' } },
12328
- React.createElement("li", null, "Links between top nodes should route around the blocking element."),
12329
- React.createElement("li", null, "Sibling links inside a shared parent should ignore the parent as an obstacle."),
12330
- React.createElement("li", null, "Parent-child links should stay within the parent and avoid the child interior when ports are on edges."),
12331
- 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."))) })); };
12332
13038
 
12333
13039
  var initialPayloads = {
12334
13040
  elementDeleted: null,
@@ -13316,6 +14022,18 @@ var demoTabs = [
13316
14022
  description: portConstraintsDemoConfig.description,
13317
14023
  Component: wrapSimpleDemo(portConstraintsDemoConfig),
13318
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
+ },
13319
14037
  {
13320
14038
  id: portBorderDemoConfig.id,
13321
14039
  title: portBorderDemoConfig.title,