@excalidraw/excalidraw 0.17.1-d2f67e6 → 0.17.1-e63dd02

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (252) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/browser/dev/excalidraw-assets-dev/CascadiaCode-Regular-TMZI7IJ5.woff2 +0 -0
  3. package/dist/browser/dev/excalidraw-assets-dev/ComicShanns-Regular-6TOETDFT.woff2 +0 -0
  4. package/dist/browser/dev/excalidraw-assets-dev/Excalifont-Regular-CPKEUDVM.woff2 +0 -0
  5. package/dist/browser/dev/excalidraw-assets-dev/LiberationSans-Regular-ZQD73GJM.woff2 +0 -0
  6. package/dist/browser/dev/excalidraw-assets-dev/Virgil-Regular-YHAB2VGJ.woff2 +0 -0
  7. package/dist/browser/dev/excalidraw-assets-dev/{chunk-EM6LVGFW.js → chunk-IT7T3AIK.js} +23 -5
  8. package/dist/browser/dev/excalidraw-assets-dev/chunk-IT7T3AIK.js.map +7 -0
  9. package/dist/browser/dev/excalidraw-assets-dev/{chunk-B4UMSLQQ.js → chunk-RNHSD5AR.js} +7451 -2098
  10. package/dist/browser/dev/excalidraw-assets-dev/chunk-RNHSD5AR.js.map +7 -0
  11. package/dist/browser/dev/excalidraw-assets-dev/{dist-6QVAH5JA.js → dist-DNSPZDOZ.js} +31 -19
  12. package/dist/browser/dev/excalidraw-assets-dev/dist-DNSPZDOZ.js.map +7 -0
  13. package/dist/browser/dev/excalidraw-assets-dev/{en-AZFA5HJJ.js → en-XV7OZCPP.js} +6 -2
  14. package/dist/browser/dev/excalidraw-assets-dev/{image-V7E6IT6R.js → image-77HZYGLG.js} +2 -2
  15. package/dist/browser/dev/excalidraw-assets-dev/{image-O66MQ7WQ.css → image-WDHYGKKP.css} +1 -1
  16. package/dist/browser/dev/excalidraw-assets-dev/{image-O66MQ7WQ.css.map → image-WDHYGKKP.css.map} +2 -2
  17. package/dist/browser/dev/index.css +449 -114
  18. package/dist/browser/dev/index.css.map +3 -3
  19. package/dist/browser/dev/index.js +4143 -5956
  20. package/dist/browser/dev/index.js.map +4 -4
  21. package/dist/browser/prod/excalidraw-assets/CascadiaCode-Regular-TMZI7IJ5.woff2 +0 -0
  22. package/dist/browser/prod/excalidraw-assets/ComicShanns-Regular-6TOETDFT.woff2 +0 -0
  23. package/dist/browser/prod/excalidraw-assets/Excalifont-Regular-CPKEUDVM.woff2 +0 -0
  24. package/dist/browser/prod/excalidraw-assets/LiberationSans-Regular-ZQD73GJM.woff2 +0 -0
  25. package/dist/browser/prod/excalidraw-assets/Virgil-Regular-YHAB2VGJ.woff2 +0 -0
  26. package/dist/browser/prod/excalidraw-assets/chunk-OYEADJSR.js +63 -0
  27. package/dist/browser/prod/excalidraw-assets/{chunk-7DXALCB2.js → chunk-PDYFZJMS.js} +3 -3
  28. package/dist/browser/prod/excalidraw-assets/dist-NLUQPPQQ.js +7 -0
  29. package/dist/browser/prod/excalidraw-assets/en-YVAVVILW.js +1 -0
  30. package/dist/browser/prod/excalidraw-assets/image-X3GFZHNN.js +1 -0
  31. package/dist/browser/prod/index.css +1 -1
  32. package/dist/browser/prod/index.js +40 -50
  33. package/dist/dev/CascadiaCode-Regular-TMZI7IJ5.woff2 +0 -0
  34. package/dist/dev/ComicShanns-Regular-6TOETDFT.woff2 +0 -0
  35. package/dist/dev/Excalifont-Regular-CPKEUDVM.woff2 +0 -0
  36. package/dist/dev/LiberationSans-Regular-ZQD73GJM.woff2 +0 -0
  37. package/dist/dev/Virgil-Regular-YHAB2VGJ.woff2 +0 -0
  38. package/dist/dev/{en-EB2MBPAV.json → en-YNVBSAIL.json} +18 -4
  39. package/dist/dev/index.css +449 -114
  40. package/dist/dev/index.css.map +3 -3
  41. package/dist/dev/index.js +21626 -18122
  42. package/dist/dev/index.js.map +4 -4
  43. package/dist/excalidraw/actions/actionAddToLibrary.d.ts +9 -3
  44. package/dist/excalidraw/actions/actionBoundText.d.ts +6 -2
  45. package/dist/excalidraw/actions/actionCanvas.d.ts +36 -12
  46. package/dist/excalidraw/actions/actionClipboard.d.ts +22 -7
  47. package/dist/excalidraw/actions/actionDeleteSelected.d.ts +12 -5
  48. package/dist/excalidraw/actions/actionDeleteSelected.js +24 -5
  49. package/dist/excalidraw/actions/actionDuplicateSelection.js +1 -2
  50. package/dist/excalidraw/actions/actionElementLock.d.ts +6 -2
  51. package/dist/excalidraw/actions/actionExport.d.ts +27 -9
  52. package/dist/excalidraw/actions/actionFinalize.d.ts +6 -2
  53. package/dist/excalidraw/actions/actionFinalize.js +2 -2
  54. package/dist/excalidraw/actions/actionFlip.js +2 -2
  55. package/dist/excalidraw/actions/actionFrame.d.ts +12 -4
  56. package/dist/excalidraw/actions/actionGroup.d.ts +6 -2
  57. package/dist/excalidraw/actions/actionHistory.js +4 -4
  58. package/dist/excalidraw/actions/actionLinearEditor.d.ts +3 -1
  59. package/dist/excalidraw/actions/actionLinearEditor.js +3 -2
  60. package/dist/excalidraw/actions/actionLink.d.ts +3 -1
  61. package/dist/excalidraw/actions/actionMenu.d.ts +9 -3
  62. package/dist/excalidraw/actions/actionNavigate.d.ts +6 -2
  63. package/dist/excalidraw/actions/actionProperties.d.ts +411 -56
  64. package/dist/excalidraw/actions/actionProperties.js +383 -58
  65. package/dist/excalidraw/actions/actionSelectAll.d.ts +3 -1
  66. package/dist/excalidraw/actions/actionStyles.d.ts +3 -1
  67. package/dist/excalidraw/actions/actionStyles.js +3 -2
  68. package/dist/excalidraw/actions/actionToggleGridMode.d.ts +3 -1
  69. package/dist/excalidraw/actions/actionToggleObjectsSnapMode.d.ts +3 -1
  70. package/dist/excalidraw/actions/actionToggleStats.d.ts +3 -1
  71. package/dist/excalidraw/actions/actionToggleViewMode.d.ts +3 -1
  72. package/dist/excalidraw/actions/actionToggleZenMode.d.ts +3 -1
  73. package/dist/excalidraw/actions/types.d.ts +1 -1
  74. package/dist/excalidraw/analytics.js +9 -7
  75. package/dist/excalidraw/appState.d.ts +1 -0
  76. package/dist/excalidraw/appState.js +9 -1
  77. package/dist/excalidraw/binaryheap.d.ts +12 -0
  78. package/dist/excalidraw/binaryheap.js +93 -0
  79. package/dist/excalidraw/change.d.ts +2 -1
  80. package/dist/excalidraw/change.js +6 -4
  81. package/dist/excalidraw/charts.js +0 -10
  82. package/dist/excalidraw/components/Actions.js +7 -5
  83. package/dist/excalidraw/components/App.d.ts +5 -9
  84. package/dist/excalidraw/components/App.js +218 -161
  85. package/dist/excalidraw/components/ButtonIcon.d.ts +15 -0
  86. package/dist/excalidraw/components/ButtonIcon.js +8 -0
  87. package/dist/excalidraw/components/ButtonIconSelect.js +2 -3
  88. package/dist/excalidraw/components/ButtonSeparator.d.ts +2 -0
  89. package/dist/excalidraw/components/ButtonSeparator.js +7 -0
  90. package/dist/excalidraw/components/ColorPicker/ColorPicker.js +47 -79
  91. package/dist/excalidraw/components/ColorPicker/Picker.js +1 -1
  92. package/dist/excalidraw/components/FontPicker/FontPicker.d.ts +21 -0
  93. package/dist/excalidraw/components/FontPicker/FontPicker.js +49 -0
  94. package/dist/excalidraw/components/FontPicker/FontPickerList.d.ts +25 -0
  95. package/dist/excalidraw/components/FontPicker/FontPickerList.js +119 -0
  96. package/dist/excalidraw/components/FontPicker/FontPickerTrigger.d.ts +7 -0
  97. package/dist/excalidraw/components/FontPicker/FontPickerTrigger.js +13 -0
  98. package/dist/excalidraw/components/FontPicker/keyboardNavHandlers.d.ts +14 -0
  99. package/dist/excalidraw/components/FontPicker/keyboardNavHandlers.js +38 -0
  100. package/dist/excalidraw/components/HelpDialog.js +1 -1
  101. package/dist/excalidraw/components/HintViewer.js +6 -3
  102. package/dist/excalidraw/components/PropertiesPopover.d.ts +15 -0
  103. package/dist/excalidraw/components/PropertiesPopover.js +31 -0
  104. package/dist/excalidraw/components/QuickSearch.d.ts +9 -0
  105. package/dist/excalidraw/components/QuickSearch.js +8 -0
  106. package/dist/excalidraw/components/ScrollableList.d.ts +9 -0
  107. package/dist/excalidraw/components/ScrollableList.js +8 -0
  108. package/dist/excalidraw/components/Stats/Angle.d.ts +7 -3
  109. package/dist/excalidraw/components/Stats/Angle.js +39 -31
  110. package/dist/excalidraw/components/Stats/Dimension.d.ts +6 -3
  111. package/dist/excalidraw/components/Stats/Dimension.js +51 -49
  112. package/dist/excalidraw/components/Stats/DragInput.d.ts +15 -6
  113. package/dist/excalidraw/components/Stats/DragInput.js +59 -26
  114. package/dist/excalidraw/components/Stats/FontSize.d.ts +8 -4
  115. package/dist/excalidraw/components/Stats/FontSize.js +39 -36
  116. package/dist/excalidraw/components/Stats/MultiAngle.d.ts +5 -3
  117. package/dist/excalidraw/components/Stats/MultiAngle.js +43 -34
  118. package/dist/excalidraw/components/Stats/MultiDimension.d.ts +5 -3
  119. package/dist/excalidraw/components/Stats/MultiDimension.js +101 -99
  120. package/dist/excalidraw/components/Stats/MultiFontSize.d.ts +6 -3
  121. package/dist/excalidraw/components/Stats/MultiFontSize.js +47 -32
  122. package/dist/excalidraw/components/Stats/MultiPosition.d.ts +3 -1
  123. package/dist/excalidraw/components/Stats/MultiPosition.js +52 -48
  124. package/dist/excalidraw/components/Stats/Position.d.ts +5 -1
  125. package/dist/excalidraw/components/Stats/Position.js +31 -29
  126. package/dist/excalidraw/components/Stats/index.js +5 -17
  127. package/dist/excalidraw/components/Stats/utils.d.ts +14 -3
  128. package/dist/excalidraw/components/Stats/utils.js +48 -9
  129. package/dist/excalidraw/components/TTDDialog/common.d.ts +2 -2
  130. package/dist/excalidraw/components/TTDDialog/common.js +3 -7
  131. package/dist/excalidraw/components/UserList.js +22 -22
  132. package/dist/excalidraw/components/canvases/StaticCanvas.js +1 -0
  133. package/dist/excalidraw/components/dropdownMenu/DropdownMenu.d.ts +12 -3
  134. package/dist/excalidraw/components/dropdownMenu/DropdownMenuItem.d.ts +24 -4
  135. package/dist/excalidraw/components/dropdownMenu/DropdownMenuItem.js +55 -14
  136. package/dist/excalidraw/components/dropdownMenu/DropdownMenuItemContent.d.ts +2 -1
  137. package/dist/excalidraw/components/dropdownMenu/DropdownMenuItemContent.js +2 -2
  138. package/dist/excalidraw/components/dropdownMenu/common.d.ts +1 -1
  139. package/dist/excalidraw/components/dropdownMenu/common.js +3 -2
  140. package/dist/excalidraw/components/icons.d.ts +4 -0
  141. package/dist/excalidraw/components/icons.js +7 -0
  142. package/dist/excalidraw/components/main-menu/MainMenu.d.ts +12 -3
  143. package/dist/excalidraw/components/welcome-screen/WelcomeScreen.Center.js +2 -2
  144. package/dist/excalidraw/components/welcome-screen/WelcomeScreen.Hints.js +3 -3
  145. package/dist/excalidraw/constants.d.ts +17 -2
  146. package/dist/excalidraw/constants.js +21 -4
  147. package/dist/excalidraw/data/reconcile.js +18 -1
  148. package/dist/excalidraw/data/restore.js +55 -9
  149. package/dist/excalidraw/data/transform.js +8 -5
  150. package/dist/excalidraw/element/binding.d.ts +28 -9
  151. package/dist/excalidraw/element/binding.js +303 -71
  152. package/dist/excalidraw/element/collision.d.ts +1 -1
  153. package/dist/excalidraw/element/collision.js +4 -1
  154. package/dist/excalidraw/element/dragElements.d.ts +2 -2
  155. package/dist/excalidraw/element/dragElements.js +13 -3
  156. package/dist/excalidraw/element/embeddable.d.ts +3 -1
  157. package/dist/excalidraw/element/heading.d.ts +11 -0
  158. package/dist/excalidraw/element/heading.js +81 -0
  159. package/dist/excalidraw/element/index.d.ts +1 -1
  160. package/dist/excalidraw/element/index.js +1 -1
  161. package/dist/excalidraw/element/linearElementEditor.d.ts +21 -13
  162. package/dist/excalidraw/element/linearElementEditor.js +133 -56
  163. package/dist/excalidraw/element/newElement.d.ts +8 -3
  164. package/dist/excalidraw/element/newElement.js +15 -2
  165. package/dist/excalidraw/element/resizeElements.d.ts +4 -3
  166. package/dist/excalidraw/element/resizeElements.js +47 -23
  167. package/dist/excalidraw/element/routing.d.ts +13 -0
  168. package/dist/excalidraw/element/routing.js +641 -0
  169. package/dist/excalidraw/element/textElement.d.ts +3 -26
  170. package/dist/excalidraw/element/textElement.js +54 -110
  171. package/dist/excalidraw/element/textWysiwyg.js +39 -47
  172. package/dist/excalidraw/element/transformHandles.js +7 -2
  173. package/dist/excalidraw/element/typeChecks.d.ts +5 -2
  174. package/dist/excalidraw/element/typeChecks.js +17 -0
  175. package/dist/excalidraw/element/types.d.ts +12 -1
  176. package/dist/excalidraw/fonts/ExcalidrawFont.d.ts +21 -0
  177. package/dist/excalidraw/fonts/ExcalidrawFont.js +112 -0
  178. package/dist/excalidraw/fonts/index.d.ts +58 -0
  179. package/dist/excalidraw/fonts/index.js +240 -0
  180. package/dist/excalidraw/fonts/metadata.d.ts +36 -0
  181. package/dist/excalidraw/fonts/metadata.js +91 -0
  182. package/dist/excalidraw/fractionalIndex.d.ts +11 -4
  183. package/dist/excalidraw/fractionalIndex.js +38 -6
  184. package/dist/excalidraw/frame.d.ts +1 -1
  185. package/dist/excalidraw/frame.js +3 -3
  186. package/dist/excalidraw/history.d.ts +4 -3
  187. package/dist/excalidraw/history.js +8 -8
  188. package/dist/excalidraw/index.d.ts +1 -1
  189. package/dist/excalidraw/index.js +3 -3
  190. package/dist/excalidraw/locales/en.json +18 -4
  191. package/dist/excalidraw/math.d.ts +43 -0
  192. package/dist/excalidraw/math.js +110 -0
  193. package/dist/excalidraw/mermaid.js +4 -3
  194. package/dist/excalidraw/renderer/interactiveScene.js +33 -17
  195. package/dist/excalidraw/renderer/renderElement.d.ts +2 -0
  196. package/dist/excalidraw/renderer/renderElement.js +74 -54
  197. package/dist/excalidraw/renderer/staticSvgScene.js +2 -1
  198. package/dist/excalidraw/scene/Scene.js +9 -3
  199. package/dist/excalidraw/scene/Shape.js +56 -5
  200. package/dist/excalidraw/scene/comparisons.d.ts +1 -0
  201. package/dist/excalidraw/scene/comparisons.js +1 -1
  202. package/dist/excalidraw/scene/export.d.ts +2 -1
  203. package/dist/excalidraw/scene/export.js +33 -35
  204. package/dist/excalidraw/scene/types.d.ts +1 -4
  205. package/dist/excalidraw/shapes.d.ts +8 -0
  206. package/dist/excalidraw/shapes.js +57 -0
  207. package/dist/excalidraw/types.d.ts +8 -3
  208. package/dist/excalidraw/utils.d.ts +11 -1
  209. package/dist/excalidraw/utils.js +22 -0
  210. package/dist/prod/CascadiaCode-Regular-TMZI7IJ5.woff2 +0 -0
  211. package/dist/prod/ComicShanns-Regular-6TOETDFT.woff2 +0 -0
  212. package/dist/prod/Excalifont-Regular-CPKEUDVM.woff2 +0 -0
  213. package/dist/prod/LiberationSans-Regular-ZQD73GJM.woff2 +0 -0
  214. package/dist/prod/Virgil-Regular-YHAB2VGJ.woff2 +0 -0
  215. package/dist/prod/{en-EB2MBPAV.json → en-YNVBSAIL.json} +18 -4
  216. package/dist/prod/index.css +1 -1
  217. package/dist/prod/index.js +49 -53
  218. package/dist/utils/export.d.ts +2 -1
  219. package/dist/utils/export.js +2 -1
  220. package/dist/utils/geometry/geometry.d.ts +2 -1
  221. package/dist/utils/geometry/geometry.js +5 -1
  222. package/dist/utils/index.d.ts +1 -0
  223. package/dist/utils/index.js +1 -0
  224. package/history.ts +9 -2
  225. package/package.json +2 -2
  226. package/dist/browser/dev/Cascadia-CYPE3OJC.woff2 +0 -0
  227. package/dist/browser/dev/Virgil-UZN6MUT6.woff2 +0 -0
  228. package/dist/browser/dev/excalidraw-assets-dev/chunk-B4UMSLQQ.js.map +0 -7
  229. package/dist/browser/dev/excalidraw-assets-dev/chunk-EM6LVGFW.js.map +0 -7
  230. package/dist/browser/dev/excalidraw-assets-dev/dist-6QVAH5JA.js.map +0 -7
  231. package/dist/browser/prod/Cascadia-CYPE3OJC.woff2 +0 -0
  232. package/dist/browser/prod/Virgil-UZN6MUT6.woff2 +0 -0
  233. package/dist/browser/prod/excalidraw-assets/chunk-EGOLGOLD.js +0 -55
  234. package/dist/browser/prod/excalidraw-assets/dist-567JAXHK.js +0 -7
  235. package/dist/browser/prod/excalidraw-assets/en-6E7MYLWO.js +0 -1
  236. package/dist/browser/prod/excalidraw-assets/image-SI7BKULC.js +0 -1
  237. package/dist/dev/Cascadia-CYPE3OJC.woff2 +0 -0
  238. package/dist/dev/Virgil-UZN6MUT6.woff2 +0 -0
  239. package/dist/excalidraw/scene/Fonts.d.ts +0 -19
  240. package/dist/excalidraw/scene/Fonts.js +0 -66
  241. package/dist/prod/Cascadia-CYPE3OJC.woff2 +0 -0
  242. package/dist/prod/Virgil-UZN6MUT6.woff2 +0 -0
  243. /package/dist/browser/dev/{Assistant-Bold-ZDZZ6JHA.woff2 → excalidraw-assets-dev/Assistant-Bold-ZDZZ6JHA.woff2} +0 -0
  244. /package/dist/browser/dev/{Assistant-Medium-DZ25RZU3.woff2 → excalidraw-assets-dev/Assistant-Medium-DZ25RZU3.woff2} +0 -0
  245. /package/dist/browser/dev/{Assistant-Regular-PLF2XOGW.woff2 → excalidraw-assets-dev/Assistant-Regular-PLF2XOGW.woff2} +0 -0
  246. /package/dist/browser/dev/{Assistant-SemiBold-CZ5MX6FK.woff2 → excalidraw-assets-dev/Assistant-SemiBold-CZ5MX6FK.woff2} +0 -0
  247. /package/dist/browser/dev/excalidraw-assets-dev/{en-AZFA5HJJ.js.map → en-XV7OZCPP.js.map} +0 -0
  248. /package/dist/browser/dev/excalidraw-assets-dev/{image-V7E6IT6R.js.map → image-77HZYGLG.js.map} +0 -0
  249. /package/dist/browser/prod/{Assistant-Bold-ZDZZ6JHA.woff2 → excalidraw-assets/Assistant-Bold-ZDZZ6JHA.woff2} +0 -0
  250. /package/dist/browser/prod/{Assistant-Medium-DZ25RZU3.woff2 → excalidraw-assets/Assistant-Medium-DZ25RZU3.woff2} +0 -0
  251. /package/dist/browser/prod/{Assistant-Regular-PLF2XOGW.woff2 → excalidraw-assets/Assistant-Regular-PLF2XOGW.woff2} +0 -0
  252. /package/dist/browser/prod/{Assistant-SemiBold-CZ5MX6FK.woff2 → excalidraw-assets/Assistant-SemiBold-CZ5MX6FK.woff2} +0 -0
@@ -1,19 +1,22 @@
1
1
  import { generateNKeysBetween } from "fractional-indexing";
2
2
  import { mutateElement } from "./element/mutateElement";
3
3
  import { InvalidFractionalIndexError } from "./errors";
4
+ import { hasBoundTextElement } from "./element/typeChecks";
5
+ import { getBoundTextElement } from "./element/textElement";
6
+ import { arrayToMap } from "./utils";
4
7
  /**
5
8
  * Envisioned relation between array order and fractional indices:
6
9
  *
7
10
  * 1) Array (or array-like ordered data structure) should be used as a cache of elements order, hiding the internal fractional indices implementation.
8
- * - it's undesirable to to perform reorder for each related operation, thefeore it's necessary to cache the order defined by fractional indices into an ordered data structure
11
+ * - it's undesirable to perform reorder for each related operation, therefore it's necessary to cache the order defined by fractional indices into an ordered data structure
9
12
  * - it's easy enough to define the order of the elements from the outside (boundaries), without worrying about the underlying structure of fractional indices (especially for the host apps)
10
13
  * - it's necessary to always keep the array support for backwards compatibility (restore) - old scenes, old libraries, supporting multiple excalidraw versions etc.
11
14
  * - it's necessary to always keep the fractional indices in sync with the array order
12
15
  * - elements with invalid indices should be detected and synced, without altering the already valid indices
13
16
  *
14
17
  * 2) Fractional indices should be used to reorder the elements, whenever the cached order is expected to be invalidated.
15
- * - as the fractional indices are encoded as part of the elements, it opens up possibilties for incremental-like APIs
16
- * - re-order based on fractional indices should be part of (multiplayer) operations such as reconcillitation & undo/redo
18
+ * - as the fractional indices are encoded as part of the elements, it opens up possibilities for incremental-like APIs
19
+ * - re-order based on fractional indices should be part of (multiplayer) operations such as reconciliation & undo/redo
17
20
  * - technically all the z-index actions could perform also re-order based on fractional indices,but in current state it would not bring much benefits,
18
21
  * as it's faster & more efficient to perform re-order based on array manipulation and later synchronisation of moved indices with the array order
19
22
  */
@@ -22,12 +25,38 @@ import { InvalidFractionalIndexError } from "./errors";
22
25
  *
23
26
  * @throws `InvalidFractionalIndexError` if invalid index is detected.
24
27
  */
25
- export const validateFractionalIndices = (indices) => {
28
+ export const validateFractionalIndices = (elements, { shouldThrow = false, includeBoundTextValidation = false, reconciliationContext, }) => {
29
+ const errorMessages = [];
30
+ const stringifyElement = (element) => `${element?.index}:${element?.id}:${element?.type}:${element?.isDeleted}:${element?.version}:${element?.versionNonce}`;
31
+ const indices = elements.map((x) => x.index);
26
32
  for (const [i, index] of indices.entries()) {
27
33
  const predecessorIndex = indices[i - 1];
28
34
  const successorIndex = indices[i + 1];
29
35
  if (!isValidFractionalIndex(index, predecessorIndex, successorIndex)) {
30
- throw new InvalidFractionalIndexError(`Fractional indices invariant for element has been compromised - ["${predecessorIndex}", "${index}", "${successorIndex}"] [predecessor, current, successor]`);
36
+ errorMessages.push(`Fractional indices invariant has been compromised: "${stringifyElement(elements[i - 1])}", "${stringifyElement(elements[i])}", "${stringifyElement(elements[i + 1])}"`);
37
+ }
38
+ // disabled by default, as we don't fix it
39
+ if (includeBoundTextValidation && hasBoundTextElement(elements[i])) {
40
+ const container = elements[i];
41
+ const text = getBoundTextElement(container, arrayToMap(elements));
42
+ if (text && text.index <= container.index) {
43
+ errorMessages.push(`Fractional indices invariant for bound elements has been compromised: "${stringifyElement(text)}", "${stringifyElement(container)}"`);
44
+ }
45
+ }
46
+ }
47
+ if (errorMessages.length) {
48
+ const error = new InvalidFractionalIndexError();
49
+ const additionalContext = [];
50
+ if (reconciliationContext) {
51
+ additionalContext.push("Additional reconciliation context:");
52
+ additionalContext.push(reconciliationContext.localElements.map((x) => stringifyElement(x)));
53
+ additionalContext.push(reconciliationContext.remoteElements.map((x) => stringifyElement(x)));
54
+ }
55
+ // report just once and with the stacktrace
56
+ console.error(errorMessages.join("\n\n"), error.stack, elements.map((x) => stringifyElement(x)), ...additionalContext);
57
+ if (shouldThrow) {
58
+ // if enabled, gather all the errors first, throw once
59
+ throw error;
31
60
  }
32
61
  }
33
62
  };
@@ -62,8 +91,11 @@ export const syncMovedIndices = (elements, movedElements) => {
62
91
  const indicesGroups = getMovedIndicesGroups(elements, movedElements);
63
92
  // try generatating indices, throws on invalid movedElements
64
93
  const elementsUpdates = generateIndices(elements, indicesGroups);
94
+ const elementsCandidates = elements.map((x) => elementsUpdates.has(x) ? { ...x, ...elementsUpdates.get(x) } : x);
65
95
  // ensure next indices are valid before mutation, throws on invalid ones
66
- validateFractionalIndices(elements.map((x) => elementsUpdates.get(x)?.index || x.index));
96
+ validateFractionalIndices(elementsCandidates,
97
+ // we don't autofix invalid bound text indices, hence don't include it in the validation
98
+ { includeBoundTextValidation: false, shouldThrow: true });
67
99
  // split mutation so we don't end up in an incosistent state
68
100
  for (const [element, update] of elementsUpdates) {
69
101
  mutateElement(element, update, false);
@@ -59,5 +59,5 @@ export declare const omitGroupsContainingFrameLikes: (allElements: ElementsMapOr
59
59
  */
60
60
  export declare const getTargetFrame: (element: ExcalidrawElement, elementsMap: ElementsMap, appState: StaticCanvasAppState) => import("./element/types").ExcalidrawFrameElement | import("./element/types").ExcalidrawMagicFrameElement | null;
61
61
  export declare const isElementInFrame: (element: ExcalidrawElement, allElementsMap: ElementsMap, appState: StaticCanvasAppState) => boolean;
62
- export declare const getFrameLikeTitle: (element: ExcalidrawFrameLikeElement, frameIdx: number) => string;
62
+ export declare const getFrameLikeTitle: (element: ExcalidrawFrameLikeElement) => string;
63
63
  export declare const getElementsOverlappingFrame: (elements: readonly ExcalidrawElement[], frame: ExcalidrawFrameLikeElement) => NonDeletedExcalidrawElement[];
@@ -426,12 +426,12 @@ export const isElementInFrame = (element, allElementsMap, appState) => {
426
426
  }
427
427
  return false;
428
428
  };
429
- export const getFrameLikeTitle = (element, frameIdx) => {
429
+ export const getFrameLikeTitle = (element) => {
430
430
  // TODO name frames "AI" only if specific to AI frames
431
431
  return element.name === null
432
432
  ? isFrameElement(element)
433
- ? `Frame ${frameIdx}`
434
- : `AI Frame $${frameIdx}`
433
+ ? "Frame"
434
+ : "AI Frame"
435
435
  : element.name;
436
436
  };
437
437
  export const getElementsOverlappingFrame = (elements, frame) => {
@@ -1,6 +1,7 @@
1
1
  import type { AppStateChange, ElementsChange } from "./change";
2
2
  import type { SceneElementsMap } from "./element/types";
3
3
  import { Emitter } from "./emitter";
4
+ import type Scene from "./scene/Scene";
4
5
  import type { Snapshot } from "./store";
5
6
  import type { AppState } from "./types";
6
7
  export declare class HistoryChangedEvent {
@@ -19,8 +20,8 @@ export declare class History {
19
20
  * Record a local change which will go into the history
20
21
  */
21
22
  record(elementsChange: ElementsChange, appStateChange: AppStateChange): void;
22
- undo(elements: SceneElementsMap, appState: AppState, snapshot: Readonly<Snapshot>): void | [SceneElementsMap, AppState];
23
- redo(elements: SceneElementsMap, appState: AppState, snapshot: Readonly<Snapshot>): void | [SceneElementsMap, AppState];
23
+ undo(elements: SceneElementsMap, appState: AppState, snapshot: Readonly<Snapshot>, scene: Scene): void | [SceneElementsMap, AppState];
24
+ redo(elements: SceneElementsMap, appState: AppState, snapshot: Readonly<Snapshot>, scene: Scene): void | [SceneElementsMap, AppState];
24
25
  private perform;
25
26
  private static pop;
26
27
  private static push;
@@ -31,7 +32,7 @@ export declare class HistoryEntry {
31
32
  private constructor();
32
33
  static create(appStateChange: AppStateChange, elementsChange: ElementsChange): HistoryEntry;
33
34
  inverse(): HistoryEntry;
34
- applyTo(elements: SceneElementsMap, appState: AppState, snapshot: Readonly<Snapshot>): [SceneElementsMap, AppState, boolean];
35
+ applyTo(elements: SceneElementsMap, appState: AppState, snapshot: Readonly<Snapshot>, scene: Scene): [SceneElementsMap, AppState, boolean];
35
36
  /**
36
37
  * Apply latest (remote) changes to the history entry, creates new instance of `HistoryEntry`.
37
38
  */
@@ -38,13 +38,13 @@ export class History {
38
38
  this.onHistoryChangedEmitter.trigger(new HistoryChangedEvent(this.isUndoStackEmpty, this.isRedoStackEmpty));
39
39
  }
40
40
  }
41
- undo(elements, appState, snapshot) {
42
- return this.perform(elements, appState, snapshot, () => History.pop(this.undoStack), (entry) => History.push(this.redoStack, entry, elements));
41
+ undo(elements, appState, snapshot, scene) {
42
+ return this.perform(elements, appState, snapshot, () => History.pop(this.undoStack), (entry) => History.push(this.redoStack, entry, elements), scene);
43
43
  }
44
- redo(elements, appState, snapshot) {
45
- return this.perform(elements, appState, snapshot, () => History.pop(this.redoStack), (entry) => History.push(this.undoStack, entry, elements));
44
+ redo(elements, appState, snapshot, scene) {
45
+ return this.perform(elements, appState, snapshot, () => History.pop(this.redoStack), (entry) => History.push(this.undoStack, entry, elements), scene);
46
46
  }
47
- perform(elements, appState, snapshot, pop, push) {
47
+ perform(elements, appState, snapshot, pop, push, scene) {
48
48
  try {
49
49
  let historyEntry = pop();
50
50
  if (historyEntry === null) {
@@ -57,7 +57,7 @@ export class History {
57
57
  while (historyEntry) {
58
58
  try {
59
59
  [nextElements, nextAppState, containsVisibleChange] =
60
- historyEntry.applyTo(nextElements, nextAppState, snapshot);
60
+ historyEntry.applyTo(nextElements, nextAppState, snapshot, scene);
61
61
  }
62
62
  finally {
63
63
  // make sure to always push / pop, even if the increment is corrupted
@@ -104,8 +104,8 @@ export class HistoryEntry {
104
104
  inverse() {
105
105
  return new HistoryEntry(this.appStateChange.inverse(), this.elementsChange.inverse());
106
106
  }
107
- applyTo(elements, appState, snapshot) {
108
- const [nextElements, elementsContainVisibleChange] = this.elementsChange.applyTo(elements, snapshot.elements);
107
+ applyTo(elements, appState, snapshot, scene) {
108
+ const [nextElements, elementsContainVisibleChange] = this.elementsChange.applyTo(elements, snapshot.elements, scene);
109
109
  const [nextAppState, appStateContainsVisibleChange] = this.appStateChange.applyTo(appState, nextElements);
110
110
  const appliedVisibleChanges = elementsContainVisibleChange || appStateContainsVisibleChange;
111
111
  return [nextElements, nextAppState, appliedVisibleChanges];
@@ -1,7 +1,7 @@
1
1
  import React from "react";
2
2
  import "./css/app.scss";
3
3
  import "./css/styles.scss";
4
- import "../../public/fonts/fonts.css";
4
+ import "./fonts/assets/fonts.css";
5
5
  import type { ExcalidrawProps } from "./types";
6
6
  import Footer from "./components/footer/FooterCenter";
7
7
  import MainMenu from "./components/main-menu/MainMenu";
@@ -5,7 +5,7 @@ import App from "./components/App";
5
5
  import { isShallowEqual } from "./utils";
6
6
  import "./css/app.scss";
7
7
  import "./css/styles.scss";
8
- import "../../public/fonts/fonts.css";
8
+ import "./fonts/assets/fonts.css";
9
9
  import polyfill from "./polyfill";
10
10
  import { defaultLang } from "./i18n";
11
11
  import { DEFAULT_UI_OPTIONS } from "./constants";
@@ -17,7 +17,7 @@ import WelcomeScreen from "./components/welcome-screen/WelcomeScreen";
17
17
  import LiveCollaborationTrigger from "./components/live-collaboration/LiveCollaborationTrigger";
18
18
  polyfill();
19
19
  const ExcalidrawBase = (props) => {
20
- const { onChange, initialData, excalidrawAPI, isCollaborating = false, onPointerUpdate, renderTopRightUI, langCode = defaultLang.code, viewModeEnabled, zenModeEnabled, gridModeEnabled, libraryReturnUrl, theme, name, renderCustomStats, onPaste, detectScroll = true, handleKeyboardGlobally = false, onLibraryChange, autoFocus = false, generateIdForFile, onLinkOpen, onPointerDown, onPointerUp, onScrollChange, children, validateEmbeddable, renderEmbeddable, aiEnabled, } = props;
20
+ const { onChange, initialData, excalidrawAPI, isCollaborating = false, onPointerUpdate, renderTopRightUI, langCode = defaultLang.code, viewModeEnabled, zenModeEnabled, gridModeEnabled, libraryReturnUrl, theme, name, renderCustomStats, onPaste, detectScroll = true, handleKeyboardGlobally = false, onLibraryChange, autoFocus = false, generateIdForFile, onLinkOpen, onPointerDown, onPointerUp, onScrollChange, children, validateEmbeddable, renderEmbeddable, aiEnabled, showDeprecatedFonts, } = props;
21
21
  const canvasActions = props.UIOptions?.canvasActions;
22
22
  // FIXME normalize/set defaults in parent component so that the memo resolver
23
23
  // compares the same values
@@ -60,7 +60,7 @@ const ExcalidrawBase = (props) => {
60
60
  document.removeEventListener("touchmove", handleTouchMove);
61
61
  };
62
62
  }, []);
63
- return (_jsx(Provider, { unstable_createStore: () => jotaiStore, scope: jotaiScope, children: _jsx(InitializeApp, { langCode: langCode, theme: theme, children: _jsx(App, { onChange: onChange, initialData: initialData, excalidrawAPI: excalidrawAPI, isCollaborating: isCollaborating, onPointerUpdate: onPointerUpdate, renderTopRightUI: renderTopRightUI, langCode: langCode, viewModeEnabled: viewModeEnabled, zenModeEnabled: zenModeEnabled, gridModeEnabled: gridModeEnabled, libraryReturnUrl: libraryReturnUrl, theme: theme, name: name, renderCustomStats: renderCustomStats, UIOptions: UIOptions, onPaste: onPaste, detectScroll: detectScroll, handleKeyboardGlobally: handleKeyboardGlobally, onLibraryChange: onLibraryChange, autoFocus: autoFocus, generateIdForFile: generateIdForFile, onLinkOpen: onLinkOpen, onPointerDown: onPointerDown, onPointerUp: onPointerUp, onScrollChange: onScrollChange, validateEmbeddable: validateEmbeddable, renderEmbeddable: renderEmbeddable, aiEnabled: aiEnabled !== false, children: children }) }) }));
63
+ return (_jsx(Provider, { unstable_createStore: () => jotaiStore, scope: jotaiScope, children: _jsx(InitializeApp, { langCode: langCode, theme: theme, children: _jsx(App, { onChange: onChange, initialData: initialData, excalidrawAPI: excalidrawAPI, isCollaborating: isCollaborating, onPointerUpdate: onPointerUpdate, renderTopRightUI: renderTopRightUI, langCode: langCode, viewModeEnabled: viewModeEnabled, zenModeEnabled: zenModeEnabled, gridModeEnabled: gridModeEnabled, libraryReturnUrl: libraryReturnUrl, theme: theme, name: name, renderCustomStats: renderCustomStats, UIOptions: UIOptions, onPaste: onPaste, detectScroll: detectScroll, handleKeyboardGlobally: handleKeyboardGlobally, onLibraryChange: onLibraryChange, autoFocus: autoFocus, generateIdForFile: generateIdForFile, onLinkOpen: onLinkOpen, onPointerDown: onPointerDown, onPointerUp: onPointerUp, onScrollChange: onScrollChange, validateEmbeddable: validateEmbeddable, renderEmbeddable: renderEmbeddable, aiEnabled: aiEnabled !== false, showDeprecatedFonts: showDeprecatedFonts, children: children }) }) }));
64
64
  };
65
65
  const areEqual = (prevProps, nextProps) => {
66
66
  // short-circuit early
@@ -46,6 +46,10 @@
46
46
  "arrowhead_triangle_outline": "Triangle (outline)",
47
47
  "arrowhead_diamond": "Diamond",
48
48
  "arrowhead_diamond_outline": "Diamond (outline)",
49
+ "arrowtypes": "Arrow type",
50
+ "arrowtype_sharp": "Sharp arrow",
51
+ "arrowtype_round": "Curved arrow",
52
+ "arrowtype_elbowed": "Elbow arrow",
49
53
  "fontSize": "Font size",
50
54
  "fontFamily": "Font family",
51
55
  "addWatermark": "Add \"Made with Excalidraw\"",
@@ -109,6 +113,7 @@
109
113
  "share": "Share",
110
114
  "showStroke": "Show stroke color picker",
111
115
  "showBackground": "Show background color picker",
116
+ "showFonts": "Show font picker",
112
117
  "toggleTheme": "Toggle light/dark theme",
113
118
  "theme": "Theme",
114
119
  "personalLib": "Personal Library",
@@ -294,6 +299,7 @@
294
299
  "hints": {
295
300
  "canvasPanning": "To move canvas, hold mouse wheel or spacebar while dragging, or use the hand tool",
296
301
  "linearElement": "Click to start multiple points, drag for single line",
302
+ "arrowTool": "Click to start multiple points, drag for single line. Press {{arrowShortcut}} again to change arrow type.",
297
303
  "freeDraw": "Click and drag, release when you're finished",
298
304
  "text": "Tip: you can also add text by double-clicking anywhere with the selection tool",
299
305
  "embeddable": "Click-drag to create a website embed",
@@ -557,11 +563,19 @@
557
563
  "syntax": "Mermaid Syntax",
558
564
  "preview": "Preview"
559
565
  },
560
- "userList": {
561
- "search": {
562
- "placeholder": "Quick search",
563
- "empty": "No users found"
566
+ "quickSearch": {
567
+ "placeholder": "Quick search"
568
+ },
569
+ "fontList": {
570
+ "badge": {
571
+ "old": "old"
564
572
  },
573
+ "sceneFonts": "In this scene",
574
+ "availableFonts": "Available fonts",
575
+ "empty": "No fonts found"
576
+ },
577
+ "userList": {
578
+ "empty": "No users found",
565
579
  "hint": {
566
580
  "text": "Click on user to follow",
567
581
  "followStatus": "You're currently following this user",
@@ -1,5 +1,6 @@
1
1
  import type { Point, Zoom } from "./types";
2
2
  import type { ExcalidrawElement, ExcalidrawLinearElement, NonDeleted } from "./element/types";
3
+ import type { Bounds } from "./element/bounds";
3
4
  export declare const rotate: (x: number, y: number, cx: number, cy: number, angle: number) => [number, number];
4
5
  export declare const rotatePoint: (point: readonly [number, number], center: readonly [number, number], angle: number) => [number, number];
5
6
  export declare const adjustXYWithRotation: (sides: {
@@ -14,6 +15,7 @@ export declare const getPointOnAPath: (point: readonly [number, number], path: P
14
15
  segment: number;
15
16
  } | null;
16
17
  export declare const distance2d: (x1: number, y1: number, x2: number, y2: number) => number;
18
+ export declare const distanceSq2d: (p1: readonly [number, number], p2: readonly [number, number]) => number;
17
19
  export declare const centerPoint: (a: readonly [number, number], b: readonly [number, number]) => readonly [number, number];
18
20
  export declare const isPathALoop: (points: ExcalidrawLinearElement["points"], zoomValue?: Zoom["value"]) => boolean;
19
21
  export declare const isPointInPolygon: (points: Point[], x: number, y: number) => boolean;
@@ -33,3 +35,44 @@ export declare const degreeToRadian: (d: number) => number;
33
35
  export declare const rangesOverlap: ([a0, a1]: [number, number], [b0, b1]: [number, number]) => boolean;
34
36
  export declare const rangeIntersection: (rangeA: [number, number], rangeB: [number, number]) => [number, number] | null;
35
37
  export declare const isValueInRange: (value: number, min: number, max: number) => boolean;
38
+ export declare const translatePoint: (p: readonly [number, number], v: import("../utils/geometry/shape").Point) => readonly [number, number];
39
+ export declare const scaleVector: (v: import("../utils/geometry/shape").Point, scalar: number) => import("../utils/geometry/shape").Point;
40
+ export declare const pointToVector: (p: readonly [number, number], origin?: readonly [number, number]) => import("../utils/geometry/shape").Point;
41
+ export declare const scalePointFromOrigin: (p: readonly [number, number], mid: readonly [number, number], multiplier: number) => readonly [number, number];
42
+ export declare const PointInTriangle: (pt: readonly [number, number], v1: readonly [number, number], v2: readonly [number, number], v3: readonly [number, number]) => boolean;
43
+ export declare const magnitudeSq: (vector: import("../utils/geometry/shape").Point) => number;
44
+ export declare const magnitude: (vector: import("../utils/geometry/shape").Point) => number;
45
+ export declare const normalize: (vector: import("../utils/geometry/shape").Point) => import("../utils/geometry/shape").Point;
46
+ export declare const addVectors: (vec1: readonly [number, number], vec2: readonly [number, number]) => import("../utils/geometry/shape").Point;
47
+ export declare const subtractVectors: (vec1: readonly [number, number], vec2: readonly [number, number]) => import("../utils/geometry/shape").Point;
48
+ export declare const pointInsideBounds: (p: readonly [number, number], bounds: Bounds) => boolean;
49
+ /**
50
+ * Get the axis-aligned bounding box for a given element
51
+ */
52
+ export declare const aabbForElement: (element: Readonly<ExcalidrawElement>, offset?: [number, number, number, number]) => Bounds;
53
+ type PolarCoords = [number, number];
54
+ /**
55
+ * Return the polar coordinates for the given carthesian point represented by
56
+ * (x, y) for the center point 0,0 where the first number returned is the radius,
57
+ * the second is the angle in radians.
58
+ */
59
+ export declare const carthesian2Polar: ([x, y]: readonly [number, number]) => PolarCoords;
60
+ /**
61
+ * Angles are in radians and centered on 0, 0. Zero radians on a 1 radius circle
62
+ * corresponds to (1, 0) carthesian coordinates (point), i.e. to the "right".
63
+ */
64
+ type SymmetricArc = {
65
+ radius: number;
66
+ startAngle: number;
67
+ endAngle: number;
68
+ };
69
+ /**
70
+ * Determines if a carthesian point lies on a symmetric arc, i.e. an arc which
71
+ * is part of a circle contour centered on 0, 0.
72
+ */
73
+ export declare const isPointOnSymmetricArc: ({ radius: arcRadius, startAngle, endAngle }: SymmetricArc, point: readonly [number, number]) => boolean;
74
+ export declare const getCenterForBounds: (bounds: Bounds) => readonly [number, number];
75
+ export declare const getCenterForElement: (element: ExcalidrawElement) => readonly [number, number];
76
+ export declare const aabbsOverlapping: (a: Bounds, b: Bounds) => boolean;
77
+ export declare const clamp: (value: number, min: number, max: number) => number;
78
+ export {};
@@ -102,6 +102,11 @@ export const distance2d = (x1, y1, x2, y2) => {
102
102
  const yd = y2 - y1;
103
103
  return Math.hypot(xd, yd);
104
104
  };
105
+ export const distanceSq2d = (p1, p2) => {
106
+ const xd = p2[0] - p1[0];
107
+ const yd = p2[1] - p1[1];
108
+ return xd * xd + yd * yd;
109
+ };
105
110
  export const centerPoint = (a, b) => {
106
111
  return [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2];
107
112
  };
@@ -368,3 +373,108 @@ export const rangeIntersection = (rangeA, rangeB) => {
368
373
  export const isValueInRange = (value, min, max) => {
369
374
  return value >= min && value <= max;
370
375
  };
376
+ export const translatePoint = (p, v) => [
377
+ p[0] + v[0],
378
+ p[1] + v[1],
379
+ ];
380
+ export const scaleVector = (v, scalar) => [
381
+ v[0] * scalar,
382
+ v[1] * scalar,
383
+ ];
384
+ export const pointToVector = (p, origin = [0, 0]) => [
385
+ p[0] - origin[0],
386
+ p[1] - origin[1],
387
+ ];
388
+ export const scalePointFromOrigin = (p, mid, multiplier) => translatePoint(mid, scaleVector(pointToVector(p, mid), multiplier));
389
+ const triangleSign = (p1, p2, p3) => (p1[0] - p3[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p3[1]);
390
+ export const PointInTriangle = (pt, v1, v2, v3) => {
391
+ const d1 = triangleSign(pt, v1, v2);
392
+ const d2 = triangleSign(pt, v2, v3);
393
+ const d3 = triangleSign(pt, v3, v1);
394
+ const has_neg = d1 < 0 || d2 < 0 || d3 < 0;
395
+ const has_pos = d1 > 0 || d2 > 0 || d3 > 0;
396
+ return !(has_neg && has_pos);
397
+ };
398
+ export const magnitudeSq = (vector) => vector[0] * vector[0] + vector[1] * vector[1];
399
+ export const magnitude = (vector) => Math.sqrt(magnitudeSq(vector));
400
+ export const normalize = (vector) => {
401
+ const m = magnitude(vector);
402
+ return [vector[0] / m, vector[1] / m];
403
+ };
404
+ export const addVectors = (vec1, vec2) => [vec1[0] + vec2[0], vec1[1] + vec2[1]];
405
+ export const subtractVectors = (vec1, vec2) => [vec1[0] - vec2[0], vec1[1] - vec2[1]];
406
+ export const pointInsideBounds = (p, bounds) => p[0] > bounds[0] && p[0] < bounds[2] && p[1] > bounds[1] && p[1] < bounds[3];
407
+ /**
408
+ * Get the axis-aligned bounding box for a given element
409
+ */
410
+ export const aabbForElement = (element, offset) => {
411
+ const bbox = {
412
+ minX: element.x,
413
+ minY: element.y,
414
+ maxX: element.x + element.width,
415
+ maxY: element.y + element.height,
416
+ midX: element.x + element.width / 2,
417
+ midY: element.y + element.height / 2,
418
+ };
419
+ const center = [bbox.midX, bbox.midY];
420
+ const [topLeftX, topLeftY] = rotatePoint([bbox.minX, bbox.minY], center, element.angle);
421
+ const [topRightX, topRightY] = rotatePoint([bbox.maxX, bbox.minY], center, element.angle);
422
+ const [bottomRightX, bottomRightY] = rotatePoint([bbox.maxX, bbox.maxY], center, element.angle);
423
+ const [bottomLeftX, bottomLeftY] = rotatePoint([bbox.minX, bbox.maxY], center, element.angle);
424
+ const bounds = [
425
+ Math.min(topLeftX, topRightX, bottomRightX, bottomLeftX),
426
+ Math.min(topLeftY, topRightY, bottomRightY, bottomLeftY),
427
+ Math.max(topLeftX, topRightX, bottomRightX, bottomLeftX),
428
+ Math.max(topLeftY, topRightY, bottomRightY, bottomLeftY),
429
+ ];
430
+ if (offset) {
431
+ const [topOffset, rightOffset, downOffset, leftOffset] = offset;
432
+ return [
433
+ bounds[0] - leftOffset,
434
+ bounds[1] - topOffset,
435
+ bounds[2] + rightOffset,
436
+ bounds[3] + downOffset,
437
+ ];
438
+ }
439
+ return bounds;
440
+ };
441
+ /**
442
+ * Return the polar coordinates for the given carthesian point represented by
443
+ * (x, y) for the center point 0,0 where the first number returned is the radius,
444
+ * the second is the angle in radians.
445
+ */
446
+ export const carthesian2Polar = ([x, y]) => [
447
+ Math.hypot(x, y),
448
+ Math.atan2(y, x),
449
+ ];
450
+ /**
451
+ * Determines if a carthesian point lies on a symmetric arc, i.e. an arc which
452
+ * is part of a circle contour centered on 0, 0.
453
+ */
454
+ export const isPointOnSymmetricArc = ({ radius: arcRadius, startAngle, endAngle }, point) => {
455
+ const [radius, angle] = carthesian2Polar(point);
456
+ return startAngle < endAngle
457
+ ? Math.abs(radius - arcRadius) < 0.0000001 &&
458
+ startAngle <= angle &&
459
+ endAngle >= angle
460
+ : startAngle <= angle || endAngle >= angle;
461
+ };
462
+ export const getCenterForBounds = (bounds) => [
463
+ bounds[0] + (bounds[2] - bounds[0]) / 2,
464
+ bounds[1] + (bounds[3] - bounds[1]) / 2,
465
+ ];
466
+ export const getCenterForElement = (element) => [
467
+ element.x + element.width / 2,
468
+ element.y + element.height / 2,
469
+ ];
470
+ export const aabbsOverlapping = (a, b) => pointInsideBounds([a[0], a[1]], b) ||
471
+ pointInsideBounds([a[2], a[1]], b) ||
472
+ pointInsideBounds([a[2], a[3]], b) ||
473
+ pointInsideBounds([a[0], a[3]], b) ||
474
+ pointInsideBounds([b[0], b[1]], a) ||
475
+ pointInsideBounds([b[2], b[1]], a) ||
476
+ pointInsideBounds([b[2], b[3]], a) ||
477
+ pointInsideBounds([b[0], b[3]], a);
478
+ export const clamp = (value, min, max) => {
479
+ return Math.min(Math.max(value, min), max);
480
+ };
@@ -2,6 +2,7 @@
2
2
  export const isMaybeMermaidDefinition = (text) => {
3
3
  const chartTypes = [
4
4
  "flowchart",
5
+ "graph",
5
6
  "sequenceDiagram",
6
7
  "classDiagram",
7
8
  "stateDiagram",
@@ -21,8 +22,8 @@ export const isMaybeMermaidDefinition = (text) => {
21
22
  "xychart",
22
23
  "block",
23
24
  ];
24
- const re = new RegExp(`^(?:%%{.*?}%%[\\s\\n]*)?\\b${chartTypes
25
- .map((x) => `${x}(-beta)?`)
26
- .join("|")}\\b`);
25
+ const re = new RegExp(`^(?:%%{.*?}%%[\\s\\n]*)?\\b(?:${chartTypes
26
+ .map((x) => `\\s*${x}(-beta)?`)
27
+ .join("|")})\\b`);
27
28
  return re.test(text.trim());
28
29
  };
@@ -12,7 +12,8 @@ import { maxBindingGap } from "../element/binding";
12
12
  import { LinearElementEditor } from "../element/linearElementEditor";
13
13
  import { bootstrapCanvas, fillCircle, getNormalizedCanvasDimensions, } from "./helpers";
14
14
  import oc from "open-color";
15
- import { isFrameLikeElement, isLinearElement, isTextElement, } from "../element/typeChecks";
15
+ import { isArrowElement, isElbowArrow, isFrameLikeElement, isLinearElement, isTextElement, } from "../element/typeChecks";
16
+ import { getCornerRadius } from "../math";
16
17
  const renderLinearElementPointHighlight = (context, appState, elementsMap) => {
17
18
  const { elementId, hoverPointIndex } = appState.selectedLinearElement;
18
19
  if (appState.editingLinearElement?.selectedPointsIndices?.includes(hoverPointIndex)) {
@@ -86,12 +87,13 @@ const renderBindingHighlightForBindableElement = (context, element, elementsMap)
86
87
  const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
87
88
  const width = x2 - x1;
88
89
  const height = y2 - y1;
89
- const threshold = maxBindingGap(element, width, height);
90
+ const thickness = 10;
90
91
  // So that we don't overlap the element itself
91
92
  const strokeOffset = 4;
92
93
  context.strokeStyle = "rgba(0,0,0,.05)";
93
- context.lineWidth = threshold - strokeOffset;
94
- const padding = strokeOffset / 2 + threshold / 2;
94
+ context.lineWidth = thickness - strokeOffset;
95
+ const padding = strokeOffset / 2 + thickness / 2;
96
+ const radius = getCornerRadius(Math.min(element.width, element.height), element);
95
97
  switch (element.type) {
96
98
  case "rectangle":
97
99
  case "text":
@@ -100,7 +102,7 @@ const renderBindingHighlightForBindableElement = (context, element, elementsMap)
100
102
  case "embeddable":
101
103
  case "frame":
102
104
  case "magicframe":
103
- strokeRectWithRotation(context, x1 - padding, y1 - padding, width + padding * 2, height + padding * 2, x1 + width / 2, y1 + height / 2, element.angle);
105
+ strokeRectWithRotation(context, x1 - padding, y1 - padding, width + padding * 2, height + padding * 2, x1 + width / 2, y1 + height / 2, element.angle, undefined, radius);
104
106
  break;
105
107
  case "diamond":
106
108
  const side = Math.hypot(width, height);
@@ -211,6 +213,9 @@ const renderLinearPointHandles = (context, appState, element, elementsMap) => {
211
213
  ? POINT_HANDLE_SIZE
212
214
  : POINT_HANDLE_SIZE / 2;
213
215
  points.forEach((point, idx) => {
216
+ if (isElbowArrow(element) && idx !== 0 && idx !== points.length - 1) {
217
+ return;
218
+ }
214
219
  const isSelected = !!appState.editingLinearElement?.selectedPointsIndices?.includes(idx);
215
220
  renderSingleLinearPoint(context, appState, point, radius, isSelected);
216
221
  });
@@ -346,7 +351,11 @@ const _renderInteractiveScene = ({ canvas, elementsMap, visibleElements, selecte
346
351
  renderLinearPointHandles(context, appState, selectedElements[0], elementsMap);
347
352
  }
348
353
  if (appState.selectedLinearElement &&
349
- appState.selectedLinearElement.hoverPointIndex >= 0) {
354
+ appState.selectedLinearElement.hoverPointIndex >= 0 &&
355
+ !(isElbowArrow(selectedElements[0]) &&
356
+ appState.selectedLinearElement.hoverPointIndex > 0 &&
357
+ appState.selectedLinearElement.hoverPointIndex <
358
+ selectedElements[0].points.length - 1)) {
350
359
  renderLinearElementPointHighlight(context, appState, elementsMap);
351
360
  }
352
361
  // Paint selected elements
@@ -366,18 +375,25 @@ const _renderInteractiveScene = ({ canvas, elementsMap, visibleElements, selecte
366
375
  const selections = [];
367
376
  for (const element of elementsMap.values()) {
368
377
  const selectionColors = [];
369
- // local user
370
- if (locallySelectedIds.has(element.id) &&
371
- !isSelectedViaGroup(appState, element)) {
372
- selectionColors.push(selectionColor);
373
- }
374
- // remote users
375
378
  const remoteClients = renderConfig.remoteSelectedElementIds.get(element.id);
376
- if (remoteClients) {
377
- selectionColors.push(...remoteClients.map((socketId) => {
378
- const background = getClientColor(socketId, appState.collaborators.get(socketId));
379
- return background;
380
- }));
379
+ if (!(
380
+ // Elbow arrow elements cannot be selected when bound on either end
381
+ (isSingleLinearElementSelected &&
382
+ isArrowElement(element) &&
383
+ isElbowArrow(element) &&
384
+ (element.startBinding || element.endBinding)))) {
385
+ // local user
386
+ if (locallySelectedIds.has(element.id) &&
387
+ !isSelectedViaGroup(appState, element)) {
388
+ selectionColors.push(selectionColor);
389
+ }
390
+ // remote users
391
+ if (remoteClients) {
392
+ selectionColors.push(...remoteClients.map((socketId) => {
393
+ const background = getClientColor(socketId, appState.collaborators.get(socketId));
394
+ return background;
395
+ }));
396
+ }
381
397
  }
382
398
  if (selectionColors.length) {
383
399
  const [elementX1, elementY1, elementX2, elementY2, cx, cy] = getElementAbsoluteCoords(element, elementsMap, true);
@@ -9,11 +9,13 @@ export interface ExcalidrawElementWithCanvas {
9
9
  canvas: HTMLCanvasElement;
10
10
  theme: AppState["theme"];
11
11
  scale: number;
12
+ angle: number;
12
13
  zoomValue: AppState["zoom"]["value"];
13
14
  canvasOffsetX: number;
14
15
  canvasOffsetY: number;
15
16
  boundTextElementVersion: number | null;
16
17
  containingFrameOpacity: number;
18
+ boundTextCanvas: HTMLCanvasElement;
17
19
  }
18
20
  export declare const DEFAULT_LINK_SIZE = 14;
19
21
  export declare const elementWithCanvasCache: WeakMap<ExcalidrawElement, ExcalidrawElementWithCanvas>;