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

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 (33) hide show
  1. package/dist/dev/{chunk-KYBDXI6F.js → chunk-GBJ7S76A.js} +120 -260
  2. package/dist/dev/chunk-GBJ7S76A.js.map +7 -0
  3. package/dist/dev/{chunk-H7XJ5UVD.js → chunk-GIMGG4AT.js} +2 -2
  4. package/dist/dev/data/{image-NKFINVKH.js → image-JN3Y4CVN.js} +3 -3
  5. package/dist/dev/index.js +332 -229
  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-KJYFYP64.js → chunk-GKI4RZ6C.js} +1 -1
  10. package/dist/prod/chunk-TVF64BAY.js +33 -0
  11. package/dist/prod/data/image-CZ2OEVDB.js +1 -0
  12. package/dist/prod/index.js +14 -14
  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 +4 -1
  17. package/dist/types/element/src/binding.d.ts +1 -2
  18. package/dist/types/element/src/collision.d.ts +1 -1
  19. package/dist/types/element/src/duplicate.d.ts +10 -13
  20. package/dist/types/element/src/frame.d.ts +1 -1
  21. package/dist/types/element/src/selection.d.ts +11 -0
  22. package/dist/types/element/src/textElement.d.ts +2 -1
  23. package/dist/types/excalidraw/components/App.d.ts +19 -5
  24. package/dist/types/excalidraw/eraser/index.d.ts +14 -0
  25. package/dist/types/excalidraw/lasso/utils.d.ts +1 -2
  26. package/dist/types/excalidraw/types.d.ts +4 -0
  27. package/dist/types/math/src/types.d.ts +1 -0
  28. package/package.json +1 -1
  29. package/dist/dev/chunk-KYBDXI6F.js.map +0 -7
  30. package/dist/prod/chunk-CAN5RS4P.js +0 -31
  31. package/dist/prod/data/image-5XD47O4X.js +0 -1
  32. /package/dist/dev/{chunk-H7XJ5UVD.js.map → chunk-GIMGG4AT.js.map} +0 -0
  33. /package/dist/dev/data/{image-NKFINVKH.js.map → image-JN3Y4CVN.js.map} +0 -0
package/dist/dev/index.js CHANGED
@@ -89,6 +89,7 @@ import {
89
89
  STATS_PANELS,
90
90
  STROKE_WIDTH,
91
91
  SVGStringToFile,
92
+ SVG_DOCUMENT_PREAMBLE,
92
93
  SVG_NS,
93
94
  Scene_default,
94
95
  ShapeCache,
@@ -171,7 +172,6 @@ import {
171
172
  findLastIndex,
172
173
  fixBindingsAfterDeletion,
173
174
  fixDuplicatedBindingsAfterDuplication,
174
- fixReversedBindings,
175
175
  focusNearestParent,
176
176
  frameAndChildrenSelectedTogether,
177
177
  generateIdFromFile,
@@ -248,6 +248,7 @@ import {
248
248
  getSelectedGroupIdForElement,
249
249
  getSelectedGroupIds,
250
250
  getSelectionBoxShape,
251
+ getSelectionStateForElements,
251
252
  getShortcutKey,
252
253
  getSizeFromPoints,
253
254
  getSuggestedBindingsForArrows,
@@ -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-GBJ7S76A.js";
464
466
  import {
465
467
  define_import_meta_env_default
466
- } from "./chunk-H7XJ5UVD.js";
468
+ } from "./chunk-GIMGG4AT.js";
467
469
  import {
468
470
  en_default
469
471
  } from "./chunk-X3RYHLJU.js";
@@ -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));
657
- return;
658
- }
659
- if (!reverseOrder && index > elementsWithClones.length - 1) {
660
- elementsWithClones.push(...castArray(elements2));
654
+ if (index > elementsWithDuplicates.length - 1) {
655
+ elementsWithDuplicates.push(...castArray(elements2));
661
656
  return;
662
657
  }
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) => {
@@ -7447,7 +7446,7 @@ var actionDuplicateSelection = register({
7447
7446
  return false;
7448
7447
  }
7449
7448
  }
7450
- let { newElements: duplicatedElements, elementsWithClones: nextElements } = duplicateElements({
7449
+ let { duplicatedElements, elementsWithDuplicates } = duplicateElements({
7451
7450
  type: "in-place",
7452
7451
  elements,
7453
7452
  idsOfElementsToDuplicate: arrayToMap(
@@ -7458,38 +7457,35 @@ var actionDuplicateSelection = register({
7458
7457
  ),
7459
7458
  appState,
7460
7459
  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
7460
+ overrides: ({ origElement, origIdToDuplicateId }) => {
7461
+ const duplicateFrameId = origElement.frameId && origIdToDuplicateId.get(origElement.frameId);
7462
+ return {
7463
+ x: origElement.x + DEFAULT_GRID_SIZE / 2,
7464
+ y: origElement.y + DEFAULT_GRID_SIZE / 2,
7465
+ frameId: duplicateFrameId ?? origElement.frameId
7466
+ };
7467
+ }
7466
7468
  });
7467
- if (app.props.onDuplicate && nextElements) {
7468
- const mappedElements = app.props.onDuplicate(nextElements, elements);
7469
+ if (app.props.onDuplicate && elementsWithDuplicates) {
7470
+ const mappedElements = app.props.onDuplicate(
7471
+ elementsWithDuplicates,
7472
+ elements
7473
+ );
7469
7474
  if (mappedElements) {
7470
- nextElements = mappedElements;
7475
+ elementsWithDuplicates = mappedElements;
7471
7476
  }
7472
7477
  }
7473
7478
  return {
7474
- elements: syncMovedIndices(nextElements, arrayToMap(duplicatedElements)),
7479
+ elements: syncMovedIndices(
7480
+ elementsWithDuplicates,
7481
+ arrayToMap(duplicatedElements)
7482
+ ),
7475
7483
  appState: {
7476
7484
  ...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
7485
+ ...getSelectionStateForElements(
7486
+ duplicatedElements,
7487
+ getNonDeletedElements(elementsWithDuplicates),
7488
+ appState
7493
7489
  )
7494
7490
  },
7495
7491
  captureUpdate: CaptureUpdateAction.IMMEDIATELY
@@ -7510,24 +7506,6 @@ var actionDuplicateSelection = register({
7510
7506
  }
7511
7507
  )
7512
7508
  });
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
7509
 
7532
7510
  // actions/actionProperties.tsx
7533
7511
  import { useEffect as useEffect13, useMemo as useMemo4, useRef as useRef11, useState as useState6 } from "react";
@@ -12772,7 +12750,9 @@ var exportCanvas = async (type, elements, appState, files, {
12772
12750
  if (type === "svg") {
12773
12751
  return fileSave(
12774
12752
  svgPromise.then((svg) => {
12775
- return new Blob([svg.outerHTML], { type: MIME_TYPES.svg });
12753
+ return new Blob([SVG_DOCUMENT_PREAMBLE + svg.outerHTML], {
12754
+ type: MIME_TYPES.svg
12755
+ });
12776
12756
  }),
12777
12757
  {
12778
12758
  description: "Export to SVG",
@@ -12802,7 +12782,7 @@ var exportCanvas = async (type, elements, appState, files, {
12802
12782
  let blob = canvasToBlob(tempCanvas);
12803
12783
  if (appState.exportEmbedScene) {
12804
12784
  blob = blob.then(
12805
- (blob2) => import("./data/image-NKFINVKH.js").then(
12785
+ (blob2) => import("./data/image-JN3Y4CVN.js").then(
12806
12786
  ({ encodePngMetadata: encodePngMetadata2 }) => encodePngMetadata2({
12807
12787
  blob: blob2,
12808
12788
  metadata: serializeAsJSON(elements, appState, files, "local")
@@ -14892,7 +14872,8 @@ var actionBindText = register({
14892
14872
  containerId: container.id,
14893
14873
  verticalAlign: VERTICAL_ALIGN.MIDDLE,
14894
14874
  textAlign: TEXT_ALIGN.CENTER,
14895
- autoResize: true
14875
+ autoResize: true,
14876
+ angle: isArrowElement(container) ? 0 : container?.angle ?? 0
14896
14877
  });
14897
14878
  mutateElement(container, {
14898
14879
  boundElements: (container.boundElements || []).concat({
@@ -18142,7 +18123,7 @@ function LibraryMenuItems({
18142
18123
  type: "everything",
18143
18124
  elements: item.elements,
18144
18125
  randomizeSeed: true
18145
- }).newElements
18126
+ }).duplicatedElements
18146
18127
  };
18147
18128
  });
18148
18129
  },
@@ -24341,6 +24322,161 @@ var LassoTrail = class extends AnimatedTrail {
24341
24322
  }
24342
24323
  };
24343
24324
 
24325
+ // eraser/index.ts
24326
+ var POINTS_ON_TRAIL = 2;
24327
+ var EraserTrail = class extends AnimatedTrail {
24328
+ constructor(animationFrameHandler, app) {
24329
+ super(animationFrameHandler, app, {
24330
+ streamline: 0.2,
24331
+ size: 5,
24332
+ keepHead: true,
24333
+ sizeMapping: (c) => {
24334
+ const DECAY_TIME = 200;
24335
+ const DECAY_LENGTH = 10;
24336
+ const t2 = Math.max(
24337
+ 0,
24338
+ 1 - (performance.now() - c.pressure) / DECAY_TIME
24339
+ );
24340
+ const l = (DECAY_LENGTH - Math.min(DECAY_LENGTH, c.totalLength - c.currentIndex)) / DECAY_LENGTH;
24341
+ return Math.min(easeOut(l), easeOut(t2));
24342
+ },
24343
+ fill: () => app.state.theme === THEME.LIGHT ? "rgba(0, 0, 0, 0.2)" : "rgba(255, 255, 255, 0.2)"
24344
+ });
24345
+ __publicField(this, "elementsToErase", /* @__PURE__ */ new Set());
24346
+ __publicField(this, "groupsToErase", /* @__PURE__ */ new Set());
24347
+ __publicField(this, "segmentsCache", /* @__PURE__ */ new Map());
24348
+ __publicField(this, "geometricShapesCache", /* @__PURE__ */ new Map());
24349
+ }
24350
+ startPath(x, y) {
24351
+ this.endPath();
24352
+ super.startPath(x, y);
24353
+ this.elementsToErase.clear();
24354
+ }
24355
+ addPointToPath(x, y, restore2 = false) {
24356
+ super.addPointToPath(x, y);
24357
+ const elementsToEraser = this.updateElementsToBeErased(restore2);
24358
+ return elementsToEraser;
24359
+ }
24360
+ updateElementsToBeErased(restoreToErase) {
24361
+ let eraserPath = super.getCurrentTrail()?.originalPoints?.map((p) => pointFrom(p[0], p[1])) || [];
24362
+ eraserPath = eraserPath?.slice(eraserPath.length - POINTS_ON_TRAIL);
24363
+ const candidateElements = this.app.visibleElements.filter(
24364
+ (el) => !el.locked
24365
+ );
24366
+ const candidateElementsMap = arrayToMap(candidateElements);
24367
+ const pathSegments = eraserPath.reduce((acc, point, index) => {
24368
+ if (index === 0) {
24369
+ return acc;
24370
+ }
24371
+ acc.push(lineSegment(eraserPath[index - 1], point));
24372
+ return acc;
24373
+ }, []);
24374
+ if (pathSegments.length === 0) {
24375
+ return [];
24376
+ }
24377
+ for (const element of candidateElements) {
24378
+ if (restoreToErase && this.elementsToErase.has(element.id)) {
24379
+ const intersects = eraserTest(
24380
+ pathSegments,
24381
+ element,
24382
+ this.segmentsCache,
24383
+ this.geometricShapesCache,
24384
+ candidateElementsMap,
24385
+ this.app
24386
+ );
24387
+ if (intersects) {
24388
+ const shallowestGroupId = element.groupIds.at(-1);
24389
+ if (this.groupsToErase.has(shallowestGroupId)) {
24390
+ const elementsInGroup = getElementsInGroup(
24391
+ this.app.scene.getNonDeletedElementsMap(),
24392
+ shallowestGroupId
24393
+ );
24394
+ for (const elementInGroup of elementsInGroup) {
24395
+ this.elementsToErase.delete(elementInGroup.id);
24396
+ }
24397
+ this.groupsToErase.delete(shallowestGroupId);
24398
+ }
24399
+ if (isBoundToContainer(element)) {
24400
+ this.elementsToErase.delete(element.containerId);
24401
+ }
24402
+ if (hasBoundTextElement(element)) {
24403
+ const boundText = getBoundTextElementId(element);
24404
+ if (boundText) {
24405
+ this.elementsToErase.delete(boundText);
24406
+ }
24407
+ }
24408
+ this.elementsToErase.delete(element.id);
24409
+ }
24410
+ } else if (!restoreToErase && !this.elementsToErase.has(element.id)) {
24411
+ const intersects = eraserTest(
24412
+ pathSegments,
24413
+ element,
24414
+ this.segmentsCache,
24415
+ this.geometricShapesCache,
24416
+ candidateElementsMap,
24417
+ this.app
24418
+ );
24419
+ if (intersects) {
24420
+ const shallowestGroupId = element.groupIds.at(-1);
24421
+ if (!this.groupsToErase.has(shallowestGroupId)) {
24422
+ const elementsInGroup = getElementsInGroup(
24423
+ this.app.scene.getNonDeletedElementsMap(),
24424
+ shallowestGroupId
24425
+ );
24426
+ for (const elementInGroup of elementsInGroup) {
24427
+ this.elementsToErase.add(elementInGroup.id);
24428
+ }
24429
+ this.groupsToErase.add(shallowestGroupId);
24430
+ }
24431
+ if (hasBoundTextElement(element)) {
24432
+ const boundText = getBoundTextElementId(element);
24433
+ if (boundText) {
24434
+ this.elementsToErase.add(boundText);
24435
+ }
24436
+ }
24437
+ if (isBoundToContainer(element)) {
24438
+ this.elementsToErase.add(element.containerId);
24439
+ }
24440
+ this.elementsToErase.add(element.id);
24441
+ }
24442
+ }
24443
+ }
24444
+ return Array.from(this.elementsToErase);
24445
+ }
24446
+ endPath() {
24447
+ super.endPath();
24448
+ super.clearTrails();
24449
+ this.elementsToErase.clear();
24450
+ this.groupsToErase.clear();
24451
+ this.segmentsCache.clear();
24452
+ }
24453
+ };
24454
+ var eraserTest = (pathSegments, element, elementsSegments, shapesCache, elementsMap, app) => {
24455
+ let shape = shapesCache.get(element.id);
24456
+ if (!shape) {
24457
+ shape = getElementShape(element, elementsMap);
24458
+ shapesCache.set(element.id, shape);
24459
+ }
24460
+ const lastPoint = pathSegments[pathSegments.length - 1][1];
24461
+ if (shouldTestInside(element) && isPointInShape(lastPoint, shape)) {
24462
+ return true;
24463
+ }
24464
+ let elementSegments = elementsSegments.get(element.id);
24465
+ if (!elementSegments) {
24466
+ elementSegments = getElementLineSegments(element, elementsMap);
24467
+ elementsSegments.set(element.id, elementSegments);
24468
+ }
24469
+ return pathSegments.some(
24470
+ (pathSegment) => elementSegments?.some(
24471
+ (elementSegment) => lineSegmentIntersectionPoints(
24472
+ pathSegment,
24473
+ elementSegment,
24474
+ app.getElementHitThreshold()
24475
+ ) !== null
24476
+ )
24477
+ );
24478
+ };
24479
+
24344
24480
  // components/BraveMeasureTextError.tsx
24345
24481
  import { jsx as jsx77, jsxs as jsxs42 } from "react/jsx-runtime";
24346
24482
  var BraveMeasureTextError = () => {
@@ -32040,19 +32176,7 @@ var App = class _App extends React43.Component {
32040
32176
  __publicField(this, "lastViewportPosition", { x: 0, y: 0 });
32041
32177
  __publicField(this, "animationFrameHandler", new AnimationFrameHandler());
32042
32178
  __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
- }));
32179
+ __publicField(this, "eraserTrail", new EraserTrail(this.animationFrameHandler, this));
32056
32180
  __publicField(this, "lassoTrail", new LassoTrail(this.animationFrameHandler, this));
32057
32181
  __publicField(this, "onChangeEmitter", new Emitter());
32058
32182
  __publicField(this, "onPointerDownEmitter", new Emitter());
@@ -32891,7 +33015,7 @@ var App = class _App extends React43.Component {
32891
33015
  const dx = x - elementsCenterX;
32892
33016
  const dy = y - elementsCenterY;
32893
33017
  const [gridX, gridY] = getGridPoint(dx, dy, this.getEffectiveGridSize());
32894
- const { newElements } = duplicateElements({
33018
+ const { duplicatedElements } = duplicateElements({
32895
33019
  type: "everything",
32896
33020
  elements: elements.map((element) => {
32897
33021
  return newElementWith(element, {
@@ -32902,17 +33026,17 @@ var App = class _App extends React43.Component {
32902
33026
  randomizeSeed: !opts.retainSeed
32903
33027
  });
32904
33028
  const prevElements = this.scene.getElementsIncludingDeleted();
32905
- let nextElements = [...prevElements, ...newElements];
33029
+ let nextElements = [...prevElements, ...duplicatedElements];
32906
33030
  const mappedNewSceneElements = this.props.onDuplicate?.(
32907
33031
  nextElements,
32908
33032
  prevElements
32909
33033
  );
32910
33034
  nextElements = mappedNewSceneElements || nextElements;
32911
- syncMovedIndices(nextElements, arrayToMap(newElements));
33035
+ syncMovedIndices(nextElements, arrayToMap(duplicatedElements));
32912
33036
  const topLayerFrame = this.getTopLayerFrameAtSceneCoords({ x, y });
32913
33037
  if (topLayerFrame) {
32914
33038
  const eligibleElements = filterElementsEligibleAsFrameChildren(
32915
- newElements,
33039
+ duplicatedElements,
32916
33040
  topLayerFrame
32917
33041
  );
32918
33042
  addElementsToFrame(
@@ -32923,7 +33047,7 @@ var App = class _App extends React43.Component {
32923
33047
  );
32924
33048
  }
32925
33049
  this.scene.replaceAllElements(nextElements);
32926
- newElements.forEach((newElement2) => {
33050
+ duplicatedElements.forEach((newElement2) => {
32927
33051
  if (isTextElement(newElement2) && isBoundToContainer(newElement2)) {
32928
33052
  const container = getContainerElement(
32929
33053
  newElement2,
@@ -32937,7 +33061,7 @@ var App = class _App extends React43.Component {
32937
33061
  }
32938
33062
  });
32939
33063
  if (isSafari) {
32940
- Fonts.loadElementsFonts(newElements).then((fontFaces) => {
33064
+ Fonts.loadElementsFonts(duplicatedElements).then((fontFaces) => {
32941
33065
  this.fonts.onLoaded(fontFaces);
32942
33066
  });
32943
33067
  }
@@ -32945,7 +33069,7 @@ var App = class _App extends React43.Component {
32945
33069
  this.addMissingFiles(opts.files);
32946
33070
  }
32947
33071
  this.store.shouldCaptureIncrement();
32948
- const nextElementsToSelect = excludeElementsInFramesFromSelection(newElements);
33072
+ const nextElementsToSelect = excludeElementsInFramesFromSelection(duplicatedElements);
32949
33073
  this.setState(
32950
33074
  {
32951
33075
  ...this.state,
@@ -32981,7 +33105,7 @@ var App = class _App extends React43.Component {
32981
33105
  );
32982
33106
  this.setActiveTool({ type: "selection" });
32983
33107
  if (opts.fitToContent) {
32984
- this.scrollToContent(newElements, {
33108
+ this.scrollToContent(duplicatedElements, {
32985
33109
  fitToContent: true,
32986
33110
  canvasOffsets: this.getEditorUIOffsets()
32987
33111
  });
@@ -33965,7 +34089,7 @@ var App = class _App extends React43.Component {
33965
34089
  x: sceneX,
33966
34090
  y: sceneY
33967
34091
  });
33968
- const element = existingTextElement ? existingTextElement : newTextElement({
34092
+ const element = existingTextElement || newTextElement({
33969
34093
  x: parentCenterPosition ? parentCenterPosition.elementCenterX : sceneX,
33970
34094
  y: parentCenterPosition ? parentCenterPosition.elementCenterY : sceneY,
33971
34095
  strokeColor: this.state.currentItemStrokeColor,
@@ -33983,7 +34107,7 @@ var App = class _App extends React43.Component {
33983
34107
  containerId: shouldBindToContainer ? container?.id : void 0,
33984
34108
  groupIds: container?.groupIds ?? [],
33985
34109
  lineHeight,
33986
- angle: container?.angle ?? 0,
34110
+ angle: container ? isArrowElement(container) ? 0 : container.angle : 0,
33987
34111
  frameId: topLayerFrame ? topLayerFrame.id : null
33988
34112
  });
33989
34113
  if (!existingTextElement && shouldBindToContainer && container) {
@@ -34601,80 +34725,14 @@ var App = class _App extends React43.Component {
34601
34725
  }));
34602
34726
  }
34603
34727
  });
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)
34728
+ __publicField(this, "handleEraser", (event, scenePointer) => {
34729
+ const elementsToErase = this.eraserTrail.addPointToPath(
34730
+ scenePointer.x,
34731
+ scenePointer.y,
34732
+ event.altKey
34644
34733
  );
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
- }
34734
+ this.elementsPendingErasure = new Set(elementsToErase);
34735
+ this.triggerRender();
34678
34736
  });
34679
34737
  // set touch moving for mobile context menu
34680
34738
  __publicField(this, "handleTouchMove", (event) => {
@@ -37225,8 +37283,8 @@ var App = class _App extends React43.Component {
37225
37283
  {
37226
37284
  trails: [
37227
37285
  this.laserTrails,
37228
- this.eraserTrail,
37229
- this.lassoTrail
37286
+ this.lassoTrail,
37287
+ this.eraserTrail
37230
37288
  ]
37231
37289
  }
37232
37290
  ),
@@ -38378,7 +38436,8 @@ var App = class _App extends React43.Component {
38378
38436
  },
38379
38437
  drag: {
38380
38438
  hasOccurred: false,
38381
- offset: null
38439
+ offset: null,
38440
+ origin: { ...origin }
38382
38441
  },
38383
38442
  eventListeners: {
38384
38443
  onMove: null,
@@ -38557,7 +38616,7 @@ var App = class _App extends React43.Component {
38557
38616
  return;
38558
38617
  }
38559
38618
  if (isEraserActive(this.state)) {
38560
- this.handleEraser(event, pointerDownState, pointerCoords);
38619
+ this.handleEraser(event, pointerCoords);
38561
38620
  return;
38562
38621
  }
38563
38622
  if (this.state.activeTool.type === "laser") {
@@ -38678,8 +38737,8 @@ var App = class _App extends React43.Component {
38678
38737
  pointerDownState.drag.hasOccurred = true;
38679
38738
  if (selectedElements.length > 0 && !pointerDownState.withCmdOrCtrl && !this.state.editingTextElement && this.state.activeEmbeddable?.state !== "active") {
38680
38739
  const dragOffset = {
38681
- x: pointerCoords.x - pointerDownState.origin.x,
38682
- y: pointerCoords.y - pointerDownState.origin.y
38740
+ x: pointerCoords.x - pointerDownState.drag.origin.x,
38741
+ y: pointerCoords.y - pointerDownState.drag.origin.y
38683
38742
  };
38684
38743
  const originalElements = [
38685
38744
  ...pointerDownState.originalElements.values()
@@ -38818,46 +38877,90 @@ var App = class _App extends React43.Component {
38818
38877
  const idsOfElementsToDuplicate = new Map(
38819
38878
  selectedElements2.map((el) => [el.id, el])
38820
38879
  );
38821
- const { newElements: clonedElements, elementsWithClones } = duplicateElements({
38880
+ const {
38881
+ duplicatedElements,
38882
+ duplicateElementsMap,
38883
+ elementsWithDuplicates,
38884
+ origIdToDuplicateId
38885
+ } = duplicateElements({
38822
38886
  type: "in-place",
38823
38887
  elements,
38824
38888
  appState: this.state,
38825
38889
  randomizeSeed: true,
38826
38890
  idsOfElementsToDuplicate,
38827
- overrides: (el) => {
38891
+ overrides: ({ duplicateElement: duplicateElement2, origElement }) => {
38892
+ return {
38893
+ // reset to the original element's frameId (unless we've
38894
+ // duplicated alongside a frame in which case we need to
38895
+ // keep the duplicate frame's id) so that the element
38896
+ // frame membership is refreshed on pointerup
38897
+ // NOTE this is a hacky solution and should be done
38898
+ // differently
38899
+ frameId: duplicateElement2.frameId ?? origElement.frameId,
38900
+ seed: randomInteger()
38901
+ };
38902
+ }
38903
+ });
38904
+ duplicatedElements.forEach((element) => {
38905
+ pointerDownState.originalElements.set(
38906
+ element.id,
38907
+ deepCopyElement(element)
38908
+ );
38909
+ });
38910
+ const mappedClonedElements = elementsWithDuplicates.map((el) => {
38911
+ if (idsOfElementsToDuplicate.has(el.id)) {
38828
38912
  const origEl = pointerDownState.originalElements.get(el.id);
38829
38913
  if (origEl) {
38830
- return {
38914
+ return newElementWith(el, {
38831
38915
  x: origEl.x,
38832
- y: origEl.y,
38833
- seed: origEl.seed
38834
- };
38916
+ y: origEl.y
38917
+ });
38835
38918
  }
38836
- return {};
38837
- },
38838
- reverseOrder: true
38839
- });
38840
- clonedElements.forEach((element) => {
38841
- pointerDownState.originalElements.set(element.id, element);
38919
+ }
38920
+ return el;
38842
38921
  });
38843
38922
  const mappedNewSceneElements = this.props.onDuplicate?.(
38844
- elementsWithClones,
38923
+ mappedClonedElements,
38845
38924
  elements
38846
38925
  );
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
- });
38926
+ const elementsWithIndices = syncMovedIndices(
38927
+ mappedNewSceneElements || mappedClonedElements,
38928
+ arrayToMap(duplicatedElements)
38929
+ );
38930
+ flushSync2(() => {
38931
+ if (pointerDownState.hit.element) {
38932
+ const cloneId = origIdToDuplicateId.get(
38933
+ pointerDownState.hit.element.id
38934
+ );
38935
+ const clonedElement = cloneId && duplicateElementsMap.get(cloneId);
38936
+ pointerDownState.hit.element = clonedElement || null;
38855
38937
  }
38856
- return el;
38938
+ pointerDownState.hit.allHitElements = pointerDownState.hit.allHitElements.reduce(
38939
+ (acc, origHitElement) => {
38940
+ const cloneId = origIdToDuplicateId.get(origHitElement.id);
38941
+ const clonedElement = cloneId && duplicateElementsMap.get(cloneId);
38942
+ if (clonedElement) {
38943
+ acc.push(clonedElement);
38944
+ }
38945
+ return acc;
38946
+ },
38947
+ []
38948
+ );
38949
+ pointerDownState.drag.origin = viewportCoordsToSceneCoords(
38950
+ event,
38951
+ this.state
38952
+ );
38953
+ this.setState((prevState) => ({
38954
+ ...getSelectionStateForElements(
38955
+ duplicatedElements,
38956
+ this.scene.getNonDeletedElements(),
38957
+ prevState
38958
+ )
38959
+ }));
38960
+ this.scene.replaceAllElements(elementsWithIndices);
38961
+ this.maybeCacheVisibleGaps(event, selectedElements2, true);
38962
+ this.maybeCacheReferenceSnapPoints(event, selectedElements2, true);
38857
38963
  });
38858
- this.scene.replaceAllElements(nextSceneElements);
38859
- this.maybeCacheVisibleGaps(event, selectedElements2, true);
38860
- this.maybeCacheReferenceSnapPoints(event, selectedElements2, true);
38861
38964
  }
38862
38965
  return;
38863
38966
  }