@excalidraw/excalidraw 0.18.0-6fc8502 → 0.18.0-a18b139

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 (90) hide show
  1. package/dist/dev/{chunk-H7XJ5UVD.js → chunk-MJMGTOVG.js} +2 -2
  2. package/dist/dev/{chunk-KYBDXI6F.js → chunk-U3G3LY5D.js} +5304 -5762
  3. package/dist/dev/chunk-U3G3LY5D.js.map +7 -0
  4. package/dist/dev/data/{image-NKFINVKH.js → image-Y366K5SN.js} +3 -3
  5. package/dist/dev/index.js +1090 -730
  6. package/dist/dev/index.js.map +4 -4
  7. package/dist/dev/subset-shared.chunk.js +1 -1
  8. package/dist/dev/subset-worker.chunk.js +1 -1
  9. package/dist/prod/chunk-PUQLEN73.js +33 -0
  10. package/dist/prod/{chunk-KJYFYP64.js → chunk-VQA74LVG.js} +1 -1
  11. package/dist/prod/data/image-WY2VMQLG.js +1 -0
  12. package/dist/prod/index.js +17 -17
  13. package/dist/prod/subset-shared.chunk.js +1 -1
  14. package/dist/prod/subset-worker.chunk.js +1 -1
  15. package/dist/types/common/src/constants.d.ts +2 -0
  16. package/dist/types/common/src/utils.d.ts +7 -1
  17. package/dist/types/{excalidraw/scene → element/src}/Scene.d.ts +8 -11
  18. package/dist/types/element/src/align.d.ts +3 -3
  19. package/dist/types/element/src/binding.d.ts +10 -11
  20. package/dist/types/element/src/bounds.d.ts +2 -2
  21. package/dist/types/element/src/collision.d.ts +1 -1
  22. package/dist/types/element/src/dragElements.d.ts +3 -2
  23. package/dist/types/element/src/duplicate.d.ts +10 -13
  24. package/dist/types/element/src/elbowArrow.d.ts +1 -1
  25. package/dist/types/element/src/flowchart.d.ts +3 -2
  26. package/dist/types/element/src/fractionalIndex.d.ts +2 -2
  27. package/dist/types/element/src/frame.d.ts +2 -2
  28. package/dist/types/element/src/linearElementEditor.d.ts +13 -13
  29. package/dist/types/element/src/mutateElement.d.ts +10 -2
  30. package/dist/types/element/src/resizeElements.d.ts +4 -4
  31. package/dist/types/element/src/selection.d.ts +11 -0
  32. package/dist/types/element/src/sizeHelpers.d.ts +0 -1
  33. package/dist/types/element/src/textElement.d.ts +5 -3
  34. package/dist/types/element/src/zindex.d.ts +3 -3
  35. package/dist/types/excalidraw/actions/actionAddToLibrary.d.ts +3 -3
  36. package/dist/types/excalidraw/actions/actionBoundText.d.ts +2 -2
  37. package/dist/types/excalidraw/actions/actionCanvas.d.ts +27 -27
  38. package/dist/types/excalidraw/actions/actionClipboard.d.ts +6 -6
  39. package/dist/types/excalidraw/actions/actionCropEditor.d.ts +1 -1
  40. package/dist/types/excalidraw/actions/actionDeleteSelected.d.ts +3 -3
  41. package/dist/types/excalidraw/actions/actionElementLink.d.ts +1 -1
  42. package/dist/types/excalidraw/actions/actionElementLock.d.ts +2 -2
  43. package/dist/types/excalidraw/actions/actionEmbeddable.d.ts +1 -1
  44. package/dist/types/excalidraw/actions/actionExport.d.ts +31 -31
  45. package/dist/types/excalidraw/actions/actionFinalize.d.ts +2 -2
  46. package/dist/types/excalidraw/actions/actionFrame.d.ts +4 -4
  47. package/dist/types/excalidraw/actions/actionGroup.d.ts +2 -2
  48. package/dist/types/excalidraw/actions/actionLinearEditor.d.ts +1 -1
  49. package/dist/types/excalidraw/actions/actionMenu.d.ts +1 -1
  50. package/dist/types/excalidraw/actions/actionNavigate.d.ts +1 -1
  51. package/dist/types/excalidraw/actions/actionProperties.d.ts +17 -17
  52. package/dist/types/excalidraw/actions/actionSelectAll.d.ts +1 -1
  53. package/dist/types/excalidraw/actions/actionStyles.d.ts +1 -1
  54. package/dist/types/excalidraw/actions/actionToggleGridMode.d.ts +1 -1
  55. package/dist/types/excalidraw/actions/actionToggleObjectsSnapMode.d.ts +1 -1
  56. package/dist/types/excalidraw/actions/actionToggleSearchMenu.d.ts +2 -2
  57. package/dist/types/excalidraw/actions/actionToggleStats.d.ts +1 -1
  58. package/dist/types/excalidraw/actions/actionToggleViewMode.d.ts +1 -1
  59. package/dist/types/excalidraw/actions/actionToggleZenMode.d.ts +1 -1
  60. package/dist/types/excalidraw/actions/actionZindex.d.ts +2 -2
  61. package/dist/types/excalidraw/appState.d.ts +15 -15
  62. package/dist/types/excalidraw/components/App.d.ts +15 -4
  63. package/dist/types/excalidraw/components/ElementLinkDialog.d.ts +4 -3
  64. package/dist/types/excalidraw/components/Stats/Angle.d.ts +1 -1
  65. package/dist/types/excalidraw/components/Stats/CanvasGrid.d.ts +1 -1
  66. package/dist/types/excalidraw/components/Stats/Dimension.d.ts +1 -1
  67. package/dist/types/excalidraw/components/Stats/DragInput.d.ts +1 -1
  68. package/dist/types/excalidraw/components/Stats/FontSize.d.ts +1 -1
  69. package/dist/types/excalidraw/components/Stats/MultiAngle.d.ts +1 -1
  70. package/dist/types/excalidraw/components/Stats/MultiDimension.d.ts +1 -1
  71. package/dist/types/excalidraw/components/Stats/MultiFontSize.d.ts +1 -1
  72. package/dist/types/excalidraw/components/Stats/MultiPosition.d.ts +1 -1
  73. package/dist/types/excalidraw/components/Stats/Position.d.ts +1 -1
  74. package/dist/types/excalidraw/components/Stats/utils.d.ts +4 -4
  75. package/dist/types/excalidraw/components/canvases/InteractiveCanvas.d.ts +1 -0
  76. package/dist/types/excalidraw/components/hyperlink/Hyperlink.d.ts +3 -2
  77. package/dist/types/excalidraw/eraser/index.d.ts +14 -0
  78. package/dist/types/excalidraw/fonts/Fonts.d.ts +1 -1
  79. package/dist/types/excalidraw/lasso/utils.d.ts +1 -2
  80. package/dist/types/excalidraw/scene/Renderer.d.ts +1 -1
  81. package/dist/types/excalidraw/scene/scrollbars.d.ts +2 -3
  82. package/dist/types/excalidraw/scene/types.d.ts +2 -0
  83. package/dist/types/excalidraw/types.d.ts +6 -0
  84. package/dist/types/math/src/types.d.ts +1 -0
  85. package/package.json +1 -1
  86. package/dist/dev/chunk-KYBDXI6F.js.map +0 -7
  87. package/dist/prod/chunk-CAN5RS4P.js +0 -31
  88. package/dist/prod/data/image-5XD47O4X.js +0 -1
  89. /package/dist/dev/{chunk-H7XJ5UVD.js.map → chunk-MJMGTOVG.js.map} +0 -0
  90. /package/dist/dev/data/{image-NKFINVKH.js.map → image-Y366K5SN.js.map} +0 -0
package/dist/dev/index.js CHANGED
@@ -89,8 +89,8 @@ import {
89
89
  STATS_PANELS,
90
90
  STROKE_WIDTH,
91
91
  SVGStringToFile,
92
+ SVG_DOCUMENT_PREAMBLE,
92
93
  SVG_NS,
93
- Scene_default,
94
94
  ShapeCache,
95
95
  TAP_TWICE_TIMEOUT,
96
96
  TEXT_ALIGN,
@@ -171,7 +171,6 @@ import {
171
171
  findLastIndex,
172
172
  fixBindingsAfterDeletion,
173
173
  fixDuplicatedBindingsAfterDuplication,
174
- fixReversedBindings,
175
174
  focusNearestParent,
176
175
  frameAndChildrenSelectedTogether,
177
176
  generateIdFromFile,
@@ -248,6 +247,7 @@ import {
248
247
  getSelectedGroupIdForElement,
249
248
  getSelectedGroupIds,
250
249
  getSelectionBoxShape,
250
+ getSelectionStateForElements,
251
251
  getShortcutKey,
252
252
  getSizeFromPoints,
253
253
  getSuggestedBindingsForArrows,
@@ -328,6 +328,7 @@ import {
328
328
  isPointHittingLinkIcon,
329
329
  isPointInShape,
330
330
  isPromiseLike,
331
+ isReadonlyArray,
331
332
  isSafari,
332
333
  isSelectedViaGroup,
333
334
  isShallowEqual,
@@ -433,6 +434,7 @@ import {
433
434
  shouldMaintainAspectRatio,
434
435
  shouldResizeFromCenter,
435
436
  shouldRotateWithDiscreteAngle,
437
+ shouldTestInside,
436
438
  supportsResizeObserver,
437
439
  suppportsHorizontalAlign,
438
440
  syncInvalidIndices,
@@ -460,10 +462,10 @@ import {
460
462
  viewportCoordsToSceneCoords,
461
463
  wrapEvent,
462
464
  wrapText
463
- } from "./chunk-KYBDXI6F.js";
465
+ } from "./chunk-U3G3LY5D.js";
464
466
  import {
465
467
  define_import_meta_env_default
466
- } from "./chunk-H7XJ5UVD.js";
468
+ } from "./chunk-MJMGTOVG.js";
467
469
  import {
468
470
  en_default
469
471
  } from "./chunk-X3RYHLJU.js";
@@ -481,7 +483,7 @@ import React45, { useEffect as useEffect45 } from "react";
481
483
 
482
484
  // components/App.tsx
483
485
  import clsx56 from "clsx";
484
- import throttle3 from "lodash.throttle";
486
+ import throttle4 from "lodash.throttle";
485
487
  import React43, { useContext as useContext3 } from "react";
486
488
  import { flushSync as flushSync2 } from "react-dom";
487
489
  import rough from "roughjs/bin/rough";
@@ -573,8 +575,8 @@ var normalizeElementOrder = (elements) => {
573
575
  };
574
576
 
575
577
  // ../element/src/duplicate.ts
576
- var duplicateElement = (editingGroupId, groupIdMapForOperation, element, overrides, randomizeSeed) => {
577
- let copy = deepCopyElement(element);
578
+ var duplicateElement = (editingGroupId, groupIdMapForOperation, element, randomizeSeed) => {
579
+ const copy = deepCopyElement(element);
578
580
  if (isTestEnv()) {
579
581
  __test__defineOrigId(copy, element.id);
580
582
  }
@@ -594,9 +596,6 @@ var duplicateElement = (editingGroupId, groupIdMapForOperation, element, overrid
594
596
  return groupIdMapForOperation.get(groupId);
595
597
  }
596
598
  );
597
- if (overrides) {
598
- copy = Object.assign(copy, overrides);
599
- }
600
599
  return copy;
601
600
  };
602
601
  var duplicateElements = (opts) => {
@@ -605,13 +604,13 @@ var duplicateElements = (opts) => {
605
604
  editingGroupId: null,
606
605
  selectedGroupIds: {}
607
606
  };
608
- const reverseOrder = opts.type === "in-place" ? opts.reverseOrder : false;
609
607
  const processedIds = /* @__PURE__ */ new Map();
610
608
  const groupIdMap = /* @__PURE__ */ new Map();
611
- const newElements = [];
612
- const oldElements = [];
613
- const oldIdToDuplicatedId = /* @__PURE__ */ new Map();
614
- const duplicatedElementsMap = /* @__PURE__ */ new Map();
609
+ const duplicatedElements = [];
610
+ const origElements = [];
611
+ const origIdToDuplicateId = /* @__PURE__ */ new Map();
612
+ const duplicateIdToOrigElement = /* @__PURE__ */ new Map();
613
+ const duplicateElementsMap = /* @__PURE__ */ new Map();
615
614
  const elementsMap = arrayToMap(elements);
616
615
  const _idsOfElementsToDuplicate = opts.type === "in-place" ? opts.idsOfElementsToDuplicate : new Map(elements.map((el) => [el.id, el]));
617
616
  if (opts.type === "in-place") {
@@ -620,7 +619,7 @@ var duplicateElements = (opts) => {
620
619
  }
621
620
  }
622
621
  elements = normalizeElementOrder(elements);
623
- const elementsWithClones = elements.slice();
622
+ const elementsWithDuplicates = elements.slice();
624
623
  const copyElements = (element) => {
625
624
  const elements2 = castArray(element);
626
625
  const _newElements = elements2.reduce(
@@ -633,14 +632,14 @@ var duplicateElements = (opts) => {
633
632
  appState.editingGroupId,
634
633
  groupIdMap,
635
634
  element2,
636
- opts.overrides?.(element2),
637
635
  opts.randomizeSeed
638
636
  );
639
637
  processedIds.set(newElement2.id, true);
640
- duplicatedElementsMap.set(newElement2.id, newElement2);
641
- oldIdToDuplicatedId.set(element2.id, newElement2.id);
642
- oldElements.push(element2);
643
- newElements.push(newElement2);
638
+ duplicateElementsMap.set(newElement2.id, newElement2);
639
+ origIdToDuplicateId.set(element2.id, newElement2.id);
640
+ duplicateIdToOrigElement.set(newElement2.id, element2);
641
+ origElements.push(element2);
642
+ duplicatedElements.push(newElement2);
644
643
  acc.push(newElement2);
645
644
  return acc;
646
645
  },
@@ -652,19 +651,11 @@ var duplicateElements = (opts) => {
652
651
  if (!elements2) {
653
652
  return;
654
653
  }
655
- if (reverseOrder && index < 1) {
656
- elementsWithClones.unshift(...castArray(elements2));
654
+ if (index > elementsWithDuplicates.length - 1) {
655
+ elementsWithDuplicates.push(...castArray(elements2));
657
656
  return;
658
657
  }
659
- if (!reverseOrder && index > elementsWithClones.length - 1) {
660
- elementsWithClones.push(...castArray(elements2));
661
- return;
662
- }
663
- elementsWithClones.splice(
664
- index + (reverseOrder ? 0 : 1),
665
- 0,
666
- ...castArray(elements2)
667
- );
658
+ elementsWithDuplicates.splice(index + 1, 0, ...castArray(elements2));
668
659
  };
669
660
  const frameIdsToDuplicate = new Set(
670
661
  elements.filter(
@@ -683,9 +674,7 @@ var duplicateElements = (opts) => {
683
674
  const groupElements = getElementsInGroup(elements, groupId).flatMap(
684
675
  (element2) => isFrameLikeElement(element2) ? [...getFrameChildren(elements, element2.id), element2] : [element2]
685
676
  );
686
- const targetIndex = reverseOrder ? elementsWithClones.findIndex((el) => {
687
- return el.groupIds?.includes(groupId);
688
- }) : findLastIndex(elementsWithClones, (el) => {
677
+ const targetIndex = findLastIndex(elementsWithDuplicates, (el) => {
689
678
  return el.groupIds?.includes(groupId);
690
679
  });
691
680
  insertBeforeOrAfterIndex(targetIndex, copyElements(groupElements));
@@ -697,7 +686,7 @@ var duplicateElements = (opts) => {
697
686
  if (isFrameLikeElement(element)) {
698
687
  const frameId = element.id;
699
688
  const frameChildren = getFrameChildren(elements, frameId);
700
- const targetIndex = findLastIndex(elementsWithClones, (el) => {
689
+ const targetIndex = findLastIndex(elementsWithDuplicates, (el) => {
701
690
  return el.frameId === frameId || el.id === frameId;
702
691
  });
703
692
  insertBeforeOrAfterIndex(
@@ -708,12 +697,12 @@ var duplicateElements = (opts) => {
708
697
  }
709
698
  if (hasBoundTextElement(element)) {
710
699
  const boundTextElement = getBoundTextElement(element, elementsMap);
711
- const targetIndex = findLastIndex(elementsWithClones, (el) => {
700
+ const targetIndex = findLastIndex(elementsWithDuplicates, (el) => {
712
701
  return el.id === element.id || "containerId" in el && el.containerId === element.id;
713
702
  });
714
703
  if (boundTextElement) {
715
704
  insertBeforeOrAfterIndex(
716
- targetIndex + (reverseOrder ? -1 : 0),
705
+ targetIndex,
717
706
  copyElements([element, boundTextElement])
718
707
  );
719
708
  } else {
@@ -723,7 +712,7 @@ var duplicateElements = (opts) => {
723
712
  }
724
713
  if (isBoundToContainer(element)) {
725
714
  const container = getContainerElement(element, elementsMap);
726
- const targetIndex = findLastIndex(elementsWithClones, (el) => {
715
+ const targetIndex = findLastIndex(elementsWithDuplicates, (el) => {
727
716
  return el.id === element.id || el.id === container?.id;
728
717
  });
729
718
  if (container) {
@@ -737,30 +726,40 @@ var duplicateElements = (opts) => {
737
726
  continue;
738
727
  }
739
728
  insertBeforeOrAfterIndex(
740
- findLastIndex(elementsWithClones, (el) => el.id === element.id),
729
+ findLastIndex(elementsWithDuplicates, (el) => el.id === element.id),
741
730
  copyElements(element)
742
731
  );
743
732
  }
744
733
  fixDuplicatedBindingsAfterDuplication(
745
- newElements,
746
- oldIdToDuplicatedId,
747
- duplicatedElementsMap
748
- );
749
- if (reverseOrder) {
750
- fixReversedBindings(
751
- _idsOfElementsToDuplicate,
752
- elementsWithClones,
753
- oldIdToDuplicatedId
754
- );
755
- }
734
+ duplicatedElements,
735
+ origIdToDuplicateId,
736
+ duplicateElementsMap
737
+ );
756
738
  bindElementsToFramesAfterDuplication(
757
- elementsWithClones,
758
- oldElements,
759
- oldIdToDuplicatedId
739
+ elementsWithDuplicates,
740
+ origElements,
741
+ origIdToDuplicateId
760
742
  );
743
+ if (opts.overrides) {
744
+ for (const duplicateElement2 of duplicatedElements) {
745
+ const origElement = duplicateIdToOrigElement.get(duplicateElement2.id);
746
+ if (origElement) {
747
+ Object.assign(
748
+ duplicateElement2,
749
+ opts.overrides({
750
+ duplicateElement: duplicateElement2,
751
+ origElement,
752
+ origIdToDuplicateId
753
+ })
754
+ );
755
+ }
756
+ }
757
+ }
761
758
  return {
762
- newElements,
763
- elementsWithClones
759
+ duplicatedElements,
760
+ duplicateElementsMap,
761
+ elementsWithDuplicates,
762
+ origIdToDuplicateId
764
763
  };
765
764
  };
766
765
  var _deepCopyElement = (val, depth = 0) => {
@@ -1139,7 +1138,8 @@ var getOffsets = (element, linkedNodes, direction) => {
1139
1138
  y
1140
1139
  };
1141
1140
  };
1142
- var addNewNode = (element, elementsMap, appState, direction) => {
1141
+ var addNewNode = (element, appState, direction, scene) => {
1142
+ const elementsMap = scene.getNonDeletedElementsMap();
1143
1143
  const successors = getSuccessors(element, elementsMap, direction);
1144
1144
  const predeccessors = getPredecessors(element, elementsMap, direction);
1145
1145
  const offsets = getOffsets(
@@ -1170,16 +1170,16 @@ var addNewNode = (element, elementsMap, appState, direction) => {
1170
1170
  const bindingArrow = createBindingArrow(
1171
1171
  element,
1172
1172
  nextNode,
1173
- elementsMap,
1174
1173
  direction,
1175
- appState
1174
+ appState,
1175
+ scene
1176
1176
  );
1177
1177
  return {
1178
1178
  nextNode,
1179
1179
  bindingArrow
1180
1180
  };
1181
1181
  };
1182
- var addNewNodes = (startNode, elementsMap, appState, direction, numberOfNodes) => {
1182
+ var addNewNodes = (startNode, appState, direction, scene, numberOfNodes) => {
1183
1183
  const newNodes = [];
1184
1184
  for (let i = 0; i < numberOfNodes; i++) {
1185
1185
  let nextX;
@@ -1228,16 +1228,16 @@ var addNewNodes = (startNode, elementsMap, appState, direction, numberOfNodes) =
1228
1228
  const bindingArrow = createBindingArrow(
1229
1229
  startNode,
1230
1230
  nextNode,
1231
- elementsMap,
1232
1231
  direction,
1233
- appState
1232
+ appState,
1233
+ scene
1234
1234
  );
1235
1235
  newNodes.push(nextNode);
1236
1236
  newNodes.push(bindingArrow);
1237
1237
  }
1238
1238
  return newNodes;
1239
1239
  };
1240
- var createBindingArrow = (startBindingElement, endBindingElement, elementsMap, direction, appState) => {
1240
+ var createBindingArrow = (startBindingElement, endBindingElement, direction, appState, scene) => {
1241
1241
  let startX;
1242
1242
  let startY;
1243
1243
  const PADDING = 6;
@@ -1301,18 +1301,9 @@ var createBindingArrow = (startBindingElement, endBindingElement, elementsMap, d
1301
1301
  points: [pointFrom(0, 0), pointFrom(endX, endY)],
1302
1302
  elbowed: true
1303
1303
  });
1304
- bindLinearElement(
1305
- bindingArrow,
1306
- startBindingElement,
1307
- "start",
1308
- elementsMap
1309
- );
1310
- bindLinearElement(
1311
- bindingArrow,
1312
- endBindingElement,
1313
- "end",
1314
- elementsMap
1315
- );
1304
+ const elementsMap = scene.getNonDeletedElementsMap();
1305
+ bindLinearElement(bindingArrow, startBindingElement, "start", scene);
1306
+ bindLinearElement(bindingArrow, endBindingElement, "end", scene);
1316
1307
  const changedElements = /* @__PURE__ */ new Map();
1317
1308
  changedElements.set(
1318
1309
  startBindingElement.id,
@@ -1326,7 +1317,7 @@ var createBindingArrow = (startBindingElement, endBindingElement, elementsMap, d
1326
1317
  bindingArrow.id,
1327
1318
  bindingArrow
1328
1319
  );
1329
- LinearElementEditor.movePoints(bindingArrow, [
1320
+ LinearElementEditor.movePoints(bindingArrow, scene, [
1330
1321
  {
1331
1322
  index: 1,
1332
1323
  point: bindingArrow.points[1]
@@ -1426,13 +1417,14 @@ var FlowChartCreator = class {
1426
1417
  __publicField(this, "direction", "right");
1427
1418
  __publicField(this, "pendingNodes", null);
1428
1419
  }
1429
- createNodes(startNode, elementsMap, appState, direction) {
1420
+ createNodes(startNode, appState, direction, scene) {
1421
+ const elementsMap = scene.getNonDeletedElementsMap();
1430
1422
  if (direction !== this.direction) {
1431
1423
  const { nextNode, bindingArrow } = addNewNode(
1432
1424
  startNode,
1433
- elementsMap,
1434
1425
  appState,
1435
- direction
1426
+ direction,
1427
+ scene
1436
1428
  );
1437
1429
  this.numberOfNodes = 1;
1438
1430
  this.isCreatingChart = true;
@@ -1442,9 +1434,9 @@ var FlowChartCreator = class {
1442
1434
  this.numberOfNodes += 1;
1443
1435
  const newNodes = addNewNodes(
1444
1436
  startNode,
1445
- elementsMap,
1446
1437
  appState,
1447
1438
  direction,
1439
+ scene,
1448
1440
  this.numberOfNodes
1449
1441
  );
1450
1442
  this.isCreatingChart = true;
@@ -1461,13 +1453,9 @@ var FlowChartCreator = class {
1461
1453
  (node) => elementsAreInFrameBounds([node], frame, elementsMap) || elementOverlapsWithFrame(node, frame, elementsMap)
1462
1454
  )) {
1463
1455
  this.pendingNodes = this.pendingNodes.map(
1464
- (node) => mutateElement(
1465
- node,
1466
- {
1467
- frameId: startNode.frameId
1468
- },
1469
- false
1470
- )
1456
+ (node) => mutateElement(node, elementsMap, {
1457
+ frameId: startNode.frameId
1458
+ })
1471
1459
  );
1472
1460
  }
1473
1461
  }
@@ -1489,32 +1477,32 @@ var isNodeInFlowchart = (element, elementsMap) => {
1489
1477
  };
1490
1478
 
1491
1479
  // ../element/src/resizeElements.ts
1492
- var transformElements = (originalElements, transformHandleType, selectedElements, elementsMap, scene, shouldRotateWithDiscreteAngle2, shouldResizeFromCenter2, shouldMaintainAspectRatio2, pointerX, pointerY, centerX, centerY) => {
1480
+ var transformElements = (originalElements, transformHandleType, selectedElements, scene, shouldRotateWithDiscreteAngle2, shouldResizeFromCenter2, shouldMaintainAspectRatio2, pointerX, pointerY, centerX, centerY) => {
1481
+ const elementsMap = scene.getNonDeletedElementsMap();
1493
1482
  if (selectedElements.length === 1) {
1494
1483
  const [element] = selectedElements;
1495
1484
  if (transformHandleType === "rotation") {
1496
1485
  if (!isElbowArrow(element)) {
1497
1486
  rotateSingleElement(
1498
1487
  element,
1499
- elementsMap,
1500
1488
  scene,
1501
1489
  pointerX,
1502
1490
  pointerY,
1503
1491
  shouldRotateWithDiscreteAngle2
1504
1492
  );
1505
- updateBoundElements(element, elementsMap);
1493
+ updateBoundElements(element, scene);
1506
1494
  }
1507
1495
  } else if (isTextElement(element) && transformHandleType) {
1508
1496
  resizeSingleTextElement(
1509
1497
  originalElements,
1510
1498
  element,
1511
- elementsMap,
1499
+ scene,
1512
1500
  transformHandleType,
1513
1501
  shouldResizeFromCenter2,
1514
1502
  pointerX,
1515
1503
  pointerY
1516
1504
  );
1517
- updateBoundElements(element, elementsMap);
1505
+ updateBoundElements(element, scene);
1518
1506
  return true;
1519
1507
  } else if (transformHandleType) {
1520
1508
  const elementId = selectedElements[0].id;
@@ -1524,8 +1512,6 @@ var transformElements = (originalElements, transformHandleType, selectedElements
1524
1512
  const { nextWidth, nextHeight } = getNextSingleWidthAndHeightFromPointer(
1525
1513
  latestElement,
1526
1514
  origElement,
1527
- elementsMap,
1528
- originalElements,
1529
1515
  transformHandleType,
1530
1516
  pointerX,
1531
1517
  pointerY,
@@ -1539,8 +1525,8 @@ var transformElements = (originalElements, transformHandleType, selectedElements
1539
1525
  nextHeight,
1540
1526
  latestElement,
1541
1527
  origElement,
1542
- elementsMap,
1543
1528
  originalElements,
1529
+ scene,
1544
1530
  transformHandleType,
1545
1531
  {
1546
1532
  shouldMaintainAspectRatio: shouldMaintainAspectRatio2,
@@ -1555,7 +1541,6 @@ var transformElements = (originalElements, transformHandleType, selectedElements
1555
1541
  rotateMultipleElements(
1556
1542
  originalElements,
1557
1543
  selectedElements,
1558
- elementsMap,
1559
1544
  scene,
1560
1545
  pointerX,
1561
1546
  pointerY,
@@ -1598,8 +1583,11 @@ var transformElements = (originalElements, transformHandleType, selectedElements
1598
1583
  }
1599
1584
  return false;
1600
1585
  };
1601
- var rotateSingleElement = (element, elementsMap, scene, pointerX, pointerY, shouldRotateWithDiscreteAngle2) => {
1602
- const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
1586
+ var rotateSingleElement = (element, scene, pointerX, pointerY, shouldRotateWithDiscreteAngle2) => {
1587
+ const [x1, y1, x2, y2] = getElementAbsoluteCoords(
1588
+ element,
1589
+ scene.getNonDeletedElementsMap()
1590
+ );
1603
1591
  const cx = (x1 + x2) / 2;
1604
1592
  const cy = (y1 + y2) / 2;
1605
1593
  let angle;
@@ -1614,11 +1602,11 @@ var rotateSingleElement = (element, elementsMap, scene, pointerX, pointerY, shou
1614
1602
  angle = normalizeRadians(angle);
1615
1603
  }
1616
1604
  const boundTextElementId = getBoundTextElementId(element);
1617
- mutateElement(element, { angle });
1605
+ scene.mutateElement(element, { angle });
1618
1606
  if (boundTextElementId) {
1619
1607
  const textElement = scene.getElement(boundTextElementId);
1620
1608
  if (textElement && !isArrowElement(element)) {
1621
- mutateElement(textElement, { angle });
1609
+ scene.mutateElement(textElement, { angle });
1622
1610
  }
1623
1611
  }
1624
1612
  };
@@ -1647,7 +1635,8 @@ var measureFontSizeFromWidth = (element, elementsMap, nextWidth) => {
1647
1635
  size: nextFontSize
1648
1636
  };
1649
1637
  };
1650
- var resizeSingleTextElement = (originalElements, element, elementsMap, transformHandleType, shouldResizeFromCenter2, pointerX, pointerY) => {
1638
+ var resizeSingleTextElement = (originalElements, element, scene, transformHandleType, shouldResizeFromCenter2, pointerX, pointerY) => {
1639
+ const elementsMap = scene.getNonDeletedElementsMap();
1651
1640
  const [x1, y1, x2, y2, cx, cy] = getElementAbsoluteCoords(
1652
1641
  element,
1653
1642
  elementsMap
@@ -1736,7 +1725,7 @@ var resizeSingleTextElement = (originalElements, element, elementsMap, transform
1736
1725
  -angle
1737
1726
  );
1738
1727
  const [nextX, nextY] = newTopLeft;
1739
- mutateElement(element, {
1728
+ scene.mutateElement(element, {
1740
1729
  fontSize: metrics.size,
1741
1730
  width: nextWidth,
1742
1731
  height: nextHeight,
@@ -1833,10 +1822,11 @@ var resizeSingleTextElement = (originalElements, element, elementsMap, transform
1833
1822
  text,
1834
1823
  autoResize: false
1835
1824
  };
1836
- mutateElement(element, resizedElement);
1825
+ scene.mutateElement(element, resizedElement);
1837
1826
  }
1838
1827
  };
1839
- var rotateMultipleElements = (originalElements, elements, elementsMap, scene, pointerX, pointerY, shouldRotateWithDiscreteAngle2, centerX, centerY) => {
1828
+ var rotateMultipleElements = (originalElements, elements, scene, pointerX, pointerY, shouldRotateWithDiscreteAngle2, centerX, centerY) => {
1829
+ const elementsMap = scene.getNonDeletedElementsMap();
1840
1830
  let centerAngle = 5 * Math.PI / 2 + Math.atan2(pointerY - centerY, pointerX - centerX);
1841
1831
  if (shouldRotateWithDiscreteAngle2) {
1842
1832
  centerAngle += SHIFT_LOCKING_ANGLE / 2;
@@ -1853,35 +1843,25 @@ var rotateMultipleElements = (originalElements, elements, elementsMap, scene, po
1853
1843
  pointFrom(centerX, centerY),
1854
1844
  centerAngle + origAngle - element.angle
1855
1845
  );
1856
- if (isElbowArrow(element)) {
1857
- mutateElement(element, {
1858
- points: getArrowLocalFixedPoints(element, elementsMap)
1859
- });
1860
- } else {
1861
- mutateElement(
1862
- element,
1863
- {
1864
- x: element.x + (rotatedCX - cx),
1865
- y: element.y + (rotatedCY - cy),
1866
- angle: normalizeRadians(centerAngle + origAngle)
1867
- },
1868
- false
1869
- );
1870
- }
1871
- updateBoundElements(element, elementsMap, {
1846
+ const updates = isElbowArrow(element) ? {
1847
+ // Needed to re-route the arrow
1848
+ points: getArrowLocalFixedPoints(element, elementsMap)
1849
+ } : {
1850
+ x: element.x + (rotatedCX - cx),
1851
+ y: element.y + (rotatedCY - cy),
1852
+ angle: normalizeRadians(centerAngle + origAngle)
1853
+ };
1854
+ scene.mutateElement(element, updates);
1855
+ updateBoundElements(element, scene, {
1872
1856
  simultaneouslyUpdated: elements
1873
1857
  });
1874
1858
  const boundText = getBoundTextElement(element, elementsMap);
1875
1859
  if (boundText && !isArrowElement(element)) {
1876
- mutateElement(
1877
- boundText,
1878
- {
1879
- x: boundText.x + (rotatedCX - cx),
1880
- y: boundText.y + (rotatedCY - cy),
1881
- angle: normalizeRadians(centerAngle + origAngle)
1882
- },
1883
- false
1884
- );
1860
+ scene.mutateElement(boundText, {
1861
+ x: boundText.x + (rotatedCX - cx),
1862
+ y: boundText.y + (rotatedCY - cy),
1863
+ angle: normalizeRadians(centerAngle + origAngle)
1864
+ });
1885
1865
  }
1886
1866
  }
1887
1867
  }
@@ -2028,12 +2008,13 @@ var getResizedOrigin = (prevOrigin, prevWidth, prevHeight, newWidth, newHeight,
2028
2008
  };
2029
2009
  }
2030
2010
  };
2031
- var resizeSingleElement = (nextWidth, nextHeight, latestElement, origElement, elementsMap, originalElementsMap, handleDirection, {
2011
+ var resizeSingleElement = (nextWidth, nextHeight, latestElement, origElement, originalElementsMap, scene, handleDirection, {
2032
2012
  shouldInformMutation = true,
2033
2013
  shouldMaintainAspectRatio: shouldMaintainAspectRatio2 = false,
2034
2014
  shouldResizeFromCenter: shouldResizeFromCenter2 = false
2035
2015
  } = {}) => {
2036
2016
  let boundTextFont = {};
2017
+ const elementsMap = scene.getNonDeletedElementsMap();
2037
2018
  const boundTextElement = getBoundTextElement(latestElement, elementsMap);
2038
2019
  if (boundTextElement) {
2039
2020
  const stateOfBoundTextElementAtResize = originalElementsMap.get(
@@ -2116,7 +2097,7 @@ var resizeSingleElement = (nextWidth, nextHeight, latestElement, origElement, el
2116
2097
  newOrigin.y = newOrigin.y + nextHeight;
2117
2098
  }
2118
2099
  if ("scale" in latestElement && "scale" in origElement) {
2119
- mutateElement(latestElement, {
2100
+ scene.mutateElement(latestElement, {
2120
2101
  scale: [
2121
2102
  // defaulting because scaleX/Y can be 0/-0
2122
2103
  (Math.sign(nextWidth) || origElement.scale[0]) * origElement.scale[0],
@@ -2138,25 +2119,28 @@ var resizeSingleElement = (nextWidth, nextHeight, latestElement, origElement, el
2138
2119
  height: Math.abs(nextHeight),
2139
2120
  ...rescaledPoints
2140
2121
  };
2141
- mutateElement(latestElement, updates, shouldInformMutation);
2142
- updateBoundElements(latestElement, elementsMap, {
2122
+ scene.mutateElement(latestElement, updates, {
2123
+ informMutation: shouldInformMutation,
2124
+ isDragging: false
2125
+ });
2126
+ updateBoundElements(latestElement, scene, {
2143
2127
  // TODO: confirm with MARK if this actually makes sense
2144
2128
  newSize: { width: nextWidth, height: nextHeight }
2145
2129
  });
2146
2130
  if (boundTextElement && boundTextFont != null) {
2147
- mutateElement(boundTextElement, {
2131
+ scene.mutateElement(boundTextElement, {
2148
2132
  fontSize: boundTextFont.fontSize
2149
2133
  });
2150
2134
  }
2151
2135
  handleBindTextResize(
2152
2136
  latestElement,
2153
- elementsMap,
2137
+ scene,
2154
2138
  handleDirection,
2155
2139
  shouldMaintainAspectRatio2
2156
2140
  );
2157
2141
  }
2158
2142
  };
2159
- var getNextSingleWidthAndHeightFromPointer = (latestElement, origElement, elementsMap, originalElementsMap, handleDirection, pointerX, pointerY, {
2143
+ var getNextSingleWidthAndHeightFromPointer = (latestElement, origElement, handleDirection, pointerX, pointerY, {
2160
2144
  shouldMaintainAspectRatio: shouldMaintainAspectRatio2 = false,
2161
2145
  shouldResizeFromCenter: shouldResizeFromCenter2 = false
2162
2146
  } = {}) => {
@@ -2507,25 +2491,22 @@ var resizeMultipleElements = (selectedElements, elementsMap, handleDirection, sc
2507
2491
  update: { boundTextFontSize, ...update }
2508
2492
  } of elementsAndUpdates) {
2509
2493
  const { width: width2, height: height2, angle } = update;
2510
- mutateElement(element, update, false, {
2494
+ scene.mutateElement(element, update, {
2495
+ informMutation: true,
2511
2496
  // needed for the fixed binding point udpate to take effect
2512
2497
  isDragging: true
2513
2498
  });
2514
- updateBoundElements(element, elementsMap, {
2499
+ updateBoundElements(element, scene, {
2515
2500
  simultaneouslyUpdated: elementsToUpdate,
2516
2501
  newSize: { width: width2, height: height2 }
2517
2502
  });
2518
2503
  const boundTextElement = getBoundTextElement(element, elementsMap);
2519
2504
  if (boundTextElement && boundTextFontSize) {
2520
- mutateElement(
2521
- boundTextElement,
2522
- {
2523
- fontSize: boundTextFontSize,
2524
- angle: isLinearElement(element) ? void 0 : angle
2525
- },
2526
- false
2527
- );
2528
- handleBindTextResize(element, elementsMap, handleDirection, true);
2505
+ scene.mutateElement(boundTextElement, {
2506
+ fontSize: boundTextFontSize,
2507
+ angle: isLinearElement(element) ? void 0 : angle
2508
+ });
2509
+ handleBindTextResize(element, scene, handleDirection, true);
2529
2510
  }
2530
2511
  }
2531
2512
  scene.triggerUpdate();
@@ -2742,16 +2723,21 @@ var dragSelectedElements = (pointerDownState, _selectedElements, offset, scene,
2742
2723
  gridSize
2743
2724
  );
2744
2725
  elementsToUpdate.forEach((element) => {
2745
- updateElementCoords(pointerDownState, element, adjustedOffset);
2726
+ updateElementCoords(pointerDownState, element, scene, adjustedOffset);
2746
2727
  if (!isArrowElement(element)) {
2747
2728
  const textElement = getBoundTextElement(
2748
2729
  element,
2749
2730
  scene.getNonDeletedElementsMap()
2750
2731
  );
2751
2732
  if (textElement) {
2752
- updateElementCoords(pointerDownState, textElement, adjustedOffset);
2733
+ updateElementCoords(
2734
+ pointerDownState,
2735
+ textElement,
2736
+ scene,
2737
+ adjustedOffset
2738
+ );
2753
2739
  }
2754
- updateBoundElements(element, scene.getElementsMapIncludingDeleted(), {
2740
+ updateBoundElements(element, scene, {
2755
2741
  simultaneouslyUpdated: Array.from(elementsToUpdate)
2756
2742
  });
2757
2743
  }
@@ -2779,11 +2765,11 @@ var calculateOffset = (commonBounds, dragOffset, snapOffset, gridSize) => {
2779
2765
  y: nextY - y
2780
2766
  };
2781
2767
  };
2782
- var updateElementCoords = (pointerDownState, element, dragOffset) => {
2768
+ var updateElementCoords = (pointerDownState, element, scene, dragOffset) => {
2783
2769
  const originalElement = pointerDownState.originalElements.get(element.id) ?? element;
2784
2770
  const nextX = originalElement.x + dragOffset.x;
2785
2771
  const nextY = originalElement.y + dragOffset.y;
2786
- mutateElement(element, {
2772
+ scene.mutateElement(element, {
2787
2773
  x: nextX,
2788
2774
  y: nextY
2789
2775
  });
@@ -2804,6 +2790,7 @@ var dragNewElement = ({
2804
2790
  shouldMaintainAspectRatio: shouldMaintainAspectRatio2,
2805
2791
  shouldResizeFromCenter: shouldResizeFromCenter2,
2806
2792
  zoom,
2793
+ scene,
2807
2794
  widthAspectRatio = null,
2808
2795
  originOffset = null,
2809
2796
  informMutation = true
@@ -2867,7 +2854,7 @@ var dragNewElement = ({
2867
2854
  initialHeight: height
2868
2855
  };
2869
2856
  }
2870
- mutateElement(
2857
+ scene.mutateElement(
2871
2858
  newElement2,
2872
2859
  {
2873
2860
  x: newX + (originOffset?.x ?? 0),
@@ -2877,10 +2864,298 @@ var dragNewElement = ({
2877
2864
  ...textAutoResize,
2878
2865
  ...imageInitialDimension
2879
2866
  },
2880
- informMutation
2867
+ { informMutation, isDragging: false }
2868
+ );
2869
+ }
2870
+ };
2871
+
2872
+ // ../element/src/Scene.ts
2873
+ import throttle from "lodash.throttle";
2874
+ var getNonDeletedElements2 = (allElements) => {
2875
+ const elementsMap = /* @__PURE__ */ new Map();
2876
+ const elements = [];
2877
+ for (const element of allElements) {
2878
+ if (!element.isDeleted) {
2879
+ elements.push(element);
2880
+ elementsMap.set(
2881
+ element.id,
2882
+ element
2883
+ );
2884
+ }
2885
+ }
2886
+ return { elementsMap, elements };
2887
+ };
2888
+ var validateIndicesThrottled = throttle(
2889
+ (elements) => {
2890
+ if (isDevEnv() || isTestEnv() || window?.DEBUG_FRACTIONAL_INDICES) {
2891
+ validateFractionalIndices(elements, {
2892
+ // throw only in dev & test, to remain functional on `DEBUG_FRACTIONAL_INDICES`
2893
+ shouldThrow: isDevEnv() || isTestEnv(),
2894
+ includeBoundTextValidation: true
2895
+ });
2896
+ }
2897
+ },
2898
+ 1e3 * 60,
2899
+ { leading: true, trailing: false }
2900
+ );
2901
+ var hashSelectionOpts = (opts) => {
2902
+ const keys = ["includeBoundTextElement", "includeElementsInFrames"];
2903
+ let hash = "";
2904
+ for (const key of keys) {
2905
+ hash += `${key}:${opts[key] ? "1" : "0"}`;
2906
+ }
2907
+ return hash;
2908
+ };
2909
+ var Scene = class {
2910
+ constructor(elements = null) {
2911
+ // ---------------------------------------------------------------------------
2912
+ // instance methods/props
2913
+ // ---------------------------------------------------------------------------
2914
+ __publicField(this, "callbacks", /* @__PURE__ */ new Set());
2915
+ __publicField(this, "nonDeletedElements", []);
2916
+ __publicField(this, "nonDeletedElementsMap", toBrandedType(
2917
+ /* @__PURE__ */ new Map()
2918
+ ));
2919
+ // ideally all elements within the scene should be wrapped around with `Ordered` type, but right now there is no real benefit doing so
2920
+ __publicField(this, "elements", []);
2921
+ __publicField(this, "nonDeletedFramesLikes", []);
2922
+ __publicField(this, "frames", []);
2923
+ __publicField(this, "elementsMap", toBrandedType(/* @__PURE__ */ new Map()));
2924
+ __publicField(this, "selectedElementsCache", {
2925
+ selectedElementIds: null,
2926
+ elements: null,
2927
+ cache: /* @__PURE__ */ new Map()
2928
+ });
2929
+ /**
2930
+ * Random integer regenerated each scene update.
2931
+ *
2932
+ * Does not relate to elements versions, it's only a renderer
2933
+ * cache-invalidation nonce at the moment.
2934
+ */
2935
+ __publicField(this, "sceneNonce");
2936
+ __publicField(this, "insertElement", (element) => {
2937
+ const index = element.frameId ? this.getElementIndex(element.frameId) : this.elements.length;
2938
+ this.insertElementAtIndex(element, index);
2939
+ });
2940
+ __publicField(this, "insertElements", (elements) => {
2941
+ if (!elements.length) {
2942
+ return;
2943
+ }
2944
+ const index = elements[0]?.frameId ? this.getElementIndex(elements[0].frameId) : this.elements.length;
2945
+ this.insertElementsAtIndex(elements, index);
2946
+ });
2947
+ __publicField(this, "getContainerElement", (element) => {
2948
+ if (!element) {
2949
+ return null;
2950
+ }
2951
+ if (element.containerId) {
2952
+ return this.getElement(element.containerId) || null;
2953
+ }
2954
+ return null;
2955
+ });
2956
+ __publicField(this, "getElementsFromId", (id) => {
2957
+ const elementsMap = this.getNonDeletedElementsMap();
2958
+ const el = elementsMap.get(id);
2959
+ if (el) {
2960
+ return [el];
2961
+ }
2962
+ return getElementsInGroup(elementsMap, id);
2963
+ });
2964
+ if (elements) {
2965
+ this.replaceAllElements(elements);
2966
+ }
2967
+ }
2968
+ getSceneNonce() {
2969
+ return this.sceneNonce;
2970
+ }
2971
+ getNonDeletedElementsMap() {
2972
+ return this.nonDeletedElementsMap;
2973
+ }
2974
+ getElementsIncludingDeleted() {
2975
+ return this.elements;
2976
+ }
2977
+ getElementsMapIncludingDeleted() {
2978
+ return this.elementsMap;
2979
+ }
2980
+ getNonDeletedElements() {
2981
+ return this.nonDeletedElements;
2982
+ }
2983
+ getFramesIncludingDeleted() {
2984
+ return this.frames;
2985
+ }
2986
+ getSelectedElements(opts) {
2987
+ const hash = hashSelectionOpts(opts);
2988
+ const elements = opts?.elements || this.nonDeletedElements;
2989
+ if (this.selectedElementsCache.elements === elements && this.selectedElementsCache.selectedElementIds === opts.selectedElementIds) {
2990
+ const cached = this.selectedElementsCache.cache.get(hash);
2991
+ if (cached) {
2992
+ return cached;
2993
+ }
2994
+ } else if (opts?.elements == null) {
2995
+ this.selectedElementsCache.cache.clear();
2996
+ }
2997
+ const selectedElements = getSelectedElements(
2998
+ elements,
2999
+ { selectedElementIds: opts.selectedElementIds },
3000
+ opts
2881
3001
  );
3002
+ if (opts?.elements == null) {
3003
+ this.selectedElementsCache.selectedElementIds = opts.selectedElementIds;
3004
+ this.selectedElementsCache.elements = this.nonDeletedElements;
3005
+ this.selectedElementsCache.cache.set(hash, selectedElements);
3006
+ }
3007
+ return selectedElements;
3008
+ }
3009
+ getNonDeletedFramesLikes() {
3010
+ return this.nonDeletedFramesLikes;
3011
+ }
3012
+ getElement(id) {
3013
+ return this.elementsMap.get(id) || null;
3014
+ }
3015
+ getNonDeletedElement(id) {
3016
+ const element = this.getElement(id);
3017
+ if (element && isNonDeletedElement(element)) {
3018
+ return element;
3019
+ }
3020
+ return null;
3021
+ }
3022
+ /**
3023
+ * A utility method to help with updating all scene elements, with the added
3024
+ * performance optimization of not renewing the array if no change is made.
3025
+ *
3026
+ * Maps all current excalidraw elements, invoking the callback for each
3027
+ * element. The callback should either return a new mapped element, or the
3028
+ * original element if no changes are made. If no changes are made to any
3029
+ * element, this results in a no-op. Otherwise, the newly mapped elements
3030
+ * are set as the next scene's elements.
3031
+ *
3032
+ * @returns whether a change was made
3033
+ */
3034
+ mapElements(iteratee) {
3035
+ let didChange = false;
3036
+ const newElements = this.elements.map((element) => {
3037
+ const nextElement = iteratee(element);
3038
+ if (nextElement !== element) {
3039
+ didChange = true;
3040
+ }
3041
+ return nextElement;
3042
+ });
3043
+ if (didChange) {
3044
+ this.replaceAllElements(newElements);
3045
+ }
3046
+ return didChange;
3047
+ }
3048
+ replaceAllElements(nextElements) {
3049
+ if (!isReadonlyArray(nextElements)) {
3050
+ nextElements = orderByFractionalIndex(
3051
+ Array.from(nextElements.values())
3052
+ );
3053
+ }
3054
+ const nextFrameLikes = [];
3055
+ validateIndicesThrottled(nextElements);
3056
+ this.elements = syncInvalidIndices(nextElements);
3057
+ this.elementsMap.clear();
3058
+ this.elements.forEach((element) => {
3059
+ if (isFrameLikeElement(element)) {
3060
+ nextFrameLikes.push(element);
3061
+ }
3062
+ this.elementsMap.set(element.id, element);
3063
+ });
3064
+ const nonDeletedElements = getNonDeletedElements2(this.elements);
3065
+ this.nonDeletedElements = nonDeletedElements.elements;
3066
+ this.nonDeletedElementsMap = nonDeletedElements.elementsMap;
3067
+ this.frames = nextFrameLikes;
3068
+ this.nonDeletedFramesLikes = getNonDeletedElements2(this.frames).elements;
3069
+ this.triggerUpdate();
3070
+ }
3071
+ triggerUpdate() {
3072
+ this.sceneNonce = randomInteger();
3073
+ for (const callback of Array.from(this.callbacks)) {
3074
+ callback();
3075
+ }
3076
+ }
3077
+ onUpdate(cb) {
3078
+ if (this.callbacks.has(cb)) {
3079
+ throw new Error();
3080
+ }
3081
+ this.callbacks.add(cb);
3082
+ return () => {
3083
+ if (!this.callbacks.has(cb)) {
3084
+ throw new Error();
3085
+ }
3086
+ this.callbacks.delete(cb);
3087
+ };
3088
+ }
3089
+ destroy() {
3090
+ this.elements = [];
3091
+ this.nonDeletedElements = [];
3092
+ this.nonDeletedFramesLikes = [];
3093
+ this.frames = [];
3094
+ this.elementsMap.clear();
3095
+ this.selectedElementsCache.selectedElementIds = null;
3096
+ this.selectedElementsCache.elements = null;
3097
+ this.selectedElementsCache.cache.clear();
3098
+ this.callbacks.clear();
3099
+ }
3100
+ insertElementAtIndex(element, index) {
3101
+ if (!Number.isFinite(index) || index < 0) {
3102
+ throw new Error(
3103
+ "insertElementAtIndex can only be called with index >= 0"
3104
+ );
3105
+ }
3106
+ const nextElements = [
3107
+ ...this.elements.slice(0, index),
3108
+ element,
3109
+ ...this.elements.slice(index)
3110
+ ];
3111
+ syncMovedIndices(nextElements, arrayToMap([element]));
3112
+ this.replaceAllElements(nextElements);
3113
+ }
3114
+ insertElementsAtIndex(elements, index) {
3115
+ if (!elements.length) {
3116
+ return;
3117
+ }
3118
+ if (!Number.isFinite(index) || index < 0) {
3119
+ throw new Error(
3120
+ "insertElementAtIndex can only be called with index >= 0"
3121
+ );
3122
+ }
3123
+ const nextElements = [
3124
+ ...this.elements.slice(0, index),
3125
+ ...elements,
3126
+ ...this.elements.slice(index)
3127
+ ];
3128
+ syncMovedIndices(nextElements, arrayToMap(elements));
3129
+ this.replaceAllElements(nextElements);
3130
+ }
3131
+ getElementIndex(elementId) {
3132
+ return this.elements.findIndex((element) => element.id === elementId);
3133
+ }
3134
+ // Mutate an element with passed updates and trigger the component to update. Make sure you
3135
+ // are calling it either from a React event handler or within unstable_batchedUpdates().
3136
+ mutateElement(element, updates, options = {
3137
+ informMutation: true,
3138
+ isDragging: false
3139
+ }) {
3140
+ const elementsMap = this.getNonDeletedElementsMap();
3141
+ const { version: prevVersion } = element;
3142
+ const { version: nextVersion } = mutateElement(
3143
+ element,
3144
+ elementsMap,
3145
+ updates,
3146
+ options
3147
+ );
3148
+ if (
3149
+ // skip if the element is not in the scene (i.e. selection)
3150
+ this.elementsMap.has(element.id) && // skip if the element's version hasn't changed, as mutateElement returned the same element
3151
+ prevVersion !== nextVersion && options.informMutation
3152
+ ) {
3153
+ this.triggerUpdate();
3154
+ }
3155
+ return element;
2882
3156
  }
2883
3157
  };
3158
+ var Scene_default = Scene;
2884
3159
 
2885
3160
  // editor-jotai.ts
2886
3161
  import { atom, createStore } from "jotai";
@@ -3333,12 +3608,14 @@ var AppStateChange = class _AppStateChange {
3333
3608
  const selectedLinearElement = selectedLinearElementId && nextElements.has(selectedLinearElementId) ? new LinearElementEditor(
3334
3609
  nextElements.get(
3335
3610
  selectedLinearElementId
3336
- )
3611
+ ),
3612
+ nextElements
3337
3613
  ) : null;
3338
3614
  const editingLinearElement = editingLinearElementId && nextElements.has(editingLinearElementId) ? new LinearElementEditor(
3339
3615
  nextElements.get(
3340
3616
  editingLinearElementId
3341
- )
3617
+ ),
3618
+ nextElements
3342
3619
  ) : null;
3343
3620
  const nextAppState = {
3344
3621
  ...appState,
@@ -3757,13 +4034,14 @@ var _ElementsChange = class _ElementsChange {
3757
4034
  return [elements, true];
3758
4035
  }
3759
4036
  try {
3760
- _ElementsChange.redrawTextBoundingBoxes(nextElements, changedElements);
3761
4037
  nextElements = _ElementsChange.reorderElements(
3762
4038
  nextElements,
3763
4039
  changedElements,
3764
4040
  flags
3765
4041
  );
3766
- _ElementsChange.redrawBoundArrows(nextElements, changedElements);
4042
+ const tempScene = new Scene_default(nextElements);
4043
+ _ElementsChange.redrawTextBoundingBoxes(tempScene, changedElements);
4044
+ _ElementsChange.redrawBoundArrows(tempScene, changedElements);
3767
4045
  } catch (e) {
3768
4046
  console.error(
3769
4047
  `Couldn't mutate elements after applying elements change`,
@@ -3849,6 +4127,7 @@ var _ElementsChange = class _ElementsChange {
3849
4127
  } else {
3850
4128
  affectedElement = mutateElement(
3851
4129
  nextElement,
4130
+ nextElements,
3852
4131
  updates
3853
4132
  );
3854
4133
  }
@@ -3922,7 +4201,8 @@ var _ElementsChange = class _ElementsChange {
3922
4201
  );
3923
4202
  BindableElement.rebindAffected(nextElements, nextElement(), updater);
3924
4203
  }
3925
- static redrawTextBoundingBoxes(elements, changed) {
4204
+ static redrawTextBoundingBoxes(scene, changed) {
4205
+ const elements = scene.getNonDeletedElementsMap();
3926
4206
  const boxesToRedraw = /* @__PURE__ */ new Map();
3927
4207
  for (const element of changed.values()) {
3928
4208
  if (isBoundToContainer(element)) {
@@ -3950,13 +4230,13 @@ var _ElementsChange = class _ElementsChange {
3950
4230
  if (container.isDeleted || boundText.isDeleted) {
3951
4231
  continue;
3952
4232
  }
3953
- redrawTextBoundingBox(boundText, container, elements, false);
4233
+ redrawTextBoundingBox(boundText, container, scene);
3954
4234
  }
3955
4235
  }
3956
- static redrawBoundArrows(elements, changed) {
4236
+ static redrawBoundArrows(scene, changed) {
3957
4237
  for (const element of changed.values()) {
3958
4238
  if (!element.isDeleted && isBindableElement(element)) {
3959
- updateBoundElements(element, elements, {
4239
+ updateBoundElements(element, scene, {
3960
4240
  changedElements: changed
3961
4241
  });
3962
4242
  }
@@ -6741,11 +7021,10 @@ var deleteSelectedElements = (elements, appState, app) => {
6741
7021
  el.boundElements.forEach((candidate) => {
6742
7022
  const bound = app.scene.getNonDeletedElementsMap().get(candidate.id);
6743
7023
  if (bound && isElbowArrow(bound)) {
6744
- mutateElement(bound, {
7024
+ app.scene.mutateElement(bound, {
6745
7025
  startBinding: el.id === bound.startBinding?.elementId ? null : bound.startBinding,
6746
7026
  endBinding: el.id === bound.endBinding?.elementId ? null : bound.endBinding
6747
7027
  });
6748
- mutateElement(bound, { points: bound.points });
6749
7028
  }
6750
7029
  });
6751
7030
  }
@@ -6875,7 +7154,11 @@ var actionDeleteSelected = register({
6875
7154
  element.points.length - 1
6876
7155
  ) ? null : endBindingElement
6877
7156
  };
6878
- LinearElementEditor.deletePoints(element, selectedPointsIndices);
7157
+ LinearElementEditor.deletePoints(
7158
+ element,
7159
+ app.scene,
7160
+ selectedPointsIndices
7161
+ );
6879
7162
  return {
6880
7163
  elements,
6881
7164
  appState: {
@@ -7412,7 +7695,7 @@ var actionSelectAll = register({
7412
7695
  ),
7413
7696
  selectedLinearElement: (
7414
7697
  // single linear element selected
7415
- Object.keys(selectedElementIds).length === 1 && isLinearElement(elements[0]) ? new LinearElementEditor(elements[0]) : null
7698
+ Object.keys(selectedElementIds).length === 1 && isLinearElement(elements[0]) ? new LinearElementEditor(elements[0], arrayToMap(elements)) : null
7416
7699
  )
7417
7700
  },
7418
7701
  captureUpdate: CaptureUpdateAction.IMMEDIATELY
@@ -7436,7 +7719,7 @@ var actionDuplicateSelection = register({
7436
7719
  try {
7437
7720
  const newAppState = LinearElementEditor.duplicateSelectedPoints(
7438
7721
  appState,
7439
- app.scene.getNonDeletedElementsMap()
7722
+ app.scene
7440
7723
  );
7441
7724
  return {
7442
7725
  elements,
@@ -7447,7 +7730,7 @@ var actionDuplicateSelection = register({
7447
7730
  return false;
7448
7731
  }
7449
7732
  }
7450
- let { newElements: duplicatedElements, elementsWithClones: nextElements } = duplicateElements({
7733
+ let { duplicatedElements, elementsWithDuplicates } = duplicateElements({
7451
7734
  type: "in-place",
7452
7735
  elements,
7453
7736
  idsOfElementsToDuplicate: arrayToMap(
@@ -7458,38 +7741,35 @@ var actionDuplicateSelection = register({
7458
7741
  ),
7459
7742
  appState,
7460
7743
  randomizeSeed: true,
7461
- overrides: (element) => ({
7462
- x: element.x + DEFAULT_GRID_SIZE / 2,
7463
- y: element.y + DEFAULT_GRID_SIZE / 2
7464
- }),
7465
- reverseOrder: false
7744
+ overrides: ({ origElement, origIdToDuplicateId }) => {
7745
+ const duplicateFrameId = origElement.frameId && origIdToDuplicateId.get(origElement.frameId);
7746
+ return {
7747
+ x: origElement.x + DEFAULT_GRID_SIZE / 2,
7748
+ y: origElement.y + DEFAULT_GRID_SIZE / 2,
7749
+ frameId: duplicateFrameId ?? origElement.frameId
7750
+ };
7751
+ }
7466
7752
  });
7467
- if (app.props.onDuplicate && nextElements) {
7468
- const mappedElements = app.props.onDuplicate(nextElements, elements);
7753
+ if (app.props.onDuplicate && elementsWithDuplicates) {
7754
+ const mappedElements = app.props.onDuplicate(
7755
+ elementsWithDuplicates,
7756
+ elements
7757
+ );
7469
7758
  if (mappedElements) {
7470
- nextElements = mappedElements;
7759
+ elementsWithDuplicates = mappedElements;
7471
7760
  }
7472
7761
  }
7473
7762
  return {
7474
- elements: syncMovedIndices(nextElements, arrayToMap(duplicatedElements)),
7763
+ elements: syncMovedIndices(
7764
+ elementsWithDuplicates,
7765
+ arrayToMap(duplicatedElements)
7766
+ ),
7475
7767
  appState: {
7476
7768
  ...appState,
7477
- ...updateLinearElementEditors(duplicatedElements),
7478
- ...selectGroupsForSelectedElements(
7479
- {
7480
- editingGroupId: appState.editingGroupId,
7481
- selectedElementIds: excludeElementsInFramesFromSelection(
7482
- duplicatedElements
7483
- ).reduce((acc, element) => {
7484
- if (!isBoundToContainer(element)) {
7485
- acc[element.id] = true;
7486
- }
7487
- return acc;
7488
- }, {})
7489
- },
7490
- getNonDeletedElements(nextElements),
7491
- appState,
7492
- null
7769
+ ...getSelectionStateForElements(
7770
+ duplicatedElements,
7771
+ getNonDeletedElements(elementsWithDuplicates),
7772
+ appState
7493
7773
  )
7494
7774
  },
7495
7775
  captureUpdate: CaptureUpdateAction.IMMEDIATELY
@@ -7510,24 +7790,6 @@ var actionDuplicateSelection = register({
7510
7790
  }
7511
7791
  )
7512
7792
  });
7513
- var updateLinearElementEditors = (clonedElements) => {
7514
- const linears = clonedElements.filter(isLinearElement);
7515
- if (linears.length === 1) {
7516
- const linear = linears[0];
7517
- const boundElements = linear.boundElements?.map((def) => def.id) ?? [];
7518
- const onlySingleLinearSelected = clonedElements.every(
7519
- (el) => el.id === linear.id || boundElements.includes(el.id)
7520
- );
7521
- if (onlySingleLinearSelected) {
7522
- return {
7523
- selectedLinearElement: new LinearElementEditor(linear)
7524
- };
7525
- }
7526
- }
7527
- return {
7528
- selectedLinearElement: null
7529
- };
7530
- };
7531
7793
 
7532
7794
  // actions/actionProperties.tsx
7533
7795
  import { useEffect as useEffect13, useMemo as useMemo4, useRef as useRef11, useState as useState6 } from "react";
@@ -9782,20 +10044,16 @@ var getFormValue = function(elements, appState, getAttribute, isRelevantElement,
9782
10044
  }
9783
10045
  return ret;
9784
10046
  };
9785
- var offsetElementAfterFontResize = (prevElement, nextElement) => {
10047
+ var offsetElementAfterFontResize = (prevElement, nextElement, scene) => {
9786
10048
  if (isBoundToContainer(nextElement) || !nextElement.autoResize) {
9787
10049
  return nextElement;
9788
10050
  }
9789
- return mutateElement(
9790
- nextElement,
9791
- {
9792
- x: prevElement.textAlign === "left" ? prevElement.x : prevElement.x + (prevElement.width - nextElement.width) / (prevElement.textAlign === "center" ? 2 : 1),
9793
- // centering vertically is non-standard, but for Excalidraw I think
9794
- // it makes sense
9795
- y: prevElement.y + (prevElement.height - nextElement.height) / 2
9796
- },
9797
- false
9798
- );
10051
+ return scene.mutateElement(nextElement, {
10052
+ x: prevElement.textAlign === "left" ? prevElement.x : prevElement.x + (prevElement.width - nextElement.width) / (prevElement.textAlign === "center" ? 2 : 1),
10053
+ // centering vertically is non-standard, but for Excalidraw I think
10054
+ // it makes sense
10055
+ y: prevElement.y + (prevElement.height - nextElement.height) / 2
10056
+ });
9799
10057
  };
9800
10058
  var changeFontSize = (elements, appState, app, getNewFontSize, fallbackValue) => {
9801
10059
  const newFontSizes = /* @__PURE__ */ new Set();
@@ -9812,24 +10070,24 @@ var changeFontSize = (elements, appState, app, getNewFontSize, fallbackValue) =>
9812
10070
  redrawTextBoundingBox(
9813
10071
  newElement2,
9814
10072
  app.scene.getContainerElement(oldElement),
9815
- app.scene.getNonDeletedElementsMap()
10073
+ app.scene
10074
+ );
10075
+ newElement2 = offsetElementAfterFontResize(
10076
+ oldElement,
10077
+ newElement2,
10078
+ app.scene
9816
10079
  );
9817
- newElement2 = offsetElementAfterFontResize(oldElement, newElement2);
9818
10080
  return newElement2;
9819
10081
  }
9820
10082
  return oldElement;
9821
10083
  },
9822
10084
  true
9823
10085
  );
9824
- const updatedElementsMap = arrayToMap(updatedElements);
9825
10086
  getSelectedElements(elements, appState, {
9826
10087
  includeBoundTextElement: true
9827
10088
  }).forEach((element) => {
9828
10089
  if (isTextElement(element)) {
9829
- updateBoundElements(
9830
- element,
9831
- updatedElementsMap
9832
- );
10090
+ updateBoundElements(element, app.scene);
9833
10091
  }
9834
10092
  });
9835
10093
  return {
@@ -10393,7 +10651,7 @@ var actionChangeFontFamily = register({
10393
10651
  const cachedContainer = cachedElements?.get(oldElement.containerId || "") || {};
10394
10652
  const container = app.scene.getContainerElement(oldElement);
10395
10653
  if (resetContainers && container && cachedContainer) {
10396
- mutateElement(container, { ...cachedContainer }, false);
10654
+ app.scene.mutateElement(container, { ...cachedContainer });
10397
10655
  }
10398
10656
  if (!skipFontFaceCheck) {
10399
10657
  uniqueChars = /* @__PURE__ */ new Set([
@@ -10415,12 +10673,7 @@ var actionChangeFontFamily = register({
10415
10673
  const chars = Array.from(uniqueChars.values()).join();
10416
10674
  if (skipFontFaceCheck || window.document.fonts.check(fontString, chars)) {
10417
10675
  for (const [element, container] of elementContainerMapping) {
10418
- redrawTextBoundingBox(
10419
- element,
10420
- container,
10421
- app.scene.getNonDeletedElementsMap(),
10422
- false
10423
- );
10676
+ redrawTextBoundingBox(element, container, app.scene);
10424
10677
  }
10425
10678
  } else {
10426
10679
  window.document.fonts.load(fontString, chars).then((fontFaces) => {
@@ -10431,8 +10684,7 @@ var actionChangeFontFamily = register({
10431
10684
  redrawTextBoundingBox(
10432
10685
  latestElement,
10433
10686
  latestContainer,
10434
- app.scene.getNonDeletedElementsMap(),
10435
- false
10687
+ app.scene
10436
10688
  );
10437
10689
  }
10438
10690
  }
@@ -10592,7 +10844,7 @@ var actionChangeTextAlign = register({
10592
10844
  redrawTextBoundingBox(
10593
10845
  newElement2,
10594
10846
  app.scene.getContainerElement(oldElement),
10595
- app.scene.getNonDeletedElementsMap()
10847
+ app.scene
10596
10848
  );
10597
10849
  return newElement2;
10598
10850
  }
@@ -10678,7 +10930,7 @@ var actionChangeVerticalAlign = register({
10678
10930
  redrawTextBoundingBox(
10679
10931
  newElement2,
10680
10932
  app.scene.getContainerElement(oldElement),
10681
- app.scene.getNonDeletedElementsMap()
10933
+ app.scene
10682
10934
  );
10683
10935
  return newElement2;
10684
10936
  }
@@ -11020,17 +11272,16 @@ var actionChangeArrowType = register({
11020
11272
  newElement2,
11021
11273
  startHoveredElement,
11022
11274
  "start",
11023
- elementsMap
11275
+ app.scene
11024
11276
  );
11025
- endHoveredElement && bindLinearElement(newElement2, endHoveredElement, "end", elementsMap);
11277
+ endHoveredElement && bindLinearElement(newElement2, endHoveredElement, "end", app.scene);
11026
11278
  const startBinding = startElement && newElement2.startBinding ? {
11027
11279
  // @ts-ignore TS cannot discern check above
11028
11280
  ...newElement2.startBinding,
11029
11281
  ...calculateFixedPointForElbowArrowBinding(
11030
11282
  newElement2,
11031
11283
  startElement,
11032
- "start",
11033
- elementsMap
11284
+ "start"
11034
11285
  )
11035
11286
  } : null;
11036
11287
  const endBinding = endElement && newElement2.endBinding ? {
@@ -11039,8 +11290,7 @@ var actionChangeArrowType = register({
11039
11290
  ...calculateFixedPointForElbowArrowBinding(
11040
11291
  newElement2,
11041
11292
  endElement,
11042
- "end",
11043
- elementsMap
11293
+ "end"
11044
11294
  )
11045
11295
  } : null;
11046
11296
  newElement2 = {
@@ -11068,7 +11318,7 @@ var actionChangeArrowType = register({
11068
11318
  newElement2.startBinding.elementId
11069
11319
  );
11070
11320
  if (startElement) {
11071
- bindLinearElement(newElement2, startElement, "start", elementsMap);
11321
+ bindLinearElement(newElement2, startElement, "start", app.scene);
11072
11322
  }
11073
11323
  }
11074
11324
  if (newElement2.endBinding) {
@@ -11076,7 +11326,7 @@ var actionChangeArrowType = register({
11076
11326
  newElement2.endBinding.elementId
11077
11327
  );
11078
11328
  if (endElement) {
11079
- bindLinearElement(newElement2, endElement, "end", elementsMap);
11329
+ bindLinearElement(newElement2, endElement, "end", app.scene);
11080
11330
  }
11081
11331
  }
11082
11332
  }
@@ -11091,7 +11341,8 @@ var actionChangeArrowType = register({
11091
11341
  const selected = newElements.find((el) => el.id === selectedId);
11092
11342
  if (selected) {
11093
11343
  newState.selectedLinearElement = new LinearElementEditor(
11094
- selected
11344
+ selected,
11345
+ arrayToMap(elements)
11095
11346
  );
11096
11347
  }
11097
11348
  }
@@ -11823,7 +12074,6 @@ var actionFinalize = register({
11823
12074
  element,
11824
12075
  startBindingElement,
11825
12076
  endBindingElement,
11826
- elementsMap,
11827
12077
  scene
11828
12078
  );
11829
12079
  }
@@ -11841,7 +12091,11 @@ var actionFinalize = register({
11841
12091
  let newElements = elements;
11842
12092
  const pendingImageElement = appState.pendingImageElementId && scene.getElement(appState.pendingImageElementId);
11843
12093
  if (pendingImageElement) {
11844
- mutateElement(pendingImageElement, { isDeleted: true }, false);
12094
+ scene.mutateElement(
12095
+ pendingImageElement,
12096
+ { isDeleted: true },
12097
+ { informMutation: false, isDragging: false }
12098
+ );
11845
12099
  }
11846
12100
  if (window.document.activeElement instanceof HTMLElement) {
11847
12101
  focusContainer();
@@ -11851,7 +12105,7 @@ var actionFinalize = register({
11851
12105
  if (multiPointElement.type !== "freedraw" && appState.lastPointerDownWith !== "touch") {
11852
12106
  const { points, lastCommittedPoint } = multiPointElement;
11853
12107
  if (!lastCommittedPoint || points[points.length - 1] !== lastCommittedPoint) {
11854
- mutateElement(multiPointElement, {
12108
+ scene.mutateElement(multiPointElement, {
11855
12109
  points: multiPointElement.points.slice(0, -1)
11856
12110
  });
11857
12111
  }
@@ -11866,7 +12120,7 @@ var actionFinalize = register({
11866
12120
  if (isLoop) {
11867
12121
  const linePoints = multiPointElement.points;
11868
12122
  const firstPoint = linePoints[0];
11869
- mutateElement(multiPointElement, {
12123
+ scene.mutateElement(multiPointElement, {
11870
12124
  points: linePoints.map(
11871
12125
  (p, index) => index === linePoints.length - 1 ? pointFrom(firstPoint[0], firstPoint[1]) : p
11872
12126
  )
@@ -11879,13 +12133,7 @@ var actionFinalize = register({
11879
12133
  -1,
11880
12134
  arrayToMap(elements)
11881
12135
  );
11882
- maybeBindLinearElement(
11883
- multiPointElement,
11884
- appState,
11885
- { x, y },
11886
- elementsMap,
11887
- elements
11888
- );
12136
+ maybeBindLinearElement(multiPointElement, appState, { x, y }, scene);
11889
12137
  }
11890
12138
  }
11891
12139
  if (!appState.activeTool.locked && appState.activeTool.type !== "freedraw" || !multiPointElement) {
@@ -11922,7 +12170,10 @@ var actionFinalize = register({
11922
12170
  [multiPointElement.id]: true
11923
12171
  } : appState.selectedElementIds,
11924
12172
  // To select the linear element when user has finished mutipoint editing
11925
- selectedLinearElement: multiPointElement && isLinearElement(multiPointElement) ? new LinearElementEditor(multiPointElement) : appState.selectedLinearElement,
12173
+ selectedLinearElement: multiPointElement && isLinearElement(multiPointElement) ? new LinearElementEditor(
12174
+ multiPointElement,
12175
+ arrayToMap(newElements)
12176
+ ) : appState.selectedLinearElement,
11926
12177
  pendingImageElementId: null
11927
12178
  },
11928
12179
  // TODO: #7348 we should not capture everything, but if we don't, it leads to incosistencies -> revisit
@@ -12469,7 +12720,7 @@ var serializeAsClipboardJSON = ({
12469
12720
  elements: elements.map((element) => {
12470
12721
  if (getContainingFrame(element, elementsMap) && !framesToCopy.has(getContainingFrame(element, elementsMap))) {
12471
12722
  const copiedElement = deepCopyElement(element);
12472
- mutateElement(copiedElement, {
12723
+ mutateElement(copiedElement, elementsMap, {
12473
12724
  frameId: null
12474
12725
  });
12475
12726
  return copiedElement;
@@ -12772,7 +13023,9 @@ var exportCanvas = async (type, elements, appState, files, {
12772
13023
  if (type === "svg") {
12773
13024
  return fileSave(
12774
13025
  svgPromise.then((svg) => {
12775
- return new Blob([svg.outerHTML], { type: MIME_TYPES.svg });
13026
+ return new Blob([SVG_DOCUMENT_PREAMBLE + svg.outerHTML], {
13027
+ type: MIME_TYPES.svg
13028
+ });
12776
13029
  }),
12777
13030
  {
12778
13031
  description: "Export to SVG",
@@ -12802,7 +13055,7 @@ var exportCanvas = async (type, elements, appState, files, {
12802
13055
  let blob = canvasToBlob(tempCanvas);
12803
13056
  if (appState.exportEmbedScene) {
12804
13057
  blob = blob.then(
12805
- (blob2) => import("./data/image-NKFINVKH.js").then(
13058
+ (blob2) => import("./data/image-Y366K5SN.js").then(
12806
13059
  ({ encodePngMetadata: encodePngMetadata2 }) => encodePngMetadata2({
12807
13060
  blob: blob2,
12808
13061
  metadata: serializeAsJSON(elements, appState, files, "local")
@@ -13219,11 +13472,7 @@ var actionPasteStyles = register({
13219
13472
  (element2) => isTextElement(newElement2) && element2.id === newElement2.containerId
13220
13473
  ) || null;
13221
13474
  }
13222
- redrawTextBoundingBox(
13223
- newElement2,
13224
- container,
13225
- app.scene.getNonDeletedElementsMap()
13226
- );
13475
+ redrawTextBoundingBox(newElement2, container, app.scene);
13227
13476
  }
13228
13477
  if (newElement2.type === "arrow" && isArrowElement(elementStylesToCopyFrom)) {
13229
13478
  newElement2 = newElementWith(newElement2, {
@@ -13952,7 +14201,8 @@ var actionAddToLibrary = register({
13952
14201
  });
13953
14202
 
13954
14203
  // ../element/src/align.ts
13955
- var alignElements = (selectedElements, elementsMap, alignment, scene) => {
14204
+ var alignElements = (selectedElements, alignment, scene) => {
14205
+ const elementsMap = scene.getNonDeletedElementsMap();
13956
14206
  const groups = getMaximumGroups(
13957
14207
  selectedElements,
13958
14208
  elementsMap
@@ -13965,11 +14215,11 @@ var alignElements = (selectedElements, elementsMap, alignment, scene) => {
13965
14215
  alignment
13966
14216
  );
13967
14217
  return group.map((element) => {
13968
- const updatedEle = mutateElement(element, {
14218
+ const updatedEle = scene.mutateElement(element, {
13969
14219
  x: element.x + translation.x,
13970
14220
  y: element.y + translation.y
13971
14221
  });
13972
- updateBoundElements(element, scene.getNonDeletedElementsMap(), {
14222
+ updateBoundElements(element, scene, {
13973
14223
  simultaneouslyUpdated: group
13974
14224
  });
13975
14225
  return updatedEle;
@@ -14006,13 +14256,7 @@ var alignActionsPredicate = (appState, app) => {
14006
14256
  };
14007
14257
  var alignSelectedElements = (elements, appState, app, alignment) => {
14008
14258
  const selectedElements = app.scene.getSelectedElements(appState);
14009
- const elementsMap = arrayToMap(elements);
14010
- const updatedElements = alignElements(
14011
- selectedElements,
14012
- elementsMap,
14013
- alignment,
14014
- app.scene
14015
- );
14259
+ const updatedElements = alignElements(selectedElements, alignment, app.scene);
14016
14260
  const updatedElementsMap = arrayToMap(updatedElements);
14017
14261
  return updateFrameMembershipOfSelectedElements(
14018
14262
  elements.map((element) => updatedElementsMap.get(element.id) || element),
@@ -14440,11 +14684,9 @@ var flipElements = (selectedElements, elementsMap, appState, flipDirection, app)
14440
14684
  );
14441
14685
  bindOrUnbindLinearElements(
14442
14686
  selectedElements.filter(isLinearElement),
14443
- elementsMap,
14444
- app.scene.getNonDeletedElements(),
14445
- app.scene,
14446
14687
  isBindingEnabled(appState),
14447
14688
  [],
14689
+ app.scene,
14448
14690
  appState.zoom
14449
14691
  );
14450
14692
  const { elbowArrows, otherElements } = selectedElements.reduce(
@@ -14454,13 +14696,13 @@ var flipElements = (selectedElements, elementsMap, appState, flipDirection, app)
14454
14696
  const { midX: newMidX, midY: newMidY } = getCommonBoundingBox(selectedElements);
14455
14697
  const [diffX, diffY] = [midX - newMidX, midY - newMidY];
14456
14698
  otherElements.forEach(
14457
- (element) => mutateElement(element, {
14699
+ (element) => app.scene.mutateElement(element, {
14458
14700
  x: element.x + diffX,
14459
14701
  y: element.y + diffY
14460
14702
  })
14461
14703
  );
14462
14704
  elbowArrows.forEach(
14463
- (element) => mutateElement(element, {
14705
+ (element) => app.scene.mutateElement(element, {
14464
14706
  x: element.x + diffX,
14465
14707
  y: element.y + diffY
14466
14708
  })
@@ -14831,7 +15073,7 @@ var actionUnbindText = register({
14831
15073
  boundTextElement,
14832
15074
  elementsMap
14833
15075
  );
14834
- mutateElement(boundTextElement, {
15076
+ app.scene.mutateElement(boundTextElement, {
14835
15077
  containerId: null,
14836
15078
  width,
14837
15079
  height,
@@ -14839,7 +15081,7 @@ var actionUnbindText = register({
14839
15081
  x,
14840
15082
  y
14841
15083
  });
14842
- mutateElement(element, {
15084
+ app.scene.mutateElement(element, {
14843
15085
  boundElements: element.boundElements?.filter(
14844
15086
  (ele) => ele.id !== boundTextElement.id
14845
15087
  ),
@@ -14888,24 +15130,21 @@ var actionBindText = register({
14888
15130
  textElement = selectedElements[1];
14889
15131
  container = selectedElements[0];
14890
15132
  }
14891
- mutateElement(textElement, {
15133
+ app.scene.mutateElement(textElement, {
14892
15134
  containerId: container.id,
14893
15135
  verticalAlign: VERTICAL_ALIGN.MIDDLE,
14894
15136
  textAlign: TEXT_ALIGN.CENTER,
14895
- autoResize: true
15137
+ autoResize: true,
15138
+ angle: isArrowElement(container) ? 0 : container?.angle ?? 0
14896
15139
  });
14897
- mutateElement(container, {
15140
+ app.scene.mutateElement(container, {
14898
15141
  boundElements: (container.boundElements || []).concat({
14899
15142
  type: "text",
14900
15143
  id: textElement.id
14901
15144
  })
14902
15145
  });
14903
15146
  const originalContainerHeight = container.height;
14904
- redrawTextBoundingBox(
14905
- textElement,
14906
- container,
14907
- app.scene.getNonDeletedElementsMap()
14908
- );
15147
+ redrawTextBoundingBox(textElement, container, app.scene);
14909
15148
  updateOriginalContainerCache(container.id, originalContainerHeight);
14910
15149
  return {
14911
15150
  elements: pushTextAboveContainer(elements, container, textElement),
@@ -15004,26 +15243,21 @@ var actionWrapTextInContainer = register({
15004
15243
  endBinding = { ...endBinding, elementId: container.id };
15005
15244
  }
15006
15245
  if (startBinding || endBinding) {
15007
- mutateElement(ele, { startBinding, endBinding }, false);
15246
+ app.scene.mutateElement(ele, {
15247
+ startBinding,
15248
+ endBinding
15249
+ });
15008
15250
  }
15009
15251
  });
15010
15252
  }
15011
- mutateElement(
15012
- textElement,
15013
- {
15014
- containerId: container.id,
15015
- verticalAlign: VERTICAL_ALIGN.MIDDLE,
15016
- boundElements: null,
15017
- textAlign: TEXT_ALIGN.CENTER,
15018
- autoResize: true
15019
- },
15020
- false
15021
- );
15022
- redrawTextBoundingBox(
15023
- textElement,
15024
- container,
15025
- app.scene.getNonDeletedElementsMap()
15026
- );
15253
+ app.scene.mutateElement(textElement, {
15254
+ containerId: container.id,
15255
+ verticalAlign: VERTICAL_ALIGN.MIDDLE,
15256
+ boundElements: null,
15257
+ textAlign: TEXT_ALIGN.CENTER,
15258
+ autoResize: true
15259
+ });
15260
+ redrawTextBoundingBox(textElement, container, app.scene);
15027
15261
  updatedElements = pushContainerBelowText(
15028
15262
  [...updatedElements, container],
15029
15263
  container,
@@ -15062,12 +15296,13 @@ var IS_HYPERLINK_TOOLTIP_VISIBLE = false;
15062
15296
  var embeddableLinkCache = /* @__PURE__ */ new Map();
15063
15297
  var Hyperlink = ({
15064
15298
  element,
15065
- elementsMap,
15299
+ scene,
15066
15300
  setAppState,
15067
15301
  onLinkOpen,
15068
15302
  setToast,
15069
15303
  updateEmbedValidationStatus
15070
15304
  }) => {
15305
+ const elementsMap = scene.getNonDeletedElementsMap();
15071
15306
  const appState = useExcalidrawAppState();
15072
15307
  const appProps = useAppProps();
15073
15308
  const device = useDevice();
@@ -15088,7 +15323,7 @@ var Hyperlink = ({
15088
15323
  setAppState({ activeEmbeddable: null });
15089
15324
  }
15090
15325
  if (!link) {
15091
- mutateElement(element, {
15326
+ scene.mutateElement(element, {
15092
15327
  link: null
15093
15328
  });
15094
15329
  updateEmbedValidationStatus(element, false);
@@ -15099,7 +15334,7 @@ var Hyperlink = ({
15099
15334
  setToast({ message: t("toast.unableToEmbed"), closable: true });
15100
15335
  }
15101
15336
  element.link && embeddableLinkCache.set(element.id, element.link);
15102
- mutateElement(element, {
15337
+ scene.mutateElement(element, {
15103
15338
  link
15104
15339
  });
15105
15340
  updateEmbedValidationStatus(element, false);
@@ -15114,7 +15349,7 @@ var Hyperlink = ({
15114
15349
  }
15115
15350
  const ar = embedLink ? embedLink.intrinsicSize.w / embedLink.intrinsicSize.h : 1;
15116
15351
  const hasLinkChanged = embeddableLinkCache.get(element.id) !== element.link;
15117
- mutateElement(element, {
15352
+ scene.mutateElement(element, {
15118
15353
  ...hasLinkChanged ? {
15119
15354
  width: embedLink?.type === "video" ? width > height ? width : height * ar : width,
15120
15355
  height: embedLink?.type === "video" ? width > height ? width / ar : height : height
@@ -15127,10 +15362,11 @@ var Hyperlink = ({
15127
15362
  }
15128
15363
  }
15129
15364
  } else {
15130
- mutateElement(element, { link });
15365
+ scene.mutateElement(element, { link });
15131
15366
  }
15132
15367
  }, [
15133
15368
  element,
15369
+ scene,
15134
15370
  setToast,
15135
15371
  appProps.validateEmbeddable,
15136
15372
  appState.activeEmbeddable,
@@ -15178,9 +15414,9 @@ var Hyperlink = ({
15178
15414
  }, [appState, element, isEditing, setAppState, elementsMap]);
15179
15415
  const handleRemove = useCallback4(() => {
15180
15416
  trackEvent("hyperlink", "delete");
15181
- mutateElement(element, { link: null });
15417
+ scene.mutateElement(element, { link: null });
15182
15418
  setAppState({ showHyperlinkPopup: false });
15183
- }, [setAppState, element]);
15419
+ }, [setAppState, element, scene]);
15184
15420
  const onEdit = () => {
15185
15421
  trackEvent("hyperlink", "edit", "popup-ui");
15186
15422
  setAppState({ showHyperlinkPopup: "editor" });
@@ -16712,7 +16948,7 @@ import {
16712
16948
  } from "react";
16713
16949
 
16714
16950
  // hooks/useScrollPosition.ts
16715
- import throttle from "lodash.throttle";
16951
+ import throttle2 from "lodash.throttle";
16716
16952
  import { useEffect as useEffect18 } from "react";
16717
16953
  var scrollPositionAtom = atom(0);
16718
16954
  var useScrollPosition = (elementRef) => {
@@ -16722,7 +16958,7 @@ var useScrollPosition = (elementRef) => {
16722
16958
  if (!element) {
16723
16959
  return;
16724
16960
  }
16725
- const handleScroll = throttle(() => {
16961
+ const handleScroll = throttle2(() => {
16726
16962
  const { scrollTop } = element;
16727
16963
  setScrollPosition(scrollTop);
16728
16964
  }, 200);
@@ -18142,7 +18378,7 @@ function LibraryMenuItems({
18142
18378
  type: "everything",
18143
18379
  elements: item.elements,
18144
18380
  randomizeSeed: true
18145
- }).newElements
18381
+ }).duplicatedElements
18146
18382
  };
18147
18383
  });
18148
18384
  },
@@ -19965,7 +20201,7 @@ var actionToggleLinearEditor = register({
19965
20201
  selectedElementIds: appState.selectedElementIds,
19966
20202
  includeBoundTextElement: true
19967
20203
  })[0];
19968
- const editingLinearElement = appState.editingLinearElement?.elementId === selectedElement.id ? null : new LinearElementEditor(selectedElement);
20204
+ const editingLinearElement = appState.editingLinearElement?.elementId === selectedElement.id ? null : new LinearElementEditor(selectedElement, arrayToMap(elements));
19969
20205
  return {
19970
20206
  appState: {
19971
20207
  ...appState,
@@ -20201,10 +20437,8 @@ var actionWrapSelectionInFrame = register({
20201
20437
  },
20202
20438
  perform: (elements, appState, _, app) => {
20203
20439
  const selectedElements = getSelectedElements(elements, appState);
20204
- const [x1, y1, x2, y2] = getCommonBounds(
20205
- selectedElements,
20206
- app.scene.getNonDeletedElementsMap()
20207
- );
20440
+ const elementsMap = app.scene.getNonDeletedElementsMap();
20441
+ const [x1, y1, x2, y2] = getCommonBounds(selectedElements, elementsMap);
20208
20442
  const PADDING = 16;
20209
20443
  const frame = newFrameElement({
20210
20444
  x: x1 - PADDING,
@@ -20219,13 +20453,9 @@ var actionWrapSelectionInFrame = register({
20219
20453
  );
20220
20454
  for (const elementInGroup of elementsInGroup) {
20221
20455
  const index = elementInGroup.groupIds.indexOf(appState.editingGroupId);
20222
- mutateElement(
20223
- elementInGroup,
20224
- {
20225
- groupIds: elementInGroup.groupIds.slice(0, index)
20226
- },
20227
- false
20228
- );
20456
+ mutateElement(elementInGroup, elementsMap, {
20457
+ groupIds: elementInGroup.groupIds.slice(0, index)
20458
+ });
20229
20459
  }
20230
20460
  }
20231
20461
  const nextElements = addElementsToFrame(
@@ -21707,7 +21937,7 @@ var DEFAULT_LINEAR_ELEMENT_PROPS = {
21707
21937
  height: 0
21708
21938
  };
21709
21939
  var DEFAULT_DIMENSION = 100;
21710
- var bindTextToContainer = (container, textProps, elementsMap) => {
21940
+ var bindTextToContainer = (container, textProps, scene) => {
21711
21941
  const textElement = newTextElement({
21712
21942
  x: 0,
21713
21943
  y: 0,
@@ -21723,10 +21953,10 @@ var bindTextToContainer = (container, textProps, elementsMap) => {
21723
21953
  id: textElement.id
21724
21954
  })
21725
21955
  });
21726
- redrawTextBoundingBox(textElement, container, elementsMap);
21956
+ redrawTextBoundingBox(textElement, container, scene);
21727
21957
  return [container, textElement];
21728
21958
  };
21729
- var bindLinearElementToElement = (linearElement, start2, end, elementStore, elementsMap) => {
21959
+ var bindLinearElementToElement = (linearElement, start2, end, elementStore, scene) => {
21730
21960
  let startBoundElement;
21731
21961
  let endBoundElement;
21732
21962
  Object.assign(linearElement, {
@@ -21800,7 +22030,7 @@ var bindLinearElementToElement = (linearElement, start2, end, elementStore, elem
21800
22030
  linearElement,
21801
22031
  startBoundElement,
21802
22032
  "start",
21803
- elementsMap
22033
+ scene
21804
22034
  );
21805
22035
  }
21806
22036
  }
@@ -21870,7 +22100,7 @@ var bindLinearElementToElement = (linearElement, start2, end, elementStore, elem
21870
22100
  linearElement,
21871
22101
  endBoundElement,
21872
22102
  "end",
21873
- elementsMap
22103
+ scene
21874
22104
  );
21875
22105
  }
21876
22106
  }
@@ -22061,6 +22291,7 @@ var convertToExcalidrawElements = (elementsSkeleton, opts) => {
22061
22291
  }
22062
22292
  }
22063
22293
  const elementsMap = elementStore.getElementsMap();
22294
+ const scene = new Scene_default(elementsMap);
22064
22295
  for (const [id, element] of elementsWithIds) {
22065
22296
  const excalidrawElement = elementStore.getElement(id);
22066
22297
  switch (element.type) {
@@ -22072,7 +22303,7 @@ var convertToExcalidrawElements = (elementsSkeleton, opts) => {
22072
22303
  let [container, text] = bindTextToContainer(
22073
22304
  excalidrawElement,
22074
22305
  element?.label,
22075
- elementsMap
22306
+ scene
22076
22307
  );
22077
22308
  elementStore.add(container);
22078
22309
  elementStore.add(text);
@@ -22096,7 +22327,7 @@ var convertToExcalidrawElements = (elementsSkeleton, opts) => {
22096
22327
  originalStart,
22097
22328
  originalEnd,
22098
22329
  elementStore,
22099
- elementsMap
22330
+ scene
22100
22331
  );
22101
22332
  container = linearElement;
22102
22333
  elementStore.add(linearElement);
@@ -22120,7 +22351,7 @@ var convertToExcalidrawElements = (elementsSkeleton, opts) => {
22120
22351
  start2,
22121
22352
  end,
22122
22353
  elementStore,
22123
- elementsMap
22354
+ scene
22124
22355
  );
22125
22356
  elementStore.add(linearElement);
22126
22357
  elementStore.add(startBoundElement);
@@ -22327,7 +22558,7 @@ var SCROLLBAR_MARGIN = 4;
22327
22558
  var SCROLLBAR_WIDTH = 6;
22328
22559
  var SCROLLBAR_COLOR = "rgba(0,0,0,0.3)";
22329
22560
  var getScrollBars = (elements, viewportWidth, viewportHeight, appState) => {
22330
- if (!elements.length) {
22561
+ if (!elements.size) {
22331
22562
  return {
22332
22563
  horizontal: null,
22333
22564
  vertical: null
@@ -22336,8 +22567,6 @@ var getScrollBars = (elements, viewportWidth, viewportHeight, appState) => {
22336
22567
  const [elementsMinX, elementsMinY, elementsMaxX, elementsMaxY] = getCommonBounds(elements);
22337
22568
  const viewportWidthWithZoom = viewportWidth / appState.zoom.value;
22338
22569
  const viewportHeightWithZoom = viewportHeight / appState.zoom.value;
22339
- const viewportWidthDiff = viewportWidth - viewportWidthWithZoom;
22340
- const viewportHeightDiff = viewportHeight - viewportHeightWithZoom;
22341
22570
  const safeArea = {
22342
22571
  top: parseInt(getGlobalCSSVariable("sat")) || 0,
22343
22572
  bottom: parseInt(getGlobalCSSVariable("sab")) || 0,
@@ -22345,26 +22574,38 @@ var getScrollBars = (elements, viewportWidth, viewportHeight, appState) => {
22345
22574
  right: parseInt(getGlobalCSSVariable("sar")) || 0
22346
22575
  };
22347
22576
  const isRTL = getLanguage().rtl;
22348
- const viewportMinX = -appState.scrollX + viewportWidthDiff / 2 + safeArea.left;
22349
- const viewportMinY = -appState.scrollY + viewportHeightDiff / 2 + safeArea.top;
22577
+ const viewportMinX = -appState.scrollX + safeArea.left;
22578
+ const viewportMinY = -appState.scrollY + safeArea.top;
22350
22579
  const viewportMaxX = viewportMinX + viewportWidthWithZoom - safeArea.right;
22351
22580
  const viewportMaxY = viewportMinY + viewportHeightWithZoom - safeArea.bottom;
22352
22581
  const sceneMinX = Math.min(elementsMinX, viewportMinX);
22353
22582
  const sceneMinY = Math.min(elementsMinY, viewportMinY);
22354
22583
  const sceneMaxX = Math.max(elementsMaxX, viewportMaxX);
22355
22584
  const sceneMaxY = Math.max(elementsMaxY, viewportMaxY);
22585
+ const sceneWidth = elementsMaxX - elementsMinX;
22586
+ const sceneHeight = elementsMaxY - elementsMinY;
22587
+ const extendedSceneWidth = sceneMaxX - sceneMinX;
22588
+ const extendedSceneHeight = sceneMaxY - sceneMinY;
22589
+ const scrollWidthOffset = Math.max(SCROLLBAR_MARGIN * 2, safeArea.left + safeArea.right) + SCROLLBAR_WIDTH * 2;
22590
+ const scrollbarWidth = viewportWidth * (viewportWidthWithZoom / extendedSceneWidth) - scrollWidthOffset;
22591
+ const scrollbarHeightOffset = Math.max(SCROLLBAR_MARGIN * 2, safeArea.top + safeArea.bottom) + SCROLLBAR_WIDTH * 2;
22592
+ const scrollbarHeight = viewportHeight * (viewportHeightWithZoom / extendedSceneHeight) - scrollbarHeightOffset;
22593
+ const horizontalDeltaMultiplier = extendedSceneWidth > sceneWidth ? extendedSceneWidth * appState.zoom.value / (scrollbarWidth + scrollWidthOffset) : viewportWidth / (scrollbarWidth + scrollWidthOffset);
22594
+ const verticalDeltaMultiplier = extendedSceneHeight > sceneHeight ? extendedSceneHeight * appState.zoom.value / (scrollbarHeight + scrollbarHeightOffset) : viewportHeight / (scrollbarHeight + scrollbarHeightOffset);
22356
22595
  return {
22357
22596
  horizontal: viewportMinX === sceneMinX && viewportMaxX === sceneMaxX ? null : {
22358
- x: Math.max(safeArea.left, SCROLLBAR_MARGIN) + (viewportMinX - sceneMinX) / (sceneMaxX - sceneMinX) * viewportWidth,
22597
+ x: Math.max(safeArea.left, SCROLLBAR_MARGIN) + SCROLLBAR_WIDTH + (viewportMinX - sceneMinX) / extendedSceneWidth * viewportWidth,
22359
22598
  y: viewportHeight - SCROLLBAR_WIDTH - Math.max(SCROLLBAR_MARGIN, safeArea.bottom),
22360
- width: (viewportMaxX - viewportMinX) / (sceneMaxX - sceneMinX) * viewportWidth - Math.max(SCROLLBAR_MARGIN * 2, safeArea.left + safeArea.right),
22361
- height: SCROLLBAR_WIDTH
22599
+ width: scrollbarWidth,
22600
+ height: SCROLLBAR_WIDTH,
22601
+ deltaMultiplier: horizontalDeltaMultiplier
22362
22602
  },
22363
22603
  vertical: viewportMinY === sceneMinY && viewportMaxY === sceneMaxY ? null : {
22364
22604
  x: isRTL ? Math.max(safeArea.left, SCROLLBAR_MARGIN) : viewportWidth - SCROLLBAR_WIDTH - Math.max(safeArea.right, SCROLLBAR_MARGIN),
22365
- y: (viewportMinY - sceneMinY) / (sceneMaxY - sceneMinY) * viewportHeight + Math.max(safeArea.top, SCROLLBAR_MARGIN),
22605
+ y: Math.max(safeArea.top, SCROLLBAR_MARGIN) + SCROLLBAR_WIDTH + (viewportMinY - sceneMinY) / extendedSceneHeight * viewportHeight,
22366
22606
  width: SCROLLBAR_WIDTH,
22367
- height: (viewportMaxY - viewportMinY) / (sceneMaxY - sceneMinY) * viewportHeight - Math.max(SCROLLBAR_MARGIN * 2, safeArea.top + safeArea.bottom)
22607
+ height: scrollbarHeight,
22608
+ deltaMultiplier: verticalDeltaMultiplier
22368
22609
  }
22369
22610
  };
22370
22611
  };
@@ -23166,7 +23407,7 @@ var _renderInteractiveScene = ({
23166
23407
  let scrollBars;
23167
23408
  if (renderConfig.renderScrollbars) {
23168
23409
  scrollBars = getScrollBars(
23169
- visibleElements,
23410
+ elementsMap,
23170
23411
  normalizedWidth,
23171
23412
  normalizedHeight,
23172
23413
  appState
@@ -23201,8 +23442,8 @@ var renderInteractiveSceneThrottled = throttleRAF(
23201
23442
  },
23202
23443
  { trailing: true }
23203
23444
  );
23204
- var renderInteractiveScene = (renderConfig, throttle5) => {
23205
- if (throttle5) {
23445
+ var renderInteractiveScene = (renderConfig, throttle6) => {
23446
+ if (throttle6) {
23206
23447
  renderInteractiveSceneThrottled(renderConfig);
23207
23448
  return void 0;
23208
23449
  }
@@ -23435,7 +23676,6 @@ var AnimatedTrail = class {
23435
23676
  this.update();
23436
23677
  }
23437
23678
  update() {
23438
- this.pastTrails = [];
23439
23679
  this.start();
23440
23680
  if (this.trailAnimation) {
23441
23681
  this.trailAnimation.setAttribute("begin", "indefinite");
@@ -23655,7 +23895,7 @@ var textWysiwyg = ({
23655
23895
  };
23656
23896
  const updateWysiwygStyle = () => {
23657
23897
  const appState = app.state;
23658
- const updatedTextElement = Scene_default.getScene(element)?.getElement(id);
23898
+ const updatedTextElement = app.scene.getElement(id);
23659
23899
  if (!updatedTextElement) {
23660
23900
  return;
23661
23901
  }
@@ -23711,7 +23951,7 @@ var textWysiwyg = ({
23711
23951
  height,
23712
23952
  container.type
23713
23953
  );
23714
- mutateElement(container, { height: targetContainerHeight });
23954
+ app.scene.mutateElement(container, { height: targetContainerHeight });
23715
23955
  return;
23716
23956
  } else if (
23717
23957
  // autoshrink container height until original container height
@@ -23722,7 +23962,7 @@ var textWysiwyg = ({
23722
23962
  height,
23723
23963
  container.type
23724
23964
  );
23725
- mutateElement(container, { height: targetContainerHeight });
23965
+ app.scene.mutateElement(container, { height: targetContainerHeight });
23726
23966
  } else {
23727
23967
  const { y } = computeBoundTextPosition(
23728
23968
  container,
@@ -23777,7 +24017,7 @@ var textWysiwyg = ({
23777
24017
  if (isTestEnv()) {
23778
24018
  editable.style.fontFamily = getFontFamilyString(updatedTextElement);
23779
24019
  }
23780
- mutateElement(updatedTextElement, { x: coordX, y: coordY });
24020
+ app.scene.mutateElement(updatedTextElement, { x: coordX, y: coordY });
23781
24021
  }
23782
24022
  };
23783
24023
  const editable = document.createElement("textarea");
@@ -23978,7 +24218,7 @@ var textWysiwyg = ({
23978
24218
  }
23979
24219
  isDestroyed = true;
23980
24220
  cleanup();
23981
- const updateElement = Scene_default.getScene(element)?.getElement(
24221
+ const updateElement = app.scene.getElement(
23982
24222
  element.id
23983
24223
  );
23984
24224
  if (!updateElement) {
@@ -23992,7 +24232,7 @@ var textWysiwyg = ({
23992
24232
  if (editable.value.trim()) {
23993
24233
  const boundTextElementId = getBoundTextElementId(container);
23994
24234
  if (!boundTextElementId || boundTextElementId !== element.id) {
23995
- mutateElement(container, {
24235
+ app.scene.mutateElement(container, {
23996
24236
  boundElements: (container.boundElements || []).concat({
23997
24237
  type: "text",
23998
24238
  id: element.id
@@ -24002,7 +24242,7 @@ var textWysiwyg = ({
24002
24242
  bumpVersion(container);
24003
24243
  }
24004
24244
  } else {
24005
- mutateElement(container, {
24245
+ app.scene.mutateElement(container, {
24006
24246
  boundElements: container.boundElements?.filter(
24007
24247
  (ele) => !isTextElement(
24008
24248
  ele
@@ -24010,11 +24250,7 @@ var textWysiwyg = ({
24010
24250
  )
24011
24251
  });
24012
24252
  }
24013
- redrawTextBoundingBox(
24014
- updateElement,
24015
- container,
24016
- app.scene.getNonDeletedElementsMap()
24017
- );
24253
+ redrawTextBoundingBox(updateElement, container, app.scene);
24018
24254
  }
24019
24255
  onSubmit({
24020
24256
  viaKeyboard: submittedViaKeyboard,
@@ -24285,7 +24521,8 @@ var LassoTrail = class extends AnimatedTrail {
24285
24521
  selectedLinearElement: selectedIds.length === 1 && !selectedGroupIds.length && isLinearElement(this.app.scene.getNonDeletedElement(selectedIds[0])) ? new LinearElementEditor(
24286
24522
  this.app.scene.getNonDeletedElement(
24287
24523
  selectedIds[0]
24288
- )
24524
+ ),
24525
+ this.app.scene.getNonDeletedElementsMap()
24289
24526
  ) : null
24290
24527
  };
24291
24528
  });
@@ -24341,6 +24578,161 @@ var LassoTrail = class extends AnimatedTrail {
24341
24578
  }
24342
24579
  };
24343
24580
 
24581
+ // eraser/index.ts
24582
+ var POINTS_ON_TRAIL = 2;
24583
+ var EraserTrail = class extends AnimatedTrail {
24584
+ constructor(animationFrameHandler, app) {
24585
+ super(animationFrameHandler, app, {
24586
+ streamline: 0.2,
24587
+ size: 5,
24588
+ keepHead: true,
24589
+ sizeMapping: (c) => {
24590
+ const DECAY_TIME = 200;
24591
+ const DECAY_LENGTH = 10;
24592
+ const t2 = Math.max(
24593
+ 0,
24594
+ 1 - (performance.now() - c.pressure) / DECAY_TIME
24595
+ );
24596
+ const l = (DECAY_LENGTH - Math.min(DECAY_LENGTH, c.totalLength - c.currentIndex)) / DECAY_LENGTH;
24597
+ return Math.min(easeOut(l), easeOut(t2));
24598
+ },
24599
+ fill: () => app.state.theme === THEME.LIGHT ? "rgba(0, 0, 0, 0.2)" : "rgba(255, 255, 255, 0.2)"
24600
+ });
24601
+ __publicField(this, "elementsToErase", /* @__PURE__ */ new Set());
24602
+ __publicField(this, "groupsToErase", /* @__PURE__ */ new Set());
24603
+ __publicField(this, "segmentsCache", /* @__PURE__ */ new Map());
24604
+ __publicField(this, "geometricShapesCache", /* @__PURE__ */ new Map());
24605
+ }
24606
+ startPath(x, y) {
24607
+ this.endPath();
24608
+ super.startPath(x, y);
24609
+ this.elementsToErase.clear();
24610
+ }
24611
+ addPointToPath(x, y, restore2 = false) {
24612
+ super.addPointToPath(x, y);
24613
+ const elementsToEraser = this.updateElementsToBeErased(restore2);
24614
+ return elementsToEraser;
24615
+ }
24616
+ updateElementsToBeErased(restoreToErase) {
24617
+ let eraserPath = super.getCurrentTrail()?.originalPoints?.map((p) => pointFrom(p[0], p[1])) || [];
24618
+ eraserPath = eraserPath?.slice(eraserPath.length - POINTS_ON_TRAIL);
24619
+ const candidateElements = this.app.visibleElements.filter(
24620
+ (el) => !el.locked
24621
+ );
24622
+ const candidateElementsMap = arrayToMap(candidateElements);
24623
+ const pathSegments = eraserPath.reduce((acc, point, index) => {
24624
+ if (index === 0) {
24625
+ return acc;
24626
+ }
24627
+ acc.push(lineSegment(eraserPath[index - 1], point));
24628
+ return acc;
24629
+ }, []);
24630
+ if (pathSegments.length === 0) {
24631
+ return [];
24632
+ }
24633
+ for (const element of candidateElements) {
24634
+ if (restoreToErase && this.elementsToErase.has(element.id)) {
24635
+ const intersects = eraserTest(
24636
+ pathSegments,
24637
+ element,
24638
+ this.segmentsCache,
24639
+ this.geometricShapesCache,
24640
+ candidateElementsMap,
24641
+ this.app
24642
+ );
24643
+ if (intersects) {
24644
+ const shallowestGroupId = element.groupIds.at(-1);
24645
+ if (this.groupsToErase.has(shallowestGroupId)) {
24646
+ const elementsInGroup = getElementsInGroup(
24647
+ this.app.scene.getNonDeletedElementsMap(),
24648
+ shallowestGroupId
24649
+ );
24650
+ for (const elementInGroup of elementsInGroup) {
24651
+ this.elementsToErase.delete(elementInGroup.id);
24652
+ }
24653
+ this.groupsToErase.delete(shallowestGroupId);
24654
+ }
24655
+ if (isBoundToContainer(element)) {
24656
+ this.elementsToErase.delete(element.containerId);
24657
+ }
24658
+ if (hasBoundTextElement(element)) {
24659
+ const boundText = getBoundTextElementId(element);
24660
+ if (boundText) {
24661
+ this.elementsToErase.delete(boundText);
24662
+ }
24663
+ }
24664
+ this.elementsToErase.delete(element.id);
24665
+ }
24666
+ } else if (!restoreToErase && !this.elementsToErase.has(element.id)) {
24667
+ const intersects = eraserTest(
24668
+ pathSegments,
24669
+ element,
24670
+ this.segmentsCache,
24671
+ this.geometricShapesCache,
24672
+ candidateElementsMap,
24673
+ this.app
24674
+ );
24675
+ if (intersects) {
24676
+ const shallowestGroupId = element.groupIds.at(-1);
24677
+ if (!this.groupsToErase.has(shallowestGroupId)) {
24678
+ const elementsInGroup = getElementsInGroup(
24679
+ this.app.scene.getNonDeletedElementsMap(),
24680
+ shallowestGroupId
24681
+ );
24682
+ for (const elementInGroup of elementsInGroup) {
24683
+ this.elementsToErase.add(elementInGroup.id);
24684
+ }
24685
+ this.groupsToErase.add(shallowestGroupId);
24686
+ }
24687
+ if (hasBoundTextElement(element)) {
24688
+ const boundText = getBoundTextElementId(element);
24689
+ if (boundText) {
24690
+ this.elementsToErase.add(boundText);
24691
+ }
24692
+ }
24693
+ if (isBoundToContainer(element)) {
24694
+ this.elementsToErase.add(element.containerId);
24695
+ }
24696
+ this.elementsToErase.add(element.id);
24697
+ }
24698
+ }
24699
+ }
24700
+ return Array.from(this.elementsToErase);
24701
+ }
24702
+ endPath() {
24703
+ super.endPath();
24704
+ super.clearTrails();
24705
+ this.elementsToErase.clear();
24706
+ this.groupsToErase.clear();
24707
+ this.segmentsCache.clear();
24708
+ }
24709
+ };
24710
+ var eraserTest = (pathSegments, element, elementsSegments, shapesCache, elementsMap, app) => {
24711
+ let shape = shapesCache.get(element.id);
24712
+ if (!shape) {
24713
+ shape = getElementShape(element, elementsMap);
24714
+ shapesCache.set(element.id, shape);
24715
+ }
24716
+ const lastPoint = pathSegments[pathSegments.length - 1][1];
24717
+ if (shouldTestInside(element) && isPointInShape(lastPoint, shape)) {
24718
+ return true;
24719
+ }
24720
+ let elementSegments = elementsSegments.get(element.id);
24721
+ if (!elementSegments) {
24722
+ elementSegments = getElementLineSegments(element, elementsMap);
24723
+ elementsSegments.set(element.id, elementSegments);
24724
+ }
24725
+ return pathSegments.some(
24726
+ (pathSegment) => elementSegments?.some(
24727
+ (elementSegment) => lineSegmentIntersectionPoints(
24728
+ pathSegment,
24729
+ elementSegment,
24730
+ app.getElementHitThreshold()
24731
+ ) !== null
24732
+ )
24733
+ );
24734
+ };
24735
+
24344
24736
  // components/BraveMeasureTextError.tsx
24345
24737
  import { jsx as jsx77, jsxs as jsxs42 } from "react/jsx-runtime";
24346
24738
  var BraveMeasureTextError = () => {
@@ -27754,7 +28146,7 @@ TTD mermaid definition render errror: ${error3.message}`,
27754
28146
 
27755
28147
  // components/Stats/index.tsx
27756
28148
  import clsx51 from "clsx";
27757
- import throttle2 from "lodash.throttle";
28149
+ import throttle3 from "lodash.throttle";
27758
28150
  import { useEffect as useEffect36, useMemo as useMemo9, useState as useState31, memo as memo5 } from "react";
27759
28151
 
27760
28152
  // components/Stats/DragInput.tsx
@@ -27785,7 +28177,8 @@ var getElementsInAtomicUnit = (atomicUnit, elementsMap, originalElementsMap) =>
27785
28177
  latest: elementsMap.get(id)
27786
28178
  })).filter((el) => el.original !== void 0 && el.latest !== void 0);
27787
28179
  };
27788
- var moveElement = (newTopLeftX, newTopLeftY, originalElement, elementsMap, elements, scene, originalElementsMap, shouldInformMutation = true) => {
28180
+ var moveElement = (newTopLeftX, newTopLeftY, originalElement, scene, originalElementsMap, shouldInformMutation = true) => {
28181
+ const elementsMap = scene.getNonDeletedElementsMap();
27789
28182
  const latestElement = elementsMap.get(originalElement.id);
27790
28183
  if (!latestElement) {
27791
28184
  return;
@@ -27806,28 +28199,28 @@ var moveElement = (newTopLeftX, newTopLeftY, originalElement, elementsMap, eleme
27806
28199
  pointFrom(cx + changeInX, cy + changeInY),
27807
28200
  -originalElement.angle
27808
28201
  );
27809
- mutateElement(
28202
+ scene.mutateElement(
27810
28203
  latestElement,
27811
28204
  {
27812
28205
  x,
27813
28206
  y
27814
28207
  },
27815
- shouldInformMutation
28208
+ { informMutation: shouldInformMutation, isDragging: false }
27816
28209
  );
27817
- updateBindings(latestElement, elementsMap, elements, scene);
28210
+ updateBindings(latestElement, scene);
27818
28211
  const boundTextElement = getBoundTextElement(
27819
28212
  originalElement,
27820
28213
  originalElementsMap
27821
28214
  );
27822
28215
  if (boundTextElement) {
27823
28216
  const latestBoundTextElement = elementsMap.get(boundTextElement.id);
27824
- latestBoundTextElement && mutateElement(
28217
+ latestBoundTextElement && scene.mutateElement(
27825
28218
  latestBoundTextElement,
27826
28219
  {
27827
28220
  x: boundTextElement.x + changeInX,
27828
28221
  y: boundTextElement.y + changeInY
27829
28222
  },
27830
- shouldInformMutation
28223
+ { informMutation: shouldInformMutation, isDragging: false }
27831
28224
  );
27832
28225
  }
27833
28226
  };
@@ -27846,19 +28239,11 @@ var getAtomicUnits = (targetElements, appState) => {
27846
28239
  });
27847
28240
  return _atomicUnits;
27848
28241
  };
27849
- var updateBindings = (latestElement, elementsMap, elements, scene, options) => {
28242
+ var updateBindings = (latestElement, scene, options) => {
27850
28243
  if (isLinearElement(latestElement)) {
27851
- bindOrUnbindLinearElements(
27852
- [latestElement],
27853
- elementsMap,
27854
- elements,
27855
- scene,
27856
- true,
27857
- [],
27858
- options?.zoom
27859
- );
28244
+ bindOrUnbindLinearElements([latestElement], true, [], scene, options?.zoom);
27860
28245
  } else {
27861
- updateBoundElements(latestElement, elementsMap, options);
28246
+ updateBoundElements(latestElement, scene, options);
27862
28247
  }
27863
28248
  };
27864
28249
 
@@ -28110,7 +28495,6 @@ var handleDegreeChange = ({
28110
28495
  scene
28111
28496
  }) => {
28112
28497
  const elementsMap = scene.getNonDeletedElementsMap();
28113
- const elements = scene.getNonDeletedElements();
28114
28498
  const origElement = originalElements[0];
28115
28499
  if (origElement && !isElbowArrow(origElement)) {
28116
28500
  const latestElement = elementsMap.get(origElement.id);
@@ -28119,13 +28503,13 @@ var handleDegreeChange = ({
28119
28503
  }
28120
28504
  if (nextValue !== void 0) {
28121
28505
  const nextAngle2 = degreesToRadians(nextValue);
28122
- mutateElement(latestElement, {
28506
+ scene.mutateElement(latestElement, {
28123
28507
  angle: nextAngle2
28124
28508
  });
28125
- updateBindings(latestElement, elementsMap, elements, scene);
28509
+ updateBindings(latestElement, scene);
28126
28510
  const boundTextElement2 = getBoundTextElement(latestElement, elementsMap);
28127
28511
  if (boundTextElement2 && !isArrowElement(latestElement)) {
28128
- mutateElement(boundTextElement2, { angle: nextAngle2 });
28512
+ scene.mutateElement(boundTextElement2, { angle: nextAngle2 });
28129
28513
  }
28130
28514
  return;
28131
28515
  }
@@ -28137,13 +28521,13 @@ var handleDegreeChange = ({
28137
28521
  }
28138
28522
  nextAngleInDegrees = nextAngleInDegrees < 0 ? nextAngleInDegrees + 360 : nextAngleInDegrees;
28139
28523
  const nextAngle = degreesToRadians(nextAngleInDegrees);
28140
- mutateElement(latestElement, {
28524
+ scene.mutateElement(latestElement, {
28141
28525
  angle: nextAngle
28142
28526
  });
28143
- updateBindings(latestElement, elementsMap, elements, scene);
28527
+ updateBindings(latestElement, scene);
28144
28528
  const boundTextElement = getBoundTextElement(latestElement, elementsMap);
28145
28529
  if (boundTextElement && !isArrowElement(latestElement)) {
28146
- mutateElement(boundTextElement, { angle: nextAngle });
28530
+ scene.mutateElement(boundTextElement, { angle: nextAngle });
28147
28531
  }
28148
28532
  }
28149
28533
  };
@@ -28282,7 +28666,7 @@ var handleDimensionChange = ({
28282
28666
  y: isFlippedByY ? crop.y + crop.height - nextCropHeight2 : crop.y
28283
28667
  };
28284
28668
  }
28285
- mutateElement(element, {
28669
+ scene.mutateElement(element, {
28286
28670
  crop: nextCrop,
28287
28671
  width: nextCrop.width / (crop.naturalWidth / uncroppedWidth),
28288
28672
  height: nextCrop.height / (crop.naturalHeight / uncroppedHeight)
@@ -28308,7 +28692,7 @@ var handleDimensionChange = ({
28308
28692
  width: nextCropWidth,
28309
28693
  height: nextCropHeight
28310
28694
  };
28311
- mutateElement(element, {
28695
+ scene.mutateElement(element, {
28312
28696
  crop: nextCrop,
28313
28697
  width: nextCrop.width / (crop.naturalWidth / uncroppedWidth),
28314
28698
  height: nextCrop.height / (crop.naturalHeight / uncroppedHeight)
@@ -28329,8 +28713,8 @@ var handleDimensionChange = ({
28329
28713
  nextHeight2,
28330
28714
  latestElement,
28331
28715
  origElement,
28332
- elementsMap,
28333
28716
  originalElementsMap,
28717
+ scene,
28334
28718
  property === "width" ? "e" : "s",
28335
28719
  {
28336
28720
  shouldMaintainAspectRatio: keepAspectRatio
@@ -28370,8 +28754,8 @@ var handleDimensionChange = ({
28370
28754
  nextHeight,
28371
28755
  latestElement,
28372
28756
  origElement,
28373
- elementsMap,
28374
28757
  originalElementsMap,
28758
+ scene,
28375
28759
  property === "width" ? "e" : "s",
28376
28760
  {
28377
28761
  shouldMaintainAspectRatio: keepAspectRatio
@@ -28446,13 +28830,13 @@ var handleFontSizeChange = ({
28446
28830
  }
28447
28831
  }
28448
28832
  if (nextFontSize) {
28449
- mutateElement(latestElement, {
28833
+ scene.mutateElement(latestElement, {
28450
28834
  fontSize: nextFontSize
28451
28835
  });
28452
28836
  redrawTextBoundingBox(
28453
28837
  latestElement,
28454
28838
  scene.getContainerElement(latestElement),
28455
- scene.getNonDeletedElementsMap()
28839
+ scene
28456
28840
  );
28457
28841
  }
28458
28842
  }
@@ -28500,16 +28884,12 @@ var handleDegreeChange2 = ({
28500
28884
  if (!element) {
28501
28885
  continue;
28502
28886
  }
28503
- mutateElement(
28504
- element,
28505
- {
28506
- angle: nextAngle
28507
- },
28508
- false
28509
- );
28887
+ scene.mutateElement(element, {
28888
+ angle: nextAngle
28889
+ });
28510
28890
  const boundTextElement = getBoundTextElement(element, elementsMap);
28511
28891
  if (boundTextElement && !isArrowElement(element)) {
28512
- mutateElement(boundTextElement, { angle: nextAngle }, false);
28892
+ scene.mutateElement(boundTextElement, { angle: nextAngle });
28513
28893
  }
28514
28894
  }
28515
28895
  scene.triggerUpdate();
@@ -28529,16 +28909,12 @@ var handleDegreeChange2 = ({
28529
28909
  }
28530
28910
  nextAngleInDegrees = nextAngleInDegrees < 0 ? nextAngleInDegrees + 360 : nextAngleInDegrees;
28531
28911
  const nextAngle = degreesToRadians(nextAngleInDegrees);
28532
- mutateElement(
28533
- latestElement,
28534
- {
28535
- angle: nextAngle
28536
- },
28537
- false
28538
- );
28912
+ scene.mutateElement(latestElement, {
28913
+ angle: nextAngle
28914
+ });
28539
28915
  const boundTextElement = getBoundTextElement(latestElement, elementsMap);
28540
28916
  if (boundTextElement && !isArrowElement(latestElement)) {
28541
- mutateElement(boundTextElement, { angle: nextAngle }, false);
28917
+ scene.mutateElement(boundTextElement, { angle: nextAngle });
28542
28918
  }
28543
28919
  }
28544
28920
  scene.triggerUpdate();
@@ -28596,37 +28972,34 @@ var getResizedUpdates = (anchorX, anchorY, scale, origElement) => {
28596
28972
  ...isTextElement(origElement) ? { fontSize: origElement.fontSize * scale } : {}
28597
28973
  };
28598
28974
  };
28599
- var resizeElementInGroup = (anchorX, anchorY, property, scale, latestElement, origElement, elementsMap, originalElementsMap) => {
28975
+ var resizeElementInGroup = (anchorX, anchorY, property, scale, latestElement, origElement, originalElementsMap, scene) => {
28976
+ const elementsMap = scene.getNonDeletedElementsMap();
28600
28977
  const updates = getResizedUpdates(anchorX, anchorY, scale, origElement);
28601
- mutateElement(latestElement, updates, false);
28978
+ scene.mutateElement(latestElement, updates);
28602
28979
  const boundTextElement = getBoundTextElement(
28603
28980
  origElement,
28604
28981
  originalElementsMap
28605
28982
  );
28606
28983
  if (boundTextElement) {
28607
28984
  const newFontSize = boundTextElement.fontSize * scale;
28608
- updateBoundElements(latestElement, elementsMap, {
28985
+ updateBoundElements(latestElement, scene, {
28609
28986
  newSize: { width: updates.width, height: updates.height }
28610
28987
  });
28611
28988
  const latestBoundTextElement = elementsMap.get(boundTextElement.id);
28612
28989
  if (latestBoundTextElement && isTextElement(latestBoundTextElement)) {
28613
- mutateElement(
28614
- latestBoundTextElement,
28615
- {
28616
- fontSize: newFontSize
28617
- },
28618
- false
28619
- );
28990
+ scene.mutateElement(latestBoundTextElement, {
28991
+ fontSize: newFontSize
28992
+ });
28620
28993
  handleBindTextResize(
28621
28994
  latestElement,
28622
- elementsMap,
28995
+ scene,
28623
28996
  property === "width" ? "e" : "s",
28624
28997
  true
28625
28998
  );
28626
28999
  }
28627
29000
  }
28628
29001
  };
28629
- var resizeGroup = (nextWidth, nextHeight, initialHeight, aspectRatio, anchor, property, latestElements, originalElements, elementsMap, originalElementsMap) => {
29002
+ var resizeGroup = (nextWidth, nextHeight, initialHeight, aspectRatio, anchor, property, latestElements, originalElements, originalElementsMap, scene) => {
28630
29003
  if (property === "width") {
28631
29004
  nextHeight = Math.round(nextWidth / aspectRatio * 100) / 100;
28632
29005
  } else {
@@ -28643,8 +29016,8 @@ var resizeGroup = (nextWidth, nextHeight, initialHeight, aspectRatio, anchor, pr
28643
29016
  scale,
28644
29017
  latestElement,
28645
29018
  origElement,
28646
- elementsMap,
28647
- originalElementsMap
29019
+ originalElementsMap,
29020
+ scene
28648
29021
  );
28649
29022
  }
28650
29023
  };
@@ -28691,8 +29064,8 @@ var handleDimensionChange2 = ({
28691
29064
  property,
28692
29065
  latestElements,
28693
29066
  originalElements2,
28694
- elementsMap,
28695
- originalElementsMap
29067
+ originalElementsMap,
29068
+ scene
28696
29069
  );
28697
29070
  } else {
28698
29071
  const [el] = elementsInUnit;
@@ -28722,8 +29095,8 @@ var handleDimensionChange2 = ({
28722
29095
  nextHeight,
28723
29096
  latestElement,
28724
29097
  origElement,
28725
- elementsMap,
28726
29098
  originalElementsMap,
29099
+ scene,
28727
29100
  property === "width" ? "e" : "s",
28728
29101
  {
28729
29102
  shouldInformMutation: false
@@ -28777,8 +29150,8 @@ var handleDimensionChange2 = ({
28777
29150
  property,
28778
29151
  latestElements,
28779
29152
  originalElements2,
28780
- elementsMap,
28781
- originalElementsMap
29153
+ originalElementsMap,
29154
+ scene
28782
29155
  );
28783
29156
  } else {
28784
29157
  const [el] = elementsInUnit;
@@ -28808,8 +29181,8 @@ var handleDimensionChange2 = ({
28808
29181
  nextHeight,
28809
29182
  latestElement,
28810
29183
  origElement,
28811
- elementsMap,
28812
29184
  originalElementsMap,
29185
+ scene,
28813
29186
  property === "width" ? "e" : "s",
28814
29187
  {
28815
29188
  shouldInformMutation: false
@@ -28901,18 +29274,13 @@ var handleFontSizeChange2 = ({
28901
29274
  if (nextValue) {
28902
29275
  nextFontSize = Math.max(Math.round(nextValue), MIN_FONT_SIZE3);
28903
29276
  for (const textElement of latestTextElements) {
28904
- mutateElement(
28905
- textElement,
28906
- {
28907
- fontSize: nextFontSize
28908
- },
28909
- false
28910
- );
29277
+ scene.mutateElement(textElement, {
29278
+ fontSize: nextFontSize
29279
+ });
28911
29280
  redrawTextBoundingBox(
28912
29281
  textElement,
28913
29282
  scene.getContainerElement(textElement),
28914
- elementsMap,
28915
- false
29283
+ scene
28916
29284
  );
28917
29285
  }
28918
29286
  scene.triggerUpdate();
@@ -28930,18 +29298,13 @@ var handleFontSizeChange2 = ({
28930
29298
  if (shouldChangeByStepSize) {
28931
29299
  nextFontSize2 = getStepSizedValue(nextFontSize2, STEP_SIZE7);
28932
29300
  }
28933
- mutateElement(
28934
- latestElement,
28935
- {
28936
- fontSize: nextFontSize2
28937
- },
28938
- false
28939
- );
29301
+ scene.mutateElement(latestElement, {
29302
+ fontSize: nextFontSize2
29303
+ });
28940
29304
  redrawTextBoundingBox(
28941
29305
  latestElement,
28942
29306
  scene.getContainerElement(latestElement),
28943
- elementsMap,
28944
- false
29307
+ scene
28945
29308
  );
28946
29309
  }
28947
29310
  scene.triggerUpdate();
@@ -28984,8 +29347,8 @@ var MultiFontSize_default = MultiFontSize;
28984
29347
  import { useMemo as useMemo8 } from "react";
28985
29348
  import { jsx as jsx129 } from "react/jsx-runtime";
28986
29349
  var STEP_SIZE8 = 10;
28987
- var moveElements = (property, changeInTopX, changeInTopY, elements, originalElements, elementsMap, originalElementsMap, scene) => {
28988
- for (let i = 0; i < elements.length; i++) {
29350
+ var moveElements = (property, changeInTopX, changeInTopY, originalElements, originalElementsMap, scene) => {
29351
+ for (let i = 0; i < originalElements.length; i++) {
28989
29352
  const origElement = originalElements[i];
28990
29353
  const [cx, cy] = [
28991
29354
  origElement.x + origElement.width / 2,
@@ -29002,15 +29365,14 @@ var moveElements = (property, changeInTopX, changeInTopY, elements, originalElem
29002
29365
  newTopLeftX,
29003
29366
  newTopLeftY,
29004
29367
  origElement,
29005
- elementsMap,
29006
- elements,
29007
29368
  scene,
29008
29369
  originalElementsMap,
29009
29370
  false
29010
29371
  );
29011
29372
  }
29012
29373
  };
29013
- var moveGroupTo = (nextX, nextY, originalElements, elementsMap, elements, originalElementsMap, scene) => {
29374
+ var moveGroupTo = (nextX, nextY, originalElements, originalElementsMap, scene) => {
29375
+ const elementsMap = scene.getNonDeletedElementsMap();
29014
29376
  const [x1, y1, ,] = getCommonBounds(originalElements);
29015
29377
  const offsetX = nextX - x1;
29016
29378
  const offsetY = nextY - y1;
@@ -29034,8 +29396,6 @@ var moveGroupTo = (nextX, nextY, originalElements, elementsMap, elements, origin
29034
29396
  topLeftX + offsetX,
29035
29397
  topLeftY + offsetY,
29036
29398
  origElement,
29037
- elementsMap,
29038
- elements,
29039
29399
  scene,
29040
29400
  originalElementsMap,
29041
29401
  false
@@ -29054,7 +29414,6 @@ var handlePositionChange = ({
29054
29414
  originalAppState
29055
29415
  }) => {
29056
29416
  const elementsMap = scene.getNonDeletedElementsMap();
29057
- const elements = scene.getNonDeletedElements();
29058
29417
  if (nextValue !== void 0) {
29059
29418
  for (const atomicUnit of getAtomicUnits(
29060
29419
  originalElements,
@@ -29075,8 +29434,6 @@ var handlePositionChange = ({
29075
29434
  newTopLeftX,
29076
29435
  newTopLeftY,
29077
29436
  elementsInUnit.map((el) => el.original),
29078
- elementsMap,
29079
- elements,
29080
29437
  originalElementsMap,
29081
29438
  scene
29082
29439
  );
@@ -29099,8 +29456,6 @@ var handlePositionChange = ({
29099
29456
  newTopLeftX,
29100
29457
  newTopLeftY,
29101
29458
  origElement,
29102
- elementsMap,
29103
- elements,
29104
29459
  scene,
29105
29460
  originalElementsMap,
29106
29461
  false
@@ -29119,8 +29474,6 @@ var handlePositionChange = ({
29119
29474
  changeInTopX,
29120
29475
  changeInTopY,
29121
29476
  originalElements,
29122
- originalElements,
29123
- elementsMap,
29124
29477
  originalElementsMap,
29125
29478
  scene
29126
29479
  );
@@ -29183,7 +29536,6 @@ var handlePositionChange2 = ({
29183
29536
  originalAppState
29184
29537
  }) => {
29185
29538
  const elementsMap = scene.getNonDeletedElementsMap();
29186
- const elements = scene.getNonDeletedElements();
29187
29539
  const origElement = originalElements[0];
29188
29540
  const [cx, cy] = [
29189
29541
  origElement.x + origElement.width / 2,
@@ -29237,7 +29589,7 @@ var handlePositionChange2 = ({
29237
29589
  )
29238
29590
  };
29239
29591
  }
29240
- mutateElement(element, {
29592
+ scene.mutateElement(element, {
29241
29593
  crop: nextCrop
29242
29594
  });
29243
29595
  return;
@@ -29249,7 +29601,7 @@ var handlePositionChange2 = ({
29249
29601
  x: clamp(crop.x + changeInX, 0, crop.naturalWidth - crop.width),
29250
29602
  y: clamp(crop.y + changeInY, 0, crop.naturalHeight - crop.height)
29251
29603
  };
29252
- mutateElement(element, {
29604
+ scene.mutateElement(element, {
29253
29605
  crop: nextCrop
29254
29606
  });
29255
29607
  return;
@@ -29261,8 +29613,6 @@ var handlePositionChange2 = ({
29261
29613
  newTopLeftX2,
29262
29614
  newTopLeftY2,
29263
29615
  origElement,
29264
- elementsMap,
29265
- elements,
29266
29616
  scene,
29267
29617
  originalElementsMap
29268
29618
  );
@@ -29280,8 +29630,6 @@ var handlePositionChange2 = ({
29280
29630
  newTopLeftX,
29281
29631
  newTopLeftY,
29282
29632
  origElement,
29283
- elementsMap,
29284
- elements,
29285
29633
  scene,
29286
29634
  originalElementsMap
29287
29635
  );
@@ -29396,7 +29744,7 @@ var StatsInner = memo5(
29396
29744
  height: 0
29397
29745
  });
29398
29746
  const throttledSetSceneDimension = useMemo9(
29399
- () => throttle2((elements2) => {
29747
+ () => throttle3((elements2) => {
29400
29748
  const boundingBox = getCommonBounds(elements2);
29401
29749
  setSceneDimension({
29402
29750
  width: Math.round(boundingBox[2]) - Math.round(boundingBox[0]),
@@ -29653,10 +30001,11 @@ import { jsx as jsx132, jsxs as jsxs71 } from "react/jsx-runtime";
29653
30001
  var ElementLinkDialog = ({
29654
30002
  sourceElementId,
29655
30003
  onClose,
29656
- elementsMap,
29657
30004
  appState,
30005
+ scene,
29658
30006
  generateLinkForSelection = defaultGetElementLinkFromSelection
29659
30007
  }) => {
30008
+ const elementsMap = scene.getNonDeletedElementsMap();
29660
30009
  const originalLink = elementsMap.get(sourceElementId)?.link ?? null;
29661
30010
  const [nextLink, setNextLink] = useState32(originalLink);
29662
30011
  const [linkEdited, setLinkEdited] = useState32(false);
@@ -29685,18 +30034,18 @@ var ElementLinkDialog = ({
29685
30034
  const handleConfirm = useCallback12(() => {
29686
30035
  if (nextLink && nextLink !== elementsMap.get(sourceElementId)?.link) {
29687
30036
  const elementToLink = elementsMap.get(sourceElementId);
29688
- elementToLink && mutateElement(elementToLink, {
30037
+ elementToLink && scene.mutateElement(elementToLink, {
29689
30038
  link: nextLink
29690
30039
  });
29691
30040
  }
29692
30041
  if (!nextLink && linkEdited && sourceElementId) {
29693
30042
  const elementToLink = elementsMap.get(sourceElementId);
29694
- elementToLink && mutateElement(elementToLink, {
30043
+ elementToLink && scene.mutateElement(elementToLink, {
29695
30044
  link: null
29696
30045
  });
29697
30046
  }
29698
30047
  onClose?.();
29699
- }, [sourceElementId, nextLink, elementsMap, linkEdited, onClose]);
30048
+ }, [sourceElementId, nextLink, elementsMap, linkEdited, scene, onClose]);
29700
30049
  useEffect37(() => {
29701
30050
  const handleKeyDown = (event) => {
29702
30051
  if (appState.openDialog?.name === "elementLinkSelector" && event.key === KEYS.ENTER) {
@@ -31338,16 +31687,12 @@ var LayerUI = ({
31338
31687
  }
31339
31688
  if (selectedElements.length) {
31340
31689
  for (const element of selectedElements) {
31341
- mutateElement(
31342
- element,
31343
- {
31344
- [altKey && eyeDropperState.swapPreviewOnAlt ? colorPickerType === "elementBackground" ? "strokeColor" : "backgroundColor" : colorPickerType === "elementBackground" ? "backgroundColor" : "strokeColor"]: color
31345
- },
31346
- false
31347
- );
31690
+ mutateElement(element, arrayToMap(elements), {
31691
+ [altKey && eyeDropperState.swapPreviewOnAlt ? colorPickerType === "elementBackground" ? "strokeColor" : "backgroundColor" : colorPickerType === "elementBackground" ? "backgroundColor" : "strokeColor"]: color
31692
+ });
31348
31693
  ShapeCache.delete(element);
31349
31694
  }
31350
- Scene_default.getScene(selectedElements[0])?.triggerUpdate();
31695
+ app.scene.triggerUpdate();
31351
31696
  } else if (colorPickerType === "elementBackground") {
31352
31697
  setAppState({
31353
31698
  currentItemBackgroundColor: color
@@ -31382,7 +31727,7 @@ var LayerUI = ({
31382
31727
  openDialog: null
31383
31728
  });
31384
31729
  },
31385
- elementsMap: app.scene.getNonDeletedElementsMap(),
31730
+ scene: app.scene,
31386
31731
  appState,
31387
31732
  generateLinkForSelection
31388
31733
  }
@@ -31608,7 +31953,7 @@ var InteractiveCanvas = (props) => {
31608
31953
  remotePointerUsernames,
31609
31954
  remotePointerUserStates,
31610
31955
  selectionColor,
31611
- renderScrollbars: false
31956
+ renderScrollbars: props.renderScrollbars
31612
31957
  },
31613
31958
  device: props.device,
31614
31959
  callback: props.renderInteractiveSceneCallback
@@ -31677,7 +32022,7 @@ var areEqual3 = (prevProps, nextProps) => {
31677
32022
  if (prevProps.selectionNonce !== nextProps.selectionNonce || prevProps.sceneNonce !== nextProps.sceneNonce || prevProps.scale !== nextProps.scale || // we need to memoize on elementsMap because they may have renewed
31678
32023
  // even if sceneNonce didn't change (e.g. we filter elements out based
31679
32024
  // on appState)
31680
- prevProps.elementsMap !== nextProps.elementsMap || prevProps.visibleElements !== nextProps.visibleElements || prevProps.selectedElements !== nextProps.selectedElements) {
32025
+ prevProps.elementsMap !== nextProps.elementsMap || prevProps.visibleElements !== nextProps.visibleElements || prevProps.selectedElements !== nextProps.selectedElements || prevProps.renderScrollbars !== nextProps.renderScrollbars) {
31681
32026
  return false;
31682
32027
  }
31683
32028
  return isShallowEqual(
@@ -31831,8 +32176,8 @@ var renderNewElementSceneThrottled = throttleRAF(
31831
32176
  },
31832
32177
  { trailing: true }
31833
32178
  );
31834
- var renderNewElementScene = (renderConfig, throttle5) => {
31835
- if (throttle5) {
32179
+ var renderNewElementScene = (renderConfig, throttle6) => {
32180
+ if (throttle6) {
31836
32181
  renderNewElementSceneThrottled(renderConfig);
31837
32182
  return;
31838
32183
  }
@@ -32040,19 +32385,7 @@ var App = class _App extends React43.Component {
32040
32385
  __publicField(this, "lastViewportPosition", { x: 0, y: 0 });
32041
32386
  __publicField(this, "animationFrameHandler", new AnimationFrameHandler());
32042
32387
  __publicField(this, "laserTrails", new LaserTrails(this.animationFrameHandler, this));
32043
- __publicField(this, "eraserTrail", new AnimatedTrail(this.animationFrameHandler, this, {
32044
- streamline: 0.2,
32045
- size: 5,
32046
- keepHead: true,
32047
- sizeMapping: (c) => {
32048
- const DECAY_TIME = 200;
32049
- const DECAY_LENGTH = 10;
32050
- const t2 = Math.max(0, 1 - (performance.now() - c.pressure) / DECAY_TIME);
32051
- const l = (DECAY_LENGTH - Math.min(DECAY_LENGTH, c.totalLength - c.currentIndex)) / DECAY_LENGTH;
32052
- return Math.min(easeOut(l), easeOut(t2));
32053
- },
32054
- fill: () => this.state.theme === THEME.LIGHT ? "rgba(0, 0, 0, 0.2)" : "rgba(255, 255, 255, 0.2)"
32055
- }));
32388
+ __publicField(this, "eraserTrail", new EraserTrail(this.animationFrameHandler, this));
32056
32389
  __publicField(this, "lassoTrail", new LassoTrail(this.animationFrameHandler, this));
32057
32390
  __publicField(this, "onChangeEmitter", new Emitter());
32058
32391
  __publicField(this, "onPointerDownEmitter", new Emitter());
@@ -32143,7 +32476,7 @@ var App = class _App extends React43.Component {
32143
32476
  });
32144
32477
  __publicField(this, "resetEditingFrame", (frame) => {
32145
32478
  if (frame) {
32146
- mutateElement(frame, { name: frame.name?.trim() || null });
32479
+ this.scene.mutateElement(frame, { name: frame.name?.trim() || null });
32147
32480
  }
32148
32481
  this.setState({ editingFrame: null });
32149
32482
  });
@@ -32189,7 +32522,7 @@ var App = class _App extends React43.Component {
32189
32522
  autoFocus: true,
32190
32523
  value: frameNameInEdit,
32191
32524
  onChange: (e) => {
32192
- mutateElement(f, {
32525
+ this.scene.mutateElement(f, {
32193
32526
  name: e.target.value
32194
32527
  });
32195
32528
  },
@@ -32308,16 +32641,20 @@ var App = class _App extends React43.Component {
32308
32641
  data
32309
32642
  }) => {
32310
32643
  if (data.status === "pending") {
32311
- mutateElement(
32644
+ this.scene.mutateElement(
32312
32645
  frameElement,
32313
- { customData: { generationData: void 0 } },
32314
- false
32646
+ {
32647
+ customData: { generationData: void 0 }
32648
+ },
32649
+ { informMutation: false, isDragging: false }
32315
32650
  );
32316
32651
  } else {
32317
- mutateElement(
32652
+ this.scene.mutateElement(
32318
32653
  frameElement,
32319
- { customData: { generationData: data } },
32320
- false
32654
+ {
32655
+ customData: { generationData: data }
32656
+ },
32657
+ { informMutation: false, isDragging: false }
32321
32658
  );
32322
32659
  }
32323
32660
  this.magicGenerations.set(frameElement.id, data);
@@ -32355,7 +32692,7 @@ var App = class _App extends React43.Component {
32355
32692
  });
32356
32693
  this.scene.insertElement(frame);
32357
32694
  for (const child of selectedElements) {
32358
- mutateElement(child, { frameId: frame.id });
32695
+ this.scene.mutateElement(child, { frameId: frame.id });
32359
32696
  }
32360
32697
  this.setState({
32361
32698
  selectedElementIds: { [frame.id]: true }
@@ -32891,7 +33228,7 @@ var App = class _App extends React43.Component {
32891
33228
  const dx = x - elementsCenterX;
32892
33229
  const dy = y - elementsCenterY;
32893
33230
  const [gridX, gridY] = getGridPoint(dx, dy, this.getEffectiveGridSize());
32894
- const { newElements } = duplicateElements({
33231
+ const { duplicatedElements } = duplicateElements({
32895
33232
  type: "everything",
32896
33233
  elements: elements.map((element) => {
32897
33234
  return newElementWith(element, {
@@ -32902,17 +33239,17 @@ var App = class _App extends React43.Component {
32902
33239
  randomizeSeed: !opts.retainSeed
32903
33240
  });
32904
33241
  const prevElements = this.scene.getElementsIncludingDeleted();
32905
- let nextElements = [...prevElements, ...newElements];
33242
+ let nextElements = [...prevElements, ...duplicatedElements];
32906
33243
  const mappedNewSceneElements = this.props.onDuplicate?.(
32907
33244
  nextElements,
32908
33245
  prevElements
32909
33246
  );
32910
33247
  nextElements = mappedNewSceneElements || nextElements;
32911
- syncMovedIndices(nextElements, arrayToMap(newElements));
33248
+ syncMovedIndices(nextElements, arrayToMap(duplicatedElements));
32912
33249
  const topLayerFrame = this.getTopLayerFrameAtSceneCoords({ x, y });
32913
33250
  if (topLayerFrame) {
32914
33251
  const eligibleElements = filterElementsEligibleAsFrameChildren(
32915
- newElements,
33252
+ duplicatedElements,
32916
33253
  topLayerFrame
32917
33254
  );
32918
33255
  addElementsToFrame(
@@ -32923,21 +33260,17 @@ var App = class _App extends React43.Component {
32923
33260
  );
32924
33261
  }
32925
33262
  this.scene.replaceAllElements(nextElements);
32926
- newElements.forEach((newElement2) => {
33263
+ duplicatedElements.forEach((newElement2) => {
32927
33264
  if (isTextElement(newElement2) && isBoundToContainer(newElement2)) {
32928
33265
  const container = getContainerElement(
32929
33266
  newElement2,
32930
33267
  this.scene.getElementsMapIncludingDeleted()
32931
33268
  );
32932
- redrawTextBoundingBox(
32933
- newElement2,
32934
- container,
32935
- this.scene.getElementsMapIncludingDeleted()
32936
- );
33269
+ redrawTextBoundingBox(newElement2, container, this.scene);
32937
33270
  }
32938
33271
  });
32939
33272
  if (isSafari) {
32940
- Fonts.loadElementsFonts(newElements).then((fontFaces) => {
33273
+ Fonts.loadElementsFonts(duplicatedElements).then((fontFaces) => {
32941
33274
  this.fonts.onLoaded(fontFaces);
32942
33275
  });
32943
33276
  }
@@ -32945,7 +33278,7 @@ var App = class _App extends React43.Component {
32945
33278
  this.addMissingFiles(opts.files);
32946
33279
  }
32947
33280
  this.store.shouldCaptureIncrement();
32948
- const nextElementsToSelect = excludeElementsInFramesFromSelection(newElements);
33281
+ const nextElementsToSelect = excludeElementsInFramesFromSelection(duplicatedElements);
32949
33282
  this.setState(
32950
33283
  {
32951
33284
  ...this.state,
@@ -32981,7 +33314,7 @@ var App = class _App extends React43.Component {
32981
33314
  );
32982
33315
  this.setActiveTool({ type: "selection" });
32983
33316
  if (opts.fitToContent) {
32984
- this.scrollToContent(newElements, {
33317
+ this.scrollToContent(duplicatedElements, {
32985
33318
  fitToContent: true,
32986
33319
  canvasOffsets: this.getEditorUIOffsets()
32987
33320
  });
@@ -33255,6 +33588,12 @@ var App = class _App extends React43.Component {
33255
33588
  }
33256
33589
  }
33257
33590
  ));
33591
+ __publicField(this, "mutateElement", (element, updates, informMutation = true) => {
33592
+ return this.scene.mutateElement(element, updates, {
33593
+ informMutation,
33594
+ isDragging: false
33595
+ });
33596
+ });
33258
33597
  __publicField(this, "triggerRender", (force) => {
33259
33598
  if (force === true) {
33260
33599
  this.scene.triggerUpdate();
@@ -33358,9 +33697,9 @@ var App = class _App extends React43.Component {
33358
33697
  if (selectedElements2.length === 1 && isFlowchartNodeElement(selectedElements2[0])) {
33359
33698
  this.flowChartCreator.createNodes(
33360
33699
  selectedElements2[0],
33361
- this.scene.getNonDeletedElementsMap(),
33362
33700
  this.state,
33363
- getLinkDirectionFromKey(event.key)
33701
+ getLinkDirectionFromKey(event.key),
33702
+ this.scene
33364
33703
  );
33365
33704
  }
33366
33705
  if (this.flowChartCreator.pendingNodes?.length && !isElementCompletelyInViewport(
@@ -33533,15 +33872,15 @@ var App = class _App extends React43.Component {
33533
33872
  offsetY = step;
33534
33873
  }
33535
33874
  selectedElements.forEach((element) => {
33536
- mutateElement(
33875
+ this.scene.mutateElement(
33537
33876
  element,
33538
33877
  {
33539
33878
  x: element.x + offsetX,
33540
33879
  y: element.y + offsetY
33541
33880
  },
33542
- false
33881
+ { informMutation: false, isDragging: false }
33543
33882
  );
33544
- updateBoundElements(element, this.scene.getNonDeletedElementsMap(), {
33883
+ updateBoundElements(element, this.scene, {
33545
33884
  simultaneouslyUpdated: selectedElements
33546
33885
  });
33547
33886
  });
@@ -33567,7 +33906,8 @@ var App = class _App extends React43.Component {
33567
33906
  if (!isElbowArrow(selectedElement)) {
33568
33907
  this.setState({
33569
33908
  editingLinearElement: new LinearElementEditor(
33570
- selectedElement
33909
+ selectedElement,
33910
+ this.scene.getNonDeletedElementsMap()
33571
33911
  )
33572
33912
  });
33573
33913
  }
@@ -33698,11 +34038,9 @@ var App = class _App extends React43.Component {
33698
34038
  if (isArrowKey(event.key)) {
33699
34039
  bindOrUnbindLinearElements(
33700
34040
  this.scene.getSelectedElements(this.state).filter(isLinearElement),
33701
- this.scene.getNonDeletedElementsMap(),
33702
- this.scene.getNonDeletedElements(),
33703
- this.scene,
33704
34041
  isBindingEnabled(this.state),
33705
34042
  this.state.selectedLinearElement?.selectedPointsIndices ?? [],
34043
+ this.scene,
33706
34044
  this.state.zoom
33707
34045
  );
33708
34046
  this.setState({ suggestedBindings: [] });
@@ -33949,7 +34287,10 @@ var App = class _App extends React43.Component {
33949
34287
  const minHeight = getApproxMinLineHeight(fontSize, lineHeight);
33950
34288
  const newHeight = Math.max(container.height, minHeight);
33951
34289
  const newWidth = Math.max(container.width, minWidth);
33952
- mutateElement(container, { height: newHeight, width: newWidth });
34290
+ this.scene.mutateElement(container, {
34291
+ height: newHeight,
34292
+ width: newWidth
34293
+ });
33953
34294
  sceneX = container.x + newWidth / 2;
33954
34295
  sceneY = container.y + newHeight / 2;
33955
34296
  if (parentCenterPosition) {
@@ -33965,7 +34306,7 @@ var App = class _App extends React43.Component {
33965
34306
  x: sceneX,
33966
34307
  y: sceneY
33967
34308
  });
33968
- const element = existingTextElement ? existingTextElement : newTextElement({
34309
+ const element = existingTextElement || newTextElement({
33969
34310
  x: parentCenterPosition ? parentCenterPosition.elementCenterX : sceneX,
33970
34311
  y: parentCenterPosition ? parentCenterPosition.elementCenterY : sceneY,
33971
34312
  strokeColor: this.state.currentItemStrokeColor,
@@ -33983,11 +34324,11 @@ var App = class _App extends React43.Component {
33983
34324
  containerId: shouldBindToContainer ? container?.id : void 0,
33984
34325
  groupIds: container?.groupIds ?? [],
33985
34326
  lineHeight,
33986
- angle: container?.angle ?? 0,
34327
+ angle: container ? isArrowElement(container) ? 0 : container.angle : 0,
33987
34328
  frameId: topLayerFrame ? topLayerFrame.id : null
33988
34329
  });
33989
34330
  if (!existingTextElement && shouldBindToContainer && container) {
33990
- mutateElement(container, {
34331
+ this.scene.mutateElement(container, {
33991
34332
  boundElements: (container.boundElements || []).concat({
33992
34333
  type: "text",
33993
34334
  id: element.id
@@ -34044,7 +34385,10 @@ var App = class _App extends React43.Component {
34044
34385
  if (event[KEYS.CTRL_OR_CMD] && (!this.state.editingLinearElement || this.state.editingLinearElement.elementId !== selectedElements[0].id) && !isElbowArrow(selectedElements[0])) {
34045
34386
  this.store.shouldCaptureIncrement();
34046
34387
  this.setState({
34047
- editingLinearElement: new LinearElementEditor(selectedElements[0])
34388
+ editingLinearElement: new LinearElementEditor(
34389
+ selectedElements[0],
34390
+ this.scene.getNonDeletedElementsMap()
34391
+ )
34048
34392
  });
34049
34393
  return;
34050
34394
  } else if (this.state.selectedLinearElement && isElbowArrow(selectedElements[0])) {
@@ -34062,7 +34406,11 @@ var App = class _App extends React43.Component {
34062
34406
  ) : -1;
34063
34407
  if (midPoint && midPoint > -1) {
34064
34408
  this.store.shouldCaptureIncrement();
34065
- LinearElementEditor.deleteFixedSegment(selectedElements[0], midPoint);
34409
+ LinearElementEditor.deleteFixedSegment(
34410
+ selectedElements[0],
34411
+ this.scene,
34412
+ midPoint
34413
+ );
34066
34414
  const nextCoords = LinearElementEditor.getSegmentMidpointHitCoords(
34067
34415
  {
34068
34416
  ...this.state.selectedLinearElement,
@@ -34346,8 +34694,7 @@ var App = class _App extends React43.Component {
34346
34694
  event,
34347
34695
  scenePointerX,
34348
34696
  scenePointerY,
34349
- this,
34350
- this.scene.getNonDeletedElementsMap()
34697
+ this
34351
34698
  );
34352
34699
  if (editingLinearElement && editingLinearElement !== this.state.editingLinearElement) {
34353
34700
  flushSync2(() => {
@@ -34390,7 +34737,7 @@ var App = class _App extends React43.Component {
34390
34737
  pointFrom(scenePointerX - rx, scenePointerY - ry),
34391
34738
  lastPoint
34392
34739
  ) >= LINE_CONFIRM_THRESHOLD) {
34393
- mutateElement(
34740
+ this.scene.mutateElement(
34394
34741
  multiElement,
34395
34742
  {
34396
34743
  points: [
@@ -34398,7 +34745,7 @@ var App = class _App extends React43.Component {
34398
34745
  pointFrom(scenePointerX - rx, scenePointerY - ry)
34399
34746
  ]
34400
34747
  },
34401
- false
34748
+ { informMutation: false, isDragging: false }
34402
34749
  );
34403
34750
  } else {
34404
34751
  setCursor(this.interactiveCanvas, CURSOR_TYPE.POINTER);
@@ -34408,12 +34755,12 @@ var App = class _App extends React43.Component {
34408
34755
  lastCommittedPoint
34409
34756
  ) < LINE_CONFIRM_THRESHOLD) {
34410
34757
  setCursor(this.interactiveCanvas, CURSOR_TYPE.POINTER);
34411
- mutateElement(
34758
+ this.scene.mutateElement(
34412
34759
  multiElement,
34413
34760
  {
34414
34761
  points: points.slice(0, -1)
34415
34762
  },
34416
- false
34763
+ { informMutation: false, isDragging: false }
34417
34764
  );
34418
34765
  } else {
34419
34766
  const [gridX, gridY] = getGridPoint(
@@ -34437,7 +34784,7 @@ var App = class _App extends React43.Component {
34437
34784
  if (isPathALoop(points, this.state.zoom.value)) {
34438
34785
  setCursor(this.interactiveCanvas, CURSOR_TYPE.POINTER);
34439
34786
  }
34440
- mutateElement(
34787
+ this.scene.mutateElement(
34441
34788
  multiElement,
34442
34789
  {
34443
34790
  points: [
@@ -34448,9 +34795,9 @@ var App = class _App extends React43.Component {
34448
34795
  )
34449
34796
  ]
34450
34797
  },
34451
- false,
34452
34798
  {
34453
- isDragging: true
34799
+ isDragging: true,
34800
+ informMutation: false
34454
34801
  }
34455
34802
  );
34456
34803
  this.triggerRender(false);
@@ -34601,80 +34948,14 @@ var App = class _App extends React43.Component {
34601
34948
  }));
34602
34949
  }
34603
34950
  });
34604
- __publicField(this, "handleEraser", (event, pointerDownState, scenePointer) => {
34605
- this.eraserTrail.addPointToPath(scenePointer.x, scenePointer.y);
34606
- let didChange = false;
34607
- const processedGroups = /* @__PURE__ */ new Set();
34608
- const nonDeletedElements = this.scene.getNonDeletedElements();
34609
- const processElements = (elements) => {
34610
- for (const element of elements) {
34611
- if (element.locked) {
34612
- return;
34613
- }
34614
- if (event.altKey) {
34615
- if (this.elementsPendingErasure.delete(element.id)) {
34616
- didChange = true;
34617
- }
34618
- } else if (!this.elementsPendingErasure.has(element.id)) {
34619
- didChange = true;
34620
- this.elementsPendingErasure.add(element.id);
34621
- }
34622
- if (didChange && element.groupIds?.length) {
34623
- const shallowestGroupId = element.groupIds.at(-1);
34624
- if (!processedGroups.has(shallowestGroupId)) {
34625
- processedGroups.add(shallowestGroupId);
34626
- const elems = getElementsInGroup(
34627
- nonDeletedElements,
34628
- shallowestGroupId
34629
- );
34630
- for (const elem of elems) {
34631
- if (event.altKey) {
34632
- this.elementsPendingErasure.delete(elem.id);
34633
- } else {
34634
- this.elementsPendingErasure.add(elem.id);
34635
- }
34636
- }
34637
- }
34638
- }
34639
- }
34640
- };
34641
- const distance2 = pointDistance(
34642
- pointFrom(pointerDownState.lastCoords.x, pointerDownState.lastCoords.y),
34643
- pointFrom(scenePointer.x, scenePointer.y)
34951
+ __publicField(this, "handleEraser", (event, scenePointer) => {
34952
+ const elementsToErase = this.eraserTrail.addPointToPath(
34953
+ scenePointer.x,
34954
+ scenePointer.y,
34955
+ event.altKey
34644
34956
  );
34645
- const threshold = this.getElementHitThreshold();
34646
- const p = { ...pointerDownState.lastCoords };
34647
- let samplingInterval = 0;
34648
- while (samplingInterval <= distance2) {
34649
- const hitElements = this.getElementsAtPosition(p.x, p.y);
34650
- processElements(hitElements);
34651
- if (samplingInterval === distance2) {
34652
- break;
34653
- }
34654
- samplingInterval = Math.min(samplingInterval + threshold, distance2);
34655
- const distanceRatio = samplingInterval / distance2;
34656
- const nextX = (1 - distanceRatio) * p.x + distanceRatio * scenePointer.x;
34657
- const nextY = (1 - distanceRatio) * p.y + distanceRatio * scenePointer.y;
34658
- p.x = nextX;
34659
- p.y = nextY;
34660
- }
34661
- pointerDownState.lastCoords.x = scenePointer.x;
34662
- pointerDownState.lastCoords.y = scenePointer.y;
34663
- if (didChange) {
34664
- for (const element of this.scene.getNonDeletedElements()) {
34665
- if (isBoundToContainer(element) && (this.elementsPendingErasure.has(element.id) || this.elementsPendingErasure.has(element.containerId))) {
34666
- if (event.altKey) {
34667
- this.elementsPendingErasure.delete(element.id);
34668
- this.elementsPendingErasure.delete(element.containerId);
34669
- } else {
34670
- this.elementsPendingErasure.add(element.id);
34671
- this.elementsPendingErasure.add(element.containerId);
34672
- }
34673
- }
34674
- }
34675
- this.elementsPendingErasure = new Set(this.elementsPendingErasure);
34676
- this.triggerRender();
34677
- }
34957
+ this.elementsPendingErasure = new Set(elementsToErase);
34958
+ this.triggerRender();
34678
34959
  });
34679
34960
  // set touch moving for mobile context menu
34680
34961
  __publicField(this, "handleTouchMove", (event) => {
@@ -34843,7 +35124,7 @@ var App = class _App extends React43.Component {
34843
35124
  });
34844
35125
  const { x, y } = viewportCoordsToSceneCoords(event, this.state);
34845
35126
  const frame = this.getTopLayerFrameAtSceneCoords({ x, y });
34846
- mutateElement(pendingImageElement, {
35127
+ this.scene.mutateElement(pendingImageElement, {
34847
35128
  x,
34848
35129
  y,
34849
35130
  frameId: frame ? frame.id : null
@@ -35480,14 +35761,14 @@ var App = class _App extends React43.Component {
35480
35761
  if (this.state.multiElement) {
35481
35762
  const { multiElement } = this.state;
35482
35763
  if (multiElement.type === "line" && isPathALoop(multiElement.points, this.state.zoom.value)) {
35483
- mutateElement(multiElement, {
35764
+ this.scene.mutateElement(multiElement, {
35484
35765
  lastCommittedPoint: multiElement.points[multiElement.points.length - 1]
35485
35766
  });
35486
35767
  this.actionManager.executeAction(actionFinalize);
35487
35768
  return;
35488
35769
  }
35489
35770
  if (isElbowArrow(multiElement) && multiElement.points.length > 1) {
35490
- mutateElement(multiElement, {
35771
+ this.scene.mutateElement(multiElement, {
35491
35772
  lastCommittedPoint: multiElement.points[multiElement.points.length - 1]
35492
35773
  });
35493
35774
  this.actionManager.executeAction(actionFinalize);
@@ -35513,7 +35794,7 @@ var App = class _App extends React43.Component {
35513
35794
  prevState
35514
35795
  )
35515
35796
  }));
35516
- mutateElement(multiElement, {
35797
+ this.scene.mutateElement(multiElement, {
35517
35798
  lastCommittedPoint: multiElement.points[multiElement.points.length - 1]
35518
35799
  });
35519
35800
  setCursor(this.interactiveCanvas, CURSOR_TYPE.POINTER);
@@ -35578,7 +35859,7 @@ var App = class _App extends React43.Component {
35578
35859
  )
35579
35860
  };
35580
35861
  });
35581
- mutateElement(element, {
35862
+ this.scene.mutateElement(element, {
35582
35863
  points: [...element.points, pointFrom(0, 0)]
35583
35864
  });
35584
35865
  const boundElement = getHoveredElementForBinding(
@@ -35740,12 +36021,12 @@ var App = class _App extends React43.Component {
35740
36021
  this.setImagePreviewCursor(resizedFile || imageFile);
35741
36022
  }
35742
36023
  const dataURL = this.files[fileId]?.dataURL || await getDataURL(imageFile);
35743
- const imageElement = mutateElement(
36024
+ const imageElement = this.scene.mutateElement(
35744
36025
  _imageElement,
35745
36026
  {
35746
36027
  fileId
35747
36028
  },
35748
- false
36029
+ { informMutation: false, isDragging: false }
35749
36030
  );
35750
36031
  return new Promise(
35751
36032
  async (resolve, reject) => {
@@ -35798,7 +36079,7 @@ var App = class _App extends React43.Component {
35798
36079
  showCursorImagePreview
35799
36080
  });
35800
36081
  } catch (error) {
35801
- mutateElement(imageElement, {
36082
+ this.scene.mutateElement(imageElement, {
35802
36083
  isDeleted: true
35803
36084
  });
35804
36085
  this.actionManager.executeAction(actionFinalize);
@@ -35914,7 +36195,7 @@ var App = class _App extends React43.Component {
35914
36195
  if (!image || image instanceof Promise) {
35915
36196
  if (imageElement.width < DRAGGING_THRESHOLD / this.state.zoom.value && imageElement.height < DRAGGING_THRESHOLD / this.state.zoom.value) {
35916
36197
  const placeholderSize = 100 / this.state.zoom.value;
35917
- mutateElement(imageElement, {
36198
+ this.scene.mutateElement(imageElement, {
35918
36199
  x: imageElement.x - placeholderSize / 2,
35919
36200
  y: imageElement.y - placeholderSize / 2,
35920
36201
  width: placeholderSize,
@@ -35936,7 +36217,7 @@ var App = class _App extends React43.Component {
35936
36217
  const width = height * (image.naturalWidth / image.naturalHeight);
35937
36218
  const x = imageElement.x + imageElement.width / 2 - width / 2;
35938
36219
  const y = imageElement.y + imageElement.height / 2 - height / 2;
35939
- mutateElement(imageElement, {
36220
+ this.scene.mutateElement(imageElement, {
35940
36221
  x,
35941
36222
  y,
35942
36223
  width,
@@ -35993,7 +36274,7 @@ var App = class _App extends React43.Component {
35993
36274
  });
35994
36275
  /** generally you should use `addNewImagesToImageCache()` directly if you need
35995
36276
  * to render new images. This is just a failsafe */
35996
- __publicField(this, "scheduleImageRefresh", throttle3(() => {
36277
+ __publicField(this, "scheduleImageRefresh", throttle4(() => {
35997
36278
  this.addNewImagesToImageCache();
35998
36279
  }, IMAGE_RENDER_TIMEOUT));
35999
36280
  __publicField(this, "updateBindingEnabledOnPointerMove", (event) => {
@@ -36234,7 +36515,10 @@ var App = class _App extends React43.Component {
36234
36515
  this.state,
36235
36516
  this
36236
36517
  ),
36237
- selectedLinearElement: isLinearElement(element) ? new LinearElementEditor(element) : null
36518
+ selectedLinearElement: isLinearElement(element) ? new LinearElementEditor(
36519
+ element,
36520
+ this.scene.getNonDeletedElementsMap()
36521
+ ) : null
36238
36522
  } : this.state,
36239
36523
  showHyperlinkPopup: false
36240
36524
  },
@@ -36260,8 +36544,9 @@ var App = class _App extends React43.Component {
36260
36544
  height: distance(pointerDownState.origin.y, pointerCoords.y),
36261
36545
  shouldMaintainAspectRatio: shouldMaintainAspectRatio(event),
36262
36546
  shouldResizeFromCenter: false,
36547
+ scene: this.scene,
36263
36548
  zoom: this.state.zoom.value,
36264
- informMutation
36549
+ informMutation: false
36265
36550
  });
36266
36551
  return;
36267
36552
  }
@@ -36308,6 +36593,7 @@ var App = class _App extends React43.Component {
36308
36593
  shouldMaintainAspectRatio: isImageElement(newElement2) ? !shouldMaintainAspectRatio(event) : shouldMaintainAspectRatio(event),
36309
36594
  shouldResizeFromCenter: shouldResizeFromCenter(event),
36310
36595
  zoom: this.state.zoom.value,
36596
+ scene: this.scene,
36311
36597
  widthAspectRatio: aspectRatio,
36312
36598
  originOffset: this.state.originSnapOffset,
36313
36599
  informMutation
@@ -36362,7 +36648,7 @@ var App = class _App extends React43.Component {
36362
36648
  dragOffset,
36363
36649
  transformHandleType
36364
36650
  );
36365
- mutateElement(
36651
+ this.scene.mutateElement(
36366
36652
  croppingElement,
36367
36653
  cropElement(
36368
36654
  croppingElement,
@@ -36374,16 +36660,12 @@ var App = class _App extends React43.Component {
36374
36660
  event.shiftKey ? croppingAtStateStart.width / croppingAtStateStart.height : void 0
36375
36661
  )
36376
36662
  );
36377
- updateBoundElements(
36378
- croppingElement,
36379
- this.scene.getNonDeletedElementsMap(),
36380
- {
36381
- newSize: {
36382
- width: croppingElement.width,
36383
- height: croppingElement.height
36384
- }
36663
+ updateBoundElements(croppingElement, this.scene, {
36664
+ newSize: {
36665
+ width: croppingElement.width,
36666
+ height: croppingElement.height
36385
36667
  }
36386
- );
36668
+ });
36387
36669
  this.setState({
36388
36670
  isCropping: transformHandleType && transformHandleType !== "rotation",
36389
36671
  snapLines
@@ -36464,7 +36746,6 @@ var App = class _App extends React43.Component {
36464
36746
  pointerDownState.originalElements,
36465
36747
  transformHandleType,
36466
36748
  selectedElements,
36467
- this.scene.getElementsMapIncludingDeleted(),
36468
36749
  this.scene,
36469
36750
  shouldRotateWithDiscreteAngle(event),
36470
36751
  shouldResizeFromCenter(event),
@@ -36730,6 +37011,7 @@ var App = class _App extends React43.Component {
36730
37011
  if (excalidrawAPI) {
36731
37012
  const api = {
36732
37013
  updateScene: this.updateScene,
37014
+ mutateElement: this.mutateElement,
36733
37015
  updateLibrary: this.library.updateLibrary,
36734
37016
  addFiles: this.addFiles,
36735
37017
  resetScene: this.resetScene,
@@ -37225,8 +37507,8 @@ var App = class _App extends React43.Component {
37225
37507
  {
37226
37508
  trails: [
37227
37509
  this.laserTrails,
37228
- this.eraserTrail,
37229
- this.lassoTrail
37510
+ this.lassoTrail,
37511
+ this.eraserTrail
37230
37512
  ]
37231
37513
  }
37232
37514
  ),
@@ -37234,7 +37516,7 @@ var App = class _App extends React43.Component {
37234
37516
  Hyperlink,
37235
37517
  {
37236
37518
  element: firstSelectedElement,
37237
- elementsMap: allElementsMap,
37519
+ scene: this.scene,
37238
37520
  setAppState: this.setAppState,
37239
37521
  onLinkOpen: this.props.onLinkOpen,
37240
37522
  setToast: this.setToast,
@@ -37392,6 +37674,7 @@ var App = class _App extends React43.Component {
37392
37674
  selectionNonce: this.state.selectionElement?.versionNonce,
37393
37675
  scale: window.devicePixelRatio,
37394
37676
  appState: this.state,
37677
+ renderScrollbars: this.props.renderScrollbars === true,
37395
37678
  device: this.device,
37396
37679
  renderInteractiveSceneCallback: this.renderInteractiveSceneCallback,
37397
37680
  handleCanvasRef: this.handleInteractiveCanvasRef,
@@ -37841,8 +38124,7 @@ var App = class _App extends React43.Component {
37841
38124
  nonDeletedElementsMap
37842
38125
  )
37843
38126
  ),
37844
- this.scene.getNonDeletedElementsMap(),
37845
- this.scene.getNonDeletedElements()
38127
+ this.scene
37846
38128
  );
37847
38129
  }
37848
38130
  this.store.commit(elementsMap, this.state);
@@ -37896,7 +38178,11 @@ var App = class _App extends React43.Component {
37896
38178
  firstImageYOffsetDone = true;
37897
38179
  y -= initializedImageElement.height / 2;
37898
38180
  }
37899
- mutateElement(initializedImageElement, { y }, false);
38181
+ this.scene.mutateElement(
38182
+ initializedImageElement,
38183
+ { y },
38184
+ { informMutation: false, isDragging: false }
38185
+ );
37900
38186
  y = imageElement.y + imageElement.height + 25;
37901
38187
  nextSelectedIds[imageElement.id] = true;
37902
38188
  }
@@ -38059,7 +38345,7 @@ var App = class _App extends React43.Component {
38059
38345
  onChange: withBatchedUpdates((nextOriginalText) => {
38060
38346
  updateElement(nextOriginalText, false);
38061
38347
  if (isNonDeletedElement(element)) {
38062
- updateBoundElements(element, this.scene.getNonDeletedElementsMap());
38348
+ updateBoundElements(element, this.scene);
38063
38349
  }
38064
38350
  }),
38065
38351
  onSubmit: withBatchedUpdates(({ viaKeyboard, nextOriginalText }) => {
@@ -38378,7 +38664,8 @@ var App = class _App extends React43.Component {
38378
38664
  },
38379
38665
  drag: {
38380
38666
  hasOccurred: false,
38381
- offset: null
38667
+ offset: null,
38668
+ origin: { ...origin }
38382
38669
  },
38383
38670
  eventListeners: {
38384
38671
  onMove: null,
@@ -38523,7 +38810,7 @@ var App = class _App extends React43.Component {
38523
38810
  index,
38524
38811
  gridX2,
38525
38812
  gridY2,
38526
- this.scene.getNonDeletedElementsMap()
38813
+ this.scene
38527
38814
  );
38528
38815
  flushSync2(() => {
38529
38816
  if (this.state.selectedLinearElement) {
@@ -38557,7 +38844,7 @@ var App = class _App extends React43.Component {
38557
38844
  return;
38558
38845
  }
38559
38846
  if (isEraserActive(this.state)) {
38560
- this.handleEraser(event, pointerDownState, pointerCoords);
38847
+ this.handleEraser(event, pointerCoords);
38561
38848
  return;
38562
38849
  }
38563
38850
  if (this.state.activeTool.type === "laser") {
@@ -38600,7 +38887,7 @@ var App = class _App extends React43.Component {
38600
38887
  pointerCoords,
38601
38888
  this,
38602
38889
  !event[KEYS.CTRL_OR_CMD],
38603
- elementsMap
38890
+ this.scene
38604
38891
  );
38605
38892
  if (!ret) {
38606
38893
  return;
@@ -38678,8 +38965,8 @@ var App = class _App extends React43.Component {
38678
38965
  pointerDownState.drag.hasOccurred = true;
38679
38966
  if (selectedElements.length > 0 && !pointerDownState.withCmdOrCtrl && !this.state.editingTextElement && this.state.activeEmbeddable?.state !== "active") {
38680
38967
  const dragOffset = {
38681
- x: pointerCoords.x - pointerDownState.origin.x,
38682
- y: pointerCoords.y - pointerDownState.origin.y
38968
+ x: pointerCoords.x - pointerDownState.drag.origin.x,
38969
+ y: pointerCoords.y - pointerDownState.drag.origin.y
38683
38970
  };
38684
38971
  const originalElements = [
38685
38972
  ...pointerDownState.originalElements.values()
@@ -38758,7 +39045,7 @@ var App = class _App extends React43.Component {
38758
39045
  image.naturalHeight - crop.height
38759
39046
  )
38760
39047
  };
38761
- mutateElement(croppingElement, {
39048
+ this.scene.mutateElement(croppingElement, {
38762
39049
  crop: nextCrop
38763
39050
  });
38764
39051
  return;
@@ -38818,46 +39105,90 @@ var App = class _App extends React43.Component {
38818
39105
  const idsOfElementsToDuplicate = new Map(
38819
39106
  selectedElements2.map((el) => [el.id, el])
38820
39107
  );
38821
- const { newElements: clonedElements, elementsWithClones } = duplicateElements({
39108
+ const {
39109
+ duplicatedElements,
39110
+ duplicateElementsMap,
39111
+ elementsWithDuplicates,
39112
+ origIdToDuplicateId
39113
+ } = duplicateElements({
38822
39114
  type: "in-place",
38823
39115
  elements,
38824
39116
  appState: this.state,
38825
39117
  randomizeSeed: true,
38826
39118
  idsOfElementsToDuplicate,
38827
- overrides: (el) => {
39119
+ overrides: ({ duplicateElement: duplicateElement2, origElement }) => {
39120
+ return {
39121
+ // reset to the original element's frameId (unless we've
39122
+ // duplicated alongside a frame in which case we need to
39123
+ // keep the duplicate frame's id) so that the element
39124
+ // frame membership is refreshed on pointerup
39125
+ // NOTE this is a hacky solution and should be done
39126
+ // differently
39127
+ frameId: duplicateElement2.frameId ?? origElement.frameId,
39128
+ seed: randomInteger()
39129
+ };
39130
+ }
39131
+ });
39132
+ duplicatedElements.forEach((element) => {
39133
+ pointerDownState.originalElements.set(
39134
+ element.id,
39135
+ deepCopyElement(element)
39136
+ );
39137
+ });
39138
+ const mappedClonedElements = elementsWithDuplicates.map((el) => {
39139
+ if (idsOfElementsToDuplicate.has(el.id)) {
38828
39140
  const origEl = pointerDownState.originalElements.get(el.id);
38829
39141
  if (origEl) {
38830
- return {
39142
+ return newElementWith(el, {
38831
39143
  x: origEl.x,
38832
- y: origEl.y,
38833
- seed: origEl.seed
38834
- };
39144
+ y: origEl.y
39145
+ });
38835
39146
  }
38836
- return {};
38837
- },
38838
- reverseOrder: true
38839
- });
38840
- clonedElements.forEach((element) => {
38841
- pointerDownState.originalElements.set(element.id, element);
39147
+ }
39148
+ return el;
38842
39149
  });
38843
39150
  const mappedNewSceneElements = this.props.onDuplicate?.(
38844
- elementsWithClones,
39151
+ mappedClonedElements,
38845
39152
  elements
38846
39153
  );
38847
- const nextSceneElements = syncMovedIndices(
38848
- mappedNewSceneElements || elementsWithClones,
38849
- arrayToMap(clonedElements)
38850
- ).map((el) => {
38851
- if (idsOfElementsToDuplicate.has(el.id)) {
38852
- return newElementWith(el, {
38853
- seed: randomInteger()
38854
- });
39154
+ const elementsWithIndices = syncMovedIndices(
39155
+ mappedNewSceneElements || mappedClonedElements,
39156
+ arrayToMap(duplicatedElements)
39157
+ );
39158
+ flushSync2(() => {
39159
+ if (pointerDownState.hit.element) {
39160
+ const cloneId = origIdToDuplicateId.get(
39161
+ pointerDownState.hit.element.id
39162
+ );
39163
+ const clonedElement = cloneId && duplicateElementsMap.get(cloneId);
39164
+ pointerDownState.hit.element = clonedElement || null;
38855
39165
  }
38856
- return el;
39166
+ pointerDownState.hit.allHitElements = pointerDownState.hit.allHitElements.reduce(
39167
+ (acc, origHitElement) => {
39168
+ const cloneId = origIdToDuplicateId.get(origHitElement.id);
39169
+ const clonedElement = cloneId && duplicateElementsMap.get(cloneId);
39170
+ if (clonedElement) {
39171
+ acc.push(clonedElement);
39172
+ }
39173
+ return acc;
39174
+ },
39175
+ []
39176
+ );
39177
+ pointerDownState.drag.origin = viewportCoordsToSceneCoords(
39178
+ event,
39179
+ this.state
39180
+ );
39181
+ this.setState((prevState) => ({
39182
+ ...getSelectionStateForElements(
39183
+ duplicatedElements,
39184
+ this.scene.getNonDeletedElements(),
39185
+ prevState
39186
+ )
39187
+ }));
39188
+ this.scene.replaceAllElements(elementsWithIndices);
39189
+ this.maybeCacheVisibleGaps(event, selectedElements2, true);
39190
+ this.maybeCacheReferenceSnapPoints(event, selectedElements2, true);
38857
39191
  });
38858
- this.scene.replaceAllElements(nextSceneElements);
38859
- this.maybeCacheVisibleGaps(event, selectedElements2, true);
38860
- this.maybeCacheReferenceSnapPoints(event, selectedElements2, true);
38861
39192
  }
38862
39193
  return;
38863
39194
  }
@@ -38909,13 +39240,16 @@ var App = class _App extends React43.Component {
38909
39240
  const discardPoint = lastPoint && lastPoint[0] === dx && lastPoint[1] === dy;
38910
39241
  if (!discardPoint) {
38911
39242
  const pressures = newElement2.simulatePressure ? newElement2.pressures : [...newElement2.pressures, event.pressure];
38912
- mutateElement(
39243
+ this.scene.mutateElement(
38913
39244
  newElement2,
38914
39245
  {
38915
39246
  points: [...points, pointFrom(dx, dy)],
38916
39247
  pressures
38917
39248
  },
38918
- false
39249
+ {
39250
+ informMutation: false,
39251
+ isDragging: false
39252
+ }
38919
39253
  );
38920
39254
  this.setState({
38921
39255
  newElement: newElement2
@@ -38935,21 +39269,20 @@ var App = class _App extends React43.Component {
38935
39269
  ));
38936
39270
  }
38937
39271
  if (points.length === 1) {
38938
- mutateElement(
39272
+ this.scene.mutateElement(
38939
39273
  newElement2,
38940
39274
  {
38941
39275
  points: [...points, pointFrom(dx, dy)]
38942
39276
  },
38943
- false
39277
+ { informMutation: false, isDragging: false }
38944
39278
  );
38945
39279
  } else if (points.length === 2 || points.length > 1 && isElbowArrow(newElement2)) {
38946
- mutateElement(
39280
+ this.scene.mutateElement(
38947
39281
  newElement2,
38948
39282
  {
38949
39283
  points: [...points.slice(0, -1), pointFrom(dx, dy)]
38950
39284
  },
38951
- false,
38952
- { isDragging: true }
39285
+ { isDragging: true, informMutation: false }
38953
39286
  );
38954
39287
  }
38955
39288
  this.setState({
@@ -39035,7 +39368,10 @@ var App = class _App extends React43.Component {
39035
39368
  this
39036
39369
  ),
39037
39370
  // select linear element only when we haven't box-selected anything else
39038
- selectedLinearElement: elementsWithinSelection.length === 1 && isLinearElement(elementsWithinSelection[0]) ? new LinearElementEditor(elementsWithinSelection[0]) : null,
39371
+ selectedLinearElement: elementsWithinSelection.length === 1 && isLinearElement(elementsWithinSelection[0]) ? new LinearElementEditor(
39372
+ elementsWithinSelection[0],
39373
+ this.scene.getNonDeletedElementsMap()
39374
+ ) : null,
39039
39375
  showHyperlinkPopup: elementsWithinSelection.length === 1 && (elementsWithinSelection[0].link || isEmbeddableElement(elementsWithinSelection[0])) ? "info" : false
39040
39376
  };
39041
39377
  });
@@ -39049,7 +39385,7 @@ var App = class _App extends React43.Component {
39049
39385
  const x = event.clientX;
39050
39386
  const dx = x - pointerDownState.lastCoords.x;
39051
39387
  this.translateCanvas({
39052
- scrollX: this.state.scrollX - dx / this.state.zoom.value
39388
+ scrollX: this.state.scrollX - dx * (currentScrollBars.horizontal?.deltaMultiplier || 1) / this.state.zoom.value
39053
39389
  });
39054
39390
  pointerDownState.lastCoords.x = x;
39055
39391
  return true;
@@ -39058,7 +39394,7 @@ var App = class _App extends React43.Component {
39058
39394
  const y = event.clientY;
39059
39395
  const dy = y - pointerDownState.lastCoords.y;
39060
39396
  this.translateCanvas({
39061
- scrollY: this.state.scrollY - dy / this.state.zoom.value
39397
+ scrollY: this.state.scrollY - dy * (currentScrollBars.vertical?.deltaMultiplier || 1) / this.state.zoom.value
39062
39398
  });
39063
39399
  pointerDownState.lastCoords.y = y;
39064
39400
  return true;
@@ -39106,7 +39442,7 @@ var App = class _App extends React43.Component {
39106
39442
  const element = elementsMap.get(pointerDownState.hit.element.id);
39107
39443
  if (isBindableElement(element)) {
39108
39444
  element.boundElements?.filter((e) => e.type === "arrow").map((e) => elementsMap.get(e.id)).filter((e) => isElbowArrow(e)).forEach((e) => {
39109
- !!e && mutateElement(e, {}, true);
39445
+ !!e && this.scene.mutateElement(e, {});
39110
39446
  });
39111
39447
  }
39112
39448
  }
@@ -39134,7 +39470,10 @@ var App = class _App extends React43.Component {
39134
39470
  this.scene.getNonDeletedElementsMap()
39135
39471
  );
39136
39472
  if (element) {
39137
- mutateElement(element, {}, true);
39473
+ this.scene.mutateElement(
39474
+ element,
39475
+ {}
39476
+ );
39138
39477
  }
39139
39478
  }
39140
39479
  if (pointerDownState.hit?.element?.id !== this.state.selectedLinearElement.elementId) {
@@ -39156,7 +39495,6 @@ var App = class _App extends React43.Component {
39156
39495
  element,
39157
39496
  startBindingElement,
39158
39497
  endBindingElement,
39159
- elementsMap,
39160
39498
  this.scene
39161
39499
  );
39162
39500
  }
@@ -39210,7 +39548,7 @@ var App = class _App extends React43.Component {
39210
39548
  dx += 1e-4;
39211
39549
  }
39212
39550
  const pressures = newElement2.simulatePressure ? [] : [...newElement2.pressures, childEvent.pressure];
39213
- mutateElement(newElement2, {
39551
+ this.scene.mutateElement(newElement2, {
39214
39552
  points: [...points, pointFrom(dx, dy)],
39215
39553
  pressures,
39216
39554
  lastCommittedPoint: pointFrom(dx, dy)
@@ -39251,15 +39589,19 @@ var App = class _App extends React43.Component {
39251
39589
  this.state
39252
39590
  );
39253
39591
  if (!pointerDownState.drag.hasOccurred && newElement2 && !multiElement) {
39254
- mutateElement(newElement2, {
39255
- points: [
39256
- ...newElement2.points,
39257
- pointFrom(
39258
- pointerCoords.x - newElement2.x,
39259
- pointerCoords.y - newElement2.y
39260
- )
39261
- ]
39262
- });
39592
+ this.scene.mutateElement(
39593
+ newElement2,
39594
+ {
39595
+ points: [
39596
+ ...newElement2.points,
39597
+ pointFrom(
39598
+ pointerCoords.x - newElement2.x,
39599
+ pointerCoords.y - newElement2.y
39600
+ )
39601
+ ]
39602
+ },
39603
+ { informMutation: false, isDragging: false }
39604
+ );
39263
39605
  this.setState({
39264
39606
  multiElement: newElement2,
39265
39607
  newElement: newElement2
@@ -39270,8 +39612,7 @@ var App = class _App extends React43.Component {
39270
39612
  newElement2,
39271
39613
  this.state,
39272
39614
  pointerCoords,
39273
- this.scene.getNonDeletedElementsMap(),
39274
- this.scene.getNonDeletedElements()
39615
+ this.scene
39275
39616
  );
39276
39617
  }
39277
39618
  this.setState({ suggestedBindings: [], startBoundElement: null });
@@ -39289,7 +39630,10 @@ var App = class _App extends React43.Component {
39289
39630
  },
39290
39631
  prevState
39291
39632
  ),
39292
- selectedLinearElement: new LinearElementEditor(newElement2)
39633
+ selectedLinearElement: new LinearElementEditor(
39634
+ newElement2,
39635
+ this.scene.getNonDeletedElementsMap()
39636
+ )
39293
39637
  }));
39294
39638
  } else {
39295
39639
  this.setState((prevState) => ({
@@ -39309,7 +39653,7 @@ var App = class _App extends React43.Component {
39309
39653
  newElement2.lineHeight
39310
39654
  );
39311
39655
  if (newElement2.width < minWidth) {
39312
- mutateElement(newElement2, {
39656
+ this.scene.mutateElement(newElement2, {
39313
39657
  autoResize: true
39314
39658
  });
39315
39659
  }
@@ -39344,7 +39688,14 @@ var App = class _App extends React43.Component {
39344
39688
  );
39345
39689
  }
39346
39690
  if (newElement2) {
39347
- mutateElement(newElement2, getNormalizedDimensions(newElement2));
39691
+ this.scene.mutateElement(
39692
+ newElement2,
39693
+ getNormalizedDimensions(newElement2),
39694
+ {
39695
+ informMutation: false,
39696
+ isDragging: false
39697
+ }
39698
+ );
39348
39699
  this.scene.triggerUpdate();
39349
39700
  }
39350
39701
  if (pointerDownState.drag.hasOccurred) {
@@ -39361,7 +39712,7 @@ var App = class _App extends React43.Component {
39361
39712
  frame,
39362
39713
  this.scene.getNonDeletedElementsMap()
39363
39714
  )) {
39364
- mutateElement(linearElement, {
39715
+ this.scene.mutateElement(linearElement, {
39365
39716
  groupIds: []
39366
39717
  });
39367
39718
  removeElementsFromFrame(
@@ -39382,12 +39733,12 @@ var App = class _App extends React43.Component {
39382
39733
  const index = element.groupIds.indexOf(
39383
39734
  this.state.editingGroupId
39384
39735
  );
39385
- mutateElement(
39736
+ this.scene.mutateElement(
39386
39737
  element,
39387
39738
  {
39388
39739
  groupIds: element.groupIds.slice(0, index)
39389
39740
  },
39390
- false
39741
+ { informMutation: false, isDragging: false }
39391
39742
  );
39392
39743
  }
39393
39744
  nextElements.forEach((element) => {
@@ -39395,12 +39746,12 @@ var App = class _App extends React43.Component {
39395
39746
  nextElements,
39396
39747
  element.groupIds[element.groupIds.length - 1]
39397
39748
  ).length < 2) {
39398
- mutateElement(
39749
+ this.scene.mutateElement(
39399
39750
  element,
39400
39751
  {
39401
39752
  groupIds: []
39402
39753
  },
39403
- false
39754
+ { informMutation: false, isDragging: false }
39404
39755
  );
39405
39756
  }
39406
39757
  });
@@ -39476,7 +39827,10 @@ var App = class _App extends React43.Component {
39476
39827
  const selectedElements = this.scene.getSelectedElements(this.state);
39477
39828
  if (selectedElements.length === 1) {
39478
39829
  this.setState({
39479
- selectedLinearElement: new LinearElementEditor(hitElement)
39830
+ selectedLinearElement: new LinearElementEditor(
39831
+ hitElement,
39832
+ this.scene.getNonDeletedElementsMap()
39833
+ )
39480
39834
  });
39481
39835
  }
39482
39836
  }
@@ -39568,7 +39922,10 @@ var App = class _App extends React43.Component {
39568
39922
  this
39569
39923
  ),
39570
39924
  // set selectedLinearElement only if thats the only element selected
39571
- selectedLinearElement: newSelectedElements.length === 1 && isLinearElement(newSelectedElements[0]) ? new LinearElementEditor(newSelectedElements[0]) : prevState.selectedLinearElement
39925
+ selectedLinearElement: newSelectedElements.length === 1 && isLinearElement(newSelectedElements[0]) ? new LinearElementEditor(
39926
+ newSelectedElements[0],
39927
+ this.scene.getNonDeletedElementsMap()
39928
+ ) : prevState.selectedLinearElement
39572
39929
  };
39573
39930
  });
39574
39931
  }
@@ -39621,7 +39978,10 @@ var App = class _App extends React43.Component {
39621
39978
  ),
39622
39979
  selectedLinearElement: isLinearElement(hitElement) && // Don't set `selectedLinearElement` if its same as the hitElement, this is mainly to prevent resetting the `hoverPointIndex` to -1.
39623
39980
  // Future we should update the API to take care of setting the correct `hoverPointIndex` when initialized
39624
- prevState.selectedLinearElement?.elementId !== hitElement.id ? new LinearElementEditor(hitElement) : prevState.selectedLinearElement
39981
+ prevState.selectedLinearElement?.elementId !== hitElement.id ? new LinearElementEditor(
39982
+ hitElement,
39983
+ this.scene.getNonDeletedElementsMap()
39984
+ ) : prevState.selectedLinearElement
39625
39985
  }));
39626
39986
  }
39627
39987
  }
@@ -39681,11 +40041,9 @@ var App = class _App extends React43.Component {
39681
40041
  const linearElements = this.scene.getSelectedElements(this.state).filter(isLinearElement);
39682
40042
  bindOrUnbindLinearElements(
39683
40043
  linearElements,
39684
- this.scene.getNonDeletedElementsMap(),
39685
- this.scene.getNonDeletedElements(),
39686
- this.scene,
39687
40044
  isBindingEnabled(this.state),
39688
40045
  this.state.selectedLinearElement?.selectedPointsIndices ?? [],
40046
+ this.scene,
39689
40047
  this.state.zoom
39690
40048
  );
39691
40049
  }
@@ -40136,7 +40494,7 @@ var polyfill = () => {
40136
40494
  var polyfill_default = polyfill;
40137
40495
 
40138
40496
  // data/reconcile.ts
40139
- import throttle4 from "lodash.throttle";
40497
+ import throttle5 from "lodash.throttle";
40140
40498
  var shouldDiscardRemoteElement = (localAppState, local, remote) => {
40141
40499
  if (local && // local element is being edited
40142
40500
  (local.id === localAppState.editingTextElement?.id || local.id === localAppState.resizingElement?.id || local.id === localAppState.newElement?.id || // TODO: Is this still valid? As newElement is selection element, which is never part of the elements array
@@ -40148,7 +40506,7 @@ var shouldDiscardRemoteElement = (localAppState, local, remote) => {
40148
40506
  }
40149
40507
  return false;
40150
40508
  };
40151
- var validateIndicesThrottled = throttle4(
40509
+ var validateIndicesThrottled2 = throttle5(
40152
40510
  (orderedElements, localElements, remoteElements) => {
40153
40511
  if (isDevEnv() || isTestEnv() || window?.DEBUG_FRACTIONAL_INDICES) {
40154
40512
  const elements = syncInvalidIndices(
@@ -40196,7 +40554,7 @@ var reconcileElements = (localElements, remoteElements, localAppState) => {
40196
40554
  }
40197
40555
  }
40198
40556
  const orderedElements = orderByFractionalIndex(reconciledElements);
40199
- validateIndicesThrottled(orderedElements, localElements, remoteElements);
40557
+ validateIndicesThrottled2(orderedElements, localElements, remoteElements);
40200
40558
  syncInvalidIndices(orderedElements);
40201
40559
  return orderedElements;
40202
40560
  };
@@ -40273,7 +40631,8 @@ var ExcalidrawBase = (props) => {
40273
40631
  validateEmbeddable,
40274
40632
  renderEmbeddable,
40275
40633
  aiEnabled,
40276
- showDeprecatedFonts
40634
+ showDeprecatedFonts,
40635
+ renderScrollbars
40277
40636
  } = props;
40278
40637
  const canvasActions = props.UIOptions?.canvasActions;
40279
40638
  const UIOptions = {
@@ -40343,6 +40702,7 @@ var ExcalidrawBase = (props) => {
40343
40702
  renderEmbeddable,
40344
40703
  aiEnabled: aiEnabled !== false,
40345
40704
  showDeprecatedFonts,
40705
+ renderScrollbars,
40346
40706
  children
40347
40707
  }
40348
40708
  ) }) });