@excalidraw/excalidraw 0.17.1-7441-8b04eb5 → 0.17.1-7500-ac247a0

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 (69) hide show
  1. package/dist/browser/dev/excalidraw-assets-dev/{chunk-SUHLFFEF.js → chunk-KGZXLFLR.js} +12342 -12294
  2. package/dist/browser/dev/excalidraw-assets-dev/chunk-KGZXLFLR.js.map +7 -0
  3. package/dist/browser/dev/excalidraw-assets-dev/{image-NOPDRTTM.css → image-3MFRCKYM.css} +3929 -3929
  4. package/dist/browser/dev/excalidraw-assets-dev/image-3MFRCKYM.css.map +7 -0
  5. package/dist/browser/dev/excalidraw-assets-dev/{image-HYNUJ3XL.js → image-5TVMINCA.js} +2 -2
  6. package/dist/browser/dev/index.js +17 -7
  7. package/dist/browser/prod/excalidraw-assets/chunk-4YN2HN3S.js +257 -0
  8. package/dist/browser/prod/excalidraw-assets/{image-DZ6B4AID.js → image-LTLHTTSE.js} +1 -1
  9. package/dist/browser/prod/excalidraw-assets/image-QBL334OA.css +1 -0
  10. package/dist/browser/prod/index.js +1 -1
  11. package/dist/dev/index.js +785 -740
  12. package/dist/dev/index.js.map +4 -4
  13. package/dist/excalidraw/actions/actionAddToLibrary.d.ts +3 -3
  14. package/dist/excalidraw/actions/actionBoundText.d.ts +2 -2
  15. package/dist/excalidraw/actions/actionCanvas.d.ts +12 -12
  16. package/dist/excalidraw/actions/actionClipboard.d.ts +7 -7
  17. package/dist/excalidraw/actions/actionDeleteSelected.d.ts +3 -3
  18. package/dist/excalidraw/actions/actionElementLock.d.ts +2 -2
  19. package/dist/excalidraw/actions/actionExport.d.ts +8 -8
  20. package/dist/excalidraw/actions/actionFinalize.d.ts +2 -2
  21. package/dist/excalidraw/actions/actionFrame.d.ts +3 -3
  22. package/dist/excalidraw/actions/actionGroup.d.ts +2 -2
  23. package/dist/excalidraw/actions/actionLinearEditor.d.ts +1 -1
  24. package/dist/excalidraw/actions/actionMenu.d.ts +2 -2
  25. package/dist/excalidraw/actions/actionNavigate.d.ts +2 -2
  26. package/dist/excalidraw/actions/actionProperties.d.ts +13 -13
  27. package/dist/excalidraw/actions/actionSelectAll.d.ts +1 -1
  28. package/dist/excalidraw/actions/actionStyles.d.ts +1 -1
  29. package/dist/excalidraw/actions/actionToggleGridMode.d.ts +1 -1
  30. package/dist/excalidraw/actions/actionToggleObjectsSnapMode.d.ts +1 -1
  31. package/dist/excalidraw/actions/actionToggleStats.d.ts +1 -1
  32. package/dist/excalidraw/actions/actionToggleViewMode.d.ts +1 -1
  33. package/dist/excalidraw/actions/actionToggleZenMode.d.ts +1 -1
  34. package/dist/excalidraw/components/App.d.ts +9 -1
  35. package/dist/excalidraw/components/App.js +91 -71
  36. package/dist/excalidraw/components/ImageExportDialog.js +1 -1
  37. package/dist/excalidraw/components/PublishLibrary.js +1 -1
  38. package/dist/excalidraw/components/Sidebar/Sidebar.d.ts +1 -1
  39. package/dist/excalidraw/components/dropdownMenu/common.d.ts +1 -1
  40. package/dist/excalidraw/constants.d.ts +2 -0
  41. package/dist/excalidraw/constants.js +4 -0
  42. package/dist/excalidraw/data/index.js +1 -1
  43. package/dist/excalidraw/element/Hyperlink.d.ts +1 -1
  44. package/dist/excalidraw/element/embeddable.d.ts +1 -1
  45. package/dist/excalidraw/element/linearElementEditor.d.ts +1 -1
  46. package/dist/excalidraw/emitter.d.ts +5 -9
  47. package/dist/excalidraw/emitter.js +12 -12
  48. package/dist/excalidraw/frame.js +1 -1
  49. package/dist/excalidraw/hooks/useLibraryItemSvg.js +1 -1
  50. package/dist/excalidraw/index.d.ts +9 -4
  51. package/dist/excalidraw/index.js +9 -4
  52. package/dist/excalidraw/scene/export.js +1 -1
  53. package/dist/excalidraw/types.d.ts +1 -1
  54. package/dist/excalidraw/utils.d.ts +7 -1
  55. package/dist/excalidraw/utils.js +15 -0
  56. package/dist/prod/index.js +30 -30
  57. package/dist/utils/bbox.d.ts +2 -2
  58. package/dist/utils/export.d.ts +3 -9
  59. package/dist/utils/export.js +13 -9
  60. package/dist/utils/index.d.ts +3 -0
  61. package/dist/utils/index.js +3 -0
  62. package/dist/utils/withinBounds.d.ts +1 -1
  63. package/dist/utils/withinBounds.js +1 -3
  64. package/package.json +4 -4
  65. package/dist/browser/dev/excalidraw-assets-dev/chunk-SUHLFFEF.js.map +0 -7
  66. package/dist/browser/dev/excalidraw-assets-dev/image-NOPDRTTM.css.map +0 -7
  67. package/dist/browser/prod/excalidraw-assets/chunk-HE2P7BQ6.js +0 -257
  68. package/dist/browser/prod/excalidraw-assets/image-J2QCCYAR.css +0 -1
  69. /package/dist/browser/dev/excalidraw-assets-dev/{image-HYNUJ3XL.js.map → image-5TVMINCA.js.map} +0 -0
package/dist/dev/index.js CHANGED
@@ -411,7 +411,7 @@ var init_colors = __esm({
411
411
  });
412
412
 
413
413
  // constants.ts
414
- var isDarwin, isWindows, isAndroid, isFirefox, isChrome, isSafari, isBrave, APP_NAME, DRAGGING_THRESHOLD, LINE_CONFIRM_THRESHOLD, ELEMENT_SHIFT_TRANSLATE_AMOUNT, ELEMENT_TRANSLATE_AMOUNT, TEXT_TO_CENTER_SNAP_THRESHOLD, SHIFT_LOCKING_ANGLE, CURSOR_TYPE, POINTER_BUTTON, POINTER_EVENTS, YOUTUBE_STATES, ENV, CLASSES, FONT_FAMILY, THEME, FRAME_STYLE, WINDOWS_EMOJI_FALLBACK_FONT, MIN_FONT_SIZE, DEFAULT_FONT_SIZE, DEFAULT_FONT_FAMILY, DEFAULT_TEXT_ALIGN, DEFAULT_VERTICAL_ALIGN, GRID_SIZE, IMAGE_MIME_TYPES, ALLOWED_PASTE_MIME_TYPES, MIME_TYPES, EXPORT_IMAGE_TYPES, EXPORT_DATA_TYPES, EXPORT_SOURCE, IMAGE_RENDER_TIMEOUT, TAP_TWICE_TIMEOUT, TOUCH_CTX_MENU_TIMEOUT, SCROLL_TIMEOUT, ZOOM_STEP, MIN_ZOOM, HYPERLINK_TOOLTIP_DELAY, THEME_FILTER, URL_QUERY_KEYS, URL_HASH_KEYS, DEFAULT_UI_OPTIONS, MQ_MAX_WIDTH_PORTRAIT, MQ_MAX_WIDTH_LANDSCAPE, MQ_MAX_HEIGHT_LANDSCAPE, MQ_RIGHT_SIDEBAR_MIN_WIDTH, LIBRARY_SIDEBAR_WIDTH, MAX_DECIMALS_FOR_SVG_EXPORT, EXPORT_SCALES, DEFAULT_EXPORT_PADDING, DEFAULT_MAX_IMAGE_WIDTH_OR_HEIGHT, MAX_ALLOWED_FILE_BYTES, SVG_NS, VERSIONS, BOUND_TEXT_PADDING, ARROW_LABEL_WIDTH_FRACTION, ARROW_LABEL_FONT_SIZE_TO_MIN_WIDTH_RATIO, VERTICAL_ALIGN, TEXT_ALIGN, ELEMENT_READY_TO_ERASE_OPACITY, DEFAULT_PROPORTIONAL_RADIUS, DEFAULT_ADAPTIVE_RADIUS, ROUNDNESS, PRECEDING_ELEMENT_KEY, ROUGHNESS, STROKE_WIDTH, DEFAULT_ELEMENT_PROPS, LIBRARY_SIDEBAR_TAB, DEFAULT_SIDEBAR, LIBRARY_DISABLED_TYPES, TOOL_TYPE, EDITOR_LS_KEYS;
414
+ var isDarwin, isWindows, isAndroid, isFirefox, isChrome, isSafari, isIOS, isBrave, APP_NAME, DRAGGING_THRESHOLD, LINE_CONFIRM_THRESHOLD, ELEMENT_SHIFT_TRANSLATE_AMOUNT, ELEMENT_TRANSLATE_AMOUNT, TEXT_TO_CENTER_SNAP_THRESHOLD, SHIFT_LOCKING_ANGLE, CURSOR_TYPE, POINTER_BUTTON, POINTER_EVENTS, YOUTUBE_STATES, ENV, CLASSES, FONT_FAMILY, THEME, FRAME_STYLE, WINDOWS_EMOJI_FALLBACK_FONT, MIN_FONT_SIZE, DEFAULT_FONT_SIZE, DEFAULT_FONT_FAMILY, DEFAULT_TEXT_ALIGN, DEFAULT_VERTICAL_ALIGN, GRID_SIZE, IMAGE_MIME_TYPES, ALLOWED_PASTE_MIME_TYPES, MIME_TYPES, EXPORT_IMAGE_TYPES, EXPORT_DATA_TYPES, EXPORT_SOURCE, IMAGE_RENDER_TIMEOUT, TAP_TWICE_TIMEOUT, TOUCH_CTX_MENU_TIMEOUT, SCROLL_TIMEOUT, ZOOM_STEP, MIN_ZOOM, HYPERLINK_TOOLTIP_DELAY, THEME_FILTER, URL_QUERY_KEYS, URL_HASH_KEYS, DEFAULT_UI_OPTIONS, MQ_MAX_WIDTH_PORTRAIT, MQ_MAX_WIDTH_LANDSCAPE, MQ_MAX_HEIGHT_LANDSCAPE, MQ_RIGHT_SIDEBAR_MIN_WIDTH, LIBRARY_SIDEBAR_WIDTH, MAX_DECIMALS_FOR_SVG_EXPORT, EXPORT_SCALES, DEFAULT_EXPORT_PADDING, DEFAULT_MAX_IMAGE_WIDTH_OR_HEIGHT, MAX_ALLOWED_FILE_BYTES, SVG_NS, VERSIONS, BOUND_TEXT_PADDING, ARROW_LABEL_WIDTH_FRACTION, ARROW_LABEL_FONT_SIZE_TO_MIN_WIDTH_RATIO, VERTICAL_ALIGN, TEXT_ALIGN, ELEMENT_READY_TO_ERASE_OPACITY, DEFAULT_PROPORTIONAL_RADIUS, DEFAULT_ADAPTIVE_RADIUS, ROUNDNESS, PRECEDING_ELEMENT_KEY, ROUGHNESS, STROKE_WIDTH, DEFAULT_ELEMENT_PROPS, LIBRARY_SIDEBAR_TAB, DEFAULT_SIDEBAR, LIBRARY_DISABLED_TYPES, TOOL_TYPE, EDITOR_LS_KEYS;
415
415
  var init_constants = __esm({
416
416
  "constants.ts"() {
417
417
  "use strict";
@@ -424,6 +424,8 @@ var init_constants = __esm({
424
424
  isFirefox = "netscape" in window && navigator.userAgent.indexOf("rv:") > 1 && navigator.userAgent.indexOf("Gecko") > 1;
425
425
  isChrome = navigator.userAgent.indexOf("Chrome") !== -1;
426
426
  isSafari = !isChrome && navigator.userAgent.indexOf("Safari") !== -1;
427
+ isIOS = /iPad|iPhone/.test(navigator.platform) || // iPadOS 13+
428
+ navigator.userAgent.includes("Mac") && "ontouchend" in document;
427
429
  isBrave = () => navigator.brave?.isBrave?.name === "isBrave";
428
430
  APP_NAME = "Excalidraw";
429
431
  DRAGGING_THRESHOLD = 10;
@@ -445,7 +447,8 @@ var init_constants = __esm({
445
447
  MAIN: 0,
446
448
  WHEEL: 1,
447
449
  SECONDARY: 2,
448
- TOUCH: -1
450
+ TOUCH: -1,
451
+ ERASER: 5
449
452
  };
450
453
  POINTER_EVENTS = {
451
454
  enabled: "all",
@@ -743,6 +746,16 @@ var init_InitializeApp = __esm({
743
746
  // utils.ts
744
747
  import { unstable_batchedUpdates } from "react-dom";
745
748
  import React3 from "react";
749
+ function addEventListener(target, type, listener, options) {
750
+ if (!target) {
751
+ return () => {
752
+ };
753
+ }
754
+ target?.addEventListener?.(type, listener, options);
755
+ return () => {
756
+ target?.removeEventListener?.(type, listener, options);
757
+ };
758
+ }
746
759
  var mockDateTime, getDateTime, capitalizeString, isToolIcon, isInputLike, isInteractive, isWritableElement, getFontFamilyString, getFontString, debounce, throttleRAF, easeOut, easeOutInterpolate, easeToValuesRAF, chunk, distance, updateActiveTool, getShortcutKey, viewportCoordsToSceneCoords, sceneCoordsToViewportCoords, getGlobalCSSVariable, RS_LTR_CHARS, RS_RTL_CHARS, RE_RTL_CHECK, isRTL, tupleToCoors, muteFSAbortError, findIndex, findLastIndex, isTransparent, withBatchedUpdates, withBatchedUpdatesThrottled, getNearestScrollableContainer, focusNearestParent, bytesToHexString, getUpdatedTimestamp, arrayToMap, arrayToMapWithIndex, isTestEnv, wrapEvent, updateObject, getFrame, isPromiseLike, queryFocusableElements, _defaultIsShallowComparatorFallback, isShallowEqual, composeEventHandlers, assertNever, memoize, isRenderThrottlingEnabled, isMemberOf, cloneJSON, isFiniteNumber, updateStable;
747
760
  var init_utils = __esm({
748
761
  "utils.ts"() {
@@ -16575,6 +16588,177 @@ var init_math = __esm({
16575
16588
  }
16576
16589
  });
16577
16590
 
16591
+ // ../utils/bbox.ts
16592
+ function getBBox(line) {
16593
+ return [
16594
+ Math.min(line[0][0], line[1][0]),
16595
+ Math.min(line[0][1], line[1][1]),
16596
+ Math.max(line[0][0], line[1][0]),
16597
+ Math.max(line[0][1], line[1][1])
16598
+ ];
16599
+ }
16600
+ function crossProduct(a, b) {
16601
+ return a[0] * b[1] - b[0] * a[1];
16602
+ }
16603
+ function doBBoxesIntersect(a, b) {
16604
+ return a[0] <= b[2] && a[2] >= b[0] && a[1] <= b[3] && a[3] >= b[1];
16605
+ }
16606
+ function translate(a, b) {
16607
+ return [a[0] - b[0], a[1] - b[1]];
16608
+ }
16609
+ function isPointOnLine(l, p) {
16610
+ const p1 = translate(l[1], l[0]);
16611
+ const p2 = translate(p, l[0]);
16612
+ const r = crossProduct(p1, p2);
16613
+ return Math.abs(r) < EPSILON;
16614
+ }
16615
+ function isPointRightOfLine(l, p) {
16616
+ const p1 = translate(l[1], l[0]);
16617
+ const p2 = translate(p, l[0]);
16618
+ return crossProduct(p1, p2) < 0;
16619
+ }
16620
+ function isLineSegmentTouchingOrCrossingLine(a, b) {
16621
+ return isPointOnLine(a, b[0]) || isPointOnLine(a, b[1]) || (isPointRightOfLine(a, b[0]) ? !isPointRightOfLine(a, b[1]) : isPointRightOfLine(a, b[1]));
16622
+ }
16623
+ function doLineSegmentsIntersect(a, b) {
16624
+ return doBBoxesIntersect(getBBox(a), getBBox(b)) && isLineSegmentTouchingOrCrossingLine(a, b) && isLineSegmentTouchingOrCrossingLine(b, a);
16625
+ }
16626
+ var EPSILON;
16627
+ var init_bbox = __esm({
16628
+ "../utils/bbox.ts"() {
16629
+ "use strict";
16630
+ init_define_import_meta_env();
16631
+ EPSILON = 1e-6;
16632
+ }
16633
+ });
16634
+
16635
+ // ../utils/withinBounds.ts
16636
+ var getNonLinearElementRelativePoints, getElementRelativePoints, getMinMaxPoints, getRotatedBBox, isElementInsideBBox, elementPartiallyOverlapsWithOrContainsBBox, elementsOverlappingBBox;
16637
+ var init_withinBounds = __esm({
16638
+ "../utils/withinBounds.ts"() {
16639
+ "use strict";
16640
+ init_define_import_meta_env();
16641
+ init_excalidraw();
16642
+ getNonLinearElementRelativePoints = (element) => {
16643
+ if (element.type === "diamond") {
16644
+ return [
16645
+ [element.width / 2, 0],
16646
+ [element.width, element.height / 2],
16647
+ [element.width / 2, element.height],
16648
+ [0, element.height / 2]
16649
+ ];
16650
+ }
16651
+ return [
16652
+ [0, 0],
16653
+ [0 + element.width, 0],
16654
+ [0 + element.width, element.height],
16655
+ [0, element.height]
16656
+ ];
16657
+ };
16658
+ getElementRelativePoints = (element) => {
16659
+ if (isLinearElement(element) || isFreeDrawElement(element)) {
16660
+ return element.points;
16661
+ }
16662
+ return getNonLinearElementRelativePoints(element);
16663
+ };
16664
+ getMinMaxPoints = (points) => {
16665
+ const ret = points.reduce(
16666
+ (limits, [x, y]) => {
16667
+ limits.minY = Math.min(limits.minY, y);
16668
+ limits.minX = Math.min(limits.minX, x);
16669
+ limits.maxX = Math.max(limits.maxX, x);
16670
+ limits.maxY = Math.max(limits.maxY, y);
16671
+ return limits;
16672
+ },
16673
+ {
16674
+ minX: Infinity,
16675
+ minY: Infinity,
16676
+ maxX: -Infinity,
16677
+ maxY: -Infinity,
16678
+ cx: 0,
16679
+ cy: 0
16680
+ }
16681
+ );
16682
+ ret.cx = (ret.maxX + ret.minX) / 2;
16683
+ ret.cy = (ret.maxY + ret.minY) / 2;
16684
+ return ret;
16685
+ };
16686
+ getRotatedBBox = (element) => {
16687
+ const points = getElementRelativePoints(element);
16688
+ const { cx, cy } = getMinMaxPoints(points);
16689
+ const centerPoint2 = [cx, cy];
16690
+ const rotatedPoints = points.map(
16691
+ (point2) => rotatePoint([point2[0], point2[1]], centerPoint2, element.angle)
16692
+ );
16693
+ const { minX, minY, maxX, maxY } = getMinMaxPoints(rotatedPoints);
16694
+ return [
16695
+ minX + element.x,
16696
+ minY + element.y,
16697
+ maxX + element.x,
16698
+ maxY + element.y
16699
+ ];
16700
+ };
16701
+ isElementInsideBBox = (element, bbox, eitherDirection = false) => {
16702
+ const elementBBox = getRotatedBBox(element);
16703
+ const elementInsideBbox = bbox[0] <= elementBBox[0] && bbox[2] >= elementBBox[2] && bbox[1] <= elementBBox[1] && bbox[3] >= elementBBox[3];
16704
+ if (!eitherDirection) {
16705
+ return elementInsideBbox;
16706
+ }
16707
+ if (elementInsideBbox) {
16708
+ return true;
16709
+ }
16710
+ return elementBBox[0] <= bbox[0] && elementBBox[2] >= bbox[2] && elementBBox[1] <= bbox[1] && elementBBox[3] >= bbox[3];
16711
+ };
16712
+ elementPartiallyOverlapsWithOrContainsBBox = (element, bbox) => {
16713
+ const elementBBox = getRotatedBBox(element);
16714
+ return (isValueInRange(elementBBox[0], bbox[0], bbox[2]) || isValueInRange(bbox[0], elementBBox[0], elementBBox[2])) && (isValueInRange(elementBBox[1], bbox[1], bbox[3]) || isValueInRange(bbox[1], elementBBox[1], elementBBox[3]));
16715
+ };
16716
+ elementsOverlappingBBox = ({
16717
+ elements,
16718
+ bounds,
16719
+ type,
16720
+ errorMargin = 0
16721
+ }) => {
16722
+ if (isExcalidrawElement(bounds)) {
16723
+ bounds = getElementBounds(bounds);
16724
+ }
16725
+ const adjustedBBox = [
16726
+ bounds[0] - errorMargin,
16727
+ bounds[1] - errorMargin,
16728
+ bounds[2] + errorMargin,
16729
+ bounds[3] + errorMargin
16730
+ ];
16731
+ const includedElementSet = /* @__PURE__ */ new Set();
16732
+ for (const element of elements) {
16733
+ if (includedElementSet.has(element.id)) {
16734
+ continue;
16735
+ }
16736
+ const isOverlaping = type === "overlap" ? elementPartiallyOverlapsWithOrContainsBBox(element, adjustedBBox) : type === "inside" ? isElementInsideBBox(element, adjustedBBox) : isElementInsideBBox(element, adjustedBBox, true);
16737
+ if (isOverlaping) {
16738
+ includedElementSet.add(element.id);
16739
+ if (element.boundElements) {
16740
+ for (const boundElement of element.boundElements) {
16741
+ includedElementSet.add(boundElement.id);
16742
+ }
16743
+ }
16744
+ if (isTextElement(element) && element.containerId) {
16745
+ includedElementSet.add(element.containerId);
16746
+ }
16747
+ if (isArrowElement(element)) {
16748
+ if (element.startBinding) {
16749
+ includedElementSet.add(element.startBinding.elementId);
16750
+ }
16751
+ if (element.endBinding) {
16752
+ includedElementSet.add(element.endBinding?.elementId);
16753
+ }
16754
+ }
16755
+ }
16756
+ }
16757
+ return elements.filter((element) => includedElementSet.has(element.id));
16758
+ };
16759
+ }
16760
+ });
16761
+
16578
16762
  // data/filesystem.ts
16579
16763
  import {
16580
16764
  fileOpen as _fileOpen,
@@ -17798,7 +17982,7 @@ var init_export = __esm({
17798
17982
  init_appState();
17799
17983
  init_json();
17800
17984
  init_image2();
17801
- init_export2();
17985
+ init_utils2();
17802
17986
  init_frame();
17803
17987
  init_element();
17804
17988
  init_mutateElement();
@@ -18093,578 +18277,6 @@ var init_export = __esm({
18093
18277
  }
18094
18278
  });
18095
18279
 
18096
- // ../utils/bbox.ts
18097
- function getBBox(line) {
18098
- return [
18099
- Math.min(line[0][0], line[1][0]),
18100
- Math.min(line[0][1], line[1][1]),
18101
- Math.max(line[0][0], line[1][0]),
18102
- Math.max(line[0][1], line[1][1])
18103
- ];
18104
- }
18105
- function crossProduct(a, b) {
18106
- return a[0] * b[1] - b[0] * a[1];
18107
- }
18108
- function doBBoxesIntersect(a, b) {
18109
- return a[0] <= b[2] && a[2] >= b[0] && a[1] <= b[3] && a[3] >= b[1];
18110
- }
18111
- function translate(a, b) {
18112
- return [a[0] - b[0], a[1] - b[1]];
18113
- }
18114
- function isPointOnLine(l, p) {
18115
- const p1 = translate(l[1], l[0]);
18116
- const p2 = translate(p, l[0]);
18117
- const r = crossProduct(p1, p2);
18118
- return Math.abs(r) < EPSILON;
18119
- }
18120
- function isPointRightOfLine(l, p) {
18121
- const p1 = translate(l[1], l[0]);
18122
- const p2 = translate(p, l[0]);
18123
- return crossProduct(p1, p2) < 0;
18124
- }
18125
- function isLineSegmentTouchingOrCrossingLine(a, b) {
18126
- return isPointOnLine(a, b[0]) || isPointOnLine(a, b[1]) || (isPointRightOfLine(a, b[0]) ? !isPointRightOfLine(a, b[1]) : isPointRightOfLine(a, b[1]));
18127
- }
18128
- function doLineSegmentsIntersect(a, b) {
18129
- return doBBoxesIntersect(getBBox(a), getBBox(b)) && isLineSegmentTouchingOrCrossingLine(a, b) && isLineSegmentTouchingOrCrossingLine(b, a);
18130
- }
18131
- var EPSILON;
18132
- var init_bbox = __esm({
18133
- "../utils/bbox.ts"() {
18134
- "use strict";
18135
- init_define_import_meta_env();
18136
- EPSILON = 1e-6;
18137
- }
18138
- });
18139
-
18140
- // ../utils/withinBounds.ts
18141
- var getNonLinearElementRelativePoints, getElementRelativePoints, getMinMaxPoints, getRotatedBBox, isElementInsideBBox, elementPartiallyOverlapsWithOrContainsBBox, elementsOverlappingBBox;
18142
- var init_withinBounds = __esm({
18143
- "../utils/withinBounds.ts"() {
18144
- "use strict";
18145
- init_define_import_meta_env();
18146
- init_typeChecks();
18147
- init_math();
18148
- init_bounds();
18149
- getNonLinearElementRelativePoints = (element) => {
18150
- if (element.type === "diamond") {
18151
- return [
18152
- [element.width / 2, 0],
18153
- [element.width, element.height / 2],
18154
- [element.width / 2, element.height],
18155
- [0, element.height / 2]
18156
- ];
18157
- }
18158
- return [
18159
- [0, 0],
18160
- [0 + element.width, 0],
18161
- [0 + element.width, element.height],
18162
- [0, element.height]
18163
- ];
18164
- };
18165
- getElementRelativePoints = (element) => {
18166
- if (isLinearElement(element) || isFreeDrawElement(element)) {
18167
- return element.points;
18168
- }
18169
- return getNonLinearElementRelativePoints(element);
18170
- };
18171
- getMinMaxPoints = (points) => {
18172
- const ret = points.reduce(
18173
- (limits, [x, y]) => {
18174
- limits.minY = Math.min(limits.minY, y);
18175
- limits.minX = Math.min(limits.minX, x);
18176
- limits.maxX = Math.max(limits.maxX, x);
18177
- limits.maxY = Math.max(limits.maxY, y);
18178
- return limits;
18179
- },
18180
- {
18181
- minX: Infinity,
18182
- minY: Infinity,
18183
- maxX: -Infinity,
18184
- maxY: -Infinity,
18185
- cx: 0,
18186
- cy: 0
18187
- }
18188
- );
18189
- ret.cx = (ret.maxX + ret.minX) / 2;
18190
- ret.cy = (ret.maxY + ret.minY) / 2;
18191
- return ret;
18192
- };
18193
- getRotatedBBox = (element) => {
18194
- const points = getElementRelativePoints(element);
18195
- const { cx, cy } = getMinMaxPoints(points);
18196
- const centerPoint2 = [cx, cy];
18197
- const rotatedPoints = points.map(
18198
- (point2) => rotatePoint([point2[0], point2[1]], centerPoint2, element.angle)
18199
- );
18200
- const { minX, minY, maxX, maxY } = getMinMaxPoints(rotatedPoints);
18201
- return [
18202
- minX + element.x,
18203
- minY + element.y,
18204
- maxX + element.x,
18205
- maxY + element.y
18206
- ];
18207
- };
18208
- isElementInsideBBox = (element, bbox, eitherDirection = false) => {
18209
- const elementBBox = getRotatedBBox(element);
18210
- const elementInsideBbox = bbox[0] <= elementBBox[0] && bbox[2] >= elementBBox[2] && bbox[1] <= elementBBox[1] && bbox[3] >= elementBBox[3];
18211
- if (!eitherDirection) {
18212
- return elementInsideBbox;
18213
- }
18214
- if (elementInsideBbox) {
18215
- return true;
18216
- }
18217
- return elementBBox[0] <= bbox[0] && elementBBox[2] >= bbox[2] && elementBBox[1] <= bbox[1] && elementBBox[3] >= bbox[3];
18218
- };
18219
- elementPartiallyOverlapsWithOrContainsBBox = (element, bbox) => {
18220
- const elementBBox = getRotatedBBox(element);
18221
- return (isValueInRange(elementBBox[0], bbox[0], bbox[2]) || isValueInRange(bbox[0], elementBBox[0], elementBBox[2])) && (isValueInRange(elementBBox[1], bbox[1], bbox[3]) || isValueInRange(bbox[1], elementBBox[1], elementBBox[3]));
18222
- };
18223
- elementsOverlappingBBox = ({
18224
- elements,
18225
- bounds,
18226
- type,
18227
- errorMargin = 0
18228
- }) => {
18229
- if (isExcalidrawElement(bounds)) {
18230
- bounds = getElementBounds(bounds);
18231
- }
18232
- const adjustedBBox = [
18233
- bounds[0] - errorMargin,
18234
- bounds[1] - errorMargin,
18235
- bounds[2] + errorMargin,
18236
- bounds[3] + errorMargin
18237
- ];
18238
- const includedElementSet = /* @__PURE__ */ new Set();
18239
- for (const element of elements) {
18240
- if (includedElementSet.has(element.id)) {
18241
- continue;
18242
- }
18243
- const isOverlaping = type === "overlap" ? elementPartiallyOverlapsWithOrContainsBBox(element, adjustedBBox) : type === "inside" ? isElementInsideBBox(element, adjustedBBox) : isElementInsideBBox(element, adjustedBBox, true);
18244
- if (isOverlaping) {
18245
- includedElementSet.add(element.id);
18246
- if (element.boundElements) {
18247
- for (const boundElement of element.boundElements) {
18248
- includedElementSet.add(boundElement.id);
18249
- }
18250
- }
18251
- if (isTextElement(element) && element.containerId) {
18252
- includedElementSet.add(element.containerId);
18253
- }
18254
- if (isArrowElement(element)) {
18255
- if (element.startBinding) {
18256
- includedElementSet.add(element.startBinding.elementId);
18257
- }
18258
- if (element.endBinding) {
18259
- includedElementSet.add(element.endBinding?.elementId);
18260
- }
18261
- }
18262
- }
18263
- }
18264
- return elements.filter((element) => includedElementSet.has(element.id));
18265
- };
18266
- }
18267
- });
18268
-
18269
- // hooks/useLibraryItemSvg.ts
18270
- import { atom as atom4, useAtom as useAtom8 } from "jotai";
18271
- import { useEffect as useEffect14, useState as useState8 } from "react";
18272
- var libraryItemSvgsCache, exportLibraryItemToSvg, useLibraryItemSvg, useLibraryCache;
18273
- var init_useLibraryItemSvg = __esm({
18274
- "hooks/useLibraryItemSvg.ts"() {
18275
- "use strict";
18276
- init_define_import_meta_env();
18277
- init_colors();
18278
- init_jotai();
18279
- init_export2();
18280
- libraryItemSvgsCache = atom4(/* @__PURE__ */ new Map());
18281
- exportLibraryItemToSvg = async (elements) => {
18282
- return await exportToSvg2({
18283
- elements,
18284
- appState: {
18285
- exportBackground: false,
18286
- viewBackgroundColor: COLOR_PALETTE.white
18287
- },
18288
- files: null,
18289
- renderEmbeddables: false
18290
- });
18291
- };
18292
- useLibraryItemSvg = (id, elements, svgCache) => {
18293
- const [svg, setSvg] = useState8();
18294
- useEffect14(() => {
18295
- if (elements) {
18296
- if (id) {
18297
- const cachedSvg = svgCache.get(id);
18298
- if (cachedSvg) {
18299
- setSvg(cachedSvg);
18300
- } else {
18301
- (async () => {
18302
- const exportedSvg = await exportLibraryItemToSvg(elements);
18303
- exportedSvg.querySelector(".style-fonts")?.remove();
18304
- if (exportedSvg) {
18305
- svgCache.set(id, exportedSvg);
18306
- setSvg(exportedSvg);
18307
- }
18308
- })();
18309
- }
18310
- } else {
18311
- (async () => {
18312
- const exportedSvg = await exportLibraryItemToSvg(elements);
18313
- setSvg(exportedSvg);
18314
- })();
18315
- }
18316
- }
18317
- }, [id, elements, svgCache, setSvg]);
18318
- return svg;
18319
- };
18320
- useLibraryCache = () => {
18321
- const [svgCache] = useAtom8(libraryItemSvgsCache, jotaiScope);
18322
- const clearLibraryCache = () => svgCache.clear();
18323
- const deleteItemsFromLibraryCache = (items) => {
18324
- items.forEach((item) => svgCache.delete(item));
18325
- };
18326
- return {
18327
- clearLibraryCache,
18328
- deleteItemsFromLibraryCache,
18329
- svgCache
18330
- };
18331
- };
18332
- }
18333
- });
18334
-
18335
- // data/library.ts
18336
- import { atom as atom5 } from "jotai";
18337
- import { useEffect as useEffect15, useRef as useRef11 } from "react";
18338
- var libraryItemsAtom, cloneLibraryItems, isUniqueItem, mergeLibraryItems, Library, library_default, distributeLibraryItemsOnSquareGrid, parseLibraryTokensFromUrl, useHandleLibrary;
18339
- var init_library = __esm({
18340
- "data/library.ts"() {
18341
- "use strict";
18342
- init_define_import_meta_env();
18343
- init_blob();
18344
- init_restore();
18345
- init_jotai();
18346
- init_bounds();
18347
- init_errors();
18348
- init_i18n();
18349
- init_constants();
18350
- init_useLibraryItemSvg();
18351
- init_utils();
18352
- libraryItemsAtom = atom5({ status: "loaded", isInitialized: true, libraryItems: [] });
18353
- cloneLibraryItems = (libraryItems) => cloneJSON(libraryItems);
18354
- isUniqueItem = (existingLibraryItems, targetLibraryItem) => {
18355
- return !existingLibraryItems.find((libraryItem) => {
18356
- if (libraryItem.elements.length !== targetLibraryItem.elements.length) {
18357
- return false;
18358
- }
18359
- return libraryItem.elements.every((libItemExcalidrawItem, idx) => {
18360
- return libItemExcalidrawItem.id === targetLibraryItem.elements[idx].id && libItemExcalidrawItem.versionNonce === targetLibraryItem.elements[idx].versionNonce;
18361
- });
18362
- });
18363
- };
18364
- mergeLibraryItems = (localItems, otherItems) => {
18365
- const newItems = [];
18366
- for (const item of otherItems) {
18367
- if (isUniqueItem(localItems, item)) {
18368
- newItems.push(item);
18369
- }
18370
- }
18371
- return [...newItems, ...localItems];
18372
- };
18373
- Library = class {
18374
- /** latest libraryItems */
18375
- lastLibraryItems = [];
18376
- /** indicates whether library is initialized with library items (has gone
18377
- * though at least one update) */
18378
- isInitialized = false;
18379
- app;
18380
- constructor(app) {
18381
- this.app = app;
18382
- }
18383
- updateQueue = [];
18384
- getLastUpdateTask = () => {
18385
- return this.updateQueue[this.updateQueue.length - 1];
18386
- };
18387
- notifyListeners = () => {
18388
- if (this.updateQueue.length > 0) {
18389
- jotaiStore.set(libraryItemsAtom, {
18390
- status: "loading",
18391
- libraryItems: this.lastLibraryItems,
18392
- isInitialized: this.isInitialized
18393
- });
18394
- } else {
18395
- this.isInitialized = true;
18396
- jotaiStore.set(libraryItemsAtom, {
18397
- status: "loaded",
18398
- libraryItems: this.lastLibraryItems,
18399
- isInitialized: this.isInitialized
18400
- });
18401
- try {
18402
- this.app.props.onLibraryChange?.(
18403
- cloneLibraryItems(this.lastLibraryItems)
18404
- );
18405
- } catch (error) {
18406
- console.error(error);
18407
- }
18408
- }
18409
- };
18410
- /** call on excalidraw instance unmount */
18411
- destroy = () => {
18412
- this.isInitialized = false;
18413
- this.updateQueue = [];
18414
- this.lastLibraryItems = [];
18415
- jotaiStore.set(libraryItemSvgsCache, /* @__PURE__ */ new Map());
18416
- };
18417
- resetLibrary = () => {
18418
- return this.setLibrary([]);
18419
- };
18420
- /**
18421
- * @returns latest cloned libraryItems. Awaits all in-progress updates first.
18422
- */
18423
- getLatestLibrary = () => {
18424
- return new Promise(async (resolve) => {
18425
- try {
18426
- const libraryItems = await (this.getLastUpdateTask() || this.lastLibraryItems);
18427
- if (this.updateQueue.length > 0) {
18428
- resolve(this.getLatestLibrary());
18429
- } else {
18430
- resolve(cloneLibraryItems(libraryItems));
18431
- }
18432
- } catch (error) {
18433
- return resolve(this.lastLibraryItems);
18434
- }
18435
- });
18436
- };
18437
- // NOTE this is a high-level public API (exposed on ExcalidrawAPI) with
18438
- // a slight overhead (always restoring library items). For internal use
18439
- // where merging isn't needed, use `library.setLibrary()` directly.
18440
- updateLibrary = async ({
18441
- libraryItems,
18442
- prompt = false,
18443
- merge = false,
18444
- openLibraryMenu = false,
18445
- defaultStatus = "unpublished"
18446
- }) => {
18447
- if (openLibraryMenu) {
18448
- this.app.setState({
18449
- openSidebar: { name: DEFAULT_SIDEBAR.name, tab: LIBRARY_SIDEBAR_TAB }
18450
- });
18451
- }
18452
- return this.setLibrary(() => {
18453
- return new Promise(async (resolve, reject) => {
18454
- try {
18455
- const source = await (typeof libraryItems === "function" && !(libraryItems instanceof Blob) ? libraryItems(this.lastLibraryItems) : libraryItems);
18456
- let nextItems;
18457
- if (source instanceof Blob) {
18458
- nextItems = await loadLibraryFromBlob(source, defaultStatus);
18459
- } else {
18460
- nextItems = restoreLibraryItems(source, defaultStatus);
18461
- }
18462
- if (!prompt || window.confirm(
18463
- t("alerts.confirmAddLibrary", {
18464
- numShapes: nextItems.length
18465
- })
18466
- )) {
18467
- if (prompt) {
18468
- this.app.focusContainer();
18469
- }
18470
- if (merge) {
18471
- resolve(mergeLibraryItems(this.lastLibraryItems, nextItems));
18472
- } else {
18473
- resolve(nextItems);
18474
- }
18475
- } else {
18476
- reject(new AbortError());
18477
- }
18478
- } catch (error) {
18479
- reject(error);
18480
- }
18481
- });
18482
- });
18483
- };
18484
- setLibrary = (libraryItems) => {
18485
- const task = new Promise(async (resolve, reject) => {
18486
- try {
18487
- await this.getLastUpdateTask();
18488
- if (typeof libraryItems === "function") {
18489
- libraryItems = libraryItems(this.lastLibraryItems);
18490
- }
18491
- this.lastLibraryItems = cloneLibraryItems(await libraryItems);
18492
- resolve(this.lastLibraryItems);
18493
- } catch (error) {
18494
- reject(error);
18495
- }
18496
- }).catch((error) => {
18497
- if (error.name === "AbortError") {
18498
- console.warn("Library update aborted by user");
18499
- return this.lastLibraryItems;
18500
- }
18501
- throw error;
18502
- }).finally(() => {
18503
- this.updateQueue = this.updateQueue.filter((_task) => _task !== task);
18504
- this.notifyListeners();
18505
- });
18506
- this.updateQueue.push(task);
18507
- this.notifyListeners();
18508
- return task;
18509
- };
18510
- };
18511
- library_default = Library;
18512
- distributeLibraryItemsOnSquareGrid = (libraryItems) => {
18513
- const PADDING = 50;
18514
- const ITEMS_PER_ROW = Math.ceil(Math.sqrt(libraryItems.length));
18515
- const resElements = [];
18516
- const getMaxHeightPerRow = (row2) => {
18517
- const maxHeight = libraryItems.slice(row2 * ITEMS_PER_ROW, row2 * ITEMS_PER_ROW + ITEMS_PER_ROW).reduce((acc, item) => {
18518
- const { height } = getCommonBoundingBox(item.elements);
18519
- return Math.max(acc, height);
18520
- }, 0);
18521
- return maxHeight;
18522
- };
18523
- const getMaxWidthPerCol = (targetCol) => {
18524
- let index2 = 0;
18525
- let currCol = 0;
18526
- let maxWidth = 0;
18527
- for (const item of libraryItems) {
18528
- if (index2 % ITEMS_PER_ROW === 0) {
18529
- currCol = 0;
18530
- }
18531
- if (currCol === targetCol) {
18532
- const { width } = getCommonBoundingBox(item.elements);
18533
- maxWidth = Math.max(maxWidth, width);
18534
- }
18535
- index2++;
18536
- currCol++;
18537
- }
18538
- return maxWidth;
18539
- };
18540
- let colOffsetX = 0;
18541
- let rowOffsetY = 0;
18542
- let maxHeightCurrRow = 0;
18543
- let maxWidthCurrCol = 0;
18544
- let index = 0;
18545
- let col = 0;
18546
- let row = 0;
18547
- for (const item of libraryItems) {
18548
- if (index && index % ITEMS_PER_ROW === 0) {
18549
- rowOffsetY += maxHeightCurrRow + PADDING;
18550
- colOffsetX = 0;
18551
- col = 0;
18552
- row++;
18553
- }
18554
- if (col === 0) {
18555
- maxHeightCurrRow = getMaxHeightPerRow(row);
18556
- }
18557
- maxWidthCurrCol = getMaxWidthPerCol(col);
18558
- const { minX, minY, width, height } = getCommonBoundingBox(item.elements);
18559
- const offsetCenterX = (maxWidthCurrCol - width) / 2;
18560
- const offsetCenterY = (maxHeightCurrRow - height) / 2;
18561
- resElements.push(
18562
- ...item.elements.map((element) => ({
18563
- ...element,
18564
- x: element.x + // offset for column
18565
- colOffsetX + // offset to center in given square grid
18566
- offsetCenterX - // subtract minX so that given item starts at 0 coord
18567
- minX,
18568
- y: element.y + // offset for row
18569
- rowOffsetY + // offset to center in given square grid
18570
- offsetCenterY - // subtract minY so that given item starts at 0 coord
18571
- minY
18572
- }))
18573
- );
18574
- colOffsetX += maxWidthCurrCol + PADDING;
18575
- index++;
18576
- col++;
18577
- }
18578
- return resElements;
18579
- };
18580
- parseLibraryTokensFromUrl = () => {
18581
- const libraryUrl = (
18582
- // current
18583
- new URLSearchParams(window.location.hash.slice(1)).get(
18584
- URL_HASH_KEYS.addLibrary
18585
- ) || // legacy, kept for compat reasons
18586
- new URLSearchParams(window.location.search).get(URL_QUERY_KEYS.addLibrary)
18587
- );
18588
- const idToken = libraryUrl ? new URLSearchParams(window.location.hash.slice(1)).get("token") : null;
18589
- return libraryUrl ? { libraryUrl, idToken } : null;
18590
- };
18591
- useHandleLibrary = ({
18592
- excalidrawAPI,
18593
- getInitialLibraryItems
18594
- }) => {
18595
- const getInitialLibraryRef = useRef11(getInitialLibraryItems);
18596
- useEffect15(() => {
18597
- if (!excalidrawAPI) {
18598
- return;
18599
- }
18600
- const importLibraryFromURL = async ({
18601
- libraryUrl,
18602
- idToken
18603
- }) => {
18604
- const libraryPromise = new Promise(async (resolve, reject) => {
18605
- try {
18606
- const request = await fetch(decodeURIComponent(libraryUrl));
18607
- const blob = await request.blob();
18608
- resolve(blob);
18609
- } catch (error) {
18610
- reject(error);
18611
- }
18612
- });
18613
- const shouldPrompt = idToken !== excalidrawAPI.id;
18614
- await (shouldPrompt && document.hidden ? new Promise((resolve) => {
18615
- window.addEventListener("focus", () => resolve(), {
18616
- once: true
18617
- });
18618
- }) : null);
18619
- try {
18620
- await excalidrawAPI.updateLibrary({
18621
- libraryItems: libraryPromise,
18622
- prompt: shouldPrompt,
18623
- merge: true,
18624
- defaultStatus: "published",
18625
- openLibraryMenu: true
18626
- });
18627
- } catch (error) {
18628
- throw error;
18629
- } finally {
18630
- if (window.location.hash.includes(URL_HASH_KEYS.addLibrary)) {
18631
- const hash = new URLSearchParams(window.location.hash.slice(1));
18632
- hash.delete(URL_HASH_KEYS.addLibrary);
18633
- window.history.replaceState({}, APP_NAME, `#${hash.toString()}`);
18634
- } else if (window.location.search.includes(URL_QUERY_KEYS.addLibrary)) {
18635
- const query = new URLSearchParams(window.location.search);
18636
- query.delete(URL_QUERY_KEYS.addLibrary);
18637
- window.history.replaceState({}, APP_NAME, `?${query.toString()}`);
18638
- }
18639
- }
18640
- };
18641
- const onHashChange = (event) => {
18642
- event.preventDefault();
18643
- const libraryUrlTokens2 = parseLibraryTokensFromUrl();
18644
- if (libraryUrlTokens2) {
18645
- event.stopImmediatePropagation();
18646
- window.history.replaceState({}, "", event.oldURL);
18647
- importLibraryFromURL(libraryUrlTokens2);
18648
- }
18649
- };
18650
- if (getInitialLibraryRef.current) {
18651
- excalidrawAPI.updateLibrary({
18652
- libraryItems: getInitialLibraryRef.current()
18653
- });
18654
- }
18655
- const libraryUrlTokens = parseLibraryTokensFromUrl();
18656
- if (libraryUrlTokens) {
18657
- importLibraryFromURL(libraryUrlTokens);
18658
- }
18659
- window.addEventListener("hashchange" /* HASHCHANGE */, onHashChange);
18660
- return () => {
18661
- window.removeEventListener("hashchange" /* HASHCHANGE */, onHashChange);
18662
- };
18663
- }, [excalidrawAPI]);
18664
- };
18665
- }
18666
- });
18667
-
18668
18280
  // ../utils/export.ts
18669
18281
  var exportToCanvas2, exportToBlob, exportToSvg2, exportToClipboard;
18670
18282
  var init_export2 = __esm({
@@ -18672,18 +18284,10 @@ var init_export2 = __esm({
18672
18284
  "use strict";
18673
18285
  init_define_import_meta_env();
18674
18286
  init_export();
18675
- init_appState();
18676
- init_restore();
18677
- init_constants();
18287
+ init_excalidraw();
18678
18288
  init_image();
18679
18289
  init_json();
18680
18290
  init_clipboard();
18681
- init_bbox();
18682
- init_withinBounds();
18683
- init_json();
18684
- init_blob();
18685
- init_renderElement();
18686
- init_library();
18687
18291
  exportToCanvas2 = ({
18688
18292
  elements,
18689
18293
  appState,
@@ -18814,6 +18418,17 @@ var init_export2 = __esm({
18814
18418
  }
18815
18419
  });
18816
18420
 
18421
+ // ../utils/index.ts
18422
+ var init_utils2 = __esm({
18423
+ "../utils/index.ts"() {
18424
+ "use strict";
18425
+ init_define_import_meta_env();
18426
+ init_bbox();
18427
+ init_withinBounds();
18428
+ init_export2();
18429
+ }
18430
+ });
18431
+
18817
18432
  // frame.ts
18818
18433
  function isElementIntersectingFrame(element, frame) {
18819
18434
  const frameLineSegments = getElementLineSegments(frame);
@@ -18839,7 +18454,7 @@ var init_frame = __esm({
18839
18454
  init_groups();
18840
18455
  init_Scene();
18841
18456
  init_bounds();
18842
- init_export2();
18457
+ init_utils2();
18843
18458
  init_typeChecks();
18844
18459
  bindElementsToFramesAfterDuplication = (nextElements, oldElements, oldIdToDuplicatedId) => {
18845
18460
  const nextElementMap = arrayToMap(nextElements);
@@ -21914,7 +21529,7 @@ var init_ProjectName = __esm({
21914
21529
  });
21915
21530
 
21916
21531
  // components/ProjectName.tsx
21917
- import { useState as useState9 } from "react";
21532
+ import { useState as useState8 } from "react";
21918
21533
  import { jsx as jsx26, jsxs as jsxs16 } from "react/jsx-runtime";
21919
21534
  var ProjectName;
21920
21535
  var init_ProjectName2 = __esm({
@@ -21928,7 +21543,7 @@ var init_ProjectName2 = __esm({
21928
21543
  init_keys();
21929
21544
  ProjectName = (props) => {
21930
21545
  const { id } = useExcalidrawContainer();
21931
- const [fileName, setFileName] = useState9(props.value);
21546
+ const [fileName, setFileName] = useState8(props.value);
21932
21547
  const handleBlur = (event) => {
21933
21548
  if (!props.ignoreFocus) {
21934
21549
  focusNearestParent(event.target);
@@ -22021,7 +21636,7 @@ var init_data = __esm({
22021
21636
  init_element();
22022
21637
  init_typeChecks();
22023
21638
  init_i18n();
22024
- init_export2();
21639
+ init_utils2();
22025
21640
  init_scene();
22026
21641
  init_export();
22027
21642
  init_utils();
@@ -22934,7 +22549,7 @@ var init_Avatar = __esm({
22934
22549
  });
22935
22550
 
22936
22551
  // components/Avatar.tsx
22937
- import { useState as useState10 } from "react";
22552
+ import { useState as useState9 } from "react";
22938
22553
  import clsx14 from "clsx";
22939
22554
  import { jsx as jsx32 } from "react/jsx-runtime";
22940
22555
  var Avatar;
@@ -22953,7 +22568,7 @@ var init_Avatar2 = __esm({
22953
22568
  isCurrentUser
22954
22569
  }) => {
22955
22570
  const shortName = getNameInitial(name);
22956
- const [error, setError] = useState10(false);
22571
+ const [error, setError] = useState9(false);
22957
22572
  const loadImg = !error && src;
22958
22573
  const style = loadImg ? void 0 : { background: color };
22959
22574
  return /* @__PURE__ */ jsx32(
@@ -24618,6 +24233,405 @@ var init_manager = __esm({
24618
24233
  }
24619
24234
  });
24620
24235
 
24236
+ // hooks/useLibraryItemSvg.ts
24237
+ import { atom as atom4, useAtom as useAtom8 } from "jotai";
24238
+ import { useEffect as useEffect14, useState as useState10 } from "react";
24239
+ var libraryItemSvgsCache, exportLibraryItemToSvg, useLibraryItemSvg, useLibraryCache;
24240
+ var init_useLibraryItemSvg = __esm({
24241
+ "hooks/useLibraryItemSvg.ts"() {
24242
+ "use strict";
24243
+ init_define_import_meta_env();
24244
+ init_colors();
24245
+ init_jotai();
24246
+ init_utils2();
24247
+ libraryItemSvgsCache = atom4(/* @__PURE__ */ new Map());
24248
+ exportLibraryItemToSvg = async (elements) => {
24249
+ return await exportToSvg2({
24250
+ elements,
24251
+ appState: {
24252
+ exportBackground: false,
24253
+ viewBackgroundColor: COLOR_PALETTE.white
24254
+ },
24255
+ files: null,
24256
+ renderEmbeddables: false
24257
+ });
24258
+ };
24259
+ useLibraryItemSvg = (id, elements, svgCache) => {
24260
+ const [svg, setSvg] = useState10();
24261
+ useEffect14(() => {
24262
+ if (elements) {
24263
+ if (id) {
24264
+ const cachedSvg = svgCache.get(id);
24265
+ if (cachedSvg) {
24266
+ setSvg(cachedSvg);
24267
+ } else {
24268
+ (async () => {
24269
+ const exportedSvg = await exportLibraryItemToSvg(elements);
24270
+ exportedSvg.querySelector(".style-fonts")?.remove();
24271
+ if (exportedSvg) {
24272
+ svgCache.set(id, exportedSvg);
24273
+ setSvg(exportedSvg);
24274
+ }
24275
+ })();
24276
+ }
24277
+ } else {
24278
+ (async () => {
24279
+ const exportedSvg = await exportLibraryItemToSvg(elements);
24280
+ setSvg(exportedSvg);
24281
+ })();
24282
+ }
24283
+ }
24284
+ }, [id, elements, svgCache, setSvg]);
24285
+ return svg;
24286
+ };
24287
+ useLibraryCache = () => {
24288
+ const [svgCache] = useAtom8(libraryItemSvgsCache, jotaiScope);
24289
+ const clearLibraryCache = () => svgCache.clear();
24290
+ const deleteItemsFromLibraryCache = (items) => {
24291
+ items.forEach((item) => svgCache.delete(item));
24292
+ };
24293
+ return {
24294
+ clearLibraryCache,
24295
+ deleteItemsFromLibraryCache,
24296
+ svgCache
24297
+ };
24298
+ };
24299
+ }
24300
+ });
24301
+
24302
+ // data/library.ts
24303
+ import { atom as atom5 } from "jotai";
24304
+ import { useEffect as useEffect15, useRef as useRef11 } from "react";
24305
+ var libraryItemsAtom, cloneLibraryItems, isUniqueItem, mergeLibraryItems, Library, library_default, distributeLibraryItemsOnSquareGrid, parseLibraryTokensFromUrl, useHandleLibrary;
24306
+ var init_library = __esm({
24307
+ "data/library.ts"() {
24308
+ "use strict";
24309
+ init_define_import_meta_env();
24310
+ init_blob();
24311
+ init_restore();
24312
+ init_jotai();
24313
+ init_bounds();
24314
+ init_errors();
24315
+ init_i18n();
24316
+ init_constants();
24317
+ init_useLibraryItemSvg();
24318
+ init_utils();
24319
+ libraryItemsAtom = atom5({ status: "loaded", isInitialized: true, libraryItems: [] });
24320
+ cloneLibraryItems = (libraryItems) => cloneJSON(libraryItems);
24321
+ isUniqueItem = (existingLibraryItems, targetLibraryItem) => {
24322
+ return !existingLibraryItems.find((libraryItem) => {
24323
+ if (libraryItem.elements.length !== targetLibraryItem.elements.length) {
24324
+ return false;
24325
+ }
24326
+ return libraryItem.elements.every((libItemExcalidrawItem, idx) => {
24327
+ return libItemExcalidrawItem.id === targetLibraryItem.elements[idx].id && libItemExcalidrawItem.versionNonce === targetLibraryItem.elements[idx].versionNonce;
24328
+ });
24329
+ });
24330
+ };
24331
+ mergeLibraryItems = (localItems, otherItems) => {
24332
+ const newItems = [];
24333
+ for (const item of otherItems) {
24334
+ if (isUniqueItem(localItems, item)) {
24335
+ newItems.push(item);
24336
+ }
24337
+ }
24338
+ return [...newItems, ...localItems];
24339
+ };
24340
+ Library = class {
24341
+ /** latest libraryItems */
24342
+ lastLibraryItems = [];
24343
+ /** indicates whether library is initialized with library items (has gone
24344
+ * though at least one update) */
24345
+ isInitialized = false;
24346
+ app;
24347
+ constructor(app) {
24348
+ this.app = app;
24349
+ }
24350
+ updateQueue = [];
24351
+ getLastUpdateTask = () => {
24352
+ return this.updateQueue[this.updateQueue.length - 1];
24353
+ };
24354
+ notifyListeners = () => {
24355
+ if (this.updateQueue.length > 0) {
24356
+ jotaiStore.set(libraryItemsAtom, {
24357
+ status: "loading",
24358
+ libraryItems: this.lastLibraryItems,
24359
+ isInitialized: this.isInitialized
24360
+ });
24361
+ } else {
24362
+ this.isInitialized = true;
24363
+ jotaiStore.set(libraryItemsAtom, {
24364
+ status: "loaded",
24365
+ libraryItems: this.lastLibraryItems,
24366
+ isInitialized: this.isInitialized
24367
+ });
24368
+ try {
24369
+ this.app.props.onLibraryChange?.(
24370
+ cloneLibraryItems(this.lastLibraryItems)
24371
+ );
24372
+ } catch (error) {
24373
+ console.error(error);
24374
+ }
24375
+ }
24376
+ };
24377
+ /** call on excalidraw instance unmount */
24378
+ destroy = () => {
24379
+ this.isInitialized = false;
24380
+ this.updateQueue = [];
24381
+ this.lastLibraryItems = [];
24382
+ jotaiStore.set(libraryItemSvgsCache, /* @__PURE__ */ new Map());
24383
+ };
24384
+ resetLibrary = () => {
24385
+ return this.setLibrary([]);
24386
+ };
24387
+ /**
24388
+ * @returns latest cloned libraryItems. Awaits all in-progress updates first.
24389
+ */
24390
+ getLatestLibrary = () => {
24391
+ return new Promise(async (resolve) => {
24392
+ try {
24393
+ const libraryItems = await (this.getLastUpdateTask() || this.lastLibraryItems);
24394
+ if (this.updateQueue.length > 0) {
24395
+ resolve(this.getLatestLibrary());
24396
+ } else {
24397
+ resolve(cloneLibraryItems(libraryItems));
24398
+ }
24399
+ } catch (error) {
24400
+ return resolve(this.lastLibraryItems);
24401
+ }
24402
+ });
24403
+ };
24404
+ // NOTE this is a high-level public API (exposed on ExcalidrawAPI) with
24405
+ // a slight overhead (always restoring library items). For internal use
24406
+ // where merging isn't needed, use `library.setLibrary()` directly.
24407
+ updateLibrary = async ({
24408
+ libraryItems,
24409
+ prompt = false,
24410
+ merge = false,
24411
+ openLibraryMenu = false,
24412
+ defaultStatus = "unpublished"
24413
+ }) => {
24414
+ if (openLibraryMenu) {
24415
+ this.app.setState({
24416
+ openSidebar: { name: DEFAULT_SIDEBAR.name, tab: LIBRARY_SIDEBAR_TAB }
24417
+ });
24418
+ }
24419
+ return this.setLibrary(() => {
24420
+ return new Promise(async (resolve, reject) => {
24421
+ try {
24422
+ const source = await (typeof libraryItems === "function" && !(libraryItems instanceof Blob) ? libraryItems(this.lastLibraryItems) : libraryItems);
24423
+ let nextItems;
24424
+ if (source instanceof Blob) {
24425
+ nextItems = await loadLibraryFromBlob(source, defaultStatus);
24426
+ } else {
24427
+ nextItems = restoreLibraryItems(source, defaultStatus);
24428
+ }
24429
+ if (!prompt || window.confirm(
24430
+ t("alerts.confirmAddLibrary", {
24431
+ numShapes: nextItems.length
24432
+ })
24433
+ )) {
24434
+ if (prompt) {
24435
+ this.app.focusContainer();
24436
+ }
24437
+ if (merge) {
24438
+ resolve(mergeLibraryItems(this.lastLibraryItems, nextItems));
24439
+ } else {
24440
+ resolve(nextItems);
24441
+ }
24442
+ } else {
24443
+ reject(new AbortError());
24444
+ }
24445
+ } catch (error) {
24446
+ reject(error);
24447
+ }
24448
+ });
24449
+ });
24450
+ };
24451
+ setLibrary = (libraryItems) => {
24452
+ const task = new Promise(async (resolve, reject) => {
24453
+ try {
24454
+ await this.getLastUpdateTask();
24455
+ if (typeof libraryItems === "function") {
24456
+ libraryItems = libraryItems(this.lastLibraryItems);
24457
+ }
24458
+ this.lastLibraryItems = cloneLibraryItems(await libraryItems);
24459
+ resolve(this.lastLibraryItems);
24460
+ } catch (error) {
24461
+ reject(error);
24462
+ }
24463
+ }).catch((error) => {
24464
+ if (error.name === "AbortError") {
24465
+ console.warn("Library update aborted by user");
24466
+ return this.lastLibraryItems;
24467
+ }
24468
+ throw error;
24469
+ }).finally(() => {
24470
+ this.updateQueue = this.updateQueue.filter((_task) => _task !== task);
24471
+ this.notifyListeners();
24472
+ });
24473
+ this.updateQueue.push(task);
24474
+ this.notifyListeners();
24475
+ return task;
24476
+ };
24477
+ };
24478
+ library_default = Library;
24479
+ distributeLibraryItemsOnSquareGrid = (libraryItems) => {
24480
+ const PADDING = 50;
24481
+ const ITEMS_PER_ROW = Math.ceil(Math.sqrt(libraryItems.length));
24482
+ const resElements = [];
24483
+ const getMaxHeightPerRow = (row2) => {
24484
+ const maxHeight = libraryItems.slice(row2 * ITEMS_PER_ROW, row2 * ITEMS_PER_ROW + ITEMS_PER_ROW).reduce((acc, item) => {
24485
+ const { height } = getCommonBoundingBox(item.elements);
24486
+ return Math.max(acc, height);
24487
+ }, 0);
24488
+ return maxHeight;
24489
+ };
24490
+ const getMaxWidthPerCol = (targetCol) => {
24491
+ let index2 = 0;
24492
+ let currCol = 0;
24493
+ let maxWidth = 0;
24494
+ for (const item of libraryItems) {
24495
+ if (index2 % ITEMS_PER_ROW === 0) {
24496
+ currCol = 0;
24497
+ }
24498
+ if (currCol === targetCol) {
24499
+ const { width } = getCommonBoundingBox(item.elements);
24500
+ maxWidth = Math.max(maxWidth, width);
24501
+ }
24502
+ index2++;
24503
+ currCol++;
24504
+ }
24505
+ return maxWidth;
24506
+ };
24507
+ let colOffsetX = 0;
24508
+ let rowOffsetY = 0;
24509
+ let maxHeightCurrRow = 0;
24510
+ let maxWidthCurrCol = 0;
24511
+ let index = 0;
24512
+ let col = 0;
24513
+ let row = 0;
24514
+ for (const item of libraryItems) {
24515
+ if (index && index % ITEMS_PER_ROW === 0) {
24516
+ rowOffsetY += maxHeightCurrRow + PADDING;
24517
+ colOffsetX = 0;
24518
+ col = 0;
24519
+ row++;
24520
+ }
24521
+ if (col === 0) {
24522
+ maxHeightCurrRow = getMaxHeightPerRow(row);
24523
+ }
24524
+ maxWidthCurrCol = getMaxWidthPerCol(col);
24525
+ const { minX, minY, width, height } = getCommonBoundingBox(item.elements);
24526
+ const offsetCenterX = (maxWidthCurrCol - width) / 2;
24527
+ const offsetCenterY = (maxHeightCurrRow - height) / 2;
24528
+ resElements.push(
24529
+ ...item.elements.map((element) => ({
24530
+ ...element,
24531
+ x: element.x + // offset for column
24532
+ colOffsetX + // offset to center in given square grid
24533
+ offsetCenterX - // subtract minX so that given item starts at 0 coord
24534
+ minX,
24535
+ y: element.y + // offset for row
24536
+ rowOffsetY + // offset to center in given square grid
24537
+ offsetCenterY - // subtract minY so that given item starts at 0 coord
24538
+ minY
24539
+ }))
24540
+ );
24541
+ colOffsetX += maxWidthCurrCol + PADDING;
24542
+ index++;
24543
+ col++;
24544
+ }
24545
+ return resElements;
24546
+ };
24547
+ parseLibraryTokensFromUrl = () => {
24548
+ const libraryUrl = (
24549
+ // current
24550
+ new URLSearchParams(window.location.hash.slice(1)).get(
24551
+ URL_HASH_KEYS.addLibrary
24552
+ ) || // legacy, kept for compat reasons
24553
+ new URLSearchParams(window.location.search).get(URL_QUERY_KEYS.addLibrary)
24554
+ );
24555
+ const idToken = libraryUrl ? new URLSearchParams(window.location.hash.slice(1)).get("token") : null;
24556
+ return libraryUrl ? { libraryUrl, idToken } : null;
24557
+ };
24558
+ useHandleLibrary = ({
24559
+ excalidrawAPI,
24560
+ getInitialLibraryItems
24561
+ }) => {
24562
+ const getInitialLibraryRef = useRef11(getInitialLibraryItems);
24563
+ useEffect15(() => {
24564
+ if (!excalidrawAPI) {
24565
+ return;
24566
+ }
24567
+ const importLibraryFromURL = async ({
24568
+ libraryUrl,
24569
+ idToken
24570
+ }) => {
24571
+ const libraryPromise = new Promise(async (resolve, reject) => {
24572
+ try {
24573
+ const request = await fetch(decodeURIComponent(libraryUrl));
24574
+ const blob = await request.blob();
24575
+ resolve(blob);
24576
+ } catch (error) {
24577
+ reject(error);
24578
+ }
24579
+ });
24580
+ const shouldPrompt = idToken !== excalidrawAPI.id;
24581
+ await (shouldPrompt && document.hidden ? new Promise((resolve) => {
24582
+ window.addEventListener("focus", () => resolve(), {
24583
+ once: true
24584
+ });
24585
+ }) : null);
24586
+ try {
24587
+ await excalidrawAPI.updateLibrary({
24588
+ libraryItems: libraryPromise,
24589
+ prompt: shouldPrompt,
24590
+ merge: true,
24591
+ defaultStatus: "published",
24592
+ openLibraryMenu: true
24593
+ });
24594
+ } catch (error) {
24595
+ throw error;
24596
+ } finally {
24597
+ if (window.location.hash.includes(URL_HASH_KEYS.addLibrary)) {
24598
+ const hash = new URLSearchParams(window.location.hash.slice(1));
24599
+ hash.delete(URL_HASH_KEYS.addLibrary);
24600
+ window.history.replaceState({}, APP_NAME, `#${hash.toString()}`);
24601
+ } else if (window.location.search.includes(URL_QUERY_KEYS.addLibrary)) {
24602
+ const query = new URLSearchParams(window.location.search);
24603
+ query.delete(URL_QUERY_KEYS.addLibrary);
24604
+ window.history.replaceState({}, APP_NAME, `?${query.toString()}`);
24605
+ }
24606
+ }
24607
+ };
24608
+ const onHashChange = (event) => {
24609
+ event.preventDefault();
24610
+ const libraryUrlTokens2 = parseLibraryTokensFromUrl();
24611
+ if (libraryUrlTokens2) {
24612
+ event.stopImmediatePropagation();
24613
+ window.history.replaceState({}, "", event.oldURL);
24614
+ importLibraryFromURL(libraryUrlTokens2);
24615
+ }
24616
+ };
24617
+ if (getInitialLibraryRef.current) {
24618
+ excalidrawAPI.updateLibrary({
24619
+ libraryItems: getInitialLibraryRef.current()
24620
+ });
24621
+ }
24622
+ const libraryUrlTokens = parseLibraryTokensFromUrl();
24623
+ if (libraryUrlTokens) {
24624
+ importLibraryFromURL(libraryUrlTokens);
24625
+ }
24626
+ window.addEventListener("hashchange" /* HASHCHANGE */, onHashChange);
24627
+ return () => {
24628
+ window.removeEventListener("hashchange" /* HASHCHANGE */, onHashChange);
24629
+ };
24630
+ }, [excalidrawAPI]);
24631
+ };
24632
+ }
24633
+ });
24634
+
24621
24635
  // gesture.ts
24622
24636
  var getCenter, getDistance, sum;
24623
24637
  var init_gesture = __esm({
@@ -26364,7 +26378,7 @@ var init_PublishLibrary2 = __esm({
26364
26378
  init_Dialog2();
26365
26379
  init_i18n();
26366
26380
  init_Trans();
26367
- init_export2();
26381
+ init_utils2();
26368
26382
  init_constants();
26369
26383
  init_blob();
26370
26384
  init_utils();
@@ -28061,7 +28075,7 @@ var init_ImageExportDialog2 = __esm({
28061
28075
  init_filesystem();
28062
28076
  init_i18n();
28063
28077
  init_scene();
28064
- init_export2();
28078
+ init_utils2();
28065
28079
  init_icons();
28066
28080
  init_Dialog2();
28067
28081
  init_RadioGroup2();
@@ -35450,12 +35464,6 @@ var init_emitter = __esm({
35450
35464
  init_define_import_meta_env();
35451
35465
  Emitter = class {
35452
35466
  subscribers = [];
35453
- value;
35454
- updateOnChangeOnly;
35455
- constructor(opts) {
35456
- this.updateOnChangeOnly = opts?.updateOnChangeOnly ?? false;
35457
- this.value = opts?.initialState;
35458
- }
35459
35467
  /**
35460
35468
  * Attaches subscriber
35461
35469
  *
@@ -35466,6 +35474,12 @@ var init_emitter = __esm({
35466
35474
  this.subscribers.push(..._handlers);
35467
35475
  return () => this.off(_handlers);
35468
35476
  }
35477
+ once(...handlers) {
35478
+ const _handlers = handlers.flat().filter((item) => typeof item === "function");
35479
+ _handlers.push(() => detach());
35480
+ const detach = this.on(..._handlers);
35481
+ return detach;
35482
+ }
35469
35483
  off(...handlers) {
35470
35484
  const _handlers = handlers.flat();
35471
35485
  this.subscribers = this.subscribers.filter(
@@ -35473,15 +35487,13 @@ var init_emitter = __esm({
35473
35487
  );
35474
35488
  }
35475
35489
  trigger(...payload) {
35476
- if (this.updateOnChangeOnly && this.value === payload) {
35477
- return [];
35490
+ for (const handler of this.subscribers) {
35491
+ handler(...payload);
35478
35492
  }
35479
- this.value = payload;
35480
- return this.subscribers.map((handler) => handler(...payload));
35493
+ return this;
35481
35494
  }
35482
- destroy() {
35495
+ clear() {
35483
35496
  this.subscribers = [];
35484
- this.value = void 0;
35485
35497
  }
35486
35498
  };
35487
35499
  }
@@ -35797,7 +35809,7 @@ var init_App = __esm({
35797
35809
  init_emitter();
35798
35810
  init_ElementCanvasButtons2();
35799
35811
  init_magic();
35800
- init_export2();
35812
+ init_utils2();
35801
35813
  init_colors();
35802
35814
  init_MagicButton();
35803
35815
  init_icons();
@@ -35890,6 +35902,7 @@ var init_App = __esm({
35890
35902
  hitLinkElement;
35891
35903
  lastPointerDownEvent = null;
35892
35904
  lastPointerUpEvent = null;
35905
+ lastPointerMoveEvent = null;
35893
35906
  lastViewportPosition = { x: 0, y: 0 };
35894
35907
  laserPathManager = new LaserPathManager(this);
35895
35908
  onChangeEmitter = new Emitter();
@@ -35897,6 +35910,8 @@ var init_App = __esm({
35897
35910
  onPointerUpEmitter = new Emitter();
35898
35911
  onUserFollowEmitter = new Emitter();
35899
35912
  onScrollChangeEmitter = new Emitter();
35913
+ missingPointerEventCleanupEmitter = new Emitter();
35914
+ onRemoveEventListenersEmitter = new Emitter();
35900
35915
  constructor(props) {
35901
35916
  super(props);
35902
35917
  const defaultAppState2 = getDefaultAppState();
@@ -37274,6 +37289,7 @@ var init_App = __esm({
37274
37289
  return false;
37275
37290
  };
37276
37291
  async componentDidMount() {
37292
+ console.log("HELLO IS");
37277
37293
  this.unmounted = false;
37278
37294
  this.excalidrawContainerValue.container = this.excalidrawContainerRef.current;
37279
37295
  if (define_import_meta_env_default.MODE === ENV.TEST || define_import_meta_env_default.DEV) {
@@ -37346,7 +37362,7 @@ var init_App = __esm({
37346
37362
  this.scene.destroy();
37347
37363
  this.library.destroy();
37348
37364
  this.laserPathManager.destroy();
37349
- this.onChangeEmitter.destroy();
37365
+ this.onChangeEmitter.clear();
37350
37366
  ShapeCache.destroy();
37351
37367
  SnapCache.destroy();
37352
37368
  clearTimeout(touchTimeout);
@@ -37363,60 +37379,6 @@ var init_App = __esm({
37363
37379
  }
37364
37380
  this.setState({});
37365
37381
  });
37366
- removeEventListeners() {
37367
- document.removeEventListener("pointerup" /* POINTER_UP */, this.removePointer);
37368
- document.removeEventListener("copy" /* COPY */, this.onCopy);
37369
- document.removeEventListener("paste" /* PASTE */, this.pasteFromClipboard);
37370
- document.removeEventListener("cut" /* CUT */, this.onCut);
37371
- this.excalidrawContainerRef.current?.removeEventListener(
37372
- "wheel" /* WHEEL */,
37373
- this.onWheel
37374
- );
37375
- this.nearestScrollableContainer?.removeEventListener(
37376
- "scroll" /* SCROLL */,
37377
- this.onScroll
37378
- );
37379
- document.removeEventListener("keydown" /* KEYDOWN */, this.onKeyDown, false);
37380
- document.removeEventListener(
37381
- "mousemove" /* MOUSE_MOVE */,
37382
- this.updateCurrentCursorPosition,
37383
- false
37384
- );
37385
- document.removeEventListener("keyup" /* KEYUP */, this.onKeyUp);
37386
- window.removeEventListener("resize" /* RESIZE */, this.onResize, false);
37387
- window.removeEventListener("unload" /* UNLOAD */, this.onUnload, false);
37388
- window.removeEventListener("blur" /* BLUR */, this.onBlur, false);
37389
- this.excalidrawContainerRef.current?.removeEventListener(
37390
- "dragover" /* DRAG_OVER */,
37391
- this.disableEvent,
37392
- false
37393
- );
37394
- this.excalidrawContainerRef.current?.removeEventListener(
37395
- "drop" /* DROP */,
37396
- this.disableEvent,
37397
- false
37398
- );
37399
- document.removeEventListener(
37400
- "gesturestart" /* GESTURE_START */,
37401
- this.onGestureStart,
37402
- false
37403
- );
37404
- document.removeEventListener(
37405
- "gesturechange" /* GESTURE_CHANGE */,
37406
- this.onGestureChange,
37407
- false
37408
- );
37409
- document.removeEventListener(
37410
- "gestureend" /* GESTURE_END */,
37411
- this.onGestureEnd,
37412
- false
37413
- );
37414
- document.removeEventListener(
37415
- "fullscreenchange" /* FULLSCREENCHANGE */,
37416
- this.onFullscreenChange
37417
- );
37418
- window.removeEventListener("message" /* MESSAGE */, this.onWindowMessage, false);
37419
- }
37420
37382
  /** generally invoked only if fullscreen was invoked programmatically */
37421
37383
  onFullscreenChange = () => {
37422
37384
  if (
@@ -37428,71 +37390,97 @@ var init_App = __esm({
37428
37390
  });
37429
37391
  }
37430
37392
  };
37393
+ removeEventListeners() {
37394
+ this.onRemoveEventListenersEmitter.trigger();
37395
+ }
37431
37396
  addEventListeners() {
37432
37397
  this.removeEventListeners();
37433
- window.addEventListener("message" /* MESSAGE */, this.onWindowMessage, false);
37434
- document.addEventListener("pointerup" /* POINTER_UP */, this.removePointer);
37435
- document.addEventListener("copy" /* COPY */, this.onCopy);
37436
- this.excalidrawContainerRef.current?.addEventListener(
37437
- "wheel" /* WHEEL */,
37438
- this.onWheel,
37439
- { passive: false }
37440
- );
37441
37398
  if (this.props.handleKeyboardGlobally) {
37442
- document.addEventListener("keydown" /* KEYDOWN */, this.onKeyDown, false);
37399
+ this.onRemoveEventListenersEmitter.once(
37400
+ addEventListener(document, "keydown" /* KEYDOWN */, this.onKeyDown, false)
37401
+ );
37443
37402
  }
37444
- document.addEventListener("keyup" /* KEYUP */, this.onKeyUp, { passive: true });
37445
- document.addEventListener(
37446
- "mousemove" /* MOUSE_MOVE */,
37447
- this.updateCurrentCursorPosition
37448
- );
37449
- document.fonts?.addEventListener?.("loadingdone", (event) => {
37450
- const loadedFontFaces = event.fontfaces;
37451
- this.fonts.onFontsLoaded(loadedFontFaces);
37452
- });
37453
- document.addEventListener(
37454
- "gesturestart" /* GESTURE_START */,
37455
- this.onGestureStart,
37456
- false
37457
- );
37458
- document.addEventListener(
37459
- "gesturechange" /* GESTURE_CHANGE */,
37460
- this.onGestureChange,
37461
- false
37462
- );
37463
- document.addEventListener(
37464
- "gestureend" /* GESTURE_END */,
37465
- this.onGestureEnd,
37466
- false
37403
+ this.onRemoveEventListenersEmitter.once(
37404
+ addEventListener(
37405
+ this.excalidrawContainerRef.current,
37406
+ "wheel" /* WHEEL */,
37407
+ this.onWheel,
37408
+ { passive: false }
37409
+ ),
37410
+ addEventListener(window, "message" /* MESSAGE */, this.onWindowMessage, false),
37411
+ addEventListener(document, "pointerup" /* POINTER_UP */, this.removePointer),
37412
+ // #3553
37413
+ addEventListener(document, "copy" /* COPY */, this.onCopy),
37414
+ addEventListener(document, "keyup" /* KEYUP */, this.onKeyUp, { passive: true }),
37415
+ addEventListener(
37416
+ document,
37417
+ "mousemove" /* MOUSE_MOVE */,
37418
+ this.updateCurrentCursorPosition
37419
+ ),
37420
+ // rerender text elements on font load to fix #637 && #1553
37421
+ addEventListener(document.fonts, "loadingdone", (event) => {
37422
+ const loadedFontFaces = event.fontfaces;
37423
+ this.fonts.onFontsLoaded(loadedFontFaces);
37424
+ }),
37425
+ // Safari-only desktop pinch zoom
37426
+ addEventListener(
37427
+ document,
37428
+ "gesturestart" /* GESTURE_START */,
37429
+ this.onGestureStart,
37430
+ false
37431
+ ),
37432
+ addEventListener(
37433
+ document,
37434
+ "gesturechange" /* GESTURE_CHANGE */,
37435
+ this.onGestureChange,
37436
+ false
37437
+ ),
37438
+ addEventListener(
37439
+ document,
37440
+ "gestureend" /* GESTURE_END */,
37441
+ this.onGestureEnd,
37442
+ false
37443
+ ),
37444
+ addEventListener(window, "focus" /* FOCUS */, () => {
37445
+ this.maybeCleanupAfterMissingPointerUp(null);
37446
+ })
37467
37447
  );
37468
37448
  if (this.state.viewModeEnabled) {
37469
37449
  return;
37470
37450
  }
37471
- document.addEventListener("fullscreenchange" /* FULLSCREENCHANGE */, this.onFullscreenChange);
37472
- document.addEventListener("paste" /* PASTE */, this.pasteFromClipboard);
37473
- document.addEventListener("cut" /* CUT */, this.onCut);
37451
+ this.onRemoveEventListenersEmitter.once(
37452
+ addEventListener(
37453
+ document,
37454
+ "fullscreenchange" /* FULLSCREENCHANGE */,
37455
+ this.onFullscreenChange
37456
+ ),
37457
+ addEventListener(document, "paste" /* PASTE */, this.pasteFromClipboard),
37458
+ addEventListener(document, "cut" /* CUT */, this.onCut),
37459
+ addEventListener(window, "resize" /* RESIZE */, this.onResize, false),
37460
+ addEventListener(window, "unload" /* UNLOAD */, this.onUnload, false),
37461
+ addEventListener(window, "blur" /* BLUR */, this.onBlur, false),
37462
+ addEventListener(
37463
+ this.excalidrawContainerRef.current,
37464
+ "dragover" /* DRAG_OVER */,
37465
+ this.disableEvent,
37466
+ false
37467
+ ),
37468
+ addEventListener(
37469
+ this.excalidrawContainerRef.current,
37470
+ "drop" /* DROP */,
37471
+ this.disableEvent,
37472
+ false
37473
+ )
37474
+ );
37474
37475
  if (this.props.detectScroll) {
37475
- this.nearestScrollableContainer = getNearestScrollableContainer(
37476
- this.excalidrawContainerRef.current
37477
- );
37478
- this.nearestScrollableContainer.addEventListener(
37479
- "scroll" /* SCROLL */,
37480
- this.onScroll
37476
+ this.onRemoveEventListenersEmitter.once(
37477
+ addEventListener(
37478
+ getNearestScrollableContainer(this.excalidrawContainerRef.current),
37479
+ "scroll" /* SCROLL */,
37480
+ this.onScroll
37481
+ )
37481
37482
  );
37482
37483
  }
37483
- window.addEventListener("resize" /* RESIZE */, this.onResize, false);
37484
- window.addEventListener("unload" /* UNLOAD */, this.onUnload, false);
37485
- window.addEventListener("blur" /* BLUR */, this.onBlur, false);
37486
- this.excalidrawContainerRef.current?.addEventListener(
37487
- "dragover" /* DRAG_OVER */,
37488
- this.disableEvent,
37489
- false
37490
- );
37491
- this.excalidrawContainerRef.current?.addEventListener(
37492
- "drop" /* DROP */,
37493
- this.disableEvent,
37494
- false
37495
- );
37496
37484
  }
37497
37485
  componentDidUpdate(prevProps, prevState) {
37498
37486
  this.updateEmbeddables();
@@ -37665,7 +37653,7 @@ var init_App = __esm({
37665
37653
  didTapTwice = false;
37666
37654
  }
37667
37655
  onTouchStart = (event) => {
37668
- if (!isAndroid) {
37656
+ if (isIOS) {
37669
37657
  event.preventDefault();
37670
37658
  }
37671
37659
  if (!didTapTwice) {
@@ -37686,9 +37674,6 @@ var init_App = __esm({
37686
37674
  didTapTwice = false;
37687
37675
  clearTimeout(tappedTwiceTimer);
37688
37676
  }
37689
- if (isAndroid) {
37690
- event.preventDefault();
37691
- }
37692
37677
  if (event.touches.length === 2) {
37693
37678
  this.setState({
37694
37679
  selectedElementIds: makeNextSelectedElementIds({}, this.state),
@@ -39018,6 +39003,7 @@ var init_App = __esm({
39018
39003
  };
39019
39004
  handleCanvasPointerMove = (event) => {
39020
39005
  this.savePointer(event.clientX, event.clientY, this.state.cursorButton);
39006
+ this.lastPointerMoveEvent = event.nativeEvent;
39021
39007
  if (gesture.pointers.has(event.pointerId)) {
39022
39008
  gesture.pointers.set(event.pointerId, {
39023
39009
  x: event.clientX,
@@ -39443,6 +39429,7 @@ var init_App = __esm({
39443
39429
  }
39444
39430
  }
39445
39431
  handleCanvasPointerDown = (event) => {
39432
+ this.maybeCleanupAfterMissingPointerUp(event.nativeEvent);
39446
39433
  this.maybeUnfollowRemoteUser();
39447
39434
  if (this.state.contextMenu) {
39448
39435
  this.setState({ contextMenu: null });
@@ -39478,7 +39465,6 @@ var init_App = __esm({
39478
39465
  selection.removeAllRanges();
39479
39466
  }
39480
39467
  this.maybeOpenContextMenuAfterPointerDownOnTouchDevices(event);
39481
- this.maybeCleanupAfterMissingPointerUp(event);
39482
39468
  if (!this.state.penDetected && event.pointerType === "pen") {
39483
39469
  this.setState((prevState) => {
39484
39470
  return {
@@ -39502,7 +39488,47 @@ var init_App = __esm({
39502
39488
  cursorButton: "down"
39503
39489
  });
39504
39490
  this.savePointer(event.clientX, event.clientY, "down");
39505
- if (event.button !== POINTER_BUTTON.MAIN && event.button !== POINTER_BUTTON.TOUCH) {
39491
+ if (event.button === POINTER_BUTTON.ERASER && this.state.activeTool.type !== TOOL_TYPE.eraser) {
39492
+ this.setState(
39493
+ {
39494
+ activeTool: updateActiveTool(this.state, {
39495
+ type: TOOL_TYPE.eraser,
39496
+ lastActiveToolBeforeEraser: this.state.activeTool
39497
+ })
39498
+ },
39499
+ () => {
39500
+ this.handleCanvasPointerDown(event);
39501
+ const onPointerUp2 = () => {
39502
+ unsubPointerUp();
39503
+ unsubCleanup?.();
39504
+ if (isEraserActive(this.state)) {
39505
+ this.setState({
39506
+ activeTool: updateActiveTool(this.state, {
39507
+ ...this.state.activeTool.lastActiveTool || {
39508
+ type: TOOL_TYPE.selection
39509
+ },
39510
+ lastActiveToolBeforeEraser: null
39511
+ })
39512
+ });
39513
+ }
39514
+ };
39515
+ const unsubPointerUp = addEventListener(
39516
+ window,
39517
+ "pointerup" /* POINTER_UP */,
39518
+ onPointerUp2,
39519
+ {
39520
+ once: true
39521
+ }
39522
+ );
39523
+ let unsubCleanup;
39524
+ requestAnimationFrame(() => {
39525
+ unsubCleanup = this.missingPointerEventCleanupEmitter.once(onPointerUp2);
39526
+ });
39527
+ }
39528
+ );
39529
+ return;
39530
+ }
39531
+ if (event.button !== POINTER_BUTTON.MAIN && event.button !== POINTER_BUTTON.TOUCH && event.button !== POINTER_BUTTON.ERASER) {
39506
39532
  return;
39507
39533
  }
39508
39534
  if (gesture.pointers.size > 1) {
@@ -39586,7 +39612,9 @@ var init_App = __esm({
39586
39612
  const onPointerUp = this.onPointerUpFromPointerDownHandler(pointerDownState);
39587
39613
  const onKeyDown = this.onKeyDownFromPointerDownHandler(pointerDownState);
39588
39614
  const onKeyUp = this.onKeyUpFromPointerDownHandler(pointerDownState);
39589
- lastPointerUp = onPointerUp;
39615
+ this.missingPointerEventCleanupEmitter.once(
39616
+ (_event) => onPointerUp(_event || event.nativeEvent)
39617
+ );
39590
39618
  if (!this.state.viewModeEnabled || this.state.activeTool.type === "laser") {
39591
39619
  window.addEventListener("pointermove" /* POINTER_MOVE */, onPointerMove);
39592
39620
  window.addEventListener("pointerup" /* POINTER_UP */, onPointerUp);
@@ -39667,11 +39695,15 @@ var init_App = __esm({
39667
39695
  touchTimeout = 0;
39668
39696
  invalidateContextMenu = false;
39669
39697
  };
39670
- maybeCleanupAfterMissingPointerUp(event) {
39671
- if (lastPointerUp !== null) {
39672
- lastPointerUp(event);
39673
- }
39674
- }
39698
+ /**
39699
+ * pointerup may not fire in certian cases (user tabs away...), so in order
39700
+ * to properly cleanup pointerdown state, we need to fire any hanging
39701
+ * pointerup handlers manually
39702
+ */
39703
+ maybeCleanupAfterMissingPointerUp = (event) => {
39704
+ lastPointerUp?.();
39705
+ this.missingPointerEventCleanupEmitter.trigger(event).clear();
39706
+ };
39675
39707
  // Returns whether the event is a panning
39676
39708
  handleCanvasPanUsingWheelOrSpaceDrag = (event) => {
39677
39709
  if (!(gesture.pointers.size <= 1 && (event.button === POINTER_BUTTON.WHEEL || event.button === POINTER_BUTTON.MAIN && isHoldingSpace || isHandToolActive(this.state) || this.state.viewModeEnabled)) || isTextElement(this.state.editingElement)) {
@@ -39823,9 +39855,9 @@ var init_App = __esm({
39823
39855
  this.handlePointerMoveOverScrollbars(event2, pointerDownState);
39824
39856
  });
39825
39857
  const onPointerUp = withBatchedUpdates(() => {
39858
+ lastPointerUp = null;
39826
39859
  isDraggingScrollBar = false;
39827
39860
  setCursorForShape(this.interactiveCanvas, this.state);
39828
- lastPointerUp = null;
39829
39861
  this.setState({
39830
39862
  cursorButton: "up"
39831
39863
  });
@@ -40872,6 +40904,7 @@ var init_App = __esm({
40872
40904
  }
40873
40905
  onPointerUpFromPointerDownHandler(pointerDownState) {
40874
40906
  return withBatchedUpdates((childEvent) => {
40907
+ this.removePointer(childEvent);
40875
40908
  if (pointerDownState.eventListeners.onMove) {
40876
40909
  pointerDownState.eventListeners.onMove.flush();
40877
40910
  }
@@ -40951,7 +40984,7 @@ var init_App = __esm({
40951
40984
  }
40952
40985
  }
40953
40986
  }
40954
- lastPointerUp = null;
40987
+ this.missingPointerEventCleanupEmitter.clear();
40955
40988
  window.removeEventListener(
40956
40989
  "pointermove" /* POINTER_MOVE */,
40957
40990
  pointerDownState.eventListeners.onMove
@@ -41242,18 +41275,20 @@ var init_App = __esm({
41242
41275
  });
41243
41276
  }
41244
41277
  }
41245
- if (isEraserActive(this.state)) {
41278
+ const pointerStart = this.lastPointerDownEvent;
41279
+ const pointerEnd = this.lastPointerUpEvent || this.lastPointerMoveEvent;
41280
+ if (isEraserActive(this.state) && pointerStart && pointerEnd) {
41246
41281
  const draggedDistance = distance2d(
41247
- this.lastPointerDownEvent.clientX,
41248
- this.lastPointerDownEvent.clientY,
41249
- this.lastPointerUpEvent.clientX,
41250
- this.lastPointerUpEvent.clientY
41282
+ pointerStart.clientX,
41283
+ pointerStart.clientY,
41284
+ pointerEnd.clientX,
41285
+ pointerEnd.clientY
41251
41286
  );
41252
41287
  if (draggedDistance === 0) {
41253
41288
  const scenePointer = viewportCoordsToSceneCoords(
41254
41289
  {
41255
- clientX: this.lastPointerUpEvent.clientX,
41256
- clientY: this.lastPointerUpEvent.clientY
41290
+ clientX: pointerEnd.clientX,
41291
+ clientY: pointerEnd.clientY
41257
41292
  },
41258
41293
  this.state
41259
41294
  );
@@ -42922,7 +42957,9 @@ var init_excalidraw = __esm({
42922
42957
  init_element();
42923
42958
  init_i18n();
42924
42959
  init_restore();
42925
- init_export2();
42960
+ init_utils2();
42961
+ init_json();
42962
+ init_blob();
42926
42963
  init_typeChecks();
42927
42964
  init_constants();
42928
42965
  init_mutateElement();
@@ -42938,7 +42975,10 @@ var init_excalidraw = __esm({
42938
42975
  init_actionCanvas();
42939
42976
  init_transform();
42940
42977
  init_bounds();
42941
- init_export2();
42978
+ init_appState();
42979
+ init_math();
42980
+ init_typeChecks();
42981
+ init_renderElement();
42942
42982
  polyfill_default();
42943
42983
  ExcalidrawBase = (props) => {
42944
42984
  const {
@@ -43091,20 +43131,24 @@ export {
43091
43131
  bumpVersion,
43092
43132
  convertToExcalidrawElements,
43093
43133
  defaultLang,
43094
- elementPartiallyOverlapsWithOrContainsBBox,
43095
- elementsOverlappingBBox,
43096
43134
  exportToBlob,
43097
43135
  exportToCanvas2 as exportToCanvas,
43098
43136
  exportToClipboard,
43099
43137
  exportToSvg2 as exportToSvg,
43100
43138
  getCommonBounds,
43139
+ getDefaultAppState,
43140
+ getElementBounds,
43101
43141
  getFreeDrawSvgPath,
43102
43142
  getNonDeletedElements2 as getNonDeletedElements,
43103
43143
  getSceneVersion,
43104
43144
  getVisibleSceneBounds,
43105
- isElementInsideBBox,
43145
+ isArrowElement,
43146
+ isExcalidrawElement,
43147
+ isFreeDrawElement,
43106
43148
  isInvisiblySmallElement,
43107
43149
  isLinearElement,
43150
+ isTextElement,
43151
+ isValueInRange,
43108
43152
  languages,
43109
43153
  loadFromBlob,
43110
43154
  loadLibraryFromBlob,
@@ -43118,6 +43162,7 @@ export {
43118
43162
  restoreAppState,
43119
43163
  restoreElements,
43120
43164
  restoreLibraryItems,
43165
+ rotatePoint,
43121
43166
  sceneCoordsToViewportCoords,
43122
43167
  serializeAsJSON,
43123
43168
  serializeLibraryAsJSON,