@excalidraw/excalidraw 0.17.1-d2f67e6 → 0.17.1-e63dd02

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 (252) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/browser/dev/excalidraw-assets-dev/CascadiaCode-Regular-TMZI7IJ5.woff2 +0 -0
  3. package/dist/browser/dev/excalidraw-assets-dev/ComicShanns-Regular-6TOETDFT.woff2 +0 -0
  4. package/dist/browser/dev/excalidraw-assets-dev/Excalifont-Regular-CPKEUDVM.woff2 +0 -0
  5. package/dist/browser/dev/excalidraw-assets-dev/LiberationSans-Regular-ZQD73GJM.woff2 +0 -0
  6. package/dist/browser/dev/excalidraw-assets-dev/Virgil-Regular-YHAB2VGJ.woff2 +0 -0
  7. package/dist/browser/dev/excalidraw-assets-dev/{chunk-EM6LVGFW.js → chunk-IT7T3AIK.js} +23 -5
  8. package/dist/browser/dev/excalidraw-assets-dev/chunk-IT7T3AIK.js.map +7 -0
  9. package/dist/browser/dev/excalidraw-assets-dev/{chunk-B4UMSLQQ.js → chunk-RNHSD5AR.js} +7451 -2098
  10. package/dist/browser/dev/excalidraw-assets-dev/chunk-RNHSD5AR.js.map +7 -0
  11. package/dist/browser/dev/excalidraw-assets-dev/{dist-6QVAH5JA.js → dist-DNSPZDOZ.js} +31 -19
  12. package/dist/browser/dev/excalidraw-assets-dev/dist-DNSPZDOZ.js.map +7 -0
  13. package/dist/browser/dev/excalidraw-assets-dev/{en-AZFA5HJJ.js → en-XV7OZCPP.js} +6 -2
  14. package/dist/browser/dev/excalidraw-assets-dev/{image-V7E6IT6R.js → image-77HZYGLG.js} +2 -2
  15. package/dist/browser/dev/excalidraw-assets-dev/{image-O66MQ7WQ.css → image-WDHYGKKP.css} +1 -1
  16. package/dist/browser/dev/excalidraw-assets-dev/{image-O66MQ7WQ.css.map → image-WDHYGKKP.css.map} +2 -2
  17. package/dist/browser/dev/index.css +449 -114
  18. package/dist/browser/dev/index.css.map +3 -3
  19. package/dist/browser/dev/index.js +4143 -5956
  20. package/dist/browser/dev/index.js.map +4 -4
  21. package/dist/browser/prod/excalidraw-assets/CascadiaCode-Regular-TMZI7IJ5.woff2 +0 -0
  22. package/dist/browser/prod/excalidraw-assets/ComicShanns-Regular-6TOETDFT.woff2 +0 -0
  23. package/dist/browser/prod/excalidraw-assets/Excalifont-Regular-CPKEUDVM.woff2 +0 -0
  24. package/dist/browser/prod/excalidraw-assets/LiberationSans-Regular-ZQD73GJM.woff2 +0 -0
  25. package/dist/browser/prod/excalidraw-assets/Virgil-Regular-YHAB2VGJ.woff2 +0 -0
  26. package/dist/browser/prod/excalidraw-assets/chunk-OYEADJSR.js +63 -0
  27. package/dist/browser/prod/excalidraw-assets/{chunk-7DXALCB2.js → chunk-PDYFZJMS.js} +3 -3
  28. package/dist/browser/prod/excalidraw-assets/dist-NLUQPPQQ.js +7 -0
  29. package/dist/browser/prod/excalidraw-assets/en-YVAVVILW.js +1 -0
  30. package/dist/browser/prod/excalidraw-assets/image-X3GFZHNN.js +1 -0
  31. package/dist/browser/prod/index.css +1 -1
  32. package/dist/browser/prod/index.js +40 -50
  33. package/dist/dev/CascadiaCode-Regular-TMZI7IJ5.woff2 +0 -0
  34. package/dist/dev/ComicShanns-Regular-6TOETDFT.woff2 +0 -0
  35. package/dist/dev/Excalifont-Regular-CPKEUDVM.woff2 +0 -0
  36. package/dist/dev/LiberationSans-Regular-ZQD73GJM.woff2 +0 -0
  37. package/dist/dev/Virgil-Regular-YHAB2VGJ.woff2 +0 -0
  38. package/dist/dev/{en-EB2MBPAV.json → en-YNVBSAIL.json} +18 -4
  39. package/dist/dev/index.css +449 -114
  40. package/dist/dev/index.css.map +3 -3
  41. package/dist/dev/index.js +21626 -18122
  42. package/dist/dev/index.js.map +4 -4
  43. package/dist/excalidraw/actions/actionAddToLibrary.d.ts +9 -3
  44. package/dist/excalidraw/actions/actionBoundText.d.ts +6 -2
  45. package/dist/excalidraw/actions/actionCanvas.d.ts +36 -12
  46. package/dist/excalidraw/actions/actionClipboard.d.ts +22 -7
  47. package/dist/excalidraw/actions/actionDeleteSelected.d.ts +12 -5
  48. package/dist/excalidraw/actions/actionDeleteSelected.js +24 -5
  49. package/dist/excalidraw/actions/actionDuplicateSelection.js +1 -2
  50. package/dist/excalidraw/actions/actionElementLock.d.ts +6 -2
  51. package/dist/excalidraw/actions/actionExport.d.ts +27 -9
  52. package/dist/excalidraw/actions/actionFinalize.d.ts +6 -2
  53. package/dist/excalidraw/actions/actionFinalize.js +2 -2
  54. package/dist/excalidraw/actions/actionFlip.js +2 -2
  55. package/dist/excalidraw/actions/actionFrame.d.ts +12 -4
  56. package/dist/excalidraw/actions/actionGroup.d.ts +6 -2
  57. package/dist/excalidraw/actions/actionHistory.js +4 -4
  58. package/dist/excalidraw/actions/actionLinearEditor.d.ts +3 -1
  59. package/dist/excalidraw/actions/actionLinearEditor.js +3 -2
  60. package/dist/excalidraw/actions/actionLink.d.ts +3 -1
  61. package/dist/excalidraw/actions/actionMenu.d.ts +9 -3
  62. package/dist/excalidraw/actions/actionNavigate.d.ts +6 -2
  63. package/dist/excalidraw/actions/actionProperties.d.ts +411 -56
  64. package/dist/excalidraw/actions/actionProperties.js +383 -58
  65. package/dist/excalidraw/actions/actionSelectAll.d.ts +3 -1
  66. package/dist/excalidraw/actions/actionStyles.d.ts +3 -1
  67. package/dist/excalidraw/actions/actionStyles.js +3 -2
  68. package/dist/excalidraw/actions/actionToggleGridMode.d.ts +3 -1
  69. package/dist/excalidraw/actions/actionToggleObjectsSnapMode.d.ts +3 -1
  70. package/dist/excalidraw/actions/actionToggleStats.d.ts +3 -1
  71. package/dist/excalidraw/actions/actionToggleViewMode.d.ts +3 -1
  72. package/dist/excalidraw/actions/actionToggleZenMode.d.ts +3 -1
  73. package/dist/excalidraw/actions/types.d.ts +1 -1
  74. package/dist/excalidraw/analytics.js +9 -7
  75. package/dist/excalidraw/appState.d.ts +1 -0
  76. package/dist/excalidraw/appState.js +9 -1
  77. package/dist/excalidraw/binaryheap.d.ts +12 -0
  78. package/dist/excalidraw/binaryheap.js +93 -0
  79. package/dist/excalidraw/change.d.ts +2 -1
  80. package/dist/excalidraw/change.js +6 -4
  81. package/dist/excalidraw/charts.js +0 -10
  82. package/dist/excalidraw/components/Actions.js +7 -5
  83. package/dist/excalidraw/components/App.d.ts +5 -9
  84. package/dist/excalidraw/components/App.js +218 -161
  85. package/dist/excalidraw/components/ButtonIcon.d.ts +15 -0
  86. package/dist/excalidraw/components/ButtonIcon.js +8 -0
  87. package/dist/excalidraw/components/ButtonIconSelect.js +2 -3
  88. package/dist/excalidraw/components/ButtonSeparator.d.ts +2 -0
  89. package/dist/excalidraw/components/ButtonSeparator.js +7 -0
  90. package/dist/excalidraw/components/ColorPicker/ColorPicker.js +47 -79
  91. package/dist/excalidraw/components/ColorPicker/Picker.js +1 -1
  92. package/dist/excalidraw/components/FontPicker/FontPicker.d.ts +21 -0
  93. package/dist/excalidraw/components/FontPicker/FontPicker.js +49 -0
  94. package/dist/excalidraw/components/FontPicker/FontPickerList.d.ts +25 -0
  95. package/dist/excalidraw/components/FontPicker/FontPickerList.js +119 -0
  96. package/dist/excalidraw/components/FontPicker/FontPickerTrigger.d.ts +7 -0
  97. package/dist/excalidraw/components/FontPicker/FontPickerTrigger.js +13 -0
  98. package/dist/excalidraw/components/FontPicker/keyboardNavHandlers.d.ts +14 -0
  99. package/dist/excalidraw/components/FontPicker/keyboardNavHandlers.js +38 -0
  100. package/dist/excalidraw/components/HelpDialog.js +1 -1
  101. package/dist/excalidraw/components/HintViewer.js +6 -3
  102. package/dist/excalidraw/components/PropertiesPopover.d.ts +15 -0
  103. package/dist/excalidraw/components/PropertiesPopover.js +31 -0
  104. package/dist/excalidraw/components/QuickSearch.d.ts +9 -0
  105. package/dist/excalidraw/components/QuickSearch.js +8 -0
  106. package/dist/excalidraw/components/ScrollableList.d.ts +9 -0
  107. package/dist/excalidraw/components/ScrollableList.js +8 -0
  108. package/dist/excalidraw/components/Stats/Angle.d.ts +7 -3
  109. package/dist/excalidraw/components/Stats/Angle.js +39 -31
  110. package/dist/excalidraw/components/Stats/Dimension.d.ts +6 -3
  111. package/dist/excalidraw/components/Stats/Dimension.js +51 -49
  112. package/dist/excalidraw/components/Stats/DragInput.d.ts +15 -6
  113. package/dist/excalidraw/components/Stats/DragInput.js +59 -26
  114. package/dist/excalidraw/components/Stats/FontSize.d.ts +8 -4
  115. package/dist/excalidraw/components/Stats/FontSize.js +39 -36
  116. package/dist/excalidraw/components/Stats/MultiAngle.d.ts +5 -3
  117. package/dist/excalidraw/components/Stats/MultiAngle.js +43 -34
  118. package/dist/excalidraw/components/Stats/MultiDimension.d.ts +5 -3
  119. package/dist/excalidraw/components/Stats/MultiDimension.js +101 -99
  120. package/dist/excalidraw/components/Stats/MultiFontSize.d.ts +6 -3
  121. package/dist/excalidraw/components/Stats/MultiFontSize.js +47 -32
  122. package/dist/excalidraw/components/Stats/MultiPosition.d.ts +3 -1
  123. package/dist/excalidraw/components/Stats/MultiPosition.js +52 -48
  124. package/dist/excalidraw/components/Stats/Position.d.ts +5 -1
  125. package/dist/excalidraw/components/Stats/Position.js +31 -29
  126. package/dist/excalidraw/components/Stats/index.js +5 -17
  127. package/dist/excalidraw/components/Stats/utils.d.ts +14 -3
  128. package/dist/excalidraw/components/Stats/utils.js +48 -9
  129. package/dist/excalidraw/components/TTDDialog/common.d.ts +2 -2
  130. package/dist/excalidraw/components/TTDDialog/common.js +3 -7
  131. package/dist/excalidraw/components/UserList.js +22 -22
  132. package/dist/excalidraw/components/canvases/StaticCanvas.js +1 -0
  133. package/dist/excalidraw/components/dropdownMenu/DropdownMenu.d.ts +12 -3
  134. package/dist/excalidraw/components/dropdownMenu/DropdownMenuItem.d.ts +24 -4
  135. package/dist/excalidraw/components/dropdownMenu/DropdownMenuItem.js +55 -14
  136. package/dist/excalidraw/components/dropdownMenu/DropdownMenuItemContent.d.ts +2 -1
  137. package/dist/excalidraw/components/dropdownMenu/DropdownMenuItemContent.js +2 -2
  138. package/dist/excalidraw/components/dropdownMenu/common.d.ts +1 -1
  139. package/dist/excalidraw/components/dropdownMenu/common.js +3 -2
  140. package/dist/excalidraw/components/icons.d.ts +4 -0
  141. package/dist/excalidraw/components/icons.js +7 -0
  142. package/dist/excalidraw/components/main-menu/MainMenu.d.ts +12 -3
  143. package/dist/excalidraw/components/welcome-screen/WelcomeScreen.Center.js +2 -2
  144. package/dist/excalidraw/components/welcome-screen/WelcomeScreen.Hints.js +3 -3
  145. package/dist/excalidraw/constants.d.ts +17 -2
  146. package/dist/excalidraw/constants.js +21 -4
  147. package/dist/excalidraw/data/reconcile.js +18 -1
  148. package/dist/excalidraw/data/restore.js +55 -9
  149. package/dist/excalidraw/data/transform.js +8 -5
  150. package/dist/excalidraw/element/binding.d.ts +28 -9
  151. package/dist/excalidraw/element/binding.js +303 -71
  152. package/dist/excalidraw/element/collision.d.ts +1 -1
  153. package/dist/excalidraw/element/collision.js +4 -1
  154. package/dist/excalidraw/element/dragElements.d.ts +2 -2
  155. package/dist/excalidraw/element/dragElements.js +13 -3
  156. package/dist/excalidraw/element/embeddable.d.ts +3 -1
  157. package/dist/excalidraw/element/heading.d.ts +11 -0
  158. package/dist/excalidraw/element/heading.js +81 -0
  159. package/dist/excalidraw/element/index.d.ts +1 -1
  160. package/dist/excalidraw/element/index.js +1 -1
  161. package/dist/excalidraw/element/linearElementEditor.d.ts +21 -13
  162. package/dist/excalidraw/element/linearElementEditor.js +133 -56
  163. package/dist/excalidraw/element/newElement.d.ts +8 -3
  164. package/dist/excalidraw/element/newElement.js +15 -2
  165. package/dist/excalidraw/element/resizeElements.d.ts +4 -3
  166. package/dist/excalidraw/element/resizeElements.js +47 -23
  167. package/dist/excalidraw/element/routing.d.ts +13 -0
  168. package/dist/excalidraw/element/routing.js +641 -0
  169. package/dist/excalidraw/element/textElement.d.ts +3 -26
  170. package/dist/excalidraw/element/textElement.js +54 -110
  171. package/dist/excalidraw/element/textWysiwyg.js +39 -47
  172. package/dist/excalidraw/element/transformHandles.js +7 -2
  173. package/dist/excalidraw/element/typeChecks.d.ts +5 -2
  174. package/dist/excalidraw/element/typeChecks.js +17 -0
  175. package/dist/excalidraw/element/types.d.ts +12 -1
  176. package/dist/excalidraw/fonts/ExcalidrawFont.d.ts +21 -0
  177. package/dist/excalidraw/fonts/ExcalidrawFont.js +112 -0
  178. package/dist/excalidraw/fonts/index.d.ts +58 -0
  179. package/dist/excalidraw/fonts/index.js +240 -0
  180. package/dist/excalidraw/fonts/metadata.d.ts +36 -0
  181. package/dist/excalidraw/fonts/metadata.js +91 -0
  182. package/dist/excalidraw/fractionalIndex.d.ts +11 -4
  183. package/dist/excalidraw/fractionalIndex.js +38 -6
  184. package/dist/excalidraw/frame.d.ts +1 -1
  185. package/dist/excalidraw/frame.js +3 -3
  186. package/dist/excalidraw/history.d.ts +4 -3
  187. package/dist/excalidraw/history.js +8 -8
  188. package/dist/excalidraw/index.d.ts +1 -1
  189. package/dist/excalidraw/index.js +3 -3
  190. package/dist/excalidraw/locales/en.json +18 -4
  191. package/dist/excalidraw/math.d.ts +43 -0
  192. package/dist/excalidraw/math.js +110 -0
  193. package/dist/excalidraw/mermaid.js +4 -3
  194. package/dist/excalidraw/renderer/interactiveScene.js +33 -17
  195. package/dist/excalidraw/renderer/renderElement.d.ts +2 -0
  196. package/dist/excalidraw/renderer/renderElement.js +74 -54
  197. package/dist/excalidraw/renderer/staticSvgScene.js +2 -1
  198. package/dist/excalidraw/scene/Scene.js +9 -3
  199. package/dist/excalidraw/scene/Shape.js +56 -5
  200. package/dist/excalidraw/scene/comparisons.d.ts +1 -0
  201. package/dist/excalidraw/scene/comparisons.js +1 -1
  202. package/dist/excalidraw/scene/export.d.ts +2 -1
  203. package/dist/excalidraw/scene/export.js +33 -35
  204. package/dist/excalidraw/scene/types.d.ts +1 -4
  205. package/dist/excalidraw/shapes.d.ts +8 -0
  206. package/dist/excalidraw/shapes.js +57 -0
  207. package/dist/excalidraw/types.d.ts +8 -3
  208. package/dist/excalidraw/utils.d.ts +11 -1
  209. package/dist/excalidraw/utils.js +22 -0
  210. package/dist/prod/CascadiaCode-Regular-TMZI7IJ5.woff2 +0 -0
  211. package/dist/prod/ComicShanns-Regular-6TOETDFT.woff2 +0 -0
  212. package/dist/prod/Excalifont-Regular-CPKEUDVM.woff2 +0 -0
  213. package/dist/prod/LiberationSans-Regular-ZQD73GJM.woff2 +0 -0
  214. package/dist/prod/Virgil-Regular-YHAB2VGJ.woff2 +0 -0
  215. package/dist/prod/{en-EB2MBPAV.json → en-YNVBSAIL.json} +18 -4
  216. package/dist/prod/index.css +1 -1
  217. package/dist/prod/index.js +49 -53
  218. package/dist/utils/export.d.ts +2 -1
  219. package/dist/utils/export.js +2 -1
  220. package/dist/utils/geometry/geometry.d.ts +2 -1
  221. package/dist/utils/geometry/geometry.js +5 -1
  222. package/dist/utils/index.d.ts +1 -0
  223. package/dist/utils/index.js +1 -0
  224. package/history.ts +9 -2
  225. package/package.json +2 -2
  226. package/dist/browser/dev/Cascadia-CYPE3OJC.woff2 +0 -0
  227. package/dist/browser/dev/Virgil-UZN6MUT6.woff2 +0 -0
  228. package/dist/browser/dev/excalidraw-assets-dev/chunk-B4UMSLQQ.js.map +0 -7
  229. package/dist/browser/dev/excalidraw-assets-dev/chunk-EM6LVGFW.js.map +0 -7
  230. package/dist/browser/dev/excalidraw-assets-dev/dist-6QVAH5JA.js.map +0 -7
  231. package/dist/browser/prod/Cascadia-CYPE3OJC.woff2 +0 -0
  232. package/dist/browser/prod/Virgil-UZN6MUT6.woff2 +0 -0
  233. package/dist/browser/prod/excalidraw-assets/chunk-EGOLGOLD.js +0 -55
  234. package/dist/browser/prod/excalidraw-assets/dist-567JAXHK.js +0 -7
  235. package/dist/browser/prod/excalidraw-assets/en-6E7MYLWO.js +0 -1
  236. package/dist/browser/prod/excalidraw-assets/image-SI7BKULC.js +0 -1
  237. package/dist/dev/Cascadia-CYPE3OJC.woff2 +0 -0
  238. package/dist/dev/Virgil-UZN6MUT6.woff2 +0 -0
  239. package/dist/excalidraw/scene/Fonts.d.ts +0 -19
  240. package/dist/excalidraw/scene/Fonts.js +0 -66
  241. package/dist/prod/Cascadia-CYPE3OJC.woff2 +0 -0
  242. package/dist/prod/Virgil-UZN6MUT6.woff2 +0 -0
  243. /package/dist/browser/dev/{Assistant-Bold-ZDZZ6JHA.woff2 → excalidraw-assets-dev/Assistant-Bold-ZDZZ6JHA.woff2} +0 -0
  244. /package/dist/browser/dev/{Assistant-Medium-DZ25RZU3.woff2 → excalidraw-assets-dev/Assistant-Medium-DZ25RZU3.woff2} +0 -0
  245. /package/dist/browser/dev/{Assistant-Regular-PLF2XOGW.woff2 → excalidraw-assets-dev/Assistant-Regular-PLF2XOGW.woff2} +0 -0
  246. /package/dist/browser/dev/{Assistant-SemiBold-CZ5MX6FK.woff2 → excalidraw-assets-dev/Assistant-SemiBold-CZ5MX6FK.woff2} +0 -0
  247. /package/dist/browser/dev/excalidraw-assets-dev/{en-AZFA5HJJ.js.map → en-XV7OZCPP.js.map} +0 -0
  248. /package/dist/browser/dev/excalidraw-assets-dev/{image-V7E6IT6R.js.map → image-77HZYGLG.js.map} +0 -0
  249. /package/dist/browser/prod/{Assistant-Bold-ZDZZ6JHA.woff2 → excalidraw-assets/Assistant-Bold-ZDZZ6JHA.woff2} +0 -0
  250. /package/dist/browser/prod/{Assistant-Medium-DZ25RZU3.woff2 → excalidraw-assets/Assistant-Medium-DZ25RZU3.woff2} +0 -0
  251. /package/dist/browser/prod/{Assistant-Regular-PLF2XOGW.woff2 → excalidraw-assets/Assistant-Regular-PLF2XOGW.woff2} +0 -0
  252. /package/dist/browser/prod/{Assistant-SemiBold-CZ5MX6FK.woff2 → excalidraw-assets/Assistant-SemiBold-CZ5MX6FK.woff2} +0 -0
@@ -11,7 +11,7 @@ import { actions } from "../actions/register";
11
11
  import { trackEvent } from "../analytics";
12
12
  import { getDefaultAppState, isEraserActive, isHandToolActive, } from "../appState";
13
13
  import { copyTextToSystemClipboard, parseClipboard } from "../clipboard";
14
- import { DEFAULT_FONT_SIZE } from "../constants";
14
+ import { ARROW_TYPE } from "../constants";
15
15
  import { APP_NAME, CURSOR_TYPE, DEFAULT_MAX_IMAGE_WIDTH_OR_HEIGHT, DEFAULT_VERTICAL_ALIGN, DRAGGING_THRESHOLD, ELEMENT_SHIFT_TRANSLATE_AMOUNT, ELEMENT_TRANSLATE_AMOUNT, ENV, EVENT, FRAME_STYLE, GRID_SIZE, IMAGE_MIME_TYPES, IMAGE_RENDER_TIMEOUT, isBrave, LINE_CONFIRM_THRESHOLD, MAX_ALLOWED_FILE_BYTES, MIME_TYPES, MQ_MAX_HEIGHT_LANDSCAPE, MQ_MAX_WIDTH_LANDSCAPE, MQ_MAX_WIDTH_PORTRAIT, MQ_RIGHT_SIDEBAR_MIN_WIDTH, POINTER_BUTTON, ROUNDNESS, SCROLL_TIMEOUT, TAP_TWICE_TIMEOUT, TEXT_TO_CENTER_SNAP_THRESHOLD, THEME, THEME_FILTER, TOUCH_CTX_MENU_TIMEOUT, VERTICAL_ALIGN, YOUTUBE_STATES, ZOOM_STEP, POINTER_EVENTS, TOOL_TYPE, EDITOR_LS_KEYS, isIOS, supportsResizeObserver, DEFAULT_COLLISION_THRESHOLD, DEFAULT_TEXT_ALIGN, } from "../constants";
16
16
  import { exportCanvas, loadFromBlob } from "../data";
17
17
  import Library, { distributeLibraryItemsOnSquareGrid } from "../data/library";
@@ -20,8 +20,8 @@ import { dragNewElement, dragSelectedElements, duplicateElement, getCommonBounds
20
20
  import { bindOrUnbindLinearElement, bindOrUnbindLinearElements, fixBindingsAfterDeletion, fixBindingsAfterDuplication, getHoveredElementForBinding, isBindingEnabled, isLinearElementSimpleAndAlreadyBound, maybeBindLinearElement, shouldEnableBindingForPointerEvent, updateBoundElements, getSuggestedBindingsForArrows, } from "../element/binding";
21
21
  import { LinearElementEditor } from "../element/linearElementEditor";
22
22
  import { mutateElement, newElementWith } from "../element/mutateElement";
23
- import { deepCopyElement, duplicateElements, newFrameElement, newFreeDrawElement, newEmbeddableElement, newMagicFrameElement, newIframeElement, } from "../element/newElement";
24
- import { hasBoundTextElement, isArrowElement, isBindingElement, isBindingElementType, isBoundToContainer, isFrameLikeElement, isImageElement, isEmbeddableElement, isInitializedImageElement, isLinearElement, isLinearElementType, isUsingAdaptiveRadius, isFrameElement, isIframeElement, isIframeLikeElement, isMagicFrameElement, isTextBindableContainer, } from "../element/typeChecks";
23
+ import { deepCopyElement, duplicateElements, newFrameElement, newFreeDrawElement, newEmbeddableElement, newMagicFrameElement, newIframeElement, newArrowElement, } from "../element/newElement";
24
+ import { hasBoundTextElement, isArrowElement, isBindingElement, isBindingElementType, isBoundToContainer, isFrameLikeElement, isImageElement, isEmbeddableElement, isInitializedImageElement, isLinearElement, isLinearElementType, isUsingAdaptiveRadius, isIframeElement, isIframeLikeElement, isMagicFrameElement, isTextBindableContainer, isElbowArrow, } from "../element/typeChecks";
25
25
  import { getCenter, getDistance } from "../gesture";
26
26
  import { editGroupForSelectedElement, getElementsInGroup, getSelectedGroupIdForElement, getSelectedGroupIds, isElementInGroup, isSelectedViaGroup, selectGroupsForSelectedElements, } from "../groups";
27
27
  import { History } from "../history";
@@ -32,8 +32,8 @@ import { distance2d, getCornerRadius, getGridPoint, isPathALoop, } from "../math
32
32
  import { calculateScrollCenter, getElementsWithinSelection, getNormalizedZoom, getSelectedElements, hasBackground, isSomeElementSelected, } from "../scene";
33
33
  import Scene from "../scene/Scene";
34
34
  import { getStateForZoom } from "../scene/zoom";
35
- import { findShapeByKey } from "../shapes";
36
- import { getClosedCurveShape, getCurveShape, getEllipseShape, getFreedrawShape, getPolygonShape, getSelectionBoxShape, } from "../../utils/geometry/shape";
35
+ import { findShapeByKey, getBoundTextShape, getElementShape } from "../shapes";
36
+ import { getSelectionBoxShape } from "../../utils/geometry/shape";
37
37
  import { isPointInShape } from "../../utils/collision";
38
38
  import { debounce, distance, getFontString, getNearestScrollableContainer, isInputLike, isToolIcon, isWritableElement, sceneCoordsToViewportCoords, tupleToCoors, viewportCoordsToSceneCoords, wrapEvent, updateObject, updateActiveTool, getShortcutKey, isTransparent, easeToValuesRAF, muteFSAbortError, isTestEnv, easeOut, updateStable, addEventListener, normalizeEOL, getDateTime, isShallowEqual, arrayToMap, } from "../utils";
39
39
  import { createSrcDoc, embeddableURLValidator, maybeParseEmbedSrc, getEmbedLink, } from "../element/embeddable";
@@ -45,12 +45,12 @@ import { dataURLToFile, generateIdFromFile, getDataURL, getFileFromEvent, ImageU
45
45
  import { getInitializedImageElements, loadHTMLImageElement, normalizeSVG, updateImageCache as _updateImageCache, } from "../element/image";
46
46
  import throttle from "lodash.throttle";
47
47
  import { fileOpen } from "../data/filesystem";
48
- import { bindTextToShapeAfterDuplication, getApproxMinLineHeight, getApproxMinLineWidth, getBoundTextElement, getContainerCenter, getContainerElement, getDefaultLineHeight, getLineHeightInPx, getMinTextElementWidth, isMeasureTextSupported, isValidTextContainer, measureText, wrapText, } from "../element/textElement";
48
+ import { bindTextToShapeAfterDuplication, getApproxMinLineHeight, getApproxMinLineWidth, getBoundTextElement, getContainerCenter, getContainerElement, getLineHeightInPx, getMinTextElementWidth, isMeasureTextSupported, isValidTextContainer, measureText, wrapText, } from "../element/textElement";
49
49
  import { showHyperlinkTooltip, hideHyperlinkToolip, Hyperlink, } from "../components/hyperlink/Hyperlink";
50
50
  import { isLocalLink, normalizeLink, toValidURL } from "../data/url";
51
51
  import { shouldShowBoundingBox } from "../element/transformHandles";
52
52
  import { actionUnlockAllElements } from "../actions/actionElementLock";
53
- import { Fonts } from "../scene/Fonts";
53
+ import { Fonts, getLineHeight } from "../fonts";
54
54
  import { getFrameChildren, isCursorInFrame, bindElementsToFramesAfterDuplication, addElementsToFrame, replaceAllElementsInFrame, removeElementsFromFrame, getElementsInResizingFrame, getElementsInNewFrame, getContainingFrame, elementOverlapsWithFrame, updateFrameMembershipOfSelectedElements, isElementInFrame, getFrameLikeTitle, getElementsOverlappingFrame, filterElementsEligibleAsFrameChildren, } from "../frame";
55
55
  import { excludeElementsInFramesFromSelection, makeNextSelectedElementIds, } from "../scene/selection";
56
56
  import { actionPaste } from "../actions/actionClipboard";
@@ -85,7 +85,7 @@ import { AnimatedTrail } from "../animated-trail";
85
85
  import { LaserTrails } from "../laser-trails";
86
86
  import { withBatchedUpdates, withBatchedUpdatesThrottled } from "../reactUtils";
87
87
  import { getRenderOpacity } from "../renderer/renderElement";
88
- import { hitElementBoundText, hitElementBoundingBoxOnly, hitElementItself, shouldTestInside, } from "../element/collision";
88
+ import { hitElementBoundText, hitElementBoundingBoxOnly, hitElementItself, } from "../element/collision";
89
89
  import { textWysiwyg } from "../element/textWysiwyg";
90
90
  import { isOverScrollBars } from "../scene/scrollbars";
91
91
  import { syncInvalidIndices, syncMovedIndices } from "../fractionalIndex";
@@ -94,6 +94,7 @@ import { getShortcutFromShortcutName } from "../actions/shortcuts";
94
94
  import { actionTextAutoResize } from "../actions/actionTextAutoResize";
95
95
  import { getVisibleSceneBounds } from "../element/bounds";
96
96
  import { isMaybeMermaidDefinition } from "../mermaid";
97
+ import { mutateElbowArrow } from "../element/routing";
97
98
  const AppContext = React.createContext(null);
98
99
  const AppPropsContext = React.createContext(null);
99
100
  const deviceContextInitialValue = {
@@ -166,8 +167,8 @@ class App extends React.Component {
166
167
  device = deviceContextInitialValue;
167
168
  excalidrawContainerRef = React.createRef();
168
169
  scene;
169
- renderer;
170
170
  fonts;
171
+ renderer;
171
172
  resizeObserver;
172
173
  nearestScrollableContainer;
173
174
  library;
@@ -701,15 +702,7 @@ class App extends React.Component {
701
702
  return null;
702
703
  }
703
704
  const isDarkTheme = this.state.theme === THEME.DARK;
704
- let frameIndex = 0;
705
- let magicFrameIndex = 0;
706
705
  return this.scene.getNonDeletedFramesLikes().map((f) => {
707
- if (isFrameElement(f)) {
708
- frameIndex++;
709
- }
710
- else {
711
- magicFrameIndex++;
712
- }
713
706
  if (!isElementInViewport(f, this.canvas.width / window.devicePixelRatio, this.canvas.height / window.devicePixelRatio, {
714
707
  offsetLeft: this.state.offsetLeft,
715
708
  offsetTop: this.state.offsetTop,
@@ -727,7 +720,7 @@ class App extends React.Component {
727
720
  this.setState({ editingFrame: null });
728
721
  };
729
722
  let frameNameJSX;
730
- const frameName = getFrameLikeTitle(f, isFrameElement(f) ? frameIndex : magicFrameIndex);
723
+ const frameName = getFrameLikeTitle(f);
731
724
  if (f.id === this.state.editingFrame) {
732
725
  const frameNameInEdit = frameName;
733
726
  frameNameJSX = (_jsx("input", { autoFocus: true, value: frameNameInEdit, onChange: (e) => {
@@ -1181,6 +1174,13 @@ class App extends React.Component {
1181
1174
  keepOpenOnAlt: false,
1182
1175
  });
1183
1176
  };
1177
+ dismissLinearEditor = () => {
1178
+ setTimeout(() => {
1179
+ this.setState({
1180
+ editingLinearElement: null,
1181
+ });
1182
+ });
1183
+ };
1184
1184
  syncActionResult = withBatchedUpdates((actionResult) => {
1185
1185
  if (this.unmounted || actionResult === false) {
1186
1186
  return;
@@ -1305,7 +1305,12 @@ class App extends React.Component {
1305
1305
  }
1306
1306
  let initialData = null;
1307
1307
  try {
1308
- initialData = (await this.props.initialData) || null;
1308
+ if (typeof this.props.initialData === "function") {
1309
+ initialData = (await this.props.initialData()) || null;
1310
+ }
1311
+ else {
1312
+ initialData = (await this.props.initialData) || null;
1313
+ }
1309
1314
  if (initialData?.libraryItems) {
1310
1315
  this.library
1311
1316
  .updateLibrary({
@@ -1353,17 +1358,17 @@ class App extends React.Component {
1353
1358
  }),
1354
1359
  };
1355
1360
  }
1356
- // FontFaceSet loadingdone event we listen on may not always fire
1357
- // (looking at you Safari), so on init we manually load fonts for current
1358
- // text elements on canvas, and rerender them once done. This also
1359
- // seems faster even in browsers that do fire the loadingdone event.
1360
- this.fonts.loadFontsForElements(scene.elements);
1361
1361
  this.resetStore();
1362
1362
  this.resetHistory();
1363
1363
  this.syncActionResult({
1364
1364
  ...scene,
1365
1365
  storeAction: StoreAction.UPDATE,
1366
1366
  });
1367
+ // FontFaceSet loadingdone event we listen on may not always
1368
+ // fire (looking at you Safari), so on init we manually load all
1369
+ // fonts and rerender scene text elements once done. This also
1370
+ // seems faster even in browsers that do fire the loadingdone event.
1371
+ this.fonts.loadSceneFonts();
1367
1372
  };
1368
1373
  isMobileBreakpoint = (width, height) => {
1369
1374
  return (width < MQ_MAX_WIDTH_PORTRAIT ||
@@ -1437,6 +1442,10 @@ class App extends React.Component {
1437
1442
  configurable: true,
1438
1443
  value: this.store,
1439
1444
  },
1445
+ fonts: {
1446
+ configurable: true,
1447
+ value: this.fonts,
1448
+ },
1440
1449
  });
1441
1450
  }
1442
1451
  this.store.onStoreIncrementEmitter.on((increment) => {
@@ -1478,7 +1487,9 @@ class App extends React.Component {
1478
1487
  }
1479
1488
  }
1480
1489
  componentWillUnmount() {
1490
+ window.launchQueue?.setConsumer(() => { });
1481
1491
  this.renderer.destroy();
1492
+ this.scene.destroy();
1482
1493
  this.scene = new Scene();
1483
1494
  this.fonts = new Fonts({ scene: this.scene });
1484
1495
  this.renderer = new Renderer(this.scene);
@@ -1487,7 +1498,6 @@ class App extends React.Component {
1487
1498
  this.resizeObserver?.disconnect();
1488
1499
  this.unmounted = true;
1489
1500
  this.removeEventListeners();
1490
- this.scene.destroy();
1491
1501
  this.library.destroy();
1492
1502
  this.laserTrails.stop();
1493
1503
  this.eraserTrail.stop();
@@ -1540,7 +1550,7 @@ class App extends React.Component {
1540
1550
  // rerender text elements on font load to fix #637 && #1553
1541
1551
  addEventListener(document.fonts, "loadingdone", (event) => {
1542
1552
  const loadedFontFaces = event.fontfaces;
1543
- this.fonts.onFontsLoaded(loadedFontFaces);
1553
+ this.fonts.onLoaded(loadedFontFaces);
1544
1554
  }),
1545
1555
  // Safari-only desktop pinch zoom
1546
1556
  addEventListener(document, EVENT.GESTURE_START, this.onGestureStart, false), addEventListener(document, EVENT.GESTURE_CHANGE, this.onGestureChange, false), addEventListener(document, EVENT.GESTURE_END, this.onGestureEnd, false), addEventListener(window, EVENT.FOCUS, () => {
@@ -1665,7 +1675,7 @@ class App extends React.Component {
1665
1675
  multiElement != null &&
1666
1676
  isBindingEnabled(this.state) &&
1667
1677
  isBindingElement(multiElement, false)) {
1668
- maybeBindLinearElement(multiElement, this.state, tupleToCoors(LinearElementEditor.getPointAtIndexGlobalCoordinates(multiElement, -1, nonDeletedElementsMap)), this);
1678
+ maybeBindLinearElement(multiElement, this.state, tupleToCoors(LinearElementEditor.getPointAtIndexGlobalCoordinates(multiElement, -1, nonDeletedElementsMap)), this.scene.getNonDeletedElementsMap(), this.scene.getNonDeletedElements());
1669
1679
  }
1670
1680
  this.store.commit(elementsMap, this.state);
1671
1681
  // Do not notify consumers if we're still loading the scene. Among other
@@ -1857,9 +1867,7 @@ class App extends React.Component {
1857
1867
  if (data.text && isMaybeMermaidDefinition(data.text)) {
1858
1868
  const api = await import("@excalidraw/mermaid-to-excalidraw");
1859
1869
  try {
1860
- const { elements: skeletonElements, files } = await api.parseMermaidToExcalidraw(data.text, {
1861
- fontSize: DEFAULT_FONT_SIZE,
1862
- });
1870
+ const { elements: skeletonElements, files } = await api.parseMermaidToExcalidraw(data.text);
1863
1871
  const elements = convertToExcalidrawElements(skeletonElements, {
1864
1872
  regenerateIds: true,
1865
1873
  });
@@ -2086,7 +2094,7 @@ class App extends React.Component {
2086
2094
  fontSize: textElementProps.fontSize,
2087
2095
  fontFamily: textElementProps.fontFamily,
2088
2096
  });
2089
- const lineHeight = getDefaultLineHeight(textElementProps.fontFamily);
2097
+ const lineHeight = getLineHeight(textElementProps.fontFamily);
2090
2098
  const [x1, , x2] = getVisibleSceneBounds(this.state);
2091
2099
  // long texts should not go beyond 800 pixels in width nor should it go below 200 px
2092
2100
  const maxTextWidth = Math.max(Math.min((x2 - x1) * 0.5, 800), 200);
@@ -2101,11 +2109,11 @@ class App extends React.Component {
2101
2109
  y: currentY,
2102
2110
  });
2103
2111
  let metrics = measureText(originalText, fontString, lineHeight);
2104
- const isTextWrapped = metrics.width > maxTextWidth;
2105
- const text = isTextWrapped
2112
+ const isTextUnwrapped = metrics.width > maxTextWidth;
2113
+ const text = isTextUnwrapped
2106
2114
  ? wrapText(originalText, fontString, maxTextWidth)
2107
2115
  : originalText;
2108
- metrics = isTextWrapped
2116
+ metrics = isTextUnwrapped
2109
2117
  ? measureText(text, fontString, lineHeight)
2110
2118
  : metrics;
2111
2119
  const startX = x - metrics.width / 2;
@@ -2117,7 +2125,7 @@ class App extends React.Component {
2117
2125
  text,
2118
2126
  originalText,
2119
2127
  lineHeight,
2120
- autoResize: !isTextWrapped,
2128
+ autoResize: !isTextUnwrapped,
2121
2129
  frameId: topLayerFrame ? topLayerFrame.id : null,
2122
2130
  });
2123
2131
  acc.push(element);
@@ -2510,13 +2518,23 @@ class App extends React.Component {
2510
2518
  this.setState({ isBindingEnabled: false });
2511
2519
  }
2512
2520
  if (isArrowKey(event.key)) {
2513
- const step = (this.state.gridSize &&
2514
- (event.shiftKey
2515
- ? ELEMENT_TRANSLATE_AMOUNT
2516
- : this.state.gridSize)) ||
2517
- (event.shiftKey
2518
- ? ELEMENT_SHIFT_TRANSLATE_AMOUNT
2519
- : ELEMENT_TRANSLATE_AMOUNT);
2521
+ const selectedElements = this.scene.getSelectedElements({
2522
+ selectedElementIds: this.state.selectedElementIds,
2523
+ includeBoundTextElement: true,
2524
+ includeElementsInFrames: true,
2525
+ });
2526
+ const elbowArrow = selectedElements.find(isElbowArrow);
2527
+ const step = elbowArrow
2528
+ ? elbowArrow.startBinding || elbowArrow.endBinding
2529
+ ? 0
2530
+ : ELEMENT_TRANSLATE_AMOUNT
2531
+ : (this.state.gridSize &&
2532
+ (event.shiftKey
2533
+ ? ELEMENT_TRANSLATE_AMOUNT
2534
+ : this.state.gridSize)) ||
2535
+ (event.shiftKey
2536
+ ? ELEMENT_SHIFT_TRANSLATE_AMOUNT
2537
+ : ELEMENT_TRANSLATE_AMOUNT);
2520
2538
  let offsetX = 0;
2521
2539
  let offsetY = 0;
2522
2540
  if (event.key === KEYS.ARROW_LEFT) {
@@ -2531,22 +2549,17 @@ class App extends React.Component {
2531
2549
  else if (event.key === KEYS.ARROW_DOWN) {
2532
2550
  offsetY = step;
2533
2551
  }
2534
- const selectedElements = this.scene.getSelectedElements({
2535
- selectedElementIds: this.state.selectedElementIds,
2536
- includeBoundTextElement: true,
2537
- includeElementsInFrames: true,
2538
- });
2539
2552
  selectedElements.forEach((element) => {
2540
2553
  mutateElement(element, {
2541
2554
  x: element.x + offsetX,
2542
2555
  y: element.y + offsetY,
2543
2556
  });
2544
- updateBoundElements(element, this.scene.getNonDeletedElementsMap(), {
2557
+ updateBoundElements(element, this.scene.getNonDeletedElementsMap(), this.scene, {
2545
2558
  simultaneouslyUpdated: selectedElements,
2546
2559
  });
2547
2560
  });
2548
2561
  this.setState({
2549
- suggestedBindings: getSuggestedBindingsForArrows(selectedElements, this),
2562
+ suggestedBindings: getSuggestedBindingsForArrows(selectedElements.filter((element) => element.id !== elbowArrow?.id || step !== 0), this.scene.getNonDeletedElementsMap()),
2550
2563
  });
2551
2564
  event.preventDefault();
2552
2565
  }
@@ -2560,9 +2573,11 @@ class App extends React.Component {
2560
2573
  this.state.editingLinearElement.elementId !==
2561
2574
  selectedElements[0].id) {
2562
2575
  this.store.shouldCaptureIncrement();
2563
- this.setState({
2564
- editingLinearElement: new LinearElementEditor(selectedElement),
2565
- });
2576
+ if (!isElbowArrow(selectedElement)) {
2577
+ this.setState({
2578
+ editingLinearElement: new LinearElementEditor(selectedElement),
2579
+ });
2580
+ }
2566
2581
  }
2567
2582
  }
2568
2583
  }
@@ -2599,6 +2614,15 @@ class App extends React.Component {
2599
2614
  if (this.state.activeTool.type !== shape) {
2600
2615
  trackEvent("toolbar", shape, `keyboard (${this.device.editor.isMobile ? "mobile" : "desktop"})`);
2601
2616
  }
2617
+ if (shape === "arrow" && this.state.activeTool.type === "arrow") {
2618
+ this.setState((prevState) => ({
2619
+ currentItemArrowType: prevState.currentItemArrowType === ARROW_TYPE.sharp
2620
+ ? ARROW_TYPE.round
2621
+ : prevState.currentItemArrowType === ARROW_TYPE.round
2622
+ ? ARROW_TYPE.elbow
2623
+ : ARROW_TYPE.sharp,
2624
+ }));
2625
+ }
2602
2626
  this.setActiveTool({ type: shape });
2603
2627
  event.stopPropagation();
2604
2628
  }
@@ -2631,6 +2655,21 @@ class App extends React.Component {
2631
2655
  event.stopPropagation();
2632
2656
  }
2633
2657
  }
2658
+ if (!event[KEYS.CTRL_OR_CMD] &&
2659
+ event.shiftKey &&
2660
+ event.key.toLowerCase() === KEYS.F) {
2661
+ const selectedElements = this.scene.getSelectedElements(this.state);
2662
+ if (this.state.activeTool.type === "selection" &&
2663
+ !selectedElements.length) {
2664
+ return;
2665
+ }
2666
+ if (this.state.activeTool.type === "text" ||
2667
+ selectedElements.find((element) => isTextElement(element) ||
2668
+ getBoundTextElement(element, this.scene.getNonDeletedElementsMap()))) {
2669
+ event.preventDefault();
2670
+ this.setState({ openPopup: "fontFamily" });
2671
+ }
2672
+ }
2634
2673
  if (event.key === KEYS.K && !event.altKey && !event[KEYS.CTRL_OR_CMD]) {
2635
2674
  if (this.state.activeTool.type === "laser") {
2636
2675
  this.setActiveTool({ type: "selection" });
@@ -2685,7 +2724,7 @@ class App extends React.Component {
2685
2724
  this.setState({ isBindingEnabled: true });
2686
2725
  }
2687
2726
  if (isArrowKey(event.key)) {
2688
- bindOrUnbindLinearElements(this.scene.getSelectedElements(this.state).filter(isLinearElement), this, isBindingEnabled(this.state), this.state.selectedLinearElement?.selectedPointsIndices ?? []);
2727
+ bindOrUnbindLinearElements(this.scene.getSelectedElements(this.state).filter(isLinearElement), this.scene.getNonDeletedElementsMap(), this.scene.getNonDeletedElements(), this.scene, isBindingEnabled(this.state), this.state.selectedLinearElement?.selectedPointsIndices ?? []);
2689
2728
  this.setState({ suggestedBindings: [] });
2690
2729
  }
2691
2730
  });
@@ -2855,7 +2894,7 @@ class App extends React.Component {
2855
2894
  onChange: withBatchedUpdates((nextOriginalText) => {
2856
2895
  updateElement(nextOriginalText, false);
2857
2896
  if (isNonDeletedElement(element)) {
2858
- updateBoundElements(element, elementsMap);
2897
+ updateBoundElements(element, elementsMap, this.scene);
2859
2898
  }
2860
2899
  }),
2861
2900
  onSubmit: withBatchedUpdates(({ viaKeyboard, nextOriginalText }) => {
@@ -2923,57 +2962,6 @@ class App extends React.Component {
2923
2962
  }
2924
2963
  return null;
2925
2964
  }
2926
- /**
2927
- * get the pure geometric shape of an excalidraw element
2928
- * which is then used for hit detection
2929
- */
2930
- getElementShape(element) {
2931
- switch (element.type) {
2932
- case "rectangle":
2933
- case "diamond":
2934
- case "frame":
2935
- case "magicframe":
2936
- case "embeddable":
2937
- case "image":
2938
- case "iframe":
2939
- case "text":
2940
- case "selection":
2941
- return getPolygonShape(element);
2942
- case "arrow":
2943
- case "line": {
2944
- const roughShape = ShapeCache.get(element)?.[0] ??
2945
- ShapeCache.generateElementShape(element, null)[0];
2946
- const [, , , , cx, cy] = getElementAbsoluteCoords(element, this.scene.getNonDeletedElementsMap());
2947
- return shouldTestInside(element)
2948
- ? getClosedCurveShape(element, roughShape, [element.x, element.y], element.angle, [cx, cy])
2949
- : getCurveShape(roughShape, [element.x, element.y], element.angle, [
2950
- cx,
2951
- cy,
2952
- ]);
2953
- }
2954
- case "ellipse":
2955
- return getEllipseShape(element);
2956
- case "freedraw": {
2957
- const [, , , , cx, cy] = getElementAbsoluteCoords(element, this.scene.getNonDeletedElementsMap());
2958
- return getFreedrawShape(element, [cx, cy], shouldTestInside(element));
2959
- }
2960
- }
2961
- }
2962
- getBoundTextShape(element) {
2963
- const boundTextElement = getBoundTextElement(element, this.scene.getNonDeletedElementsMap());
2964
- if (boundTextElement) {
2965
- if (element.type === "arrow") {
2966
- return this.getElementShape({
2967
- ...boundTextElement,
2968
- // arrow's bound text accurate position is not stored in the element's property
2969
- // but rather calculated and returned from the following static method
2970
- ...LinearElementEditor.getBoundTextElementPosition(element, boundTextElement, this.scene.getNonDeletedElementsMap()),
2971
- });
2972
- }
2973
- return this.getElementShape(boundTextElement);
2974
- }
2975
- return null;
2976
- }
2977
2965
  getElementAtPosition(x, y, opts) {
2978
2966
  const allHitElements = this.getElementsAtPosition(x, y, opts?.includeBoundTextElement, opts?.includeLockedElements);
2979
2967
  if (allHitElements.length > 1) {
@@ -2991,7 +2979,7 @@ class App extends React.Component {
2991
2979
  x,
2992
2980
  y,
2993
2981
  element: elementWithHighestZIndex,
2994
- shape: this.getElementShape(elementWithHighestZIndex),
2982
+ shape: getElementShape(elementWithHighestZIndex, this.scene.getNonDeletedElementsMap()),
2995
2983
  // when overlapping, we would like to be more precise
2996
2984
  // this also avoids the need to update past tests
2997
2985
  threshold: this.getElementHitThreshold() / 2,
@@ -3053,7 +3041,7 @@ class App extends React.Component {
3053
3041
  return isPointInShape([x, y], selectionShape);
3054
3042
  }
3055
3043
  // take bound text element into consideration for hit collision as well
3056
- const hitBoundTextOfElement = hitElementBoundText(x, y, this.getBoundTextShape(element));
3044
+ const hitBoundTextOfElement = hitElementBoundText(x, y, getBoundTextShape(element, this.scene.getNonDeletedElementsMap()));
3057
3045
  if (hitBoundTextOfElement) {
3058
3046
  return true;
3059
3047
  }
@@ -3061,7 +3049,7 @@ class App extends React.Component {
3061
3049
  x,
3062
3050
  y,
3063
3051
  element,
3064
- shape: this.getElementShape(element),
3052
+ shape: getElementShape(element, this.scene.getNonDeletedElementsMap()),
3065
3053
  threshold: this.getElementHitThreshold(),
3066
3054
  frameNameBound: isFrameLikeElement(element)
3067
3055
  ? this.frameNameBoundsCache.get(element)
@@ -3088,7 +3076,7 @@ class App extends React.Component {
3088
3076
  x,
3089
3077
  y,
3090
3078
  element: elements[index],
3091
- shape: this.getElementShape(elements[index]),
3079
+ shape: getElementShape(elements[index], this.scene.getNonDeletedElementsMap()),
3092
3080
  threshold: this.getElementHitThreshold(),
3093
3081
  })) {
3094
3082
  hitElement = elements[index];
@@ -3128,7 +3116,7 @@ class App extends React.Component {
3128
3116
  existingTextElement = this.getTextElementAtPosition(sceneX, sceneY);
3129
3117
  }
3130
3118
  const fontFamily = existingTextElement?.fontFamily || this.state.currentItemFontFamily;
3131
- const lineHeight = existingTextElement?.lineHeight || getDefaultLineHeight(fontFamily);
3119
+ const lineHeight = existingTextElement?.lineHeight || getLineHeight(fontFamily);
3132
3120
  const fontSize = this.state.currentItemFontSize;
3133
3121
  if (!existingTextElement &&
3134
3122
  shouldBindToContainer &&
@@ -3228,7 +3216,9 @@ class App extends React.Component {
3228
3216
  if (selectedElements.length === 1 && isLinearElement(selectedElements[0])) {
3229
3217
  if (event[KEYS.CTRL_OR_CMD] &&
3230
3218
  (!this.state.editingLinearElement ||
3231
- this.state.editingLinearElement.elementId !== selectedElements[0].id)) {
3219
+ this.state.editingLinearElement.elementId !==
3220
+ selectedElements[0].id) &&
3221
+ !isElbowArrow(selectedElements[0])) {
3232
3222
  this.store.shouldCaptureIncrement();
3233
3223
  this.setState({
3234
3224
  editingLinearElement: new LinearElementEditor(selectedElements[0]),
@@ -3272,7 +3262,7 @@ class App extends React.Component {
3272
3262
  x: sceneX,
3273
3263
  y: sceneY,
3274
3264
  element: container,
3275
- shape: this.getElementShape(container),
3265
+ shape: getElementShape(container, this.scene.getNonDeletedElementsMap()),
3276
3266
  threshold: this.getElementHitThreshold(),
3277
3267
  })) {
3278
3268
  const midPoint = getContainerCenter(container, this.state, this.scene.getNonDeletedElementsMap());
@@ -3446,7 +3436,7 @@ class App extends React.Component {
3446
3436
  }
3447
3437
  if (this.state.editingLinearElement &&
3448
3438
  !this.state.editingLinearElement.isDragging) {
3449
- const editingLinearElement = LinearElementEditor.handlePointerMove(event, scenePointerX, scenePointerY, this.state, this.scene.getNonDeletedElementsMap());
3439
+ const editingLinearElement = LinearElementEditor.handlePointerMove(event, scenePointerX, scenePointerY, this.state, this.scene);
3450
3440
  if (editingLinearElement &&
3451
3441
  editingLinearElement !== this.state.editingLinearElement) {
3452
3442
  // Since we are reading from previous state which is not possible with
@@ -3508,7 +3498,9 @@ class App extends React.Component {
3508
3498
  });
3509
3499
  }
3510
3500
  else {
3511
- const [gridX, gridY] = getGridPoint(scenePointerX, scenePointerY, event[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize);
3501
+ const [gridX, gridY] = getGridPoint(scenePointerX, scenePointerY, event[KEYS.CTRL_OR_CMD] || isElbowArrow(multiElement)
3502
+ ? null
3503
+ : this.state.gridSize);
3512
3504
  const [lastCommittedX, lastCommittedY] = multiElement?.lastCommittedPoint ?? [0, 0];
3513
3505
  let dxFromLastCommitted = gridX - rx - lastCommittedX;
3514
3506
  let dyFromLastCommitted = gridY - ry - lastCommittedY;
@@ -3523,16 +3515,29 @@ class App extends React.Component {
3523
3515
  if (isPathALoop(points, this.state.zoom.value)) {
3524
3516
  setCursor(this.interactiveCanvas, CURSOR_TYPE.POINTER);
3525
3517
  }
3526
- // update last uncommitted point
3527
- mutateElement(multiElement, {
3528
- points: [
3518
+ if (isElbowArrow(multiElement)) {
3519
+ mutateElbowArrow(multiElement, this.scene, [
3529
3520
  ...points.slice(0, -1),
3530
3521
  [
3531
3522
  lastCommittedX + dxFromLastCommitted,
3532
3523
  lastCommittedY + dyFromLastCommitted,
3533
3524
  ],
3534
- ],
3535
- });
3525
+ ], undefined, undefined, {
3526
+ isDragging: true,
3527
+ });
3528
+ }
3529
+ else {
3530
+ // update last uncommitted point
3531
+ mutateElement(multiElement, {
3532
+ points: [
3533
+ ...points.slice(0, -1),
3534
+ [
3535
+ lastCommittedX + dxFromLastCommitted,
3536
+ lastCommittedY + dyFromLastCommitted,
3537
+ ],
3538
+ ],
3539
+ });
3540
+ }
3536
3541
  }
3537
3542
  return;
3538
3543
  }
@@ -3553,8 +3558,9 @@ class App extends React.Component {
3553
3558
  if (this.state.selectedLinearElement) {
3554
3559
  this.handleHoverSelectedLinearElement(this.state.selectedLinearElement, scenePointerX, scenePointerY);
3555
3560
  }
3556
- if (!this.state.selectedLinearElement ||
3557
- this.state.selectedLinearElement.hoverPointIndex === -1) {
3561
+ if ((!this.state.selectedLinearElement ||
3562
+ this.state.selectedLinearElement.hoverPointIndex === -1) &&
3563
+ !(selectedElements.length === 1 && isElbowArrow(selectedElements[0]))) {
3558
3564
  const elementWithTransformHandleType = getElementWithTransformHandleType(elements, this.state, scenePointerX, scenePointerY, this.state.zoom, event.pointerType, this.scene.getNonDeletedElementsMap(), this.device);
3559
3565
  if (elementWithTransformHandleType &&
3560
3566
  elementWithTransformHandleType.transformHandleType) {
@@ -3724,7 +3730,7 @@ class App extends React.Component {
3724
3730
  x: scenePointerX,
3725
3731
  y: scenePointerY,
3726
3732
  element,
3727
- shape: this.getElementShape(element),
3733
+ shape: getElementShape(element, this.scene.getNonDeletedElementsMap()),
3728
3734
  })) {
3729
3735
  hoverPointIndex = LinearElementEditor.getPointIndexUnderCursor(element, elementsMap, this.state.zoom, scenePointerX, scenePointerY);
3730
3736
  segmentMidPointHoveredCoords =
@@ -3737,7 +3743,10 @@ class App extends React.Component {
3737
3743
  }
3738
3744
  }
3739
3745
  else if (this.hitElement(scenePointerX, scenePointerY, element)) {
3740
- setCursor(this.interactiveCanvas, CURSOR_TYPE.MOVE);
3746
+ if (!isElbowArrow(element) ||
3747
+ !(element.startBinding || element.endBinding)) {
3748
+ setCursor(this.interactiveCanvas, CURSOR_TYPE.MOVE);
3749
+ }
3741
3750
  }
3742
3751
  if (this.state.selectedLinearElement.hoverPointIndex !== hoverPointIndex) {
3743
3752
  this.setState({
@@ -4150,10 +4159,13 @@ class App extends React.Component {
4150
4159
  const origin = viewportCoordsToSceneCoords(event, this.state);
4151
4160
  const selectedElements = this.scene.getSelectedElements(this.state);
4152
4161
  const [minX, minY, maxX, maxY] = getCommonBounds(selectedElements);
4162
+ const isElbowArrowOnly = selectedElements.findIndex(isElbowArrow) === 0;
4153
4163
  return {
4154
4164
  origin,
4155
4165
  withCmdOrCtrl: event[KEYS.CTRL_OR_CMD],
4156
- originInGrid: tupleToCoors(getGridPoint(origin.x, origin.y, event[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize)),
4166
+ originInGrid: tupleToCoors(getGridPoint(origin.x, origin.y, event[KEYS.CTRL_OR_CMD] || isElbowArrowOnly
4167
+ ? null
4168
+ : this.state.gridSize)),
4157
4169
  scrollbars: isOverScrollBars(currentScrollBars, event.clientX - this.state.offsetLeft, event.clientY - this.state.offsetTop),
4158
4170
  // we need to duplicate because we'll be updating this state
4159
4171
  lastCoords: { ...origin },
@@ -4270,7 +4282,7 @@ class App extends React.Component {
4270
4282
  else {
4271
4283
  if (this.state.selectedLinearElement) {
4272
4284
  const linearElementEditor = this.state.editingLinearElement || this.state.selectedLinearElement;
4273
- const ret = LinearElementEditor.handlePointerDown(event, this.state, this.store, pointerDownState.origin, linearElementEditor, this);
4285
+ const ret = LinearElementEditor.handlePointerDown(event, this.state, this.store, pointerDownState.origin, linearElementEditor, this.scene);
4274
4286
  if (ret.hitElement) {
4275
4287
  pointerDownState.hit.element = ret.hitElement;
4276
4288
  }
@@ -4510,7 +4522,7 @@ class App extends React.Component {
4510
4522
  points: [[0, 0]],
4511
4523
  pressures,
4512
4524
  });
4513
- const boundElement = getHoveredElementForBinding(pointerDownState.origin, this);
4525
+ const boundElement = getHoveredElementForBinding(pointerDownState.origin, this.scene.getNonDeletedElements(), this.scene.getNonDeletedElementsMap());
4514
4526
  this.scene.insertElement(element);
4515
4527
  this.setState({
4516
4528
  draggingElement: element,
@@ -4616,6 +4628,15 @@ class App extends React.Component {
4616
4628
  this.actionManager.executeAction(actionFinalize);
4617
4629
  return;
4618
4630
  }
4631
+ // Elbow arrows cannot be created by putting down points
4632
+ // only the start and end points can be defined
4633
+ if (isElbowArrow(multiElement) && multiElement.points.length > 1) {
4634
+ mutateElement(multiElement, {
4635
+ lastCommittedPoint: multiElement.points[multiElement.points.length - 1],
4636
+ });
4637
+ this.actionManager.executeAction(actionFinalize);
4638
+ return;
4639
+ }
4619
4640
  const { x: rx, y: ry, lastCommittedPoint } = multiElement;
4620
4641
  // clicking inside commit zone → finalize arrow
4621
4642
  if (multiElement.points.length > 1 &&
@@ -4651,25 +4672,46 @@ class App extends React.Component {
4651
4672
  const [startArrowhead, endArrowhead] = elementType === "arrow"
4652
4673
  ? [currentItemStartArrowhead, currentItemEndArrowhead]
4653
4674
  : [null, null];
4654
- const element = newLinearElement({
4655
- type: elementType,
4656
- x: gridX,
4657
- y: gridY,
4658
- strokeColor: this.state.currentItemStrokeColor,
4659
- backgroundColor: this.state.currentItemBackgroundColor,
4660
- fillStyle: this.state.currentItemFillStyle,
4661
- strokeWidth: this.state.currentItemStrokeWidth,
4662
- strokeStyle: this.state.currentItemStrokeStyle,
4663
- roughness: this.state.currentItemRoughness,
4664
- opacity: this.state.currentItemOpacity,
4665
- roundness: this.state.currentItemRoundness === "round"
4666
- ? { type: ROUNDNESS.PROPORTIONAL_RADIUS }
4667
- : null,
4668
- startArrowhead,
4669
- endArrowhead,
4670
- locked: false,
4671
- frameId: topLayerFrame ? topLayerFrame.id : null,
4672
- });
4675
+ const element = elementType === "arrow"
4676
+ ? newArrowElement({
4677
+ type: elementType,
4678
+ x: gridX,
4679
+ y: gridY,
4680
+ strokeColor: this.state.currentItemStrokeColor,
4681
+ backgroundColor: this.state.currentItemBackgroundColor,
4682
+ fillStyle: this.state.currentItemFillStyle,
4683
+ strokeWidth: this.state.currentItemStrokeWidth,
4684
+ strokeStyle: this.state.currentItemStrokeStyle,
4685
+ roughness: this.state.currentItemRoughness,
4686
+ opacity: this.state.currentItemOpacity,
4687
+ roundness: this.state.currentItemArrowType === ARROW_TYPE.round
4688
+ ? { type: ROUNDNESS.PROPORTIONAL_RADIUS }
4689
+ : // note, roundness doesn't have any effect for elbow arrows,
4690
+ // but it's best to set it to null as well
4691
+ null,
4692
+ startArrowhead,
4693
+ endArrowhead,
4694
+ locked: false,
4695
+ frameId: topLayerFrame ? topLayerFrame.id : null,
4696
+ elbowed: this.state.currentItemArrowType === ARROW_TYPE.elbow,
4697
+ })
4698
+ : newLinearElement({
4699
+ type: elementType,
4700
+ x: gridX,
4701
+ y: gridY,
4702
+ strokeColor: this.state.currentItemStrokeColor,
4703
+ backgroundColor: this.state.currentItemBackgroundColor,
4704
+ fillStyle: this.state.currentItemFillStyle,
4705
+ strokeWidth: this.state.currentItemStrokeWidth,
4706
+ strokeStyle: this.state.currentItemStrokeStyle,
4707
+ roughness: this.state.currentItemRoughness,
4708
+ opacity: this.state.currentItemOpacity,
4709
+ roundness: this.state.currentItemRoundness === "round"
4710
+ ? { type: ROUNDNESS.PROPORTIONAL_RADIUS }
4711
+ : null,
4712
+ locked: false,
4713
+ frameId: topLayerFrame ? topLayerFrame.id : null,
4714
+ });
4673
4715
  this.setState((prevState) => {
4674
4716
  const nextSelectedElementIds = {
4675
4717
  ...prevState.selectedElementIds,
@@ -4682,7 +4724,7 @@ class App extends React.Component {
4682
4724
  mutateElement(element, {
4683
4725
  points: [...element.points, [0, 0]],
4684
4726
  });
4685
- const boundElement = getHoveredElementForBinding(pointerDownState.origin, this);
4727
+ const boundElement = getHoveredElementForBinding(pointerDownState.origin, this.scene.getNonDeletedElements(), this.scene.getNonDeletedElementsMap(), isElbowArrow(element));
4686
4728
  this.scene.insertElement(element);
4687
4729
  this.setState({
4688
4730
  draggingElement: element,
@@ -4892,7 +4934,7 @@ class App extends React.Component {
4892
4934
  }
4893
4935
  const didDrag = LinearElementEditor.handlePointDragging(event, this.state, pointerCoords.x, pointerCoords.y, (element, pointsSceneCoords) => {
4894
4936
  this.maybeSuggestBindingsForLinearElementAtCoords(element, pointsSceneCoords);
4895
- }, linearElementEditor, this.scene.getNonDeletedElementsMap());
4937
+ }, linearElementEditor, this.scene);
4896
4938
  if (didDrag) {
4897
4939
  pointerDownState.lastCoords.x = pointerCoords.x;
4898
4940
  pointerDownState.lastCoords.y = pointerCoords.y;
@@ -4978,10 +5020,14 @@ class App extends React.Component {
4978
5020
  // when we're editing the name of a frame, we want the user to be
4979
5021
  // able to select and interact with the text input
4980
5022
  !this.state.editingFrame &&
4981
- dragSelectedElements(pointerDownState, selectedElements, dragOffset, this.state, this.scene, snapOffset, event[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize);
4982
- this.setState({
4983
- suggestedBindings: getSuggestedBindingsForArrows(selectedElements, this),
4984
- });
5023
+ dragSelectedElements(pointerDownState, selectedElements, dragOffset, this.scene, snapOffset, event[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize);
5024
+ if (selectedElements.length !== 1 ||
5025
+ !isElbowArrow(selectedElements[0])) {
5026
+ this.setState({
5027
+ suggestedBindings: getSuggestedBindingsForArrows(selectedElements, this.scene.getNonDeletedElementsMap()),
5028
+ });
5029
+ }
5030
+ //}
4985
5031
  // We duplicate the selected element if alt is pressed on pointer move
4986
5032
  if (event.altKey && !pointerDownState.hit.hasBeenDuplicated) {
4987
5033
  // Move the currently selected elements to the top of the z index stack, and
@@ -5074,6 +5120,11 @@ class App extends React.Component {
5074
5120
  points: [...points, [dx, dy]],
5075
5121
  });
5076
5122
  }
5123
+ else if (points.length > 1 && isElbowArrow(draggingElement)) {
5124
+ mutateElbowArrow(draggingElement, this.scene, [...points.slice(0, -1), [dx, dy]], [0, 0], undefined, {
5125
+ isDragging: true,
5126
+ });
5127
+ }
5077
5128
  else if (points.length === 2) {
5078
5129
  mutateElement(draggingElement, {
5079
5130
  points: [...points.slice(0, -1), [dx, dy]],
@@ -5217,7 +5268,7 @@ class App extends React.Component {
5217
5268
  this.actionManager.executeAction(actionFinalize);
5218
5269
  }
5219
5270
  else {
5220
- const editingLinearElement = LinearElementEditor.handlePointerUp(childEvent, this.state.editingLinearElement, this.state, this);
5271
+ const editingLinearElement = LinearElementEditor.handlePointerUp(childEvent, this.state.editingLinearElement, this.state, this.scene);
5221
5272
  if (editingLinearElement !== this.state.editingLinearElement) {
5222
5273
  this.setState({
5223
5274
  editingLinearElement,
@@ -5236,11 +5287,11 @@ class App extends React.Component {
5236
5287
  }
5237
5288
  }
5238
5289
  else {
5239
- const linearElementEditor = LinearElementEditor.handlePointerUp(childEvent, this.state.selectedLinearElement, this.state, this);
5290
+ const linearElementEditor = LinearElementEditor.handlePointerUp(childEvent, this.state.selectedLinearElement, this.state, this.scene);
5240
5291
  const { startBindingElement, endBindingElement } = linearElementEditor;
5241
5292
  const element = this.scene.getElement(linearElementEditor.elementId);
5242
5293
  if (isBindingElement(element)) {
5243
- bindOrUnbindLinearElement(element, startBindingElement, endBindingElement, elementsMap);
5294
+ bindOrUnbindLinearElement(element, startBindingElement, endBindingElement, elementsMap, this.scene);
5244
5295
  }
5245
5296
  if (linearElementEditor !== this.state.selectedLinearElement) {
5246
5297
  this.setState({
@@ -5328,7 +5379,7 @@ class App extends React.Component {
5328
5379
  else if (pointerDownState.drag.hasOccurred && !multiElement) {
5329
5380
  if (isBindingEnabled(this.state) &&
5330
5381
  isBindingElement(draggingElement, false)) {
5331
- maybeBindLinearElement(draggingElement, this.state, pointerCoords, this);
5382
+ maybeBindLinearElement(draggingElement, this.state, pointerCoords, this.scene.getNonDeletedElementsMap(), this.scene.getNonDeletedElements());
5332
5383
  }
5333
5384
  this.setState({ suggestedBindings: [], startBoundElement: null });
5334
5385
  if (!activeTool.locked) {
@@ -5636,7 +5687,7 @@ class App extends React.Component {
5636
5687
  x: pointerDownState.origin.x,
5637
5688
  y: pointerDownState.origin.y,
5638
5689
  element: hitElement,
5639
- shape: this.getElementShape(hitElement),
5690
+ shape: getElementShape(hitElement, this.scene.getNonDeletedElementsMap()),
5640
5691
  threshold: this.getElementHitThreshold(),
5641
5692
  frameNameBound: isFrameLikeElement(hitElement)
5642
5693
  ? this.frameNameBoundsCache.get(hitElement)
@@ -5685,7 +5736,7 @@ class App extends React.Component {
5685
5736
  const linearElements = this.scene
5686
5737
  .getSelectedElements(this.state)
5687
5738
  .filter(isLinearElement);
5688
- bindOrUnbindLinearElements(linearElements, this, isBindingEnabled(this.state), this.state.selectedLinearElement?.selectedPointsIndices ?? []);
5739
+ bindOrUnbindLinearElements(linearElements, this.scene.getNonDeletedElementsMap(), this.scene.getNonDeletedElements(), this.scene, isBindingEnabled(this.state), this.state.selectedLinearElement?.selectedPointsIndices ?? []);
5689
5740
  }
5690
5741
  if (activeTool.type === "laser") {
5691
5742
  this.laserTrails.endPath();
@@ -6028,7 +6079,7 @@ class App extends React.Component {
6028
6079
  }
6029
6080
  };
6030
6081
  maybeSuggestBindingAtCursor = (pointerCoords) => {
6031
- const hoveredBindableElement = getHoveredElementForBinding(pointerCoords, this);
6082
+ const hoveredBindableElement = getHoveredElementForBinding(pointerCoords, this.scene.getNonDeletedElements(), this.scene.getNonDeletedElementsMap());
6032
6083
  this.setState({
6033
6084
  suggestedBindings: hoveredBindableElement != null ? [hoveredBindableElement] : [],
6034
6085
  });
@@ -6043,7 +6094,7 @@ class App extends React.Component {
6043
6094
  return;
6044
6095
  }
6045
6096
  const suggestedBindings = pointerCoords.reduce((acc, coords) => {
6046
- const hoveredBindableElement = getHoveredElementForBinding(coords, this);
6097
+ const hoveredBindableElement = getHoveredElementForBinding(coords, this.scene.getNonDeletedElements(), this.scene.getNonDeletedElementsMap(), isArrowElement(linearElement) && isElbowArrow(linearElement));
6047
6098
  if (hoveredBindableElement != null &&
6048
6099
  !isLinearElementSimpleAndAlreadyBound(linearElement, oppositeBindingBoundElement?.id, hoveredBindableElement)) {
6049
6100
  acc.push(hoveredBindableElement);
@@ -6373,8 +6424,8 @@ class App extends React.Component {
6373
6424
  }
6374
6425
  if (transformElements(pointerDownState.originalElements, transformHandleType, selectedElements, this.scene.getElementsMapIncludingDeleted(), shouldRotateWithDiscreteAngle(event), shouldResizeFromCenter(event), selectedElements.some((element) => isImageElement(element))
6375
6426
  ? !shouldMaintainAspectRatio(event)
6376
- : shouldMaintainAspectRatio(event), resizeX, resizeY, pointerDownState.resize.center.x, pointerDownState.resize.center.y)) {
6377
- const suggestedBindings = getSuggestedBindingsForArrows(selectedElements, this);
6427
+ : shouldMaintainAspectRatio(event), resizeX, resizeY, pointerDownState.resize.center.x, pointerDownState.resize.center.y, this.scene)) {
6428
+ const suggestedBindings = getSuggestedBindingsForArrows(selectedElements, this.scene.getNonDeletedElementsMap());
6378
6429
  const elementsToHighlight = new Set();
6379
6430
  selectedFrames.forEach((frame) => {
6380
6431
  getElementsInResizingFrame(this.scene.getNonDeletedElements(), frame, this.state, this.scene.getNonDeletedElementsMap()).forEach((element) => elementsToHighlight.add(element));
@@ -6611,6 +6662,12 @@ export const createTestHook = () => {
6611
6662
  return this.app?.scene.replaceAllElements(syncInvalidIndices(elements));
6612
6663
  },
6613
6664
  },
6665
+ scene: {
6666
+ configurable: true,
6667
+ get() {
6668
+ return this.app?.scene;
6669
+ },
6670
+ },
6614
6671
  });
6615
6672
  }
6616
6673
  };