@excalidraw/element 0.18.0-b1c6bfc → 0.18.0-b2b2815

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 (46) hide show
  1. package/dist/dev/index.js +552 -706
  2. package/dist/dev/index.js.map +4 -4
  3. package/dist/prod/index.js +19 -19
  4. package/dist/types/element/src/Scene.d.ts +5 -3
  5. package/dist/types/element/src/duplicate.d.ts +1 -0
  6. package/dist/types/element/src/frame.d.ts +6 -5
  7. package/dist/types/element/src/typeChecks.d.ts +1 -0
  8. package/dist/types/excalidraw/actions/actionAddToLibrary.d.ts +0 -3
  9. package/dist/types/excalidraw/actions/actionBoundText.d.ts +0 -2
  10. package/dist/types/excalidraw/actions/actionCanvas.d.ts +0 -12
  11. package/dist/types/excalidraw/actions/actionClipboard.d.ts +0 -2
  12. package/dist/types/excalidraw/actions/actionCropEditor.d.ts +0 -1
  13. package/dist/types/excalidraw/actions/actionDeleteSelected.d.ts +0 -3
  14. package/dist/types/excalidraw/actions/actionDeselect.d.ts +1 -2
  15. package/dist/types/excalidraw/actions/actionElementLink.d.ts +0 -1
  16. package/dist/types/excalidraw/actions/actionElementLock.d.ts +0 -2
  17. package/dist/types/excalidraw/actions/actionEmbeddable.d.ts +0 -1
  18. package/dist/types/excalidraw/actions/actionExport.d.ts +0 -2
  19. package/dist/types/excalidraw/actions/actionFrame.d.ts +0 -4
  20. package/dist/types/excalidraw/actions/actionGroup.d.ts +0 -2
  21. package/dist/types/excalidraw/actions/actionLinearEditor.d.ts +0 -1
  22. package/dist/types/excalidraw/actions/actionLink.d.ts +0 -1
  23. package/dist/types/excalidraw/actions/actionMenu.d.ts +0 -1
  24. package/dist/types/excalidraw/actions/actionProperties.d.ts +0 -2
  25. package/dist/types/excalidraw/actions/actionSelectAll.d.ts +0 -1
  26. package/dist/types/excalidraw/actions/actionStyles.d.ts +0 -1
  27. package/dist/types/excalidraw/actions/actionToggleArrowBinding.d.ts +0 -1
  28. package/dist/types/excalidraw/actions/actionToggleGridMode.d.ts +0 -1
  29. package/dist/types/excalidraw/actions/actionToggleMidpointSnapping.d.ts +0 -1
  30. package/dist/types/excalidraw/actions/actionToggleObjectsSnapMode.d.ts +0 -1
  31. package/dist/types/excalidraw/actions/actionToggleSearchMenu.d.ts +0 -1
  32. package/dist/types/excalidraw/actions/actionToggleStats.d.ts +0 -1
  33. package/dist/types/excalidraw/actions/actionToggleViewMode.d.ts +0 -1
  34. package/dist/types/excalidraw/actions/actionToggleZenMode.d.ts +0 -1
  35. package/dist/types/excalidraw/components/App.d.ts +9 -0
  36. package/dist/types/excalidraw/components/canvases/InteractiveCanvas.d.ts +1 -1
  37. package/dist/types/excalidraw/components/canvases/NewElementCanvas.d.ts +1 -0
  38. package/dist/types/excalidraw/components/canvases/StaticCanvas.d.ts +1 -1
  39. package/dist/types/excalidraw/components/shapes.d.ts +7 -0
  40. package/dist/types/excalidraw/data/blob.d.ts +0 -2
  41. package/dist/types/excalidraw/data/json.d.ts +0 -1
  42. package/dist/types/excalidraw/scene/Renderer.d.ts +425 -19
  43. package/dist/types/excalidraw/types.d.ts +5 -2
  44. package/dist/types/fractional-indexing/src/index.d.ts +29 -0
  45. package/dist/types/math/src/constants.d.ts +0 -1
  46. package/package.json +4 -3
package/dist/dev/index.js CHANGED
@@ -2260,7 +2260,7 @@ var rough_default = {
2260
2260
 
2261
2261
  // src/bounds.ts
2262
2262
  import {
2263
- arrayToMap as arrayToMap5,
2263
+ arrayToMap as arrayToMap6,
2264
2264
  invariant as invariant10,
2265
2265
  rescalePoints,
2266
2266
  sizeOf
@@ -3374,6 +3374,25 @@ var canBecomePolygon = (points) => {
3374
3374
  return points.length > 3 || // 3-point polygons can't have all points in a single line
3375
3375
  points.length === 3 && !pointsEqual(points[0], points[points.length - 1]);
3376
3376
  };
3377
+ var isEligibleFrameChildType = (type) => {
3378
+ switch (type) {
3379
+ case "rectangle":
3380
+ case "diamond":
3381
+ case "ellipse":
3382
+ case "arrow":
3383
+ case "line":
3384
+ case "freedraw":
3385
+ case "text":
3386
+ case "image":
3387
+ case "frame":
3388
+ case "embeddable": {
3389
+ return true;
3390
+ }
3391
+ default: {
3392
+ return false;
3393
+ }
3394
+ }
3395
+ };
3377
3396
 
3378
3397
  // src/utils.ts
3379
3398
  var ElementShapesCache = /* @__PURE__ */ new WeakMap();
@@ -3820,7 +3839,7 @@ var getSnapOutlineMidPoint = (point, element, elementsMap, zoom) => {
3820
3839
  )
3821
3840
  ];
3822
3841
  const candidate = sideMidpoints.find(
3823
- (midpoint2) => pointDistance2(point, midpoint2) <= maxBindingDistance_simple(zoom) + element.strokeWidth / 2 && !hitElementItself({
3842
+ (midpoint) => pointDistance2(point, midpoint) <= maxBindingDistance_simple(zoom) + element.strokeWidth / 2 && !hitElementItself({
3824
3843
  point,
3825
3844
  element,
3826
3845
  threshold: 0,
@@ -4937,12 +4956,22 @@ var distanceToLinearOrFreeDraElement = (element, elementsMap, p) => {
4937
4956
  );
4938
4957
  };
4939
4958
 
4959
+ // src/comparisons.ts
4960
+ init_define_import_meta_env();
4961
+ var hasBackground = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "ellipse" || type === "diamond" || type === "line" || type === "freedraw";
4962
+ var hasStrokeColor = (type) => type === "rectangle" || type === "ellipse" || type === "diamond" || type === "freedraw" || type === "arrow" || type === "line" || type === "text" || type === "embeddable";
4963
+ var hasStrokeWidth = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "ellipse" || type === "diamond" || type === "freedraw" || type === "arrow" || type === "line";
4964
+ var hasStrokeStyle = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "ellipse" || type === "diamond" || type === "arrow" || type === "line";
4965
+ var canChangeRoundness = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "line" || type === "diamond" || type === "image";
4966
+ var toolIsArrow = (type) => type === "arrow";
4967
+ var canHaveArrowheads = (type) => type === "arrow";
4968
+
4940
4969
  // src/collision.ts
4941
4970
  var shouldTestInside = (element) => {
4942
4971
  if (element.type === "arrow") {
4943
4972
  return false;
4944
4973
  }
4945
- const isDraggableFromInside = !isTransparent(element.backgroundColor) || hasBoundTextElement(element) || isIframeLikeElement(element) || isTextElement(element);
4974
+ const isDraggableFromInside = hasBackground(element.type) && !isTransparent(element.backgroundColor) || hasBoundTextElement(element) || isIframeLikeElement(element) || isTextElement(element);
4946
4975
  if (element.type === "line") {
4947
4976
  return isDraggableFromInside && isPathALoop(element.points);
4948
4977
  }
@@ -5075,7 +5104,7 @@ var getAllHoveredElementAtPoint = (point, elements, elementsMap, tolerance) => {
5075
5104
  );
5076
5105
  if (isBindableElement(element, false) && bindingBorderTest(element, point, elementsMap, tolerance)) {
5077
5106
  candidateElements.push(element);
5078
- if (!isTransparent(element.backgroundColor)) {
5107
+ if (hasBackground(element.type) && !isTransparent(element.backgroundColor)) {
5079
5108
  break;
5080
5109
  }
5081
5110
  }
@@ -6953,7 +6982,7 @@ var normalizeArrowElementUpdate = (global2, nextFixedSegments, startIsSpecial, e
6953
6982
  vectorScale6(vectorFromPoint6(global2[0]), -1)
6954
6983
  )
6955
6984
  );
6956
- if (offsetX < -MAX_POS || offsetX > MAX_POS || offsetY < -MAX_POS || offsetY > MAX_POS || offsetX + points[points.length - 1][0] < -MAX_POS || offsetY + points[points.length - 1][0] > MAX_POS || offsetX + points[points.length - 1][1] < -MAX_POS || offsetY + points[points.length - 1][1] > MAX_POS) {
6985
+ if (offsetX < -MAX_POS || offsetX > MAX_POS || offsetY < -MAX_POS || offsetY > MAX_POS || offsetX + points[points.length - 1][0] < -MAX_POS || offsetX + points[points.length - 1][0] > MAX_POS || offsetY + points[points.length - 1][1] < -MAX_POS || offsetY + points[points.length - 1][1] > MAX_POS) {
6957
6986
  console.error(
6958
6987
  "Elbow arrow normalization is outside reasonable bounds (> 1e6)",
6959
6988
  {
@@ -7557,11 +7586,7 @@ var getBindingStrategyForDraggingBindingElementEndpoints_simple = (arrow, draggi
7557
7586
  threshold: 0,
7558
7587
  overrideShouldTestInside: true
7559
7588
  });
7560
- if (otherBinding && otherBinding.elementId === hit?.id) {
7561
- invariant7(
7562
- !opts?.newArrow || appState.selectedLinearElement?.initialState.origin,
7563
- "appState.selectedLinearElement.initialState.origin must be defined for new arrows"
7564
- );
7589
+ if (otherBinding && otherBinding.elementId === hit?.id && (!opts?.newArrow || appState.selectedLinearElement?.initialState.origin)) {
7565
7590
  return {
7566
7591
  start: {
7567
7592
  mode: "inside",
@@ -8371,8 +8396,8 @@ var calculateFixedPointForElbowArrowBinding = (linearElement, hoveredElement, st
8371
8396
  );
8372
8397
  return {
8373
8398
  fixedPoint: normalizeFixedPoint([
8374
- (nonRotatedSnappedGlobalPoint[0] - hoveredElement.x) / hoveredElement.width,
8375
- (nonRotatedSnappedGlobalPoint[1] - hoveredElement.y) / hoveredElement.height
8399
+ (nonRotatedSnappedGlobalPoint[0] - hoveredElement.x) / Math.max(hoveredElement.width, PRECISION2),
8400
+ (nonRotatedSnappedGlobalPoint[1] - hoveredElement.y) / Math.max(hoveredElement.height, PRECISION2)
8376
8401
  ])
8377
8402
  };
8378
8403
  };
@@ -8388,8 +8413,8 @@ var calculateFixedPointForNonElbowArrowBinding = (linearElement, hoveredElement,
8388
8413
  elementCenter,
8389
8414
  -hoveredElement.angle
8390
8415
  );
8391
- const fixedPointX = (nonRotatedPoint[0] - hoveredElement.x) / hoveredElement.width;
8392
- const fixedPointY = (nonRotatedPoint[1] - hoveredElement.y) / hoveredElement.height;
8416
+ const fixedPointX = (nonRotatedPoint[0] - hoveredElement.x) / Math.max(hoveredElement.width, PRECISION2);
8417
+ const fixedPointY = (nonRotatedPoint[1] - hoveredElement.y) / Math.max(hoveredElement.height, PRECISION2);
8393
8418
  return {
8394
8419
  fixedPoint: normalizeFixedPoint([fixedPointX, fixedPointY])
8395
8420
  };
@@ -9337,7 +9362,7 @@ var LinearElementEditor = class _LinearElementEditor {
9337
9362
  console.error(
9338
9363
  `There must be a valid lastClickedPoint in order to drag it. selectedPointsIndices(${JSON.stringify(
9339
9364
  selectedPointsIndices
9340
- )}) points(0..${element.points.length - 1}) lastClickedPoint(${lastClickedPoint})`
9365
+ )}) points(0..${element.points.length - 1}) lastClickedPoint(${lastClickedPoint}) isElbowArrow: ${elbowed}`
9341
9366
  );
9342
9367
  lastClickedPoint = element.points.length - 1;
9343
9368
  }
@@ -10185,7 +10210,7 @@ var LinearElementEditor = class _LinearElementEditor {
10185
10210
  pointerDownState: linearElementEditor.initialState,
10186
10211
  selectedPointsIndices: linearElementEditor.selectedPointsIndices
10187
10212
  };
10188
- const midpoint2 = _LinearElementEditor.createPointAt(
10213
+ const midpoint = _LinearElementEditor.createPointAt(
10189
10214
  element,
10190
10215
  elementsMap,
10191
10216
  pointerCoords.x,
@@ -10194,7 +10219,7 @@ var LinearElementEditor = class _LinearElementEditor {
10194
10219
  );
10195
10220
  const points = [
10196
10221
  ...element.points.slice(0, segmentMidpoint.index),
10197
- midpoint2,
10222
+ midpoint,
10198
10223
  ...element.points.slice(segmentMidpoint.index)
10199
10224
  ];
10200
10225
  scene.mutateElement(element, { points });
@@ -10489,13 +10514,11 @@ var normalizeSelectedPoints = (points) => {
10489
10514
  var pointDraggingUpdates = (selectedPointsIndices, deltaX, deltaY, scenePointerX, scenePointerY, elementsMap, element, elements, app, angleLocked, altKey, linearElementEditor) => {
10490
10515
  const naiveDraggingPoints = new Map(
10491
10516
  selectedPointsIndices.map((pointIndex) => {
10517
+ const point = element.points[pointIndex] ?? element.points.at(-1);
10492
10518
  return [
10493
10519
  pointIndex,
10494
10520
  {
10495
- point: pointFrom9(
10496
- element.points[pointIndex][0] + deltaX,
10497
- element.points[pointIndex][1] + deltaY
10498
- ),
10521
+ point: pointFrom9(point[0] + deltaX, point[1] + deltaY),
10499
10522
  isDragging: true
10500
10523
  }
10501
10524
  ];
@@ -10733,7 +10756,7 @@ var determineCustomLinearAngle = (pivotPoint, draggedPoint) => Math.atan2(dragge
10733
10756
 
10734
10757
  // src/frame.ts
10735
10758
  init_define_import_meta_env();
10736
- import { arrayToMap as arrayToMap4 } from "@excalidraw/common";
10759
+ import { arrayToMap as arrayToMap5 } from "@excalidraw/common";
10737
10760
  import { isPointWithinBounds as isPointWithinBounds2, pointFrom as pointFrom11 } from "@excalidraw/math";
10738
10761
 
10739
10762
  // ../utils/src/bbox.ts
@@ -11104,11 +11127,19 @@ var getElementsWithinSelection = (elements, selection, elementsMap, excludeEleme
11104
11127
  )
11105
11128
  ];
11106
11129
  const framesInSelection = excludeElementsInFrames ? /* @__PURE__ */ new Set() : null;
11107
- let elementsInSelection = [];
11130
+ const groups = {};
11131
+ const elementsInSelection = /* @__PURE__ */ new Set();
11108
11132
  for (const element of elements) {
11109
11133
  if (shouldIgnoreElementFromSelection(element)) {
11110
11134
  continue;
11111
11135
  }
11136
+ const groupId = element.groupIds.at(-1);
11137
+ if (groupId) {
11138
+ if (!groups[groupId]) {
11139
+ groups[groupId] = [];
11140
+ }
11141
+ groups[groupId].push(element);
11142
+ }
11112
11143
  const strokeWidth = element.strokeWidth;
11113
11144
  let labelAABB = null;
11114
11145
  let elementAABB = getElementBounds(element, elementsMap);
@@ -11133,7 +11164,7 @@ var getElementsWithinSelection = (elements, selection, elementsMap, excludeEleme
11133
11164
  ];
11134
11165
  }
11135
11166
  const associatedFrame = getContainingFrame(element, elementsMap);
11136
- if (associatedFrame && isElementIntersectingFrame(element, associatedFrame, elementsMap)) {
11167
+ if (associatedFrame && elementOverlapsWithFrame(element, associatedFrame, elementsMap)) {
11137
11168
  const frameAABB = getElementBounds(associatedFrame, elementsMap);
11138
11169
  elementAABB = [
11139
11170
  Math.max(elementAABB[0], frameAABB[0]),
@@ -11157,13 +11188,12 @@ var getElementsWithinSelection = (elements, selection, elementsMap, excludeEleme
11157
11188
  if (boundsContainBounds(selectionBounds, commonAABB2)) {
11158
11189
  if (framesInSelection && isFrameLikeElement(element)) {
11159
11190
  framesInSelection.add(element.id);
11160
- } else {
11161
- elementsInSelection.push(element);
11162
- continue;
11163
11191
  }
11192
+ elementsInSelection.add(element);
11193
+ continue;
11164
11194
  }
11165
11195
  if (boxSelectionMode === "overlap" && labelAABB && doBoundsIntersect(selectionBounds, labelAABB)) {
11166
- elementsInSelection.push(element);
11196
+ elementsInSelection.add(element);
11167
11197
  continue;
11168
11198
  }
11169
11199
  if (boxSelectionMode === "overlap" && doBoundsIntersect(selectionBounds, elementAABB)) {
@@ -11241,20 +11271,34 @@ var getElementsWithinSelection = (elements, selection, elementsMap, excludeEleme
11241
11271
  if (framesInSelection && isFrameLikeElement(element)) {
11242
11272
  framesInSelection.add(element.id);
11243
11273
  }
11244
- elementsInSelection.push(element);
11274
+ elementsInSelection.add(element);
11245
11275
  continue;
11246
11276
  }
11247
11277
  }
11248
11278
  }
11249
- elementsInSelection = framesInSelection ? excludeElementsFromFrames(elementsInSelection, framesInSelection) : elementsInSelection;
11250
- elementsInSelection = elementsInSelection.filter((element) => {
11251
- const containingFrame = getContainingFrame(element, elementsMap);
11252
- if (containingFrame) {
11253
- return elementOverlapsWithFrame(element, containingFrame, elementsMap);
11254
- }
11255
- return true;
11256
- });
11257
- return elementsInSelection;
11279
+ if (framesInSelection) {
11280
+ elementsInSelection.forEach((element) => {
11281
+ if (element.frameId && framesInSelection.has(element.frameId)) {
11282
+ elementsInSelection.delete(element);
11283
+ }
11284
+ });
11285
+ }
11286
+ if (boxSelectionMode === "overlap") {
11287
+ Array.from(elementsInSelection).forEach((element) => {
11288
+ const groupId = element.groupIds.at(-1);
11289
+ const group = groupId ? groups[groupId] : null;
11290
+ group?.forEach((groupElement) => elementsInSelection.add(groupElement));
11291
+ });
11292
+ } else if (boxSelectionMode === "contain") {
11293
+ elementsInSelection.forEach((element) => {
11294
+ const groupId = element.groupIds.at(-1);
11295
+ const group = groupId ? groups[groupId] : null;
11296
+ if (group && !group.every((groupElement) => elementsInSelection.has(groupElement))) {
11297
+ elementsInSelection.delete(element);
11298
+ }
11299
+ });
11300
+ }
11301
+ return elements.filter((element) => elementsInSelection.has(element));
11258
11302
  };
11259
11303
  var getVisibleAndNonSelectedElements = (elements, selectedElements, appState, elementsMap) => {
11260
11304
  const selectedElementsSet = new Set(
@@ -11371,100 +11415,351 @@ var getActiveTextElement = (selectedElements, appState) => {
11371
11415
  return activeTextElement || null;
11372
11416
  };
11373
11417
 
11374
- // src/frame.ts
11375
- var bindElementsToFramesAfterDuplication = (nextElements, origElements, origIdToDuplicateId) => {
11376
- const nextElementMap = arrayToMap4(nextElements);
11377
- for (const element of origElements) {
11378
- if (element.frameId) {
11379
- const nextElementId = origIdToDuplicateId.get(element.id);
11380
- const nextFrameId = origIdToDuplicateId.get(element.frameId);
11381
- const nextElement = nextElementId && nextElementMap.get(nextElementId);
11382
- if (nextElement) {
11383
- mutateElement(nextElement, nextElementMap, {
11384
- frameId: nextFrameId ?? null
11385
- });
11418
+ // src/fractionalIndex.ts
11419
+ init_define_import_meta_env();
11420
+ import { arrayToMap as arrayToMap4 } from "@excalidraw/common";
11421
+ import {
11422
+ validateOrderKey,
11423
+ generateNKeysBetween
11424
+ } from "@excalidraw/fractional-indexing";
11425
+ var InvalidFractionalIndexError = class extends Error {
11426
+ code = "ELEMENT_HAS_INVALID_INDEX";
11427
+ };
11428
+ var validateFractionalIndices = (elements, {
11429
+ shouldThrow = false,
11430
+ includeBoundTextValidation = false,
11431
+ ignoreLogs,
11432
+ reconciliationContext
11433
+ }) => {
11434
+ const errorMessages = [];
11435
+ const stringifyElement = (element) => `${element?.index}:${element?.id}:${element?.type}:${element?.isDeleted}:${element?.version}:${element?.versionNonce}`;
11436
+ const indices = elements.map((x) => x.index);
11437
+ for (const [i, index] of indices.entries()) {
11438
+ const predecessorIndex = indices[i - 1];
11439
+ const successorIndex = indices[i + 1];
11440
+ if (!isValidFractionalIndex(index, predecessorIndex, successorIndex)) {
11441
+ errorMessages.push(
11442
+ `Fractional indices invariant has been compromised: "${stringifyElement(
11443
+ elements[i - 1]
11444
+ )}", "${stringifyElement(elements[i])}", "${stringifyElement(
11445
+ elements[i + 1]
11446
+ )}"`
11447
+ );
11448
+ }
11449
+ if (includeBoundTextValidation && hasBoundTextElement(elements[i])) {
11450
+ const container = elements[i];
11451
+ const text = getBoundTextElement(container, arrayToMap4(elements));
11452
+ if (text && text.index <= container.index) {
11453
+ errorMessages.push(
11454
+ `Fractional indices invariant for bound elements has been compromised: "${stringifyElement(
11455
+ text
11456
+ )}", "${stringifyElement(container)}"`
11457
+ );
11386
11458
  }
11387
11459
  }
11388
11460
  }
11461
+ if (errorMessages.length) {
11462
+ const error = new InvalidFractionalIndexError();
11463
+ const additionalContext = [];
11464
+ if (reconciliationContext) {
11465
+ additionalContext.push("Additional reconciliation context:");
11466
+ additionalContext.push(
11467
+ reconciliationContext.localElements.map((x) => stringifyElement(x))
11468
+ );
11469
+ additionalContext.push(
11470
+ reconciliationContext.remoteElements.map((x) => stringifyElement(x))
11471
+ );
11472
+ }
11473
+ if (!ignoreLogs) {
11474
+ console.error(
11475
+ errorMessages.join("\n\n"),
11476
+ error.stack,
11477
+ elements.map((x) => stringifyElement(x)),
11478
+ ...additionalContext
11479
+ );
11480
+ }
11481
+ if (shouldThrow) {
11482
+ throw error;
11483
+ }
11484
+ }
11389
11485
  };
11390
- function isElementIntersectingFrame(element, frame, elementsMap) {
11391
- const frameLineSegments = getElementLineSegments(frame, elementsMap);
11392
- const elementLineSegments = getElementLineSegments(element, elementsMap);
11393
- const intersecting = frameLineSegments.some(
11394
- (frameLineSegment) => elementLineSegments.some(
11395
- (elementLineSegment) => doLineSegmentsIntersect(frameLineSegment, elementLineSegment)
11396
- )
11397
- );
11398
- return intersecting;
11399
- }
11400
- var getElementsCompletelyInFrame = (elements, frame, elementsMap) => omitGroupsContainingFrameLikes(
11401
- getElementsWithinSelection(elements, frame, elementsMap, false)
11402
- ).filter(
11403
- (element) => !isFrameLikeElement(element) && !element.frameId || element.frameId === frame.id
11404
- );
11405
- var isElementContainingFrame = (element, frame, elementsMap) => {
11406
- return getElementsWithinSelection([frame], element, elementsMap).some(
11407
- (e) => e.id === frame.id
11408
- );
11409
- };
11410
- var getElementsIntersectingFrame = (elements, frame) => {
11411
- const elementsMap = arrayToMap4(elements);
11412
- return elements.filter(
11413
- (element) => isElementIntersectingFrame(element, frame, elementsMap)
11414
- );
11415
- };
11416
- var elementsAreInFrameBounds = (elements, frame, elementsMap) => {
11417
- const [frameX1, frameY1, frameX2, frameY2] = getElementAbsoluteCoords2(
11418
- frame,
11419
- elementsMap
11420
- );
11421
- const [elementX1, elementY1, elementX2, elementY2] = getCommonBounds(elements);
11422
- return frameX1 <= elementX1 && frameY1 <= elementY1 && frameX2 >= elementX2 && frameY2 >= elementY2;
11423
- };
11424
- var elementOverlapsWithFrame = (element, frame, elementsMap) => {
11425
- return elementsAreInFrameBounds([element], frame, elementsMap) || isElementIntersectingFrame(element, frame, elementsMap) || isElementContainingFrame(element, frame, elementsMap);
11486
+ var orderByFractionalIndex = (elements) => {
11487
+ return elements.sort((a2, b2) => {
11488
+ if (isOrderedElement(a2) && isOrderedElement(b2)) {
11489
+ if (a2.index < b2.index) {
11490
+ return -1;
11491
+ } else if (a2.index > b2.index) {
11492
+ return 1;
11493
+ }
11494
+ return a2.id < b2.id ? -1 : 1;
11495
+ }
11496
+ return 1;
11497
+ });
11426
11498
  };
11427
- var isCursorInFrame = (cursorCoords, frame, elementsMap) => {
11428
- const [fx1, fy1, fx2, fy2] = getElementAbsoluteCoords2(frame, elementsMap);
11429
- return isPointWithinBounds2(
11430
- pointFrom11(fx1, fy1),
11431
- pointFrom11(cursorCoords.x, cursorCoords.y),
11432
- pointFrom11(fx2, fy2)
11433
- );
11499
+ var syncMovedIndices = (elements, movedElements) => {
11500
+ try {
11501
+ const elementsMap = arrayToMap4(elements);
11502
+ const indicesGroups = getMovedIndicesGroups(elements, movedElements);
11503
+ const elementsUpdates = generateIndices(elements, indicesGroups);
11504
+ const elementsCandidates = elements.map((x) => {
11505
+ const elementUpdates = elementsUpdates.get(x);
11506
+ if (elementUpdates) {
11507
+ return { ...x, index: elementUpdates.index };
11508
+ }
11509
+ return x;
11510
+ });
11511
+ validateFractionalIndices(
11512
+ elementsCandidates,
11513
+ // we don't autofix invalid bound text indices, hence don't include it in the validation
11514
+ {
11515
+ includeBoundTextValidation: false,
11516
+ shouldThrow: true,
11517
+ ignoreLogs: true
11518
+ }
11519
+ );
11520
+ for (const [element, { index }] of elementsUpdates) {
11521
+ mutateElement(element, elementsMap, { index });
11522
+ }
11523
+ } catch (e) {
11524
+ syncInvalidIndices(elements);
11525
+ }
11526
+ return elements;
11434
11527
  };
11435
- var groupsAreAtLeastIntersectingTheFrame = (elements, groupIds, frame) => {
11528
+ var syncInvalidIndices = (elements) => {
11436
11529
  const elementsMap = arrayToMap4(elements);
11437
- const elementsInGroup = groupIds.flatMap(
11438
- (groupId) => getElementsInGroup(elements, groupId)
11439
- );
11440
- if (elementsInGroup.length === 0) {
11441
- return true;
11530
+ const indicesGroups = getInvalidIndicesGroups(elements);
11531
+ const elementsUpdates = generateIndices(elements, indicesGroups);
11532
+ for (const [element, { index }] of elementsUpdates) {
11533
+ mutateElement(element, elementsMap, { index });
11442
11534
  }
11443
- return !!elementsInGroup.find(
11444
- (element) => elementsAreInFrameBounds([element], frame, elementsMap) || isElementIntersectingFrame(element, frame, elementsMap)
11445
- );
11535
+ return elements;
11446
11536
  };
11447
- var groupsAreCompletelyOutOfFrame = (elements, groupIds, frame) => {
11448
- const elementsMap = arrayToMap4(elements);
11449
- const elementsInGroup = groupIds.flatMap(
11450
- (groupId) => getElementsInGroup(elements, groupId)
11451
- );
11452
- if (elementsInGroup.length === 0) {
11453
- return true;
11537
+ var syncInvalidIndicesImmutable = (elements) => {
11538
+ const syncedElements = arrayToMap4(elements);
11539
+ const indicesGroups = getInvalidIndicesGroups(elements);
11540
+ const elementsUpdates = generateIndices(elements, indicesGroups);
11541
+ for (const [element, { index }] of elementsUpdates) {
11542
+ syncedElements.set(element.id, newElementWith(element, { index }));
11454
11543
  }
11455
- return elementsInGroup.find(
11456
- (element) => elementsAreInFrameBounds([element], frame, elementsMap) || isElementIntersectingFrame(element, frame, elementsMap)
11457
- ) === void 0;
11544
+ return syncedElements;
11458
11545
  };
11459
- var groupByFrameLikes = (elements) => {
11460
- const frameElementsMap = /* @__PURE__ */ new Map();
11461
- for (const element of elements) {
11462
- const frameId = isFrameLikeElement(element) ? element.id : element.frameId;
11463
- if (frameId && !frameElementsMap.has(frameId)) {
11464
- frameElementsMap.set(frameId, getFrameChildren(elements, frameId));
11546
+ var getMovedIndicesGroups = (elements, movedElements) => {
11547
+ const indicesGroups = [];
11548
+ let i = 0;
11549
+ while (i < elements.length) {
11550
+ if (movedElements.has(elements[i].id)) {
11551
+ const indicesGroup = [i - 1, i];
11552
+ while (++i < elements.length) {
11553
+ if (!movedElements.has(elements[i].id)) {
11554
+ break;
11555
+ }
11556
+ indicesGroup.push(i);
11557
+ }
11558
+ indicesGroup.push(i);
11559
+ indicesGroups.push(indicesGroup);
11560
+ } else {
11561
+ i++;
11465
11562
  }
11466
11563
  }
11467
- return frameElementsMap;
11564
+ return indicesGroups;
11565
+ };
11566
+ var getInvalidIndicesGroups = (elements) => {
11567
+ const indicesGroups = [];
11568
+ let lowerBound = void 0;
11569
+ let upperBound = void 0;
11570
+ let lowerBoundIndex = -1;
11571
+ let upperBoundIndex = 0;
11572
+ const getLowerBound = (index) => {
11573
+ const lowerBound2 = elements[lowerBoundIndex] ? elements[lowerBoundIndex].index : void 0;
11574
+ const candidate = elements[index - 1]?.index;
11575
+ if (!lowerBound2 && candidate || // first lowerBound
11576
+ lowerBound2 && candidate && candidate > lowerBound2) {
11577
+ return [candidate, index - 1];
11578
+ }
11579
+ return [lowerBound2, lowerBoundIndex];
11580
+ };
11581
+ const getUpperBound = (index) => {
11582
+ const upperBound2 = elements[upperBoundIndex] ? elements[upperBoundIndex].index : void 0;
11583
+ if (upperBound2 && index < upperBoundIndex) {
11584
+ return [upperBound2, upperBoundIndex];
11585
+ }
11586
+ let i2 = upperBoundIndex;
11587
+ while (++i2 < elements.length) {
11588
+ const candidate = elements[i2]?.index;
11589
+ if (!upperBound2 && candidate || // first upperBound
11590
+ upperBound2 && candidate && candidate > upperBound2) {
11591
+ return [candidate, i2];
11592
+ }
11593
+ }
11594
+ return [void 0, i2];
11595
+ };
11596
+ let i = 0;
11597
+ while (i < elements.length) {
11598
+ const current = elements[i].index;
11599
+ [lowerBound, lowerBoundIndex] = getLowerBound(i);
11600
+ [upperBound, upperBoundIndex] = getUpperBound(i);
11601
+ if (!isValidFractionalIndex(current, lowerBound, upperBound)) {
11602
+ const indicesGroup = [lowerBoundIndex, i];
11603
+ while (++i < elements.length) {
11604
+ const current2 = elements[i].index;
11605
+ const [nextLowerBound, nextLowerBoundIndex] = getLowerBound(i);
11606
+ const [nextUpperBound, nextUpperBoundIndex] = getUpperBound(i);
11607
+ if (isValidFractionalIndex(current2, nextLowerBound, nextUpperBound)) {
11608
+ break;
11609
+ }
11610
+ [lowerBound, lowerBoundIndex] = [nextLowerBound, nextLowerBoundIndex];
11611
+ [upperBound, upperBoundIndex] = [nextUpperBound, nextUpperBoundIndex];
11612
+ indicesGroup.push(i);
11613
+ }
11614
+ indicesGroup.push(upperBoundIndex);
11615
+ indicesGroups.push(indicesGroup);
11616
+ } else {
11617
+ i++;
11618
+ }
11619
+ }
11620
+ return indicesGroups;
11621
+ };
11622
+ var isValidFractionalIndex = (index, predecessor, successor) => {
11623
+ if (!index) {
11624
+ return false;
11625
+ }
11626
+ try {
11627
+ validateOrderKey(index);
11628
+ } catch {
11629
+ return false;
11630
+ }
11631
+ if (predecessor && successor) {
11632
+ return predecessor < index && index < successor;
11633
+ }
11634
+ if (!predecessor && successor) {
11635
+ return index < successor;
11636
+ }
11637
+ if (predecessor && !successor) {
11638
+ return predecessor < index;
11639
+ }
11640
+ return !!index;
11641
+ };
11642
+ var generateIndices = (elements, indicesGroups) => {
11643
+ const elementsUpdates = /* @__PURE__ */ new Map();
11644
+ for (const indices of indicesGroups) {
11645
+ const lowerBoundIndex = indices.shift();
11646
+ const upperBoundIndex = indices.pop();
11647
+ const fractionalIndices = generateNKeysBetween(
11648
+ elements[lowerBoundIndex]?.index,
11649
+ elements[upperBoundIndex]?.index,
11650
+ indices.length
11651
+ );
11652
+ for (let i = 0; i < indices.length; i++) {
11653
+ const element = elements[indices[i]];
11654
+ elementsUpdates.set(element, {
11655
+ index: fractionalIndices[i]
11656
+ });
11657
+ }
11658
+ }
11659
+ return elementsUpdates;
11660
+ };
11661
+ var isOrderedElement = (element) => {
11662
+ if (element.index) {
11663
+ return true;
11664
+ }
11665
+ return false;
11666
+ };
11667
+
11668
+ // src/frame.ts
11669
+ var bindElementsToFramesAfterDuplication = (nextElements, origElements, origIdToDuplicateId) => {
11670
+ const nextElementMap = arrayToMap5(nextElements);
11671
+ for (const element of origElements) {
11672
+ if (element.frameId) {
11673
+ const nextElementId = origIdToDuplicateId.get(element.id);
11674
+ const nextFrameId = origIdToDuplicateId.get(element.frameId);
11675
+ const nextElement = nextElementId && nextElementMap.get(nextElementId);
11676
+ if (nextElement) {
11677
+ mutateElement(nextElement, nextElementMap, {
11678
+ frameId: nextFrameId ?? null
11679
+ });
11680
+ }
11681
+ }
11682
+ }
11683
+ };
11684
+ function isElementIntersectingFrame(element, frame, elementsMap) {
11685
+ const frameLineSegments = getElementLineSegments(frame, elementsMap);
11686
+ const elementLineSegments = getElementLineSegments(element, elementsMap);
11687
+ const intersecting = frameLineSegments.some(
11688
+ (frameLineSegment) => elementLineSegments.some(
11689
+ (elementLineSegment) => doLineSegmentsIntersect(frameLineSegment, elementLineSegment)
11690
+ )
11691
+ );
11692
+ return intersecting;
11693
+ }
11694
+ var getElementsCompletelyInFrame = (elements, frame, elementsMap) => omitGroupsContainingFrameLikes(
11695
+ getElementsWithinSelection(elements, frame, elementsMap, false)
11696
+ ).filter(
11697
+ (element) => !isFrameLikeElement(element) && !element.frameId || element.frameId === frame.id
11698
+ );
11699
+ var isElementContainingFrame = (element, frame, elementsMap) => {
11700
+ return boundsContainBounds(
11701
+ getElementBounds(element, elementsMap),
11702
+ getElementBounds(frame, elementsMap)
11703
+ );
11704
+ };
11705
+ var getElementsIntersectingFrame = (elements, frame) => {
11706
+ const elementsMap = arrayToMap5(elements);
11707
+ return elements.filter(
11708
+ (element) => isElementIntersectingFrame(element, frame, elementsMap)
11709
+ );
11710
+ };
11711
+ var elementsAreInFrameBounds = (elements, frame, elementsMap) => {
11712
+ const [frameX1, frameY1, frameX2, frameY2] = getElementAbsoluteCoords2(
11713
+ frame,
11714
+ elementsMap
11715
+ );
11716
+ const [elementX1, elementY1, elementX2, elementY2] = getCommonBounds(elements);
11717
+ return frameX1 <= elementX1 && frameY1 <= elementY1 && frameX2 >= elementX2 && frameY2 >= elementY2;
11718
+ };
11719
+ var elementOverlapsWithFrame = (element, frame, elementsMap) => {
11720
+ return elementsAreInFrameBounds([element], frame, elementsMap) || isElementIntersectingFrame(element, frame, elementsMap) || isElementContainingFrame(element, frame, elementsMap);
11721
+ };
11722
+ var isCursorInFrame = (cursorCoords, frame, elementsMap) => {
11723
+ const [fx1, fy1, fx2, fy2] = getElementAbsoluteCoords2(frame, elementsMap);
11724
+ return isPointWithinBounds2(
11725
+ pointFrom11(fx1, fy1),
11726
+ pointFrom11(cursorCoords.x, cursorCoords.y),
11727
+ pointFrom11(fx2, fy2)
11728
+ );
11729
+ };
11730
+ var groupsAreAtLeastIntersectingTheFrame = (elements, groupIds, frame) => {
11731
+ const elementsMap = arrayToMap5(elements);
11732
+ const elementsInGroup = groupIds.flatMap(
11733
+ (groupId) => getElementsInGroup(elements, groupId)
11734
+ );
11735
+ if (elementsInGroup.length === 0) {
11736
+ return true;
11737
+ }
11738
+ return !!elementsInGroup.find(
11739
+ (element) => elementsAreInFrameBounds([element], frame, elementsMap) || isElementIntersectingFrame(element, frame, elementsMap)
11740
+ );
11741
+ };
11742
+ var groupsAreCompletelyOutOfFrame = (elements, groupIds, frame) => {
11743
+ const elementsMap = arrayToMap5(elements);
11744
+ const elementsInGroup = groupIds.flatMap(
11745
+ (groupId) => getElementsInGroup(elements, groupId)
11746
+ );
11747
+ if (elementsInGroup.length === 0) {
11748
+ return true;
11749
+ }
11750
+ return elementsInGroup.find(
11751
+ (element) => elementsAreInFrameBounds([element], frame, elementsMap) || isElementIntersectingFrame(element, frame, elementsMap)
11752
+ ) === void 0;
11753
+ };
11754
+ var groupByFrameLikes = (elements) => {
11755
+ const frameElementsMap = /* @__PURE__ */ new Map();
11756
+ for (const element of elements) {
11757
+ const frameId = isFrameLikeElement(element) ? element.id : element.frameId;
11758
+ if (frameId && !frameElementsMap.has(frameId)) {
11759
+ frameElementsMap.set(frameId, getFrameChildren(elements, frameId));
11760
+ }
11761
+ }
11762
+ return frameElementsMap;
11468
11763
  };
11469
11764
  var getFrameChildren = (allElements, frameId) => {
11470
11765
  const frameChildren = [];
@@ -11481,7 +11776,7 @@ var getFrameLikeElements = (allElements) => {
11481
11776
  );
11482
11777
  };
11483
11778
  var getRootElements = (allElements) => {
11484
- const frameElements = arrayToMap4(getFrameLikeElements(allElements));
11779
+ const frameElements = arrayToMap5(getFrameLikeElements(allElements));
11485
11780
  return allElements.filter(
11486
11781
  (element) => frameElements.has(element.id) || !element.frameId || !frameElements.has(element.frameId)
11487
11782
  );
@@ -11602,7 +11897,7 @@ var getContainingFrame = (element, elementsMap) => {
11602
11897
  };
11603
11898
  var filterElementsEligibleAsFrameChildren = (elements, frame) => {
11604
11899
  const otherFrames = /* @__PURE__ */ new Set();
11605
- const elementsMap = arrayToMap4(elements);
11900
+ const elementsMap = arrayToMap5(elements);
11606
11901
  elements = omitGroupsContainingFrameLikes(elements);
11607
11902
  for (const element of elements) {
11608
11903
  if (isFrameLikeElement(element) && element.id !== frame.id) {
@@ -11637,16 +11932,35 @@ var filterElementsEligibleAsFrameChildren = (elements, frame) => {
11637
11932
  }
11638
11933
  return eligibleElements;
11639
11934
  };
11640
- var addElementsToFrame = (allElements, elementsToAdd, frame, appState) => {
11641
- const elementsMap = arrayToMap4(allElements);
11642
- const currTargetFrameChildrenMap = /* @__PURE__ */ new Map();
11643
- for (const element of allElements.values()) {
11644
- if (element.frameId === frame.id) {
11645
- currTargetFrameChildrenMap.set(element.id, true);
11935
+ var getCommonFrameId = (elements) => {
11936
+ let commonFrameId;
11937
+ for (const element of elements) {
11938
+ if (isFrameLikeElement(element) || !element.frameId) {
11939
+ return null;
11940
+ }
11941
+ if (commonFrameId === void 0) {
11942
+ commonFrameId = element.frameId;
11943
+ } else if (commonFrameId !== element.frameId) {
11944
+ return null;
11646
11945
  }
11647
11946
  }
11648
- const suppliedElementsToAddSet = new Set(elementsToAdd.map((el) => el.id));
11649
- const finalElementsToAdd = [];
11947
+ return commonFrameId ?? null;
11948
+ };
11949
+ var getFrameChildrenInsertionIndex = (elements, frameId) => {
11950
+ for (let index = elements.length - 1; index >= 0; index--) {
11951
+ const element = elements[index];
11952
+ if (element.id === frameId) {
11953
+ return index;
11954
+ } else if (element.frameId === frameId) {
11955
+ return index + 1;
11956
+ }
11957
+ }
11958
+ return null;
11959
+ };
11960
+ var addElementsToFrame = (allElements, elementsToAdd, frame) => {
11961
+ const elementsMap = arrayToMap5(allElements);
11962
+ const commonFrameId = getCommonFrameId(elementsToAdd);
11963
+ const finalElementsToAdd = /* @__PURE__ */ new Set();
11650
11964
  const otherFrames = /* @__PURE__ */ new Set();
11651
11965
  for (const element of elementsToAdd) {
11652
11966
  if (isFrameLikeElement(element) && element.id !== frame.id) {
@@ -11660,23 +11974,44 @@ var addElementsToFrame = (allElements, elementsToAdd, frame, appState) => {
11660
11974
  if (isFrameLikeElement(element) || element.frameId && otherFrames.has(element.frameId)) {
11661
11975
  continue;
11662
11976
  }
11663
- if (element.frameId && appState.selectedElementIds[element.id] && appState.selectedElementIds[element.frameId]) {
11977
+ if (element.frameId && element.frameId !== frame.id) {
11664
11978
  continue;
11665
11979
  }
11666
- if (!currTargetFrameChildrenMap.has(element.id)) {
11667
- finalElementsToAdd.push(element);
11668
- }
11980
+ finalElementsToAdd.add(element);
11669
11981
  const boundTextElement = getBoundTextElement(element, elementsMap);
11670
- if (boundTextElement && !suppliedElementsToAddSet.has(boundTextElement.id) && !currTargetFrameChildrenMap.has(boundTextElement.id)) {
11671
- finalElementsToAdd.push(boundTextElement);
11982
+ if (boundTextElement && !finalElementsToAdd.has(boundTextElement)) {
11983
+ finalElementsToAdd.add(boundTextElement);
11672
11984
  }
11673
11985
  }
11674
11986
  for (const element of finalElementsToAdd) {
11675
- mutateElement(element, elementsMap, {
11676
- frameId: frame.id
11677
- });
11987
+ if (element.frameId !== frame.id) {
11988
+ mutateElement(element, elementsMap, {
11989
+ frameId: frame.id
11990
+ });
11991
+ }
11678
11992
  }
11679
- return allElements;
11993
+ if (!finalElementsToAdd.size || // if all elements to add already belong to the frame, then we don't want to
11994
+ // reorder (case: we're dragging element children within the frame)
11995
+ commonFrameId === frame.id) {
11996
+ return allElements;
11997
+ }
11998
+ const otherElements = Array.from(allElements.values()).filter(
11999
+ (element) => !finalElementsToAdd.has(element)
12000
+ );
12001
+ const insertionIndex = getFrameChildrenInsertionIndex(
12002
+ otherElements,
12003
+ frame.id
12004
+ );
12005
+ if (insertionIndex === null) {
12006
+ return allElements;
12007
+ }
12008
+ const reorderedElements = [
12009
+ ...otherElements.slice(0, insertionIndex),
12010
+ ...finalElementsToAdd,
12011
+ ...otherElements.slice(insertionIndex)
12012
+ ];
12013
+ syncMovedIndices(reorderedElements, arrayToMap5([...finalElementsToAdd]));
12014
+ return Array.isArray(allElements) ? reorderedElements : new Map(reorderedElements.map((element) => [element.id, element]));
11680
12015
  };
11681
12016
  var removeElementsFromFrame = (elementsToRemove, elementsMap) => {
11682
12017
  const _elementsToRemove = /* @__PURE__ */ new Map();
@@ -11702,15 +12037,14 @@ var removeElementsFromFrame = (elementsToRemove, elementsMap) => {
11702
12037
  };
11703
12038
  var removeAllElementsFromFrame = (allElements, frame) => {
11704
12039
  const elementsInFrame = getFrameChildren(allElements, frame.id);
11705
- removeElementsFromFrame(elementsInFrame, arrayToMap4(allElements));
12040
+ removeElementsFromFrame(elementsInFrame, arrayToMap5(allElements));
11706
12041
  return allElements;
11707
12042
  };
11708
- var replaceAllElementsInFrame = (allElements, nextElementsInFrame, frame, app) => {
12043
+ var replaceAllElementsInFrame = (allElements, nextElementsInFrame, frame) => {
11709
12044
  return addElementsToFrame(
11710
12045
  removeAllElementsFromFrame(allElements, frame),
11711
12046
  nextElementsInFrame,
11712
- frame,
11713
- app.state
12047
+ frame
11714
12048
  ).slice();
11715
12049
  };
11716
12050
  var updateFrameMembershipOfSelectedElements = (allElements, appState, app) => {
@@ -11730,7 +12064,7 @@ var updateFrameMembershipOfSelectedElements = (allElements, appState, app) => {
11730
12064
  }
11731
12065
  }
11732
12066
  const elementsToRemove = /* @__PURE__ */ new Set();
11733
- const elementsMap = arrayToMap4(allElements);
12067
+ const elementsMap = arrayToMap5(allElements);
11734
12068
  elementsToFilter.forEach((element) => {
11735
12069
  if (element.frameId && !isFrameLikeElement(element) && !isElementInFrame(element, elementsMap, appState)) {
11736
12070
  elementsToRemove.add(element);
@@ -11888,7 +12222,7 @@ var getElementsOverlappingFrame = (elements, frame, elementsMap) => {
11888
12222
  );
11889
12223
  };
11890
12224
  var frameAndChildrenSelectedTogether = (selectedElements) => {
11891
- const selectedElementsMap = arrayToMap4(selectedElements);
12225
+ const selectedElementsMap = arrayToMap5(selectedElements);
11892
12226
  return selectedElements.length > 1 && selectedElements.some(
11893
12227
  (element) => element.frameId && selectedElementsMap.has(element.frameId)
11894
12228
  );
@@ -12575,16 +12909,6 @@ function getFreedrawOutlineAsSegments(element, points, elementsMap) {
12575
12909
  );
12576
12910
  }
12577
12911
 
12578
- // src/comparisons.ts
12579
- init_define_import_meta_env();
12580
- var hasBackground = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "ellipse" || type === "diamond" || type === "line" || type === "freedraw";
12581
- var hasStrokeColor = (type) => type === "rectangle" || type === "ellipse" || type === "diamond" || type === "freedraw" || type === "arrow" || type === "line" || type === "text" || type === "embeddable";
12582
- var hasStrokeWidth = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "ellipse" || type === "diamond" || type === "freedraw" || type === "arrow" || type === "line";
12583
- var hasStrokeStyle = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "ellipse" || type === "diamond" || type === "arrow" || type === "line";
12584
- var canChangeRoundness = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "line" || type === "diamond" || type === "image";
12585
- var toolIsArrow = (type) => type === "arrow";
12586
- var canHaveArrowheads = (type) => type === "arrow";
12587
-
12588
12912
  // src/shape.ts
12589
12913
  var ShapeCache = class _ShapeCache {
12590
12914
  static rg = new RoughGenerator();
@@ -14063,7 +14387,7 @@ var getCommonBounds = (elements, elementsMap) => {
14063
14387
  let maxX = -Infinity;
14064
14388
  let minY = Infinity;
14065
14389
  let maxY = -Infinity;
14066
- const _elementsMap = elementsMap || arrayToMap5(elements);
14390
+ const _elementsMap = elementsMap || arrayToMap6(elements);
14067
14391
  elements.forEach((element) => {
14068
14392
  const [x1, y1, x2, y2] = getElementBounds(element, _elementsMap);
14069
14393
  minX = Math.min(minX, x1);
@@ -14138,7 +14462,7 @@ var getClosestElementBounds = (elements, from) => {
14138
14462
  }
14139
14463
  let minDistance = Infinity;
14140
14464
  let closestElement = elements[0];
14141
- const elementsMap = arrayToMap5(elements);
14465
+ const elementsMap = arrayToMap6(elements);
14142
14466
  elements.forEach((element) => {
14143
14467
  const [x1, y1, x2, y2] = getElementBounds(element, elementsMap);
14144
14468
  const distance3 = pointDistance7(
@@ -14431,7 +14755,7 @@ var calculateTranslation = (group, selectionBoundingBox, { axis, position }) =>
14431
14755
  // src/delta.ts
14432
14756
  init_define_import_meta_env();
14433
14757
  import {
14434
- arrayToMap as arrayToMap9,
14758
+ arrayToMap as arrayToMap10,
14435
14759
  arrayToObject,
14436
14760
  assertNever as assertNever4,
14437
14761
  isDevEnv as isDevEnv6,
@@ -14458,7 +14782,7 @@ import {
14458
14782
  ORIG_ID,
14459
14783
  randomId,
14460
14784
  randomInteger as randomInteger2,
14461
- arrayToMap as arrayToMap6,
14785
+ arrayToMap as arrayToMap8,
14462
14786
  castArray,
14463
14787
  findLastIndex,
14464
14788
  getUpdatedTimestamp as getUpdatedTimestamp2,
@@ -14467,79 +14791,61 @@ import {
14467
14791
 
14468
14792
  // src/sortElements.ts
14469
14793
  init_define_import_meta_env();
14470
- import { arrayToMapWithIndex } from "@excalidraw/common";
14471
- var normalizeGroupElementOrder = (elements) => {
14472
- const origElements = elements.slice();
14473
- const sortedElements = /* @__PURE__ */ new Set();
14474
- const orderInnerGroups = (elements2) => {
14475
- const firstGroupSig = elements2[0]?.groupIds?.join("");
14476
- const aGroup = [elements2[0]];
14477
- const bGroup = [];
14478
- for (const element of elements2.slice(1)) {
14479
- if (element.groupIds?.join("") === firstGroupSig) {
14480
- aGroup.push(element);
14481
- } else {
14482
- bGroup.push(element);
14483
- }
14484
- }
14485
- return bGroup.length ? [...aGroup, ...orderInnerGroups(bGroup)] : aGroup;
14794
+ import { arrayToMap as arrayToMap7 } from "@excalidraw/common";
14795
+ var defragmentGroups = (elements) => {
14796
+ const groupIdAtLevel = (element, level) => {
14797
+ return element.groupIds[element.groupIds.length - level - 1];
14486
14798
  };
14487
- const groupHandledElements = /* @__PURE__ */ new Map();
14488
- origElements.forEach((element, idx) => {
14489
- if (groupHandledElements.has(element.id)) {
14490
- return;
14491
- }
14492
- if (element.groupIds?.length) {
14493
- const topGroup = element.groupIds[element.groupIds.length - 1];
14494
- const groupElements = origElements.slice(idx).filter((element2) => {
14495
- const ret = element2?.groupIds?.some((id) => id === topGroup);
14496
- if (ret) {
14497
- groupHandledElements.set(element2.id, true);
14498
- }
14499
- return ret;
14500
- });
14501
- for (const elem of orderInnerGroups(groupElements)) {
14502
- sortedElements.add(elem);
14799
+ const orderLevel = (levelElements, level) => {
14800
+ const buckets = /* @__PURE__ */ new Map();
14801
+ const slots = [];
14802
+ for (const element of levelElements) {
14803
+ const groupId = groupIdAtLevel(element, level);
14804
+ if (groupId === void 0) {
14805
+ slots.push(element);
14806
+ continue;
14503
14807
  }
14504
- } else {
14505
- sortedElements.add(element);
14808
+ let bucket = buckets.get(groupId);
14809
+ if (!bucket) {
14810
+ bucket = [];
14811
+ buckets.set(groupId, bucket);
14812
+ slots.push(groupId);
14813
+ }
14814
+ bucket.push(element);
14506
14815
  }
14507
- });
14508
- if (sortedElements.size !== elements.length) {
14509
- console.error("normalizeGroupElementOrder: lost some elements... bailing!");
14816
+ return slots.flatMap(
14817
+ (slot) => typeof slot === "string" ? orderLevel(buckets.get(slot), level + 1) : [slot]
14818
+ );
14819
+ };
14820
+ const sortedElements = orderLevel(elements, 0);
14821
+ if (sortedElements.length !== elements.length) {
14822
+ console.error("defragmentGroups: lost some elements... bailing!");
14510
14823
  return elements;
14511
14824
  }
14512
- return [...sortedElements];
14825
+ return sortedElements;
14513
14826
  };
14514
14827
  var normalizeBoundElementsOrder = (elements) => {
14515
- const elementsMap = arrayToMapWithIndex(elements);
14516
- const origElements = elements.slice();
14828
+ const elementsMap = arrayToMap7(elements);
14517
14829
  const sortedElements = /* @__PURE__ */ new Set();
14518
- origElements.forEach((element, idx) => {
14519
- if (!element) {
14520
- return;
14521
- }
14830
+ for (const element of elements) {
14831
+ if (sortedElements.has(element)) {
14832
+ continue;
14833
+ }
14522
14834
  if (element.boundElements?.length) {
14523
14835
  sortedElements.add(element);
14524
- origElements[idx] = null;
14525
- element.boundElements.forEach((boundElement) => {
14836
+ for (const boundElement of element.boundElements) {
14526
14837
  const child = elementsMap.get(boundElement.id);
14527
14838
  if (child && boundElement.type === "text") {
14528
- sortedElements.add(child[0]);
14529
- origElements[child[1]] = null;
14839
+ sortedElements.add(child);
14530
14840
  }
14531
- });
14532
- } else if (element.type === "text" && element.containerId) {
14533
- const parent = elementsMap.get(element.containerId);
14534
- if (!parent?.[0].boundElements?.find((x) => x.id === element.id)) {
14535
- sortedElements.add(element);
14536
- origElements[idx] = null;
14537
14841
  }
14538
- } else {
14539
- sortedElements.add(element);
14540
- origElements[idx] = null;
14842
+ continue;
14541
14843
  }
14542
- });
14844
+ if (element.type === "text" && element.containerId && elementsMap.get(element.containerId)?.boundElements?.some((el) => el.id === element.id)) {
14845
+ continue;
14846
+ }
14847
+ sortedElements.add(element);
14848
+ }
14543
14849
  if (sortedElements.size !== elements.length) {
14544
14850
  console.error(
14545
14851
  "normalizeBoundElementsOrder: lost some elements... bailing!"
@@ -14549,7 +14855,7 @@ var normalizeBoundElementsOrder = (elements) => {
14549
14855
  return [...sortedElements];
14550
14856
  };
14551
14857
  var normalizeElementOrder = (elements) => {
14552
- return normalizeBoundElementsOrder(normalizeGroupElementOrder(elements));
14858
+ return normalizeBoundElementsOrder(defragmentGroups(elements));
14553
14859
  };
14554
14860
 
14555
14861
  // src/duplicate.ts
@@ -14589,8 +14895,9 @@ var duplicateElements = (opts) => {
14589
14895
  const origIdToDuplicateId = /* @__PURE__ */ new Map();
14590
14896
  const duplicateIdToOrigElement = /* @__PURE__ */ new Map();
14591
14897
  const duplicateElementsMap = /* @__PURE__ */ new Map();
14592
- const elementsMap = arrayToMap6(elements);
14898
+ const elementsMap = arrayToMap8(elements);
14593
14899
  const _idsOfElementsToDuplicate = opts.type === "in-place" ? opts.idsOfElementsToDuplicate : new Map(elements.map((el) => [el.id, el]));
14900
+ const preserveFrameChildrenOrder = opts.type === "everything" && opts.preserveFrameChildrenOrder;
14594
14901
  if (opts.type === "in-place") {
14595
14902
  for (const groupId of Object.keys(opts.appState.selectedGroupIds)) {
14596
14903
  elements.filter((el) => el.groupIds?.includes(groupId)).forEach((el) => _idsOfElementsToDuplicate.set(el.id, el));
@@ -14650,7 +14957,7 @@ var duplicateElements = (opts) => {
14650
14957
  const groupId = getSelectedGroupForElement(appState, element);
14651
14958
  if (groupId) {
14652
14959
  const groupElements = getElementsInGroup(elements, groupId).flatMap(
14653
- (element2) => isFrameLikeElement(element2) ? [...getFrameChildren(elements, element2.id), element2] : [element2]
14960
+ (element2) => isFrameLikeElement(element2) && !preserveFrameChildrenOrder ? [...getFrameChildren(elements, element2.id), element2] : [element2]
14654
14961
  );
14655
14962
  const targetIndex = findLastIndex(elementsWithDuplicates, (el) => {
14656
14963
  return el.groupIds?.includes(groupId);
@@ -14658,11 +14965,18 @@ var duplicateElements = (opts) => {
14658
14965
  insertBeforeOrAfterIndex(targetIndex, copyElements(groupElements));
14659
14966
  continue;
14660
14967
  }
14661
- if (element.frameId && frameIdsToDuplicate.has(element.frameId)) {
14968
+ if (!preserveFrameChildrenOrder && element.frameId && frameIdsToDuplicate.has(element.frameId)) {
14662
14969
  continue;
14663
14970
  }
14664
14971
  if (isFrameLikeElement(element)) {
14665
14972
  const frameId = element.id;
14973
+ if (preserveFrameChildrenOrder) {
14974
+ insertBeforeOrAfterIndex(
14975
+ findLastIndex(elementsWithDuplicates, (el) => el.id === frameId),
14976
+ copyElements(element)
14977
+ );
14978
+ continue;
14979
+ }
14666
14980
  const frameChildren = getFrameChildren(elements, frameId);
14667
14981
  const targetIndex = findLastIndex(elementsWithDuplicates, (el) => {
14668
14982
  return el.frameId === frameId || el.id === frameId;
@@ -15443,466 +15757,12 @@ var getObservedAppState = (appState) => {
15443
15757
  };
15444
15758
  var isObservedAppState = (appState) => !!Reflect.get(appState, hiddenObservedAppStateProp);
15445
15759
 
15446
- // src/fractionalIndex.ts
15447
- init_define_import_meta_env();
15448
-
15449
- // ../../node_modules/fractional-indexing/src/index.js
15450
- init_define_import_meta_env();
15451
- var BASE_62_DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
15452
- function midpoint(a2, b2, digits) {
15453
- const zero = digits[0];
15454
- if (b2 != null && a2 >= b2) {
15455
- throw new Error(a2 + " >= " + b2);
15456
- }
15457
- if (a2.slice(-1) === zero || b2 && b2.slice(-1) === zero) {
15458
- throw new Error("trailing zero");
15459
- }
15460
- if (b2) {
15461
- let n = 0;
15462
- while ((a2[n] || zero) === b2[n]) {
15463
- n++;
15464
- }
15465
- if (n > 0) {
15466
- return b2.slice(0, n) + midpoint(a2.slice(n), b2.slice(n), digits);
15467
- }
15468
- }
15469
- const digitA = a2 ? digits.indexOf(a2[0]) : 0;
15470
- const digitB = b2 != null ? digits.indexOf(b2[0]) : digits.length;
15471
- if (digitB - digitA > 1) {
15472
- const midDigit = Math.round(0.5 * (digitA + digitB));
15473
- return digits[midDigit];
15474
- } else {
15475
- if (b2 && b2.length > 1) {
15476
- return b2.slice(0, 1);
15477
- } else {
15478
- return digits[digitA] + midpoint(a2.slice(1), null, digits);
15479
- }
15480
- }
15481
- }
15482
- function validateInteger(int) {
15483
- if (int.length !== getIntegerLength(int[0])) {
15484
- throw new Error("invalid integer part of order key: " + int);
15485
- }
15486
- }
15487
- function getIntegerLength(head) {
15488
- if (head >= "a" && head <= "z") {
15489
- return head.charCodeAt(0) - "a".charCodeAt(0) + 2;
15490
- } else if (head >= "A" && head <= "Z") {
15491
- return "Z".charCodeAt(0) - head.charCodeAt(0) + 2;
15492
- } else {
15493
- throw new Error("invalid order key head: " + head);
15494
- }
15495
- }
15496
- function getIntegerPart(key) {
15497
- const integerPartLength = getIntegerLength(key[0]);
15498
- if (integerPartLength > key.length) {
15499
- throw new Error("invalid order key: " + key);
15500
- }
15501
- return key.slice(0, integerPartLength);
15502
- }
15503
- function validateOrderKey(key, digits) {
15504
- if (key === "A" + digits[0].repeat(26)) {
15505
- throw new Error("invalid order key: " + key);
15506
- }
15507
- const i = getIntegerPart(key);
15508
- const f = key.slice(i.length);
15509
- if (f.slice(-1) === digits[0]) {
15510
- throw new Error("invalid order key: " + key);
15511
- }
15512
- }
15513
- function incrementInteger(x, digits) {
15514
- validateInteger(x);
15515
- const [head, ...digs] = x.split("");
15516
- let carry = true;
15517
- for (let i = digs.length - 1; carry && i >= 0; i--) {
15518
- const d = digits.indexOf(digs[i]) + 1;
15519
- if (d === digits.length) {
15520
- digs[i] = digits[0];
15521
- } else {
15522
- digs[i] = digits[d];
15523
- carry = false;
15524
- }
15525
- }
15526
- if (carry) {
15527
- if (head === "Z") {
15528
- return "a" + digits[0];
15529
- }
15530
- if (head === "z") {
15531
- return null;
15532
- }
15533
- const h = String.fromCharCode(head.charCodeAt(0) + 1);
15534
- if (h > "a") {
15535
- digs.push(digits[0]);
15536
- } else {
15537
- digs.pop();
15538
- }
15539
- return h + digs.join("");
15540
- } else {
15541
- return head + digs.join("");
15542
- }
15543
- }
15544
- function decrementInteger(x, digits) {
15545
- validateInteger(x);
15546
- const [head, ...digs] = x.split("");
15547
- let borrow = true;
15548
- for (let i = digs.length - 1; borrow && i >= 0; i--) {
15549
- const d = digits.indexOf(digs[i]) - 1;
15550
- if (d === -1) {
15551
- digs[i] = digits.slice(-1);
15552
- } else {
15553
- digs[i] = digits[d];
15554
- borrow = false;
15555
- }
15556
- }
15557
- if (borrow) {
15558
- if (head === "a") {
15559
- return "Z" + digits.slice(-1);
15560
- }
15561
- if (head === "A") {
15562
- return null;
15563
- }
15564
- const h = String.fromCharCode(head.charCodeAt(0) - 1);
15565
- if (h < "Z") {
15566
- digs.push(digits.slice(-1));
15567
- } else {
15568
- digs.pop();
15569
- }
15570
- return h + digs.join("");
15571
- } else {
15572
- return head + digs.join("");
15573
- }
15574
- }
15575
- function generateKeyBetween(a2, b2, digits = BASE_62_DIGITS) {
15576
- if (a2 != null) {
15577
- validateOrderKey(a2, digits);
15578
- }
15579
- if (b2 != null) {
15580
- validateOrderKey(b2, digits);
15581
- }
15582
- if (a2 != null && b2 != null && a2 >= b2) {
15583
- throw new Error(a2 + " >= " + b2);
15584
- }
15585
- if (a2 == null) {
15586
- if (b2 == null) {
15587
- return "a" + digits[0];
15588
- }
15589
- const ib2 = getIntegerPart(b2);
15590
- const fb2 = b2.slice(ib2.length);
15591
- if (ib2 === "A" + digits[0].repeat(26)) {
15592
- return ib2 + midpoint("", fb2, digits);
15593
- }
15594
- if (ib2 < b2) {
15595
- return ib2;
15596
- }
15597
- const res = decrementInteger(ib2, digits);
15598
- if (res == null) {
15599
- throw new Error("cannot decrement any more");
15600
- }
15601
- return res;
15602
- }
15603
- if (b2 == null) {
15604
- const ia2 = getIntegerPart(a2);
15605
- const fa2 = a2.slice(ia2.length);
15606
- const i2 = incrementInteger(ia2, digits);
15607
- return i2 == null ? ia2 + midpoint(fa2, null, digits) : i2;
15608
- }
15609
- const ia = getIntegerPart(a2);
15610
- const fa = a2.slice(ia.length);
15611
- const ib = getIntegerPart(b2);
15612
- const fb = b2.slice(ib.length);
15613
- if (ia === ib) {
15614
- return ia + midpoint(fa, fb, digits);
15615
- }
15616
- const i = incrementInteger(ia, digits);
15617
- if (i == null) {
15618
- throw new Error("cannot increment any more");
15619
- }
15620
- if (i < b2) {
15621
- return i;
15622
- }
15623
- return ia + midpoint(fa, null, digits);
15624
- }
15625
- function generateNKeysBetween(a2, b2, n, digits = BASE_62_DIGITS) {
15626
- if (n === 0) {
15627
- return [];
15628
- }
15629
- if (n === 1) {
15630
- return [generateKeyBetween(a2, b2, digits)];
15631
- }
15632
- if (b2 == null) {
15633
- let c2 = generateKeyBetween(a2, b2, digits);
15634
- const result = [c2];
15635
- for (let i = 0; i < n - 1; i++) {
15636
- c2 = generateKeyBetween(c2, b2, digits);
15637
- result.push(c2);
15638
- }
15639
- return result;
15640
- }
15641
- if (a2 == null) {
15642
- let c2 = generateKeyBetween(a2, b2, digits);
15643
- const result = [c2];
15644
- for (let i = 0; i < n - 1; i++) {
15645
- c2 = generateKeyBetween(a2, c2, digits);
15646
- result.push(c2);
15647
- }
15648
- result.reverse();
15649
- return result;
15650
- }
15651
- const mid = Math.floor(n / 2);
15652
- const c = generateKeyBetween(a2, b2, digits);
15653
- return [
15654
- ...generateNKeysBetween(a2, c, mid, digits),
15655
- c,
15656
- ...generateNKeysBetween(c, b2, n - mid - 1, digits)
15657
- ];
15658
- }
15659
-
15660
- // src/fractionalIndex.ts
15661
- import { arrayToMap as arrayToMap7 } from "@excalidraw/common";
15662
- var InvalidFractionalIndexError = class extends Error {
15663
- code = "ELEMENT_HAS_INVALID_INDEX";
15664
- };
15665
- var validateFractionalIndices = (elements, {
15666
- shouldThrow = false,
15667
- includeBoundTextValidation = false,
15668
- ignoreLogs,
15669
- reconciliationContext
15670
- }) => {
15671
- const errorMessages = [];
15672
- const stringifyElement = (element) => `${element?.index}:${element?.id}:${element?.type}:${element?.isDeleted}:${element?.version}:${element?.versionNonce}`;
15673
- const indices = elements.map((x) => x.index);
15674
- for (const [i, index] of indices.entries()) {
15675
- const predecessorIndex = indices[i - 1];
15676
- const successorIndex = indices[i + 1];
15677
- if (!isValidFractionalIndex(index, predecessorIndex, successorIndex)) {
15678
- errorMessages.push(
15679
- `Fractional indices invariant has been compromised: "${stringifyElement(
15680
- elements[i - 1]
15681
- )}", "${stringifyElement(elements[i])}", "${stringifyElement(
15682
- elements[i + 1]
15683
- )}"`
15684
- );
15685
- }
15686
- if (includeBoundTextValidation && hasBoundTextElement(elements[i])) {
15687
- const container = elements[i];
15688
- const text = getBoundTextElement(container, arrayToMap7(elements));
15689
- if (text && text.index <= container.index) {
15690
- errorMessages.push(
15691
- `Fractional indices invariant for bound elements has been compromised: "${stringifyElement(
15692
- text
15693
- )}", "${stringifyElement(container)}"`
15694
- );
15695
- }
15696
- }
15697
- }
15698
- if (errorMessages.length) {
15699
- const error = new InvalidFractionalIndexError();
15700
- const additionalContext = [];
15701
- if (reconciliationContext) {
15702
- additionalContext.push("Additional reconciliation context:");
15703
- additionalContext.push(
15704
- reconciliationContext.localElements.map((x) => stringifyElement(x))
15705
- );
15706
- additionalContext.push(
15707
- reconciliationContext.remoteElements.map((x) => stringifyElement(x))
15708
- );
15709
- }
15710
- if (!ignoreLogs) {
15711
- console.error(
15712
- errorMessages.join("\n\n"),
15713
- error.stack,
15714
- elements.map((x) => stringifyElement(x)),
15715
- ...additionalContext
15716
- );
15717
- }
15718
- if (shouldThrow) {
15719
- throw error;
15720
- }
15721
- }
15722
- };
15723
- var orderByFractionalIndex = (elements) => {
15724
- return elements.sort((a2, b2) => {
15725
- if (isOrderedElement(a2) && isOrderedElement(b2)) {
15726
- if (a2.index < b2.index) {
15727
- return -1;
15728
- } else if (a2.index > b2.index) {
15729
- return 1;
15730
- }
15731
- return a2.id < b2.id ? -1 : 1;
15732
- }
15733
- return 1;
15734
- });
15735
- };
15736
- var syncMovedIndices = (elements, movedElements) => {
15737
- try {
15738
- const elementsMap = arrayToMap7(elements);
15739
- const indicesGroups = getMovedIndicesGroups(elements, movedElements);
15740
- const elementsUpdates = generateIndices(elements, indicesGroups);
15741
- const elementsCandidates = elements.map((x) => {
15742
- const elementUpdates = elementsUpdates.get(x);
15743
- if (elementUpdates) {
15744
- return { ...x, index: elementUpdates.index };
15745
- }
15746
- return x;
15747
- });
15748
- validateFractionalIndices(
15749
- elementsCandidates,
15750
- // we don't autofix invalid bound text indices, hence don't include it in the validation
15751
- {
15752
- includeBoundTextValidation: false,
15753
- shouldThrow: true,
15754
- ignoreLogs: true
15755
- }
15756
- );
15757
- for (const [element, { index }] of elementsUpdates) {
15758
- mutateElement(element, elementsMap, { index });
15759
- }
15760
- } catch (e) {
15761
- syncInvalidIndices(elements);
15762
- }
15763
- return elements;
15764
- };
15765
- var syncInvalidIndices = (elements) => {
15766
- const elementsMap = arrayToMap7(elements);
15767
- const indicesGroups = getInvalidIndicesGroups(elements);
15768
- const elementsUpdates = generateIndices(elements, indicesGroups);
15769
- for (const [element, { index }] of elementsUpdates) {
15770
- mutateElement(element, elementsMap, { index });
15771
- }
15772
- return elements;
15773
- };
15774
- var syncInvalidIndicesImmutable = (elements) => {
15775
- const syncedElements = arrayToMap7(elements);
15776
- const indicesGroups = getInvalidIndicesGroups(elements);
15777
- const elementsUpdates = generateIndices(elements, indicesGroups);
15778
- for (const [element, { index }] of elementsUpdates) {
15779
- syncedElements.set(element.id, newElementWith(element, { index }));
15780
- }
15781
- return syncedElements;
15782
- };
15783
- var getMovedIndicesGroups = (elements, movedElements) => {
15784
- const indicesGroups = [];
15785
- let i = 0;
15786
- while (i < elements.length) {
15787
- if (movedElements.has(elements[i].id)) {
15788
- const indicesGroup = [i - 1, i];
15789
- while (++i < elements.length) {
15790
- if (!movedElements.has(elements[i].id)) {
15791
- break;
15792
- }
15793
- indicesGroup.push(i);
15794
- }
15795
- indicesGroup.push(i);
15796
- indicesGroups.push(indicesGroup);
15797
- } else {
15798
- i++;
15799
- }
15800
- }
15801
- return indicesGroups;
15802
- };
15803
- var getInvalidIndicesGroups = (elements) => {
15804
- const indicesGroups = [];
15805
- let lowerBound = void 0;
15806
- let upperBound = void 0;
15807
- let lowerBoundIndex = -1;
15808
- let upperBoundIndex = 0;
15809
- const getLowerBound = (index) => {
15810
- const lowerBound2 = elements[lowerBoundIndex] ? elements[lowerBoundIndex].index : void 0;
15811
- const candidate = elements[index - 1]?.index;
15812
- if (!lowerBound2 && candidate || // first lowerBound
15813
- lowerBound2 && candidate && candidate > lowerBound2) {
15814
- return [candidate, index - 1];
15815
- }
15816
- return [lowerBound2, lowerBoundIndex];
15817
- };
15818
- const getUpperBound = (index) => {
15819
- const upperBound2 = elements[upperBoundIndex] ? elements[upperBoundIndex].index : void 0;
15820
- if (upperBound2 && index < upperBoundIndex) {
15821
- return [upperBound2, upperBoundIndex];
15822
- }
15823
- let i2 = upperBoundIndex;
15824
- while (++i2 < elements.length) {
15825
- const candidate = elements[i2]?.index;
15826
- if (!upperBound2 && candidate || // first upperBound
15827
- upperBound2 && candidate && candidate > upperBound2) {
15828
- return [candidate, i2];
15829
- }
15830
- }
15831
- return [void 0, i2];
15832
- };
15833
- let i = 0;
15834
- while (i < elements.length) {
15835
- const current = elements[i].index;
15836
- [lowerBound, lowerBoundIndex] = getLowerBound(i);
15837
- [upperBound, upperBoundIndex] = getUpperBound(i);
15838
- if (!isValidFractionalIndex(current, lowerBound, upperBound)) {
15839
- const indicesGroup = [lowerBoundIndex, i];
15840
- while (++i < elements.length) {
15841
- const current2 = elements[i].index;
15842
- const [nextLowerBound, nextLowerBoundIndex] = getLowerBound(i);
15843
- const [nextUpperBound, nextUpperBoundIndex] = getUpperBound(i);
15844
- if (isValidFractionalIndex(current2, nextLowerBound, nextUpperBound)) {
15845
- break;
15846
- }
15847
- [lowerBound, lowerBoundIndex] = [nextLowerBound, nextLowerBoundIndex];
15848
- [upperBound, upperBoundIndex] = [nextUpperBound, nextUpperBoundIndex];
15849
- indicesGroup.push(i);
15850
- }
15851
- indicesGroup.push(upperBoundIndex);
15852
- indicesGroups.push(indicesGroup);
15853
- } else {
15854
- i++;
15855
- }
15856
- }
15857
- return indicesGroups;
15858
- };
15859
- var isValidFractionalIndex = (index, predecessor, successor) => {
15860
- if (!index) {
15861
- return false;
15862
- }
15863
- if (predecessor && successor) {
15864
- return predecessor < index && index < successor;
15865
- }
15866
- if (!predecessor && successor) {
15867
- return index < successor;
15868
- }
15869
- if (predecessor && !successor) {
15870
- return predecessor < index;
15871
- }
15872
- return !!index;
15873
- };
15874
- var generateIndices = (elements, indicesGroups) => {
15875
- const elementsUpdates = /* @__PURE__ */ new Map();
15876
- for (const indices of indicesGroups) {
15877
- const lowerBoundIndex = indices.shift();
15878
- const upperBoundIndex = indices.pop();
15879
- const fractionalIndices = generateNKeysBetween(
15880
- elements[lowerBoundIndex]?.index,
15881
- elements[upperBoundIndex]?.index,
15882
- indices.length
15883
- );
15884
- for (let i = 0; i < indices.length; i++) {
15885
- const element = elements[indices[i]];
15886
- elementsUpdates.set(element, {
15887
- index: fractionalIndices[i]
15888
- });
15889
- }
15890
- }
15891
- return elementsUpdates;
15892
- };
15893
- var isOrderedElement = (element) => {
15894
- if (element.index) {
15895
- return true;
15896
- }
15897
- return false;
15898
- };
15899
-
15900
15760
  // src/Scene.ts
15901
15761
  init_define_import_meta_env();
15902
15762
  var import_lodash = __toESM(require_lodash(), 1);
15903
15763
  import {
15904
15764
  randomInteger as randomInteger3,
15905
- arrayToMap as arrayToMap8,
15765
+ arrayToMap as arrayToMap9,
15906
15766
  toBrandedType,
15907
15767
  isDevEnv as isDevEnv5,
15908
15768
  isTestEnv as isTestEnv6,
@@ -16114,24 +15974,14 @@ var Scene = class {
16114
15974
  this.selectedElementsCache.cache.clear();
16115
15975
  this.callbacks.clear();
16116
15976
  }
16117
- insertElementAtIndex(element, index) {
16118
- if (!Number.isFinite(index) || index < 0) {
16119
- throw new Error(
16120
- "insertElementAtIndex can only be called with index >= 0"
16121
- );
16122
- }
16123
- const nextElements = [
16124
- ...this.elements.slice(0, index),
16125
- element,
16126
- ...this.elements.slice(index)
16127
- ];
16128
- syncMovedIndices2(nextElements, arrayToMap8([element]));
16129
- this.replaceAllElements(nextElements);
16130
- }
15977
+ /** low-level - generally use app.insertNewElements() */
16131
15978
  insertElementsAtIndex(elements, index) {
16132
15979
  if (!elements.length) {
16133
15980
  return;
16134
15981
  }
15982
+ if (index === null) {
15983
+ index = this.elements.length;
15984
+ }
16135
15985
  if (!Number.isFinite(index) || index < 0) {
16136
15986
  throw new Error(
16137
15987
  "insertElementAtIndex can only be called with index >= 0"
@@ -16142,19 +15992,12 @@ var Scene = class {
16142
15992
  ...elements,
16143
15993
  ...this.elements.slice(index)
16144
15994
  ];
16145
- syncMovedIndices2(nextElements, arrayToMap8(elements));
15995
+ syncMovedIndices2(nextElements, arrayToMap9(elements));
16146
15996
  this.replaceAllElements(nextElements);
16147
15997
  }
15998
+ /** low-level - generally use app.insertNewElement() */
16148
15999
  insertElement = (element) => {
16149
- const index = element.frameId ? this.getElementIndex(element.frameId) : this.elements.length;
16150
- this.insertElementAtIndex(element, index);
16151
- };
16152
- insertElements = (elements) => {
16153
- if (!elements.length) {
16154
- return;
16155
- }
16156
- const index = elements[0]?.frameId ? this.getElementIndex(elements[0].frameId) : this.elements.length;
16157
- this.insertElementsAtIndex(elements, index);
16000
+ this.insertElementsAtIndex([element], null);
16158
16001
  };
16159
16002
  getElementIndex(elementId) {
16160
16003
  return this.elements.findIndex((element) => element.id === elementId);
@@ -17453,7 +17296,7 @@ var ElementsDelta = class _ElementsDelta {
17453
17296
  if (!flags.containsVisibleDifference && moved.size) {
17454
17297
  flags.containsVisibleDifference = true;
17455
17298
  }
17456
- return arrayToMap9(syncMovedIndices(ordered, moved));
17299
+ return arrayToMap10(syncMovedIndices(ordered, moved));
17457
17300
  }
17458
17301
  /**
17459
17302
  * It is necessary to post process the partials in case of reference values,
@@ -19044,7 +18887,7 @@ import { invariant as invariant12 } from "@excalidraw/common";
19044
18887
 
19045
18888
  // src/zindex.ts
19046
18889
  init_define_import_meta_env();
19047
- import { arrayToMap as arrayToMap10, findIndex, findLastIndex as findLastIndex2 } from "@excalidraw/common";
18890
+ import { arrayToMap as arrayToMap11, findIndex, findLastIndex as findLastIndex2 } from "@excalidraw/common";
19048
18891
  var isOfTargetFrame = (element, frameId) => {
19049
18892
  return element.frameId === frameId || element.id === frameId;
19050
18893
  };
@@ -19053,7 +18896,7 @@ var getIndicesToMove = (elements, appState, elementsToBeMoved) => {
19053
18896
  let deletedIndices = [];
19054
18897
  let includeDeletedIndex = null;
19055
18898
  let index = -1;
19056
- const selectedElementIds = arrayToMap10(
18899
+ const selectedElementIds = arrayToMap11(
19057
18900
  elementsToBeMoved ? elementsToBeMoved : getSelectedElements(elements, appState, {
19058
18901
  includeBoundTextElement: true,
19059
18902
  includeElementsInFrames: true
@@ -19343,7 +19186,7 @@ var shiftElementsToEnd = (elements, appState, direction, containingFrame, elemen
19343
19186
  return nextElements;
19344
19187
  };
19345
19188
  function shiftElementsAccountingForFrames(allElements, appState, direction, shiftFunction) {
19346
- const elementsToMove = arrayToMap10(
19189
+ const elementsToMove = arrayToMap11(
19347
19190
  getSelectedElements(allElements, appState, {
19348
19191
  includeBoundTextElement: true,
19349
19192
  includeElementsInFrames: true
@@ -21348,7 +21191,7 @@ import {
21348
21191
  VERTICAL_ALIGN as VERTICAL_ALIGN4,
21349
21192
  getSizeFromPoints as getSizeFromPoints3,
21350
21193
  randomId as randomId4,
21351
- arrayToMap as arrayToMap11,
21194
+ arrayToMap as arrayToMap12,
21352
21195
  assertNever as assertNever5,
21353
21196
  cloneJSON,
21354
21197
  getFontString as getFontString8,
@@ -21582,7 +21425,7 @@ var ElementStore = class {
21582
21425
  };
21583
21426
  getElementsMap = () => {
21584
21427
  return toBrandedType3(
21585
- arrayToMap11(this.getElements())
21428
+ arrayToMap12(this.getElements())
21586
21429
  );
21587
21430
  };
21588
21431
  getElement = (id) => {
@@ -22043,6 +21886,7 @@ export {
22043
21886
  getClosestElementBounds,
22044
21887
  getCommonBoundingBox,
22045
21888
  getCommonBounds,
21889
+ getCommonFrameId,
22046
21890
  getContainerCenter,
22047
21891
  getContainerCoords,
22048
21892
  getContainerElement,
@@ -22072,6 +21916,7 @@ export {
22072
21916
  getEmbedLink,
22073
21917
  getFlipAdjustedCropPosition,
22074
21918
  getFrameChildren,
21919
+ getFrameChildrenInsertionIndex,
22075
21920
  getFrameLikeElements,
22076
21921
  getFrameLikeTitle,
22077
21922
  getFreedrawOutlineAsSegments,
@@ -22175,6 +22020,7 @@ export {
22175
22020
  isElementInViewport,
22176
22021
  isElementIntersectingFrame,
22177
22022
  isElementLink,
22023
+ isEligibleFrameChildType,
22178
22024
  isEmbeddableElement,
22179
22025
  isExcalidrawElement,
22180
22026
  isFixedPoint,