@excalidraw/excalidraw 0.17.1-550a388 → 0.17.1-62228e0

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 (552) hide show
  1. package/CHANGELOG.md +18 -2
  2. package/dist/browser/dev/excalidraw-assets-dev/CascadiaMono-Regular-NWCXRPUG.woff2 +0 -0
  3. package/dist/browser/dev/excalidraw-assets-dev/ComicShanns-Regular-6TOETDFT.woff2 +0 -0
  4. package/dist/browser/dev/excalidraw-assets-dev/Excalifont-Regular-CPKEUDVM.woff2 +0 -0
  5. package/dist/browser/dev/excalidraw-assets-dev/LiberationSans-Regular-ZQD73GJM.woff2 +0 -0
  6. package/dist/browser/dev/excalidraw-assets-dev/Virgil-Regular-YHAB2VGJ.woff2 +0 -0
  7. package/dist/browser/dev/excalidraw-assets-dev/{blockDiagram-91b80b7a-ACFH36JV.js → blockDiagram-91b80b7a-H47FTXHA.js} +5 -5
  8. package/dist/browser/dev/excalidraw-assets-dev/{c4Diagram-b2a90758-QZ27YR47.js → c4Diagram-b2a90758-NNJK6GKC.js} +3 -3
  9. package/dist/browser/dev/excalidraw-assets-dev/{chunk-HO2HMSK7.js → chunk-4KQVEBHW.js} +3 -3
  10. package/dist/browser/dev/excalidraw-assets-dev/{chunk-USGV265L.js → chunk-53YI56GV.js} +4 -4
  11. package/dist/browser/dev/excalidraw-assets-dev/{chunk-EDFX3S7X.js → chunk-A2WCJI4I.js} +3 -3
  12. package/dist/browser/dev/excalidraw-assets-dev/{chunk-IX4V72YG.js → chunk-EFLPX7NE.js} +6 -6
  13. package/dist/browser/dev/excalidraw-assets-dev/{chunk-MXVETLVM.js → chunk-JYIQCNWV.js} +2 -2
  14. package/dist/browser/dev/excalidraw-assets-dev/{chunk-YZIOORVX.js → chunk-LVIQQW6F.js} +2 -2
  15. package/dist/browser/dev/excalidraw-assets-dev/{chunk-6U7GQNJT.js → chunk-PXLO3FOU.js} +2 -2
  16. package/dist/browser/dev/excalidraw-assets-dev/{chunk-7DACDEY3.js → chunk-TO2AW5PW.js} +2 -2
  17. package/dist/browser/dev/excalidraw-assets-dev/{chunk-NJ77ZFNJ.js → chunk-VURILHLY.js} +2 -2
  18. package/dist/browser/dev/excalidraw-assets-dev/{chunk-AK7SWNLN.js → chunk-XDYNNNIZ.js} +48 -10
  19. package/dist/browser/dev/excalidraw-assets-dev/chunk-XDYNNNIZ.js.map +7 -0
  20. package/dist/browser/dev/excalidraw-assets-dev/{chunk-2T2GU7NF.js → chunk-ZAYGSUHF.js} +2 -2
  21. package/dist/browser/dev/excalidraw-assets-dev/{chunk-Z3PH3V2B.js → chunk-ZQR5ML6Y.js} +26 -26
  22. package/dist/browser/dev/excalidraw-assets-dev/chunk-ZQR5ML6Y.js.map +7 -0
  23. package/dist/browser/dev/excalidraw-assets-dev/{chunk-RWZVJAQU.js → chunk-ZYC7SDAJ.js} +13172 -7476
  24. package/dist/browser/dev/excalidraw-assets-dev/chunk-ZYC7SDAJ.js.map +7 -0
  25. package/dist/browser/dev/excalidraw-assets-dev/{classDiagram-30eddba6-QSLMH4JW.js → classDiagram-30eddba6-CUYIJICN.js} +5 -5
  26. package/dist/browser/dev/excalidraw-assets-dev/{classDiagram-v2-f2df5561-DY4DYQ5P.js → classDiagram-v2-f2df5561-K6WW6K73.js} +8 -8
  27. package/dist/browser/dev/excalidraw-assets-dev/{dist-Z46EOVOL.js → dist-DNSPZDOZ.js} +67 -33
  28. package/dist/browser/dev/excalidraw-assets-dev/dist-DNSPZDOZ.js.map +7 -0
  29. package/dist/browser/dev/excalidraw-assets-dev/{en-5TCZHGGJ.js → en-RAFN4KPD.js} +8 -2
  30. package/dist/browser/dev/excalidraw-assets-dev/{erDiagram-47591fe2-SOOJRTCB.js → erDiagram-47591fe2-XGAD7EEP.js} +4 -4
  31. package/dist/browser/dev/excalidraw-assets-dev/{flowDiagram-5540d9b9-AHGL4KPK.js → flowDiagram-5540d9b9-B6EOVNNO.js} +9 -9
  32. package/dist/browser/dev/excalidraw-assets-dev/{flowDiagram-v2-3b53844e-56LDZZWY.js → flowDiagram-v2-3b53844e-NUG24FJH.js} +9 -9
  33. package/dist/browser/dev/excalidraw-assets-dev/{flowchart-elk-definition-5fe447d6-27LUKRI6.js → flowchart-elk-definition-5fe447d6-25Y7PCBL.js} +5 -5
  34. package/dist/browser/dev/excalidraw-assets-dev/{ganttDiagram-9a3bba1f-EHGYGNG6.js → ganttDiagram-9a3bba1f-GNL6ZDTC.js} +2 -2
  35. package/dist/browser/dev/excalidraw-assets-dev/{gitGraphDiagram-96e6b4ee-AJQNBDW5.js → gitGraphDiagram-96e6b4ee-HNW52NVO.js} +2 -2
  36. package/dist/browser/dev/excalidraw-assets-dev/{image-OFRRV5MB.css → image-WDHYGKKP.css} +1 -1
  37. package/dist/browser/dev/excalidraw-assets-dev/image-WDHYGKKP.css.map +7 -0
  38. package/dist/browser/dev/excalidraw-assets-dev/{image-EDKQZH7Z.js → image-ZNLDWTK3.js} +2 -2
  39. package/dist/browser/dev/excalidraw-assets-dev/{infoDiagram-bcd20f53-SWLLQVES.js → infoDiagram-bcd20f53-FWEUVFLT.js} +2 -2
  40. package/dist/browser/dev/excalidraw-assets-dev/{journeyDiagram-4fe6b3dc-7UAVCWOZ.js → journeyDiagram-4fe6b3dc-RZIUI7UG.js} +3 -3
  41. package/dist/browser/dev/excalidraw-assets-dev/{mindmap-definition-f354de21-SROW5KGM.js → mindmap-definition-f354de21-GBVN45GU.js} +3 -3
  42. package/dist/browser/dev/excalidraw-assets-dev/{pieDiagram-79897490-QKCI6NCB.js → pieDiagram-79897490-ECENNII6.js} +2 -2
  43. package/dist/browser/dev/excalidraw-assets-dev/{quadrantDiagram-62f64e94-LNYJZFC5.js → quadrantDiagram-62f64e94-ZMEOFVNL.js} +2 -2
  44. package/dist/browser/dev/excalidraw-assets-dev/{requirementDiagram-05bf5f74-ZZD7ZHFA.js → requirementDiagram-05bf5f74-FHZSFHCR.js} +4 -4
  45. package/dist/browser/dev/excalidraw-assets-dev/{sankeyDiagram-97764748-L75ZZ4UM.js → sankeyDiagram-97764748-VDKIKTA6.js} +2 -2
  46. package/dist/browser/dev/excalidraw-assets-dev/{sequenceDiagram-acc0e65c-6PCU7TDK.js → sequenceDiagram-acc0e65c-6JUSPVKX.js} +3 -3
  47. package/dist/browser/dev/excalidraw-assets-dev/{stateDiagram-0ff1cf1a-WM76WOPR.js → stateDiagram-0ff1cf1a-L3AKWENF.js} +5 -5
  48. package/dist/browser/dev/excalidraw-assets-dev/{stateDiagram-v2-9a9d610d-N4HZW3O2.js → stateDiagram-v2-9a9d610d-NU3GGMCH.js} +8 -8
  49. package/dist/browser/dev/excalidraw-assets-dev/{timeline-definition-fea2a41d-ZHGCAXGP.js → timeline-definition-fea2a41d-JGP7XCHW.js} +2 -2
  50. package/dist/browser/dev/excalidraw-assets-dev/{xychartDiagram-ab372869-2DLOVRAZ.js → xychartDiagram-ab372869-HLFHHF2I.js} +3 -3
  51. package/dist/browser/dev/index.css +668 -191
  52. package/dist/browser/dev/index.css.map +3 -3
  53. package/dist/browser/dev/index.js +14529 -9273
  54. package/dist/browser/dev/index.js.map +4 -4
  55. package/dist/browser/prod/excalidraw-assets/CascadiaMono-Regular-NWCXRPUG.woff2 +0 -0
  56. package/dist/browser/prod/excalidraw-assets/ComicShanns-Regular-6TOETDFT.woff2 +0 -0
  57. package/dist/browser/prod/excalidraw-assets/Excalifont-Regular-CPKEUDVM.woff2 +0 -0
  58. package/dist/browser/prod/excalidraw-assets/LiberationSans-Regular-ZQD73GJM.woff2 +0 -0
  59. package/dist/browser/prod/excalidraw-assets/Virgil-Regular-YHAB2VGJ.woff2 +0 -0
  60. package/dist/browser/prod/excalidraw-assets/{blockDiagram-91b80b7a-ONPS22AM.js → blockDiagram-91b80b7a-FVCRVGN5.js} +1 -1
  61. package/dist/browser/prod/excalidraw-assets/{c4Diagram-b2a90758-XMIQY7ZT.js → c4Diagram-b2a90758-56CXO7GA.js} +1 -1
  62. package/dist/browser/prod/excalidraw-assets/{chunk-GCHQBOKV.js → chunk-635MQ3CK.js} +1 -1
  63. package/dist/browser/prod/excalidraw-assets/{chunk-P5M3G2RP.js → chunk-AIKXYJX3.js} +1 -1
  64. package/dist/browser/prod/excalidraw-assets/{chunk-E2YLWFZX.js → chunk-CR7VMNWC.js} +1 -1
  65. package/dist/browser/prod/excalidraw-assets/chunk-F3JN4YS2.js +61 -0
  66. package/dist/browser/prod/excalidraw-assets/{chunk-WEYK4A2L.js → chunk-FFF2CSVG.js} +1 -1
  67. package/dist/browser/prod/excalidraw-assets/{chunk-R3HAIP6R.js → chunk-G4WDCSPE.js} +1 -1
  68. package/dist/browser/prod/excalidraw-assets/{chunk-HFOXJM22.js → chunk-HKZSHFLX.js} +1 -1
  69. package/dist/browser/prod/excalidraw-assets/{chunk-XIMFFJTE.js → chunk-IKCDYWMW.js} +1 -1
  70. package/dist/browser/prod/excalidraw-assets/{chunk-AHLLBBVJ.js → chunk-L5DS24G6.js} +1 -1
  71. package/dist/browser/prod/excalidraw-assets/{chunk-CQJF3C6G.js → chunk-MUNOKHUD.js} +1 -1
  72. package/dist/browser/prod/excalidraw-assets/{chunk-CTYINSWT.js → chunk-MXFM2KIK.js} +5 -5
  73. package/dist/browser/prod/excalidraw-assets/{chunk-NI6SYCUG.js → chunk-QOQYOOQ4.js} +1 -1
  74. package/dist/browser/prod/excalidraw-assets/{chunk-I2PZFXTK.js → chunk-ZTIWFPBM.js} +21 -21
  75. package/dist/browser/prod/excalidraw-assets/{classDiagram-30eddba6-IEJXXCVX.js → classDiagram-30eddba6-BCUTAUMD.js} +1 -1
  76. package/dist/browser/prod/excalidraw-assets/{classDiagram-v2-f2df5561-7LZDSWOS.js → classDiagram-v2-f2df5561-6SOXSGQ2.js} +1 -1
  77. package/dist/browser/prod/excalidraw-assets/dist-NLUQPPQQ.js +7 -0
  78. package/dist/browser/prod/excalidraw-assets/en-SQSS4H2K.js +1 -0
  79. package/dist/browser/prod/excalidraw-assets/{erDiagram-47591fe2-E5V666CF.js → erDiagram-47591fe2-RE6HB7RM.js} +1 -1
  80. package/dist/browser/prod/excalidraw-assets/{flowDiagram-5540d9b9-GMBRCYVF.js → flowDiagram-5540d9b9-ZNJZBERW.js} +1 -1
  81. package/dist/browser/prod/excalidraw-assets/{flowDiagram-v2-3b53844e-Z4HUWP6B.js → flowDiagram-v2-3b53844e-LY44JLQJ.js} +1 -1
  82. package/dist/browser/prod/excalidraw-assets/{flowchart-elk-definition-5fe447d6-5ZCYTX5N.js → flowchart-elk-definition-5fe447d6-TMTJ6Z7O.js} +1 -1
  83. package/dist/browser/prod/excalidraw-assets/{ganttDiagram-9a3bba1f-WM32OMT5.js → ganttDiagram-9a3bba1f-5O6EA6LX.js} +1 -1
  84. package/dist/browser/prod/excalidraw-assets/{gitGraphDiagram-96e6b4ee-CAKZ2U6E.js → gitGraphDiagram-96e6b4ee-UHYNM5DI.js} +1 -1
  85. package/dist/browser/prod/excalidraw-assets/image-CMH7O36X.js +1 -0
  86. package/dist/browser/prod/excalidraw-assets/{infoDiagram-bcd20f53-MUIKXGC4.js → infoDiagram-bcd20f53-BP77NQEH.js} +1 -1
  87. package/dist/browser/prod/excalidraw-assets/{journeyDiagram-4fe6b3dc-NYRV4HK2.js → journeyDiagram-4fe6b3dc-XMGKCMES.js} +1 -1
  88. package/dist/browser/prod/excalidraw-assets/{mindmap-definition-f354de21-MY55DRSM.js → mindmap-definition-f354de21-ZQRRBRWF.js} +1 -1
  89. package/dist/browser/prod/excalidraw-assets/{pieDiagram-79897490-47L6J6L2.js → pieDiagram-79897490-IGXEC2KX.js} +1 -1
  90. package/dist/browser/prod/excalidraw-assets/{quadrantDiagram-62f64e94-DF5C2GDT.js → quadrantDiagram-62f64e94-WTHHDYJL.js} +1 -1
  91. package/dist/browser/prod/excalidraw-assets/{requirementDiagram-05bf5f74-C4IMUBDN.js → requirementDiagram-05bf5f74-MV4OFRVW.js} +1 -1
  92. package/dist/browser/prod/excalidraw-assets/{sankeyDiagram-97764748-YHW7EUST.js → sankeyDiagram-97764748-ZGYUHEJT.js} +1 -1
  93. package/dist/browser/prod/excalidraw-assets/{sequenceDiagram-acc0e65c-H3XEHT32.js → sequenceDiagram-acc0e65c-IBSENK6W.js} +1 -1
  94. package/dist/browser/prod/excalidraw-assets/{stateDiagram-0ff1cf1a-Z5WB6Q3P.js → stateDiagram-0ff1cf1a-DB73XNZH.js} +1 -1
  95. package/dist/browser/prod/excalidraw-assets/{stateDiagram-v2-9a9d610d-T7OZETQC.js → stateDiagram-v2-9a9d610d-2OOBUPNR.js} +1 -1
  96. package/dist/browser/prod/excalidraw-assets/{timeline-definition-fea2a41d-VVC22BWF.js → timeline-definition-fea2a41d-P3NQQVDU.js} +1 -1
  97. package/dist/browser/prod/excalidraw-assets/{xychartDiagram-ab372869-JAXODQF7.js → xychartDiagram-ab372869-HI3XLK3Y.js} +1 -1
  98. package/dist/browser/prod/index.css +1 -1
  99. package/dist/browser/prod/index.js +70 -51
  100. package/dist/dev/CascadiaMono-Regular-NWCXRPUG.woff2 +0 -0
  101. package/dist/dev/ComicShanns-Regular-6TOETDFT.woff2 +0 -0
  102. package/dist/dev/Excalifont-Regular-CPKEUDVM.woff2 +0 -0
  103. package/dist/dev/LiberationSans-Regular-ZQD73GJM.woff2 +0 -0
  104. package/dist/dev/Virgil-Regular-YHAB2VGJ.woff2 +0 -0
  105. package/dist/{prod/en-II4GK66F.json → dev/en-HDAPS7XK.json} +41 -9
  106. package/dist/dev/index.css +668 -191
  107. package/dist/dev/index.css.map +3 -3
  108. package/dist/dev/index.js +22026 -16675
  109. package/dist/dev/index.js.map +4 -4
  110. package/dist/excalidraw/actions/actionAddToLibrary.d.ts +22 -10
  111. package/dist/excalidraw/actions/actionAddToLibrary.js +4 -3
  112. package/dist/excalidraw/actions/actionAlign.d.ts +22 -22
  113. package/dist/excalidraw/actions/actionAlign.js +7 -6
  114. package/dist/excalidraw/actions/actionBoundText.d.ts +22 -14
  115. package/dist/excalidraw/actions/actionBoundText.js +11 -5
  116. package/dist/excalidraw/actions/actionCanvas.d.ts +124 -76
  117. package/dist/excalidraw/actions/actionCanvas.js +20 -15
  118. package/dist/excalidraw/actions/actionClipboard.d.ts +66 -38
  119. package/dist/excalidraw/actions/actionClipboard.js +14 -13
  120. package/dist/excalidraw/actions/actionDeleteSelected.d.ts +28 -16
  121. package/dist/excalidraw/actions/actionDeleteSelected.js +6 -3
  122. package/dist/excalidraw/actions/actionDistribute.d.ts +10 -10
  123. package/dist/excalidraw/actions/actionDistribute.js +3 -2
  124. package/dist/excalidraw/actions/actionDuplicateSelection.d.ts +7 -8
  125. package/dist/excalidraw/actions/actionDuplicateSelection.js +7 -3
  126. package/dist/excalidraw/actions/actionElementLock.d.ts +21 -13
  127. package/dist/excalidraw/actions/actionElementLock.js +3 -2
  128. package/dist/excalidraw/actions/actionExport.d.ts +87 -51
  129. package/dist/excalidraw/actions/actionExport.js +15 -11
  130. package/dist/excalidraw/actions/actionFinalize.d.ts +21 -13
  131. package/dist/excalidraw/actions/actionFinalize.js +9 -5
  132. package/dist/excalidraw/actions/actionFlip.d.ts +10 -10
  133. package/dist/excalidraw/actions/actionFlip.js +12 -12
  134. package/dist/excalidraw/actions/actionFrame.d.ts +193 -24
  135. package/dist/excalidraw/actions/actionFrame.js +7 -6
  136. package/dist/excalidraw/actions/actionGroup.d.ts +28 -20
  137. package/dist/excalidraw/actions/actionGroup.js +9 -11
  138. package/dist/excalidraw/actions/actionHistory.d.ts +4 -3
  139. package/dist/excalidraw/actions/actionHistory.js +27 -28
  140. package/dist/excalidraw/actions/actionLinearEditor.d.ts +12 -6
  141. package/dist/excalidraw/actions/actionLinearEditor.js +21 -5
  142. package/dist/excalidraw/actions/actionLink.d.ts +11 -7
  143. package/dist/excalidraw/actions/actionLink.js +2 -1
  144. package/dist/excalidraw/actions/actionMenu.d.ts +26 -14
  145. package/dist/excalidraw/actions/actionMenu.js +4 -3
  146. package/dist/excalidraw/actions/actionNavigate.d.ts +16 -8
  147. package/dist/excalidraw/actions/actionNavigate.js +3 -2
  148. package/dist/excalidraw/actions/actionProperties.d.ts +272 -62
  149. package/dist/excalidraw/actions/actionProperties.js +261 -59
  150. package/dist/excalidraw/actions/actionSelectAll.d.ts +11 -7
  151. package/dist/excalidraw/actions/actionSelectAll.js +2 -1
  152. package/dist/excalidraw/actions/actionStyles.d.ts +17 -13
  153. package/dist/excalidraw/actions/actionStyles.js +7 -5
  154. package/dist/excalidraw/actions/actionTextAutoResize.d.ts +17 -0
  155. package/dist/excalidraw/actions/actionTextAutoResize.js +38 -0
  156. package/dist/excalidraw/actions/actionToggleGridMode.d.ts +13 -7
  157. package/dist/excalidraw/actions/actionToggleGridMode.js +6 -2
  158. package/dist/excalidraw/actions/actionToggleObjectsSnapMode.d.ts +10 -6
  159. package/dist/excalidraw/actions/actionToggleObjectsSnapMode.js +2 -1
  160. package/dist/excalidraw/actions/actionToggleStats.d.ts +11 -6
  161. package/dist/excalidraw/actions/actionToggleStats.js +6 -4
  162. package/dist/excalidraw/actions/actionToggleViewMode.d.ts +10 -6
  163. package/dist/excalidraw/actions/actionToggleViewMode.js +2 -1
  164. package/dist/excalidraw/actions/actionToggleZenMode.d.ts +10 -6
  165. package/dist/excalidraw/actions/actionToggleZenMode.js +2 -1
  166. package/dist/excalidraw/actions/actionZindex.d.ts +23 -19
  167. package/dist/excalidraw/actions/actionZindex.js +9 -4
  168. package/dist/excalidraw/actions/manager.d.ts +5 -5
  169. package/dist/excalidraw/actions/register.d.ts +1 -1
  170. package/dist/excalidraw/actions/shortcuts.d.ts +2 -2
  171. package/dist/excalidraw/actions/shortcuts.js +1 -1
  172. package/dist/excalidraw/actions/types.d.ts +8 -8
  173. package/dist/excalidraw/align.d.ts +1 -1
  174. package/dist/excalidraw/analytics.js +9 -7
  175. package/dist/excalidraw/animated-trail.d.ts +2 -2
  176. package/dist/excalidraw/appState.d.ts +9 -6
  177. package/dist/excalidraw/appState.js +8 -3
  178. package/dist/excalidraw/change.d.ts +191 -0
  179. package/dist/excalidraw/change.js +901 -0
  180. package/dist/excalidraw/charts.d.ts +1 -1
  181. package/dist/excalidraw/clients.d.ts +2 -2
  182. package/dist/excalidraw/clients.js +1 -1
  183. package/dist/excalidraw/clipboard.d.ts +3 -3
  184. package/dist/excalidraw/colors.d.ts +1 -1
  185. package/dist/excalidraw/components/Actions.d.ts +3 -3
  186. package/dist/excalidraw/components/Actions.js +10 -7
  187. package/dist/excalidraw/components/App.d.ts +26 -16
  188. package/dist/excalidraw/components/App.js +490 -217
  189. package/dist/excalidraw/components/ButtonIcon.d.ts +15 -0
  190. package/dist/excalidraw/components/ButtonIcon.js +8 -0
  191. package/dist/excalidraw/components/ButtonIconSelect.js +2 -3
  192. package/dist/excalidraw/components/ButtonSeparator.d.ts +2 -0
  193. package/dist/excalidraw/components/ButtonSeparator.js +7 -0
  194. package/dist/excalidraw/components/CheckboxItem.js +1 -1
  195. package/dist/excalidraw/components/ColorPicker/ColorInput.d.ts +1 -1
  196. package/dist/excalidraw/components/ColorPicker/ColorInput.js +1 -1
  197. package/dist/excalidraw/components/ColorPicker/ColorPicker.d.ts +4 -4
  198. package/dist/excalidraw/components/ColorPicker/ColorPicker.js +48 -80
  199. package/dist/excalidraw/components/ColorPicker/Picker.d.ts +3 -3
  200. package/dist/excalidraw/components/ColorPicker/Picker.js +1 -1
  201. package/dist/excalidraw/components/ColorPicker/PickerColorList.d.ts +1 -1
  202. package/dist/excalidraw/components/ColorPicker/PickerHeading.d.ts +1 -1
  203. package/dist/excalidraw/components/ColorPicker/ShadeList.d.ts +1 -1
  204. package/dist/excalidraw/components/ColorPicker/TopPicks.d.ts +1 -1
  205. package/dist/excalidraw/components/ColorPicker/colorPickerUtils.d.ts +2 -2
  206. package/dist/excalidraw/components/ColorPicker/colorPickerUtils.js +1 -1
  207. package/dist/excalidraw/components/ColorPicker/keyboardNavHandlers.d.ts +2 -2
  208. package/dist/excalidraw/components/ColorPicker/keyboardNavHandlers.js +1 -1
  209. package/dist/excalidraw/components/CommandPalette/CommandPalette.d.ts +1 -1
  210. package/dist/excalidraw/components/CommandPalette/CommandPalette.js +33 -18
  211. package/dist/excalidraw/components/CommandPalette/defaultCommandPaletteItems.d.ts +1 -1
  212. package/dist/excalidraw/components/CommandPalette/types.d.ts +3 -3
  213. package/dist/excalidraw/components/ConfirmDialog.d.ts +1 -1
  214. package/dist/excalidraw/components/ContextMenu.d.ts +2 -2
  215. package/dist/excalidraw/components/ContextMenu.js +2 -2
  216. package/dist/excalidraw/components/DarkModeToggle.d.ts +1 -1
  217. package/dist/excalidraw/components/DarkModeToggle.js +3 -1
  218. package/dist/excalidraw/components/DefaultSidebar.d.ts +2 -2
  219. package/dist/excalidraw/components/Dialog.js +1 -1
  220. package/dist/excalidraw/components/DialogActionButton.d.ts +1 -1
  221. package/dist/excalidraw/components/EyeDropper.d.ts +2 -2
  222. package/dist/excalidraw/components/FollowMode/FollowMode.d.ts +1 -1
  223. package/dist/excalidraw/components/FollowMode/FollowMode.js +1 -1
  224. package/dist/excalidraw/components/FontPicker/FontPicker.d.ts +21 -0
  225. package/dist/excalidraw/components/FontPicker/FontPicker.js +49 -0
  226. package/dist/excalidraw/components/FontPicker/FontPickerList.d.ts +25 -0
  227. package/dist/excalidraw/components/FontPicker/FontPickerList.js +119 -0
  228. package/dist/excalidraw/components/FontPicker/FontPickerTrigger.d.ts +7 -0
  229. package/dist/excalidraw/components/FontPicker/FontPickerTrigger.js +13 -0
  230. package/dist/excalidraw/components/FontPicker/keyboardNavHandlers.d.ts +14 -0
  231. package/dist/excalidraw/components/FontPicker/keyboardNavHandlers.js +38 -0
  232. package/dist/excalidraw/components/HelpDialog.js +9 -7
  233. package/dist/excalidraw/components/HintViewer.d.ts +1 -1
  234. package/dist/excalidraw/components/IconPicker.js +2 -2
  235. package/dist/excalidraw/components/ImageExportDialog.d.ts +1 -1
  236. package/dist/excalidraw/components/InitializeApp.d.ts +2 -2
  237. package/dist/excalidraw/components/JSONExportDialog.d.ts +3 -3
  238. package/dist/excalidraw/components/LayerUI.d.ts +4 -4
  239. package/dist/excalidraw/components/LayerUI.js +10 -7
  240. package/dist/excalidraw/components/LibraryMenu.d.ts +2 -2
  241. package/dist/excalidraw/components/LibraryMenuBrowseButton.d.ts +1 -1
  242. package/dist/excalidraw/components/LibraryMenuControlButtons.d.ts +1 -1
  243. package/dist/excalidraw/components/LibraryMenuHeaderContent.d.ts +2 -2
  244. package/dist/excalidraw/components/LibraryMenuItems.d.ts +1 -1
  245. package/dist/excalidraw/components/LibraryMenuSection.d.ts +5 -4
  246. package/dist/excalidraw/components/LibraryUnit.d.ts +2 -2
  247. package/dist/excalidraw/components/LoadingMessage.d.ts +1 -1
  248. package/dist/excalidraw/components/MagicSettings.js +2 -2
  249. package/dist/excalidraw/components/MobileMenu.d.ts +3 -3
  250. package/dist/excalidraw/components/MobileMenu.js +2 -6
  251. package/dist/excalidraw/components/Modal.d.ts +1 -1
  252. package/dist/excalidraw/components/OverwriteConfirm/OverwriteConfirmState.d.ts +1 -1
  253. package/dist/excalidraw/components/PasteChartDialog.d.ts +1 -1
  254. package/dist/excalidraw/components/PasteChartDialog.js +1 -1
  255. package/dist/excalidraw/components/PropertiesPopover.d.ts +15 -0
  256. package/dist/excalidraw/components/PropertiesPopover.js +31 -0
  257. package/dist/excalidraw/components/PublishLibrary.d.ts +1 -1
  258. package/dist/excalidraw/components/QuickSearch.d.ts +9 -0
  259. package/dist/excalidraw/components/QuickSearch.js +8 -0
  260. package/dist/excalidraw/components/RadioGroup.d.ts +2 -1
  261. package/dist/excalidraw/components/RadioGroup.js +1 -1
  262. package/dist/excalidraw/components/SVGLayer.d.ts +1 -1
  263. package/dist/excalidraw/components/ScrollableList.d.ts +9 -0
  264. package/dist/excalidraw/components/ScrollableList.js +8 -0
  265. package/dist/excalidraw/components/Sidebar/Sidebar.d.ts +2 -2
  266. package/dist/excalidraw/components/Sidebar/Sidebar.js +1 -1
  267. package/dist/excalidraw/components/Sidebar/SidebarTab.d.ts +1 -1
  268. package/dist/excalidraw/components/Sidebar/SidebarTabTrigger.d.ts +1 -1
  269. package/dist/excalidraw/components/Sidebar/SidebarTrigger.d.ts +1 -1
  270. package/dist/excalidraw/components/Sidebar/common.d.ts +1 -1
  271. package/dist/excalidraw/components/Stack.d.ts +2 -2
  272. package/dist/excalidraw/components/Stats/Angle.d.ts +12 -0
  273. package/dist/excalidraw/components/Stats/Angle.js +52 -0
  274. package/dist/excalidraw/components/Stats/Collapsible.d.ts +9 -0
  275. package/dist/excalidraw/components/Stats/Collapsible.js +12 -0
  276. package/dist/excalidraw/components/Stats/Dimension.d.ts +12 -0
  277. package/dist/excalidraw/components/Stats/Dimension.js +67 -0
  278. package/dist/excalidraw/components/Stats/DragInput.d.ts +32 -0
  279. package/dist/excalidraw/components/Stats/DragInput.js +174 -0
  280. package/dist/excalidraw/components/Stats/FontSize.d.ts +12 -0
  281. package/dist/excalidraw/components/Stats/FontSize.js +50 -0
  282. package/dist/excalidraw/components/Stats/MultiAngle.d.ts +12 -0
  283. package/dist/excalidraw/components/Stats/MultiAngle.js +66 -0
  284. package/dist/excalidraw/components/Stats/MultiDimension.d.ts +15 -0
  285. package/dist/excalidraw/components/Stats/MultiDimension.js +197 -0
  286. package/dist/excalidraw/components/Stats/MultiFontSize.d.ts +13 -0
  287. package/dist/excalidraw/components/Stats/MultiFontSize.js +72 -0
  288. package/dist/excalidraw/components/Stats/MultiPosition.d.ts +15 -0
  289. package/dist/excalidraw/components/Stats/MultiPosition.js +100 -0
  290. package/dist/excalidraw/components/Stats/Position.d.ts +13 -0
  291. package/dist/excalidraw/components/Stats/Position.js +39 -0
  292. package/dist/excalidraw/components/Stats/index.d.ts +16 -0
  293. package/dist/excalidraw/components/Stats/index.js +78 -0
  294. package/dist/excalidraw/components/Stats/utils.d.ts +25 -0
  295. package/dist/excalidraw/components/Stats/utils.js +158 -0
  296. package/dist/excalidraw/components/TTDDialog/MermaidToExcalidraw.d.ts +1 -1
  297. package/dist/excalidraw/components/TTDDialog/MermaidToExcalidraw.js +6 -2
  298. package/dist/excalidraw/components/TTDDialog/TTDDialog.js +2 -2
  299. package/dist/excalidraw/components/TTDDialog/TTDDialogInput.d.ts +1 -1
  300. package/dist/excalidraw/components/TTDDialog/TTDDialogPanel.d.ts +1 -1
  301. package/dist/excalidraw/components/TTDDialog/TTDDialogPanels.d.ts +1 -1
  302. package/dist/excalidraw/components/TTDDialog/TTDDialogTabs.d.ts +1 -1
  303. package/dist/excalidraw/components/TTDDialog/TTDDialogTrigger.d.ts +1 -1
  304. package/dist/excalidraw/components/TTDDialog/common.d.ts +5 -5
  305. package/dist/excalidraw/components/TTDDialog/common.js +3 -7
  306. package/dist/excalidraw/components/TextField.d.ts +1 -1
  307. package/dist/excalidraw/components/Toast.d.ts +1 -1
  308. package/dist/excalidraw/components/ToolButton.d.ts +4 -2
  309. package/dist/excalidraw/components/ToolButton.js +1 -1
  310. package/dist/excalidraw/components/Trans.d.ts +1 -1
  311. package/dist/excalidraw/components/UserList.d.ts +1 -1
  312. package/dist/excalidraw/components/UserList.js +22 -22
  313. package/dist/excalidraw/components/canvases/InteractiveCanvas.d.ts +5 -3
  314. package/dist/excalidraw/components/canvases/InteractiveCanvas.js +5 -2
  315. package/dist/excalidraw/components/canvases/StaticCanvas.d.ts +2 -2
  316. package/dist/excalidraw/components/canvases/StaticCanvas.js +3 -2
  317. package/dist/excalidraw/components/dropdownMenu/DropdownMenu.d.ts +12 -3
  318. package/dist/excalidraw/components/dropdownMenu/DropdownMenuItem.d.ts +24 -4
  319. package/dist/excalidraw/components/dropdownMenu/DropdownMenuItem.js +55 -14
  320. package/dist/excalidraw/components/dropdownMenu/DropdownMenuItemContent.d.ts +2 -1
  321. package/dist/excalidraw/components/dropdownMenu/DropdownMenuItemContent.js +2 -2
  322. package/dist/excalidraw/components/dropdownMenu/DropdownMenuItemContentRadio.d.ts +18 -0
  323. package/dist/excalidraw/components/dropdownMenu/DropdownMenuItemContentRadio.js +9 -0
  324. package/dist/excalidraw/components/dropdownMenu/common.d.ts +1 -1
  325. package/dist/excalidraw/components/dropdownMenu/common.js +3 -2
  326. package/dist/excalidraw/components/footer/Footer.d.ts +2 -2
  327. package/dist/excalidraw/components/hyperlink/Hyperlink.d.ts +2 -2
  328. package/dist/excalidraw/components/hyperlink/Hyperlink.js +3 -3
  329. package/dist/excalidraw/components/hyperlink/helpers.d.ts +3 -3
  330. package/dist/excalidraw/components/hyperlink/helpers.js +2 -3
  331. package/dist/excalidraw/components/icons.d.ts +10 -1
  332. package/dist/excalidraw/components/icons.js +27 -5
  333. package/dist/excalidraw/components/main-menu/DefaultItems.d.ts +12 -2
  334. package/dist/excalidraw/components/main-menu/DefaultItems.js +38 -7
  335. package/dist/excalidraw/components/main-menu/MainMenu.d.ts +12 -3
  336. package/dist/excalidraw/components/welcome-screen/WelcomeScreen.Center.js +2 -2
  337. package/dist/excalidraw/components/welcome-screen/WelcomeScreen.Hints.js +3 -3
  338. package/dist/excalidraw/constants.d.ts +24 -6
  339. package/dist/excalidraw/constants.js +29 -7
  340. package/dist/excalidraw/context/ui-appState.d.ts +1 -1
  341. package/dist/excalidraw/cursor.d.ts +1 -1
  342. package/dist/excalidraw/data/EditorLocalStorage.d.ts +2 -2
  343. package/dist/excalidraw/data/blob.d.ts +5 -5
  344. package/dist/excalidraw/data/filesystem.d.ts +2 -1
  345. package/dist/excalidraw/data/index.d.ts +4 -4
  346. package/dist/excalidraw/data/json.d.ts +3 -3
  347. package/dist/excalidraw/data/library.d.ts +3 -3
  348. package/dist/excalidraw/data/magic.d.ts +3 -3
  349. package/dist/excalidraw/data/magic.js +2 -1
  350. package/dist/excalidraw/data/reconcile.d.ts +6 -0
  351. package/dist/excalidraw/data/reconcile.js +49 -0
  352. package/dist/excalidraw/data/resave.d.ts +2 -2
  353. package/dist/excalidraw/data/restore.d.ts +5 -5
  354. package/dist/excalidraw/data/restore.js +25 -10
  355. package/dist/excalidraw/data/transform.d.ts +4 -4
  356. package/dist/excalidraw/data/transform.js +15 -5
  357. package/dist/excalidraw/data/types.d.ts +3 -3
  358. package/dist/excalidraw/data/url.d.ts +1 -0
  359. package/dist/excalidraw/data/url.js +4 -1
  360. package/dist/excalidraw/element/ElementCanvasButtons.d.ts +1 -1
  361. package/dist/excalidraw/element/binding.d.ts +49 -8
  362. package/dist/excalidraw/element/binding.js +716 -153
  363. package/dist/excalidraw/element/bounds.d.ts +3 -4
  364. package/dist/excalidraw/element/bounds.js +0 -3
  365. package/dist/excalidraw/element/collision.d.ts +14 -19
  366. package/dist/excalidraw/element/collision.js +36 -713
  367. package/dist/excalidraw/element/containerCache.d.ts +1 -1
  368. package/dist/excalidraw/element/dragElements.d.ts +4 -4
  369. package/dist/excalidraw/element/dragElements.js +27 -3
  370. package/dist/excalidraw/element/embeddable.d.ts +15 -8
  371. package/dist/excalidraw/element/embeddable.js +98 -62
  372. package/dist/excalidraw/element/image.d.ts +2 -2
  373. package/dist/excalidraw/element/index.d.ts +2 -3
  374. package/dist/excalidraw/element/index.js +1 -2
  375. package/dist/excalidraw/element/linearElementEditor.d.ts +18 -14
  376. package/dist/excalidraw/element/linearElementEditor.js +12 -7
  377. package/dist/excalidraw/element/mutateElement.d.ts +4 -5
  378. package/dist/excalidraw/element/mutateElement.js +5 -3
  379. package/dist/excalidraw/element/newElement.d.ts +6 -9
  380. package/dist/excalidraw/element/newElement.js +21 -17
  381. package/dist/excalidraw/element/resizeElements.d.ts +12 -4
  382. package/dist/excalidraw/element/resizeElements.js +174 -98
  383. package/dist/excalidraw/element/resizeTest.d.ts +7 -7
  384. package/dist/excalidraw/element/resizeTest.js +53 -8
  385. package/dist/excalidraw/element/showSelectedShapeActions.d.ts +2 -2
  386. package/dist/excalidraw/element/sizeHelpers.d.ts +2 -2
  387. package/dist/excalidraw/element/sizeHelpers.js +3 -0
  388. package/dist/excalidraw/element/sortElements.d.ts +1 -1
  389. package/dist/excalidraw/element/textElement.d.ts +7 -30
  390. package/dist/excalidraw/element/textElement.js +68 -144
  391. package/dist/excalidraw/element/textWysiwyg.d.ts +12 -6
  392. package/dist/excalidraw/element/textWysiwyg.js +75 -62
  393. package/dist/excalidraw/element/transformHandles.d.ts +24 -6
  394. package/dist/excalidraw/element/transformHandles.js +22 -11
  395. package/dist/excalidraw/element/typeChecks.d.ts +5 -5
  396. package/dist/excalidraw/element/types.d.ts +33 -10
  397. package/dist/excalidraw/emitter.d.ts +1 -1
  398. package/dist/excalidraw/errors.d.ts +3 -0
  399. package/dist/excalidraw/errors.js +3 -0
  400. package/dist/excalidraw/fonts/ExcalidrawFont.d.ts +17 -0
  401. package/dist/excalidraw/fonts/ExcalidrawFont.js +50 -0
  402. package/dist/excalidraw/fonts/index.d.ts +45 -0
  403. package/dist/excalidraw/fonts/index.js +202 -0
  404. package/dist/excalidraw/fonts/metadata.d.ts +34 -0
  405. package/dist/excalidraw/fonts/metadata.js +90 -0
  406. package/dist/excalidraw/fractionalIndex.d.ts +40 -0
  407. package/dist/excalidraw/fractionalIndex.js +239 -0
  408. package/dist/excalidraw/frame.d.ts +4 -4
  409. package/dist/excalidraw/gatransforms.d.ts +1 -1
  410. package/dist/excalidraw/gesture.d.ts +1 -1
  411. package/dist/excalidraw/groups.d.ts +6 -4
  412. package/dist/excalidraw/groups.js +17 -0
  413. package/dist/excalidraw/history.d.ts +35 -47
  414. package/dist/excalidraw/history.js +100 -167
  415. package/dist/excalidraw/hooks/useCreatePortalContainer.js +2 -1
  416. package/dist/excalidraw/hooks/useEmitter.d.ts +2 -0
  417. package/dist/excalidraw/hooks/useEmitter.js +13 -0
  418. package/dist/excalidraw/hooks/useLibraryItemSvg.d.ts +1 -1
  419. package/dist/excalidraw/i18n.d.ts +1 -1
  420. package/dist/excalidraw/index.d.ts +4 -2
  421. package/dist/excalidraw/index.js +5 -3
  422. package/dist/excalidraw/jotai.d.ts +1 -1
  423. package/dist/excalidraw/laser-trails.d.ts +3 -2
  424. package/dist/excalidraw/locales/en.json +41 -9
  425. package/dist/excalidraw/math.d.ts +4 -2
  426. package/dist/excalidraw/math.js +6 -0
  427. package/dist/excalidraw/mermaid.d.ts +2 -0
  428. package/dist/excalidraw/mermaid.js +28 -0
  429. package/dist/excalidraw/points.d.ts +1 -1
  430. package/dist/excalidraw/queue.d.ts +1 -1
  431. package/dist/excalidraw/renderer/helpers.d.ts +2 -2
  432. package/dist/excalidraw/renderer/helpers.js +2 -2
  433. package/dist/excalidraw/renderer/interactiveScene.d.ts +2 -2
  434. package/dist/excalidraw/renderer/interactiveScene.js +38 -11
  435. package/dist/excalidraw/renderer/renderElement.d.ts +6 -4
  436. package/dist/excalidraw/renderer/renderElement.js +81 -61
  437. package/dist/excalidraw/renderer/renderSnaps.d.ts +1 -1
  438. package/dist/excalidraw/renderer/renderSnaps.js +2 -1
  439. package/dist/excalidraw/renderer/staticScene.d.ts +1 -1
  440. package/dist/excalidraw/renderer/staticScene.js +14 -3
  441. package/dist/excalidraw/renderer/staticSvgScene.d.ts +4 -4
  442. package/dist/excalidraw/renderer/staticSvgScene.js +12 -1
  443. package/dist/excalidraw/scene/Renderer.d.ts +4 -4
  444. package/dist/excalidraw/scene/Renderer.js +2 -3
  445. package/dist/excalidraw/scene/Scene.d.ts +22 -12
  446. package/dist/excalidraw/scene/Scene.js +47 -23
  447. package/dist/excalidraw/scene/Shape.d.ts +1 -1
  448. package/dist/excalidraw/scene/ShapeCache.d.ts +4 -4
  449. package/dist/excalidraw/scene/comparisons.d.ts +2 -2
  450. package/dist/excalidraw/scene/export.d.ts +3 -2
  451. package/dist/excalidraw/scene/export.js +40 -29
  452. package/dist/excalidraw/scene/scroll.d.ts +2 -2
  453. package/dist/excalidraw/scene/scrollbars.d.ts +3 -3
  454. package/dist/excalidraw/scene/selection.d.ts +2 -2
  455. package/dist/excalidraw/scene/types.d.ts +7 -8
  456. package/dist/excalidraw/scene/zoom.d.ts +1 -1
  457. package/dist/excalidraw/shapes.d.ts +7 -0
  458. package/dist/excalidraw/shapes.js +40 -0
  459. package/dist/excalidraw/snapping.d.ts +4 -4
  460. package/dist/excalidraw/snapping.js +2 -1
  461. package/dist/excalidraw/store.d.ts +129 -0
  462. package/dist/excalidraw/store.js +296 -0
  463. package/dist/excalidraw/types.d.ts +45 -22
  464. package/dist/excalidraw/utils.d.ts +20 -4
  465. package/dist/excalidraw/utils.js +29 -0
  466. package/dist/excalidraw/zindex.d.ts +4 -4
  467. package/dist/excalidraw/zindex.js +9 -13
  468. package/dist/prod/CascadiaMono-Regular-NWCXRPUG.woff2 +0 -0
  469. package/dist/prod/ComicShanns-Regular-6TOETDFT.woff2 +0 -0
  470. package/dist/prod/Excalifont-Regular-CPKEUDVM.woff2 +0 -0
  471. package/dist/prod/LiberationSans-Regular-ZQD73GJM.woff2 +0 -0
  472. package/dist/prod/Virgil-Regular-YHAB2VGJ.woff2 +0 -0
  473. package/dist/{dev/en-II4GK66F.json → prod/en-HDAPS7XK.json} +41 -9
  474. package/dist/prod/index.css +1 -1
  475. package/dist/prod/index.js +47 -53
  476. package/dist/utils/bbox.d.ts +2 -2
  477. package/dist/utils/collision.d.ts +4 -0
  478. package/dist/utils/collision.js +48 -0
  479. package/dist/utils/export.d.ts +4 -3
  480. package/dist/utils/export.js +2 -1
  481. package/dist/utils/geometry/geometry.d.ts +71 -0
  482. package/dist/utils/geometry/geometry.js +674 -0
  483. package/dist/utils/geometry/shape.d.ts +56 -0
  484. package/dist/utils/geometry/shape.js +168 -0
  485. package/dist/utils/index.d.ts +1 -0
  486. package/dist/utils/index.js +1 -0
  487. package/dist/utils/withinBounds.d.ts +1 -1
  488. package/history.ts +163 -218
  489. package/package.json +3 -2
  490. package/dist/browser/dev/Cascadia-CYPE3OJC.woff2 +0 -0
  491. package/dist/browser/dev/Virgil-UZN6MUT6.woff2 +0 -0
  492. package/dist/browser/dev/excalidraw-assets-dev/chunk-AK7SWNLN.js.map +0 -7
  493. package/dist/browser/dev/excalidraw-assets-dev/chunk-RWZVJAQU.js.map +0 -7
  494. package/dist/browser/dev/excalidraw-assets-dev/chunk-Z3PH3V2B.js.map +0 -7
  495. package/dist/browser/dev/excalidraw-assets-dev/dist-Z46EOVOL.js.map +0 -7
  496. package/dist/browser/dev/excalidraw-assets-dev/image-OFRRV5MB.css.map +0 -7
  497. package/dist/browser/prod/Cascadia-CYPE3OJC.woff2 +0 -0
  498. package/dist/browser/prod/Virgil-UZN6MUT6.woff2 +0 -0
  499. package/dist/browser/prod/excalidraw-assets/chunk-LL4GORAM.js +0 -55
  500. package/dist/browser/prod/excalidraw-assets/dist-PIPZXALV.js +0 -6
  501. package/dist/browser/prod/excalidraw-assets/en-LROPV2RN.js +0 -1
  502. package/dist/browser/prod/excalidraw-assets/image-EFCJDJH3.js +0 -1
  503. package/dist/dev/Cascadia-CYPE3OJC.woff2 +0 -0
  504. package/dist/dev/Virgil-UZN6MUT6.woff2 +0 -0
  505. package/dist/excalidraw/components/Stats.d.ts +0 -11
  506. package/dist/excalidraw/components/Stats.js +0 -13
  507. package/dist/excalidraw/scene/Fonts.d.ts +0 -21
  508. package/dist/excalidraw/scene/Fonts.js +0 -72
  509. package/dist/prod/Cascadia-CYPE3OJC.woff2 +0 -0
  510. package/dist/prod/Virgil-UZN6MUT6.woff2 +0 -0
  511. /package/dist/browser/dev/{Assistant-Bold-ZDZZ6JHA.woff2 → excalidraw-assets-dev/Assistant-Bold-ZDZZ6JHA.woff2} +0 -0
  512. /package/dist/browser/dev/{Assistant-Medium-DZ25RZU3.woff2 → excalidraw-assets-dev/Assistant-Medium-DZ25RZU3.woff2} +0 -0
  513. /package/dist/browser/dev/{Assistant-Regular-PLF2XOGW.woff2 → excalidraw-assets-dev/Assistant-Regular-PLF2XOGW.woff2} +0 -0
  514. /package/dist/browser/dev/{Assistant-SemiBold-CZ5MX6FK.woff2 → excalidraw-assets-dev/Assistant-SemiBold-CZ5MX6FK.woff2} +0 -0
  515. /package/dist/browser/dev/excalidraw-assets-dev/{blockDiagram-91b80b7a-ACFH36JV.js.map → blockDiagram-91b80b7a-H47FTXHA.js.map} +0 -0
  516. /package/dist/browser/dev/excalidraw-assets-dev/{c4Diagram-b2a90758-QZ27YR47.js.map → c4Diagram-b2a90758-NNJK6GKC.js.map} +0 -0
  517. /package/dist/browser/dev/excalidraw-assets-dev/{chunk-HO2HMSK7.js.map → chunk-4KQVEBHW.js.map} +0 -0
  518. /package/dist/browser/dev/excalidraw-assets-dev/{chunk-USGV265L.js.map → chunk-53YI56GV.js.map} +0 -0
  519. /package/dist/browser/dev/excalidraw-assets-dev/{chunk-EDFX3S7X.js.map → chunk-A2WCJI4I.js.map} +0 -0
  520. /package/dist/browser/dev/excalidraw-assets-dev/{chunk-IX4V72YG.js.map → chunk-EFLPX7NE.js.map} +0 -0
  521. /package/dist/browser/dev/excalidraw-assets-dev/{chunk-MXVETLVM.js.map → chunk-JYIQCNWV.js.map} +0 -0
  522. /package/dist/browser/dev/excalidraw-assets-dev/{chunk-YZIOORVX.js.map → chunk-LVIQQW6F.js.map} +0 -0
  523. /package/dist/browser/dev/excalidraw-assets-dev/{chunk-6U7GQNJT.js.map → chunk-PXLO3FOU.js.map} +0 -0
  524. /package/dist/browser/dev/excalidraw-assets-dev/{chunk-7DACDEY3.js.map → chunk-TO2AW5PW.js.map} +0 -0
  525. /package/dist/browser/dev/excalidraw-assets-dev/{chunk-NJ77ZFNJ.js.map → chunk-VURILHLY.js.map} +0 -0
  526. /package/dist/browser/dev/excalidraw-assets-dev/{chunk-2T2GU7NF.js.map → chunk-ZAYGSUHF.js.map} +0 -0
  527. /package/dist/browser/dev/excalidraw-assets-dev/{classDiagram-30eddba6-QSLMH4JW.js.map → classDiagram-30eddba6-CUYIJICN.js.map} +0 -0
  528. /package/dist/browser/dev/excalidraw-assets-dev/{classDiagram-v2-f2df5561-DY4DYQ5P.js.map → classDiagram-v2-f2df5561-K6WW6K73.js.map} +0 -0
  529. /package/dist/browser/dev/excalidraw-assets-dev/{en-5TCZHGGJ.js.map → en-RAFN4KPD.js.map} +0 -0
  530. /package/dist/browser/dev/excalidraw-assets-dev/{erDiagram-47591fe2-SOOJRTCB.js.map → erDiagram-47591fe2-XGAD7EEP.js.map} +0 -0
  531. /package/dist/browser/dev/excalidraw-assets-dev/{flowDiagram-5540d9b9-AHGL4KPK.js.map → flowDiagram-5540d9b9-B6EOVNNO.js.map} +0 -0
  532. /package/dist/browser/dev/excalidraw-assets-dev/{flowDiagram-v2-3b53844e-56LDZZWY.js.map → flowDiagram-v2-3b53844e-NUG24FJH.js.map} +0 -0
  533. /package/dist/browser/dev/excalidraw-assets-dev/{flowchart-elk-definition-5fe447d6-27LUKRI6.js.map → flowchart-elk-definition-5fe447d6-25Y7PCBL.js.map} +0 -0
  534. /package/dist/browser/dev/excalidraw-assets-dev/{ganttDiagram-9a3bba1f-EHGYGNG6.js.map → ganttDiagram-9a3bba1f-GNL6ZDTC.js.map} +0 -0
  535. /package/dist/browser/dev/excalidraw-assets-dev/{gitGraphDiagram-96e6b4ee-AJQNBDW5.js.map → gitGraphDiagram-96e6b4ee-HNW52NVO.js.map} +0 -0
  536. /package/dist/browser/dev/excalidraw-assets-dev/{image-EDKQZH7Z.js.map → image-ZNLDWTK3.js.map} +0 -0
  537. /package/dist/browser/dev/excalidraw-assets-dev/{infoDiagram-bcd20f53-SWLLQVES.js.map → infoDiagram-bcd20f53-FWEUVFLT.js.map} +0 -0
  538. /package/dist/browser/dev/excalidraw-assets-dev/{journeyDiagram-4fe6b3dc-7UAVCWOZ.js.map → journeyDiagram-4fe6b3dc-RZIUI7UG.js.map} +0 -0
  539. /package/dist/browser/dev/excalidraw-assets-dev/{mindmap-definition-f354de21-SROW5KGM.js.map → mindmap-definition-f354de21-GBVN45GU.js.map} +0 -0
  540. /package/dist/browser/dev/excalidraw-assets-dev/{pieDiagram-79897490-QKCI6NCB.js.map → pieDiagram-79897490-ECENNII6.js.map} +0 -0
  541. /package/dist/browser/dev/excalidraw-assets-dev/{quadrantDiagram-62f64e94-LNYJZFC5.js.map → quadrantDiagram-62f64e94-ZMEOFVNL.js.map} +0 -0
  542. /package/dist/browser/dev/excalidraw-assets-dev/{requirementDiagram-05bf5f74-ZZD7ZHFA.js.map → requirementDiagram-05bf5f74-FHZSFHCR.js.map} +0 -0
  543. /package/dist/browser/dev/excalidraw-assets-dev/{sankeyDiagram-97764748-L75ZZ4UM.js.map → sankeyDiagram-97764748-VDKIKTA6.js.map} +0 -0
  544. /package/dist/browser/dev/excalidraw-assets-dev/{sequenceDiagram-acc0e65c-6PCU7TDK.js.map → sequenceDiagram-acc0e65c-6JUSPVKX.js.map} +0 -0
  545. /package/dist/browser/dev/excalidraw-assets-dev/{stateDiagram-0ff1cf1a-WM76WOPR.js.map → stateDiagram-0ff1cf1a-L3AKWENF.js.map} +0 -0
  546. /package/dist/browser/dev/excalidraw-assets-dev/{stateDiagram-v2-9a9d610d-N4HZW3O2.js.map → stateDiagram-v2-9a9d610d-NU3GGMCH.js.map} +0 -0
  547. /package/dist/browser/dev/excalidraw-assets-dev/{timeline-definition-fea2a41d-ZHGCAXGP.js.map → timeline-definition-fea2a41d-JGP7XCHW.js.map} +0 -0
  548. /package/dist/browser/dev/excalidraw-assets-dev/{xychartDiagram-ab372869-2DLOVRAZ.js.map → xychartDiagram-ab372869-HLFHHF2I.js.map} +0 -0
  549. /package/dist/browser/prod/{Assistant-Bold-ZDZZ6JHA.woff2 → excalidraw-assets/Assistant-Bold-ZDZZ6JHA.woff2} +0 -0
  550. /package/dist/browser/prod/{Assistant-Medium-DZ25RZU3.woff2 → excalidraw-assets/Assistant-Medium-DZ25RZU3.woff2} +0 -0
  551. /package/dist/browser/prod/{Assistant-Regular-PLF2XOGW.woff2 → excalidraw-assets/Assistant-Regular-PLF2XOGW.woff2} +0 -0
  552. /package/dist/browser/prod/{Assistant-SemiBold-CZ5MX6FK.woff2 → excalidraw-assets/Assistant-SemiBold-CZ5MX6FK.woff2} +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, DEFAULT_TEXT_ALIGN, } 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, refreshTextDimensions, 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
- 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";
34
+ import { findShapeByKey, getElementShape } from "../shapes";
35
+ import { 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,13 +44,12 @@ 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, getLineHeightInPx, getMinTextElementWidth, isMeasureTextSupported, isValidTextContainer, measureText, wrapText, } 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";
50
51
  import { actionUnlockAllElements } from "../actions/actionElementLock";
51
- import { Fonts } from "../scene/Fonts";
52
+ import { Fonts, getLineHeight } from "../fonts";
52
53
  import { getFrameChildren, isCursorInFrame, bindElementsToFramesAfterDuplication, addElementsToFrame, replaceAllElementsInFrame, removeElementsFromFrame, getElementsInResizingFrame, getElementsInNewFrame, getContainingFrame, elementOverlapsWithFrame, updateFrameMembershipOfSelectedElements, isElementInFrame, getFrameLikeTitle, getElementsOverlappingFrame, filterElementsEligibleAsFrameChildren, } from "../frame";
53
54
  import { excludeElementsInFramesFromSelection, makeNextSelectedElementIds, } from "../scene/selection";
54
55
  import { actionPaste } from "../actions/actionClipboard";
@@ -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,15 +78,21 @@ 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, } 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";
93
+ import { actionTextAutoResize } from "../actions/actionTextAutoResize";
94
+ import { getVisibleSceneBounds } from "../element/bounds";
95
+ import { isMaybeMermaidDefinition } from "../mermaid";
89
96
  const AppContext = React.createContext(null);
90
97
  const AppPropsContext = React.createContext(null);
91
98
  const deviceContextInitialValue = {
@@ -158,13 +165,14 @@ class App extends React.Component {
158
165
  device = deviceContextInitialValue;
159
166
  excalidrawContainerRef = React.createRef();
160
167
  scene;
161
- renderer;
162
168
  fonts;
169
+ renderer;
163
170
  resizeObserver;
164
171
  nearestScrollableContainer;
165
172
  library;
166
173
  libraryItemsFromStorage;
167
174
  id;
175
+ store;
168
176
  history;
169
177
  excalidrawContainerValue;
170
178
  files = {};
@@ -236,6 +244,8 @@ class App extends React.Component {
236
244
  this.canvas = document.createElement("canvas");
237
245
  this.rc = rough.canvas(this.canvas);
238
246
  this.renderer = new Renderer(this.scene);
247
+ this.store = new Store();
248
+ this.history = new History();
239
249
  if (excalidrawAPI) {
240
250
  const api = {
241
251
  updateScene: this.updateScene,
@@ -279,14 +289,11 @@ class App extends React.Component {
279
289
  container: this.excalidrawContainerRef.current,
280
290
  id: this.id,
281
291
  };
282
- this.fonts = new Fonts({
283
- scene: this.scene,
284
- onSceneUpdated: this.onSceneUpdated,
285
- });
292
+ this.fonts = new Fonts({ scene: this.scene });
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" &&
@@ -437,7 +444,7 @@ class App extends React.Component {
437
444
  return false;
438
445
  });
439
446
  if (updated) {
440
- this.scene.informMutation();
447
+ this.scene.triggerUpdate();
441
448
  }
442
449
  // GC
443
450
  this.iFrameRefs.forEach((ref, id) => {
@@ -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) => {
@@ -796,9 +803,9 @@ class App extends React.Component {
796
803
  render() {
797
804
  const selectedElements = this.scene.getSelectedElements(this.state);
798
805
  const { renderTopRightUI, renderCustomStats } = this.props;
799
- const versionNonce = this.scene.getVersionNonce();
806
+ const sceneNonce = this.scene.getSceneNonce();
800
807
  const { elementsMap, visibleElements } = this.renderer.getRenderableElements({
801
- versionNonce,
808
+ sceneNonce,
802
809
  zoom: this.state.zoom,
803
810
  offsetLeft: this.state.offsetLeft,
804
811
  offsetTop: this.state.offsetTop,
@@ -867,14 +874,14 @@ class App extends React.Component {
867
874
  this.focusContainer();
868
875
  callback?.();
869
876
  });
870
- } })), _jsx(StaticCanvas, { canvas: this.canvas, rc: this.rc, elementsMap: elementsMap, allElementsMap: allElementsMap, visibleElements: visibleElements, versionNonce: versionNonce, selectionNonce: this.state.selectionElement?.versionNonce, scale: window.devicePixelRatio, appState: this.state, renderConfig: {
877
+ } })), _jsx(StaticCanvas, { canvas: this.canvas, rc: this.rc, elementsMap: elementsMap, allElementsMap: allElementsMap, visibleElements: visibleElements, sceneNonce: sceneNonce, selectionNonce: this.state.selectionElement?.versionNonce, scale: window.devicePixelRatio, appState: this.state, renderConfig: {
871
878
  imageCache: this.imageCache,
872
879
  isExporting: false,
873
880
  renderGrid: true,
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, allElementsMap: allElementsMap, selectedElements: selectedElements, sceneNonce: sceneNonce, selectionNonce: this.state.selectionElement?.versionNonce, scale: window.devicePixelRatio, appState: this.state, device: this.device, renderInteractiveSceneCallback: this.renderInteractiveSceneCallback, handleCanvasRef: this.handleInteractiveCanvasRef, onContextMenu: this.handleCanvasContextMenu, onPointerMove: this.handleCanvasPointerMove, onPointerUp: this.handleCanvasPointerUp, onPointerCancel: this.removePointer, onTouchMove: this.handleTouchMove, onPointerDown: this.handleCanvasPointerDown, onDoubleClick: this.handleCanvasDoubleClick }), this.state.userToFollow && (_jsx(FollowMode, { width: this.state.width, height: this.state.height, userToFollow: this.state.userToFollow, onDisconnect: this.maybeUnfollowRemoteUser })), this.renderFrameNames()] }), this.renderEmbeddables()] }) }) }) }) }) }) }) }));
878
885
  }
879
886
  focusContainer = () => {
880
887
  this.excalidrawContainerRef.current?.focus();
@@ -924,7 +931,7 @@ class App extends React.Component {
924
931
  mutateElement(frameElement, { customData: { generationData: data } }, false);
925
932
  }
926
933
  this.magicGenerations.set(frameElement.id, data);
927
- this.onSceneUpdated();
934
+ this.triggerRender();
928
935
  };
929
936
  getTextFromElements(elements) {
930
937
  const text = elements
@@ -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
  },
@@ -1176,6 +1184,13 @@ class App extends React.Component {
1176
1184
  if (this.unmounted || actionResult === false) {
1177
1185
  return;
1178
1186
  }
1187
+ if (actionResult.storeAction === StoreAction.UPDATE) {
1188
+ this.store.shouldUpdateSnapshot();
1189
+ }
1190
+ else if (actionResult.storeAction === StoreAction.CAPTURE) {
1191
+ this.store.shouldCaptureIncrement();
1192
+ }
1193
+ let didUpdate = false;
1179
1194
  let editingElement = null;
1180
1195
  if (actionResult.elements) {
1181
1196
  actionResult.elements.forEach((element) => {
@@ -1186,9 +1201,7 @@ class App extends React.Component {
1186
1201
  }
1187
1202
  });
1188
1203
  this.scene.replaceAllElements(actionResult.elements);
1189
- if (actionResult.commitToHistory) {
1190
- this.history.resumeRecording();
1191
- }
1204
+ didUpdate = true;
1192
1205
  }
1193
1206
  if (actionResult.files) {
1194
1207
  this.files = actionResult.replaceFiles
@@ -1197,9 +1210,6 @@ class App extends React.Component {
1197
1210
  this.addNewImagesToImageCache();
1198
1211
  }
1199
1212
  if (actionResult.appState || editingElement || this.state.contextMenu) {
1200
- if (actionResult.commitToHistory) {
1201
- this.history.resumeRecording();
1202
- }
1203
1213
  let viewModeEnabled = actionResult?.appState?.viewModeEnabled || false;
1204
1214
  let zenModeEnabled = actionResult?.appState?.zenModeEnabled || false;
1205
1215
  let gridSize = actionResult?.appState?.gridSize || null;
@@ -1237,11 +1247,11 @@ class App extends React.Component {
1237
1247
  name,
1238
1248
  errorMessage,
1239
1249
  });
1240
- }, () => {
1241
- if (actionResult.syncHistory) {
1242
- this.history.setCurrentState(this.state, this.scene.getElementsIncludingDeleted());
1243
- }
1244
1250
  });
1251
+ didUpdate = true;
1252
+ }
1253
+ if (!didUpdate && actionResult.storeAction !== StoreAction.NONE) {
1254
+ this.scene.triggerUpdate();
1245
1255
  }
1246
1256
  });
1247
1257
  // Lifecycle
@@ -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 () => {
@@ -1290,7 +1304,12 @@ class App extends React.Component {
1290
1304
  }
1291
1305
  let initialData = null;
1292
1306
  try {
1293
- initialData = (await this.props.initialData) || null;
1307
+ if (typeof this.props.initialData === "function") {
1308
+ initialData = (await this.props.initialData()) || null;
1309
+ }
1310
+ else {
1311
+ initialData = (await this.props.initialData) || null;
1312
+ }
1294
1313
  if (initialData?.libraryItems) {
1295
1314
  this.library
1296
1315
  .updateLibrary({
@@ -1338,16 +1357,17 @@ class App extends React.Component {
1338
1357
  }),
1339
1358
  };
1340
1359
  }
1341
- // FontFaceSet loadingdone event we listen on may not always fire
1342
- // (looking at you Safari), so on init we manually load fonts for current
1343
- // text elements on canvas, and rerender them once done. This also
1344
- // seems faster even in browsers that do fire the loadingdone event.
1345
- 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
  });
1366
+ // FontFaceSet loadingdone event we listen on may not always
1367
+ // fire (looking at you Safari), so on init we manually load all
1368
+ // fonts and rerender scene text elements once done. This also
1369
+ // seems faster even in browsers that do fire the loadingdone event.
1370
+ this.fonts.load();
1351
1371
  };
1352
1372
  isMobileBreakpoint = (width, height) => {
1353
1373
  return (width < MQ_MAX_WIDTH_PORTRAIT ||
@@ -1417,9 +1437,20 @@ class App extends React.Component {
1417
1437
  configurable: true,
1418
1438
  value: this.history,
1419
1439
  },
1440
+ store: {
1441
+ configurable: true,
1442
+ value: this.store,
1443
+ },
1444
+ fonts: {
1445
+ configurable: true,
1446
+ value: this.fonts,
1447
+ },
1420
1448
  });
1421
1449
  }
1422
- this.scene.addCallback(this.onSceneUpdated);
1450
+ this.store.onStoreIncrementEmitter.on((increment) => {
1451
+ this.history.record(increment.elementsChange, increment.appStateChange);
1452
+ });
1453
+ this.scene.onUpdate(this.triggerRender);
1423
1454
  this.addEventListeners();
1424
1455
  if (this.props.autoFocus && this.excalidrawContainerRef.current) {
1425
1456
  this.focusContainer();
@@ -1455,19 +1486,22 @@ class App extends React.Component {
1455
1486
  }
1456
1487
  }
1457
1488
  componentWillUnmount() {
1489
+ window.launchQueue?.setConsumer(() => { });
1458
1490
  this.renderer.destroy();
1491
+ this.scene.destroy();
1459
1492
  this.scene = new Scene();
1493
+ this.fonts = new Fonts({ scene: this.scene });
1460
1494
  this.renderer = new Renderer(this.scene);
1461
1495
  this.files = {};
1462
1496
  this.imageCache.clear();
1463
1497
  this.resizeObserver?.disconnect();
1464
1498
  this.unmounted = true;
1465
1499
  this.removeEventListeners();
1466
- this.scene.destroy();
1467
1500
  this.library.destroy();
1468
1501
  this.laserTrails.stop();
1469
1502
  this.eraserTrail.stop();
1470
1503
  this.onChangeEmitter.clear();
1504
+ this.store.onStoreIncrementEmitter.clear();
1471
1505
  ShapeCache.destroy();
1472
1506
  SnapCache.destroy();
1473
1507
  clearTimeout(touchTimeout);
@@ -1511,15 +1545,18 @@ class App extends React.Component {
1511
1545
  this.onRemoveEventListenersEmitter.once(addEventListener(document, EVENT.KEYDOWN, this.onKeyDown, false));
1512
1546
  }
1513
1547
  this.onRemoveEventListenersEmitter.once(addEventListener(this.excalidrawContainerRef.current, EVENT.WHEEL, this.onWheel, { passive: false }), addEventListener(window, EVENT.MESSAGE, this.onWindowMessage, false), addEventListener(document, EVENT.POINTER_UP, this.removePointer), // #3553
1514
- addEventListener(document, EVENT.COPY, this.onCopy), addEventListener(document, EVENT.KEYUP, this.onKeyUp, { passive: true }), addEventListener(document, EVENT.MOUSE_MOVE, this.updateCurrentCursorPosition),
1548
+ addEventListener(document, EVENT.COPY, this.onCopy), addEventListener(document, EVENT.KEYUP, this.onKeyUp, { passive: true }), addEventListener(document, EVENT.POINTER_MOVE, this.updateCurrentCursorPosition),
1515
1549
  // rerender text elements on font load to fix #637 && #1553
1516
1550
  addEventListener(document.fonts, "loadingdone", (event) => {
1517
1551
  const loadedFontFaces = event.fontfaces;
1518
- this.fonts.onFontsLoaded(loadedFontFaces);
1552
+ this.fonts.onLoaded(loadedFontFaces);
1519
1553
  }),
1520
1554
  // Safari-only desktop pinch zoom
1521
1555
  addEventListener(document, EVENT.GESTURE_START, this.onGestureStart, false), addEventListener(document, EVENT.GESTURE_CHANGE, this.onGestureChange, false), addEventListener(document, EVENT.GESTURE_END, this.onGestureEnd, false), addEventListener(window, EVENT.FOCUS, () => {
1522
1556
  this.maybeCleanupAfterMissingPointerUp(null);
1557
+ // browsers (chrome?) tend to free up memory a lot, which results
1558
+ // in canvas context being cleared. Thus re-render on focus.
1559
+ this.triggerRender(true);
1523
1560
  }));
1524
1561
  if (this.state.viewModeEnabled) {
1525
1562
  return;
@@ -1535,7 +1572,8 @@ class App extends React.Component {
1535
1572
  componentDidUpdate(prevProps, prevState) {
1536
1573
  this.updateEmbeddables();
1537
1574
  const elements = this.scene.getElementsIncludingDeleted();
1538
- const elementsMap = this.scene.getNonDeletedElementsMap();
1575
+ const elementsMap = this.scene.getElementsMapIncludingDeleted();
1576
+ const nonDeletedElementsMap = this.scene.getNonDeletedElementsMap();
1539
1577
  if (!this.state.showWelcomeScreen && !elements.length) {
1540
1578
  this.setState({ showWelcomeScreen: true });
1541
1579
  }
@@ -1608,10 +1646,10 @@ class App extends React.Component {
1608
1646
  gridSize: this.props.gridModeEnabled ? GRID_SIZE : null,
1609
1647
  });
1610
1648
  }
1611
- this.excalidrawContainerRef.current?.classList.toggle("theme--dark", this.state.theme === "dark");
1649
+ this.excalidrawContainerRef.current?.classList.toggle("theme--dark", this.state.theme === THEME.DARK);
1612
1650
  if (this.state.editingLinearElement &&
1613
1651
  !this.state.selectedElementIds[this.state.editingLinearElement.elementId]) {
1614
- // defer so that the commitToHistory flag isn't reset via current update
1652
+ // defer so that the storeAction flag isn't reset via current update
1615
1653
  setTimeout(() => {
1616
1654
  // execute only if the condition still holds when the deferred callback
1617
1655
  // executes (it can be scheduled multiple times depending on how
@@ -1636,9 +1674,9 @@ class App extends React.Component {
1636
1674
  multiElement != null &&
1637
1675
  isBindingEnabled(this.state) &&
1638
1676
  isBindingElement(multiElement, false)) {
1639
- maybeBindLinearElement(multiElement, this.state, this.scene, tupleToCoors(LinearElementEditor.getPointAtIndexGlobalCoordinates(multiElement, -1, elementsMap)), elementsMap);
1677
+ maybeBindLinearElement(multiElement, this.state, tupleToCoors(LinearElementEditor.getPointAtIndexGlobalCoordinates(multiElement, -1, nonDeletedElementsMap)), this.scene.getNonDeletedElementsMap());
1640
1678
  }
1641
- this.history.record(this.state, elements);
1679
+ this.store.commit(elementsMap, this.state);
1642
1680
  // Do not notify consumers if we're still loading the scene. Among other
1643
1681
  // potential issues, this fixes a case where the tab isn't focused during
1644
1682
  // init, which would trigger onChange with empty elements, which would then
@@ -1825,6 +1863,24 @@ class App extends React.Component {
1825
1863
  });
1826
1864
  }
1827
1865
  else if (data.text) {
1866
+ if (data.text && isMaybeMermaidDefinition(data.text)) {
1867
+ const api = await import("@excalidraw/mermaid-to-excalidraw");
1868
+ try {
1869
+ const { elements: skeletonElements, files } = await api.parseMermaidToExcalidraw(data.text);
1870
+ const elements = convertToExcalidrawElements(skeletonElements, {
1871
+ regenerateIds: true,
1872
+ });
1873
+ this.addElementsFromPasteOrLibrary({
1874
+ elements,
1875
+ files,
1876
+ position: "cursor",
1877
+ });
1878
+ return;
1879
+ }
1880
+ catch (err) {
1881
+ console.warn(`parsing pasted text as mermaid definition failed: ${err.message}`);
1882
+ }
1883
+ }
1828
1884
  const nonEmptyLines = normalizeEOL(data.text)
1829
1885
  .split(/\n+/)
1830
1886
  .map((s) => s.trim())
@@ -1894,16 +1950,15 @@ class App extends React.Component {
1894
1950
  }), {
1895
1951
  randomizeSeed: !opts.retainSeed,
1896
1952
  });
1897
- const allElements = [
1898
- ...this.scene.getElementsIncludingDeleted(),
1899
- ...newElements,
1900
- ];
1953
+ const prevElements = this.scene.getElementsIncludingDeleted();
1954
+ const nextElements = [...prevElements, ...newElements];
1955
+ syncMovedIndices(nextElements, arrayToMap(newElements));
1901
1956
  const topLayerFrame = this.getTopLayerFrameAtSceneCoords({ x, y });
1902
1957
  if (topLayerFrame) {
1903
1958
  const eligibleElements = filterElementsEligibleAsFrameChildren(newElements, topLayerFrame);
1904
- addElementsToFrame(allElements, eligibleElements, topLayerFrame);
1959
+ addElementsToFrame(nextElements, eligibleElements, topLayerFrame);
1905
1960
  }
1906
- this.scene.replaceAllElements(allElements);
1961
+ this.scene.replaceAllElements(nextElements);
1907
1962
  newElements.forEach((newElement) => {
1908
1963
  if (isTextElement(newElement) && isBoundToContainer(newElement)) {
1909
1964
  const container = getContainerElement(newElement, this.scene.getElementsMapIncludingDeleted());
@@ -1913,7 +1968,7 @@ class App extends React.Component {
1913
1968
  if (opts.files) {
1914
1969
  this.files = { ...this.files, ...opts.files };
1915
1970
  }
1916
- this.history.resumeRecording();
1971
+ this.store.shouldCaptureIncrement();
1917
1972
  const nextElementsToSelect = excludeElementsInFramesFromSelection(newElements);
1918
1973
  this.setState({
1919
1974
  ...this.state,
@@ -2030,27 +2085,46 @@ class App extends React.Component {
2030
2085
  text,
2031
2086
  fontSize: this.state.currentItemFontSize,
2032
2087
  fontFamily: this.state.currentItemFontFamily,
2033
- textAlign: this.state.currentItemTextAlign,
2088
+ textAlign: DEFAULT_TEXT_ALIGN,
2034
2089
  verticalAlign: DEFAULT_VERTICAL_ALIGN,
2035
2090
  locked: false,
2036
2091
  };
2092
+ const fontString = getFontString({
2093
+ fontSize: textElementProps.fontSize,
2094
+ fontFamily: textElementProps.fontFamily,
2095
+ });
2096
+ const lineHeight = getLineHeight(textElementProps.fontFamily);
2097
+ const [x1, , x2] = getVisibleSceneBounds(this.state);
2098
+ // long texts should not go beyond 800 pixels in width nor should it go below 200 px
2099
+ const maxTextWidth = Math.max(Math.min((x2 - x1) * 0.5, 800), 200);
2037
2100
  const LINE_GAP = 10;
2038
2101
  let currentY = y;
2039
2102
  const lines = isPlainPaste ? [text] : text.split("\n");
2040
2103
  const textElements = lines.reduce((acc, line, idx) => {
2041
- const text = line.trim();
2042
- const lineHeight = getDefaultLineHeight(textElementProps.fontFamily);
2043
- if (text.length) {
2104
+ const originalText = line.trim();
2105
+ if (originalText.length) {
2044
2106
  const topLayerFrame = this.getTopLayerFrameAtSceneCoords({
2045
2107
  x,
2046
2108
  y: currentY,
2047
2109
  });
2110
+ let metrics = measureText(originalText, fontString, lineHeight);
2111
+ const isTextUnwrapped = metrics.width > maxTextWidth;
2112
+ const text = isTextUnwrapped
2113
+ ? wrapText(originalText, fontString, maxTextWidth)
2114
+ : originalText;
2115
+ metrics = isTextUnwrapped
2116
+ ? measureText(text, fontString, lineHeight)
2117
+ : metrics;
2118
+ const startX = x - metrics.width / 2;
2119
+ const startY = currentY - metrics.height / 2;
2048
2120
  const element = newTextElement({
2049
2121
  ...textElementProps,
2050
- x,
2051
- y: currentY,
2122
+ x: startX,
2123
+ y: startY,
2052
2124
  text,
2125
+ originalText,
2053
2126
  lineHeight,
2127
+ autoResize: !isTextUnwrapped,
2054
2128
  frameId: topLayerFrame ? topLayerFrame.id : null,
2055
2129
  });
2056
2130
  acc.push(element);
@@ -2071,16 +2145,7 @@ class App extends React.Component {
2071
2145
  if (textElements.length === 0) {
2072
2146
  return;
2073
2147
  }
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
- }
2148
+ this.scene.insertElements(textElements);
2084
2149
  this.setState({
2085
2150
  selectedElementIds: makeNextSelectedElementIds(Object.fromEntries(textElements.map((el) => [el.id, true])), this.state),
2086
2151
  });
@@ -2096,7 +2161,7 @@ class App extends React.Component {
2096
2161
  });
2097
2162
  PLAIN_PASTE_TOAST_SHOWN = true;
2098
2163
  }
2099
- this.history.resumeRecording();
2164
+ this.store.shouldCaptureIncrement();
2100
2165
  }
2101
2166
  setAppState = (state, callback) => {
2102
2167
  this.setState(state, callback);
@@ -2278,25 +2343,49 @@ class App extends React.Component {
2278
2343
  ShapeCache.delete(element);
2279
2344
  }
2280
2345
  });
2281
- this.scene.informMutation();
2346
+ this.scene.triggerUpdate();
2282
2347
  this.addNewImagesToImageCache();
2283
2348
  });
2284
2349
  updateScene = withBatchedUpdates((sceneData) => {
2285
- if (sceneData.commitToHistory) {
2286
- this.history.resumeRecording();
2350
+ const nextElements = syncInvalidIndices(sceneData.elements ?? []);
2351
+ if (sceneData.storeAction && sceneData.storeAction !== StoreAction.NONE) {
2352
+ const prevCommittedAppState = this.store.snapshot.appState;
2353
+ const prevCommittedElements = this.store.snapshot.elements;
2354
+ const nextCommittedAppState = sceneData.appState
2355
+ ? Object.assign({}, prevCommittedAppState, sceneData.appState) // new instance, with partial appstate applied to previously captured one, including hidden prop inside `prevCommittedAppState`
2356
+ : prevCommittedAppState;
2357
+ const nextCommittedElements = sceneData.elements
2358
+ ? this.store.filterUncomittedElements(this.scene.getElementsMapIncludingDeleted(), // Only used to detect uncomitted local elements
2359
+ arrayToMap(nextElements))
2360
+ : prevCommittedElements;
2361
+ // 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
2362
+ // do NOT schedule store actions (execute after re-render), as it might cause unexpected concurrency issues if not handled well
2363
+ if (sceneData.storeAction === StoreAction.CAPTURE) {
2364
+ this.store.captureIncrement(nextCommittedElements, nextCommittedAppState);
2365
+ }
2366
+ else if (sceneData.storeAction === StoreAction.UPDATE) {
2367
+ this.store.updateSnapshot(nextCommittedElements, nextCommittedAppState);
2368
+ }
2287
2369
  }
2288
2370
  if (sceneData.appState) {
2289
2371
  this.setState(sceneData.appState);
2290
2372
  }
2291
2373
  if (sceneData.elements) {
2292
- this.scene.replaceAllElements(sceneData.elements);
2374
+ this.scene.replaceAllElements(nextElements);
2293
2375
  }
2294
2376
  if (sceneData.collaborators) {
2295
2377
  this.setState({ collaborators: sceneData.collaborators });
2296
2378
  }
2297
2379
  });
2298
- onSceneUpdated = () => {
2299
- this.setState({});
2380
+ triggerRender = (
2381
+ /** force always re-renders canvas even if no change */
2382
+ force) => {
2383
+ if (force === true) {
2384
+ this.scene.triggerUpdate();
2385
+ }
2386
+ else {
2387
+ this.setState({});
2388
+ }
2300
2389
  };
2301
2390
  /**
2302
2391
  * @returns whether the menu was toggled on or off
@@ -2355,8 +2444,7 @@ class App extends React.Component {
2355
2444
  !event.altKey) {
2356
2445
  this.setToast({
2357
2446
  message: t("commandPalette.shortcutHint", {
2358
- shortcutOne: getShortcutFromShortcutName("commandPalette"),
2359
- shortcutTwo: getShortcutFromShortcutName("commandPalette", 1),
2447
+ shortcut: getShortcutFromShortcutName("commandPalette"),
2360
2448
  }),
2361
2449
  });
2362
2450
  event.preventDefault();
@@ -2464,7 +2552,9 @@ class App extends React.Component {
2464
2552
  simultaneouslyUpdated: selectedElements,
2465
2553
  });
2466
2554
  });
2467
- this.maybeSuggestBindingForAll(selectedElements);
2555
+ this.setState({
2556
+ suggestedBindings: getSuggestedBindingsForArrows(selectedElements, this.scene.getNonDeletedElementsMap()),
2557
+ });
2468
2558
  event.preventDefault();
2469
2559
  }
2470
2560
  else if (event.key === KEYS.ENTER) {
@@ -2476,7 +2566,7 @@ class App extends React.Component {
2476
2566
  if (!this.state.editingLinearElement ||
2477
2567
  this.state.editingLinearElement.elementId !==
2478
2568
  selectedElements[0].id) {
2479
- this.history.resumeRecording();
2569
+ this.store.shouldCaptureIncrement();
2480
2570
  this.setState({
2481
2571
  editingLinearElement: new LinearElementEditor(selectedElement),
2482
2572
  });
@@ -2548,6 +2638,21 @@ class App extends React.Component {
2548
2638
  event.stopPropagation();
2549
2639
  }
2550
2640
  }
2641
+ if (!event[KEYS.CTRL_OR_CMD] &&
2642
+ event.shiftKey &&
2643
+ event.key.toLowerCase() === KEYS.F) {
2644
+ const selectedElements = this.scene.getSelectedElements(this.state);
2645
+ if (this.state.activeTool.type === "selection" &&
2646
+ !selectedElements.length) {
2647
+ return;
2648
+ }
2649
+ if (this.state.activeTool.type === "text" ||
2650
+ selectedElements.find((element) => isTextElement(element) ||
2651
+ getBoundTextElement(element, this.scene.getNonDeletedElementsMap()))) {
2652
+ event.preventDefault();
2653
+ this.setState({ openPopup: "fontFamily" });
2654
+ }
2655
+ }
2551
2656
  if (event.key === KEYS.K && !event.altKey && !event[KEYS.CTRL_OR_CMD]) {
2552
2657
  if (this.state.activeTool.type === "laser") {
2553
2658
  this.setActiveTool({ type: "selection" });
@@ -2602,11 +2707,7 @@ class App extends React.Component {
2602
2707
  this.setState({ isBindingEnabled: true });
2603
2708
  }
2604
2709
  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);
2710
+ bindOrUnbindLinearElements(this.scene.getSelectedElements(this.state).filter(isLinearElement), this.scene.getNonDeletedElementsMap(), isBindingEnabled(this.state), this.state.selectedLinearElement?.selectedPointsIndices ?? []);
2610
2711
  this.setState({ suggestedBindings: [] });
2611
2712
  }
2612
2713
  });
@@ -2644,6 +2745,9 @@ class App extends React.Component {
2644
2745
  originSnapOffset: null,
2645
2746
  activeEmbeddable: null,
2646
2747
  };
2748
+ if (nextActiveTool.type === "freedraw") {
2749
+ this.store.shouldCaptureIncrement();
2750
+ }
2647
2751
  if (nextActiveTool.type !== "selection") {
2648
2752
  return {
2649
2753
  ...prevState,
@@ -2741,15 +2845,16 @@ class App extends React.Component {
2741
2845
  });
2742
2846
  handleTextWysiwyg(element, { isExistingElement = false, }) {
2743
2847
  const elementsMap = this.scene.getElementsMapIncludingDeleted();
2744
- const updateElement = (text, originalText, isDeleted) => {
2848
+ const updateElement = (nextOriginalText, isDeleted) => {
2745
2849
  this.scene.replaceAllElements([
2746
2850
  // Not sure why we include deleted elements as well hence using deleted elements map
2747
2851
  ...this.scene.getElementsIncludingDeleted().map((_element) => {
2748
2852
  if (_element.id === element.id && isTextElement(_element)) {
2749
- return updateTextElement(_element, getContainerElement(_element, elementsMap), elementsMap, {
2750
- text,
2751
- isDeleted,
2752
- originalText,
2853
+ return newElementWith(_element, {
2854
+ originalText: nextOriginalText,
2855
+ isDeleted: isDeleted ?? _element.isDeleted,
2856
+ // returns (wrapped) text and new dimensions
2857
+ ...refreshTextDimensions(_element, getContainerElement(_element, elementsMap), elementsMap, nextOriginalText),
2753
2858
  });
2754
2859
  }
2755
2860
  return _element;
@@ -2769,15 +2874,15 @@ class App extends React.Component {
2769
2874
  viewportY - this.state.offsetTop,
2770
2875
  ];
2771
2876
  },
2772
- onChange: withBatchedUpdates((text) => {
2773
- updateElement(text, text, false);
2877
+ onChange: withBatchedUpdates((nextOriginalText) => {
2878
+ updateElement(nextOriginalText, false);
2774
2879
  if (isNonDeletedElement(element)) {
2775
2880
  updateBoundElements(element, elementsMap);
2776
2881
  }
2777
2882
  }),
2778
- onSubmit: withBatchedUpdates(({ text, viaKeyboard, originalText }) => {
2779
- const isDeleted = !text.trim();
2780
- updateElement(text, originalText, isDeleted);
2883
+ onSubmit: withBatchedUpdates(({ viaKeyboard, nextOriginalText }) => {
2884
+ const isDeleted = !nextOriginalText.trim();
2885
+ updateElement(nextOriginalText, isDeleted);
2781
2886
  // select the created text element only if submitting via keyboard
2782
2887
  // (when submitting via click it should act as signal to deselect)
2783
2888
  if (!isDeleted && viaKeyboard) {
@@ -2797,7 +2902,7 @@ class App extends React.Component {
2797
2902
  ]);
2798
2903
  }
2799
2904
  if (!isDeleted || isExistingElement) {
2800
- this.history.resumeRecording();
2905
+ this.store.shouldCaptureIncrement();
2801
2906
  }
2802
2907
  this.setState({
2803
2908
  draggingElement: null,
@@ -2811,12 +2916,17 @@ class App extends React.Component {
2811
2916
  element,
2812
2917
  excalidrawContainer: this.excalidrawContainerRef.current,
2813
2918
  app: this,
2919
+ // when text is selected, it's hard (at least on iOS) to re-position the
2920
+ // caret (i.e. deselect). There's not much use for always selecting
2921
+ // the text on edit anyway (and users can select-all from contextmenu
2922
+ // if needed)
2923
+ autoSelect: !this.device.isTouchScreen,
2814
2924
  });
2815
2925
  // deselect all other elements when inserting text
2816
2926
  this.deselectElements();
2817
2927
  // do an initial update to re-initialize element position since we were
2818
2928
  // modifying element's x/y for sake of editor (case: syncing to remote)
2819
- updateElement(element.text, element.originalText, false);
2929
+ updateElement(element.originalText, false);
2820
2930
  }
2821
2931
  deselectElements() {
2822
2932
  this.setState({
@@ -2835,6 +2945,21 @@ class App extends React.Component {
2835
2945
  }
2836
2946
  return null;
2837
2947
  }
2948
+ getBoundTextShape(element) {
2949
+ const boundTextElement = getBoundTextElement(element, this.scene.getNonDeletedElementsMap());
2950
+ if (boundTextElement) {
2951
+ if (element.type === "arrow") {
2952
+ return getElementShape({
2953
+ ...boundTextElement,
2954
+ // arrow's bound text accurate position is not stored in the element's property
2955
+ // but rather calculated and returned from the following static method
2956
+ ...LinearElementEditor.getBoundTextElementPosition(element, boundTextElement, this.scene.getNonDeletedElementsMap()),
2957
+ }, this.scene.getNonDeletedElementsMap());
2958
+ }
2959
+ return getElementShape(boundTextElement, this.scene.getNonDeletedElementsMap());
2960
+ }
2961
+ return null;
2962
+ }
2838
2963
  getElementAtPosition(x, y, opts) {
2839
2964
  const allHitElements = this.getElementsAtPosition(x, y, opts?.includeBoundTextElement, opts?.includeLockedElements);
2840
2965
  if (allHitElements.length > 1) {
@@ -2848,9 +2973,20 @@ class App extends React.Component {
2848
2973
  const elementWithHighestZIndex = allHitElements[allHitElements.length - 1];
2849
2974
  // If we're hitting element with highest z-index only on its bounding box
2850
2975
  // 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;
2976
+ return hitElementItself({
2977
+ x,
2978
+ y,
2979
+ element: elementWithHighestZIndex,
2980
+ shape: getElementShape(elementWithHighestZIndex, this.scene.getNonDeletedElementsMap()),
2981
+ // when overlapping, we would like to be more precise
2982
+ // this also avoids the need to update past tests
2983
+ threshold: this.getElementHitThreshold() / 2,
2984
+ frameNameBound: isFrameLikeElement(elementWithHighestZIndex)
2985
+ ? this.frameNameBoundsCache.get(elementWithHighestZIndex)
2986
+ : null,
2987
+ })
2988
+ ? elementWithHighestZIndex
2989
+ : allHitElements[allHitElements.length - 2];
2854
2990
  }
2855
2991
  if (allHitElements.length === 1) {
2856
2992
  return allHitElements[0];
@@ -2858,15 +2994,17 @@ class App extends React.Component {
2858
2994
  return null;
2859
2995
  }
2860
2996
  getElementsAtPosition(x, y, includeBoundTextElement = false, includeLockedElements = false) {
2861
- const elements = includeBoundTextElement && includeLockedElements
2997
+ const iframeLikes = [];
2998
+ const elementsMap = this.scene.getNonDeletedElementsMap();
2999
+ const elements = (includeBoundTextElement && includeLockedElements
2862
3000
  ? this.scene.getNonDeletedElements()
2863
3001
  : this.scene
2864
3002
  .getNonDeletedElements()
2865
3003
  .filter((element) => (includeLockedElements || !element.locked) &&
2866
3004
  (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) => {
3005
+ !(isTextElement(element) && element.containerId))))
3006
+ .filter((el) => this.hitElement(x, y, el))
3007
+ .filter((element) => {
2870
3008
  // hitting a frame's element from outside the frame is not considered a hit
2871
3009
  const containingFrame = getContainingFrame(element, elementsMap);
2872
3010
  return containingFrame &&
@@ -2874,9 +3012,82 @@ class App extends React.Component {
2874
3012
  this.state.frameRendering.clip
2875
3013
  ? isCursorInFrame({ x, y }, containingFrame, elementsMap)
2876
3014
  : true;
3015
+ })
3016
+ .filter((el) => {
3017
+ // The parameter elements comes ordered from lower z-index to higher.
3018
+ // We want to preserve that order on the returned array.
3019
+ // Exception being embeddables which should be on top of everything else in
3020
+ // terms of hit testing.
3021
+ if (isIframeElement(el)) {
3022
+ iframeLikes.push(el);
3023
+ return false;
3024
+ }
3025
+ return true;
3026
+ })
3027
+ .concat(iframeLikes);
3028
+ return elements;
3029
+ }
3030
+ getElementHitThreshold() {
3031
+ return DEFAULT_COLLISION_THRESHOLD / this.state.zoom.value;
3032
+ }
3033
+ hitElement(x, y, element, considerBoundingBox = true) {
3034
+ // if the element is selected, then hit test is done against its bounding box
3035
+ if (considerBoundingBox &&
3036
+ this.state.selectedElementIds[element.id] &&
3037
+ shouldShowBoundingBox([element], this.state)) {
3038
+ const selectionShape = getSelectionBoxShape(element, this.scene.getNonDeletedElementsMap(), this.getElementHitThreshold());
3039
+ return isPointInShape([x, y], selectionShape);
3040
+ }
3041
+ // take bound text element into consideration for hit collision as well
3042
+ const hitBoundTextOfElement = hitElementBoundText(x, y, this.getBoundTextShape(element));
3043
+ if (hitBoundTextOfElement) {
3044
+ return true;
3045
+ }
3046
+ return hitElementItself({
3047
+ x,
3048
+ y,
3049
+ element,
3050
+ shape: getElementShape(element, this.scene.getNonDeletedElementsMap()),
3051
+ threshold: this.getElementHitThreshold(),
3052
+ frameNameBound: isFrameLikeElement(element)
3053
+ ? this.frameNameBoundsCache.get(element)
3054
+ : null,
2877
3055
  });
2878
3056
  }
2879
- startTextEditing = ({ sceneX, sceneY, insertAtParentCenter = true, container, }) => {
3057
+ getTextBindableContainerAtPosition(x, y) {
3058
+ const elements = this.scene.getNonDeletedElements();
3059
+ const selectedElements = this.scene.getSelectedElements(this.state);
3060
+ if (selectedElements.length === 1) {
3061
+ return isTextBindableContainer(selectedElements[0], false)
3062
+ ? selectedElements[0]
3063
+ : null;
3064
+ }
3065
+ let hitElement = null;
3066
+ // We need to do hit testing from front (end of the array) to back (beginning of the array)
3067
+ for (let index = elements.length - 1; index >= 0; --index) {
3068
+ if (elements[index].isDeleted) {
3069
+ continue;
3070
+ }
3071
+ const [x1, y1, x2, y2] = getElementAbsoluteCoords(elements[index], this.scene.getNonDeletedElementsMap());
3072
+ if (isArrowElement(elements[index]) &&
3073
+ hitElementItself({
3074
+ x,
3075
+ y,
3076
+ element: elements[index],
3077
+ shape: getElementShape(elements[index], this.scene.getNonDeletedElementsMap()),
3078
+ threshold: this.getElementHitThreshold(),
3079
+ })) {
3080
+ hitElement = elements[index];
3081
+ break;
3082
+ }
3083
+ else if (x1 < x && x < x2 && y1 < y && y < y2) {
3084
+ hitElement = elements[index];
3085
+ break;
3086
+ }
3087
+ }
3088
+ return isTextBindableContainer(hitElement, false) ? hitElement : null;
3089
+ }
3090
+ startTextEditing = ({ sceneX, sceneY, insertAtParentCenter = true, container, autoEdit = true, }) => {
2880
3091
  let shouldBindToContainer = false;
2881
3092
  let parentCenterPosition = insertAtParentCenter &&
2882
3093
  this.getTextWysiwygSnappedToCenterPosition(sceneX, sceneY, this.state, container);
@@ -2903,7 +3114,7 @@ class App extends React.Component {
2903
3114
  existingTextElement = this.getTextElementAtPosition(sceneX, sceneY);
2904
3115
  }
2905
3116
  const fontFamily = existingTextElement?.fontFamily || this.state.currentItemFontFamily;
2906
- const lineHeight = existingTextElement?.lineHeight || getDefaultLineHeight(fontFamily);
3117
+ const lineHeight = existingTextElement?.lineHeight || getLineHeight(fontFamily);
2907
3118
  const fontSize = this.state.currentItemFontSize;
2908
3119
  if (!existingTextElement &&
2909
3120
  shouldBindToContainer &&
@@ -2974,15 +3185,20 @@ class App extends React.Component {
2974
3185
  this.scene.insertElementAtIndex(element, containerIndex + 1);
2975
3186
  }
2976
3187
  else {
2977
- this.scene.addNewElement(element);
3188
+ this.scene.insertElement(element);
2978
3189
  }
2979
3190
  }
2980
- this.setState({
2981
- editingElement: element,
2982
- });
2983
- this.handleTextWysiwyg(element, {
2984
- isExistingElement: !!existingTextElement,
2985
- });
3191
+ if (autoEdit || existingTextElement || container) {
3192
+ this.handleTextWysiwyg(element, {
3193
+ isExistingElement: !!existingTextElement,
3194
+ });
3195
+ }
3196
+ else {
3197
+ this.setState({
3198
+ draggingElement: element,
3199
+ multiElement: null,
3200
+ });
3201
+ }
2986
3202
  };
2987
3203
  handleCanvasDoubleClick = (event) => {
2988
3204
  // case: double-clicking with arrow/line tool selected would both create
@@ -2999,7 +3215,7 @@ class App extends React.Component {
2999
3215
  if (event[KEYS.CTRL_OR_CMD] &&
3000
3216
  (!this.state.editingLinearElement ||
3001
3217
  this.state.editingLinearElement.elementId !== selectedElements[0].id)) {
3002
- this.history.resumeRecording();
3218
+ this.store.shouldCaptureIncrement();
3003
3219
  this.setState({
3004
3220
  editingLinearElement: new LinearElementEditor(selectedElements[0]),
3005
3221
  });
@@ -3014,6 +3230,7 @@ class App extends React.Component {
3014
3230
  const selectedGroupId = hitElement &&
3015
3231
  getSelectedGroupIdForElement(hitElement, this.state.selectedGroupIds);
3016
3232
  if (selectedGroupId) {
3233
+ this.store.shouldCaptureIncrement();
3017
3234
  this.setState((prevState) => ({
3018
3235
  ...prevState,
3019
3236
  ...selectGroupsForSelectedElements({
@@ -3033,11 +3250,17 @@ class App extends React.Component {
3033
3250
  });
3034
3251
  return;
3035
3252
  }
3036
- const container = getTextBindableContainerAtPosition(this.scene.getNonDeletedElements(), this.state, sceneX, sceneY, this.scene.getNonDeletedElementsMap());
3253
+ const container = this.getTextBindableContainerAtPosition(sceneX, sceneY);
3037
3254
  if (container) {
3038
3255
  if (hasBoundTextElement(container) ||
3039
3256
  !isTransparent(container.backgroundColor) ||
3040
- isHittingElementNotConsideringBoundingBox(container, this.state, this.frameNameBoundsCache, [sceneX, sceneY], this.scene.getNonDeletedElementsMap())) {
3257
+ hitElementItself({
3258
+ x: sceneX,
3259
+ y: sceneY,
3260
+ element: container,
3261
+ shape: getElementShape(container, this.scene.getNonDeletedElementsMap()),
3262
+ threshold: this.getElementHitThreshold(),
3263
+ })) {
3041
3264
  const midPoint = getContainerCenter(container, this.state, this.scene.getNonDeletedElementsMap());
3042
3265
  sceneX = midPoint.x;
3043
3266
  sceneY = midPoint.y;
@@ -3092,7 +3315,7 @@ class App extends React.Component {
3092
3315
  }
3093
3316
  if (!customEvent?.defaultPrevented) {
3094
3317
  const target = isLocalLink(url) ? "_self" : "_blank";
3095
- const newWindow = window.open(undefined, target, "noopener noreferrer");
3318
+ const newWindow = window.open(undefined, target);
3096
3319
  // https://mathiasbynens.github.io/rel-noopener/
3097
3320
  if (newWindow) {
3098
3321
  newWindow.opener = null;
@@ -3142,8 +3365,11 @@ class App extends React.Component {
3142
3365
  }, state);
3143
3366
  this.translateCanvas({
3144
3367
  zoom: zoomState.zoom,
3145
- scrollX: zoomState.scrollX + deltaX / nextZoom,
3146
- scrollY: zoomState.scrollY + deltaY / nextZoom,
3368
+ // 2x multiplier is just a magic number that makes this work correctly
3369
+ // on touchscreen devices (note: if we get report that panning is slower/faster
3370
+ // than actual movement, consider swapping with devicePixelRatio)
3371
+ scrollX: zoomState.scrollX + 2 * (deltaX / nextZoom),
3372
+ scrollY: zoomState.scrollY + 2 * (deltaY / nextZoom),
3147
3373
  shouldCacheIgnoreZoom: true,
3148
3374
  });
3149
3375
  });
@@ -3308,15 +3534,23 @@ class App extends React.Component {
3308
3534
  if (selectedElements.length === 1 &&
3309
3535
  !isOverScrollBar &&
3310
3536
  !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;
3537
+ // for linear elements, we'd like to prioritize point dragging over edge resizing
3538
+ // therefore, we update and check hovered point index first
3539
+ if (this.state.selectedLinearElement) {
3540
+ this.handleHoverSelectedLinearElement(this.state.selectedLinearElement, scenePointerX, scenePointerY);
3541
+ }
3542
+ if (!this.state.selectedLinearElement ||
3543
+ this.state.selectedLinearElement.hoverPointIndex === -1) {
3544
+ const elementWithTransformHandleType = getElementWithTransformHandleType(elements, this.state, scenePointerX, scenePointerY, this.state.zoom, event.pointerType, this.scene.getNonDeletedElementsMap(), this.device);
3545
+ if (elementWithTransformHandleType &&
3546
+ elementWithTransformHandleType.transformHandleType) {
3547
+ setCursor(this.interactiveCanvas, getCursorForResizingElement(elementWithTransformHandleType));
3548
+ return;
3549
+ }
3316
3550
  }
3317
3551
  }
3318
3552
  else if (selectedElements.length > 1 && !isOverScrollBar) {
3319
- const transformHandleType = getTransformHandleTypeFromCoords(getCommonBounds(selectedElements), scenePointerX, scenePointerY, this.state.zoom, event.pointerType);
3553
+ const transformHandleType = getTransformHandleTypeFromCoords(getCommonBounds(selectedElements), scenePointerX, scenePointerY, this.state.zoom, event.pointerType, this.device);
3320
3554
  if (transformHandleType) {
3321
3555
  setCursor(this.interactiveCanvas, getCursorForResizingElement({
3322
3556
  transformHandleType,
@@ -3420,7 +3654,7 @@ class App extends React.Component {
3420
3654
  }
3421
3655
  };
3422
3656
  const distance = distance2d(pointerDownState.lastCoords.x, pointerDownState.lastCoords.y, scenePointer.x, scenePointer.y);
3423
- const threshold = 10 / this.state.zoom.value;
3657
+ const threshold = this.getElementHitThreshold();
3424
3658
  const point = { ...pointerDownState.lastCoords };
3425
3659
  let samplingInterval = 0;
3426
3660
  while (samplingInterval <= distance) {
@@ -3456,7 +3690,7 @@ class App extends React.Component {
3456
3690
  }
3457
3691
  }
3458
3692
  this.elementsPendingErasure = new Set(this.elementsPendingErasure);
3459
- this.onSceneUpdated();
3693
+ this.triggerRender();
3460
3694
  }
3461
3695
  };
3462
3696
  // set touch moving for mobile context menu
@@ -3466,30 +3700,29 @@ class App extends React.Component {
3466
3700
  handleHoverSelectedLinearElement(linearElementEditor, scenePointerX, scenePointerY) {
3467
3701
  const elementsMap = this.scene.getNonDeletedElementsMap();
3468
3702
  const element = LinearElementEditor.getElement(linearElementEditor.elementId, elementsMap);
3469
- const boundTextElement = getBoundTextElement(element, elementsMap);
3470
3703
  if (!element) {
3471
3704
  return;
3472
3705
  }
3473
3706
  if (this.state.selectedLinearElement) {
3474
3707
  let hoverPointIndex = -1;
3475
3708
  let segmentMidPointHoveredCoords = null;
3476
- if (isHittingElementNotConsideringBoundingBox(element, this.state, this.frameNameBoundsCache, [scenePointerX, scenePointerY], elementsMap)) {
3709
+ if (hitElementItself({
3710
+ x: scenePointerX,
3711
+ y: scenePointerY,
3712
+ element,
3713
+ shape: getElementShape(element, this.scene.getNonDeletedElementsMap()),
3714
+ })) {
3477
3715
  hoverPointIndex = LinearElementEditor.getPointIndexUnderCursor(element, elementsMap, this.state.zoom, scenePointerX, scenePointerY);
3478
3716
  segmentMidPointHoveredCoords =
3479
3717
  LinearElementEditor.getSegmentMidpointHitCoords(linearElementEditor, { x: scenePointerX, y: scenePointerY }, this.state, this.scene.getNonDeletedElementsMap());
3480
3718
  if (hoverPointIndex >= 0 || segmentMidPointHoveredCoords) {
3481
3719
  setCursor(this.interactiveCanvas, CURSOR_TYPE.POINTER);
3482
3720
  }
3483
- else {
3721
+ else if (this.hitElement(scenePointerX, scenePointerY, element)) {
3484
3722
  setCursor(this.interactiveCanvas, CURSOR_TYPE.MOVE);
3485
3723
  }
3486
3724
  }
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())) {
3725
+ else if (this.hitElement(scenePointerX, scenePointerY, element)) {
3493
3726
  setCursor(this.interactiveCanvas, CURSOR_TYPE.MOVE);
3494
3727
  }
3495
3728
  if (this.state.selectedLinearElement.hoverPointIndex !== hoverPointIndex) {
@@ -3556,6 +3789,7 @@ class App extends React.Component {
3556
3789
  return obj;
3557
3790
  }, {}), this.state),
3558
3791
  },
3792
+ storeAction: StoreAction.UPDATE,
3559
3793
  });
3560
3794
  return;
3561
3795
  }
@@ -3667,7 +3901,6 @@ class App extends React.Component {
3667
3901
  }
3668
3902
  if (this.state.activeTool.type === "text") {
3669
3903
  this.handleTextOnPointerDown(event, pointerDownState);
3670
- return;
3671
3904
  }
3672
3905
  else if (this.state.activeTool.type === "arrow" ||
3673
3906
  this.state.activeTool.type === "line") {
@@ -3995,8 +4228,11 @@ class App extends React.Component {
3995
4228
  const elements = this.scene.getNonDeletedElements();
3996
4229
  const elementsMap = this.scene.getNonDeletedElementsMap();
3997
4230
  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());
4231
+ if (selectedElements.length === 1 &&
4232
+ !this.state.editingLinearElement &&
4233
+ !(this.state.selectedLinearElement &&
4234
+ this.state.selectedLinearElement.hoverPointIndex !== -1)) {
4235
+ const elementWithTransformHandleType = getElementWithTransformHandleType(elements, this.state, pointerDownState.origin.x, pointerDownState.origin.y, this.state.zoom, event.pointerType, this.scene.getNonDeletedElementsMap(), this.device);
4000
4236
  if (elementWithTransformHandleType != null) {
4001
4237
  this.setState({
4002
4238
  resizingElement: elementWithTransformHandleType.element,
@@ -4006,7 +4242,7 @@ class App extends React.Component {
4006
4242
  }
4007
4243
  }
4008
4244
  else if (selectedElements.length > 1) {
4009
- pointerDownState.resize.handleType = getTransformHandleTypeFromCoords(getCommonBounds(selectedElements), pointerDownState.origin.x, pointerDownState.origin.y, this.state.zoom, event.pointerType);
4245
+ pointerDownState.resize.handleType = getTransformHandleTypeFromCoords(getCommonBounds(selectedElements), pointerDownState.origin.x, pointerDownState.origin.y, this.state.zoom, event.pointerType, this.device);
4010
4246
  }
4011
4247
  if (pointerDownState.resize.handleType) {
4012
4248
  pointerDownState.resize.isResizing = true;
@@ -4020,7 +4256,7 @@ class App extends React.Component {
4020
4256
  else {
4021
4257
  if (this.state.selectedLinearElement) {
4022
4258
  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);
4259
+ const ret = LinearElementEditor.handlePointerDown(event, this.state, this.store, pointerDownState.origin, linearElementEditor, this);
4024
4260
  if (ret.hitElement) {
4025
4261
  pointerDownState.hit.element = ret.hitElement;
4026
4262
  }
@@ -4181,7 +4417,7 @@ class App extends React.Component {
4181
4417
  return false;
4182
4418
  }
4183
4419
  // How many pixels off the shape boundary we still consider a hit
4184
- const threshold = 10 / this.state.zoom.value;
4420
+ const threshold = this.getElementHitThreshold();
4185
4421
  const [x1, y1, x2, y2] = getCommonBounds(selectedElements);
4186
4422
  return (point.x > x1 - threshold &&
4187
4423
  point.x < x2 + threshold &&
@@ -4201,7 +4437,7 @@ class App extends React.Component {
4201
4437
  includeBoundTextElement: true,
4202
4438
  });
4203
4439
  // FIXME
4204
- let container = getTextBindableContainerAtPosition(this.scene.getNonDeletedElements(), this.state, sceneX, sceneY, this.scene.getNonDeletedElementsMap());
4440
+ let container = this.getTextBindableContainerAtPosition(sceneX, sceneY);
4205
4441
  if (hasBoundTextElement(element)) {
4206
4442
  container = element;
4207
4443
  sceneX = element.x + element.width / 2;
@@ -4212,6 +4448,7 @@ class App extends React.Component {
4212
4448
  sceneY,
4213
4449
  insertAtParentCenter: !event.altKey,
4214
4450
  container,
4451
+ autoEdit: false,
4215
4452
  });
4216
4453
  resetCursor(this.interactiveCanvas);
4217
4454
  if (!this.state.activeTool.locked) {
@@ -4259,8 +4496,8 @@ class App extends React.Component {
4259
4496
  points: [[0, 0]],
4260
4497
  pressures,
4261
4498
  });
4262
- const boundElement = getHoveredElementForBinding(pointerDownState.origin, this.scene.getNonDeletedElements(), this.scene.getNonDeletedElementsMap());
4263
- this.scene.addNewElement(element);
4499
+ const boundElement = getHoveredElementForBinding(pointerDownState.origin, this.scene.getNonDeletedElementsMap());
4500
+ this.scene.insertElement(element);
4264
4501
  this.setState({
4265
4502
  draggingElement: element,
4266
4503
  editingElement: element,
@@ -4288,10 +4525,7 @@ class App extends React.Component {
4288
4525
  width,
4289
4526
  height,
4290
4527
  });
4291
- this.scene.replaceAllElements([
4292
- ...this.scene.getElementsIncludingDeleted(),
4293
- element,
4294
- ]);
4528
+ this.scene.insertElement(element);
4295
4529
  return element;
4296
4530
  };
4297
4531
  //create rectangle element with youtube top left on nearest grid point width / hight 640/360
@@ -4326,10 +4560,7 @@ class App extends React.Component {
4326
4560
  height: embedLink.intrinsicSize.h,
4327
4561
  link,
4328
4562
  });
4329
- this.scene.replaceAllElements([
4330
- ...this.scene.getElementsIncludingDeleted(),
4331
- element,
4332
- ]);
4563
+ this.scene.insertElement(element);
4333
4564
  return element;
4334
4565
  };
4335
4566
  createImageElement = ({ sceneX, sceneY, addToFrameUnderCursor = true, }) => {
@@ -4437,8 +4668,8 @@ class App extends React.Component {
4437
4668
  mutateElement(element, {
4438
4669
  points: [...element.points, [0, 0]],
4439
4670
  });
4440
- const boundElement = getHoveredElementForBinding(pointerDownState.origin, this.scene.getNonDeletedElements(), this.scene.getNonDeletedElementsMap());
4441
- this.scene.addNewElement(element);
4671
+ const boundElement = getHoveredElementForBinding(pointerDownState.origin, this.scene.getNonDeletedElementsMap());
4672
+ this.scene.insertElement(element);
4442
4673
  this.setState({
4443
4674
  draggingElement: element,
4444
4675
  editingElement: element,
@@ -4498,7 +4729,7 @@ class App extends React.Component {
4498
4729
  });
4499
4730
  }
4500
4731
  else {
4501
- this.scene.addNewElement(element);
4732
+ this.scene.insertElement(element);
4502
4733
  this.setState({
4503
4734
  multiElement: null,
4504
4735
  draggingElement: element,
@@ -4520,10 +4751,7 @@ class App extends React.Component {
4520
4751
  const frame = type === TOOL_TYPE.magicframe
4521
4752
  ? newMagicFrameElement(constructorOpts)
4522
4753
  : newFrameElement(constructorOpts);
4523
- this.scene.replaceAllElements([
4524
- ...this.scene.getElementsIncludingDeleted(),
4525
- frame,
4526
- ]);
4754
+ this.scene.insertElement(frame);
4527
4755
  this.setState({
4528
4756
  multiElement: null,
4529
4757
  draggingElement: frame,
@@ -4737,7 +4965,9 @@ class App extends React.Component {
4737
4965
  // able to select and interact with the text input
4738
4966
  !this.state.editingFrame &&
4739
4967
  dragSelectedElements(pointerDownState, selectedElements, dragOffset, this.state, this.scene, snapOffset, event[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize);
4740
- this.maybeSuggestBindingForAll(selectedElements);
4968
+ this.setState({
4969
+ suggestedBindings: getSuggestedBindingsForArrows(selectedElements, this.scene.getNonDeletedElementsMap()),
4970
+ });
4741
4971
  // We duplicate the selected element if alt is pressed on pointer move
4742
4972
  if (event.altKey && !pointerDownState.hit.hasBeenDuplicated) {
4743
4973
  // Move the currently selected elements to the top of the z index stack, and
@@ -4781,6 +5011,7 @@ class App extends React.Component {
4781
5011
  }
4782
5012
  }
4783
5013
  const nextSceneElements = [...nextElements, ...elementsToAppend];
5014
+ syncMovedIndices(nextSceneElements, arrayToMap(elementsToAppend));
4784
5015
  bindTextToShapeAfterDuplication(nextElements, elementsToAppend, oldIdToDuplicatedId);
4785
5016
  fixBindingsAfterDuplication(nextSceneElements, elementsToAppend, oldIdToDuplicatedId, "duplicatesServeAsOld");
4786
5017
  bindElementsToFramesAfterDuplication(nextSceneElements, elementsToAppend, oldIdToDuplicatedId);
@@ -4972,7 +5203,7 @@ class App extends React.Component {
4972
5203
  this.actionManager.executeAction(actionFinalize);
4973
5204
  }
4974
5205
  else {
4975
- const editingLinearElement = LinearElementEditor.handlePointerUp(childEvent, this.state.editingLinearElement, this.state, this.scene.getNonDeletedElements(), elementsMap);
5206
+ const editingLinearElement = LinearElementEditor.handlePointerUp(childEvent, this.state.editingLinearElement, this.state, this);
4976
5207
  if (editingLinearElement !== this.state.editingLinearElement) {
4977
5208
  this.setState({
4978
5209
  editingLinearElement,
@@ -4991,7 +5222,7 @@ class App extends React.Component {
4991
5222
  }
4992
5223
  }
4993
5224
  else {
4994
- const linearElementEditor = LinearElementEditor.handlePointerUp(childEvent, this.state.selectedLinearElement, this.state, this.scene.getNonDeletedElements(), elementsMap);
5225
+ const linearElementEditor = LinearElementEditor.handlePointerUp(childEvent, this.state.selectedLinearElement, this.state, this);
4995
5226
  const { startBindingElement, endBindingElement } = linearElementEditor;
4996
5227
  const element = this.scene.getElement(linearElementEditor.elementId);
4997
5228
  if (isBindingElement(element)) {
@@ -5060,7 +5291,7 @@ class App extends React.Component {
5060
5291
  }
5061
5292
  if (isLinearElement(draggingElement)) {
5062
5293
  if (draggingElement.points.length > 1) {
5063
- this.history.resumeRecording();
5294
+ this.store.shouldCaptureIncrement();
5064
5295
  }
5065
5296
  const pointerCoords = viewportCoordsToSceneCoords(childEvent, this.state);
5066
5297
  if (!pointerDownState.drag.hasOccurred &&
@@ -5083,7 +5314,7 @@ class App extends React.Component {
5083
5314
  else if (pointerDownState.drag.hasOccurred && !multiElement) {
5084
5315
  if (isBindingEnabled(this.state) &&
5085
5316
  isBindingElement(draggingElement, false)) {
5086
- maybeBindLinearElement(draggingElement, this.state, this.scene, pointerCoords, elementsMap);
5317
+ maybeBindLinearElement(draggingElement, this.state, pointerCoords, this.scene.getNonDeletedElementsMap());
5087
5318
  }
5088
5319
  this.setState({ suggestedBindings: [], startBoundElement: null });
5089
5320
  if (!activeTool.locked) {
@@ -5108,15 +5339,34 @@ class App extends React.Component {
5108
5339
  }
5109
5340
  return;
5110
5341
  }
5342
+ if (isTextElement(draggingElement)) {
5343
+ const minWidth = getMinTextElementWidth(getFontString({
5344
+ fontSize: draggingElement.fontSize,
5345
+ fontFamily: draggingElement.fontFamily,
5346
+ }), draggingElement.lineHeight);
5347
+ if (draggingElement.width < minWidth) {
5348
+ mutateElement(draggingElement, {
5349
+ autoResize: true,
5350
+ });
5351
+ }
5352
+ this.resetCursor();
5353
+ this.handleTextWysiwyg(draggingElement, {
5354
+ isExistingElement: true,
5355
+ });
5356
+ }
5111
5357
  if (activeTool.type !== "selection" &&
5112
5358
  draggingElement &&
5113
5359
  isInvisiblySmallElement(draggingElement)) {
5114
5360
  // 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,
5361
+ // update the store snapshot, so that invisible elements are not captured by the store
5362
+ this.updateScene({
5363
+ elements: this.scene
5364
+ .getElementsIncludingDeleted()
5365
+ .filter((el) => el.id !== draggingElement.id),
5366
+ appState: {
5367
+ draggingElement: null,
5368
+ },
5369
+ storeAction: StoreAction.UPDATE,
5120
5370
  });
5121
5371
  return;
5122
5372
  }
@@ -5139,7 +5389,7 @@ class App extends React.Component {
5139
5389
  groupIds: [],
5140
5390
  });
5141
5391
  removeElementsFromFrame([linearElement], this.scene.getNonDeletedElementsMap());
5142
- this.scene.informMutation();
5392
+ this.scene.triggerUpdate();
5143
5393
  }
5144
5394
  }
5145
5395
  }
@@ -5197,12 +5447,16 @@ class App extends React.Component {
5197
5447
  mutateElement(draggingElement, getNormalizedDimensions(draggingElement));
5198
5448
  }
5199
5449
  if (resizingElement) {
5200
- this.history.resumeRecording();
5450
+ this.store.shouldCaptureIncrement();
5201
5451
  }
5202
5452
  if (resizingElement && isInvisiblySmallElement(resizingElement)) {
5203
- this.scene.replaceAllElements(this.scene
5204
- .getElementsIncludingDeleted()
5205
- .filter((el) => el.id !== resizingElement.id));
5453
+ // update the store snapshot, so that invisible elements are not captured by the store
5454
+ this.updateScene({
5455
+ elements: this.scene
5456
+ .getElementsIncludingDeleted()
5457
+ .filter((el) => el.id !== resizingElement.id),
5458
+ storeAction: StoreAction.UPDATE,
5459
+ });
5206
5460
  }
5207
5461
  // handle frame membership for resizing frames and/or selected elements
5208
5462
  if (pointerDownState.resize.isResizing) {
@@ -5357,10 +5611,23 @@ class App extends React.Component {
5357
5611
  }));
5358
5612
  }
5359
5613
  }
5360
- if (!pointerDownState.drag.hasOccurred &&
5614
+ if (
5615
+ // not dragged
5616
+ !pointerDownState.drag.hasOccurred &&
5617
+ // not resized
5361
5618
  !this.state.isResizing &&
5619
+ // only hitting the bounding box of the previous hit element
5362
5620
  ((hitElement &&
5363
- isHittingElementBoundingBoxWithoutHittingElement(hitElement, this.state, this.frameNameBoundsCache, pointerDownState.origin.x, pointerDownState.origin.y, this.scene.getNonDeletedElementsMap())) ||
5621
+ hitElementBoundingBoxOnly({
5622
+ x: pointerDownState.origin.x,
5623
+ y: pointerDownState.origin.y,
5624
+ element: hitElement,
5625
+ shape: getElementShape(hitElement, this.scene.getNonDeletedElementsMap()),
5626
+ threshold: this.getElementHitThreshold(),
5627
+ frameNameBound: isFrameLikeElement(hitElement)
5628
+ ? this.frameNameBoundsCache.get(hitElement)
5629
+ : null,
5630
+ }, elementsMap)) ||
5364
5631
  (!hitElement &&
5365
5632
  pointerDownState.hit.hasHitCommonBoundingBoxOfSelectedElements))) {
5366
5633
  if (this.state.editingLinearElement) {
@@ -5375,6 +5642,8 @@ class App extends React.Component {
5375
5642
  activeEmbeddable: null,
5376
5643
  });
5377
5644
  }
5645
+ // reset cursor
5646
+ setCursor(this.interactiveCanvas, CURSOR_TYPE.AUTO);
5378
5647
  return;
5379
5648
  }
5380
5649
  if (!activeTool.locked &&
@@ -5392,13 +5661,17 @@ class App extends React.Component {
5392
5661
  }));
5393
5662
  }
5394
5663
  if (activeTool.type !== "selection" ||
5395
- isSomeElementSelected(this.scene.getNonDeletedElements(), this.state)) {
5396
- this.history.resumeRecording();
5664
+ isSomeElementSelected(this.scene.getNonDeletedElements(), this.state) ||
5665
+ !isShallowEqual(this.state.previousSelectedElementIds, this.state.selectedElementIds)) {
5666
+ this.store.shouldCaptureIncrement();
5397
5667
  }
5398
5668
  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);
5669
+ // We only allow binding via linear elements, specifically via dragging
5670
+ // the endpoints ("start" or "end").
5671
+ const linearElements = this.scene
5672
+ .getSelectedElements(this.state)
5673
+ .filter(isLinearElement);
5674
+ bindOrUnbindLinearElements(linearElements, this.scene.getNonDeletedElementsMap(), isBindingEnabled(this.state), this.state.selectedLinearElement?.selectedPointsIndices ?? []);
5402
5675
  }
5403
5676
  if (activeTool.type === "laser") {
5404
5677
  this.laserTrails.endPath();
@@ -5433,7 +5706,7 @@ class App extends React.Component {
5433
5706
  }
5434
5707
  restoreReadyToEraseElements = () => {
5435
5708
  this.elementsPendingErasure = new Set();
5436
- this.onSceneUpdated();
5709
+ this.triggerRender();
5437
5710
  };
5438
5711
  eraseElements = () => {
5439
5712
  let didChange = false;
@@ -5449,7 +5722,7 @@ class App extends React.Component {
5449
5722
  });
5450
5723
  this.elementsPendingErasure = new Set();
5451
5724
  if (didChange) {
5452
- this.history.resumeRecording();
5725
+ this.store.shouldCaptureIncrement();
5453
5726
  this.scene.replaceAllElements(elements);
5454
5727
  }
5455
5728
  };
@@ -5552,7 +5825,7 @@ class App extends React.Component {
5552
5825
  this.setState({ errorMessage: t("errors.imageToolNotSupported") });
5553
5826
  return;
5554
5827
  }
5555
- this.scene.addNewElement(imageElement);
5828
+ this.scene.insertElement(imageElement);
5556
5829
  try {
5557
5830
  return await this.initializeImage({
5558
5831
  imageFile,
@@ -5725,7 +5998,7 @@ class App extends React.Component {
5725
5998
  if (uncachedImageElements.length) {
5726
5999
  const { updatedFiles } = await this.updateImageCache(uncachedImageElements, files);
5727
6000
  if (updatedFiles.size) {
5728
- this.scene.informMutation();
6001
+ this.scene.triggerUpdate();
5729
6002
  }
5730
6003
  }
5731
6004
  };
@@ -5741,7 +6014,7 @@ class App extends React.Component {
5741
6014
  }
5742
6015
  };
5743
6016
  maybeSuggestBindingAtCursor = (pointerCoords) => {
5744
- const hoveredBindableElement = getHoveredElementForBinding(pointerCoords, this.scene.getNonDeletedElements(), this.scene.getNonDeletedElementsMap());
6017
+ const hoveredBindableElement = getHoveredElementForBinding(pointerCoords, this.scene.getNonDeletedElementsMap());
5745
6018
  this.setState({
5746
6019
  suggestedBindings: hoveredBindableElement != null ? [hoveredBindableElement] : [],
5747
6020
  });
@@ -5756,7 +6029,7 @@ class App extends React.Component {
5756
6029
  return;
5757
6030
  }
5758
6031
  const suggestedBindings = pointerCoords.reduce((acc, coords) => {
5759
- const hoveredBindableElement = getHoveredElementForBinding(coords, this.scene.getNonDeletedElements(), this.scene.getNonDeletedElementsMap());
6032
+ const hoveredBindableElement = getHoveredElementForBinding(coords, this.scene.getNonDeletedElementsMap());
5760
6033
  if (hoveredBindableElement != null &&
5761
6034
  !isLinearElementSimpleAndAlreadyBound(linearElement, oppositeBindingBoundElement?.id, hoveredBindableElement)) {
5762
6035
  acc.push(hoveredBindableElement);
@@ -5765,13 +6038,6 @@ class App extends React.Component {
5765
6038
  }, []);
5766
6039
  this.setState({ suggestedBindings });
5767
6040
  };
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
6041
  clearSelection(hitElement) {
5776
6042
  this.setState((prevState) => ({
5777
6043
  selectedElementIds: makeNextSelectedElementIds({}, prevState),
@@ -5831,7 +6097,7 @@ class App extends React.Component {
5831
6097
  isLoading: false,
5832
6098
  },
5833
6099
  replaceFiles: true,
5834
- commitToHistory: true,
6100
+ storeAction: StoreAction.CAPTURE,
5835
6101
  });
5836
6102
  return;
5837
6103
  }
@@ -5899,9 +6165,10 @@ class App extends React.Component {
5899
6165
  loadFileToCanvas = async (file, fileHandle) => {
5900
6166
  file = await normalizeFile(file);
5901
6167
  try {
6168
+ const elements = this.scene.getElementsIncludingDeleted();
5902
6169
  let ret;
5903
6170
  try {
5904
- ret = await loadSceneOrLibraryFromBlob(file, this.state, this.scene.getElementsIncludingDeleted(), fileHandle);
6171
+ ret = await loadSceneOrLibraryFromBlob(file, this.state, elements, fileHandle);
5905
6172
  }
5906
6173
  catch (error) {
5907
6174
  const imageSceneDataError = error instanceof ImageSceneDataError;
@@ -5926,6 +6193,10 @@ class App extends React.Component {
5926
6193
  return;
5927
6194
  }
5928
6195
  if (ret.type === MIME_TYPES.excalidraw) {
6196
+ // restore the fractional indices by mutating elements
6197
+ syncInvalidIndices(elements.concat(ret.data.elements));
6198
+ // update the store snapshot for old elements, otherwise we would end up with duplicated fractional indices on undo
6199
+ this.store.updateSnapshot(arrayToMap(elements), this.state);
5929
6200
  this.setState({ isLoading: true });
5930
6201
  this.syncActionResult({
5931
6202
  ...ret.data,
@@ -5934,7 +6205,7 @@ class App extends React.Component {
5934
6205
  isLoading: false,
5935
6206
  },
5936
6207
  replaceFiles: true,
5937
- commitToHistory: true,
6208
+ storeAction: StoreAction.CAPTURE,
5938
6209
  });
5939
6210
  }
5940
6211
  else if (ret.type === MIME_TYPES.excalidrawlib) {
@@ -6006,7 +6277,7 @@ class App extends React.Component {
6006
6277
  }
6007
6278
  if (draggingElement.type === "selection" &&
6008
6279
  this.state.activeTool.type !== "eraser") {
6009
- dragNewElement(draggingElement, this.state.activeTool.type, pointerDownState.origin.x, pointerDownState.origin.y, pointerCoords.x, pointerCoords.y, distance(pointerDownState.origin.x, pointerCoords.x), distance(pointerDownState.origin.y, pointerCoords.y), shouldMaintainAspectRatio(event), shouldResizeFromCenter(event));
6280
+ dragNewElement(draggingElement, this.state.activeTool.type, pointerDownState.origin.x, pointerDownState.origin.y, pointerCoords.x, pointerCoords.y, distance(pointerDownState.origin.x, pointerCoords.x), distance(pointerDownState.origin.y, pointerCoords.y), shouldMaintainAspectRatio(event), shouldResizeFromCenter(event), this.state.zoom.value);
6010
6281
  }
6011
6282
  else {
6012
6283
  let [gridX, gridY] = getGridPoint(pointerCoords.x, pointerCoords.y, event[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize);
@@ -6032,8 +6303,7 @@ class App extends React.Component {
6032
6303
  });
6033
6304
  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
6305
  ? !shouldMaintainAspectRatio(event)
6035
- : shouldMaintainAspectRatio(event), shouldResizeFromCenter(event), aspectRatio, this.state.originSnapOffset);
6036
- this.maybeSuggestBindingForAll([draggingElement]);
6306
+ : shouldMaintainAspectRatio(event), shouldResizeFromCenter(event), this.state.zoom.value, aspectRatio, this.state.originSnapOffset);
6037
6307
  // highlight elements that are to be added to frames on frames creation
6038
6308
  if (this.state.activeTool.type === TOOL_TYPE.frame ||
6039
6309
  this.state.activeTool.type === TOOL_TYPE.magicframe) {
@@ -6087,16 +6357,17 @@ class App extends React.Component {
6087
6357
  snapLines,
6088
6358
  });
6089
6359
  }
6090
- if (transformElements(pointerDownState.originalElements, transformHandleType, selectedElements, this.scene.getElementsMapIncludingDeleted(), shouldRotateWithDiscreteAngle(event), shouldResizeFromCenter(event), selectedElements.length === 1 && isImageElement(selectedElements[0])
6360
+ if (transformElements(pointerDownState.originalElements, transformHandleType, selectedElements, this.scene.getElementsMapIncludingDeleted(), shouldRotateWithDiscreteAngle(event), shouldResizeFromCenter(event), selectedElements.some((element) => isImageElement(element))
6091
6361
  ? !shouldMaintainAspectRatio(event)
6092
6362
  : shouldMaintainAspectRatio(event), resizeX, resizeY, pointerDownState.resize.center.x, pointerDownState.resize.center.y)) {
6093
- this.maybeSuggestBindingForAll(selectedElements);
6363
+ const suggestedBindings = getSuggestedBindingsForArrows(selectedElements, this.scene.getNonDeletedElementsMap());
6094
6364
  const elementsToHighlight = new Set();
6095
6365
  selectedFrames.forEach((frame) => {
6096
6366
  getElementsInResizingFrame(this.scene.getNonDeletedElements(), frame, this.state, this.scene.getNonDeletedElementsMap()).forEach((element) => elementsToHighlight.add(element));
6097
6367
  });
6098
6368
  this.setState({
6099
6369
  elementsToHighlight: [...elementsToHighlight],
6370
+ suggestedBindings,
6100
6371
  });
6101
6372
  return true;
6102
6373
  }
@@ -6141,6 +6412,7 @@ class App extends React.Component {
6141
6412
  return [actionCopy, ...options];
6142
6413
  }
6143
6414
  return [
6415
+ CONTEXT_MENU_SEPARATOR,
6144
6416
  actionCut,
6145
6417
  actionCopy,
6146
6418
  actionPaste,
@@ -6153,6 +6425,7 @@ class App extends React.Component {
6153
6425
  actionPasteStyles,
6154
6426
  CONTEXT_MENU_SEPARATOR,
6155
6427
  actionGroup,
6428
+ actionTextAutoResize,
6156
6429
  actionUnbindText,
6157
6430
  actionBindText,
6158
6431
  actionWrapTextInContainer,
@@ -6321,7 +6594,7 @@ export const createTestHook = () => {
6321
6594
  return this.app?.scene.getElementsIncludingDeleted();
6322
6595
  },
6323
6596
  set(elements) {
6324
- return this.app?.scene.replaceAllElements(elements);
6597
+ return this.app?.scene.replaceAllElements(syncInvalidIndices(elements));
6325
6598
  },
6326
6599
  },
6327
6600
  });