@excalidraw/element 0.18.0-4e758db → 0.18.0-4f68076

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 (80) hide show
  1. package/dist/dev/index.js +936 -437
  2. package/dist/dev/index.js.map +4 -4
  3. package/dist/prod/index.js +16 -15
  4. package/dist/types/common/src/constants.d.ts +10 -0
  5. package/dist/types/common/src/utils.d.ts +5 -0
  6. package/dist/types/element/src/binding.d.ts +13 -7
  7. package/dist/types/element/src/collision.d.ts +14 -5
  8. package/dist/types/element/src/duplicate.d.ts +60 -0
  9. package/dist/types/element/src/index.d.ts +1 -0
  10. package/dist/types/element/src/listMarker.d.ts +34 -0
  11. package/dist/types/element/src/newElement.d.ts +15 -1
  12. package/dist/types/element/src/renderElement.d.ts +21 -4
  13. package/dist/types/element/src/shape.d.ts +15 -1
  14. package/dist/types/element/src/textElement.d.ts +7 -0
  15. package/dist/types/element/src/utils.d.ts +8 -1
  16. package/dist/types/element/src/zindex.d.ts +1 -1
  17. package/dist/types/excalidraw/actions/actionAddToLibrary.d.ts +30 -3
  18. package/dist/types/excalidraw/actions/actionBoundText.d.ts +20 -2
  19. package/dist/types/excalidraw/actions/actionCanvas.d.ts +70 -7
  20. package/dist/types/excalidraw/actions/actionClipboard.d.ts +20 -2
  21. package/dist/types/excalidraw/actions/actionCropEditor.d.ts +10 -1
  22. package/dist/types/excalidraw/actions/actionDeleteSelected.d.ts +30 -3
  23. package/dist/types/excalidraw/actions/actionDeselect.d.ts +13 -1
  24. package/dist/types/excalidraw/actions/actionElementLink.d.ts +10 -1
  25. package/dist/types/excalidraw/actions/actionElementLock.d.ts +20 -2
  26. package/dist/types/excalidraw/actions/actionExport.d.ts +20 -2
  27. package/dist/types/excalidraw/actions/actionFrame.d.ts +30 -3
  28. package/dist/types/excalidraw/actions/actionGroup.d.ts +20 -2
  29. package/dist/types/excalidraw/actions/actionLinearEditor.d.ts +10 -1
  30. package/dist/types/excalidraw/actions/actionLink.d.ts +10 -1
  31. package/dist/types/excalidraw/actions/actionMenu.d.ts +10 -1
  32. package/dist/types/excalidraw/actions/actionProperties.d.ts +20 -2
  33. package/dist/types/excalidraw/actions/actionSelectAll.d.ts +10 -1
  34. package/dist/types/excalidraw/actions/actionStyles.d.ts +10 -1
  35. package/dist/types/excalidraw/actions/actionToggleArrowBinding.d.ts +10 -1
  36. package/dist/types/excalidraw/actions/actionToggleGridMode.d.ts +10 -1
  37. package/dist/types/excalidraw/actions/actionToggleMidpointSnapping.d.ts +10 -1
  38. package/dist/types/excalidraw/actions/actionToggleObjectsSnapMode.d.ts +10 -1
  39. package/dist/types/excalidraw/actions/actionToggleSearchMenu.d.ts +10 -1
  40. package/dist/types/excalidraw/actions/actionToggleShowHints.d.ts +197 -0
  41. package/dist/types/excalidraw/actions/actionToggleStats.d.ts +10 -1
  42. package/dist/types/excalidraw/actions/actionToggleViewMode.d.ts +10 -1
  43. package/dist/types/excalidraw/actions/actionToggleZenMode.d.ts +10 -1
  44. package/dist/types/excalidraw/actions/index.d.ts +1 -0
  45. package/dist/types/excalidraw/actions/types.d.ts +1 -1
  46. package/dist/types/excalidraw/appState.d.ts +2 -0
  47. package/dist/types/excalidraw/components/App.arrowText.d.ts +5 -39
  48. package/dist/types/excalidraw/components/App.clipboard.d.ts +31 -0
  49. package/dist/types/excalidraw/components/App.d.ts +82 -262
  50. package/dist/types/excalidraw/components/App.duplicate.d.ts +101 -0
  51. package/dist/types/excalidraw/components/App.modifiers.d.ts +38 -0
  52. package/dist/types/excalidraw/components/App.pan.d.ts +62 -0
  53. package/dist/types/excalidraw/components/App.selectionTool.d.ts +57 -0
  54. package/dist/types/excalidraw/components/App.text.d.ts +146 -0
  55. package/dist/types/excalidraw/components/App.textTool.d.ts +140 -0
  56. package/dist/types/excalidraw/components/App.viewport.d.ts +41 -1
  57. package/dist/types/excalidraw/components/App.wheel.d.ts +3 -10
  58. package/dist/types/excalidraw/components/ColorPicker/Picker.d.ts +2 -0
  59. package/dist/types/excalidraw/components/ColorPicker/colorTopPicksDnD.d.ts +11 -0
  60. package/dist/types/excalidraw/components/FileDropOverlay.d.ts +2 -0
  61. package/dist/types/excalidraw/components/FontPicker/FontPicker.d.ts +4 -1
  62. package/dist/types/excalidraw/components/FontPicker/FontPickerList.d.ts +6 -1
  63. package/dist/types/excalidraw/components/FontPicker/fontTopPicksDnD.d.ts +12 -0
  64. package/dist/types/excalidraw/components/LibraryMenuSection.d.ts +6 -2
  65. package/dist/types/excalidraw/components/LibraryUnit.d.ts +7 -2
  66. package/dist/types/excalidraw/components/Tooltip.d.ts +15 -1
  67. package/dist/types/excalidraw/components/TopPicksDnD/TopPicksContextMenu.d.ts +14 -0
  68. package/dist/types/excalidraw/components/TopPicksDnD/TopPicksTip.d.ts +11 -0
  69. package/dist/types/excalidraw/components/TopPicksDnD/topPicksDnD.d.ts +58 -0
  70. package/dist/types/excalidraw/components/main-menu/DefaultItems.d.ts +1 -0
  71. package/dist/types/excalidraw/data/blob.d.ts +20 -2
  72. package/dist/types/excalidraw/data/json.d.ts +10 -1
  73. package/dist/types/excalidraw/renderOverrides.d.ts +9 -0
  74. package/dist/types/excalidraw/scene/Renderer.d.ts +14 -1
  75. package/dist/types/excalidraw/scene/types.d.ts +2 -1
  76. package/dist/types/excalidraw/types.d.ts +92 -19
  77. package/dist/types/excalidraw/viewport.d.ts +23 -1
  78. package/dist/types/excalidraw/wysiwyg/textWysiwyg.d.ts +12 -1
  79. package/package.json +4 -4
  80. package/dist/types/excalidraw/components/ColorPicker/topPicksDnD.d.ts +0 -31
package/dist/dev/index.js CHANGED
@@ -2970,7 +2970,8 @@ var LaserPointer = class _LaserPointer {
2970
2970
  import {
2971
2971
  pointFrom as pointFrom12,
2972
2972
  pointDistance as pointDistance6,
2973
- pointRotateRads as pointRotateRads11
2973
+ pointRotateRads as pointRotateRads11,
2974
+ polygonFromPoints as polygonFromPoints2
2974
2975
  } from "@excalidraw/math";
2975
2976
  import {
2976
2977
  ROUGHNESS,
@@ -2986,6 +2987,7 @@ import {
2986
2987
  // src/renderElement.ts
2987
2988
  init_define_import_meta_env();
2988
2989
  import {
2990
+ clamp as clamp8,
2989
2991
  isRightAngleRads,
2990
2992
  lineSegment as lineSegment5,
2991
2993
  pointFrom as pointFrom11,
@@ -3009,7 +3011,8 @@ import {
3009
3011
  STICKY_NOTE_EDGE_SHADOW_OPACITY,
3010
3012
  STICKY_NOTE_EDGE_SHADOW_WIDTH,
3011
3013
  STICKY_NOTE_FOOTER as STICKY_NOTE_FOOTER2,
3012
- STICKY_NOTE_SHADOW_OPACITY
3014
+ STICKY_NOTE_SHADOW_OPACITY,
3015
+ DEFAULT_ZOOM as DEFAULT_ZOOM2
3013
3016
  } from "@excalidraw/common";
3014
3017
 
3015
3018
  // src/cropElement.ts
@@ -3485,7 +3488,7 @@ import {
3485
3488
  lineSegmentClosestParameter,
3486
3489
  lineSegmentPointAt,
3487
3490
  curveClosestParameter,
3488
- clamp as clamp6,
3491
+ clamp as clamp7,
3489
3492
  bezierEquation as bezierEquation2,
3490
3493
  isCurve
3491
3494
  } from "@excalidraw/math";
@@ -3518,7 +3521,7 @@ import {
3518
3521
  } from "@excalidraw/common";
3519
3522
  import {
3520
3523
  PRECISION as PRECISION2,
3521
- clamp as clamp5,
3524
+ clamp as clamp6,
3522
3525
  lineSegment as lineSegment4,
3523
3526
  pointDistance as pointDistance4,
3524
3527
  pointDistanceSq,
@@ -3544,6 +3547,7 @@ import {
3544
3547
  pointFromVector as pointFromVector5,
3545
3548
  pointRotateRads as pointRotateRads7,
3546
3549
  pointsEqual as pointsEqual4,
3550
+ polygonIncludesPoint,
3547
3551
  vectorFromPoint as vectorFromPoint6,
3548
3552
  vectorNormalize as vectorNormalize3,
3549
3553
  vectorScale as vectorScale6
@@ -3564,6 +3568,7 @@ import {
3564
3568
  } from "@excalidraw/common";
3565
3569
  import {
3566
3570
  bezierEquation,
3571
+ clamp as clamp5,
3567
3572
  curve as curve3,
3568
3573
  curveCatmullRomCubicApproxPoints,
3569
3574
  curveOffsetPoints,
@@ -3820,7 +3825,8 @@ import {
3820
3825
  isAnyTrue,
3821
3826
  getSizeFromPoints,
3822
3827
  isDevEnv as isDevEnv2,
3823
- arrayToMap
3828
+ arrayToMap,
3829
+ DEFAULT_ZOOM
3824
3830
  } from "@excalidraw/common";
3825
3831
 
3826
3832
  // src/distance.ts
@@ -3828,7 +3834,8 @@ init_define_import_meta_env();
3828
3834
  import {
3829
3835
  curvePointDistance,
3830
3836
  distanceToLineSegment,
3831
- pointRotateRads as pointRotateRads3
3837
+ pointRotateRads as pointRotateRads3,
3838
+ polygonIncludesPointNonZero
3832
3839
  } from "@excalidraw/math";
3833
3840
  import { ellipse as ellipse2, ellipseDistanceFromPoint } from "@excalidraw/math/ellipse";
3834
3841
  var distanceToElement = (element, elementsMap, p) => {
@@ -3849,8 +3856,9 @@ var distanceToElement = (element, elementsMap, p) => {
3849
3856
  return distanceToEllipseElement(element, elementsMap, p);
3850
3857
  case "line":
3851
3858
  case "arrow":
3852
- case "freedraw":
3853
3859
  return distanceToLinearOrFreeDraElement(element, elementsMap, p);
3860
+ case "freedraw":
3861
+ return distanceToFreeDrawElement(element, elementsMap, p);
3854
3862
  }
3855
3863
  };
3856
3864
  var distanceToRectanguloidElement = (element, elementsMap, p) => {
@@ -3889,6 +3897,17 @@ var distanceToLinearOrFreeDraElement = (element, elementsMap, p) => {
3889
3897
  ...curves.map((a2) => curvePointDistance(a2, p))
3890
3898
  );
3891
3899
  };
3900
+ var distanceToFreeDrawElement = (element, elementsMap, p) => {
3901
+ const [lines] = deconstructLinearOrFreeDrawElement(element, elementsMap);
3902
+ if (lines.length === 0) {
3903
+ return Infinity;
3904
+ }
3905
+ const polygon3 = lines.map((line2) => line2[0]);
3906
+ if (polygonIncludesPointNonZero(p, polygon3)) {
3907
+ return 0;
3908
+ }
3909
+ return Math.min(...lines.map((s) => distanceToLineSegment(p, s)));
3910
+ };
3892
3911
 
3893
3912
  // src/heading.ts
3894
3913
  init_define_import_meta_env();
@@ -4802,22 +4821,13 @@ var updateElbowArrowPoints = (arrow, elementsMap, updates, options) => {
4802
4821
  var getElbowArrowData = (arrow, elementsMap, nextPoints, options) => {
4803
4822
  const origStartGlobalPoint = pointTranslate(nextPoints[0], vector2(arrow.x, arrow.y));
4804
4823
  const origEndGlobalPoint = pointTranslate(nextPoints[nextPoints.length - 1], vector2(arrow.x, arrow.y));
4824
+ const zoom = options?.zoom ?? DEFAULT_ZOOM;
4805
4825
  let hoveredStartElement = null;
4806
4826
  let hoveredEndElement = null;
4807
4827
  if (options?.isDragging && options?.isBindingEnabled !== false) {
4808
4828
  const elements = Array.from(elementsMap.values());
4809
- hoveredStartElement = getHoveredElement(
4810
- origStartGlobalPoint,
4811
- elementsMap,
4812
- elements,
4813
- options?.zoom
4814
- ) || null;
4815
- hoveredEndElement = getHoveredElement(
4816
- origEndGlobalPoint,
4817
- elementsMap,
4818
- elements,
4819
- options?.zoom
4820
- ) || null;
4829
+ hoveredStartElement = getHoveredElement(origStartGlobalPoint, elementsMap, elements, zoom) || null;
4830
+ hoveredEndElement = getHoveredElement(origEndGlobalPoint, elementsMap, elements, zoom) || null;
4821
4831
  } else {
4822
4832
  hoveredStartElement = arrow.startBinding ? getBindableElementForId(arrow.startBinding.elementId, elementsMap) || null : null;
4823
4833
  hoveredEndElement = arrow.endBinding ? getBindableElementForId(arrow.endBinding.elementId, elementsMap) || null : null;
@@ -4833,6 +4843,7 @@ var getElbowArrowData = (arrow, elementsMap, nextPoints, options) => {
4833
4843
  "start",
4834
4844
  arrow.startBinding?.fixedPoint,
4835
4845
  origStartGlobalPoint,
4846
+ zoom,
4836
4847
  hoveredStartElement,
4837
4848
  elementsMap,
4838
4849
  options?.isDragging,
@@ -4850,6 +4861,7 @@ var getElbowArrowData = (arrow, elementsMap, nextPoints, options) => {
4850
4861
  "end",
4851
4862
  arrow.endBinding?.fixedPoint,
4852
4863
  origEndGlobalPoint,
4864
+ zoom,
4853
4865
  hoveredEndElement,
4854
4866
  elementsMap,
4855
4867
  options?.isDragging,
@@ -4862,7 +4874,7 @@ var getElbowArrowData = (arrow, elementsMap, nextPoints, options) => {
4862
4874
  hoveredStartElement,
4863
4875
  origStartGlobalPoint,
4864
4876
  elementsMap,
4865
- options?.zoom
4877
+ zoom
4866
4878
  );
4867
4879
  const endHeading = getBindPointHeading(
4868
4880
  endGlobalPoint,
@@ -4870,7 +4882,7 @@ var getElbowArrowData = (arrow, elementsMap, nextPoints, options) => {
4870
4882
  hoveredEndElement,
4871
4883
  origEndGlobalPoint,
4872
4884
  elementsMap,
4873
- options?.zoom
4885
+ zoom
4874
4886
  );
4875
4887
  const startPointBounds = [
4876
4888
  startGlobalPoint[0] - 2,
@@ -4889,7 +4901,7 @@ var getElbowArrowData = (arrow, elementsMap, nextPoints, options) => {
4889
4901
  elementsMap,
4890
4902
  offsetFromHeading(
4891
4903
  startHeading,
4892
- arrow.startArrowhead ? getBindingGap(hoveredStartElement, { elbowed: true }) * 6 : getBindingGap(hoveredStartElement, { elbowed: true }) * 2,
4904
+ arrow.startArrowhead ? getBindingGap(hoveredStartElement) * 6 : getBindingGap(hoveredStartElement) * 2,
4893
4905
  1
4894
4906
  )
4895
4907
  ) : startPointBounds;
@@ -4898,7 +4910,7 @@ var getElbowArrowData = (arrow, elementsMap, nextPoints, options) => {
4898
4910
  elementsMap,
4899
4911
  offsetFromHeading(
4900
4912
  endHeading,
4901
- arrow.endArrowhead ? getBindingGap(hoveredEndElement, { elbowed: true }) * 6 : getBindingGap(hoveredEndElement, { elbowed: true }) * 2,
4913
+ arrow.endArrowhead ? getBindingGap(hoveredEndElement) * 6 : getBindingGap(hoveredEndElement) * 2,
4902
4914
  1
4903
4915
  )
4904
4916
  ) : endPointBounds;
@@ -4930,7 +4942,7 @@ var getElbowArrowData = (arrow, elementsMap, nextPoints, options) => {
4930
4942
  0
4931
4943
  ) : offsetFromHeading(
4932
4944
  startHeading,
4933
- !hoveredStartElement && !hoveredEndElement ? 0 : BASE_PADDING - (arrow.startArrowhead ? BASE_BINDING_GAP_ELBOW * 6 : BASE_BINDING_GAP_ELBOW * 2),
4945
+ !hoveredStartElement && !hoveredEndElement ? 0 : BASE_PADDING - (arrow.startArrowhead ? BASE_BINDING_GAP * 6 : BASE_BINDING_GAP * 2),
4934
4946
  BASE_PADDING
4935
4947
  ),
4936
4948
  boundsOverlap ? offsetFromHeading(
@@ -4939,7 +4951,7 @@ var getElbowArrowData = (arrow, elementsMap, nextPoints, options) => {
4939
4951
  0
4940
4952
  ) : offsetFromHeading(
4941
4953
  endHeading,
4942
- !hoveredStartElement && !hoveredEndElement ? 0 : BASE_PADDING - (arrow.endArrowhead ? BASE_BINDING_GAP_ELBOW * 6 : BASE_BINDING_GAP_ELBOW * 2),
4954
+ !hoveredStartElement && !hoveredEndElement ? 0 : BASE_PADDING - (arrow.endArrowhead ? BASE_BINDING_GAP * 6 : BASE_BINDING_GAP * 2),
4943
4955
  BASE_PADDING
4944
4956
  ),
4945
4957
  boundsOverlap,
@@ -5477,7 +5489,7 @@ var neighborIndexToHeading = (idx) => {
5477
5489
  }
5478
5490
  return HEADING_LEFT;
5479
5491
  };
5480
- var getGlobalPoint = (arrow, startOrEnd, fixedPointRatio, initialPoint, element, elementsMap, isDragging, isBindingEnabled2 = true, isMidpointSnappingEnabled = true) => {
5492
+ var getGlobalPoint = (arrow, startOrEnd, fixedPointRatio, initialPoint, zoom, element, elementsMap, isDragging, isBindingEnabled2 = true, isMidpointSnappingEnabled = true) => {
5481
5493
  if (isDragging) {
5482
5494
  if (isBindingEnabled2 && element && elementsMap) {
5483
5495
  return bindPointToSnapToElementOutline(
@@ -5485,6 +5497,7 @@ var getGlobalPoint = (arrow, startOrEnd, fixedPointRatio, initialPoint, element,
5485
5497
  element,
5486
5498
  startOrEnd,
5487
5499
  elementsMap,
5500
+ zoom,
5488
5501
  void 0,
5489
5502
  isMidpointSnappingEnabled
5490
5503
  );
@@ -5514,12 +5527,7 @@ var getBindPointHeading = (p, otherPoint, hoveredElement, origPoint, elementsMap
5514
5527
  zoom
5515
5528
  );
5516
5529
  var getHoveredElement = (origPoint, elementsMap, elements, zoom) => {
5517
- return getHoveredElementForBinding(
5518
- origPoint,
5519
- elements,
5520
- elementsMap,
5521
- maxBindingDistance_simple(zoom)
5522
- );
5530
+ return getHoveredElementForBinding(origPoint, elements, elementsMap, zoom);
5523
5531
  };
5524
5532
  var gridAddressesEqual = (a2, b2) => a2[0] === b2[0] && a2[1] === b2[1];
5525
5533
  var validateElbowPoints = (points, tolerance = DEDUP_TRESHOLD) => points.slice(1).map(
@@ -6526,6 +6534,23 @@ var getBoundTextElementPosition = (container, boundTextElement, elementsMap) =>
6526
6534
  );
6527
6535
  }
6528
6536
  };
6537
+ var getTextElementWithAccuratePosition = (textElement, elementsMap) => {
6538
+ if (!textElement.containerId) {
6539
+ return textElement;
6540
+ }
6541
+ const container = elementsMap.get(textElement.containerId);
6542
+ if (!container || !isArrowElement(container)) {
6543
+ return textElement;
6544
+ }
6545
+ return {
6546
+ ...textElement,
6547
+ ...LinearElementEditor.getBoundTextElementPosition(
6548
+ container,
6549
+ textElement,
6550
+ elementsMap
6551
+ )
6552
+ };
6553
+ };
6529
6554
  var shouldAllowVerticalAlign = (selectedElements, elementsMap) => {
6530
6555
  return selectedElements.some((element) => {
6531
6556
  if (isBoundToContainer(element)) {
@@ -7580,71 +7605,127 @@ var getDiagonalsForBindableElement = (element, elementsMap) => {
7580
7605
  );
7581
7606
  return [diagonalOne, diagonalTwo];
7582
7607
  };
7583
- var getSnapOutlineMidPoint = (point, element, elementsMap, zoom) => {
7584
- const center = elementCenterPoint(element, elementsMap);
7585
- const sideMidpoints = element.type === "diamond" ? getDiamondBaseCorners(element).map((curve4) => {
7586
- const point2 = bezierEquation(curve4, 0.5);
7587
- const rotatedPoint = pointRotateRads6(point2, center, element.angle);
7588
- return pointFrom6(rotatedPoint[0], rotatedPoint[1]);
7589
- }) : [
7590
- // RIGHT midpoint
7591
- pointRotateRads6(
7592
- pointFrom6(
7593
- element.x + element.width,
7594
- element.y + element.height / 2
7595
- ),
7596
- center,
7597
- element.angle
7598
- ),
7599
- // BOTTOM midpoint
7600
- pointRotateRads6(
7601
- pointFrom6(
7602
- element.x + element.width / 2,
7603
- element.y + element.height
7604
- ),
7605
- center,
7606
- element.angle
7607
- ),
7608
- // LEFT midpoint
7609
- pointRotateRads6(
7610
- pointFrom6(element.x, element.y + element.height / 2),
7611
- center,
7612
- element.angle
7613
- ),
7614
- // TOP midpoint
7615
- pointRotateRads6(
7616
- pointFrom6(element.x + element.width / 2, element.y),
7617
- center,
7618
- element.angle
7619
- )
7620
- ];
7621
- const candidate = sideMidpoints.find(
7622
- (midpoint) => pointDistance3(point, midpoint) <= maxBindingDistance_simple(zoom) + element.strokeWidth / 2 && !hitElementItself({
7608
+ var getSnappedMidpointForElbowArrow = (element, point, elementsMap, center, horizontalThreshold, verticalThreshold) => {
7609
+ const { x, y, width, height, angle: angle2 } = element;
7610
+ const nonRotated = pointRotateRads6(point, center, -angle2);
7611
+ const bindingGap = getBindingGap(element);
7612
+ if (pointDistance3(center, nonRotated) < bindingGap) {
7613
+ return void 0;
7614
+ }
7615
+ const [right, bottom, left, top] = getAllMidpoints(element, elementsMap);
7616
+ if (nonRotated[0] <= x + width / 2 && nonRotated[1] > center[1] - verticalThreshold && nonRotated[1] < center[1] + verticalThreshold) {
7617
+ return { point: left, onAxis: true };
7618
+ } else if (nonRotated[1] <= y + height / 2 && nonRotated[0] > center[0] - horizontalThreshold && nonRotated[0] < center[0] + horizontalThreshold) {
7619
+ return { point: top, onAxis: true };
7620
+ } else if (nonRotated[0] >= x + width / 2 && nonRotated[1] > center[1] - verticalThreshold && nonRotated[1] < center[1] + verticalThreshold) {
7621
+ return { point: right, onAxis: true };
7622
+ } else if (nonRotated[1] >= y + height / 2 && nonRotated[0] > center[0] - horizontalThreshold && nonRotated[0] < center[0] + horizontalThreshold) {
7623
+ return { point: bottom, onAxis: true };
7624
+ } else if (element.type === "diamond") {
7625
+ const threshold = Math.max(horizontalThreshold, verticalThreshold);
7626
+ const edgeMidpoints = [
7627
+ // top-left, top-right, bottom-left, bottom-right
7628
+ [x + width / 4, y + height / 4, -1, -1],
7629
+ [x + 3 * width / 4, y + height / 4, 1, -1],
7630
+ [x + width / 4, y + 3 * height / 4, -1, 1],
7631
+ [x + 3 * width / 4, y + 3 * height / 4, 1, 1]
7632
+ ];
7633
+ for (const [edgeX, edgeY, dirX, dirY] of edgeMidpoints) {
7634
+ const zoneCenter = pointFrom6(
7635
+ edgeX + dirX * bindingGap,
7636
+ edgeY + dirY * bindingGap
7637
+ );
7638
+ if (pointDistance3(zoneCenter, nonRotated) < threshold) {
7639
+ return {
7640
+ point: pointRotateRads6(
7641
+ pointFrom6(edgeX, edgeY),
7642
+ center,
7643
+ angle2
7644
+ ),
7645
+ onAxis: false
7646
+ };
7647
+ }
7648
+ }
7649
+ }
7650
+ return void 0;
7651
+ };
7652
+ var getSnappedMidpointIndexForSimpleArrow = (element, point, elementsMap, threshold) => {
7653
+ const baseMidpoints = getAllMidpoints(element, elementsMap);
7654
+ for (let i = 0; i < baseMidpoints.length; i++) {
7655
+ if (pointDistance3(baseMidpoints[i], point) <= threshold && !hitElementItself({
7623
7656
  point,
7624
7657
  element,
7625
7658
  threshold: 0,
7626
7659
  elementsMap,
7627
7660
  overrideShouldTestInside: true
7628
- })
7661
+ })) {
7662
+ return i;
7663
+ }
7664
+ }
7665
+ return -1;
7666
+ };
7667
+ var getAllMidpoints = (element, elementsMap) => {
7668
+ const center = elementCenterPoint(element, elementsMap);
7669
+ if (element.type === "diamond") {
7670
+ return getDiamondBaseCorners(element).map(
7671
+ (curve4) => pointRotateRads6(bezierEquation(curve4, 0.5), center, element.angle)
7672
+ );
7673
+ }
7674
+ return [
7675
+ pointFrom6(element.width, element.height / 2),
7676
+ pointFrom6(element.width / 2, element.height),
7677
+ pointFrom6(0, element.height / 2),
7678
+ pointFrom6(element.width / 2, 0)
7679
+ ].map(
7680
+ ([x, y]) => pointRotateRads6(
7681
+ pointFrom6(element.x + x, element.y + y),
7682
+ center,
7683
+ element.angle
7684
+ )
7685
+ );
7686
+ };
7687
+ var getElbowArrowSnapMidPoint = (point, element, elementsMap, zoom) => {
7688
+ const TOLERANCE = 0.05;
7689
+ const maxDistance = maxBindingDistance_simple(zoom) + element.strokeWidth / 2;
7690
+ return getSnappedMidpointForElbowArrow(
7691
+ element,
7692
+ point,
7693
+ elementsMap,
7694
+ elementCenterPoint(element, elementsMap),
7695
+ clamp5(TOLERANCE * element.width, 5, maxDistance),
7696
+ clamp5(TOLERANCE * element.height, 5, maxDistance)
7697
+ );
7698
+ };
7699
+ var getSnapOutlineMidPoint = (point, element, elementsMap, zoom, arrow) => {
7700
+ if (arrow.elbowed) {
7701
+ return getElbowArrowSnapMidPoint(point, element, elementsMap, zoom)?.point;
7702
+ }
7703
+ const maxDistance = maxBindingDistance_simple(zoom) + element.strokeWidth / 2;
7704
+ const idx = getSnappedMidpointIndexForSimpleArrow(
7705
+ element,
7706
+ point,
7707
+ elementsMap,
7708
+ maxDistance
7629
7709
  );
7630
- return candidate;
7710
+ return idx === -1 ? void 0 : getAllMidpoints(element, elementsMap)[idx];
7631
7711
  };
7632
7712
  var projectFixedPointOntoDiagonal = (arrow, point, element, startOrEnd, elementsMap, zoom, isMidpointSnappingEnabled = true) => {
7633
7713
  invariant5(arrow.points.length >= 2, "Arrow must have at least two points");
7634
- if (arrow.width < 3 && arrow.height < 3) {
7635
- return null;
7636
- }
7637
7714
  if (isMidpointSnappingEnabled) {
7638
7715
  const sideMidPoint = getSnapOutlineMidPoint(
7639
7716
  point,
7640
7717
  element,
7641
7718
  elementsMap,
7642
- zoom
7719
+ zoom,
7720
+ arrow
7643
7721
  );
7644
7722
  if (sideMidPoint) {
7645
7723
  return sideMidPoint;
7646
7724
  }
7647
7725
  }
7726
+ if (arrow.width < 3 && arrow.height < 3) {
7727
+ return null;
7728
+ }
7648
7729
  const [diagonalOne, diagonalTwo] = getDiagonalsForBindableElement(
7649
7730
  element,
7650
7731
  elementsMap
@@ -7736,7 +7817,7 @@ var hitElementItself = ({
7736
7817
  frameNameBound = null,
7737
7818
  overrideShouldTestInside = false
7738
7819
  }) => {
7739
- if (cachedPoint && pointsEqual4(point, cachedPoint) && (cachedHit ? cachedThreshold <= threshold : cachedThreshold >= threshold) && overrideShouldTestInside === cachedOverrideShouldTestInside && frameNameBoundsEqual(frameNameBound, cachedFrameNameBound)) {
7820
+ if (!isBoundToContainer(element) && cachedPoint && pointsEqual4(point, cachedPoint) && (cachedHit ? cachedThreshold <= threshold : cachedThreshold >= threshold) && overrideShouldTestInside === cachedOverrideShouldTestInside && frameNameBoundsEqual(frameNameBound, cachedFrameNameBound)) {
7740
7821
  const derefElement = cachedElement?.deref();
7741
7822
  if (derefElement && derefElement.id === element.id && derefElement.version === element.version && derefElement.versionNonce === element.versionNonce) {
7742
7823
  return cachedHit;
@@ -7755,11 +7836,15 @@ var hitElementItself = ({
7755
7836
  point,
7756
7837
  bounds,
7757
7838
  element.angle,
7758
- threshold
7839
+ // Freedraw bounds follow the centerline points, but the ink extends past them
7840
+ isFreeDrawElement(element) ? threshold + getFreedrawMaxStrokeRadius(element) : threshold
7759
7841
  );
7760
7842
  if (!hitBounds && !hitFrameName) {
7761
7843
  return false;
7762
7844
  }
7845
+ if (isTextElement(element)) {
7846
+ element = getTextElementWithAccuratePosition(element, elementsMap);
7847
+ }
7763
7848
  const hitElement = (overrideShouldTestInside ? true : shouldTestInside(element)) ? (
7764
7849
  // Since `inShape` tests STRICTLY againt the insides of a shape
7765
7850
  // we would need `onShape` as well to include the "borders"
@@ -7793,110 +7878,111 @@ var hitElementBoundText = (point, element, elementsMap) => {
7793
7878
  if (!boundTextElementCandidate) {
7794
7879
  return false;
7795
7880
  }
7796
- const boundTextElement = isLinearElement(element) ? {
7797
- ...boundTextElementCandidate,
7798
- // arrow's bound text accurate position is not stored in the element's property
7799
- // but rather calculated and returned from the following static method
7800
- ...LinearElementEditor.getBoundTextElementPosition(
7801
- element,
7802
- boundTextElementCandidate,
7803
- elementsMap
7804
- )
7805
- } : boundTextElementCandidate;
7881
+ const boundTextElement = getTextElementWithAccuratePosition(
7882
+ boundTextElementCandidate,
7883
+ elementsMap
7884
+ );
7806
7885
  return isPointInElement(point, boundTextElement, elementsMap);
7807
7886
  };
7808
- var bindingBorderTest = (element, [x, y], elementsMap, tolerance = 0) => {
7809
- const p = pointFrom7(x, y);
7810
- const shouldTestInside2 = (
7811
- // disable fullshape snapping for frame elements so we
7812
- // can bind to frame children
7813
- !isFrameLikeElement(element)
7814
- );
7887
+ var isPointClippedByEnclosingFrame = (element, point, elementsMap) => {
7888
+ if (!element.frameId) {
7889
+ return false;
7890
+ }
7891
+ const enclosingFrame = elementsMap.get(element.frameId);
7892
+ return !!enclosingFrame && isFrameLikeElement(enclosingFrame) && !pointInsideBounds(point, getElementBounds(enclosingFrame, elementsMap));
7893
+ };
7894
+ var bindableElementBorderDistanceIfClose = (element, point, elementsMap, tolerance = 0) => {
7895
+ const [x, y] = point;
7815
7896
  const t = Math.max(1, tolerance);
7816
7897
  const bounds = [x - t, y - t, x + t, y + t];
7817
7898
  const elementBounds = getElementBounds(element, elementsMap);
7818
7899
  if (!doBoundsIntersect(bounds, elementBounds)) {
7819
- return false;
7900
+ return -Infinity;
7820
7901
  }
7821
- if (element.frameId) {
7822
- const enclosingFrame = elementsMap.get(element.frameId);
7823
- if (enclosingFrame && isFrameLikeElement(enclosingFrame)) {
7824
- const enclosingFrameBounds = getElementBounds(
7825
- enclosingFrame,
7826
- elementsMap
7827
- );
7828
- if (!pointInsideBounds(p, enclosingFrameBounds)) {
7829
- return false;
7830
- }
7831
- }
7902
+ if (isPointClippedByEnclosingFrame(element, point, elementsMap)) {
7903
+ return -Infinity;
7832
7904
  }
7833
- const intersections = intersectElementWithLineSegment(
7834
- element,
7835
- elementsMap,
7836
- lineSegment3(elementCenterPoint(element, elementsMap), p)
7837
- );
7838
- const distance3 = distanceToElement(element, elementsMap, p);
7839
- return shouldTestInside2 ? intersections.length === 0 || distance3 <= tolerance : intersections.length > 0 && distance3 <= t;
7905
+ const isInside = isPointInElement(point, element, elementsMap);
7906
+ if (isInside && isFrameLikeElement(element)) {
7907
+ return -Infinity;
7908
+ }
7909
+ const distance3 = distanceToElement(element, elementsMap, point);
7910
+ if (isInside) {
7911
+ return distance3;
7912
+ }
7913
+ return distance3 > tolerance ? -Infinity : -distance3;
7840
7914
  };
7841
- var getAllHoveredElementAtPoint = (point, elements, elementsMap, tolerance) => {
7842
- const candidateElements = [];
7915
+ var isOpaqueForBinding = (element) => isImageElement(element) || hasBackground(element.type) && !isTransparent2(element.backgroundColor);
7916
+ var getBindingCandidates = (point, elements, elementsMap, zoom) => {
7917
+ const maxDistance = maxBindingDistance_simple(zoom);
7918
+ const candidates = [];
7919
+ let occludingFrameId = null;
7843
7920
  for (let index = elements.length - 1; index >= 0; --index) {
7844
7921
  const element = elements[index];
7845
7922
  invariant6(
7846
7923
  !element.isDeleted,
7847
- "Elements in the function parameter for getAllElementsAtPositionForBinding() should not contain deleted elements"
7924
+ "Elements passed to binding hit tests should not contain deleted elements"
7848
7925
  );
7849
- if (isBindableElement(element, false) && bindingBorderTest(element, point, elementsMap, tolerance)) {
7850
- candidateElements.push(element);
7851
- if (hasBackground(element.type) && !isTransparent2(element.backgroundColor)) {
7852
- break;
7926
+ if (occludingFrameId && element.frameId !== occludingFrameId) {
7927
+ continue;
7928
+ }
7929
+ if (!isBindableElement(element)) {
7930
+ continue;
7931
+ }
7932
+ if (isFrameLikeElement(element)) {
7933
+ if (isOpaqueForBinding(element) && isPointInElement(point, element, elementsMap)) {
7934
+ occludingFrameId = element.id;
7853
7935
  }
7854
7936
  }
7855
- }
7856
- return candidateElements;
7857
- };
7858
- var getHoveredElementForBinding = (point, elements, elementsMap, tolerance) => {
7859
- const candidateElements = getAllHoveredElementAtPoint(
7860
- point,
7861
- elements,
7862
- elementsMap,
7863
- tolerance
7864
- );
7865
- if (!candidateElements || candidateElements.length === 0) {
7866
- return null;
7867
- }
7868
- if (candidateElements.length === 1) {
7869
- return candidateElements[0];
7870
- }
7871
- return candidateElements.sort(
7872
- (a2, b2) => b2.width ** 2 + b2.height ** 2 - (a2.width ** 2 + a2.height ** 2)
7873
- ).pop();
7874
- };
7875
- var getHoveredElementForFocusPoint = (point, arrow, elements, elementsMap, tolerance) => {
7876
- const candidateElements = [];
7877
- for (let index = elements.length - 1; index >= 0; --index) {
7878
- const element = elements[index];
7879
- invariant6(
7880
- !element.isDeleted,
7881
- "Elements in the function parameter for getAllElementsAtPositionForBinding() should not contain deleted elements"
7937
+ const distance3 = bindableElementBorderDistanceIfClose(
7938
+ element,
7939
+ point,
7940
+ elementsMap,
7941
+ maxDistance
7882
7942
  );
7883
- if (isBindableElement(element, false) && bindingBorderTest(element, point, elementsMap, tolerance)) {
7884
- candidateElements.push(element);
7943
+ if (distance3 > -maxDistance) {
7944
+ if (!element.locked) {
7945
+ candidates.push({ element, distance: distance3 });
7946
+ }
7947
+ if (distance3 >= 0 && isOpaqueForBinding(element)) {
7948
+ break;
7949
+ }
7885
7950
  }
7886
7951
  }
7887
- if (!candidateElements || candidateElements.length === 0) {
7952
+ return candidates;
7953
+ };
7954
+ var getAllHoveredElementAtPoint = (point, elements, elementsMap, zoom) => getBindingCandidates(point, elements, elementsMap, zoom).map(
7955
+ ({ element }) => element
7956
+ );
7957
+ var getHoveredElementForBinding = (point, elements, elementsMap, zoom) => {
7958
+ const candidates = getBindingCandidates(point, elements, elementsMap, zoom);
7959
+ if (candidates.length === 0) {
7888
7960
  return null;
7889
7961
  }
7890
- if (candidateElements.length === 1) {
7891
- return candidateElements[0];
7962
+ if (candidates.length === 1) {
7963
+ return candidates[0].element;
7892
7964
  }
7893
- const distanceFilteredCandidateElements = candidateElements.filter(
7894
- (el) => distanceToElement(el, elementsMap, point) <= getBindingGap(el, arrow) || isPointInElement(point, el, elementsMap)
7965
+ const closestElements = candidates.sort(
7966
+ (a2, b2) => Math.abs(a2.distance) - Math.abs(b2.distance)
7895
7967
  );
7896
- if (distanceFilteredCandidateElements.length === 0) {
7897
- return null;
7898
- }
7899
- return distanceFilteredCandidateElements[0];
7968
+ const candidate = closestElements[0];
7969
+ const [cx1, cy1, cx2, cy2] = getElementBounds(candidate.element, elementsMap);
7970
+ const candidateArea = Math.max(
7971
+ 1e-5,
7972
+ Math.abs(cx2 - cx1) * Math.abs(cy2 - cy1)
7973
+ );
7974
+ const overlaps = closestElements.filter((c) => c.element !== candidate.element && c.distance >= 0).map((c) => {
7975
+ const [x1, y1, x2, y2] = getElementBounds(c.element, elementsMap);
7976
+ const overlapWidth = Math.max(0, Math.min(x2, cx2) - Math.max(x1, cx1));
7977
+ const overlapHeight = Math.max(0, Math.min(y2, cy2) - Math.max(y1, cy1));
7978
+ const area = Math.max(1e-5, Math.abs(x2 - x1) * Math.abs(y2 - y1));
7979
+ return {
7980
+ ...c,
7981
+ overlapPercent: overlapHeight * overlapWidth / area,
7982
+ relativeArea: area / candidateArea
7983
+ };
7984
+ }).filter((c) => c.overlapPercent > 0.25 && c.relativeArea < 0.75);
7985
+ return candidate.distance >= 0 && overlaps.length > 0 ? overlaps[0].element : candidate.element;
7900
7986
  };
7901
7987
  var intersectElementWithLineSegment = (element, elementsMap, line2, offset = 0, onlyFirst = false) => {
7902
7988
  const intersectorBounds = [
@@ -7952,15 +8038,16 @@ var intersectElementWithLineSegment = (element, elementsMap, line2, offset = 0,
7952
8038
  );
7953
8039
  }
7954
8040
  };
8041
+ var CURVE_BOUNDS_EPSILON = 1e-6;
7955
8042
  var curveIntersections = (curves, segment, intersections, center, angle2, onlyFirst = false) => {
8043
+ const b2 = [
8044
+ Math.min(segment[0][0], segment[1][0]) - CURVE_BOUNDS_EPSILON,
8045
+ Math.min(segment[0][1], segment[1][1]) - CURVE_BOUNDS_EPSILON,
8046
+ Math.max(segment[0][0], segment[1][0]) + CURVE_BOUNDS_EPSILON,
8047
+ Math.max(segment[0][1], segment[1][1]) + CURVE_BOUNDS_EPSILON
8048
+ ];
7956
8049
  for (const c of curves) {
7957
8050
  const b1 = getCubicBezierCurveBound(c[0], c[1], c[2], c[3]);
7958
- const b2 = [
7959
- Math.min(segment[0][0], segment[1][0]),
7960
- Math.min(segment[0][1], segment[1][1]),
7961
- Math.max(segment[0][0], segment[1][0]),
7962
- Math.max(segment[0][1], segment[1][1])
7963
- ];
7964
8051
  if (!doBoundsIntersect(b1, b2)) {
7965
8052
  continue;
7966
8053
  }
@@ -8104,6 +8191,18 @@ var isPointInElement = (point, element, elementsMap) => {
8104
8191
  if ((isLinearElement(element) || isFreeDrawElement(element)) && !isPathALoop(element.points)) {
8105
8192
  return false;
8106
8193
  }
8194
+ if (isFreeDrawElement(element)) {
8195
+ const [x12, y12, x22, y22] = getElementBounds(element, elementsMap, true);
8196
+ const [px, py] = pointRotateRads7(
8197
+ point,
8198
+ pointFrom7((x12 + x22) / 2, (y12 + y22) / 2),
8199
+ -element.angle
8200
+ );
8201
+ return polygonIncludesPoint(
8202
+ pointFrom7(px - element.x, py - element.y),
8203
+ getFreedrawFillPolygon(element)
8204
+ );
8205
+ }
8107
8206
  const [x1, y1, x2, y2] = getElementBounds(element, elementsMap);
8108
8207
  if (!isPointWithinBounds(pointFrom7(x1, y1), point, pointFrom7(x2, y2))) {
8109
8208
  return false;
@@ -8180,17 +8279,16 @@ var isBindableElementInsideOtherBindable = (innerElement, outerElement, elements
8180
8279
 
8181
8280
  // src/binding.ts
8182
8281
  var BASE_BINDING_GAP = 5;
8183
- var BASE_BINDING_GAP_ELBOW = 5;
8184
8282
  var BASE_ARROW_MIN_LENGTH = 10;
8185
8283
  var FOCUS_POINT_SIZE = 10 / 1.5;
8186
8284
  var MIN_BINDABLE_SIZE = 1;
8187
- var getBindingGap = (bindTarget, opts) => {
8188
- return (opts.elbowed ? BASE_BINDING_GAP_ELBOW : BASE_BINDING_GAP) + bindTarget.strokeWidth / 2;
8285
+ var getBindingGap = (bindTarget) => {
8286
+ return BASE_BINDING_GAP + bindTarget.strokeWidth / 2;
8189
8287
  };
8190
8288
  var maxBindingDistance_simple = (zoom) => {
8191
8289
  const BASE_BINDING_DISTANCE = Math.max(BASE_BINDING_GAP, 15);
8192
8290
  const zoomValue = zoom?.value && zoom.value < 1 ? zoom.value : 1;
8193
- return clamp5(
8291
+ return clamp6(
8194
8292
  // reducing zoom impact so that the diff between binding distance and
8195
8293
  // binding gap is kept to minimum when possible
8196
8294
  BASE_BINDING_DISTANCE / (zoomValue * 1.5),
@@ -8221,6 +8319,7 @@ var bindOrUnbindBindingElement = (arrow, draggingPoints, scenePointerX, scenePoi
8221
8319
  start,
8222
8320
  "start",
8223
8321
  scene,
8322
+ appState.zoom,
8224
8323
  appState.isBindingEnabled,
8225
8324
  isMidpointSnappingEnabled
8226
8325
  );
@@ -8229,6 +8328,7 @@ var bindOrUnbindBindingElement = (arrow, draggingPoints, scenePointerX, scenePoi
8229
8328
  end,
8230
8329
  "end",
8231
8330
  scene,
8331
+ appState.zoom,
8232
8332
  appState.isBindingEnabled,
8233
8333
  isMidpointSnappingEnabled
8234
8334
  );
@@ -8260,7 +8360,7 @@ var bindOrUnbindBindingElement = (arrow, draggingPoints, scenePointerX, scenePoi
8260
8360
  }
8261
8361
  return { start, end };
8262
8362
  };
8263
- var bindOrUnbindBindingElementEdge = (arrow, { mode, element, focusPoint }, startOrEnd, scene, shouldSnapToOutline = true, isMidpointSnappingEnabled = true) => {
8363
+ var bindOrUnbindBindingElementEdge = (arrow, { mode, element, focusPoint }, startOrEnd, scene, zoom, shouldSnapToOutline = true, isMidpointSnappingEnabled = true) => {
8264
8364
  if (mode === null) {
8265
8365
  unbindBindingElement(arrow, startOrEnd, scene);
8266
8366
  } else if (mode !== void 0) {
@@ -8270,6 +8370,7 @@ var bindOrUnbindBindingElementEdge = (arrow, { mode, element, focusPoint }, star
8270
8370
  mode,
8271
8371
  startOrEnd,
8272
8372
  scene,
8373
+ zoom,
8273
8374
  focusPoint,
8274
8375
  shouldSnapToOutline,
8275
8376
  isMidpointSnappingEnabled
@@ -8293,7 +8394,7 @@ var bindingStrategyForElbowArrowEndpointDragging = (arrow, draggingPoints, eleme
8293
8394
  globalPoint,
8294
8395
  elements,
8295
8396
  elementsMap,
8296
- maxBindingDistance_simple(zoom)
8397
+ zoom
8297
8398
  );
8298
8399
  const current = hit ? {
8299
8400
  element: hit,
@@ -8318,7 +8419,12 @@ var bindingStrategyForNewSimpleArrowEndpointDragging = (arrow, draggingPoints, e
8318
8419
  draggingPoints.get(startDragged ? startIdx : endIdx).point,
8319
8420
  elementsMap
8320
8421
  );
8321
- const hit = getHoveredElementForBinding(point, elements, elementsMap);
8422
+ const hit = getHoveredElementForBinding(
8423
+ point,
8424
+ elements,
8425
+ elementsMap,
8426
+ appState.zoom
8427
+ );
8322
8428
  if (startDragged) {
8323
8429
  if (hit) {
8324
8430
  start = {
@@ -8349,7 +8455,12 @@ var bindingStrategyForNewSimpleArrowEndpointDragging = (arrow, draggingPoints, e
8349
8455
  }
8350
8456
  if (hit && arrow.startBinding) {
8351
8457
  const startBinding = arrow.startBinding;
8352
- const allHits = getAllHoveredElementAtPoint(point, elements, elementsMap);
8458
+ const allHits = getAllHoveredElementAtPoint(
8459
+ point,
8460
+ elements,
8461
+ elementsMap,
8462
+ appState.zoom
8463
+ );
8353
8464
  if (allHits.find((el) => el.id === startBinding.elementId)) {
8354
8465
  const otherElement = elementsMap.get(
8355
8466
  arrow.startBinding.elementId
@@ -8413,12 +8524,12 @@ var bindingStrategyForNewSimpleArrowEndpointDragging = (arrow, draggingPoints, e
8413
8524
  }
8414
8525
  invariant7(false, "New arrow creation should not reach here");
8415
8526
  };
8416
- var bindingStrategyForSimpleArrowEndpointDragging_complex = (point, currentBinding, oppositeBinding, elementsMap, elements, globalBindMode, arrow, finalize) => {
8527
+ var bindingStrategyForSimpleArrowEndpointDragging_complex = (point, currentBinding, oppositeBinding, elementsMap, elements, globalBindMode, arrow, zoom, finalize) => {
8417
8528
  let current = { mode: void 0 };
8418
8529
  let other = { mode: void 0 };
8419
8530
  const isMultiPoint = arrow.points.length > 2;
8420
- const hit = getHoveredElementForBinding(point, elements, elementsMap);
8421
- const isOverlapping = oppositeBinding ? getAllHoveredElementAtPoint(point, elements, elementsMap).some(
8531
+ const hit = getHoveredElementForBinding(point, elements, elementsMap, zoom);
8532
+ const isOverlapping = oppositeBinding ? getAllHoveredElementAtPoint(point, elements, elementsMap, zoom).some(
8422
8533
  (el) => el.id === oppositeBinding.elementId
8423
8534
  ) : false;
8424
8535
  const oppositeElement = oppositeBinding ? elementsMap.get(
@@ -8531,7 +8642,7 @@ var getBindingStrategyForDraggingBindingElementEndpoints_simple = (arrow, draggi
8531
8642
  draggingPoints,
8532
8643
  elementsMap,
8533
8644
  elements,
8534
- opts?.zoom
8645
+ appState.zoom
8535
8646
  );
8536
8647
  }
8537
8648
  const otherBinding = startDragged ? arrow.endBinding : arrow.startBinding;
@@ -8551,7 +8662,7 @@ var getBindingStrategyForDraggingBindingElementEndpoints_simple = (arrow, draggi
8551
8662
  opts?.angleLocked || appState.gridModeEnabled ? pointFrom8(scenePointerX, scenePointerY) : globalPoint,
8552
8663
  elements,
8553
8664
  elementsMap,
8554
- maxBindingDistance_simple(appState.zoom)
8665
+ appState.zoom
8555
8666
  );
8556
8667
  const pointInElement = hit && (opts?.angleLocked ? isPointInElement(
8557
8668
  pointFrom8(scenePointerX, scenePointerY),
@@ -8736,7 +8847,8 @@ var getBindingStrategyForDraggingBindingElementEndpoints_complex = (arrow, dragg
8736
8847
  arrow,
8737
8848
  draggingPoints,
8738
8849
  elementsMap,
8739
- elements
8850
+ elements,
8851
+ appState.zoom
8740
8852
  );
8741
8853
  }
8742
8854
  if (opts?.newArrow) {
@@ -8771,6 +8883,7 @@ var getBindingStrategyForDraggingBindingElementEndpoints_complex = (arrow, dragg
8771
8883
  elements,
8772
8884
  globalBindMode,
8773
8885
  arrow,
8886
+ appState.zoom,
8774
8887
  opts?.finalize
8775
8888
  );
8776
8889
  return { start: current, end: other };
@@ -8791,6 +8904,7 @@ var getBindingStrategyForDraggingBindingElementEndpoints_complex = (arrow, dragg
8791
8904
  elements,
8792
8905
  globalBindMode,
8793
8906
  arrow,
8907
+ appState.zoom,
8794
8908
  opts?.finalize
8795
8909
  );
8796
8910
  return { start: other, end: current };
@@ -8824,7 +8938,7 @@ var applyBinding = (arrow, bindableElement, binding, startOrEnd, scene) => {
8824
8938
  });
8825
8939
  }
8826
8940
  };
8827
- var bindBindingElement = (arrow, hoveredElement, mode, startOrEnd, scene, focusPoint, shouldSnapToOutline = true, isMidpointSnappingEnabled = true) => {
8941
+ var bindBindingElement = (arrow, hoveredElement, mode, startOrEnd, scene, zoom, focusPoint, shouldSnapToOutline = true, isMidpointSnappingEnabled = true) => {
8828
8942
  const elementsMap = scene.getNonDeletedElementsMap();
8829
8943
  let binding;
8830
8944
  if (isElbowArrow(arrow)) {
@@ -8836,6 +8950,7 @@ var bindBindingElement = (arrow, hoveredElement, mode, startOrEnd, scene, focusP
8836
8950
  hoveredElement,
8837
8951
  startOrEnd,
8838
8952
  elementsMap,
8953
+ zoom,
8839
8954
  shouldSnapToOutline,
8840
8955
  isMidpointSnappingEnabled
8841
8956
  )
@@ -8873,6 +8988,84 @@ var unbindBindingElement = (arrow, startOrEnd, scene) => {
8873
8988
  scene.mutateElement(arrow, { [field]: null });
8874
8989
  return binding.elementId;
8875
8990
  };
8991
+ var reanchorBindingsToOutline = (changedElement, scene, zoom) => {
8992
+ const elementsMap = scene.getNonDeletedElementsMap();
8993
+ const center = elementCenterPoint(changedElement, elementsMap);
8994
+ const reach = Math.max(changedElement.width, changedElement.height) * 2;
8995
+ boundElementsVisitor(elementsMap, changedElement, (element) => {
8996
+ if (!isArrowElement(element) || !isNonDeletedElement(element)) {
8997
+ return;
8998
+ }
8999
+ const updates = {};
9000
+ for (const startOrEnd of ["startBinding", "endBinding"]) {
9001
+ const binding = element[startOrEnd];
9002
+ if (binding?.elementId !== changedElement.id) {
9003
+ continue;
9004
+ }
9005
+ const startOrEndName = startOrEnd === "startBinding" ? "start" : "end";
9006
+ if (isElbowArrow(element)) {
9007
+ updates[startOrEnd] = {
9008
+ ...binding,
9009
+ ...calculateFixedPointForElbowArrowBinding(
9010
+ element,
9011
+ changedElement,
9012
+ startOrEndName,
9013
+ elementsMap,
9014
+ zoom
9015
+ )
9016
+ };
9017
+ continue;
9018
+ }
9019
+ const focusPoint = getGlobalFixedPointForBindableElement(
9020
+ binding.fixedPoint,
9021
+ changedElement,
9022
+ elementsMap
9023
+ );
9024
+ if (hitElementItself({
9025
+ element: changedElement,
9026
+ point: focusPoint,
9027
+ elementsMap,
9028
+ threshold: getBindingGap(changedElement),
9029
+ overrideShouldTestInside: true
9030
+ })) {
9031
+ continue;
9032
+ }
9033
+ const direction = vectorFromPoint7(focusPoint, center);
9034
+ if (direction[0] === 0 && direction[1] === 0) {
9035
+ continue;
9036
+ }
9037
+ const outlinePoint = intersectElementWithLineSegment(
9038
+ changedElement,
9039
+ elementsMap,
9040
+ lineSegment4(
9041
+ center,
9042
+ pointFromVector6(
9043
+ vectorScale7(vectorNormalize4(direction), reach),
9044
+ center
9045
+ )
9046
+ )
9047
+ ).sort(
9048
+ (a2, b2) => pointDistanceSq(a2, focusPoint) - pointDistanceSq(b2, focusPoint)
9049
+ )[0];
9050
+ if (!outlinePoint) {
9051
+ continue;
9052
+ }
9053
+ updates[startOrEnd] = {
9054
+ ...binding,
9055
+ ...calculateFixedPointForNonElbowArrowBinding(
9056
+ element,
9057
+ changedElement,
9058
+ startOrEndName,
9059
+ elementsMap,
9060
+ outlinePoint
9061
+ )
9062
+ };
9063
+ }
9064
+ if (updates.startBinding || updates.endBinding) {
9065
+ mutateElement(element, elementsMap, updates);
9066
+ }
9067
+ });
9068
+ };
8876
9069
  var updateBoundElements = (changedElement, scene, options) => {
8877
9070
  if (!isBindableElement(changedElement)) {
8878
9071
  return;
@@ -8983,6 +9176,7 @@ var updateArrowBindings = (latestElement, startOrEnd, elementsMap, scene, appSta
8983
9176
  strategy[strategyName].mode,
8984
9177
  strategyName,
8985
9178
  scene,
9179
+ appState.zoom,
8986
9180
  strategy[strategyName].focusPoint
8987
9181
  );
8988
9182
  }
@@ -9019,8 +9213,17 @@ var updateBindings = (latestElement, scene, appState, options) => {
9019
9213
  var doesNeedUpdate = (boundElement, changedElement) => {
9020
9214
  return boundElement.startBinding?.elementId === changedElement.id || boundElement.endBinding?.elementId === changedElement.id;
9021
9215
  };
9216
+ var simultaneouslyUpdatedElementIdsCache = /* @__PURE__ */ new WeakMap();
9022
9217
  var getSimultaneouslyUpdatedElementIds = (simultaneouslyUpdated) => {
9023
- return new Set((simultaneouslyUpdated || []).map((element) => element.id));
9218
+ if (!simultaneouslyUpdated) {
9219
+ return /* @__PURE__ */ new Set();
9220
+ }
9221
+ let ids = simultaneouslyUpdatedElementIdsCache.get(simultaneouslyUpdated);
9222
+ if (!ids) {
9223
+ ids = new Set(simultaneouslyUpdated.map((element) => element.id));
9224
+ simultaneouslyUpdatedElementIdsCache.set(simultaneouslyUpdated, ids);
9225
+ }
9226
+ return ids;
9024
9227
  };
9025
9228
  var getHeadingForElbowArrowSnap = (p, otherPoint, bindableElement, aabb, origPoint, elementsMap, zoom) => {
9026
9229
  const otherPointHeading = vectorToHeading(vectorFromPoint7(otherPoint, p));
@@ -9045,7 +9248,7 @@ var getDistanceForBinding = (point, bindableElement, elementsMap, zoom) => {
9045
9248
  const bindDistance = maxBindingDistance_simple(zoom);
9046
9249
  return distance3 > bindDistance ? null : distance3;
9047
9250
  };
9048
- var bindPointToSnapToElementOutline = (arrowElement, bindableElement, startOrEnd, elementsMap, customIntersector, isMidpointSnappingEnabled = true) => {
9251
+ var bindPointToSnapToElementOutline = (arrowElement, bindableElement, startOrEnd, elementsMap, zoom, customIntersector, isMidpointSnappingEnabled = true) => {
9049
9252
  const elbowed = isElbowArrow(arrowElement);
9050
9253
  const point = LinearElementEditor.getPointAtIndexGlobalCoordinates(
9051
9254
  arrowElement,
@@ -9066,16 +9269,16 @@ var bindPointToSnapToElementOutline = (arrowElement, bindableElement, startOrEnd
9066
9269
  startOrEnd === "start" ? 1 : -2,
9067
9270
  elementsMap
9068
9271
  );
9069
- const bindingGap = getBindingGap(bindableElement, arrowElement);
9272
+ const bindingGap = getBindingGap(bindableElement);
9070
9273
  const aabb = aabbForElement(bindableElement, elementsMap);
9071
9274
  const bindableCenter = getCenterForBounds(aabb);
9072
9275
  let intersection = null;
9073
9276
  if (elbowed) {
9277
+ const snap = isMidpointSnappingEnabled ? getElbowArrowSnapMidPoint(edgePoint, bindableElement, elementsMap, zoom) : void 0;
9278
+ const resolved = snap?.point ?? point;
9074
9279
  const isHorizontal = headingIsHorizontal(
9075
- headingForPointFromElement(bindableElement, aabb, point)
9280
+ snap?.onAxis ? vectorToHeading(vectorFromPoint7(snap.point, bindableCenter)) : headingForPointFromElement(bindableElement, aabb, point)
9076
9281
  );
9077
- const snapPoint = isMidpointSnappingEnabled ? snapToMid(bindableElement, elementsMap, edgePoint, 0.05, arrowElement) : void 0;
9078
- const resolved = snapPoint || point;
9079
9282
  const otherPoint = pointFrom8(
9080
9283
  isHorizontal ? bindableCenter[0] : resolved[0],
9081
9284
  !isHorizontal ? bindableCenter[1] : resolved[1]
@@ -9090,12 +9293,13 @@ var bindPointToSnapToElementOutline = (arrowElement, bindableElement, startOrEnd
9090
9293
  otherPoint
9091
9294
  )
9092
9295
  );
9296
+ const byDistanceToResolved = (a2, b2) => pointDistanceSq(a2, resolved) - pointDistanceSq(b2, resolved);
9093
9297
  intersection = intersectElementWithLineSegment(
9094
9298
  bindableElement,
9095
9299
  elementsMap,
9096
9300
  intersector,
9097
9301
  bindingGap
9098
- ).sort(pointDistanceSq)[0];
9302
+ ).sort(byDistanceToResolved)[0];
9099
9303
  if (!intersection) {
9100
9304
  const anotherPoint = pointFrom8(
9101
9305
  !isHorizontal ? bindableCenter[0] : resolved[0],
@@ -9115,8 +9319,8 @@ var bindPointToSnapToElementOutline = (arrowElement, bindableElement, startOrEnd
9115
9319
  bindableElement,
9116
9320
  elementsMap,
9117
9321
  anotherIntersector,
9118
- BASE_BINDING_GAP_ELBOW
9119
- ).sort(pointDistanceSq)[0];
9322
+ BASE_BINDING_GAP
9323
+ ).sort(byDistanceToResolved)[0];
9120
9324
  }
9121
9325
  } else {
9122
9326
  let intersector = customIntersector;
@@ -9152,7 +9356,7 @@ var avoidRectangularCorner = (arrowElement, bindTarget, elementsMap, p) => {
9152
9356
  center,
9153
9357
  -bindTarget.angle
9154
9358
  );
9155
- const bindingGap = getBindingGap(bindTarget, arrowElement);
9359
+ const bindingGap = getBindingGap(bindTarget);
9156
9360
  if (nonRotatedPoint[0] < bindTarget.x && nonRotatedPoint[1] < bindTarget.y) {
9157
9361
  if (nonRotatedPoint[1] - bindTarget.y > -bindingGap) {
9158
9362
  return pointRotateRads8(
@@ -9214,73 +9418,6 @@ var avoidRectangularCorner = (arrowElement, bindTarget, elementsMap, p) => {
9214
9418
  }
9215
9419
  return p;
9216
9420
  };
9217
- var snapToMid = (bindTarget, elementsMap, p, tolerance = 0.05, arrowElement) => {
9218
- const { x, y, width, height, angle: angle2 } = bindTarget;
9219
- const center = elementCenterPoint(bindTarget, elementsMap, -0.1, -0.1);
9220
- const nonRotated = pointRotateRads8(p, center, -angle2);
9221
- const bindingGap = arrowElement ? getBindingGap(bindTarget, arrowElement) : 0;
9222
- const verticalThreshold = clamp5(tolerance * height, 5, 80);
9223
- const horizontalThreshold = clamp5(tolerance * width, 5, 80);
9224
- if (pointDistance4(center, nonRotated) < bindingGap) {
9225
- return void 0;
9226
- }
9227
- if (nonRotated[0] <= x + width / 2 && nonRotated[1] > center[1] - verticalThreshold && nonRotated[1] < center[1] + verticalThreshold) {
9228
- return pointRotateRads8(
9229
- pointFrom8(x - bindingGap, center[1]),
9230
- center,
9231
- angle2
9232
- );
9233
- } else if (nonRotated[1] <= y + height / 2 && nonRotated[0] > center[0] - horizontalThreshold && nonRotated[0] < center[0] + horizontalThreshold) {
9234
- return pointRotateRads8(
9235
- pointFrom8(center[0], y - bindingGap),
9236
- center,
9237
- angle2
9238
- );
9239
- } else if (nonRotated[0] >= x + width / 2 && nonRotated[1] > center[1] - verticalThreshold && nonRotated[1] < center[1] + verticalThreshold) {
9240
- return pointRotateRads8(
9241
- pointFrom8(x + width + bindingGap, center[1]),
9242
- center,
9243
- angle2
9244
- );
9245
- } else if (nonRotated[1] >= y + height / 2 && nonRotated[0] > center[0] - horizontalThreshold && nonRotated[0] < center[0] + horizontalThreshold) {
9246
- return pointRotateRads8(
9247
- pointFrom8(center[0], y + height + bindingGap),
9248
- center,
9249
- angle2
9250
- );
9251
- } else if (bindTarget.type === "diamond") {
9252
- const distance3 = bindingGap;
9253
- const topLeft = pointFrom8(
9254
- x + width / 4 - distance3,
9255
- y + height / 4 - distance3
9256
- );
9257
- const topRight = pointFrom8(
9258
- x + 3 * width / 4 + distance3,
9259
- y + height / 4 - distance3
9260
- );
9261
- const bottomLeft = pointFrom8(
9262
- x + width / 4 - distance3,
9263
- y + 3 * height / 4 + distance3
9264
- );
9265
- const bottomRight = pointFrom8(
9266
- x + 3 * width / 4 + distance3,
9267
- y + 3 * height / 4 + distance3
9268
- );
9269
- if (pointDistance4(topLeft, nonRotated) < Math.max(horizontalThreshold, verticalThreshold)) {
9270
- return pointRotateRads8(topLeft, center, angle2);
9271
- }
9272
- if (pointDistance4(topRight, nonRotated) < Math.max(horizontalThreshold, verticalThreshold)) {
9273
- return pointRotateRads8(topRight, center, angle2);
9274
- }
9275
- if (pointDistance4(bottomLeft, nonRotated) < Math.max(horizontalThreshold, verticalThreshold)) {
9276
- return pointRotateRads8(bottomLeft, center, angle2);
9277
- }
9278
- if (pointDistance4(bottomRight, nonRotated) < Math.max(horizontalThreshold, verticalThreshold)) {
9279
- return pointRotateRads8(bottomRight, center, angle2);
9280
- }
9281
- }
9282
- return void 0;
9283
- };
9284
9421
  var extractBinding = (arrow, startOrEnd, elementsMap) => {
9285
9422
  const binding = arrow[startOrEnd];
9286
9423
  if (!binding) {
@@ -9313,7 +9450,7 @@ var snapBoundPointToGrid = (outlinePoint, bindableElement, elementsMap, gridSize
9313
9450
  }
9314
9451
  const aabb = aabbForElement(bindableElement, elementsMap);
9315
9452
  const heading = adjacentPoint && (bindableElement.type === "ellipse" || bindableElement.type === "diamond") ? vectorToHeading(vectorFromPoint7(adjacentPoint, outlinePoint)) : headingForPointFromElement(bindableElement, aabb, outlinePoint);
9316
- const bindingGap = getBindingGap(bindableElement, arrowElement);
9453
+ const bindingGap = getBindingGap(bindableElement);
9317
9454
  const extent = Math.max(bindableElement.width, bindableElement.height) + bindingGap * 2;
9318
9455
  const center = getCenterForBounds(aabb);
9319
9456
  const absNX = Math.abs(heading[0]);
@@ -9393,7 +9530,7 @@ var updateBoundPoint = (arrow, startOrEnd, binding, bindableElement, elementsMap
9393
9530
  otherBindable,
9394
9531
  elementsMap,
9395
9532
  intersector,
9396
- getBindingGap(otherBindable, arrow)
9533
+ getBindingGap(otherBindable)
9397
9534
  ).sort(
9398
9535
  (a2, b2) => pointDistanceSq(a2, focusPoint) - pointDistanceSq(b2, focusPoint)
9399
9536
  )[0];
@@ -9401,7 +9538,7 @@ var updateBoundPoint = (arrow, startOrEnd, binding, bindableElement, elementsMap
9401
9538
  bindableElement,
9402
9539
  elementsMap,
9403
9540
  intersector,
9404
- getBindingGap(bindableElement, arrow)
9541
+ getBindingGap(bindableElement)
9405
9542
  ).sort(
9406
9543
  (a2, b2) => pointDistanceSq(a2, otherFocusPointOrArrowPoint) - pointDistanceSq(b2, otherFocusPointOrArrowPoint)
9407
9544
  )[0];
@@ -9413,7 +9550,7 @@ var updateBoundPoint = (arrow, startOrEnd, binding, bindableElement, elementsMap
9413
9550
  element: otherBindable,
9414
9551
  point: outlinePoint,
9415
9552
  elementsMap,
9416
- threshold: getBindingGap(otherBindable, arrow),
9553
+ threshold: getBindingGap(otherBindable),
9417
9554
  overrideShouldTestInside: true
9418
9555
  })) {
9419
9556
  return LinearElementEditor.createPointAt(
@@ -9452,7 +9589,7 @@ var updateBoundPoint = (arrow, startOrEnd, binding, bindableElement, elementsMap
9452
9589
  null
9453
9590
  );
9454
9591
  };
9455
- var calculateFixedPointForElbowArrowBinding = (linearElement, hoveredElement, startOrEnd, elementsMap, shouldSnapToOutline = true, isMidpointSnappingEnabled = true) => {
9592
+ var calculateFixedPointForElbowArrowBinding = (linearElement, hoveredElement, startOrEnd, elementsMap, zoom, shouldSnapToOutline = true, isMidpointSnappingEnabled = true) => {
9456
9593
  const bounds = [
9457
9594
  hoveredElement.x,
9458
9595
  hoveredElement.y,
@@ -9464,6 +9601,7 @@ var calculateFixedPointForElbowArrowBinding = (linearElement, hoveredElement, st
9464
9601
  hoveredElement,
9465
9602
  startOrEnd,
9466
9603
  elementsMap,
9604
+ zoom,
9467
9605
  void 0,
9468
9606
  isMidpointSnappingEnabled
9469
9607
  ) : LinearElementEditor.getPointAtIndexGlobalCoordinates(
@@ -9483,7 +9621,7 @@ var calculateFixedPointForElbowArrowBinding = (linearElement, hoveredElement, st
9483
9621
  if (hoveredElement.width < MIN_BINDABLE_SIZE || hoveredElement.height < MIN_BINDABLE_SIZE) {
9484
9622
  return { fixedPoint: normalizeFixedPoint([0.5, 0.5]) };
9485
9623
  }
9486
- const sizeFloor = getBindingGap(hoveredElement, linearElement);
9624
+ const sizeFloor = getBindingGap(hoveredElement);
9487
9625
  return {
9488
9626
  fixedPoint: normalizeFixedPoint([
9489
9627
  (nonRotatedSnappedGlobalPoint[0] - hoveredElement.x) / Math.max(hoveredElement.width, sizeFloor),
@@ -9506,7 +9644,7 @@ var calculateFixedPointForNonElbowArrowBinding = (linearElement, hoveredElement,
9506
9644
  if (hoveredElement.width < MIN_BINDABLE_SIZE || hoveredElement.height < MIN_BINDABLE_SIZE) {
9507
9645
  return { fixedPoint: normalizeFixedPoint([0.5, 0.5]) };
9508
9646
  }
9509
- const sizeFloor = getBindingGap(hoveredElement, linearElement);
9647
+ const sizeFloor = getBindingGap(hoveredElement);
9510
9648
  const fixedPointX = (nonRotatedPoint[0] - hoveredElement.x) / Math.max(hoveredElement.width, sizeFloor);
9511
9649
  const fixedPointY = (nonRotatedPoint[1] - hoveredElement.y) / Math.max(hoveredElement.height, sizeFloor);
9512
9650
  return {
@@ -9840,7 +9978,7 @@ var normalizeFixedPoint = (fixedPoint) => {
9840
9978
  }
9841
9979
  const EPSILON = 1e-4;
9842
9980
  const clamped = fixedPoint.map(
9843
- (ratio) => clamp5(ratio, -FIXED_POINT_BOUND, FIXED_POINT_BOUND)
9981
+ (ratio) => clamp6(ratio, -FIXED_POINT_BOUND, FIXED_POINT_BOUND)
9844
9982
  );
9845
9983
  if (Math.abs(clamped[0] - 0.5) < EPSILON || Math.abs(clamped[1] - 0.5) < EPSILON) {
9846
9984
  return clamped.map(
@@ -10373,7 +10511,7 @@ var LinearElementEditor = class _LinearElementEditor {
10373
10511
  deltaY = newDraggingPointPosition[1] - point[1];
10374
10512
  }
10375
10513
  let suggestedBinding = null;
10376
- const { positions, updates } = pointDraggingUpdates(
10514
+ const { positions, updates, hit } = pointDraggingUpdates(
10377
10515
  [idx],
10378
10516
  deltaX,
10379
10517
  deltaY,
@@ -10408,17 +10546,20 @@ var LinearElementEditor = class _LinearElementEditor {
10408
10546
  }
10409
10547
  }
10410
10548
  if (isBindingElement(element)) {
10411
- moveArrowAboveBindable(
10412
- _LinearElementEditor.getPointGlobalCoordinates(
10549
+ if (hit) {
10550
+ moveArrowAboveBindable(
10551
+ _LinearElementEditor.getPointGlobalCoordinates(
10552
+ element,
10553
+ element.points[element.points.length - 1],
10554
+ elementsMap
10555
+ ),
10413
10556
  element,
10414
- element.points[element.points.length - 1],
10415
- elementsMap
10416
- ),
10417
- element,
10418
- elements,
10419
- elementsMap,
10420
- app.scene
10421
- );
10557
+ elements,
10558
+ elementsMap,
10559
+ app.scene,
10560
+ hit
10561
+ );
10562
+ }
10422
10563
  }
10423
10564
  if (app.state.selectedLinearElement?.customLineAngle === customLineAngle && linearElementEditor.initialState.altFocusPoint && (!suggestedBinding || isShallowEqual(app.state.suggestedBinding ?? [], suggestedBinding))) {
10424
10565
  return null;
@@ -10517,7 +10658,7 @@ var LinearElementEditor = class _LinearElementEditor {
10517
10658
  deltaY = newDraggingPointPosition[1] - draggingPoint[1];
10518
10659
  }
10519
10660
  let suggestedBinding = null;
10520
- const { positions, updates } = pointDraggingUpdates(
10661
+ const { positions, updates, hit } = pointDraggingUpdates(
10521
10662
  selectedPointsIndices,
10522
10663
  deltaX,
10523
10664
  deltaY,
@@ -10552,17 +10693,20 @@ var LinearElementEditor = class _LinearElementEditor {
10552
10693
  }
10553
10694
  }
10554
10695
  if (isBindingElement(element) && startIsSelected !== endIsSelected) {
10555
- moveArrowAboveBindable(
10556
- _LinearElementEditor.getPointGlobalCoordinates(
10696
+ if (hit) {
10697
+ moveArrowAboveBindable(
10698
+ _LinearElementEditor.getPointGlobalCoordinates(
10699
+ element,
10700
+ startIsSelected ? element.points[0] : element.points[element.points.length - 1],
10701
+ elementsMap
10702
+ ),
10557
10703
  element,
10558
- startIsSelected ? element.points[0] : element.points[element.points.length - 1],
10559
- elementsMap
10560
- ),
10561
- element,
10562
- elements,
10563
- elementsMap,
10564
- app.scene
10565
- );
10704
+ elements,
10705
+ elementsMap,
10706
+ app.scene,
10707
+ hit
10708
+ );
10709
+ }
10566
10710
  }
10567
10711
  const boundTextElement = getBoundTextElement(element, elementsMap);
10568
10712
  if (boundTextElement) {
@@ -11511,7 +11655,7 @@ var LinearElementEditor = class _LinearElementEditor {
11511
11655
  bestParameter,
11512
11656
  lengths[bestSegmentIndex]
11513
11657
  );
11514
- const labelPosition = clamp6(
11658
+ const labelPosition = clamp7(
11515
11659
  (prefixSums[bestSegmentIndex] + lengthWithinSegment) / totalLength,
11516
11660
  0,
11517
11661
  1
@@ -11542,12 +11686,12 @@ var LinearElementEditor = class _LinearElementEditor {
11542
11686
  if (segments.length === 0) {
11543
11687
  return null;
11544
11688
  }
11545
- const targetLength = clamp6(pathParameter, 0, 1) * totalLength;
11689
+ const targetLength = clamp7(pathParameter, 0, 1) * totalLength;
11546
11690
  const found = segments.findIndex(
11547
11691
  (_, i2) => targetLength <= prefixSums[i2 + 1]
11548
11692
  );
11549
11693
  const i = found === -1 ? segments.length - 1 : found;
11550
- const lengthFraction = lengths[i] === 0 ? 0 : clamp6((targetLength - prefixSums[i]) / lengths[i], 0, 1);
11694
+ const lengthFraction = lengths[i] === 0 ? 0 : clamp7((targetLength - prefixSums[i]) / lengths[i], 0, 1);
11551
11695
  return pathSegmentPointAtLength(segments[i], lengthFraction, lengths[i]);
11552
11696
  }
11553
11697
  static getBoundTextElementPosition = (element, boundTextElement, elementsMap) => {
@@ -11815,16 +11959,19 @@ var pointDraggingUpdates = (selectedPointsIndices, deltaX, deltaY, scenePointerX
11815
11959
  updates: {
11816
11960
  suggestedBinding: suggestedBindingElement ? {
11817
11961
  element: suggestedBindingElement,
11818
- midPoint: app.state.isMidpointSnappingEnabled ? snapToMid(
11819
- suggestedBindingElement,
11820
- elementsMap,
11962
+ midPoint: app.state.isMidpointSnappingEnabled ? getSnapOutlineMidPoint2(
11821
11963
  pointFrom9(
11822
11964
  scenePointerX - linearElementEditor.pointerOffset.x,
11823
11965
  scenePointerY - linearElementEditor.pointerOffset.y
11824
- )
11966
+ ),
11967
+ suggestedBindingElement,
11968
+ elementsMap,
11969
+ app.state.zoom,
11970
+ element
11825
11971
  ) : void 0
11826
11972
  } : null
11827
- }
11973
+ },
11974
+ hit: suggestedBindingElement
11828
11975
  };
11829
11976
  }
11830
11977
  if (!startIsDragged && !endIsDragged) {
@@ -11904,7 +12051,8 @@ var pointDraggingUpdates = (selectedPointsIndices, deltaX, deltaY, scenePointerX
11904
12051
  ),
11905
12052
  start.element,
11906
12053
  elementsMap,
11907
- app.state.zoom
12054
+ app.state.zoom,
12055
+ element
11908
12056
  )
11909
12057
  } : null;
11910
12058
  }
@@ -11935,7 +12083,8 @@ var pointDraggingUpdates = (selectedPointsIndices, deltaX, deltaY, scenePointerX
11935
12083
  ),
11936
12084
  end.element,
11937
12085
  elementsMap,
11938
- app.state.zoom
12086
+ app.state.zoom,
12087
+ element
11939
12088
  )
11940
12089
  } : null;
11941
12090
  }
@@ -12010,7 +12159,8 @@ var pointDraggingUpdates = (selectedPointsIndices, deltaX, deltaY, scenePointerX
12010
12159
  } : naiveDraggingPoints.get(idx)
12011
12160
  ];
12012
12161
  })
12013
- )
12162
+ ),
12163
+ hit: startIsDragged ? start.element : end.element
12014
12164
  };
12015
12165
  };
12016
12166
  var determineCustomLinearAngle = (pivotPoint, draggedPoint) => Math.atan2(draggedPoint[1] - pivotPoint[1], draggedPoint[0] - pivotPoint[0]);
@@ -13318,12 +13468,44 @@ var getCanvasPadding = (element) => {
13318
13468
  return 20;
13319
13469
  }
13320
13470
  };
13321
- var getRenderOpacity = (element, containingFrame, elementsPendingErasure, pendingNodes, globalAlpha = 1) => {
13322
- let opacity = (containingFrame?.opacity ?? 100) * element.opacity / 1e4 * globalAlpha;
13323
- if (elementsPendingErasure.has(element.id) || pendingNodes && pendingNodes.some((node) => node.id === element.id) || containingFrame && elementsPendingErasure.has(containingFrame.id)) {
13471
+ var ZERO_RENDER_OFFSET = { x: 0, y: 0 };
13472
+ var getElementRenderOffset = (element, elementsMap, overrides) => {
13473
+ const container = isTextElement(element) ? getContainerElement(element, elementsMap) : null;
13474
+ return overrides?.get((container ?? element).id)?.offset;
13475
+ };
13476
+ var getRenderElementWithPositionOverride = (element, positionOffset) => {
13477
+ if (positionOffset.x === 0 && positionOffset.y === 0) {
13478
+ return element;
13479
+ }
13480
+ return {
13481
+ ...element,
13482
+ x: element.x + positionOffset.x,
13483
+ y: element.y + positionOffset.y
13484
+ };
13485
+ };
13486
+ var resolveElementRenderState = (element, elementsMap, renderConfig, allElementsMap = elementsMap) => {
13487
+ const {
13488
+ elementRenderOverrides,
13489
+ elementsPendingErasure,
13490
+ pendingFlowchartNodes
13491
+ } = renderConfig;
13492
+ const override = elementRenderOverrides?.get(element.id);
13493
+ const containingFrame = getContainingFrame(element, elementsMap);
13494
+ const frameOpacity = containingFrame ? clamp8(
13495
+ elementRenderOverrides?.get(containingFrame.id)?.opacity ?? containingFrame.opacity,
13496
+ 0,
13497
+ 100
13498
+ ) : 100;
13499
+ let opacity = frameOpacity * clamp8(override?.opacity ?? element.opacity, 0, 100) / 1e4;
13500
+ if (elementsPendingErasure.has(element.id) || pendingFlowchartNodes?.some((node) => node.id === element.id) || containingFrame && elementsPendingErasure.has(containingFrame.id)) {
13324
13501
  opacity *= ELEMENT_READY_TO_ERASE_OPACITY / 100;
13325
13502
  }
13326
- return opacity;
13503
+ const offset = getElementRenderOffset(
13504
+ element,
13505
+ allElementsMap,
13506
+ elementRenderOverrides
13507
+ );
13508
+ return { opacity, offset: offset ?? ZERO_RENDER_OFFSET };
13327
13509
  };
13328
13510
  var cappedElementCanvasSize = (element, elementsMap, zoom) => {
13329
13511
  const AREA_LIMIT = 16777216;
@@ -13648,9 +13830,7 @@ var drawElementOnCanvas = (element, rc, context, renderConfig) => {
13648
13830
  };
13649
13831
  var elementWithCanvasCache = /* @__PURE__ */ new WeakMap();
13650
13832
  var generateElementWithCanvas = (element, elementsMap, renderConfig, appState) => {
13651
- const zoom = renderConfig ? appState.zoom : {
13652
- value: 1
13653
- };
13833
+ const zoom = renderConfig ? appState.zoom : DEFAULT_ZOOM2;
13654
13834
  const prevElementWithCanvas = elementWithCanvasCache.get(element);
13655
13835
  const shouldRegenerateBecauseZoom = prevElementWithCanvas && prevElementWithCanvas.zoomValue !== zoom.value && !appState?.shouldCacheIgnoreZoom;
13656
13836
  const imageCrop = isImageElement(element) ? element.crop : null;
@@ -13673,13 +13853,13 @@ var generateElementWithCanvas = (element, elementsMap, renderConfig, appState) =
13673
13853
  };
13674
13854
  var canSnapElement = (element, appState) => !appState?.shouldCacheIgnoreZoom && (!element.angle || isRightAngleRads(element.angle));
13675
13855
  var SNAP_TIE_BIAS = 1e-6;
13676
- var drawElementFromCanvas = (elementWithCanvas, context, renderConfig, appState, allElementsMap) => {
13856
+ var drawElementFromCanvas = (elementWithCanvas, context, renderConfig, appState, allElementsMap, positionOffset) => {
13677
13857
  const element = elementWithCanvas.element;
13678
13858
  const devicePixelRatio = window.devicePixelRatio;
13679
13859
  const padding = getCanvasPadding(element);
13680
13860
  const [x1, y1, x2, y2] = getElementAbsoluteCoords2(element, allElementsMap);
13681
- const cx = ((x1 + x2) / 2 + appState.scrollX) * devicePixelRatio;
13682
- const cy = ((y1 + y2) / 2 + appState.scrollY) * devicePixelRatio;
13861
+ const cx = ((x1 + x2) / 2 + positionOffset.x + appState.scrollX) * devicePixelRatio;
13862
+ const cy = ((y1 + y2) / 2 + positionOffset.y + appState.scrollY) * devicePixelRatio;
13683
13863
  context.save();
13684
13864
  context.scale(1 / devicePixelRatio, 1 / devicePixelRatio);
13685
13865
  const boundTextElement = getBoundTextElement(element, allElementsMap);
@@ -13692,8 +13872,8 @@ var drawElementFromCanvas = (elementWithCanvas, context, renderConfig, appState,
13692
13872
  context.beginPath();
13693
13873
  context.rect(cx - outerHalf, cy - outerHalf, outerHalf * 2, outerHalf * 2);
13694
13874
  context.rect(
13695
- (boundTextCx - boundTextElement.width / 2 - BOUND_TEXT_PADDING3 + appState.scrollX) * devicePixelRatio,
13696
- (boundTextCy - boundTextElement.height / 2 - BOUND_TEXT_PADDING3 + appState.scrollY) * devicePixelRatio,
13875
+ (boundTextCx - boundTextElement.width / 2 - BOUND_TEXT_PADDING3 + positionOffset.x + appState.scrollX) * devicePixelRatio,
13876
+ (boundTextCy - boundTextElement.height / 2 - BOUND_TEXT_PADDING3 + positionOffset.y + appState.scrollY) * devicePixelRatio,
13697
13877
  (boundTextElement.width + BOUND_TEXT_PADDING3 * 2) * devicePixelRatio,
13698
13878
  (boundTextElement.height + BOUND_TEXT_PADDING3 * 2) * devicePixelRatio
13699
13879
  );
@@ -13708,8 +13888,8 @@ var drawElementFromCanvas = (elementWithCanvas, context, renderConfig, appState,
13708
13888
  );
13709
13889
  }
13710
13890
  context.translate(-cx, -cy);
13711
- let drawX = (x1 + appState.scrollX) * devicePixelRatio - padding;
13712
- let drawY = (y1 + appState.scrollY) * devicePixelRatio - padding;
13891
+ let drawX = (x1 + positionOffset.x + appState.scrollX) * devicePixelRatio - padding;
13892
+ let drawY = (y1 + positionOffset.y + appState.scrollY) * devicePixelRatio - padding;
13713
13893
  const transform = context.getTransform();
13714
13894
  if (canSnapElement(element, appState)) {
13715
13895
  const { a: a2, b: b2, c, d, e, f } = transform;
@@ -13719,8 +13899,8 @@ var drawElementFromCanvas = (elementWithCanvas, context, renderConfig, appState,
13719
13899
  const anchorSceneX = anchorCoords?.[0] ?? x1;
13720
13900
  const anchorSceneY = anchorCoords?.[1] ?? y1;
13721
13901
  const anchorPadding = anchor === element ? padding : getCanvasPadding(anchor);
13722
- const anchorX = (anchorSceneX + appState.scrollX) * devicePixelRatio - anchorPadding;
13723
- const anchorY = (anchorSceneY + appState.scrollY) * devicePixelRatio - anchorPadding;
13902
+ const anchorX = (anchorSceneX + positionOffset.x + appState.scrollX) * devicePixelRatio - anchorPadding;
13903
+ const anchorY = (anchorSceneY + positionOffset.y + appState.scrollY) * devicePixelRatio - anchorPadding;
13724
13904
  const dx = (x1 - anchorSceneX) * devicePixelRatio + anchorPadding - padding;
13725
13905
  const dy = (y1 - anchorSceneY) * devicePixelRatio + anchorPadding - padding;
13726
13906
  context.setTransform(
@@ -13751,8 +13931,8 @@ var drawElementFromCanvas = (elementWithCanvas, context, renderConfig, appState,
13751
13931
  context.strokeStyle = "#c92a2a";
13752
13932
  context.lineWidth = 3;
13753
13933
  context.strokeRect(
13754
- (coords.x + appState.scrollX) * devicePixelRatio,
13755
- (coords.y + appState.scrollY) * devicePixelRatio,
13934
+ (coords.x + positionOffset.x + appState.scrollX) * devicePixelRatio,
13935
+ (coords.y + positionOffset.y + appState.scrollY) * devicePixelRatio,
13756
13936
  getBoundTextMaxWidth(element, textElement) * devicePixelRatio,
13757
13937
  getBoundTextMaxHeight(element, textElement) * devicePixelRatio
13758
13938
  );
@@ -13773,15 +13953,34 @@ var renderSelectionElement = (element, context, appState, selectionColor) => {
13773
13953
  context.strokeRect(offset, offset, element.width, element.height);
13774
13954
  context.restore();
13775
13955
  };
13776
- var renderElement = (element, elementsMap, allElementsMap, rc, context, renderConfig, appState) => {
13956
+ var renderElement = (element, elementsMap, allElementsMap, rc, context, renderConfig, appState, renderState = resolveElementRenderState(
13957
+ element,
13958
+ elementsMap,
13959
+ renderConfig,
13960
+ allElementsMap
13961
+ )) => {
13777
13962
  const reduceAlphaForSelection = appState.openDialog?.name === "elementLinkSelector" && !appState.selectedElementIds[element.id] && !appState.hoveredElementIds[element.id];
13778
- context.globalAlpha = getRenderOpacity(
13779
- element,
13780
- getContainingFrame(element, elementsMap),
13781
- renderConfig.elementsPendingErasure,
13782
- renderConfig.pendingFlowchartNodes,
13783
- reduceAlphaForSelection ? DEFAULT_REDUCED_GLOBAL_ALPHA : 1
13784
- );
13963
+ context.save();
13964
+ context.globalAlpha = renderState.opacity * (reduceAlphaForSelection ? DEFAULT_REDUCED_GLOBAL_ALPHA : 1);
13965
+ if ((renderConfig.isExporting || isFrameLikeElement(element)) && (renderState.offset.x || renderState.offset.y)) {
13966
+ context.translate(renderState.offset.x, renderState.offset.y);
13967
+ }
13968
+ try {
13969
+ drawElement(
13970
+ element,
13971
+ elementsMap,
13972
+ allElementsMap,
13973
+ rc,
13974
+ context,
13975
+ renderConfig,
13976
+ appState,
13977
+ renderState
13978
+ );
13979
+ } finally {
13980
+ context.restore();
13981
+ }
13982
+ };
13983
+ var drawElement = (element, elementsMap, allElementsMap, rc, context, renderConfig, appState, renderState) => {
13785
13984
  switch (element.type) {
13786
13985
  case "magicframe":
13787
13986
  case "frame": {
@@ -13846,7 +14045,8 @@ var renderElement = (element, elementsMap, allElementsMap, rc, context, renderCo
13846
14045
  context,
13847
14046
  renderConfig,
13848
14047
  appState,
13849
- allElementsMap
14048
+ allElementsMap,
14049
+ renderState.offset
13850
14050
  );
13851
14051
  }
13852
14052
  break;
@@ -13947,7 +14147,9 @@ var renderElement = (element, elementsMap, allElementsMap, rc, context, renderCo
13947
14147
  context,
13948
14148
  renderConfig,
13949
14149
  appState,
13950
- allElementsMap
14150
+ allElementsMap,
14151
+ // The crop editor's uncropped preview stays at document coordinates.
14152
+ ZERO_RENDER_OFFSET
13951
14153
  );
13952
14154
  }
13953
14155
  context.restore();
@@ -13957,7 +14159,8 @@ var renderElement = (element, elementsMap, allElementsMap, rc, context, renderCo
13957
14159
  context,
13958
14160
  renderConfig,
13959
14161
  appState,
13960
- allElementsMap
14162
+ allElementsMap,
14163
+ renderState.offset
13961
14164
  );
13962
14165
  context.imageSmoothingEnabled = currentImageSmoothingStatus;
13963
14166
  }
@@ -13967,7 +14170,6 @@ var renderElement = (element, elementsMap, allElementsMap, rc, context, renderCo
13967
14170
  throw new Error(`Unimplemented type ${element.type}`);
13968
14171
  }
13969
14172
  }
13970
- context.globalAlpha = 1;
13971
14173
  };
13972
14174
  function getFreedrawOutlineAsSegments(element, points, elementsMap) {
13973
14175
  const bounds = getElementBounds(
@@ -14373,6 +14575,29 @@ var getArrowheadShapes = (element, shape, position, arrowhead, generator, option
14373
14575
  }
14374
14576
  }
14375
14577
  };
14578
+ var getFreedrawFillCurvePoints = (element) => simplify(element.points, 0.75);
14579
+ var freedrawFillPolygonCache = /* @__PURE__ */ new WeakMap();
14580
+ var getFreedrawFillPolygon = (element) => {
14581
+ const cached = freedrawFillPolygonCache.get(element);
14582
+ if (cached?.version === element.version) {
14583
+ return cached.polygon;
14584
+ }
14585
+ const ops = new RoughGenerator().curve(getFreedrawFillCurvePoints(element), {
14586
+ roughness: 0,
14587
+ disableMultiStroke: true
14588
+ }).sets[0].ops;
14589
+ const bezierPoints = [];
14590
+ for (const { data } of ops) {
14591
+ for (let i = 0; i < data.length; i += 2) {
14592
+ bezierPoints.push(pointFrom12(data[i], data[i + 1]));
14593
+ }
14594
+ }
14595
+ const polygon3 = polygonFromPoints2(
14596
+ pointsOnBezierCurves(bezierPoints, 0.5)
14597
+ );
14598
+ freedrawFillPolygonCache.set(element, { version: element.version, polygon: polygon3 });
14599
+ return polygon3;
14600
+ };
14376
14601
  var generateLinearCollisionShape = (element, elementsMap) => {
14377
14602
  const generator = new RoughGenerator();
14378
14603
  const options = {
@@ -14451,58 +14676,29 @@ var generateLinearCollisionShape = (element, elementsMap) => {
14451
14676
  });
14452
14677
  }
14453
14678
  case "freedraw": {
14454
- if (element.points.length < 2) {
14679
+ const outlinePoints = getFreedrawOutlinePoints(element);
14680
+ if (outlinePoints.length < 2) {
14455
14681
  return [];
14456
14682
  }
14457
- const simplifiedPoints = simplify(
14458
- element.points,
14459
- 0.75
14460
- );
14461
- return generator.curve(simplifiedPoints, options).sets[0].ops.slice(0, element.points.length).map((op, i) => {
14462
- if (i === 0) {
14463
- const p = pointRotateRads11(
14464
- pointFrom12(
14465
- element.x + op.data[0],
14466
- element.y + op.data[1]
14467
- ),
14468
- center,
14469
- element.angle
14470
- );
14471
- return {
14472
- op: "move",
14473
- data: pointFrom12(p[0] - element.x, p[1] - element.y)
14474
- };
14475
- }
14683
+ const collisionOutline = element.strokeOptions?.variability === "constant" ? simplify(
14684
+ outlinePoints,
14685
+ CONSTANT_WIDTH_FREEDRAW.COLLISION_SIMPLIFY_TOLERANCE
14686
+ ) : outlinePoints;
14687
+ if (collisionOutline.length < 2) {
14688
+ return [];
14689
+ }
14690
+ const [firstX, firstY] = collisionOutline[0];
14691
+ const [lastX, lastY] = collisionOutline[collisionOutline.length - 1];
14692
+ const closedOutline = firstX === lastX && firstY === lastY ? collisionOutline : [...collisionOutline, collisionOutline[0]];
14693
+ return closedOutline.map((point, idx) => {
14694
+ const p = pointRotateRads11(
14695
+ pointFrom12(element.x + point[0], element.y + point[1]),
14696
+ center,
14697
+ element.angle
14698
+ );
14476
14699
  return {
14477
- op: "bcurveTo",
14478
- data: [
14479
- pointRotateRads11(
14480
- pointFrom12(
14481
- element.x + op.data[0],
14482
- element.y + op.data[1]
14483
- ),
14484
- center,
14485
- element.angle
14486
- ),
14487
- pointRotateRads11(
14488
- pointFrom12(
14489
- element.x + op.data[2],
14490
- element.y + op.data[3]
14491
- ),
14492
- center,
14493
- element.angle
14494
- ),
14495
- pointRotateRads11(
14496
- pointFrom12(
14497
- element.x + op.data[4],
14498
- element.y + op.data[5]
14499
- ),
14500
- center,
14501
- element.angle
14502
- )
14503
- ].map(
14504
- (p) => pointFrom12(p[0] - element.x, p[1] - element.y)
14505
- ).flat()
14700
+ op: idx === 0 ? "move" : "lineTo",
14701
+ data: pointFrom12(p[0] - element.x, p[1] - element.y)
14506
14702
  };
14507
14703
  });
14508
14704
  }
@@ -14670,12 +14866,8 @@ var _generateElementShape = (element, generator, {
14670
14866
  case "freedraw": {
14671
14867
  const shapes = [];
14672
14868
  if (isPathALoop(element.points)) {
14673
- const simplifiedPoints = simplify(
14674
- element.points,
14675
- 0.75
14676
- );
14677
14869
  shapes.push(
14678
- generator.curve(simplifiedPoints, {
14870
+ generator.curve(getFreedrawFillCurvePoints(element), {
14679
14871
  ...generateRoughOptions(element, false, isDarkMode),
14680
14872
  stroke: "none"
14681
14873
  })
@@ -14830,7 +15022,12 @@ var VARIABLE_WIDTH_FREEDRAW = {
14830
15022
  };
14831
15023
  var CONSTANT_WIDTH_FREEDRAW = {
14832
15024
  /** Stroke size relative to `strokeWidth` for uniform (laser) strokes. */
14833
- SIZE_FACTOR: 1.4
15025
+ SIZE_FACTOR: 1.4,
15026
+ /**
15027
+ * Max deviation (px) when dropping vertices of the dense laser outline for
15028
+ * collision. Perfect-freehand outlines are already thinned by `smoothing`.
15029
+ */
15030
+ COLLISION_SIMPLIFY_TOLERANCE: 0.2
14834
15031
  };
14835
15032
  var getFreedrawStreamline = (element) => element.strokeOptions?.streamline ?? DEFAULT_STROKE_STREAMLINE;
14836
15033
  var getVariableWidthFreedrawOutline = (element) => {
@@ -14862,6 +15059,7 @@ var getConstantWidthFreedrawOutline = (element) => {
14862
15059
  var getFreedrawOutlinePoints = (element) => {
14863
15060
  return element.strokeOptions?.variability === "constant" ? getConstantWidthFreedrawOutline(element) : getVariableWidthFreedrawOutline(element);
14864
15061
  };
15062
+ var getFreedrawMaxStrokeRadius = (element) => element.strokeOptions?.variability === "constant" ? element.strokeWidth * CONSTANT_WIDTH_FREEDRAW.SIZE_FACTOR : element.strokeWidth * VARIABLE_WIDTH_FREEDRAW.SIZE_FACTOR + 3;
14865
15063
  var getFreedrawStrokeCenterPoints = (element) => me(element.points, {
14866
15064
  size: element.strokeWidth * VARIABLE_WIDTH_FREEDRAW.SIZE_FACTOR,
14867
15065
  streamline: getFreedrawStreamline(element),
@@ -16313,13 +16511,15 @@ var newTextElement = (opts) => {
16313
16511
  );
16314
16512
  return textElement;
16315
16513
  };
16316
- var getAdjustedDimensions = (element, elementsMap, nextText) => {
16514
+ var getAdjustedDimensions = (element, elementsMap, nextText, nextFixedWidth) => {
16317
16515
  let { width: nextWidth, height: nextHeight } = measureText(
16318
16516
  nextText,
16319
16517
  getFontString5(element),
16320
16518
  element.lineHeight
16321
16519
  );
16322
- if (!element.autoResize) {
16520
+ if (nextFixedWidth !== void 0) {
16521
+ nextWidth = nextFixedWidth;
16522
+ } else if (!element.autoResize) {
16323
16523
  nextWidth = element.width;
16324
16524
  }
16325
16525
  const { textAlign, verticalAlign } = element;
@@ -16403,10 +16603,26 @@ var adjustXYWithRotation = (sides, x, y, angle2, deltaX1, deltaY1, deltaX2, delt
16403
16603
  }
16404
16604
  return [x, y];
16405
16605
  };
16406
- var refreshTextDimensions = (textElement, container, elementsMap, text = textElement.text) => {
16606
+ var refreshTextDimensions = (textElement, container, elementsMap, text = textElement.text, maxWidth) => {
16407
16607
  if (textElement.isDeleted) {
16408
16608
  return;
16409
16609
  }
16610
+ if (maxWidth !== void 0 && !container && textElement.autoResize && textElement.width <= maxWidth) {
16611
+ const font = getFontString5(textElement);
16612
+ if (measureText(text, font, textElement.lineHeight).width > maxWidth) {
16613
+ const wrappedText = wrapText(text, font, maxWidth);
16614
+ return {
16615
+ text: wrappedText,
16616
+ autoResize: false,
16617
+ ...getAdjustedDimensions(
16618
+ textElement,
16619
+ elementsMap,
16620
+ wrappedText,
16621
+ maxWidth
16622
+ )
16623
+ };
16624
+ }
16625
+ }
16410
16626
  if (container || !textElement.autoResize) {
16411
16627
  text = wrapText(
16412
16628
  text,
@@ -16530,7 +16746,7 @@ var getTextBindingForArrowEndpoint = (arrow, startOrEnd, elementsMap, targetStro
16530
16746
  }
16531
16747
  const direction = vectorFromPoint9(endpoint, neighbor);
16532
16748
  const heading = Math.abs(direction[0]) >= Math.abs(direction[1]) ? direction[0] >= 0 ? HEADING_RIGHT : HEADING_LEFT : direction[1] >= 0 ? HEADING_DOWN : HEADING_UP;
16533
- const gap = isElbowArrow(arrow) ? 0 : getBindingGap({ strokeWidth: targetStrokeWidth }, arrow);
16749
+ const gap = isElbowArrow(arrow) ? 0 : getBindingGap({ strokeWidth: targetStrokeWidth });
16534
16750
  const awayFromTip = vectorNormalize5(vectorFromPoint9(neighbor, endpoint));
16535
16751
  const alongHeading = Math.abs(
16536
16752
  vectorDot(awayFromTip, vector3(heading[0], heading[1]))
@@ -16595,8 +16811,8 @@ import {
16595
16811
  pointFrom as pointFrom14,
16596
16812
  pointRotateRads as pointRotateRads13,
16597
16813
  polygonArea,
16598
- polygonIncludesPoint,
16599
- polygonIncludesPointNonZero,
16814
+ polygonIncludesPoint as polygonIncludesPoint2,
16815
+ polygonIncludesPointNonZero as polygonIncludesPointNonZero2,
16600
16816
  polygonSignedArea
16601
16817
  } from "@excalidraw/math";
16602
16818
  import { isOpaqueColor, isTransparent as isTransparent5 } from "@excalidraw/common";
@@ -17267,7 +17483,7 @@ var selectFaceFromArrangement = (faces, point, options) => {
17267
17483
  if (absArea < options.minArea) {
17268
17484
  continue;
17269
17485
  }
17270
- if (!polygonIncludesPointNonZero(point, face.ring)) {
17486
+ if (!polygonIncludesPointNonZero2(point, face.ring)) {
17271
17487
  continue;
17272
17488
  }
17273
17489
  if (absArea < bestArea) {
@@ -17283,13 +17499,13 @@ var selectFaceFromArrangement = (faces, point, options) => {
17283
17499
  (face) => face.componentId !== selected.componentId && // a component's outside contour shares the unbounded face's orientation
17284
17500
  Math.sign(areaOf.get(face)) === outerSign && Math.abs(areaOf.get(face)) >= options.minArea && // the click face never sits inside one of its own holes (defensive —
17285
17501
  // the smallest-containing-face selection above already guarantees it)
17286
- !polygonIncludesPointNonZero(point, face.ring) && // components never intersect (crossings would have merged them), so a
17502
+ !polygonIncludesPointNonZero2(point, face.ring) && // components never intersect (crossings would have merged them), so a
17287
17503
  // single-vertex test decides containment
17288
- polygonIncludesPointNonZero(face.ring[0], selected.ring)
17504
+ polygonIncludesPointNonZero2(face.ring[0], selected.ring)
17289
17505
  );
17290
17506
  const holes = islandCandidates.filter(
17291
17507
  (hole) => !islandCandidates.some(
17292
- (other) => other !== hole && polygonIncludesPointNonZero(hole.ring[0], other.ring)
17508
+ (other) => other !== hole && polygonIncludesPointNonZero2(hole.ring[0], other.ring)
17293
17509
  )
17294
17510
  );
17295
17511
  return { face: selected, holes };
@@ -17575,7 +17791,7 @@ var computeBucketFillPolygon = (args) => {
17575
17791
  );
17576
17792
  }
17577
17793
  }
17578
- return samples.some((sample) => polygonIncludesPoint(sample, fillRegion));
17794
+ return samples.some((sample) => polygonIncludesPoint2(sample, fillRegion));
17579
17795
  };
17580
17796
  const paintOverlapsRegion = (element) => {
17581
17797
  const [minX, minY, maxX, maxY] = getElementBounds(element, elementsMap);
@@ -17588,7 +17804,7 @@ var computeBucketFillPolygon = (args) => {
17588
17804
  const outline = getElementLineSegments(element, elementsMap);
17589
17805
  const outlineStep = Math.max(1, Math.floor(outline.length / 16));
17590
17806
  for (let i = 0; i < outline.length; i += outlineStep) {
17591
- if (polygonIncludesPoint(outline[i][0], fillRegion)) {
17807
+ if (polygonIncludesPoint2(outline[i][0], fillRegion)) {
17592
17808
  return true;
17593
17809
  }
17594
17810
  }
@@ -17789,8 +18005,9 @@ var duplicateElements = (opts) => {
17789
18005
  const duplicatedElements = [];
17790
18006
  const origElements = [];
17791
18007
  const origIdToDuplicateId = /* @__PURE__ */ new Map();
17792
- const duplicateIdToOrigElement = /* @__PURE__ */ new Map();
18008
+ const duplicateIdToOrigId = /* @__PURE__ */ new Map();
17793
18009
  const duplicateElementsMap = /* @__PURE__ */ new Map();
18010
+ const origElementsMap = /* @__PURE__ */ new Map();
17794
18011
  const elementsMap = arrayToMap9(elements);
17795
18012
  const _idsOfElementsToDuplicate = opts.type === "in-place" ? opts.idsOfElementsToDuplicate : new Map(elements.map((el) => [el.id, el]));
17796
18013
  const preserveFrameChildrenOrder = opts.type === "everything" && opts.preserveFrameChildrenOrder;
@@ -17822,8 +18039,9 @@ var duplicateElements = (opts) => {
17822
18039
  );
17823
18040
  processedIds.set(newElement3.id, true);
17824
18041
  duplicateElementsMap.set(newElement3.id, newElement3);
18042
+ origElementsMap.set(element2.id, element2);
17825
18043
  origIdToDuplicateId.set(element2.id, newElement3.id);
17826
- duplicateIdToOrigElement.set(newElement3.id, element2);
18044
+ duplicateIdToOrigId.set(newElement3.id, element2.id);
17827
18045
  origElements.push(element2);
17828
18046
  duplicatedElements.push(newElement3);
17829
18047
  acc.push(newElement3);
@@ -17935,7 +18153,9 @@ var duplicateElements = (opts) => {
17935
18153
  );
17936
18154
  if (opts.overrides) {
17937
18155
  for (const duplicateElement2 of duplicatedElements) {
17938
- const origElement = duplicateIdToOrigElement.get(duplicateElement2.id);
18156
+ const origElement = origElementsMap.get(
18157
+ duplicateIdToOrigId.get(duplicateElement2.id)
18158
+ );
17939
18159
  if (origElement) {
17940
18160
  Object.assign(
17941
18161
  duplicateElement2,
@@ -17951,10 +18171,82 @@ var duplicateElements = (opts) => {
17951
18171
  return {
17952
18172
  duplicatedElements,
17953
18173
  duplicateElementsMap,
18174
+ origElementsMap,
17954
18175
  elementsWithDuplicates,
17955
- origIdToDuplicateId
18176
+ origIdToDuplicateId,
18177
+ duplicateIdToOrigId
17956
18178
  };
17957
18179
  };
18180
+ var reconcileDuplicatedElements = (hostElements, nextElements, duplicatedElements) => {
18181
+ if (hostElements === false) {
18182
+ return { elements: nextElements, duplicatedElements: [] };
18183
+ }
18184
+ if (!hostElements) {
18185
+ return { elements: nextElements, duplicatedElements };
18186
+ }
18187
+ const duplicatesMap = arrayToMap9(duplicatedElements);
18188
+ const hostDuplicates = /* @__PURE__ */ new Map();
18189
+ for (const element of hostElements) {
18190
+ if (duplicatesMap.has(element.id)) {
18191
+ hostDuplicates.set(element.id, element);
18192
+ }
18193
+ }
18194
+ const survivedIds = /* @__PURE__ */ new Set();
18195
+ for (const [id, element] of hostDuplicates) {
18196
+ const duplicate = duplicatesMap.get(id);
18197
+ if (element !== duplicate) {
18198
+ Object.assign(duplicate, element);
18199
+ ShapeCache.delete(duplicate);
18200
+ bumpVersion(duplicate);
18201
+ }
18202
+ if (!duplicate.isDeleted) {
18203
+ survivedIds.add(id);
18204
+ }
18205
+ }
18206
+ const isVetoed = (id) => duplicatesMap.has(id) && !survivedIds.has(id);
18207
+ for (const id of survivedIds) {
18208
+ const duplicate = duplicatesMap.get(id);
18209
+ if (isBoundToContainer(duplicate) && isVetoed(duplicate.containerId)) {
18210
+ survivedIds.delete(id);
18211
+ }
18212
+ }
18213
+ const elements = [];
18214
+ for (const element of hostElements) {
18215
+ const duplicate = duplicatesMap.get(element.id);
18216
+ if (!duplicate) {
18217
+ elements.push(element);
18218
+ } else if (survivedIds.has(element.id) && hostDuplicates.get(element.id) === element) {
18219
+ elements.push(duplicate);
18220
+ }
18221
+ }
18222
+ if (survivedIds.size === duplicatedElements.length) {
18223
+ return { elements, duplicatedElements };
18224
+ }
18225
+ const survivedDuplicates = duplicatedElements.filter(
18226
+ (duplicate) => survivedIds.has(duplicate.id)
18227
+ );
18228
+ for (const duplicate of survivedDuplicates) {
18229
+ const updates = {};
18230
+ if (duplicate.boundElements?.some((binding) => isVetoed(binding.id))) {
18231
+ updates.boundElements = duplicate.boundElements.filter(
18232
+ (binding) => !isVetoed(binding.id)
18233
+ );
18234
+ }
18235
+ if (duplicate.frameId && isVetoed(duplicate.frameId)) {
18236
+ updates.frameId = null;
18237
+ }
18238
+ if (isArrowElement(duplicate)) {
18239
+ if (duplicate.startBinding && isVetoed(duplicate.startBinding.elementId)) {
18240
+ updates.startBinding = null;
18241
+ }
18242
+ if (duplicate.endBinding && isVetoed(duplicate.endBinding.elementId)) {
18243
+ updates.endBinding = null;
18244
+ }
18245
+ }
18246
+ Object.assign(duplicate, updates);
18247
+ }
18248
+ return { elements, duplicatedElements: survivedDuplicates };
18249
+ };
17958
18250
  var _deepCopyElement = (val, depth = 0) => {
17959
18251
  if (val == null || typeof val !== "object") {
17960
18252
  return val;
@@ -20430,13 +20722,11 @@ var dragSelectedElements = (pointerDownState, _selectedElements, offset, scene,
20430
20722
  snapOffset,
20431
20723
  gridSize
20432
20724
  );
20433
- const elementsToUpdateIds = new Set(
20434
- Array.from(elementsToUpdate, (el) => el.id)
20725
+ const elementsToUpdateArray = Array.from(elementsToUpdate);
20726
+ const elementsToUpdateIds = getSimultaneouslyUpdatedElementIds(
20727
+ elementsToUpdateArray
20435
20728
  );
20436
20729
  elementsToUpdate.forEach((element) => {
20437
- const isArrow = !isArrowElement(element);
20438
- const isStartBoundElementSelected = isArrow || (element.startBinding ? elementsToUpdateIds.has(element.startBinding.elementId) : false);
20439
- const isEndBoundElementSelected = isArrow || (element.endBinding ? elementsToUpdateIds.has(element.endBinding.elementId) : false);
20440
20730
  if (!isArrowElement(element)) {
20441
20731
  updateElementCoords(pointerDownState, element, scene, adjustedOffset);
20442
20732
  const textElement = getBoundTextElement(
@@ -20452,7 +20742,7 @@ var dragSelectedElements = (pointerDownState, _selectedElements, offset, scene,
20452
20742
  );
20453
20743
  }
20454
20744
  updateBoundElements(element, scene, {
20455
- simultaneouslyUpdated: Array.from(elementsToUpdate)
20745
+ simultaneouslyUpdated: elementsToUpdateArray
20456
20746
  });
20457
20747
  } else if (
20458
20748
  // NOTE: Add a little initial drag to the arrow dragging when the arrow
@@ -20461,8 +20751,8 @@ var dragSelectedElements = (pointerDownState, _selectedElements, offset, scene,
20461
20751
  elementsToUpdate.size > 1 || Math.max(Math.abs(adjustedOffset.x), Math.abs(adjustedOffset.y)) > DRAGGING_THRESHOLD2 || !element.startBinding && !element.endBinding
20462
20752
  ) {
20463
20753
  updateElementCoords(pointerDownState, element, scene, adjustedOffset);
20464
- const shouldUnbindStart = element.startBinding && !isStartBoundElementSelected;
20465
- const shouldUnbindEnd = element.endBinding && !isEndBoundElementSelected;
20754
+ const shouldUnbindStart = element.startBinding ? !elementsToUpdateIds.has(element.startBinding.elementId) : true;
20755
+ const shouldUnbindEnd = element.endBinding ? !elementsToUpdateIds.has(element.endBinding.elementId) : true;
20466
20756
  if (shouldUnbindStart || shouldUnbindEnd) {
20467
20757
  if (shouldUnbindStart) {
20468
20758
  unbindBindingElement(element, "start", scene);
@@ -20701,7 +20991,7 @@ import {
20701
20991
  getFontString as getFontString7
20702
20992
  } from "@excalidraw/common";
20703
20993
  var embeddedLinkCache = /* @__PURE__ */ new Map();
20704
- var RE_YOUTUBE = /^(?:http(?:s)?:\/\/)?(?:www\.)?youtu(?:be\.com|\.be)\/(embed\/|watch\?v=|shorts\/|playlist\?list=|embed\/videoseries\?list=)?([a-zA-Z0-9_-]+)/;
20994
+ var RE_YOUTUBE = /^(?:http(?:s)?:\/\/)?(?:www\.)?youtu(?:be\.com|\.be)\/(embed\/|watch\?v=|shorts\/|live\/|playlist\?list=|embed\/videoseries\?list=)?([a-zA-Z0-9_-]+)/;
20705
20995
  var RE_VIMEO = /^(?:http(?:s)?:\/\/)?(?:(?:w){3}\.)?(?:player\.)?vimeo\.com\/(?:video\/)?([^?\s]+)(?:\?.*)?$/;
20706
20996
  var RE_FIGMA = /^https:\/\/(?:www\.)?figma\.com/;
20707
20997
  var RE_GH_GIST = /^https:\/\/gist\.github\.com\/([\w_-]+)\/([\w_-]+)/;
@@ -21110,7 +21400,7 @@ var embeddableURLValidator = (url, validateEmbeddable) => {
21110
21400
  init_define_import_meta_env();
21111
21401
  import { invariant as invariant11, toBrandedType as toBrandedType2 } from "@excalidraw/common";
21112
21402
  import {
21113
- clamp as clamp7,
21403
+ clamp as clamp9,
21114
21404
  pointFrom as pointFrom15
21115
21405
  } from "@excalidraw/math";
21116
21406
  var VERTICAL_OFFSET = 100;
@@ -21141,7 +21431,7 @@ var findNearestFreeSlot = (ideal, size, occupied) => {
21141
21431
  if (gapEnds[i] - gapStarts[i] < size) {
21142
21432
  continue;
21143
21433
  }
21144
- const start = clamp7(ideal, gapStarts[i], gapEnds[i] - size);
21434
+ const start = clamp9(ideal, gapStarts[i], gapEnds[i] - size);
21145
21435
  const distance3 = Math.abs(start - ideal);
21146
21436
  if (distance3 <= bestDistance) {
21147
21437
  best = start;
@@ -21339,9 +21629,17 @@ var createBindingArrow = (startBindingElement, endBindingElement, direction, app
21339
21629
  startBindingElement,
21340
21630
  "orbit",
21341
21631
  "start",
21342
- scene
21632
+ scene,
21633
+ appState.zoom
21634
+ );
21635
+ bindBindingElement(
21636
+ bindingArrow,
21637
+ endBindingElement,
21638
+ "orbit",
21639
+ "end",
21640
+ scene,
21641
+ appState.zoom
21343
21642
  );
21344
- bindBindingElement(bindingArrow, endBindingElement, "orbit", "end", scene);
21345
21643
  const changedElements = /* @__PURE__ */ new Map();
21346
21644
  changedElements.set(
21347
21645
  startBindingElement.id,
@@ -21670,13 +21968,7 @@ var getContiguousFrameRangeElements = (allElements, frameId) => {
21670
21968
  }
21671
21969
  return allElements.slice(rangeStart, rangeEnd + 1);
21672
21970
  };
21673
- var moveArrowAboveBindable2 = (point, arrow, elements, elementsMap, scene, hit) => {
21674
- const hoveredElement = hit ? hit : getHoveredElementForBinding(
21675
- point,
21676
- // SAFETY: callers that omit `hit` pass non-deleted elements
21677
- getNonDeletedElements(elements),
21678
- elementsMap
21679
- );
21971
+ var moveArrowAboveBindable2 = (point, arrow, elements, elementsMap, scene, hoveredElement) => {
21680
21972
  if (!hoveredElement) {
21681
21973
  return elements;
21682
21974
  }
@@ -22027,7 +22319,7 @@ var isFocusPointVisible = (focusPoint, arrow, bindableElement, elementsMap, appS
22027
22319
  element: bindableElement,
22028
22320
  elementsMap,
22029
22321
  point: focusPoint,
22030
- threshold: getBindingGap(bindableElement, arrow),
22322
+ threshold: getBindingGap(bindableElement),
22031
22323
  overrideShouldTestInside: true
22032
22324
  });
22033
22325
  };
@@ -22119,12 +22411,11 @@ var handleFocusPointDrag = (linearElementEditor, elementsMap, pointerCoords, sce
22119
22411
  pointerCoords.y - offsetY
22120
22412
  );
22121
22413
  const bindingField = isStartBinding ? "startBinding" : "endBinding";
22122
- const hit = getHoveredElementForFocusPoint(
22414
+ const hit = getHoveredElementForBinding(
22123
22415
  point,
22124
- arrow,
22125
22416
  scene.getNonDeletedElements(),
22126
22417
  elementsMap,
22127
- maxBindingDistance_simple(appState.zoom)
22418
+ appState.zoom
22128
22419
  );
22129
22420
  if (hit && isBindingEnabled(appState)) {
22130
22421
  if (arrow[bindingField] && hit.id !== binding?.elementId) {
@@ -22142,6 +22433,7 @@ var handleFocusPointDrag = (linearElementEditor, elementsMap, pointerCoords, sce
22142
22433
  newMode || "orbit",
22143
22434
  linearElementEditor.draggedFocusPointBinding,
22144
22435
  scene,
22436
+ appState.zoom,
22145
22437
  point
22146
22438
  );
22147
22439
  }
@@ -22455,6 +22747,198 @@ var normalizeSVG = (SVGString) => {
22455
22747
  }
22456
22748
  };
22457
22749
 
22750
+ // src/listMarker.ts
22751
+ init_define_import_meta_env();
22752
+ import { arrayToMap as arrayToMap13 } from "@excalidraw/common";
22753
+ var LIST_MARKER_REGEX = /^(\s*)(?:\((\d{1,9}|[a-zA-Z]+)\)|\[(\d{1,9})\]|(\d{1,9}|[a-zA-Z]+)([.)]))(?=\s|$)/;
22754
+ var LONE_NUMBER_REGEX = /^(\s*)(\d{1,9})\s*$/;
22755
+ var LAST_ALPHA_ORDINAL = "h".charCodeAt(0) - "a".charCodeAt(0) + 1;
22756
+ var MAX_ORDINAL = {
22757
+ decimal: 42,
22758
+ alpha: 9,
22759
+ // i. (then switching to roman)
22760
+ roman: 99
22761
+ };
22762
+ var toRoman = (value) => ["", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc"][Math.floor(value / 10)] + ["", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix"][value % 10];
22763
+ var ROMAN_VALUES = new Map(
22764
+ Array.from({ length: MAX_ORDINAL.roman }, (_, index) => [
22765
+ toRoman(index + 1),
22766
+ index + 1
22767
+ ])
22768
+ );
22769
+ var parseLoneNumber = (text) => {
22770
+ const match = text.match(LONE_NUMBER_REGEX);
22771
+ if (!match) {
22772
+ return null;
22773
+ }
22774
+ const [, indent, value] = match;
22775
+ return {
22776
+ index: indent.length,
22777
+ value,
22778
+ kind: "decimal",
22779
+ isUpperCase: false,
22780
+ style: "decimal",
22781
+ ordinal: parseInt(value, 10),
22782
+ contentIndex: text.length
22783
+ };
22784
+ };
22785
+ var parseListMarker = (text) => {
22786
+ const match = text.match(LIST_MARKER_REGEX);
22787
+ if (!match) {
22788
+ return null;
22789
+ }
22790
+ const [, indent, parenValue, bracketValue, plainValue, plainSuffix] = match;
22791
+ const value = parenValue ?? bracketValue ?? plainValue;
22792
+ const lowerCaseValue = value.toLowerCase();
22793
+ const isUpperCase = /[A-Z]/.test(value);
22794
+ const alphaOrdinal = lowerCaseValue.charCodeAt(0) - "a".charCodeAt(0) + 1;
22795
+ let kind;
22796
+ let ordinal;
22797
+ if (/\d/.test(value)) {
22798
+ kind = "decimal";
22799
+ ordinal = parseInt(value, 10);
22800
+ } else if (isUpperCase && /[a-z]/.test(value)) {
22801
+ return null;
22802
+ } else if (value.length === 1 && alphaOrdinal <= LAST_ALPHA_ORDINAL) {
22803
+ kind = "alpha";
22804
+ ordinal = alphaOrdinal;
22805
+ } else if (ROMAN_VALUES.has(lowerCaseValue)) {
22806
+ kind = "roman";
22807
+ ordinal = ROMAN_VALUES.get(lowerCaseValue);
22808
+ } else {
22809
+ return null;
22810
+ }
22811
+ const affixes = parenValue !== void 0 ? "()" : bracketValue !== void 0 ? "[]" : plainSuffix;
22812
+ return {
22813
+ index: indent.length + (plainValue !== void 0 ? 0 : 1),
22814
+ value,
22815
+ kind,
22816
+ isUpperCase,
22817
+ style: `${kind}${isUpperCase ? "-upper" : ""}${affixes}`,
22818
+ ordinal,
22819
+ contentIndex: match[0].length + text.slice(match[0].length).match(/^\s*/)[0].length
22820
+ };
22821
+ };
22822
+ var getListItemMarker = (text) => {
22823
+ const marker = parseLoneNumber(text) ?? parseListMarker(text);
22824
+ if (!marker || marker.ordinal > MAX_ORDINAL[marker.kind]) {
22825
+ return null;
22826
+ }
22827
+ const otherLines = text.slice(marker.index).split("\n").slice(1);
22828
+ if (otherLines.some((line2) => parseListMarker(line2)?.style === marker.style)) {
22829
+ return null;
22830
+ }
22831
+ return marker;
22832
+ };
22833
+ var renumberListMarker = (text, marker, ordinal) => {
22834
+ if (ordinal > MAX_ORDINAL[marker.kind]) {
22835
+ return null;
22836
+ }
22837
+ let value;
22838
+ if (marker.kind === "decimal") {
22839
+ value = String(ordinal).padStart(
22840
+ marker.value.startsWith("0") ? marker.value.length : 0,
22841
+ "0"
22842
+ );
22843
+ } else if (marker.kind === "alpha") {
22844
+ value = String.fromCharCode("a".charCodeAt(0) + ordinal - 1);
22845
+ } else {
22846
+ value = toRoman(ordinal);
22847
+ }
22848
+ if (marker.isUpperCase) {
22849
+ value = value.toUpperCase();
22850
+ }
22851
+ return text.slice(0, marker.index) + value + text.slice(marker.index + marker.value.length);
22852
+ };
22853
+ var setText = (element, container, originalText, scene) => {
22854
+ if (container) {
22855
+ scene.mutateElement(element, { originalText });
22856
+ redrawTextBoundingBox(element, container, scene);
22857
+ } else {
22858
+ scene.mutateElement(element, {
22859
+ originalText,
22860
+ // re-wraps the text, and keeps it anchored as per its alignment
22861
+ ...refreshTextDimensions(
22862
+ element,
22863
+ null,
22864
+ scene.getNonDeletedElementsMap(),
22865
+ originalText
22866
+ )
22867
+ });
22868
+ }
22869
+ };
22870
+ var advanceDuplicatedListMarkers = (duplicatedElements, scene) => {
22871
+ const duplicatesMap = arrayToMap13(duplicatedElements);
22872
+ const elementsMap = scene.getNonDeletedElementsMap();
22873
+ const itemsByStyle = /* @__PURE__ */ new Map();
22874
+ for (const element of duplicatedElements) {
22875
+ if (isBoundToContainer(element) && duplicatesMap.has(element.containerId)) {
22876
+ continue;
22877
+ }
22878
+ const text = isTextElement(element) ? element : getBoundTextElement(element, elementsMap);
22879
+ const marker = text && getListItemMarker(text.originalText);
22880
+ if (!text || !marker) {
22881
+ return [];
22882
+ }
22883
+ const items = itemsByStyle.get(marker.style) ?? [];
22884
+ items.push({
22885
+ element: text,
22886
+ container: text === element ? null : element,
22887
+ marker
22888
+ });
22889
+ itemsByStyle.set(marker.style, items);
22890
+ }
22891
+ const allItems = [...itemsByStyle.values()].flat();
22892
+ if (allItems.length > 1 && allItems.some(
22893
+ ({ element, marker }) => marker.contentIndex < element.originalText.length
22894
+ )) {
22895
+ return [];
22896
+ }
22897
+ const advances = [];
22898
+ for (const items of itemsByStyle.values()) {
22899
+ const maxOrdinal = Math.max(...items.map((item) => item.marker.ordinal));
22900
+ items.sort((a2, b2) => a2.marker.ordinal - b2.marker.ordinal);
22901
+ for (const [index, { element, container, marker }] of items.entries()) {
22902
+ const nextOriginalText = renumberListMarker(
22903
+ element.originalText,
22904
+ marker,
22905
+ maxOrdinal + 1 + index
22906
+ );
22907
+ if (nextOriginalText === null) {
22908
+ continue;
22909
+ }
22910
+ advances.push({
22911
+ elementId: element.id,
22912
+ prevOriginalText: element.originalText,
22913
+ nextOriginalText,
22914
+ contentStart: marker.contentIndex + nextOriginalText.length - element.originalText.length,
22915
+ prevContainerSize: container ? { width: container.width, height: container.height } : null
22916
+ });
22917
+ setText(element, container, nextOriginalText, scene);
22918
+ }
22919
+ }
22920
+ return advances;
22921
+ };
22922
+ var applyListMarkerAdvances = (advances, direction, scene) => {
22923
+ const elementsMap = scene.getNonDeletedElementsMap();
22924
+ for (const advance of advances) {
22925
+ const element = elementsMap.get(advance.elementId);
22926
+ if (!element || !isTextElement(element)) {
22927
+ continue;
22928
+ }
22929
+ const container = getContainerElement(element, elementsMap);
22930
+ if (direction === "prev" && container && advance.prevContainerSize) {
22931
+ scene.mutateElement(container, advance.prevContainerSize);
22932
+ }
22933
+ setText(
22934
+ element,
22935
+ container,
22936
+ direction === "prev" ? advance.prevOriginalText : advance.nextOriginalText,
22937
+ scene
22938
+ );
22939
+ }
22940
+ };
22941
+
22458
22942
  // src/positionElementsOnGrid.ts
22459
22943
  init_define_import_meta_env();
22460
22944
  var positionElementsOnGrid = (elements, centerX, centerY, padding = 50) => {
@@ -24008,7 +24492,7 @@ import {
24008
24492
  VERTICAL_ALIGN as VERTICAL_ALIGN5,
24009
24493
  getSizeFromPoints as getSizeFromPoints3,
24010
24494
  randomId as randomId4,
24011
- arrayToMap as arrayToMap13,
24495
+ arrayToMap as arrayToMap14,
24012
24496
  assertNever as assertNever5,
24013
24497
  cloneJSON,
24014
24498
  getFontString as getFontString9,
@@ -24017,7 +24501,8 @@ import {
24017
24501
  toBrandedType as toBrandedType3,
24018
24502
  getLineHeight as getLineHeight3,
24019
24503
  DEFAULT_STICKY_NOTE_SIZE as DEFAULT_STICKY_NOTE_SIZE2,
24020
- isTransparent as isTransparent6
24504
+ isTransparent as isTransparent6,
24505
+ DEFAULT_ZOOM as DEFAULT_ZOOM3
24021
24506
  } from "@excalidraw/common";
24022
24507
  var DEFAULT_LINEAR_ELEMENT_PROPS = {
24023
24508
  width: 100,
@@ -24048,7 +24533,7 @@ var bindTextToContainer = (container, textProps, scene) => {
24048
24533
  redrawTextBoundingBox(textElement, container, scene);
24049
24534
  return [container, textElement];
24050
24535
  };
24051
- var bindLinearElementToElement = (linearElement, start, end, elementStore, scene) => {
24536
+ var bindLinearElementToElement = (linearElement, start, end, elementStore, scene, zoom) => {
24052
24537
  let startBoundElement;
24053
24538
  let endBoundElement;
24054
24539
  Object.assign(linearElement, {
@@ -24123,7 +24608,8 @@ var bindLinearElementToElement = (linearElement, start, end, elementStore, scene
24123
24608
  startBoundElement,
24124
24609
  "orbit",
24125
24610
  "start",
24126
- scene
24611
+ scene,
24612
+ zoom
24127
24613
  );
24128
24614
  }
24129
24615
  }
@@ -24194,7 +24680,8 @@ var bindLinearElementToElement = (linearElement, start, end, elementStore, scene
24194
24680
  endBoundElement,
24195
24681
  "orbit",
24196
24682
  "end",
24197
- scene
24683
+ scene,
24684
+ zoom
24198
24685
  );
24199
24686
  }
24200
24687
  }
@@ -24252,7 +24739,7 @@ var ElementStore = class {
24252
24739
  };
24253
24740
  getElementsMap = () => {
24254
24741
  return toBrandedType3(
24255
- arrayToMap13(this.getElements())
24742
+ arrayToMap14(this.getElements())
24256
24743
  );
24257
24744
  };
24258
24745
  getElement = (id) => {
@@ -24443,7 +24930,8 @@ var convertToExcalidrawElements = (elementsSkeleton, opts) => {
24443
24930
  originalStart,
24444
24931
  originalEnd,
24445
24932
  elementStore,
24446
- scene
24933
+ scene,
24934
+ DEFAULT_ZOOM3
24447
24935
  );
24448
24936
  container = linearElement;
24449
24937
  elementStore.add(linearElement);
@@ -24467,7 +24955,8 @@ var convertToExcalidrawElements = (elementsSkeleton, opts) => {
24467
24955
  start,
24468
24956
  end,
24469
24957
  elementStore,
24470
- scene
24958
+ scene,
24959
+ DEFAULT_ZOOM3
24471
24960
  );
24472
24961
  elementStore.add(linearElement);
24473
24962
  elementStore.add(startBoundElement);
@@ -25117,7 +25606,6 @@ export {
25117
25606
  AppStateDelta,
25118
25607
  BASE_ARROW_MIN_LENGTH,
25119
25608
  BASE_BINDING_GAP,
25120
- BASE_BINDING_GAP_ELBOW,
25121
25609
  BASE_PADDING,
25122
25610
  BindableElement,
25123
25611
  BoundElement,
@@ -25155,7 +25643,9 @@ export {
25155
25643
  addElementsToFrame,
25156
25644
  addNewNodes,
25157
25645
  addToGroup,
25646
+ advanceDuplicatedListMarkers,
25158
25647
  alignElements,
25648
+ applyListMarkerAdvances,
25159
25649
  avoidRectangularCorner,
25160
25650
  bindBindingElement,
25161
25651
  bindBindingElementToFixedPoint,
@@ -25218,6 +25708,7 @@ export {
25218
25708
  generateRoughOptions,
25219
25709
  getActiveTextElement,
25220
25710
  getAllHoveredElementAtPoint,
25711
+ getAllMidpoints,
25221
25712
  getApproxMinLineHeight,
25222
25713
  getApproxMinLineWidth,
25223
25714
  getArrowLocalFixedPoints,
@@ -25255,10 +25746,12 @@ export {
25255
25746
  getDiamondPoints,
25256
25747
  getDragOffsetXY,
25257
25748
  getDraggedElementsBounds,
25749
+ getElbowArrowSnapMidPoint,
25258
25750
  getElementAbsoluteCoords2 as getElementAbsoluteCoords,
25259
25751
  getElementBounds,
25260
25752
  getElementLineSegments,
25261
25753
  getElementPointsCoords,
25754
+ getElementRenderOffset,
25262
25755
  getElementShape,
25263
25756
  getElementWithTransformHandleType,
25264
25757
  getElementsCompletelyInFrame,
@@ -25275,6 +25768,8 @@ export {
25275
25768
  getFrameChildrenInsertionIndex,
25276
25769
  getFrameLikeElements,
25277
25770
  getFrameLikeTitle,
25771
+ getFreedrawFillPolygon,
25772
+ getFreedrawMaxStrokeRadius,
25278
25773
  getFreedrawOutlineAsSegments,
25279
25774
  getFreedrawOutlinePoints,
25280
25775
  getFreedrawStrokeCenterPoints,
@@ -25282,7 +25777,6 @@ export {
25282
25777
  getGlobalFixedPoints,
25283
25778
  getHeadingForElbowArrowSnap,
25284
25779
  getHoveredElementForBinding,
25285
- getHoveredElementForFocusPoint,
25286
25780
  getInitializedImageElements,
25287
25781
  getLineHeightInPx,
25288
25782
  getLineWidth,
@@ -25305,7 +25799,7 @@ export {
25305
25799
  getPerfectElementSize,
25306
25800
  getPositionAfterHeightChange,
25307
25801
  getRectangleBoxAbsoluteCoords,
25308
- getRenderOpacity,
25802
+ getRenderElementWithPositionOverride,
25309
25803
  getResizeArrowDirection,
25310
25804
  getResizeOffsetXY,
25311
25805
  getResizedElementAbsoluteCoords,
@@ -25317,6 +25811,7 @@ export {
25317
25811
  getSelectedGroupIdForElement,
25318
25812
  getSelectedGroupIds,
25319
25813
  getSelectionStateForElements,
25814
+ getSimultaneouslyUpdatedElementIds,
25320
25815
  getSnapOutlineMidPoint,
25321
25816
  getStickyNoteCornerRadius,
25322
25817
  getStickyNoteDateLabel,
@@ -25331,6 +25826,7 @@ export {
25331
25826
  getTextAnchorRatios,
25332
25827
  getTextBindingForArrowEndpoint,
25333
25828
  getTextElementAngle,
25829
+ getTextElementWithAccuratePosition,
25334
25830
  getTextFromElements,
25335
25831
  getTextHeight,
25336
25832
  getTextWidth,
@@ -25417,6 +25913,7 @@ export {
25417
25913
  isMeasureTextSupported,
25418
25914
  isNodeInFlowchart,
25419
25915
  isNonDeletedElement,
25916
+ isOpaqueForBinding,
25420
25917
  isPathALoop,
25421
25918
  isPointInElement,
25422
25919
  isRectangularElement,
@@ -25481,7 +25978,9 @@ export {
25481
25978
  pointInsideBoundsInclusive,
25482
25979
  positionElementsOnGrid,
25483
25980
  projectFixedPointOntoDiagonal,
25981
+ reanchorBindingsToOutline,
25484
25982
  recognizeShape,
25983
+ reconcileDuplicatedElements,
25485
25984
  redrawTextBoundingBox,
25486
25985
  refreshTextDimensions,
25487
25986
  relayoutStickyNotes,
@@ -25498,6 +25997,7 @@ export {
25498
25997
  resizeSingleElement,
25499
25998
  resizeSingleTextElement,
25500
25999
  resizeTest,
26000
+ resolveElementRenderState,
25501
26001
  selectGroup,
25502
26002
  selectGroupsForSelectedElements,
25503
26003
  selectGroupsFromGivenElements,
@@ -25506,7 +26006,6 @@ export {
25506
26006
  shouldApplyFrameClip,
25507
26007
  shouldTestInside,
25508
26008
  showSelectedShapeActions,
25509
- snapToMid,
25510
26009
  suppportsHorizontalAlign,
25511
26010
  syncInvalidIndices,
25512
26011
  syncInvalidIndicesImmutable,