@myoc/element 0.19.506 → 0.19.508

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 (57) hide show
  1. package/dist/dev/index.js +875 -983
  2. package/dist/dev/index.js.map +4 -4
  3. package/dist/prod/index.js +12 -12
  4. package/dist/types/element/src/Scene.d.ts +5 -3
  5. package/dist/types/element/src/bounds.d.ts +4 -2
  6. package/dist/types/element/src/duplicate.d.ts +1 -0
  7. package/dist/types/element/src/frame.d.ts +7 -6
  8. package/dist/types/element/src/linearElementEditor.d.ts +2 -2
  9. package/dist/types/element/src/selection.d.ts +2 -2
  10. package/dist/types/element/src/shape.d.ts +1 -1
  11. package/dist/types/element/src/typeChecks.d.ts +1 -0
  12. package/dist/types/element/src/utils.d.ts +1 -1
  13. package/dist/types/excalidraw/actions/actionAddToLibrary.d.ts +3 -3
  14. package/dist/types/excalidraw/actions/actionBoundText.d.ts +2 -2
  15. package/dist/types/excalidraw/actions/actionCanvas.d.ts +11 -11
  16. package/dist/types/excalidraw/actions/actionClipboard.d.ts +2 -2
  17. package/dist/types/excalidraw/actions/actionCropEditor.d.ts +1 -1
  18. package/dist/types/excalidraw/actions/actionDeleteSelected.d.ts +3 -3
  19. package/dist/types/excalidraw/actions/actionDeselect.d.ts +168 -0
  20. package/dist/types/excalidraw/actions/actionElementLink.d.ts +1 -1
  21. package/dist/types/excalidraw/actions/actionElementLock.d.ts +2 -2
  22. package/dist/types/excalidraw/actions/actionEmbeddable.d.ts +1 -1
  23. package/dist/types/excalidraw/actions/actionExport.d.ts +2 -2
  24. package/dist/types/excalidraw/actions/actionFrame.d.ts +4 -4
  25. package/dist/types/excalidraw/actions/actionGroup.d.ts +2 -2
  26. package/dist/types/excalidraw/actions/actionLinearEditor.d.ts +1 -1
  27. package/dist/types/excalidraw/actions/actionLink.d.ts +1 -1
  28. package/dist/types/excalidraw/actions/actionMenu.d.ts +1 -1
  29. package/dist/types/excalidraw/actions/actionProperties.d.ts +2 -2
  30. package/dist/types/excalidraw/actions/actionSelectAll.d.ts +1 -1
  31. package/dist/types/excalidraw/actions/actionStyles.d.ts +1 -1
  32. package/dist/types/excalidraw/actions/actionToggleArrowBinding.d.ts +1 -1
  33. package/dist/types/excalidraw/actions/actionToggleGridMode.d.ts +1 -1
  34. package/dist/types/excalidraw/actions/actionToggleMidpointSnapping.d.ts +1 -1
  35. package/dist/types/excalidraw/actions/actionToggleObjectsSnapMode.d.ts +1 -1
  36. package/dist/types/excalidraw/actions/actionToggleSearchMenu.d.ts +1 -1
  37. package/dist/types/excalidraw/actions/actionToggleStats.d.ts +1 -1
  38. package/dist/types/excalidraw/actions/actionToggleViewMode.d.ts +1 -1
  39. package/dist/types/excalidraw/actions/actionToggleZenMode.d.ts +1 -1
  40. package/dist/types/excalidraw/actions/index.d.ts +1 -0
  41. package/dist/types/excalidraw/actions/types.d.ts +1 -1
  42. package/dist/types/excalidraw/appState.d.ts +1 -0
  43. package/dist/types/excalidraw/components/App.d.ts +9 -0
  44. package/dist/types/excalidraw/components/PublishLibrary.d.ts +3 -2
  45. package/dist/types/excalidraw/components/canvases/InteractiveCanvas.d.ts +1 -1
  46. package/dist/types/excalidraw/components/canvases/NewElementCanvas.d.ts +1 -0
  47. package/dist/types/excalidraw/components/canvases/StaticCanvas.d.ts +1 -1
  48. package/dist/types/excalidraw/components/dropdownMenu/DropdownMenuItemContentRadio.d.ts +2 -1
  49. package/dist/types/excalidraw/components/main-menu/DefaultItems.d.ts +1 -0
  50. package/dist/types/excalidraw/data/blob.d.ts +4 -8
  51. package/dist/types/excalidraw/data/json.d.ts +1 -1
  52. package/dist/types/excalidraw/scene/Renderer.d.ts +425 -19
  53. package/dist/types/excalidraw/types.d.ts +15 -2
  54. package/dist/types/fractional-indexing/src/index.d.ts +29 -0
  55. package/dist/types/math/src/constants.d.ts +0 -1
  56. package/dist/types/math/src/curve.d.ts +4 -1
  57. package/package.json +6 -4
package/dist/dev/index.js CHANGED
@@ -3862,14 +3862,14 @@ var rough_default = {
3862
3862
 
3863
3863
  // src/bounds.ts
3864
3864
  import {
3865
- arrayToMap as arrayToMap8,
3865
+ arrayToMap as arrayToMap7,
3866
3866
  invariant as invariant10,
3867
3867
  rescalePoints,
3868
3868
  sizeOf
3869
3869
  } from "@excalidraw/common";
3870
3870
  import {
3871
3871
  degreesToRadians,
3872
- lineSegment as lineSegment6,
3872
+ lineSegment as lineSegment7,
3873
3873
  pointDistance as pointDistance7,
3874
3874
  pointFrom as pointFrom14,
3875
3875
  pointFromArray as pointFromArray3,
@@ -4233,7 +4233,7 @@ import {
4233
4233
  init_define_import_meta_env();
4234
4234
  import {
4235
4235
  isRightAngleRads,
4236
- lineSegment as lineSegment5,
4236
+ lineSegment as lineSegment6,
4237
4237
  pointFrom as pointFrom12,
4238
4238
  pointRotateRads as pointRotateRads11
4239
4239
  } from "@excalidraw/math";
@@ -4969,6 +4969,25 @@ var canBecomePolygon = (points) => {
4969
4969
  return points.length > 3 || // 3-point polygons can't have all points in a single line
4970
4970
  points.length === 3 && !pointsEqual(points[0], points[points.length - 1]);
4971
4971
  };
4972
+ var isEligibleFrameChildType = (type) => {
4973
+ switch (type) {
4974
+ case "rectangle":
4975
+ case "diamond":
4976
+ case "ellipse":
4977
+ case "arrow":
4978
+ case "line":
4979
+ case "freedraw":
4980
+ case "text":
4981
+ case "image":
4982
+ case "frame":
4983
+ case "embeddable": {
4984
+ return true;
4985
+ }
4986
+ default: {
4987
+ return false;
4988
+ }
4989
+ }
4990
+ };
4972
4991
 
4973
4992
  // src/utils.ts
4974
4993
  var ElementShapesCache = /* @__PURE__ */ new WeakMap();
@@ -5003,12 +5022,12 @@ var setElementShapesCacheEntry = (element, shape, offset) => {
5003
5022
  }
5004
5023
  shapes.set(offset, shape);
5005
5024
  };
5006
- function deconstructLinearOrFreeDrawElement(element) {
5025
+ function deconstructLinearOrFreeDrawElement(element, elementsMap) {
5007
5026
  const cachedShape = getElementShapesCacheEntry(element, 0);
5008
5027
  if (cachedShape) {
5009
5028
  return cachedShape;
5010
5029
  }
5011
- const ops = generateLinearCollisionShape(element);
5030
+ const ops = generateLinearCollisionShape(element, elementsMap);
5012
5031
  const lines = [];
5013
5032
  const curves = [];
5014
5033
  for (let idx = 0; idx < ops.length; idx += 1) {
@@ -5415,7 +5434,7 @@ var getSnapOutlineMidPoint = (point, element, elementsMap, zoom) => {
5415
5434
  )
5416
5435
  ];
5417
5436
  const candidate = sideMidpoints.find(
5418
- (midpoint2) => pointDistance2(point, midpoint2) <= maxBindingDistance_simple(zoom) + element.strokeWidth / 2 && !hitElementItself({
5437
+ (midpoint) => pointDistance2(point, midpoint) <= maxBindingDistance_simple(zoom) + element.strokeWidth / 2 && !hitElementItself({
5419
5438
  point,
5420
5439
  element,
5421
5440
  threshold: 0,
@@ -6340,7 +6359,8 @@ var getContainerCenter = (container, appState, elementsMap) => {
6340
6359
  if (!midSegmentMidpoint) {
6341
6360
  midSegmentMidpoint = LinearElementEditor.getSegmentMidPoint(
6342
6361
  container,
6343
- index + 1
6362
+ index + 1,
6363
+ elementsMap
6344
6364
  );
6345
6365
  }
6346
6366
  return { x: midSegmentMidpoint[0], y: midSegmentMidpoint[1] };
@@ -6491,7 +6511,7 @@ var distanceToElement = (element, elementsMap, p) => {
6491
6511
  case "line":
6492
6512
  case "arrow":
6493
6513
  case "freedraw":
6494
- return distanceToLinearOrFreeDraElement(element, p);
6514
+ return distanceToLinearOrFreeDraElement(element, elementsMap, p);
6495
6515
  }
6496
6516
  };
6497
6517
  var distanceToRectanguloidElement = (element, elementsMap, p) => {
@@ -6520,20 +6540,33 @@ var distanceToEllipseElement = (element, elementsMap, p) => {
6520
6540
  ellipse2(center, element.width / 2, element.height / 2)
6521
6541
  );
6522
6542
  };
6523
- var distanceToLinearOrFreeDraElement = (element, p) => {
6524
- const [lines, curves] = deconstructLinearOrFreeDrawElement(element);
6543
+ var distanceToLinearOrFreeDraElement = (element, elementsMap, p) => {
6544
+ const [lines, curves] = deconstructLinearOrFreeDrawElement(
6545
+ element,
6546
+ elementsMap
6547
+ );
6525
6548
  return Math.min(
6526
6549
  ...lines.map((s) => distanceToLineSegment(p, s)),
6527
6550
  ...curves.map((a2) => curvePointDistance(a2, p))
6528
6551
  );
6529
6552
  };
6530
6553
 
6554
+ // src/comparisons.ts
6555
+ init_define_import_meta_env();
6556
+ var hasBackground = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "ellipse" || type === "diamond" || type === "line" || type === "freedraw";
6557
+ var hasStrokeColor = (type) => type === "rectangle" || type === "ellipse" || type === "diamond" || type === "freedraw" || type === "arrow" || type === "line" || type === "text" || type === "embeddable";
6558
+ var hasStrokeWidth = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "ellipse" || type === "diamond" || type === "freedraw" || type === "arrow" || type === "line";
6559
+ var hasStrokeStyle = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "ellipse" || type === "diamond" || type === "arrow" || type === "line";
6560
+ var canChangeRoundness = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "line" || type === "diamond" || type === "image";
6561
+ var toolIsArrow = (type) => type === "arrow";
6562
+ var canHaveArrowheads = (type) => type === "arrow";
6563
+
6531
6564
  // src/collision.ts
6532
6565
  var shouldTestInside = (element) => {
6533
6566
  if (element.type === "arrow") {
6534
6567
  return false;
6535
6568
  }
6536
- const isDraggableFromInside = !isTransparent(element.backgroundColor) || hasBoundTextElement(element) || isIframeLikeElement(element) || isTextElement(element);
6569
+ const isDraggableFromInside = hasBackground(element.type) && !isTransparent(element.backgroundColor) || hasBoundTextElement(element) || isIframeLikeElement(element) || isTextElement(element);
6537
6570
  if (element.type === "line") {
6538
6571
  return isDraggableFromInside && isPathALoop(element.points);
6539
6572
  }
@@ -6570,14 +6603,11 @@ var hitElementItself = ({
6570
6603
  )
6571
6604
  ) : false;
6572
6605
  const bounds = getElementBounds(element, elementsMap, true);
6573
- const hitBounds = isPointWithinBounds(
6574
- pointFrom5(bounds[0] - threshold, bounds[1] - threshold),
6575
- pointRotateRads6(
6576
- point,
6577
- getCenterForBounds(bounds),
6578
- -element.angle
6579
- ),
6580
- pointFrom5(bounds[2] + threshold, bounds[3] + threshold)
6606
+ const hitBounds = isPointInRotatedBounds(
6607
+ point,
6608
+ bounds,
6609
+ element.angle,
6610
+ threshold
6581
6611
  );
6582
6612
  if (!hitBounds && !hitFrameName) {
6583
6613
  return false;
@@ -6595,13 +6625,17 @@ var hitElementItself = ({
6595
6625
  cachedHit = result;
6596
6626
  return result;
6597
6627
  };
6628
+ var isPointInRotatedBounds = (point, bounds, angle, tolerance = 0) => {
6629
+ const adjustedPoint = angle === 0 ? point : pointRotateRads6(point, getCenterForBounds(bounds), -angle);
6630
+ return isPointWithinBounds(
6631
+ pointFrom5(bounds[0] - tolerance, bounds[1] - tolerance),
6632
+ adjustedPoint,
6633
+ pointFrom5(bounds[2] + tolerance, bounds[3] + tolerance)
6634
+ );
6635
+ };
6598
6636
  var hitElementBoundingBox = (point, element, elementsMap, tolerance = 0) => {
6599
- let [x1, y1, x2, y2] = getElementBounds(element, elementsMap);
6600
- x1 -= tolerance;
6601
- y1 -= tolerance;
6602
- x2 += tolerance;
6603
- y2 += tolerance;
6604
- return isPointWithinBounds(pointFrom5(x1, y1), point, pointFrom5(x2, y2));
6637
+ const bounds = getElementBounds(element, elementsMap, true);
6638
+ return isPointInRotatedBounds(point, bounds, element.angle, tolerance);
6605
6639
  };
6606
6640
  var hitElementBoundingBoxOnly = (hitArgs, elementsMap) => !hitElementItself(hitArgs) && // bound text is considered part of the element (even if it's outside the bounding box)
6607
6641
  !hitElementBoundText(hitArgs.point, hitArgs.element, elementsMap) && hitElementBoundingBox(hitArgs.point, hitArgs.element, elementsMap);
@@ -6665,7 +6699,7 @@ var getAllHoveredElementAtPoint = (point, elements, elementsMap, tolerance) => {
6665
6699
  );
6666
6700
  if (isBindableElement(element, false) && bindingBorderTest(element, point, elementsMap, tolerance)) {
6667
6701
  candidateElements.push(element);
6668
- if (!isTransparent(element.backgroundColor)) {
6702
+ if (hasBackground(element.type) && !isTransparent(element.backgroundColor)) {
6669
6703
  break;
6670
6704
  }
6671
6705
  }
@@ -6760,7 +6794,12 @@ var intersectElementWithLineSegment = (element, elementsMap, line2, offset = 0,
6760
6794
  case "line":
6761
6795
  case "freedraw":
6762
6796
  case "arrow":
6763
- return intersectLinearOrFreeDrawWithLineSegment(element, line2, onlyFirst);
6797
+ return intersectLinearOrFreeDrawWithLineSegment(
6798
+ element,
6799
+ line2,
6800
+ elementsMap,
6801
+ onlyFirst
6802
+ );
6764
6803
  }
6765
6804
  };
6766
6805
  var curveIntersections = (curves, segment, intersections, center, angle, onlyFirst = false) => {
@@ -6799,8 +6838,11 @@ var lineIntersections = (lines, segment, intersections, center, angle, onlyFirst
6799
6838
  }
6800
6839
  return intersections;
6801
6840
  };
6802
- var intersectLinearOrFreeDrawWithLineSegment = (element, segment, onlyFirst = false) => {
6803
- const [lines, curves] = deconstructLinearOrFreeDrawElement(element);
6841
+ var intersectLinearOrFreeDrawWithLineSegment = (element, segment, elementsMap, onlyFirst = false) => {
6842
+ const [lines, curves] = deconstructLinearOrFreeDrawElement(
6843
+ element,
6844
+ elementsMap
6845
+ );
6804
6846
  const intersections = [];
6805
6847
  for (const l2 of lines) {
6806
6848
  const intersection = lineSegmentIntersectionPoints2(l2, segment);
@@ -6822,7 +6864,9 @@ var intersectLinearOrFreeDrawWithLineSegment = (element, segment, onlyFirst = fa
6822
6864
  if (!doBoundsIntersect(b1, b2)) {
6823
6865
  continue;
6824
6866
  }
6825
- const hits = curveIntersectLineSegment(c, segment);
6867
+ const hits = curveIntersectLineSegment(c, segment, {
6868
+ iterLimit: 10
6869
+ });
6826
6870
  if (hits.length > 0) {
6827
6871
  intersections.push(...hits);
6828
6872
  if (onlyFirst) {
@@ -8533,7 +8577,7 @@ var normalizeArrowElementUpdate = (global2, nextFixedSegments, startIsSpecial, e
8533
8577
  vectorScale6(vectorFromPoint6(global2[0]), -1)
8534
8578
  )
8535
8579
  );
8536
- 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) {
8580
+ 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) {
8537
8581
  console.error(
8538
8582
  "Elbow arrow normalization is outside reasonable bounds (> 1e6)",
8539
8583
  {
@@ -9137,11 +9181,7 @@ var getBindingStrategyForDraggingBindingElementEndpoints_simple = (arrow, draggi
9137
9181
  threshold: 0,
9138
9182
  overrideShouldTestInside: true
9139
9183
  });
9140
- if (otherBinding && otherBinding.elementId === hit?.id) {
9141
- invariant7(
9142
- !opts?.newArrow || appState.selectedLinearElement?.initialState.origin,
9143
- "appState.selectedLinearElement.initialState.origin must be defined for new arrows"
9144
- );
9184
+ if (otherBinding && otherBinding.elementId === hit?.id && (!opts?.newArrow || appState.selectedLinearElement?.initialState.origin)) {
9145
9185
  return {
9146
9186
  start: {
9147
9187
  mode: "inside",
@@ -9951,8 +9991,8 @@ var calculateFixedPointForElbowArrowBinding = (linearElement, hoveredElement, st
9951
9991
  );
9952
9992
  return {
9953
9993
  fixedPoint: normalizeFixedPoint([
9954
- (nonRotatedSnappedGlobalPoint[0] - hoveredElement.x) / hoveredElement.width,
9955
- (nonRotatedSnappedGlobalPoint[1] - hoveredElement.y) / hoveredElement.height
9994
+ (nonRotatedSnappedGlobalPoint[0] - hoveredElement.x) / Math.max(hoveredElement.width, PRECISION2),
9995
+ (nonRotatedSnappedGlobalPoint[1] - hoveredElement.y) / Math.max(hoveredElement.height, PRECISION2)
9956
9996
  ])
9957
9997
  };
9958
9998
  };
@@ -9968,8 +10008,8 @@ var calculateFixedPointForNonElbowArrowBinding = (linearElement, hoveredElement,
9968
10008
  elementCenter,
9969
10009
  -hoveredElement.angle
9970
10010
  );
9971
- const fixedPointX = (nonRotatedPoint[0] - hoveredElement.x) / hoveredElement.width;
9972
- const fixedPointY = (nonRotatedPoint[1] - hoveredElement.y) / hoveredElement.height;
10011
+ const fixedPointX = (nonRotatedPoint[0] - hoveredElement.x) / Math.max(hoveredElement.width, PRECISION2);
10012
+ const fixedPointY = (nonRotatedPoint[1] - hoveredElement.y) / Math.max(hoveredElement.height, PRECISION2);
9973
10013
  return {
9974
10014
  fixedPoint: normalizeFixedPoint([fixedPointX, fixedPointY])
9975
10015
  };
@@ -10659,7 +10699,7 @@ var getMidPoint = (p1, p2) => {
10659
10699
 
10660
10700
  // src/zindex.ts
10661
10701
  init_define_import_meta_env();
10662
- import { arrayToMap as arrayToMap7, findIndex, findLastIndex } from "@excalidraw/common";
10702
+ import { arrayToMap as arrayToMap6, findIndex, findLastIndex } from "@excalidraw/common";
10663
10703
 
10664
10704
  // src/groups.ts
10665
10705
  init_define_import_meta_env();
@@ -10667,11 +10707,16 @@ init_define_import_meta_env();
10667
10707
  // src/selection.ts
10668
10708
  init_define_import_meta_env();
10669
10709
  import { arrayToMap as arrayToMap5, isShallowEqual } from "@excalidraw/common";
10710
+ import {
10711
+ lineSegment as lineSegment5,
10712
+ pointFrom as pointFrom10,
10713
+ pointRotateRads as pointRotateRads9
10714
+ } from "@excalidraw/math";
10670
10715
 
10671
10716
  // src/frame.ts
10672
10717
  init_define_import_meta_env();
10673
10718
  import { arrayToMap as arrayToMap4 } from "@excalidraw/common";
10674
- import { isPointWithinBounds as isPointWithinBounds2, pointFrom as pointFrom10 } from "@excalidraw/math";
10719
+ import { isPointWithinBounds as isPointWithinBounds2, pointFrom as pointFrom9 } from "@excalidraw/math";
10675
10720
 
10676
10721
  // ../utils/src/bbox.ts
10677
10722
  init_define_import_meta_env();
@@ -10709,163 +10754,272 @@ function doLineSegmentsIntersect(a2, b2) {
10709
10754
  return doBBoxesIntersect(getBBox(a2), getBBox(b2)) && isLineSegmentTouchingOrCrossingLine(a2, b2) && isLineSegmentTouchingOrCrossingLine(b2, a2);
10710
10755
  }
10711
10756
 
10712
- // ../utils/src/withinBounds.ts
10757
+ // src/fractionalIndex.ts
10713
10758
  init_define_import_meta_env();
10714
10759
  import { arrayToMap as arrayToMap3 } from "@excalidraw/common";
10715
- import { getElementBounds as getElementBounds2 } from "@excalidraw/element";
10716
- import {
10717
- isArrowElement as isArrowElement2,
10718
- isExcalidrawElement as isExcalidrawElement2,
10719
- isFreeDrawElement as isFreeDrawElement2,
10720
- isLinearElement as isLinearElement2,
10721
- isTextElement as isTextElement2
10722
- } from "@excalidraw/element";
10723
10760
  import {
10724
- rangeIncludesValue,
10725
- pointFrom as pointFrom9,
10726
- pointRotateRads as pointRotateRads9,
10727
- rangeInclusive
10728
- } from "@excalidraw/math";
10729
- var getNonLinearElementRelativePoints = (element) => {
10730
- if (element.type === "diamond") {
10731
- return [
10732
- pointFrom9(element.width / 2, 0),
10733
- pointFrom9(element.width, element.height / 2),
10734
- pointFrom9(element.width / 2, element.height),
10735
- pointFrom9(0, element.height / 2)
10736
- ];
10737
- }
10738
- return [
10739
- pointFrom9(0, 0),
10740
- pointFrom9(0 + element.width, 0),
10741
- pointFrom9(0 + element.width, element.height),
10742
- pointFrom9(0, element.height)
10743
- ];
10761
+ validateOrderKey,
10762
+ generateNKeysBetween
10763
+ } from "@excalidraw/fractional-indexing";
10764
+ var InvalidFractionalIndexError = class extends Error {
10765
+ code = "ELEMENT_HAS_INVALID_INDEX";
10744
10766
  };
10745
- var getElementRelativePoints = (element) => {
10746
- if (isLinearElement2(element) || isFreeDrawElement2(element)) {
10747
- return element.points;
10767
+ var validateFractionalIndices = (elements, {
10768
+ shouldThrow = false,
10769
+ includeBoundTextValidation = false,
10770
+ ignoreLogs,
10771
+ reconciliationContext
10772
+ }) => {
10773
+ const errorMessages = [];
10774
+ const stringifyElement = (element) => `${element?.index}:${element?.id}:${element?.type}:${element?.isDeleted}:${element?.version}:${element?.versionNonce}`;
10775
+ const indices = elements.map((x) => x.index);
10776
+ for (const [i, index] of indices.entries()) {
10777
+ const predecessorIndex = indices[i - 1];
10778
+ const successorIndex = indices[i + 1];
10779
+ if (!isValidFractionalIndex(index, predecessorIndex, successorIndex)) {
10780
+ errorMessages.push(
10781
+ `Fractional indices invariant has been compromised: "${stringifyElement(
10782
+ elements[i - 1]
10783
+ )}", "${stringifyElement(elements[i])}", "${stringifyElement(
10784
+ elements[i + 1]
10785
+ )}"`
10786
+ );
10787
+ }
10788
+ if (includeBoundTextValidation && hasBoundTextElement(elements[i])) {
10789
+ const container = elements[i];
10790
+ const text = getBoundTextElement(container, arrayToMap3(elements));
10791
+ if (text && text.index <= container.index) {
10792
+ errorMessages.push(
10793
+ `Fractional indices invariant for bound elements has been compromised: "${stringifyElement(
10794
+ text
10795
+ )}", "${stringifyElement(container)}"`
10796
+ );
10797
+ }
10798
+ }
10799
+ }
10800
+ if (errorMessages.length) {
10801
+ const error = new InvalidFractionalIndexError();
10802
+ const additionalContext = [];
10803
+ if (reconciliationContext) {
10804
+ additionalContext.push("Additional reconciliation context:");
10805
+ additionalContext.push(
10806
+ reconciliationContext.localElements.map((x) => stringifyElement(x))
10807
+ );
10808
+ additionalContext.push(
10809
+ reconciliationContext.remoteElements.map((x) => stringifyElement(x))
10810
+ );
10811
+ }
10812
+ if (!ignoreLogs) {
10813
+ console.error(
10814
+ errorMessages.join("\n\n"),
10815
+ error.stack,
10816
+ elements.map((x) => stringifyElement(x)),
10817
+ ...additionalContext
10818
+ );
10819
+ }
10820
+ if (shouldThrow) {
10821
+ throw error;
10822
+ }
10748
10823
  }
10749
- return getNonLinearElementRelativePoints(element);
10750
10824
  };
10751
- var getMinMaxPoints = (points) => {
10752
- const ret = points.reduce(
10753
- (limits, [x, y]) => {
10754
- limits.minY = Math.min(limits.minY, y);
10755
- limits.minX = Math.min(limits.minX, x);
10756
- limits.maxX = Math.max(limits.maxX, x);
10757
- limits.maxY = Math.max(limits.maxY, y);
10758
- return limits;
10759
- },
10760
- {
10761
- minX: Infinity,
10762
- minY: Infinity,
10763
- maxX: -Infinity,
10764
- maxY: -Infinity,
10765
- cx: 0,
10766
- cy: 0
10825
+ var orderByFractionalIndex = (elements) => {
10826
+ return elements.sort((a2, b2) => {
10827
+ if (isOrderedElement(a2) && isOrderedElement(b2)) {
10828
+ if (a2.index < b2.index) {
10829
+ return -1;
10830
+ } else if (a2.index > b2.index) {
10831
+ return 1;
10832
+ }
10833
+ return a2.id < b2.id ? -1 : 1;
10767
10834
  }
10768
- );
10769
- ret.cx = (ret.maxX + ret.minX) / 2;
10770
- ret.cy = (ret.maxY + ret.minY) / 2;
10771
- return ret;
10835
+ return 1;
10836
+ });
10772
10837
  };
10773
- var getRotatedBBox = (element) => {
10774
- const points = getElementRelativePoints(element);
10775
- const { cx, cy } = getMinMaxPoints(points);
10776
- const centerPoint = pointFrom9(cx, cy);
10777
- const rotatedPoints = points.map(
10778
- (p) => pointRotateRads9(p, centerPoint, element.angle)
10779
- );
10780
- const { minX, minY, maxX, maxY } = getMinMaxPoints(rotatedPoints);
10781
- return [
10782
- minX + element.x,
10783
- minY + element.y,
10784
- maxX + element.x,
10785
- maxY + element.y
10786
- ];
10838
+ var syncMovedIndices = (elements, movedElements) => {
10839
+ try {
10840
+ const elementsMap = arrayToMap3(elements);
10841
+ const indicesGroups = getMovedIndicesGroups(elements, movedElements);
10842
+ const elementsUpdates = generateIndices(elements, indicesGroups);
10843
+ const elementsCandidates = elements.map((x) => {
10844
+ const elementUpdates = elementsUpdates.get(x);
10845
+ if (elementUpdates) {
10846
+ return { ...x, index: elementUpdates.index };
10847
+ }
10848
+ return x;
10849
+ });
10850
+ validateFractionalIndices(
10851
+ elementsCandidates,
10852
+ // we don't autofix invalid bound text indices, hence don't include it in the validation
10853
+ {
10854
+ includeBoundTextValidation: false,
10855
+ shouldThrow: true,
10856
+ ignoreLogs: true
10857
+ }
10858
+ );
10859
+ for (const [element, { index }] of elementsUpdates) {
10860
+ mutateElement(element, elementsMap, { index });
10861
+ }
10862
+ } catch (e) {
10863
+ syncInvalidIndices(elements);
10864
+ }
10865
+ return elements;
10787
10866
  };
10788
- var isElementInsideBBox = (element, bbox, eitherDirection = false) => {
10789
- const elementBBox = getRotatedBBox(element);
10790
- const elementInsideBbox = bbox[0] <= elementBBox[0] && bbox[2] >= elementBBox[2] && bbox[1] <= elementBBox[1] && bbox[3] >= elementBBox[3];
10791
- if (!eitherDirection) {
10792
- return elementInsideBbox;
10867
+ var syncInvalidIndices = (elements) => {
10868
+ const elementsMap = arrayToMap3(elements);
10869
+ const indicesGroups = getInvalidIndicesGroups(elements);
10870
+ const elementsUpdates = generateIndices(elements, indicesGroups);
10871
+ for (const [element, { index }] of elementsUpdates) {
10872
+ mutateElement(element, elementsMap, { index });
10793
10873
  }
10794
- if (elementInsideBbox) {
10795
- return true;
10874
+ return elements;
10875
+ };
10876
+ var syncInvalidIndicesImmutable = (elements) => {
10877
+ const syncedElements = arrayToMap3(elements);
10878
+ const indicesGroups = getInvalidIndicesGroups(elements);
10879
+ const elementsUpdates = generateIndices(elements, indicesGroups);
10880
+ for (const [element, { index }] of elementsUpdates) {
10881
+ syncedElements.set(element.id, newElementWith(element, { index }));
10796
10882
  }
10797
- return elementBBox[0] <= bbox[0] && elementBBox[2] >= bbox[2] && elementBBox[1] <= bbox[1] && elementBBox[3] >= bbox[3];
10798
- };
10799
- var elementPartiallyOverlapsWithOrContainsBBox = (element, bbox) => {
10800
- const elementBBox = getRotatedBBox(element);
10801
- return (rangeIncludesValue(elementBBox[0], rangeInclusive(bbox[0], bbox[2])) || rangeIncludesValue(
10802
- bbox[0],
10803
- rangeInclusive(elementBBox[0], elementBBox[2])
10804
- )) && (rangeIncludesValue(elementBBox[1], rangeInclusive(bbox[1], bbox[3])) || rangeIncludesValue(
10805
- bbox[1],
10806
- rangeInclusive(elementBBox[1], elementBBox[3])
10807
- ));
10808
- };
10809
- var elementsOverlappingBBox = ({
10810
- elements,
10811
- bounds,
10812
- type,
10813
- errorMargin = 0
10814
- }) => {
10815
- if (isExcalidrawElement2(bounds)) {
10816
- bounds = getElementBounds2(bounds, arrayToMap3(elements));
10817
- }
10818
- const adjustedBBox = [
10819
- bounds[0] - errorMargin,
10820
- bounds[1] - errorMargin,
10821
- bounds[2] + errorMargin,
10822
- bounds[3] + errorMargin
10823
- ];
10824
- const includedElementSet = /* @__PURE__ */ new Set();
10825
- for (const element of elements) {
10826
- if (includedElementSet.has(element.id)) {
10827
- continue;
10828
- }
10829
- const isOverlaping = type === "overlap" ? elementPartiallyOverlapsWithOrContainsBBox(element, adjustedBBox) : type === "inside" ? isElementInsideBBox(element, adjustedBBox) : isElementInsideBBox(element, adjustedBBox, true);
10830
- if (isOverlaping) {
10831
- includedElementSet.add(element.id);
10832
- if (element.boundElements) {
10833
- for (const boundElement of element.boundElements) {
10834
- includedElementSet.add(boundElement.id);
10835
- }
10836
- }
10837
- if (isTextElement2(element) && element.containerId) {
10838
- includedElementSet.add(element.containerId);
10839
- }
10840
- if (isArrowElement2(element)) {
10841
- if (element.startBinding) {
10842
- includedElementSet.add(element.startBinding.elementId);
10843
- }
10844
- if (element.endBinding) {
10845
- includedElementSet.add(element.endBinding?.elementId);
10883
+ return syncedElements;
10884
+ };
10885
+ var getMovedIndicesGroups = (elements, movedElements) => {
10886
+ const indicesGroups = [];
10887
+ let i = 0;
10888
+ while (i < elements.length) {
10889
+ if (movedElements.has(elements[i].id)) {
10890
+ const indicesGroup = [i - 1, i];
10891
+ while (++i < elements.length) {
10892
+ if (!movedElements.has(elements[i].id)) {
10893
+ break;
10846
10894
  }
10895
+ indicesGroup.push(i);
10847
10896
  }
10897
+ indicesGroup.push(i);
10898
+ indicesGroups.push(indicesGroup);
10899
+ } else {
10900
+ i++;
10848
10901
  }
10849
10902
  }
10850
- return elements.filter((element) => includedElementSet.has(element.id));
10903
+ return indicesGroups;
10851
10904
  };
10852
-
10853
- // src/frame.ts
10854
- var bindElementsToFramesAfterDuplication = (nextElements, origElements, origIdToDuplicateId) => {
10855
- const nextElementMap = arrayToMap4(nextElements);
10856
- for (const element of origElements) {
10857
- if (element.frameId) {
10858
- const nextElementId = origIdToDuplicateId.get(element.id);
10859
- const nextFrameId = origIdToDuplicateId.get(element.frameId);
10860
- const nextElement = nextElementId && nextElementMap.get(nextElementId);
10861
- if (nextElement) {
10862
- mutateElement(nextElement, nextElementMap, {
10863
- frameId: nextFrameId ?? null
10864
- });
10905
+ var getInvalidIndicesGroups = (elements) => {
10906
+ const indicesGroups = [];
10907
+ let lowerBound = void 0;
10908
+ let upperBound = void 0;
10909
+ let lowerBoundIndex = -1;
10910
+ let upperBoundIndex = 0;
10911
+ const getLowerBound = (index) => {
10912
+ const lowerBound2 = elements[lowerBoundIndex] ? elements[lowerBoundIndex].index : void 0;
10913
+ const candidate = elements[index - 1]?.index;
10914
+ if (!lowerBound2 && candidate || // first lowerBound
10915
+ lowerBound2 && candidate && candidate > lowerBound2) {
10916
+ return [candidate, index - 1];
10917
+ }
10918
+ return [lowerBound2, lowerBoundIndex];
10919
+ };
10920
+ const getUpperBound = (index) => {
10921
+ const upperBound2 = elements[upperBoundIndex] ? elements[upperBoundIndex].index : void 0;
10922
+ if (upperBound2 && index < upperBoundIndex) {
10923
+ return [upperBound2, upperBoundIndex];
10924
+ }
10925
+ let i2 = upperBoundIndex;
10926
+ while (++i2 < elements.length) {
10927
+ const candidate = elements[i2]?.index;
10928
+ if (!upperBound2 && candidate || // first upperBound
10929
+ upperBound2 && candidate && candidate > upperBound2) {
10930
+ return [candidate, i2];
10865
10931
  }
10866
10932
  }
10867
- }
10868
- };
10933
+ return [void 0, i2];
10934
+ };
10935
+ let i = 0;
10936
+ while (i < elements.length) {
10937
+ const current = elements[i].index;
10938
+ [lowerBound, lowerBoundIndex] = getLowerBound(i);
10939
+ [upperBound, upperBoundIndex] = getUpperBound(i);
10940
+ if (!isValidFractionalIndex(current, lowerBound, upperBound)) {
10941
+ const indicesGroup = [lowerBoundIndex, i];
10942
+ while (++i < elements.length) {
10943
+ const current2 = elements[i].index;
10944
+ const [nextLowerBound, nextLowerBoundIndex] = getLowerBound(i);
10945
+ const [nextUpperBound, nextUpperBoundIndex] = getUpperBound(i);
10946
+ if (isValidFractionalIndex(current2, nextLowerBound, nextUpperBound)) {
10947
+ break;
10948
+ }
10949
+ [lowerBound, lowerBoundIndex] = [nextLowerBound, nextLowerBoundIndex];
10950
+ [upperBound, upperBoundIndex] = [nextUpperBound, nextUpperBoundIndex];
10951
+ indicesGroup.push(i);
10952
+ }
10953
+ indicesGroup.push(upperBoundIndex);
10954
+ indicesGroups.push(indicesGroup);
10955
+ } else {
10956
+ i++;
10957
+ }
10958
+ }
10959
+ return indicesGroups;
10960
+ };
10961
+ var isValidFractionalIndex = (index, predecessor, successor) => {
10962
+ if (!index) {
10963
+ return false;
10964
+ }
10965
+ try {
10966
+ validateOrderKey(index);
10967
+ } catch {
10968
+ return false;
10969
+ }
10970
+ if (predecessor && successor) {
10971
+ return predecessor < index && index < successor;
10972
+ }
10973
+ if (!predecessor && successor) {
10974
+ return index < successor;
10975
+ }
10976
+ if (predecessor && !successor) {
10977
+ return predecessor < index;
10978
+ }
10979
+ return !!index;
10980
+ };
10981
+ var generateIndices = (elements, indicesGroups) => {
10982
+ const elementsUpdates = /* @__PURE__ */ new Map();
10983
+ for (const indices of indicesGroups) {
10984
+ const lowerBoundIndex = indices.shift();
10985
+ const upperBoundIndex = indices.pop();
10986
+ const fractionalIndices = generateNKeysBetween(
10987
+ elements[lowerBoundIndex]?.index,
10988
+ elements[upperBoundIndex]?.index,
10989
+ indices.length
10990
+ );
10991
+ for (let i = 0; i < indices.length; i++) {
10992
+ const element = elements[indices[i]];
10993
+ elementsUpdates.set(element, {
10994
+ index: fractionalIndices[i]
10995
+ });
10996
+ }
10997
+ }
10998
+ return elementsUpdates;
10999
+ };
11000
+ var isOrderedElement = (element) => {
11001
+ if (element.index) {
11002
+ return true;
11003
+ }
11004
+ return false;
11005
+ };
11006
+
11007
+ // src/frame.ts
11008
+ var bindElementsToFramesAfterDuplication = (nextElements, origElements, origIdToDuplicateId) => {
11009
+ const nextElementMap = arrayToMap4(nextElements);
11010
+ for (const element of origElements) {
11011
+ if (element.frameId) {
11012
+ const nextElementId = origIdToDuplicateId.get(element.id);
11013
+ const nextFrameId = origIdToDuplicateId.get(element.frameId);
11014
+ const nextElement = nextElementId && nextElementMap.get(nextElementId);
11015
+ if (nextElement) {
11016
+ mutateElement(nextElement, nextElementMap, {
11017
+ frameId: nextFrameId ?? null
11018
+ });
11019
+ }
11020
+ }
11021
+ }
11022
+ };
10869
11023
  function isElementIntersectingFrame(element, frame, elementsMap) {
10870
11024
  const frameLineSegments = getElementLineSegments(frame, elementsMap);
10871
11025
  const elementLineSegments = getElementLineSegments(element, elementsMap);
@@ -10882,8 +11036,9 @@ var getElementsCompletelyInFrame = (elements, frame, elementsMap) => omitGroupsC
10882
11036
  (element) => !isFrameLikeElement(element) && !element.frameId || element.frameId === frame.id
10883
11037
  );
10884
11038
  var isElementContainingFrame = (element, frame, elementsMap) => {
10885
- return getElementsWithinSelection([frame], element, elementsMap).some(
10886
- (e) => e.id === frame.id
11039
+ return boundsContainBounds(
11040
+ getElementBounds(element, elementsMap),
11041
+ getElementBounds(frame, elementsMap)
10887
11042
  );
10888
11043
  };
10889
11044
  var getElementsIntersectingFrame = (elements, frame) => {
@@ -10906,9 +11061,9 @@ var elementOverlapsWithFrame = (element, frame, elementsMap) => {
10906
11061
  var isCursorInFrame = (cursorCoords, frame, elementsMap) => {
10907
11062
  const [fx1, fy1, fx2, fy2] = getElementAbsoluteCoords2(frame, elementsMap);
10908
11063
  return isPointWithinBounds2(
10909
- pointFrom10(fx1, fy1),
10910
- pointFrom10(cursorCoords.x, cursorCoords.y),
10911
- pointFrom10(fx2, fy2)
11064
+ pointFrom9(fx1, fy1),
11065
+ pointFrom9(cursorCoords.x, cursorCoords.y),
11066
+ pointFrom9(fx2, fy2)
10912
11067
  );
10913
11068
  };
10914
11069
  var groupsAreAtLeastIntersectingTheFrame = (elements, groupIds, frame) => {
@@ -11116,16 +11271,35 @@ var filterElementsEligibleAsFrameChildren = (elements, frame) => {
11116
11271
  }
11117
11272
  return eligibleElements;
11118
11273
  };
11119
- var addElementsToFrame = (allElements, elementsToAdd, frame, appState) => {
11120
- const elementsMap = arrayToMap4(allElements);
11121
- const currTargetFrameChildrenMap = /* @__PURE__ */ new Map();
11122
- for (const element of allElements.values()) {
11123
- if (element.frameId === frame.id) {
11124
- currTargetFrameChildrenMap.set(element.id, true);
11274
+ var getCommonFrameId = (elements) => {
11275
+ let commonFrameId;
11276
+ for (const element of elements) {
11277
+ if (isFrameLikeElement(element) || !element.frameId) {
11278
+ return null;
11279
+ }
11280
+ if (commonFrameId === void 0) {
11281
+ commonFrameId = element.frameId;
11282
+ } else if (commonFrameId !== element.frameId) {
11283
+ return null;
11284
+ }
11285
+ }
11286
+ return commonFrameId ?? null;
11287
+ };
11288
+ var getFrameChildrenInsertionIndex = (elements, frameId) => {
11289
+ for (let index = elements.length - 1; index >= 0; index--) {
11290
+ const element = elements[index];
11291
+ if (element.id === frameId) {
11292
+ return index;
11293
+ } else if (element.frameId === frameId) {
11294
+ return index + 1;
11125
11295
  }
11126
11296
  }
11127
- const suppliedElementsToAddSet = new Set(elementsToAdd.map((el) => el.id));
11128
- const finalElementsToAdd = [];
11297
+ return null;
11298
+ };
11299
+ var addElementsToFrame = (allElements, elementsToAdd, frame) => {
11300
+ const elementsMap = arrayToMap4(allElements);
11301
+ const commonFrameId = getCommonFrameId(elementsToAdd);
11302
+ const finalElementsToAdd = /* @__PURE__ */ new Set();
11129
11303
  const otherFrames = /* @__PURE__ */ new Set();
11130
11304
  for (const element of elementsToAdd) {
11131
11305
  if (isFrameLikeElement(element) && element.id !== frame.id) {
@@ -11139,23 +11313,44 @@ var addElementsToFrame = (allElements, elementsToAdd, frame, appState) => {
11139
11313
  if (isFrameLikeElement(element) || element.frameId && otherFrames.has(element.frameId)) {
11140
11314
  continue;
11141
11315
  }
11142
- if (element.frameId && appState.selectedElementIds[element.id] && appState.selectedElementIds[element.frameId]) {
11316
+ if (element.frameId && element.frameId !== frame.id) {
11143
11317
  continue;
11144
11318
  }
11145
- if (!currTargetFrameChildrenMap.has(element.id)) {
11146
- finalElementsToAdd.push(element);
11147
- }
11319
+ finalElementsToAdd.add(element);
11148
11320
  const boundTextElement = getBoundTextElement(element, elementsMap);
11149
- if (boundTextElement && !suppliedElementsToAddSet.has(boundTextElement.id) && !currTargetFrameChildrenMap.has(boundTextElement.id)) {
11150
- finalElementsToAdd.push(boundTextElement);
11321
+ if (boundTextElement && !finalElementsToAdd.has(boundTextElement)) {
11322
+ finalElementsToAdd.add(boundTextElement);
11151
11323
  }
11152
11324
  }
11153
11325
  for (const element of finalElementsToAdd) {
11154
- mutateElement(element, elementsMap, {
11155
- frameId: frame.id
11156
- });
11326
+ if (element.frameId !== frame.id) {
11327
+ mutateElement(element, elementsMap, {
11328
+ frameId: frame.id
11329
+ });
11330
+ }
11157
11331
  }
11158
- return allElements;
11332
+ if (!finalElementsToAdd.size || // if all elements to add already belong to the frame, then we don't want to
11333
+ // reorder (case: we're dragging element children within the frame)
11334
+ commonFrameId === frame.id) {
11335
+ return allElements;
11336
+ }
11337
+ const otherElements = Array.from(allElements.values()).filter(
11338
+ (element) => !finalElementsToAdd.has(element)
11339
+ );
11340
+ const insertionIndex = getFrameChildrenInsertionIndex(
11341
+ otherElements,
11342
+ frame.id
11343
+ );
11344
+ if (insertionIndex === null) {
11345
+ return allElements;
11346
+ }
11347
+ const reorderedElements = [
11348
+ ...otherElements.slice(0, insertionIndex),
11349
+ ...finalElementsToAdd,
11350
+ ...otherElements.slice(insertionIndex)
11351
+ ];
11352
+ syncMovedIndices(reorderedElements, arrayToMap4([...finalElementsToAdd]));
11353
+ return Array.isArray(allElements) ? reorderedElements : new Map(reorderedElements.map((element) => [element.id, element]));
11159
11354
  };
11160
11355
  var removeElementsFromFrame = (elementsToRemove, elementsMap) => {
11161
11356
  const _elementsToRemove = /* @__PURE__ */ new Map();
@@ -11184,12 +11379,11 @@ var removeAllElementsFromFrame = (allElements, frame) => {
11184
11379
  removeElementsFromFrame(elementsInFrame, arrayToMap4(allElements));
11185
11380
  return allElements;
11186
11381
  };
11187
- var replaceAllElementsInFrame = (allElements, nextElementsInFrame, frame, app) => {
11382
+ var replaceAllElementsInFrame = (allElements, nextElementsInFrame, frame) => {
11188
11383
  return addElementsToFrame(
11189
11384
  removeAllElementsFromFrame(allElements, frame),
11190
11385
  nextElementsInFrame,
11191
- frame,
11192
- app.state
11386
+ frame
11193
11387
  ).slice();
11194
11388
  };
11195
11389
  var updateFrameMembershipOfSelectedElements = (allElements, appState, app) => {
@@ -11354,12 +11548,17 @@ var getDefaultFrameName = (element) => {
11354
11548
  var getFrameLikeTitle = (element) => {
11355
11549
  return element.name === null ? getDefaultFrameName(element) : element.name;
11356
11550
  };
11357
- var getElementsOverlappingFrame = (elements, frame) => {
11358
- return elementsOverlappingBBox({
11359
- elements,
11360
- bounds: frame,
11361
- type: "overlap"
11362
- }).filter((el) => !el.frameId || el.frameId === frame.id);
11551
+ var getElementsOverlappingFrame = (elements, frame, elementsMap) => {
11552
+ return elements.filter(
11553
+ (el) => (
11554
+ // exclude elements which are overlapping, but are in a different frame,
11555
+ // and thus invisible in target frame
11556
+ (!el.frameId || el.frameId === frame.id) && doBoundsIntersect(
11557
+ getElementBounds(el, elementsMap),
11558
+ getElementBounds(frame, elementsMap)
11559
+ )
11560
+ )
11561
+ );
11363
11562
  };
11364
11563
  var frameAndChildrenSelectedTogether = (selectedElements) => {
11365
11564
  const selectedElementsMap = arrayToMap4(selectedElements);
@@ -11369,6 +11568,15 @@ var frameAndChildrenSelectedTogether = (selectedElements) => {
11369
11568
  };
11370
11569
 
11371
11570
  // src/selection.ts
11571
+ var shouldIgnoreElementFromSelection = (element) => element.locked || isBoundToContainer(element);
11572
+ var excludeElementsFromFrames = (selectedElements, framesInSelection) => {
11573
+ return selectedElements.filter((element) => {
11574
+ if (element.frameId && framesInSelection.has(element.frameId)) {
11575
+ return false;
11576
+ }
11577
+ return true;
11578
+ });
11579
+ };
11372
11580
  var excludeElementsInFramesFromSelection = (selectedElements) => {
11373
11581
  const framesInSelection = /* @__PURE__ */ new Set();
11374
11582
  selectedElements.forEach((element) => {
@@ -11376,42 +11584,211 @@ var excludeElementsInFramesFromSelection = (selectedElements) => {
11376
11584
  framesInSelection.add(element.id);
11377
11585
  }
11378
11586
  });
11379
- return selectedElements.filter((element) => {
11380
- if (element.frameId && framesInSelection.has(element.frameId)) {
11381
- return false;
11587
+ return excludeElementsFromFrames(selectedElements, framesInSelection);
11588
+ };
11589
+ var getElementsWithinSelection = (elements, selection, elementsMap, excludeElementsInFrames = true, boxSelectionMode = "contain") => {
11590
+ const [selectionStartX, selectionStartY, selectionEndX, selectionEndY] = getElementAbsoluteCoords2(selection, elementsMap);
11591
+ const selectionX1 = Math.min(selectionStartX, selectionEndX);
11592
+ const selectionY1 = Math.min(selectionStartY, selectionEndY);
11593
+ const selectionX2 = Math.max(selectionStartX, selectionEndX);
11594
+ const selectionY2 = Math.max(selectionStartY, selectionEndY);
11595
+ const selectionBounds = [
11596
+ selectionX1,
11597
+ selectionY1,
11598
+ selectionX2,
11599
+ selectionY2
11600
+ ];
11601
+ const selectionEdges = [
11602
+ lineSegment5(
11603
+ pointFrom10(selectionX1, selectionY1),
11604
+ pointFrom10(selectionX2, selectionY1)
11605
+ ),
11606
+ lineSegment5(
11607
+ pointFrom10(selectionX2, selectionY1),
11608
+ pointFrom10(selectionX2, selectionY2)
11609
+ ),
11610
+ lineSegment5(
11611
+ pointFrom10(selectionX2, selectionY2),
11612
+ pointFrom10(selectionX1, selectionY2)
11613
+ ),
11614
+ lineSegment5(
11615
+ pointFrom10(selectionX1, selectionY2),
11616
+ pointFrom10(selectionX1, selectionY1)
11617
+ )
11618
+ ];
11619
+ const framesInSelection = excludeElementsInFrames ? /* @__PURE__ */ new Set() : null;
11620
+ const groups = {};
11621
+ const elementsInSelection = /* @__PURE__ */ new Set();
11622
+ for (const element of elements) {
11623
+ if (shouldIgnoreElementFromSelection(element)) {
11624
+ continue;
11382
11625
  }
11383
- return true;
11384
- });
11385
- };
11386
- var getElementsWithinSelection = (elements, selection, elementsMap, excludeElementsInFrames = true) => {
11387
- const [selectionX1, selectionY1, selectionX2, selectionY2] = getElementAbsoluteCoords2(selection, elementsMap);
11388
- let elementsInSelection = elements.filter((element) => {
11389
- let [elementX1, elementY1, elementX2, elementY2] = getElementBounds(
11390
- element,
11391
- elementsMap
11392
- );
11393
- const containingFrame = getContainingFrame(element, elementsMap);
11394
- if (containingFrame) {
11395
- const [fx1, fy1, fx2, fy2] = getElementBounds(
11396
- containingFrame,
11626
+ const groupId = element.groupIds.at(-1);
11627
+ if (groupId) {
11628
+ if (!groups[groupId]) {
11629
+ groups[groupId] = [];
11630
+ }
11631
+ groups[groupId].push(element);
11632
+ }
11633
+ const strokeWidth = element.strokeWidth;
11634
+ let labelAABB = null;
11635
+ let elementAABB = getElementBounds(element, elementsMap);
11636
+ elementAABB = [
11637
+ elementAABB[0] - strokeWidth / 2,
11638
+ elementAABB[1] - strokeWidth / 2,
11639
+ elementAABB[2] + strokeWidth / 2,
11640
+ elementAABB[3] + strokeWidth / 2
11641
+ ];
11642
+ const boundTextElement = isArrowElement(element) && getBoundTextElement(element, elementsMap);
11643
+ if (boundTextElement) {
11644
+ const { x, y } = LinearElementEditor.getBoundTextElementPosition(
11645
+ element,
11646
+ boundTextElement,
11397
11647
  elementsMap
11398
11648
  );
11399
- elementX1 = Math.max(fx1, elementX1);
11400
- elementY1 = Math.max(fy1, elementY1);
11401
- elementX2 = Math.min(fx2, elementX2);
11402
- elementY2 = Math.min(fy2, elementY2);
11649
+ labelAABB = [
11650
+ x,
11651
+ y,
11652
+ x + boundTextElement.width,
11653
+ y + boundTextElement.height
11654
+ ];
11403
11655
  }
11404
- return element.locked === false && element.type !== "selection" && !isBoundToContainer(element) && selectionX1 <= elementX1 && selectionY1 <= elementY1 && selectionX2 >= elementX2 && selectionY2 >= elementY2;
11405
- });
11406
- elementsInSelection = excludeElementsInFrames ? excludeElementsInFramesFromSelection(elementsInSelection) : elementsInSelection;
11407
- elementsInSelection = elementsInSelection.filter((element) => {
11408
- const containingFrame = getContainingFrame(element, elementsMap);
11409
- if (containingFrame) {
11410
- return elementOverlapsWithFrame(element, containingFrame, elementsMap);
11656
+ const associatedFrame = getContainingFrame(element, elementsMap);
11657
+ if (associatedFrame && elementOverlapsWithFrame(element, associatedFrame, elementsMap)) {
11658
+ const frameAABB = getElementBounds(associatedFrame, elementsMap);
11659
+ elementAABB = [
11660
+ Math.max(elementAABB[0], frameAABB[0]),
11661
+ Math.max(elementAABB[1], frameAABB[1]),
11662
+ Math.min(elementAABB[2], frameAABB[2]),
11663
+ Math.min(elementAABB[3], frameAABB[3])
11664
+ ];
11665
+ labelAABB = labelAABB ? [
11666
+ Math.max(labelAABB[0], frameAABB[0]),
11667
+ Math.max(labelAABB[1], frameAABB[1]),
11668
+ Math.min(labelAABB[2], frameAABB[2]),
11669
+ Math.min(labelAABB[3], frameAABB[3])
11670
+ ] : null;
11671
+ }
11672
+ const commonAABB2 = labelAABB ? [
11673
+ Math.min(labelAABB[0], elementAABB[0]),
11674
+ Math.min(labelAABB[1], elementAABB[1]),
11675
+ Math.max(labelAABB[2], elementAABB[2]),
11676
+ Math.max(labelAABB[3], elementAABB[3])
11677
+ ] : elementAABB;
11678
+ if (boundsContainBounds(selectionBounds, commonAABB2)) {
11679
+ if (framesInSelection && isFrameLikeElement(element)) {
11680
+ framesInSelection.add(element.id);
11681
+ }
11682
+ elementsInSelection.add(element);
11683
+ continue;
11411
11684
  }
11412
- return true;
11413
- });
11414
- return elementsInSelection;
11685
+ if (boxSelectionMode === "overlap" && labelAABB && doBoundsIntersect(selectionBounds, labelAABB)) {
11686
+ elementsInSelection.add(element);
11687
+ continue;
11688
+ }
11689
+ if (boxSelectionMode === "overlap" && doBoundsIntersect(selectionBounds, elementAABB)) {
11690
+ let hasIntersection = false;
11691
+ if (isLinearElement(element) || isFreeDrawElement(element)) {
11692
+ const center = elementCenterPoint(element, elementsMap);
11693
+ hasIntersection = element.points.some((point) => {
11694
+ const rotatedPoint = pointRotateRads9(
11695
+ pointFrom10(element.x + point[0], element.y + point[1]),
11696
+ center,
11697
+ element.angle
11698
+ );
11699
+ return pointInsideBounds(rotatedPoint, selectionBounds);
11700
+ });
11701
+ } else {
11702
+ const nonRotatedElementBounds = getElementBounds(
11703
+ element,
11704
+ elementsMap,
11705
+ true
11706
+ );
11707
+ const center = elementCenterPoint(element, elementsMap);
11708
+ hasIntersection = [
11709
+ pointRotateRads9(
11710
+ pointFrom10(
11711
+ (nonRotatedElementBounds[0] + nonRotatedElementBounds[2]) / 2,
11712
+ nonRotatedElementBounds[1]
11713
+ ),
11714
+ center,
11715
+ element.angle
11716
+ ),
11717
+ pointRotateRads9(
11718
+ pointFrom10(
11719
+ nonRotatedElementBounds[2],
11720
+ (nonRotatedElementBounds[1] + nonRotatedElementBounds[3]) / 2
11721
+ ),
11722
+ center,
11723
+ element.angle
11724
+ ),
11725
+ pointRotateRads9(
11726
+ pointFrom10(
11727
+ (nonRotatedElementBounds[0] + nonRotatedElementBounds[2]) / 2,
11728
+ nonRotatedElementBounds[3]
11729
+ ),
11730
+ center,
11731
+ element.angle
11732
+ ),
11733
+ pointRotateRads9(
11734
+ pointFrom10(
11735
+ nonRotatedElementBounds[0],
11736
+ (nonRotatedElementBounds[1] + nonRotatedElementBounds[3]) / 2
11737
+ ),
11738
+ center,
11739
+ element.angle
11740
+ )
11741
+ ].some((point) => {
11742
+ return pointInsideBounds(
11743
+ pointRotateRads9(point, center, element.angle),
11744
+ selectionBounds
11745
+ );
11746
+ });
11747
+ }
11748
+ if (!hasIntersection) {
11749
+ hasIntersection = selectionEdges.some(
11750
+ (selectionEdge) => intersectElementWithLineSegment(
11751
+ element,
11752
+ elementsMap,
11753
+ selectionEdge,
11754
+ strokeWidth / 2,
11755
+ true
11756
+ // Stop at first hit for better performance
11757
+ ).length > 0
11758
+ );
11759
+ }
11760
+ if (hasIntersection) {
11761
+ if (framesInSelection && isFrameLikeElement(element)) {
11762
+ framesInSelection.add(element.id);
11763
+ }
11764
+ elementsInSelection.add(element);
11765
+ continue;
11766
+ }
11767
+ }
11768
+ }
11769
+ if (framesInSelection) {
11770
+ elementsInSelection.forEach((element) => {
11771
+ if (element.frameId && framesInSelection.has(element.frameId)) {
11772
+ elementsInSelection.delete(element);
11773
+ }
11774
+ });
11775
+ }
11776
+ if (boxSelectionMode === "overlap") {
11777
+ Array.from(elementsInSelection).forEach((element) => {
11778
+ const groupId = element.groupIds.at(-1);
11779
+ const group = groupId ? groups[groupId] : null;
11780
+ group?.forEach((groupElement) => elementsInSelection.add(groupElement));
11781
+ });
11782
+ } else if (boxSelectionMode === "contain") {
11783
+ elementsInSelection.forEach((element) => {
11784
+ const groupId = element.groupIds.at(-1);
11785
+ const group = groupId ? groups[groupId] : null;
11786
+ if (group && !group.every((groupElement) => elementsInSelection.has(groupElement))) {
11787
+ elementsInSelection.delete(element);
11788
+ }
11789
+ });
11790
+ }
11791
+ return elements.filter((element) => elementsInSelection.has(element));
11415
11792
  };
11416
11793
  var getVisibleAndNonSelectedElements = (elements, selectedElements, appState, elementsMap) => {
11417
11794
  const selectedElementsSet = new Set(
@@ -11654,604 +12031,150 @@ var selectGroupsFromGivenElements = (elements, appState) => {
11654
12031
  ...appState,
11655
12032
  selectedGroupIds: {}
11656
12033
  };
11657
- for (const element of elements) {
11658
- let groupIds = element.groupIds;
11659
- if (appState.editingGroupId) {
11660
- const indexOfEditingGroup = groupIds.indexOf(appState.editingGroupId);
11661
- if (indexOfEditingGroup > -1) {
11662
- groupIds = groupIds.slice(0, indexOfEditingGroup);
11663
- }
11664
- }
11665
- if (groupIds.length > 0) {
11666
- const groupId = groupIds[groupIds.length - 1];
11667
- nextAppState = {
11668
- ...nextAppState,
11669
- ...selectGroup(groupId, nextAppState, elements)
11670
- };
11671
- }
11672
- }
11673
- return nextAppState.selectedGroupIds;
11674
- };
11675
- var editGroupForSelectedElement = (appState, element) => {
11676
- return {
11677
- ...appState,
11678
- editingGroupId: element.groupIds.length ? element.groupIds[0] : null,
11679
- selectedGroupIds: {},
11680
- selectedElementIds: {
11681
- [element.id]: true
11682
- }
11683
- };
11684
- };
11685
- var isElementInGroup = (element, groupId) => element.groupIds.includes(groupId);
11686
- var getElementsInGroup = (elements, groupId) => {
11687
- const elementsInGroup = [];
11688
- for (const element of elements.values()) {
11689
- if (isElementInGroup(element, groupId)) {
11690
- elementsInGroup.push(element);
11691
- }
11692
- }
11693
- return elementsInGroup;
11694
- };
11695
- var getSelectedGroupIdForElement = (element, selectedGroupIds) => element.groupIds.find((groupId) => selectedGroupIds[groupId]);
11696
- var addToGroup = (prevGroupIds, newGroupId, editingGroupId) => {
11697
- const groupIds = [...prevGroupIds];
11698
- const positionOfEditingGroupId = editingGroupId ? groupIds.indexOf(editingGroupId) : -1;
11699
- const positionToInsert = positionOfEditingGroupId > -1 ? positionOfEditingGroupId : groupIds.length;
11700
- groupIds.splice(positionToInsert, 0, newGroupId);
11701
- return groupIds;
11702
- };
11703
- var removeFromSelectedGroups = (groupIds, selectedGroupIds) => groupIds.filter((groupId) => !selectedGroupIds[groupId]);
11704
- var getMaximumGroups = (elements, elementsMap) => {
11705
- const groups = /* @__PURE__ */ new Map();
11706
- elements.forEach((element) => {
11707
- const groupId = element.groupIds.length === 0 ? element.id : element.groupIds[element.groupIds.length - 1];
11708
- const currentGroupMembers = groups.get(groupId) || [];
11709
- const boundTextElement = getBoundTextElement(element, elementsMap);
11710
- if (boundTextElement) {
11711
- currentGroupMembers.push(boundTextElement);
11712
- }
11713
- groups.set(groupId, [...currentGroupMembers, element]);
11714
- });
11715
- return Array.from(groups.values());
11716
- };
11717
- var getNonDeletedGroupIds = (elements) => {
11718
- const nonDeletedGroupIds = /* @__PURE__ */ new Set();
11719
- for (const [, element] of elements) {
11720
- if (element.isDeleted) {
11721
- continue;
11722
- }
11723
- for (const groupId of element.groupIds ?? []) {
11724
- nonDeletedGroupIds.add(groupId);
11725
- }
11726
- }
11727
- return nonDeletedGroupIds;
11728
- };
11729
- var elementsAreInSameGroup = (elements) => {
11730
- const allGroups = elements.flatMap((element) => element.groupIds);
11731
- const groupCount = /* @__PURE__ */ new Map();
11732
- let maxGroup = 0;
11733
- for (const group of allGroups) {
11734
- groupCount.set(group, (groupCount.get(group) ?? 0) + 1);
11735
- if (groupCount.get(group) > maxGroup) {
11736
- maxGroup = groupCount.get(group);
11737
- }
11738
- }
11739
- return maxGroup === elements.length;
11740
- };
11741
- var isInGroup = (element) => {
11742
- return element.groupIds.length > 0;
11743
- };
11744
- var getNewGroupIdsForDuplication = (groupIds, editingGroupId, mapper) => {
11745
- const copy = [...groupIds];
11746
- const positionOfEditingGroupId = editingGroupId ? groupIds.indexOf(editingGroupId) : -1;
11747
- const endIndex = positionOfEditingGroupId > -1 ? positionOfEditingGroupId : groupIds.length;
11748
- for (let index = 0; index < endIndex; index++) {
11749
- copy[index] = mapper(copy[index]);
11750
- }
11751
- return copy;
11752
- };
11753
- var getSelectedElementsByGroup = (selectedElements, elementsMap, appState) => {
11754
- const selectedGroupIds = getSelectedGroupIds(appState);
11755
- const unboundElements = selectedElements.filter(
11756
- (element) => !isBoundToContainer(element)
11757
- );
11758
- const groups = /* @__PURE__ */ new Map();
11759
- const elements = /* @__PURE__ */ new Map();
11760
- const addToElementsMap = (element) => {
11761
- const currentElementMembers = elements.get(element.id) || [];
11762
- const boundTextElement = getBoundTextElement(element, elementsMap);
11763
- if (boundTextElement) {
11764
- currentElementMembers.push(boundTextElement);
11765
- }
11766
- elements.set(element.id, [...currentElementMembers, element]);
11767
- };
11768
- const addToGroupsMap = (element, groupId) => {
11769
- const currentGroupMembers = groups.get(groupId) || [];
11770
- const boundTextElement = getBoundTextElement(element, elementsMap);
11771
- if (boundTextElement) {
11772
- currentGroupMembers.push(boundTextElement);
11773
- }
11774
- groups.set(groupId, [...currentGroupMembers, element]);
11775
- };
11776
- const handleSingleSelectedGroupCase = (element, selectedGroupId) => {
11777
- const indexOfSelectedGroupId = element.groupIds.indexOf(selectedGroupId, 0);
11778
- const nestedGroupCount = element.groupIds.slice(
11779
- 0,
11780
- indexOfSelectedGroupId
11781
- ).length;
11782
- return nestedGroupCount > 0 ? addToGroupsMap(element, element.groupIds[indexOfSelectedGroupId - 1]) : addToElementsMap(element);
11783
- };
11784
- const isAllInSameGroup = selectedElements.every(
11785
- (element) => isSelectedViaGroup(appState, element)
11786
- );
11787
- unboundElements.forEach((element) => {
11788
- const selectedGroupId = getSelectedGroupIdForElement(
11789
- element,
11790
- appState.selectedGroupIds
11791
- );
11792
- if (!selectedGroupId) {
11793
- addToElementsMap(element);
11794
- } else if (selectedGroupIds.length === 1 && isAllInSameGroup) {
11795
- handleSingleSelectedGroupCase(element, selectedGroupId);
11796
- } else {
11797
- addToGroupsMap(element, selectedGroupId);
11798
- }
11799
- });
11800
- return Array.from(groups.values()).concat(Array.from(elements.values()));
11801
- };
11802
-
11803
- // src/fractionalIndex.ts
11804
- init_define_import_meta_env();
11805
-
11806
- // ../../node_modules/fractional-indexing/src/index.js
11807
- init_define_import_meta_env();
11808
- var BASE_62_DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
11809
- function midpoint(a2, b2, digits) {
11810
- const zero = digits[0];
11811
- if (b2 != null && a2 >= b2) {
11812
- throw new Error(a2 + " >= " + b2);
11813
- }
11814
- if (a2.slice(-1) === zero || b2 && b2.slice(-1) === zero) {
11815
- throw new Error("trailing zero");
11816
- }
11817
- if (b2) {
11818
- let n = 0;
11819
- while ((a2[n] || zero) === b2[n]) {
11820
- n++;
11821
- }
11822
- if (n > 0) {
11823
- return b2.slice(0, n) + midpoint(a2.slice(n), b2.slice(n), digits);
11824
- }
11825
- }
11826
- const digitA = a2 ? digits.indexOf(a2[0]) : 0;
11827
- const digitB = b2 != null ? digits.indexOf(b2[0]) : digits.length;
11828
- if (digitB - digitA > 1) {
11829
- const midDigit = Math.round(0.5 * (digitA + digitB));
11830
- return digits[midDigit];
11831
- } else {
11832
- if (b2 && b2.length > 1) {
11833
- return b2.slice(0, 1);
11834
- } else {
11835
- return digits[digitA] + midpoint(a2.slice(1), null, digits);
11836
- }
11837
- }
11838
- }
11839
- function validateInteger(int) {
11840
- if (int.length !== getIntegerLength(int[0])) {
11841
- throw new Error("invalid integer part of order key: " + int);
11842
- }
11843
- }
11844
- function getIntegerLength(head) {
11845
- if (head >= "a" && head <= "z") {
11846
- return head.charCodeAt(0) - "a".charCodeAt(0) + 2;
11847
- } else if (head >= "A" && head <= "Z") {
11848
- return "Z".charCodeAt(0) - head.charCodeAt(0) + 2;
11849
- } else {
11850
- throw new Error("invalid order key head: " + head);
11851
- }
11852
- }
11853
- function getIntegerPart(key) {
11854
- const integerPartLength = getIntegerLength(key[0]);
11855
- if (integerPartLength > key.length) {
11856
- throw new Error("invalid order key: " + key);
11857
- }
11858
- return key.slice(0, integerPartLength);
11859
- }
11860
- function validateOrderKey(key, digits) {
11861
- if (key === "A" + digits[0].repeat(26)) {
11862
- throw new Error("invalid order key: " + key);
11863
- }
11864
- const i = getIntegerPart(key);
11865
- const f = key.slice(i.length);
11866
- if (f.slice(-1) === digits[0]) {
11867
- throw new Error("invalid order key: " + key);
11868
- }
11869
- }
11870
- function incrementInteger(x, digits) {
11871
- validateInteger(x);
11872
- const [head, ...digs] = x.split("");
11873
- let carry = true;
11874
- for (let i = digs.length - 1; carry && i >= 0; i--) {
11875
- const d = digits.indexOf(digs[i]) + 1;
11876
- if (d === digits.length) {
11877
- digs[i] = digits[0];
11878
- } else {
11879
- digs[i] = digits[d];
11880
- carry = false;
11881
- }
11882
- }
11883
- if (carry) {
11884
- if (head === "Z") {
11885
- return "a" + digits[0];
11886
- }
11887
- if (head === "z") {
11888
- return null;
11889
- }
11890
- const h = String.fromCharCode(head.charCodeAt(0) + 1);
11891
- if (h > "a") {
11892
- digs.push(digits[0]);
11893
- } else {
11894
- digs.pop();
11895
- }
11896
- return h + digs.join("");
11897
- } else {
11898
- return head + digs.join("");
11899
- }
11900
- }
11901
- function decrementInteger(x, digits) {
11902
- validateInteger(x);
11903
- const [head, ...digs] = x.split("");
11904
- let borrow = true;
11905
- for (let i = digs.length - 1; borrow && i >= 0; i--) {
11906
- const d = digits.indexOf(digs[i]) - 1;
11907
- if (d === -1) {
11908
- digs[i] = digits.slice(-1);
11909
- } else {
11910
- digs[i] = digits[d];
11911
- borrow = false;
11912
- }
11913
- }
11914
- if (borrow) {
11915
- if (head === "a") {
11916
- return "Z" + digits.slice(-1);
11917
- }
11918
- if (head === "A") {
11919
- return null;
11920
- }
11921
- const h = String.fromCharCode(head.charCodeAt(0) - 1);
11922
- if (h < "Z") {
11923
- digs.push(digits.slice(-1));
11924
- } else {
11925
- digs.pop();
11926
- }
11927
- return h + digs.join("");
11928
- } else {
11929
- return head + digs.join("");
11930
- }
11931
- }
11932
- function generateKeyBetween(a2, b2, digits = BASE_62_DIGITS) {
11933
- if (a2 != null) {
11934
- validateOrderKey(a2, digits);
11935
- }
11936
- if (b2 != null) {
11937
- validateOrderKey(b2, digits);
11938
- }
11939
- if (a2 != null && b2 != null && a2 >= b2) {
11940
- throw new Error(a2 + " >= " + b2);
11941
- }
11942
- if (a2 == null) {
11943
- if (b2 == null) {
11944
- return "a" + digits[0];
11945
- }
11946
- const ib2 = getIntegerPart(b2);
11947
- const fb2 = b2.slice(ib2.length);
11948
- if (ib2 === "A" + digits[0].repeat(26)) {
11949
- return ib2 + midpoint("", fb2, digits);
11950
- }
11951
- if (ib2 < b2) {
11952
- return ib2;
11953
- }
11954
- const res = decrementInteger(ib2, digits);
11955
- if (res == null) {
11956
- throw new Error("cannot decrement any more");
11957
- }
11958
- return res;
11959
- }
11960
- if (b2 == null) {
11961
- const ia2 = getIntegerPart(a2);
11962
- const fa2 = a2.slice(ia2.length);
11963
- const i2 = incrementInteger(ia2, digits);
11964
- return i2 == null ? ia2 + midpoint(fa2, null, digits) : i2;
11965
- }
11966
- const ia = getIntegerPart(a2);
11967
- const fa = a2.slice(ia.length);
11968
- const ib = getIntegerPart(b2);
11969
- const fb = b2.slice(ib.length);
11970
- if (ia === ib) {
11971
- return ia + midpoint(fa, fb, digits);
11972
- }
11973
- const i = incrementInteger(ia, digits);
11974
- if (i == null) {
11975
- throw new Error("cannot increment any more");
11976
- }
11977
- if (i < b2) {
11978
- return i;
11979
- }
11980
- return ia + midpoint(fa, null, digits);
11981
- }
11982
- function generateNKeysBetween(a2, b2, n, digits = BASE_62_DIGITS) {
11983
- if (n === 0) {
11984
- return [];
11985
- }
11986
- if (n === 1) {
11987
- return [generateKeyBetween(a2, b2, digits)];
11988
- }
11989
- if (b2 == null) {
11990
- let c2 = generateKeyBetween(a2, b2, digits);
11991
- const result = [c2];
11992
- for (let i = 0; i < n - 1; i++) {
11993
- c2 = generateKeyBetween(c2, b2, digits);
11994
- result.push(c2);
11995
- }
11996
- return result;
11997
- }
11998
- if (a2 == null) {
11999
- let c2 = generateKeyBetween(a2, b2, digits);
12000
- const result = [c2];
12001
- for (let i = 0; i < n - 1; i++) {
12002
- c2 = generateKeyBetween(a2, c2, digits);
12003
- result.push(c2);
12004
- }
12005
- result.reverse();
12006
- return result;
12007
- }
12008
- const mid = Math.floor(n / 2);
12009
- const c = generateKeyBetween(a2, b2, digits);
12010
- return [
12011
- ...generateNKeysBetween(a2, c, mid, digits),
12012
- c,
12013
- ...generateNKeysBetween(c, b2, n - mid - 1, digits)
12014
- ];
12015
- }
12016
-
12017
- // src/fractionalIndex.ts
12018
- import { arrayToMap as arrayToMap6 } from "@excalidraw/common";
12019
- var InvalidFractionalIndexError = class extends Error {
12020
- code = "ELEMENT_HAS_INVALID_INDEX";
12021
- };
12022
- var validateFractionalIndices = (elements, {
12023
- shouldThrow = false,
12024
- includeBoundTextValidation = false,
12025
- ignoreLogs,
12026
- reconciliationContext
12027
- }) => {
12028
- const errorMessages = [];
12029
- const stringifyElement = (element) => `${element?.index}:${element?.id}:${element?.type}:${element?.isDeleted}:${element?.version}:${element?.versionNonce}`;
12030
- const indices = elements.map((x) => x.index);
12031
- for (const [i, index] of indices.entries()) {
12032
- const predecessorIndex = indices[i - 1];
12033
- const successorIndex = indices[i + 1];
12034
- if (!isValidFractionalIndex(index, predecessorIndex, successorIndex)) {
12035
- errorMessages.push(
12036
- `Fractional indices invariant has been compromised: "${stringifyElement(
12037
- elements[i - 1]
12038
- )}", "${stringifyElement(elements[i])}", "${stringifyElement(
12039
- elements[i + 1]
12040
- )}"`
12041
- );
12042
- }
12043
- if (includeBoundTextValidation && hasBoundTextElement(elements[i])) {
12044
- const container = elements[i];
12045
- const text = getBoundTextElement(container, arrayToMap6(elements));
12046
- if (text && text.index <= container.index) {
12047
- errorMessages.push(
12048
- `Fractional indices invariant for bound elements has been compromised: "${stringifyElement(
12049
- text
12050
- )}", "${stringifyElement(container)}"`
12051
- );
12052
- }
12053
- }
12054
- }
12055
- if (errorMessages.length) {
12056
- const error = new InvalidFractionalIndexError();
12057
- const additionalContext = [];
12058
- if (reconciliationContext) {
12059
- additionalContext.push("Additional reconciliation context:");
12060
- additionalContext.push(
12061
- reconciliationContext.localElements.map((x) => stringifyElement(x))
12062
- );
12063
- additionalContext.push(
12064
- reconciliationContext.remoteElements.map((x) => stringifyElement(x))
12065
- );
12066
- }
12067
- if (!ignoreLogs) {
12068
- console.error(
12069
- errorMessages.join("\n\n"),
12070
- error.stack,
12071
- elements.map((x) => stringifyElement(x)),
12072
- ...additionalContext
12073
- );
12074
- }
12075
- if (shouldThrow) {
12076
- throw error;
12077
- }
12078
- }
12079
- };
12080
- var orderByFractionalIndex = (elements) => {
12081
- return elements.sort((a2, b2) => {
12082
- if (isOrderedElement(a2) && isOrderedElement(b2)) {
12083
- if (a2.index < b2.index) {
12084
- return -1;
12085
- } else if (a2.index > b2.index) {
12086
- return 1;
12087
- }
12088
- return a2.id < b2.id ? -1 : 1;
12089
- }
12090
- return 1;
12091
- });
12092
- };
12093
- var syncMovedIndices = (elements, movedElements) => {
12094
- try {
12095
- const elementsMap = arrayToMap6(elements);
12096
- const indicesGroups = getMovedIndicesGroups(elements, movedElements);
12097
- const elementsUpdates = generateIndices(elements, indicesGroups);
12098
- const elementsCandidates = elements.map((x) => {
12099
- const elementUpdates = elementsUpdates.get(x);
12100
- if (elementUpdates) {
12101
- return { ...x, index: elementUpdates.index };
12102
- }
12103
- return x;
12104
- });
12105
- validateFractionalIndices(
12106
- elementsCandidates,
12107
- // we don't autofix invalid bound text indices, hence don't include it in the validation
12108
- {
12109
- includeBoundTextValidation: false,
12110
- shouldThrow: true,
12111
- ignoreLogs: true
12112
- }
12113
- );
12114
- for (const [element, { index }] of elementsUpdates) {
12115
- mutateElement(element, elementsMap, { index });
12116
- }
12117
- } catch (e) {
12118
- syncInvalidIndices(elements);
12119
- }
12120
- return elements;
12121
- };
12122
- var syncInvalidIndices = (elements) => {
12123
- const elementsMap = arrayToMap6(elements);
12124
- const indicesGroups = getInvalidIndicesGroups(elements);
12125
- const elementsUpdates = generateIndices(elements, indicesGroups);
12126
- for (const [element, { index }] of elementsUpdates) {
12127
- mutateElement(element, elementsMap, { index });
12128
- }
12129
- return elements;
12130
- };
12131
- var syncInvalidIndicesImmutable = (elements) => {
12132
- const syncedElements = arrayToMap6(elements);
12133
- const indicesGroups = getInvalidIndicesGroups(elements);
12134
- const elementsUpdates = generateIndices(elements, indicesGroups);
12135
- for (const [element, { index }] of elementsUpdates) {
12136
- syncedElements.set(element.id, newElementWith(element, { index }));
12137
- }
12138
- return syncedElements;
12139
- };
12140
- var getMovedIndicesGroups = (elements, movedElements) => {
12141
- const indicesGroups = [];
12142
- let i = 0;
12143
- while (i < elements.length) {
12144
- if (movedElements.has(elements[i].id)) {
12145
- const indicesGroup = [i - 1, i];
12146
- while (++i < elements.length) {
12147
- if (!movedElements.has(elements[i].id)) {
12148
- break;
12149
- }
12150
- indicesGroup.push(i);
12151
- }
12152
- indicesGroup.push(i);
12153
- indicesGroups.push(indicesGroup);
12154
- } else {
12155
- i++;
12156
- }
12157
- }
12158
- return indicesGroups;
12159
- };
12160
- var getInvalidIndicesGroups = (elements) => {
12161
- const indicesGroups = [];
12162
- let lowerBound = void 0;
12163
- let upperBound = void 0;
12164
- let lowerBoundIndex = -1;
12165
- let upperBoundIndex = 0;
12166
- const getLowerBound = (index) => {
12167
- const lowerBound2 = elements[lowerBoundIndex] ? elements[lowerBoundIndex].index : void 0;
12168
- const candidate = elements[index - 1]?.index;
12169
- if (!lowerBound2 && candidate || // first lowerBound
12170
- lowerBound2 && candidate && candidate > lowerBound2) {
12171
- return [candidate, index - 1];
12172
- }
12173
- return [lowerBound2, lowerBoundIndex];
12174
- };
12175
- const getUpperBound = (index) => {
12176
- const upperBound2 = elements[upperBoundIndex] ? elements[upperBoundIndex].index : void 0;
12177
- if (upperBound2 && index < upperBoundIndex) {
12178
- return [upperBound2, upperBoundIndex];
12179
- }
12180
- let i2 = upperBoundIndex;
12181
- while (++i2 < elements.length) {
12182
- const candidate = elements[i2]?.index;
12183
- if (!upperBound2 && candidate || // first upperBound
12184
- upperBound2 && candidate && candidate > upperBound2) {
12185
- return [candidate, i2];
12186
- }
12187
- }
12188
- return [void 0, i2];
12189
- };
12190
- let i = 0;
12191
- while (i < elements.length) {
12192
- const current = elements[i].index;
12193
- [lowerBound, lowerBoundIndex] = getLowerBound(i);
12194
- [upperBound, upperBoundIndex] = getUpperBound(i);
12195
- if (!isValidFractionalIndex(current, lowerBound, upperBound)) {
12196
- const indicesGroup = [lowerBoundIndex, i];
12197
- while (++i < elements.length) {
12198
- const current2 = elements[i].index;
12199
- const [nextLowerBound, nextLowerBoundIndex] = getLowerBound(i);
12200
- const [nextUpperBound, nextUpperBoundIndex] = getUpperBound(i);
12201
- if (isValidFractionalIndex(current2, nextLowerBound, nextUpperBound)) {
12202
- break;
12203
- }
12204
- [lowerBound, lowerBoundIndex] = [nextLowerBound, nextLowerBoundIndex];
12205
- [upperBound, upperBoundIndex] = [nextUpperBound, nextUpperBoundIndex];
12206
- indicesGroup.push(i);
12034
+ for (const element of elements) {
12035
+ let groupIds = element.groupIds;
12036
+ if (appState.editingGroupId) {
12037
+ const indexOfEditingGroup = groupIds.indexOf(appState.editingGroupId);
12038
+ if (indexOfEditingGroup > -1) {
12039
+ groupIds = groupIds.slice(0, indexOfEditingGroup);
12207
12040
  }
12208
- indicesGroup.push(upperBoundIndex);
12209
- indicesGroups.push(indicesGroup);
12210
- } else {
12211
- i++;
12041
+ }
12042
+ if (groupIds.length > 0) {
12043
+ const groupId = groupIds[groupIds.length - 1];
12044
+ nextAppState = {
12045
+ ...nextAppState,
12046
+ ...selectGroup(groupId, nextAppState, elements)
12047
+ };
12212
12048
  }
12213
12049
  }
12214
- return indicesGroups;
12050
+ return nextAppState.selectedGroupIds;
12215
12051
  };
12216
- var isValidFractionalIndex = (index, predecessor, successor) => {
12217
- if (!index) {
12218
- return false;
12219
- }
12220
- if (predecessor && successor) {
12221
- return predecessor < index && index < successor;
12222
- }
12223
- if (!predecessor && successor) {
12224
- return index < successor;
12052
+ var editGroupForSelectedElement = (appState, element) => {
12053
+ return {
12054
+ ...appState,
12055
+ editingGroupId: element.groupIds.length ? element.groupIds[0] : null,
12056
+ selectedGroupIds: {},
12057
+ selectedElementIds: {
12058
+ [element.id]: true
12059
+ }
12060
+ };
12061
+ };
12062
+ var isElementInGroup = (element, groupId) => element.groupIds.includes(groupId);
12063
+ var getElementsInGroup = (elements, groupId) => {
12064
+ const elementsInGroup = [];
12065
+ for (const element of elements.values()) {
12066
+ if (isElementInGroup(element, groupId)) {
12067
+ elementsInGroup.push(element);
12068
+ }
12225
12069
  }
12226
- if (predecessor && !successor) {
12227
- return predecessor < index;
12070
+ return elementsInGroup;
12071
+ };
12072
+ var getSelectedGroupIdForElement = (element, selectedGroupIds) => element.groupIds.find((groupId) => selectedGroupIds[groupId]);
12073
+ var addToGroup = (prevGroupIds, newGroupId, editingGroupId) => {
12074
+ const groupIds = [...prevGroupIds];
12075
+ const positionOfEditingGroupId = editingGroupId ? groupIds.indexOf(editingGroupId) : -1;
12076
+ const positionToInsert = positionOfEditingGroupId > -1 ? positionOfEditingGroupId : groupIds.length;
12077
+ groupIds.splice(positionToInsert, 0, newGroupId);
12078
+ return groupIds;
12079
+ };
12080
+ var removeFromSelectedGroups = (groupIds, selectedGroupIds) => groupIds.filter((groupId) => !selectedGroupIds[groupId]);
12081
+ var getMaximumGroups = (elements, elementsMap) => {
12082
+ const groups = /* @__PURE__ */ new Map();
12083
+ elements.forEach((element) => {
12084
+ const groupId = element.groupIds.length === 0 ? element.id : element.groupIds[element.groupIds.length - 1];
12085
+ const currentGroupMembers = groups.get(groupId) || [];
12086
+ const boundTextElement = getBoundTextElement(element, elementsMap);
12087
+ if (boundTextElement) {
12088
+ currentGroupMembers.push(boundTextElement);
12089
+ }
12090
+ groups.set(groupId, [...currentGroupMembers, element]);
12091
+ });
12092
+ return Array.from(groups.values());
12093
+ };
12094
+ var getNonDeletedGroupIds = (elements) => {
12095
+ const nonDeletedGroupIds = /* @__PURE__ */ new Set();
12096
+ for (const [, element] of elements) {
12097
+ if (element.isDeleted) {
12098
+ continue;
12099
+ }
12100
+ for (const groupId of element.groupIds ?? []) {
12101
+ nonDeletedGroupIds.add(groupId);
12102
+ }
12228
12103
  }
12229
- return !!index;
12104
+ return nonDeletedGroupIds;
12230
12105
  };
12231
- var generateIndices = (elements, indicesGroups) => {
12232
- const elementsUpdates = /* @__PURE__ */ new Map();
12233
- for (const indices of indicesGroups) {
12234
- const lowerBoundIndex = indices.shift();
12235
- const upperBoundIndex = indices.pop();
12236
- const fractionalIndices = generateNKeysBetween(
12237
- elements[lowerBoundIndex]?.index,
12238
- elements[upperBoundIndex]?.index,
12239
- indices.length
12240
- );
12241
- for (let i = 0; i < indices.length; i++) {
12242
- const element = elements[indices[i]];
12243
- elementsUpdates.set(element, {
12244
- index: fractionalIndices[i]
12245
- });
12106
+ var elementsAreInSameGroup = (elements) => {
12107
+ const allGroups = elements.flatMap((element) => element.groupIds);
12108
+ const groupCount = /* @__PURE__ */ new Map();
12109
+ let maxGroup = 0;
12110
+ for (const group of allGroups) {
12111
+ groupCount.set(group, (groupCount.get(group) ?? 0) + 1);
12112
+ if (groupCount.get(group) > maxGroup) {
12113
+ maxGroup = groupCount.get(group);
12246
12114
  }
12247
12115
  }
12248
- return elementsUpdates;
12116
+ return maxGroup === elements.length;
12249
12117
  };
12250
- var isOrderedElement = (element) => {
12251
- if (element.index) {
12252
- return true;
12118
+ var isInGroup = (element) => {
12119
+ return element.groupIds.length > 0;
12120
+ };
12121
+ var getNewGroupIdsForDuplication = (groupIds, editingGroupId, mapper) => {
12122
+ const copy = [...groupIds];
12123
+ const positionOfEditingGroupId = editingGroupId ? groupIds.indexOf(editingGroupId) : -1;
12124
+ const endIndex = positionOfEditingGroupId > -1 ? positionOfEditingGroupId : groupIds.length;
12125
+ for (let index = 0; index < endIndex; index++) {
12126
+ copy[index] = mapper(copy[index]);
12253
12127
  }
12254
- return false;
12128
+ return copy;
12129
+ };
12130
+ var getSelectedElementsByGroup = (selectedElements, elementsMap, appState) => {
12131
+ const selectedGroupIds = getSelectedGroupIds(appState);
12132
+ const unboundElements = selectedElements.filter(
12133
+ (element) => !isBoundToContainer(element)
12134
+ );
12135
+ const groups = /* @__PURE__ */ new Map();
12136
+ const elements = /* @__PURE__ */ new Map();
12137
+ const addToElementsMap = (element) => {
12138
+ const currentElementMembers = elements.get(element.id) || [];
12139
+ const boundTextElement = getBoundTextElement(element, elementsMap);
12140
+ if (boundTextElement) {
12141
+ currentElementMembers.push(boundTextElement);
12142
+ }
12143
+ elements.set(element.id, [...currentElementMembers, element]);
12144
+ };
12145
+ const addToGroupsMap = (element, groupId) => {
12146
+ const currentGroupMembers = groups.get(groupId) || [];
12147
+ const boundTextElement = getBoundTextElement(element, elementsMap);
12148
+ if (boundTextElement) {
12149
+ currentGroupMembers.push(boundTextElement);
12150
+ }
12151
+ groups.set(groupId, [...currentGroupMembers, element]);
12152
+ };
12153
+ const handleSingleSelectedGroupCase = (element, selectedGroupId) => {
12154
+ const indexOfSelectedGroupId = element.groupIds.indexOf(selectedGroupId, 0);
12155
+ const nestedGroupCount = element.groupIds.slice(
12156
+ 0,
12157
+ indexOfSelectedGroupId
12158
+ ).length;
12159
+ return nestedGroupCount > 0 ? addToGroupsMap(element, element.groupIds[indexOfSelectedGroupId - 1]) : addToElementsMap(element);
12160
+ };
12161
+ const isAllInSameGroup = selectedElements.every(
12162
+ (element) => isSelectedViaGroup(appState, element)
12163
+ );
12164
+ unboundElements.forEach((element) => {
12165
+ const selectedGroupId = getSelectedGroupIdForElement(
12166
+ element,
12167
+ appState.selectedGroupIds
12168
+ );
12169
+ if (!selectedGroupId) {
12170
+ addToElementsMap(element);
12171
+ } else if (selectedGroupIds.length === 1 && isAllInSameGroup) {
12172
+ handleSingleSelectedGroupCase(element, selectedGroupId);
12173
+ } else {
12174
+ addToGroupsMap(element, selectedGroupId);
12175
+ }
12176
+ });
12177
+ return Array.from(groups.values()).concat(Array.from(elements.values()));
12255
12178
  };
12256
12179
 
12257
12180
  // src/zindex.ts
@@ -12263,7 +12186,7 @@ var getIndicesToMove = (elements, appState, elementsToBeMoved) => {
12263
12186
  let deletedIndices = [];
12264
12187
  let includeDeletedIndex = null;
12265
12188
  let index = -1;
12266
- const selectedElementIds = arrayToMap7(
12189
+ const selectedElementIds = arrayToMap6(
12267
12190
  elementsToBeMoved ? elementsToBeMoved : getSelectedElements(elements, appState, {
12268
12191
  includeBoundTextElement: true,
12269
12192
  includeElementsInFrames: true
@@ -12553,7 +12476,7 @@ var shiftElementsToEnd = (elements, appState, direction, containingFrame, elemen
12553
12476
  return nextElements;
12554
12477
  };
12555
12478
  function shiftElementsAccountingForFrames(allElements, appState, direction, shiftFunction) {
12556
- const elementsToMove = arrayToMap7(
12479
+ const elementsToMove = arrayToMap6(
12557
12480
  getSelectedElements(allElements, appState, {
12558
12481
  includeBoundTextElement: true,
12559
12482
  includeElementsInFrames: true
@@ -12888,7 +12811,7 @@ var LinearElementEditor = class _LinearElementEditor {
12888
12811
  console.error(
12889
12812
  `There must be a valid lastClickedPoint in order to drag it. selectedPointsIndices(${JSON.stringify(
12890
12813
  selectedPointsIndices
12891
- )}) points(0..${element.points.length - 1}) lastClickedPoint(${lastClickedPoint})`
12814
+ )}) points(0..${element.points.length - 1}) lastClickedPoint(${lastClickedPoint}) isElbowArrow: ${elbowed}`
12892
12815
  );
12893
12816
  lastClickedPoint = element.points.length - 1;
12894
12817
  }
@@ -13105,7 +13028,8 @@ var LinearElementEditor = class _LinearElementEditor {
13105
13028
  element.points[index],
13106
13029
  element.points[index + 1],
13107
13030
  index,
13108
- appState.zoom
13031
+ appState.zoom,
13032
+ elementsMap
13109
13033
  )) {
13110
13034
  midpoints.push(null);
13111
13035
  index++;
@@ -13113,7 +13037,8 @@ var LinearElementEditor = class _LinearElementEditor {
13113
13037
  }
13114
13038
  const segmentMidPoint = _LinearElementEditor.getSegmentMidPoint(
13115
13039
  element,
13116
- index + 1
13040
+ index + 1,
13041
+ elementsMap
13117
13042
  );
13118
13043
  midpoints.push(segmentMidPoint);
13119
13044
  index++;
@@ -13177,7 +13102,7 @@ var LinearElementEditor = class _LinearElementEditor {
13177
13102
  }
13178
13103
  return null;
13179
13104
  };
13180
- static isSegmentTooShort(element, startPoint, endPoint, index, zoom) {
13105
+ static isSegmentTooShort(element, startPoint, endPoint, index, zoom, elementsMap) {
13181
13106
  if (isElbowArrow(element)) {
13182
13107
  if (index >= 0 && index < element.points.length) {
13183
13108
  return pointDistance5(startPoint, endPoint) * zoom.value < _LinearElementEditor.POINT_HANDLE_SIZE / 2;
@@ -13186,7 +13111,10 @@ var LinearElementEditor = class _LinearElementEditor {
13186
13111
  }
13187
13112
  let distance3 = pointDistance5(startPoint, endPoint);
13188
13113
  if (element.points.length > 2 && element.roundness) {
13189
- const [lines, curves] = deconstructLinearOrFreeDrawElement(element);
13114
+ const [lines, curves] = deconstructLinearOrFreeDrawElement(
13115
+ element,
13116
+ elementsMap
13117
+ );
13190
13118
  invariant8(
13191
13119
  lines.length === 0 && curves.length > 0,
13192
13120
  "Only linears built out of curves are supported"
@@ -13199,7 +13127,7 @@ var LinearElementEditor = class _LinearElementEditor {
13199
13127
  }
13200
13128
  return distance3 * zoom.value < _LinearElementEditor.POINT_HANDLE_SIZE * 4;
13201
13129
  }
13202
- static getSegmentMidPoint(element, index) {
13130
+ static getSegmentMidPoint(element, index, elementsMap) {
13203
13131
  if (isElbowArrow(element)) {
13204
13132
  invariant8(
13205
13133
  element.points.length >= index,
@@ -13208,7 +13136,10 @@ var LinearElementEditor = class _LinearElementEditor {
13208
13136
  const p = pointCenter2(element.points[index - 1], element.points[index]);
13209
13137
  return pointFrom11(element.x + p[0], element.y + p[1]);
13210
13138
  }
13211
- const [lines, curves] = deconstructLinearOrFreeDrawElement(element);
13139
+ const [lines, curves] = deconstructLinearOrFreeDrawElement(
13140
+ element,
13141
+ elementsMap
13142
+ );
13212
13143
  invariant8(
13213
13144
  lines.length === 0 && curves.length > 0 || lines.length > 0 && curves.length === 0,
13214
13145
  "Only linears built out of either segments or curves are supported"
@@ -13728,7 +13659,7 @@ var LinearElementEditor = class _LinearElementEditor {
13728
13659
  pointerDownState: linearElementEditor.initialState,
13729
13660
  selectedPointsIndices: linearElementEditor.selectedPointsIndices
13730
13661
  };
13731
- const midpoint2 = _LinearElementEditor.createPointAt(
13662
+ const midpoint = _LinearElementEditor.createPointAt(
13732
13663
  element,
13733
13664
  elementsMap,
13734
13665
  pointerCoords.x,
@@ -13737,7 +13668,7 @@ var LinearElementEditor = class _LinearElementEditor {
13737
13668
  );
13738
13669
  const points = [
13739
13670
  ...element.points.slice(0, segmentMidpoint.index),
13740
- midpoint2,
13671
+ midpoint,
13741
13672
  ...element.points.slice(segmentMidpoint.index)
13742
13673
  ];
13743
13674
  scene.mutateElement(element, { points });
@@ -13843,7 +13774,8 @@ var LinearElementEditor = class _LinearElementEditor {
13843
13774
  const index = element.points.length / 2 - 1;
13844
13775
  const midSegmentMidpoint = _LinearElementEditor.getSegmentMidPoint(
13845
13776
  element,
13846
- index + 1
13777
+ index + 1,
13778
+ elementsMap
13847
13779
  );
13848
13780
  x = midSegmentMidpoint[0] - boundTextElement.width / 2;
13849
13781
  y = midSegmentMidpoint[1] - boundTextElement.height / 2;
@@ -14031,13 +13963,11 @@ var normalizeSelectedPoints = (points) => {
14031
13963
  var pointDraggingUpdates = (selectedPointsIndices, deltaX, deltaY, scenePointerX, scenePointerY, elementsMap, element, elements, app, angleLocked, altKey, linearElementEditor) => {
14032
13964
  const naiveDraggingPoints = new Map(
14033
13965
  selectedPointsIndices.map((pointIndex) => {
13966
+ const point = element.points[pointIndex] ?? element.points.at(-1);
14034
13967
  return [
14035
13968
  pointIndex,
14036
13969
  {
14037
- point: pointFrom11(
14038
- element.points[pointIndex][0] + deltaX,
14039
- element.points[pointIndex][1] + deltaY
14040
- ),
13970
+ point: pointFrom11(point[0] + deltaX, point[1] + deltaY),
14041
13971
  isDragging: true
14042
13972
  }
14043
13973
  ];
@@ -14224,7 +14154,7 @@ var pointDraggingUpdates = (selectedPointsIndices, deltaX, deltaY, scenePointerX
14224
14154
  nextArrow.endBinding.elementId
14225
14155
  ) : null;
14226
14156
  const endLocalPoint = startIsDraggingOverEndElement ? nextArrow.points[nextArrow.points.length - 1] : endIsDraggingOverStartElement && app.state.bindMode !== "inside" && getFeatureFlag2("COMPLEX_BINDINGS") ? nextArrow.points[0] : endBindable ? updateBoundPoint(
14227
- element,
14157
+ nextArrow,
14228
14158
  "endBinding",
14229
14159
  nextArrow.endBinding,
14230
14160
  endBindable,
@@ -14236,7 +14166,7 @@ var pointDraggingUpdates = (selectedPointsIndices, deltaX, deltaY, scenePointerX
14236
14166
  nextArrow.startBinding.elementId
14237
14167
  ) : null;
14238
14168
  const startLocalPoint = endIsDraggingOverStartElement && getFeatureFlag2("COMPLEX_BINDINGS") ? nextArrow.points[0] : startIsDraggingOverEndElement && app.state.bindMode !== "inside" && getFeatureFlag2("COMPLEX_BINDINGS") ? endLocalPoint : startBindable ? updateBoundPoint(
14239
- element,
14169
+ nextArrow,
14240
14170
  "startBinding",
14241
14171
  nextArrow.startBinding,
14242
14172
  startBindable,
@@ -14920,7 +14850,7 @@ function getFreedrawOutlineAsSegments(element, points, elementsMap) {
14920
14850
  return points.slice(2).reduce(
14921
14851
  (acc, curr) => {
14922
14852
  acc.push(
14923
- lineSegment5(
14853
+ lineSegment6(
14924
14854
  acc[acc.length - 1][1],
14925
14855
  pointRotateRads11(
14926
14856
  pointFrom12(curr[0] + element.x, curr[1] + element.y),
@@ -14932,7 +14862,7 @@ function getFreedrawOutlineAsSegments(element, points, elementsMap) {
14932
14862
  return acc;
14933
14863
  },
14934
14864
  [
14935
- lineSegment5(
14865
+ lineSegment6(
14936
14866
  pointRotateRads11(
14937
14867
  pointFrom12(
14938
14868
  points[0][0] + element.x,
@@ -14954,16 +14884,6 @@ function getFreedrawOutlineAsSegments(element, points, elementsMap) {
14954
14884
  );
14955
14885
  }
14956
14886
 
14957
- // src/comparisons.ts
14958
- init_define_import_meta_env();
14959
- var hasBackground = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "ellipse" || type === "diamond" || type === "line" || type === "freedraw";
14960
- var hasStrokeColor = (type) => type === "rectangle" || type === "ellipse" || type === "diamond" || type === "freedraw" || type === "arrow" || type === "line" || type === "text" || type === "embeddable";
14961
- var hasStrokeWidth = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "ellipse" || type === "diamond" || type === "freedraw" || type === "arrow" || type === "line";
14962
- var hasStrokeStyle = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "ellipse" || type === "diamond" || type === "arrow" || type === "line";
14963
- var canChangeRoundness = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "line" || type === "diamond" || type === "image";
14964
- var toolIsArrow = (type) => type === "arrow";
14965
- var canHaveArrowheads = (type) => type === "arrow";
14966
-
14967
14887
  // src/shape.ts
14968
14888
  var ShapeCache = class _ShapeCache {
14969
14889
  static rg = new RoughGenerator();
@@ -15315,7 +15235,7 @@ var getArrowheadShapes = (element, shape, position, arrowhead, generator, option
15315
15235
  }
15316
15236
  }
15317
15237
  };
15318
- var generateLinearCollisionShape = (element) => {
15238
+ var generateLinearCollisionShape = (element, elementsMap) => {
15319
15239
  const generator = new RoughGenerator();
15320
15240
  const options = {
15321
15241
  seed: element.seed,
@@ -15324,20 +15244,7 @@ var generateLinearCollisionShape = (element) => {
15324
15244
  roughness: 0,
15325
15245
  preserveVertices: true
15326
15246
  };
15327
- const center = getCenterForBounds(
15328
- // Need a non-rotated center point
15329
- element.points.reduce(
15330
- (acc, point) => {
15331
- return [
15332
- Math.min(element.x + point[0], acc[0]),
15333
- Math.min(element.y + point[1], acc[1]),
15334
- Math.max(element.x + point[0], acc[2]),
15335
- Math.max(element.y + point[1], acc[3])
15336
- ];
15337
- },
15338
- [Infinity, Infinity, -Infinity, -Infinity]
15339
- )
15340
- );
15247
+ const center = elementCenterPoint(element, elementsMap);
15341
15248
  switch (element.type) {
15342
15249
  case "line":
15343
15250
  case "arrow": {
@@ -15984,7 +15891,7 @@ var getElementLineSegments = (element, elementsMap) => {
15984
15891
  let i = 0;
15985
15892
  while (i < points.length - 1) {
15986
15893
  segments.push(
15987
- lineSegment6(
15894
+ lineSegment7(
15988
15895
  pointFrom14(points[i][0], points[i][1]),
15989
15896
  pointFrom14(points[i + 1][0], points[i + 1][1])
15990
15897
  )
@@ -15997,7 +15904,7 @@ var getElementLineSegments = (element, elementsMap) => {
15997
15904
  let i = 0;
15998
15905
  while (i < points.length - 1) {
15999
15906
  segments.push(
16000
- lineSegment6(
15907
+ lineSegment7(
16001
15908
  pointFrom14(points[i][0], points[i][1]),
16002
15909
  pointFrom14(points[i + 1][0], points[i + 1][1])
16003
15910
  )
@@ -16023,10 +15930,10 @@ var getElementLineSegments = (element, elementsMap) => {
16023
15930
  const container = getContainerElement(element, elementsMap);
16024
15931
  if (container && isLinearElement(container)) {
16025
15932
  const segments2 = [
16026
- lineSegment6(pointFrom14(x1, y1), pointFrom14(x2, y1)),
16027
- lineSegment6(pointFrom14(x2, y1), pointFrom14(x2, y2)),
16028
- lineSegment6(pointFrom14(x2, y2), pointFrom14(x1, y2)),
16029
- lineSegment6(pointFrom14(x1, y2), pointFrom14(x1, y1))
15933
+ lineSegment7(pointFrom14(x1, y1), pointFrom14(x2, y1)),
15934
+ lineSegment7(pointFrom14(x2, y1), pointFrom14(x2, y2)),
15935
+ lineSegment7(pointFrom14(x2, y2), pointFrom14(x1, y2)),
15936
+ lineSegment7(pointFrom14(x1, y2), pointFrom14(x1, y1))
16030
15937
  ];
16031
15938
  return segments2;
16032
15939
  }
@@ -16034,7 +15941,7 @@ var getElementLineSegments = (element, elementsMap) => {
16034
15941
  const points = shape.data;
16035
15942
  const segments = [];
16036
15943
  for (let i = 0; i < points.length - 1; i++) {
16037
- segments.push(lineSegment6(points[i], points[i + 1]));
15944
+ segments.push(lineSegment7(points[i], points[i + 1]));
16038
15945
  }
16039
15946
  return segments;
16040
15947
  } else if (shape.type === "ellipse") {
@@ -16051,14 +15958,14 @@ var getElementLineSegments = (element, elementsMap) => {
16051
15958
  [x2, cy]
16052
15959
  ].map((point) => pointRotateRads13(point, center, element.angle));
16053
15960
  return [
16054
- lineSegment6(nw, ne),
16055
- lineSegment6(sw, se2),
16056
- lineSegment6(nw, sw),
16057
- lineSegment6(ne, se2),
16058
- lineSegment6(nw, e),
16059
- lineSegment6(sw, e),
16060
- lineSegment6(ne, w),
16061
- lineSegment6(se2, w)
15961
+ lineSegment7(nw, ne),
15962
+ lineSegment7(sw, se2),
15963
+ lineSegment7(nw, sw),
15964
+ lineSegment7(ne, se2),
15965
+ lineSegment7(nw, e),
15966
+ lineSegment7(sw, e),
15967
+ lineSegment7(ne, w),
15968
+ lineSegment7(se2, w)
16062
15969
  ];
16063
15970
  };
16064
15971
  var _isRectanguloidElement = (element) => {
@@ -16066,7 +15973,7 @@ var _isRectanguloidElement = (element) => {
16066
15973
  };
16067
15974
  var getRotatedSides = (sides, center, angle) => {
16068
15975
  return sides.map((side) => {
16069
- return lineSegment6(
15976
+ return lineSegment7(
16070
15977
  pointRotateRads13(side[0], center, angle),
16071
15978
  pointRotateRads13(side[1], center, angle)
16072
15979
  );
@@ -16078,7 +15985,7 @@ var getSegmentsOnCurve = (curve4, center, angle) => {
16078
15985
  const segments = [];
16079
15986
  while (i < points.length - 1) {
16080
15987
  segments.push(
16081
- lineSegment6(
15988
+ lineSegment7(
16082
15989
  pointRotateRads13(
16083
15990
  pointFrom14(points[i][0], points[i][1]),
16084
15991
  center,
@@ -16113,9 +16020,9 @@ var getSegmentsOnEllipse = (ellipse4) => {
16113
16020
  points.push(pointRotateRads13(pointFrom14(x, y), center, ellipse4.angle));
16114
16021
  }
16115
16022
  for (let i = 0; i < points.length - 1; i++) {
16116
- segments.push(lineSegment6(points[i], points[i + 1]));
16023
+ segments.push(lineSegment7(points[i], points[i + 1]));
16117
16024
  }
16118
- segments.push(lineSegment6(points[points.length - 1], points[0]));
16025
+ segments.push(lineSegment7(points[points.length - 1], points[0]));
16119
16026
  return segments;
16120
16027
  };
16121
16028
  var getRectangleBoxAbsoluteCoords = (boxSceneCoords) => {
@@ -16228,7 +16135,7 @@ var getMinMaxXYFromCurvePathOps = (ops, transformXY) => {
16228
16135
  );
16229
16136
  return [minX, minY, maxX, maxY];
16230
16137
  };
16231
- var getBoundsFromPoints = (points) => {
16138
+ var getBoundsFromPoints = (points, padding = 0) => {
16232
16139
  let minX = Infinity;
16233
16140
  let minY = Infinity;
16234
16141
  let maxX = -Infinity;
@@ -16239,7 +16146,7 @@ var getBoundsFromPoints = (points) => {
16239
16146
  maxX = Math.max(maxX, x);
16240
16147
  maxY = Math.max(maxY, y);
16241
16148
  }
16242
- return [minX, minY, maxX, maxY];
16149
+ return [minX - padding, minY - padding, maxX + padding, maxY + padding];
16243
16150
  };
16244
16151
  var getFreeDrawElementAbsoluteCoords = (element) => {
16245
16152
  const [minX, minY, maxX, maxY] = getBoundsFromPoints(element.points);
@@ -16455,7 +16362,7 @@ var getCommonBounds = (elements, elementsMap) => {
16455
16362
  let maxX = -Infinity;
16456
16363
  let minY = Infinity;
16457
16364
  let maxY = -Infinity;
16458
- const _elementsMap = elementsMap || arrayToMap8(elements);
16365
+ const _elementsMap = elementsMap || arrayToMap7(elements);
16459
16366
  elements.forEach((element) => {
16460
16367
  const [x1, y1, x2, y2] = getElementBounds(element, _elementsMap);
16461
16368
  minX = Math.min(minX, x1);
@@ -16530,7 +16437,7 @@ var getClosestElementBounds = (elements, from) => {
16530
16437
  }
16531
16438
  let minDistance = Infinity;
16532
16439
  let closestElement = elements[0];
16533
- const elementsMap = arrayToMap8(elements);
16440
+ const elementsMap = arrayToMap7(elements);
16534
16441
  elements.forEach((element) => {
16535
16442
  const [x1, y1, x2, y2] = getElementBounds(element, elementsMap);
16536
16443
  const distance3 = pointDistance7(
@@ -16623,6 +16530,7 @@ var aabbForElement = (element, elementsMap, offset) => {
16623
16530
  return bounds;
16624
16531
  };
16625
16532
  var pointInsideBounds = (p, bounds) => p[0] > bounds[0] && p[0] < bounds[2] && p[1] > bounds[1] && p[1] < bounds[3];
16533
+ var pointInsideBoundsInclusive = (p, bounds) => p[0] >= bounds[0] && p[0] <= bounds[2] && p[1] >= bounds[1] && p[1] <= bounds[3];
16626
16534
  var doBoundsIntersect = (bounds1, bounds2) => {
16627
16535
  if (bounds1 == null || bounds2 == null) {
16628
16536
  return false;
@@ -16631,8 +16539,14 @@ var doBoundsIntersect = (bounds1, bounds2) => {
16631
16539
  const [minX2, minY2, maxX2, maxY2] = bounds2;
16632
16540
  return minX1 < maxX2 && maxX1 > minX2 && minY1 < maxY2 && maxY1 > minY2;
16633
16541
  };
16542
+ var boundsContainBounds = (outerBounds, innerBounds) => [
16543
+ pointFrom14(innerBounds[0], innerBounds[1]),
16544
+ pointFrom14(innerBounds[0], innerBounds[3]),
16545
+ pointFrom14(innerBounds[2], innerBounds[1]),
16546
+ pointFrom14(innerBounds[2], innerBounds[3])
16547
+ ].every((point) => pointInsideBoundsInclusive(point, outerBounds));
16634
16548
  var elementCenterPoint = (element, elementsMap, xOffset = 0, yOffset = 0) => {
16635
- if (isLinearElement(element)) {
16549
+ if (isLinearElement(element) || isFreeDrawElement(element)) {
16636
16550
  const [x1, y1, x2, y2] = getElementAbsoluteCoords2(element, elementsMap);
16637
16551
  const [x3, y3] = pointFrom14((x1 + x2) / 2, (y1 + y2) / 2);
16638
16552
  return pointFrom14(x3 + xOffset, y3 + yOffset);
@@ -17398,79 +17312,61 @@ import {
17398
17312
 
17399
17313
  // src/sortElements.ts
17400
17314
  init_define_import_meta_env();
17401
- import { arrayToMapWithIndex } from "@excalidraw/common";
17402
- var normalizeGroupElementOrder = (elements) => {
17403
- const origElements = elements.slice();
17404
- const sortedElements = /* @__PURE__ */ new Set();
17405
- const orderInnerGroups = (elements2) => {
17406
- const firstGroupSig = elements2[0]?.groupIds?.join("");
17407
- const aGroup = [elements2[0]];
17408
- const bGroup = [];
17409
- for (const element of elements2.slice(1)) {
17410
- if (element.groupIds?.join("") === firstGroupSig) {
17411
- aGroup.push(element);
17412
- } else {
17413
- bGroup.push(element);
17414
- }
17415
- }
17416
- return bGroup.length ? [...aGroup, ...orderInnerGroups(bGroup)] : aGroup;
17315
+ import { arrayToMap as arrayToMap8 } from "@excalidraw/common";
17316
+ var defragmentGroups = (elements) => {
17317
+ const groupIdAtLevel = (element, level) => {
17318
+ return element.groupIds[element.groupIds.length - level - 1];
17417
17319
  };
17418
- const groupHandledElements = /* @__PURE__ */ new Map();
17419
- origElements.forEach((element, idx) => {
17420
- if (groupHandledElements.has(element.id)) {
17421
- return;
17422
- }
17423
- if (element.groupIds?.length) {
17424
- const topGroup = element.groupIds[element.groupIds.length - 1];
17425
- const groupElements = origElements.slice(idx).filter((element2) => {
17426
- const ret = element2?.groupIds?.some((id) => id === topGroup);
17427
- if (ret) {
17428
- groupHandledElements.set(element2.id, true);
17429
- }
17430
- return ret;
17431
- });
17432
- for (const elem of orderInnerGroups(groupElements)) {
17433
- sortedElements.add(elem);
17320
+ const orderLevel = (levelElements, level) => {
17321
+ const buckets = /* @__PURE__ */ new Map();
17322
+ const slots = [];
17323
+ for (const element of levelElements) {
17324
+ const groupId = groupIdAtLevel(element, level);
17325
+ if (groupId === void 0) {
17326
+ slots.push(element);
17327
+ continue;
17434
17328
  }
17435
- } else {
17436
- sortedElements.add(element);
17329
+ let bucket = buckets.get(groupId);
17330
+ if (!bucket) {
17331
+ bucket = [];
17332
+ buckets.set(groupId, bucket);
17333
+ slots.push(groupId);
17334
+ }
17335
+ bucket.push(element);
17437
17336
  }
17438
- });
17439
- if (sortedElements.size !== elements.length) {
17440
- console.error("normalizeGroupElementOrder: lost some elements... bailing!");
17337
+ return slots.flatMap(
17338
+ (slot) => typeof slot === "string" ? orderLevel(buckets.get(slot), level + 1) : [slot]
17339
+ );
17340
+ };
17341
+ const sortedElements = orderLevel(elements, 0);
17342
+ if (sortedElements.length !== elements.length) {
17343
+ console.error("defragmentGroups: lost some elements... bailing!");
17441
17344
  return elements;
17442
17345
  }
17443
- return [...sortedElements];
17346
+ return sortedElements;
17444
17347
  };
17445
17348
  var normalizeBoundElementsOrder = (elements) => {
17446
- const elementsMap = arrayToMapWithIndex(elements);
17447
- const origElements = elements.slice();
17349
+ const elementsMap = arrayToMap8(elements);
17448
17350
  const sortedElements = /* @__PURE__ */ new Set();
17449
- origElements.forEach((element, idx) => {
17450
- if (!element) {
17451
- return;
17351
+ for (const element of elements) {
17352
+ if (sortedElements.has(element)) {
17353
+ continue;
17452
17354
  }
17453
17355
  if (element.boundElements?.length) {
17454
17356
  sortedElements.add(element);
17455
- origElements[idx] = null;
17456
- element.boundElements.forEach((boundElement) => {
17357
+ for (const boundElement of element.boundElements) {
17457
17358
  const child = elementsMap.get(boundElement.id);
17458
17359
  if (child && boundElement.type === "text") {
17459
- sortedElements.add(child[0]);
17460
- origElements[child[1]] = null;
17360
+ sortedElements.add(child);
17461
17361
  }
17462
- });
17463
- } else if (element.type === "text" && element.containerId) {
17464
- const parent = elementsMap.get(element.containerId);
17465
- if (!parent?.[0].boundElements?.find((x) => x.id === element.id)) {
17466
- sortedElements.add(element);
17467
- origElements[idx] = null;
17468
17362
  }
17469
- } else {
17470
- sortedElements.add(element);
17471
- origElements[idx] = null;
17363
+ continue;
17472
17364
  }
17473
- });
17365
+ if (element.type === "text" && element.containerId && elementsMap.get(element.containerId)?.boundElements?.some((el) => el.id === element.id)) {
17366
+ continue;
17367
+ }
17368
+ sortedElements.add(element);
17369
+ }
17474
17370
  if (sortedElements.size !== elements.length) {
17475
17371
  console.error(
17476
17372
  "normalizeBoundElementsOrder: lost some elements... bailing!"
@@ -17480,7 +17376,7 @@ var normalizeBoundElementsOrder = (elements) => {
17480
17376
  return [...sortedElements];
17481
17377
  };
17482
17378
  var normalizeElementOrder = (elements) => {
17483
- return normalizeBoundElementsOrder(normalizeGroupElementOrder(elements));
17379
+ return normalizeBoundElementsOrder(defragmentGroups(elements));
17484
17380
  };
17485
17381
 
17486
17382
  // src/duplicate.ts
@@ -17522,6 +17418,7 @@ var duplicateElements = (opts) => {
17522
17418
  const duplicateElementsMap = /* @__PURE__ */ new Map();
17523
17419
  const elementsMap = arrayToMap9(elements);
17524
17420
  const _idsOfElementsToDuplicate = opts.type === "in-place" ? opts.idsOfElementsToDuplicate : new Map(elements.map((el) => [el.id, el]));
17421
+ const preserveFrameChildrenOrder = opts.type === "everything" && opts.preserveFrameChildrenOrder;
17525
17422
  if (opts.type === "in-place") {
17526
17423
  for (const groupId of Object.keys(opts.appState.selectedGroupIds)) {
17527
17424
  elements.filter((el) => el.groupIds?.includes(groupId)).forEach((el) => _idsOfElementsToDuplicate.set(el.id, el));
@@ -17581,7 +17478,7 @@ var duplicateElements = (opts) => {
17581
17478
  const groupId = getSelectedGroupForElement(appState, element);
17582
17479
  if (groupId) {
17583
17480
  const groupElements = getElementsInGroup(elements, groupId).flatMap(
17584
- (element2) => isFrameLikeElement(element2) ? [...getFrameChildren(elements, element2.id), element2] : [element2]
17481
+ (element2) => isFrameLikeElement(element2) && !preserveFrameChildrenOrder ? [...getFrameChildren(elements, element2.id), element2] : [element2]
17585
17482
  );
17586
17483
  const targetIndex = findLastIndex2(elementsWithDuplicates, (el) => {
17587
17484
  return el.groupIds?.includes(groupId);
@@ -17589,11 +17486,18 @@ var duplicateElements = (opts) => {
17589
17486
  insertBeforeOrAfterIndex(targetIndex, copyElements(groupElements));
17590
17487
  continue;
17591
17488
  }
17592
- if (element.frameId && frameIdsToDuplicate.has(element.frameId)) {
17489
+ if (!preserveFrameChildrenOrder && element.frameId && frameIdsToDuplicate.has(element.frameId)) {
17593
17490
  continue;
17594
17491
  }
17595
17492
  if (isFrameLikeElement(element)) {
17596
17493
  const frameId = element.id;
17494
+ if (preserveFrameChildrenOrder) {
17495
+ insertBeforeOrAfterIndex(
17496
+ findLastIndex2(elementsWithDuplicates, (el) => el.id === frameId),
17497
+ copyElements(element)
17498
+ );
17499
+ continue;
17500
+ }
17597
17501
  const frameChildren = getFrameChildren(elements, frameId);
17598
17502
  const targetIndex = findLastIndex2(elementsWithDuplicates, (el) => {
17599
17503
  return el.frameId === frameId || el.id === frameId;
@@ -18591,24 +18495,14 @@ var Scene = class {
18591
18495
  this.selectedElementsCache.cache.clear();
18592
18496
  this.callbacks.clear();
18593
18497
  }
18594
- insertElementAtIndex(element, index) {
18595
- if (!Number.isFinite(index) || index < 0) {
18596
- throw new Error(
18597
- "insertElementAtIndex can only be called with index >= 0"
18598
- );
18599
- }
18600
- const nextElements = [
18601
- ...this.elements.slice(0, index),
18602
- element,
18603
- ...this.elements.slice(index)
18604
- ];
18605
- syncMovedIndices2(nextElements, arrayToMap10([element]));
18606
- this.replaceAllElements(nextElements);
18607
- }
18498
+ /** low-level - generally use app.insertNewElements() */
18608
18499
  insertElementsAtIndex(elements, index) {
18609
18500
  if (!elements.length) {
18610
18501
  return;
18611
18502
  }
18503
+ if (index === null) {
18504
+ index = this.elements.length;
18505
+ }
18612
18506
  if (!Number.isFinite(index) || index < 0) {
18613
18507
  throw new Error(
18614
18508
  "insertElementAtIndex can only be called with index >= 0"
@@ -18622,16 +18516,9 @@ var Scene = class {
18622
18516
  syncMovedIndices2(nextElements, arrayToMap10(elements));
18623
18517
  this.replaceAllElements(nextElements);
18624
18518
  }
18519
+ /** low-level - generally use app.insertNewElement() */
18625
18520
  insertElement = (element) => {
18626
- const index = element.frameId ? this.getElementIndex(element.frameId) : this.elements.length;
18627
- this.insertElementAtIndex(element, index);
18628
- };
18629
- insertElements = (elements) => {
18630
- if (!elements.length) {
18631
- return;
18632
- }
18633
- const index = elements[0]?.frameId ? this.getElementIndex(elements[0].frameId) : this.elements.length;
18634
- this.insertElementsAtIndex(elements, index);
18521
+ this.insertElementsAtIndex([element], null);
18635
18522
  };
18636
18523
  getElementIndex(elementId) {
18637
18524
  return this.elements.findIndex((element) => element.id === elementId);
@@ -26758,6 +26645,7 @@ export {
26758
26645
  bindOrUnbindBindingElements,
26759
26646
  bindPointToSnapToElementOutline,
26760
26647
  bindingProperties,
26648
+ boundsContainBounds,
26761
26649
  bumpVersion,
26762
26650
  calculateFixedPointForElbowArrowBinding,
26763
26651
  calculateFixedPointForNonElbowArrowBinding,
@@ -26828,6 +26716,7 @@ export {
26828
26716
  getClosestElementBounds,
26829
26717
  getCommonBoundingBox,
26830
26718
  getCommonBounds,
26719
+ getCommonFrameId,
26831
26720
  getContainerCenter,
26832
26721
  getContainerCoords,
26833
26722
  getContainerElement,
@@ -26857,6 +26746,7 @@ export {
26857
26746
  getEmbedLink,
26858
26747
  getFlipAdjustedCropPosition,
26859
26748
  getFrameChildren,
26749
+ getFrameChildrenInsertionIndex,
26860
26750
  getFrameLikeElements,
26861
26751
  getFrameLikeTitle,
26862
26752
  getFreedrawOutlineAsSegments,
@@ -26961,6 +26851,7 @@ export {
26961
26851
  isElementInViewport,
26962
26852
  isElementIntersectingFrame,
26963
26853
  isElementLink,
26854
+ isEligibleFrameChildType,
26964
26855
  isEmbeddableElement,
26965
26856
  isExcalidrawElement,
26966
26857
  isFixedPoint,
@@ -27035,6 +26926,7 @@ export {
27035
26926
  parseElementLinkFromURL,
27036
26927
  parseTokens,
27037
26928
  pointInsideBounds,
26929
+ pointInsideBoundsInclusive,
27038
26930
  positionElementsOnGrid,
27039
26931
  projectFixedPointOntoDiagonal,
27040
26932
  redrawTextBoundingBox,