orcasvn-react-diagrams 0.2.4 → 0.2.5

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 (53) hide show
  1. package/CHANGELOG.md +19 -4
  2. package/README.md +17 -15
  3. package/ai/api-contract.json +50 -2
  4. package/ai/manifest.json +1 -1
  5. package/dist/cjs/examples.js +2059 -1102
  6. package/dist/cjs/index.js +703 -105
  7. package/dist/cjs/types/api/createDiagramEditor.d.ts +3 -1
  8. package/dist/cjs/types/api/types.d.ts +18 -1
  9. package/dist/cjs/types/displaybox/demos/LinkColorPoolDemoTab.d.ts +3 -0
  10. package/dist/cjs/types/displaybox/demos/SimpleDemo.d.ts +4 -1
  11. package/dist/cjs/types/displaybox/demos/linkColorPoolDemo.d.ts +2 -0
  12. package/dist/cjs/types/displaybox/types.d.ts +3 -1
  13. package/dist/cjs/types/displaybox/useDemoEditor.d.ts +4 -2
  14. package/dist/cjs/types/engine/AutoLayoutService.d.ts +15 -1
  15. package/dist/cjs/types/engine/DiagramEngine.d.ts +8 -1
  16. package/dist/cjs/types/engine/LinkRoutingService.d.ts +8 -0
  17. package/dist/cjs/types/renderer/konva/KonvaInteraction.d.ts +4 -0
  18. package/dist/cjs/types/strategies/ObstacleRouter.d.ts +2 -0
  19. package/dist/cjs/types/strategies/RouterStrategy.d.ts +10 -0
  20. package/dist/esm/examples.js +2059 -1102
  21. package/dist/esm/examples.js.map +1 -1
  22. package/dist/esm/index.js +703 -105
  23. package/dist/esm/index.js.map +1 -1
  24. package/dist/esm/types/api/createDiagramEditor.d.ts +3 -1
  25. package/dist/esm/types/api/types.d.ts +18 -1
  26. package/dist/esm/types/displaybox/demos/LinkColorPoolDemoTab.d.ts +3 -0
  27. package/dist/esm/types/displaybox/demos/SimpleDemo.d.ts +4 -1
  28. package/dist/esm/types/displaybox/demos/linkColorPoolDemo.d.ts +2 -0
  29. package/dist/esm/types/displaybox/types.d.ts +3 -1
  30. package/dist/esm/types/displaybox/useDemoEditor.d.ts +4 -2
  31. package/dist/esm/types/engine/AutoLayoutService.d.ts +15 -1
  32. package/dist/esm/types/engine/DiagramEngine.d.ts +8 -1
  33. package/dist/esm/types/engine/LinkRoutingService.d.ts +8 -0
  34. package/dist/esm/types/renderer/konva/KonvaInteraction.d.ts +4 -0
  35. package/dist/esm/types/strategies/ObstacleRouter.d.ts +2 -0
  36. package/dist/esm/types/strategies/RouterStrategy.d.ts +10 -0
  37. package/dist/examples.d.ts +30 -1
  38. package/dist/index.d.ts +33 -2
  39. package/docs/API_CONTRACT.md +36 -1
  40. package/docs/CAPABILITIES.md +5 -0
  41. package/docs/DOCUMENTATION_WORKFLOW.md +3 -1
  42. package/package.json +1 -1
  43. package/src/displaybox/demos/AutoLayoutDemoTab.tsx +196 -83
  44. package/src/displaybox/demos/LinkColorPoolDemoTab.tsx +49 -0
  45. package/src/displaybox/demos/ObstacleRoutingDemoTab.tsx +52 -31
  46. package/src/displaybox/demos/SimpleDemo.tsx +18 -13
  47. package/src/displaybox/demos/autoLayoutDemo.ts +48 -13
  48. package/src/displaybox/demos/index.tsx +10 -2
  49. package/src/displaybox/demos/linkColorPoolDemo.ts +122 -0
  50. package/src/displaybox/demos/obstacleRoutingDemo.ts +77 -1
  51. package/src/displaybox/demos/routingDemo.ts +5 -5
  52. package/src/displaybox/demos/shared.ts +17 -12
  53. package/src/displaybox/types.ts +10 -8
@@ -82,6 +82,454 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
82
82
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
83
83
  };
84
84
 
85
+ var DEFAULT_PADDING = 12;
86
+ var DEFAULT_TOLERANCE = 0.5;
87
+ var DEFAULT_STUB_LENGTH = 12;
88
+ var shrinkRect = function (rect, amount) {
89
+ var width = rect.width - amount * 2;
90
+ var height = rect.height - amount * 2;
91
+ if (width <= 0 || height <= 0)
92
+ return null;
93
+ return {
94
+ x: rect.x + amount,
95
+ y: rect.y + amount,
96
+ width: width,
97
+ height: height,
98
+ };
99
+ };
100
+ var segmentIntersectsRect = function (start, end, rect) {
101
+ var dx = end.x - start.x;
102
+ var dy = end.y - start.y;
103
+ var p = [-dx, dx, -dy, dy];
104
+ var q = [
105
+ start.x - rect.x,
106
+ rect.x + rect.width - start.x,
107
+ start.y - rect.y,
108
+ rect.y + rect.height - start.y,
109
+ ];
110
+ var t0 = 0;
111
+ var t1 = 1;
112
+ for (var i = 0; i < 4; i += 1) {
113
+ var pi = p[i];
114
+ var qi = q[i];
115
+ if (pi === 0) {
116
+ if (qi < 0)
117
+ return false;
118
+ }
119
+ else {
120
+ var r = qi / pi;
121
+ if (pi < 0) {
122
+ if (r > t1)
123
+ return false;
124
+ if (r > t0)
125
+ t0 = r;
126
+ }
127
+ else {
128
+ if (r < t0)
129
+ return false;
130
+ if (r < t1)
131
+ t1 = r;
132
+ }
133
+ }
134
+ }
135
+ return true;
136
+ };
137
+ var segmentIntersectsRectInterior = function (start, end, rect, tolerance) {
138
+ var inner = shrinkRect(rect, tolerance);
139
+ if (!inner)
140
+ return false;
141
+ return segmentIntersectsRect(start, end, inner);
142
+ };
143
+ var pointWithinRect = function (point, rect, tolerance) {
144
+ return (point.x >= rect.x - tolerance &&
145
+ point.x <= rect.x + rect.width + tolerance &&
146
+ point.y >= rect.y - tolerance &&
147
+ point.y <= rect.y + rect.height + tolerance);
148
+ };
149
+ var compactPath = function (points) {
150
+ var deduped = [];
151
+ points.forEach(function (point) {
152
+ var last = deduped[deduped.length - 1];
153
+ if (!last || last.x !== point.x || last.y !== point.y) {
154
+ deduped.push(point);
155
+ }
156
+ });
157
+ if (deduped.length < 3)
158
+ return deduped;
159
+ var compacted = [deduped[0]];
160
+ for (var i = 1; i < deduped.length; i += 1) {
161
+ var prev = compacted[compacted.length - 1];
162
+ var next = deduped[i];
163
+ var beforePrev = compacted[compacted.length - 2];
164
+ if (beforePrev &&
165
+ ((beforePrev.x === prev.x && prev.x === next.x) || (beforePrev.y === prev.y && prev.y === next.y))) {
166
+ compacted[compacted.length - 1] = next;
167
+ }
168
+ else {
169
+ compacted.push(next);
170
+ }
171
+ }
172
+ return compacted;
173
+ };
174
+ var pathLength = function (points) {
175
+ var total = 0;
176
+ for (var i = 1; i < points.length; i += 1) {
177
+ var dx = points[i].x - points[i - 1].x;
178
+ var dy = points[i].y - points[i - 1].y;
179
+ total += Math.sqrt(dx * dx + dy * dy);
180
+ }
181
+ return total;
182
+ };
183
+ var clamp$1 = function (value, min, max) { return Math.max(min, Math.min(max, value)); };
184
+ var ObstacleRouter = /** @class */ (function () {
185
+ function ObstacleRouter(options) {
186
+ var _a, _b, _c;
187
+ this.padding = (_a = options === null || options === void 0 ? void 0 : options.padding) !== null && _a !== void 0 ? _a : DEFAULT_PADDING;
188
+ this.tolerance = (_b = options === null || options === void 0 ? void 0 : options.tolerance) !== null && _b !== void 0 ? _b : DEFAULT_TOLERANCE;
189
+ this.stubLength = (_c = options === null || options === void 0 ? void 0 : options.stubLength) !== null && _c !== void 0 ? _c : DEFAULT_STUB_LENGTH;
190
+ }
191
+ ObstacleRouter.prototype.route = function (source, target, context) {
192
+ var _a;
193
+ var obstacles = (_a = context === null || context === void 0 ? void 0 : context.obstacles) !== null && _a !== void 0 ? _a : [];
194
+ var bounds = context === null || context === void 0 ? void 0 : context.bounds;
195
+ var sourceStub = this.computeStubEndpoint(source, context === null || context === void 0 ? void 0 : context.sourceEndpoint, bounds, obstacles);
196
+ var targetStub = this.computeStubEndpoint(target, context === null || context === void 0 ? void 0 : context.targetEndpoint, bounds, obstacles);
197
+ var innerSource = sourceStub !== null && sourceStub !== void 0 ? sourceStub : source;
198
+ var innerTarget = targetStub !== null && targetStub !== void 0 ? targetStub : target;
199
+ var core = this.routeBetween(innerSource, innerTarget, obstacles, bounds, context);
200
+ var normalizedCore = this.ensureEndpoints(core, innerSource, innerTarget);
201
+ var middle = normalizedCore.slice(1, -1).map(function (p) { return (__assign({}, p)); });
202
+ var result = [__assign({}, source)];
203
+ var hasSourceStub = Boolean(sourceStub);
204
+ var hasTargetStub = Boolean(targetStub);
205
+ if (hasSourceStub && sourceStub) {
206
+ result.push(__assign({}, sourceStub));
207
+ }
208
+ result.push.apply(result, middle);
209
+ if (hasTargetStub && targetStub) {
210
+ result.push(__assign({}, targetStub));
211
+ }
212
+ result.push(__assign({}, target));
213
+ return this.compactPreservingStubs(result, hasSourceStub, hasTargetStub);
214
+ };
215
+ ObstacleRouter.prototype.buildCandidates = function (source, target, obstacles, context) {
216
+ var _this = this;
217
+ var midXValues = new Set();
218
+ var midYValues = new Set();
219
+ midXValues.add(Math.round((source.x + target.x) / 2));
220
+ midXValues.add(source.x);
221
+ midXValues.add(target.x);
222
+ midYValues.add(Math.round((source.y + target.y) / 2));
223
+ midYValues.add(source.y);
224
+ midYValues.add(target.y);
225
+ obstacles.forEach(function (obstacle) {
226
+ midXValues.add(obstacle.rect.x - _this.padding);
227
+ midXValues.add(obstacle.rect.x + obstacle.rect.width + _this.padding);
228
+ midYValues.add(obstacle.rect.y - _this.padding);
229
+ midYValues.add(obstacle.rect.y + obstacle.rect.height + _this.padding);
230
+ });
231
+ var candidates = [];
232
+ midXValues.forEach(function (midX) {
233
+ var path = compactPath([
234
+ __assign({}, source),
235
+ { x: midX, y: source.y },
236
+ { x: midX, y: target.y },
237
+ __assign({}, target),
238
+ ]);
239
+ candidates.push(path);
240
+ });
241
+ midYValues.forEach(function (midY) {
242
+ var path = compactPath([
243
+ __assign({}, source),
244
+ { x: source.x, y: midY },
245
+ { x: target.x, y: midY },
246
+ __assign({}, target),
247
+ ]);
248
+ candidates.push(path);
249
+ });
250
+ var corridorCandidate = this.buildCommonAncestorCorridorCandidate(source, target, context === null || context === void 0 ? void 0 : context.commonAncestorCorridor);
251
+ if (corridorCandidate) {
252
+ candidates.push(corridorCandidate);
253
+ }
254
+ return candidates;
255
+ };
256
+ ObstacleRouter.prototype.routeBetween = function (source, target, obstacles, bounds, context) {
257
+ var _this = this;
258
+ var direct = compactPath([__assign({}, source), __assign({}, target)]);
259
+ var hasCommonAncestorCorridor = Boolean(context === null || context === void 0 ? void 0 : context.commonAncestorCorridor);
260
+ if (!hasCommonAncestorCorridor && obstacles.length === 0 && (!bounds || this.pointsWithinBounds(direct, bounds))) {
261
+ return direct;
262
+ }
263
+ if (!hasCommonAncestorCorridor && this.isPathClear(direct, obstacles, bounds)) {
264
+ return direct;
265
+ }
266
+ var candidates = this.buildCandidates(source, target, obstacles, context);
267
+ var best = null;
268
+ var bestPenalty = Number.POSITIVE_INFINITY;
269
+ var bestCorridorSpan = Number.NEGATIVE_INFINITY;
270
+ var bestLength = Number.POSITIVE_INFINITY;
271
+ candidates.forEach(function (candidate) {
272
+ if (!_this.isPathClear(candidate, obstacles, bounds))
273
+ return;
274
+ var scored = _this.scoreCommonAncestorCorridor(candidate, context === null || context === void 0 ? void 0 : context.commonAncestorCorridor, source, target);
275
+ var length = pathLength(candidate);
276
+ if (scored.penalty < bestPenalty ||
277
+ (scored.penalty === bestPenalty && scored.corridorSpan > bestCorridorSpan) ||
278
+ (scored.penalty === bestPenalty && scored.corridorSpan === bestCorridorSpan && length < bestLength)) {
279
+ best = candidate;
280
+ bestPenalty = scored.penalty;
281
+ bestCorridorSpan = scored.corridorSpan;
282
+ bestLength = length;
283
+ }
284
+ });
285
+ return best !== null && best !== void 0 ? best : direct;
286
+ };
287
+ ObstacleRouter.prototype.pointsWithinBounds = function (points, bounds) {
288
+ var _this = this;
289
+ return points.every(function (point) { return pointWithinRect(point, bounds, _this.tolerance); });
290
+ };
291
+ ObstacleRouter.prototype.isPathClear = function (points, obstacles, bounds) {
292
+ if (points.length < 2)
293
+ return true;
294
+ if (bounds && !this.pointsWithinBounds(points, bounds))
295
+ return false;
296
+ for (var i = 1; i < points.length; i += 1) {
297
+ var start = points[i - 1];
298
+ var end = points[i];
299
+ if (start.x === end.x && start.y === end.y)
300
+ continue;
301
+ for (var j = 0; j < obstacles.length; j += 1) {
302
+ if (segmentIntersectsRectInterior(start, end, obstacles[j].rect, this.tolerance)) {
303
+ return false;
304
+ }
305
+ }
306
+ }
307
+ return true;
308
+ };
309
+ ObstacleRouter.prototype.computeStubEndpoint = function (point, endpoint, bounds, obstacles) {
310
+ if (!(endpoint === null || endpoint === void 0 ? void 0 : endpoint.onEdgeSide) || !endpoint.rect)
311
+ return null;
312
+ var outwardNormal = this.getNormal(endpoint.onEdgeSide);
313
+ var selectedNormal = outwardNormal;
314
+ var maxLength = this.computeAvailableStubLength(point, outwardNormal, bounds, obstacles, endpoint.elementId);
315
+ // If the outward normal is fully blocked by route bounds on the endpoint host, fall back inward
316
+ // to preserve a visible perpendicular stub for bounded parent-child routes.
317
+ if (maxLength <= this.tolerance && bounds && this.rectsEqual(bounds, endpoint.rect)) {
318
+ var inwardNormal = { x: -outwardNormal.x, y: -outwardNormal.y };
319
+ var inwardLength = this.computeAvailableStubLength(point, inwardNormal, bounds, obstacles, endpoint.elementId);
320
+ if (inwardLength > maxLength) {
321
+ selectedNormal = inwardNormal;
322
+ maxLength = inwardLength;
323
+ }
324
+ }
325
+ if (maxLength <= 0)
326
+ return null;
327
+ return {
328
+ x: point.x + selectedNormal.x * maxLength,
329
+ y: point.y + selectedNormal.y * maxLength,
330
+ };
331
+ };
332
+ ObstacleRouter.prototype.computeAvailableStubLength = function (point, normal, bounds, obstacles, ignoreId) {
333
+ var maxLength = this.stubLength;
334
+ if (bounds) {
335
+ maxLength = Math.min(maxLength, this.distanceToBounds(point, normal, bounds));
336
+ }
337
+ var obstacleLimit = this.distanceToObstacles(point, normal, obstacles, ignoreId);
338
+ if (obstacleLimit !== null) {
339
+ maxLength = Math.min(maxLength, obstacleLimit);
340
+ }
341
+ return maxLength;
342
+ };
343
+ ObstacleRouter.prototype.rectsEqual = function (a, b) {
344
+ return (Math.abs(a.x - b.x) <= this.tolerance &&
345
+ Math.abs(a.y - b.y) <= this.tolerance &&
346
+ Math.abs(a.width - b.width) <= this.tolerance &&
347
+ Math.abs(a.height - b.height) <= this.tolerance);
348
+ };
349
+ ObstacleRouter.prototype.getNormal = function (side) {
350
+ switch (side) {
351
+ case 'left':
352
+ return { x: -1, y: 0 };
353
+ case 'right':
354
+ return { x: 1, y: 0 };
355
+ case 'top':
356
+ return { x: 0, y: -1 };
357
+ case 'bottom':
358
+ default:
359
+ return { x: 0, y: 1 };
360
+ }
361
+ };
362
+ ObstacleRouter.prototype.distanceToBounds = function (point, normal, bounds) {
363
+ if (normal.x < 0) {
364
+ return Math.max(0, point.x - bounds.x);
365
+ }
366
+ if (normal.x > 0) {
367
+ return Math.max(0, bounds.x + bounds.width - point.x);
368
+ }
369
+ if (normal.y < 0) {
370
+ return Math.max(0, point.y - bounds.y);
371
+ }
372
+ return Math.max(0, bounds.y + bounds.height - point.y);
373
+ };
374
+ ObstacleRouter.prototype.distanceToObstacles = function (point, normal, obstacles, ignoreId) {
375
+ var _this = this;
376
+ var limit = Number.POSITIVE_INFINITY;
377
+ var overlapsHorizontally = function (rect) {
378
+ return point.y >= rect.y - _this.tolerance && point.y <= rect.y + rect.height + _this.tolerance;
379
+ };
380
+ var overlapsVertically = function (rect) {
381
+ return point.x >= rect.x - _this.tolerance && point.x <= rect.x + rect.width + _this.tolerance;
382
+ };
383
+ obstacles.forEach(function (obstacle) {
384
+ if (ignoreId && obstacle.id === ignoreId)
385
+ return;
386
+ var rect = obstacle.rect;
387
+ if (normal.x < 0 && overlapsHorizontally(rect) && rect.x + rect.width <= point.x) {
388
+ var available = point.x - (rect.x + rect.width + _this.tolerance);
389
+ if (available < limit)
390
+ limit = available;
391
+ }
392
+ else if (normal.x > 0 && overlapsHorizontally(rect) && rect.x >= point.x) {
393
+ var available = rect.x - _this.tolerance - point.x;
394
+ if (available < limit)
395
+ limit = available;
396
+ }
397
+ else if (normal.y < 0 && overlapsVertically(rect) && rect.y + rect.height <= point.y) {
398
+ var available = point.y - (rect.y + rect.height + _this.tolerance);
399
+ if (available < limit)
400
+ limit = available;
401
+ }
402
+ else if (normal.y > 0 && overlapsVertically(rect) && rect.y >= point.y) {
403
+ var available = rect.y - _this.tolerance - point.y;
404
+ if (available < limit)
405
+ limit = available;
406
+ }
407
+ });
408
+ if (!Number.isFinite(limit))
409
+ return null;
410
+ return Math.max(0, limit);
411
+ };
412
+ ObstacleRouter.prototype.ensureEndpoints = function (points, source, target) {
413
+ if (points.length === 0)
414
+ return [__assign({}, source), __assign({}, target)];
415
+ var result = __spreadArray([], points, true);
416
+ if (result.length === 1) {
417
+ result.unshift(__assign({}, source));
418
+ result.push(__assign({}, target));
419
+ return result;
420
+ }
421
+ if (result[0].x !== source.x || result[0].y !== source.y) {
422
+ result.unshift(__assign({}, source));
423
+ }
424
+ var last = result[result.length - 1];
425
+ if (last.x !== target.x || last.y !== target.y) {
426
+ result.push(__assign({}, target));
427
+ }
428
+ return result;
429
+ };
430
+ ObstacleRouter.prototype.compactPreservingStubs = function (points, hasSourceStub, hasTargetStub) {
431
+ if (points.length <= 2)
432
+ return points.map(function (p) { return (__assign({}, p)); });
433
+ var deduped = [];
434
+ points.forEach(function (point) {
435
+ var last = deduped[deduped.length - 1];
436
+ if (!last || last.x !== point.x || last.y !== point.y) {
437
+ deduped.push(point);
438
+ }
439
+ });
440
+ if (deduped.length < 3)
441
+ return deduped.map(function (p) { return (__assign({}, p)); });
442
+ var result = [deduped[0]];
443
+ for (var i = 1; i < deduped.length - 1; i += 1) {
444
+ var prev = result[result.length - 1];
445
+ var current = deduped[i];
446
+ var next = deduped[i + 1];
447
+ var isFirstTurn = hasSourceStub && result.length === 1;
448
+ var isLastTurn = hasTargetStub && i === deduped.length - 2;
449
+ var collinear = (prev.x === current.x && current.x === next.x) || (prev.y === current.y && current.y === next.y);
450
+ if (collinear && !isFirstTurn && !isLastTurn) {
451
+ continue;
452
+ }
453
+ result.push(current);
454
+ }
455
+ result.push(deduped[deduped.length - 1]);
456
+ return result.map(function (p) { return (__assign({}, p)); });
457
+ };
458
+ ObstacleRouter.prototype.buildCommonAncestorCorridorCandidate = function (source, target, corridor) {
459
+ if (!corridor)
460
+ return null;
461
+ if (corridor.axis === 'horizontal') {
462
+ var direction_1 = source.x <= target.x ? 1 : -1;
463
+ var corridorY = clamp$1((source.y + target.y) / 2, corridor.bounds.y + this.padding, corridor.bounds.y + corridor.bounds.height - this.padding);
464
+ var sourceExitX = direction_1 > 0
465
+ ? corridor.sourceBranchRect.x + corridor.sourceBranchRect.width + this.padding
466
+ : corridor.sourceBranchRect.x - this.padding;
467
+ var targetEntryX = direction_1 > 0
468
+ ? corridor.targetBranchRect.x - this.padding
469
+ : corridor.targetBranchRect.x + corridor.targetBranchRect.width + this.padding;
470
+ return compactPath([
471
+ __assign({}, source),
472
+ { x: sourceExitX, y: source.y },
473
+ { x: sourceExitX, y: corridorY },
474
+ { x: targetEntryX, y: corridorY },
475
+ { x: targetEntryX, y: target.y },
476
+ __assign({}, target),
477
+ ]);
478
+ }
479
+ var direction = source.y <= target.y ? 1 : -1;
480
+ var corridorX = clamp$1((source.x + target.x) / 2, corridor.bounds.x + this.padding, corridor.bounds.x + corridor.bounds.width - this.padding);
481
+ var sourceExitY = direction > 0
482
+ ? corridor.sourceBranchRect.y + corridor.sourceBranchRect.height + this.padding
483
+ : corridor.sourceBranchRect.y - this.padding;
484
+ var targetEntryY = direction > 0
485
+ ? corridor.targetBranchRect.y - this.padding
486
+ : corridor.targetBranchRect.y + corridor.targetBranchRect.height + this.padding;
487
+ return compactPath([
488
+ __assign({}, source),
489
+ { x: source.x, y: sourceExitY },
490
+ { x: corridorX, y: sourceExitY },
491
+ { x: corridorX, y: targetEntryY },
492
+ { x: target.x, y: targetEntryY },
493
+ __assign({}, target),
494
+ ]);
495
+ };
496
+ ObstacleRouter.prototype.scoreCommonAncestorCorridor = function (candidate, corridor, source, target) {
497
+ if (!corridor || candidate.length < 3) {
498
+ return { penalty: 0, corridorSpan: 0 };
499
+ }
500
+ var firstTurn = candidate[1];
501
+ var lastTurn = candidate[candidate.length - 2];
502
+ var penalty = 0;
503
+ var corridorSpan = 0;
504
+ if (corridor.axis === 'horizontal') {
505
+ var direction = source.x <= target.x ? 1 : -1;
506
+ var sourceBoundary = direction > 0 ? corridor.sourceBranchRect.x + corridor.sourceBranchRect.width : corridor.sourceBranchRect.x;
507
+ var targetBoundary = direction > 0 ? corridor.targetBranchRect.x : corridor.targetBranchRect.x + corridor.targetBranchRect.width;
508
+ var sourceExited = direction > 0 ? firstTurn.x > sourceBoundary + this.tolerance : firstTurn.x < sourceBoundary - this.tolerance;
509
+ var targetDeferred = direction > 0 ? lastTurn.x < targetBoundary - this.tolerance : lastTurn.x > targetBoundary + this.tolerance;
510
+ if (!sourceExited)
511
+ penalty += 1000;
512
+ if (!targetDeferred)
513
+ penalty += 1000;
514
+ corridorSpan = Math.abs(lastTurn.x - firstTurn.x);
515
+ }
516
+ else {
517
+ var direction = source.y <= target.y ? 1 : -1;
518
+ var sourceBoundary = direction > 0 ? corridor.sourceBranchRect.y + corridor.sourceBranchRect.height : corridor.sourceBranchRect.y;
519
+ var targetBoundary = direction > 0 ? corridor.targetBranchRect.y : corridor.targetBranchRect.y + corridor.targetBranchRect.height;
520
+ var sourceExited = direction > 0 ? firstTurn.y > sourceBoundary + this.tolerance : firstTurn.y < sourceBoundary - this.tolerance;
521
+ var targetDeferred = direction > 0 ? lastTurn.y < targetBoundary - this.tolerance : lastTurn.y > targetBoundary + this.tolerance;
522
+ if (!sourceExited)
523
+ penalty += 1000;
524
+ if (!targetDeferred)
525
+ penalty += 1000;
526
+ corridorSpan = Math.abs(lastTurn.y - firstTurn.y);
527
+ }
528
+ return { penalty: penalty, corridorSpan: corridorSpan };
529
+ };
530
+ return ObstacleRouter;
531
+ }());
532
+
85
533
  var baseElementShapes = [
86
534
  {
87
535
  id: 'default',
@@ -211,8 +659,13 @@ var externalToolboxItems = [
211
659
  var defaultGhostSize = { width: 180, height: 120 };
212
660
  var defaultSvgPath = 'M10 10 H110 V90 H10 Z';
213
661
  var defaultSvgPathSize = { width: 180, height: 120 };
662
+ var obstacleAwareRouter = new ObstacleRouter();
214
663
  var orthogonalRouter = {
215
- route: function (source, target) {
664
+ route: function (source, target, context) {
665
+ var _a, _b;
666
+ if (((_b = (_a = context === null || context === void 0 ? void 0 : context.obstacles) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) > 0) {
667
+ return obstacleAwareRouter.route(source, target, context);
668
+ }
216
669
  if (Math.abs(source.x - target.x) < 1 || Math.abs(source.y - target.y) < 1) {
217
670
  return [__assign({}, source), __assign({}, target)];
218
671
  }
@@ -816,6 +1269,27 @@ var createAutoLayoutState = function () { return ({
816
1269
  { id: 'col-a', position: { x: 0, y: 0 }, size: { width: 44, height: 28 }, shapeId: 'default', parentId: 'layout-column' },
817
1270
  { id: 'col-b', position: { x: 0, y: 0 }, size: { width: 70, height: 36 }, shapeId: 'default', parentId: 'layout-column' },
818
1271
  { id: 'col-c', position: { x: 0, y: 0 }, size: { width: 92, height: 44 }, shapeId: 'default', parentId: 'layout-column' },
1272
+ {
1273
+ id: 'layout-grid',
1274
+ position: { x: 620, y: 70 },
1275
+ size: { width: 160, height: 110 },
1276
+ shapeId: 'panel',
1277
+ layout: {
1278
+ mode: 'grid',
1279
+ padding: { x: 10, y: 10 },
1280
+ gap: 10,
1281
+ align: 'center',
1282
+ autoResize: 'grow-shrink',
1283
+ gridTemplate: [[3], [1, 2]],
1284
+ childMinWidth: 24,
1285
+ childMinHeight: 18,
1286
+ },
1287
+ },
1288
+ { id: 'grid-a', position: { x: 0, y: 0 }, size: { width: 36, height: 26 }, shapeId: 'default', parentId: 'layout-grid' },
1289
+ { id: 'grid-b', position: { x: 0, y: 0 }, size: { width: 44, height: 24 }, shapeId: 'default', parentId: 'layout-grid' },
1290
+ { id: 'grid-c', position: { x: 0, y: 0 }, size: { width: 50, height: 20 }, shapeId: 'default', parentId: 'layout-grid' },
1291
+ { id: 'grid-d', position: { x: 0, y: 0 }, size: { width: 28, height: 30 }, shapeId: 'default', parentId: 'layout-grid' },
1292
+ { id: 'grid-e', position: { x: 0, y: 0 }, size: { width: 52, height: 28 }, shapeId: 'default', parentId: 'layout-grid' },
819
1293
  {
820
1294
  id: 'layout-nested',
821
1295
  position: { x: 120, y: 260 },
@@ -874,6 +1348,18 @@ var createAutoLayoutState = function () { return ({
874
1348
  { id: 'label-col-a', content: 'col-a', position: { x: 6, y: -14 }, ownerId: 'col-a' },
875
1349
  { id: 'label-col-b', content: 'col-b', position: { x: 6, y: -14 }, ownerId: 'col-b' },
876
1350
  { id: 'label-col-c', content: 'col-c', position: { x: 6, y: -14 }, ownerId: 'col-c' },
1351
+ {
1352
+ id: 'label-layout-grid',
1353
+ content: 'Grid template [[3],[1,2]]',
1354
+ position: { x: 8, y: 6 },
1355
+ ownerId: 'layout-grid',
1356
+ layout: { boundsMode: 'owner-width', wrap: 'word', overflow: 'clip', padding: 0 },
1357
+ },
1358
+ { id: 'label-grid-a', content: 'grid-a', position: { x: 6, y: -14 }, ownerId: 'grid-a' },
1359
+ { id: 'label-grid-b', content: 'grid-b', position: { x: 6, y: -14 }, ownerId: 'grid-b' },
1360
+ { id: 'label-grid-c', content: 'grid-c', position: { x: 6, y: -14 }, ownerId: 'grid-c' },
1361
+ { id: 'label-grid-d', content: 'grid-d', position: { x: 6, y: -14 }, ownerId: 'grid-d' },
1362
+ { id: 'label-grid-e', content: 'grid-e', position: { x: 6, y: -14 }, ownerId: 'grid-e' },
877
1363
  {
878
1364
  id: 'label-layout-nested',
879
1365
  content: 'Row + nested column lane',
@@ -894,7 +1380,7 @@ var createAutoLayoutState = function () { return ({
894
1380
  var autoLayoutDemoConfig = ({
895
1381
  id: 'auto-layout',
896
1382
  title: 'Element Auto-Layout + Label Lane',
897
- description: 'Consolidated auto-layout verification with baseline/nested containers, fit options, and label reserved-space modes.',
1383
+ description: 'Consolidated auto-layout verification with horizontal/vertical/grid containers, fit options, auto-resize policies, and label reserved-space modes.',
898
1384
  createState: createAutoLayoutState,
899
1385
  elementShapes: baseElementShapes,
900
1386
  portShapes: basePortShapes,
@@ -2432,6 +2918,122 @@ var labelStyleDemoConfig = {
2432
2918
  ],
2433
2919
  };
2434
2920
 
2921
+ var createLinkColorPoolState = function () { return ({
2922
+ elements: [
2923
+ {
2924
+ id: 'lc-off-source',
2925
+ position: { x: 80, y: 170 },
2926
+ size: { width: 190, height: 120 },
2927
+ shapeId: 'default',
2928
+ },
2929
+ {
2930
+ id: 'lc-off-target',
2931
+ position: { x: 350, y: 170 },
2932
+ size: { width: 190, height: 120 },
2933
+ shapeId: 'default',
2934
+ },
2935
+ {
2936
+ id: 'lc-default-source',
2937
+ position: { x: 80, y: 360 },
2938
+ size: { width: 190, height: 120 },
2939
+ shapeId: 'panel',
2940
+ },
2941
+ {
2942
+ id: 'lc-default-target',
2943
+ position: { x: 350, y: 360 },
2944
+ size: { width: 190, height: 120 },
2945
+ shapeId: 'panel',
2946
+ },
2947
+ {
2948
+ id: 'lc-custom-source',
2949
+ position: { x: 620, y: 170 },
2950
+ size: { width: 190, height: 120 },
2951
+ shapeId: 'default',
2952
+ },
2953
+ {
2954
+ id: 'lc-custom-target',
2955
+ position: { x: 620, y: 360 },
2956
+ size: { width: 190, height: 120 },
2957
+ shapeId: 'default',
2958
+ },
2959
+ ],
2960
+ ports: [
2961
+ { id: 'lc-off-source-port', elementId: 'lc-off-source', position: { x: 170, y: 60 }, shapeId: 'port-circle' },
2962
+ { id: 'lc-off-target-port', elementId: 'lc-off-target', position: { x: 20, y: 60 }, shapeId: 'port-circle' },
2963
+ { id: 'lc-default-source-port', elementId: 'lc-default-source', position: { x: 170, y: 60 }, shapeId: 'port-circle' },
2964
+ { id: 'lc-default-target-port', elementId: 'lc-default-target', position: { x: 20, y: 60 }, shapeId: 'port-circle' },
2965
+ { id: 'lc-custom-source-port', elementId: 'lc-custom-source', position: { x: 95, y: 110 }, shapeId: 'port-circle' },
2966
+ { id: 'lc-custom-target-port', elementId: 'lc-custom-target', position: { x: 95, y: 10 }, shapeId: 'port-circle' },
2967
+ ],
2968
+ links: [
2969
+ {
2970
+ id: 'lc-reference-link',
2971
+ sourcePortId: 'lc-off-source-port',
2972
+ targetPortId: 'lc-off-target-port',
2973
+ points: [],
2974
+ style: { stroke: '#374151', strokeWidth: 2 },
2975
+ },
2976
+ ],
2977
+ texts: [
2978
+ {
2979
+ id: 'lc-intro',
2980
+ content: 'Use Color Mode above: Off keeps baseline link color behavior. Default Pool enables random colors from the built-in 20-color pool. Custom Pool restricts random colors to #0b7285, #e8590c, #5f3dc4.',
2981
+ position: { x: 80, y: 86 },
2982
+ },
2983
+ { id: 'lc-off-label', content: 'Off scenario pair', ownerId: 'lc-off-source', position: { x: 10, y: -16 } },
2984
+ { id: 'lc-default-label', content: 'Default-pool scenario pair', ownerId: 'lc-default-source', position: { x: 10, y: -16 } },
2985
+ { id: 'lc-custom-label', content: 'Custom-pool scenario pair', ownerId: 'lc-custom-source', position: { x: 10, y: -16 } },
2986
+ ],
2987
+ }); };
2988
+ var linkColorPoolDemoConfig = {
2989
+ id: 'link-color-pool',
2990
+ title: 'Link Color Pool',
2991
+ description: 'Verify disabled/default/custom random link-color assignment on new links.',
2992
+ createState: createLinkColorPoolState,
2993
+ elementShapes: baseElementShapes,
2994
+ portShapes: basePortShapes,
2995
+ defaultElementShapeId: 'default',
2996
+ defaultPortShapeId: 'port-circle',
2997
+ actions: [
2998
+ {
2999
+ id: 'add-link-off-pair',
3000
+ label: 'Add link (off pair)',
3001
+ run: function (editor) {
3002
+ editor.addLink({
3003
+ id: "link-color-off-".concat(createId()),
3004
+ sourcePortId: 'lc-off-source-port',
3005
+ targetPortId: 'lc-off-target-port',
3006
+ points: [],
3007
+ });
3008
+ },
3009
+ },
3010
+ {
3011
+ id: 'add-link-default-pair',
3012
+ label: 'Add link (default pair)',
3013
+ run: function (editor) {
3014
+ editor.addLink({
3015
+ id: "link-color-default-".concat(createId()),
3016
+ sourcePortId: 'lc-default-source-port',
3017
+ targetPortId: 'lc-default-target-port',
3018
+ points: [],
3019
+ });
3020
+ },
3021
+ },
3022
+ {
3023
+ id: 'add-link-custom-pair',
3024
+ label: 'Add link (custom pair)',
3025
+ run: function (editor) {
3026
+ editor.addLink({
3027
+ id: "link-color-custom-".concat(createId()),
3028
+ sourcePortId: 'lc-custom-source-port',
3029
+ targetPortId: 'lc-custom-target-port',
3030
+ points: [],
3031
+ });
3032
+ },
3033
+ },
3034
+ ],
3035
+ };
3036
+
2435
3037
  var createGridOverlayState = function () { return ({
2436
3038
  elements: [
2437
3039
  {
@@ -2521,7 +3123,7 @@ var createRoutingDemoState = function () { return ({
2521
3123
  var routingDemoConfig = ({
2522
3124
  id: 'routing-demo',
2523
3125
  title: 'Routing Demo',
2524
- description: 'Auto routing uses an orthogonal router. Toggle routing to compare.',
3126
+ description: 'Auto routing is obstacle-aware. Toggle routing to compare manual vs auto reroute.',
2525
3127
  createState: createRoutingDemoState,
2526
3128
  elementShapes: baseElementShapes,
2527
3129
  portShapes: basePortShapes,
@@ -2560,6 +3162,17 @@ var obstacleRoutingLinks = [
2560
3162
  points: [],
2561
3163
  routing: 'auto',
2562
3164
  },
3165
+ {
3166
+ id: 'cross-branch-corridor-link',
3167
+ sourcePortId: 'corridor-left-port',
3168
+ targetPortId: 'corridor-right-port',
3169
+ routing: 'auto',
3170
+ points: [
3171
+ { x: 310, y: 560 },
3172
+ { x: 470, y: 500 },
3173
+ { x: 630, y: 560 },
3174
+ ],
3175
+ },
2563
3176
  ];
2564
3177
  var obstacleRoutingShapes = __spreadArray(__spreadArray([], baseElementShapes, true), [
2565
3178
  {
@@ -2594,30 +3207,69 @@ var createObstacleRoutingState = function () { return ({
2594
3207
  position: { x: 310, y: 75 },
2595
3208
  size: { width: 140, height: 100 },
2596
3209
  shapeId: 'routing-ellipse',
2597
- parentId: 'sibling-parent',
3210
+ parentId: 'sibling-parent',
3211
+ portMovement: { moveMode: 'anchors', anchorConstraint: { preset: 'cardinal' } },
3212
+ },
3213
+ {
3214
+ id: 'attach-parent',
3215
+ position: { x: 620, y: 90 },
3216
+ size: { width: 360, height: 250 },
3217
+ shapeId: 'routing-ellipse',
3218
+ portMovement: { moveMode: 'anchors', anchorConstraint: { preset: 'cardinal' } },
3219
+ },
3220
+ {
3221
+ id: 'attach-child',
3222
+ position: { x: 40, y: 45 },
3223
+ size: { width: 140, height: 100 },
3224
+ shapeId: 'routing-ellipse',
3225
+ parentId: 'attach-parent',
3226
+ portMovement: { moveMode: 'anchors', anchorConstraint: { preset: 'cardinal' } },
3227
+ },
3228
+ {
3229
+ id: 'attach-child-b',
3230
+ position: { x: 190, y: 120 },
3231
+ size: { width: 140, height: 100 },
3232
+ shapeId: 'routing-ellipse',
3233
+ parentId: 'attach-parent',
3234
+ portMovement: { moveMode: 'anchors', anchorConstraint: { preset: 'cardinal' } },
3235
+ },
3236
+ {
3237
+ id: 'corridor-root',
3238
+ position: { x: 40, y: 390 },
3239
+ size: { width: 940, height: 260 },
3240
+ shapeId: 'panel',
3241
+ style: { fill: '#f5f9ff' },
3242
+ },
3243
+ {
3244
+ id: 'corridor-left-branch',
3245
+ position: { x: 80, y: 40 },
3246
+ size: { width: 260, height: 170 },
3247
+ shapeId: 'routing-ellipse',
3248
+ parentId: 'corridor-root',
2598
3249
  portMovement: { moveMode: 'anchors', anchorConstraint: { preset: 'cardinal' } },
2599
3250
  },
2600
3251
  {
2601
- id: 'attach-parent',
2602
- position: { x: 620, y: 90 },
2603
- size: { width: 360, height: 250 },
3252
+ id: 'corridor-right-branch',
3253
+ position: { x: 560, y: 50 },
3254
+ size: { width: 280, height: 170 },
2604
3255
  shapeId: 'routing-ellipse',
3256
+ parentId: 'corridor-root',
2605
3257
  portMovement: { moveMode: 'anchors', anchorConstraint: { preset: 'cardinal' } },
2606
3258
  },
2607
3259
  {
2608
- id: 'attach-child',
2609
- position: { x: 40, y: 45 },
2610
- size: { width: 140, height: 100 },
2611
- shapeId: 'routing-ellipse',
2612
- parentId: 'attach-parent',
3260
+ id: 'corridor-left-leaf',
3261
+ position: { x: 120, y: 60 },
3262
+ size: { width: 120, height: 70 },
3263
+ shapeId: 'default',
3264
+ parentId: 'corridor-left-branch',
2613
3265
  portMovement: { moveMode: 'anchors', anchorConstraint: { preset: 'cardinal' } },
2614
3266
  },
2615
3267
  {
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',
3268
+ id: 'corridor-right-leaf',
3269
+ position: { x: 50, y: 70 },
3270
+ size: { width: 120, height: 70 },
3271
+ shapeId: 'default',
3272
+ parentId: 'corridor-right-branch',
2621
3273
  portMovement: { moveMode: 'anchors', anchorConstraint: { preset: 'cardinal' } },
2622
3274
  },
2623
3275
  ],
@@ -2668,6 +3320,22 @@ var createObstacleRoutingState = function () { return ({
2668
3320
  externalLinkAttachPoint: { x: 12, y: 0 },
2669
3321
  internalLinkAttachPoint: { x: -12, y: 0 },
2670
3322
  },
3323
+ {
3324
+ id: 'corridor-left-port',
3325
+ elementId: 'corridor-left-leaf',
3326
+ position: { x: 120, y: 35 },
3327
+ shapeId: 'port-dark',
3328
+ anchorCenter: true,
3329
+ currentAnchorId: 'right',
3330
+ },
3331
+ {
3332
+ id: 'corridor-right-port',
3333
+ elementId: 'corridor-right-leaf',
3334
+ position: { x: 0, y: 35 },
3335
+ shapeId: 'port-dark',
3336
+ anchorCenter: true,
3337
+ currentAnchorId: 'left',
3338
+ },
2671
3339
  ],
2672
3340
  links: obstacleRoutingLinks,
2673
3341
  texts: [
@@ -2701,6 +3369,16 @@ var createObstacleRoutingState = function () { return ({
2701
3369
  content: 'child-A->child-B: sibling endpoints EXTERNAL on both ends',
2702
3370
  position: { x: 620, y: 394 },
2703
3371
  },
3372
+ {
3373
+ id: 'corridor-group-label',
3374
+ content: 'Scenario C: Cross-branch route should leave source branch early, travel at ancestor corridor, and descend near target.',
3375
+ position: { x: 52, y: 360 },
3376
+ },
3377
+ {
3378
+ id: 'corridor-redraw-label',
3379
+ content: 'The cross-branch link starts with stale points. Toggle redraw refresh and click Manual Render to compare behavior.',
3380
+ position: { x: 52, y: 676 },
3381
+ },
2704
3382
  ],
2705
3383
  }); };
2706
3384
  var addLinksAction = {
@@ -2725,7 +3403,7 @@ var rerouteAllAction = {
2725
3403
  var obstacleRoutingDemoConfig = {
2726
3404
  id: 'obstacle-routing',
2727
3405
  title: 'Obstacle Routing',
2728
- description: 'Nested multi-anchor checks: sibling interior-avoidance + directional internal/external parent-child attach semantics.',
3406
+ description: 'Nested routing checks: sibling interior-avoidance, directional parent-child attach semantics, and cross-parent common-ancestor corridor preference.',
2729
3407
  createState: createObstacleRoutingState,
2730
3408
  elementShapes: obstacleRoutingShapes,
2731
3409
  portShapes: basePortShapes,
@@ -3895,980 +4573,617 @@ var polygonIntersectsRect = function (polygon, rect) {
3895
4573
  }
3896
4574
  return false;
3897
4575
  };
3898
- var snapToGrid = function (point, gridSize) {
3899
- if (gridSize <= 0)
3900
- return __assign({}, point);
3901
- return {
3902
- x: Math.round(point.x / gridSize) * gridSize,
3903
- y: Math.round(point.y / gridSize) * gridSize,
3904
- };
3905
- };
3906
-
3907
- var DiagramModel = /** @class */ (function () {
3908
- function DiagramModel() {
3909
- this.elements = new Map();
3910
- this.ports = new Map();
3911
- this.links = new Map();
3912
- this.texts = new Map();
3913
- this.ownership = new OwnershipIndex();
3914
- }
3915
- DiagramModel.prototype.load = function (state) {
3916
- var _this = this;
3917
- this.elements.clear();
3918
- this.ports.clear();
3919
- this.links.clear();
3920
- this.texts.clear();
3921
- this.ownership = new OwnershipIndex();
3922
- state.elements.forEach(function (data) { return _this.elements.set(data.id, new ElementModel(data)); });
3923
- state.ports.forEach(function (data) { return _this.ports.set(data.id, new PortModel(data)); });
3924
- state.links.forEach(function (data) { return _this.links.set(data.id, new LinkModel(data)); });
3925
- state.texts.forEach(function (data) {
3926
- var text = new TextModel(data);
3927
- _this.texts.set(data.id, text);
3928
- });
3929
- this.ports.forEach(function (port) {
3930
- var element = _this.elements.get(port.elementId);
3931
- if (element) {
3932
- element.addPort(port.id);
3933
- }
3934
- });
3935
- this.texts.forEach(function (text) {
3936
- if (!text.ownerId)
3937
- return;
3938
- _this.ownership.add(text.ownerId, text.id);
3939
- var element = _this.elements.get(text.ownerId);
3940
- if (element) {
3941
- element.addText(text.id);
3942
- return;
3943
- }
3944
- var port = _this.ports.get(text.ownerId);
3945
- if (port) {
3946
- port.addText(text.id);
3947
- return;
3948
- }
3949
- var link = _this.links.get(text.ownerId);
3950
- if (link) {
3951
- link.addText(text.id);
3952
- }
3953
- });
3954
- };
3955
- DiagramModel.prototype.toState = function () {
3956
- return {
3957
- elements: Array.from(this.elements.values()).map(function (element) { return element.toData(); }),
3958
- ports: Array.from(this.ports.values()).map(function (port) { return port.toData(); }),
3959
- links: Array.from(this.links.values()).map(function (link) { return link.toData(); }),
3960
- texts: Array.from(this.texts.values()).map(function (text) { return text.toData(); }),
3961
- };
3962
- };
3963
- DiagramModel.prototype.getElement = function (id) {
3964
- return this.elements.get(id);
3965
- };
3966
- DiagramModel.prototype.setElementLayout = function (id, layout) {
3967
- var element = this.elements.get(id);
3968
- if (!element)
3969
- return;
3970
- element.layout = layout;
3971
- };
3972
- DiagramModel.prototype.getChildren = function (parentId) {
3973
- return Array.from(this.elements.values()).filter(function (child) { return child.parentId === parentId; });
3974
- };
3975
- DiagramModel.prototype.getElementWorldPosition = function (id) {
3976
- var element = this.elements.get(id);
3977
- if (!element)
3978
- return null;
3979
- return this.resolveElementWorldPosition(element);
3980
- };
3981
- DiagramModel.prototype.getPort = function (id) {
3982
- return this.ports.get(id);
3983
- };
3984
- DiagramModel.prototype.getPortWorldPosition = function (id) {
3985
- var port = this.ports.get(id);
3986
- if (!port)
3987
- return null;
3988
- var elementPosition = this.getElementWorldPosition(port.elementId);
3989
- if (!elementPosition)
3990
- return __assign({}, port.position);
3991
- return {
3992
- x: elementPosition.x + port.position.x,
3993
- y: elementPosition.y + port.position.y,
3994
- };
3995
- };
3996
- DiagramModel.prototype.getLink = function (id) {
3997
- return this.links.get(id);
3998
- };
3999
- DiagramModel.prototype.getLinkMidpoint = function (id) {
4000
- var link = this.links.get(id);
4001
- if (!link)
4002
- return null;
4003
- return this.resolveLinkMidpoint(link);
4004
- };
4005
- DiagramModel.prototype.getText = function (id) {
4006
- return this.texts.get(id);
4007
- };
4008
- DiagramModel.prototype.getTextWorldPosition = function (id) {
4009
- var text = this.texts.get(id);
4010
- if (!text)
4011
- return null;
4012
- if (!text.ownerId)
4013
- return __assign({}, text.position);
4014
- var element = this.elements.get(text.ownerId);
4015
- if (element) {
4016
- var elementPosition = this.resolveElementWorldPosition(element);
4017
- return {
4018
- x: elementPosition.x + text.position.x,
4019
- y: elementPosition.y + text.position.y,
4020
- };
4021
- }
4022
- var port = this.ports.get(text.ownerId);
4023
- if (port) {
4024
- var portPosition = this.getPortWorldPosition(port.id);
4025
- if (!portPosition)
4026
- return __assign({}, text.position);
4027
- return {
4028
- x: portPosition.x + text.position.x,
4029
- y: portPosition.y + text.position.y,
4030
- };
4031
- }
4032
- var link = this.links.get(text.ownerId);
4033
- if (link) {
4034
- var midpoint = this.resolveLinkMidpoint(link);
4035
- return {
4036
- x: midpoint.x + text.position.x,
4037
- y: midpoint.y + text.position.y,
4038
- };
4039
- }
4040
- return __assign({}, text.position);
4041
- };
4042
- DiagramModel.prototype.addElement = function (data) {
4043
- var element = new ElementModel(data);
4044
- this.elements.set(element.id, element);
4045
- return element;
4046
- };
4047
- DiagramModel.prototype.moveElement = function (id, position) {
4048
- var element = this.elements.get(id);
4049
- if (!element)
4050
- return;
4051
- element.setPosition(position);
4052
- };
4053
- DiagramModel.prototype.resizeElement = function (id, size) {
4054
- var element = this.elements.get(id);
4055
- if (!element)
4056
- return;
4057
- element.setSize(size);
4058
- };
4059
- DiagramModel.prototype.removeElement = function (id) {
4060
- var _this = this;
4061
- var element = this.elements.get(id);
4062
- if (!element)
4063
- return;
4064
- var portIds = __spreadArray([], element.portIds, true);
4065
- var textIds = __spreadArray([], element.textIds, true);
4066
- portIds.forEach(function (portId) { return _this.removePort(portId); });
4067
- textIds.forEach(function (textId) { return _this.removeText(textId); });
4068
- this.elements.delete(id);
4069
- };
4070
- DiagramModel.prototype.addPort = function (data) {
4071
- var port = new PortModel(data);
4072
- this.ports.set(port.id, port);
4073
- var element = this.elements.get(port.elementId);
4074
- if (element) {
4075
- element.addPort(port.id);
4076
- }
4077
- return port;
4078
- };
4079
- DiagramModel.prototype.movePort = function (id, position) {
4080
- var port = this.ports.get(id);
4081
- if (!port)
4082
- return;
4083
- port.setPosition(position);
4084
- };
4085
- DiagramModel.prototype.setPortCurrentAnchorId = function (id, currentAnchorId) {
4086
- var port = this.ports.get(id);
4087
- if (!port)
4088
- return;
4089
- port.setCurrentAnchorId(currentAnchorId);
4090
- };
4091
- DiagramModel.prototype.removePort = function (id) {
4092
- var _this = this;
4093
- var port = this.ports.get(id);
4094
- if (!port)
4095
- return;
4096
- var element = this.elements.get(port.elementId);
4097
- if (element) {
4098
- element.removePort(id);
4099
- }
4100
- var portTextIds = __spreadArray([], port.textIds, true);
4101
- portTextIds.forEach(function (textId) { return _this.removeText(textId); });
4102
- var linksToRemove = Array.from(this.links.values())
4103
- .filter(function (link) { return link.sourcePortId === id || link.targetPortId === id; })
4104
- .map(function (link) { return link.id; });
4105
- linksToRemove.forEach(function (linkId) { return _this.removeLink(linkId); });
4106
- this.ports.delete(id);
4107
- };
4108
- DiagramModel.prototype.addLink = function (data) {
4109
- var link = new LinkModel(data);
4110
- this.links.set(link.id, link);
4111
- return link;
4112
- };
4113
- DiagramModel.prototype.updateLinkPoints = function (id, points) {
4114
- var link = this.links.get(id);
4115
- if (!link)
4116
- return;
4117
- link.setPoints(points);
4118
- };
4119
- DiagramModel.prototype.setLinkRoutingMode = function (id, mode) {
4120
- var link = this.links.get(id);
4121
- if (!link)
4122
- return;
4123
- link.setRouting(mode);
4576
+ var snapToGrid = function (point, gridSize) {
4577
+ if (gridSize <= 0)
4578
+ return __assign({}, point);
4579
+ return {
4580
+ x: Math.round(point.x / gridSize) * gridSize,
4581
+ y: Math.round(point.y / gridSize) * gridSize,
4124
4582
  };
4125
- DiagramModel.prototype.removeLink = function (id) {
4583
+ };
4584
+
4585
+ var DiagramModel = /** @class */ (function () {
4586
+ function DiagramModel() {
4587
+ this.elements = new Map();
4588
+ this.ports = new Map();
4589
+ this.links = new Map();
4590
+ this.texts = new Map();
4591
+ this.ownership = new OwnershipIndex();
4592
+ }
4593
+ DiagramModel.prototype.load = function (state) {
4126
4594
  var _this = this;
4127
- var link = this.links.get(id);
4128
- if (!link)
4129
- return;
4130
- var textIds = __spreadArray([], link.textIds, true);
4131
- textIds.forEach(function (textId) { return _this.removeText(textId); });
4132
- this.links.delete(id);
4133
- };
4134
- DiagramModel.prototype.addText = function (data) {
4135
- var text = new TextModel(data);
4136
- this.texts.set(text.id, text);
4137
- if (text.ownerId) {
4138
- this.ownership.add(text.ownerId, text.id);
4139
- var element = this.elements.get(text.ownerId);
4595
+ this.elements.clear();
4596
+ this.ports.clear();
4597
+ this.links.clear();
4598
+ this.texts.clear();
4599
+ this.ownership = new OwnershipIndex();
4600
+ state.elements.forEach(function (data) { return _this.elements.set(data.id, new ElementModel(data)); });
4601
+ state.ports.forEach(function (data) { return _this.ports.set(data.id, new PortModel(data)); });
4602
+ state.links.forEach(function (data) { return _this.links.set(data.id, new LinkModel(data)); });
4603
+ state.texts.forEach(function (data) {
4604
+ var text = new TextModel(data);
4605
+ _this.texts.set(data.id, text);
4606
+ });
4607
+ this.ports.forEach(function (port) {
4608
+ var element = _this.elements.get(port.elementId);
4609
+ if (element) {
4610
+ element.addPort(port.id);
4611
+ }
4612
+ });
4613
+ this.texts.forEach(function (text) {
4614
+ if (!text.ownerId)
4615
+ return;
4616
+ _this.ownership.add(text.ownerId, text.id);
4617
+ var element = _this.elements.get(text.ownerId);
4140
4618
  if (element) {
4141
4619
  element.addText(text.id);
4620
+ return;
4142
4621
  }
4143
- var port = this.ports.get(text.ownerId);
4622
+ var port = _this.ports.get(text.ownerId);
4144
4623
  if (port) {
4145
4624
  port.addText(text.id);
4625
+ return;
4146
4626
  }
4147
- var link = this.links.get(text.ownerId);
4627
+ var link = _this.links.get(text.ownerId);
4148
4628
  if (link) {
4149
4629
  link.addText(text.id);
4150
4630
  }
4151
- }
4152
- return text;
4631
+ });
4153
4632
  };
4154
- DiagramModel.prototype.updateText = function (id, content) {
4155
- var text = this.texts.get(id);
4156
- if (!text)
4157
- return;
4158
- text.setContent(content);
4633
+ DiagramModel.prototype.toState = function () {
4634
+ return {
4635
+ elements: Array.from(this.elements.values()).map(function (element) { return element.toData(); }),
4636
+ ports: Array.from(this.ports.values()).map(function (port) { return port.toData(); }),
4637
+ links: Array.from(this.links.values()).map(function (link) { return link.toData(); }),
4638
+ texts: Array.from(this.texts.values()).map(function (text) { return text.toData(); }),
4639
+ };
4159
4640
  };
4160
- DiagramModel.prototype.moveText = function (id, position) {
4161
- var text = this.texts.get(id);
4162
- if (!text)
4163
- return;
4164
- text.setPosition(position);
4641
+ DiagramModel.prototype.getElement = function (id) {
4642
+ return this.elements.get(id);
4165
4643
  };
4166
- DiagramModel.prototype.removeText = function (id) {
4167
- var text = this.texts.get(id);
4168
- if (!text)
4644
+ DiagramModel.prototype.setElementLayout = function (id, layout) {
4645
+ var element = this.elements.get(id);
4646
+ if (!element)
4169
4647
  return;
4170
- if (text.ownerId) {
4171
- this.ownership.remove(text.ownerId, id);
4172
- var element = this.elements.get(text.ownerId);
4173
- if (element) {
4174
- element.removeText(id);
4175
- }
4176
- var port = this.ports.get(text.ownerId);
4177
- if (port) {
4178
- port.removeText(id);
4179
- }
4180
- var link = this.links.get(text.ownerId);
4181
- if (link) {
4182
- link.removeText(id);
4183
- }
4184
- }
4185
- this.texts.delete(id);
4186
- };
4187
- DiagramModel.prototype.resolveElementWorldPosition = function (element) {
4188
- var x = element.position.x;
4189
- var y = element.position.y;
4190
- if (element.anchorCenter) {
4191
- x -= element.size.width / 2;
4192
- y -= element.size.height / 2;
4193
- }
4194
- var current = element;
4195
- while (current === null || current === void 0 ? void 0 : current.parentId) {
4196
- var parent_1 = this.elements.get(current.parentId);
4197
- if (!parent_1)
4198
- break;
4199
- var parentX = parent_1.position.x;
4200
- var parentY = parent_1.position.y;
4201
- if (parent_1.anchorCenter) {
4202
- parentX -= parent_1.size.width / 2;
4203
- parentY -= parent_1.size.height / 2;
4204
- }
4205
- x += parentX;
4206
- y += parentY;
4207
- current = parent_1;
4208
- }
4209
- return { x: x, y: y };
4210
- };
4211
- DiagramModel.prototype.resolveLinkMidpoint = function (link) {
4212
- var points = link.points;
4213
- if (points.length === 0)
4214
- return { x: 0, y: 0 };
4215
- if (points.length === 1)
4216
- return __assign({}, points[0]);
4217
- var total = 0;
4218
- for (var i = 1; i < points.length; i += 1) {
4219
- total += distance(points[i - 1], points[i]);
4220
- }
4221
- var halfway = total / 2;
4222
- var traveled = 0;
4223
- for (var i = 1; i < points.length; i += 1) {
4224
- var segment = distance(points[i - 1], points[i]);
4225
- if (traveled + segment >= halfway) {
4226
- var ratio = segment === 0 ? 0 : (halfway - traveled) / segment;
4227
- return {
4228
- x: points[i - 1].x + (points[i].x - points[i - 1].x) * ratio,
4229
- y: points[i - 1].y + (points[i].y - points[i - 1].y) * ratio,
4230
- };
4231
- }
4232
- traveled += segment;
4233
- }
4234
- return __assign({}, points[points.length - 1]);
4648
+ element.layout = layout;
4235
4649
  };
4236
- return DiagramModel;
4237
- }());
4238
-
4239
- var CommandQueue = /** @class */ (function () {
4240
- function CommandQueue() {
4241
- this.queue = [];
4242
- }
4243
- CommandQueue.prototype.enqueue = function (command) {
4244
- this.queue.push(command);
4650
+ DiagramModel.prototype.getChildren = function (parentId) {
4651
+ return Array.from(this.elements.values()).filter(function (child) { return child.parentId === parentId; });
4245
4652
  };
4246
- CommandQueue.prototype.flush = function (model) {
4247
- var patches = [];
4248
- while (this.queue.length > 0) {
4249
- var command = this.queue.shift();
4250
- if (!command)
4251
- continue;
4252
- var next = command.apply(model);
4253
- patches.push.apply(patches, next);
4254
- }
4255
- return patches;
4653
+ DiagramModel.prototype.getElementWorldPosition = function (id) {
4654
+ var element = this.elements.get(id);
4655
+ if (!element)
4656
+ return null;
4657
+ return this.resolveElementWorldPosition(element);
4256
4658
  };
4257
- CommandQueue.prototype.run = function (command, model) {
4258
- this.enqueue(command);
4259
- return this.flush(model);
4659
+ DiagramModel.prototype.getPort = function (id) {
4660
+ return this.ports.get(id);
4260
4661
  };
4261
- return CommandQueue;
4262
- }());
4263
-
4264
- var EventBus = /** @class */ (function () {
4265
- function EventBus() {
4266
- this.handlers = new Map();
4267
- }
4268
- EventBus.prototype.subscribe = function (event, handler) {
4269
- var _this = this;
4270
- var _a;
4271
- var set = (_a = this.handlers.get(event)) !== null && _a !== void 0 ? _a : new Set();
4272
- set.add(handler);
4273
- this.handlers.set(event, set);
4274
- return function () {
4275
- var existing = _this.handlers.get(event);
4276
- if (!existing)
4277
- return;
4278
- existing.delete(handler);
4279
- if (existing.size === 0) {
4280
- _this.handlers.delete(event);
4281
- }
4662
+ DiagramModel.prototype.getPortWorldPosition = function (id) {
4663
+ var port = this.ports.get(id);
4664
+ if (!port)
4665
+ return null;
4666
+ var elementPosition = this.getElementWorldPosition(port.elementId);
4667
+ if (!elementPosition)
4668
+ return __assign({}, port.position);
4669
+ return {
4670
+ x: elementPosition.x + port.position.x,
4671
+ y: elementPosition.y + port.position.y,
4282
4672
  };
4283
4673
  };
4284
- EventBus.prototype.emit = function (event, payload) {
4285
- var set = this.handlers.get(event);
4286
- if (!set)
4287
- return;
4288
- Array.from(set.values()).forEach(function (handler) {
4289
- handler(payload);
4290
- });
4674
+ DiagramModel.prototype.getLink = function (id) {
4675
+ return this.links.get(id);
4291
4676
  };
4292
- return EventBus;
4293
- }());
4294
-
4295
- var SelectionState = /** @class */ (function () {
4296
- function SelectionState() {
4297
- this.selectedIds = new Set();
4298
- }
4299
- SelectionState.prototype.set = function (ids) {
4300
- this.selectedIds = new Set(ids);
4677
+ DiagramModel.prototype.getLinkMidpoint = function (id) {
4678
+ var link = this.links.get(id);
4679
+ if (!link)
4680
+ return null;
4681
+ return this.resolveLinkMidpoint(link);
4301
4682
  };
4302
- SelectionState.prototype.toggle = function (id) {
4303
- if (this.selectedIds.has(id)) {
4304
- this.selectedIds.delete(id);
4683
+ DiagramModel.prototype.getText = function (id) {
4684
+ return this.texts.get(id);
4685
+ };
4686
+ DiagramModel.prototype.getTextWorldPosition = function (id) {
4687
+ var text = this.texts.get(id);
4688
+ if (!text)
4689
+ return null;
4690
+ if (!text.ownerId)
4691
+ return __assign({}, text.position);
4692
+ var element = this.elements.get(text.ownerId);
4693
+ if (element) {
4694
+ var elementPosition = this.resolveElementWorldPosition(element);
4695
+ return {
4696
+ x: elementPosition.x + text.position.x,
4697
+ y: elementPosition.y + text.position.y,
4698
+ };
4305
4699
  }
4306
- else {
4307
- this.selectedIds.add(id);
4700
+ var port = this.ports.get(text.ownerId);
4701
+ if (port) {
4702
+ var portPosition = this.getPortWorldPosition(port.id);
4703
+ if (!portPosition)
4704
+ return __assign({}, text.position);
4705
+ return {
4706
+ x: portPosition.x + text.position.x,
4707
+ y: portPosition.y + text.position.y,
4708
+ };
4709
+ }
4710
+ var link = this.links.get(text.ownerId);
4711
+ if (link) {
4712
+ var midpoint = this.resolveLinkMidpoint(link);
4713
+ return {
4714
+ x: midpoint.x + text.position.x,
4715
+ y: midpoint.y + text.position.y,
4716
+ };
4308
4717
  }
4718
+ return __assign({}, text.position);
4309
4719
  };
4310
- SelectionState.prototype.get = function () {
4311
- return Array.from(this.selectedIds.values());
4720
+ DiagramModel.prototype.addElement = function (data) {
4721
+ var element = new ElementModel(data);
4722
+ this.elements.set(element.id, element);
4723
+ return element;
4312
4724
  };
4313
- SelectionState.prototype.clear = function () {
4314
- this.selectedIds.clear();
4725
+ DiagramModel.prototype.moveElement = function (id, position) {
4726
+ var element = this.elements.get(id);
4727
+ if (!element)
4728
+ return;
4729
+ element.setPosition(position);
4315
4730
  };
4316
- return SelectionState;
4317
- }());
4318
-
4319
- var ViewportState = /** @class */ (function () {
4320
- function ViewportState() {
4321
- this.pan = { x: 0, y: 0 };
4322
- this.zoom = 1;
4323
- }
4324
- ViewportState.prototype.setPan = function (point) {
4325
- this.pan = __assign({}, point);
4731
+ DiagramModel.prototype.resizeElement = function (id, size) {
4732
+ var element = this.elements.get(id);
4733
+ if (!element)
4734
+ return;
4735
+ element.setSize(size);
4326
4736
  };
4327
- ViewportState.prototype.setZoom = function (value) {
4328
- this.zoom = value;
4737
+ DiagramModel.prototype.removeElement = function (id) {
4738
+ var _this = this;
4739
+ var element = this.elements.get(id);
4740
+ if (!element)
4741
+ return;
4742
+ var portIds = __spreadArray([], element.portIds, true);
4743
+ var textIds = __spreadArray([], element.textIds, true);
4744
+ portIds.forEach(function (portId) { return _this.removePort(portId); });
4745
+ textIds.forEach(function (textId) { return _this.removeText(textId); });
4746
+ this.elements.delete(id);
4329
4747
  };
4330
- return ViewportState;
4331
- }());
4332
-
4333
- var patchAdd = function (entity, id, payload) { return ({
4334
- type: 'add',
4335
- entity: entity,
4336
- id: id,
4337
- payload: payload,
4338
- }); };
4339
- var patchUpdate = function (entity, id, payload) { return ({
4340
- type: 'update',
4341
- entity: entity,
4342
- id: id,
4343
- payload: payload,
4344
- }); };
4345
- var patchRemove = function (entity, id) { return ({
4346
- type: 'remove',
4347
- entity: entity,
4348
- id: id,
4349
- }); };
4350
-
4351
- var createLoadCommand = function (state) { return ({
4352
- type: 'load',
4353
- apply: function (model) {
4354
- model.load(state);
4355
- return [{ type: 'load', entity: 'config', payload: {} }];
4356
- },
4357
- }); };
4358
- var createAddElementCommand = function (element) { return ({
4359
- type: 'addElement',
4360
- apply: function (model) {
4361
- model.addElement(element);
4362
- return [patchAdd('element', element.id, element)];
4363
- },
4364
- }); };
4365
- var createMoveElementCommand = function (id, position) { return ({
4366
- type: 'moveElement',
4367
- apply: function (model) {
4368
- model.moveElement(id, position);
4369
- return [patchUpdate('element', id, { position: position })];
4370
- },
4371
- }); };
4372
- var createResizeElementCommand = function (id, size) { return ({
4373
- type: 'resizeElement',
4374
- apply: function (model) {
4375
- var current = model.getElement(id);
4376
- var previousSize = current ? __assign({}, current.size) : undefined;
4377
- model.resizeElement(id, size);
4378
- return [patchUpdate('element', id, { size: size, previousSize: previousSize })];
4379
- },
4380
- }); };
4381
- var createRemoveElementCommand = function (id) { return ({
4382
- type: 'removeElement',
4383
- apply: function (model) {
4384
- model.removeElement(id);
4385
- return [patchRemove('element', id)];
4386
- },
4387
- }); };
4388
- var createSetElementLayoutCommand = function (id, layout) { return ({
4389
- type: 'setElementLayout',
4390
- apply: function (model) {
4391
- model.setElementLayout(id, layout);
4392
- return [patchUpdate('element', id, { layout: layout })];
4393
- },
4394
- }); };
4395
- var createAddPortCommand = function (port) { return ({
4396
- type: 'addPort',
4397
- apply: function (model) {
4398
- model.addPort(port);
4399
- return [patchAdd('port', port.id, port)];
4400
- },
4401
- }); };
4402
- var createMovePortCommand = function (id, update) { return ({
4403
- type: 'movePort',
4404
- apply: function (model) {
4405
- var _a, _b;
4406
- var hasCurrentAnchorId = Object.prototype.hasOwnProperty.call(update, 'currentAnchorId');
4407
- model.movePort(id, update.position);
4408
- if (hasCurrentAnchorId) {
4409
- model.setPortCurrentAnchorId(id, (_a = update.currentAnchorId) !== null && _a !== void 0 ? _a : undefined);
4410
- }
4411
- var payload = { position: update.position };
4412
- if (hasCurrentAnchorId) {
4413
- payload.currentAnchorId = (_b = update.currentAnchorId) !== null && _b !== void 0 ? _b : null;
4414
- }
4415
- return [patchUpdate('port', id, payload)];
4416
- },
4417
- }); };
4418
- var createRemovePortCommand = function (id) { return ({
4419
- type: 'removePort',
4420
- apply: function (model) {
4421
- model.removePort(id);
4422
- return [patchRemove('port', id)];
4423
- },
4424
- }); };
4425
- var createAddLinkCommand = function (link) { return ({
4426
- type: 'addLink',
4427
- apply: function (model) {
4428
- model.addLink(link);
4429
- return [patchAdd('link', link.id, link)];
4430
- },
4431
- }); };
4432
- var createUpdateLinkCommand = function (id, update) { return ({
4433
- type: 'updateLink',
4434
- apply: function (model) {
4435
- if (update.points !== undefined) {
4436
- model.updateLinkPoints(id, update.points);
4748
+ DiagramModel.prototype.addPort = function (data) {
4749
+ var port = new PortModel(data);
4750
+ this.ports.set(port.id, port);
4751
+ var element = this.elements.get(port.elementId);
4752
+ if (element) {
4753
+ element.addPort(port.id);
4437
4754
  }
4438
- if (update.routing !== undefined) {
4439
- model.setLinkRoutingMode(id, update.routing);
4755
+ return port;
4756
+ };
4757
+ DiagramModel.prototype.movePort = function (id, position) {
4758
+ var port = this.ports.get(id);
4759
+ if (!port)
4760
+ return;
4761
+ port.setPosition(position);
4762
+ };
4763
+ DiagramModel.prototype.setPortCurrentAnchorId = function (id, currentAnchorId) {
4764
+ var port = this.ports.get(id);
4765
+ if (!port)
4766
+ return;
4767
+ port.setCurrentAnchorId(currentAnchorId);
4768
+ };
4769
+ DiagramModel.prototype.removePort = function (id) {
4770
+ var _this = this;
4771
+ var port = this.ports.get(id);
4772
+ if (!port)
4773
+ return;
4774
+ var element = this.elements.get(port.elementId);
4775
+ if (element) {
4776
+ element.removePort(id);
4440
4777
  }
4441
- var payload = {};
4442
- if (update.points !== undefined)
4443
- payload.points = update.points;
4444
- if (update.routing !== undefined)
4445
- payload.routing = update.routing;
4446
- return [patchUpdate('link', id, payload)];
4447
- },
4448
- }); };
4449
- var createRemoveLinkCommand = function (id) { return ({
4450
- type: 'removeLink',
4451
- apply: function (model) {
4452
- model.removeLink(id);
4453
- return [patchRemove('link', id)];
4454
- },
4455
- }); };
4456
- var createAddTextCommand = function (text) { return ({
4457
- type: 'addText',
4458
- apply: function (model) {
4459
- model.addText(text);
4460
- return [patchAdd('text', text.id, text)];
4461
- },
4462
- }); };
4463
- var createUpdateTextCommand = function (id, content) { return ({
4464
- type: 'updateText',
4465
- apply: function (model) {
4466
- model.updateText(id, content);
4467
- return [patchUpdate('text', id, { content: content })];
4468
- },
4469
- }); };
4470
- var createMoveTextCommand = function (id, position) { return ({
4471
- type: 'moveText',
4472
- apply: function (model) {
4473
- model.moveText(id, position);
4474
- return [patchUpdate('text', id, { position: position })];
4475
- },
4476
- }); };
4477
- var createRemoveTextCommand = function (id) { return ({
4478
- type: 'removeText',
4479
- apply: function (model) {
4480
- model.removeText(id);
4481
- return [patchRemove('text', id)];
4482
- },
4483
- }); };
4484
-
4485
- var RenderScheduler = /** @class */ (function () {
4486
- function RenderScheduler() {
4487
- this.pending = false;
4488
- this.lastCallback = null;
4489
- }
4490
- RenderScheduler.prototype.requestRender = function (callback) {
4778
+ var portTextIds = __spreadArray([], port.textIds, true);
4779
+ portTextIds.forEach(function (textId) { return _this.removeText(textId); });
4780
+ var linksToRemove = Array.from(this.links.values())
4781
+ .filter(function (link) { return link.sourcePortId === id || link.targetPortId === id; })
4782
+ .map(function (link) { return link.id; });
4783
+ linksToRemove.forEach(function (linkId) { return _this.removeLink(linkId); });
4784
+ this.ports.delete(id);
4785
+ };
4786
+ DiagramModel.prototype.addLink = function (data) {
4787
+ var link = new LinkModel(data);
4788
+ this.links.set(link.id, link);
4789
+ return link;
4790
+ };
4791
+ DiagramModel.prototype.updateLinkPoints = function (id, points) {
4792
+ var link = this.links.get(id);
4793
+ if (!link)
4794
+ return;
4795
+ link.setPoints(points);
4796
+ };
4797
+ DiagramModel.prototype.setLinkRoutingMode = function (id, mode) {
4798
+ var link = this.links.get(id);
4799
+ if (!link)
4800
+ return;
4801
+ link.setRouting(mode);
4802
+ };
4803
+ DiagramModel.prototype.removeLink = function (id) {
4491
4804
  var _this = this;
4492
- this.lastCallback = callback;
4493
- if (this.pending)
4805
+ var link = this.links.get(id);
4806
+ if (!link)
4494
4807
  return;
4495
- this.pending = true;
4496
- var schedule = typeof requestAnimationFrame !== 'undefined'
4497
- ? requestAnimationFrame
4498
- : function (cb) { return setTimeout(function () { return cb(Date.now()); }, 16); };
4499
- schedule(function () {
4500
- _this.pending = false;
4501
- var next = _this.lastCallback;
4502
- _this.lastCallback = null;
4503
- if (next) {
4504
- next();
4808
+ var textIds = __spreadArray([], link.textIds, true);
4809
+ textIds.forEach(function (textId) { return _this.removeText(textId); });
4810
+ this.links.delete(id);
4811
+ };
4812
+ DiagramModel.prototype.addText = function (data) {
4813
+ var text = new TextModel(data);
4814
+ this.texts.set(text.id, text);
4815
+ if (text.ownerId) {
4816
+ this.ownership.add(text.ownerId, text.id);
4817
+ var element = this.elements.get(text.ownerId);
4818
+ if (element) {
4819
+ element.addText(text.id);
4505
4820
  }
4506
- });
4821
+ var port = this.ports.get(text.ownerId);
4822
+ if (port) {
4823
+ port.addText(text.id);
4824
+ }
4825
+ var link = this.links.get(text.ownerId);
4826
+ if (link) {
4827
+ link.addText(text.id);
4828
+ }
4829
+ }
4830
+ return text;
4507
4831
  };
4508
- return RenderScheduler;
4509
- }());
4510
-
4511
- var DEFAULT_PADDING = 12;
4512
- var DEFAULT_TOLERANCE = 0.5;
4513
- var DEFAULT_STUB_LENGTH = 12;
4514
- var shrinkRect = function (rect, amount) {
4515
- var width = rect.width - amount * 2;
4516
- var height = rect.height - amount * 2;
4517
- if (width <= 0 || height <= 0)
4518
- return null;
4519
- return {
4520
- x: rect.x + amount,
4521
- y: rect.y + amount,
4522
- width: width,
4523
- height: height,
4832
+ DiagramModel.prototype.updateText = function (id, content) {
4833
+ var text = this.texts.get(id);
4834
+ if (!text)
4835
+ return;
4836
+ text.setContent(content);
4524
4837
  };
4525
- };
4526
- var segmentIntersectsRect = function (start, end, rect) {
4527
- var dx = end.x - start.x;
4528
- var dy = end.y - start.y;
4529
- var p = [-dx, dx, -dy, dy];
4530
- var q = [
4531
- start.x - rect.x,
4532
- rect.x + rect.width - start.x,
4533
- start.y - rect.y,
4534
- rect.y + rect.height - start.y,
4535
- ];
4536
- var t0 = 0;
4537
- var t1 = 1;
4538
- for (var i = 0; i < 4; i += 1) {
4539
- var pi = p[i];
4540
- var qi = q[i];
4541
- if (pi === 0) {
4542
- if (qi < 0)
4543
- return false;
4544
- }
4545
- else {
4546
- var r = qi / pi;
4547
- if (pi < 0) {
4548
- if (r > t1)
4549
- return false;
4550
- if (r > t0)
4551
- t0 = r;
4838
+ DiagramModel.prototype.moveText = function (id, position) {
4839
+ var text = this.texts.get(id);
4840
+ if (!text)
4841
+ return;
4842
+ text.setPosition(position);
4843
+ };
4844
+ DiagramModel.prototype.removeText = function (id) {
4845
+ var text = this.texts.get(id);
4846
+ if (!text)
4847
+ return;
4848
+ if (text.ownerId) {
4849
+ this.ownership.remove(text.ownerId, id);
4850
+ var element = this.elements.get(text.ownerId);
4851
+ if (element) {
4852
+ element.removeText(id);
4552
4853
  }
4553
- else {
4554
- if (r < t0)
4555
- return false;
4556
- if (r < t1)
4557
- t1 = r;
4854
+ var port = this.ports.get(text.ownerId);
4855
+ if (port) {
4856
+ port.removeText(id);
4857
+ }
4858
+ var link = this.links.get(text.ownerId);
4859
+ if (link) {
4860
+ link.removeText(id);
4558
4861
  }
4559
4862
  }
4560
- }
4561
- return true;
4562
- };
4563
- var segmentIntersectsRectInterior = function (start, end, rect, tolerance) {
4564
- var inner = shrinkRect(rect, tolerance);
4565
- if (!inner)
4566
- return false;
4567
- return segmentIntersectsRect(start, end, inner);
4568
- };
4569
- var pointWithinRect = function (point, rect, tolerance) {
4570
- return (point.x >= rect.x - tolerance &&
4571
- point.x <= rect.x + rect.width + tolerance &&
4572
- point.y >= rect.y - tolerance &&
4573
- point.y <= rect.y + rect.height + tolerance);
4574
- };
4575
- var compactPath = function (points) {
4576
- var deduped = [];
4577
- points.forEach(function (point) {
4578
- var last = deduped[deduped.length - 1];
4579
- if (!last || last.x !== point.x || last.y !== point.y) {
4580
- deduped.push(point);
4581
- }
4582
- });
4583
- if (deduped.length < 3)
4584
- return deduped;
4585
- var compacted = [deduped[0]];
4586
- for (var i = 1; i < deduped.length; i += 1) {
4587
- var prev = compacted[compacted.length - 1];
4588
- var next = deduped[i];
4589
- var beforePrev = compacted[compacted.length - 2];
4590
- if (beforePrev &&
4591
- ((beforePrev.x === prev.x && prev.x === next.x) || (beforePrev.y === prev.y && prev.y === next.y))) {
4592
- compacted[compacted.length - 1] = next;
4593
- }
4594
- else {
4595
- compacted.push(next);
4596
- }
4597
- }
4598
- return compacted;
4599
- };
4600
- var pathLength = function (points) {
4601
- var total = 0;
4602
- for (var i = 1; i < points.length; i += 1) {
4603
- var dx = points[i].x - points[i - 1].x;
4604
- var dy = points[i].y - points[i - 1].y;
4605
- total += Math.sqrt(dx * dx + dy * dy);
4606
- }
4607
- return total;
4608
- };
4609
- var ObstacleRouter = /** @class */ (function () {
4610
- function ObstacleRouter(options) {
4611
- var _a, _b, _c;
4612
- this.padding = (_a = options === null || options === void 0 ? void 0 : options.padding) !== null && _a !== void 0 ? _a : DEFAULT_PADDING;
4613
- this.tolerance = (_b = options === null || options === void 0 ? void 0 : options.tolerance) !== null && _b !== void 0 ? _b : DEFAULT_TOLERANCE;
4614
- this.stubLength = (_c = options === null || options === void 0 ? void 0 : options.stubLength) !== null && _c !== void 0 ? _c : DEFAULT_STUB_LENGTH;
4615
- }
4616
- ObstacleRouter.prototype.route = function (source, target, context) {
4617
- var _a;
4618
- var obstacles = (_a = context === null || context === void 0 ? void 0 : context.obstacles) !== null && _a !== void 0 ? _a : [];
4619
- var bounds = context === null || context === void 0 ? void 0 : context.bounds;
4620
- var sourceStub = this.computeStubEndpoint(source, context === null || context === void 0 ? void 0 : context.sourceEndpoint, bounds, obstacles);
4621
- var targetStub = this.computeStubEndpoint(target, context === null || context === void 0 ? void 0 : context.targetEndpoint, bounds, obstacles);
4622
- var innerSource = sourceStub !== null && sourceStub !== void 0 ? sourceStub : source;
4623
- var innerTarget = targetStub !== null && targetStub !== void 0 ? targetStub : target;
4624
- var core = this.routeBetween(innerSource, innerTarget, obstacles, bounds);
4625
- var normalizedCore = this.ensureEndpoints(core, innerSource, innerTarget);
4626
- var middle = normalizedCore.slice(1, -1).map(function (p) { return (__assign({}, p)); });
4627
- var result = [__assign({}, source)];
4628
- var hasSourceStub = Boolean(sourceStub);
4629
- var hasTargetStub = Boolean(targetStub);
4630
- if (hasSourceStub && sourceStub) {
4631
- result.push(__assign({}, sourceStub));
4632
- }
4633
- result.push.apply(result, middle);
4634
- if (hasTargetStub && targetStub) {
4635
- result.push(__assign({}, targetStub));
4636
- }
4637
- result.push(__assign({}, target));
4638
- return this.compactPreservingStubs(result, hasSourceStub, hasTargetStub);
4639
- };
4640
- ObstacleRouter.prototype.buildCandidates = function (source, target, obstacles) {
4641
- var _this = this;
4642
- var midXValues = new Set();
4643
- var midYValues = new Set();
4644
- midXValues.add(Math.round((source.x + target.x) / 2));
4645
- midXValues.add(source.x);
4646
- midXValues.add(target.x);
4647
- midYValues.add(Math.round((source.y + target.y) / 2));
4648
- midYValues.add(source.y);
4649
- midYValues.add(target.y);
4650
- obstacles.forEach(function (obstacle) {
4651
- midXValues.add(obstacle.rect.x - _this.padding);
4652
- midXValues.add(obstacle.rect.x + obstacle.rect.width + _this.padding);
4653
- midYValues.add(obstacle.rect.y - _this.padding);
4654
- midYValues.add(obstacle.rect.y + obstacle.rect.height + _this.padding);
4655
- });
4656
- var candidates = [];
4657
- midXValues.forEach(function (midX) {
4658
- var path = compactPath([
4659
- __assign({}, source),
4660
- { x: midX, y: source.y },
4661
- { x: midX, y: target.y },
4662
- __assign({}, target),
4663
- ]);
4664
- candidates.push(path);
4665
- });
4666
- midYValues.forEach(function (midY) {
4667
- var path = compactPath([
4668
- __assign({}, source),
4669
- { x: source.x, y: midY },
4670
- { x: target.x, y: midY },
4671
- __assign({}, target),
4672
- ]);
4673
- candidates.push(path);
4674
- });
4675
- return candidates;
4863
+ this.texts.delete(id);
4676
4864
  };
4677
- ObstacleRouter.prototype.routeBetween = function (source, target, obstacles, bounds) {
4678
- var _this = this;
4679
- var direct = compactPath([__assign({}, source), __assign({}, target)]);
4680
- if (obstacles.length === 0 && (!bounds || this.pointsWithinBounds(direct, bounds))) {
4681
- return direct;
4865
+ DiagramModel.prototype.resolveElementWorldPosition = function (element) {
4866
+ var x = element.position.x;
4867
+ var y = element.position.y;
4868
+ if (element.anchorCenter) {
4869
+ x -= element.size.width / 2;
4870
+ y -= element.size.height / 2;
4871
+ }
4872
+ var current = element;
4873
+ while (current === null || current === void 0 ? void 0 : current.parentId) {
4874
+ var parent_1 = this.elements.get(current.parentId);
4875
+ if (!parent_1)
4876
+ break;
4877
+ var parentX = parent_1.position.x;
4878
+ var parentY = parent_1.position.y;
4879
+ if (parent_1.anchorCenter) {
4880
+ parentX -= parent_1.size.width / 2;
4881
+ parentY -= parent_1.size.height / 2;
4882
+ }
4883
+ x += parentX;
4884
+ y += parentY;
4885
+ current = parent_1;
4682
4886
  }
4683
- if (this.isPathClear(direct, obstacles, bounds)) {
4684
- return direct;
4887
+ return { x: x, y: y };
4888
+ };
4889
+ DiagramModel.prototype.resolveLinkMidpoint = function (link) {
4890
+ var points = link.points;
4891
+ if (points.length === 0)
4892
+ return { x: 0, y: 0 };
4893
+ if (points.length === 1)
4894
+ return __assign({}, points[0]);
4895
+ var total = 0;
4896
+ for (var i = 1; i < points.length; i += 1) {
4897
+ total += distance(points[i - 1], points[i]);
4685
4898
  }
4686
- var candidates = this.buildCandidates(source, target, obstacles);
4687
- var best = null;
4688
- var bestLength = Number.POSITIVE_INFINITY;
4689
- candidates.forEach(function (candidate) {
4690
- if (!_this.isPathClear(candidate, obstacles, bounds))
4691
- return;
4692
- var length = pathLength(candidate);
4693
- if (length < bestLength) {
4694
- best = candidate;
4695
- bestLength = length;
4899
+ var halfway = total / 2;
4900
+ var traveled = 0;
4901
+ for (var i = 1; i < points.length; i += 1) {
4902
+ var segment = distance(points[i - 1], points[i]);
4903
+ if (traveled + segment >= halfway) {
4904
+ var ratio = segment === 0 ? 0 : (halfway - traveled) / segment;
4905
+ return {
4906
+ x: points[i - 1].x + (points[i].x - points[i - 1].x) * ratio,
4907
+ y: points[i - 1].y + (points[i].y - points[i - 1].y) * ratio,
4908
+ };
4696
4909
  }
4697
- });
4698
- return best !== null && best !== void 0 ? best : direct;
4910
+ traveled += segment;
4911
+ }
4912
+ return __assign({}, points[points.length - 1]);
4699
4913
  };
4700
- ObstacleRouter.prototype.pointsWithinBounds = function (points, bounds) {
4701
- var _this = this;
4702
- return points.every(function (point) { return pointWithinRect(point, bounds, _this.tolerance); });
4914
+ return DiagramModel;
4915
+ }());
4916
+
4917
+ var CommandQueue = /** @class */ (function () {
4918
+ function CommandQueue() {
4919
+ this.queue = [];
4920
+ }
4921
+ CommandQueue.prototype.enqueue = function (command) {
4922
+ this.queue.push(command);
4703
4923
  };
4704
- ObstacleRouter.prototype.isPathClear = function (points, obstacles, bounds) {
4705
- if (points.length < 2)
4706
- return true;
4707
- if (bounds && !this.pointsWithinBounds(points, bounds))
4708
- return false;
4709
- for (var i = 1; i < points.length; i += 1) {
4710
- var start = points[i - 1];
4711
- var end = points[i];
4712
- if (start.x === end.x && start.y === end.y)
4924
+ CommandQueue.prototype.flush = function (model) {
4925
+ var patches = [];
4926
+ while (this.queue.length > 0) {
4927
+ var command = this.queue.shift();
4928
+ if (!command)
4713
4929
  continue;
4714
- for (var j = 0; j < obstacles.length; j += 1) {
4715
- if (segmentIntersectsRectInterior(start, end, obstacles[j].rect, this.tolerance)) {
4716
- return false;
4717
- }
4718
- }
4930
+ var next = command.apply(model);
4931
+ patches.push.apply(patches, next);
4719
4932
  }
4720
- return true;
4933
+ return patches;
4721
4934
  };
4722
- ObstacleRouter.prototype.computeStubEndpoint = function (point, endpoint, bounds, obstacles) {
4723
- if (!(endpoint === null || endpoint === void 0 ? void 0 : endpoint.onEdgeSide) || !endpoint.rect)
4724
- return null;
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;
4935
+ CommandQueue.prototype.run = function (command, model) {
4936
+ this.enqueue(command);
4937
+ return this.flush(model);
4938
+ };
4939
+ return CommandQueue;
4940
+ }());
4941
+
4942
+ var EventBus = /** @class */ (function () {
4943
+ function EventBus() {
4944
+ this.handlers = new Map();
4945
+ }
4946
+ EventBus.prototype.subscribe = function (event, handler) {
4947
+ var _this = this;
4948
+ var _a;
4949
+ var set = (_a = this.handlers.get(event)) !== null && _a !== void 0 ? _a : new Set();
4950
+ set.add(handler);
4951
+ this.handlers.set(event, set);
4952
+ return function () {
4953
+ var existing = _this.handlers.get(event);
4954
+ if (!existing)
4955
+ return;
4956
+ existing.delete(handler);
4957
+ if (existing.size === 0) {
4958
+ _this.handlers.delete(event);
4736
4959
  }
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
4960
  };
4744
4961
  };
4745
- ObstacleRouter.prototype.computeAvailableStubLength = function (point, normal, bounds, obstacles, ignoreId) {
4746
- var maxLength = this.stubLength;
4747
- if (bounds) {
4748
- maxLength = Math.min(maxLength, this.distanceToBounds(point, normal, bounds));
4962
+ EventBus.prototype.emit = function (event, payload) {
4963
+ var set = this.handlers.get(event);
4964
+ if (!set)
4965
+ return;
4966
+ Array.from(set.values()).forEach(function (handler) {
4967
+ handler(payload);
4968
+ });
4969
+ };
4970
+ return EventBus;
4971
+ }());
4972
+
4973
+ var SelectionState = /** @class */ (function () {
4974
+ function SelectionState() {
4975
+ this.selectedIds = new Set();
4976
+ }
4977
+ SelectionState.prototype.set = function (ids) {
4978
+ this.selectedIds = new Set(ids);
4979
+ };
4980
+ SelectionState.prototype.toggle = function (id) {
4981
+ if (this.selectedIds.has(id)) {
4982
+ this.selectedIds.delete(id);
4749
4983
  }
4750
- var obstacleLimit = this.distanceToObstacles(point, normal, obstacles, ignoreId);
4751
- if (obstacleLimit !== null) {
4752
- maxLength = Math.min(maxLength, obstacleLimit);
4984
+ else {
4985
+ this.selectedIds.add(id);
4753
4986
  }
4754
- return maxLength;
4755
4987
  };
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);
4988
+ SelectionState.prototype.get = function () {
4989
+ return Array.from(this.selectedIds.values());
4761
4990
  };
4762
- ObstacleRouter.prototype.getNormal = function (side) {
4763
- switch (side) {
4764
- case 'left':
4765
- return { x: -1, y: 0 };
4766
- case 'right':
4767
- return { x: 1, y: 0 };
4768
- case 'top':
4769
- return { x: 0, y: -1 };
4770
- case 'bottom':
4771
- default:
4772
- return { x: 0, y: 1 };
4991
+ SelectionState.prototype.clear = function () {
4992
+ this.selectedIds.clear();
4993
+ };
4994
+ return SelectionState;
4995
+ }());
4996
+
4997
+ var ViewportState = /** @class */ (function () {
4998
+ function ViewportState() {
4999
+ this.pan = { x: 0, y: 0 };
5000
+ this.zoom = 1;
5001
+ }
5002
+ ViewportState.prototype.setPan = function (point) {
5003
+ this.pan = __assign({}, point);
5004
+ };
5005
+ ViewportState.prototype.setZoom = function (value) {
5006
+ this.zoom = value;
5007
+ };
5008
+ return ViewportState;
5009
+ }());
5010
+
5011
+ var patchAdd = function (entity, id, payload) { return ({
5012
+ type: 'add',
5013
+ entity: entity,
5014
+ id: id,
5015
+ payload: payload,
5016
+ }); };
5017
+ var patchUpdate = function (entity, id, payload) { return ({
5018
+ type: 'update',
5019
+ entity: entity,
5020
+ id: id,
5021
+ payload: payload,
5022
+ }); };
5023
+ var patchRemove = function (entity, id) { return ({
5024
+ type: 'remove',
5025
+ entity: entity,
5026
+ id: id,
5027
+ }); };
5028
+
5029
+ var createLoadCommand = function (state) { return ({
5030
+ type: 'load',
5031
+ apply: function (model) {
5032
+ model.load(state);
5033
+ return [{ type: 'load', entity: 'config', payload: {} }];
5034
+ },
5035
+ }); };
5036
+ var createAddElementCommand = function (element) { return ({
5037
+ type: 'addElement',
5038
+ apply: function (model) {
5039
+ model.addElement(element);
5040
+ return [patchAdd('element', element.id, element)];
5041
+ },
5042
+ }); };
5043
+ var createMoveElementCommand = function (id, position) { return ({
5044
+ type: 'moveElement',
5045
+ apply: function (model) {
5046
+ model.moveElement(id, position);
5047
+ return [patchUpdate('element', id, { position: position })];
5048
+ },
5049
+ }); };
5050
+ var createResizeElementCommand = function (id, size) { return ({
5051
+ type: 'resizeElement',
5052
+ apply: function (model) {
5053
+ var current = model.getElement(id);
5054
+ var previousSize = current ? __assign({}, current.size) : undefined;
5055
+ model.resizeElement(id, size);
5056
+ return [patchUpdate('element', id, { size: size, previousSize: previousSize })];
5057
+ },
5058
+ }); };
5059
+ var createRemoveElementCommand = function (id) { return ({
5060
+ type: 'removeElement',
5061
+ apply: function (model) {
5062
+ model.removeElement(id);
5063
+ return [patchRemove('element', id)];
5064
+ },
5065
+ }); };
5066
+ var createSetElementLayoutCommand = function (id, layout) { return ({
5067
+ type: 'setElementLayout',
5068
+ apply: function (model) {
5069
+ model.setElementLayout(id, layout);
5070
+ return [patchUpdate('element', id, { layout: layout })];
5071
+ },
5072
+ }); };
5073
+ var createAddPortCommand = function (port) { return ({
5074
+ type: 'addPort',
5075
+ apply: function (model) {
5076
+ model.addPort(port);
5077
+ return [patchAdd('port', port.id, port)];
5078
+ },
5079
+ }); };
5080
+ var createMovePortCommand = function (id, update) { return ({
5081
+ type: 'movePort',
5082
+ apply: function (model) {
5083
+ var _a, _b;
5084
+ var hasCurrentAnchorId = Object.prototype.hasOwnProperty.call(update, 'currentAnchorId');
5085
+ model.movePort(id, update.position);
5086
+ if (hasCurrentAnchorId) {
5087
+ model.setPortCurrentAnchorId(id, (_a = update.currentAnchorId) !== null && _a !== void 0 ? _a : undefined);
4773
5088
  }
4774
- };
4775
- ObstacleRouter.prototype.distanceToBounds = function (point, normal, bounds) {
4776
- if (normal.x < 0) {
4777
- return Math.max(0, point.x - bounds.x);
5089
+ var payload = { position: update.position };
5090
+ if (hasCurrentAnchorId) {
5091
+ payload.currentAnchorId = (_b = update.currentAnchorId) !== null && _b !== void 0 ? _b : null;
4778
5092
  }
4779
- if (normal.x > 0) {
4780
- return Math.max(0, bounds.x + bounds.width - point.x);
5093
+ return [patchUpdate('port', id, payload)];
5094
+ },
5095
+ }); };
5096
+ var createRemovePortCommand = function (id) { return ({
5097
+ type: 'removePort',
5098
+ apply: function (model) {
5099
+ model.removePort(id);
5100
+ return [patchRemove('port', id)];
5101
+ },
5102
+ }); };
5103
+ var createAddLinkCommand = function (link) { return ({
5104
+ type: 'addLink',
5105
+ apply: function (model) {
5106
+ model.addLink(link);
5107
+ return [patchAdd('link', link.id, link)];
5108
+ },
5109
+ }); };
5110
+ var createUpdateLinkCommand = function (id, update) { return ({
5111
+ type: 'updateLink',
5112
+ apply: function (model) {
5113
+ if (update.points !== undefined) {
5114
+ model.updateLinkPoints(id, update.points);
4781
5115
  }
4782
- if (normal.y < 0) {
4783
- return Math.max(0, point.y - bounds.y);
5116
+ if (update.routing !== undefined) {
5117
+ model.setLinkRoutingMode(id, update.routing);
4784
5118
  }
4785
- return Math.max(0, bounds.y + bounds.height - point.y);
4786
- };
4787
- ObstacleRouter.prototype.distanceToObstacles = function (point, normal, obstacles, ignoreId) {
5119
+ var payload = {};
5120
+ if (update.points !== undefined)
5121
+ payload.points = update.points;
5122
+ if (update.routing !== undefined)
5123
+ payload.routing = update.routing;
5124
+ return [patchUpdate('link', id, payload)];
5125
+ },
5126
+ }); };
5127
+ var createRemoveLinkCommand = function (id) { return ({
5128
+ type: 'removeLink',
5129
+ apply: function (model) {
5130
+ model.removeLink(id);
5131
+ return [patchRemove('link', id)];
5132
+ },
5133
+ }); };
5134
+ var createAddTextCommand = function (text) { return ({
5135
+ type: 'addText',
5136
+ apply: function (model) {
5137
+ model.addText(text);
5138
+ return [patchAdd('text', text.id, text)];
5139
+ },
5140
+ }); };
5141
+ var createUpdateTextCommand = function (id, content) { return ({
5142
+ type: 'updateText',
5143
+ apply: function (model) {
5144
+ model.updateText(id, content);
5145
+ return [patchUpdate('text', id, { content: content })];
5146
+ },
5147
+ }); };
5148
+ var createMoveTextCommand = function (id, position) { return ({
5149
+ type: 'moveText',
5150
+ apply: function (model) {
5151
+ model.moveText(id, position);
5152
+ return [patchUpdate('text', id, { position: position })];
5153
+ },
5154
+ }); };
5155
+ var createRemoveTextCommand = function (id) { return ({
5156
+ type: 'removeText',
5157
+ apply: function (model) {
5158
+ model.removeText(id);
5159
+ return [patchRemove('text', id)];
5160
+ },
5161
+ }); };
5162
+
5163
+ var RenderScheduler = /** @class */ (function () {
5164
+ function RenderScheduler() {
5165
+ this.pending = false;
5166
+ this.lastCallback = null;
5167
+ }
5168
+ RenderScheduler.prototype.requestRender = function (callback) {
4788
5169
  var _this = this;
4789
- var limit = Number.POSITIVE_INFINITY;
4790
- var overlapsHorizontally = function (rect) {
4791
- return point.y >= rect.y - _this.tolerance && point.y <= rect.y + rect.height + _this.tolerance;
4792
- };
4793
- var overlapsVertically = function (rect) {
4794
- return point.x >= rect.x - _this.tolerance && point.x <= rect.x + rect.width + _this.tolerance;
4795
- };
4796
- obstacles.forEach(function (obstacle) {
4797
- if (ignoreId && obstacle.id === ignoreId)
4798
- return;
4799
- var rect = obstacle.rect;
4800
- if (normal.x < 0 && overlapsHorizontally(rect) && rect.x + rect.width <= point.x) {
4801
- var available = point.x - (rect.x + rect.width + _this.tolerance);
4802
- if (available < limit)
4803
- limit = available;
4804
- }
4805
- else if (normal.x > 0 && overlapsHorizontally(rect) && rect.x >= point.x) {
4806
- var available = rect.x - _this.tolerance - point.x;
4807
- if (available < limit)
4808
- limit = available;
4809
- }
4810
- else if (normal.y < 0 && overlapsVertically(rect) && rect.y + rect.height <= point.y) {
4811
- var available = point.y - (rect.y + rect.height + _this.tolerance);
4812
- if (available < limit)
4813
- limit = available;
4814
- }
4815
- else if (normal.y > 0 && overlapsVertically(rect) && rect.y >= point.y) {
4816
- var available = rect.y - _this.tolerance - point.y;
4817
- if (available < limit)
4818
- limit = available;
4819
- }
4820
- });
4821
- if (!Number.isFinite(limit))
4822
- return null;
4823
- return Math.max(0, limit);
4824
- };
4825
- ObstacleRouter.prototype.ensureEndpoints = function (points, source, target) {
4826
- if (points.length === 0)
4827
- return [__assign({}, source), __assign({}, target)];
4828
- var result = __spreadArray([], points, true);
4829
- if (result.length === 1) {
4830
- result.unshift(__assign({}, source));
4831
- result.push(__assign({}, target));
4832
- return result;
4833
- }
4834
- if (result[0].x !== source.x || result[0].y !== source.y) {
4835
- result.unshift(__assign({}, source));
4836
- }
4837
- var last = result[result.length - 1];
4838
- if (last.x !== target.x || last.y !== target.y) {
4839
- result.push(__assign({}, target));
4840
- }
4841
- return result;
4842
- };
4843
- ObstacleRouter.prototype.compactPreservingStubs = function (points, hasSourceStub, hasTargetStub) {
4844
- if (points.length <= 2)
4845
- return points.map(function (p) { return (__assign({}, p)); });
4846
- var deduped = [];
4847
- points.forEach(function (point) {
4848
- var last = deduped[deduped.length - 1];
4849
- if (!last || last.x !== point.x || last.y !== point.y) {
4850
- deduped.push(point);
5170
+ this.lastCallback = callback;
5171
+ if (this.pending)
5172
+ return;
5173
+ this.pending = true;
5174
+ var schedule = typeof requestAnimationFrame !== 'undefined'
5175
+ ? requestAnimationFrame
5176
+ : function (cb) { return setTimeout(function () { return cb(Date.now()); }, 16); };
5177
+ schedule(function () {
5178
+ _this.pending = false;
5179
+ var next = _this.lastCallback;
5180
+ _this.lastCallback = null;
5181
+ if (next) {
5182
+ next();
4851
5183
  }
4852
5184
  });
4853
- if (deduped.length < 3)
4854
- return deduped.map(function (p) { return (__assign({}, p)); });
4855
- var result = [deduped[0]];
4856
- for (var i = 1; i < deduped.length - 1; i += 1) {
4857
- var prev = result[result.length - 1];
4858
- var current = deduped[i];
4859
- var next = deduped[i + 1];
4860
- var isFirstTurn = hasSourceStub && result.length === 1;
4861
- var isLastTurn = hasTargetStub && i === deduped.length - 2;
4862
- var collinear = (prev.x === current.x && current.x === next.x) || (prev.y === current.y && current.y === next.y);
4863
- if (collinear && !isFirstTurn && !isLastTurn) {
4864
- continue;
4865
- }
4866
- result.push(current);
4867
- }
4868
- result.push(deduped[deduped.length - 1]);
4869
- return result.map(function (p) { return (__assign({}, p)); });
4870
5185
  };
4871
- return ObstacleRouter;
5186
+ return RenderScheduler;
4872
5187
  }());
4873
5188
 
4874
5189
  var DefaultSnapper = /** @class */ (function () {
@@ -5376,28 +5691,92 @@ var AutoLayoutService = /** @class */ (function () {
5376
5691
  this.commandQueue = config.commandQueue;
5377
5692
  this.collectElementPortIds = config.collectElementPortIds;
5378
5693
  }
5379
- AutoLayoutService.prototype.applyLayoutForParent = function (parentId) {
5380
- var _a;
5381
- var _this = this;
5382
- var _b, _c, _d, _e;
5694
+ AutoLayoutService.prototype.applyLayoutForParent = function (parentId, options) {
5383
5695
  var parent = this.model.getElement(parentId);
5384
5696
  if (!parent)
5385
5697
  return { patches: [], movedPortIds: [] };
5386
5698
  var layout = parent.layout;
5387
5699
  if (!layout || layout.mode === 'manual')
5388
5700
  return { patches: [], movedPortIds: [] };
5701
+ var preserveParentSize = Boolean(options === null || options === void 0 ? void 0 : options.preserveParentSize);
5702
+ var padding = this.resolveLayoutPadding(layout);
5703
+ var labelReservedTopLane = this.resolveLabelReservedTopLane(parentId, layout);
5704
+ var autoResize = this.resolveAutoResizeMode(layout);
5389
5705
  var children = this.model.getChildren(parentId);
5390
- if (children.length === 0)
5391
- return { patches: [], movedPortIds: [] };
5392
- var axis = layout.mode;
5706
+ if (children.length === 0) {
5707
+ if (preserveParentSize) {
5708
+ return { patches: [], movedPortIds: [] };
5709
+ }
5710
+ if (autoResize !== 'grow-shrink')
5711
+ return { patches: [], movedPortIds: [] };
5712
+ var targetWidth = Math.max(0, Math.round(padding.x * 2));
5713
+ var targetHeight = Math.max(0, Math.round(padding.y * 2 + labelReservedTopLane));
5714
+ if (parent.size.width === targetWidth && parent.size.height === targetHeight) {
5715
+ return { patches: [], movedPortIds: [] };
5716
+ }
5717
+ var resizePatches = this.commandQueue.run(createResizeElementCommand(parentId, { width: targetWidth, height: targetHeight }), this.model);
5718
+ return { patches: resizePatches, movedPortIds: __spreadArray([], parent.portIds, true) };
5719
+ }
5720
+ if (layout.mode === 'grid') {
5721
+ return this.applyGridLayoutForParent(parentId, parent, children, preserveParentSize);
5722
+ }
5723
+ return this.applyAxisLayoutForParent(parentId, parent, children, layout.mode, preserveParentSize);
5724
+ };
5725
+ AutoLayoutService.prototype.applyLayoutCascade = function (startParentId) {
5726
+ var _a;
5727
+ var patches = [];
5728
+ var movedPortIds = [];
5729
+ var visited = new Set();
5730
+ var current = startParentId;
5731
+ while (current) {
5732
+ if (visited.has(current))
5733
+ break;
5734
+ visited.add(current);
5735
+ var result = this.applyLayoutForParent(current);
5736
+ patches.push.apply(patches, result.patches);
5737
+ movedPortIds.push.apply(movedPortIds, result.movedPortIds);
5738
+ var parent_1 = this.model.getElement(current);
5739
+ current = (_a = parent_1 === null || parent_1 === void 0 ? void 0 : parent_1.parentId) !== null && _a !== void 0 ? _a : null;
5740
+ }
5741
+ return { patches: patches, movedPortIds: Array.from(new Set(movedPortIds)) };
5742
+ };
5743
+ AutoLayoutService.prototype.applyAllLayouts = function () {
5744
+ var _this = this;
5745
+ var layoutParents = Array.from(this.model.elements.values()).filter(function (element) { return element.layout && element.layout.mode !== 'manual'; });
5746
+ var patches = [];
5747
+ var movedPortIds = [];
5748
+ var depth = function (elementId) {
5749
+ var level = 0;
5750
+ var current = _this.model.getElement(elementId);
5751
+ while (current === null || current === void 0 ? void 0 : current.parentId) {
5752
+ level += 1;
5753
+ current = _this.model.getElement(current.parentId);
5754
+ }
5755
+ return level;
5756
+ };
5757
+ layoutParents
5758
+ .sort(function (a, b) { return depth(b.id) - depth(a.id); })
5759
+ .forEach(function (element) {
5760
+ var result = _this.applyLayoutForParent(element.id);
5761
+ patches.push.apply(patches, result.patches);
5762
+ movedPortIds.push.apply(movedPortIds, result.movedPortIds);
5763
+ });
5764
+ return { patches: patches, movedPortIds: Array.from(new Set(movedPortIds)) };
5765
+ };
5766
+ AutoLayoutService.prototype.applyAxisLayoutForParent = function (parentId, parent, children, axis, preserveParentSize) {
5767
+ var _a, _b;
5768
+ var _this = this;
5769
+ var _c, _d, _e, _f;
5770
+ if (preserveParentSize === void 0) { preserveParentSize = false; }
5771
+ var layout = parent.layout;
5393
5772
  var padding = this.resolveLayoutPadding(layout);
5394
- var gap = (_b = layout.gap) !== null && _b !== void 0 ? _b : 12;
5395
- var align = (_c = layout.align) !== null && _c !== void 0 ? _c : 'center';
5396
- var childFitMainAxis = (_d = layout.childFitMainAxis) !== null && _d !== void 0 ? _d : 'none';
5397
- var childFitCrossAxis = (_e = layout.childFitCrossAxis) !== null && _e !== void 0 ? _e : 'none';
5398
- var childFitMinSize = layout.childFitMinSize;
5399
- var childFitMaxSize = layout.childFitMaxSize;
5773
+ var gap = (_c = layout === null || layout === void 0 ? void 0 : layout.gap) !== null && _c !== void 0 ? _c : 12;
5774
+ var align = (_d = layout === null || layout === void 0 ? void 0 : layout.align) !== null && _d !== void 0 ? _d : 'center';
5775
+ var childFitMainAxis = (_e = layout === null || layout === void 0 ? void 0 : layout.childFitMainAxis) !== null && _e !== void 0 ? _e : 'none';
5776
+ var childFitCrossAxis = (_f = layout === null || layout === void 0 ? void 0 : layout.childFitCrossAxis) !== null && _f !== void 0 ? _f : 'none';
5400
5777
  var labelReservedTopLane = this.resolveLabelReservedTopLane(parentId, layout);
5778
+ var constraints = this.resolveChildSizeConstraints(layout);
5779
+ var autoResize = this.resolveAutoResizeMode(layout);
5401
5780
  var sorted = __spreadArray([], children, true).sort(function (a, b) {
5402
5781
  var aPos = axis === 'horizontal' ? a.position.x : a.position.y;
5403
5782
  var bPos = axis === 'horizontal' ? b.position.x : b.position.y;
@@ -5413,16 +5792,11 @@ var AutoLayoutService = /** @class */ (function () {
5413
5792
  ? Math.max(0, availableWidth - gap * gapCount)
5414
5793
  : Math.max(0, availableHeight - gap * gapCount);
5415
5794
  var distributedMainSizes = childFitMainAxis === 'distribute'
5416
- ? _this.distributeLayoutSizes(availableMain, sorted.length, sorted.map(function () { var _a, _b; return axis === 'horizontal' ? (_a = childFitMinSize === null || childFitMinSize === void 0 ? void 0 : childFitMinSize.width) !== null && _a !== void 0 ? _a : 0 : (_b = childFitMinSize === null || childFitMinSize === void 0 ? void 0 : childFitMinSize.height) !== null && _b !== void 0 ? _b : 0; }), sorted.map(function () {
5417
- var _a, _b;
5418
- return axis === 'horizontal'
5419
- ? (_a = childFitMaxSize === null || childFitMaxSize === void 0 ? void 0 : childFitMaxSize.width) !== null && _a !== void 0 ? _a : Number.POSITIVE_INFINITY
5420
- : (_b = childFitMaxSize === null || childFitMaxSize === void 0 ? void 0 : childFitMaxSize.height) !== null && _b !== void 0 ? _b : Number.POSITIVE_INFINITY;
5421
- }))
5795
+ ? _this.distributeLayoutSizes(availableMain, sorted.length, sorted.map(function () { return (axis === 'horizontal' ? constraints.minWidth : constraints.minHeight); }), sorted.map(function () { return (axis === 'horizontal' ? constraints.maxWidth : constraints.maxHeight); }))
5422
5796
  : null;
5423
5797
  return sorted.map(function (child, index) {
5424
- var width = child.size.width;
5425
- var height = child.size.height;
5798
+ var width = _this.clampLayoutSize(child.size.width, constraints.minWidth, constraints.maxWidth);
5799
+ var height = _this.clampLayoutSize(child.size.height, constraints.minHeight, constraints.maxHeight);
5426
5800
  if (distributedMainSizes) {
5427
5801
  if (axis === 'horizontal') {
5428
5802
  width = distributedMainSizes[index];
@@ -5440,8 +5814,8 @@ var AutoLayoutService = /** @class */ (function () {
5440
5814
  }
5441
5815
  }
5442
5816
  return {
5443
- width: _this.clampLayoutSize(width, childFitMinSize === null || childFitMinSize === void 0 ? void 0 : childFitMinSize.width, childFitMaxSize === null || childFitMaxSize === void 0 ? void 0 : childFitMaxSize.width),
5444
- height: _this.clampLayoutSize(height, childFitMinSize === null || childFitMinSize === void 0 ? void 0 : childFitMinSize.height, childFitMaxSize === null || childFitMaxSize === void 0 ? void 0 : childFitMaxSize.height),
5817
+ width: _this.clampLayoutSize(width, constraints.minWidth, constraints.maxWidth),
5818
+ height: _this.clampLayoutSize(height, constraints.minHeight, constraints.maxHeight),
5445
5819
  };
5446
5820
  });
5447
5821
  };
@@ -5451,20 +5825,34 @@ var AutoLayoutService = /** @class */ (function () {
5451
5825
  var totalWidth = fittedSizes.reduce(function (sum, size) { return sum + size.width; }, 0);
5452
5826
  var totalHeight = fittedSizes.reduce(function (sum, size) { return sum + size.height; }, 0);
5453
5827
  if (axis === 'horizontal') {
5828
+ var requiredWidth_1 = padding.x * 2 + totalWidth + gap * gapCount;
5829
+ var requiredHeight_1 = padding.y * 2 + labelReservedTopLane + maxHeight;
5454
5830
  return {
5455
- width: Math.max(parent.size.width, padding.x * 2 + totalWidth + gap * gapCount),
5456
- height: Math.max(parent.size.height, padding.y * 2 + labelReservedTopLane + maxHeight),
5831
+ width: _this.resolveAutoResizedDimension(parent.size.width, requiredWidth_1, autoResize),
5832
+ height: _this.resolveAutoResizedDimension(parent.size.height, requiredHeight_1, autoResize),
5457
5833
  };
5458
5834
  }
5835
+ var requiredWidth = padding.x * 2 + maxWidth;
5836
+ var requiredHeight = padding.y * 2 + labelReservedTopLane + totalHeight + gap * gapCount;
5459
5837
  return {
5460
- width: Math.max(parent.size.width, padding.x * 2 + maxWidth),
5461
- height: Math.max(parent.size.height, padding.y * 2 + labelReservedTopLane + totalHeight + gap * gapCount),
5838
+ width: _this.resolveAutoResizedDimension(parent.size.width, requiredWidth, autoResize),
5839
+ height: _this.resolveAutoResizedDimension(parent.size.height, requiredHeight, autoResize),
5462
5840
  };
5463
5841
  };
5464
- var fittedSizes = calculateFittedSizes(parent.size.width, parent.size.height);
5465
- var _f = resolveParentSize(fittedSizes), newParentWidth = _f.width, newParentHeight = _f.height;
5466
- fittedSizes = calculateFittedSizes(newParentWidth, newParentHeight);
5467
- (_a = resolveParentSize(fittedSizes), newParentWidth = _a.width, newParentHeight = _a.height);
5842
+ var preservedParentWidth = Math.max(0, Math.round(parent.size.width));
5843
+ var preservedParentHeight = Math.max(0, Math.round(parent.size.height));
5844
+ var fittedSizes = calculateFittedSizes(preservedParentWidth, preservedParentHeight);
5845
+ var newParentWidth;
5846
+ var newParentHeight;
5847
+ if (preserveParentSize) {
5848
+ newParentWidth = preservedParentWidth;
5849
+ newParentHeight = preservedParentHeight;
5850
+ }
5851
+ else {
5852
+ (_a = resolveParentSize(fittedSizes), newParentWidth = _a.width, newParentHeight = _a.height);
5853
+ fittedSizes = calculateFittedSizes(newParentWidth, newParentHeight);
5854
+ (_b = resolveParentSize(fittedSizes), newParentWidth = _b.width, newParentHeight = _b.height);
5855
+ }
5468
5856
  var availableWidth = Math.max(0, newParentWidth - padding.x * 2);
5469
5857
  var availableHeight = Math.max(0, newParentHeight - padding.y * 2 - labelReservedTopLane);
5470
5858
  var cursorX = padding.x;
@@ -5493,6 +5881,99 @@ var AutoLayoutService = /** @class */ (function () {
5493
5881
  cursorY += fittedSize.height + (index < sorted.length - 1 ? gap : 0);
5494
5882
  }
5495
5883
  });
5884
+ return this.applyResolvedLayout(parentId, parent, desiredPositions, desiredSizes, {
5885
+ width: newParentWidth,
5886
+ height: newParentHeight,
5887
+ });
5888
+ };
5889
+ AutoLayoutService.prototype.applyGridLayoutForParent = function (parentId, parent, children, preserveParentSize) {
5890
+ var _this = this;
5891
+ var _a, _b;
5892
+ if (preserveParentSize === void 0) { preserveParentSize = false; }
5893
+ var layout = parent.layout;
5894
+ var padding = this.resolveLayoutPadding(layout);
5895
+ var gap = (_a = layout === null || layout === void 0 ? void 0 : layout.gap) !== null && _a !== void 0 ? _a : 12;
5896
+ var align = (_b = layout === null || layout === void 0 ? void 0 : layout.align) !== null && _b !== void 0 ? _b : 'center';
5897
+ var labelReservedTopLane = this.resolveLabelReservedTopLane(parentId, layout);
5898
+ var constraints = this.resolveChildSizeConstraints(layout);
5899
+ var autoResize = this.resolveAutoResizeMode(layout);
5900
+ // Keep grid child ordering stable across resize/layout cycles.
5901
+ // Grid slot assignment should not depend on transient child positions produced during drag.
5902
+ var sorted = __spreadArray([], children, true);
5903
+ var templateRows = this.normalizeGridTemplate(layout === null || layout === void 0 ? void 0 : layout.gridTemplate);
5904
+ var assignedRows = this.assignChildrenToGridRows(sorted.map(function (child) { return child.id; }), templateRows);
5905
+ var rowMetrics = assignedRows.map(function (row) {
5906
+ var effectiveWeights = row.weights.slice(0, row.childIds.length);
5907
+ var clampedSizes = row.childIds.map(function (childId) {
5908
+ var child = _this.model.getElement(childId);
5909
+ var width = child ? child.size.width : 0;
5910
+ var height = child ? child.size.height : 0;
5911
+ return {
5912
+ width: _this.clampLayoutSize(width, constraints.minWidth, constraints.maxWidth),
5913
+ height: _this.clampLayoutSize(height, constraints.minHeight, constraints.maxHeight),
5914
+ };
5915
+ });
5916
+ var rowHeight = clampedSizes.reduce(function (max, size) { return Math.max(max, size.height); }, 0);
5917
+ var rowWidth = clampedSizes.reduce(function (sum, size) { return sum + size.width; }, 0) + Math.max(0, clampedSizes.length - 1) * gap;
5918
+ return {
5919
+ row: row,
5920
+ rowHeight: rowHeight,
5921
+ rowWidth: rowWidth,
5922
+ effectiveWeights: effectiveWeights,
5923
+ };
5924
+ });
5925
+ var requiredInnerWidth = rowMetrics.reduce(function (max, metric) { return Math.max(max, metric.rowWidth); }, 0);
5926
+ var requiredInnerHeight = rowMetrics.reduce(function (sum, metric) { return sum + metric.rowHeight; }, 0) + Math.max(0, rowMetrics.length - 1) * gap;
5927
+ var newParentWidth = preserveParentSize
5928
+ ? Math.max(0, Math.round(parent.size.width))
5929
+ : this.resolveAutoResizedDimension(parent.size.width, padding.x * 2 + requiredInnerWidth, autoResize);
5930
+ var newParentHeight = preserveParentSize
5931
+ ? Math.max(0, Math.round(parent.size.height))
5932
+ : this.resolveAutoResizedDimension(parent.size.height, padding.y * 2 + labelReservedTopLane + requiredInnerHeight, autoResize);
5933
+ var availableWidth = Math.max(0, newParentWidth - padding.x * 2);
5934
+ var availableHeight = Math.max(0, newParentHeight - padding.y * 2 - labelReservedTopLane);
5935
+ var contentHeight = requiredInnerHeight;
5936
+ var blockOffsetY = align === 'start'
5937
+ ? 0
5938
+ : align === 'end'
5939
+ ? Math.max(0, availableHeight - contentHeight)
5940
+ : Math.max(0, (availableHeight - contentHeight) / 2);
5941
+ var cursorY = padding.y + labelReservedTopLane + blockOffsetY;
5942
+ var desiredPositions = new Map();
5943
+ var desiredSizes = new Map();
5944
+ rowMetrics.forEach(function (metric) {
5945
+ var childCount = metric.row.childIds.length;
5946
+ if (childCount <= 0)
5947
+ return;
5948
+ var totalGap = Math.max(0, childCount - 1) * gap;
5949
+ var rowInnerWidth = Math.max(0, availableWidth - totalGap);
5950
+ var cellWidths = _this.distributeWeightedSizes(rowInnerWidth, metric.effectiveWeights);
5951
+ var rowHeight = metric.rowHeight;
5952
+ var cursorX = padding.x;
5953
+ metric.row.childIds.forEach(function (childId, index) {
5954
+ var _a;
5955
+ var cellWidth = (_a = cellWidths[index]) !== null && _a !== void 0 ? _a : 0;
5956
+ var targetWidth = _this.clampLayoutSize(cellWidth, constraints.minWidth, constraints.maxWidth);
5957
+ var targetHeight = _this.clampLayoutSize(rowHeight, constraints.minHeight, constraints.maxHeight);
5958
+ var offsetX = _this.resolveAlignmentOffset(align, cellWidth, targetWidth);
5959
+ var offsetY = _this.resolveAlignmentOffset(align, rowHeight, targetHeight);
5960
+ desiredSizes.set(childId, { width: targetWidth, height: targetHeight });
5961
+ desiredPositions.set(childId, {
5962
+ x: cursorX + offsetX,
5963
+ y: cursorY + offsetY,
5964
+ });
5965
+ cursorX += cellWidth + gap;
5966
+ });
5967
+ cursorY += rowHeight + gap;
5968
+ });
5969
+ return this.applyResolvedLayout(parentId, parent, desiredPositions, desiredSizes, {
5970
+ width: newParentWidth,
5971
+ height: newParentHeight,
5972
+ });
5973
+ };
5974
+ AutoLayoutService.prototype.applyResolvedLayout = function (parentId, parent, desiredPositions, desiredSizes, parentSize) {
5975
+ var _this = this;
5976
+ var _a;
5496
5977
  var patches = [];
5497
5978
  var movedPortIds = [];
5498
5979
  desiredPositions.forEach(function (position, childId) {
@@ -5514,54 +5995,13 @@ var AutoLayoutService = /** @class */ (function () {
5514
5995
  movedPortIds.push.apply(movedPortIds, nested.movedPortIds);
5515
5996
  }
5516
5997
  });
5517
- if (newParentWidth !== parent.size.width || newParentHeight !== parent.size.height) {
5518
- var resizePatches = this.commandQueue.run(createResizeElementCommand(parentId, { width: newParentWidth, height: newParentHeight }), this.model);
5998
+ if (parentSize.width !== parent.size.width || parentSize.height !== parent.size.height) {
5999
+ var resizePatches = this.commandQueue.run(createResizeElementCommand(parentId, parentSize), this.model);
5519
6000
  patches.push.apply(patches, resizePatches);
5520
- movedPortIds.push.apply(movedPortIds, parent.portIds);
5521
- }
5522
- return { patches: patches, movedPortIds: Array.from(new Set(movedPortIds)) };
5523
- };
5524
- AutoLayoutService.prototype.applyLayoutCascade = function (startParentId) {
5525
- var _a;
5526
- var patches = [];
5527
- var movedPortIds = [];
5528
- var visited = new Set();
5529
- var current = startParentId;
5530
- while (current) {
5531
- if (visited.has(current))
5532
- break;
5533
- visited.add(current);
5534
- var result = this.applyLayoutForParent(current);
5535
- patches.push.apply(patches, result.patches);
5536
- movedPortIds.push.apply(movedPortIds, result.movedPortIds);
5537
- var parent_1 = this.model.getElement(current);
5538
- current = (_a = parent_1 === null || parent_1 === void 0 ? void 0 : parent_1.parentId) !== null && _a !== void 0 ? _a : null;
6001
+ movedPortIds.push.apply(movedPortIds, ((_a = parent.portIds) !== null && _a !== void 0 ? _a : []));
5539
6002
  }
5540
6003
  return { patches: patches, movedPortIds: Array.from(new Set(movedPortIds)) };
5541
6004
  };
5542
- AutoLayoutService.prototype.applyAllLayouts = function () {
5543
- var _this = this;
5544
- var layoutParents = Array.from(this.model.elements.values()).filter(function (element) { return element.layout && element.layout.mode !== 'manual'; });
5545
- var patches = [];
5546
- var movedPortIds = [];
5547
- var depth = function (elementId) {
5548
- var level = 0;
5549
- var current = _this.model.getElement(elementId);
5550
- while (current === null || current === void 0 ? void 0 : current.parentId) {
5551
- level += 1;
5552
- current = _this.model.getElement(current.parentId);
5553
- }
5554
- return level;
5555
- };
5556
- layoutParents
5557
- .sort(function (a, b) { return depth(b.id) - depth(a.id); })
5558
- .forEach(function (element) {
5559
- var result = _this.applyLayoutForParent(element.id);
5560
- patches.push.apply(patches, result.patches);
5561
- movedPortIds.push.apply(movedPortIds, result.movedPortIds);
5562
- });
5563
- return { patches: patches, movedPortIds: Array.from(new Set(movedPortIds)) };
5564
- };
5565
6005
  AutoLayoutService.prototype.resolveLayoutPadding = function (layout) {
5566
6006
  var _a, _b, _c;
5567
6007
  var padding = (_a = layout === null || layout === void 0 ? void 0 : layout.padding) !== null && _a !== void 0 ? _a : 12;
@@ -5572,6 +6012,92 @@ var AutoLayoutService = /** @class */ (function () {
5572
6012
  y: (_c = padding.y) !== null && _c !== void 0 ? _c : 12,
5573
6013
  };
5574
6014
  };
6015
+ AutoLayoutService.prototype.resolveAutoResizeMode = function (layout) {
6016
+ return (layout === null || layout === void 0 ? void 0 : layout.autoResize) === 'grow-shrink' ? 'grow-shrink' : 'grow';
6017
+ };
6018
+ AutoLayoutService.prototype.resolveAutoResizedDimension = function (current, required, mode) {
6019
+ var safeRequired = Math.max(0, Math.round(required));
6020
+ return mode === 'grow-shrink' ? safeRequired : Math.max(Math.max(0, current), safeRequired);
6021
+ };
6022
+ AutoLayoutService.prototype.resolveChildSizeConstraints = function (layout) {
6023
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
6024
+ var minWidth = Math.max(0, (_c = (_a = layout === null || layout === void 0 ? void 0 : layout.childMinWidth) !== null && _a !== void 0 ? _a : (_b = layout === null || layout === void 0 ? void 0 : layout.childFitMinSize) === null || _b === void 0 ? void 0 : _b.width) !== null && _c !== void 0 ? _c : 0);
6025
+ var minHeight = Math.max(0, (_f = (_d = layout === null || layout === void 0 ? void 0 : layout.childMinHeight) !== null && _d !== void 0 ? _d : (_e = layout === null || layout === void 0 ? void 0 : layout.childFitMinSize) === null || _e === void 0 ? void 0 : _e.height) !== null && _f !== void 0 ? _f : 0);
6026
+ var resolvedMaxWidth = (_g = layout === null || layout === void 0 ? void 0 : layout.childMaxWidth) !== null && _g !== void 0 ? _g : (_h = layout === null || layout === void 0 ? void 0 : layout.childFitMaxSize) === null || _h === void 0 ? void 0 : _h.width;
6027
+ var resolvedMaxHeight = (_j = layout === null || layout === void 0 ? void 0 : layout.childMaxHeight) !== null && _j !== void 0 ? _j : (_k = layout === null || layout === void 0 ? void 0 : layout.childFitMaxSize) === null || _k === void 0 ? void 0 : _k.height;
6028
+ var maxWidth = Number.isFinite(resolvedMaxWidth !== null && resolvedMaxWidth !== void 0 ? resolvedMaxWidth : Number.NaN)
6029
+ ? Math.max(minWidth, resolvedMaxWidth)
6030
+ : Number.POSITIVE_INFINITY;
6031
+ var maxHeight = Number.isFinite(resolvedMaxHeight !== null && resolvedMaxHeight !== void 0 ? resolvedMaxHeight : Number.NaN)
6032
+ ? Math.max(minHeight, resolvedMaxHeight)
6033
+ : Number.POSITIVE_INFINITY;
6034
+ return { minWidth: minWidth, maxWidth: maxWidth, minHeight: minHeight, maxHeight: maxHeight };
6035
+ };
6036
+ AutoLayoutService.prototype.normalizeGridTemplate = function (template) {
6037
+ var rows = [];
6038
+ (template !== null && template !== void 0 ? template : []).forEach(function (entry) {
6039
+ if (typeof entry === 'number') {
6040
+ var count = Math.max(0, Math.floor(entry));
6041
+ if (count > 0) {
6042
+ rows.push({ weights: Array.from({ length: count }, function () { return 1; }) });
6043
+ }
6044
+ return;
6045
+ }
6046
+ if (!Array.isArray(entry))
6047
+ return;
6048
+ if (entry.length === 1 && Number.isFinite(entry[0]) && entry[0] > 1) {
6049
+ var count = Math.max(0, Math.floor(entry[0]));
6050
+ if (count > 0) {
6051
+ rows.push({ weights: Array.from({ length: count }, function () { return 1; }) });
6052
+ }
6053
+ return;
6054
+ }
6055
+ var weights = entry
6056
+ .map(function (value) {
6057
+ if (!Number.isFinite(value) || value <= 0)
6058
+ return 1;
6059
+ return value;
6060
+ })
6061
+ .filter(function (value) { return value > 0; });
6062
+ if (weights.length > 0) {
6063
+ rows.push({ weights: weights });
6064
+ }
6065
+ });
6066
+ if (rows.length === 0) {
6067
+ rows.push({ weights: [1] });
6068
+ }
6069
+ return rows;
6070
+ };
6071
+ AutoLayoutService.prototype.assignChildrenToGridRows = function (childIds, templateRows) {
6072
+ var _a;
6073
+ var assigned = [];
6074
+ var cursor = 0;
6075
+ templateRows.forEach(function (rowTemplate) {
6076
+ if (cursor >= childIds.length)
6077
+ return;
6078
+ var rowChildren = childIds.slice(cursor, cursor + rowTemplate.weights.length);
6079
+ if (rowChildren.length === 0)
6080
+ return;
6081
+ assigned.push({
6082
+ weights: rowTemplate.weights,
6083
+ childIds: rowChildren,
6084
+ });
6085
+ cursor += rowChildren.length;
6086
+ });
6087
+ while (cursor < childIds.length) {
6088
+ var remaining = childIds.length - cursor;
6089
+ var lastRow = templateRows[templateRows.length - 1];
6090
+ var fallbackColumns = (_a = lastRow === null || lastRow === void 0 ? void 0 : lastRow.weights.length) !== null && _a !== void 0 ? _a : 1;
6091
+ var fallbackCount = Math.max(1, Math.min(remaining, fallbackColumns));
6092
+ var rowChildren = childIds.slice(cursor, cursor + fallbackCount);
6093
+ assigned.push({
6094
+ weights: Array.from({ length: rowChildren.length }, function () { return 1; }),
6095
+ childIds: rowChildren,
6096
+ });
6097
+ cursor += rowChildren.length;
6098
+ }
6099
+ return assigned;
6100
+ };
5575
6101
  AutoLayoutService.prototype.resolveLabelReservedTopLane = function (parentId, layout) {
5576
6102
  var _a, _b, _c, _d;
5577
6103
  var policy = layout === null || layout === void 0 ? void 0 : layout.labelReservedSpace;
@@ -5611,11 +6137,43 @@ var AutoLayoutService = /** @class */ (function () {
5611
6137
  });
5612
6138
  return Math.max(0, lane);
5613
6139
  };
6140
+ AutoLayoutService.prototype.resolveAlignmentOffset = function (align, container, content) {
6141
+ var delta = Math.max(0, container - content);
6142
+ if (align === 'start')
6143
+ return 0;
6144
+ if (align === 'end')
6145
+ return delta;
6146
+ return delta / 2;
6147
+ };
5614
6148
  AutoLayoutService.prototype.clampLayoutSize = function (value, min, max) {
5615
6149
  var minValue = min !== null && min !== void 0 ? min : 0;
5616
6150
  var maxValue = max !== null && max !== void 0 ? max : Number.POSITIVE_INFINITY;
5617
6151
  return Math.max(minValue, Math.min(maxValue, value));
5618
6152
  };
6153
+ AutoLayoutService.prototype.distributeWeightedSizes = function (total, weights) {
6154
+ if (weights.length === 0)
6155
+ return [];
6156
+ var safeTotal = Math.max(0, total);
6157
+ var normalized = weights.map(function (weight) { return (Number.isFinite(weight) && weight > 0 ? weight : 1); });
6158
+ var sumWeights = normalized.reduce(function (sum, weight) { return sum + weight; }, 0);
6159
+ if (sumWeights <= 0) {
6160
+ return Array.from({ length: normalized.length }, function () { return 0; });
6161
+ }
6162
+ var raw = normalized.map(function (weight) { return (safeTotal * weight) / sumWeights; });
6163
+ var floored = raw.map(function (value) { return Math.floor(value); });
6164
+ var remainder = Math.max(0, Math.round(safeTotal - floored.reduce(function (sum, value) { return sum + value; }, 0)));
6165
+ var fractions = raw
6166
+ .map(function (value, index) { return ({ index: index, fraction: value - floored[index] }); })
6167
+ .sort(function (a, b) { return b.fraction - a.fraction || a.index - b.index; });
6168
+ var cursor = 0;
6169
+ while (remainder > 0 && fractions.length > 0) {
6170
+ var index = fractions[cursor % fractions.length].index;
6171
+ floored[index] += 1;
6172
+ remainder -= 1;
6173
+ cursor += 1;
6174
+ }
6175
+ return floored;
6176
+ };
5619
6177
  AutoLayoutService.prototype.distributeLayoutSizes = function (total, count, minValues, maxValues) {
5620
6178
  if (count <= 0)
5621
6179
  return [];
@@ -5834,6 +6392,35 @@ var LinkRoutingService = /** @class */ (function () {
5834
6392
  });
5835
6393
  return patches;
5836
6394
  };
6395
+ LinkRoutingService.prototype.refreshLinksForRedraw = function (options) {
6396
+ var _this = this;
6397
+ var includeManual = Boolean(options === null || options === void 0 ? void 0 : options.includeManual);
6398
+ var patches = [];
6399
+ var links = Array.from(this.model.links.values()).sort(function (a, b) { return a.id.localeCompare(b.id); });
6400
+ links.forEach(function (link) {
6401
+ var _a;
6402
+ if (link.points.length < 2)
6403
+ return;
6404
+ var routing = (_a = link.routing) !== null && _a !== void 0 ? _a : 'auto';
6405
+ if (routing === 'manual' && !includeManual)
6406
+ return;
6407
+ var endpoints = _this.resolveLinkEndpoints(link);
6408
+ var source = endpoints.source;
6409
+ var target = endpoints.target;
6410
+ if (!source || !target)
6411
+ return;
6412
+ var first = link.points[0];
6413
+ var last = link.points[link.points.length - 1];
6414
+ var sourceChanged = !_this.pointsEqual(first, source);
6415
+ var targetChanged = !_this.pointsEqual(last, target);
6416
+ if (!sourceChanged || !targetChanged)
6417
+ return;
6418
+ var points = _this.resolveLinkPointsForUpdate(link, source, target);
6419
+ var linkPatches = _this.commandQueue.run(createUpdateLinkCommand(link.id, { points: points }), _this.model);
6420
+ patches.push.apply(patches, linkPatches);
6421
+ });
6422
+ return patches;
6423
+ };
5837
6424
  LinkRoutingService.prototype.getPortLinkWorldPosition = function (portId, options) {
5838
6425
  var _a;
5839
6426
  var port = this.model.getPort(portId);
@@ -5911,6 +6498,9 @@ var LinkRoutingService = /** @class */ (function () {
5911
6498
  return __assign({}, point);
5912
6499
  });
5913
6500
  };
6501
+ LinkRoutingService.prototype.pointsEqual = function (a, b) {
6502
+ return Math.abs(a.x - b.x) <= POSITION_EPSILON$1 && Math.abs(a.y - b.y) <= POSITION_EPSILON$1;
6503
+ };
5914
6504
  LinkRoutingService.prototype.buildRouteContext = function (link) {
5915
6505
  var _this = this;
5916
6506
  var _a, _b;
@@ -5933,6 +6523,8 @@ var LinkRoutingService = /** @class */ (function () {
5933
6523
  var targetOnEdge = (_b = targetEndpointGeometry === null || targetEndpointGeometry === void 0 ? void 0 : targetEndpointGeometry.onEdge) !== null && _b !== void 0 ? _b : false;
5934
6524
  var ancestorExclusions = this.getAncestorExclusions(sourceElementId, targetElementId);
5935
6525
  var _c = this.resolveRouteBounds(sourceElementId, targetElementId), bounds = _c.bounds, boundsElementId = _c.boundsElementId;
6526
+ var commonAncestorCorridor = this.resolveCommonAncestorCorridorHint(sourceElementId, targetElementId);
6527
+ var effectiveBounds = bounds !== null && bounds !== void 0 ? bounds : commonAncestorCorridor === null || commonAncestorCorridor === void 0 ? void 0 : commonAncestorCorridor.bounds;
5936
6528
  var forcedObstacles = new Set();
5937
6529
  if (sourceElementId && sourceOnEdge)
5938
6530
  forcedObstacles.add(sourceElementId);
@@ -5975,7 +6567,7 @@ var LinkRoutingService = /** @class */ (function () {
5975
6567
  sourcePortId: link.sourcePortId,
5976
6568
  targetPortId: link.targetPortId,
5977
6569
  obstacles: obstacles,
5978
- bounds: bounds,
6570
+ bounds: effectiveBounds,
5979
6571
  sourceEndpoint: sourcePosition && sourceRect
5980
6572
  ? {
5981
6573
  elementId: sourceElementId !== null && sourceElementId !== void 0 ? sourceElementId : undefined,
@@ -5990,6 +6582,48 @@ var LinkRoutingService = /** @class */ (function () {
5990
6582
  onEdgeSide: targetEndpointGeometry === null || targetEndpointGeometry === void 0 ? void 0 : targetEndpointGeometry.onEdgeSide,
5991
6583
  }
5992
6584
  : undefined,
6585
+ commonAncestorCorridor: commonAncestorCorridor,
6586
+ };
6587
+ };
6588
+ LinkRoutingService.prototype.resolveCommonAncestorCorridorHint = function (sourceElementId, targetElementId) {
6589
+ if (!sourceElementId || !targetElementId)
6590
+ return undefined;
6591
+ if (sourceElementId === targetElementId)
6592
+ return undefined;
6593
+ var sourceElement = this.model.getElement(sourceElementId);
6594
+ var targetElement = this.model.getElement(targetElementId);
6595
+ if (!sourceElement || !targetElement)
6596
+ return undefined;
6597
+ if (sourceElement.parentId === targetElement.parentId)
6598
+ return undefined;
6599
+ if (this.isAncestorOf(sourceElementId, targetElementId) || this.isAncestorOf(targetElementId, sourceElementId)) {
6600
+ return undefined;
6601
+ }
6602
+ var commonAncestorId = this.getFirstCommonAncestorId(sourceElementId, targetElementId);
6603
+ if (!commonAncestorId)
6604
+ return undefined;
6605
+ var commonBounds = this.getElementRect(commonAncestorId);
6606
+ if (!commonBounds)
6607
+ return undefined;
6608
+ var sourceBranchId = this.resolveImmediateChildUnderAncestor(sourceElementId, commonAncestorId);
6609
+ var targetBranchId = this.resolveImmediateChildUnderAncestor(targetElementId, commonAncestorId);
6610
+ if (!sourceBranchId || !targetBranchId || sourceBranchId === targetBranchId)
6611
+ return undefined;
6612
+ var sourceBranchRect = this.getElementRect(sourceBranchId);
6613
+ var targetBranchRect = this.getElementRect(targetBranchId);
6614
+ if (!sourceBranchRect || !targetBranchRect)
6615
+ return undefined;
6616
+ var horizontalSeparation = sourceBranchRect.x + sourceBranchRect.width <= targetBranchRect.x ||
6617
+ targetBranchRect.x + targetBranchRect.width <= sourceBranchRect.x;
6618
+ var axis = horizontalSeparation ? 'horizontal' : 'vertical';
6619
+ return {
6620
+ ancestorId: commonAncestorId,
6621
+ bounds: commonBounds,
6622
+ sourceBranchId: sourceBranchId,
6623
+ targetBranchId: targetBranchId,
6624
+ sourceBranchRect: sourceBranchRect,
6625
+ targetBranchRect: targetBranchRect,
6626
+ axis: axis,
5993
6627
  };
5994
6628
  };
5995
6629
  LinkRoutingService.prototype.resolveEndpointGeometry = function (point, rect, elementId, oppositePoint) {
@@ -6094,6 +6728,34 @@ var LinkRoutingService = /** @class */ (function () {
6094
6728
  }
6095
6729
  return chain;
6096
6730
  };
6731
+ LinkRoutingService.prototype.getFirstCommonAncestorId = function (sourceElementId, targetElementId) {
6732
+ var sourceChain = this.getAncestorChain(sourceElementId);
6733
+ var targetSet = new Set(this.getAncestorChain(targetElementId));
6734
+ for (var i = 0; i < sourceChain.length; i += 1) {
6735
+ var candidate = sourceChain[i];
6736
+ if (targetSet.has(candidate))
6737
+ return candidate;
6738
+ }
6739
+ return null;
6740
+ };
6741
+ LinkRoutingService.prototype.resolveImmediateChildUnderAncestor = function (elementId, ancestorId) {
6742
+ var _a;
6743
+ if (elementId === ancestorId)
6744
+ return null;
6745
+ var currentId = elementId;
6746
+ var previousId = null;
6747
+ while (currentId) {
6748
+ var current = this.model.getElement(currentId);
6749
+ if (!current)
6750
+ return null;
6751
+ if (current.id === ancestorId) {
6752
+ return previousId;
6753
+ }
6754
+ previousId = current.id;
6755
+ currentId = (_a = current.parentId) !== null && _a !== void 0 ? _a : null;
6756
+ }
6757
+ return null;
6758
+ };
6097
6759
  LinkRoutingService.prototype.getAncestorExclusions = function (sourceElementId, targetElementId) {
6098
6760
  var exclusions = new Set();
6099
6761
  if (sourceElementId) {
@@ -6200,10 +6862,41 @@ var snapToRectBorder = function (point, rect) {
6200
6862
  };
6201
6863
  var POSITION_EPSILON = 1e-6;
6202
6864
  var ANCHOR_PRESET_FALLBACK_ORDER = ['vertices', 'cardinal'];
6865
+ var DEFAULT_LINK_COLOR_POOL = [
6866
+ '#1f77b4',
6867
+ '#ff7f0e',
6868
+ '#2ca02c',
6869
+ '#d62728',
6870
+ '#9467bd',
6871
+ '#8c564b',
6872
+ '#e377c2',
6873
+ '#7f7f7f',
6874
+ '#bcbd22',
6875
+ '#17becf',
6876
+ '#4e79a7',
6877
+ '#f28e2b',
6878
+ '#e15759',
6879
+ '#76b7b2',
6880
+ '#59a14f',
6881
+ '#edc948',
6882
+ '#b07aa1',
6883
+ '#ff9da7',
6884
+ '#9c755f',
6885
+ '#bab0ab',
6886
+ ];
6887
+ var normalizeLinkColorPoolPolicy = function (policy) {
6888
+ var _a;
6889
+ var enabled = Boolean(policy === null || policy === void 0 ? void 0 : policy.enabled);
6890
+ var normalizedColors = ((_a = policy === null || policy === void 0 ? void 0 : policy.colors) !== null && _a !== void 0 ? _a : [])
6891
+ .map(function (color) { return color.trim(); })
6892
+ .filter(function (color) { return color.length > 0; });
6893
+ var colors = normalizedColors.length > 0 ? normalizedColors : __spreadArray([], DEFAULT_LINK_COLOR_POOL, true);
6894
+ return { enabled: enabled, colors: colors };
6895
+ };
6203
6896
  var DiagramEngine = /** @class */ (function () {
6204
6897
  function DiagramEngine(config) {
6205
6898
  var _this = this;
6206
- var _a, _b, _c, _d, _e;
6899
+ var _a, _b, _c, _d, _e, _f, _g, _h;
6207
6900
  this.model = new DiagramModel();
6208
6901
  this.events = new EventBus();
6209
6902
  this.commandQueue = new CommandQueue();
@@ -6227,6 +6920,11 @@ var DiagramEngine = /** @class */ (function () {
6227
6920
  router: router,
6228
6921
  shapeRegistry: this.shapeRegistry,
6229
6922
  });
6923
+ this.linkRouteRefreshPolicy = {
6924
+ mode: (_g = (_f = config.linkRouteRefreshPolicy) === null || _f === void 0 ? void 0 : _f.mode) !== null && _g !== void 0 ? _g : 'mutation-only',
6925
+ includeManual: Boolean((_h = config.linkRouteRefreshPolicy) === null || _h === void 0 ? void 0 : _h.includeManual),
6926
+ };
6927
+ this.linkColorPoolPolicy = normalizeLinkColorPoolPolicy(config.linkColorPoolPolicy);
6230
6928
  this.mutationPipeline = new MutationPipeline({
6231
6929
  routeLinksWithEmptyPoints: function () { return _this.routeLinksWithEmptyPoints(); },
6232
6930
  reprojectBorderPortsForResizedElements: function (patches) { return _this.reprojectBorderPortsForResizedElements(patches); },
@@ -6353,11 +7051,15 @@ var DiagramEngine = /** @class */ (function () {
6353
7051
  var _a;
6354
7052
  var element = this.model.getElement(id);
6355
7053
  var oldSize = element === null || element === void 0 ? void 0 : element.size;
7054
+ var preserveParentSize = Boolean((element === null || element === void 0 ? void 0 : element.layout) && element.layout.mode !== 'manual');
6356
7055
  var patches = this.commandQueue.run(createResizeElementCommand(id, { width: width, height: height }), this.model);
6357
7056
  var movedPortIds = element ? this.collectElementPortIds(element.id) : [];
6358
7057
  var allPatches = this.mutationPipeline.run({
6359
7058
  basePatches: patches,
6360
- layoutSteps: [function () { return _this.applyLayoutForParent(id); }, function () { var _a; return _this.applyLayoutCascade((_a = element === null || element === void 0 ? void 0 : element.parentId) !== null && _a !== void 0 ? _a : null); }],
7059
+ layoutSteps: [
7060
+ function () { return _this.applyLayoutForParent(id, { preserveParentSize: preserveParentSize }); },
7061
+ function () { var _a; return _this.applyLayoutCascade((_a = element === null || element === void 0 ? void 0 : element.parentId) !== null && _a !== void 0 ? _a : null); },
7062
+ ],
6361
7063
  movedPortIds: movedPortIds,
6362
7064
  });
6363
7065
  this.emitChange(allPatches);
@@ -6464,23 +7166,24 @@ var DiagramEngine = /** @class */ (function () {
6464
7166
  };
6465
7167
  DiagramEngine.prototype.addLink = function (link) {
6466
7168
  var _a;
6467
- var source = this.linkRoutingService.getPortLinkWorldPosition(link.sourcePortId, {
6468
- oppositePortId: link.targetPortId,
7169
+ var nextLink = this.applyOptionalLinkColor(link);
7170
+ var source = this.linkRoutingService.getPortLinkWorldPosition(nextLink.sourcePortId, {
7171
+ oppositePortId: nextLink.targetPortId,
6469
7172
  });
6470
- var target = this.linkRoutingService.getPortLinkWorldPosition(link.targetPortId, {
6471
- oppositePortId: link.sourcePortId,
7173
+ var target = this.linkRoutingService.getPortLinkWorldPosition(nextLink.targetPortId, {
7174
+ oppositePortId: nextLink.sourcePortId,
6472
7175
  });
6473
- var routing = (_a = link.routing) !== null && _a !== void 0 ? _a : 'auto';
6474
- var points = link.points;
7176
+ var routing = (_a = nextLink.routing) !== null && _a !== void 0 ? _a : 'auto';
7177
+ var points = nextLink.points;
6475
7178
  if (source && target) {
6476
7179
  if (routing === 'auto') {
6477
- points = this.computeAutoRoute(link, source, target);
7180
+ points = this.computeAutoRoute(nextLink, source, target);
6478
7181
  }
6479
7182
  else if (points.length < 2) {
6480
7183
  points = [__assign({}, source), __assign({}, target)];
6481
7184
  }
6482
7185
  }
6483
- var patches = this.commandQueue.run(createAddLinkCommand(__assign(__assign({}, link), { points: points, routing: routing })), this.model);
7186
+ var patches = this.commandQueue.run(createAddLinkCommand(__assign(__assign({}, nextLink), { points: points, routing: routing })), this.model);
6484
7187
  this.emitChange(patches);
6485
7188
  };
6486
7189
  DiagramEngine.prototype.updateLinkPoints = function (id, points) {
@@ -6675,16 +7378,25 @@ var DiagramEngine = /** @class */ (function () {
6675
7378
  var element = this.model.getElement(id);
6676
7379
  if (!element)
6677
7380
  return clamped;
7381
+ var lockHorizontalResize = this.shouldLockAutoLayoutChildHorizontalResize(element);
6678
7382
  var shape = this.shapeRegistry.get(element.shapeId);
6679
- if (!(shape === null || shape === void 0 ? void 0 : shape.normalizeResize))
6680
- return clamped;
7383
+ if (!(shape === null || shape === void 0 ? void 0 : shape.normalizeResize)) {
7384
+ if (!lockHorizontalResize)
7385
+ return clamped;
7386
+ var width_1 = Math.max(minSize, element.size.width);
7387
+ var x_1 = clamped.x;
7388
+ if (handle === 'sw' || handle === 'nw') {
7389
+ x_1 += clamped.width - width_1;
7390
+ }
7391
+ return { x: x_1, y: clamped.y, width: width_1, height: clamped.height };
7392
+ }
6681
7393
  var normalized = shape.normalizeResize({
6682
7394
  element: element.toData(),
6683
7395
  handle: handle,
6684
7396
  minSize: minSize,
6685
7397
  proposal: { width: clamped.width, height: clamped.height },
6686
7398
  });
6687
- var width = Math.max(minSize, normalized.width);
7399
+ var width = lockHorizontalResize ? Math.max(minSize, element.size.width) : Math.max(minSize, normalized.width);
6688
7400
  var height = Math.max(minSize, normalized.height);
6689
7401
  var x = clamped.x;
6690
7402
  var y = clamped.y;
@@ -6696,6 +7408,18 @@ var DiagramEngine = /** @class */ (function () {
6696
7408
  }
6697
7409
  return { x: x, y: y, width: width, height: height };
6698
7410
  };
7411
+ DiagramEngine.prototype.shouldLockAutoLayoutChildHorizontalResize = function (element) {
7412
+ var _a, _b;
7413
+ var parentId = (_a = element.parentId) !== null && _a !== void 0 ? _a : null;
7414
+ if (!parentId)
7415
+ return false;
7416
+ if (this.model.getChildren(element.id).length > 0)
7417
+ return false;
7418
+ if (element.layout && element.layout.mode !== 'manual')
7419
+ return false;
7420
+ var parent = this.model.getElement(parentId);
7421
+ return ((_b = parent === null || parent === void 0 ? void 0 : parent.layout) === null || _b === void 0 ? void 0 : _b.mode) === 'grid';
7422
+ };
6699
7423
  DiagramEngine.prototype.getPortWorldPosition = function (id) {
6700
7424
  return this.model.getPortWorldPosition(id);
6701
7425
  };
@@ -6778,6 +7502,10 @@ var DiagramEngine = /** @class */ (function () {
6778
7502
  this.events.emit('config', { type: 'rendering' });
6779
7503
  };
6780
7504
  DiagramEngine.prototype.render = function () {
7505
+ var redrawRoutingPatches = this.refreshLinksForRenderCycle();
7506
+ if (redrawRoutingPatches.length > 0) {
7507
+ this.emitChange(redrawRoutingPatches, { render: false });
7508
+ }
6781
7509
  this.renderer.render(this.model);
6782
7510
  };
6783
7511
  DiagramEngine.prototype.emitElementDrop = function (event) {
@@ -7266,8 +7994,8 @@ var DiagramEngine = /** @class */ (function () {
7266
7994
  });
7267
7995
  this.renderer.renderSelection(selectedIds);
7268
7996
  };
7269
- DiagramEngine.prototype.applyLayoutForParent = function (parentId) {
7270
- return this.autoLayoutService.applyLayoutForParent(parentId);
7997
+ DiagramEngine.prototype.applyLayoutForParent = function (parentId, options) {
7998
+ return this.autoLayoutService.applyLayoutForParent(parentId, options);
7271
7999
  };
7272
8000
  DiagramEngine.prototype.applyLayoutCascade = function (startParentId) {
7273
8001
  return this.autoLayoutService.applyLayoutCascade(startParentId);
@@ -7333,6 +8061,30 @@ var DiagramEngine = /** @class */ (function () {
7333
8061
  DiagramEngine.prototype.routeLinksWithEmptyPoints = function () {
7334
8062
  return this.linkRoutingService.routeLinksWithEmptyPoints();
7335
8063
  };
8064
+ DiagramEngine.prototype.applyOptionalLinkColor = function (link) {
8065
+ if (!this.linkColorPoolPolicy.enabled)
8066
+ return link;
8067
+ var existingStyle = link.style;
8068
+ var hasExplicitStroke = existingStyle !== undefined &&
8069
+ Object.prototype.hasOwnProperty.call(existingStyle, 'stroke') &&
8070
+ existingStyle.stroke !== undefined &&
8071
+ existingStyle.stroke !== null &&
8072
+ "".concat(existingStyle.stroke).trim().length > 0;
8073
+ if (hasExplicitStroke)
8074
+ return link;
8075
+ var colors = this.linkColorPoolPolicy.colors;
8076
+ if (colors.length === 0)
8077
+ return link;
8078
+ var index = Math.max(0, Math.min(colors.length - 1, Math.floor(Math.random() * colors.length)));
8079
+ return __assign(__assign({}, link), { style: __assign(__assign({}, (existingStyle !== null && existingStyle !== void 0 ? existingStyle : {})), { stroke: colors[index] }) });
8080
+ };
8081
+ DiagramEngine.prototype.refreshLinksForRenderCycle = function () {
8082
+ if (this.linkRouteRefreshPolicy.mode !== 'redraw-two-endpoint-change')
8083
+ return [];
8084
+ return this.linkRoutingService.refreshLinksForRedraw({
8085
+ includeManual: this.linkRouteRefreshPolicy.includeManual,
8086
+ });
8087
+ };
7336
8088
  DiagramEngine.prototype.normalizePortsForHostPolicies = function (portIds) {
7337
8089
  var _this = this;
7338
8090
  var ids = portIds !== null && portIds !== void 0 ? portIds : Array.from(this.model.ports.keys());
@@ -8546,6 +9298,7 @@ var KonvaInteraction = /** @class */ (function () {
8546
9298
  this.dragThreshold = 4;
8547
9299
  this.panSpeed = 0.5;
8548
9300
  this.occupiedVertexTolerance = 2;
9301
+ this.nestedHoverElementPadding = 12;
8549
9302
  this.emittingElementLinkEnded = false;
8550
9303
  this.engine = engine;
8551
9304
  this.stage = config.stage;
@@ -9765,7 +10518,13 @@ var KonvaInteraction = /** @class */ (function () {
9765
10518
  this.clearActiveShapeHoverControl();
9766
10519
  return;
9767
10520
  }
9768
- var resolved = this.resolveShapeHoverControl(hit.id, point);
10521
+ var hoverElementIds = this.resolveNestedHoverElementCandidates(point, hit.id);
10522
+ var resolved = null;
10523
+ for (var i = 0; i < hoverElementIds.length; i += 1) {
10524
+ resolved = this.resolveShapeHoverControl(hoverElementIds[i], point);
10525
+ if (resolved)
10526
+ break;
10527
+ }
9769
10528
  if (!resolved) {
9770
10529
  this.clearActiveShapeHoverControl();
9771
10530
  return;
@@ -9781,6 +10540,70 @@ var KonvaInteraction = /** @class */ (function () {
9781
10540
  this.activeShapeHoverControl = resolved;
9782
10541
  (_b = (_a = this.renderer) === null || _a === void 0 ? void 0 : _a.renderShapeHoverControl) === null || _b === void 0 ? void 0 : _b.call(_a, this.toShapeHoverRenderConfig(resolved));
9783
10542
  };
10543
+ KonvaInteraction.prototype.resolveNestedHoverElementCandidates = function (point, rootElementId) {
10544
+ var _this = this;
10545
+ var _a;
10546
+ var state = this.engine.getState();
10547
+ var root = state.elements.find(function (element) { return element.id === rootElementId; });
10548
+ if (!root)
10549
+ return [rootElementId];
10550
+ var byId = new Map(state.elements.map(function (element) { var _a; return [element.id, { parentId: (_a = element.parentId) !== null && _a !== void 0 ? _a : null }]; }));
10551
+ var activeElementId = (_a = this.activeShapeHoverControl) === null || _a === void 0 ? void 0 : _a.elementId;
10552
+ var entries = state.elements
10553
+ .map(function (element, order) { return ({ element: element, order: order }); })
10554
+ .filter(function (_a) {
10555
+ var element = _a.element;
10556
+ return _this.isDescendantOrSelf(element.id, rootElementId, byId);
10557
+ })
10558
+ .filter(function (_a) {
10559
+ var element = _a.element;
10560
+ return _this.isPointWithinElementHoverBounds(point, element, _this.nestedHoverElementPadding);
10561
+ })
10562
+ .map(function (_a) {
10563
+ var element = _a.element, order = _a.order;
10564
+ return ({
10565
+ id: element.id,
10566
+ depth: _this.computeElementDepth(element.id, byId),
10567
+ order: order,
10568
+ active: activeElementId === element.id,
10569
+ });
10570
+ });
10571
+ entries.sort(function (a, b) {
10572
+ if (a.active !== b.active)
10573
+ return a.active ? -1 : 1;
10574
+ if (a.depth !== b.depth)
10575
+ return b.depth - a.depth;
10576
+ return b.order - a.order;
10577
+ });
10578
+ var ordered = entries.map(function (entry) { return entry.id; });
10579
+ if (!ordered.includes(rootElementId)) {
10580
+ ordered.push(rootElementId);
10581
+ }
10582
+ return ordered;
10583
+ };
10584
+ KonvaInteraction.prototype.isDescendantOrSelf = function (candidateId, ancestorId, byId) {
10585
+ if (candidateId === ancestorId)
10586
+ return true;
10587
+ var current = byId.get(candidateId);
10588
+ while (current === null || current === void 0 ? void 0 : current.parentId) {
10589
+ if (current.parentId === ancestorId)
10590
+ return true;
10591
+ current = byId.get(current.parentId);
10592
+ }
10593
+ return false;
10594
+ };
10595
+ KonvaInteraction.prototype.isPointWithinElementHoverBounds = function (point, element, padding) {
10596
+ var _a, _b;
10597
+ var world = (_a = this.engine.getElementWorldPosition(element.id)) !== null && _a !== void 0 ? _a : element.position;
10598
+ var rect = {
10599
+ x: world.x - padding,
10600
+ y: world.y - padding,
10601
+ width: Math.max(0, element.size.width + padding * 2),
10602
+ height: Math.max(0, element.size.height + padding * 2),
10603
+ };
10604
+ var rotation = this.engine.getElementRotation(element.id);
10605
+ return pointInRotatedRect(point, { x: rect.x, y: rect.y }, { width: rect.width, height: rect.height }, rotation, (_b = element.anchorCenter) !== null && _b !== void 0 ? _b : false);
10606
+ };
9784
10607
  KonvaInteraction.prototype.resolveShapeHoverControl = function (elementId, point) {
9785
10608
  var _a, _b, _c, _d, _e, _f, _g;
9786
10609
  var controls = (_a = this.shapeHoverControls) === null || _a === void 0 ? void 0 : _a.controls;
@@ -11438,7 +12261,12 @@ var createDiagramEditor = function (config) {
11438
12261
  registerSimpleShapes(shapeRegistry, config.elementShapes, false);
11439
12262
  registerSimpleShapes(shapeRegistry, config.portShapes, true);
11440
12263
  var renderer = new KonvaRenderer({ stage: stage, konva: Konva, shapeRegistry: shapeRegistry });
11441
- var engine = new DiagramEngine({ renderer: renderer, shapeRegistry: shapeRegistry });
12264
+ var engine = new DiagramEngine({
12265
+ renderer: renderer,
12266
+ shapeRegistry: shapeRegistry,
12267
+ linkRouteRefreshPolicy: config.linkRouteRefreshPolicy,
12268
+ linkColorPoolPolicy: config.linkColorPoolPolicy,
12269
+ });
11442
12270
  if (config.onChange) {
11443
12271
  engine.on('change', config.onChange);
11444
12272
  }
@@ -11620,7 +12448,7 @@ var useDisplayBoxMetricsUpdater = function () {
11620
12448
  };
11621
12449
 
11622
12450
  var useDemoEditor = function (_a) {
11623
- var createState = _a.createState, elementShapes = _a.elementShapes, portShapes = _a.portShapes, router = _a.router, snapDefault = _a.snapDefault, elementShapeHoverControls = _a.elementShapeHoverControls, onElementShapeHoverControlInteraction = _a.onElementShapeHoverControlInteraction, onElementShapeHoverControlActivated = _a.onElementShapeHoverControlActivated;
12451
+ var createState = _a.createState, elementShapes = _a.elementShapes, portShapes = _a.portShapes, router = _a.router, snapDefault = _a.snapDefault, linkRouteRefreshPolicy = _a.linkRouteRefreshPolicy, linkColorPoolPolicy = _a.linkColorPoolPolicy, elementShapeHoverControls = _a.elementShapeHoverControls, onElementShapeHoverControlInteraction = _a.onElementShapeHoverControlInteraction, onElementShapeHoverControlActivated = _a.onElementShapeHoverControlActivated;
11624
12452
  var containerRef = React.useRef(null);
11625
12453
  var editorRef = React.useRef(null);
11626
12454
  var _b = React.useState(null), diagramState = _b[0], setDiagramState = _b[1];
@@ -11662,6 +12490,8 @@ var useDemoEditor = function (_a) {
11662
12490
  initialState: createState(),
11663
12491
  elementShapes: elementShapes,
11664
12492
  portShapes: portShapes,
12493
+ linkRouteRefreshPolicy: linkRouteRefreshPolicy,
12494
+ linkColorPoolPolicy: linkColorPoolPolicy,
11665
12495
  elementShapeHoverControls: elementShapeHoverControls,
11666
12496
  onElementShapeHoverControlInteraction: onElementShapeHoverControlInteraction,
11667
12497
  onElementShapeHoverControlActivated: onElementShapeHoverControlActivated,
@@ -11707,6 +12537,8 @@ var useDemoEditor = function (_a) {
11707
12537
  portShapes,
11708
12538
  router,
11709
12539
  snapDefault,
12540
+ linkRouteRefreshPolicy,
12541
+ linkColorPoolPolicy,
11710
12542
  elementShapeHoverControls,
11711
12543
  onElementShapeHoverControlInteraction,
11712
12544
  onElementShapeHoverControlActivated,
@@ -12059,11 +12891,51 @@ var LinkCancelDemo = function () {
12059
12891
  var parentOptions = [
12060
12892
  { id: 'layout-row', label: 'Horizontal layout' },
12061
12893
  { id: 'layout-column', label: 'Vertical layout' },
12894
+ { id: 'layout-grid', label: 'Grid layout' },
12062
12895
  { id: 'layout-nested', label: 'Nested layout' },
12063
12896
  { id: 'layout-manual', label: 'Manual (compare)' },
12064
12897
  ];
12065
12898
  var shortLabel = 'Parent label lane demo';
12066
12899
  var longLabel = 'Parent label lane demo with longer content to increase flexible reserved space and push children downward.';
12900
+ var defaultGridTemplateText = '[[3],[1,2]]';
12901
+ var serializeGridTemplate = function (template) {
12902
+ return Array.isArray(template) && template.length > 0 ? JSON.stringify(template) : defaultGridTemplateText;
12903
+ };
12904
+ var parseGridTemplate = function (value) {
12905
+ var trimmed = value.trim();
12906
+ if (!trimmed)
12907
+ return undefined;
12908
+ var parsed;
12909
+ try {
12910
+ parsed = JSON.parse(trimmed);
12911
+ }
12912
+ catch (_a) {
12913
+ return null;
12914
+ }
12915
+ if (!Array.isArray(parsed))
12916
+ return null;
12917
+ var normalized = [];
12918
+ for (var i = 0; i < parsed.length; i += 1) {
12919
+ var row = parsed[i];
12920
+ if (typeof row === 'number') {
12921
+ if (!Number.isFinite(row) || row <= 0)
12922
+ return null;
12923
+ normalized.push(Math.floor(row));
12924
+ continue;
12925
+ }
12926
+ if (!Array.isArray(row) || row.length === 0)
12927
+ return null;
12928
+ var weights = [];
12929
+ for (var j = 0; j < row.length; j += 1) {
12930
+ var cell = row[j];
12931
+ if (typeof cell !== 'number' || !Number.isFinite(cell) || cell <= 0)
12932
+ return null;
12933
+ weights.push(cell);
12934
+ }
12935
+ normalized.push(weights);
12936
+ }
12937
+ return normalized;
12938
+ };
12067
12939
  var AutoLayoutDemo = function () {
12068
12940
  var demo = autoLayoutDemoConfig;
12069
12941
  var _a = useDemoEditor({
@@ -12085,19 +12957,21 @@ var AutoLayoutDemo = function () {
12085
12957
  var _b = React.useState(parentOptions[0].id), targetId = _b[0], setTargetId = _b[1];
12086
12958
  var _c = React.useState('horizontal'), mode = _c[0], setMode = _c[1];
12087
12959
  var _d = React.useState('center'), align = _d[0], setAlign = _d[1];
12088
- var _e = React.useState(12), padding = _e[0], setPadding = _e[1];
12089
- var _f = React.useState(12), gap = _f[0], setGap = _f[1];
12090
- var _g = React.useState('none'), childFitMainAxis = _g[0], setChildFitMainAxis = _g[1];
12091
- var _h = React.useState('none'), childFitCrossAxis = _h[0], setChildFitCrossAxis = _h[1];
12092
- var _j = React.useState(''), childFitMinWidth = _j[0], setChildFitMinWidth = _j[1];
12093
- var _k = React.useState(''), childFitMinHeight = _k[0], setChildFitMinHeight = _k[1];
12094
- var _l = React.useState(''), childFitMaxWidth = _l[0], setChildFitMaxWidth = _l[1];
12095
- var _m = React.useState(''), childFitMaxHeight = _m[0], setChildFitMaxHeight = _m[1];
12096
- var _o = React.useState('none'), labelReservedMode = _o[0], setLabelReservedMode = _o[1];
12097
- var _p = React.useState(32), labelReservedFixedSize = _p[0], setLabelReservedFixedSize = _p[1];
12098
- var _q = React.useState(''), labelReservedMinSize = _q[0], setLabelReservedMinSize = _q[1];
12099
- var _r = React.useState(''), labelReservedMaxSize = _r[0], setLabelReservedMaxSize = _r[1];
12100
- var _s = React.useState('None yet'), lastTrigger = _s[0], setLastTrigger = _s[1];
12960
+ var _e = React.useState('grow'), autoResize = _e[0], setAutoResize = _e[1];
12961
+ var _f = React.useState(12), padding = _f[0], setPadding = _f[1];
12962
+ var _g = React.useState(12), gap = _g[0], setGap = _g[1];
12963
+ var _h = React.useState('none'), childFitMainAxis = _h[0], setChildFitMainAxis = _h[1];
12964
+ var _j = React.useState('none'), childFitCrossAxis = _j[0], setChildFitCrossAxis = _j[1];
12965
+ var _k = React.useState(''), childMinWidth = _k[0], setChildMinWidth = _k[1];
12966
+ var _l = React.useState(''), childMinHeight = _l[0], setChildMinHeight = _l[1];
12967
+ var _m = React.useState(''), childMaxWidth = _m[0], setChildMaxWidth = _m[1];
12968
+ var _o = React.useState(''), childMaxHeight = _o[0], setChildMaxHeight = _o[1];
12969
+ var _p = React.useState(defaultGridTemplateText), gridTemplateText = _p[0], setGridTemplateText = _p[1];
12970
+ var _q = React.useState('none'), labelReservedMode = _q[0], setLabelReservedMode = _q[1];
12971
+ var _r = React.useState(32), labelReservedFixedSize = _r[0], setLabelReservedFixedSize = _r[1];
12972
+ var _s = React.useState(''), labelReservedMinSize = _s[0], setLabelReservedMinSize = _s[1];
12973
+ var _t = React.useState(''), labelReservedMaxSize = _t[0], setLabelReservedMaxSize = _t[1];
12974
+ var _u = React.useState('None yet'), lastTrigger = _u[0], setLastTrigger = _u[1];
12101
12975
  React.useEffect(function () {
12102
12976
  var editor = editorRef.current;
12103
12977
  if (!editor)
@@ -12157,30 +13031,32 @@ var AutoLayoutDemo = function () {
12157
13031
  return options;
12158
13032
  }, [diagramState, targetElement]);
12159
13033
  React.useEffect(function () {
12160
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
13034
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3;
12161
13035
  var parent = diagramState === null || diagramState === void 0 ? void 0 : diagramState.elements.find(function (el) { return el.id === targetId; });
12162
13036
  if (!parent)
12163
13037
  return;
12164
13038
  var layout = parent.layout;
12165
13039
  setMode((_a = layout === null || layout === void 0 ? void 0 : layout.mode) !== null && _a !== void 0 ? _a : 'manual');
12166
13040
  setAlign((_b = layout === null || layout === void 0 ? void 0 : layout.align) !== null && _b !== void 0 ? _b : 'center');
13041
+ setAutoResize((_c = layout === null || layout === void 0 ? void 0 : layout.autoResize) !== null && _c !== void 0 ? _c : 'grow');
12167
13042
  var paddingValue = typeof (layout === null || layout === void 0 ? void 0 : layout.padding) === 'number'
12168
13043
  ? layout.padding
12169
13044
  : (layout === null || layout === void 0 ? void 0 : layout.padding)
12170
- ? Math.max((_c = layout.padding.x) !== null && _c !== void 0 ? _c : 0, (_d = layout.padding.y) !== null && _d !== void 0 ? _d : 0)
13045
+ ? Math.max((_d = layout.padding.x) !== null && _d !== void 0 ? _d : 0, (_e = layout.padding.y) !== null && _e !== void 0 ? _e : 0)
12171
13046
  : 12;
12172
13047
  setPadding(paddingValue !== null && paddingValue !== void 0 ? paddingValue : 12);
12173
- setGap((_e = layout === null || layout === void 0 ? void 0 : layout.gap) !== null && _e !== void 0 ? _e : 12);
12174
- setChildFitMainAxis((_f = layout === null || layout === void 0 ? void 0 : layout.childFitMainAxis) !== null && _f !== void 0 ? _f : 'none');
12175
- setChildFitCrossAxis((_g = layout === null || layout === void 0 ? void 0 : layout.childFitCrossAxis) !== null && _g !== void 0 ? _g : 'none');
12176
- setChildFitMinWidth((_j = (_h = layout === null || layout === void 0 ? void 0 : layout.childFitMinSize) === null || _h === void 0 ? void 0 : _h.width) !== null && _j !== void 0 ? _j : '');
12177
- setChildFitMinHeight((_l = (_k = layout === null || layout === void 0 ? void 0 : layout.childFitMinSize) === null || _k === void 0 ? void 0 : _k.height) !== null && _l !== void 0 ? _l : '');
12178
- setChildFitMaxWidth((_o = (_m = layout === null || layout === void 0 ? void 0 : layout.childFitMaxSize) === null || _m === void 0 ? void 0 : _m.width) !== null && _o !== void 0 ? _o : '');
12179
- setChildFitMaxHeight((_q = (_p = layout === null || layout === void 0 ? void 0 : layout.childFitMaxSize) === null || _p === void 0 ? void 0 : _p.height) !== null && _q !== void 0 ? _q : '');
12180
- setLabelReservedMode((_s = (_r = layout === null || layout === void 0 ? void 0 : layout.labelReservedSpace) === null || _r === void 0 ? void 0 : _r.mode) !== null && _s !== void 0 ? _s : 'none');
12181
- setLabelReservedFixedSize((_u = (_t = layout === null || layout === void 0 ? void 0 : layout.labelReservedSpace) === null || _t === void 0 ? void 0 : _t.size) !== null && _u !== void 0 ? _u : 32);
12182
- setLabelReservedMinSize((_w = (_v = layout === null || layout === void 0 ? void 0 : layout.labelReservedSpace) === null || _v === void 0 ? void 0 : _v.minSize) !== null && _w !== void 0 ? _w : '');
12183
- setLabelReservedMaxSize((_y = (_x = layout === null || layout === void 0 ? void 0 : layout.labelReservedSpace) === null || _x === void 0 ? void 0 : _x.maxSize) !== null && _y !== void 0 ? _y : '');
13048
+ setGap((_f = layout === null || layout === void 0 ? void 0 : layout.gap) !== null && _f !== void 0 ? _f : 12);
13049
+ setChildFitMainAxis((_g = layout === null || layout === void 0 ? void 0 : layout.childFitMainAxis) !== null && _g !== void 0 ? _g : 'none');
13050
+ setChildFitCrossAxis((_h = layout === null || layout === void 0 ? void 0 : layout.childFitCrossAxis) !== null && _h !== void 0 ? _h : 'none');
13051
+ setChildMinWidth((_l = (_j = layout === null || layout === void 0 ? void 0 : layout.childMinWidth) !== null && _j !== void 0 ? _j : (_k = layout === null || layout === void 0 ? void 0 : layout.childFitMinSize) === null || _k === void 0 ? void 0 : _k.width) !== null && _l !== void 0 ? _l : '');
13052
+ setChildMinHeight((_p = (_m = layout === null || layout === void 0 ? void 0 : layout.childMinHeight) !== null && _m !== void 0 ? _m : (_o = layout === null || layout === void 0 ? void 0 : layout.childFitMinSize) === null || _o === void 0 ? void 0 : _o.height) !== null && _p !== void 0 ? _p : '');
13053
+ setChildMaxWidth((_s = (_q = layout === null || layout === void 0 ? void 0 : layout.childMaxWidth) !== null && _q !== void 0 ? _q : (_r = layout === null || layout === void 0 ? void 0 : layout.childFitMaxSize) === null || _r === void 0 ? void 0 : _r.width) !== null && _s !== void 0 ? _s : '');
13054
+ setChildMaxHeight((_v = (_t = layout === null || layout === void 0 ? void 0 : layout.childMaxHeight) !== null && _t !== void 0 ? _t : (_u = layout === null || layout === void 0 ? void 0 : layout.childFitMaxSize) === null || _u === void 0 ? void 0 : _u.height) !== null && _v !== void 0 ? _v : '');
13055
+ setGridTemplateText(serializeGridTemplate(layout === null || layout === void 0 ? void 0 : layout.gridTemplate));
13056
+ setLabelReservedMode((_x = (_w = layout === null || layout === void 0 ? void 0 : layout.labelReservedSpace) === null || _w === void 0 ? void 0 : _w.mode) !== null && _x !== void 0 ? _x : 'none');
13057
+ setLabelReservedFixedSize((_z = (_y = layout === null || layout === void 0 ? void 0 : layout.labelReservedSpace) === null || _y === void 0 ? void 0 : _y.size) !== null && _z !== void 0 ? _z : 32);
13058
+ setLabelReservedMinSize((_1 = (_0 = layout === null || layout === void 0 ? void 0 : layout.labelReservedSpace) === null || _0 === void 0 ? void 0 : _0.minSize) !== null && _1 !== void 0 ? _1 : '');
13059
+ setLabelReservedMaxSize((_3 = (_2 = layout === null || layout === void 0 ? void 0 : layout.labelReservedSpace) === null || _2 === void 0 ? void 0 : _2.maxSize) !== null && _3 !== void 0 ? _3 : '');
12184
13060
  }, [diagramState, targetId]);
12185
13061
  var childOrder = React.useMemo(function () {
12186
13062
  var _a;
@@ -12188,13 +13064,22 @@ var AutoLayoutDemo = function () {
12188
13064
  return [];
12189
13065
  var children = diagramState.elements.filter(function (el) { return el.parentId === targetId; });
12190
13066
  var parent = diagramState.elements.find(function (el) { return el.id === targetId; });
12191
- var axis = ((_a = parent === null || parent === void 0 ? void 0 : parent.layout) === null || _a === void 0 ? void 0 : _a.mode) === 'vertical' ? 'y' : 'x';
13067
+ var layoutMode = (_a = parent === null || parent === void 0 ? void 0 : parent.layout) === null || _a === void 0 ? void 0 : _a.mode;
12192
13068
  return __spreadArray([], children, true).sort(function (a, b) {
12193
- var aPos = axis === 'y' ? a.position.y : a.position.x;
12194
- var bPos = axis === 'y' ? b.position.y : b.position.x;
12195
- if (aPos === bPos)
12196
- return a.id.localeCompare(b.id);
12197
- return aPos - bPos;
13069
+ if (layoutMode === 'vertical' || layoutMode === 'grid') {
13070
+ if (a.position.y === b.position.y) {
13071
+ if (a.position.x === b.position.x)
13072
+ return a.id.localeCompare(b.id);
13073
+ return a.position.x - b.position.x;
13074
+ }
13075
+ return a.position.y - b.position.y;
13076
+ }
13077
+ if (a.position.x === b.position.x) {
13078
+ if (a.position.y === b.position.y)
13079
+ return a.id.localeCompare(b.id);
13080
+ return a.position.y - b.position.y;
13081
+ }
13082
+ return a.position.x - b.position.x;
12198
13083
  });
12199
13084
  }, [diagramState, targetId]);
12200
13085
  var parentSummaries = React.useMemo(function () {
@@ -12211,28 +13096,17 @@ var AutoLayoutDemo = function () {
12211
13096
  return;
12212
13097
  if (!targetElement)
12213
13098
  return;
12214
- var childFitMinSize = childFitMinWidth === '' && childFitMinHeight === ''
12215
- ? undefined
12216
- : __assign(__assign({}, (childFitMinWidth === '' ? {} : { width: childFitMinWidth })), (childFitMinHeight === '' ? {} : { height: childFitMinHeight }));
12217
- var childFitMaxSize = childFitMaxWidth === '' && childFitMaxHeight === ''
12218
- ? undefined
12219
- : __assign(__assign({}, (childFitMaxWidth === '' ? {} : { width: childFitMaxWidth })), (childFitMaxHeight === '' ? {} : { height: childFitMaxHeight }));
13099
+ var parsedGridTemplate = mode === 'grid' ? parseGridTemplate(gridTemplateText) : undefined;
13100
+ if (mode === 'grid' && parsedGridTemplate === null) {
13101
+ setLastTrigger('invalid grid template JSON');
13102
+ return;
13103
+ }
12220
13104
  var labelReservedSpace = mode === 'manual'
12221
13105
  ? undefined
12222
13106
  : __assign(__assign(__assign({ mode: labelReservedMode, placement: 'top' }, (labelReservedMode === 'fixed' ? { size: labelReservedFixedSize } : {})), (labelReservedMinSize === '' ? {} : { minSize: labelReservedMinSize })), (labelReservedMaxSize === '' ? {} : { maxSize: labelReservedMaxSize }));
12223
13107
  var layout = mode === 'manual'
12224
13108
  ? { mode: 'manual' }
12225
- : {
12226
- mode: mode,
12227
- padding: padding,
12228
- gap: gap,
12229
- align: align !== null && align !== void 0 ? align : 'center',
12230
- childFitMainAxis: childFitMainAxis,
12231
- childFitCrossAxis: childFitCrossAxis,
12232
- childFitMinSize: childFitMinSize,
12233
- childFitMaxSize: childFitMaxSize,
12234
- labelReservedSpace: labelReservedSpace,
12235
- };
13109
+ : __assign(__assign(__assign(__assign(__assign(__assign({ mode: mode, autoResize: autoResize, padding: padding, gap: gap, align: align !== null && align !== void 0 ? align : 'center', childFitMainAxis: childFitMainAxis, childFitCrossAxis: childFitCrossAxis }, (childMinWidth === '' ? {} : { childMinWidth: childMinWidth })), (childMinHeight === '' ? {} : { childMinHeight: childMinHeight })), (childMaxWidth === '' ? {} : { childMaxWidth: childMaxWidth })), (childMaxHeight === '' ? {} : { childMaxHeight: childMaxHeight })), (mode === 'grid' && parsedGridTemplate ? { gridTemplate: parsedGridTemplate } : {})), { labelReservedSpace: labelReservedSpace });
12236
13110
  editor.setElementLayout(targetElement.id, layout);
12237
13111
  setLastTrigger("layout applied (".concat(mode, ")"));
12238
13112
  };
@@ -12275,11 +13149,37 @@ var AutoLayoutDemo = function () {
12275
13149
  setLastTrigger('child removed');
12276
13150
  }
12277
13151
  };
12278
- var alignmentLabel = mode === 'horizontal' ? 'Vertical align (top/center/bottom)' : 'Horizontal align (left/center/right)';
13152
+ var handleFocusResizeLockChild = function () {
13153
+ var editor = editorRef.current;
13154
+ if (!editor)
13155
+ return;
13156
+ setTargetId('layout-grid');
13157
+ editor.setSelection(['grid-b']);
13158
+ setLastTrigger('focus resize-lock child');
13159
+ };
13160
+ var handleFocusResizeLockParent = function () {
13161
+ var editor = editorRef.current;
13162
+ if (!editor)
13163
+ return;
13164
+ setTargetId('layout-grid');
13165
+ editor.setSelection(['layout-grid']);
13166
+ setLastTrigger('focus resize-lock parent');
13167
+ };
13168
+ var alignmentLabel = mode === 'horizontal'
13169
+ ? 'Vertical align (top/center/bottom)'
13170
+ : mode === 'vertical'
13171
+ ? 'Horizontal align (left/center/right)'
13172
+ : 'Cell align (start/center/end)';
12279
13173
  return (React.createElement("section", null,
12280
13174
  React.createElement("div", { style: { marginBottom: 12 } },
12281
13175
  React.createElement("h2", { style: { marginTop: 0, marginBottom: 4 } }, demo.title),
12282
- React.createElement("p", { style: { marginTop: 0 } }, "Try horizontal, vertical, nested, and manual layout containers. Select a layout parent directly on the canvas or pick it from the list; the controls below always act on the current selection. Drag, resize, add, or remove children to see automatic reflow. Use fit controls to distribute/stretch child sizes with optional min/max guards. Nested column shows fit propagation boundaries to its parent row.")),
13176
+ React.createElement("p", { style: { marginTop: 0 } }, "Try horizontal, vertical, grid, nested, and manual layout containers. Select a layout parent directly on the canvas or pick it from the list; the controls below always act on the current selection. Drag, resize, add, or remove children to see automatic reflow. Use fit controls, explicit child min/max constraints, auto-resize policy, and grid templates to validate v0.2.5 behavior."),
13177
+ React.createElement("p", { style: { marginTop: 0, marginBottom: 0, fontSize: 13, color: '#333' } },
13178
+ "Resize-lock check: select ",
13179
+ React.createElement("code", null, "grid-b"),
13180
+ " and drag a corner mostly downward. Width should stay stable while height changes. Then select ",
13181
+ React.createElement("code", null, "layout-grid"),
13182
+ " and drag a corner to verify parent width and height both follow pointer movement.")),
12283
13183
  React.createElement(DisplayBoxControls, { actions: demo.actions, snapEnabled: controls.snapEnabled, selectedLinkRouting: controls.selectedLinkRouting, canToggleLinkRouting: controls.canToggleLinkRouting, onReload: controls.handleReload, onZoomIn: controls.handleZoomIn, onZoomOut: controls.handleZoomOut, onResetViewport: controls.handleResetViewport, onToggleSnap: controls.handleToggleSnap, onManualRender: controls.handleManualRender, onToggleLinkRouting: controls.handleToggleLinkRouting, onAction: controls.handleAction, onExportImage: controls.handleExportImage, onClearExportPreview: controls.handleClearExportPreview, exportPreviewDataUrl: controls.exportPreviewDataUrl, exportError: controls.exportError }),
12284
13184
  React.createElement("div", { style: { display: 'grid', gap: 12, marginBottom: 12 } },
12285
13185
  React.createElement("div", { style: { display: 'flex', flexWrap: 'wrap', gap: 12, alignItems: 'center' } },
@@ -12295,32 +13195,39 @@ var AutoLayoutDemo = function () {
12295
13195
  React.createElement("select", { id: "mode-select", value: mode, onChange: function (event) { return setMode(event.target.value); }, style: { padding: '6px 10px', minWidth: 140 } },
12296
13196
  React.createElement("option", { value: "manual" }, "Manual"),
12297
13197
  React.createElement("option", { value: "horizontal" }, "Horizontal"),
12298
- React.createElement("option", { value: "vertical" }, "Vertical")),
13198
+ React.createElement("option", { value: "vertical" }, "Vertical"),
13199
+ React.createElement("option", { value: "grid" }, "Grid")),
12299
13200
  React.createElement("label", { htmlFor: "align-select", style: { fontWeight: 600 } }, alignmentLabel),
12300
13201
  React.createElement("select", { id: "align-select", value: align !== null && align !== void 0 ? align : 'center', onChange: function (event) { return setAlign(event.target.value); }, style: { padding: '6px 10px', minWidth: 140 } },
12301
- React.createElement("option", { value: "start" }, mode === 'horizontal' ? 'Top' : 'Left'),
13202
+ React.createElement("option", { value: "start" }, mode === 'horizontal' ? 'Top' : mode === 'vertical' ? 'Left' : 'Start'),
12302
13203
  React.createElement("option", { value: "center" }, "Center"),
12303
- React.createElement("option", { value: "end" }, mode === 'horizontal' ? 'Bottom' : 'Right')),
13204
+ React.createElement("option", { value: "end" }, mode === 'horizontal' ? 'Bottom' : mode === 'vertical' ? 'Right' : 'End')),
13205
+ React.createElement("label", { htmlFor: "auto-resize-select", style: { fontWeight: 600 } }, "Auto resize"),
13206
+ React.createElement("select", { id: "auto-resize-select", value: autoResize, onChange: function (event) { return setAutoResize(event.target.value); }, style: { padding: '6px 10px', minWidth: 130 }, disabled: mode === 'manual' },
13207
+ React.createElement("option", { value: "grow" }, "Grow only"),
13208
+ React.createElement("option", { value: "grow-shrink" }, "Grow + shrink")),
12304
13209
  React.createElement("label", { htmlFor: "padding-input", style: { fontWeight: 600 } }, "Padding"),
12305
13210
  React.createElement("input", { id: "padding-input", type: "number", value: padding, onChange: function (event) { return setPadding(Number(event.target.value) || 0); }, style: { width: 72, padding: '6px 8px' }, min: 0 }),
12306
13211
  React.createElement("label", { htmlFor: "gap-input", style: { fontWeight: 600 } }, "Gap"),
12307
13212
  React.createElement("input", { id: "gap-input", type: "number", value: gap, onChange: function (event) { return setGap(Number(event.target.value) || 0); }, style: { width: 72, padding: '6px 8px' }, min: 0 }),
12308
13213
  React.createElement("label", { htmlFor: "fit-main-select", style: { fontWeight: 600 } }, "Main-axis fit"),
12309
- React.createElement("select", { id: "fit-main-select", value: childFitMainAxis !== null && childFitMainAxis !== void 0 ? childFitMainAxis : 'none', onChange: function (event) { return setChildFitMainAxis(event.target.value); }, style: { padding: '6px 10px', minWidth: 130 } },
13214
+ React.createElement("select", { id: "fit-main-select", value: childFitMainAxis !== null && childFitMainAxis !== void 0 ? childFitMainAxis : 'none', onChange: function (event) { return setChildFitMainAxis(event.target.value); }, style: { padding: '6px 10px', minWidth: 130 }, disabled: mode === 'manual' || mode === 'grid' },
12310
13215
  React.createElement("option", { value: "none" }, "Off"),
12311
13216
  React.createElement("option", { value: "distribute" }, "Distribute")),
12312
13217
  React.createElement("label", { htmlFor: "fit-cross-select", style: { fontWeight: 600 } }, "Cross-axis fit"),
12313
- React.createElement("select", { id: "fit-cross-select", value: childFitCrossAxis !== null && childFitCrossAxis !== void 0 ? childFitCrossAxis : 'none', onChange: function (event) { return setChildFitCrossAxis(event.target.value); }, style: { padding: '6px 10px', minWidth: 130 } },
13218
+ React.createElement("select", { id: "fit-cross-select", value: childFitCrossAxis !== null && childFitCrossAxis !== void 0 ? childFitCrossAxis : 'none', onChange: function (event) { return setChildFitCrossAxis(event.target.value); }, style: { padding: '6px 10px', minWidth: 130 }, disabled: mode === 'manual' },
12314
13219
  React.createElement("option", { value: "none" }, "Off"),
12315
13220
  React.createElement("option", { value: "stretch" }, "Stretch")),
12316
- React.createElement("label", { htmlFor: "fit-min-width-input", style: { fontWeight: 600 } }, "Min W/H"),
13221
+ React.createElement("label", { htmlFor: "fit-min-width-input", style: { fontWeight: 600 } }, "Child min W/H"),
12317
13222
  React.createElement("div", { style: { display: 'inline-flex', gap: 6 } },
12318
- React.createElement("input", { id: "fit-min-width-input", type: "number", value: childFitMinWidth, onChange: function (event) { return setChildFitMinWidth(event.target.value === '' ? '' : Math.max(0, Number(event.target.value) || 0)); }, style: { width: 64, padding: '6px 8px' }, min: 0, placeholder: "w" }),
12319
- React.createElement("input", { type: "number", value: childFitMinHeight, onChange: function (event) { return setChildFitMinHeight(event.target.value === '' ? '' : Math.max(0, Number(event.target.value) || 0)); }, style: { width: 64, padding: '6px 8px' }, min: 0, placeholder: "h" })),
12320
- React.createElement("label", { htmlFor: "fit-max-width-input", style: { fontWeight: 600 } }, "Max W/H"),
13223
+ React.createElement("input", { id: "fit-min-width-input", type: "number", value: childMinWidth, onChange: function (event) { return setChildMinWidth(event.target.value === '' ? '' : Math.max(0, Number(event.target.value) || 0)); }, style: { width: 64, padding: '6px 8px' }, min: 0, placeholder: "w" }),
13224
+ React.createElement("input", { type: "number", value: childMinHeight, onChange: function (event) { return setChildMinHeight(event.target.value === '' ? '' : Math.max(0, Number(event.target.value) || 0)); }, style: { width: 64, padding: '6px 8px' }, min: 0, placeholder: "h" })),
13225
+ React.createElement("label", { htmlFor: "fit-max-width-input", style: { fontWeight: 600 } }, "Child max W/H"),
12321
13226
  React.createElement("div", { style: { display: 'inline-flex', gap: 6 } },
12322
- React.createElement("input", { id: "fit-max-width-input", type: "number", value: childFitMaxWidth, onChange: function (event) { return setChildFitMaxWidth(event.target.value === '' ? '' : Math.max(0, Number(event.target.value) || 0)); }, style: { width: 64, padding: '6px 8px' }, min: 0, placeholder: "w" }),
12323
- React.createElement("input", { type: "number", value: childFitMaxHeight, onChange: function (event) { return setChildFitMaxHeight(event.target.value === '' ? '' : Math.max(0, Number(event.target.value) || 0)); }, style: { width: 64, padding: '6px 8px' }, min: 0, placeholder: "h" })),
13227
+ React.createElement("input", { id: "fit-max-width-input", type: "number", value: childMaxWidth, onChange: function (event) { return setChildMaxWidth(event.target.value === '' ? '' : Math.max(0, Number(event.target.value) || 0)); }, style: { width: 64, padding: '6px 8px' }, min: 0, placeholder: "w" }),
13228
+ React.createElement("input", { type: "number", value: childMaxHeight, onChange: function (event) { return setChildMaxHeight(event.target.value === '' ? '' : Math.max(0, Number(event.target.value) || 0)); }, style: { width: 64, padding: '6px 8px' }, min: 0, placeholder: "h" })),
13229
+ React.createElement("label", { htmlFor: "grid-template-input", style: { fontWeight: 600 } }, "Grid template"),
13230
+ React.createElement("input", { id: "grid-template-input", type: "text", value: gridTemplateText, onChange: function (event) { return setGridTemplateText(event.target.value); }, style: { minWidth: 220, padding: '6px 8px' }, placeholder: "[[3],[1,2]] or [[1,1,1,1],[3,1]]", disabled: mode !== 'grid' }),
12324
13231
  React.createElement("label", { htmlFor: "label-reserved-mode-select", style: { fontWeight: 600 } }, "Label lane"),
12325
13232
  React.createElement("select", { id: "label-reserved-mode-select", value: labelReservedMode, onChange: function (event) { return setLabelReservedMode(event.target.value); }, style: { padding: '6px 10px', minWidth: 120 }, disabled: mode === 'manual' },
12326
13233
  React.createElement("option", { value: "none" }, "None"),
@@ -12350,10 +13257,12 @@ var AutoLayoutDemo = function () {
12350
13257
  "Last trigger: ",
12351
13258
  React.createElement("strong", null, lastTrigger)),
12352
13259
  React.createElement("button", { type: "button", onClick: function () { return handleSetLabelContent(shortLabel); }, style: { padding: '6px 10px' }, disabled: !targetElement || mode === 'manual' }, "Label short"),
12353
- React.createElement("button", { type: "button", onClick: function () { return handleSetLabelContent(longLabel); }, style: { padding: '6px 10px' }, disabled: !targetElement || mode === 'manual' }, "Label long")),
13260
+ React.createElement("button", { type: "button", onClick: function () { return handleSetLabelContent(longLabel); }, style: { padding: '6px 10px' }, disabled: !targetElement || mode === 'manual' }, "Label long"),
13261
+ React.createElement("button", { type: "button", onClick: handleFocusResizeLockChild, style: { padding: '6px 10px' } }, "Focus lock child"),
13262
+ React.createElement("button", { type: "button", onClick: handleFocusResizeLockParent, style: { padding: '6px 10px' } }, "Focus lock parent")),
12354
13263
  React.createElement("div", { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 } },
12355
13264
  React.createElement("div", null,
12356
- React.createElement("div", { style: { fontWeight: 600, marginBottom: 6 } }, "Child order (based on layout axis)"),
13265
+ React.createElement("div", { style: { fontWeight: 600, marginBottom: 6 } }, "Child order (axis or grid row-major)"),
12357
13266
  React.createElement("ol", { style: { marginTop: 0, paddingLeft: 18 } },
12358
13267
  childOrder.map(function (child) { return (React.createElement("li", { key: child.id, style: { marginBottom: 4, fontSize: 13 } },
12359
13268
  child.id,
@@ -12371,12 +13280,16 @@ var AutoLayoutDemo = function () {
12371
13280
  React.createElement("div", null,
12372
13281
  React.createElement("div", { style: { fontWeight: 600, marginBottom: 6 } }, "Scenario tips"),
12373
13282
  React.createElement("ul", { style: { marginTop: 0, paddingLeft: 18, fontSize: 13 } },
13283
+ React.createElement("li", null, "Resize lock: select grid-b, drag corner diagonally; width should stay fixed while height changes."),
13284
+ React.createElement("li", null, "Parent resize: select layout-grid, drag corner; parent width and height should both track drag."),
12374
13285
  React.createElement("li", null, "Horizontal: try center vs bottom alignment and larger padding."),
12375
13286
  React.createElement("li", null, "Vertical: see parent grow taller as children stack."),
13287
+ React.createElement("li", null, "Grid: apply templates like [[3],[1,2]] and [[1,1,1,1],[3,1]]."),
13288
+ React.createElement("li", null, "Auto resize: compare grow-only vs grow-shrink while adding/removing children."),
12376
13289
  React.createElement("li", null, "Label lane: compare none/fixed/flexible and observe children start below the reserved lane."),
12377
13290
  React.createElement("li", null, "Fit main-axis distribute fills inner layout space across siblings."),
12378
13291
  React.createElement("li", null, "Fit cross-axis stretch extends children across inner cross axis."),
12379
- React.createElement("li", null, "Min/max fit guards cap distributed or stretched child sizes."),
13292
+ React.createElement("li", null, "Child min/max guards cap distributed, stretched, and grid cell sizes."),
12380
13293
  React.createElement("li", null, "Nested: the left column owns its own children and expands the row."),
12381
13294
  React.createElement("li", null, "Deep bounds: stack-overflow widens the nested column and the outer row."),
12382
13295
  React.createElement("li", null, "Re-order: drag along the layout axis then apply layout to keep the new order."),
@@ -12993,13 +13906,15 @@ var TextLayoutDemo = function () {
12993
13906
  };
12994
13907
 
12995
13908
  var SimpleDemo = function (_a) {
12996
- var demo = _a.demo, beforeStage = _a.beforeStage, stageHandlers = _a.stageHandlers;
13909
+ var demo = _a.demo, linkRouteRefreshPolicy = _a.linkRouteRefreshPolicy, linkColorPoolPolicy = _a.linkColorPoolPolicy, beforeStage = _a.beforeStage, stageHandlers = _a.stageHandlers;
12997
13910
  var _b = useDemoEditor({
12998
13911
  createState: demo.createState,
12999
13912
  elementShapes: demo.elementShapes,
13000
13913
  portShapes: demo.portShapes,
13001
13914
  router: demo.router,
13002
13915
  snapDefault: demo.snapDefault,
13916
+ linkRouteRefreshPolicy: linkRouteRefreshPolicy !== null && linkRouteRefreshPolicy !== void 0 ? linkRouteRefreshPolicy : demo.linkRouteRefreshPolicy,
13917
+ linkColorPoolPolicy: linkColorPoolPolicy !== null && linkColorPoolPolicy !== void 0 ? linkColorPoolPolicy : demo.linkColorPoolPolicy,
13003
13918
  }), containerRef = _b.containerRef, editorRef = _b.editorRef, diagramState = _b.diagramState, selection = _b.selection, snapEnabled = _b.snapEnabled, setSnapEnabled = _b.setSnapEnabled;
13004
13919
  var nextOffset = useOffsetSequence();
13005
13920
  var actionHelpers = React.useMemo(function () { return ({ nextOffset: nextOffset }); }, [nextOffset]);
@@ -13021,20 +13936,31 @@ var SimpleDemo = function (_a) {
13021
13936
  React.createElement(DisplayBoxStage, { containerRef: containerRef, onDragOver: stageHandlers === null || stageHandlers === void 0 ? void 0 : stageHandlers.onDragOver, onDragLeave: stageHandlers === null || stageHandlers === void 0 ? void 0 : stageHandlers.onDragLeave, onDrop: stageHandlers === null || stageHandlers === void 0 ? void 0 : stageHandlers.onDrop, stageStyle: demo.stageStyle })));
13022
13937
  };
13023
13938
 
13024
- var ObstacleRoutingDemo = function () { return (React.createElement(SimpleDemo, { demo: obstacleRoutingDemoConfig, beforeStage: React.createElement("div", { style: {
13025
- marginBottom: 12,
13026
- padding: '10px 12px',
13027
- borderRadius: 8,
13028
- border: '1px solid #2d2d2d',
13029
- background: '#f4f4f4',
13030
- } },
13031
- React.createElement("strong", null, "Expected routing behavior"),
13032
- React.createElement("ul", { style: { margin: '6px 0 0 16px' } },
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."))) })); };
13939
+ var ObstacleRoutingDemo = function () {
13940
+ var _a = React.useState(false), redrawRefreshEnabled = _a[0], setRedrawRefreshEnabled = _a[1];
13941
+ var linkRouteRefreshPolicy = React.useMemo(function () {
13942
+ return redrawRefreshEnabled
13943
+ ? { mode: 'redraw-two-endpoint-change', includeManual: false }
13944
+ : undefined;
13945
+ }, [redrawRefreshEnabled]);
13946
+ return (React.createElement(SimpleDemo, { demo: obstacleRoutingDemoConfig, linkRouteRefreshPolicy: linkRouteRefreshPolicy, beforeStage: React.createElement("div", { style: {
13947
+ marginBottom: 12,
13948
+ padding: '10px 12px',
13949
+ borderRadius: 8,
13950
+ border: '1px solid #2d2d2d',
13951
+ background: '#f4f4f4',
13952
+ } },
13953
+ React.createElement("strong", null, "Expected routing behavior"),
13954
+ React.createElement("ul", { style: { margin: '6px 0 10px 16px' } },
13955
+ React.createElement("li", null, "Scenario A: the sibling link must not cross either child host interior."),
13956
+ React.createElement("li", null, "Scenario B: parent endpoints use internal attach points while child/sibling endpoints use external attach points."),
13957
+ React.createElement("li", null, "Scenario C: cross-parent link should exit source branch early, travel along the first common-ancestor corridor, then descend near target."),
13958
+ React.createElement("li", null, "Use `Reroute All Links` repeatedly to verify deterministic routing output."),
13959
+ React.createElement("li", null, "For redraw refresh check: keep the stale cross-branch link, toggle the option below, then click `Manual Render`.")),
13960
+ React.createElement("label", { htmlFor: "obstacle-redraw-refresh-toggle", style: { display: 'inline-flex', alignItems: 'center', gap: 8, fontWeight: 600 } },
13961
+ React.createElement("input", { id: "obstacle-redraw-refresh-toggle", type: "checkbox", checked: redrawRefreshEnabled, onChange: function (event) { return setRedrawRefreshEnabled(event.target.checked); } }),
13962
+ "Enable redraw refresh on two-endpoint change")) }));
13963
+ };
13038
13964
 
13039
13965
  var initialPayloads = {
13040
13966
  elementDeleted: null,
@@ -13492,6 +14418,31 @@ var EllipseMidPointControlSection = function (_a) {
13492
14418
  midPoint)); })))));
13493
14419
  };
13494
14420
 
14421
+ var customPool = ['#0b7285', '#e8590c', '#5f3dc4'];
14422
+ var LinkColorPoolDemo = function () {
14423
+ var _a = React.useState('off'), mode = _a[0], setMode = _a[1];
14424
+ var linkColorPoolPolicy = React.useMemo(function () {
14425
+ if (mode === 'off')
14426
+ return undefined;
14427
+ if (mode === 'default')
14428
+ return { enabled: true };
14429
+ return { enabled: true, colors: customPool };
14430
+ }, [mode]);
14431
+ return (React.createElement(SimpleDemo, { demo: linkColorPoolDemoConfig, linkColorPoolPolicy: linkColorPoolPolicy, beforeStage: React.createElement("div", { style: {
14432
+ marginBottom: 12,
14433
+ padding: '10px 12px',
14434
+ borderRadius: 8,
14435
+ border: '1px solid #2d2d2d',
14436
+ background: '#f4f4f4',
14437
+ } },
14438
+ React.createElement("label", { htmlFor: "link-color-mode", style: { display: 'inline-flex', alignItems: 'center', gap: 8, fontWeight: 600 } },
14439
+ "Color Mode",
14440
+ React.createElement("select", { id: "link-color-mode", value: mode, onChange: function (event) { return setMode(event.target.value); } },
14441
+ React.createElement("option", { value: "off" }, "Off (default behavior)"),
14442
+ React.createElement("option", { value: "default" }, "Default 20-color pool"),
14443
+ React.createElement("option", { value: "custom" }, "Custom pool (#0b7285, #e8590c, #5f3dc4)")))) }));
14444
+ };
14445
+
13495
14446
  var sidePositions = {
13496
14447
  top: { x: 112, y: 0 },
13497
14448
  right: { x: 280, y: 68 },
@@ -14034,6 +14985,12 @@ var demoTabs = [
14034
14985
  description: labelStyleDemoConfig.description,
14035
14986
  Component: wrapSimpleDemo(labelStyleDemoConfig),
14036
14987
  },
14988
+ {
14989
+ id: linkColorPoolDemoConfig.id,
14990
+ title: linkColorPoolDemoConfig.title,
14991
+ description: linkColorPoolDemoConfig.description,
14992
+ Component: LinkColorPoolDemo,
14993
+ },
14037
14994
  {
14038
14995
  id: portBorderDemoConfig.id,
14039
14996
  title: portBorderDemoConfig.title,