@excalidraw/element 0.18.0-60b2758 → 0.18.0-699826

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 (115) hide show
  1. package/dist/dev/index.js +1599 -1365
  2. package/dist/dev/index.js.map +4 -4
  3. package/dist/prod/index.js +15 -14
  4. package/dist/types/common/src/appEventBus.d.ts +27 -0
  5. package/dist/types/common/src/colors.d.ts +1 -1
  6. package/dist/types/common/src/constants.d.ts +1 -2
  7. package/dist/types/common/src/index.d.ts +2 -0
  8. package/dist/types/common/src/utils.d.ts +1 -3
  9. package/dist/types/common/src/versionedSnapshotStore.d.ts +17 -0
  10. package/dist/types/element/src/Scene.d.ts +7 -3
  11. package/dist/types/element/src/arrowheads.d.ts +3 -0
  12. package/dist/types/element/src/binding.d.ts +3 -4
  13. package/dist/types/element/src/bounds.d.ts +22 -3
  14. package/dist/types/element/src/duplicate.d.ts +1 -0
  15. package/dist/types/element/src/elbowArrow.d.ts +2 -0
  16. package/dist/types/element/src/frame.d.ts +7 -6
  17. package/dist/types/element/src/index.d.ts +1 -0
  18. package/dist/types/element/src/linearElementEditor.d.ts +5 -2
  19. package/dist/types/element/src/mutateElement.d.ts +2 -0
  20. package/dist/types/element/src/selection.d.ts +7 -3
  21. package/dist/types/element/src/shape.d.ts +1 -1
  22. package/dist/types/element/src/textElement.d.ts +1 -1
  23. package/dist/types/element/src/textWrapping.d.ts +26 -0
  24. package/dist/types/element/src/typeChecks.d.ts +1 -0
  25. package/dist/types/element/src/types.d.ts +4 -1
  26. package/dist/types/element/src/utils.d.ts +2 -2
  27. package/dist/types/element/src/visualdebug.d.ts +1 -2
  28. package/dist/types/excalidraw/actions/actionAddToLibrary.d.ts +17 -11
  29. package/dist/types/excalidraw/actions/actionBoundText.d.ts +13 -9
  30. package/dist/types/excalidraw/actions/actionCanvas.d.ts +72 -48
  31. package/dist/types/excalidraw/actions/actionClipboard.d.ts +12 -8
  32. package/dist/types/excalidraw/actions/actionCropEditor.d.ts +6 -4
  33. package/dist/types/excalidraw/actions/actionDeleteSelected.d.ts +18 -12
  34. package/dist/types/excalidraw/actions/actionDeselect.d.ts +159 -0
  35. package/dist/types/excalidraw/actions/actionElementLink.d.ts +6 -4
  36. package/dist/types/excalidraw/actions/actionElementLock.d.ts +12 -8
  37. package/dist/types/excalidraw/actions/actionEmbeddable.d.ts +6 -4
  38. package/dist/types/excalidraw/actions/actionExport.d.ts +55 -319
  39. package/dist/types/excalidraw/actions/actionFrame.d.ts +24 -16
  40. package/dist/types/excalidraw/actions/actionGroup.d.ts +13 -9
  41. package/dist/types/excalidraw/actions/actionLinearEditor.d.ts +6 -4
  42. package/dist/types/excalidraw/actions/actionLink.d.ts +6 -4
  43. package/dist/types/excalidraw/actions/actionMenu.d.ts +6 -4
  44. package/dist/types/excalidraw/actions/actionProperties.d.ts +14 -10
  45. package/dist/types/excalidraw/actions/actionSelectAll.d.ts +6 -4
  46. package/dist/types/excalidraw/actions/actionStyles.d.ts +6 -3
  47. package/dist/types/excalidraw/actions/actionTextAutoResize.d.ts +3 -3
  48. package/dist/types/excalidraw/actions/actionToggleArrowBinding.d.ts +172 -0
  49. package/dist/types/excalidraw/actions/actionToggleGridMode.d.ts +6 -4
  50. package/dist/types/excalidraw/actions/actionToggleMidpointSnapping.d.ts +172 -0
  51. package/dist/types/excalidraw/actions/actionToggleObjectsSnapMode.d.ts +6 -4
  52. package/dist/types/excalidraw/actions/actionToggleSearchMenu.d.ts +6 -4
  53. package/dist/types/excalidraw/actions/actionToggleStats.d.ts +6 -4
  54. package/dist/types/excalidraw/actions/actionToggleViewMode.d.ts +6 -4
  55. package/dist/types/excalidraw/actions/actionToggleZenMode.d.ts +6 -4
  56. package/dist/types/excalidraw/actions/index.d.ts +3 -0
  57. package/dist/types/excalidraw/actions/types.d.ts +1 -1
  58. package/dist/types/excalidraw/{animated-trail.d.ts → animatedTrail.d.ts} +4 -3
  59. package/dist/types/excalidraw/appState.d.ts +4 -0
  60. package/dist/types/excalidraw/clipboard.d.ts +2 -3
  61. package/dist/types/excalidraw/components/App.d.ts +53 -14
  62. package/dist/types/excalidraw/components/AppStateObserver.d.ts +37 -0
  63. package/dist/types/excalidraw/components/CommandPalette/defaultCommandPaletteItems.d.ts +1 -2
  64. package/dist/types/excalidraw/components/IconPicker.d.ts +14 -9
  65. package/dist/types/excalidraw/components/Range.d.ts +10 -4
  66. package/dist/types/excalidraw/components/SVGLayer.d.ts +1 -1
  67. package/dist/types/excalidraw/components/TTDDialog/CodeMirrorEditor.d.ts +11 -0
  68. package/dist/types/excalidraw/components/TTDDialog/TTDDialogInput.d.ts +3 -3
  69. package/dist/types/excalidraw/components/TTDDialog/TTDDialogOutput.d.ts +4 -1
  70. package/dist/types/excalidraw/components/TTDDialog/mermaid-lang-lite.d.ts +2 -0
  71. package/dist/types/excalidraw/components/TTDDialog/utils/mermaidAutoFix.d.ts +1 -0
  72. package/dist/types/excalidraw/components/TTDDialog/utils/mermaidError.d.ts +10 -0
  73. package/dist/types/excalidraw/components/Toast.d.ts +8 -4
  74. package/dist/types/excalidraw/components/canvases/InteractiveCanvas.d.ts +2 -1
  75. package/dist/types/excalidraw/components/canvases/NewElementCanvas.d.ts +1 -0
  76. package/dist/types/excalidraw/components/canvases/StaticCanvas.d.ts +1 -1
  77. package/dist/types/excalidraw/components/dropdownMenu/DropdownMenuItemContentRadio.d.ts +2 -1
  78. package/dist/types/excalidraw/components/icons.d.ts +17 -8
  79. package/dist/types/excalidraw/components/main-menu/DefaultItems.d.ts +9 -3
  80. package/dist/types/excalidraw/components/shapes.d.ts +7 -0
  81. package/dist/types/excalidraw/data/blob.d.ts +19 -16
  82. package/dist/types/excalidraw/data/filesystem.d.ts +3 -5
  83. package/dist/types/excalidraw/data/index.d.ts +2 -3
  84. package/dist/types/excalidraw/data/json.d.ts +24 -14
  85. package/dist/types/excalidraw/data/resave.d.ts +7 -2
  86. package/dist/types/excalidraw/eraser/index.d.ts +2 -3
  87. package/dist/types/excalidraw/{laser-trails.d.ts → laserTrails.d.ts} +5 -7
  88. package/dist/types/excalidraw/lasso/index.d.ts +2 -3
  89. package/dist/types/excalidraw/renderer/animation.d.ts +4 -1
  90. package/dist/types/excalidraw/scene/Renderer.d.ts +425 -19
  91. package/dist/types/excalidraw/textAutoResizeHandle.d.ts +15 -0
  92. package/dist/types/excalidraw/types.d.ts +103 -8
  93. package/dist/types/excalidraw/wysiwyg/textWysiwyg.d.ts +5 -1
  94. package/dist/types/fractional-indexing/src/index.d.ts +29 -0
  95. package/dist/types/math/src/constants.d.ts +0 -1
  96. package/dist/types/math/src/curve.d.ts +4 -1
  97. package/dist/types/math/src/point.d.ts +2 -1
  98. package/dist/types/utils/src/index.d.ts +1 -2
  99. package/package.json +4 -3
  100. package/dist/types/excalidraw/animation-frame-handler.d.ts +0 -16
  101. package/dist/types/excalidraw/components/DiagramToCodePlugin/DiagramToCodePlugin.d.ts +0 -4
  102. package/dist/types/excalidraw/components/ExcalidrawLogo.d.ts +0 -15
  103. package/dist/types/excalidraw/components/InitializeApp.d.ts +0 -10
  104. package/dist/types/excalidraw/components/TTDDialog/TTDDialogTrigger.d.ts +0 -8
  105. package/dist/types/excalidraw/components/TTDDialog/utils/TTDStreamFetch.d.ts +0 -24
  106. package/dist/types/excalidraw/components/footer/FooterCenter.d.ts +0 -8
  107. package/dist/types/excalidraw/components/live-collaboration/LiveCollaborationTrigger.d.ts +0 -11
  108. package/dist/types/excalidraw/components/welcome-screen/WelcomeScreen.Center.d.ts +0 -58
  109. package/dist/types/excalidraw/components/welcome-screen/WelcomeScreen.Hints.d.ts +0 -19
  110. package/dist/types/excalidraw/components/welcome-screen/WelcomeScreen.d.ts +0 -84
  111. package/dist/types/excalidraw/data/reconcile.d.ts +0 -7
  112. package/dist/types/excalidraw/index.d.ts +0 -48
  113. package/dist/types/excalidraw/polyfill.d.ts +0 -2
  114. package/dist/types/utils/src/bbox.d.ts +0 -9
  115. package/dist/types/utils/src/withinBounds.d.ts +0 -19
package/dist/dev/index.js CHANGED
@@ -2269,9 +2269,9 @@ import {
2269
2269
  degreesToRadians,
2270
2270
  lineSegment as lineSegment6,
2271
2271
  pointDistance as pointDistance7,
2272
- pointFrom as pointFrom14,
2272
+ pointFrom as pointFrom13,
2273
2273
  pointFromArray as pointFromArray3,
2274
- pointRotateRads as pointRotateRads13
2274
+ pointRotateRads as pointRotateRads12
2275
2275
  } from "@excalidraw/math";
2276
2276
 
2277
2277
  // ../utils/src/shape.ts
@@ -2613,9 +2613,9 @@ function ae(e, t = {}) {
2613
2613
 
2614
2614
  // src/shape.ts
2615
2615
  import {
2616
- pointFrom as pointFrom13,
2616
+ pointFrom as pointFrom12,
2617
2617
  pointDistance as pointDistance6,
2618
- pointRotateRads as pointRotateRads12
2618
+ pointRotateRads as pointRotateRads11
2619
2619
  } from "@excalidraw/math";
2620
2620
  import {
2621
2621
  ROUGHNESS,
@@ -2632,8 +2632,8 @@ init_define_import_meta_env();
2632
2632
  import {
2633
2633
  isRightAngleRads,
2634
2634
  lineSegment as lineSegment5,
2635
- pointFrom as pointFrom12,
2636
- pointRotateRads as pointRotateRads11
2635
+ pointFrom as pointFrom11,
2636
+ pointRotateRads as pointRotateRads10
2637
2637
  } from "@excalidraw/math";
2638
2638
  import {
2639
2639
  BOUND_TEXT_PADDING as BOUND_TEXT_PADDING3,
@@ -3124,7 +3124,7 @@ import {
3124
3124
  } from "@excalidraw/math";
3125
3125
  import {
3126
3126
  DRAGGING_THRESHOLD,
3127
- KEYS as KEYS2,
3127
+ KEYS,
3128
3128
  shouldRotateWithDiscreteAngle,
3129
3129
  getGridPoint,
3130
3130
  invariant as invariant8,
@@ -3142,7 +3142,6 @@ import {
3142
3142
  // src/binding.ts
3143
3143
  init_define_import_meta_env();
3144
3144
  import {
3145
- KEYS,
3146
3145
  arrayToMap as arrayToMap2,
3147
3146
  getFeatureFlag,
3148
3147
  invariant as invariant7,
@@ -3375,6 +3374,25 @@ var canBecomePolygon = (points) => {
3375
3374
  return points.length > 3 || // 3-point polygons can't have all points in a single line
3376
3375
  points.length === 3 && !pointsEqual(points[0], points[points.length - 1]);
3377
3376
  };
3377
+ var isEligibleFrameChildType = (type) => {
3378
+ switch (type) {
3379
+ case "rectangle":
3380
+ case "diamond":
3381
+ case "ellipse":
3382
+ case "arrow":
3383
+ case "line":
3384
+ case "freedraw":
3385
+ case "text":
3386
+ case "image":
3387
+ case "frame":
3388
+ case "embeddable": {
3389
+ return true;
3390
+ }
3391
+ default: {
3392
+ return false;
3393
+ }
3394
+ }
3395
+ };
3378
3396
 
3379
3397
  // src/utils.ts
3380
3398
  var ElementShapesCache = /* @__PURE__ */ new WeakMap();
@@ -3409,12 +3427,12 @@ var setElementShapesCacheEntry = (element, shape, offset) => {
3409
3427
  }
3410
3428
  shapes.set(offset, shape);
3411
3429
  };
3412
- function deconstructLinearOrFreeDrawElement(element) {
3430
+ function deconstructLinearOrFreeDrawElement(element, elementsMap) {
3413
3431
  const cachedShape = getElementShapesCacheEntry(element, 0);
3414
3432
  if (cachedShape) {
3415
3433
  return cachedShape;
3416
3434
  }
3417
- const ops = generateLinearCollisionShape(element);
3435
+ const ops = generateLinearCollisionShape(element, elementsMap);
3418
3436
  const lines = [];
3419
3437
  const curves = [];
3420
3438
  for (let idx = 0; idx < ops.length; idx += 1) {
@@ -3821,7 +3839,7 @@ var getSnapOutlineMidPoint = (point, element, elementsMap, zoom) => {
3821
3839
  )
3822
3840
  ];
3823
3841
  const candidate = sideMidpoints.find(
3824
- (midpoint2) => pointDistance2(point, midpoint2) <= maxBindingDistance_simple(zoom) + element.strokeWidth / 2 && !hitElementItself({
3842
+ (midpoint) => pointDistance2(point, midpoint) <= maxBindingDistance_simple(zoom) + element.strokeWidth / 2 && !hitElementItself({
3825
3843
  point,
3826
3844
  element,
3827
3845
  threshold: 0,
@@ -3831,19 +3849,21 @@ var getSnapOutlineMidPoint = (point, element, elementsMap, zoom) => {
3831
3849
  );
3832
3850
  return candidate;
3833
3851
  };
3834
- var projectFixedPointOntoDiagonal = (arrow, point, element, startOrEnd, elementsMap, zoom) => {
3852
+ var projectFixedPointOntoDiagonal = (arrow, point, element, startOrEnd, elementsMap, zoom, isMidpointSnappingEnabled = true) => {
3835
3853
  invariant2(arrow.points.length >= 2, "Arrow must have at least two points");
3836
3854
  if (arrow.width < 3 && arrow.height < 3) {
3837
3855
  return null;
3838
3856
  }
3839
- const sideMidPoint = getSnapOutlineMidPoint(
3840
- point,
3841
- element,
3842
- elementsMap,
3843
- zoom
3844
- );
3845
- if (sideMidPoint) {
3846
- return sideMidPoint;
3857
+ if (isMidpointSnappingEnabled) {
3858
+ const sideMidPoint = getSnapOutlineMidPoint(
3859
+ point,
3860
+ element,
3861
+ elementsMap,
3862
+ zoom
3863
+ );
3864
+ if (sideMidPoint) {
3865
+ return sideMidPoint;
3866
+ }
3847
3867
  }
3848
3868
  const [diagonalOne, diagonalTwo] = getDiagonalsForBindableElement(
3849
3869
  element,
@@ -4315,91 +4335,168 @@ var parseTokens = (line2) => {
4315
4335
  return line2.normalize("NFC").split(breakLineRegex).filter(Boolean);
4316
4336
  };
4317
4337
  var wrapText = (text, font, maxWidth) => {
4338
+ return getWrappedTextLines(text, font, maxWidth).map((line2) => line2.text).join("\n");
4339
+ };
4340
+ var getHardLineBreaks = (text) => {
4341
+ let offset = 0;
4342
+ return text.split("\n").map((line2) => {
4343
+ const start = offset;
4344
+ const end = start + line2.length;
4345
+ offset = end + 1;
4346
+ return {
4347
+ text: line2,
4348
+ start,
4349
+ end
4350
+ };
4351
+ });
4352
+ };
4353
+ var getWrappedTextLines = (text, font, maxWidth) => {
4318
4354
  if (!Number.isFinite(maxWidth) || maxWidth < 0) {
4319
- return text;
4355
+ return getHardLineBreaks(text);
4320
4356
  }
4321
4357
  const lines = [];
4322
- const originalLines = text.split("\n");
4323
- for (const originalLine of originalLines) {
4324
- const currentLineWidth = getLineWidth(originalLine, font);
4325
- if (currentLineWidth <= maxWidth) {
4326
- lines.push(originalLine);
4327
- continue;
4358
+ let offset = 0;
4359
+ for (const originalLine of text.split("\n")) {
4360
+ const originalLineWidth = getLineWidth(originalLine, font);
4361
+ if (originalLineWidth <= maxWidth) {
4362
+ lines.push({
4363
+ text: originalLine,
4364
+ start: offset,
4365
+ end: offset + originalLine.length
4366
+ });
4367
+ } else {
4368
+ lines.push(...wrapLine(originalLine, font, maxWidth, offset));
4328
4369
  }
4329
- const wrappedLine = wrapLine(originalLine, font, maxWidth);
4330
- lines.push(...wrappedLine);
4370
+ offset += originalLine.length + 1;
4331
4371
  }
4332
- return lines.join("\n");
4372
+ return lines;
4333
4373
  };
4334
- var wrapLine = (line2, font, maxWidth) => {
4374
+ var wrapLine = (line2, font, maxWidth, lineStart) => {
4335
4375
  const lines = [];
4336
4376
  const tokens = parseTokens(line2);
4337
- const tokenIterator = tokens[Symbol.iterator]();
4338
4377
  let currentLine = "";
4378
+ let currentLineStart = lineStart;
4379
+ let currentLineEnd = lineStart;
4339
4380
  let currentLineWidth = 0;
4340
- let iterator = tokenIterator.next();
4341
- while (!iterator.done) {
4342
- const token = iterator.value;
4381
+ let tokenOffset = lineStart;
4382
+ let tokenIndex = 0;
4383
+ while (tokenIndex < tokens.length) {
4384
+ const token = tokens[tokenIndex];
4385
+ const tokenStart = tokenOffset;
4386
+ const tokenEnd = tokenStart + token.length;
4343
4387
  const testLine = currentLine + token;
4344
4388
  const testLineWidth = isSingleCharacter(token) ? currentLineWidth + charWidth.calculate(token, font) : getLineWidth(testLine, font);
4345
4389
  if (/\s/.test(token) || testLineWidth <= maxWidth) {
4390
+ if (!currentLine) {
4391
+ currentLineStart = tokenStart;
4392
+ }
4346
4393
  currentLine = testLine;
4394
+ currentLineEnd = tokenEnd;
4347
4395
  currentLineWidth = testLineWidth;
4348
- iterator = tokenIterator.next();
4396
+ tokenOffset = tokenEnd;
4397
+ tokenIndex++;
4349
4398
  continue;
4350
4399
  }
4351
4400
  if (!currentLine) {
4352
- const wrappedWord = wrapWord(token, font, maxWidth);
4353
- const trailingLine = wrappedWord[wrappedWord.length - 1] ?? "";
4401
+ const wrappedWord = wrapWord(token, font, maxWidth, tokenStart);
4402
+ const trailingLine = wrappedWord[wrappedWord.length - 1] ?? {
4403
+ text: "",
4404
+ start: tokenStart,
4405
+ end: tokenStart
4406
+ };
4354
4407
  const precedingLines = wrappedWord.slice(0, -1);
4355
4408
  lines.push(...precedingLines);
4356
- currentLine = trailingLine;
4357
- currentLineWidth = getLineWidth(trailingLine, font);
4358
- iterator = tokenIterator.next();
4409
+ currentLine = trailingLine.text;
4410
+ currentLineStart = trailingLine.start;
4411
+ currentLineEnd = trailingLine.end;
4412
+ currentLineWidth = getLineWidth(trailingLine.text, font);
4413
+ tokenOffset = tokenEnd;
4414
+ tokenIndex++;
4359
4415
  } else {
4360
- lines.push(currentLine.trimEnd());
4416
+ lines.push(
4417
+ trimLineEndAtSoftBreak(currentLine, currentLineStart, currentLineEnd)
4418
+ );
4361
4419
  currentLine = "";
4420
+ currentLineStart = tokenStart;
4421
+ currentLineEnd = tokenStart;
4362
4422
  currentLineWidth = 0;
4363
4423
  }
4364
4424
  }
4365
4425
  if (currentLine) {
4366
- const trailingLine = trimLine(currentLine, font, maxWidth);
4426
+ const trailingLine = trimLine(
4427
+ currentLine,
4428
+ currentLineStart,
4429
+ currentLineEnd,
4430
+ font,
4431
+ maxWidth
4432
+ );
4367
4433
  lines.push(trailingLine);
4368
4434
  }
4369
4435
  return lines;
4370
4436
  };
4371
- var wrapWord = (word, font, maxWidth) => {
4437
+ var wrapWord = (word, font, maxWidth, wordStart) => {
4372
4438
  if (getEmojiRegex().test(word)) {
4373
- return [word];
4439
+ return [
4440
+ {
4441
+ text: word,
4442
+ start: wordStart,
4443
+ end: wordStart + word.length
4444
+ }
4445
+ ];
4374
4446
  }
4375
4447
  satisfiesWordInvariant(word);
4376
4448
  const lines = [];
4377
4449
  const chars = Array.from(word);
4378
4450
  let currentLine = "";
4451
+ let currentLineStart = wordStart;
4452
+ let currentLineEnd = wordStart;
4379
4453
  let currentLineWidth = 0;
4454
+ let offset = wordStart;
4380
4455
  for (const char of chars) {
4456
+ const charStart = offset;
4457
+ const charEnd = charStart + char.length;
4381
4458
  const _charWidth = charWidth.calculate(char, font);
4382
4459
  const testLineWidth = currentLineWidth + _charWidth;
4383
4460
  if (testLineWidth <= maxWidth) {
4461
+ if (!currentLine) {
4462
+ currentLineStart = charStart;
4463
+ }
4384
4464
  currentLine = currentLine + char;
4465
+ currentLineEnd = charEnd;
4385
4466
  currentLineWidth = testLineWidth;
4467
+ offset = charEnd;
4386
4468
  continue;
4387
4469
  }
4388
4470
  if (currentLine) {
4389
- lines.push(currentLine);
4471
+ lines.push({
4472
+ text: currentLine,
4473
+ start: currentLineStart,
4474
+ end: currentLineEnd
4475
+ });
4390
4476
  }
4391
4477
  currentLine = char;
4478
+ currentLineStart = charStart;
4479
+ currentLineEnd = charEnd;
4392
4480
  currentLineWidth = _charWidth;
4481
+ offset = charEnd;
4393
4482
  }
4394
4483
  if (currentLine) {
4395
- lines.push(currentLine);
4484
+ lines.push({
4485
+ text: currentLine,
4486
+ start: currentLineStart,
4487
+ end: currentLineEnd
4488
+ });
4396
4489
  }
4397
4490
  return lines;
4398
4491
  };
4399
- var trimLine = (line2, font, maxWidth) => {
4492
+ var trimLine = (line2, start, end, font, maxWidth) => {
4400
4493
  const shouldTrimWhitespaces = getLineWidth(line2, font) > maxWidth;
4401
4494
  if (!shouldTrimWhitespaces) {
4402
- return line2;
4495
+ return {
4496
+ text: line2,
4497
+ start,
4498
+ end
4499
+ };
4403
4500
  }
4404
4501
  let [, trimmedLine, whitespaces] = line2.match(/^(.+?)(\s+)$/) ?? [
4405
4502
  line2,
@@ -4416,7 +4513,19 @@ var trimLine = (line2, font, maxWidth) => {
4416
4513
  trimmedLine = trimmedLine + whitespace;
4417
4514
  trimmedLineWidth = testLineWidth;
4418
4515
  }
4419
- return trimmedLine;
4516
+ return {
4517
+ text: trimmedLine,
4518
+ start,
4519
+ end: end - (line2.length - trimmedLine.length)
4520
+ };
4521
+ };
4522
+ var trimLineEndAtSoftBreak = (line2, start, end) => {
4523
+ const trimmedLine = line2.trimEnd();
4524
+ return {
4525
+ text: trimmedLine,
4526
+ start,
4527
+ end: end - (line2.length - trimmedLine.length)
4528
+ };
4420
4529
  };
4421
4530
  var isSingleCharacter = (maybeSingleCharacter) => {
4422
4531
  return maybeSingleCharacter.codePointAt(0) !== void 0 && maybeSingleCharacter.codePointAt(1) === void 0;
@@ -4655,7 +4764,8 @@ var getContainerCenter = (container, appState, elementsMap) => {
4655
4764
  if (!midSegmentMidpoint) {
4656
4765
  midSegmentMidpoint = LinearElementEditor.getSegmentMidPoint(
4657
4766
  container,
4658
- index + 1
4767
+ index + 1,
4768
+ elementsMap
4659
4769
  );
4660
4770
  }
4661
4771
  return { x: midSegmentMidpoint[0], y: midSegmentMidpoint[1] };
@@ -4806,7 +4916,7 @@ var distanceToElement = (element, elementsMap, p) => {
4806
4916
  case "line":
4807
4917
  case "arrow":
4808
4918
  case "freedraw":
4809
- return distanceToLinearOrFreeDraElement(element, p);
4919
+ return distanceToLinearOrFreeDraElement(element, elementsMap, p);
4810
4920
  }
4811
4921
  };
4812
4922
  var distanceToRectanguloidElement = (element, elementsMap, p) => {
@@ -4835,20 +4945,33 @@ var distanceToEllipseElement = (element, elementsMap, p) => {
4835
4945
  ellipse2(center, element.width / 2, element.height / 2)
4836
4946
  );
4837
4947
  };
4838
- var distanceToLinearOrFreeDraElement = (element, p) => {
4839
- const [lines, curves] = deconstructLinearOrFreeDrawElement(element);
4948
+ var distanceToLinearOrFreeDraElement = (element, elementsMap, p) => {
4949
+ const [lines, curves] = deconstructLinearOrFreeDrawElement(
4950
+ element,
4951
+ elementsMap
4952
+ );
4840
4953
  return Math.min(
4841
4954
  ...lines.map((s) => distanceToLineSegment(p, s)),
4842
4955
  ...curves.map((a2) => curvePointDistance(a2, p))
4843
4956
  );
4844
4957
  };
4845
4958
 
4959
+ // src/comparisons.ts
4960
+ init_define_import_meta_env();
4961
+ var hasBackground = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "ellipse" || type === "diamond" || type === "line" || type === "freedraw";
4962
+ var hasStrokeColor = (type) => type === "rectangle" || type === "ellipse" || type === "diamond" || type === "freedraw" || type === "arrow" || type === "line" || type === "text" || type === "embeddable";
4963
+ var hasStrokeWidth = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "ellipse" || type === "diamond" || type === "freedraw" || type === "arrow" || type === "line";
4964
+ var hasStrokeStyle = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "ellipse" || type === "diamond" || type === "arrow" || type === "line";
4965
+ var canChangeRoundness = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "line" || type === "diamond" || type === "image";
4966
+ var toolIsArrow = (type) => type === "arrow";
4967
+ var canHaveArrowheads = (type) => type === "arrow";
4968
+
4846
4969
  // src/collision.ts
4847
4970
  var shouldTestInside = (element) => {
4848
4971
  if (element.type === "arrow") {
4849
4972
  return false;
4850
4973
  }
4851
- const isDraggableFromInside = !isTransparent(element.backgroundColor) || hasBoundTextElement(element) || isIframeLikeElement(element) || isTextElement(element);
4974
+ const isDraggableFromInside = hasBackground(element.type) && !isTransparent(element.backgroundColor) || hasBoundTextElement(element) || isIframeLikeElement(element) || isTextElement(element);
4852
4975
  if (element.type === "line") {
4853
4976
  return isDraggableFromInside && isPathALoop(element.points);
4854
4977
  }
@@ -4885,14 +5008,11 @@ var hitElementItself = ({
4885
5008
  )
4886
5009
  ) : false;
4887
5010
  const bounds = getElementBounds(element, elementsMap, true);
4888
- const hitBounds = isPointWithinBounds(
4889
- pointFrom5(bounds[0] - threshold, bounds[1] - threshold),
4890
- pointRotateRads6(
4891
- point,
4892
- getCenterForBounds(bounds),
4893
- -element.angle
4894
- ),
4895
- pointFrom5(bounds[2] + threshold, bounds[3] + threshold)
5011
+ const hitBounds = isPointInRotatedBounds(
5012
+ point,
5013
+ bounds,
5014
+ element.angle,
5015
+ threshold
4896
5016
  );
4897
5017
  if (!hitBounds && !hitFrameName) {
4898
5018
  return false;
@@ -4910,13 +5030,17 @@ var hitElementItself = ({
4910
5030
  cachedHit = result;
4911
5031
  return result;
4912
5032
  };
5033
+ var isPointInRotatedBounds = (point, bounds, angle, tolerance = 0) => {
5034
+ const adjustedPoint = angle === 0 ? point : pointRotateRads6(point, getCenterForBounds(bounds), -angle);
5035
+ return isPointWithinBounds(
5036
+ pointFrom5(bounds[0] - tolerance, bounds[1] - tolerance),
5037
+ adjustedPoint,
5038
+ pointFrom5(bounds[2] + tolerance, bounds[3] + tolerance)
5039
+ );
5040
+ };
4913
5041
  var hitElementBoundingBox = (point, element, elementsMap, tolerance = 0) => {
4914
- let [x1, y1, x2, y2] = getElementBounds(element, elementsMap);
4915
- x1 -= tolerance;
4916
- y1 -= tolerance;
4917
- x2 += tolerance;
4918
- y2 += tolerance;
4919
- return isPointWithinBounds(pointFrom5(x1, y1), point, pointFrom5(x2, y2));
5042
+ const bounds = getElementBounds(element, elementsMap, true);
5043
+ return isPointInRotatedBounds(point, bounds, element.angle, tolerance);
4920
5044
  };
4921
5045
  var hitElementBoundingBoxOnly = (hitArgs, elementsMap) => !hitElementItself(hitArgs) && // bound text is considered part of the element (even if it's outside the bounding box)
4922
5046
  !hitElementBoundText(hitArgs.point, hitArgs.element, elementsMap) && hitElementBoundingBox(hitArgs.point, hitArgs.element, elementsMap);
@@ -4980,7 +5104,7 @@ var getAllHoveredElementAtPoint = (point, elements, elementsMap, tolerance) => {
4980
5104
  );
4981
5105
  if (isBindableElement(element, false) && bindingBorderTest(element, point, elementsMap, tolerance)) {
4982
5106
  candidateElements.push(element);
4983
- if (!isTransparent(element.backgroundColor)) {
5107
+ if (hasBackground(element.type) && !isTransparent(element.backgroundColor)) {
4984
5108
  break;
4985
5109
  }
4986
5110
  }
@@ -5075,7 +5199,12 @@ var intersectElementWithLineSegment = (element, elementsMap, line2, offset = 0,
5075
5199
  case "line":
5076
5200
  case "freedraw":
5077
5201
  case "arrow":
5078
- return intersectLinearOrFreeDrawWithLineSegment(element, line2, onlyFirst);
5202
+ return intersectLinearOrFreeDrawWithLineSegment(
5203
+ element,
5204
+ line2,
5205
+ elementsMap,
5206
+ onlyFirst
5207
+ );
5079
5208
  }
5080
5209
  };
5081
5210
  var curveIntersections = (curves, segment, intersections, center, angle, onlyFirst = false) => {
@@ -5114,8 +5243,11 @@ var lineIntersections = (lines, segment, intersections, center, angle, onlyFirst
5114
5243
  }
5115
5244
  return intersections;
5116
5245
  };
5117
- var intersectLinearOrFreeDrawWithLineSegment = (element, segment, onlyFirst = false) => {
5118
- const [lines, curves] = deconstructLinearOrFreeDrawElement(element);
5246
+ var intersectLinearOrFreeDrawWithLineSegment = (element, segment, elementsMap, onlyFirst = false) => {
5247
+ const [lines, curves] = deconstructLinearOrFreeDrawElement(
5248
+ element,
5249
+ elementsMap
5250
+ );
5119
5251
  const intersections = [];
5120
5252
  for (const l2 of lines) {
5121
5253
  const intersection = lineSegmentIntersectionPoints2(l2, segment);
@@ -5137,7 +5269,9 @@ var intersectLinearOrFreeDrawWithLineSegment = (element, segment, onlyFirst = fa
5137
5269
  if (!doBoundsIntersect(b1, b2)) {
5138
5270
  continue;
5139
5271
  }
5140
- const hits = curveIntersectLineSegment(c, segment);
5272
+ const hits = curveIntersectLineSegment(c, segment, {
5273
+ iterLimit: 10
5274
+ });
5141
5275
  if (hits.length > 0) {
5142
5276
  intersections.push(...hits);
5143
5277
  if (onlyFirst) {
@@ -6242,7 +6376,7 @@ var getElbowArrowData = (arrow, elementsMap, nextPoints, options) => {
6242
6376
  const origEndGlobalPoint = pointTranslate2(nextPoints[nextPoints.length - 1], vector2(arrow.x, arrow.y));
6243
6377
  let hoveredStartElement = null;
6244
6378
  let hoveredEndElement = null;
6245
- if (options?.isDragging) {
6379
+ if (options?.isDragging && options?.isBindingEnabled !== false) {
6246
6380
  const elements = Array.from(elementsMap.values());
6247
6381
  hoveredStartElement = getHoveredElement(
6248
6382
  origStartGlobalPoint,
@@ -6273,7 +6407,9 @@ var getElbowArrowData = (arrow, elementsMap, nextPoints, options) => {
6273
6407
  origStartGlobalPoint,
6274
6408
  hoveredStartElement,
6275
6409
  elementsMap,
6276
- options?.isDragging
6410
+ options?.isDragging,
6411
+ options?.isBindingEnabled,
6412
+ options?.isMidpointSnappingEnabled
6277
6413
  );
6278
6414
  const endGlobalPoint = getGlobalPoint(
6279
6415
  {
@@ -6288,7 +6424,9 @@ var getElbowArrowData = (arrow, elementsMap, nextPoints, options) => {
6288
6424
  origEndGlobalPoint,
6289
6425
  hoveredEndElement,
6290
6426
  elementsMap,
6291
- options?.isDragging
6427
+ options?.isDragging,
6428
+ options?.isBindingEnabled,
6429
+ options?.isMidpointSnappingEnabled
6292
6430
  );
6293
6431
  const startHeading = getBindPointHeading(
6294
6432
  startGlobalPoint,
@@ -6844,7 +6982,7 @@ var normalizeArrowElementUpdate = (global2, nextFixedSegments, startIsSpecial, e
6844
6982
  vectorScale6(vectorFromPoint6(global2[0]), -1)
6845
6983
  )
6846
6984
  );
6847
- if (offsetX < -MAX_POS || offsetX > MAX_POS || offsetY < -MAX_POS || offsetY > MAX_POS || offsetX + points[points.length - 1][0] < -MAX_POS || offsetY + points[points.length - 1][0] > MAX_POS || offsetX + points[points.length - 1][1] < -MAX_POS || offsetY + points[points.length - 1][1] > MAX_POS) {
6985
+ if (offsetX < -MAX_POS || offsetX > MAX_POS || offsetY < -MAX_POS || offsetY > MAX_POS || offsetX + points[points.length - 1][0] < -MAX_POS || offsetX + points[points.length - 1][0] > MAX_POS || offsetY + points[points.length - 1][1] < -MAX_POS || offsetY + points[points.length - 1][1] > MAX_POS) {
6848
6986
  console.error(
6849
6987
  "Elbow arrow normalization is outside reasonable bounds (> 1e6)",
6850
6988
  {
@@ -6911,14 +7049,16 @@ var neighborIndexToHeading = (idx) => {
6911
7049
  }
6912
7050
  return HEADING_LEFT;
6913
7051
  };
6914
- var getGlobalPoint = (arrow, startOrEnd, fixedPointRatio, initialPoint, element, elementsMap, isDragging) => {
7052
+ var getGlobalPoint = (arrow, startOrEnd, fixedPointRatio, initialPoint, element, elementsMap, isDragging, isBindingEnabled2 = true, isMidpointSnappingEnabled = true) => {
6915
7053
  if (isDragging) {
6916
- if (element && elementsMap) {
7054
+ if (isBindingEnabled2 && element && elementsMap) {
6917
7055
  return bindPointToSnapToElementOutline(
6918
7056
  arrow,
6919
7057
  element,
6920
7058
  startOrEnd,
6921
- elementsMap
7059
+ elementsMap,
7060
+ void 0,
7061
+ isMidpointSnappingEnabled
6922
7062
  );
6923
7063
  }
6924
7064
  return initialPoint;
@@ -7079,9 +7219,6 @@ var maxBindingDistance_simple = (zoom) => {
7079
7219
  BASE_BINDING_DISTANCE * 2
7080
7220
  );
7081
7221
  };
7082
- var shouldEnableBindingForPointerEvent = (event) => {
7083
- return !event[KEYS.CTRL_OR_CMD];
7084
- };
7085
7222
  var isBindingEnabled = (appState) => {
7086
7223
  return appState.isBindingEnabled;
7087
7224
  };
@@ -7099,8 +7236,20 @@ var bindOrUnbindBindingElement = (arrow, draggingPoints, scenePointerX, scenePoi
7099
7236
  finalize: true
7100
7237
  }
7101
7238
  );
7102
- bindOrUnbindBindingElementEdge(arrow, start, "start", scene);
7103
- bindOrUnbindBindingElementEdge(arrow, end, "end", scene);
7239
+ bindOrUnbindBindingElementEdge(
7240
+ arrow,
7241
+ start,
7242
+ "start",
7243
+ scene,
7244
+ appState.isBindingEnabled
7245
+ );
7246
+ bindOrUnbindBindingElementEdge(
7247
+ arrow,
7248
+ end,
7249
+ "end",
7250
+ scene,
7251
+ appState.isBindingEnabled
7252
+ );
7104
7253
  if (start.focusPoint || end.focusPoint) {
7105
7254
  const updates = /* @__PURE__ */ new Map();
7106
7255
  if (start.focusPoint) {
@@ -7129,11 +7278,19 @@ var bindOrUnbindBindingElement = (arrow, draggingPoints, scenePointerX, scenePoi
7129
7278
  }
7130
7279
  return { start, end };
7131
7280
  };
7132
- var bindOrUnbindBindingElementEdge = (arrow, { mode, element, focusPoint }, startOrEnd, scene) => {
7281
+ var bindOrUnbindBindingElementEdge = (arrow, { mode, element, focusPoint }, startOrEnd, scene, shouldSnapToOutline = true) => {
7133
7282
  if (mode === null) {
7134
7283
  unbindBindingElement(arrow, startOrEnd, scene);
7135
7284
  } else if (mode !== void 0) {
7136
- bindBindingElement(arrow, element, mode, startOrEnd, scene, focusPoint);
7285
+ bindBindingElement(
7286
+ arrow,
7287
+ element,
7288
+ mode,
7289
+ startOrEnd,
7290
+ scene,
7291
+ focusPoint,
7292
+ shouldSnapToOutline
7293
+ );
7137
7294
  }
7138
7295
  };
7139
7296
  var bindingStrategyForElbowArrowEndpointDragging = (arrow, draggingPoints, elementsMap, elements, zoom) => {
@@ -7429,11 +7586,7 @@ var getBindingStrategyForDraggingBindingElementEndpoints_simple = (arrow, draggi
7429
7586
  threshold: 0,
7430
7587
  overrideShouldTestInside: true
7431
7588
  });
7432
- if (otherBinding && otherBinding.elementId === hit?.id) {
7433
- invariant7(
7434
- !opts?.newArrow || appState.selectedLinearElement?.initialState.origin,
7435
- "appState.selectedLinearElement.initialState.origin must be defined for new arrows"
7436
- );
7589
+ if (otherBinding && otherBinding.elementId === hit?.id && (!opts?.newArrow || appState.selectedLinearElement?.initialState.origin)) {
7437
7590
  return {
7438
7591
  start: {
7439
7592
  mode: "inside",
@@ -7487,7 +7640,8 @@ var getBindingStrategyForDraggingBindingElementEndpoints_simple = (arrow, draggi
7487
7640
  hit,
7488
7641
  startDragged ? "start" : "end",
7489
7642
  elementsMap,
7490
- appState.zoom
7643
+ appState.zoom,
7644
+ appState.isMidpointSnappingEnabled
7491
7645
  ) || globalPoint
7492
7646
  } : { mode: null };
7493
7647
  const otherEndpoint = LinearElementEditor.getPointAtIndexGlobalCoordinates(
@@ -7516,7 +7670,8 @@ var getBindingStrategyForDraggingBindingElementEndpoints_simple = (arrow, draggi
7516
7670
  otherBindableElement,
7517
7671
  startDragged ? "end" : "start",
7518
7672
  elementsMap,
7519
- appState.zoom
7673
+ appState.zoom,
7674
+ appState.isMidpointSnappingEnabled
7520
7675
  ) || otherEndpoint
7521
7676
  } : { mode: void 0 } : { mode: void 0 };
7522
7677
  return {
@@ -7626,7 +7781,7 @@ var bindOrUnbindBindingElements = (selectedArrows, scene, appState) => {
7626
7781
  );
7627
7782
  });
7628
7783
  };
7629
- var bindBindingElement = (arrow, hoveredElement, mode, startOrEnd, scene, focusPoint) => {
7784
+ var bindBindingElement = (arrow, hoveredElement, mode, startOrEnd, scene, focusPoint, shouldSnapToOutline = true) => {
7630
7785
  const elementsMap = scene.getNonDeletedElementsMap();
7631
7786
  let binding;
7632
7787
  if (isElbowArrow(arrow)) {
@@ -7637,7 +7792,8 @@ var bindBindingElement = (arrow, hoveredElement, mode, startOrEnd, scene, focusP
7637
7792
  arrow,
7638
7793
  hoveredElement,
7639
7794
  startOrEnd,
7640
- elementsMap
7795
+ elementsMap,
7796
+ shouldSnapToOutline
7641
7797
  )
7642
7798
  };
7643
7799
  } else {
@@ -7849,7 +8005,7 @@ var getDistanceForBinding = (point, bindableElement, elementsMap, zoom) => {
7849
8005
  const bindDistance = maxBindingDistance_simple(zoom);
7850
8006
  return distance3 > bindDistance ? null : distance3;
7851
8007
  };
7852
- var bindPointToSnapToElementOutline = (arrowElement, bindableElement, startOrEnd, elementsMap, customIntersector) => {
8008
+ var bindPointToSnapToElementOutline = (arrowElement, bindableElement, startOrEnd, elementsMap, customIntersector, isMidpointSnappingEnabled = true) => {
7853
8009
  const elbowed = isElbowArrow(arrowElement);
7854
8010
  const point = LinearElementEditor.getPointAtIndexGlobalCoordinates(
7855
8011
  arrowElement,
@@ -7878,13 +8034,7 @@ var bindPointToSnapToElementOutline = (arrowElement, bindableElement, startOrEnd
7878
8034
  const isHorizontal = headingIsHorizontal(
7879
8035
  headingForPointFromElement(bindableElement, aabb, point)
7880
8036
  );
7881
- const snapPoint = snapToMid(
7882
- bindableElement,
7883
- elementsMap,
7884
- edgePoint,
7885
- 0.05,
7886
- arrowElement
7887
- );
8037
+ const snapPoint = isMidpointSnappingEnabled ? snapToMid(bindableElement, elementsMap, edgePoint, 0.05, arrowElement) : void 0;
7888
8038
  const resolved = snapPoint || point;
7889
8039
  const otherPoint = pointFrom8(
7890
8040
  isHorizontal ? bindableCenter[0] : resolved[0],
@@ -8216,17 +8366,23 @@ var updateBoundPoint = (arrow, startOrEnd, binding, bindableElement, elementsMap
8216
8366
  null
8217
8367
  );
8218
8368
  };
8219
- var calculateFixedPointForElbowArrowBinding = (linearElement, hoveredElement, startOrEnd, elementsMap) => {
8369
+ var calculateFixedPointForElbowArrowBinding = (linearElement, hoveredElement, startOrEnd, elementsMap, shouldSnapToOutline = true, isMidpointSnappingEnabled = true) => {
8220
8370
  const bounds = [
8221
8371
  hoveredElement.x,
8222
8372
  hoveredElement.y,
8223
8373
  hoveredElement.x + hoveredElement.width,
8224
8374
  hoveredElement.y + hoveredElement.height
8225
8375
  ];
8226
- const snappedPoint = bindPointToSnapToElementOutline(
8376
+ const snappedPoint = shouldSnapToOutline ? bindPointToSnapToElementOutline(
8227
8377
  linearElement,
8228
8378
  hoveredElement,
8229
8379
  startOrEnd,
8380
+ elementsMap,
8381
+ void 0,
8382
+ isMidpointSnappingEnabled
8383
+ ) : LinearElementEditor.getPointAtIndexGlobalCoordinates(
8384
+ linearElement,
8385
+ startOrEnd === "start" ? 0 : -1,
8230
8386
  elementsMap
8231
8387
  );
8232
8388
  const globalMidPoint = pointFrom8(
@@ -8240,8 +8396,8 @@ var calculateFixedPointForElbowArrowBinding = (linearElement, hoveredElement, st
8240
8396
  );
8241
8397
  return {
8242
8398
  fixedPoint: normalizeFixedPoint([
8243
- (nonRotatedSnappedGlobalPoint[0] - hoveredElement.x) / hoveredElement.width,
8244
- (nonRotatedSnappedGlobalPoint[1] - hoveredElement.y) / hoveredElement.height
8399
+ (nonRotatedSnappedGlobalPoint[0] - hoveredElement.x) / Math.max(hoveredElement.width, PRECISION2),
8400
+ (nonRotatedSnappedGlobalPoint[1] - hoveredElement.y) / Math.max(hoveredElement.height, PRECISION2)
8245
8401
  ])
8246
8402
  };
8247
8403
  };
@@ -8257,8 +8413,8 @@ var calculateFixedPointForNonElbowArrowBinding = (linearElement, hoveredElement,
8257
8413
  elementCenter,
8258
8414
  -hoveredElement.angle
8259
8415
  );
8260
- const fixedPointX = (nonRotatedPoint[0] - hoveredElement.x) / hoveredElement.width;
8261
- const fixedPointY = (nonRotatedPoint[1] - hoveredElement.y) / hoveredElement.height;
8416
+ const fixedPointX = (nonRotatedPoint[0] - hoveredElement.x) / Math.max(hoveredElement.width, PRECISION2);
8417
+ const fixedPointY = (nonRotatedPoint[1] - hoveredElement.y) / Math.max(hoveredElement.height, PRECISION2);
8262
8418
  return {
8263
8419
  fixedPoint: normalizeFixedPoint([fixedPointX, fixedPointY])
8264
8420
  };
@@ -8587,10 +8743,10 @@ var normalizeFixedPoint = (fixedPoint) => {
8587
8743
  if (!isFixedPoint(fixedPoint)) {
8588
8744
  return [0.5001, 0.5001];
8589
8745
  }
8590
- const EPSILON2 = 1e-4;
8591
- if (Math.abs(fixedPoint[0] - 0.5) < EPSILON2 || Math.abs(fixedPoint[1] - 0.5) < EPSILON2) {
8746
+ const EPSILON = 1e-4;
8747
+ if (Math.abs(fixedPoint[0] - 0.5) < EPSILON || Math.abs(fixedPoint[1] - 0.5) < EPSILON) {
8592
8748
  return fixedPoint.map(
8593
- (ratio) => Math.abs(ratio - 0.5) < EPSILON2 ? 0.5001 : ratio
8749
+ (ratio) => Math.abs(ratio - 0.5) < EPSILON ? 0.5001 : ratio
8594
8750
  );
8595
8751
  }
8596
8752
  return fixedPoint;
@@ -9083,7 +9239,7 @@ var LinearElementEditor = class _LinearElementEditor {
9083
9239
  elementsMap,
9084
9240
  pivotPoint,
9085
9241
  pointFrom9(scenePointerX, scenePointerY),
9086
- event[KEYS2.CTRL_OR_CMD] ? null : app.getEffectiveGridSize(),
9242
+ event[KEYS.CTRL_OR_CMD] ? null : app.getEffectiveGridSize(),
9087
9243
  customLineAngle
9088
9244
  );
9089
9245
  const target = pointFrom9(
@@ -9098,7 +9254,7 @@ var LinearElementEditor = class _LinearElementEditor {
9098
9254
  elementsMap,
9099
9255
  scenePointerX - linearElementEditor.pointerOffset.x,
9100
9256
  scenePointerY - linearElementEditor.pointerOffset.y,
9101
- event[KEYS2.CTRL_OR_CMD] ? null : app.getEffectiveGridSize()
9257
+ event[KEYS.CTRL_OR_CMD] ? null : app.getEffectiveGridSize()
9102
9258
  );
9103
9259
  deltaX = newDraggingPointPosition[0] - point[0];
9104
9260
  deltaY = newDraggingPointPosition[1] - point[1];
@@ -9118,11 +9274,20 @@ var LinearElementEditor = class _LinearElementEditor {
9118
9274
  event.altKey,
9119
9275
  linearElementEditor
9120
9276
  );
9121
- _LinearElementEditor.movePoints(element, app.scene, positions, {
9122
- startBinding: updates?.startBinding,
9123
- endBinding: updates?.endBinding,
9124
- moveMidPointsWithElement: updates?.moveMidPointsWithElement
9125
- });
9277
+ _LinearElementEditor.movePoints(
9278
+ element,
9279
+ app.scene,
9280
+ positions,
9281
+ {
9282
+ startBinding: updates?.startBinding,
9283
+ endBinding: updates?.endBinding,
9284
+ moveMidPointsWithElement: updates?.moveMidPointsWithElement
9285
+ },
9286
+ {
9287
+ isBindingEnabled: app.state.isBindingEnabled,
9288
+ isMidpointSnappingEnabled: app.state.isMidpointSnappingEnabled
9289
+ }
9290
+ );
9126
9291
  if (isBindingElement(element, false)) {
9127
9292
  if (isBindingEnabled(app.state)) {
9128
9293
  suggestedBinding = updates?.suggestedBinding ?? null;
@@ -9158,7 +9323,8 @@ var LinearElementEditor = class _LinearElementEditor {
9158
9323
  startBindingElement,
9159
9324
  "start",
9160
9325
  elementsMap,
9161
- app.state.zoom
9326
+ app.state.zoom,
9327
+ app.state.isMidpointSnappingEnabled
9162
9328
  ) : linearElementEditor.initialState.altFocusPoint
9163
9329
  }
9164
9330
  };
@@ -9192,12 +9358,14 @@ var LinearElementEditor = class _LinearElementEditor {
9192
9358
  return false;
9193
9359
  });
9194
9360
  }
9195
- invariant8(
9196
- lastClickedPoint > -1 && selectedPointsIndices.includes(lastClickedPoint) && element.points[lastClickedPoint],
9197
- `There must be a valid lastClickedPoint in order to drag it. selectedPointsIndices(${JSON.stringify(
9198
- selectedPointsIndices
9199
- )}) points(0..${element.points.length - 1}) lastClickedPoint(${lastClickedPoint})`
9200
- );
9361
+ if (lastClickedPoint < 0 || !selectedPointsIndices.includes(lastClickedPoint) || !element.points[lastClickedPoint]) {
9362
+ console.error(
9363
+ `There must be a valid lastClickedPoint in order to drag it. selectedPointsIndices(${JSON.stringify(
9364
+ selectedPointsIndices
9365
+ )}) points(0..${element.points.length - 1}) lastClickedPoint(${lastClickedPoint}) isElbowArrow: ${elbowed}`
9366
+ );
9367
+ lastClickedPoint = element.points.length - 1;
9368
+ }
9201
9369
  const draggingPoint = element.points[lastClickedPoint];
9202
9370
  const pivotPoint = element.points[lastClickedPoint === 0 ? 1 : lastClickedPoint - 1];
9203
9371
  const singlePointDragged = selectedPointsIndices.length === 1;
@@ -9214,7 +9382,7 @@ var LinearElementEditor = class _LinearElementEditor {
9214
9382
  elementsMap,
9215
9383
  pivotPoint,
9216
9384
  pointFrom9(scenePointerX, scenePointerY),
9217
- event[KEYS2.CTRL_OR_CMD] ? null : app.getEffectiveGridSize(),
9385
+ event[KEYS.CTRL_OR_CMD] ? null : app.getEffectiveGridSize(),
9218
9386
  customLineAngle
9219
9387
  );
9220
9388
  const target = pointFrom9(
@@ -9229,7 +9397,7 @@ var LinearElementEditor = class _LinearElementEditor {
9229
9397
  elementsMap,
9230
9398
  scenePointerX - linearElementEditor.pointerOffset.x,
9231
9399
  scenePointerY - linearElementEditor.pointerOffset.y,
9232
- event[KEYS2.CTRL_OR_CMD] ? null : app.getEffectiveGridSize()
9400
+ event[KEYS.CTRL_OR_CMD] ? null : app.getEffectiveGridSize()
9233
9401
  );
9234
9402
  deltaX = newDraggingPointPosition[0] - draggingPoint[0];
9235
9403
  deltaY = newDraggingPointPosition[1] - draggingPoint[1];
@@ -9249,11 +9417,20 @@ var LinearElementEditor = class _LinearElementEditor {
9249
9417
  event.altKey,
9250
9418
  linearElementEditor
9251
9419
  );
9252
- _LinearElementEditor.movePoints(element, app.scene, positions, {
9253
- startBinding: updates?.startBinding,
9254
- endBinding: updates?.endBinding,
9255
- moveMidPointsWithElement: updates?.moveMidPointsWithElement
9256
- });
9420
+ _LinearElementEditor.movePoints(
9421
+ element,
9422
+ app.scene,
9423
+ positions,
9424
+ {
9425
+ startBinding: updates?.startBinding,
9426
+ endBinding: updates?.endBinding,
9427
+ moveMidPointsWithElement: updates?.moveMidPointsWithElement
9428
+ },
9429
+ {
9430
+ isBindingEnabled: app.state.isBindingEnabled,
9431
+ isMidpointSnappingEnabled: app.state.isMidpointSnappingEnabled
9432
+ }
9433
+ );
9257
9434
  if (isBindingElement(element, false)) {
9258
9435
  if (isBindingEnabled(app.state) && (startIsSelected || endIsSelected)) {
9259
9436
  suggestedBinding = updates?.suggestedBinding ?? null;
@@ -9306,7 +9483,8 @@ var LinearElementEditor = class _LinearElementEditor {
9306
9483
  altFocusPointBindableElement,
9307
9484
  "start",
9308
9485
  elementsMap,
9309
- app.state.zoom
9486
+ app.state.zoom,
9487
+ app.state.isMidpointSnappingEnabled
9310
9488
  ) : linearElementEditor.initialState.altFocusPoint
9311
9489
  },
9312
9490
  segmentMidPointHoveredCoords: newSelectedMidPointHoveredCoords,
@@ -9401,7 +9579,8 @@ var LinearElementEditor = class _LinearElementEditor {
9401
9579
  element.points[index],
9402
9580
  element.points[index + 1],
9403
9581
  index,
9404
- appState.zoom
9582
+ appState.zoom,
9583
+ elementsMap
9405
9584
  )) {
9406
9585
  midpoints.push(null);
9407
9586
  index++;
@@ -9409,7 +9588,8 @@ var LinearElementEditor = class _LinearElementEditor {
9409
9588
  }
9410
9589
  const segmentMidPoint = _LinearElementEditor.getSegmentMidPoint(
9411
9590
  element,
9412
- index + 1
9591
+ index + 1,
9592
+ elementsMap
9413
9593
  );
9414
9594
  midpoints.push(segmentMidPoint);
9415
9595
  index++;
@@ -9473,7 +9653,7 @@ var LinearElementEditor = class _LinearElementEditor {
9473
9653
  }
9474
9654
  return null;
9475
9655
  };
9476
- static isSegmentTooShort(element, startPoint, endPoint, index, zoom) {
9656
+ static isSegmentTooShort(element, startPoint, endPoint, index, zoom, elementsMap) {
9477
9657
  if (isElbowArrow(element)) {
9478
9658
  if (index >= 0 && index < element.points.length) {
9479
9659
  return pointDistance5(startPoint, endPoint) * zoom.value < _LinearElementEditor.POINT_HANDLE_SIZE / 2;
@@ -9482,7 +9662,10 @@ var LinearElementEditor = class _LinearElementEditor {
9482
9662
  }
9483
9663
  let distance3 = pointDistance5(startPoint, endPoint);
9484
9664
  if (element.points.length > 2 && element.roundness) {
9485
- const [lines, curves] = deconstructLinearOrFreeDrawElement2(element);
9665
+ const [lines, curves] = deconstructLinearOrFreeDrawElement2(
9666
+ element,
9667
+ elementsMap
9668
+ );
9486
9669
  invariant8(
9487
9670
  lines.length === 0 && curves.length > 0,
9488
9671
  "Only linears built out of curves are supported"
@@ -9495,7 +9678,7 @@ var LinearElementEditor = class _LinearElementEditor {
9495
9678
  }
9496
9679
  return distance3 * zoom.value < _LinearElementEditor.POINT_HANDLE_SIZE * 4;
9497
9680
  }
9498
- static getSegmentMidPoint(element, index) {
9681
+ static getSegmentMidPoint(element, index, elementsMap) {
9499
9682
  if (isElbowArrow(element)) {
9500
9683
  invariant8(
9501
9684
  element.points.length >= index,
@@ -9504,7 +9687,10 @@ var LinearElementEditor = class _LinearElementEditor {
9504
9687
  const p = pointCenter2(element.points[index - 1], element.points[index]);
9505
9688
  return pointFrom9(element.x + p[0], element.y + p[1]);
9506
9689
  }
9507
- const [lines, curves] = deconstructLinearOrFreeDrawElement2(element);
9690
+ const [lines, curves] = deconstructLinearOrFreeDrawElement2(
9691
+ element,
9692
+ elementsMap
9693
+ );
9508
9694
  invariant8(
9509
9695
  lines.length === 0 && curves.length > 0 || lines.length > 0 && curves.length === 0,
9510
9696
  "Only linears built out of either segments or curves are supported"
@@ -9586,7 +9772,7 @@ var LinearElementEditor = class _LinearElementEditor {
9586
9772
  elementsMap,
9587
9773
  scenePointer.x,
9588
9774
  scenePointer.y,
9589
- event[KEYS2.CTRL_OR_CMD] ? null : app.getEffectiveGridSize()
9775
+ event[KEYS.CTRL_OR_CMD] ? null : app.getEffectiveGridSize()
9590
9776
  )
9591
9777
  ]
9592
9778
  });
@@ -9699,7 +9885,7 @@ var LinearElementEditor = class _LinearElementEditor {
9699
9885
  elementsMap,
9700
9886
  anchor,
9701
9887
  pointFrom9(scenePointerX, scenePointerY),
9702
- event[KEYS2.CTRL_OR_CMD] ? null : app.getEffectiveGridSize()
9888
+ event[KEYS.CTRL_OR_CMD] ? null : app.getEffectiveGridSize()
9703
9889
  );
9704
9890
  newPoint = pointFrom9(width + anchor[0], height + anchor[1]);
9705
9891
  } else {
@@ -9708,7 +9894,7 @@ var LinearElementEditor = class _LinearElementEditor {
9708
9894
  elementsMap,
9709
9895
  scenePointerX - appState.selectedLinearElement.pointerOffset.x,
9710
9896
  scenePointerY - appState.selectedLinearElement.pointerOffset.y,
9711
- event[KEYS2.CTRL_OR_CMD] || isElbowArrow(element) ? null : app.getEffectiveGridSize()
9897
+ event[KEYS.CTRL_OR_CMD] || isElbowArrow(element) ? null : app.getEffectiveGridSize()
9712
9898
  );
9713
9899
  }
9714
9900
  if (lastPoint === lastUncommittedPoint) {
@@ -9936,7 +10122,7 @@ var LinearElementEditor = class _LinearElementEditor {
9936
10122
  offsetY
9937
10123
  );
9938
10124
  }
9939
- static movePoints(element, scene, pointUpdates, otherUpdates) {
10125
+ static movePoints(element, scene, pointUpdates, otherUpdates, options) {
9940
10126
  const { points } = element;
9941
10127
  if (isLineElement(element) && element.polygon) {
9942
10128
  const firstPointUpdate = pointUpdates.get(0);
@@ -9979,7 +10165,9 @@ var LinearElementEditor = class _LinearElementEditor {
9979
10165
  offsetY,
9980
10166
  otherUpdates,
9981
10167
  {
9982
- isDragging: Array.from(pointUpdates.values()).some((t) => t.isDragging)
10168
+ isDragging: Array.from(pointUpdates.values()).some((t) => t.isDragging),
10169
+ isBindingEnabled: options?.isBindingEnabled,
10170
+ isMidpointSnappingEnabled: options?.isMidpointSnappingEnabled
9983
10171
  }
9984
10172
  );
9985
10173
  }
@@ -10022,7 +10210,7 @@ var LinearElementEditor = class _LinearElementEditor {
10022
10210
  pointerDownState: linearElementEditor.initialState,
10023
10211
  selectedPointsIndices: linearElementEditor.selectedPointsIndices
10024
10212
  };
10025
- const midpoint2 = _LinearElementEditor.createPointAt(
10213
+ const midpoint = _LinearElementEditor.createPointAt(
10026
10214
  element,
10027
10215
  elementsMap,
10028
10216
  pointerCoords.x,
@@ -10031,7 +10219,7 @@ var LinearElementEditor = class _LinearElementEditor {
10031
10219
  );
10032
10220
  const points = [
10033
10221
  ...element.points.slice(0, segmentMidpoint.index),
10034
- midpoint2,
10222
+ midpoint,
10035
10223
  ...element.points.slice(segmentMidpoint.index)
10036
10224
  ];
10037
10225
  scene.mutateElement(element, { points });
@@ -10058,7 +10246,9 @@ var LinearElementEditor = class _LinearElementEditor {
10058
10246
  updates.points = Array.from(nextPoints);
10059
10247
  scene.mutateElement(element, updates, {
10060
10248
  informMutation: true,
10061
- isDragging: options?.isDragging ?? false
10249
+ isDragging: options?.isDragging ?? false,
10250
+ isBindingEnabled: options?.isBindingEnabled,
10251
+ isMidpointSnappingEnabled: options?.isMidpointSnappingEnabled
10062
10252
  });
10063
10253
  } else {
10064
10254
  const nextCoords = getElementPointsCoords(element, nextPoints);
@@ -10135,7 +10325,8 @@ var LinearElementEditor = class _LinearElementEditor {
10135
10325
  const index = element.points.length / 2 - 1;
10136
10326
  const midSegmentMidpoint = _LinearElementEditor.getSegmentMidPoint(
10137
10327
  element,
10138
- index + 1
10328
+ index + 1,
10329
+ elementsMap
10139
10330
  );
10140
10331
  x = midSegmentMidpoint[0] - boundTextElement.width / 2;
10141
10332
  y = midSegmentMidpoint[1] - boundTextElement.height / 2;
@@ -10323,13 +10514,11 @@ var normalizeSelectedPoints = (points) => {
10323
10514
  var pointDraggingUpdates = (selectedPointsIndices, deltaX, deltaY, scenePointerX, scenePointerY, elementsMap, element, elements, app, angleLocked, altKey, linearElementEditor) => {
10324
10515
  const naiveDraggingPoints = new Map(
10325
10516
  selectedPointsIndices.map((pointIndex) => {
10517
+ const point = element.points[pointIndex] ?? element.points.at(-1);
10326
10518
  return [
10327
10519
  pointIndex,
10328
10520
  {
10329
- point: pointFrom9(
10330
- element.points[pointIndex][0] + deltaX,
10331
- element.points[pointIndex][1] + deltaY
10332
- ),
10521
+ point: pointFrom9(point[0] + deltaX, point[1] + deltaY),
10333
10522
  isDragging: true
10334
10523
  }
10335
10524
  ];
@@ -10365,14 +10554,14 @@ var pointDraggingUpdates = (selectedPointsIndices, deltaX, deltaY, scenePointerX
10365
10554
  updates: {
10366
10555
  suggestedBinding: suggestedBindingElement ? {
10367
10556
  element: suggestedBindingElement,
10368
- midPoint: snapToMid(
10557
+ midPoint: app.state.isMidpointSnappingEnabled ? snapToMid(
10369
10558
  suggestedBindingElement,
10370
10559
  elementsMap,
10371
10560
  pointFrom9(
10372
10561
  scenePointerX - linearElementEditor.pointerOffset.x,
10373
10562
  scenePointerY - linearElementEditor.pointerOffset.y
10374
10563
  )
10375
- )
10564
+ ) : void 0
10376
10565
  } : null
10377
10566
  }
10378
10567
  };
@@ -10516,7 +10705,7 @@ var pointDraggingUpdates = (selectedPointsIndices, deltaX, deltaY, scenePointerX
10516
10705
  nextArrow.endBinding.elementId
10517
10706
  ) : null;
10518
10707
  const endLocalPoint = startIsDraggingOverEndElement ? nextArrow.points[nextArrow.points.length - 1] : endIsDraggingOverStartElement && app.state.bindMode !== "inside" && getFeatureFlag2("COMPLEX_BINDINGS") ? nextArrow.points[0] : endBindable ? updateBoundPoint(
10519
- element,
10708
+ nextArrow,
10520
10709
  "endBinding",
10521
10710
  nextArrow.endBinding,
10522
10711
  endBindable,
@@ -10528,7 +10717,7 @@ var pointDraggingUpdates = (selectedPointsIndices, deltaX, deltaY, scenePointerX
10528
10717
  nextArrow.startBinding.elementId
10529
10718
  ) : null;
10530
10719
  const startLocalPoint = endIsDraggingOverStartElement && getFeatureFlag2("COMPLEX_BINDINGS") ? nextArrow.points[0] : startIsDraggingOverEndElement && app.state.bindMode !== "inside" && getFeatureFlag2("COMPLEX_BINDINGS") ? endLocalPoint : startBindable ? updateBoundPoint(
10531
- element,
10720
+ nextArrow,
10532
10721
  "startBinding",
10533
10722
  nextArrow.startBinding,
10534
10723
  startBindable,
@@ -10568,188 +10757,15 @@ var determineCustomLinearAngle = (pivotPoint, draggedPoint) => Math.atan2(dragge
10568
10757
  // src/frame.ts
10569
10758
  init_define_import_meta_env();
10570
10759
  import { arrayToMap as arrayToMap5 } from "@excalidraw/common";
10571
- import { isPointWithinBounds as isPointWithinBounds2, pointFrom as pointFrom11 } from "@excalidraw/math";
10572
-
10573
- // ../utils/src/bbox.ts
10574
- init_define_import_meta_env();
10575
- import {
10576
- vectorCross as vectorCross3,
10577
- vectorFromPoint as vectorFromPoint9
10578
- } from "@excalidraw/math";
10579
- function getBBox(line2) {
10580
- return [
10581
- Math.min(line2[0][0], line2[1][0]),
10582
- Math.min(line2[0][1], line2[1][1]),
10583
- Math.max(line2[0][0], line2[1][0]),
10584
- Math.max(line2[0][1], line2[1][1])
10585
- ];
10586
- }
10587
- function doBBoxesIntersect(a2, b2) {
10588
- return a2[0] <= b2[2] && a2[2] >= b2[0] && a2[1] <= b2[3] && a2[3] >= b2[1];
10589
- }
10590
- var EPSILON = 1e-6;
10591
- function isPointOnLine(l2, p) {
10592
- const p1 = vectorFromPoint9(l2[1], l2[0]);
10593
- const p2 = vectorFromPoint9(p, l2[0]);
10594
- const r = vectorCross3(p1, p2);
10595
- return Math.abs(r) < EPSILON;
10596
- }
10597
- function isPointRightOfLine(l2, p) {
10598
- const p1 = vectorFromPoint9(l2[1], l2[0]);
10599
- const p2 = vectorFromPoint9(p, l2[0]);
10600
- return vectorCross3(p1, p2) < 0;
10601
- }
10602
- function isLineSegmentTouchingOrCrossingLine(a2, b2) {
10603
- return isPointOnLine(a2, b2[0]) || isPointOnLine(a2, b2[1]) || (isPointRightOfLine(a2, b2[0]) ? !isPointRightOfLine(a2, b2[1]) : isPointRightOfLine(a2, b2[1]));
10604
- }
10605
- function doLineSegmentsIntersect(a2, b2) {
10606
- return doBBoxesIntersect(getBBox(a2), getBBox(b2)) && isLineSegmentTouchingOrCrossingLine(a2, b2) && isLineSegmentTouchingOrCrossingLine(b2, a2);
10607
- }
10608
-
10609
- // ../utils/src/withinBounds.ts
10610
- init_define_import_meta_env();
10611
- import { arrayToMap as arrayToMap3 } from "@excalidraw/common";
10612
- import { getElementBounds as getElementBounds2 } from "@excalidraw/element";
10613
- import {
10614
- isArrowElement as isArrowElement2,
10615
- isExcalidrawElement as isExcalidrawElement2,
10616
- isFreeDrawElement as isFreeDrawElement2,
10617
- isLinearElement as isLinearElement2,
10618
- isTextElement as isTextElement2
10619
- } from "@excalidraw/element";
10620
10760
  import {
10621
- rangeIncludesValue,
10761
+ isPointWithinBounds as isPointWithinBounds2,
10622
10762
  pointFrom as pointFrom10,
10623
- pointRotateRads as pointRotateRads10,
10624
- rangeInclusive
10763
+ segmentsIntersectAt as segmentsIntersectAt2
10625
10764
  } from "@excalidraw/math";
10626
- var getNonLinearElementRelativePoints = (element) => {
10627
- if (element.type === "diamond") {
10628
- return [
10629
- pointFrom10(element.width / 2, 0),
10630
- pointFrom10(element.width, element.height / 2),
10631
- pointFrom10(element.width / 2, element.height),
10632
- pointFrom10(0, element.height / 2)
10633
- ];
10634
- }
10635
- return [
10636
- pointFrom10(0, 0),
10637
- pointFrom10(0 + element.width, 0),
10638
- pointFrom10(0 + element.width, element.height),
10639
- pointFrom10(0, element.height)
10640
- ];
10641
- };
10642
- var getElementRelativePoints = (element) => {
10643
- if (isLinearElement2(element) || isFreeDrawElement2(element)) {
10644
- return element.points;
10645
- }
10646
- return getNonLinearElementRelativePoints(element);
10647
- };
10648
- var getMinMaxPoints = (points) => {
10649
- const ret = points.reduce(
10650
- (limits, [x, y]) => {
10651
- limits.minY = Math.min(limits.minY, y);
10652
- limits.minX = Math.min(limits.minX, x);
10653
- limits.maxX = Math.max(limits.maxX, x);
10654
- limits.maxY = Math.max(limits.maxY, y);
10655
- return limits;
10656
- },
10657
- {
10658
- minX: Infinity,
10659
- minY: Infinity,
10660
- maxX: -Infinity,
10661
- maxY: -Infinity,
10662
- cx: 0,
10663
- cy: 0
10664
- }
10665
- );
10666
- ret.cx = (ret.maxX + ret.minX) / 2;
10667
- ret.cy = (ret.maxY + ret.minY) / 2;
10668
- return ret;
10669
- };
10670
- var getRotatedBBox = (element) => {
10671
- const points = getElementRelativePoints(element);
10672
- const { cx, cy } = getMinMaxPoints(points);
10673
- const centerPoint = pointFrom10(cx, cy);
10674
- const rotatedPoints = points.map(
10675
- (p) => pointRotateRads10(p, centerPoint, element.angle)
10676
- );
10677
- const { minX, minY, maxX, maxY } = getMinMaxPoints(rotatedPoints);
10678
- return [
10679
- minX + element.x,
10680
- minY + element.y,
10681
- maxX + element.x,
10682
- maxY + element.y
10683
- ];
10684
- };
10685
- var isElementInsideBBox = (element, bbox, eitherDirection = false) => {
10686
- const elementBBox = getRotatedBBox(element);
10687
- const elementInsideBbox = bbox[0] <= elementBBox[0] && bbox[2] >= elementBBox[2] && bbox[1] <= elementBBox[1] && bbox[3] >= elementBBox[3];
10688
- if (!eitherDirection) {
10689
- return elementInsideBbox;
10690
- }
10691
- if (elementInsideBbox) {
10692
- return true;
10693
- }
10694
- return elementBBox[0] <= bbox[0] && elementBBox[2] >= bbox[2] && elementBBox[1] <= bbox[1] && elementBBox[3] >= bbox[3];
10695
- };
10696
- var elementPartiallyOverlapsWithOrContainsBBox = (element, bbox) => {
10697
- const elementBBox = getRotatedBBox(element);
10698
- return (rangeIncludesValue(elementBBox[0], rangeInclusive(bbox[0], bbox[2])) || rangeIncludesValue(
10699
- bbox[0],
10700
- rangeInclusive(elementBBox[0], elementBBox[2])
10701
- )) && (rangeIncludesValue(elementBBox[1], rangeInclusive(bbox[1], bbox[3])) || rangeIncludesValue(
10702
- bbox[1],
10703
- rangeInclusive(elementBBox[1], elementBBox[3])
10704
- ));
10705
- };
10706
- var elementsOverlappingBBox = ({
10707
- elements,
10708
- bounds,
10709
- type,
10710
- errorMargin = 0
10711
- }) => {
10712
- if (isExcalidrawElement2(bounds)) {
10713
- bounds = getElementBounds2(bounds, arrayToMap3(elements));
10714
- }
10715
- const adjustedBBox = [
10716
- bounds[0] - errorMargin,
10717
- bounds[1] - errorMargin,
10718
- bounds[2] + errorMargin,
10719
- bounds[3] + errorMargin
10720
- ];
10721
- const includedElementSet = /* @__PURE__ */ new Set();
10722
- for (const element of elements) {
10723
- if (includedElementSet.has(element.id)) {
10724
- continue;
10725
- }
10726
- const isOverlaping = type === "overlap" ? elementPartiallyOverlapsWithOrContainsBBox(element, adjustedBBox) : type === "inside" ? isElementInsideBBox(element, adjustedBBox) : isElementInsideBBox(element, adjustedBBox, true);
10727
- if (isOverlaping) {
10728
- includedElementSet.add(element.id);
10729
- if (element.boundElements) {
10730
- for (const boundElement of element.boundElements) {
10731
- includedElementSet.add(boundElement.id);
10732
- }
10733
- }
10734
- if (isTextElement2(element) && element.containerId) {
10735
- includedElementSet.add(element.containerId);
10736
- }
10737
- if (isArrowElement2(element)) {
10738
- if (element.startBinding) {
10739
- includedElementSet.add(element.startBinding.elementId);
10740
- }
10741
- if (element.endBinding) {
10742
- includedElementSet.add(element.endBinding?.elementId);
10743
- }
10744
- }
10745
- }
10746
- }
10747
- return elements.filter((element) => includedElementSet.has(element.id));
10748
- };
10749
10765
 
10750
10766
  // src/selection.ts
10751
10767
  init_define_import_meta_env();
10752
- import { arrayToMap as arrayToMap4, isShallowEqual as isShallowEqual2 } from "@excalidraw/common";
10768
+ import { arrayToMap as arrayToMap3, isShallowEqual as isShallowEqual2 } from "@excalidraw/common";
10753
10769
 
10754
10770
  // src/groups.ts
10755
10771
  init_define_import_meta_env();
@@ -11025,13 +11041,8 @@ var getSelectedElementsByGroup = (selectedElements, elementsMap, appState) => {
11025
11041
  };
11026
11042
 
11027
11043
  // src/selection.ts
11028
- var excludeElementsInFramesFromSelection = (selectedElements) => {
11029
- const framesInSelection = /* @__PURE__ */ new Set();
11030
- selectedElements.forEach((element) => {
11031
- if (isFrameLikeElement(element)) {
11032
- framesInSelection.add(element.id);
11033
- }
11034
- });
11044
+ var shouldIgnoreElementFromSelection = (element) => element.locked || isBoundToContainer(element);
11045
+ var excludeElementsFromFrames = (selectedElements, framesInSelection) => {
11035
11046
  return selectedElements.filter((element) => {
11036
11047
  if (element.frameId && framesInSelection.has(element.frameId)) {
11037
11048
  return false;
@@ -11039,35 +11050,35 @@ var excludeElementsInFramesFromSelection = (selectedElements) => {
11039
11050
  return true;
11040
11051
  });
11041
11052
  };
11042
- var getElementsWithinSelection = (elements, selection, elementsMap, excludeElementsInFrames = true) => {
11043
- const [selectionX1, selectionY1, selectionX2, selectionY2] = getElementAbsoluteCoords2(selection, elementsMap);
11044
- let elementsInSelection = elements.filter((element) => {
11045
- let [elementX1, elementY1, elementX2, elementY2] = getElementBounds(
11046
- element,
11047
- elementsMap
11048
- );
11049
- const containingFrame = getContainingFrame(element, elementsMap);
11050
- if (containingFrame) {
11051
- const [fx1, fy1, fx2, fy2] = getElementBounds(
11052
- containingFrame,
11053
- elementsMap
11054
- );
11055
- elementX1 = Math.max(fx1, elementX1);
11056
- elementY1 = Math.max(fy1, elementY1);
11057
- elementX2 = Math.min(fx2, elementX2);
11058
- elementY2 = Math.min(fy2, elementY2);
11053
+ var excludeElementsInFramesFromSelection = (selectedElements) => {
11054
+ const framesInSelection = /* @__PURE__ */ new Set();
11055
+ selectedElements.forEach((element) => {
11056
+ if (isFrameLikeElement(element)) {
11057
+ framesInSelection.add(element.id);
11059
11058
  }
11060
- return element.locked === false && element.type !== "selection" && !isBoundToContainer(element) && selectionX1 <= elementX1 && selectionY1 <= elementY1 && selectionX2 >= elementX2 && selectionY2 >= elementY2;
11061
11059
  });
11062
- elementsInSelection = excludeElementsInFrames ? excludeElementsInFramesFromSelection(elementsInSelection) : elementsInSelection;
11063
- elementsInSelection = elementsInSelection.filter((element) => {
11064
- const containingFrame = getContainingFrame(element, elementsMap);
11065
- if (containingFrame) {
11066
- return elementOverlapsWithFrame(element, containingFrame, elementsMap);
11067
- }
11068
- return true;
11060
+ return excludeElementsFromFrames(selectedElements, framesInSelection);
11061
+ };
11062
+ var getElementsWithinSelection = (elements, selection, elementsMap, excludeElementsInFrames = true, boxSelectionMode = "contain") => {
11063
+ const [selectionStartX, selectionStartY, selectionEndX, selectionEndY] = getElementAbsoluteCoords2(selection, elementsMap);
11064
+ const selectionX1 = Math.min(selectionStartX, selectionEndX);
11065
+ const selectionY1 = Math.min(selectionStartY, selectionEndY);
11066
+ const selectionX2 = Math.max(selectionStartX, selectionEndX);
11067
+ const selectionY2 = Math.max(selectionStartY, selectionEndY);
11068
+ const selectionBounds = [
11069
+ selectionX1,
11070
+ selectionY1,
11071
+ selectionX2,
11072
+ selectionY2
11073
+ ];
11074
+ return elementsOverlappingBBox({
11075
+ elements,
11076
+ bounds: selectionBounds,
11077
+ elementsMap,
11078
+ type: boxSelectionMode,
11079
+ shouldIgnoreElementFromSelection,
11080
+ excludeElementsInFrames
11069
11081
  });
11070
- return elementsInSelection;
11071
11082
  };
11072
11083
  var getVisibleAndNonSelectedElements = (elements, selectedElements, appState, elementsMap) => {
11073
11084
  const selectedElementsSet = new Set(
@@ -11153,7 +11164,7 @@ var _getLinearElementEditor = (targetElements, allElements) => {
11153
11164
  (el) => el.id === linear.id || boundElements.includes(el.id)
11154
11165
  );
11155
11166
  if (onlySingleLinearSelected) {
11156
- return new LinearElementEditor(linear, arrayToMap4(allElements));
11167
+ return new LinearElementEditor(linear, arrayToMap3(allElements));
11157
11168
  }
11158
11169
  }
11159
11170
  return null;
@@ -11179,9 +11190,263 @@ var getSelectionStateForElements = (targetElements, allElements, appState) => {
11179
11190
  )
11180
11191
  };
11181
11192
  };
11193
+ var getActiveTextElement = (selectedElements, appState) => {
11194
+ const activeTextElement = appState.editingTextElement || selectedElements.length === 1 && isTextElement(selectedElements[0]) && selectedElements[0];
11195
+ return activeTextElement || null;
11196
+ };
11182
11197
 
11183
- // src/frame.ts
11184
- var bindElementsToFramesAfterDuplication = (nextElements, origElements, origIdToDuplicateId) => {
11198
+ // src/fractionalIndex.ts
11199
+ init_define_import_meta_env();
11200
+ import { arrayToMap as arrayToMap4 } from "@excalidraw/common";
11201
+ import {
11202
+ validateOrderKey,
11203
+ generateNKeysBetween
11204
+ } from "@excalidraw/fractional-indexing";
11205
+ var InvalidFractionalIndexError = class extends Error {
11206
+ code = "ELEMENT_HAS_INVALID_INDEX";
11207
+ };
11208
+ var validateFractionalIndices = (elements, {
11209
+ shouldThrow = false,
11210
+ includeBoundTextValidation = false,
11211
+ ignoreLogs,
11212
+ reconciliationContext
11213
+ }) => {
11214
+ const errorMessages = [];
11215
+ const stringifyElement = (element) => `${element?.index}:${element?.id}:${element?.type}:${element?.isDeleted}:${element?.version}:${element?.versionNonce}`;
11216
+ const indices = elements.map((x) => x.index);
11217
+ for (const [i, index] of indices.entries()) {
11218
+ const predecessorIndex = indices[i - 1];
11219
+ const successorIndex = indices[i + 1];
11220
+ if (!isValidFractionalIndex(index, predecessorIndex, successorIndex)) {
11221
+ errorMessages.push(
11222
+ `Fractional indices invariant has been compromised: "${stringifyElement(
11223
+ elements[i - 1]
11224
+ )}", "${stringifyElement(elements[i])}", "${stringifyElement(
11225
+ elements[i + 1]
11226
+ )}"`
11227
+ );
11228
+ }
11229
+ if (includeBoundTextValidation && hasBoundTextElement(elements[i])) {
11230
+ const container = elements[i];
11231
+ const text = getBoundTextElement(container, arrayToMap4(elements));
11232
+ if (text && text.index <= container.index) {
11233
+ errorMessages.push(
11234
+ `Fractional indices invariant for bound elements has been compromised: "${stringifyElement(
11235
+ text
11236
+ )}", "${stringifyElement(container)}"`
11237
+ );
11238
+ }
11239
+ }
11240
+ }
11241
+ if (errorMessages.length) {
11242
+ const error = new InvalidFractionalIndexError();
11243
+ const additionalContext = [];
11244
+ if (reconciliationContext) {
11245
+ additionalContext.push("Additional reconciliation context:");
11246
+ additionalContext.push(
11247
+ reconciliationContext.localElements.map((x) => stringifyElement(x))
11248
+ );
11249
+ additionalContext.push(
11250
+ reconciliationContext.remoteElements.map((x) => stringifyElement(x))
11251
+ );
11252
+ }
11253
+ if (!ignoreLogs) {
11254
+ console.error(
11255
+ errorMessages.join("\n\n"),
11256
+ error.stack,
11257
+ elements.map((x) => stringifyElement(x)),
11258
+ ...additionalContext
11259
+ );
11260
+ }
11261
+ if (shouldThrow) {
11262
+ throw error;
11263
+ }
11264
+ }
11265
+ };
11266
+ var orderByFractionalIndex = (elements) => {
11267
+ return elements.sort((a2, b2) => {
11268
+ if (isOrderedElement(a2) && isOrderedElement(b2)) {
11269
+ if (a2.index < b2.index) {
11270
+ return -1;
11271
+ } else if (a2.index > b2.index) {
11272
+ return 1;
11273
+ }
11274
+ return a2.id < b2.id ? -1 : 1;
11275
+ }
11276
+ return 1;
11277
+ });
11278
+ };
11279
+ var syncMovedIndices = (elements, movedElements) => {
11280
+ try {
11281
+ const elementsMap = arrayToMap4(elements);
11282
+ const indicesGroups = getMovedIndicesGroups(elements, movedElements);
11283
+ const elementsUpdates = generateIndices(elements, indicesGroups);
11284
+ const elementsCandidates = elements.map((x) => {
11285
+ const elementUpdates = elementsUpdates.get(x);
11286
+ if (elementUpdates) {
11287
+ return { ...x, index: elementUpdates.index };
11288
+ }
11289
+ return x;
11290
+ });
11291
+ validateFractionalIndices(
11292
+ elementsCandidates,
11293
+ // we don't autofix invalid bound text indices, hence don't include it in the validation
11294
+ {
11295
+ includeBoundTextValidation: false,
11296
+ shouldThrow: true,
11297
+ ignoreLogs: true
11298
+ }
11299
+ );
11300
+ for (const [element, { index }] of elementsUpdates) {
11301
+ mutateElement(element, elementsMap, { index });
11302
+ }
11303
+ } catch (e) {
11304
+ syncInvalidIndices(elements);
11305
+ }
11306
+ return elements;
11307
+ };
11308
+ var syncInvalidIndices = (elements) => {
11309
+ const elementsMap = arrayToMap4(elements);
11310
+ const indicesGroups = getInvalidIndicesGroups(elements);
11311
+ const elementsUpdates = generateIndices(elements, indicesGroups);
11312
+ for (const [element, { index }] of elementsUpdates) {
11313
+ mutateElement(element, elementsMap, { index });
11314
+ }
11315
+ return elements;
11316
+ };
11317
+ var syncInvalidIndicesImmutable = (elements) => {
11318
+ const syncedElements = arrayToMap4(elements);
11319
+ const indicesGroups = getInvalidIndicesGroups(elements);
11320
+ const elementsUpdates = generateIndices(elements, indicesGroups);
11321
+ for (const [element, { index }] of elementsUpdates) {
11322
+ syncedElements.set(element.id, newElementWith(element, { index }));
11323
+ }
11324
+ return syncedElements;
11325
+ };
11326
+ var getMovedIndicesGroups = (elements, movedElements) => {
11327
+ const indicesGroups = [];
11328
+ let i = 0;
11329
+ while (i < elements.length) {
11330
+ if (movedElements.has(elements[i].id)) {
11331
+ const indicesGroup = [i - 1, i];
11332
+ while (++i < elements.length) {
11333
+ if (!movedElements.has(elements[i].id)) {
11334
+ break;
11335
+ }
11336
+ indicesGroup.push(i);
11337
+ }
11338
+ indicesGroup.push(i);
11339
+ indicesGroups.push(indicesGroup);
11340
+ } else {
11341
+ i++;
11342
+ }
11343
+ }
11344
+ return indicesGroups;
11345
+ };
11346
+ var getInvalidIndicesGroups = (elements) => {
11347
+ const indicesGroups = [];
11348
+ let lowerBound = void 0;
11349
+ let upperBound = void 0;
11350
+ let lowerBoundIndex = -1;
11351
+ let upperBoundIndex = 0;
11352
+ const getLowerBound = (index) => {
11353
+ const lowerBound2 = elements[lowerBoundIndex] ? elements[lowerBoundIndex].index : void 0;
11354
+ const candidate = elements[index - 1]?.index;
11355
+ if (!lowerBound2 && candidate || // first lowerBound
11356
+ lowerBound2 && candidate && candidate > lowerBound2) {
11357
+ return [candidate, index - 1];
11358
+ }
11359
+ return [lowerBound2, lowerBoundIndex];
11360
+ };
11361
+ const getUpperBound = (index) => {
11362
+ const upperBound2 = elements[upperBoundIndex] ? elements[upperBoundIndex].index : void 0;
11363
+ if (upperBound2 && index < upperBoundIndex) {
11364
+ return [upperBound2, upperBoundIndex];
11365
+ }
11366
+ let i2 = upperBoundIndex;
11367
+ while (++i2 < elements.length) {
11368
+ const candidate = elements[i2]?.index;
11369
+ if (!upperBound2 && candidate || // first upperBound
11370
+ upperBound2 && candidate && candidate > upperBound2) {
11371
+ return [candidate, i2];
11372
+ }
11373
+ }
11374
+ return [void 0, i2];
11375
+ };
11376
+ let i = 0;
11377
+ while (i < elements.length) {
11378
+ const current = elements[i].index;
11379
+ [lowerBound, lowerBoundIndex] = getLowerBound(i);
11380
+ [upperBound, upperBoundIndex] = getUpperBound(i);
11381
+ if (!isValidFractionalIndex(current, lowerBound, upperBound)) {
11382
+ const indicesGroup = [lowerBoundIndex, i];
11383
+ while (++i < elements.length) {
11384
+ const current2 = elements[i].index;
11385
+ const [nextLowerBound, nextLowerBoundIndex] = getLowerBound(i);
11386
+ const [nextUpperBound, nextUpperBoundIndex] = getUpperBound(i);
11387
+ if (isValidFractionalIndex(current2, nextLowerBound, nextUpperBound)) {
11388
+ break;
11389
+ }
11390
+ [lowerBound, lowerBoundIndex] = [nextLowerBound, nextLowerBoundIndex];
11391
+ [upperBound, upperBoundIndex] = [nextUpperBound, nextUpperBoundIndex];
11392
+ indicesGroup.push(i);
11393
+ }
11394
+ indicesGroup.push(upperBoundIndex);
11395
+ indicesGroups.push(indicesGroup);
11396
+ } else {
11397
+ i++;
11398
+ }
11399
+ }
11400
+ return indicesGroups;
11401
+ };
11402
+ var isValidFractionalIndex = (index, predecessor, successor) => {
11403
+ if (!index) {
11404
+ return false;
11405
+ }
11406
+ try {
11407
+ validateOrderKey(index);
11408
+ } catch {
11409
+ return false;
11410
+ }
11411
+ if (predecessor && successor) {
11412
+ return predecessor < index && index < successor;
11413
+ }
11414
+ if (!predecessor && successor) {
11415
+ return index < successor;
11416
+ }
11417
+ if (predecessor && !successor) {
11418
+ return predecessor < index;
11419
+ }
11420
+ return !!index;
11421
+ };
11422
+ var generateIndices = (elements, indicesGroups) => {
11423
+ const elementsUpdates = /* @__PURE__ */ new Map();
11424
+ for (const indices of indicesGroups) {
11425
+ const lowerBoundIndex = indices.shift();
11426
+ const upperBoundIndex = indices.pop();
11427
+ const fractionalIndices = generateNKeysBetween(
11428
+ elements[lowerBoundIndex]?.index,
11429
+ elements[upperBoundIndex]?.index,
11430
+ indices.length
11431
+ );
11432
+ for (let i = 0; i < indices.length; i++) {
11433
+ const element = elements[indices[i]];
11434
+ elementsUpdates.set(element, {
11435
+ index: fractionalIndices[i]
11436
+ });
11437
+ }
11438
+ }
11439
+ return elementsUpdates;
11440
+ };
11441
+ var isOrderedElement = (element) => {
11442
+ if (element.index) {
11443
+ return true;
11444
+ }
11445
+ return false;
11446
+ };
11447
+
11448
+ // src/frame.ts
11449
+ var bindElementsToFramesAfterDuplication = (nextElements, origElements, origIdToDuplicateId) => {
11185
11450
  const nextElementMap = arrayToMap5(nextElements);
11186
11451
  for (const element of origElements) {
11187
11452
  if (element.frameId) {
@@ -11201,7 +11466,7 @@ function isElementIntersectingFrame(element, frame, elementsMap) {
11201
11466
  const elementLineSegments = getElementLineSegments(element, elementsMap);
11202
11467
  const intersecting = frameLineSegments.some(
11203
11468
  (frameLineSegment) => elementLineSegments.some(
11204
- (elementLineSegment) => doLineSegmentsIntersect(frameLineSegment, elementLineSegment)
11469
+ (elementLineSegment) => segmentsIntersectAt2(frameLineSegment, elementLineSegment)
11205
11470
  )
11206
11471
  );
11207
11472
  return intersecting;
@@ -11212,8 +11477,9 @@ var getElementsCompletelyInFrame = (elements, frame, elementsMap) => omitGroupsC
11212
11477
  (element) => !isFrameLikeElement(element) && !element.frameId || element.frameId === frame.id
11213
11478
  );
11214
11479
  var isElementContainingFrame = (element, frame, elementsMap) => {
11215
- return getElementsWithinSelection([frame], element, elementsMap).some(
11216
- (e) => e.id === frame.id
11480
+ return boundsContainBounds(
11481
+ getElementBounds(element, elementsMap),
11482
+ getElementBounds(frame, elementsMap)
11217
11483
  );
11218
11484
  };
11219
11485
  var getElementsIntersectingFrame = (elements, frame) => {
@@ -11236,9 +11502,9 @@ var elementOverlapsWithFrame = (element, frame, elementsMap) => {
11236
11502
  var isCursorInFrame = (cursorCoords, frame, elementsMap) => {
11237
11503
  const [fx1, fy1, fx2, fy2] = getElementAbsoluteCoords2(frame, elementsMap);
11238
11504
  return isPointWithinBounds2(
11239
- pointFrom11(fx1, fy1),
11240
- pointFrom11(cursorCoords.x, cursorCoords.y),
11241
- pointFrom11(fx2, fy2)
11505
+ pointFrom10(fx1, fy1),
11506
+ pointFrom10(cursorCoords.x, cursorCoords.y),
11507
+ pointFrom10(fx2, fy2)
11242
11508
  );
11243
11509
  };
11244
11510
  var groupsAreAtLeastIntersectingTheFrame = (elements, groupIds, frame) => {
@@ -11446,16 +11712,35 @@ var filterElementsEligibleAsFrameChildren = (elements, frame) => {
11446
11712
  }
11447
11713
  return eligibleElements;
11448
11714
  };
11449
- var addElementsToFrame = (allElements, elementsToAdd, frame, appState) => {
11450
- const elementsMap = arrayToMap5(allElements);
11451
- const currTargetFrameChildrenMap = /* @__PURE__ */ new Map();
11452
- for (const element of allElements.values()) {
11453
- if (element.frameId === frame.id) {
11454
- currTargetFrameChildrenMap.set(element.id, true);
11715
+ var getCommonFrameId = (elements) => {
11716
+ let commonFrameId;
11717
+ for (const element of elements) {
11718
+ if (isFrameLikeElement(element) || !element.frameId) {
11719
+ return null;
11720
+ }
11721
+ if (commonFrameId === void 0) {
11722
+ commonFrameId = element.frameId;
11723
+ } else if (commonFrameId !== element.frameId) {
11724
+ return null;
11725
+ }
11726
+ }
11727
+ return commonFrameId ?? null;
11728
+ };
11729
+ var getFrameChildrenInsertionIndex = (elements, frameId) => {
11730
+ for (let index = elements.length - 1; index >= 0; index--) {
11731
+ const element = elements[index];
11732
+ if (element.id === frameId) {
11733
+ return index;
11734
+ } else if (element.frameId === frameId) {
11735
+ return index + 1;
11455
11736
  }
11456
11737
  }
11457
- const suppliedElementsToAddSet = new Set(elementsToAdd.map((el) => el.id));
11458
- const finalElementsToAdd = [];
11738
+ return null;
11739
+ };
11740
+ var addElementsToFrame = (allElements, elementsToAdd, frame) => {
11741
+ const elementsMap = arrayToMap5(allElements);
11742
+ const commonFrameId = getCommonFrameId(elementsToAdd);
11743
+ const finalElementsToAdd = /* @__PURE__ */ new Set();
11459
11744
  const otherFrames = /* @__PURE__ */ new Set();
11460
11745
  for (const element of elementsToAdd) {
11461
11746
  if (isFrameLikeElement(element) && element.id !== frame.id) {
@@ -11469,23 +11754,41 @@ var addElementsToFrame = (allElements, elementsToAdd, frame, appState) => {
11469
11754
  if (isFrameLikeElement(element) || element.frameId && otherFrames.has(element.frameId)) {
11470
11755
  continue;
11471
11756
  }
11472
- if (element.frameId && appState.selectedElementIds[element.id] && appState.selectedElementIds[element.frameId]) {
11473
- continue;
11474
- }
11475
- if (!currTargetFrameChildrenMap.has(element.id)) {
11476
- finalElementsToAdd.push(element);
11477
- }
11757
+ finalElementsToAdd.add(element);
11478
11758
  const boundTextElement = getBoundTextElement(element, elementsMap);
11479
- if (boundTextElement && !suppliedElementsToAddSet.has(boundTextElement.id) && !currTargetFrameChildrenMap.has(boundTextElement.id)) {
11480
- finalElementsToAdd.push(boundTextElement);
11759
+ if (boundTextElement && !finalElementsToAdd.has(boundTextElement)) {
11760
+ finalElementsToAdd.add(boundTextElement);
11481
11761
  }
11482
11762
  }
11483
11763
  for (const element of finalElementsToAdd) {
11484
- mutateElement(element, elementsMap, {
11485
- frameId: frame.id
11486
- });
11764
+ if (element.frameId !== frame.id) {
11765
+ mutateElement(element, elementsMap, {
11766
+ frameId: frame.id
11767
+ });
11768
+ }
11487
11769
  }
11488
- return allElements;
11770
+ if (!finalElementsToAdd.size || // if all elements to add already belong to the frame, then we don't want to
11771
+ // reorder (case: we're dragging element children within the frame)
11772
+ commonFrameId === frame.id) {
11773
+ return allElements;
11774
+ }
11775
+ const otherElements = Array.from(allElements.values()).filter(
11776
+ (element) => !finalElementsToAdd.has(element)
11777
+ );
11778
+ const insertionIndex = getFrameChildrenInsertionIndex(
11779
+ otherElements,
11780
+ frame.id
11781
+ );
11782
+ if (insertionIndex === null) {
11783
+ return allElements;
11784
+ }
11785
+ const reorderedElements = [
11786
+ ...otherElements.slice(0, insertionIndex),
11787
+ ...finalElementsToAdd,
11788
+ ...otherElements.slice(insertionIndex)
11789
+ ];
11790
+ syncMovedIndices(reorderedElements, arrayToMap5([...finalElementsToAdd]));
11791
+ return Array.isArray(allElements) ? reorderedElements : new Map(reorderedElements.map((element) => [element.id, element]));
11489
11792
  };
11490
11793
  var removeElementsFromFrame = (elementsToRemove, elementsMap) => {
11491
11794
  const _elementsToRemove = /* @__PURE__ */ new Map();
@@ -11514,12 +11817,11 @@ var removeAllElementsFromFrame = (allElements, frame) => {
11514
11817
  removeElementsFromFrame(elementsInFrame, arrayToMap5(allElements));
11515
11818
  return allElements;
11516
11819
  };
11517
- var replaceAllElementsInFrame = (allElements, nextElementsInFrame, frame, app) => {
11820
+ var replaceAllElementsInFrame = (allElements, nextElementsInFrame, frame) => {
11518
11821
  return addElementsToFrame(
11519
11822
  removeAllElementsFromFrame(allElements, frame),
11520
11823
  nextElementsInFrame,
11521
- frame,
11522
- app.state
11824
+ frame
11523
11825
  ).slice();
11524
11826
  };
11525
11827
  var updateFrameMembershipOfSelectedElements = (allElements, appState, app) => {
@@ -11684,12 +11986,17 @@ var getDefaultFrameName = (element) => {
11684
11986
  var getFrameLikeTitle = (element) => {
11685
11987
  return element.name === null ? getDefaultFrameName(element) : element.name;
11686
11988
  };
11687
- var getElementsOverlappingFrame = (elements, frame) => {
11688
- return elementsOverlappingBBox({
11689
- elements,
11690
- bounds: frame,
11691
- type: "overlap"
11692
- }).filter((el) => !el.frameId || el.frameId === frame.id);
11989
+ var getElementsOverlappingFrame = (elements, frame, elementsMap) => {
11990
+ return elements.filter(
11991
+ (el) => (
11992
+ // exclude elements which are overlapping, but are in a different frame,
11993
+ // and thus invisible in target frame
11994
+ (!el.frameId || el.frameId === frame.id) && doBoundsIntersect(
11995
+ getElementBounds(el, elementsMap),
11996
+ getElementBounds(frame, elementsMap)
11997
+ )
11998
+ )
11999
+ );
11693
12000
  };
11694
12001
  var frameAndChildrenSelectedTogether = (selectedElements) => {
11695
12002
  const selectedElementsMap = arrayToMap5(selectedElements);
@@ -11878,7 +12185,10 @@ var drawElementOnCanvas = (element, rc, context, renderConfig) => {
11878
12185
  const shapes = ShapeCache.generateElementShape(element, renderConfig);
11879
12186
  for (const shape of shapes) {
11880
12187
  if (typeof shape === "string") {
11881
- context.fillStyle = renderConfig.theme === THEME.DARK ? applyDarkModeFilter(element.strokeColor) : element.strokeColor;
12188
+ context.fillStyle = applyDarkModeFilter(
12189
+ element.strokeColor,
12190
+ renderConfig.theme === THEME.DARK
12191
+ );
11882
12192
  context.fill(new Path2D(shape));
11883
12193
  } else {
11884
12194
  rc.draw(shape);
@@ -11986,7 +12296,10 @@ var drawElementOnCanvas = (element, rc, context, renderConfig) => {
11986
12296
  context.canvas.setAttribute("dir", rtl ? "rtl" : "ltr");
11987
12297
  context.save();
11988
12298
  context.font = getFontString3(element);
11989
- context.fillStyle = renderConfig.theme === THEME.DARK ? applyDarkModeFilter(element.strokeColor) : element.strokeColor;
12299
+ context.fillStyle = applyDarkModeFilter(
12300
+ element.strokeColor,
12301
+ renderConfig.theme === THEME.DARK
12302
+ );
11990
12303
  context.textAlign = element.textAlign;
11991
12304
  const lines = element.text.replace(/\r\n?/g, "\n").split("\n");
11992
12305
  const horizontalOffset = element.textAlign === "center" ? element.width / 2 : element.textAlign === "right" ? element.width : 0;
@@ -12134,7 +12447,10 @@ var renderElement = (element, elementsMap, allElementsMap, rc, context, renderCo
12134
12447
  );
12135
12448
  context.fillStyle = "rgba(0, 0, 200, 0.04)";
12136
12449
  context.lineWidth = FRAME_STYLE.strokeWidth / appState.zoom.value;
12137
- context.strokeStyle = appState.theme === THEME.DARK ? applyDarkModeFilter(FRAME_STYLE.strokeColor) : FRAME_STYLE.strokeColor;
12450
+ context.strokeStyle = applyDarkModeFilter(
12451
+ FRAME_STYLE.strokeColor,
12452
+ appState.theme === THEME.DARK
12453
+ );
12138
12454
  if (isMagicFrameElement(element)) {
12139
12455
  context.strokeStyle = appState.theme === THEME.LIGHT ? "#7affd7" : applyDarkModeFilter("#1d8264");
12140
12456
  }
@@ -12337,7 +12653,7 @@ function getFreedrawOutlineAsSegments(element, points, elementsMap) {
12337
12653
  },
12338
12654
  elementsMap
12339
12655
  );
12340
- const center = pointFrom12(
12656
+ const center = pointFrom11(
12341
12657
  (bounds[0] + bounds[2]) / 2,
12342
12658
  (bounds[1] + bounds[3]) / 2
12343
12659
  );
@@ -12347,8 +12663,8 @@ function getFreedrawOutlineAsSegments(element, points, elementsMap) {
12347
12663
  acc.push(
12348
12664
  lineSegment5(
12349
12665
  acc[acc.length - 1][1],
12350
- pointRotateRads11(
12351
- pointFrom12(curr[0] + element.x, curr[1] + element.y),
12666
+ pointRotateRads10(
12667
+ pointFrom11(curr[0] + element.x, curr[1] + element.y),
12352
12668
  center,
12353
12669
  element.angle
12354
12670
  )
@@ -12358,16 +12674,16 @@ function getFreedrawOutlineAsSegments(element, points, elementsMap) {
12358
12674
  },
12359
12675
  [
12360
12676
  lineSegment5(
12361
- pointRotateRads11(
12362
- pointFrom12(
12677
+ pointRotateRads10(
12678
+ pointFrom11(
12363
12679
  points[0][0] + element.x,
12364
12680
  points[0][1] + element.y
12365
12681
  ),
12366
12682
  center,
12367
12683
  element.angle
12368
12684
  ),
12369
- pointRotateRads11(
12370
- pointFrom12(
12685
+ pointRotateRads10(
12686
+ pointFrom11(
12371
12687
  points[1][0] + element.x,
12372
12688
  points[1][1] + element.y
12373
12689
  ),
@@ -12379,16 +12695,6 @@ function getFreedrawOutlineAsSegments(element, points, elementsMap) {
12379
12695
  );
12380
12696
  }
12381
12697
 
12382
- // src/comparisons.ts
12383
- init_define_import_meta_env();
12384
- var hasBackground = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "ellipse" || type === "diamond" || type === "line" || type === "freedraw";
12385
- var hasStrokeColor = (type) => type === "rectangle" || type === "ellipse" || type === "diamond" || type === "freedraw" || type === "arrow" || type === "line" || type === "text" || type === "embeddable";
12386
- var hasStrokeWidth = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "ellipse" || type === "diamond" || type === "freedraw" || type === "arrow" || type === "line";
12387
- var hasStrokeStyle = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "ellipse" || type === "diamond" || type === "arrow" || type === "line";
12388
- var canChangeRoundness = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "line" || type === "diamond" || type === "image";
12389
- var toolIsArrow = (type) => type === "arrow";
12390
- var canHaveArrowheads = (type) => type === "arrow";
12391
-
12392
12698
  // src/shape.ts
12393
12699
  var ShapeCache = class _ShapeCache {
12394
12700
  static rg = new RoughGenerator();
@@ -12472,7 +12778,7 @@ var generateRoughOptions = (element, continuousPath = false, isDarkMode = false)
12472
12778
  fillWeight: element.strokeWidth / 2,
12473
12779
  hachureGap: element.strokeWidth * 4,
12474
12780
  roughness: adjustRoughness(element),
12475
- stroke: isDarkMode ? applyDarkModeFilter2(element.strokeColor) : element.strokeColor,
12781
+ stroke: applyDarkModeFilter2(element.strokeColor, isDarkMode),
12476
12782
  preserveVertices: continuousPath || element.roughness < ROUGHNESS.cartoonist
12477
12783
  };
12478
12784
  switch (element.type) {
@@ -12482,7 +12788,7 @@ var generateRoughOptions = (element, continuousPath = false, isDarkMode = false)
12482
12788
  case "diamond":
12483
12789
  case "ellipse": {
12484
12790
  options.fillStyle = element.fillStyle;
12485
- options.fill = isTransparent3(element.backgroundColor) ? void 0 : isDarkMode ? applyDarkModeFilter2(element.backgroundColor) : element.backgroundColor;
12791
+ options.fill = isTransparent3(element.backgroundColor) ? void 0 : applyDarkModeFilter2(element.backgroundColor, isDarkMode);
12486
12792
  if (element.type === "ellipse") {
12487
12793
  options.curveFitting = 1;
12488
12794
  }
@@ -12492,7 +12798,7 @@ var generateRoughOptions = (element, continuousPath = false, isDarkMode = false)
12492
12798
  case "freedraw": {
12493
12799
  if (isPathALoop(element.points)) {
12494
12800
  options.fillStyle = element.fillStyle;
12495
- options.fill = element.backgroundColor === "transparent" ? void 0 : isDarkMode ? applyDarkModeFilter2(element.backgroundColor) : element.backgroundColor;
12801
+ options.fill = element.backgroundColor === "transparent" ? void 0 : applyDarkModeFilter2(element.backgroundColor, isDarkMode);
12496
12802
  }
12497
12803
  return options;
12498
12804
  }
@@ -12520,44 +12826,90 @@ var modifyIframeLikeForRoughOptions = (element, isExporting, embedsValidationSta
12520
12826
  }
12521
12827
  return element;
12522
12828
  };
12523
- var getArrowheadShapes = (element, shape, position, arrowhead, generator, options, canvasBackgroundColor, isDarkMode) => {
12524
- const arrowheadPoints = getArrowheadPoints(
12525
- element,
12526
- shape,
12527
- position,
12528
- arrowhead
12529
- );
12829
+ var generateArrowheadCardinalityOne = (generator, arrowheadPoints, lineOptions) => {
12530
12830
  if (arrowheadPoints === null) {
12531
12831
  return [];
12532
12832
  }
12533
- const generateCrowfootOne = (arrowheadPoints2, options2) => {
12534
- if (arrowheadPoints2 === null) {
12535
- return [];
12536
- }
12537
- const [, , x3, y3, x4, y4] = arrowheadPoints2;
12538
- return [generator.line(x3, y3, x4, y4, options2)];
12833
+ const [, , x3, y3, x4, y4] = arrowheadPoints;
12834
+ return [generator.line(x3, y3, x4, y4, lineOptions)];
12835
+ };
12836
+ var generateArrowheadLinesToTip = (generator, arrowheadPoints, lineOptions) => {
12837
+ if (arrowheadPoints === null) {
12838
+ return [];
12839
+ }
12840
+ const [x2, y2, x3, y3, x4, y4] = arrowheadPoints;
12841
+ return [
12842
+ generator.line(x3, y3, x2, y2, lineOptions),
12843
+ generator.line(x4, y4, x2, y2, lineOptions)
12844
+ ];
12845
+ };
12846
+ var getArrowheadLineOptions = (element, options) => {
12847
+ const lineOptions = { ...options };
12848
+ if (element.strokeStyle === "dotted") {
12849
+ const dash = getDashArrayDotted(element.strokeWidth - 1);
12850
+ lineOptions.strokeLineDash = [dash[0], dash[1] - 1];
12851
+ } else {
12852
+ delete lineOptions.strokeLineDash;
12853
+ }
12854
+ lineOptions.roughness = Math.min(1, lineOptions.roughness || 0);
12855
+ return lineOptions;
12856
+ };
12857
+ var generateArrowheadOutlineCircle = (generator, options, strokeColor, arrowheadPoints, fill, diameterScale = 1) => {
12858
+ if (arrowheadPoints === null) {
12859
+ return [];
12860
+ }
12861
+ const [x, y, diameter] = arrowheadPoints;
12862
+ const circleOptions = {
12863
+ ...options,
12864
+ fill,
12865
+ fillStyle: "solid",
12866
+ stroke: strokeColor,
12867
+ roughness: Math.min(0.5, options.roughness || 0)
12539
12868
  };
12540
- const strokeColor = isDarkMode ? applyDarkModeFilter2(element.strokeColor) : element.strokeColor;
12869
+ delete circleOptions.strokeLineDash;
12870
+ return [generator.circle(x, y, diameter * diameterScale, circleOptions)];
12871
+ };
12872
+ var getArrowheadShapes = (element, shape, position, arrowhead, generator, options, canvasBackgroundColor, isDarkMode) => {
12873
+ if (arrowhead === null) {
12874
+ return [];
12875
+ }
12876
+ const strokeColor = applyDarkModeFilter2(element.strokeColor, isDarkMode);
12877
+ const backgroundFillColor = applyDarkModeFilter2(
12878
+ canvasBackgroundColor,
12879
+ isDarkMode
12880
+ );
12881
+ const cardinalityOneOrManyOffset = -0.25;
12882
+ const cardinalityZeroCircleScale = 0.8;
12541
12883
  switch (arrowhead) {
12542
- case "dot":
12543
12884
  case "circle":
12544
12885
  case "circle_outline": {
12545
- const [x, y, diameter] = arrowheadPoints;
12546
- delete options.strokeLineDash;
12547
- return [
12548
- generator.circle(x, y, diameter, {
12549
- ...options,
12550
- fill: arrowhead === "circle_outline" ? canvasBackgroundColor : strokeColor,
12551
- fillStyle: "solid",
12552
- stroke: strokeColor,
12553
- roughness: Math.min(0.5, options.roughness || 0)
12554
- })
12555
- ];
12886
+ return generateArrowheadOutlineCircle(
12887
+ generator,
12888
+ options,
12889
+ strokeColor,
12890
+ getArrowheadPoints(element, shape, position, arrowhead),
12891
+ arrowhead === "circle_outline" ? backgroundFillColor : strokeColor
12892
+ );
12556
12893
  }
12557
12894
  case "triangle":
12558
12895
  case "triangle_outline": {
12896
+ const arrowheadPoints = getArrowheadPoints(
12897
+ element,
12898
+ shape,
12899
+ position,
12900
+ arrowhead
12901
+ );
12902
+ if (arrowheadPoints === null) {
12903
+ return [];
12904
+ }
12559
12905
  const [x, y, x2, y2, x3, y3] = arrowheadPoints;
12560
- delete options.strokeLineDash;
12906
+ const triangleOptions = {
12907
+ ...options,
12908
+ fill: arrowhead === "triangle_outline" ? backgroundFillColor : strokeColor,
12909
+ fillStyle: "solid",
12910
+ roughness: Math.min(1, options.roughness || 0)
12911
+ };
12912
+ delete triangleOptions.strokeLineDash;
12561
12913
  return [
12562
12914
  generator.polygon(
12563
12915
  [
@@ -12566,19 +12918,29 @@ var getArrowheadShapes = (element, shape, position, arrowhead, generator, option
12566
12918
  [x3, y3],
12567
12919
  [x, y]
12568
12920
  ],
12569
- {
12570
- ...options,
12571
- fill: arrowhead === "triangle_outline" ? canvasBackgroundColor : strokeColor,
12572
- fillStyle: "solid",
12573
- roughness: Math.min(1, options.roughness || 0)
12574
- }
12921
+ triangleOptions
12575
12922
  )
12576
12923
  ];
12577
12924
  }
12578
12925
  case "diamond":
12579
12926
  case "diamond_outline": {
12927
+ const arrowheadPoints = getArrowheadPoints(
12928
+ element,
12929
+ shape,
12930
+ position,
12931
+ arrowhead
12932
+ );
12933
+ if (arrowheadPoints === null) {
12934
+ return [];
12935
+ }
12580
12936
  const [x, y, x2, y2, x3, y3, x4, y4] = arrowheadPoints;
12581
- delete options.strokeLineDash;
12937
+ const diamondOptions = {
12938
+ ...options,
12939
+ fill: arrowhead === "diamond_outline" ? backgroundFillColor : strokeColor,
12940
+ fillStyle: "solid",
12941
+ roughness: Math.min(1, options.roughness || 0)
12942
+ };
12943
+ delete diamondOptions.strokeLineDash;
12582
12944
  return [
12583
12945
  generator.polygon(
12584
12946
  [
@@ -12588,42 +12950,106 @@ var getArrowheadShapes = (element, shape, position, arrowhead, generator, option
12588
12950
  [x4, y4],
12589
12951
  [x, y]
12590
12952
  ],
12591
- {
12592
- ...options,
12593
- fill: arrowhead === "diamond_outline" ? canvasBackgroundColor : strokeColor,
12594
- fillStyle: "solid",
12595
- roughness: Math.min(1, options.roughness || 0)
12596
- }
12953
+ diamondOptions
12954
+ )
12955
+ ];
12956
+ }
12957
+ case "cardinality_one":
12958
+ return generateArrowheadCardinalityOne(
12959
+ generator,
12960
+ getArrowheadPoints(element, shape, position, arrowhead),
12961
+ getArrowheadLineOptions(element, options)
12962
+ );
12963
+ case "cardinality_many":
12964
+ return generateArrowheadLinesToTip(
12965
+ generator,
12966
+ getArrowheadPoints(element, shape, position, arrowhead),
12967
+ getArrowheadLineOptions(element, options)
12968
+ );
12969
+ case "cardinality_one_or_many": {
12970
+ const lineOptions = getArrowheadLineOptions(element, options);
12971
+ return [
12972
+ ...generateArrowheadLinesToTip(
12973
+ generator,
12974
+ getArrowheadPoints(element, shape, position, "cardinality_many"),
12975
+ lineOptions
12976
+ ),
12977
+ ...generateArrowheadCardinalityOne(
12978
+ generator,
12979
+ getArrowheadPoints(
12980
+ element,
12981
+ shape,
12982
+ position,
12983
+ "cardinality_one",
12984
+ cardinalityOneOrManyOffset
12985
+ ),
12986
+ lineOptions
12987
+ )
12988
+ ];
12989
+ }
12990
+ case "cardinality_exactly_one": {
12991
+ const lineOptions = getArrowheadLineOptions(element, options);
12992
+ return [
12993
+ ...generateArrowheadCardinalityOne(
12994
+ generator,
12995
+ getArrowheadPoints(element, shape, position, "cardinality_one", -0.5),
12996
+ lineOptions
12997
+ ),
12998
+ ...generateArrowheadCardinalityOne(
12999
+ generator,
13000
+ getArrowheadPoints(element, shape, position, "cardinality_one"),
13001
+ lineOptions
13002
+ )
13003
+ ];
13004
+ }
13005
+ case "cardinality_zero_or_one": {
13006
+ const lineOptions = getArrowheadLineOptions(element, options);
13007
+ return [
13008
+ ...generateArrowheadOutlineCircle(
13009
+ generator,
13010
+ options,
13011
+ strokeColor,
13012
+ getArrowheadPoints(element, shape, position, "circle_outline", 1.5),
13013
+ backgroundFillColor,
13014
+ cardinalityZeroCircleScale
13015
+ ),
13016
+ ...generateArrowheadCardinalityOne(
13017
+ generator,
13018
+ getArrowheadPoints(element, shape, position, "cardinality_one", -0.5),
13019
+ lineOptions
13020
+ )
13021
+ ];
13022
+ }
13023
+ case "cardinality_zero_or_many": {
13024
+ const lineOptions = getArrowheadLineOptions(element, options);
13025
+ return [
13026
+ ...generateArrowheadLinesToTip(
13027
+ generator,
13028
+ getArrowheadPoints(element, shape, position, "cardinality_many"),
13029
+ lineOptions
13030
+ ),
13031
+ ...generateArrowheadOutlineCircle(
13032
+ generator,
13033
+ options,
13034
+ strokeColor,
13035
+ getArrowheadPoints(element, shape, position, "circle_outline", 1.5),
13036
+ backgroundFillColor,
13037
+ cardinalityZeroCircleScale
12597
13038
  )
12598
13039
  ];
12599
13040
  }
12600
- case "crowfoot_one":
12601
- return generateCrowfootOne(arrowheadPoints, options);
12602
13041
  case "bar":
12603
13042
  case "arrow":
12604
- case "crowfoot_many":
12605
- case "crowfoot_one_or_many":
12606
13043
  default: {
12607
- const [x2, y2, x3, y3, x4, y4] = arrowheadPoints;
12608
- if (element.strokeStyle === "dotted") {
12609
- const dash = getDashArrayDotted(element.strokeWidth - 1);
12610
- options.strokeLineDash = [dash[0], dash[1] - 1];
12611
- } else {
12612
- delete options.strokeLineDash;
12613
- }
12614
- options.roughness = Math.min(1, options.roughness || 0);
12615
- return [
12616
- generator.line(x3, y3, x2, y2, options),
12617
- generator.line(x4, y4, x2, y2, options),
12618
- ...arrowhead === "crowfoot_one_or_many" ? generateCrowfootOne(
12619
- getArrowheadPoints(element, shape, position, "crowfoot_one"),
12620
- options
12621
- ) : []
12622
- ];
13044
+ return generateArrowheadLinesToTip(
13045
+ generator,
13046
+ getArrowheadPoints(element, shape, position, arrowhead),
13047
+ getArrowheadLineOptions(element, options)
13048
+ );
12623
13049
  }
12624
13050
  }
12625
13051
  };
12626
- var generateLinearCollisionShape = (element) => {
13052
+ var generateLinearCollisionShape = (element, elementsMap) => {
12627
13053
  const generator = new RoughGenerator();
12628
13054
  const options = {
12629
13055
  seed: element.seed,
@@ -12632,43 +13058,30 @@ var generateLinearCollisionShape = (element) => {
12632
13058
  roughness: 0,
12633
13059
  preserveVertices: true
12634
13060
  };
12635
- const center = getCenterForBounds(
12636
- // Need a non-rotated center point
12637
- element.points.reduce(
12638
- (acc, point) => {
12639
- return [
12640
- Math.min(element.x + point[0], acc[0]),
12641
- Math.min(element.y + point[1], acc[1]),
12642
- Math.max(element.x + point[0], acc[2]),
12643
- Math.max(element.y + point[1], acc[3])
12644
- ];
12645
- },
12646
- [Infinity, Infinity, -Infinity, -Infinity]
12647
- )
12648
- );
13061
+ const center = elementCenterPoint(element, elementsMap);
12649
13062
  switch (element.type) {
12650
13063
  case "line":
12651
13064
  case "arrow": {
12652
- const points = element.points.length ? element.points : [pointFrom13(0, 0)];
13065
+ const points = element.points.length ? element.points : [pointFrom12(0, 0)];
12653
13066
  if (isElbowArrow(element)) {
12654
13067
  return generator.path(generateElbowArrowShape(points, 16), options).sets[0].ops;
12655
13068
  } else if (!element.roundness) {
12656
13069
  return points.map((point, idx) => {
12657
- const p = pointRotateRads12(
12658
- pointFrom13(element.x + point[0], element.y + point[1]),
13070
+ const p = pointRotateRads11(
13071
+ pointFrom12(element.x + point[0], element.y + point[1]),
12659
13072
  center,
12660
13073
  element.angle
12661
13074
  );
12662
13075
  return {
12663
13076
  op: idx === 0 ? "move" : "lineTo",
12664
- data: pointFrom13(p[0] - element.x, p[1] - element.y)
13077
+ data: pointFrom12(p[0] - element.x, p[1] - element.y)
12665
13078
  };
12666
13079
  });
12667
13080
  }
12668
13081
  return generator.curve(points, options).sets[0].ops.slice(0, element.points.length).map((op, i) => {
12669
13082
  if (i === 0) {
12670
- const p = pointRotateRads12(
12671
- pointFrom13(
13083
+ const p = pointRotateRads11(
13084
+ pointFrom12(
12672
13085
  element.x + op.data[0],
12673
13086
  element.y + op.data[1]
12674
13087
  ),
@@ -12677,30 +13090,30 @@ var generateLinearCollisionShape = (element) => {
12677
13090
  );
12678
13091
  return {
12679
13092
  op: "move",
12680
- data: pointFrom13(p[0] - element.x, p[1] - element.y)
13093
+ data: pointFrom12(p[0] - element.x, p[1] - element.y)
12681
13094
  };
12682
13095
  }
12683
13096
  return {
12684
13097
  op: "bcurveTo",
12685
13098
  data: [
12686
- pointRotateRads12(
12687
- pointFrom13(
13099
+ pointRotateRads11(
13100
+ pointFrom12(
12688
13101
  element.x + op.data[0],
12689
13102
  element.y + op.data[1]
12690
13103
  ),
12691
13104
  center,
12692
13105
  element.angle
12693
13106
  ),
12694
- pointRotateRads12(
12695
- pointFrom13(
13107
+ pointRotateRads11(
13108
+ pointFrom12(
12696
13109
  element.x + op.data[2],
12697
13110
  element.y + op.data[3]
12698
13111
  ),
12699
13112
  center,
12700
13113
  element.angle
12701
13114
  ),
12702
- pointRotateRads12(
12703
- pointFrom13(
13115
+ pointRotateRads11(
13116
+ pointFrom12(
12704
13117
  element.x + op.data[4],
12705
13118
  element.y + op.data[5]
12706
13119
  ),
@@ -12708,7 +13121,7 @@ var generateLinearCollisionShape = (element) => {
12708
13121
  element.angle
12709
13122
  )
12710
13123
  ].map(
12711
- (p) => pointFrom13(p[0] - element.x, p[1] - element.y)
13124
+ (p) => pointFrom12(p[0] - element.x, p[1] - element.y)
12712
13125
  ).flat()
12713
13126
  };
12714
13127
  });
@@ -12723,8 +13136,8 @@ var generateLinearCollisionShape = (element) => {
12723
13136
  );
12724
13137
  return generator.curve(simplifiedPoints, options).sets[0].ops.slice(0, element.points.length).map((op, i) => {
12725
13138
  if (i === 0) {
12726
- const p = pointRotateRads12(
12727
- pointFrom13(
13139
+ const p = pointRotateRads11(
13140
+ pointFrom12(
12728
13141
  element.x + op.data[0],
12729
13142
  element.y + op.data[1]
12730
13143
  ),
@@ -12733,30 +13146,30 @@ var generateLinearCollisionShape = (element) => {
12733
13146
  );
12734
13147
  return {
12735
13148
  op: "move",
12736
- data: pointFrom13(p[0] - element.x, p[1] - element.y)
13149
+ data: pointFrom12(p[0] - element.x, p[1] - element.y)
12737
13150
  };
12738
13151
  }
12739
13152
  return {
12740
13153
  op: "bcurveTo",
12741
13154
  data: [
12742
- pointRotateRads12(
12743
- pointFrom13(
13155
+ pointRotateRads11(
13156
+ pointFrom12(
12744
13157
  element.x + op.data[0],
12745
13158
  element.y + op.data[1]
12746
13159
  ),
12747
13160
  center,
12748
13161
  element.angle
12749
13162
  ),
12750
- pointRotateRads12(
12751
- pointFrom13(
13163
+ pointRotateRads11(
13164
+ pointFrom12(
12752
13165
  element.x + op.data[2],
12753
13166
  element.y + op.data[3]
12754
13167
  ),
12755
13168
  center,
12756
13169
  element.angle
12757
13170
  ),
12758
- pointRotateRads12(
12759
- pointFrom13(
13171
+ pointRotateRads11(
13172
+ pointFrom12(
12760
13173
  element.x + op.data[4],
12761
13174
  element.y + op.data[5]
12762
13175
  ),
@@ -12764,7 +13177,7 @@ var generateLinearCollisionShape = (element) => {
12764
13177
  element.angle
12765
13178
  )
12766
13179
  ].map(
12767
- (p) => pointFrom13(p[0] - element.x, p[1] - element.y)
13180
+ (p) => pointFrom12(p[0] - element.x, p[1] - element.y)
12768
13181
  ).flat()
12769
13182
  };
12770
13183
  });
@@ -12865,7 +13278,7 @@ var _generateElementShape = (element, generator, {
12865
13278
  case "arrow": {
12866
13279
  let shape;
12867
13280
  const options = generateRoughOptions(element, false, isDarkMode);
12868
- const points = element.points.length ? element.points : [pointFrom13(0, 0)];
13281
+ const points = element.points.length ? element.points : [pointFrom12(0, 0)];
12869
13282
  if (isElbowArrow(element)) {
12870
13283
  if (!points.every(
12871
13284
  (point) => Math.abs(point[0]) <= 1e6 && Math.abs(point[1]) <= 1e6
@@ -13029,14 +13442,14 @@ var getElementShape = (element, elementsMap) => {
13029
13442
  return shouldTestInside(element) ? getClosedCurveShape(
13030
13443
  element,
13031
13444
  roughShape,
13032
- pointFrom13(element.x, element.y),
13445
+ pointFrom12(element.x, element.y),
13033
13446
  element.angle,
13034
- pointFrom13(cx, cy)
13447
+ pointFrom12(cx, cy)
13035
13448
  ) : getCurveShape(
13036
13449
  roughShape,
13037
- pointFrom13(element.x, element.y),
13450
+ pointFrom12(element.x, element.y),
13038
13451
  element.angle,
13039
- pointFrom13(cx, cy)
13452
+ pointFrom12(cx, cy)
13040
13453
  );
13041
13454
  }
13042
13455
  case "ellipse":
@@ -13045,7 +13458,7 @@ var getElementShape = (element, elementsMap) => {
13045
13458
  const [, , , , cx, cy] = getElementAbsoluteCoords2(element, elementsMap);
13046
13459
  return getFreedrawShape(
13047
13460
  element,
13048
- pointFrom13(cx, cy),
13461
+ pointFrom12(cx, cy),
13049
13462
  shouldTestInside(element)
13050
13463
  );
13051
13464
  }
@@ -13064,9 +13477,9 @@ var toggleLinePolygonState = (element, nextPolygonState) => {
13064
13477
  firstPoint[1] - lastPoint[1]
13065
13478
  );
13066
13479
  if (distance3 > LINE_POLYGON_POINT_MERGE_DISTANCE || updatedPoints.length < 4) {
13067
- updatedPoints.push(pointFrom13(firstPoint[0], firstPoint[1]));
13480
+ updatedPoints.push(pointFrom12(firstPoint[0], firstPoint[1]));
13068
13481
  } else {
13069
- updatedPoints[updatedPoints.length - 1] = pointFrom13(
13482
+ updatedPoints[updatedPoints.length - 1] = pointFrom12(
13070
13483
  firstPoint[0],
13071
13484
  firstPoint[1]
13072
13485
  );
@@ -13159,9 +13572,9 @@ var ElementBounds = class _ElementBounds {
13159
13572
  if (isFreeDrawElement(element)) {
13160
13573
  const [minX, minY, maxX, maxY] = getBoundsFromPoints(
13161
13574
  element.points.map(
13162
- ([x, y]) => pointRotateRads13(
13163
- pointFrom14(x, y),
13164
- pointFrom14(cx - element.x, cy - element.y),
13575
+ ([x, y]) => pointRotateRads12(
13576
+ pointFrom13(x, y),
13577
+ pointFrom13(cx - element.x, cy - element.y),
13165
13578
  element.angle
13166
13579
  )
13167
13580
  )
@@ -13175,24 +13588,24 @@ var ElementBounds = class _ElementBounds {
13175
13588
  } else if (isLinearElement(element)) {
13176
13589
  bounds = getLinearElementRotatedBounds(element, cx, cy, elementsMap);
13177
13590
  } else if (element.type === "diamond") {
13178
- const [x11, y11] = pointRotateRads13(
13179
- pointFrom14(cx, y1),
13180
- pointFrom14(cx, cy),
13591
+ const [x11, y11] = pointRotateRads12(
13592
+ pointFrom13(cx, y1),
13593
+ pointFrom13(cx, cy),
13181
13594
  element.angle
13182
13595
  );
13183
- const [x12, y12] = pointRotateRads13(
13184
- pointFrom14(cx, y2),
13185
- pointFrom14(cx, cy),
13596
+ const [x12, y12] = pointRotateRads12(
13597
+ pointFrom13(cx, y2),
13598
+ pointFrom13(cx, cy),
13186
13599
  element.angle
13187
13600
  );
13188
- const [x22, y22] = pointRotateRads13(
13189
- pointFrom14(x1, cy),
13190
- pointFrom14(cx, cy),
13601
+ const [x22, y22] = pointRotateRads12(
13602
+ pointFrom13(x1, cy),
13603
+ pointFrom13(cx, cy),
13191
13604
  element.angle
13192
13605
  );
13193
- const [x21, y21] = pointRotateRads13(
13194
- pointFrom14(x2, cy),
13195
- pointFrom14(cx, cy),
13606
+ const [x21, y21] = pointRotateRads12(
13607
+ pointFrom13(x2, cy),
13608
+ pointFrom13(cx, cy),
13196
13609
  element.angle
13197
13610
  );
13198
13611
  const minX = Math.min(x11, x12, x22, x21);
@@ -13209,24 +13622,24 @@ var ElementBounds = class _ElementBounds {
13209
13622
  const hh = Math.hypot(h * cos, w * sin);
13210
13623
  bounds = [cx - ww, cy - hh, cx + ww, cy + hh];
13211
13624
  } else {
13212
- const [x11, y11] = pointRotateRads13(
13213
- pointFrom14(x1, y1),
13214
- pointFrom14(cx, cy),
13625
+ const [x11, y11] = pointRotateRads12(
13626
+ pointFrom13(x1, y1),
13627
+ pointFrom13(cx, cy),
13215
13628
  element.angle
13216
13629
  );
13217
- const [x12, y12] = pointRotateRads13(
13218
- pointFrom14(x1, y2),
13219
- pointFrom14(cx, cy),
13630
+ const [x12, y12] = pointRotateRads12(
13631
+ pointFrom13(x1, y2),
13632
+ pointFrom13(cx, cy),
13220
13633
  element.angle
13221
13634
  );
13222
- const [x22, y22] = pointRotateRads13(
13223
- pointFrom14(x2, y2),
13224
- pointFrom14(cx, cy),
13635
+ const [x22, y22] = pointRotateRads12(
13636
+ pointFrom13(x2, y2),
13637
+ pointFrom13(cx, cy),
13225
13638
  element.angle
13226
13639
  );
13227
- const [x21, y21] = pointRotateRads13(
13228
- pointFrom14(x2, y1),
13229
- pointFrom14(cx, cy),
13640
+ const [x21, y21] = pointRotateRads12(
13641
+ pointFrom13(x2, y1),
13642
+ pointFrom13(cx, cy),
13230
13643
  element.angle
13231
13644
  );
13232
13645
  const minX = Math.min(x11, x12, x22, x21);
@@ -13280,7 +13693,7 @@ var getElementLineSegments = (element, elementsMap) => {
13280
13693
  element,
13281
13694
  elementsMap
13282
13695
  );
13283
- const center = pointFrom14(cx, cy);
13696
+ const center = pointFrom13(cx, cy);
13284
13697
  if (shape.type === "polycurve") {
13285
13698
  const curves = shape.data;
13286
13699
  const pointsOnCurves = curves.map(
@@ -13293,8 +13706,8 @@ var getElementLineSegments = (element, elementsMap) => {
13293
13706
  while (i < points.length - 1) {
13294
13707
  segments.push(
13295
13708
  lineSegment6(
13296
- pointFrom14(points[i][0], points[i][1]),
13297
- pointFrom14(points[i + 1][0], points[i + 1][1])
13709
+ pointFrom13(points[i][0], points[i][1]),
13710
+ pointFrom13(points[i + 1][0], points[i + 1][1])
13298
13711
  )
13299
13712
  );
13300
13713
  i++;
@@ -13306,8 +13719,8 @@ var getElementLineSegments = (element, elementsMap) => {
13306
13719
  while (i < points.length - 1) {
13307
13720
  segments.push(
13308
13721
  lineSegment6(
13309
- pointFrom14(points[i][0], points[i][1]),
13310
- pointFrom14(points[i + 1][0], points[i + 1][1])
13722
+ pointFrom13(points[i][0], points[i][1]),
13723
+ pointFrom13(points[i + 1][0], points[i + 1][1])
13311
13724
  )
13312
13725
  );
13313
13726
  i++;
@@ -13331,10 +13744,10 @@ var getElementLineSegments = (element, elementsMap) => {
13331
13744
  const container = getContainerElement(element, elementsMap);
13332
13745
  if (container && isLinearElement(container)) {
13333
13746
  const segments2 = [
13334
- lineSegment6(pointFrom14(x1, y1), pointFrom14(x2, y1)),
13335
- lineSegment6(pointFrom14(x2, y1), pointFrom14(x2, y2)),
13336
- lineSegment6(pointFrom14(x2, y2), pointFrom14(x1, y2)),
13337
- lineSegment6(pointFrom14(x1, y2), pointFrom14(x1, y1))
13747
+ lineSegment6(pointFrom13(x1, y1), pointFrom13(x2, y1)),
13748
+ lineSegment6(pointFrom13(x2, y1), pointFrom13(x2, y2)),
13749
+ lineSegment6(pointFrom13(x2, y2), pointFrom13(x1, y2)),
13750
+ lineSegment6(pointFrom13(x1, y2), pointFrom13(x1, y1))
13338
13751
  ];
13339
13752
  return segments2;
13340
13753
  }
@@ -13357,7 +13770,7 @@ var getElementLineSegments = (element, elementsMap) => {
13357
13770
  [cx, y2],
13358
13771
  [x1, cy],
13359
13772
  [x2, cy]
13360
- ].map((point) => pointRotateRads13(point, center, element.angle));
13773
+ ].map((point) => pointRotateRads12(point, center, element.angle));
13361
13774
  return [
13362
13775
  lineSegment6(nw, ne),
13363
13776
  lineSegment6(sw, se2),
@@ -13375,8 +13788,8 @@ var _isRectanguloidElement = (element) => {
13375
13788
  var getRotatedSides = (sides, center, angle) => {
13376
13789
  return sides.map((side) => {
13377
13790
  return lineSegment6(
13378
- pointRotateRads13(side[0], center, angle),
13379
- pointRotateRads13(side[1], center, angle)
13791
+ pointRotateRads12(side[0], center, angle),
13792
+ pointRotateRads12(side[1], center, angle)
13380
13793
  );
13381
13794
  });
13382
13795
  };
@@ -13387,13 +13800,13 @@ var getSegmentsOnCurve = (curve4, center, angle) => {
13387
13800
  while (i < points.length - 1) {
13388
13801
  segments.push(
13389
13802
  lineSegment6(
13390
- pointRotateRads13(
13391
- pointFrom14(points[i][0], points[i][1]),
13803
+ pointRotateRads12(
13804
+ pointFrom13(points[i][0], points[i][1]),
13392
13805
  center,
13393
13806
  angle
13394
13807
  ),
13395
- pointRotateRads13(
13396
- pointFrom14(points[i + 1][0], points[i + 1][1]),
13808
+ pointRotateRads12(
13809
+ pointFrom13(points[i + 1][0], points[i + 1][1]),
13397
13810
  center,
13398
13811
  angle
13399
13812
  )
@@ -13404,7 +13817,7 @@ var getSegmentsOnCurve = (curve4, center, angle) => {
13404
13817
  return segments;
13405
13818
  };
13406
13819
  var getSegmentsOnEllipse = (ellipse4) => {
13407
- const center = pointFrom14(
13820
+ const center = pointFrom13(
13408
13821
  ellipse4.x + ellipse4.width / 2,
13409
13822
  ellipse4.y + ellipse4.height / 2
13410
13823
  );
@@ -13418,7 +13831,7 @@ var getSegmentsOnEllipse = (ellipse4) => {
13418
13831
  const t = i * deltaT;
13419
13832
  const x = center[0] + a2 * Math.cos(t);
13420
13833
  const y = center[1] + b2 * Math.sin(t);
13421
- points.push(pointRotateRads13(pointFrom14(x, y), center, ellipse4.angle));
13834
+ points.push(pointRotateRads12(pointFrom13(x, y), center, ellipse4.angle));
13422
13835
  }
13423
13836
  for (let i = 0; i < points.length - 1; i++) {
13424
13837
  segments.push(lineSegment6(points[i], points[i + 1]));
@@ -13501,7 +13914,7 @@ var getCubicBezierCurveBound = (p0, p1, p2, p3) => {
13501
13914
  return [minX, minY, maxX, maxY];
13502
13915
  };
13503
13916
  var getMinMaxXYFromCurvePathOps = (ops, transformXY) => {
13504
- let currentP = pointFrom14(0, 0);
13917
+ let currentP = pointFrom13(0, 0);
13505
13918
  const { minX, minY, maxX, maxY } = ops.reduce(
13506
13919
  (limits, { op, data }) => {
13507
13920
  if (op === "move") {
@@ -13509,9 +13922,9 @@ var getMinMaxXYFromCurvePathOps = (ops, transformXY) => {
13509
13922
  invariant10(p != null, "Op data is not a point");
13510
13923
  currentP = p;
13511
13924
  } else if (op === "bcurveTo") {
13512
- const _p1 = pointFrom14(data[0], data[1]);
13513
- const _p2 = pointFrom14(data[2], data[3]);
13514
- const _p3 = pointFrom14(data[4], data[5]);
13925
+ const _p1 = pointFrom13(data[0], data[1]);
13926
+ const _p2 = pointFrom13(data[2], data[3]);
13927
+ const _p3 = pointFrom13(data[4], data[5]);
13515
13928
  const p1 = transformXY ? transformXY(_p1) : _p1;
13516
13929
  const p2 = transformXY ? transformXY(_p2) : _p2;
13517
13930
  const p3 = transformXY ? transformXY(_p3) : _p3;
@@ -13536,7 +13949,7 @@ var getMinMaxXYFromCurvePathOps = (ops, transformXY) => {
13536
13949
  );
13537
13950
  return [minX, minY, maxX, maxY];
13538
13951
  };
13539
- var getBoundsFromPoints = (points) => {
13952
+ var getBoundsFromPoints = (points, padding = 0) => {
13540
13953
  let minX = Infinity;
13541
13954
  let minY = Infinity;
13542
13955
  let maxX = -Infinity;
@@ -13547,7 +13960,7 @@ var getBoundsFromPoints = (points) => {
13547
13960
  maxX = Math.max(maxX, x);
13548
13961
  maxY = Math.max(maxY, y);
13549
13962
  }
13550
- return [minX, minY, maxX, maxY];
13963
+ return [minX - padding, minY - padding, maxX + padding, maxY + padding];
13551
13964
  };
13552
13965
  var getFreeDrawElementAbsoluteCoords = (element) => {
13553
13966
  const [minX, minY, maxX, maxY] = getBoundsFromPoints(element.points);
@@ -13557,6 +13970,8 @@ var getFreeDrawElementAbsoluteCoords = (element) => {
13557
13970
  const y2 = maxY + element.y;
13558
13971
  return [x1, y1, x2, y2, (x1 + x2) / 2, (y1 + y2) / 2];
13559
13972
  };
13973
+ var CARDINALITY_MARKER_SIZE = 20;
13974
+ var CROWFOOT_ARROWHEAD_SIZE = 15;
13560
13975
  var getArrowheadSize = (arrowhead) => {
13561
13976
  switch (arrowhead) {
13562
13977
  case "arrow":
@@ -13564,10 +13979,14 @@ var getArrowheadSize = (arrowhead) => {
13564
13979
  case "diamond":
13565
13980
  case "diamond_outline":
13566
13981
  return 12;
13567
- case "crowfoot_many":
13568
- case "crowfoot_one":
13569
- case "crowfoot_one_or_many":
13570
- return 20;
13982
+ case "cardinality_many":
13983
+ case "cardinality_one_or_many":
13984
+ case "cardinality_zero_or_many":
13985
+ return CROWFOOT_ARROWHEAD_SIZE;
13986
+ case "cardinality_one":
13987
+ case "cardinality_exactly_one":
13988
+ case "cardinality_zero_or_one":
13989
+ return CARDINALITY_MARKER_SIZE;
13571
13990
  default:
13572
13991
  return 15;
13573
13992
  }
@@ -13582,7 +14001,10 @@ var getArrowheadAngle = (arrowhead) => {
13582
14001
  return 25;
13583
14002
  }
13584
14003
  };
13585
- var getArrowheadPoints = (element, shape, position, arrowhead) => {
14004
+ var getArrowheadPoints = (element, shape, position, arrowhead, offsetMultiplier = 0) => {
14005
+ if (arrowhead === null) {
14006
+ return null;
14007
+ }
13586
14008
  if (shape.length < 1) {
13587
14009
  return null;
13588
14010
  }
@@ -13593,17 +14015,17 @@ var getArrowheadPoints = (element, shape, position, arrowhead) => {
13593
14015
  const index = position === "start" ? 1 : ops.length - 1;
13594
14016
  const data = ops[index].data;
13595
14017
  invariant10(data.length === 6, "Op data length is not 6");
13596
- const p3 = pointFrom14(data[4], data[5]);
13597
- const p2 = pointFrom14(data[2], data[3]);
13598
- const p1 = pointFrom14(data[0], data[1]);
14018
+ const p3 = pointFrom13(data[4], data[5]);
14019
+ const p2 = pointFrom13(data[2], data[3]);
14020
+ const p1 = pointFrom13(data[0], data[1]);
13599
14021
  const prevOp = ops[index - 1];
13600
- let p0 = pointFrom14(0, 0);
14022
+ let p0 = pointFrom13(0, 0);
13601
14023
  if (prevOp.op === "move") {
13602
14024
  const p = pointFromArray3(prevOp.data);
13603
14025
  invariant10(p != null, "Op data is not a point");
13604
14026
  p0 = p;
13605
14027
  } else if (prevOp.op === "bcurveTo") {
13606
- p0 = pointFrom14(prevOp.data[4], prevOp.data[5]);
14028
+ p0 = pointFrom13(prevOp.data[4], prevOp.data[5]);
13607
14029
  }
13608
14030
  const equation = (t, idx) => Math.pow(1 - t, 3) * p3[idx] + 3 * t * Math.pow(1 - t, 2) * p2[idx] + 3 * Math.pow(t, 2) * (1 - t) * p1[idx] + p0[idx] * Math.pow(t, 3);
13609
14031
  const [x2, y2] = position === "start" ? p0 : p3;
@@ -13620,34 +14042,36 @@ var getArrowheadPoints = (element, shape, position, arrowhead) => {
13620
14042
  }
13621
14043
  const lengthMultiplier = arrowhead === "diamond" || arrowhead === "diamond_outline" ? 0.25 : 0.5;
13622
14044
  const minSize = Math.min(size, length * lengthMultiplier);
13623
- const xs = x2 - nx * minSize;
13624
- const ys = y2 - ny * minSize;
13625
- if (arrowhead === "dot" || arrowhead === "circle" || arrowhead === "circle_outline") {
13626
- const diameter = Math.hypot(ys - y2, xs - x2) + element.strokeWidth - 2;
13627
- return [x2, y2, diameter];
14045
+ const tx = x2 - nx * minSize * offsetMultiplier;
14046
+ const ty = y2 - ny * minSize * offsetMultiplier;
14047
+ const xs = tx - nx * minSize;
14048
+ const ys = ty - ny * minSize;
14049
+ if (arrowhead === "circle" || arrowhead === "circle_outline") {
14050
+ const diameter = Math.hypot(ys - ty, xs - tx) + element.strokeWidth - 2;
14051
+ return [tx, ty, diameter];
13628
14052
  }
13629
14053
  const angle = getArrowheadAngle(arrowhead);
13630
- if (arrowhead === "crowfoot_many" || arrowhead === "crowfoot_one_or_many") {
13631
- const [x32, y32] = pointRotateRads13(
13632
- pointFrom14(x2, y2),
13633
- pointFrom14(xs, ys),
14054
+ if (arrowhead === "cardinality_many" || arrowhead === "cardinality_one_or_many") {
14055
+ const [x32, y32] = pointRotateRads12(
14056
+ pointFrom13(tx, ty),
14057
+ pointFrom13(xs, ys),
13634
14058
  degreesToRadians(-angle)
13635
14059
  );
13636
- const [x42, y42] = pointRotateRads13(
13637
- pointFrom14(x2, y2),
13638
- pointFrom14(xs, ys),
14060
+ const [x42, y42] = pointRotateRads12(
14061
+ pointFrom13(tx, ty),
14062
+ pointFrom13(xs, ys),
13639
14063
  degreesToRadians(angle)
13640
14064
  );
13641
14065
  return [xs, ys, x32, y32, x42, y42];
13642
14066
  }
13643
- const [x3, y3] = pointRotateRads13(
13644
- pointFrom14(xs, ys),
13645
- pointFrom14(x2, y2),
14067
+ const [x3, y3] = pointRotateRads12(
14068
+ pointFrom13(xs, ys),
14069
+ pointFrom13(tx, ty),
13646
14070
  -angle * Math.PI / 180
13647
14071
  );
13648
- const [x4, y4] = pointRotateRads13(
13649
- pointFrom14(xs, ys),
13650
- pointFrom14(x2, y2),
14072
+ const [x4, y4] = pointRotateRads12(
14073
+ pointFrom13(xs, ys),
14074
+ pointFrom13(tx, ty),
13651
14075
  degreesToRadians(angle)
13652
14076
  );
13653
14077
  if (arrowhead === "diamond" || arrowhead === "diamond_outline") {
@@ -13655,22 +14079,22 @@ var getArrowheadPoints = (element, shape, position, arrowhead) => {
13655
14079
  let oy;
13656
14080
  if (position === "start") {
13657
14081
  const [px, py] = element.points.length > 1 ? element.points[1] : [0, 0];
13658
- [ox, oy] = pointRotateRads13(
13659
- pointFrom14(x2 + minSize * 2, y2),
13660
- pointFrom14(x2, y2),
13661
- Math.atan2(py - y2, px - x2)
14082
+ [ox, oy] = pointRotateRads12(
14083
+ pointFrom13(tx + minSize * 2, ty),
14084
+ pointFrom13(tx, ty),
14085
+ Math.atan2(py - ty, px - tx)
13662
14086
  );
13663
14087
  } else {
13664
14088
  const [px, py] = element.points.length > 1 ? element.points[element.points.length - 2] : [0, 0];
13665
- [ox, oy] = pointRotateRads13(
13666
- pointFrom14(x2 - minSize * 2, y2),
13667
- pointFrom14(x2, y2),
13668
- Math.atan2(y2 - py, x2 - px)
14089
+ [ox, oy] = pointRotateRads12(
14090
+ pointFrom13(tx - minSize * 2, ty),
14091
+ pointFrom13(tx, ty),
14092
+ Math.atan2(ty - py, tx - px)
13669
14093
  );
13670
14094
  }
13671
- return [x2, y2, x3, y3, ox, oy, x4, y4];
14095
+ return [tx, ty, x3, y3, ox, oy, x4, y4];
13672
14096
  }
13673
- return [x2, y2, x3, y3, x4, y4];
14097
+ return [tx, ty, x3, y3, x4, y4];
13674
14098
  };
13675
14099
  var generateLinearElementShape = (element) => {
13676
14100
  const generator = rough_default.generator();
@@ -13693,9 +14117,9 @@ var getLinearElementRotatedBounds = (element, cx, cy, elementsMap) => {
13693
14117
  const boundTextElement = getBoundTextElement(element, elementsMap);
13694
14118
  if (element.points.length < 2) {
13695
14119
  const [pointX, pointY] = element.points[0];
13696
- const [x, y] = pointRotateRads13(
13697
- pointFrom14(element.x + pointX, element.y + pointY),
13698
- pointFrom14(cx, cy),
14120
+ const [x, y] = pointRotateRads12(
14121
+ pointFrom13(element.x + pointX, element.y + pointY),
14122
+ pointFrom13(cx, cy),
13699
14123
  element.angle
13700
14124
  );
13701
14125
  let coords2 = [x, y, x, y];
@@ -13718,9 +14142,9 @@ var getLinearElementRotatedBounds = (element, cx, cy, elementsMap) => {
13718
14142
  const cachedShape = ShapeCache.get(element, null)?.[0];
13719
14143
  const shape = cachedShape ?? generateLinearElementShape(element);
13720
14144
  const ops = getCurvePathOps(shape);
13721
- const transformXY = ([x, y]) => pointRotateRads13(
13722
- pointFrom14(element.x + x, element.y + y),
13723
- pointFrom14(cx, cy),
14145
+ const transformXY = ([x, y]) => pointRotateRads12(
14146
+ pointFrom13(element.x + x, element.y + y),
14147
+ pointFrom13(cx, cy),
13724
14148
  element.angle
13725
14149
  );
13726
14150
  const res = getMinMaxXYFromCurvePathOps(ops, transformXY);
@@ -13831,8 +14255,8 @@ var getClosestElementBounds = (elements, from) => {
13831
14255
  elements.forEach((element) => {
13832
14256
  const [x1, y1, x2, y2] = getElementBounds(element, elementsMap);
13833
14257
  const distance3 = pointDistance7(
13834
- pointFrom14((x1 + x2) / 2, (y1 + y2) / 2),
13835
- pointFrom14(from.x, from.y)
14258
+ pointFrom13((x1 + x2) / 2, (y1 + y2) / 2),
14259
+ pointFrom13(from.x, from.y)
13836
14260
  );
13837
14261
  if (distance3 < minDistance) {
13838
14262
  minDistance = distance3;
@@ -13868,7 +14292,7 @@ var getVisibleSceneBounds = ({
13868
14292
  -scrollY + height / zoom.value
13869
14293
  ];
13870
14294
  };
13871
- var getCenterForBounds = (bounds) => pointFrom14(
14295
+ var getCenterForBounds = (bounds) => pointFrom13(
13872
14296
  bounds[0] + (bounds[2] - bounds[0]) / 2,
13873
14297
  bounds[1] + (bounds[3] - bounds[1]) / 2
13874
14298
  );
@@ -13882,23 +14306,23 @@ var aabbForElement = (element, elementsMap, offset) => {
13882
14306
  midY: element.y + element.height / 2
13883
14307
  };
13884
14308
  const center = elementCenterPoint(element, elementsMap);
13885
- const [topLeftX, topLeftY] = pointRotateRads13(
13886
- pointFrom14(bbox.minX, bbox.minY),
14309
+ const [topLeftX, topLeftY] = pointRotateRads12(
14310
+ pointFrom13(bbox.minX, bbox.minY),
13887
14311
  center,
13888
14312
  element.angle
13889
14313
  );
13890
- const [topRightX, topRightY] = pointRotateRads13(
13891
- pointFrom14(bbox.maxX, bbox.minY),
14314
+ const [topRightX, topRightY] = pointRotateRads12(
14315
+ pointFrom13(bbox.maxX, bbox.minY),
13892
14316
  center,
13893
14317
  element.angle
13894
14318
  );
13895
- const [bottomRightX, bottomRightY] = pointRotateRads13(
13896
- pointFrom14(bbox.maxX, bbox.maxY),
14319
+ const [bottomRightX, bottomRightY] = pointRotateRads12(
14320
+ pointFrom13(bbox.maxX, bbox.maxY),
13897
14321
  center,
13898
14322
  element.angle
13899
14323
  );
13900
- const [bottomLeftX, bottomLeftY] = pointRotateRads13(
13901
- pointFrom14(bbox.minX, bbox.maxY),
14324
+ const [bottomLeftX, bottomLeftY] = pointRotateRads12(
14325
+ pointFrom13(bbox.minX, bbox.maxY),
13902
14326
  center,
13903
14327
  element.angle
13904
14328
  );
@@ -13920,6 +14344,7 @@ var aabbForElement = (element, elementsMap, offset) => {
13920
14344
  return bounds;
13921
14345
  };
13922
14346
  var pointInsideBounds = (p, bounds) => p[0] > bounds[0] && p[0] < bounds[2] && p[1] > bounds[1] && p[1] < bounds[3];
14347
+ var pointInsideBoundsInclusive = (p, bounds) => p[0] >= bounds[0] && p[0] <= bounds[2] && p[1] >= bounds[1] && p[1] <= bounds[3];
13923
14348
  var doBoundsIntersect = (bounds1, bounds2) => {
13924
14349
  if (bounds1 == null || bounds2 == null) {
13925
14350
  return false;
@@ -13928,14 +14353,225 @@ var doBoundsIntersect = (bounds1, bounds2) => {
13928
14353
  const [minX2, minY2, maxX2, maxY2] = bounds2;
13929
14354
  return minX1 < maxX2 && maxX1 > minX2 && minY1 < maxY2 && maxY1 > minY2;
13930
14355
  };
14356
+ var boundsContainBounds = (outerBounds, innerBounds) => [
14357
+ pointFrom13(innerBounds[0], innerBounds[1]),
14358
+ pointFrom13(innerBounds[0], innerBounds[3]),
14359
+ pointFrom13(innerBounds[2], innerBounds[1]),
14360
+ pointFrom13(innerBounds[2], innerBounds[3])
14361
+ ].every((point) => pointInsideBoundsInclusive(point, outerBounds));
14362
+ var elementsOverlappingBBox = ({
14363
+ elements,
14364
+ elementsMap,
14365
+ bounds,
14366
+ type,
14367
+ excludeElementsInFrames,
14368
+ shouldIgnoreElementFromSelection: shouldIgnoreElementFromSelection2
14369
+ }) => {
14370
+ if (!elementsMap) {
14371
+ elementsMap = arrayToMap6(elements);
14372
+ }
14373
+ const selectionBounds = isExcalidrawElement(bounds) ? getElementBounds(bounds, elementsMap) : bounds;
14374
+ const [selectionX1, selectionY1, selectionX2, selectionY2] = selectionBounds;
14375
+ const selectionEdges = [
14376
+ lineSegment6(
14377
+ pointFrom13(selectionX1, selectionY1),
14378
+ pointFrom13(selectionX2, selectionY1)
14379
+ ),
14380
+ lineSegment6(
14381
+ pointFrom13(selectionX2, selectionY1),
14382
+ pointFrom13(selectionX2, selectionY2)
14383
+ ),
14384
+ lineSegment6(
14385
+ pointFrom13(selectionX2, selectionY2),
14386
+ pointFrom13(selectionX1, selectionY2)
14387
+ ),
14388
+ lineSegment6(
14389
+ pointFrom13(selectionX1, selectionY2),
14390
+ pointFrom13(selectionX1, selectionY1)
14391
+ )
14392
+ ];
14393
+ const framesInSelection = excludeElementsInFrames ? /* @__PURE__ */ new Set() : null;
14394
+ const groups = {};
14395
+ const elementsInSelection = /* @__PURE__ */ new Set();
14396
+ for (const element of elements) {
14397
+ if (shouldIgnoreElementFromSelection2?.(element)) {
14398
+ continue;
14399
+ }
14400
+ const groupId = element.groupIds.at(-1);
14401
+ if (groupId) {
14402
+ if (!groups[groupId]) {
14403
+ groups[groupId] = [];
14404
+ }
14405
+ groups[groupId].push(element);
14406
+ }
14407
+ const strokeWidth = element.strokeWidth;
14408
+ let labelAABB = null;
14409
+ let elementAABB = getElementBounds(element, elementsMap);
14410
+ elementAABB = [
14411
+ elementAABB[0] - strokeWidth / 2,
14412
+ elementAABB[1] - strokeWidth / 2,
14413
+ elementAABB[2] + strokeWidth / 2,
14414
+ elementAABB[3] + strokeWidth / 2
14415
+ ];
14416
+ const boundTextElement = isArrowElement(element) && getBoundTextElement(element, elementsMap);
14417
+ if (boundTextElement) {
14418
+ const { x, y } = LinearElementEditor.getBoundTextElementPosition(
14419
+ element,
14420
+ boundTextElement,
14421
+ elementsMap
14422
+ );
14423
+ labelAABB = [
14424
+ x,
14425
+ y,
14426
+ x + boundTextElement.width,
14427
+ y + boundTextElement.height
14428
+ ];
14429
+ }
14430
+ const associatedFrame = getContainingFrame(element, elementsMap);
14431
+ if (associatedFrame && elementOverlapsWithFrame(element, associatedFrame, elementsMap)) {
14432
+ const frameAABB = getElementBounds(associatedFrame, elementsMap);
14433
+ elementAABB = [
14434
+ Math.max(elementAABB[0], frameAABB[0]),
14435
+ Math.max(elementAABB[1], frameAABB[1]),
14436
+ Math.min(elementAABB[2], frameAABB[2]),
14437
+ Math.min(elementAABB[3], frameAABB[3])
14438
+ ];
14439
+ labelAABB = labelAABB ? [
14440
+ Math.max(labelAABB[0], frameAABB[0]),
14441
+ Math.max(labelAABB[1], frameAABB[1]),
14442
+ Math.min(labelAABB[2], frameAABB[2]),
14443
+ Math.min(labelAABB[3], frameAABB[3])
14444
+ ] : null;
14445
+ }
14446
+ const commonAABB2 = labelAABB ? [
14447
+ Math.min(labelAABB[0], elementAABB[0]),
14448
+ Math.min(labelAABB[1], elementAABB[1]),
14449
+ Math.max(labelAABB[2], elementAABB[2]),
14450
+ Math.max(labelAABB[3], elementAABB[3])
14451
+ ] : elementAABB;
14452
+ if (boundsContainBounds(selectionBounds, commonAABB2)) {
14453
+ if (framesInSelection && isFrameLikeElement(element)) {
14454
+ framesInSelection.add(element.id);
14455
+ }
14456
+ elementsInSelection.add(element);
14457
+ continue;
14458
+ }
14459
+ if (type === "overlap" && labelAABB && doBoundsIntersect(selectionBounds, labelAABB)) {
14460
+ elementsInSelection.add(element);
14461
+ continue;
14462
+ }
14463
+ if (type === "overlap" && doBoundsIntersect(selectionBounds, elementAABB)) {
14464
+ let hasIntersection = false;
14465
+ if (isLinearElement(element) || isFreeDrawElement(element)) {
14466
+ const center = elementCenterPoint(element, elementsMap);
14467
+ hasIntersection = element.points.some((point) => {
14468
+ const rotatedPoint = pointRotateRads12(
14469
+ pointFrom13(element.x + point[0], element.y + point[1]),
14470
+ center,
14471
+ element.angle
14472
+ );
14473
+ return pointInsideBounds(rotatedPoint, selectionBounds);
14474
+ });
14475
+ } else {
14476
+ const nonRotatedElementBounds = getElementBounds(
14477
+ element,
14478
+ elementsMap,
14479
+ true
14480
+ );
14481
+ const center = elementCenterPoint(element, elementsMap);
14482
+ hasIntersection = [
14483
+ pointRotateRads12(
14484
+ pointFrom13(
14485
+ (nonRotatedElementBounds[0] + nonRotatedElementBounds[2]) / 2,
14486
+ nonRotatedElementBounds[1]
14487
+ ),
14488
+ center,
14489
+ element.angle
14490
+ ),
14491
+ pointRotateRads12(
14492
+ pointFrom13(
14493
+ nonRotatedElementBounds[2],
14494
+ (nonRotatedElementBounds[1] + nonRotatedElementBounds[3]) / 2
14495
+ ),
14496
+ center,
14497
+ element.angle
14498
+ ),
14499
+ pointRotateRads12(
14500
+ pointFrom13(
14501
+ (nonRotatedElementBounds[0] + nonRotatedElementBounds[2]) / 2,
14502
+ nonRotatedElementBounds[3]
14503
+ ),
14504
+ center,
14505
+ element.angle
14506
+ ),
14507
+ pointRotateRads12(
14508
+ pointFrom13(
14509
+ nonRotatedElementBounds[0],
14510
+ (nonRotatedElementBounds[1] + nonRotatedElementBounds[3]) / 2
14511
+ ),
14512
+ center,
14513
+ element.angle
14514
+ )
14515
+ ].some((point) => {
14516
+ return pointInsideBounds(
14517
+ pointRotateRads12(point, center, element.angle),
14518
+ selectionBounds
14519
+ );
14520
+ });
14521
+ }
14522
+ if (!hasIntersection) {
14523
+ hasIntersection = selectionEdges.some(
14524
+ (selectionEdge) => intersectElementWithLineSegment(
14525
+ element,
14526
+ elementsMap,
14527
+ selectionEdge,
14528
+ strokeWidth / 2,
14529
+ true
14530
+ // Stop at first hit for better performance
14531
+ ).length > 0
14532
+ );
14533
+ }
14534
+ if (hasIntersection) {
14535
+ if (framesInSelection && isFrameLikeElement(element)) {
14536
+ framesInSelection.add(element.id);
14537
+ }
14538
+ elementsInSelection.add(element);
14539
+ continue;
14540
+ }
14541
+ }
14542
+ }
14543
+ if (framesInSelection) {
14544
+ elementsInSelection.forEach((element) => {
14545
+ if (element.frameId && framesInSelection.has(element.frameId)) {
14546
+ elementsInSelection.delete(element);
14547
+ }
14548
+ });
14549
+ }
14550
+ if (type === "overlap") {
14551
+ Array.from(elementsInSelection).forEach((element) => {
14552
+ const groupId = element.groupIds.at(-1);
14553
+ const group = groupId ? groups[groupId] : null;
14554
+ group?.forEach((groupElement) => elementsInSelection.add(groupElement));
14555
+ });
14556
+ } else if (type === "contain") {
14557
+ elementsInSelection.forEach((element) => {
14558
+ const groupId = element.groupIds.at(-1);
14559
+ const group = groupId ? groups[groupId] : null;
14560
+ if (group && !group.every((groupElement) => elementsInSelection.has(groupElement))) {
14561
+ elementsInSelection.delete(element);
14562
+ }
14563
+ });
14564
+ }
14565
+ return elements.filter((element) => elementsInSelection.has(element));
14566
+ };
13931
14567
  var elementCenterPoint = (element, elementsMap, xOffset = 0, yOffset = 0) => {
13932
- if (isLinearElement(element)) {
14568
+ if (isLinearElement(element) || isFreeDrawElement(element)) {
13933
14569
  const [x1, y1, x2, y2] = getElementAbsoluteCoords2(element, elementsMap);
13934
- const [x3, y3] = pointFrom14((x1 + x2) / 2, (y1 + y2) / 2);
13935
- return pointFrom14(x3 + xOffset, y3 + yOffset);
14570
+ const [x3, y3] = pointFrom13((x1 + x2) / 2, (y1 + y2) / 2);
14571
+ return pointFrom13(x3 + xOffset, y3 + yOffset);
13936
14572
  }
13937
14573
  const [x, y] = getCenterForBounds(getElementBounds(element, elementsMap));
13938
- return pointFrom14(x + xOffset, y + yOffset);
14574
+ return pointFrom13(x + xOffset, y + yOffset);
13939
14575
  };
13940
14576
 
13941
14577
  // src/sizeHelpers.ts
@@ -14140,7 +14776,7 @@ import {
14140
14776
  ORIG_ID,
14141
14777
  randomId,
14142
14778
  randomInteger as randomInteger2,
14143
- arrayToMap as arrayToMap7,
14779
+ arrayToMap as arrayToMap8,
14144
14780
  castArray,
14145
14781
  findLastIndex,
14146
14782
  getUpdatedTimestamp as getUpdatedTimestamp2,
@@ -14149,79 +14785,61 @@ import {
14149
14785
 
14150
14786
  // src/sortElements.ts
14151
14787
  init_define_import_meta_env();
14152
- import { arrayToMapWithIndex } from "@excalidraw/common";
14153
- var normalizeGroupElementOrder = (elements) => {
14154
- const origElements = elements.slice();
14155
- const sortedElements = /* @__PURE__ */ new Set();
14156
- const orderInnerGroups = (elements2) => {
14157
- const firstGroupSig = elements2[0]?.groupIds?.join("");
14158
- const aGroup = [elements2[0]];
14159
- const bGroup = [];
14160
- for (const element of elements2.slice(1)) {
14161
- if (element.groupIds?.join("") === firstGroupSig) {
14162
- aGroup.push(element);
14163
- } else {
14164
- bGroup.push(element);
14165
- }
14166
- }
14167
- return bGroup.length ? [...aGroup, ...orderInnerGroups(bGroup)] : aGroup;
14788
+ import { arrayToMap as arrayToMap7 } from "@excalidraw/common";
14789
+ var defragmentGroups = (elements) => {
14790
+ const groupIdAtLevel = (element, level) => {
14791
+ return element.groupIds[element.groupIds.length - level - 1];
14168
14792
  };
14169
- const groupHandledElements = /* @__PURE__ */ new Map();
14170
- origElements.forEach((element, idx) => {
14171
- if (groupHandledElements.has(element.id)) {
14172
- return;
14173
- }
14174
- if (element.groupIds?.length) {
14175
- const topGroup = element.groupIds[element.groupIds.length - 1];
14176
- const groupElements = origElements.slice(idx).filter((element2) => {
14177
- const ret = element2?.groupIds?.some((id) => id === topGroup);
14178
- if (ret) {
14179
- groupHandledElements.set(element2.id, true);
14180
- }
14181
- return ret;
14182
- });
14183
- for (const elem of orderInnerGroups(groupElements)) {
14184
- sortedElements.add(elem);
14793
+ const orderLevel = (levelElements, level) => {
14794
+ const buckets = /* @__PURE__ */ new Map();
14795
+ const slots = [];
14796
+ for (const element of levelElements) {
14797
+ const groupId = groupIdAtLevel(element, level);
14798
+ if (groupId === void 0) {
14799
+ slots.push(element);
14800
+ continue;
14185
14801
  }
14186
- } else {
14187
- sortedElements.add(element);
14802
+ let bucket = buckets.get(groupId);
14803
+ if (!bucket) {
14804
+ bucket = [];
14805
+ buckets.set(groupId, bucket);
14806
+ slots.push(groupId);
14807
+ }
14808
+ bucket.push(element);
14188
14809
  }
14189
- });
14190
- if (sortedElements.size !== elements.length) {
14191
- console.error("normalizeGroupElementOrder: lost some elements... bailing!");
14810
+ return slots.flatMap(
14811
+ (slot) => typeof slot === "string" ? orderLevel(buckets.get(slot), level + 1) : [slot]
14812
+ );
14813
+ };
14814
+ const sortedElements = orderLevel(elements, 0);
14815
+ if (sortedElements.length !== elements.length) {
14816
+ console.error("defragmentGroups: lost some elements... bailing!");
14192
14817
  return elements;
14193
14818
  }
14194
- return [...sortedElements];
14819
+ return sortedElements;
14195
14820
  };
14196
14821
  var normalizeBoundElementsOrder = (elements) => {
14197
- const elementsMap = arrayToMapWithIndex(elements);
14198
- const origElements = elements.slice();
14822
+ const elementsMap = arrayToMap7(elements);
14199
14823
  const sortedElements = /* @__PURE__ */ new Set();
14200
- origElements.forEach((element, idx) => {
14201
- if (!element) {
14202
- return;
14824
+ for (const element of elements) {
14825
+ if (sortedElements.has(element)) {
14826
+ continue;
14203
14827
  }
14204
14828
  if (element.boundElements?.length) {
14205
14829
  sortedElements.add(element);
14206
- origElements[idx] = null;
14207
- element.boundElements.forEach((boundElement) => {
14830
+ for (const boundElement of element.boundElements) {
14208
14831
  const child = elementsMap.get(boundElement.id);
14209
14832
  if (child && boundElement.type === "text") {
14210
- sortedElements.add(child[0]);
14211
- origElements[child[1]] = null;
14833
+ sortedElements.add(child);
14212
14834
  }
14213
- });
14214
- } else if (element.type === "text" && element.containerId) {
14215
- const parent = elementsMap.get(element.containerId);
14216
- if (!parent?.[0].boundElements?.find((x) => x.id === element.id)) {
14217
- sortedElements.add(element);
14218
- origElements[idx] = null;
14219
14835
  }
14220
- } else {
14221
- sortedElements.add(element);
14222
- origElements[idx] = null;
14836
+ continue;
14223
14837
  }
14224
- });
14838
+ if (element.type === "text" && element.containerId && elementsMap.get(element.containerId)?.boundElements?.some((el) => el.id === element.id)) {
14839
+ continue;
14840
+ }
14841
+ sortedElements.add(element);
14842
+ }
14225
14843
  if (sortedElements.size !== elements.length) {
14226
14844
  console.error(
14227
14845
  "normalizeBoundElementsOrder: lost some elements... bailing!"
@@ -14231,7 +14849,7 @@ var normalizeBoundElementsOrder = (elements) => {
14231
14849
  return [...sortedElements];
14232
14850
  };
14233
14851
  var normalizeElementOrder = (elements) => {
14234
- return normalizeBoundElementsOrder(normalizeGroupElementOrder(elements));
14852
+ return normalizeBoundElementsOrder(defragmentGroups(elements));
14235
14853
  };
14236
14854
 
14237
14855
  // src/duplicate.ts
@@ -14271,8 +14889,9 @@ var duplicateElements = (opts) => {
14271
14889
  const origIdToDuplicateId = /* @__PURE__ */ new Map();
14272
14890
  const duplicateIdToOrigElement = /* @__PURE__ */ new Map();
14273
14891
  const duplicateElementsMap = /* @__PURE__ */ new Map();
14274
- const elementsMap = arrayToMap7(elements);
14892
+ const elementsMap = arrayToMap8(elements);
14275
14893
  const _idsOfElementsToDuplicate = opts.type === "in-place" ? opts.idsOfElementsToDuplicate : new Map(elements.map((el) => [el.id, el]));
14894
+ const preserveFrameChildrenOrder = opts.type === "everything" && opts.preserveFrameChildrenOrder;
14276
14895
  if (opts.type === "in-place") {
14277
14896
  for (const groupId of Object.keys(opts.appState.selectedGroupIds)) {
14278
14897
  elements.filter((el) => el.groupIds?.includes(groupId)).forEach((el) => _idsOfElementsToDuplicate.set(el.id, el));
@@ -14332,7 +14951,7 @@ var duplicateElements = (opts) => {
14332
14951
  const groupId = getSelectedGroupForElement(appState, element);
14333
14952
  if (groupId) {
14334
14953
  const groupElements = getElementsInGroup(elements, groupId).flatMap(
14335
- (element2) => isFrameLikeElement(element2) ? [...getFrameChildren(elements, element2.id), element2] : [element2]
14954
+ (element2) => isFrameLikeElement(element2) && !preserveFrameChildrenOrder ? [...getFrameChildren(elements, element2.id), element2] : [element2]
14336
14955
  );
14337
14956
  const targetIndex = findLastIndex(elementsWithDuplicates, (el) => {
14338
14957
  return el.groupIds?.includes(groupId);
@@ -14340,11 +14959,18 @@ var duplicateElements = (opts) => {
14340
14959
  insertBeforeOrAfterIndex(targetIndex, copyElements(groupElements));
14341
14960
  continue;
14342
14961
  }
14343
- if (element.frameId && frameIdsToDuplicate.has(element.frameId)) {
14962
+ if (!preserveFrameChildrenOrder && element.frameId && frameIdsToDuplicate.has(element.frameId)) {
14344
14963
  continue;
14345
14964
  }
14346
14965
  if (isFrameLikeElement(element)) {
14347
14966
  const frameId = element.id;
14967
+ if (preserveFrameChildrenOrder) {
14968
+ insertBeforeOrAfterIndex(
14969
+ findLastIndex(elementsWithDuplicates, (el) => el.id === frameId),
14970
+ copyElements(element)
14971
+ );
14972
+ continue;
14973
+ }
14348
14974
  const frameChildren = getFrameChildren(elements, frameId);
14349
14975
  const targetIndex = findLastIndex(elementsWithDuplicates, (el) => {
14350
14976
  return el.frameId === frameId || el.id === frameId;
@@ -14998,586 +15624,132 @@ var StoreSnapshot = class _StoreSnapshot {
14998
15624
  if (!didAppStateChange) {
14999
15625
  return this.appState;
15000
15626
  }
15001
- return nextAppStateSnapshot;
15002
- }
15003
- maybeCreateElementsSnapshot(elements, options = {
15004
- shouldCompareHashes: false
15005
- }) {
15006
- if (!elements) {
15007
- return this.elements;
15008
- }
15009
- const changedElements = this.detectChangedElements(elements, options);
15010
- if (!changedElements?.size) {
15011
- return this.elements;
15012
- }
15013
- const elementsSnapshot = this.createElementsSnapshot(changedElements);
15014
- return elementsSnapshot;
15015
- }
15016
- detectChangedAppState(nextObservedAppState, options = {
15017
- shouldCompareHashes: false
15018
- }) {
15019
- if (this.appState === nextObservedAppState) {
15020
- return;
15021
- }
15022
- const didAppStateChange = Delta.isRightDifferent(
15023
- this.appState,
15024
- nextObservedAppState
15025
- );
15026
- if (!didAppStateChange) {
15027
- return;
15028
- }
15029
- const changedAppStateHash = hashString(
15030
- JSON.stringify(nextObservedAppState)
15031
- );
15032
- if (options.shouldCompareHashes && this._lastChangedAppStateHash === changedAppStateHash) {
15033
- return;
15034
- }
15035
- this._lastChangedAppStateHash = changedAppStateHash;
15036
- return didAppStateChange;
15037
- }
15038
- /**
15039
- * Detect if there are any changed elements.
15040
- */
15041
- detectChangedElements(nextElements, options = {
15042
- shouldCompareHashes: false
15043
- }) {
15044
- if (this.elements === nextElements) {
15045
- return;
15046
- }
15047
- const changedElements = /* @__PURE__ */ new Map();
15048
- for (const prevElement of toIterable(this.elements)) {
15049
- const nextElement = nextElements.get(prevElement.id);
15050
- if (!nextElement) {
15051
- changedElements.set(
15052
- prevElement.id,
15053
- newElementWith(prevElement, { isDeleted: true })
15054
- );
15055
- }
15056
- }
15057
- for (const nextElement of toIterable(nextElements)) {
15058
- const prevElement = this.elements.get(nextElement.id);
15059
- if (!prevElement || // element was added
15060
- prevElement.version < nextElement.version) {
15061
- if (isImageElement(nextElement) && !isInitializedImageElement(nextElement)) {
15062
- continue;
15063
- }
15064
- changedElements.set(nextElement.id, nextElement);
15065
- }
15066
- }
15067
- if (!changedElements.size) {
15068
- return;
15069
- }
15070
- const changedElementsHash = hashElementsVersion(changedElements);
15071
- if (options.shouldCompareHashes && this._lastChangedElementsHash === changedElementsHash) {
15072
- return;
15073
- }
15074
- this._lastChangedElementsHash = changedElementsHash;
15075
- return changedElements;
15076
- }
15077
- /**
15078
- * Perform structural clone, deep cloning only elements that changed.
15079
- */
15080
- createElementsSnapshot(changedElements) {
15081
- const clonedElements = /* @__PURE__ */ new Map();
15082
- for (const prevElement of toIterable(this.elements)) {
15083
- clonedElements.set(prevElement.id, prevElement);
15084
- }
15085
- for (const changedElement of toIterable(changedElements)) {
15086
- clonedElements.set(changedElement.id, deepCopyElement(changedElement));
15087
- }
15088
- return clonedElements;
15089
- }
15090
- };
15091
- var hiddenObservedAppStateProp = "__observedAppState";
15092
- var getDefaultObservedAppState = () => {
15093
- return {
15094
- name: null,
15095
- editingGroupId: null,
15096
- viewBackgroundColor: COLOR_PALETTE2.white,
15097
- selectedElementIds: {},
15098
- selectedGroupIds: {},
15099
- selectedLinearElement: null,
15100
- croppingElementId: null,
15101
- activeLockedId: null,
15102
- lockedMultiSelections: {}
15103
- };
15104
- };
15105
- var getObservedAppState = (appState) => {
15106
- const observedAppState = {
15107
- name: appState.name,
15108
- editingGroupId: appState.editingGroupId,
15109
- viewBackgroundColor: appState.viewBackgroundColor,
15110
- selectedElementIds: appState.selectedElementIds,
15111
- selectedGroupIds: appState.selectedGroupIds,
15112
- croppingElementId: appState.croppingElementId,
15113
- activeLockedId: appState.activeLockedId,
15114
- lockedMultiSelections: appState.lockedMultiSelections,
15115
- selectedLinearElement: appState.selectedLinearElement ? {
15116
- elementId: appState.selectedLinearElement.elementId,
15117
- isEditing: !!appState.selectedLinearElement.isEditing
15118
- } : null
15119
- };
15120
- Reflect.defineProperty(observedAppState, hiddenObservedAppStateProp, {
15121
- value: true,
15122
- enumerable: false
15123
- });
15124
- return observedAppState;
15125
- };
15126
- var isObservedAppState = (appState) => !!Reflect.get(appState, hiddenObservedAppStateProp);
15127
-
15128
- // src/fractionalIndex.ts
15129
- init_define_import_meta_env();
15130
-
15131
- // ../../node_modules/fractional-indexing/src/index.js
15132
- init_define_import_meta_env();
15133
- var BASE_62_DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
15134
- function midpoint(a2, b2, digits) {
15135
- const zero = digits[0];
15136
- if (b2 != null && a2 >= b2) {
15137
- throw new Error(a2 + " >= " + b2);
15138
- }
15139
- if (a2.slice(-1) === zero || b2 && b2.slice(-1) === zero) {
15140
- throw new Error("trailing zero");
15141
- }
15142
- if (b2) {
15143
- let n = 0;
15144
- while ((a2[n] || zero) === b2[n]) {
15145
- n++;
15146
- }
15147
- if (n > 0) {
15148
- return b2.slice(0, n) + midpoint(a2.slice(n), b2.slice(n), digits);
15149
- }
15150
- }
15151
- const digitA = a2 ? digits.indexOf(a2[0]) : 0;
15152
- const digitB = b2 != null ? digits.indexOf(b2[0]) : digits.length;
15153
- if (digitB - digitA > 1) {
15154
- const midDigit = Math.round(0.5 * (digitA + digitB));
15155
- return digits[midDigit];
15156
- } else {
15157
- if (b2 && b2.length > 1) {
15158
- return b2.slice(0, 1);
15159
- } else {
15160
- return digits[digitA] + midpoint(a2.slice(1), null, digits);
15161
- }
15162
- }
15163
- }
15164
- function validateInteger(int) {
15165
- if (int.length !== getIntegerLength(int[0])) {
15166
- throw new Error("invalid integer part of order key: " + int);
15167
- }
15168
- }
15169
- function getIntegerLength(head) {
15170
- if (head >= "a" && head <= "z") {
15171
- return head.charCodeAt(0) - "a".charCodeAt(0) + 2;
15172
- } else if (head >= "A" && head <= "Z") {
15173
- return "Z".charCodeAt(0) - head.charCodeAt(0) + 2;
15174
- } else {
15175
- throw new Error("invalid order key head: " + head);
15176
- }
15177
- }
15178
- function getIntegerPart(key) {
15179
- const integerPartLength = getIntegerLength(key[0]);
15180
- if (integerPartLength > key.length) {
15181
- throw new Error("invalid order key: " + key);
15182
- }
15183
- return key.slice(0, integerPartLength);
15184
- }
15185
- function validateOrderKey(key, digits) {
15186
- if (key === "A" + digits[0].repeat(26)) {
15187
- throw new Error("invalid order key: " + key);
15188
- }
15189
- const i = getIntegerPart(key);
15190
- const f = key.slice(i.length);
15191
- if (f.slice(-1) === digits[0]) {
15192
- throw new Error("invalid order key: " + key);
15193
- }
15194
- }
15195
- function incrementInteger(x, digits) {
15196
- validateInteger(x);
15197
- const [head, ...digs] = x.split("");
15198
- let carry = true;
15199
- for (let i = digs.length - 1; carry && i >= 0; i--) {
15200
- const d = digits.indexOf(digs[i]) + 1;
15201
- if (d === digits.length) {
15202
- digs[i] = digits[0];
15203
- } else {
15204
- digs[i] = digits[d];
15205
- carry = false;
15206
- }
15207
- }
15208
- if (carry) {
15209
- if (head === "Z") {
15210
- return "a" + digits[0];
15211
- }
15212
- if (head === "z") {
15213
- return null;
15214
- }
15215
- const h = String.fromCharCode(head.charCodeAt(0) + 1);
15216
- if (h > "a") {
15217
- digs.push(digits[0]);
15218
- } else {
15219
- digs.pop();
15220
- }
15221
- return h + digs.join("");
15222
- } else {
15223
- return head + digs.join("");
15224
- }
15225
- }
15226
- function decrementInteger(x, digits) {
15227
- validateInteger(x);
15228
- const [head, ...digs] = x.split("");
15229
- let borrow = true;
15230
- for (let i = digs.length - 1; borrow && i >= 0; i--) {
15231
- const d = digits.indexOf(digs[i]) - 1;
15232
- if (d === -1) {
15233
- digs[i] = digits.slice(-1);
15234
- } else {
15235
- digs[i] = digits[d];
15236
- borrow = false;
15237
- }
15238
- }
15239
- if (borrow) {
15240
- if (head === "a") {
15241
- return "Z" + digits.slice(-1);
15242
- }
15243
- if (head === "A") {
15244
- return null;
15245
- }
15246
- const h = String.fromCharCode(head.charCodeAt(0) - 1);
15247
- if (h < "Z") {
15248
- digs.push(digits.slice(-1));
15249
- } else {
15250
- digs.pop();
15251
- }
15252
- return h + digs.join("");
15253
- } else {
15254
- return head + digs.join("");
15255
- }
15256
- }
15257
- function generateKeyBetween(a2, b2, digits = BASE_62_DIGITS) {
15258
- if (a2 != null) {
15259
- validateOrderKey(a2, digits);
15260
- }
15261
- if (b2 != null) {
15262
- validateOrderKey(b2, digits);
15263
- }
15264
- if (a2 != null && b2 != null && a2 >= b2) {
15265
- throw new Error(a2 + " >= " + b2);
15266
- }
15267
- if (a2 == null) {
15268
- if (b2 == null) {
15269
- return "a" + digits[0];
15270
- }
15271
- const ib2 = getIntegerPart(b2);
15272
- const fb2 = b2.slice(ib2.length);
15273
- if (ib2 === "A" + digits[0].repeat(26)) {
15274
- return ib2 + midpoint("", fb2, digits);
15275
- }
15276
- if (ib2 < b2) {
15277
- return ib2;
15278
- }
15279
- const res = decrementInteger(ib2, digits);
15280
- if (res == null) {
15281
- throw new Error("cannot decrement any more");
15282
- }
15283
- return res;
15284
- }
15285
- if (b2 == null) {
15286
- const ia2 = getIntegerPart(a2);
15287
- const fa2 = a2.slice(ia2.length);
15288
- const i2 = incrementInteger(ia2, digits);
15289
- return i2 == null ? ia2 + midpoint(fa2, null, digits) : i2;
15290
- }
15291
- const ia = getIntegerPart(a2);
15292
- const fa = a2.slice(ia.length);
15293
- const ib = getIntegerPart(b2);
15294
- const fb = b2.slice(ib.length);
15295
- if (ia === ib) {
15296
- return ia + midpoint(fa, fb, digits);
15297
- }
15298
- const i = incrementInteger(ia, digits);
15299
- if (i == null) {
15300
- throw new Error("cannot increment any more");
15301
- }
15302
- if (i < b2) {
15303
- return i;
15304
- }
15305
- return ia + midpoint(fa, null, digits);
15306
- }
15307
- function generateNKeysBetween(a2, b2, n, digits = BASE_62_DIGITS) {
15308
- if (n === 0) {
15309
- return [];
15310
- }
15311
- if (n === 1) {
15312
- return [generateKeyBetween(a2, b2, digits)];
15313
- }
15314
- if (b2 == null) {
15315
- let c2 = generateKeyBetween(a2, b2, digits);
15316
- const result = [c2];
15317
- for (let i = 0; i < n - 1; i++) {
15318
- c2 = generateKeyBetween(c2, b2, digits);
15319
- result.push(c2);
15320
- }
15321
- return result;
15322
- }
15323
- if (a2 == null) {
15324
- let c2 = generateKeyBetween(a2, b2, digits);
15325
- const result = [c2];
15326
- for (let i = 0; i < n - 1; i++) {
15327
- c2 = generateKeyBetween(a2, c2, digits);
15328
- result.push(c2);
15329
- }
15330
- result.reverse();
15331
- return result;
15332
- }
15333
- const mid = Math.floor(n / 2);
15334
- const c = generateKeyBetween(a2, b2, digits);
15335
- return [
15336
- ...generateNKeysBetween(a2, c, mid, digits),
15337
- c,
15338
- ...generateNKeysBetween(c, b2, n - mid - 1, digits)
15339
- ];
15340
- }
15341
-
15342
- // src/fractionalIndex.ts
15343
- import { arrayToMap as arrayToMap8 } from "@excalidraw/common";
15344
- var InvalidFractionalIndexError = class extends Error {
15345
- code = "ELEMENT_HAS_INVALID_INDEX";
15346
- };
15347
- var validateFractionalIndices = (elements, {
15348
- shouldThrow = false,
15349
- includeBoundTextValidation = false,
15350
- ignoreLogs,
15351
- reconciliationContext
15352
- }) => {
15353
- const errorMessages = [];
15354
- const stringifyElement = (element) => `${element?.index}:${element?.id}:${element?.type}:${element?.isDeleted}:${element?.version}:${element?.versionNonce}`;
15355
- const indices = elements.map((x) => x.index);
15356
- for (const [i, index] of indices.entries()) {
15357
- const predecessorIndex = indices[i - 1];
15358
- const successorIndex = indices[i + 1];
15359
- if (!isValidFractionalIndex(index, predecessorIndex, successorIndex)) {
15360
- errorMessages.push(
15361
- `Fractional indices invariant has been compromised: "${stringifyElement(
15362
- elements[i - 1]
15363
- )}", "${stringifyElement(elements[i])}", "${stringifyElement(
15364
- elements[i + 1]
15365
- )}"`
15366
- );
15367
- }
15368
- if (includeBoundTextValidation && hasBoundTextElement(elements[i])) {
15369
- const container = elements[i];
15370
- const text = getBoundTextElement(container, arrayToMap8(elements));
15371
- if (text && text.index <= container.index) {
15372
- errorMessages.push(
15373
- `Fractional indices invariant for bound elements has been compromised: "${stringifyElement(
15374
- text
15375
- )}", "${stringifyElement(container)}"`
15376
- );
15377
- }
15378
- }
15379
- }
15380
- if (errorMessages.length) {
15381
- const error = new InvalidFractionalIndexError();
15382
- const additionalContext = [];
15383
- if (reconciliationContext) {
15384
- additionalContext.push("Additional reconciliation context:");
15385
- additionalContext.push(
15386
- reconciliationContext.localElements.map((x) => stringifyElement(x))
15387
- );
15388
- additionalContext.push(
15389
- reconciliationContext.remoteElements.map((x) => stringifyElement(x))
15390
- );
15391
- }
15392
- if (!ignoreLogs) {
15393
- console.error(
15394
- errorMessages.join("\n\n"),
15395
- error.stack,
15396
- elements.map((x) => stringifyElement(x)),
15397
- ...additionalContext
15398
- );
15399
- }
15400
- if (shouldThrow) {
15401
- throw error;
15402
- }
15403
- }
15404
- };
15405
- var orderByFractionalIndex = (elements) => {
15406
- return elements.sort((a2, b2) => {
15407
- if (isOrderedElement(a2) && isOrderedElement(b2)) {
15408
- if (a2.index < b2.index) {
15409
- return -1;
15410
- } else if (a2.index > b2.index) {
15411
- return 1;
15412
- }
15413
- return a2.id < b2.id ? -1 : 1;
15414
- }
15415
- return 1;
15416
- });
15417
- };
15418
- var syncMovedIndices = (elements, movedElements) => {
15419
- try {
15420
- const elementsMap = arrayToMap8(elements);
15421
- const indicesGroups = getMovedIndicesGroups(elements, movedElements);
15422
- const elementsUpdates = generateIndices(elements, indicesGroups);
15423
- const elementsCandidates = elements.map((x) => {
15424
- const elementUpdates = elementsUpdates.get(x);
15425
- if (elementUpdates) {
15426
- return { ...x, index: elementUpdates.index };
15427
- }
15428
- return x;
15429
- });
15430
- validateFractionalIndices(
15431
- elementsCandidates,
15432
- // we don't autofix invalid bound text indices, hence don't include it in the validation
15433
- {
15434
- includeBoundTextValidation: false,
15435
- shouldThrow: true,
15436
- ignoreLogs: true
15437
- }
15438
- );
15439
- for (const [element, { index }] of elementsUpdates) {
15440
- mutateElement(element, elementsMap, { index });
15441
- }
15442
- } catch (e) {
15443
- syncInvalidIndices(elements);
15444
- }
15445
- return elements;
15446
- };
15447
- var syncInvalidIndices = (elements) => {
15448
- const elementsMap = arrayToMap8(elements);
15449
- const indicesGroups = getInvalidIndicesGroups(elements);
15450
- const elementsUpdates = generateIndices(elements, indicesGroups);
15451
- for (const [element, { index }] of elementsUpdates) {
15452
- mutateElement(element, elementsMap, { index });
15453
- }
15454
- return elements;
15455
- };
15456
- var syncInvalidIndicesImmutable = (elements) => {
15457
- const syncedElements = arrayToMap8(elements);
15458
- const indicesGroups = getInvalidIndicesGroups(elements);
15459
- const elementsUpdates = generateIndices(elements, indicesGroups);
15460
- for (const [element, { index }] of elementsUpdates) {
15461
- syncedElements.set(element.id, newElementWith(element, { index }));
15462
- }
15463
- return syncedElements;
15464
- };
15465
- var getMovedIndicesGroups = (elements, movedElements) => {
15466
- const indicesGroups = [];
15467
- let i = 0;
15468
- while (i < elements.length) {
15469
- if (movedElements.has(elements[i].id)) {
15470
- const indicesGroup = [i - 1, i];
15471
- while (++i < elements.length) {
15472
- if (!movedElements.has(elements[i].id)) {
15473
- break;
15474
- }
15475
- indicesGroup.push(i);
15476
- }
15477
- indicesGroup.push(i);
15478
- indicesGroups.push(indicesGroup);
15479
- } else {
15480
- i++;
15481
- }
15627
+ return nextAppStateSnapshot;
15482
15628
  }
15483
- return indicesGroups;
15484
- };
15485
- var getInvalidIndicesGroups = (elements) => {
15486
- const indicesGroups = [];
15487
- let lowerBound = void 0;
15488
- let upperBound = void 0;
15489
- let lowerBoundIndex = -1;
15490
- let upperBoundIndex = 0;
15491
- const getLowerBound = (index) => {
15492
- const lowerBound2 = elements[lowerBoundIndex] ? elements[lowerBoundIndex].index : void 0;
15493
- const candidate = elements[index - 1]?.index;
15494
- if (!lowerBound2 && candidate || // first lowerBound
15495
- lowerBound2 && candidate && candidate > lowerBound2) {
15496
- return [candidate, index - 1];
15629
+ maybeCreateElementsSnapshot(elements, options = {
15630
+ shouldCompareHashes: false
15631
+ }) {
15632
+ if (!elements) {
15633
+ return this.elements;
15497
15634
  }
15498
- return [lowerBound2, lowerBoundIndex];
15499
- };
15500
- const getUpperBound = (index) => {
15501
- const upperBound2 = elements[upperBoundIndex] ? elements[upperBoundIndex].index : void 0;
15502
- if (upperBound2 && index < upperBoundIndex) {
15503
- return [upperBound2, upperBoundIndex];
15635
+ const changedElements = this.detectChangedElements(elements, options);
15636
+ if (!changedElements?.size) {
15637
+ return this.elements;
15504
15638
  }
15505
- let i2 = upperBoundIndex;
15506
- while (++i2 < elements.length) {
15507
- const candidate = elements[i2]?.index;
15508
- if (!upperBound2 && candidate || // first upperBound
15509
- upperBound2 && candidate && candidate > upperBound2) {
15510
- return [candidate, i2];
15639
+ const elementsSnapshot = this.createElementsSnapshot(changedElements);
15640
+ return elementsSnapshot;
15641
+ }
15642
+ detectChangedAppState(nextObservedAppState, options = {
15643
+ shouldCompareHashes: false
15644
+ }) {
15645
+ if (this.appState === nextObservedAppState) {
15646
+ return;
15647
+ }
15648
+ const didAppStateChange = Delta.isRightDifferent(
15649
+ this.appState,
15650
+ nextObservedAppState
15651
+ );
15652
+ if (!didAppStateChange) {
15653
+ return;
15654
+ }
15655
+ const changedAppStateHash = hashString(
15656
+ JSON.stringify(nextObservedAppState)
15657
+ );
15658
+ if (options.shouldCompareHashes && this._lastChangedAppStateHash === changedAppStateHash) {
15659
+ return;
15660
+ }
15661
+ this._lastChangedAppStateHash = changedAppStateHash;
15662
+ return didAppStateChange;
15663
+ }
15664
+ /**
15665
+ * Detect if there are any changed elements.
15666
+ */
15667
+ detectChangedElements(nextElements, options = {
15668
+ shouldCompareHashes: false
15669
+ }) {
15670
+ if (this.elements === nextElements) {
15671
+ return;
15672
+ }
15673
+ const changedElements = /* @__PURE__ */ new Map();
15674
+ for (const prevElement of toIterable(this.elements)) {
15675
+ const nextElement = nextElements.get(prevElement.id);
15676
+ if (!nextElement) {
15677
+ changedElements.set(
15678
+ prevElement.id,
15679
+ newElementWith(prevElement, { isDeleted: true })
15680
+ );
15511
15681
  }
15512
15682
  }
15513
- return [void 0, i2];
15514
- };
15515
- let i = 0;
15516
- while (i < elements.length) {
15517
- const current = elements[i].index;
15518
- [lowerBound, lowerBoundIndex] = getLowerBound(i);
15519
- [upperBound, upperBoundIndex] = getUpperBound(i);
15520
- if (!isValidFractionalIndex(current, lowerBound, upperBound)) {
15521
- const indicesGroup = [lowerBoundIndex, i];
15522
- while (++i < elements.length) {
15523
- const current2 = elements[i].index;
15524
- const [nextLowerBound, nextLowerBoundIndex] = getLowerBound(i);
15525
- const [nextUpperBound, nextUpperBoundIndex] = getUpperBound(i);
15526
- if (isValidFractionalIndex(current2, nextLowerBound, nextUpperBound)) {
15527
- break;
15683
+ for (const nextElement of toIterable(nextElements)) {
15684
+ const prevElement = this.elements.get(nextElement.id);
15685
+ if (!prevElement || // element was added
15686
+ prevElement.version < nextElement.version) {
15687
+ if (isImageElement(nextElement) && !isInitializedImageElement(nextElement)) {
15688
+ continue;
15528
15689
  }
15529
- [lowerBound, lowerBoundIndex] = [nextLowerBound, nextLowerBoundIndex];
15530
- [upperBound, upperBoundIndex] = [nextUpperBound, nextUpperBoundIndex];
15531
- indicesGroup.push(i);
15690
+ changedElements.set(nextElement.id, nextElement);
15532
15691
  }
15533
- indicesGroup.push(upperBoundIndex);
15534
- indicesGroups.push(indicesGroup);
15535
- } else {
15536
- i++;
15537
15692
  }
15693
+ if (!changedElements.size) {
15694
+ return;
15695
+ }
15696
+ const changedElementsHash = hashElementsVersion(changedElements);
15697
+ if (options.shouldCompareHashes && this._lastChangedElementsHash === changedElementsHash) {
15698
+ return;
15699
+ }
15700
+ this._lastChangedElementsHash = changedElementsHash;
15701
+ return changedElements;
15538
15702
  }
15539
- return indicesGroups;
15540
- };
15541
- var isValidFractionalIndex = (index, predecessor, successor) => {
15542
- if (!index) {
15543
- return false;
15544
- }
15545
- if (predecessor && successor) {
15546
- return predecessor < index && index < successor;
15547
- }
15548
- if (!predecessor && successor) {
15549
- return index < successor;
15550
- }
15551
- if (predecessor && !successor) {
15552
- return predecessor < index;
15553
- }
15554
- return !!index;
15555
- };
15556
- var generateIndices = (elements, indicesGroups) => {
15557
- const elementsUpdates = /* @__PURE__ */ new Map();
15558
- for (const indices of indicesGroups) {
15559
- const lowerBoundIndex = indices.shift();
15560
- const upperBoundIndex = indices.pop();
15561
- const fractionalIndices = generateNKeysBetween(
15562
- elements[lowerBoundIndex]?.index,
15563
- elements[upperBoundIndex]?.index,
15564
- indices.length
15565
- );
15566
- for (let i = 0; i < indices.length; i++) {
15567
- const element = elements[indices[i]];
15568
- elementsUpdates.set(element, {
15569
- index: fractionalIndices[i]
15570
- });
15703
+ /**
15704
+ * Perform structural clone, deep cloning only elements that changed.
15705
+ */
15706
+ createElementsSnapshot(changedElements) {
15707
+ const clonedElements = /* @__PURE__ */ new Map();
15708
+ for (const prevElement of toIterable(this.elements)) {
15709
+ clonedElements.set(prevElement.id, prevElement);
15710
+ }
15711
+ for (const changedElement of toIterable(changedElements)) {
15712
+ clonedElements.set(changedElement.id, deepCopyElement(changedElement));
15571
15713
  }
15714
+ return clonedElements;
15572
15715
  }
15573
- return elementsUpdates;
15574
15716
  };
15575
- var isOrderedElement = (element) => {
15576
- if (element.index) {
15577
- return true;
15578
- }
15579
- return false;
15717
+ var hiddenObservedAppStateProp = "__observedAppState";
15718
+ var getDefaultObservedAppState = () => {
15719
+ return {
15720
+ name: null,
15721
+ editingGroupId: null,
15722
+ viewBackgroundColor: COLOR_PALETTE2.white,
15723
+ selectedElementIds: {},
15724
+ selectedGroupIds: {},
15725
+ selectedLinearElement: null,
15726
+ croppingElementId: null,
15727
+ activeLockedId: null,
15728
+ lockedMultiSelections: {}
15729
+ };
15730
+ };
15731
+ var getObservedAppState = (appState) => {
15732
+ const observedAppState = {
15733
+ name: appState.name,
15734
+ editingGroupId: appState.editingGroupId,
15735
+ viewBackgroundColor: appState.viewBackgroundColor,
15736
+ selectedElementIds: appState.selectedElementIds,
15737
+ selectedGroupIds: appState.selectedGroupIds,
15738
+ croppingElementId: appState.croppingElementId,
15739
+ activeLockedId: appState.activeLockedId,
15740
+ lockedMultiSelections: appState.lockedMultiSelections,
15741
+ selectedLinearElement: appState.selectedLinearElement ? {
15742
+ elementId: appState.selectedLinearElement.elementId,
15743
+ isEditing: !!appState.selectedLinearElement.isEditing
15744
+ } : null
15745
+ };
15746
+ Reflect.defineProperty(observedAppState, hiddenObservedAppStateProp, {
15747
+ value: true,
15748
+ enumerable: false
15749
+ });
15750
+ return observedAppState;
15580
15751
  };
15752
+ var isObservedAppState = (appState) => !!Reflect.get(appState, hiddenObservedAppStateProp);
15581
15753
 
15582
15754
  // src/Scene.ts
15583
15755
  init_define_import_meta_env();
@@ -15796,24 +15968,14 @@ var Scene = class {
15796
15968
  this.selectedElementsCache.cache.clear();
15797
15969
  this.callbacks.clear();
15798
15970
  }
15799
- insertElementAtIndex(element, index) {
15800
- if (!Number.isFinite(index) || index < 0) {
15801
- throw new Error(
15802
- "insertElementAtIndex can only be called with index >= 0"
15803
- );
15804
- }
15805
- const nextElements = [
15806
- ...this.elements.slice(0, index),
15807
- element,
15808
- ...this.elements.slice(index)
15809
- ];
15810
- syncMovedIndices2(nextElements, arrayToMap9([element]));
15811
- this.replaceAllElements(nextElements);
15812
- }
15971
+ /** low-level - generally use app.insertNewElements() */
15813
15972
  insertElementsAtIndex(elements, index) {
15814
15973
  if (!elements.length) {
15815
15974
  return;
15816
15975
  }
15976
+ if (index === null) {
15977
+ index = this.elements.length;
15978
+ }
15817
15979
  if (!Number.isFinite(index) || index < 0) {
15818
15980
  throw new Error(
15819
15981
  "insertElementAtIndex can only be called with index >= 0"
@@ -15827,16 +15989,9 @@ var Scene = class {
15827
15989
  syncMovedIndices2(nextElements, arrayToMap9(elements));
15828
15990
  this.replaceAllElements(nextElements);
15829
15991
  }
15992
+ /** low-level - generally use app.insertNewElement() */
15830
15993
  insertElement = (element) => {
15831
- const index = element.frameId ? this.getElementIndex(element.frameId) : this.elements.length;
15832
- this.insertElementAtIndex(element, index);
15833
- };
15834
- insertElements = (elements) => {
15835
- if (!elements.length) {
15836
- return;
15837
- }
15838
- const index = elements[0]?.frameId ? this.getElementIndex(elements[0].frameId) : this.elements.length;
15839
- this.insertElementsAtIndex(elements, index);
15994
+ this.insertElementsAtIndex([element], null);
15840
15995
  };
15841
15996
  getElementIndex(elementId) {
15842
15997
  return this.elements.findIndex((element) => element.id === elementId);
@@ -18252,19 +18407,19 @@ var embeddableURLValidator = (url, validateEmbeddable) => {
18252
18407
 
18253
18408
  // src/flowchart.ts
18254
18409
  init_define_import_meta_env();
18255
- import { KEYS as KEYS3, invariant as invariant11, toBrandedType as toBrandedType2 } from "@excalidraw/common";
18256
- import { pointFrom as pointFrom15 } from "@excalidraw/math";
18410
+ import { KEYS as KEYS2, invariant as invariant11, toBrandedType as toBrandedType2 } from "@excalidraw/common";
18411
+ import { pointFrom as pointFrom14 } from "@excalidraw/math";
18257
18412
  var VERTICAL_OFFSET = 100;
18258
18413
  var HORIZONTAL_OFFSET = 100;
18259
18414
  var getLinkDirectionFromKey = (key) => {
18260
18415
  switch (key) {
18261
- case KEYS3.ARROW_UP:
18416
+ case KEYS2.ARROW_UP:
18262
18417
  return "up";
18263
- case KEYS3.ARROW_DOWN:
18418
+ case KEYS2.ARROW_DOWN:
18264
18419
  return "down";
18265
- case KEYS3.ARROW_RIGHT:
18420
+ case KEYS2.ARROW_RIGHT:
18266
18421
  return "right";
18267
- case KEYS3.ARROW_LEFT:
18422
+ case KEYS2.ARROW_LEFT:
18268
18423
  return "left";
18269
18424
  default:
18270
18425
  return "right";
@@ -18533,7 +18688,7 @@ var createBindingArrow = (startBindingElement, endBindingElement, direction, app
18533
18688
  strokeWidth: startBindingElement.strokeWidth,
18534
18689
  opacity: startBindingElement.opacity,
18535
18690
  roughness: startBindingElement.roughness,
18536
- points: [pointFrom15(0, 0), pointFrom15(endX, endY)],
18691
+ points: [pointFrom14(0, 0), pointFrom14(endX, endY)],
18537
18692
  elbowed: true
18538
18693
  });
18539
18694
  const elementsMap = scene.getNonDeletedElementsMap();
@@ -18721,12 +18876,13 @@ var isNodeInFlowchart = (element, elementsMap) => {
18721
18876
 
18722
18877
  // src/arrows/focus.ts
18723
18878
  init_define_import_meta_env();
18724
- import { pointDistance as pointDistance8, pointFrom as pointFrom16 } from "@excalidraw/math";
18879
+ import { pointDistance as pointDistance8, pointFrom as pointFrom15 } from "@excalidraw/math";
18725
18880
  import { invariant as invariant12 } from "@excalidraw/common";
18726
18881
 
18727
18882
  // src/zindex.ts
18728
18883
  init_define_import_meta_env();
18729
18884
  import { arrayToMap as arrayToMap11, findIndex, findLastIndex as findLastIndex2 } from "@excalidraw/common";
18885
+ import { isFiniteNumber } from "@excalidraw/math";
18730
18886
  var isOfTargetFrame = (element, frameId) => {
18731
18887
  return element.frameId === frameId || element.id === frameId;
18732
18888
  };
@@ -18906,7 +19062,34 @@ var getTargetElementsMap = (elements, indices) => {
18906
19062
  return acc;
18907
19063
  }, /* @__PURE__ */ new Map());
18908
19064
  };
19065
+ var hasSameElementIds = (prevElements, nextElements) => {
19066
+ if (prevElements.length !== nextElements.length) {
19067
+ console.error(
19068
+ "z-index reordering failed: resulting array have different lengths"
19069
+ );
19070
+ return false;
19071
+ }
19072
+ const prevElementIdCounts = /* @__PURE__ */ new Map();
19073
+ for (const element of prevElements) {
19074
+ prevElementIdCounts.set(
19075
+ element.id,
19076
+ (prevElementIdCounts.get(element.id) || 0) + 1
19077
+ );
19078
+ }
19079
+ for (const element of nextElements) {
19080
+ const count = prevElementIdCounts.get(element.id);
19081
+ if (!count) {
19082
+ console.error(
19083
+ "z-index reordering failed: element id mismatch / duplicate ids"
19084
+ );
19085
+ return false;
19086
+ }
19087
+ prevElementIdCounts.set(element.id, count - 1);
19088
+ }
19089
+ return true;
19090
+ };
18909
19091
  var shiftElementsByOne = (elements, appState, direction, scene) => {
19092
+ const originalElements = elements;
18910
19093
  const indicesToMove = getIndicesToMove(elements, appState);
18911
19094
  const targetElementsMap = getTargetElementsMap(elements, indicesToMove);
18912
19095
  let groupedIndices = toContiguousGroups(indicesToMove);
@@ -18951,11 +19134,17 @@ var shiftElementsByOne = (elements, appState, direction, scene) => {
18951
19134
  ...trailingElements
18952
19135
  ];
18953
19136
  });
19137
+ if (!hasSameElementIds(originalElements, elements)) {
19138
+ return originalElements;
19139
+ }
18954
19140
  syncMovedIndices(elements, targetElementsMap);
18955
19141
  return elements;
18956
19142
  };
18957
19143
  var shiftElementsToEnd = (elements, appState, direction, containingFrame, elementsToBeMoved) => {
18958
19144
  const indicesToMove = getIndicesToMove(elements, appState, elementsToBeMoved);
19145
+ if (indicesToMove.length === 0) {
19146
+ return elements;
19147
+ }
18959
19148
  const targetElementsMap = getTargetElementsMap(elements, indicesToMove);
18960
19149
  const displacedElements = [];
18961
19150
  let leadingIndex;
@@ -19002,6 +19191,12 @@ var shiftElementsToEnd = (elements, appState, direction, containingFrame, elemen
19002
19191
  if (leadingIndex === -1) {
19003
19192
  leadingIndex = 0;
19004
19193
  }
19194
+ const isValidIndex = (index) => {
19195
+ return isFiniteNumber(index) && index >= 0;
19196
+ };
19197
+ if (!isValidIndex(leadingIndex) || !isValidIndex(trailingIndex) || leadingIndex > trailingIndex || indicesToMove.some((index) => index < leadingIndex || index > trailingIndex)) {
19198
+ return elements;
19199
+ }
19005
19200
  for (let index = leadingIndex; index < trailingIndex + 1; index++) {
19006
19201
  if (!indicesToMove.includes(index)) {
19007
19202
  displacedElements.push(elements[index]);
@@ -19021,6 +19216,9 @@ var shiftElementsToEnd = (elements, appState, direction, containingFrame, elemen
19021
19216
  ...targetElements,
19022
19217
  ...trailingElements
19023
19218
  ];
19219
+ if (!hasSameElementIds(elements, nextElements)) {
19220
+ return elements;
19221
+ }
19024
19222
  syncMovedIndices(nextElements, targetElementsMap);
19025
19223
  return nextElements;
19026
19224
  };
@@ -19062,7 +19260,7 @@ function shiftElementsAccountingForFrames(allElements, appState, direction, shif
19062
19260
  );
19063
19261
  for (const [frameId, children] of frameChildrenSets) {
19064
19262
  nextElements = shiftFunction(
19065
- allElements,
19263
+ nextElements,
19066
19264
  appState,
19067
19265
  direction,
19068
19266
  frameId,
@@ -19211,7 +19409,7 @@ var handleFocusPointDrag = (linearElementEditor, elementsMap, pointerCoords, sce
19211
19409
  const isStartBinding = linearElementEditor.draggedFocusPointBinding === "start";
19212
19410
  const binding = isStartBinding ? arrow.startBinding : arrow.endBinding;
19213
19411
  const { x: offsetX, y: offsetY } = linearElementEditor.pointerOffset;
19214
- const point = pointFrom16(
19412
+ const point = pointFrom15(
19215
19413
  pointerCoords.x - offsetX,
19216
19414
  pointerCoords.y - offsetY
19217
19415
  );
@@ -19294,7 +19492,7 @@ var handleFocusPointDrag = (linearElementEditor, elementsMap, pointerCoords, sce
19294
19492
  }
19295
19493
  };
19296
19494
  var handleFocusPointPointerDown = (arrow, pointerDownState, elementsMap, appState) => {
19297
- const pointerPos = pointFrom16(
19495
+ const pointerPos = pointFrom15(
19298
19496
  pointerDownState.origin.x,
19299
19497
  pointerDownState.origin.y
19300
19498
  );
@@ -19397,7 +19595,7 @@ var handleFocusPointPointerUp = (linearElementEditor, scene) => {
19397
19595
  };
19398
19596
  var handleFocusPointHover = (arrow, scenePointerX, scenePointerY, scene, appState) => {
19399
19597
  const elementsMap = scene.getNonDeletedElementsMap();
19400
- const pointerPos = pointFrom16(scenePointerX, scenePointerY);
19598
+ const pointerPos = pointFrom15(scenePointerX, scenePointerY);
19401
19599
  const hitThreshold = FOCUS_POINT_SIZE * 1.5 / appState.zoom.value;
19402
19600
  if (arrow.startBinding?.elementId) {
19403
19601
  const bindableElement = elementsMap.get(arrow.startBinding.elementId);
@@ -19618,8 +19816,8 @@ init_define_import_meta_env();
19618
19816
  import {
19619
19817
  pointCenter as pointCenter3,
19620
19818
  normalizeRadians as normalizeRadians2,
19621
- pointFrom as pointFrom17,
19622
- pointRotateRads as pointRotateRads14
19819
+ pointFrom as pointFrom16,
19820
+ pointRotateRads as pointRotateRads13
19623
19821
  } from "@excalidraw/math";
19624
19822
  import {
19625
19823
  MIN_FONT_SIZE,
@@ -19807,7 +20005,7 @@ var resizeSingleTextElement = (origElement, element, scene, transformHandleType,
19807
20005
  return;
19808
20006
  }
19809
20007
  if (transformHandleType.includes("n") || transformHandleType.includes("s")) {
19810
- const previousOrigin = pointFrom17(origElement.x, origElement.y);
20008
+ const previousOrigin = pointFrom16(origElement.x, origElement.y);
19811
20009
  const newOrigin = getResizedOrigin(
19812
20010
  previousOrigin,
19813
20011
  origElement.width,
@@ -19848,7 +20046,7 @@ var resizeSingleTextElement = (origElement, element, scene, transformHandleType,
19848
20046
  element.lineHeight
19849
20047
  );
19850
20048
  const newHeight = metrics2.height;
19851
- const previousOrigin = pointFrom17(origElement.x, origElement.y);
20049
+ const previousOrigin = pointFrom16(origElement.x, origElement.y);
19852
20050
  const newOrigin = getResizedOrigin(
19853
20051
  previousOrigin,
19854
20052
  origElement.width,
@@ -19885,9 +20083,9 @@ var rotateMultipleElements = (originalElements, elements, scene, pointerX, point
19885
20083
  const cx = (x1 + x2) / 2;
19886
20084
  const cy = (y1 + y2) / 2;
19887
20085
  const origAngle = originalElements.get(element.id)?.angle ?? element.angle;
19888
- const [rotatedCX, rotatedCY] = pointRotateRads14(
19889
- pointFrom17(cx, cy),
19890
- pointFrom17(centerX, centerY),
20086
+ const [rotatedCX, rotatedCY] = pointRotateRads13(
20087
+ pointFrom16(cx, cy),
20088
+ pointFrom16(centerX, centerY),
19891
20089
  centerAngle + origAngle - element.angle
19892
20090
  );
19893
20091
  const updates = isElbowArrow(element) ? {
@@ -19936,44 +20134,44 @@ var getResizeOffsetXY = (transformHandleType, selectedElements, elementsMap, x,
19936
20134
  const cx = (x1 + x2) / 2;
19937
20135
  const cy = (y1 + y2) / 2;
19938
20136
  const angle = selectedElements.length === 1 ? selectedElements[0].angle : 0;
19939
- [x, y] = pointRotateRads14(
19940
- pointFrom17(x, y),
19941
- pointFrom17(cx, cy),
20137
+ [x, y] = pointRotateRads13(
20138
+ pointFrom16(x, y),
20139
+ pointFrom16(cx, cy),
19942
20140
  -angle
19943
20141
  );
19944
20142
  switch (transformHandleType) {
19945
20143
  case "n":
19946
- return pointRotateRads14(
19947
- pointFrom17(x - (x1 + x2) / 2, y - y1),
19948
- pointFrom17(0, 0),
20144
+ return pointRotateRads13(
20145
+ pointFrom16(x - (x1 + x2) / 2, y - y1),
20146
+ pointFrom16(0, 0),
19949
20147
  angle
19950
20148
  );
19951
20149
  case "s":
19952
- return pointRotateRads14(
19953
- pointFrom17(x - (x1 + x2) / 2, y - y2),
19954
- pointFrom17(0, 0),
20150
+ return pointRotateRads13(
20151
+ pointFrom16(x - (x1 + x2) / 2, y - y2),
20152
+ pointFrom16(0, 0),
19955
20153
  angle
19956
20154
  );
19957
20155
  case "w":
19958
- return pointRotateRads14(
19959
- pointFrom17(x - x1, y - (y1 + y2) / 2),
19960
- pointFrom17(0, 0),
20156
+ return pointRotateRads13(
20157
+ pointFrom16(x - x1, y - (y1 + y2) / 2),
20158
+ pointFrom16(0, 0),
19961
20159
  angle
19962
20160
  );
19963
20161
  case "e":
19964
- return pointRotateRads14(
19965
- pointFrom17(x - x2, y - (y1 + y2) / 2),
19966
- pointFrom17(0, 0),
20162
+ return pointRotateRads13(
20163
+ pointFrom16(x - x2, y - (y1 + y2) / 2),
20164
+ pointFrom16(0, 0),
19967
20165
  angle
19968
20166
  );
19969
20167
  case "nw":
19970
- return pointRotateRads14(pointFrom17(x - x1, y - y1), pointFrom17(0, 0), angle);
20168
+ return pointRotateRads13(pointFrom16(x - x1, y - y1), pointFrom16(0, 0), angle);
19971
20169
  case "ne":
19972
- return pointRotateRads14(pointFrom17(x - x2, y - y1), pointFrom17(0, 0), angle);
20170
+ return pointRotateRads13(pointFrom16(x - x2, y - y1), pointFrom16(0, 0), angle);
19973
20171
  case "sw":
19974
- return pointRotateRads14(pointFrom17(x - x1, y - y2), pointFrom17(0, 0), angle);
20172
+ return pointRotateRads13(pointFrom16(x - x1, y - y2), pointFrom16(0, 0), angle);
19975
20173
  case "se":
19976
- return pointRotateRads14(pointFrom17(x - x2, y - y2), pointFrom17(0, 0), angle);
20174
+ return pointRotateRads13(pointFrom16(x - x2, y - y2), pointFrom16(0, 0), angle);
19977
20175
  default:
19978
20176
  return [0, 0];
19979
20177
  }
@@ -20136,10 +20334,10 @@ var resizeSingleElement = (nextWidth, nextHeight, latestElement, origElement, or
20136
20334
  nextHeight,
20137
20335
  true
20138
20336
  );
20139
- let previousOrigin = pointFrom17(origElement.x, origElement.y);
20337
+ let previousOrigin = pointFrom16(origElement.x, origElement.y);
20140
20338
  if (isLinearElement(origElement)) {
20141
20339
  const [x1, y1] = getElementBounds(origElement, originalElementsMap);
20142
- previousOrigin = pointFrom17(x1, y1);
20340
+ previousOrigin = pointFrom16(x1, y1);
20143
20341
  }
20144
20342
  const newOrigin = getResizedOrigin(
20145
20343
  previousOrigin,
@@ -20162,7 +20360,7 @@ var resizeSingleElement = (nextWidth, nextHeight, latestElement, origElement, or
20162
20360
  newOrigin.x += scaledX;
20163
20361
  newOrigin.y += scaledY;
20164
20362
  rescaledPoints.points = rescaledPoints.points.map(
20165
- (p) => pointFrom17(p[0] - scaledX, p[1] - scaledY)
20363
+ (p) => pointFrom16(p[0] - scaledX, p[1] - scaledY)
20166
20364
  );
20167
20365
  }
20168
20366
  if (nextWidth < 0) {
@@ -20238,11 +20436,11 @@ var getNextSingleWidthAndHeightFromPointer = (latestElement, origElement, handle
20238
20436
  origElement.height,
20239
20437
  true
20240
20438
  );
20241
- const startTopLeft = pointFrom17(x1, y1);
20242
- const startBottomRight = pointFrom17(x2, y2);
20439
+ const startTopLeft = pointFrom16(x1, y1);
20440
+ const startBottomRight = pointFrom16(x2, y2);
20243
20441
  const startCenter = pointCenter3(startTopLeft, startBottomRight);
20244
- const rotatedPointer = pointRotateRads14(
20245
- pointFrom17(pointerX, pointerY),
20442
+ const rotatedPointer = pointRotateRads13(
20443
+ pointFrom16(pointerX, pointerY),
20246
20444
  startCenter,
20247
20445
  -origElement.angle
20248
20446
  );
@@ -20612,9 +20810,9 @@ var resizeMultipleElements = (selectedElements, elementsMap, handleDirection, sc
20612
20810
  // src/resizeTest.ts
20613
20811
  init_define_import_meta_env();
20614
20812
  import {
20615
- pointFrom as pointFrom19,
20813
+ pointFrom as pointFrom18,
20616
20814
  pointOnLineSegment,
20617
- pointRotateRads as pointRotateRads16
20815
+ pointRotateRads as pointRotateRads15
20618
20816
  } from "@excalidraw/math";
20619
20817
  import {
20620
20818
  SIDE_RESIZING_THRESHOLD
@@ -20625,7 +20823,7 @@ init_define_import_meta_env();
20625
20823
  import {
20626
20824
  DEFAULT_TRANSFORM_HANDLE_SPACING
20627
20825
  } from "@excalidraw/common";
20628
- import { pointFrom as pointFrom18, pointRotateRads as pointRotateRads15 } from "@excalidraw/math";
20826
+ import { pointFrom as pointFrom17, pointRotateRads as pointRotateRads14 } from "@excalidraw/math";
20629
20827
  var transformHandleSizes = {
20630
20828
  mouse: 8,
20631
20829
  pen: 16,
@@ -20666,9 +20864,9 @@ var OMIT_SIDES_FOR_LINE_BACKSLASH = {
20666
20864
  w: true
20667
20865
  };
20668
20866
  var generateTransformHandle = (x, y, width, height, cx, cy, angle) => {
20669
- const [xx, yy] = pointRotateRads15(
20670
- pointFrom18(x + width / 2, y + height / 2),
20671
- pointFrom18(cx, cy),
20867
+ const [xx, yy] = pointRotateRads14(
20868
+ pointFrom17(x + width / 2, y + height / 2),
20869
+ pointFrom17(cx, cy),
20672
20870
  angle
20673
20871
  );
20674
20872
  return [xx - width / 2, yy - height / 2, width, height];
@@ -20882,14 +21080,14 @@ var resizeTest = (element, elementsMap, appState, x, y, zoom, pointerType, edito
20882
21080
  const SPACING = isImageElement(element) ? 0 : SIDE_RESIZING_THRESHOLD / zoom.value;
20883
21081
  const ZOOMED_SIDE_RESIZING_THRESHOLD = SIDE_RESIZING_THRESHOLD / zoom.value;
20884
21082
  const sides = getSelectionBorders(
20885
- pointFrom19(x1 - SPACING, y1 - SPACING),
20886
- pointFrom19(x2 + SPACING, y2 + SPACING),
20887
- pointFrom19(cx, cy),
21083
+ pointFrom18(x1 - SPACING, y1 - SPACING),
21084
+ pointFrom18(x2 + SPACING, y2 + SPACING),
21085
+ pointFrom18(cx, cy),
20888
21086
  element.angle
20889
21087
  );
20890
21088
  for (const [dir, side] of Object.entries(sides)) {
20891
21089
  if (pointOnLineSegment(
20892
- pointFrom19(x, y),
21090
+ pointFrom18(x, y),
20893
21091
  side,
20894
21092
  ZOOMED_SIDE_RESIZING_THRESHOLD
20895
21093
  )) {
@@ -20938,14 +21136,14 @@ var getTransformHandleTypeFromCoords = ([x1, y1, x2, y2], scenePointerX, scenePo
20938
21136
  const cy = (y1 + y2) / 2;
20939
21137
  const SPACING = SIDE_RESIZING_THRESHOLD / zoom.value;
20940
21138
  const sides = getSelectionBorders(
20941
- pointFrom19(x1 - SPACING, y1 - SPACING),
20942
- pointFrom19(x2 + SPACING, y2 + SPACING),
20943
- pointFrom19(cx, cy),
21139
+ pointFrom18(x1 - SPACING, y1 - SPACING),
21140
+ pointFrom18(x2 + SPACING, y2 + SPACING),
21141
+ pointFrom18(cx, cy),
20944
21142
  0
20945
21143
  );
20946
21144
  for (const [dir, side] of Object.entries(sides)) {
20947
21145
  if (pointOnLineSegment(
20948
- pointFrom19(scenePointerX, scenePointerY),
21146
+ pointFrom18(scenePointerX, scenePointerY),
20949
21147
  side,
20950
21148
  SPACING
20951
21149
  )) {
@@ -21002,10 +21200,10 @@ var getCursorForResizingElement = (resizingElement) => {
21002
21200
  return cursor ? `${cursor}-resize` : "";
21003
21201
  };
21004
21202
  var getSelectionBorders = ([x1, y1], [x2, y2], center, angle) => {
21005
- const topLeft = pointRotateRads16(pointFrom19(x1, y1), center, angle);
21006
- const topRight = pointRotateRads16(pointFrom19(x2, y1), center, angle);
21007
- const bottomLeft = pointRotateRads16(pointFrom19(x1, y2), center, angle);
21008
- const bottomRight = pointRotateRads16(pointFrom19(x2, y2), center, angle);
21203
+ const topLeft = pointRotateRads15(pointFrom18(x1, y1), center, angle);
21204
+ const topRight = pointRotateRads15(pointFrom18(x2, y1), center, angle);
21205
+ const bottomLeft = pointRotateRads15(pointFrom18(x1, y2), center, angle);
21206
+ const bottomRight = pointRotateRads15(pointFrom18(x2, y2), center, angle);
21009
21207
  return {
21010
21208
  n: [topLeft, topRight],
21011
21209
  e: [topRight, bottomRight],
@@ -21022,7 +21220,7 @@ var showSelectedShapeActions = (appState, elements) => Boolean(
21022
21220
 
21023
21221
  // src/transform.ts
21024
21222
  init_define_import_meta_env();
21025
- import { pointFrom as pointFrom20 } from "@excalidraw/math";
21223
+ import { pointFrom as pointFrom19 } from "@excalidraw/math";
21026
21224
  import {
21027
21225
  DEFAULT_FONT_FAMILY as DEFAULT_FONT_FAMILY3,
21028
21226
  DEFAULT_FONT_SIZE as DEFAULT_FONT_SIZE4,
@@ -21304,7 +21502,7 @@ var convertToExcalidrawElements = (elementsSkeleton, opts) => {
21304
21502
  excalidrawElement = newLinearElement({
21305
21503
  width,
21306
21504
  height,
21307
- points: [pointFrom20(0, 0), pointFrom20(width, height)],
21505
+ points: [pointFrom19(0, 0), pointFrom19(width, height)],
21308
21506
  ...element
21309
21507
  });
21310
21508
  break;
@@ -21316,7 +21514,7 @@ var convertToExcalidrawElements = (elementsSkeleton, opts) => {
21316
21514
  width,
21317
21515
  height,
21318
21516
  endArrowhead: "arrow",
21319
- points: [pointFrom20(0, 0), pointFrom20(width, height)],
21517
+ points: [pointFrom19(0, 0), pointFrom19(width, height)],
21320
21518
  ...element,
21321
21519
  type: "arrow"
21322
21520
  });
@@ -21559,6 +21757,33 @@ var maybeHandleArrowPointlikeDrag = ({
21559
21757
  return false;
21560
21758
  };
21561
21759
 
21760
+ // src/arrowheads.ts
21761
+ init_define_import_meta_env();
21762
+ var normalizeArrowhead = (arrowhead) => {
21763
+ switch (arrowhead) {
21764
+ case void 0:
21765
+ case null:
21766
+ return null;
21767
+ case "dot":
21768
+ return "circle";
21769
+ case "crowfoot_one":
21770
+ return "cardinality_one";
21771
+ case "crowfoot_many":
21772
+ return "cardinality_many";
21773
+ case "crowfoot_one_or_many":
21774
+ return "cardinality_one_or_many";
21775
+ default:
21776
+ return arrowhead;
21777
+ }
21778
+ };
21779
+ var getArrowheadForPicker = (arrowhead) => {
21780
+ const normalizedArrowhead = normalizeArrowhead(arrowhead);
21781
+ if (normalizedArrowhead === null) {
21782
+ return null;
21783
+ }
21784
+ return normalizedArrowhead;
21785
+ };
21786
+
21562
21787
  // src/index.ts
21563
21788
  var getSceneVersion = (elements) => elements.reduce((acc, el) => acc + el.version, 0);
21564
21789
  var hashElementsVersion = (elements) => {
@@ -21629,6 +21854,7 @@ export {
21629
21854
  bindOrUnbindBindingElements,
21630
21855
  bindPointToSnapToElementOutline,
21631
21856
  bindingProperties,
21857
+ boundsContainBounds,
21632
21858
  bumpVersion,
21633
21859
  calculateFixedPointForElbowArrowBinding,
21634
21860
  calculateFixedPointForNonElbowArrowBinding,
@@ -21666,6 +21892,7 @@ export {
21666
21892
  elementWithCanvasCache,
21667
21893
  elementsAreInFrameBounds,
21668
21894
  elementsAreInSameGroup,
21895
+ elementsOverlappingBBox,
21669
21896
  embeddableURLValidator,
21670
21897
  excludeElementsInFramesFromSelection,
21671
21898
  filterElementsEligibleAsFrameChildren,
@@ -21675,11 +21902,13 @@ export {
21675
21902
  frameAndChildrenSelectedTogether,
21676
21903
  generateLinearCollisionShape,
21677
21904
  generateRoughOptions,
21905
+ getActiveTextElement,
21678
21906
  getAllHoveredElementAtPoint,
21679
21907
  getApproxMinLineHeight,
21680
21908
  getApproxMinLineWidth,
21681
21909
  getArrowLocalFixedPoints,
21682
21910
  getArrowheadAngle,
21911
+ getArrowheadForPicker,
21683
21912
  getArrowheadPoints,
21684
21913
  getArrowheadSize,
21685
21914
  getBindingGap,
@@ -21695,6 +21924,7 @@ export {
21695
21924
  getClosestElementBounds,
21696
21925
  getCommonBoundingBox,
21697
21926
  getCommonBounds,
21927
+ getCommonFrameId,
21698
21928
  getContainerCenter,
21699
21929
  getContainerCoords,
21700
21930
  getContainerElement,
@@ -21724,6 +21954,7 @@ export {
21724
21954
  getEmbedLink,
21725
21955
  getFlipAdjustedCropPosition,
21726
21956
  getFrameChildren,
21957
+ getFrameChildrenInsertionIndex,
21727
21958
  getFrameLikeElements,
21728
21959
  getFrameLikeTitle,
21729
21960
  getFreedrawOutlineAsSegments,
@@ -21782,6 +22013,7 @@ export {
21782
22013
  getVisibleAndNonSelectedElements,
21783
22014
  getVisibleElements,
21784
22015
  getVisibleSceneBounds,
22016
+ getWrappedTextLines,
21785
22017
  groupByFrameLikes,
21786
22018
  groupsAreAtLeastIntersectingTheFrame,
21787
22019
  groupsAreCompletelyOutOfFrame,
@@ -21826,6 +22058,7 @@ export {
21826
22058
  isElementInViewport,
21827
22059
  isElementIntersectingFrame,
21828
22060
  isElementLink,
22061
+ isEligibleFrameChildType,
21829
22062
  isEmbeddableElement,
21830
22063
  isExcalidrawElement,
21831
22064
  isFixedPoint,
@@ -21887,6 +22120,7 @@ export {
21887
22120
  newLinearElement,
21888
22121
  newMagicFrameElement,
21889
22122
  newTextElement,
22123
+ normalizeArrowhead,
21890
22124
  normalizeElementOrder,
21891
22125
  normalizeFixedPoint,
21892
22126
  normalizeSVG,
@@ -21898,6 +22132,7 @@ export {
21898
22132
  parseElementLinkFromURL,
21899
22133
  parseTokens,
21900
22134
  pointInsideBounds,
22135
+ pointInsideBoundsInclusive,
21901
22136
  positionElementsOnGrid,
21902
22137
  projectFixedPointOntoDiagonal,
21903
22138
  redrawTextBoundingBox,
@@ -21920,7 +22155,6 @@ export {
21920
22155
  setCustomTextMetricsProvider,
21921
22156
  shouldAllowVerticalAlign,
21922
22157
  shouldApplyFrameClip,
21923
- shouldEnableBindingForPointerEvent,
21924
22158
  shouldTestInside,
21925
22159
  showSelectedShapeActions,
21926
22160
  snapToMid,