@excalidraw/excalidraw 0.17.1-1d71f84 → 0.17.1-1ed53b1

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 (426) hide show
  1. package/CHANGELOG.md +14 -2
  2. package/dist/browser/dev/excalidraw-assets-dev/{blockDiagram-91b80b7a-ACFH36JV.js → blockDiagram-91b80b7a-H47FTXHA.js} +5 -5
  3. package/dist/browser/dev/excalidraw-assets-dev/{c4Diagram-b2a90758-QZ27YR47.js → c4Diagram-b2a90758-NNJK6GKC.js} +3 -3
  4. package/dist/browser/dev/excalidraw-assets-dev/{chunk-HO2HMSK7.js → chunk-4KQVEBHW.js} +3 -3
  5. package/dist/browser/dev/excalidraw-assets-dev/{chunk-USGV265L.js → chunk-53YI56GV.js} +4 -4
  6. package/dist/browser/dev/excalidraw-assets-dev/{chunk-EDFX3S7X.js → chunk-A2WCJI4I.js} +3 -3
  7. package/dist/browser/dev/excalidraw-assets-dev/{chunk-IX4V72YG.js → chunk-EFLPX7NE.js} +6 -6
  8. package/dist/browser/dev/excalidraw-assets-dev/{chunk-AK7SWNLN.js → chunk-JKPJV7MZ.js} +6 -4
  9. package/dist/browser/dev/excalidraw-assets-dev/chunk-JKPJV7MZ.js.map +7 -0
  10. package/dist/browser/dev/excalidraw-assets-dev/{chunk-MXVETLVM.js → chunk-JYIQCNWV.js} +2 -2
  11. package/dist/browser/dev/excalidraw-assets-dev/{chunk-YZIOORVX.js → chunk-LVIQQW6F.js} +2 -2
  12. package/dist/browser/dev/excalidraw-assets-dev/{chunk-RWZVJAQU.js → chunk-OKAZAA6U.js} +4494 -2647
  13. package/dist/browser/dev/excalidraw-assets-dev/chunk-OKAZAA6U.js.map +7 -0
  14. package/dist/browser/dev/excalidraw-assets-dev/{chunk-6U7GQNJT.js → chunk-PXLO3FOU.js} +2 -2
  15. package/dist/browser/dev/excalidraw-assets-dev/{chunk-7DACDEY3.js → chunk-TO2AW5PW.js} +2 -2
  16. package/dist/browser/dev/excalidraw-assets-dev/{chunk-NJ77ZFNJ.js → chunk-VURILHLY.js} +2 -2
  17. package/dist/browser/dev/excalidraw-assets-dev/{chunk-2T2GU7NF.js → chunk-ZAYGSUHF.js} +2 -2
  18. package/dist/browser/dev/excalidraw-assets-dev/{chunk-Z3PH3V2B.js → chunk-ZQR5ML6Y.js} +26 -26
  19. package/dist/browser/dev/excalidraw-assets-dev/chunk-ZQR5ML6Y.js.map +7 -0
  20. package/dist/browser/dev/excalidraw-assets-dev/{classDiagram-30eddba6-QSLMH4JW.js → classDiagram-30eddba6-CUYIJICN.js} +5 -5
  21. package/dist/browser/dev/excalidraw-assets-dev/{classDiagram-v2-f2df5561-DY4DYQ5P.js → classDiagram-v2-f2df5561-K6WW6K73.js} +8 -8
  22. package/dist/browser/dev/excalidraw-assets-dev/{dist-Z46EOVOL.js → dist-ITJNUBZF.js} +2 -2
  23. package/dist/browser/dev/excalidraw-assets-dev/{en-5TCZHGGJ.js → en-BF4XUPIZ.js} +2 -2
  24. package/dist/browser/dev/excalidraw-assets-dev/{erDiagram-47591fe2-SOOJRTCB.js → erDiagram-47591fe2-XGAD7EEP.js} +4 -4
  25. package/dist/browser/dev/excalidraw-assets-dev/{flowDiagram-5540d9b9-AHGL4KPK.js → flowDiagram-5540d9b9-B6EOVNNO.js} +9 -9
  26. package/dist/browser/dev/excalidraw-assets-dev/{flowDiagram-v2-3b53844e-56LDZZWY.js → flowDiagram-v2-3b53844e-NUG24FJH.js} +9 -9
  27. package/dist/browser/dev/excalidraw-assets-dev/{flowchart-elk-definition-5fe447d6-27LUKRI6.js → flowchart-elk-definition-5fe447d6-25Y7PCBL.js} +5 -5
  28. package/dist/browser/dev/excalidraw-assets-dev/{ganttDiagram-9a3bba1f-EHGYGNG6.js → ganttDiagram-9a3bba1f-GNL6ZDTC.js} +2 -2
  29. package/dist/browser/dev/excalidraw-assets-dev/{gitGraphDiagram-96e6b4ee-AJQNBDW5.js → gitGraphDiagram-96e6b4ee-HNW52NVO.js} +2 -2
  30. package/dist/browser/dev/excalidraw-assets-dev/{image-EDKQZH7Z.js → image-LVS32KQQ.js} +2 -2
  31. package/dist/browser/dev/excalidraw-assets-dev/{image-OFRRV5MB.css → image-O66MQ7WQ.css} +1 -1
  32. package/dist/browser/dev/excalidraw-assets-dev/image-O66MQ7WQ.css.map +7 -0
  33. package/dist/browser/dev/excalidraw-assets-dev/{infoDiagram-bcd20f53-SWLLQVES.js → infoDiagram-bcd20f53-FWEUVFLT.js} +2 -2
  34. package/dist/browser/dev/excalidraw-assets-dev/{journeyDiagram-4fe6b3dc-7UAVCWOZ.js → journeyDiagram-4fe6b3dc-RZIUI7UG.js} +3 -3
  35. package/dist/browser/dev/excalidraw-assets-dev/{mindmap-definition-f354de21-SROW5KGM.js → mindmap-definition-f354de21-GBVN45GU.js} +3 -3
  36. package/dist/browser/dev/excalidraw-assets-dev/{pieDiagram-79897490-QKCI6NCB.js → pieDiagram-79897490-ECENNII6.js} +2 -2
  37. package/dist/browser/dev/excalidraw-assets-dev/{quadrantDiagram-62f64e94-LNYJZFC5.js → quadrantDiagram-62f64e94-ZMEOFVNL.js} +2 -2
  38. package/dist/browser/dev/excalidraw-assets-dev/{requirementDiagram-05bf5f74-ZZD7ZHFA.js → requirementDiagram-05bf5f74-FHZSFHCR.js} +4 -4
  39. package/dist/browser/dev/excalidraw-assets-dev/{sankeyDiagram-97764748-L75ZZ4UM.js → sankeyDiagram-97764748-VDKIKTA6.js} +2 -2
  40. package/dist/browser/dev/excalidraw-assets-dev/{sequenceDiagram-acc0e65c-6PCU7TDK.js → sequenceDiagram-acc0e65c-6JUSPVKX.js} +3 -3
  41. package/dist/browser/dev/excalidraw-assets-dev/{stateDiagram-0ff1cf1a-WM76WOPR.js → stateDiagram-0ff1cf1a-L3AKWENF.js} +5 -5
  42. package/dist/browser/dev/excalidraw-assets-dev/{stateDiagram-v2-9a9d610d-N4HZW3O2.js → stateDiagram-v2-9a9d610d-NU3GGMCH.js} +8 -8
  43. package/dist/browser/dev/excalidraw-assets-dev/{timeline-definition-fea2a41d-ZHGCAXGP.js → timeline-definition-fea2a41d-JGP7XCHW.js} +2 -2
  44. package/dist/browser/dev/excalidraw-assets-dev/{xychartDiagram-ab372869-2DLOVRAZ.js → xychartDiagram-ab372869-HLFHHF2I.js} +3 -3
  45. package/dist/browser/dev/index.css +72 -28
  46. package/dist/browser/dev/index.css.map +3 -3
  47. package/dist/browser/dev/index.js +2064 -1830
  48. package/dist/browser/dev/index.js.map +4 -4
  49. package/dist/browser/prod/excalidraw-assets/{blockDiagram-91b80b7a-ONPS22AM.js → blockDiagram-91b80b7a-FVCRVGN5.js} +1 -1
  50. package/dist/browser/prod/excalidraw-assets/{c4Diagram-b2a90758-XMIQY7ZT.js → c4Diagram-b2a90758-56CXO7GA.js} +1 -1
  51. package/dist/browser/prod/excalidraw-assets/{chunk-GCHQBOKV.js → chunk-635MQ3CK.js} +1 -1
  52. package/dist/browser/prod/excalidraw-assets/{chunk-P5M3G2RP.js → chunk-AIKXYJX3.js} +1 -1
  53. package/dist/browser/prod/excalidraw-assets/{chunk-E2YLWFZX.js → chunk-CR7VMNWC.js} +1 -1
  54. package/dist/browser/prod/excalidraw-assets/{chunk-WEYK4A2L.js → chunk-FFF2CSVG.js} +1 -1
  55. package/dist/browser/prod/excalidraw-assets/{chunk-R3HAIP6R.js → chunk-G4WDCSPE.js} +1 -1
  56. package/dist/browser/prod/excalidraw-assets/{chunk-HFOXJM22.js → chunk-HKZSHFLX.js} +1 -1
  57. package/dist/browser/prod/excalidraw-assets/{chunk-XIMFFJTE.js → chunk-IKCDYWMW.js} +1 -1
  58. package/dist/browser/prod/excalidraw-assets/{chunk-AHLLBBVJ.js → chunk-L5DS24G6.js} +1 -1
  59. package/dist/browser/prod/excalidraw-assets/{chunk-CQJF3C6G.js → chunk-MUNOKHUD.js} +1 -1
  60. package/dist/browser/prod/excalidraw-assets/{chunk-CTYINSWT.js → chunk-O4AI3NNG.js} +2 -2
  61. package/dist/browser/prod/excalidraw-assets/{chunk-NI6SYCUG.js → chunk-QOQYOOQ4.js} +1 -1
  62. package/dist/browser/prod/excalidraw-assets/chunk-SXBDZOS3.js +55 -0
  63. package/dist/browser/prod/excalidraw-assets/{chunk-I2PZFXTK.js → chunk-ZTIWFPBM.js} +21 -21
  64. package/dist/browser/prod/excalidraw-assets/{classDiagram-30eddba6-IEJXXCVX.js → classDiagram-30eddba6-BCUTAUMD.js} +1 -1
  65. package/dist/browser/prod/excalidraw-assets/{classDiagram-v2-f2df5561-7LZDSWOS.js → classDiagram-v2-f2df5561-6SOXSGQ2.js} +1 -1
  66. package/dist/browser/prod/excalidraw-assets/{dist-PIPZXALV.js → dist-54276HPL.js} +1 -1
  67. package/dist/browser/prod/excalidraw-assets/{en-LROPV2RN.js → en-N7CLNF6C.js} +1 -1
  68. package/dist/browser/prod/excalidraw-assets/{erDiagram-47591fe2-E5V666CF.js → erDiagram-47591fe2-RE6HB7RM.js} +1 -1
  69. package/dist/browser/prod/excalidraw-assets/{flowDiagram-5540d9b9-GMBRCYVF.js → flowDiagram-5540d9b9-ZNJZBERW.js} +1 -1
  70. package/dist/browser/prod/excalidraw-assets/{flowDiagram-v2-3b53844e-Z4HUWP6B.js → flowDiagram-v2-3b53844e-LY44JLQJ.js} +1 -1
  71. package/dist/browser/prod/excalidraw-assets/{flowchart-elk-definition-5fe447d6-5ZCYTX5N.js → flowchart-elk-definition-5fe447d6-TMTJ6Z7O.js} +1 -1
  72. package/dist/browser/prod/excalidraw-assets/{ganttDiagram-9a3bba1f-WM32OMT5.js → ganttDiagram-9a3bba1f-5O6EA6LX.js} +1 -1
  73. package/dist/browser/prod/excalidraw-assets/{gitGraphDiagram-96e6b4ee-CAKZ2U6E.js → gitGraphDiagram-96e6b4ee-UHYNM5DI.js} +1 -1
  74. package/dist/browser/prod/excalidraw-assets/image-VAGBVQ3G.js +1 -0
  75. package/dist/browser/prod/excalidraw-assets/{infoDiagram-bcd20f53-MUIKXGC4.js → infoDiagram-bcd20f53-BP77NQEH.js} +1 -1
  76. package/dist/browser/prod/excalidraw-assets/{journeyDiagram-4fe6b3dc-NYRV4HK2.js → journeyDiagram-4fe6b3dc-XMGKCMES.js} +1 -1
  77. package/dist/browser/prod/excalidraw-assets/{mindmap-definition-f354de21-MY55DRSM.js → mindmap-definition-f354de21-ZQRRBRWF.js} +1 -1
  78. package/dist/browser/prod/excalidraw-assets/{pieDiagram-79897490-47L6J6L2.js → pieDiagram-79897490-IGXEC2KX.js} +1 -1
  79. package/dist/browser/prod/excalidraw-assets/{quadrantDiagram-62f64e94-DF5C2GDT.js → quadrantDiagram-62f64e94-WTHHDYJL.js} +1 -1
  80. package/dist/browser/prod/excalidraw-assets/{requirementDiagram-05bf5f74-C4IMUBDN.js → requirementDiagram-05bf5f74-MV4OFRVW.js} +1 -1
  81. package/dist/browser/prod/excalidraw-assets/{sankeyDiagram-97764748-YHW7EUST.js → sankeyDiagram-97764748-ZGYUHEJT.js} +1 -1
  82. package/dist/browser/prod/excalidraw-assets/{sequenceDiagram-acc0e65c-H3XEHT32.js → sequenceDiagram-acc0e65c-IBSENK6W.js} +1 -1
  83. package/dist/browser/prod/excalidraw-assets/{stateDiagram-0ff1cf1a-Z5WB6Q3P.js → stateDiagram-0ff1cf1a-DB73XNZH.js} +1 -1
  84. package/dist/browser/prod/excalidraw-assets/{stateDiagram-v2-9a9d610d-T7OZETQC.js → stateDiagram-v2-9a9d610d-2OOBUPNR.js} +1 -1
  85. package/dist/browser/prod/excalidraw-assets/{timeline-definition-fea2a41d-VVC22BWF.js → timeline-definition-fea2a41d-P3NQQVDU.js} +1 -1
  86. package/dist/browser/prod/excalidraw-assets/{xychartDiagram-ab372869-JAXODQF7.js → xychartDiagram-ab372869-HI3XLK3Y.js} +1 -1
  87. package/dist/browser/prod/index.css +1 -1
  88. package/dist/browser/prod/index.js +24 -24
  89. package/dist/dev/{en-II4GK66F.json → en-UQDDYCH7.json} +5 -3
  90. package/dist/dev/index.css +72 -28
  91. package/dist/dev/index.css.map +3 -3
  92. package/dist/dev/index.js +5352 -3354
  93. package/dist/dev/index.js.map +4 -4
  94. package/dist/excalidraw/actions/actionAddToLibrary.d.ts +4 -4
  95. package/dist/excalidraw/actions/actionAddToLibrary.js +4 -3
  96. package/dist/excalidraw/actions/actionAlign.d.ts +22 -22
  97. package/dist/excalidraw/actions/actionAlign.js +7 -6
  98. package/dist/excalidraw/actions/actionBoundText.d.ts +10 -10
  99. package/dist/excalidraw/actions/actionBoundText.js +8 -4
  100. package/dist/excalidraw/actions/actionCanvas.d.ts +52 -52
  101. package/dist/excalidraw/actions/actionCanvas.js +19 -14
  102. package/dist/excalidraw/actions/actionClipboard.d.ts +24 -24
  103. package/dist/excalidraw/actions/actionClipboard.js +14 -13
  104. package/dist/excalidraw/actions/actionDeleteSelected.d.ts +10 -10
  105. package/dist/excalidraw/actions/actionDeleteSelected.js +6 -3
  106. package/dist/excalidraw/actions/actionDistribute.d.ts +10 -10
  107. package/dist/excalidraw/actions/actionDistribute.js +3 -2
  108. package/dist/excalidraw/actions/actionDuplicateSelection.d.ts +7 -8
  109. package/dist/excalidraw/actions/actionDuplicateSelection.js +7 -3
  110. package/dist/excalidraw/actions/actionElementLock.d.ts +9 -9
  111. package/dist/excalidraw/actions/actionElementLock.js +3 -2
  112. package/dist/excalidraw/actions/actionExport.d.ts +33 -33
  113. package/dist/excalidraw/actions/actionExport.js +15 -11
  114. package/dist/excalidraw/actions/actionFinalize.d.ts +9 -9
  115. package/dist/excalidraw/actions/actionFinalize.js +9 -5
  116. package/dist/excalidraw/actions/actionFlip.d.ts +10 -10
  117. package/dist/excalidraw/actions/actionFlip.js +12 -12
  118. package/dist/excalidraw/actions/actionFrame.d.ts +16 -171
  119. package/dist/excalidraw/actions/actionFrame.js +7 -6
  120. package/dist/excalidraw/actions/actionGroup.d.ts +12 -322
  121. package/dist/excalidraw/actions/actionGroup.js +9 -11
  122. package/dist/excalidraw/actions/actionHistory.d.ts +4 -3
  123. package/dist/excalidraw/actions/actionHistory.js +27 -28
  124. package/dist/excalidraw/actions/actionLinearEditor.d.ts +6 -4
  125. package/dist/excalidraw/actions/actionLinearEditor.js +21 -5
  126. package/dist/excalidraw/actions/actionLink.d.ts +5 -5
  127. package/dist/excalidraw/actions/actionLink.js +2 -1
  128. package/dist/excalidraw/actions/actionMenu.d.ts +8 -8
  129. package/dist/excalidraw/actions/actionMenu.js +4 -3
  130. package/dist/excalidraw/actions/actionNavigate.d.ts +4 -4
  131. package/dist/excalidraw/actions/actionNavigate.js +3 -2
  132. package/dist/excalidraw/actions/actionProperties.d.ts +34 -34
  133. package/dist/excalidraw/actions/actionProperties.js +18 -13
  134. package/dist/excalidraw/actions/actionSelectAll.d.ts +5 -5
  135. package/dist/excalidraw/actions/actionSelectAll.js +2 -1
  136. package/dist/excalidraw/actions/actionStyles.d.ts +9 -12
  137. package/dist/excalidraw/actions/actionStyles.js +4 -3
  138. package/dist/excalidraw/actions/actionToggleGridMode.d.ts +7 -5
  139. package/dist/excalidraw/actions/actionToggleGridMode.js +6 -2
  140. package/dist/excalidraw/actions/actionToggleObjectsSnapMode.d.ts +4 -4
  141. package/dist/excalidraw/actions/actionToggleObjectsSnapMode.js +2 -1
  142. package/dist/excalidraw/actions/actionToggleStats.d.ts +4 -4
  143. package/dist/excalidraw/actions/actionToggleStats.js +2 -1
  144. package/dist/excalidraw/actions/actionToggleViewMode.d.ts +4 -4
  145. package/dist/excalidraw/actions/actionToggleViewMode.js +2 -1
  146. package/dist/excalidraw/actions/actionToggleZenMode.d.ts +4 -4
  147. package/dist/excalidraw/actions/actionToggleZenMode.js +2 -1
  148. package/dist/excalidraw/actions/actionZindex.d.ts +23 -19
  149. package/dist/excalidraw/actions/actionZindex.js +9 -4
  150. package/dist/excalidraw/actions/manager.d.ts +5 -5
  151. package/dist/excalidraw/actions/register.d.ts +1 -1
  152. package/dist/excalidraw/actions/shortcuts.d.ts +2 -2
  153. package/dist/excalidraw/actions/shortcuts.js +1 -1
  154. package/dist/excalidraw/actions/types.d.ts +7 -7
  155. package/dist/excalidraw/align.d.ts +1 -1
  156. package/dist/excalidraw/analytics.js +1 -1
  157. package/dist/excalidraw/animated-trail.d.ts +2 -2
  158. package/dist/excalidraw/appState.d.ts +5 -5
  159. package/dist/excalidraw/change.d.ts +191 -0
  160. package/dist/excalidraw/change.js +894 -0
  161. package/dist/excalidraw/charts.d.ts +1 -1
  162. package/dist/excalidraw/clients.d.ts +2 -2
  163. package/dist/excalidraw/clients.js +1 -1
  164. package/dist/excalidraw/clipboard.d.ts +3 -3
  165. package/dist/excalidraw/colors.d.ts +1 -1
  166. package/dist/excalidraw/components/Actions.d.ts +3 -3
  167. package/dist/excalidraw/components/Actions.js +8 -5
  168. package/dist/excalidraw/components/App.d.ts +27 -13
  169. package/dist/excalidraw/components/App.js +362 -157
  170. package/dist/excalidraw/components/ColorPicker/ColorInput.d.ts +1 -1
  171. package/dist/excalidraw/components/ColorPicker/ColorInput.js +1 -1
  172. package/dist/excalidraw/components/ColorPicker/ColorPicker.d.ts +4 -4
  173. package/dist/excalidraw/components/ColorPicker/ColorPicker.js +1 -1
  174. package/dist/excalidraw/components/ColorPicker/Picker.d.ts +3 -3
  175. package/dist/excalidraw/components/ColorPicker/PickerColorList.d.ts +1 -1
  176. package/dist/excalidraw/components/ColorPicker/PickerHeading.d.ts +1 -1
  177. package/dist/excalidraw/components/ColorPicker/ShadeList.d.ts +1 -1
  178. package/dist/excalidraw/components/ColorPicker/TopPicks.d.ts +1 -1
  179. package/dist/excalidraw/components/ColorPicker/colorPickerUtils.d.ts +2 -2
  180. package/dist/excalidraw/components/ColorPicker/colorPickerUtils.js +1 -1
  181. package/dist/excalidraw/components/ColorPicker/keyboardNavHandlers.d.ts +2 -2
  182. package/dist/excalidraw/components/ColorPicker/keyboardNavHandlers.js +1 -1
  183. package/dist/excalidraw/components/CommandPalette/CommandPalette.d.ts +1 -1
  184. package/dist/excalidraw/components/CommandPalette/CommandPalette.js +28 -13
  185. package/dist/excalidraw/components/CommandPalette/defaultCommandPaletteItems.d.ts +1 -1
  186. package/dist/excalidraw/components/CommandPalette/types.d.ts +3 -3
  187. package/dist/excalidraw/components/ConfirmDialog.d.ts +1 -1
  188. package/dist/excalidraw/components/ContextMenu.d.ts +2 -2
  189. package/dist/excalidraw/components/ContextMenu.js +1 -1
  190. package/dist/excalidraw/components/DarkModeToggle.d.ts +1 -1
  191. package/dist/excalidraw/components/DarkModeToggle.js +3 -1
  192. package/dist/excalidraw/components/DefaultSidebar.d.ts +2 -2
  193. package/dist/excalidraw/components/DialogActionButton.d.ts +1 -1
  194. package/dist/excalidraw/components/EyeDropper.d.ts +2 -2
  195. package/dist/excalidraw/components/FollowMode/FollowMode.d.ts +1 -1
  196. package/dist/excalidraw/components/HelpDialog.js +8 -6
  197. package/dist/excalidraw/components/HintViewer.d.ts +1 -1
  198. package/dist/excalidraw/components/ImageExportDialog.d.ts +1 -1
  199. package/dist/excalidraw/components/InitializeApp.d.ts +2 -2
  200. package/dist/excalidraw/components/JSONExportDialog.d.ts +3 -3
  201. package/dist/excalidraw/components/LayerUI.d.ts +4 -4
  202. package/dist/excalidraw/components/LibraryMenu.d.ts +2 -2
  203. package/dist/excalidraw/components/LibraryMenuBrowseButton.d.ts +1 -1
  204. package/dist/excalidraw/components/LibraryMenuControlButtons.d.ts +1 -1
  205. package/dist/excalidraw/components/LibraryMenuHeaderContent.d.ts +2 -2
  206. package/dist/excalidraw/components/LibraryMenuItems.d.ts +1 -1
  207. package/dist/excalidraw/components/LibraryMenuSection.d.ts +5 -4
  208. package/dist/excalidraw/components/LibraryUnit.d.ts +2 -2
  209. package/dist/excalidraw/components/LoadingMessage.d.ts +1 -1
  210. package/dist/excalidraw/components/MagicSettings.js +2 -2
  211. package/dist/excalidraw/components/MobileMenu.d.ts +3 -3
  212. package/dist/excalidraw/components/Modal.d.ts +1 -1
  213. package/dist/excalidraw/components/OverwriteConfirm/OverwriteConfirmState.d.ts +1 -1
  214. package/dist/excalidraw/components/PasteChartDialog.d.ts +1 -1
  215. package/dist/excalidraw/components/PublishLibrary.d.ts +1 -1
  216. package/dist/excalidraw/components/RadioGroup.d.ts +2 -1
  217. package/dist/excalidraw/components/RadioGroup.js +1 -1
  218. package/dist/excalidraw/components/SVGLayer.d.ts +1 -1
  219. package/dist/excalidraw/components/Sidebar/Sidebar.d.ts +2 -2
  220. package/dist/excalidraw/components/Sidebar/Sidebar.js +1 -1
  221. package/dist/excalidraw/components/Sidebar/SidebarTab.d.ts +1 -1
  222. package/dist/excalidraw/components/Sidebar/SidebarTabTrigger.d.ts +1 -1
  223. package/dist/excalidraw/components/Sidebar/SidebarTrigger.d.ts +1 -1
  224. package/dist/excalidraw/components/Sidebar/common.d.ts +1 -1
  225. package/dist/excalidraw/components/Stack.d.ts +2 -2
  226. package/dist/excalidraw/components/Stats.d.ts +2 -2
  227. package/dist/excalidraw/components/TTDDialog/MermaidToExcalidraw.d.ts +1 -1
  228. package/dist/excalidraw/components/TTDDialog/MermaidToExcalidraw.js +6 -2
  229. package/dist/excalidraw/components/TTDDialog/TTDDialog.js +2 -2
  230. package/dist/excalidraw/components/TTDDialog/TTDDialogInput.d.ts +1 -1
  231. package/dist/excalidraw/components/TTDDialog/TTDDialogPanel.d.ts +1 -1
  232. package/dist/excalidraw/components/TTDDialog/TTDDialogPanels.d.ts +1 -1
  233. package/dist/excalidraw/components/TTDDialog/TTDDialogTabs.d.ts +1 -1
  234. package/dist/excalidraw/components/TTDDialog/TTDDialogTrigger.d.ts +1 -1
  235. package/dist/excalidraw/components/TTDDialog/common.d.ts +4 -4
  236. package/dist/excalidraw/components/TextField.d.ts +1 -1
  237. package/dist/excalidraw/components/Toast.d.ts +1 -1
  238. package/dist/excalidraw/components/ToolButton.d.ts +4 -2
  239. package/dist/excalidraw/components/ToolButton.js +1 -1
  240. package/dist/excalidraw/components/Trans.d.ts +1 -1
  241. package/dist/excalidraw/components/UserList.d.ts +1 -1
  242. package/dist/excalidraw/components/canvases/InteractiveCanvas.d.ts +2 -1
  243. package/dist/excalidraw/components/canvases/InteractiveCanvas.js +1 -0
  244. package/dist/excalidraw/components/canvases/StaticCanvas.d.ts +1 -1
  245. package/dist/excalidraw/components/dropdownMenu/DropdownMenuItem.js +2 -2
  246. package/dist/excalidraw/components/dropdownMenu/DropdownMenuItemContentRadio.d.ts +18 -0
  247. package/dist/excalidraw/components/dropdownMenu/DropdownMenuItemContentRadio.js +9 -0
  248. package/dist/excalidraw/components/footer/Footer.d.ts +2 -2
  249. package/dist/excalidraw/components/hyperlink/Hyperlink.d.ts +2 -2
  250. package/dist/excalidraw/components/hyperlink/Hyperlink.js +3 -3
  251. package/dist/excalidraw/components/hyperlink/helpers.d.ts +3 -3
  252. package/dist/excalidraw/components/hyperlink/helpers.js +2 -3
  253. package/dist/excalidraw/components/icons.d.ts +6 -1
  254. package/dist/excalidraw/components/icons.js +19 -5
  255. package/dist/excalidraw/components/main-menu/DefaultItems.d.ts +12 -2
  256. package/dist/excalidraw/components/main-menu/DefaultItems.js +38 -7
  257. package/dist/excalidraw/constants.d.ts +5 -5
  258. package/dist/excalidraw/constants.js +6 -3
  259. package/dist/excalidraw/context/ui-appState.d.ts +1 -1
  260. package/dist/excalidraw/cursor.d.ts +1 -1
  261. package/dist/excalidraw/data/EditorLocalStorage.d.ts +2 -2
  262. package/dist/excalidraw/data/blob.d.ts +5 -5
  263. package/dist/excalidraw/data/filesystem.d.ts +2 -1
  264. package/dist/excalidraw/data/index.d.ts +4 -4
  265. package/dist/excalidraw/data/json.d.ts +3 -3
  266. package/dist/excalidraw/data/library.d.ts +3 -3
  267. package/dist/excalidraw/data/magic.d.ts +3 -3
  268. package/dist/excalidraw/data/magic.js +2 -1
  269. package/dist/excalidraw/data/reconcile.d.ts +6 -0
  270. package/dist/excalidraw/data/reconcile.js +49 -0
  271. package/dist/excalidraw/data/resave.d.ts +2 -2
  272. package/dist/excalidraw/data/restore.d.ts +5 -5
  273. package/dist/excalidraw/data/restore.js +6 -7
  274. package/dist/excalidraw/data/transform.d.ts +4 -4
  275. package/dist/excalidraw/data/transform.js +12 -3
  276. package/dist/excalidraw/data/types.d.ts +3 -3
  277. package/dist/excalidraw/data/url.d.ts +1 -0
  278. package/dist/excalidraw/data/url.js +4 -1
  279. package/dist/excalidraw/element/ElementCanvasButtons.d.ts +1 -1
  280. package/dist/excalidraw/element/binding.d.ts +50 -9
  281. package/dist/excalidraw/element/binding.js +712 -155
  282. package/dist/excalidraw/element/bounds.d.ts +3 -4
  283. package/dist/excalidraw/element/bounds.js +0 -3
  284. package/dist/excalidraw/element/collision.d.ts +14 -19
  285. package/dist/excalidraw/element/collision.js +36 -713
  286. package/dist/excalidraw/element/containerCache.d.ts +1 -1
  287. package/dist/excalidraw/element/dragElements.d.ts +3 -3
  288. package/dist/excalidraw/element/embeddable.d.ts +9 -6
  289. package/dist/excalidraw/element/embeddable.js +98 -62
  290. package/dist/excalidraw/element/image.d.ts +2 -2
  291. package/dist/excalidraw/element/index.d.ts +1 -2
  292. package/dist/excalidraw/element/index.js +0 -1
  293. package/dist/excalidraw/element/linearElementEditor.d.ts +12 -12
  294. package/dist/excalidraw/element/linearElementEditor.js +7 -5
  295. package/dist/excalidraw/element/mutateElement.d.ts +3 -4
  296. package/dist/excalidraw/element/newElement.d.ts +4 -4
  297. package/dist/excalidraw/element/newElement.js +2 -1
  298. package/dist/excalidraw/element/resizeElements.d.ts +4 -4
  299. package/dist/excalidraw/element/resizeElements.js +110 -86
  300. package/dist/excalidraw/element/resizeTest.d.ts +7 -7
  301. package/dist/excalidraw/element/resizeTest.js +55 -8
  302. package/dist/excalidraw/element/showSelectedShapeActions.d.ts +2 -2
  303. package/dist/excalidraw/element/sizeHelpers.d.ts +2 -2
  304. package/dist/excalidraw/element/sizeHelpers.js +3 -0
  305. package/dist/excalidraw/element/sortElements.d.ts +1 -1
  306. package/dist/excalidraw/element/textElement.d.ts +5 -6
  307. package/dist/excalidraw/element/textElement.js +5 -34
  308. package/dist/excalidraw/element/textWysiwyg.d.ts +2 -2
  309. package/dist/excalidraw/element/transformHandles.d.ts +24 -6
  310. package/dist/excalidraw/element/transformHandles.js +24 -3
  311. package/dist/excalidraw/element/typeChecks.d.ts +4 -4
  312. package/dist/excalidraw/element/types.d.ts +26 -10
  313. package/dist/excalidraw/emitter.d.ts +1 -1
  314. package/dist/excalidraw/errors.d.ts +3 -0
  315. package/dist/excalidraw/errors.js +3 -0
  316. package/dist/excalidraw/fractionalIndex.d.ts +40 -0
  317. package/dist/excalidraw/fractionalIndex.js +241 -0
  318. package/dist/excalidraw/frame.d.ts +4 -4
  319. package/dist/excalidraw/gatransforms.d.ts +1 -1
  320. package/dist/excalidraw/gesture.d.ts +1 -1
  321. package/dist/excalidraw/groups.d.ts +5 -3
  322. package/dist/excalidraw/groups.js +17 -0
  323. package/dist/excalidraw/history.d.ts +35 -47
  324. package/dist/excalidraw/history.js +100 -167
  325. package/dist/excalidraw/hooks/useCreatePortalContainer.js +2 -1
  326. package/dist/excalidraw/hooks/useEmitter.d.ts +2 -0
  327. package/dist/excalidraw/hooks/useEmitter.js +13 -0
  328. package/dist/excalidraw/hooks/useLibraryItemSvg.d.ts +1 -1
  329. package/dist/excalidraw/i18n.d.ts +1 -1
  330. package/dist/excalidraw/index.d.ts +3 -1
  331. package/dist/excalidraw/index.js +2 -0
  332. package/dist/excalidraw/jotai.d.ts +1 -1
  333. package/dist/excalidraw/laser-trails.d.ts +3 -2
  334. package/dist/excalidraw/locales/en.json +5 -3
  335. package/dist/excalidraw/math.d.ts +2 -2
  336. package/dist/excalidraw/points.d.ts +1 -1
  337. package/dist/excalidraw/queue.d.ts +1 -1
  338. package/dist/excalidraw/renderer/helpers.d.ts +2 -2
  339. package/dist/excalidraw/renderer/helpers.js +2 -2
  340. package/dist/excalidraw/renderer/interactiveScene.d.ts +2 -2
  341. package/dist/excalidraw/renderer/interactiveScene.js +8 -7
  342. package/dist/excalidraw/renderer/renderElement.d.ts +3 -3
  343. package/dist/excalidraw/renderer/renderElement.js +5 -5
  344. package/dist/excalidraw/renderer/renderSnaps.d.ts +1 -1
  345. package/dist/excalidraw/renderer/renderSnaps.js +2 -1
  346. package/dist/excalidraw/renderer/staticScene.d.ts +1 -1
  347. package/dist/excalidraw/renderer/staticScene.js +14 -3
  348. package/dist/excalidraw/renderer/staticSvgScene.d.ts +4 -4
  349. package/dist/excalidraw/renderer/staticSvgScene.js +10 -0
  350. package/dist/excalidraw/scene/Fonts.d.ts +1 -1
  351. package/dist/excalidraw/scene/Renderer.d.ts +3 -3
  352. package/dist/excalidraw/scene/Scene.d.ts +9 -8
  353. package/dist/excalidraw/scene/Scene.js +31 -16
  354. package/dist/excalidraw/scene/Shape.d.ts +1 -1
  355. package/dist/excalidraw/scene/ShapeCache.d.ts +4 -4
  356. package/dist/excalidraw/scene/comparisons.d.ts +2 -2
  357. package/dist/excalidraw/scene/export.d.ts +2 -2
  358. package/dist/excalidraw/scene/export.js +5 -4
  359. package/dist/excalidraw/scene/scroll.d.ts +2 -2
  360. package/dist/excalidraw/scene/scrollbars.d.ts +3 -3
  361. package/dist/excalidraw/scene/selection.d.ts +2 -2
  362. package/dist/excalidraw/scene/types.d.ts +5 -4
  363. package/dist/excalidraw/scene/zoom.d.ts +1 -1
  364. package/dist/excalidraw/snapping.d.ts +4 -4
  365. package/dist/excalidraw/store.d.ts +99 -0
  366. package/dist/excalidraw/store.js +269 -0
  367. package/dist/excalidraw/types.d.ts +33 -19
  368. package/dist/excalidraw/utils.d.ts +11 -4
  369. package/dist/excalidraw/utils.js +8 -0
  370. package/dist/excalidraw/zindex.d.ts +4 -4
  371. package/dist/excalidraw/zindex.js +9 -13
  372. package/dist/prod/{en-II4GK66F.json → en-UQDDYCH7.json} +5 -3
  373. package/dist/prod/index.css +1 -1
  374. package/dist/prod/index.js +44 -44
  375. package/dist/utils/bbox.d.ts +2 -2
  376. package/dist/utils/collision.d.ts +4 -0
  377. package/dist/utils/collision.js +48 -0
  378. package/dist/utils/export.d.ts +2 -2
  379. package/dist/utils/geometry/geometry.d.ts +71 -0
  380. package/dist/utils/geometry/geometry.js +674 -0
  381. package/dist/utils/geometry/shape.d.ts +56 -0
  382. package/dist/utils/geometry/shape.js +168 -0
  383. package/dist/utils/withinBounds.d.ts +1 -1
  384. package/history.ts +163 -218
  385. package/package.json +2 -1
  386. package/dist/browser/dev/excalidraw-assets-dev/chunk-AK7SWNLN.js.map +0 -7
  387. package/dist/browser/dev/excalidraw-assets-dev/chunk-RWZVJAQU.js.map +0 -7
  388. package/dist/browser/dev/excalidraw-assets-dev/chunk-Z3PH3V2B.js.map +0 -7
  389. package/dist/browser/dev/excalidraw-assets-dev/image-OFRRV5MB.css.map +0 -7
  390. package/dist/browser/prod/excalidraw-assets/chunk-LL4GORAM.js +0 -55
  391. package/dist/browser/prod/excalidraw-assets/image-EFCJDJH3.js +0 -1
  392. /package/dist/browser/dev/excalidraw-assets-dev/{blockDiagram-91b80b7a-ACFH36JV.js.map → blockDiagram-91b80b7a-H47FTXHA.js.map} +0 -0
  393. /package/dist/browser/dev/excalidraw-assets-dev/{c4Diagram-b2a90758-QZ27YR47.js.map → c4Diagram-b2a90758-NNJK6GKC.js.map} +0 -0
  394. /package/dist/browser/dev/excalidraw-assets-dev/{chunk-HO2HMSK7.js.map → chunk-4KQVEBHW.js.map} +0 -0
  395. /package/dist/browser/dev/excalidraw-assets-dev/{chunk-USGV265L.js.map → chunk-53YI56GV.js.map} +0 -0
  396. /package/dist/browser/dev/excalidraw-assets-dev/{chunk-EDFX3S7X.js.map → chunk-A2WCJI4I.js.map} +0 -0
  397. /package/dist/browser/dev/excalidraw-assets-dev/{chunk-IX4V72YG.js.map → chunk-EFLPX7NE.js.map} +0 -0
  398. /package/dist/browser/dev/excalidraw-assets-dev/{chunk-MXVETLVM.js.map → chunk-JYIQCNWV.js.map} +0 -0
  399. /package/dist/browser/dev/excalidraw-assets-dev/{chunk-YZIOORVX.js.map → chunk-LVIQQW6F.js.map} +0 -0
  400. /package/dist/browser/dev/excalidraw-assets-dev/{chunk-6U7GQNJT.js.map → chunk-PXLO3FOU.js.map} +0 -0
  401. /package/dist/browser/dev/excalidraw-assets-dev/{chunk-7DACDEY3.js.map → chunk-TO2AW5PW.js.map} +0 -0
  402. /package/dist/browser/dev/excalidraw-assets-dev/{chunk-NJ77ZFNJ.js.map → chunk-VURILHLY.js.map} +0 -0
  403. /package/dist/browser/dev/excalidraw-assets-dev/{chunk-2T2GU7NF.js.map → chunk-ZAYGSUHF.js.map} +0 -0
  404. /package/dist/browser/dev/excalidraw-assets-dev/{classDiagram-30eddba6-QSLMH4JW.js.map → classDiagram-30eddba6-CUYIJICN.js.map} +0 -0
  405. /package/dist/browser/dev/excalidraw-assets-dev/{classDiagram-v2-f2df5561-DY4DYQ5P.js.map → classDiagram-v2-f2df5561-K6WW6K73.js.map} +0 -0
  406. /package/dist/browser/dev/excalidraw-assets-dev/{dist-Z46EOVOL.js.map → dist-ITJNUBZF.js.map} +0 -0
  407. /package/dist/browser/dev/excalidraw-assets-dev/{en-5TCZHGGJ.js.map → en-BF4XUPIZ.js.map} +0 -0
  408. /package/dist/browser/dev/excalidraw-assets-dev/{erDiagram-47591fe2-SOOJRTCB.js.map → erDiagram-47591fe2-XGAD7EEP.js.map} +0 -0
  409. /package/dist/browser/dev/excalidraw-assets-dev/{flowDiagram-5540d9b9-AHGL4KPK.js.map → flowDiagram-5540d9b9-B6EOVNNO.js.map} +0 -0
  410. /package/dist/browser/dev/excalidraw-assets-dev/{flowDiagram-v2-3b53844e-56LDZZWY.js.map → flowDiagram-v2-3b53844e-NUG24FJH.js.map} +0 -0
  411. /package/dist/browser/dev/excalidraw-assets-dev/{flowchart-elk-definition-5fe447d6-27LUKRI6.js.map → flowchart-elk-definition-5fe447d6-25Y7PCBL.js.map} +0 -0
  412. /package/dist/browser/dev/excalidraw-assets-dev/{ganttDiagram-9a3bba1f-EHGYGNG6.js.map → ganttDiagram-9a3bba1f-GNL6ZDTC.js.map} +0 -0
  413. /package/dist/browser/dev/excalidraw-assets-dev/{gitGraphDiagram-96e6b4ee-AJQNBDW5.js.map → gitGraphDiagram-96e6b4ee-HNW52NVO.js.map} +0 -0
  414. /package/dist/browser/dev/excalidraw-assets-dev/{image-EDKQZH7Z.js.map → image-LVS32KQQ.js.map} +0 -0
  415. /package/dist/browser/dev/excalidraw-assets-dev/{infoDiagram-bcd20f53-SWLLQVES.js.map → infoDiagram-bcd20f53-FWEUVFLT.js.map} +0 -0
  416. /package/dist/browser/dev/excalidraw-assets-dev/{journeyDiagram-4fe6b3dc-7UAVCWOZ.js.map → journeyDiagram-4fe6b3dc-RZIUI7UG.js.map} +0 -0
  417. /package/dist/browser/dev/excalidraw-assets-dev/{mindmap-definition-f354de21-SROW5KGM.js.map → mindmap-definition-f354de21-GBVN45GU.js.map} +0 -0
  418. /package/dist/browser/dev/excalidraw-assets-dev/{pieDiagram-79897490-QKCI6NCB.js.map → pieDiagram-79897490-ECENNII6.js.map} +0 -0
  419. /package/dist/browser/dev/excalidraw-assets-dev/{quadrantDiagram-62f64e94-LNYJZFC5.js.map → quadrantDiagram-62f64e94-ZMEOFVNL.js.map} +0 -0
  420. /package/dist/browser/dev/excalidraw-assets-dev/{requirementDiagram-05bf5f74-ZZD7ZHFA.js.map → requirementDiagram-05bf5f74-FHZSFHCR.js.map} +0 -0
  421. /package/dist/browser/dev/excalidraw-assets-dev/{sankeyDiagram-97764748-L75ZZ4UM.js.map → sankeyDiagram-97764748-VDKIKTA6.js.map} +0 -0
  422. /package/dist/browser/dev/excalidraw-assets-dev/{sequenceDiagram-acc0e65c-6PCU7TDK.js.map → sequenceDiagram-acc0e65c-6JUSPVKX.js.map} +0 -0
  423. /package/dist/browser/dev/excalidraw-assets-dev/{stateDiagram-0ff1cf1a-WM76WOPR.js.map → stateDiagram-0ff1cf1a-L3AKWENF.js.map} +0 -0
  424. /package/dist/browser/dev/excalidraw-assets-dev/{stateDiagram-v2-9a9d610d-N4HZW3O2.js.map → stateDiagram-v2-9a9d610d-NU3GGMCH.js.map} +0 -0
  425. /package/dist/browser/dev/excalidraw-assets-dev/{timeline-definition-fea2a41d-ZHGCAXGP.js.map → timeline-definition-fea2a41d-JGP7XCHW.js.map} +0 -0
  426. /package/dist/browser/dev/excalidraw-assets-dev/{xychartDiagram-ab372869-2DLOVRAZ.js.map → xychartDiagram-ab372869-HLFHHF2I.js.map} +0 -0
@@ -10,31 +10,33 @@ 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, } from "../constants";
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";
15
15
  import { exportCanvas, loadFromBlob } from "../data";
16
16
  import Library, { distributeLibraryItemsOnSquareGrid } from "../data/library";
17
17
  import { restore, restoreElements } from "../data/restore";
18
- import { dragNewElement, dragSelectedElements, duplicateElement, getCommonBounds, getCursorForResizingElement, getDragOffsetXY, getElementWithTransformHandleType, getNormalizedDimensions, getResizeArrowDirection, getResizeOffsetXY, getLockedLinearCursorAlignSize, getTransformHandleTypeFromCoords, hitTest, isHittingElementBoundingBoxWithoutHittingElement, isInvisiblySmallElement, isNonDeletedElement, isTextElement, newElement, newLinearElement, newTextElement, newImageElement, transformElements, updateTextElement, redrawTextBoundingBox, } from "../element";
19
- import { bindOrUnbindLinearElement, bindOrUnbindSelectedElements, fixBindingsAfterDeletion, fixBindingsAfterDuplication, getEligibleElementsForBinding, getHoveredElementForBinding, isBindingEnabled, isLinearElementSimpleAndAlreadyBound, maybeBindLinearElement, shouldEnableBindingForPointerEvent, unbindLinearElements, updateBoundElements, } from "../element/binding";
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 { bindOrUnbindLinearElement, bindOrUnbindLinearElements, fixBindingsAfterDeletion, fixBindingsAfterDuplication, getHoveredElementForBinding, isBindingEnabled, isLinearElementSimpleAndAlreadyBound, maybeBindLinearElement, shouldEnableBindingForPointerEvent, updateBoundElements, getSuggestedBindingsForArrows, } from "../element/binding";
20
20
  import { LinearElementEditor } from "../element/linearElementEditor";
21
21
  import { mutateElement, newElementWith } from "../element/mutateElement";
22
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, } from "../element/typeChecks";
23
+ import { hasBoundTextElement, isArrowElement, isBindingElement, isBindingElementType, isBoundToContainer, isFrameLikeElement, isImageElement, isEmbeddableElement, isInitializedImageElement, isLinearElement, isLinearElementType, isUsingAdaptiveRadius, isFrameElement, isIframeElement, isIframeLikeElement, isMagicFrameElement, isTextBindableContainer, } from "../element/typeChecks";
24
24
  import { getCenter, getDistance } from "../gesture";
25
25
  import { editGroupForSelectedElement, getElementsInGroup, getSelectedGroupIdForElement, getSelectedGroupIds, isElementInGroup, isSelectedViaGroup, selectGroupsForSelectedElements, } from "../groups";
26
- import History from "../history";
26
+ import { History } from "../history";
27
27
  import { defaultLang, getLanguage, languages, setLanguage, t } from "../i18n";
28
28
  import { CODES, shouldResizeFromCenter, shouldMaintainAspectRatio, shouldRotateWithDiscreteAngle, isArrowKey, KEYS, } from "../keys";
29
29
  import { isElementInViewport } from "../element/sizeHelpers";
30
30
  import { distance2d, getCornerRadius, getGridPoint, isPathALoop, } from "../math";
31
- import { calculateScrollCenter, getElementsAtPosition, getElementsWithinSelection, getNormalizedZoom, getSelectedElements, hasBackground, isSomeElementSelected, } from "../scene";
31
+ import { calculateScrollCenter, getElementsWithinSelection, getNormalizedZoom, getSelectedElements, hasBackground, isSomeElementSelected, } from "../scene";
32
32
  import Scene from "../scene/Scene";
33
33
  import { getStateForZoom } from "../scene/zoom";
34
34
  import { findShapeByKey } from "../shapes";
35
- import { debounce, distance, getFontString, getNearestScrollableContainer, isInputLike, isToolIcon, isWritableElement, sceneCoordsToViewportCoords, tupleToCoors, viewportCoordsToSceneCoords, wrapEvent, updateObject, updateActiveTool, getShortcutKey, isTransparent, easeToValuesRAF, muteFSAbortError, isTestEnv, easeOut, updateStable, addEventListener, normalizeEOL, getDateTime, } from "../utils";
35
+ import { getClosedCurveShape, getCurveShape, getEllipseShape, getFreedrawShape, getPolygonShape, getSelectionBoxShape, } from "../../utils/geometry/shape";
36
+ import { isPointInShape } from "../../utils/collision";
37
+ 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";
36
38
  import { createSrcDoc, embeddableURLValidator, maybeParseEmbedSrc, getEmbedLink, } from "../element/embeddable";
37
- import { ContextMenu, CONTEXT_MENU_SEPARATOR, } from "./ContextMenu";
39
+ import { ContextMenu, CONTEXT_MENU_SEPARATOR } from "./ContextMenu";
38
40
  import LayerUI from "./LayerUI";
39
41
  import { Toast } from "./Toast";
40
42
  import { actionToggleViewMode } from "../actions/actionToggleViewMode";
@@ -42,8 +44,7 @@ import { dataURLToFile, generateIdFromFile, getDataURL, getFileFromEvent, ImageU
42
44
  import { getInitializedImageElements, loadHTMLImageElement, normalizeSVG, updateImageCache as _updateImageCache, } from "../element/image";
43
45
  import throttle from "lodash.throttle";
44
46
  import { fileOpen } from "../data/filesystem";
45
- import { bindTextToShapeAfterDuplication, getApproxMinLineHeight, getApproxMinLineWidth, getBoundTextElement, getContainerCenter, getContainerElement, getDefaultLineHeight, getLineHeightInPx, getTextBindableContainerAtPosition, isMeasureTextSupported, isValidTextContainer, } from "../element/textElement";
46
- import { isHittingElementNotConsideringBoundingBox } from "../element/collision";
47
+ import { bindTextToShapeAfterDuplication, getApproxMinLineHeight, getApproxMinLineWidth, getBoundTextElement, getContainerCenter, getContainerElement, getDefaultLineHeight, getLineHeightInPx, isMeasureTextSupported, isValidTextContainer, } from "../element/textElement";
47
48
  import { showHyperlinkTooltip, hideHyperlinkToolip, Hyperlink, } from "../components/hyperlink/Hyperlink";
48
49
  import { isLocalLink, normalizeLink, toValidURL } from "../data/url";
49
50
  import { shouldShowBoundingBox } from "../element/transformHandles";
@@ -61,7 +62,7 @@ import { getSnapLinesAtPointer, snapDraggedElements, isActiveToolNonLinearSnappa
61
62
  import { actionWrapTextInContainer } from "../actions/actionBoundText";
62
63
  import BraveMeasureTextError from "./BraveMeasureTextError";
63
64
  import { activeEyeDropperAtom } from "./EyeDropper";
64
- import { convertToExcalidrawElements, } from "../data/transform";
65
+ import { convertToExcalidrawElements } from "../data/transform";
65
66
  import { isSidebarDockedAtom } from "./Sidebar/Sidebar";
66
67
  import { StaticCanvas, InteractiveCanvas } from "./canvases";
67
68
  import { Renderer } from "../scene/Renderer";
@@ -77,13 +78,16 @@ import { ElementCanvasButton } from "./MagicButton";
77
78
  import { MagicIcon, copyIcon, fullscreenIcon } from "./icons";
78
79
  import { EditorLocalStorage } from "../data/EditorLocalStorage";
79
80
  import FollowMode from "./FollowMode/FollowMode";
81
+ import { Store, StoreAction } from "../store";
80
82
  import { AnimationFrameHandler } from "../animation-frame-handler";
81
83
  import { AnimatedTrail } from "../animated-trail";
82
84
  import { LaserTrails } from "../laser-trails";
83
85
  import { withBatchedUpdates, withBatchedUpdatesThrottled } from "../reactUtils";
84
86
  import { getRenderOpacity } from "../renderer/renderElement";
87
+ import { hitElementBoundText, hitElementBoundingBoxOnly, hitElementItself, shouldTestInside, } from "../element/collision";
85
88
  import { textWysiwyg } from "../element/textWysiwyg";
86
89
  import { isOverScrollBars } from "../scene/scrollbars";
90
+ import { syncInvalidIndices, syncMovedIndices } from "../fractionalIndex";
87
91
  import { isPointHittingLink, isPointHittingLinkIcon, } from "./hyperlink/helpers";
88
92
  import { getShortcutFromShortcutName } from "../actions/shortcuts";
89
93
  const AppContext = React.createContext(null);
@@ -165,6 +169,7 @@ class App extends React.Component {
165
169
  library;
166
170
  libraryItemsFromStorage;
167
171
  id;
172
+ store;
168
173
  history;
169
174
  excalidrawContainerValue;
170
175
  files = {};
@@ -236,6 +241,8 @@ class App extends React.Component {
236
241
  this.canvas = document.createElement("canvas");
237
242
  this.rc = rough.canvas(this.canvas);
238
243
  this.renderer = new Renderer(this.scene);
244
+ this.store = new Store();
245
+ this.history = new History();
239
246
  if (excalidrawAPI) {
240
247
  const api = {
241
248
  updateScene: this.updateScene,
@@ -285,8 +292,8 @@ class App extends React.Component {
285
292
  });
286
293
  this.history = new History();
287
294
  this.actionManager.registerAll(actions);
288
- this.actionManager.registerAction(createUndoAction(this.history));
289
- this.actionManager.registerAction(createRedoAction(this.history));
295
+ this.actionManager.registerAction(createUndoAction(this.history, this.store));
296
+ this.actionManager.registerAction(createRedoAction(this.history, this.store));
290
297
  }
291
298
  onWindowMessage(event) {
292
299
  if (event.origin !== "https://player.vimeo.com" &&
@@ -495,7 +502,7 @@ class App extends React.Component {
495
502
  html, body {
496
503
  width: 100%;
497
504
  height: 100%;
498
- color: ${this.state.theme === "dark" ? "white" : "black"};
505
+ color: ${this.state.theme === THEME.DARK ? "white" : "black"};
499
506
  }
500
507
  body {
501
508
  display: flex;
@@ -650,7 +657,7 @@ class App extends React.Component {
650
657
  ? src.srcdoc(this.state.theme)
651
658
  : undefined, src: src?.type !== "document" ? src?.link ?? "" : undefined,
652
659
  // https://stackoverflow.com/q/18470015
653
- scrolling: "no", referrerPolicy: "no-referrer-when-downgrade", title: "Excalidraw Embedded Content", allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture", allowFullScreen: true, sandbox: "allow-same-origin allow-scripts allow-forms allow-popups allow-popups-to-escape-sandbox allow-presentation allow-downloads" })) })] }) }, el.id));
660
+ scrolling: "no", referrerPolicy: "no-referrer-when-downgrade", title: "Excalidraw Embedded Content", allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture", allowFullScreen: true, sandbox: `${src?.sandbox?.allowSameOrigin ? "allow-same-origin" : ""} allow-scripts allow-forms allow-popups allow-popups-to-escape-sandbox allow-presentation allow-downloads` })) })] }) }, el.id));
654
661
  }) }));
655
662
  }
656
663
  getFrameNameDOMId = (frameElement) => {
@@ -692,7 +699,7 @@ class App extends React.Component {
692
699
  if (!this.state.frameRendering.enabled || !this.state.frameRendering.name) {
693
700
  return null;
694
701
  }
695
- const isDarkTheme = this.state.theme === "dark";
702
+ const isDarkTheme = this.state.theme === THEME.DARK;
696
703
  let frameIndex = 0;
697
704
  let magicFrameIndex = 0;
698
705
  return this.scene.getNonDeletedFramesLikes().map((f) => {
@@ -874,7 +881,7 @@ class App extends React.Component {
874
881
  canvasBackgroundColor: this.state.viewBackgroundColor,
875
882
  embedsValidationStatus: this.embedsValidationStatus,
876
883
  elementsPendingErasure: this.elementsPendingErasure,
877
- } }), _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, 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()] }) }) }) }) }) }) }) }));
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
885
  }
879
886
  focusContainer = () => {
880
887
  this.excalidrawContainerRef.current?.focus();
@@ -1122,7 +1129,7 @@ class App extends React.Component {
1122
1129
  opacity: 100,
1123
1130
  locked: false,
1124
1131
  });
1125
- this.scene.addNewElement(frame);
1132
+ this.scene.insertElement(frame);
1126
1133
  for (const child of selectedElements) {
1127
1134
  mutateElement(child, { frameId: frame.id });
1128
1135
  }
@@ -1146,13 +1153,13 @@ class App extends React.Component {
1146
1153
  if (shouldUpdateStrokeColor) {
1147
1154
  this.syncActionResult({
1148
1155
  appState: { ...this.state, currentItemStrokeColor: color },
1149
- commitToHistory: true,
1156
+ storeAction: StoreAction.CAPTURE,
1150
1157
  });
1151
1158
  }
1152
1159
  else {
1153
1160
  this.syncActionResult({
1154
1161
  appState: { ...this.state, currentItemBackgroundColor: color },
1155
- commitToHistory: true,
1162
+ storeAction: StoreAction.CAPTURE,
1156
1163
  });
1157
1164
  }
1158
1165
  }
@@ -1166,6 +1173,7 @@ class App extends React.Component {
1166
1173
  }
1167
1174
  return el;
1168
1175
  }),
1176
+ storeAction: StoreAction.CAPTURE,
1169
1177
  });
1170
1178
  }
1171
1179
  },
@@ -1185,10 +1193,13 @@ class App extends React.Component {
1185
1193
  editingElement = element;
1186
1194
  }
1187
1195
  });
1188
- this.scene.replaceAllElements(actionResult.elements);
1189
- if (actionResult.commitToHistory) {
1190
- this.history.resumeRecording();
1196
+ if (actionResult.storeAction === StoreAction.UPDATE) {
1197
+ this.store.shouldUpdateSnapshot();
1191
1198
  }
1199
+ else if (actionResult.storeAction === StoreAction.CAPTURE) {
1200
+ this.store.shouldCaptureIncrement();
1201
+ }
1202
+ this.scene.replaceAllElements(actionResult.elements);
1192
1203
  }
1193
1204
  if (actionResult.files) {
1194
1205
  this.files = actionResult.replaceFiles
@@ -1197,8 +1208,11 @@ class App extends React.Component {
1197
1208
  this.addNewImagesToImageCache();
1198
1209
  }
1199
1210
  if (actionResult.appState || editingElement || this.state.contextMenu) {
1200
- if (actionResult.commitToHistory) {
1201
- this.history.resumeRecording();
1211
+ if (actionResult.storeAction === StoreAction.UPDATE) {
1212
+ this.store.shouldUpdateSnapshot();
1213
+ }
1214
+ else if (actionResult.storeAction === StoreAction.CAPTURE) {
1215
+ this.store.shouldCaptureIncrement();
1202
1216
  }
1203
1217
  let viewModeEnabled = actionResult?.appState?.viewModeEnabled || false;
1204
1218
  let zenModeEnabled = actionResult?.appState?.zenModeEnabled || false;
@@ -1237,10 +1251,6 @@ class App extends React.Component {
1237
1251
  name,
1238
1252
  errorMessage,
1239
1253
  });
1240
- }, () => {
1241
- if (actionResult.syncHistory) {
1242
- this.history.setCurrentState(this.state, this.scene.getElementsIncludingDeleted());
1243
- }
1244
1254
  });
1245
1255
  }
1246
1256
  });
@@ -1258,6 +1268,9 @@ class App extends React.Component {
1258
1268
  resetHistory = () => {
1259
1269
  this.history.clear();
1260
1270
  };
1271
+ resetStore = () => {
1272
+ this.store.clear();
1273
+ };
1261
1274
  /**
1262
1275
  * Resets scene & history.
1263
1276
  * ! Do not use to clear scene user action !
@@ -1269,6 +1282,7 @@ class App extends React.Component {
1269
1282
  isLoading: opts?.resetLoadingState ? false : state.isLoading,
1270
1283
  theme: this.state.theme,
1271
1284
  }));
1285
+ this.resetStore();
1272
1286
  this.resetHistory();
1273
1287
  });
1274
1288
  initializeScene = async () => {
@@ -1343,10 +1357,11 @@ class App extends React.Component {
1343
1357
  // text elements on canvas, and rerender them once done. This also
1344
1358
  // seems faster even in browsers that do fire the loadingdone event.
1345
1359
  this.fonts.loadFontsForElements(scene.elements);
1360
+ this.resetStore();
1346
1361
  this.resetHistory();
1347
1362
  this.syncActionResult({
1348
1363
  ...scene,
1349
- commitToHistory: true,
1364
+ storeAction: StoreAction.UPDATE,
1350
1365
  });
1351
1366
  };
1352
1367
  isMobileBreakpoint = (width, height) => {
@@ -1417,8 +1432,15 @@ class App extends React.Component {
1417
1432
  configurable: true,
1418
1433
  value: this.history,
1419
1434
  },
1435
+ store: {
1436
+ configurable: true,
1437
+ value: this.store,
1438
+ },
1420
1439
  });
1421
1440
  }
1441
+ this.store.onStoreIncrementEmitter.on((increment) => {
1442
+ this.history.record(increment.elementsChange, increment.appStateChange);
1443
+ });
1422
1444
  this.scene.addCallback(this.onSceneUpdated);
1423
1445
  this.addEventListeners();
1424
1446
  if (this.props.autoFocus && this.excalidrawContainerRef.current) {
@@ -1468,6 +1490,7 @@ class App extends React.Component {
1468
1490
  this.laserTrails.stop();
1469
1491
  this.eraserTrail.stop();
1470
1492
  this.onChangeEmitter.clear();
1493
+ this.store.onStoreIncrementEmitter.clear();
1471
1494
  ShapeCache.destroy();
1472
1495
  SnapCache.destroy();
1473
1496
  clearTimeout(touchTimeout);
@@ -1535,7 +1558,8 @@ class App extends React.Component {
1535
1558
  componentDidUpdate(prevProps, prevState) {
1536
1559
  this.updateEmbeddables();
1537
1560
  const elements = this.scene.getElementsIncludingDeleted();
1538
- const elementsMap = this.scene.getNonDeletedElementsMap();
1561
+ const elementsMap = this.scene.getElementsMapIncludingDeleted();
1562
+ const nonDeletedElementsMap = this.scene.getNonDeletedElementsMap();
1539
1563
  if (!this.state.showWelcomeScreen && !elements.length) {
1540
1564
  this.setState({ showWelcomeScreen: true });
1541
1565
  }
@@ -1608,10 +1632,10 @@ class App extends React.Component {
1608
1632
  gridSize: this.props.gridModeEnabled ? GRID_SIZE : null,
1609
1633
  });
1610
1634
  }
1611
- this.excalidrawContainerRef.current?.classList.toggle("theme--dark", this.state.theme === "dark");
1635
+ this.excalidrawContainerRef.current?.classList.toggle("theme--dark", this.state.theme === THEME.DARK);
1612
1636
  if (this.state.editingLinearElement &&
1613
1637
  !this.state.selectedElementIds[this.state.editingLinearElement.elementId]) {
1614
- // defer so that the commitToHistory flag isn't reset via current update
1638
+ // defer so that the storeAction flag isn't reset via current update
1615
1639
  setTimeout(() => {
1616
1640
  // execute only if the condition still holds when the deferred callback
1617
1641
  // executes (it can be scheduled multiple times depending on how
@@ -1636,9 +1660,9 @@ class App extends React.Component {
1636
1660
  multiElement != null &&
1637
1661
  isBindingEnabled(this.state) &&
1638
1662
  isBindingElement(multiElement, false)) {
1639
- maybeBindLinearElement(multiElement, this.state, this.scene, tupleToCoors(LinearElementEditor.getPointAtIndexGlobalCoordinates(multiElement, -1, elementsMap)), elementsMap);
1663
+ maybeBindLinearElement(multiElement, this.state, tupleToCoors(LinearElementEditor.getPointAtIndexGlobalCoordinates(multiElement, -1, nonDeletedElementsMap)), this);
1640
1664
  }
1641
- this.history.record(this.state, elements);
1665
+ this.store.commit(elementsMap, this.state);
1642
1666
  // Do not notify consumers if we're still loading the scene. Among other
1643
1667
  // potential issues, this fixes a case where the tab isn't focused during
1644
1668
  // init, which would trigger onChange with empty elements, which would then
@@ -1894,16 +1918,15 @@ class App extends React.Component {
1894
1918
  }), {
1895
1919
  randomizeSeed: !opts.retainSeed,
1896
1920
  });
1897
- const allElements = [
1898
- ...this.scene.getElementsIncludingDeleted(),
1899
- ...newElements,
1900
- ];
1921
+ const prevElements = this.scene.getElementsIncludingDeleted();
1922
+ const nextElements = [...prevElements, ...newElements];
1923
+ syncMovedIndices(nextElements, arrayToMap(newElements));
1901
1924
  const topLayerFrame = this.getTopLayerFrameAtSceneCoords({ x, y });
1902
1925
  if (topLayerFrame) {
1903
1926
  const eligibleElements = filterElementsEligibleAsFrameChildren(newElements, topLayerFrame);
1904
- addElementsToFrame(allElements, eligibleElements, topLayerFrame);
1927
+ addElementsToFrame(nextElements, eligibleElements, topLayerFrame);
1905
1928
  }
1906
- this.scene.replaceAllElements(allElements);
1929
+ this.scene.replaceAllElements(nextElements);
1907
1930
  newElements.forEach((newElement) => {
1908
1931
  if (isTextElement(newElement) && isBoundToContainer(newElement)) {
1909
1932
  const container = getContainerElement(newElement, this.scene.getElementsMapIncludingDeleted());
@@ -1913,7 +1936,7 @@ class App extends React.Component {
1913
1936
  if (opts.files) {
1914
1937
  this.files = { ...this.files, ...opts.files };
1915
1938
  }
1916
- this.history.resumeRecording();
1939
+ this.store.shouldCaptureIncrement();
1917
1940
  const nextElementsToSelect = excludeElementsInFramesFromSelection(newElements);
1918
1941
  this.setState({
1919
1942
  ...this.state,
@@ -2071,16 +2094,7 @@ class App extends React.Component {
2071
2094
  if (textElements.length === 0) {
2072
2095
  return;
2073
2096
  }
2074
- const frameId = textElements[0].frameId;
2075
- if (frameId) {
2076
- this.scene.insertElementsAtIndex(textElements, this.scene.getElementIndex(frameId));
2077
- }
2078
- else {
2079
- this.scene.replaceAllElements([
2080
- ...this.scene.getElementsIncludingDeleted(),
2081
- ...textElements,
2082
- ]);
2083
- }
2097
+ this.scene.insertElements(textElements);
2084
2098
  this.setState({
2085
2099
  selectedElementIds: makeNextSelectedElementIds(Object.fromEntries(textElements.map((el) => [el.id, true])), this.state),
2086
2100
  });
@@ -2096,7 +2110,7 @@ class App extends React.Component {
2096
2110
  });
2097
2111
  PLAIN_PASTE_TOAST_SHOWN = true;
2098
2112
  }
2099
- this.history.resumeRecording();
2113
+ this.store.shouldCaptureIncrement();
2100
2114
  }
2101
2115
  setAppState = (state, callback) => {
2102
2116
  this.setState(state, callback);
@@ -2282,14 +2296,31 @@ class App extends React.Component {
2282
2296
  this.addNewImagesToImageCache();
2283
2297
  });
2284
2298
  updateScene = withBatchedUpdates((sceneData) => {
2285
- if (sceneData.commitToHistory) {
2286
- this.history.resumeRecording();
2299
+ const nextElements = syncInvalidIndices(sceneData.elements ?? []);
2300
+ if (sceneData.storeAction && sceneData.storeAction !== StoreAction.NONE) {
2301
+ const prevCommittedAppState = this.store.snapshot.appState;
2302
+ const prevCommittedElements = this.store.snapshot.elements;
2303
+ const nextCommittedAppState = sceneData.appState
2304
+ ? Object.assign({}, prevCommittedAppState, sceneData.appState) // new instance, with partial appstate applied to previously captured one, including hidden prop inside `prevCommittedAppState`
2305
+ : prevCommittedAppState;
2306
+ const nextCommittedElements = sceneData.elements
2307
+ ? this.store.filterUncomittedElements(this.scene.getElementsMapIncludingDeleted(), // Only used to detect uncomitted local elements
2308
+ arrayToMap(nextElements))
2309
+ : prevCommittedElements;
2310
+ // WARN: store action always performs deep clone of changed elements, for ephemeral remote updates (i.e. remote dragging, resizing, drawing) we might consider doing something smarter
2311
+ // do NOT schedule store actions (execute after re-render), as it might cause unexpected concurrency issues if not handled well
2312
+ if (sceneData.storeAction === StoreAction.CAPTURE) {
2313
+ this.store.captureIncrement(nextCommittedElements, nextCommittedAppState);
2314
+ }
2315
+ else if (sceneData.storeAction === StoreAction.UPDATE) {
2316
+ this.store.updateSnapshot(nextCommittedElements, nextCommittedAppState);
2317
+ }
2287
2318
  }
2288
2319
  if (sceneData.appState) {
2289
2320
  this.setState(sceneData.appState);
2290
2321
  }
2291
2322
  if (sceneData.elements) {
2292
- this.scene.replaceAllElements(sceneData.elements);
2323
+ this.scene.replaceAllElements(nextElements);
2293
2324
  }
2294
2325
  if (sceneData.collaborators) {
2295
2326
  this.setState({ collaborators: sceneData.collaborators });
@@ -2355,8 +2386,7 @@ class App extends React.Component {
2355
2386
  !event.altKey) {
2356
2387
  this.setToast({
2357
2388
  message: t("commandPalette.shortcutHint", {
2358
- shortcutOne: getShortcutFromShortcutName("commandPalette"),
2359
- shortcutTwo: getShortcutFromShortcutName("commandPalette", 1),
2389
+ shortcut: getShortcutFromShortcutName("commandPalette"),
2360
2390
  }),
2361
2391
  });
2362
2392
  event.preventDefault();
@@ -2464,7 +2494,9 @@ class App extends React.Component {
2464
2494
  simultaneouslyUpdated: selectedElements,
2465
2495
  });
2466
2496
  });
2467
- this.maybeSuggestBindingForAll(selectedElements);
2497
+ this.setState({
2498
+ suggestedBindings: getSuggestedBindingsForArrows(selectedElements, this),
2499
+ });
2468
2500
  event.preventDefault();
2469
2501
  }
2470
2502
  else if (event.key === KEYS.ENTER) {
@@ -2476,7 +2508,7 @@ class App extends React.Component {
2476
2508
  if (!this.state.editingLinearElement ||
2477
2509
  this.state.editingLinearElement.elementId !==
2478
2510
  selectedElements[0].id) {
2479
- this.history.resumeRecording();
2511
+ this.store.shouldCaptureIncrement();
2480
2512
  this.setState({
2481
2513
  editingLinearElement: new LinearElementEditor(selectedElement),
2482
2514
  });
@@ -2602,11 +2634,7 @@ class App extends React.Component {
2602
2634
  this.setState({ isBindingEnabled: true });
2603
2635
  }
2604
2636
  if (isArrowKey(event.key)) {
2605
- const selectedElements = this.scene.getSelectedElements(this.state);
2606
- const elementsMap = this.scene.getNonDeletedElementsMap();
2607
- isBindingEnabled(this.state)
2608
- ? bindOrUnbindSelectedElements(selectedElements, this.scene.getNonDeletedElements(), elementsMap)
2609
- : unbindLinearElements(selectedElements, elementsMap);
2637
+ bindOrUnbindLinearElements(this.scene.getSelectedElements(this.state).filter(isLinearElement), this, isBindingEnabled(this.state), this.state.selectedLinearElement?.selectedPointsIndices ?? []);
2610
2638
  this.setState({ suggestedBindings: [] });
2611
2639
  }
2612
2640
  });
@@ -2644,6 +2672,9 @@ class App extends React.Component {
2644
2672
  originSnapOffset: null,
2645
2673
  activeEmbeddable: null,
2646
2674
  };
2675
+ if (nextActiveTool.type === "freedraw") {
2676
+ this.store.shouldCaptureIncrement();
2677
+ }
2647
2678
  if (nextActiveTool.type !== "selection") {
2648
2679
  return {
2649
2680
  ...prevState,
@@ -2797,7 +2828,7 @@ class App extends React.Component {
2797
2828
  ]);
2798
2829
  }
2799
2830
  if (!isDeleted || isExistingElement) {
2800
- this.history.resumeRecording();
2831
+ this.store.shouldCaptureIncrement();
2801
2832
  }
2802
2833
  this.setState({
2803
2834
  draggingElement: null,
@@ -2835,6 +2866,57 @@ class App extends React.Component {
2835
2866
  }
2836
2867
  return null;
2837
2868
  }
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
+ }
2838
2920
  getElementAtPosition(x, y, opts) {
2839
2921
  const allHitElements = this.getElementsAtPosition(x, y, opts?.includeBoundTextElement, opts?.includeLockedElements);
2840
2922
  if (allHitElements.length > 1) {
@@ -2848,9 +2930,20 @@ class App extends React.Component {
2848
2930
  const elementWithHighestZIndex = allHitElements[allHitElements.length - 1];
2849
2931
  // If we're hitting element with highest z-index only on its bounding box
2850
2932
  // while also hitting other element figure, the latter should be considered.
2851
- return isHittingElementBoundingBoxWithoutHittingElement(elementWithHighestZIndex, this.state, this.frameNameBoundsCache, x, y, this.scene.getNonDeletedElementsMap())
2852
- ? allHitElements[allHitElements.length - 2]
2853
- : elementWithHighestZIndex;
2933
+ return hitElementItself({
2934
+ x,
2935
+ y,
2936
+ element: elementWithHighestZIndex,
2937
+ shape: this.getElementShape(elementWithHighestZIndex),
2938
+ // when overlapping, we would like to be more precise
2939
+ // this also avoids the need to update past tests
2940
+ threshold: this.getElementHitThreshold() / 2,
2941
+ frameNameBound: isFrameLikeElement(elementWithHighestZIndex)
2942
+ ? this.frameNameBoundsCache.get(elementWithHighestZIndex)
2943
+ : null,
2944
+ })
2945
+ ? elementWithHighestZIndex
2946
+ : allHitElements[allHitElements.length - 2];
2854
2947
  }
2855
2948
  if (allHitElements.length === 1) {
2856
2949
  return allHitElements[0];
@@ -2858,15 +2951,17 @@ class App extends React.Component {
2858
2951
  return null;
2859
2952
  }
2860
2953
  getElementsAtPosition(x, y, includeBoundTextElement = false, includeLockedElements = false) {
2861
- const elements = includeBoundTextElement && includeLockedElements
2954
+ const iframeLikes = [];
2955
+ const elementsMap = this.scene.getNonDeletedElementsMap();
2956
+ const elements = (includeBoundTextElement && includeLockedElements
2862
2957
  ? this.scene.getNonDeletedElements()
2863
2958
  : this.scene
2864
2959
  .getNonDeletedElements()
2865
2960
  .filter((element) => (includeLockedElements || !element.locked) &&
2866
2961
  (includeBoundTextElement ||
2867
- !(isTextElement(element) && element.containerId)));
2868
- const elementsMap = this.scene.getNonDeletedElementsMap();
2869
- return getElementsAtPosition(elements, (element) => hitTest(element, this.state, this.frameNameBoundsCache, x, y, elementsMap)).filter((element) => {
2962
+ !(isTextElement(element) && element.containerId))))
2963
+ .filter((el) => this.hitElement(x, y, el))
2964
+ .filter((element) => {
2870
2965
  // hitting a frame's element from outside the frame is not considered a hit
2871
2966
  const containingFrame = getContainingFrame(element, elementsMap);
2872
2967
  return containingFrame &&
@@ -2874,8 +2969,81 @@ class App extends React.Component {
2874
2969
  this.state.frameRendering.clip
2875
2970
  ? isCursorInFrame({ x, y }, containingFrame, elementsMap)
2876
2971
  : true;
2972
+ })
2973
+ .filter((el) => {
2974
+ // The parameter elements comes ordered from lower z-index to higher.
2975
+ // We want to preserve that order on the returned array.
2976
+ // Exception being embeddables which should be on top of everything else in
2977
+ // terms of hit testing.
2978
+ if (isIframeElement(el)) {
2979
+ iframeLikes.push(el);
2980
+ return false;
2981
+ }
2982
+ return true;
2983
+ })
2984
+ .concat(iframeLikes);
2985
+ return elements;
2986
+ }
2987
+ getElementHitThreshold() {
2988
+ return DEFAULT_COLLISION_THRESHOLD / this.state.zoom.value;
2989
+ }
2990
+ hitElement(x, y, element, considerBoundingBox = true) {
2991
+ // if the element is selected, then hit test is done against its bounding box
2992
+ if (considerBoundingBox &&
2993
+ this.state.selectedElementIds[element.id] &&
2994
+ shouldShowBoundingBox([element], this.state)) {
2995
+ const selectionShape = getSelectionBoxShape(element, this.scene.getNonDeletedElementsMap(), this.getElementHitThreshold());
2996
+ return isPointInShape([x, y], selectionShape);
2997
+ }
2998
+ // take bound text element into consideration for hit collision as well
2999
+ const hitBoundTextOfElement = hitElementBoundText(x, y, this.getBoundTextShape(element));
3000
+ if (hitBoundTextOfElement) {
3001
+ return true;
3002
+ }
3003
+ return hitElementItself({
3004
+ x,
3005
+ y,
3006
+ element,
3007
+ shape: this.getElementShape(element),
3008
+ threshold: this.getElementHitThreshold(),
3009
+ frameNameBound: isFrameLikeElement(element)
3010
+ ? this.frameNameBoundsCache.get(element)
3011
+ : null,
2877
3012
  });
2878
3013
  }
3014
+ getTextBindableContainerAtPosition(x, y) {
3015
+ const elements = this.scene.getNonDeletedElements();
3016
+ const selectedElements = this.scene.getSelectedElements(this.state);
3017
+ if (selectedElements.length === 1) {
3018
+ return isTextBindableContainer(selectedElements[0], false)
3019
+ ? selectedElements[0]
3020
+ : null;
3021
+ }
3022
+ let hitElement = null;
3023
+ // We need to do hit testing from front (end of the array) to back (beginning of the array)
3024
+ for (let index = elements.length - 1; index >= 0; --index) {
3025
+ if (elements[index].isDeleted) {
3026
+ continue;
3027
+ }
3028
+ const [x1, y1, x2, y2] = getElementAbsoluteCoords(elements[index], this.scene.getNonDeletedElementsMap());
3029
+ if (isArrowElement(elements[index]) &&
3030
+ hitElementItself({
3031
+ x,
3032
+ y,
3033
+ element: elements[index],
3034
+ shape: this.getElementShape(elements[index]),
3035
+ threshold: this.getElementHitThreshold(),
3036
+ })) {
3037
+ hitElement = elements[index];
3038
+ break;
3039
+ }
3040
+ else if (x1 < x && x < x2 && y1 < y && y < y2) {
3041
+ hitElement = elements[index];
3042
+ break;
3043
+ }
3044
+ }
3045
+ return isTextBindableContainer(hitElement, false) ? hitElement : null;
3046
+ }
2879
3047
  startTextEditing = ({ sceneX, sceneY, insertAtParentCenter = true, container, }) => {
2880
3048
  let shouldBindToContainer = false;
2881
3049
  let parentCenterPosition = insertAtParentCenter &&
@@ -2974,7 +3142,7 @@ class App extends React.Component {
2974
3142
  this.scene.insertElementAtIndex(element, containerIndex + 1);
2975
3143
  }
2976
3144
  else {
2977
- this.scene.addNewElement(element);
3145
+ this.scene.insertElement(element);
2978
3146
  }
2979
3147
  }
2980
3148
  this.setState({
@@ -2999,7 +3167,7 @@ class App extends React.Component {
2999
3167
  if (event[KEYS.CTRL_OR_CMD] &&
3000
3168
  (!this.state.editingLinearElement ||
3001
3169
  this.state.editingLinearElement.elementId !== selectedElements[0].id)) {
3002
- this.history.resumeRecording();
3170
+ this.store.shouldCaptureIncrement();
3003
3171
  this.setState({
3004
3172
  editingLinearElement: new LinearElementEditor(selectedElements[0]),
3005
3173
  });
@@ -3014,6 +3182,7 @@ class App extends React.Component {
3014
3182
  const selectedGroupId = hitElement &&
3015
3183
  getSelectedGroupIdForElement(hitElement, this.state.selectedGroupIds);
3016
3184
  if (selectedGroupId) {
3185
+ this.store.shouldCaptureIncrement();
3017
3186
  this.setState((prevState) => ({
3018
3187
  ...prevState,
3019
3188
  ...selectGroupsForSelectedElements({
@@ -3033,11 +3202,17 @@ class App extends React.Component {
3033
3202
  });
3034
3203
  return;
3035
3204
  }
3036
- const container = getTextBindableContainerAtPosition(this.scene.getNonDeletedElements(), this.state, sceneX, sceneY, this.scene.getNonDeletedElementsMap());
3205
+ const container = this.getTextBindableContainerAtPosition(sceneX, sceneY);
3037
3206
  if (container) {
3038
3207
  if (hasBoundTextElement(container) ||
3039
3208
  !isTransparent(container.backgroundColor) ||
3040
- isHittingElementNotConsideringBoundingBox(container, this.state, this.frameNameBoundsCache, [sceneX, sceneY], this.scene.getNonDeletedElementsMap())) {
3209
+ hitElementItself({
3210
+ x: sceneX,
3211
+ y: sceneY,
3212
+ element: container,
3213
+ shape: this.getElementShape(container),
3214
+ threshold: this.getElementHitThreshold(),
3215
+ })) {
3041
3216
  const midPoint = getContainerCenter(container, this.state, this.scene.getNonDeletedElementsMap());
3042
3217
  sceneX = midPoint.x;
3043
3218
  sceneY = midPoint.y;
@@ -3092,7 +3267,7 @@ class App extends React.Component {
3092
3267
  }
3093
3268
  if (!customEvent?.defaultPrevented) {
3094
3269
  const target = isLocalLink(url) ? "_self" : "_blank";
3095
- const newWindow = window.open(undefined, target, "noopener noreferrer");
3270
+ const newWindow = window.open(undefined, target);
3096
3271
  // https://mathiasbynens.github.io/rel-noopener/
3097
3272
  if (newWindow) {
3098
3273
  newWindow.opener = null;
@@ -3308,15 +3483,23 @@ class App extends React.Component {
3308
3483
  if (selectedElements.length === 1 &&
3309
3484
  !isOverScrollBar &&
3310
3485
  !this.state.editingLinearElement) {
3311
- const elementWithTransformHandleType = getElementWithTransformHandleType(elements, this.state, scenePointerX, scenePointerY, this.state.zoom, event.pointerType, this.scene.getNonDeletedElementsMap());
3312
- if (elementWithTransformHandleType &&
3313
- elementWithTransformHandleType.transformHandleType) {
3314
- setCursor(this.interactiveCanvas, getCursorForResizingElement(elementWithTransformHandleType));
3315
- return;
3486
+ // for linear elements, we'd like to prioritize point dragging over edge resizing
3487
+ // therefore, we update and check hovered point index first
3488
+ if (this.state.selectedLinearElement) {
3489
+ this.handleHoverSelectedLinearElement(this.state.selectedLinearElement, scenePointerX, scenePointerY);
3490
+ }
3491
+ if (!this.state.selectedLinearElement ||
3492
+ this.state.selectedLinearElement.hoverPointIndex === -1) {
3493
+ const elementWithTransformHandleType = getElementWithTransformHandleType(elements, this.state, scenePointerX, scenePointerY, this.state.zoom, event.pointerType, this.scene.getNonDeletedElementsMap(), this.device);
3494
+ if (elementWithTransformHandleType &&
3495
+ elementWithTransformHandleType.transformHandleType) {
3496
+ setCursor(this.interactiveCanvas, getCursorForResizingElement(elementWithTransformHandleType));
3497
+ return;
3498
+ }
3316
3499
  }
3317
3500
  }
3318
3501
  else if (selectedElements.length > 1 && !isOverScrollBar) {
3319
- const transformHandleType = getTransformHandleTypeFromCoords(getCommonBounds(selectedElements), scenePointerX, scenePointerY, this.state.zoom, event.pointerType);
3502
+ const transformHandleType = getTransformHandleTypeFromCoords(getCommonBounds(selectedElements), scenePointerX, scenePointerY, this.state.zoom, event.pointerType, this.device);
3320
3503
  if (transformHandleType) {
3321
3504
  setCursor(this.interactiveCanvas, getCursorForResizingElement({
3322
3505
  transformHandleType,
@@ -3420,7 +3603,7 @@ class App extends React.Component {
3420
3603
  }
3421
3604
  };
3422
3605
  const distance = distance2d(pointerDownState.lastCoords.x, pointerDownState.lastCoords.y, scenePointer.x, scenePointer.y);
3423
- const threshold = 10 / this.state.zoom.value;
3606
+ const threshold = this.getElementHitThreshold();
3424
3607
  const point = { ...pointerDownState.lastCoords };
3425
3608
  let samplingInterval = 0;
3426
3609
  while (samplingInterval <= distance) {
@@ -3466,30 +3649,29 @@ class App extends React.Component {
3466
3649
  handleHoverSelectedLinearElement(linearElementEditor, scenePointerX, scenePointerY) {
3467
3650
  const elementsMap = this.scene.getNonDeletedElementsMap();
3468
3651
  const element = LinearElementEditor.getElement(linearElementEditor.elementId, elementsMap);
3469
- const boundTextElement = getBoundTextElement(element, elementsMap);
3470
3652
  if (!element) {
3471
3653
  return;
3472
3654
  }
3473
3655
  if (this.state.selectedLinearElement) {
3474
3656
  let hoverPointIndex = -1;
3475
3657
  let segmentMidPointHoveredCoords = null;
3476
- if (isHittingElementNotConsideringBoundingBox(element, this.state, this.frameNameBoundsCache, [scenePointerX, scenePointerY], elementsMap)) {
3658
+ if (hitElementItself({
3659
+ x: scenePointerX,
3660
+ y: scenePointerY,
3661
+ element,
3662
+ shape: this.getElementShape(element),
3663
+ })) {
3477
3664
  hoverPointIndex = LinearElementEditor.getPointIndexUnderCursor(element, elementsMap, this.state.zoom, scenePointerX, scenePointerY);
3478
3665
  segmentMidPointHoveredCoords =
3479
3666
  LinearElementEditor.getSegmentMidpointHitCoords(linearElementEditor, { x: scenePointerX, y: scenePointerY }, this.state, this.scene.getNonDeletedElementsMap());
3480
3667
  if (hoverPointIndex >= 0 || segmentMidPointHoveredCoords) {
3481
3668
  setCursor(this.interactiveCanvas, CURSOR_TYPE.POINTER);
3482
3669
  }
3483
- else {
3670
+ else if (this.hitElement(scenePointerX, scenePointerY, element)) {
3484
3671
  setCursor(this.interactiveCanvas, CURSOR_TYPE.MOVE);
3485
3672
  }
3486
3673
  }
3487
- else if (shouldShowBoundingBox([element], this.state) &&
3488
- isHittingElementBoundingBoxWithoutHittingElement(element, this.state, this.frameNameBoundsCache, scenePointerX, scenePointerY, elementsMap)) {
3489
- setCursor(this.interactiveCanvas, CURSOR_TYPE.MOVE);
3490
- }
3491
- else if (boundTextElement &&
3492
- hitTest(boundTextElement, this.state, this.frameNameBoundsCache, scenePointerX, scenePointerY, this.scene.getNonDeletedElementsMap())) {
3674
+ else if (this.hitElement(scenePointerX, scenePointerY, element)) {
3493
3675
  setCursor(this.interactiveCanvas, CURSOR_TYPE.MOVE);
3494
3676
  }
3495
3677
  if (this.state.selectedLinearElement.hoverPointIndex !== hoverPointIndex) {
@@ -3556,6 +3738,7 @@ class App extends React.Component {
3556
3738
  return obj;
3557
3739
  }, {}), this.state),
3558
3740
  },
3741
+ storeAction: StoreAction.UPDATE,
3559
3742
  });
3560
3743
  return;
3561
3744
  }
@@ -3995,8 +4178,11 @@ class App extends React.Component {
3995
4178
  const elements = this.scene.getNonDeletedElements();
3996
4179
  const elementsMap = this.scene.getNonDeletedElementsMap();
3997
4180
  const selectedElements = this.scene.getSelectedElements(this.state);
3998
- if (selectedElements.length === 1 && !this.state.editingLinearElement) {
3999
- const elementWithTransformHandleType = getElementWithTransformHandleType(elements, this.state, pointerDownState.origin.x, pointerDownState.origin.y, this.state.zoom, event.pointerType, this.scene.getNonDeletedElementsMap());
4181
+ if (selectedElements.length === 1 &&
4182
+ !this.state.editingLinearElement &&
4183
+ !(this.state.selectedLinearElement &&
4184
+ this.state.selectedLinearElement.hoverPointIndex !== -1)) {
4185
+ const elementWithTransformHandleType = getElementWithTransformHandleType(elements, this.state, pointerDownState.origin.x, pointerDownState.origin.y, this.state.zoom, event.pointerType, this.scene.getNonDeletedElementsMap(), this.device);
4000
4186
  if (elementWithTransformHandleType != null) {
4001
4187
  this.setState({
4002
4188
  resizingElement: elementWithTransformHandleType.element,
@@ -4006,7 +4192,7 @@ class App extends React.Component {
4006
4192
  }
4007
4193
  }
4008
4194
  else if (selectedElements.length > 1) {
4009
- pointerDownState.resize.handleType = getTransformHandleTypeFromCoords(getCommonBounds(selectedElements), pointerDownState.origin.x, pointerDownState.origin.y, this.state.zoom, event.pointerType);
4195
+ pointerDownState.resize.handleType = getTransformHandleTypeFromCoords(getCommonBounds(selectedElements), pointerDownState.origin.x, pointerDownState.origin.y, this.state.zoom, event.pointerType, this.device);
4010
4196
  }
4011
4197
  if (pointerDownState.resize.handleType) {
4012
4198
  pointerDownState.resize.isResizing = true;
@@ -4020,7 +4206,7 @@ class App extends React.Component {
4020
4206
  else {
4021
4207
  if (this.state.selectedLinearElement) {
4022
4208
  const linearElementEditor = this.state.editingLinearElement || this.state.selectedLinearElement;
4023
- const ret = LinearElementEditor.handlePointerDown(event, this.state, this.history, pointerDownState.origin, linearElementEditor, this.scene.getNonDeletedElements(), elementsMap);
4209
+ const ret = LinearElementEditor.handlePointerDown(event, this.state, this.store, pointerDownState.origin, linearElementEditor, this);
4024
4210
  if (ret.hitElement) {
4025
4211
  pointerDownState.hit.element = ret.hitElement;
4026
4212
  }
@@ -4181,7 +4367,7 @@ class App extends React.Component {
4181
4367
  return false;
4182
4368
  }
4183
4369
  // How many pixels off the shape boundary we still consider a hit
4184
- const threshold = 10 / this.state.zoom.value;
4370
+ const threshold = this.getElementHitThreshold();
4185
4371
  const [x1, y1, x2, y2] = getCommonBounds(selectedElements);
4186
4372
  return (point.x > x1 - threshold &&
4187
4373
  point.x < x2 + threshold &&
@@ -4201,7 +4387,7 @@ class App extends React.Component {
4201
4387
  includeBoundTextElement: true,
4202
4388
  });
4203
4389
  // FIXME
4204
- let container = getTextBindableContainerAtPosition(this.scene.getNonDeletedElements(), this.state, sceneX, sceneY, this.scene.getNonDeletedElementsMap());
4390
+ let container = this.getTextBindableContainerAtPosition(sceneX, sceneY);
4205
4391
  if (hasBoundTextElement(element)) {
4206
4392
  container = element;
4207
4393
  sceneX = element.x + element.width / 2;
@@ -4259,8 +4445,8 @@ class App extends React.Component {
4259
4445
  points: [[0, 0]],
4260
4446
  pressures,
4261
4447
  });
4262
- const boundElement = getHoveredElementForBinding(pointerDownState.origin, this.scene.getNonDeletedElements(), this.scene.getNonDeletedElementsMap());
4263
- this.scene.addNewElement(element);
4448
+ const boundElement = getHoveredElementForBinding(pointerDownState.origin, this);
4449
+ this.scene.insertElement(element);
4264
4450
  this.setState({
4265
4451
  draggingElement: element,
4266
4452
  editingElement: element,
@@ -4288,10 +4474,7 @@ class App extends React.Component {
4288
4474
  width,
4289
4475
  height,
4290
4476
  });
4291
- this.scene.replaceAllElements([
4292
- ...this.scene.getElementsIncludingDeleted(),
4293
- element,
4294
- ]);
4477
+ this.scene.insertElement(element);
4295
4478
  return element;
4296
4479
  };
4297
4480
  //create rectangle element with youtube top left on nearest grid point width / hight 640/360
@@ -4326,10 +4509,7 @@ class App extends React.Component {
4326
4509
  height: embedLink.intrinsicSize.h,
4327
4510
  link,
4328
4511
  });
4329
- this.scene.replaceAllElements([
4330
- ...this.scene.getElementsIncludingDeleted(),
4331
- element,
4332
- ]);
4512
+ this.scene.insertElement(element);
4333
4513
  return element;
4334
4514
  };
4335
4515
  createImageElement = ({ sceneX, sceneY, addToFrameUnderCursor = true, }) => {
@@ -4437,8 +4617,8 @@ class App extends React.Component {
4437
4617
  mutateElement(element, {
4438
4618
  points: [...element.points, [0, 0]],
4439
4619
  });
4440
- const boundElement = getHoveredElementForBinding(pointerDownState.origin, this.scene.getNonDeletedElements(), this.scene.getNonDeletedElementsMap());
4441
- this.scene.addNewElement(element);
4620
+ const boundElement = getHoveredElementForBinding(pointerDownState.origin, this);
4621
+ this.scene.insertElement(element);
4442
4622
  this.setState({
4443
4623
  draggingElement: element,
4444
4624
  editingElement: element,
@@ -4498,7 +4678,7 @@ class App extends React.Component {
4498
4678
  });
4499
4679
  }
4500
4680
  else {
4501
- this.scene.addNewElement(element);
4681
+ this.scene.insertElement(element);
4502
4682
  this.setState({
4503
4683
  multiElement: null,
4504
4684
  draggingElement: element,
@@ -4520,10 +4700,7 @@ class App extends React.Component {
4520
4700
  const frame = type === TOOL_TYPE.magicframe
4521
4701
  ? newMagicFrameElement(constructorOpts)
4522
4702
  : newFrameElement(constructorOpts);
4523
- this.scene.replaceAllElements([
4524
- ...this.scene.getElementsIncludingDeleted(),
4525
- frame,
4526
- ]);
4703
+ this.scene.insertElement(frame);
4527
4704
  this.setState({
4528
4705
  multiElement: null,
4529
4706
  draggingElement: frame,
@@ -4737,7 +4914,9 @@ class App extends React.Component {
4737
4914
  // able to select and interact with the text input
4738
4915
  !this.state.editingFrame &&
4739
4916
  dragSelectedElements(pointerDownState, selectedElements, dragOffset, this.state, this.scene, snapOffset, event[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize);
4740
- this.maybeSuggestBindingForAll(selectedElements);
4917
+ this.setState({
4918
+ suggestedBindings: getSuggestedBindingsForArrows(selectedElements, this),
4919
+ });
4741
4920
  // We duplicate the selected element if alt is pressed on pointer move
4742
4921
  if (event.altKey && !pointerDownState.hit.hasBeenDuplicated) {
4743
4922
  // Move the currently selected elements to the top of the z index stack, and
@@ -4781,6 +4960,7 @@ class App extends React.Component {
4781
4960
  }
4782
4961
  }
4783
4962
  const nextSceneElements = [...nextElements, ...elementsToAppend];
4963
+ syncMovedIndices(nextSceneElements, arrayToMap(elementsToAppend));
4784
4964
  bindTextToShapeAfterDuplication(nextElements, elementsToAppend, oldIdToDuplicatedId);
4785
4965
  fixBindingsAfterDuplication(nextSceneElements, elementsToAppend, oldIdToDuplicatedId, "duplicatesServeAsOld");
4786
4966
  bindElementsToFramesAfterDuplication(nextSceneElements, elementsToAppend, oldIdToDuplicatedId);
@@ -4972,7 +5152,7 @@ class App extends React.Component {
4972
5152
  this.actionManager.executeAction(actionFinalize);
4973
5153
  }
4974
5154
  else {
4975
- const editingLinearElement = LinearElementEditor.handlePointerUp(childEvent, this.state.editingLinearElement, this.state, this.scene.getNonDeletedElements(), elementsMap);
5155
+ const editingLinearElement = LinearElementEditor.handlePointerUp(childEvent, this.state.editingLinearElement, this.state, this);
4976
5156
  if (editingLinearElement !== this.state.editingLinearElement) {
4977
5157
  this.setState({
4978
5158
  editingLinearElement,
@@ -4991,7 +5171,7 @@ class App extends React.Component {
4991
5171
  }
4992
5172
  }
4993
5173
  else {
4994
- const linearElementEditor = LinearElementEditor.handlePointerUp(childEvent, this.state.selectedLinearElement, this.state, this.scene.getNonDeletedElements(), elementsMap);
5174
+ const linearElementEditor = LinearElementEditor.handlePointerUp(childEvent, this.state.selectedLinearElement, this.state, this);
4995
5175
  const { startBindingElement, endBindingElement } = linearElementEditor;
4996
5176
  const element = this.scene.getElement(linearElementEditor.elementId);
4997
5177
  if (isBindingElement(element)) {
@@ -5060,7 +5240,7 @@ class App extends React.Component {
5060
5240
  }
5061
5241
  if (isLinearElement(draggingElement)) {
5062
5242
  if (draggingElement.points.length > 1) {
5063
- this.history.resumeRecording();
5243
+ this.store.shouldCaptureIncrement();
5064
5244
  }
5065
5245
  const pointerCoords = viewportCoordsToSceneCoords(childEvent, this.state);
5066
5246
  if (!pointerDownState.drag.hasOccurred &&
@@ -5083,7 +5263,7 @@ class App extends React.Component {
5083
5263
  else if (pointerDownState.drag.hasOccurred && !multiElement) {
5084
5264
  if (isBindingEnabled(this.state) &&
5085
5265
  isBindingElement(draggingElement, false)) {
5086
- maybeBindLinearElement(draggingElement, this.state, this.scene, pointerCoords, elementsMap);
5266
+ maybeBindLinearElement(draggingElement, this.state, pointerCoords, this);
5087
5267
  }
5088
5268
  this.setState({ suggestedBindings: [], startBoundElement: null });
5089
5269
  if (!activeTool.locked) {
@@ -5112,11 +5292,15 @@ class App extends React.Component {
5112
5292
  draggingElement &&
5113
5293
  isInvisiblySmallElement(draggingElement)) {
5114
5294
  // remove invisible element which was added in onPointerDown
5115
- this.scene.replaceAllElements(this.scene
5116
- .getElementsIncludingDeleted()
5117
- .filter((el) => el.id !== draggingElement.id));
5118
- this.setState({
5119
- draggingElement: null,
5295
+ // update the store snapshot, so that invisible elements are not captured by the store
5296
+ this.updateScene({
5297
+ elements: this.scene
5298
+ .getElementsIncludingDeleted()
5299
+ .filter((el) => el.id !== draggingElement.id),
5300
+ appState: {
5301
+ draggingElement: null,
5302
+ },
5303
+ storeAction: StoreAction.UPDATE,
5120
5304
  });
5121
5305
  return;
5122
5306
  }
@@ -5197,12 +5381,16 @@ class App extends React.Component {
5197
5381
  mutateElement(draggingElement, getNormalizedDimensions(draggingElement));
5198
5382
  }
5199
5383
  if (resizingElement) {
5200
- this.history.resumeRecording();
5384
+ this.store.shouldCaptureIncrement();
5201
5385
  }
5202
5386
  if (resizingElement && isInvisiblySmallElement(resizingElement)) {
5203
- this.scene.replaceAllElements(this.scene
5204
- .getElementsIncludingDeleted()
5205
- .filter((el) => el.id !== resizingElement.id));
5387
+ // update the store snapshot, so that invisible elements are not captured by the store
5388
+ this.updateScene({
5389
+ elements: this.scene
5390
+ .getElementsIncludingDeleted()
5391
+ .filter((el) => el.id !== resizingElement.id),
5392
+ storeAction: StoreAction.UPDATE,
5393
+ });
5206
5394
  }
5207
5395
  // handle frame membership for resizing frames and/or selected elements
5208
5396
  if (pointerDownState.resize.isResizing) {
@@ -5357,10 +5545,23 @@ class App extends React.Component {
5357
5545
  }));
5358
5546
  }
5359
5547
  }
5360
- if (!pointerDownState.drag.hasOccurred &&
5548
+ if (
5549
+ // not dragged
5550
+ !pointerDownState.drag.hasOccurred &&
5551
+ // not resized
5361
5552
  !this.state.isResizing &&
5553
+ // only hitting the bounding box of the previous hit element
5362
5554
  ((hitElement &&
5363
- isHittingElementBoundingBoxWithoutHittingElement(hitElement, this.state, this.frameNameBoundsCache, pointerDownState.origin.x, pointerDownState.origin.y, this.scene.getNonDeletedElementsMap())) ||
5555
+ hitElementBoundingBoxOnly({
5556
+ x: pointerDownState.origin.x,
5557
+ y: pointerDownState.origin.y,
5558
+ element: hitElement,
5559
+ shape: this.getElementShape(hitElement),
5560
+ threshold: this.getElementHitThreshold(),
5561
+ frameNameBound: isFrameLikeElement(hitElement)
5562
+ ? this.frameNameBoundsCache.get(hitElement)
5563
+ : null,
5564
+ }, elementsMap)) ||
5364
5565
  (!hitElement &&
5365
5566
  pointerDownState.hit.hasHitCommonBoundingBoxOfSelectedElements))) {
5366
5567
  if (this.state.editingLinearElement) {
@@ -5375,6 +5576,8 @@ class App extends React.Component {
5375
5576
  activeEmbeddable: null,
5376
5577
  });
5377
5578
  }
5579
+ // reset cursor
5580
+ setCursor(this.interactiveCanvas, CURSOR_TYPE.AUTO);
5378
5581
  return;
5379
5582
  }
5380
5583
  if (!activeTool.locked &&
@@ -5392,13 +5595,17 @@ class App extends React.Component {
5392
5595
  }));
5393
5596
  }
5394
5597
  if (activeTool.type !== "selection" ||
5395
- isSomeElementSelected(this.scene.getNonDeletedElements(), this.state)) {
5396
- this.history.resumeRecording();
5598
+ isSomeElementSelected(this.scene.getNonDeletedElements(), this.state) ||
5599
+ !isShallowEqual(this.state.previousSelectedElementIds, this.state.selectedElementIds)) {
5600
+ this.store.shouldCaptureIncrement();
5397
5601
  }
5398
5602
  if (pointerDownState.drag.hasOccurred || isResizing || isRotating) {
5399
- isBindingEnabled(this.state)
5400
- ? bindOrUnbindSelectedElements(this.scene.getSelectedElements(this.state), this.scene.getNonDeletedElements(), elementsMap)
5401
- : unbindLinearElements(this.scene.getSelectedElements(this.state), elementsMap);
5603
+ // We only allow binding via linear elements, specifically via dragging
5604
+ // the endpoints ("start" or "end").
5605
+ const linearElements = this.scene
5606
+ .getSelectedElements(this.state)
5607
+ .filter(isLinearElement);
5608
+ bindOrUnbindLinearElements(linearElements, this, isBindingEnabled(this.state), this.state.selectedLinearElement?.selectedPointsIndices ?? []);
5402
5609
  }
5403
5610
  if (activeTool.type === "laser") {
5404
5611
  this.laserTrails.endPath();
@@ -5449,7 +5656,7 @@ class App extends React.Component {
5449
5656
  });
5450
5657
  this.elementsPendingErasure = new Set();
5451
5658
  if (didChange) {
5452
- this.history.resumeRecording();
5659
+ this.store.shouldCaptureIncrement();
5453
5660
  this.scene.replaceAllElements(elements);
5454
5661
  }
5455
5662
  };
@@ -5552,7 +5759,7 @@ class App extends React.Component {
5552
5759
  this.setState({ errorMessage: t("errors.imageToolNotSupported") });
5553
5760
  return;
5554
5761
  }
5555
- this.scene.addNewElement(imageElement);
5762
+ this.scene.insertElement(imageElement);
5556
5763
  try {
5557
5764
  return await this.initializeImage({
5558
5765
  imageFile,
@@ -5741,7 +5948,7 @@ class App extends React.Component {
5741
5948
  }
5742
5949
  };
5743
5950
  maybeSuggestBindingAtCursor = (pointerCoords) => {
5744
- const hoveredBindableElement = getHoveredElementForBinding(pointerCoords, this.scene.getNonDeletedElements(), this.scene.getNonDeletedElementsMap());
5951
+ const hoveredBindableElement = getHoveredElementForBinding(pointerCoords, this);
5745
5952
  this.setState({
5746
5953
  suggestedBindings: hoveredBindableElement != null ? [hoveredBindableElement] : [],
5747
5954
  });
@@ -5756,7 +5963,7 @@ class App extends React.Component {
5756
5963
  return;
5757
5964
  }
5758
5965
  const suggestedBindings = pointerCoords.reduce((acc, coords) => {
5759
- const hoveredBindableElement = getHoveredElementForBinding(coords, this.scene.getNonDeletedElements(), this.scene.getNonDeletedElementsMap());
5966
+ const hoveredBindableElement = getHoveredElementForBinding(coords, this);
5760
5967
  if (hoveredBindableElement != null &&
5761
5968
  !isLinearElementSimpleAndAlreadyBound(linearElement, oppositeBindingBoundElement?.id, hoveredBindableElement)) {
5762
5969
  acc.push(hoveredBindableElement);
@@ -5765,13 +5972,6 @@ class App extends React.Component {
5765
5972
  }, []);
5766
5973
  this.setState({ suggestedBindings });
5767
5974
  };
5768
- maybeSuggestBindingForAll(selectedElements) {
5769
- if (selectedElements.length > 50) {
5770
- return;
5771
- }
5772
- const suggestedBindings = getEligibleElementsForBinding(selectedElements, this.scene.getNonDeletedElements(), this.scene.getNonDeletedElementsMap());
5773
- this.setState({ suggestedBindings });
5774
- }
5775
5975
  clearSelection(hitElement) {
5776
5976
  this.setState((prevState) => ({
5777
5977
  selectedElementIds: makeNextSelectedElementIds({}, prevState),
@@ -5831,7 +6031,7 @@ class App extends React.Component {
5831
6031
  isLoading: false,
5832
6032
  },
5833
6033
  replaceFiles: true,
5834
- commitToHistory: true,
6034
+ storeAction: StoreAction.CAPTURE,
5835
6035
  });
5836
6036
  return;
5837
6037
  }
@@ -5899,9 +6099,10 @@ class App extends React.Component {
5899
6099
  loadFileToCanvas = async (file, fileHandle) => {
5900
6100
  file = await normalizeFile(file);
5901
6101
  try {
6102
+ const elements = this.scene.getElementsIncludingDeleted();
5902
6103
  let ret;
5903
6104
  try {
5904
- ret = await loadSceneOrLibraryFromBlob(file, this.state, this.scene.getElementsIncludingDeleted(), fileHandle);
6105
+ ret = await loadSceneOrLibraryFromBlob(file, this.state, elements, fileHandle);
5905
6106
  }
5906
6107
  catch (error) {
5907
6108
  const imageSceneDataError = error instanceof ImageSceneDataError;
@@ -5926,6 +6127,10 @@ class App extends React.Component {
5926
6127
  return;
5927
6128
  }
5928
6129
  if (ret.type === MIME_TYPES.excalidraw) {
6130
+ // restore the fractional indices by mutating elements
6131
+ syncInvalidIndices(elements.concat(ret.data.elements));
6132
+ // update the store snapshot for old elements, otherwise we would end up with duplicated fractional indices on undo
6133
+ this.store.updateSnapshot(arrayToMap(elements), this.state);
5929
6134
  this.setState({ isLoading: true });
5930
6135
  this.syncActionResult({
5931
6136
  ...ret.data,
@@ -5934,7 +6139,7 @@ class App extends React.Component {
5934
6139
  isLoading: false,
5935
6140
  },
5936
6141
  replaceFiles: true,
5937
- commitToHistory: true,
6142
+ storeAction: StoreAction.CAPTURE,
5938
6143
  });
5939
6144
  }
5940
6145
  else if (ret.type === MIME_TYPES.excalidrawlib) {
@@ -6033,7 +6238,6 @@ class App extends React.Component {
6033
6238
  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)
6034
6239
  ? !shouldMaintainAspectRatio(event)
6035
6240
  : shouldMaintainAspectRatio(event), shouldResizeFromCenter(event), aspectRatio, this.state.originSnapOffset);
6036
- this.maybeSuggestBindingForAll([draggingElement]);
6037
6241
  // highlight elements that are to be added to frames on frames creation
6038
6242
  if (this.state.activeTool.type === TOOL_TYPE.frame ||
6039
6243
  this.state.activeTool.type === TOOL_TYPE.magicframe) {
@@ -6087,16 +6291,17 @@ class App extends React.Component {
6087
6291
  snapLines,
6088
6292
  });
6089
6293
  }
6090
- if (transformElements(pointerDownState.originalElements, transformHandleType, selectedElements, this.scene.getElementsMapIncludingDeleted(), shouldRotateWithDiscreteAngle(event), shouldResizeFromCenter(event), selectedElements.length === 1 && isImageElement(selectedElements[0])
6294
+ if (transformElements(pointerDownState.originalElements, transformHandleType, selectedElements, this.scene.getElementsMapIncludingDeleted(), shouldRotateWithDiscreteAngle(event), shouldResizeFromCenter(event), selectedElements.some((element) => isImageElement(element))
6091
6295
  ? !shouldMaintainAspectRatio(event)
6092
6296
  : shouldMaintainAspectRatio(event), resizeX, resizeY, pointerDownState.resize.center.x, pointerDownState.resize.center.y)) {
6093
- this.maybeSuggestBindingForAll(selectedElements);
6297
+ const suggestedBindings = getSuggestedBindingsForArrows(selectedElements, this);
6094
6298
  const elementsToHighlight = new Set();
6095
6299
  selectedFrames.forEach((frame) => {
6096
6300
  getElementsInResizingFrame(this.scene.getNonDeletedElements(), frame, this.state, this.scene.getNonDeletedElementsMap()).forEach((element) => elementsToHighlight.add(element));
6097
6301
  });
6098
6302
  this.setState({
6099
6303
  elementsToHighlight: [...elementsToHighlight],
6304
+ suggestedBindings,
6100
6305
  });
6101
6306
  return true;
6102
6307
  }
@@ -6321,7 +6526,7 @@ export const createTestHook = () => {
6321
6526
  return this.app?.scene.getElementsIncludingDeleted();
6322
6527
  },
6323
6528
  set(elements) {
6324
- return this.app?.scene.replaceAllElements(elements);
6529
+ return this.app?.scene.replaceAllElements(syncInvalidIndices(elements));
6325
6530
  },
6326
6531
  },
6327
6532
  });