@excalidraw/excalidraw 0.17.1-d9bbf1e → 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 (416) 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-XOM7LNOU.js → chunk-IT7T3AIK.js} +49 -8
  8. package/dist/browser/dev/excalidraw-assets-dev/chunk-IT7T3AIK.js.map +7 -0
  9. package/dist/browser/dev/excalidraw-assets-dev/{chunk-BLEB3M62.js → chunk-RNHSD5AR.js} +7668 -2142
  10. package/dist/browser/dev/excalidraw-assets-dev/chunk-RNHSD5AR.js.map +7 -0
  11. package/dist/browser/dev/excalidraw-assets-dev/{dist-ITJNUBZF.js → dist-DNSPZDOZ.js} +66 -32
  12. package/dist/browser/dev/excalidraw-assets-dev/dist-DNSPZDOZ.js.map +7 -0
  13. package/dist/browser/dev/excalidraw-assets-dev/{en-TR4QLF5E.js → en-XV7OZCPP.js} +8 -2
  14. package/dist/browser/dev/excalidraw-assets-dev/{image-ZGDDRBEN.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 +610 -180
  18. package/dist/browser/dev/index.css.map +3 -3
  19. package/dist/browser/dev/index.js +13306 -8006
  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-5SYIAZGL.js → chunk-PDYFZJMS.js} +5 -5
  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 +70 -51
  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-XW4JO6VX.json → en-YNVBSAIL.json} +42 -7
  39. package/dist/dev/index.css +610 -180
  40. package/dist/dev/index.css.map +3 -3
  41. package/dist/dev/index.js +22165 -16833
  42. package/dist/dev/index.js.map +4 -4
  43. package/dist/excalidraw/actions/actionAddToLibrary.d.ts +24 -9
  44. package/dist/excalidraw/actions/actionAlign.d.ts +8 -8
  45. package/dist/excalidraw/actions/actionBoundText.d.ts +20 -10
  46. package/dist/excalidraw/actions/actionBoundText.js +3 -1
  47. package/dist/excalidraw/actions/actionCanvas.d.ts +100 -40
  48. package/dist/excalidraw/actions/actionCanvas.js +1 -1
  49. package/dist/excalidraw/actions/actionClipboard.d.ts +62 -26
  50. package/dist/excalidraw/actions/actionDeleteSelected.d.ts +28 -12
  51. package/dist/excalidraw/actions/actionDeleteSelected.js +24 -5
  52. package/dist/excalidraw/actions/actionDistribute.d.ts +4 -4
  53. package/dist/excalidraw/actions/actionDuplicateSelection.d.ts +3 -3
  54. package/dist/excalidraw/actions/actionDuplicateSelection.js +1 -2
  55. package/dist/excalidraw/actions/actionElementLock.d.ts +17 -7
  56. package/dist/excalidraw/actions/actionExport.d.ts +75 -30
  57. package/dist/excalidraw/actions/actionFinalize.d.ts +17 -7
  58. package/dist/excalidraw/actions/actionFinalize.js +2 -2
  59. package/dist/excalidraw/actions/actionFlip.d.ts +4 -4
  60. package/dist/excalidraw/actions/actionFlip.js +2 -2
  61. package/dist/excalidraw/actions/actionFrame.d.ts +338 -10
  62. package/dist/excalidraw/actions/actionGroup.d.ts +324 -4
  63. package/dist/excalidraw/actions/actionHistory.d.ts +3 -3
  64. package/dist/excalidraw/actions/actionHistory.js +8 -8
  65. package/dist/excalidraw/actions/actionLinearEditor.d.ts +12 -5
  66. package/dist/excalidraw/actions/actionLinearEditor.js +21 -5
  67. package/dist/excalidraw/actions/actionLink.d.ts +8 -3
  68. package/dist/excalidraw/actions/actionMenu.d.ts +24 -9
  69. package/dist/excalidraw/actions/actionNavigate.d.ts +17 -7
  70. package/dist/excalidraw/actions/actionProperties.d.ts +476 -82
  71. package/dist/excalidraw/actions/actionProperties.js +384 -59
  72. package/dist/excalidraw/actions/actionSelectAll.d.ts +9 -4
  73. package/dist/excalidraw/actions/actionStyles.d.ts +12 -4
  74. package/dist/excalidraw/actions/actionStyles.js +3 -2
  75. package/dist/excalidraw/actions/actionTextAutoResize.d.ts +17 -0
  76. package/dist/excalidraw/actions/actionTextAutoResize.js +38 -0
  77. package/dist/excalidraw/actions/actionToggleGridMode.d.ts +9 -4
  78. package/dist/excalidraw/actions/actionToggleObjectsSnapMode.d.ts +8 -3
  79. package/dist/excalidraw/actions/actionToggleStats.d.ts +9 -3
  80. package/dist/excalidraw/actions/actionToggleStats.js +4 -3
  81. package/dist/excalidraw/actions/actionToggleViewMode.d.ts +8 -3
  82. package/dist/excalidraw/actions/actionToggleZenMode.d.ts +8 -3
  83. package/dist/excalidraw/actions/actionZindex.d.ts +8 -4
  84. package/dist/excalidraw/actions/actionZindex.js +4 -0
  85. package/dist/excalidraw/actions/manager.d.ts +3 -3
  86. package/dist/excalidraw/actions/register.d.ts +1 -1
  87. package/dist/excalidraw/actions/shortcuts.d.ts +2 -2
  88. package/dist/excalidraw/actions/types.d.ts +6 -6
  89. package/dist/excalidraw/align.d.ts +1 -1
  90. package/dist/excalidraw/analytics.js +9 -7
  91. package/dist/excalidraw/animated-trail.d.ts +2 -2
  92. package/dist/excalidraw/appState.d.ts +6 -2
  93. package/dist/excalidraw/appState.js +14 -3
  94. package/dist/excalidraw/binaryheap.d.ts +12 -0
  95. package/dist/excalidraw/binaryheap.js +93 -0
  96. package/dist/excalidraw/change.d.ts +6 -5
  97. package/dist/excalidraw/change.js +20 -11
  98. package/dist/excalidraw/charts.d.ts +1 -1
  99. package/dist/excalidraw/charts.js +0 -10
  100. package/dist/excalidraw/clients.d.ts +2 -2
  101. package/dist/excalidraw/clients.js +1 -1
  102. package/dist/excalidraw/clipboard.d.ts +3 -3
  103. package/dist/excalidraw/colors.d.ts +1 -1
  104. package/dist/excalidraw/components/Actions.d.ts +3 -3
  105. package/dist/excalidraw/components/Actions.js +14 -9
  106. package/dist/excalidraw/components/App.d.ts +14 -17
  107. package/dist/excalidraw/components/App.js +356 -217
  108. package/dist/excalidraw/components/ButtonIcon.d.ts +15 -0
  109. package/dist/excalidraw/components/ButtonIcon.js +8 -0
  110. package/dist/excalidraw/components/ButtonIconSelect.js +2 -3
  111. package/dist/excalidraw/components/ButtonSeparator.d.ts +2 -0
  112. package/dist/excalidraw/components/ButtonSeparator.js +7 -0
  113. package/dist/excalidraw/components/CheckboxItem.js +1 -1
  114. package/dist/excalidraw/components/ColorPicker/ColorInput.d.ts +1 -1
  115. package/dist/excalidraw/components/ColorPicker/ColorInput.js +1 -1
  116. package/dist/excalidraw/components/ColorPicker/ColorPicker.d.ts +4 -4
  117. package/dist/excalidraw/components/ColorPicker/ColorPicker.js +48 -80
  118. package/dist/excalidraw/components/ColorPicker/Picker.d.ts +3 -3
  119. package/dist/excalidraw/components/ColorPicker/Picker.js +1 -1
  120. package/dist/excalidraw/components/ColorPicker/PickerColorList.d.ts +1 -1
  121. package/dist/excalidraw/components/ColorPicker/PickerHeading.d.ts +1 -1
  122. package/dist/excalidraw/components/ColorPicker/ShadeList.d.ts +1 -1
  123. package/dist/excalidraw/components/ColorPicker/TopPicks.d.ts +1 -1
  124. package/dist/excalidraw/components/ColorPicker/colorPickerUtils.d.ts +2 -2
  125. package/dist/excalidraw/components/ColorPicker/colorPickerUtils.js +1 -1
  126. package/dist/excalidraw/components/ColorPicker/keyboardNavHandlers.d.ts +2 -2
  127. package/dist/excalidraw/components/ColorPicker/keyboardNavHandlers.js +1 -1
  128. package/dist/excalidraw/components/CommandPalette/CommandPalette.d.ts +1 -1
  129. package/dist/excalidraw/components/CommandPalette/CommandPalette.js +5 -5
  130. package/dist/excalidraw/components/CommandPalette/defaultCommandPaletteItems.d.ts +1 -1
  131. package/dist/excalidraw/components/CommandPalette/types.d.ts +3 -3
  132. package/dist/excalidraw/components/ConfirmDialog.d.ts +1 -1
  133. package/dist/excalidraw/components/ContextMenu.d.ts +2 -2
  134. package/dist/excalidraw/components/ContextMenu.js +2 -2
  135. package/dist/excalidraw/components/DarkModeToggle.d.ts +1 -1
  136. package/dist/excalidraw/components/DefaultSidebar.d.ts +2 -2
  137. package/dist/excalidraw/components/Dialog.js +1 -1
  138. package/dist/excalidraw/components/DialogActionButton.d.ts +1 -1
  139. package/dist/excalidraw/components/EyeDropper.d.ts +2 -2
  140. package/dist/excalidraw/components/FollowMode/FollowMode.d.ts +1 -1
  141. package/dist/excalidraw/components/FollowMode/FollowMode.js +1 -1
  142. package/dist/excalidraw/components/FontPicker/FontPicker.d.ts +21 -0
  143. package/dist/excalidraw/components/FontPicker/FontPicker.js +49 -0
  144. package/dist/excalidraw/components/FontPicker/FontPickerList.d.ts +25 -0
  145. package/dist/excalidraw/components/FontPicker/FontPickerList.js +119 -0
  146. package/dist/excalidraw/components/FontPicker/FontPickerTrigger.d.ts +7 -0
  147. package/dist/excalidraw/components/FontPicker/FontPickerTrigger.js +13 -0
  148. package/dist/excalidraw/components/FontPicker/keyboardNavHandlers.d.ts +14 -0
  149. package/dist/excalidraw/components/FontPicker/keyboardNavHandlers.js +38 -0
  150. package/dist/excalidraw/components/HelpDialog.js +2 -2
  151. package/dist/excalidraw/components/HintViewer.d.ts +1 -1
  152. package/dist/excalidraw/components/HintViewer.js +6 -3
  153. package/dist/excalidraw/components/IconPicker.js +2 -2
  154. package/dist/excalidraw/components/ImageExportDialog.d.ts +1 -1
  155. package/dist/excalidraw/components/InitializeApp.d.ts +2 -2
  156. package/dist/excalidraw/components/JSONExportDialog.d.ts +3 -3
  157. package/dist/excalidraw/components/LayerUI.d.ts +4 -4
  158. package/dist/excalidraw/components/LayerUI.js +10 -7
  159. package/dist/excalidraw/components/LibraryMenu.d.ts +2 -2
  160. package/dist/excalidraw/components/LibraryMenuBrowseButton.d.ts +1 -1
  161. package/dist/excalidraw/components/LibraryMenuControlButtons.d.ts +1 -1
  162. package/dist/excalidraw/components/LibraryMenuHeaderContent.d.ts +2 -2
  163. package/dist/excalidraw/components/LibraryMenuItems.d.ts +1 -1
  164. package/dist/excalidraw/components/LibraryMenuSection.d.ts +5 -4
  165. package/dist/excalidraw/components/LibraryUnit.d.ts +2 -2
  166. package/dist/excalidraw/components/LoadingMessage.d.ts +1 -1
  167. package/dist/excalidraw/components/MagicSettings.js +2 -2
  168. package/dist/excalidraw/components/MobileMenu.d.ts +3 -3
  169. package/dist/excalidraw/components/MobileMenu.js +2 -6
  170. package/dist/excalidraw/components/Modal.d.ts +1 -1
  171. package/dist/excalidraw/components/OverwriteConfirm/OverwriteConfirmState.d.ts +1 -1
  172. package/dist/excalidraw/components/PasteChartDialog.d.ts +1 -1
  173. package/dist/excalidraw/components/PasteChartDialog.js +1 -1
  174. package/dist/excalidraw/components/PropertiesPopover.d.ts +15 -0
  175. package/dist/excalidraw/components/PropertiesPopover.js +31 -0
  176. package/dist/excalidraw/components/PublishLibrary.d.ts +1 -1
  177. package/dist/excalidraw/components/QuickSearch.d.ts +9 -0
  178. package/dist/excalidraw/components/QuickSearch.js +8 -0
  179. package/dist/excalidraw/components/SVGLayer.d.ts +1 -1
  180. package/dist/excalidraw/components/ScrollableList.d.ts +9 -0
  181. package/dist/excalidraw/components/ScrollableList.js +8 -0
  182. package/dist/excalidraw/components/Sidebar/Sidebar.js +1 -1
  183. package/dist/excalidraw/components/Sidebar/SidebarTab.d.ts +1 -1
  184. package/dist/excalidraw/components/Sidebar/SidebarTabTrigger.d.ts +1 -1
  185. package/dist/excalidraw/components/Sidebar/SidebarTrigger.d.ts +1 -1
  186. package/dist/excalidraw/components/Sidebar/common.d.ts +1 -1
  187. package/dist/excalidraw/components/Stats/Angle.d.ts +12 -0
  188. package/dist/excalidraw/components/Stats/Angle.js +53 -0
  189. package/dist/excalidraw/components/Stats/Collapsible.d.ts +9 -0
  190. package/dist/excalidraw/components/Stats/Collapsible.js +12 -0
  191. package/dist/excalidraw/components/Stats/Dimension.d.ts +12 -0
  192. package/dist/excalidraw/components/Stats/Dimension.js +68 -0
  193. package/dist/excalidraw/components/Stats/DragInput.d.ts +32 -0
  194. package/dist/excalidraw/components/Stats/DragInput.js +174 -0
  195. package/dist/excalidraw/components/Stats/FontSize.d.ts +12 -0
  196. package/dist/excalidraw/components/Stats/FontSize.js +50 -0
  197. package/dist/excalidraw/components/Stats/MultiAngle.d.ts +12 -0
  198. package/dist/excalidraw/components/Stats/MultiAngle.js +66 -0
  199. package/dist/excalidraw/components/Stats/MultiDimension.d.ts +15 -0
  200. package/dist/excalidraw/components/Stats/MultiDimension.js +199 -0
  201. package/dist/excalidraw/components/Stats/MultiFontSize.d.ts +13 -0
  202. package/dist/excalidraw/components/Stats/MultiFontSize.js +72 -0
  203. package/dist/excalidraw/components/Stats/MultiPosition.d.ts +15 -0
  204. package/dist/excalidraw/components/Stats/MultiPosition.js +101 -0
  205. package/dist/excalidraw/components/Stats/Position.d.ts +13 -0
  206. package/dist/excalidraw/components/Stats/Position.js +40 -0
  207. package/dist/excalidraw/components/Stats/index.d.ts +16 -0
  208. package/dist/excalidraw/components/Stats/index.js +79 -0
  209. package/dist/excalidraw/components/Stats/utils.d.ts +26 -0
  210. package/dist/excalidraw/components/Stats/utils.js +162 -0
  211. package/dist/excalidraw/components/TTDDialog/MermaidToExcalidraw.d.ts +1 -1
  212. package/dist/excalidraw/components/TTDDialog/TTDDialog.js +2 -2
  213. package/dist/excalidraw/components/TTDDialog/TTDDialogInput.d.ts +1 -1
  214. package/dist/excalidraw/components/TTDDialog/TTDDialogPanel.d.ts +1 -1
  215. package/dist/excalidraw/components/TTDDialog/TTDDialogPanels.d.ts +1 -1
  216. package/dist/excalidraw/components/TTDDialog/TTDDialogTabs.d.ts +1 -1
  217. package/dist/excalidraw/components/TTDDialog/TTDDialogTrigger.d.ts +1 -1
  218. package/dist/excalidraw/components/TTDDialog/common.d.ts +5 -5
  219. package/dist/excalidraw/components/TTDDialog/common.js +3 -7
  220. package/dist/excalidraw/components/TextField.d.ts +1 -1
  221. package/dist/excalidraw/components/Toast.d.ts +1 -1
  222. package/dist/excalidraw/components/ToolButton.d.ts +3 -2
  223. package/dist/excalidraw/components/Trans.d.ts +1 -1
  224. package/dist/excalidraw/components/UserList.d.ts +1 -1
  225. package/dist/excalidraw/components/UserList.js +22 -22
  226. package/dist/excalidraw/components/canvases/InteractiveCanvas.d.ts +3 -2
  227. package/dist/excalidraw/components/canvases/InteractiveCanvas.js +4 -2
  228. package/dist/excalidraw/components/canvases/StaticCanvas.d.ts +2 -2
  229. package/dist/excalidraw/components/canvases/StaticCanvas.js +3 -2
  230. package/dist/excalidraw/components/dropdownMenu/DropdownMenu.d.ts +12 -3
  231. package/dist/excalidraw/components/dropdownMenu/DropdownMenuItem.d.ts +24 -4
  232. package/dist/excalidraw/components/dropdownMenu/DropdownMenuItem.js +55 -14
  233. package/dist/excalidraw/components/dropdownMenu/DropdownMenuItemContent.d.ts +2 -1
  234. package/dist/excalidraw/components/dropdownMenu/DropdownMenuItemContent.js +2 -2
  235. package/dist/excalidraw/components/dropdownMenu/common.d.ts +1 -1
  236. package/dist/excalidraw/components/dropdownMenu/common.js +3 -2
  237. package/dist/excalidraw/components/footer/Footer.d.ts +2 -2
  238. package/dist/excalidraw/components/hyperlink/Hyperlink.d.ts +2 -2
  239. package/dist/excalidraw/components/hyperlink/helpers.d.ts +3 -3
  240. package/dist/excalidraw/components/icons.d.ts +10 -2
  241. package/dist/excalidraw/components/icons.js +29 -6
  242. package/dist/excalidraw/components/main-menu/MainMenu.d.ts +12 -3
  243. package/dist/excalidraw/components/welcome-screen/WelcomeScreen.Center.js +2 -2
  244. package/dist/excalidraw/components/welcome-screen/WelcomeScreen.Hints.js +3 -3
  245. package/dist/excalidraw/constants.d.ts +24 -3
  246. package/dist/excalidraw/constants.js +28 -4
  247. package/dist/excalidraw/context/ui-appState.d.ts +1 -1
  248. package/dist/excalidraw/cursor.d.ts +1 -1
  249. package/dist/excalidraw/data/EditorLocalStorage.d.ts +2 -2
  250. package/dist/excalidraw/data/blob.d.ts +5 -5
  251. package/dist/excalidraw/data/filesystem.d.ts +2 -1
  252. package/dist/excalidraw/data/index.d.ts +4 -4
  253. package/dist/excalidraw/data/json.d.ts +3 -3
  254. package/dist/excalidraw/data/library.d.ts +3 -3
  255. package/dist/excalidraw/data/magic.d.ts +3 -3
  256. package/dist/excalidraw/data/reconcile.d.ts +3 -3
  257. package/dist/excalidraw/data/reconcile.js +18 -1
  258. package/dist/excalidraw/data/resave.d.ts +2 -2
  259. package/dist/excalidraw/data/restore.d.ts +3 -3
  260. package/dist/excalidraw/data/restore.js +58 -9
  261. package/dist/excalidraw/data/transform.d.ts +3 -3
  262. package/dist/excalidraw/data/transform.js +8 -5
  263. package/dist/excalidraw/data/types.d.ts +3 -3
  264. package/dist/excalidraw/element/ElementCanvasButtons.d.ts +1 -1
  265. package/dist/excalidraw/element/binding.d.ts +29 -10
  266. package/dist/excalidraw/element/binding.js +303 -71
  267. package/dist/excalidraw/element/bounds.d.ts +3 -3
  268. package/dist/excalidraw/element/collision.d.ts +4 -4
  269. package/dist/excalidraw/element/collision.js +5 -2
  270. package/dist/excalidraw/element/containerCache.d.ts +1 -1
  271. package/dist/excalidraw/element/dragElements.d.ts +6 -6
  272. package/dist/excalidraw/element/dragElements.js +39 -5
  273. package/dist/excalidraw/element/embeddable.d.ts +11 -6
  274. package/dist/excalidraw/element/heading.d.ts +11 -0
  275. package/dist/excalidraw/element/heading.js +81 -0
  276. package/dist/excalidraw/element/image.d.ts +2 -2
  277. package/dist/excalidraw/element/index.d.ts +2 -2
  278. package/dist/excalidraw/element/index.js +1 -1
  279. package/dist/excalidraw/element/linearElementEditor.d.ts +27 -16
  280. package/dist/excalidraw/element/linearElementEditor.js +133 -56
  281. package/dist/excalidraw/element/mutateElement.d.ts +3 -3
  282. package/dist/excalidraw/element/mutateElement.js +5 -3
  283. package/dist/excalidraw/element/newElement.d.ts +12 -10
  284. package/dist/excalidraw/element/newElement.js +31 -16
  285. package/dist/excalidraw/element/resizeElements.d.ts +15 -6
  286. package/dist/excalidraw/element/resizeElements.js +122 -46
  287. package/dist/excalidraw/element/resizeTest.d.ts +4 -4
  288. package/dist/excalidraw/element/resizeTest.js +2 -4
  289. package/dist/excalidraw/element/routing.d.ts +13 -0
  290. package/dist/excalidraw/element/routing.js +641 -0
  291. package/dist/excalidraw/element/showSelectedShapeActions.d.ts +2 -2
  292. package/dist/excalidraw/element/sizeHelpers.d.ts +2 -2
  293. package/dist/excalidraw/element/sortElements.d.ts +1 -1
  294. package/dist/excalidraw/element/textElement.d.ts +6 -28
  295. package/dist/excalidraw/element/textElement.js +64 -112
  296. package/dist/excalidraw/element/textWysiwyg.d.ts +12 -6
  297. package/dist/excalidraw/element/textWysiwyg.js +75 -62
  298. package/dist/excalidraw/element/transformHandles.d.ts +3 -3
  299. package/dist/excalidraw/element/transformHandles.js +7 -12
  300. package/dist/excalidraw/element/typeChecks.d.ts +7 -4
  301. package/dist/excalidraw/element/typeChecks.js +17 -0
  302. package/dist/excalidraw/element/types.d.ts +22 -4
  303. package/dist/excalidraw/emitter.d.ts +1 -1
  304. package/dist/excalidraw/fonts/ExcalidrawFont.d.ts +21 -0
  305. package/dist/excalidraw/fonts/ExcalidrawFont.js +112 -0
  306. package/dist/excalidraw/fonts/index.d.ts +58 -0
  307. package/dist/excalidraw/fonts/index.js +240 -0
  308. package/dist/excalidraw/fonts/metadata.d.ts +36 -0
  309. package/dist/excalidraw/fonts/metadata.js +91 -0
  310. package/dist/excalidraw/fractionalIndex.d.ts +12 -5
  311. package/dist/excalidraw/fractionalIndex.js +40 -10
  312. package/dist/excalidraw/frame.d.ts +4 -4
  313. package/dist/excalidraw/frame.js +3 -3
  314. package/dist/excalidraw/gatransforms.d.ts +1 -1
  315. package/dist/excalidraw/gesture.d.ts +1 -1
  316. package/dist/excalidraw/groups.d.ts +4 -4
  317. package/dist/excalidraw/history.d.ts +8 -7
  318. package/dist/excalidraw/history.js +8 -8
  319. package/dist/excalidraw/hooks/useEmitter.d.ts +1 -1
  320. package/dist/excalidraw/hooks/useLibraryItemSvg.d.ts +1 -1
  321. package/dist/excalidraw/i18n.d.ts +1 -1
  322. package/dist/excalidraw/index.d.ts +2 -2
  323. package/dist/excalidraw/index.js +3 -3
  324. package/dist/excalidraw/jotai.d.ts +1 -1
  325. package/dist/excalidraw/laser-trails.d.ts +3 -2
  326. package/dist/excalidraw/locales/en.json +42 -7
  327. package/dist/excalidraw/math.d.ts +47 -2
  328. package/dist/excalidraw/math.js +116 -0
  329. package/dist/excalidraw/mermaid.d.ts +2 -0
  330. package/dist/excalidraw/mermaid.js +29 -0
  331. package/dist/excalidraw/points.d.ts +1 -1
  332. package/dist/excalidraw/queue.d.ts +1 -1
  333. package/dist/excalidraw/renderer/helpers.d.ts +2 -2
  334. package/dist/excalidraw/renderer/interactiveScene.d.ts +2 -2
  335. package/dist/excalidraw/renderer/interactiveScene.js +64 -22
  336. package/dist/excalidraw/renderer/renderElement.d.ts +6 -4
  337. package/dist/excalidraw/renderer/renderElement.js +78 -58
  338. package/dist/excalidraw/renderer/renderSnaps.d.ts +1 -1
  339. package/dist/excalidraw/renderer/staticScene.d.ts +1 -1
  340. package/dist/excalidraw/renderer/staticSvgScene.d.ts +4 -4
  341. package/dist/excalidraw/renderer/staticSvgScene.js +2 -1
  342. package/dist/excalidraw/scene/Renderer.d.ts +4 -4
  343. package/dist/excalidraw/scene/Renderer.js +2 -3
  344. package/dist/excalidraw/scene/Scene.d.ts +16 -7
  345. package/dist/excalidraw/scene/Scene.js +26 -11
  346. package/dist/excalidraw/scene/Shape.d.ts +1 -1
  347. package/dist/excalidraw/scene/Shape.js +56 -5
  348. package/dist/excalidraw/scene/ShapeCache.d.ts +4 -4
  349. package/dist/excalidraw/scene/comparisons.d.ts +3 -2
  350. package/dist/excalidraw/scene/comparisons.js +1 -1
  351. package/dist/excalidraw/scene/export.d.ts +4 -3
  352. package/dist/excalidraw/scene/export.js +34 -36
  353. package/dist/excalidraw/scene/scroll.d.ts +2 -2
  354. package/dist/excalidraw/scene/scrollbars.d.ts +3 -3
  355. package/dist/excalidraw/scene/selection.d.ts +2 -2
  356. package/dist/excalidraw/scene/types.d.ts +6 -8
  357. package/dist/excalidraw/scene/zoom.d.ts +1 -1
  358. package/dist/excalidraw/shapes.d.ts +8 -0
  359. package/dist/excalidraw/shapes.js +57 -0
  360. package/dist/excalidraw/snapping.d.ts +4 -4
  361. package/dist/excalidraw/snapping.js +2 -1
  362. package/dist/excalidraw/store.d.ts +34 -4
  363. package/dist/excalidraw/store.js +27 -0
  364. package/dist/excalidraw/types.d.ts +32 -21
  365. package/dist/excalidraw/utils.d.ts +15 -5
  366. package/dist/excalidraw/utils.js +22 -0
  367. package/dist/excalidraw/zindex.d.ts +2 -2
  368. package/dist/prod/CascadiaCode-Regular-TMZI7IJ5.woff2 +0 -0
  369. package/dist/prod/ComicShanns-Regular-6TOETDFT.woff2 +0 -0
  370. package/dist/prod/Excalifont-Regular-CPKEUDVM.woff2 +0 -0
  371. package/dist/prod/LiberationSans-Regular-ZQD73GJM.woff2 +0 -0
  372. package/dist/prod/Virgil-Regular-YHAB2VGJ.woff2 +0 -0
  373. package/dist/prod/{en-XW4JO6VX.json → en-YNVBSAIL.json} +42 -7
  374. package/dist/prod/index.css +1 -1
  375. package/dist/prod/index.js +49 -53
  376. package/dist/utils/bbox.d.ts +2 -2
  377. package/dist/utils/collision.d.ts +1 -1
  378. package/dist/utils/export.d.ts +4 -3
  379. package/dist/utils/export.js +2 -1
  380. package/dist/utils/geometry/geometry.d.ts +3 -2
  381. package/dist/utils/geometry/geometry.js +5 -1
  382. package/dist/utils/geometry/shape.d.ts +1 -1
  383. package/dist/utils/index.d.ts +1 -0
  384. package/dist/utils/index.js +1 -0
  385. package/dist/utils/withinBounds.d.ts +1 -1
  386. package/history.ts +13 -6
  387. package/package.json +2 -2
  388. package/dist/browser/dev/Cascadia-CYPE3OJC.woff2 +0 -0
  389. package/dist/browser/dev/Virgil-UZN6MUT6.woff2 +0 -0
  390. package/dist/browser/dev/excalidraw-assets-dev/chunk-BLEB3M62.js.map +0 -7
  391. package/dist/browser/dev/excalidraw-assets-dev/chunk-XOM7LNOU.js.map +0 -7
  392. package/dist/browser/dev/excalidraw-assets-dev/dist-ITJNUBZF.js.map +0 -7
  393. package/dist/browser/prod/Cascadia-CYPE3OJC.woff2 +0 -0
  394. package/dist/browser/prod/Virgil-UZN6MUT6.woff2 +0 -0
  395. package/dist/browser/prod/excalidraw-assets/chunk-UWBW5SR2.js +0 -55
  396. package/dist/browser/prod/excalidraw-assets/dist-54276HPL.js +0 -6
  397. package/dist/browser/prod/excalidraw-assets/en-7GPZE2Y2.js +0 -1
  398. package/dist/browser/prod/excalidraw-assets/image-35KQQ5EN.js +0 -1
  399. package/dist/dev/Cascadia-CYPE3OJC.woff2 +0 -0
  400. package/dist/dev/Virgil-UZN6MUT6.woff2 +0 -0
  401. package/dist/excalidraw/components/Stats.d.ts +0 -11
  402. package/dist/excalidraw/components/Stats.js +0 -13
  403. package/dist/excalidraw/scene/Fonts.d.ts +0 -21
  404. package/dist/excalidraw/scene/Fonts.js +0 -72
  405. package/dist/prod/Cascadia-CYPE3OJC.woff2 +0 -0
  406. package/dist/prod/Virgil-UZN6MUT6.woff2 +0 -0
  407. /package/dist/browser/dev/{Assistant-Bold-ZDZZ6JHA.woff2 → excalidraw-assets-dev/Assistant-Bold-ZDZZ6JHA.woff2} +0 -0
  408. /package/dist/browser/dev/{Assistant-Medium-DZ25RZU3.woff2 → excalidraw-assets-dev/Assistant-Medium-DZ25RZU3.woff2} +0 -0
  409. /package/dist/browser/dev/{Assistant-Regular-PLF2XOGW.woff2 → excalidraw-assets-dev/Assistant-Regular-PLF2XOGW.woff2} +0 -0
  410. /package/dist/browser/dev/{Assistant-SemiBold-CZ5MX6FK.woff2 → excalidraw-assets-dev/Assistant-SemiBold-CZ5MX6FK.woff2} +0 -0
  411. /package/dist/browser/dev/excalidraw-assets-dev/{en-TR4QLF5E.js.map → en-XV7OZCPP.js.map} +0 -0
  412. /package/dist/browser/dev/excalidraw-assets-dev/{image-ZGDDRBEN.js.map → image-77HZYGLG.js.map} +0 -0
  413. /package/dist/browser/prod/{Assistant-Bold-ZDZZ6JHA.woff2 → excalidraw-assets/Assistant-Bold-ZDZZ6JHA.woff2} +0 -0
  414. /package/dist/browser/prod/{Assistant-Medium-DZ25RZU3.woff2 → excalidraw-assets/Assistant-Medium-DZ25RZU3.woff2} +0 -0
  415. /package/dist/browser/prod/{Assistant-Regular-PLF2XOGW.woff2 → excalidraw-assets/Assistant-Regular-PLF2XOGW.woff2} +0 -0
  416. /package/dist/browser/prod/{Assistant-SemiBold-CZ5MX6FK.woff2 → excalidraw-assets/Assistant-SemiBold-CZ5MX6FK.woff2} +0 -0
@@ -10,17 +10,18 @@ import { ActionManager } from "../actions/manager";
10
10
  import { actions } from "../actions/register";
11
11
  import { trackEvent } from "../analytics";
12
12
  import { getDefaultAppState, isEraserActive, isHandToolActive, } from "../appState";
13
- import { copyTextToSystemClipboard, parseClipboard, } from "../clipboard";
14
- 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, } from "../constants";
13
+ import { copyTextToSystemClipboard, parseClipboard } from "../clipboard";
14
+ import { ARROW_TYPE } from "../constants";
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";
15
16
  import { exportCanvas, loadFromBlob } from "../data";
16
17
  import Library, { distributeLibraryItemsOnSquareGrid } from "../data/library";
17
18
  import { restore, restoreElements } from "../data/restore";
18
- import { dragNewElement, dragSelectedElements, duplicateElement, getCommonBounds, getCursorForResizingElement, getDragOffsetXY, getElementWithTransformHandleType, getNormalizedDimensions, getResizeArrowDirection, getResizeOffsetXY, getLockedLinearCursorAlignSize, getTransformHandleTypeFromCoords, isInvisiblySmallElement, isNonDeletedElement, isTextElement, newElement, newLinearElement, newTextElement, newImageElement, transformElements, updateTextElement, redrawTextBoundingBox, getElementAbsoluteCoords, } from "../element";
19
+ import { dragNewElement, dragSelectedElements, duplicateElement, getCommonBounds, getCursorForResizingElement, getDragOffsetXY, getElementWithTransformHandleType, getNormalizedDimensions, getResizeArrowDirection, getResizeOffsetXY, getLockedLinearCursorAlignSize, getTransformHandleTypeFromCoords, isInvisiblySmallElement, isNonDeletedElement, isTextElement, newElement, newLinearElement, newTextElement, newImageElement, transformElements, refreshTextDimensions, redrawTextBoundingBox, getElementAbsoluteCoords, } from "../element";
19
20
  import { bindOrUnbindLinearElement, bindOrUnbindLinearElements, fixBindingsAfterDeletion, fixBindingsAfterDuplication, getHoveredElementForBinding, isBindingEnabled, isLinearElementSimpleAndAlreadyBound, maybeBindLinearElement, shouldEnableBindingForPointerEvent, updateBoundElements, getSuggestedBindingsForArrows, } from "../element/binding";
20
21
  import { LinearElementEditor } from "../element/linearElementEditor";
21
22
  import { mutateElement, newElementWith } from "../element/mutateElement";
22
- import { deepCopyElement, duplicateElements, newFrameElement, newFreeDrawElement, newEmbeddableElement, newMagicFrameElement, newIframeElement, } from "../element/newElement";
23
- 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";
24
25
  import { getCenter, getDistance } from "../gesture";
25
26
  import { editGroupForSelectedElement, getElementsInGroup, getSelectedGroupIdForElement, getSelectedGroupIds, isElementInGroup, isSelectedViaGroup, selectGroupsForSelectedElements, } from "../groups";
26
27
  import { History } from "../history";
@@ -31,12 +32,12 @@ import { distance2d, getCornerRadius, getGridPoint, isPathALoop, } from "../math
31
32
  import { calculateScrollCenter, getElementsWithinSelection, getNormalizedZoom, getSelectedElements, hasBackground, isSomeElementSelected, } from "../scene";
32
33
  import Scene from "../scene/Scene";
33
34
  import { getStateForZoom } from "../scene/zoom";
34
- import { findShapeByKey } from "../shapes";
35
- 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";
36
37
  import { isPointInShape } from "../../utils/collision";
37
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";
38
39
  import { createSrcDoc, embeddableURLValidator, maybeParseEmbedSrc, getEmbedLink, } from "../element/embeddable";
39
- import { ContextMenu, CONTEXT_MENU_SEPARATOR, } from "./ContextMenu";
40
+ import { ContextMenu, CONTEXT_MENU_SEPARATOR } from "./ContextMenu";
40
41
  import LayerUI from "./LayerUI";
41
42
  import { Toast } from "./Toast";
42
43
  import { actionToggleViewMode } from "../actions/actionToggleViewMode";
@@ -44,12 +45,12 @@ import { dataURLToFile, generateIdFromFile, getDataURL, getFileFromEvent, ImageU
44
45
  import { getInitializedImageElements, loadHTMLImageElement, normalizeSVG, updateImageCache as _updateImageCache, } from "../element/image";
45
46
  import throttle from "lodash.throttle";
46
47
  import { fileOpen } from "../data/filesystem";
47
- import { bindTextToShapeAfterDuplication, getApproxMinLineHeight, getApproxMinLineWidth, getBoundTextElement, getContainerCenter, getContainerElement, getDefaultLineHeight, getLineHeightInPx, isMeasureTextSupported, isValidTextContainer, } from "../element/textElement";
48
+ import { bindTextToShapeAfterDuplication, getApproxMinLineHeight, getApproxMinLineWidth, getBoundTextElement, getContainerCenter, getContainerElement, getLineHeightInPx, getMinTextElementWidth, isMeasureTextSupported, isValidTextContainer, measureText, wrapText, } from "../element/textElement";
48
49
  import { showHyperlinkTooltip, hideHyperlinkToolip, Hyperlink, } from "../components/hyperlink/Hyperlink";
49
50
  import { isLocalLink, normalizeLink, toValidURL } from "../data/url";
50
51
  import { shouldShowBoundingBox } from "../element/transformHandles";
51
52
  import { actionUnlockAllElements } from "../actions/actionElementLock";
52
- import { Fonts } from "../scene/Fonts";
53
+ import { Fonts, getLineHeight } from "../fonts";
53
54
  import { getFrameChildren, isCursorInFrame, bindElementsToFramesAfterDuplication, addElementsToFrame, replaceAllElementsInFrame, removeElementsFromFrame, getElementsInResizingFrame, getElementsInNewFrame, getContainingFrame, elementOverlapsWithFrame, updateFrameMembershipOfSelectedElements, isElementInFrame, getFrameLikeTitle, getElementsOverlappingFrame, filterElementsEligibleAsFrameChildren, } from "../frame";
54
55
  import { excludeElementsInFramesFromSelection, makeNextSelectedElementIds, } from "../scene/selection";
55
56
  import { actionPaste } from "../actions/actionClipboard";
@@ -62,7 +63,7 @@ import { getSnapLinesAtPointer, snapDraggedElements, isActiveToolNonLinearSnappa
62
63
  import { actionWrapTextInContainer } from "../actions/actionBoundText";
63
64
  import BraveMeasureTextError from "./BraveMeasureTextError";
64
65
  import { activeEyeDropperAtom } from "./EyeDropper";
65
- import { convertToExcalidrawElements, } from "../data/transform";
66
+ import { convertToExcalidrawElements } from "../data/transform";
66
67
  import { isSidebarDockedAtom } from "./Sidebar/Sidebar";
67
68
  import { StaticCanvas, InteractiveCanvas } from "./canvases";
68
69
  import { Renderer } from "../scene/Renderer";
@@ -84,12 +85,16 @@ import { AnimatedTrail } from "../animated-trail";
84
85
  import { LaserTrails } from "../laser-trails";
85
86
  import { withBatchedUpdates, withBatchedUpdatesThrottled } from "../reactUtils";
86
87
  import { getRenderOpacity } from "../renderer/renderElement";
87
- import { hitElementBoundText, hitElementBoundingBoxOnly, hitElementItself, shouldTestInside, } from "../element/collision";
88
+ import { hitElementBoundText, hitElementBoundingBoxOnly, hitElementItself, } from "../element/collision";
88
89
  import { textWysiwyg } from "../element/textWysiwyg";
89
90
  import { isOverScrollBars } from "../scene/scrollbars";
90
91
  import { syncInvalidIndices, syncMovedIndices } from "../fractionalIndex";
91
92
  import { isPointHittingLink, isPointHittingLinkIcon, } from "./hyperlink/helpers";
92
93
  import { getShortcutFromShortcutName } from "../actions/shortcuts";
94
+ import { actionTextAutoResize } from "../actions/actionTextAutoResize";
95
+ import { getVisibleSceneBounds } from "../element/bounds";
96
+ import { isMaybeMermaidDefinition } from "../mermaid";
97
+ import { mutateElbowArrow } from "../element/routing";
93
98
  const AppContext = React.createContext(null);
94
99
  const AppPropsContext = React.createContext(null);
95
100
  const deviceContextInitialValue = {
@@ -162,8 +167,8 @@ class App extends React.Component {
162
167
  device = deviceContextInitialValue;
163
168
  excalidrawContainerRef = React.createRef();
164
169
  scene;
165
- renderer;
166
170
  fonts;
171
+ renderer;
167
172
  resizeObserver;
168
173
  nearestScrollableContainer;
169
174
  library;
@@ -286,10 +291,7 @@ class App extends React.Component {
286
291
  container: this.excalidrawContainerRef.current,
287
292
  id: this.id,
288
293
  };
289
- this.fonts = new Fonts({
290
- scene: this.scene,
291
- onSceneUpdated: this.onSceneUpdated,
292
- });
294
+ this.fonts = new Fonts({ scene: this.scene });
293
295
  this.history = new History();
294
296
  this.actionManager.registerAll(actions);
295
297
  this.actionManager.registerAction(createUndoAction(this.history, this.store));
@@ -444,7 +446,7 @@ class App extends React.Component {
444
446
  return false;
445
447
  });
446
448
  if (updated) {
447
- this.scene.informMutation();
449
+ this.scene.triggerUpdate();
448
450
  }
449
451
  // GC
450
452
  this.iFrameRefs.forEach((ref, id) => {
@@ -700,15 +702,7 @@ class App extends React.Component {
700
702
  return null;
701
703
  }
702
704
  const isDarkTheme = this.state.theme === THEME.DARK;
703
- let frameIndex = 0;
704
- let magicFrameIndex = 0;
705
705
  return this.scene.getNonDeletedFramesLikes().map((f) => {
706
- if (isFrameElement(f)) {
707
- frameIndex++;
708
- }
709
- else {
710
- magicFrameIndex++;
711
- }
712
706
  if (!isElementInViewport(f, this.canvas.width / window.devicePixelRatio, this.canvas.height / window.devicePixelRatio, {
713
707
  offsetLeft: this.state.offsetLeft,
714
708
  offsetTop: this.state.offsetTop,
@@ -726,7 +720,7 @@ class App extends React.Component {
726
720
  this.setState({ editingFrame: null });
727
721
  };
728
722
  let frameNameJSX;
729
- const frameName = getFrameLikeTitle(f, isFrameElement(f) ? frameIndex : magicFrameIndex);
723
+ const frameName = getFrameLikeTitle(f);
730
724
  if (f.id === this.state.editingFrame) {
731
725
  const frameNameInEdit = frameName;
732
726
  frameNameJSX = (_jsx("input", { autoFocus: true, value: frameNameInEdit, onChange: (e) => {
@@ -803,9 +797,9 @@ class App extends React.Component {
803
797
  render() {
804
798
  const selectedElements = this.scene.getSelectedElements(this.state);
805
799
  const { renderTopRightUI, renderCustomStats } = this.props;
806
- const versionNonce = this.scene.getVersionNonce();
800
+ const sceneNonce = this.scene.getSceneNonce();
807
801
  const { elementsMap, visibleElements } = this.renderer.getRenderableElements({
808
- versionNonce,
802
+ sceneNonce,
809
803
  zoom: this.state.zoom,
810
804
  offsetLeft: this.state.offsetLeft,
811
805
  offsetTop: this.state.offsetTop,
@@ -874,14 +868,14 @@ class App extends React.Component {
874
868
  this.focusContainer();
875
869
  callback?.();
876
870
  });
877
- } })), _jsx(StaticCanvas, { canvas: this.canvas, rc: this.rc, elementsMap: elementsMap, allElementsMap: allElementsMap, visibleElements: visibleElements, versionNonce: versionNonce, selectionNonce: this.state.selectionElement?.versionNonce, scale: window.devicePixelRatio, appState: this.state, renderConfig: {
871
+ } })), _jsx(StaticCanvas, { canvas: this.canvas, rc: this.rc, elementsMap: elementsMap, allElementsMap: allElementsMap, visibleElements: visibleElements, sceneNonce: sceneNonce, selectionNonce: this.state.selectionElement?.versionNonce, scale: window.devicePixelRatio, appState: this.state, renderConfig: {
878
872
  imageCache: this.imageCache,
879
873
  isExporting: false,
880
874
  renderGrid: true,
881
875
  canvasBackgroundColor: this.state.viewBackgroundColor,
882
876
  embedsValidationStatus: this.embedsValidationStatus,
883
877
  elementsPendingErasure: this.elementsPendingErasure,
884
- } }), _jsx(InteractiveCanvas, { containerRef: this.excalidrawContainerRef, canvas: this.interactiveCanvas, elementsMap: elementsMap, visibleElements: visibleElements, selectedElements: selectedElements, versionNonce: versionNonce, selectionNonce: this.state.selectionElement?.versionNonce, scale: window.devicePixelRatio, appState: this.state, device: this.device, renderInteractiveSceneCallback: this.renderInteractiveSceneCallback, handleCanvasRef: this.handleInteractiveCanvasRef, onContextMenu: this.handleCanvasContextMenu, onPointerMove: this.handleCanvasPointerMove, onPointerUp: this.handleCanvasPointerUp, onPointerCancel: this.removePointer, onTouchMove: this.handleTouchMove, onPointerDown: this.handleCanvasPointerDown, onDoubleClick: this.handleCanvasDoubleClick }), this.state.userToFollow && (_jsx(FollowMode, { width: this.state.width, height: this.state.height, userToFollow: this.state.userToFollow, onDisconnect: this.maybeUnfollowRemoteUser })), this.renderFrameNames()] }), this.renderEmbeddables()] }) }) }) }) }) }) }) }));
878
+ } }), _jsx(InteractiveCanvas, { containerRef: this.excalidrawContainerRef, canvas: this.interactiveCanvas, elementsMap: elementsMap, visibleElements: visibleElements, allElementsMap: allElementsMap, selectedElements: selectedElements, sceneNonce: sceneNonce, selectionNonce: this.state.selectionElement?.versionNonce, scale: window.devicePixelRatio, appState: this.state, device: this.device, renderInteractiveSceneCallback: this.renderInteractiveSceneCallback, handleCanvasRef: this.handleInteractiveCanvasRef, onContextMenu: this.handleCanvasContextMenu, onPointerMove: this.handleCanvasPointerMove, onPointerUp: this.handleCanvasPointerUp, onPointerCancel: this.removePointer, onTouchMove: this.handleTouchMove, onPointerDown: this.handleCanvasPointerDown, onDoubleClick: this.handleCanvasDoubleClick }), this.state.userToFollow && (_jsx(FollowMode, { width: this.state.width, height: this.state.height, userToFollow: this.state.userToFollow, onDisconnect: this.maybeUnfollowRemoteUser })), this.renderFrameNames()] }), this.renderEmbeddables()] }) }) }) }) }) }) }) }));
885
879
  }
886
880
  focusContainer = () => {
887
881
  this.excalidrawContainerRef.current?.focus();
@@ -931,7 +925,7 @@ class App extends React.Component {
931
925
  mutateElement(frameElement, { customData: { generationData: data } }, false);
932
926
  }
933
927
  this.magicGenerations.set(frameElement.id, data);
934
- this.onSceneUpdated();
928
+ this.triggerRender();
935
929
  };
936
930
  getTextFromElements(elements) {
937
931
  const text = elements
@@ -1180,10 +1174,24 @@ class App extends React.Component {
1180
1174
  keepOpenOnAlt: false,
1181
1175
  });
1182
1176
  };
1177
+ dismissLinearEditor = () => {
1178
+ setTimeout(() => {
1179
+ this.setState({
1180
+ editingLinearElement: null,
1181
+ });
1182
+ });
1183
+ };
1183
1184
  syncActionResult = withBatchedUpdates((actionResult) => {
1184
1185
  if (this.unmounted || actionResult === false) {
1185
1186
  return;
1186
1187
  }
1188
+ if (actionResult.storeAction === StoreAction.UPDATE) {
1189
+ this.store.shouldUpdateSnapshot();
1190
+ }
1191
+ else if (actionResult.storeAction === StoreAction.CAPTURE) {
1192
+ this.store.shouldCaptureIncrement();
1193
+ }
1194
+ let didUpdate = false;
1187
1195
  let editingElement = null;
1188
1196
  if (actionResult.elements) {
1189
1197
  actionResult.elements.forEach((element) => {
@@ -1193,13 +1201,8 @@ class App extends React.Component {
1193
1201
  editingElement = element;
1194
1202
  }
1195
1203
  });
1196
- if (actionResult.storeAction === StoreAction.UPDATE) {
1197
- this.store.shouldUpdateSnapshot();
1198
- }
1199
- else if (actionResult.storeAction === StoreAction.CAPTURE) {
1200
- this.store.shouldCaptureIncrement();
1201
- }
1202
1204
  this.scene.replaceAllElements(actionResult.elements);
1205
+ didUpdate = true;
1203
1206
  }
1204
1207
  if (actionResult.files) {
1205
1208
  this.files = actionResult.replaceFiles
@@ -1208,12 +1211,6 @@ class App extends React.Component {
1208
1211
  this.addNewImagesToImageCache();
1209
1212
  }
1210
1213
  if (actionResult.appState || editingElement || this.state.contextMenu) {
1211
- if (actionResult.storeAction === StoreAction.UPDATE) {
1212
- this.store.shouldUpdateSnapshot();
1213
- }
1214
- else if (actionResult.storeAction === StoreAction.CAPTURE) {
1215
- this.store.shouldCaptureIncrement();
1216
- }
1217
1214
  let viewModeEnabled = actionResult?.appState?.viewModeEnabled || false;
1218
1215
  let zenModeEnabled = actionResult?.appState?.zenModeEnabled || false;
1219
1216
  let gridSize = actionResult?.appState?.gridSize || null;
@@ -1252,6 +1249,10 @@ class App extends React.Component {
1252
1249
  errorMessage,
1253
1250
  });
1254
1251
  });
1252
+ didUpdate = true;
1253
+ }
1254
+ if (!didUpdate && actionResult.storeAction !== StoreAction.NONE) {
1255
+ this.scene.triggerUpdate();
1255
1256
  }
1256
1257
  });
1257
1258
  // Lifecycle
@@ -1304,7 +1305,12 @@ class App extends React.Component {
1304
1305
  }
1305
1306
  let initialData = null;
1306
1307
  try {
1307
- 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
+ }
1308
1314
  if (initialData?.libraryItems) {
1309
1315
  this.library
1310
1316
  .updateLibrary({
@@ -1352,17 +1358,17 @@ class App extends React.Component {
1352
1358
  }),
1353
1359
  };
1354
1360
  }
1355
- // FontFaceSet loadingdone event we listen on may not always fire
1356
- // (looking at you Safari), so on init we manually load fonts for current
1357
- // text elements on canvas, and rerender them once done. This also
1358
- // seems faster even in browsers that do fire the loadingdone event.
1359
- this.fonts.loadFontsForElements(scene.elements);
1360
1361
  this.resetStore();
1361
1362
  this.resetHistory();
1362
1363
  this.syncActionResult({
1363
1364
  ...scene,
1364
1365
  storeAction: StoreAction.UPDATE,
1365
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();
1366
1372
  };
1367
1373
  isMobileBreakpoint = (width, height) => {
1368
1374
  return (width < MQ_MAX_WIDTH_PORTRAIT ||
@@ -1436,12 +1442,16 @@ class App extends React.Component {
1436
1442
  configurable: true,
1437
1443
  value: this.store,
1438
1444
  },
1445
+ fonts: {
1446
+ configurable: true,
1447
+ value: this.fonts,
1448
+ },
1439
1449
  });
1440
1450
  }
1441
1451
  this.store.onStoreIncrementEmitter.on((increment) => {
1442
1452
  this.history.record(increment.elementsChange, increment.appStateChange);
1443
1453
  });
1444
- this.scene.addCallback(this.onSceneUpdated);
1454
+ this.scene.onUpdate(this.triggerRender);
1445
1455
  this.addEventListeners();
1446
1456
  if (this.props.autoFocus && this.excalidrawContainerRef.current) {
1447
1457
  this.focusContainer();
@@ -1477,15 +1487,17 @@ class App extends React.Component {
1477
1487
  }
1478
1488
  }
1479
1489
  componentWillUnmount() {
1490
+ window.launchQueue?.setConsumer(() => { });
1480
1491
  this.renderer.destroy();
1492
+ this.scene.destroy();
1481
1493
  this.scene = new Scene();
1494
+ this.fonts = new Fonts({ scene: this.scene });
1482
1495
  this.renderer = new Renderer(this.scene);
1483
1496
  this.files = {};
1484
1497
  this.imageCache.clear();
1485
1498
  this.resizeObserver?.disconnect();
1486
1499
  this.unmounted = true;
1487
1500
  this.removeEventListeners();
1488
- this.scene.destroy();
1489
1501
  this.library.destroy();
1490
1502
  this.laserTrails.stop();
1491
1503
  this.eraserTrail.stop();
@@ -1534,15 +1546,18 @@ class App extends React.Component {
1534
1546
  this.onRemoveEventListenersEmitter.once(addEventListener(document, EVENT.KEYDOWN, this.onKeyDown, false));
1535
1547
  }
1536
1548
  this.onRemoveEventListenersEmitter.once(addEventListener(this.excalidrawContainerRef.current, EVENT.WHEEL, this.onWheel, { passive: false }), addEventListener(window, EVENT.MESSAGE, this.onWindowMessage, false), addEventListener(document, EVENT.POINTER_UP, this.removePointer), // #3553
1537
- addEventListener(document, EVENT.COPY, this.onCopy), addEventListener(document, EVENT.KEYUP, this.onKeyUp, { passive: true }), addEventListener(document, EVENT.MOUSE_MOVE, this.updateCurrentCursorPosition),
1549
+ addEventListener(document, EVENT.COPY, this.onCopy), addEventListener(document, EVENT.KEYUP, this.onKeyUp, { passive: true }), addEventListener(document, EVENT.POINTER_MOVE, this.updateCurrentCursorPosition),
1538
1550
  // rerender text elements on font load to fix #637 && #1553
1539
1551
  addEventListener(document.fonts, "loadingdone", (event) => {
1540
1552
  const loadedFontFaces = event.fontfaces;
1541
- this.fonts.onFontsLoaded(loadedFontFaces);
1553
+ this.fonts.onLoaded(loadedFontFaces);
1542
1554
  }),
1543
1555
  // Safari-only desktop pinch zoom
1544
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, () => {
1545
1557
  this.maybeCleanupAfterMissingPointerUp(null);
1558
+ // browsers (chrome?) tend to free up memory a lot, which results
1559
+ // in canvas context being cleared. Thus re-render on focus.
1560
+ this.triggerRender(true);
1546
1561
  }));
1547
1562
  if (this.state.viewModeEnabled) {
1548
1563
  return;
@@ -1660,7 +1675,7 @@ class App extends React.Component {
1660
1675
  multiElement != null &&
1661
1676
  isBindingEnabled(this.state) &&
1662
1677
  isBindingElement(multiElement, false)) {
1663
- 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());
1664
1679
  }
1665
1680
  this.store.commit(elementsMap, this.state);
1666
1681
  // Do not notify consumers if we're still loading the scene. Among other
@@ -1849,6 +1864,24 @@ class App extends React.Component {
1849
1864
  });
1850
1865
  }
1851
1866
  else if (data.text) {
1867
+ if (data.text && isMaybeMermaidDefinition(data.text)) {
1868
+ const api = await import("@excalidraw/mermaid-to-excalidraw");
1869
+ try {
1870
+ const { elements: skeletonElements, files } = await api.parseMermaidToExcalidraw(data.text);
1871
+ const elements = convertToExcalidrawElements(skeletonElements, {
1872
+ regenerateIds: true,
1873
+ });
1874
+ this.addElementsFromPasteOrLibrary({
1875
+ elements,
1876
+ files,
1877
+ position: "cursor",
1878
+ });
1879
+ return;
1880
+ }
1881
+ catch (err) {
1882
+ console.warn(`parsing pasted text as mermaid definition failed: ${err.message}`);
1883
+ }
1884
+ }
1852
1885
  const nonEmptyLines = normalizeEOL(data.text)
1853
1886
  .split(/\n+/)
1854
1887
  .map((s) => s.trim())
@@ -2053,27 +2086,46 @@ class App extends React.Component {
2053
2086
  text,
2054
2087
  fontSize: this.state.currentItemFontSize,
2055
2088
  fontFamily: this.state.currentItemFontFamily,
2056
- textAlign: this.state.currentItemTextAlign,
2089
+ textAlign: DEFAULT_TEXT_ALIGN,
2057
2090
  verticalAlign: DEFAULT_VERTICAL_ALIGN,
2058
2091
  locked: false,
2059
2092
  };
2093
+ const fontString = getFontString({
2094
+ fontSize: textElementProps.fontSize,
2095
+ fontFamily: textElementProps.fontFamily,
2096
+ });
2097
+ const lineHeight = getLineHeight(textElementProps.fontFamily);
2098
+ const [x1, , x2] = getVisibleSceneBounds(this.state);
2099
+ // long texts should not go beyond 800 pixels in width nor should it go below 200 px
2100
+ const maxTextWidth = Math.max(Math.min((x2 - x1) * 0.5, 800), 200);
2060
2101
  const LINE_GAP = 10;
2061
2102
  let currentY = y;
2062
2103
  const lines = isPlainPaste ? [text] : text.split("\n");
2063
2104
  const textElements = lines.reduce((acc, line, idx) => {
2064
- const text = line.trim();
2065
- const lineHeight = getDefaultLineHeight(textElementProps.fontFamily);
2066
- if (text.length) {
2105
+ const originalText = line.trim();
2106
+ if (originalText.length) {
2067
2107
  const topLayerFrame = this.getTopLayerFrameAtSceneCoords({
2068
2108
  x,
2069
2109
  y: currentY,
2070
2110
  });
2111
+ let metrics = measureText(originalText, fontString, lineHeight);
2112
+ const isTextUnwrapped = metrics.width > maxTextWidth;
2113
+ const text = isTextUnwrapped
2114
+ ? wrapText(originalText, fontString, maxTextWidth)
2115
+ : originalText;
2116
+ metrics = isTextUnwrapped
2117
+ ? measureText(text, fontString, lineHeight)
2118
+ : metrics;
2119
+ const startX = x - metrics.width / 2;
2120
+ const startY = currentY - metrics.height / 2;
2071
2121
  const element = newTextElement({
2072
2122
  ...textElementProps,
2073
- x,
2074
- y: currentY,
2123
+ x: startX,
2124
+ y: startY,
2075
2125
  text,
2126
+ originalText,
2076
2127
  lineHeight,
2128
+ autoResize: !isTextUnwrapped,
2077
2129
  frameId: topLayerFrame ? topLayerFrame.id : null,
2078
2130
  });
2079
2131
  acc.push(element);
@@ -2292,7 +2344,7 @@ class App extends React.Component {
2292
2344
  ShapeCache.delete(element);
2293
2345
  }
2294
2346
  });
2295
- this.scene.informMutation();
2347
+ this.scene.triggerUpdate();
2296
2348
  this.addNewImagesToImageCache();
2297
2349
  });
2298
2350
  updateScene = withBatchedUpdates((sceneData) => {
@@ -2326,8 +2378,15 @@ class App extends React.Component {
2326
2378
  this.setState({ collaborators: sceneData.collaborators });
2327
2379
  }
2328
2380
  });
2329
- onSceneUpdated = () => {
2330
- this.setState({});
2381
+ triggerRender = (
2382
+ /** force always re-renders canvas even if no change */
2383
+ force) => {
2384
+ if (force === true) {
2385
+ this.scene.triggerUpdate();
2386
+ }
2387
+ else {
2388
+ this.setState({});
2389
+ }
2331
2390
  };
2332
2391
  /**
2333
2392
  * @returns whether the menu was toggled on or off
@@ -2459,13 +2518,23 @@ class App extends React.Component {
2459
2518
  this.setState({ isBindingEnabled: false });
2460
2519
  }
2461
2520
  if (isArrowKey(event.key)) {
2462
- const step = (this.state.gridSize &&
2463
- (event.shiftKey
2464
- ? ELEMENT_TRANSLATE_AMOUNT
2465
- : this.state.gridSize)) ||
2466
- (event.shiftKey
2467
- ? ELEMENT_SHIFT_TRANSLATE_AMOUNT
2468
- : 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);
2469
2538
  let offsetX = 0;
2470
2539
  let offsetY = 0;
2471
2540
  if (event.key === KEYS.ARROW_LEFT) {
@@ -2480,22 +2549,17 @@ class App extends React.Component {
2480
2549
  else if (event.key === KEYS.ARROW_DOWN) {
2481
2550
  offsetY = step;
2482
2551
  }
2483
- const selectedElements = this.scene.getSelectedElements({
2484
- selectedElementIds: this.state.selectedElementIds,
2485
- includeBoundTextElement: true,
2486
- includeElementsInFrames: true,
2487
- });
2488
2552
  selectedElements.forEach((element) => {
2489
2553
  mutateElement(element, {
2490
2554
  x: element.x + offsetX,
2491
2555
  y: element.y + offsetY,
2492
2556
  });
2493
- updateBoundElements(element, this.scene.getNonDeletedElementsMap(), {
2557
+ updateBoundElements(element, this.scene.getNonDeletedElementsMap(), this.scene, {
2494
2558
  simultaneouslyUpdated: selectedElements,
2495
2559
  });
2496
2560
  });
2497
2561
  this.setState({
2498
- suggestedBindings: getSuggestedBindingsForArrows(selectedElements, this),
2562
+ suggestedBindings: getSuggestedBindingsForArrows(selectedElements.filter((element) => element.id !== elbowArrow?.id || step !== 0), this.scene.getNonDeletedElementsMap()),
2499
2563
  });
2500
2564
  event.preventDefault();
2501
2565
  }
@@ -2509,9 +2573,11 @@ class App extends React.Component {
2509
2573
  this.state.editingLinearElement.elementId !==
2510
2574
  selectedElements[0].id) {
2511
2575
  this.store.shouldCaptureIncrement();
2512
- this.setState({
2513
- editingLinearElement: new LinearElementEditor(selectedElement),
2514
- });
2576
+ if (!isElbowArrow(selectedElement)) {
2577
+ this.setState({
2578
+ editingLinearElement: new LinearElementEditor(selectedElement),
2579
+ });
2580
+ }
2515
2581
  }
2516
2582
  }
2517
2583
  }
@@ -2548,6 +2614,15 @@ class App extends React.Component {
2548
2614
  if (this.state.activeTool.type !== shape) {
2549
2615
  trackEvent("toolbar", shape, `keyboard (${this.device.editor.isMobile ? "mobile" : "desktop"})`);
2550
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
+ }
2551
2626
  this.setActiveTool({ type: shape });
2552
2627
  event.stopPropagation();
2553
2628
  }
@@ -2580,6 +2655,21 @@ class App extends React.Component {
2580
2655
  event.stopPropagation();
2581
2656
  }
2582
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
+ }
2583
2673
  if (event.key === KEYS.K && !event.altKey && !event[KEYS.CTRL_OR_CMD]) {
2584
2674
  if (this.state.activeTool.type === "laser") {
2585
2675
  this.setActiveTool({ type: "selection" });
@@ -2634,7 +2724,7 @@ class App extends React.Component {
2634
2724
  this.setState({ isBindingEnabled: true });
2635
2725
  }
2636
2726
  if (isArrowKey(event.key)) {
2637
- 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 ?? []);
2638
2728
  this.setState({ suggestedBindings: [] });
2639
2729
  }
2640
2730
  });
@@ -2772,15 +2862,16 @@ class App extends React.Component {
2772
2862
  });
2773
2863
  handleTextWysiwyg(element, { isExistingElement = false, }) {
2774
2864
  const elementsMap = this.scene.getElementsMapIncludingDeleted();
2775
- const updateElement = (text, originalText, isDeleted) => {
2865
+ const updateElement = (nextOriginalText, isDeleted) => {
2776
2866
  this.scene.replaceAllElements([
2777
2867
  // Not sure why we include deleted elements as well hence using deleted elements map
2778
2868
  ...this.scene.getElementsIncludingDeleted().map((_element) => {
2779
2869
  if (_element.id === element.id && isTextElement(_element)) {
2780
- return updateTextElement(_element, getContainerElement(_element, elementsMap), elementsMap, {
2781
- text,
2782
- isDeleted,
2783
- originalText,
2870
+ return newElementWith(_element, {
2871
+ originalText: nextOriginalText,
2872
+ isDeleted: isDeleted ?? _element.isDeleted,
2873
+ // returns (wrapped) text and new dimensions
2874
+ ...refreshTextDimensions(_element, getContainerElement(_element, elementsMap), elementsMap, nextOriginalText),
2784
2875
  });
2785
2876
  }
2786
2877
  return _element;
@@ -2800,15 +2891,15 @@ class App extends React.Component {
2800
2891
  viewportY - this.state.offsetTop,
2801
2892
  ];
2802
2893
  },
2803
- onChange: withBatchedUpdates((text) => {
2804
- updateElement(text, text, false);
2894
+ onChange: withBatchedUpdates((nextOriginalText) => {
2895
+ updateElement(nextOriginalText, false);
2805
2896
  if (isNonDeletedElement(element)) {
2806
- updateBoundElements(element, elementsMap);
2897
+ updateBoundElements(element, elementsMap, this.scene);
2807
2898
  }
2808
2899
  }),
2809
- onSubmit: withBatchedUpdates(({ text, viaKeyboard, originalText }) => {
2810
- const isDeleted = !text.trim();
2811
- updateElement(text, originalText, isDeleted);
2900
+ onSubmit: withBatchedUpdates(({ viaKeyboard, nextOriginalText }) => {
2901
+ const isDeleted = !nextOriginalText.trim();
2902
+ updateElement(nextOriginalText, isDeleted);
2812
2903
  // select the created text element only if submitting via keyboard
2813
2904
  // (when submitting via click it should act as signal to deselect)
2814
2905
  if (!isDeleted && viaKeyboard) {
@@ -2842,12 +2933,17 @@ class App extends React.Component {
2842
2933
  element,
2843
2934
  excalidrawContainer: this.excalidrawContainerRef.current,
2844
2935
  app: this,
2936
+ // when text is selected, it's hard (at least on iOS) to re-position the
2937
+ // caret (i.e. deselect). There's not much use for always selecting
2938
+ // the text on edit anyway (and users can select-all from contextmenu
2939
+ // if needed)
2940
+ autoSelect: !this.device.isTouchScreen,
2845
2941
  });
2846
2942
  // deselect all other elements when inserting text
2847
2943
  this.deselectElements();
2848
2944
  // do an initial update to re-initialize element position since we were
2849
2945
  // modifying element's x/y for sake of editor (case: syncing to remote)
2850
- updateElement(element.text, element.originalText, false);
2946
+ updateElement(element.originalText, false);
2851
2947
  }
2852
2948
  deselectElements() {
2853
2949
  this.setState({
@@ -2866,57 +2962,6 @@ class App extends React.Component {
2866
2962
  }
2867
2963
  return null;
2868
2964
  }
2869
- /**
2870
- * get the pure geometric shape of an excalidraw element
2871
- * which is then used for hit detection
2872
- */
2873
- getElementShape(element) {
2874
- switch (element.type) {
2875
- case "rectangle":
2876
- case "diamond":
2877
- case "frame":
2878
- case "magicframe":
2879
- case "embeddable":
2880
- case "image":
2881
- case "iframe":
2882
- case "text":
2883
- case "selection":
2884
- return getPolygonShape(element);
2885
- case "arrow":
2886
- case "line": {
2887
- const roughShape = ShapeCache.get(element)?.[0] ??
2888
- ShapeCache.generateElementShape(element, null)[0];
2889
- const [, , , , cx, cy] = getElementAbsoluteCoords(element, this.scene.getNonDeletedElementsMap());
2890
- return shouldTestInside(element)
2891
- ? getClosedCurveShape(element, roughShape, [element.x, element.y], element.angle, [cx, cy])
2892
- : getCurveShape(roughShape, [element.x, element.y], element.angle, [
2893
- cx,
2894
- cy,
2895
- ]);
2896
- }
2897
- case "ellipse":
2898
- return getEllipseShape(element);
2899
- case "freedraw": {
2900
- const [, , , , cx, cy] = getElementAbsoluteCoords(element, this.scene.getNonDeletedElementsMap());
2901
- return getFreedrawShape(element, [cx, cy], shouldTestInside(element));
2902
- }
2903
- }
2904
- }
2905
- getBoundTextShape(element) {
2906
- const boundTextElement = getBoundTextElement(element, this.scene.getNonDeletedElementsMap());
2907
- if (boundTextElement) {
2908
- if (element.type === "arrow") {
2909
- return this.getElementShape({
2910
- ...boundTextElement,
2911
- // arrow's bound text accurate position is not stored in the element's property
2912
- // but rather calculated and returned from the following static method
2913
- ...LinearElementEditor.getBoundTextElementPosition(element, boundTextElement, this.scene.getNonDeletedElementsMap()),
2914
- });
2915
- }
2916
- return this.getElementShape(boundTextElement);
2917
- }
2918
- return null;
2919
- }
2920
2965
  getElementAtPosition(x, y, opts) {
2921
2966
  const allHitElements = this.getElementsAtPosition(x, y, opts?.includeBoundTextElement, opts?.includeLockedElements);
2922
2967
  if (allHitElements.length > 1) {
@@ -2934,7 +2979,7 @@ class App extends React.Component {
2934
2979
  x,
2935
2980
  y,
2936
2981
  element: elementWithHighestZIndex,
2937
- shape: this.getElementShape(elementWithHighestZIndex),
2982
+ shape: getElementShape(elementWithHighestZIndex, this.scene.getNonDeletedElementsMap()),
2938
2983
  // when overlapping, we would like to be more precise
2939
2984
  // this also avoids the need to update past tests
2940
2985
  threshold: this.getElementHitThreshold() / 2,
@@ -2996,7 +3041,7 @@ class App extends React.Component {
2996
3041
  return isPointInShape([x, y], selectionShape);
2997
3042
  }
2998
3043
  // take bound text element into consideration for hit collision as well
2999
- const hitBoundTextOfElement = hitElementBoundText(x, y, this.getBoundTextShape(element));
3044
+ const hitBoundTextOfElement = hitElementBoundText(x, y, getBoundTextShape(element, this.scene.getNonDeletedElementsMap()));
3000
3045
  if (hitBoundTextOfElement) {
3001
3046
  return true;
3002
3047
  }
@@ -3004,7 +3049,7 @@ class App extends React.Component {
3004
3049
  x,
3005
3050
  y,
3006
3051
  element,
3007
- shape: this.getElementShape(element),
3052
+ shape: getElementShape(element, this.scene.getNonDeletedElementsMap()),
3008
3053
  threshold: this.getElementHitThreshold(),
3009
3054
  frameNameBound: isFrameLikeElement(element)
3010
3055
  ? this.frameNameBoundsCache.get(element)
@@ -3031,7 +3076,7 @@ class App extends React.Component {
3031
3076
  x,
3032
3077
  y,
3033
3078
  element: elements[index],
3034
- shape: this.getElementShape(elements[index]),
3079
+ shape: getElementShape(elements[index], this.scene.getNonDeletedElementsMap()),
3035
3080
  threshold: this.getElementHitThreshold(),
3036
3081
  })) {
3037
3082
  hitElement = elements[index];
@@ -3044,7 +3089,7 @@ class App extends React.Component {
3044
3089
  }
3045
3090
  return isTextBindableContainer(hitElement, false) ? hitElement : null;
3046
3091
  }
3047
- startTextEditing = ({ sceneX, sceneY, insertAtParentCenter = true, container, }) => {
3092
+ startTextEditing = ({ sceneX, sceneY, insertAtParentCenter = true, container, autoEdit = true, }) => {
3048
3093
  let shouldBindToContainer = false;
3049
3094
  let parentCenterPosition = insertAtParentCenter &&
3050
3095
  this.getTextWysiwygSnappedToCenterPosition(sceneX, sceneY, this.state, container);
@@ -3071,7 +3116,7 @@ class App extends React.Component {
3071
3116
  existingTextElement = this.getTextElementAtPosition(sceneX, sceneY);
3072
3117
  }
3073
3118
  const fontFamily = existingTextElement?.fontFamily || this.state.currentItemFontFamily;
3074
- const lineHeight = existingTextElement?.lineHeight || getDefaultLineHeight(fontFamily);
3119
+ const lineHeight = existingTextElement?.lineHeight || getLineHeight(fontFamily);
3075
3120
  const fontSize = this.state.currentItemFontSize;
3076
3121
  if (!existingTextElement &&
3077
3122
  shouldBindToContainer &&
@@ -3145,12 +3190,17 @@ class App extends React.Component {
3145
3190
  this.scene.insertElement(element);
3146
3191
  }
3147
3192
  }
3148
- this.setState({
3149
- editingElement: element,
3150
- });
3151
- this.handleTextWysiwyg(element, {
3152
- isExistingElement: !!existingTextElement,
3153
- });
3193
+ if (autoEdit || existingTextElement || container) {
3194
+ this.handleTextWysiwyg(element, {
3195
+ isExistingElement: !!existingTextElement,
3196
+ });
3197
+ }
3198
+ else {
3199
+ this.setState({
3200
+ draggingElement: element,
3201
+ multiElement: null,
3202
+ });
3203
+ }
3154
3204
  };
3155
3205
  handleCanvasDoubleClick = (event) => {
3156
3206
  // case: double-clicking with arrow/line tool selected would both create
@@ -3166,7 +3216,9 @@ class App extends React.Component {
3166
3216
  if (selectedElements.length === 1 && isLinearElement(selectedElements[0])) {
3167
3217
  if (event[KEYS.CTRL_OR_CMD] &&
3168
3218
  (!this.state.editingLinearElement ||
3169
- this.state.editingLinearElement.elementId !== selectedElements[0].id)) {
3219
+ this.state.editingLinearElement.elementId !==
3220
+ selectedElements[0].id) &&
3221
+ !isElbowArrow(selectedElements[0])) {
3170
3222
  this.store.shouldCaptureIncrement();
3171
3223
  this.setState({
3172
3224
  editingLinearElement: new LinearElementEditor(selectedElements[0]),
@@ -3210,7 +3262,7 @@ class App extends React.Component {
3210
3262
  x: sceneX,
3211
3263
  y: sceneY,
3212
3264
  element: container,
3213
- shape: this.getElementShape(container),
3265
+ shape: getElementShape(container, this.scene.getNonDeletedElementsMap()),
3214
3266
  threshold: this.getElementHitThreshold(),
3215
3267
  })) {
3216
3268
  const midPoint = getContainerCenter(container, this.state, this.scene.getNonDeletedElementsMap());
@@ -3317,8 +3369,11 @@ class App extends React.Component {
3317
3369
  }, state);
3318
3370
  this.translateCanvas({
3319
3371
  zoom: zoomState.zoom,
3320
- scrollX: zoomState.scrollX + deltaX / nextZoom,
3321
- scrollY: zoomState.scrollY + deltaY / nextZoom,
3372
+ // 2x multiplier is just a magic number that makes this work correctly
3373
+ // on touchscreen devices (note: if we get report that panning is slower/faster
3374
+ // than actual movement, consider swapping with devicePixelRatio)
3375
+ scrollX: zoomState.scrollX + 2 * (deltaX / nextZoom),
3376
+ scrollY: zoomState.scrollY + 2 * (deltaY / nextZoom),
3322
3377
  shouldCacheIgnoreZoom: true,
3323
3378
  });
3324
3379
  });
@@ -3381,7 +3436,7 @@ class App extends React.Component {
3381
3436
  }
3382
3437
  if (this.state.editingLinearElement &&
3383
3438
  !this.state.editingLinearElement.isDragging) {
3384
- const editingLinearElement = LinearElementEditor.handlePointerMove(event, scenePointerX, scenePointerY, this.state, this.scene.getNonDeletedElementsMap());
3439
+ const editingLinearElement = LinearElementEditor.handlePointerMove(event, scenePointerX, scenePointerY, this.state, this.scene);
3385
3440
  if (editingLinearElement &&
3386
3441
  editingLinearElement !== this.state.editingLinearElement) {
3387
3442
  // Since we are reading from previous state which is not possible with
@@ -3443,7 +3498,9 @@ class App extends React.Component {
3443
3498
  });
3444
3499
  }
3445
3500
  else {
3446
- 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);
3447
3504
  const [lastCommittedX, lastCommittedY] = multiElement?.lastCommittedPoint ?? [0, 0];
3448
3505
  let dxFromLastCommitted = gridX - rx - lastCommittedX;
3449
3506
  let dyFromLastCommitted = gridY - ry - lastCommittedY;
@@ -3458,16 +3515,29 @@ class App extends React.Component {
3458
3515
  if (isPathALoop(points, this.state.zoom.value)) {
3459
3516
  setCursor(this.interactiveCanvas, CURSOR_TYPE.POINTER);
3460
3517
  }
3461
- // update last uncommitted point
3462
- mutateElement(multiElement, {
3463
- points: [
3518
+ if (isElbowArrow(multiElement)) {
3519
+ mutateElbowArrow(multiElement, this.scene, [
3464
3520
  ...points.slice(0, -1),
3465
3521
  [
3466
3522
  lastCommittedX + dxFromLastCommitted,
3467
3523
  lastCommittedY + dyFromLastCommitted,
3468
3524
  ],
3469
- ],
3470
- });
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
+ }
3471
3541
  }
3472
3542
  return;
3473
3543
  }
@@ -3488,8 +3558,9 @@ class App extends React.Component {
3488
3558
  if (this.state.selectedLinearElement) {
3489
3559
  this.handleHoverSelectedLinearElement(this.state.selectedLinearElement, scenePointerX, scenePointerY);
3490
3560
  }
3491
- if (!this.state.selectedLinearElement ||
3492
- this.state.selectedLinearElement.hoverPointIndex === -1) {
3561
+ if ((!this.state.selectedLinearElement ||
3562
+ this.state.selectedLinearElement.hoverPointIndex === -1) &&
3563
+ !(selectedElements.length === 1 && isElbowArrow(selectedElements[0]))) {
3493
3564
  const elementWithTransformHandleType = getElementWithTransformHandleType(elements, this.state, scenePointerX, scenePointerY, this.state.zoom, event.pointerType, this.scene.getNonDeletedElementsMap(), this.device);
3494
3565
  if (elementWithTransformHandleType &&
3495
3566
  elementWithTransformHandleType.transformHandleType) {
@@ -3639,7 +3710,7 @@ class App extends React.Component {
3639
3710
  }
3640
3711
  }
3641
3712
  this.elementsPendingErasure = new Set(this.elementsPendingErasure);
3642
- this.onSceneUpdated();
3713
+ this.triggerRender();
3643
3714
  }
3644
3715
  };
3645
3716
  // set touch moving for mobile context menu
@@ -3659,7 +3730,7 @@ class App extends React.Component {
3659
3730
  x: scenePointerX,
3660
3731
  y: scenePointerY,
3661
3732
  element,
3662
- shape: this.getElementShape(element),
3733
+ shape: getElementShape(element, this.scene.getNonDeletedElementsMap()),
3663
3734
  })) {
3664
3735
  hoverPointIndex = LinearElementEditor.getPointIndexUnderCursor(element, elementsMap, this.state.zoom, scenePointerX, scenePointerY);
3665
3736
  segmentMidPointHoveredCoords =
@@ -3672,7 +3743,10 @@ class App extends React.Component {
3672
3743
  }
3673
3744
  }
3674
3745
  else if (this.hitElement(scenePointerX, scenePointerY, element)) {
3675
- setCursor(this.interactiveCanvas, CURSOR_TYPE.MOVE);
3746
+ if (!isElbowArrow(element) ||
3747
+ !(element.startBinding || element.endBinding)) {
3748
+ setCursor(this.interactiveCanvas, CURSOR_TYPE.MOVE);
3749
+ }
3676
3750
  }
3677
3751
  if (this.state.selectedLinearElement.hoverPointIndex !== hoverPointIndex) {
3678
3752
  this.setState({
@@ -3850,7 +3924,6 @@ class App extends React.Component {
3850
3924
  }
3851
3925
  if (this.state.activeTool.type === "text") {
3852
3926
  this.handleTextOnPointerDown(event, pointerDownState);
3853
- return;
3854
3927
  }
3855
3928
  else if (this.state.activeTool.type === "arrow" ||
3856
3929
  this.state.activeTool.type === "line") {
@@ -4086,10 +4159,13 @@ class App extends React.Component {
4086
4159
  const origin = viewportCoordsToSceneCoords(event, this.state);
4087
4160
  const selectedElements = this.scene.getSelectedElements(this.state);
4088
4161
  const [minX, minY, maxX, maxY] = getCommonBounds(selectedElements);
4162
+ const isElbowArrowOnly = selectedElements.findIndex(isElbowArrow) === 0;
4089
4163
  return {
4090
4164
  origin,
4091
4165
  withCmdOrCtrl: event[KEYS.CTRL_OR_CMD],
4092
- 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)),
4093
4169
  scrollbars: isOverScrollBars(currentScrollBars, event.clientX - this.state.offsetLeft, event.clientY - this.state.offsetTop),
4094
4170
  // we need to duplicate because we'll be updating this state
4095
4171
  lastCoords: { ...origin },
@@ -4206,7 +4282,7 @@ class App extends React.Component {
4206
4282
  else {
4207
4283
  if (this.state.selectedLinearElement) {
4208
4284
  const linearElementEditor = this.state.editingLinearElement || this.state.selectedLinearElement;
4209
- 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);
4210
4286
  if (ret.hitElement) {
4211
4287
  pointerDownState.hit.element = ret.hitElement;
4212
4288
  }
@@ -4398,6 +4474,7 @@ class App extends React.Component {
4398
4474
  sceneY,
4399
4475
  insertAtParentCenter: !event.altKey,
4400
4476
  container,
4477
+ autoEdit: false,
4401
4478
  });
4402
4479
  resetCursor(this.interactiveCanvas);
4403
4480
  if (!this.state.activeTool.locked) {
@@ -4445,7 +4522,7 @@ class App extends React.Component {
4445
4522
  points: [[0, 0]],
4446
4523
  pressures,
4447
4524
  });
4448
- const boundElement = getHoveredElementForBinding(pointerDownState.origin, this);
4525
+ const boundElement = getHoveredElementForBinding(pointerDownState.origin, this.scene.getNonDeletedElements(), this.scene.getNonDeletedElementsMap());
4449
4526
  this.scene.insertElement(element);
4450
4527
  this.setState({
4451
4528
  draggingElement: element,
@@ -4551,6 +4628,15 @@ class App extends React.Component {
4551
4628
  this.actionManager.executeAction(actionFinalize);
4552
4629
  return;
4553
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
+ }
4554
4640
  const { x: rx, y: ry, lastCommittedPoint } = multiElement;
4555
4641
  // clicking inside commit zone → finalize arrow
4556
4642
  if (multiElement.points.length > 1 &&
@@ -4586,25 +4672,46 @@ class App extends React.Component {
4586
4672
  const [startArrowhead, endArrowhead] = elementType === "arrow"
4587
4673
  ? [currentItemStartArrowhead, currentItemEndArrowhead]
4588
4674
  : [null, null];
4589
- const element = newLinearElement({
4590
- type: elementType,
4591
- x: gridX,
4592
- y: gridY,
4593
- strokeColor: this.state.currentItemStrokeColor,
4594
- backgroundColor: this.state.currentItemBackgroundColor,
4595
- fillStyle: this.state.currentItemFillStyle,
4596
- strokeWidth: this.state.currentItemStrokeWidth,
4597
- strokeStyle: this.state.currentItemStrokeStyle,
4598
- roughness: this.state.currentItemRoughness,
4599
- opacity: this.state.currentItemOpacity,
4600
- roundness: this.state.currentItemRoundness === "round"
4601
- ? { type: ROUNDNESS.PROPORTIONAL_RADIUS }
4602
- : null,
4603
- startArrowhead,
4604
- endArrowhead,
4605
- locked: false,
4606
- frameId: topLayerFrame ? topLayerFrame.id : null,
4607
- });
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
+ });
4608
4715
  this.setState((prevState) => {
4609
4716
  const nextSelectedElementIds = {
4610
4717
  ...prevState.selectedElementIds,
@@ -4617,7 +4724,7 @@ class App extends React.Component {
4617
4724
  mutateElement(element, {
4618
4725
  points: [...element.points, [0, 0]],
4619
4726
  });
4620
- const boundElement = getHoveredElementForBinding(pointerDownState.origin, this);
4727
+ const boundElement = getHoveredElementForBinding(pointerDownState.origin, this.scene.getNonDeletedElements(), this.scene.getNonDeletedElementsMap(), isElbowArrow(element));
4621
4728
  this.scene.insertElement(element);
4622
4729
  this.setState({
4623
4730
  draggingElement: element,
@@ -4827,7 +4934,7 @@ class App extends React.Component {
4827
4934
  }
4828
4935
  const didDrag = LinearElementEditor.handlePointDragging(event, this.state, pointerCoords.x, pointerCoords.y, (element, pointsSceneCoords) => {
4829
4936
  this.maybeSuggestBindingsForLinearElementAtCoords(element, pointsSceneCoords);
4830
- }, linearElementEditor, this.scene.getNonDeletedElementsMap());
4937
+ }, linearElementEditor, this.scene);
4831
4938
  if (didDrag) {
4832
4939
  pointerDownState.lastCoords.x = pointerCoords.x;
4833
4940
  pointerDownState.lastCoords.y = pointerCoords.y;
@@ -4913,10 +5020,14 @@ class App extends React.Component {
4913
5020
  // when we're editing the name of a frame, we want the user to be
4914
5021
  // able to select and interact with the text input
4915
5022
  !this.state.editingFrame &&
4916
- dragSelectedElements(pointerDownState, selectedElements, dragOffset, this.state, this.scene, snapOffset, event[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize);
4917
- this.setState({
4918
- suggestedBindings: getSuggestedBindingsForArrows(selectedElements, this),
4919
- });
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
+ //}
4920
5031
  // We duplicate the selected element if alt is pressed on pointer move
4921
5032
  if (event.altKey && !pointerDownState.hit.hasBeenDuplicated) {
4922
5033
  // Move the currently selected elements to the top of the z index stack, and
@@ -5009,6 +5120,11 @@ class App extends React.Component {
5009
5120
  points: [...points, [dx, dy]],
5010
5121
  });
5011
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
+ }
5012
5128
  else if (points.length === 2) {
5013
5129
  mutateElement(draggingElement, {
5014
5130
  points: [...points.slice(0, -1), [dx, dy]],
@@ -5152,7 +5268,7 @@ class App extends React.Component {
5152
5268
  this.actionManager.executeAction(actionFinalize);
5153
5269
  }
5154
5270
  else {
5155
- const editingLinearElement = LinearElementEditor.handlePointerUp(childEvent, this.state.editingLinearElement, this.state, this);
5271
+ const editingLinearElement = LinearElementEditor.handlePointerUp(childEvent, this.state.editingLinearElement, this.state, this.scene);
5156
5272
  if (editingLinearElement !== this.state.editingLinearElement) {
5157
5273
  this.setState({
5158
5274
  editingLinearElement,
@@ -5171,11 +5287,11 @@ class App extends React.Component {
5171
5287
  }
5172
5288
  }
5173
5289
  else {
5174
- const linearElementEditor = LinearElementEditor.handlePointerUp(childEvent, this.state.selectedLinearElement, this.state, this);
5290
+ const linearElementEditor = LinearElementEditor.handlePointerUp(childEvent, this.state.selectedLinearElement, this.state, this.scene);
5175
5291
  const { startBindingElement, endBindingElement } = linearElementEditor;
5176
5292
  const element = this.scene.getElement(linearElementEditor.elementId);
5177
5293
  if (isBindingElement(element)) {
5178
- bindOrUnbindLinearElement(element, startBindingElement, endBindingElement, elementsMap);
5294
+ bindOrUnbindLinearElement(element, startBindingElement, endBindingElement, elementsMap, this.scene);
5179
5295
  }
5180
5296
  if (linearElementEditor !== this.state.selectedLinearElement) {
5181
5297
  this.setState({
@@ -5263,7 +5379,7 @@ class App extends React.Component {
5263
5379
  else if (pointerDownState.drag.hasOccurred && !multiElement) {
5264
5380
  if (isBindingEnabled(this.state) &&
5265
5381
  isBindingElement(draggingElement, false)) {
5266
- maybeBindLinearElement(draggingElement, this.state, pointerCoords, this);
5382
+ maybeBindLinearElement(draggingElement, this.state, pointerCoords, this.scene.getNonDeletedElementsMap(), this.scene.getNonDeletedElements());
5267
5383
  }
5268
5384
  this.setState({ suggestedBindings: [], startBoundElement: null });
5269
5385
  if (!activeTool.locked) {
@@ -5288,6 +5404,21 @@ class App extends React.Component {
5288
5404
  }
5289
5405
  return;
5290
5406
  }
5407
+ if (isTextElement(draggingElement)) {
5408
+ const minWidth = getMinTextElementWidth(getFontString({
5409
+ fontSize: draggingElement.fontSize,
5410
+ fontFamily: draggingElement.fontFamily,
5411
+ }), draggingElement.lineHeight);
5412
+ if (draggingElement.width < minWidth) {
5413
+ mutateElement(draggingElement, {
5414
+ autoResize: true,
5415
+ });
5416
+ }
5417
+ this.resetCursor();
5418
+ this.handleTextWysiwyg(draggingElement, {
5419
+ isExistingElement: true,
5420
+ });
5421
+ }
5291
5422
  if (activeTool.type !== "selection" &&
5292
5423
  draggingElement &&
5293
5424
  isInvisiblySmallElement(draggingElement)) {
@@ -5323,7 +5454,7 @@ class App extends React.Component {
5323
5454
  groupIds: [],
5324
5455
  });
5325
5456
  removeElementsFromFrame([linearElement], this.scene.getNonDeletedElementsMap());
5326
- this.scene.informMutation();
5457
+ this.scene.triggerUpdate();
5327
5458
  }
5328
5459
  }
5329
5460
  }
@@ -5556,7 +5687,7 @@ class App extends React.Component {
5556
5687
  x: pointerDownState.origin.x,
5557
5688
  y: pointerDownState.origin.y,
5558
5689
  element: hitElement,
5559
- shape: this.getElementShape(hitElement),
5690
+ shape: getElementShape(hitElement, this.scene.getNonDeletedElementsMap()),
5560
5691
  threshold: this.getElementHitThreshold(),
5561
5692
  frameNameBound: isFrameLikeElement(hitElement)
5562
5693
  ? this.frameNameBoundsCache.get(hitElement)
@@ -5605,7 +5736,7 @@ class App extends React.Component {
5605
5736
  const linearElements = this.scene
5606
5737
  .getSelectedElements(this.state)
5607
5738
  .filter(isLinearElement);
5608
- 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 ?? []);
5609
5740
  }
5610
5741
  if (activeTool.type === "laser") {
5611
5742
  this.laserTrails.endPath();
@@ -5640,7 +5771,7 @@ class App extends React.Component {
5640
5771
  }
5641
5772
  restoreReadyToEraseElements = () => {
5642
5773
  this.elementsPendingErasure = new Set();
5643
- this.onSceneUpdated();
5774
+ this.triggerRender();
5644
5775
  };
5645
5776
  eraseElements = () => {
5646
5777
  let didChange = false;
@@ -5932,7 +6063,7 @@ class App extends React.Component {
5932
6063
  if (uncachedImageElements.length) {
5933
6064
  const { updatedFiles } = await this.updateImageCache(uncachedImageElements, files);
5934
6065
  if (updatedFiles.size) {
5935
- this.scene.informMutation();
6066
+ this.scene.triggerUpdate();
5936
6067
  }
5937
6068
  }
5938
6069
  };
@@ -5948,7 +6079,7 @@ class App extends React.Component {
5948
6079
  }
5949
6080
  };
5950
6081
  maybeSuggestBindingAtCursor = (pointerCoords) => {
5951
- const hoveredBindableElement = getHoveredElementForBinding(pointerCoords, this);
6082
+ const hoveredBindableElement = getHoveredElementForBinding(pointerCoords, this.scene.getNonDeletedElements(), this.scene.getNonDeletedElementsMap());
5952
6083
  this.setState({
5953
6084
  suggestedBindings: hoveredBindableElement != null ? [hoveredBindableElement] : [],
5954
6085
  });
@@ -5963,7 +6094,7 @@ class App extends React.Component {
5963
6094
  return;
5964
6095
  }
5965
6096
  const suggestedBindings = pointerCoords.reduce((acc, coords) => {
5966
- const hoveredBindableElement = getHoveredElementForBinding(coords, this);
6097
+ const hoveredBindableElement = getHoveredElementForBinding(coords, this.scene.getNonDeletedElements(), this.scene.getNonDeletedElementsMap(), isArrowElement(linearElement) && isElbowArrow(linearElement));
5967
6098
  if (hoveredBindableElement != null &&
5968
6099
  !isLinearElementSimpleAndAlreadyBound(linearElement, oppositeBindingBoundElement?.id, hoveredBindableElement)) {
5969
6100
  acc.push(hoveredBindableElement);
@@ -6211,7 +6342,7 @@ class App extends React.Component {
6211
6342
  }
6212
6343
  if (draggingElement.type === "selection" &&
6213
6344
  this.state.activeTool.type !== "eraser") {
6214
- dragNewElement(draggingElement, this.state.activeTool.type, pointerDownState.origin.x, pointerDownState.origin.y, pointerCoords.x, pointerCoords.y, distance(pointerDownState.origin.x, pointerCoords.x), distance(pointerDownState.origin.y, pointerCoords.y), shouldMaintainAspectRatio(event), shouldResizeFromCenter(event));
6345
+ dragNewElement(draggingElement, this.state.activeTool.type, pointerDownState.origin.x, pointerDownState.origin.y, pointerCoords.x, pointerCoords.y, distance(pointerDownState.origin.x, pointerCoords.x), distance(pointerDownState.origin.y, pointerCoords.y), shouldMaintainAspectRatio(event), shouldResizeFromCenter(event), this.state.zoom.value);
6215
6346
  }
6216
6347
  else {
6217
6348
  let [gridX, gridY] = getGridPoint(pointerCoords.x, pointerCoords.y, event[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize);
@@ -6237,7 +6368,7 @@ class App extends React.Component {
6237
6368
  });
6238
6369
  dragNewElement(draggingElement, this.state.activeTool.type, pointerDownState.originInGrid.x, pointerDownState.originInGrid.y, gridX, gridY, distance(pointerDownState.originInGrid.x, gridX), distance(pointerDownState.originInGrid.y, gridY), isImageElement(draggingElement)
6239
6370
  ? !shouldMaintainAspectRatio(event)
6240
- : shouldMaintainAspectRatio(event), shouldResizeFromCenter(event), aspectRatio, this.state.originSnapOffset);
6371
+ : shouldMaintainAspectRatio(event), shouldResizeFromCenter(event), this.state.zoom.value, aspectRatio, this.state.originSnapOffset);
6241
6372
  // highlight elements that are to be added to frames on frames creation
6242
6373
  if (this.state.activeTool.type === TOOL_TYPE.frame ||
6243
6374
  this.state.activeTool.type === TOOL_TYPE.magicframe) {
@@ -6293,8 +6424,8 @@ class App extends React.Component {
6293
6424
  }
6294
6425
  if (transformElements(pointerDownState.originalElements, transformHandleType, selectedElements, this.scene.getElementsMapIncludingDeleted(), shouldRotateWithDiscreteAngle(event), shouldResizeFromCenter(event), selectedElements.some((element) => isImageElement(element))
6295
6426
  ? !shouldMaintainAspectRatio(event)
6296
- : shouldMaintainAspectRatio(event), resizeX, resizeY, pointerDownState.resize.center.x, pointerDownState.resize.center.y)) {
6297
- 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());
6298
6429
  const elementsToHighlight = new Set();
6299
6430
  selectedFrames.forEach((frame) => {
6300
6431
  getElementsInResizingFrame(this.scene.getNonDeletedElements(), frame, this.state, this.scene.getNonDeletedElementsMap()).forEach((element) => elementsToHighlight.add(element));
@@ -6346,6 +6477,7 @@ class App extends React.Component {
6346
6477
  return [actionCopy, ...options];
6347
6478
  }
6348
6479
  return [
6480
+ CONTEXT_MENU_SEPARATOR,
6349
6481
  actionCut,
6350
6482
  actionCopy,
6351
6483
  actionPaste,
@@ -6358,6 +6490,7 @@ class App extends React.Component {
6358
6490
  actionPasteStyles,
6359
6491
  CONTEXT_MENU_SEPARATOR,
6360
6492
  actionGroup,
6493
+ actionTextAutoResize,
6361
6494
  actionUnbindText,
6362
6495
  actionBindText,
6363
6496
  actionWrapTextInContainer,
@@ -6529,6 +6662,12 @@ export const createTestHook = () => {
6529
6662
  return this.app?.scene.replaceAllElements(syncInvalidIndices(elements));
6530
6663
  },
6531
6664
  },
6665
+ scene: {
6666
+ configurable: true,
6667
+ get() {
6668
+ return this.app?.scene;
6669
+ },
6670
+ },
6532
6671
  });
6533
6672
  }
6534
6673
  };