@tldraw/editor 5.4.0-next.ef99ab47e5ce → 5.5.0-canary.078a77321dc3

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 (288) hide show
  1. package/dist-cjs/index.d.ts +119 -49
  2. package/dist-cjs/index.js +4 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/TldrawEditor.js +7 -2
  5. package/dist-cjs/lib/TldrawEditor.js.map +2 -2
  6. package/dist-cjs/lib/components/MenuClickCapture.js +6 -8
  7. package/dist-cjs/lib/components/MenuClickCapture.js.map +2 -2
  8. package/dist-cjs/lib/components/default-components/CanvasOverlays.js +1 -3
  9. package/dist-cjs/lib/components/default-components/CanvasOverlays.js.map +2 -2
  10. package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js +1 -1
  11. package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js.map +1 -1
  12. package/dist-cjs/lib/config/TLUserPreferences.js +6 -2
  13. package/dist-cjs/lib/config/TLUserPreferences.js.map +2 -2
  14. package/dist-cjs/lib/config/createTLStore.js +1 -1
  15. package/dist-cjs/lib/config/createTLStore.js.map +2 -2
  16. package/dist-cjs/lib/constants.js +2 -0
  17. package/dist-cjs/lib/constants.js.map +2 -2
  18. package/dist-cjs/lib/editor/Editor.js +190 -300
  19. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  20. package/dist-cjs/lib/editor/derivations/shapeIdsInCurrentPage.js +7 -3
  21. package/dist-cjs/lib/editor/derivations/shapeIdsInCurrentPage.js.map +2 -2
  22. package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js +3 -3
  23. package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js.map +2 -2
  24. package/dist-cjs/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.js +8 -8
  25. package/dist-cjs/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.js.map +2 -2
  26. package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js +10 -6
  27. package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js.map +2 -2
  28. package/dist-cjs/lib/editor/managers/FontManager/FontManager.js +2 -1
  29. package/dist-cjs/lib/editor/managers/FontManager/FontManager.js.map +2 -2
  30. package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js +79 -6
  31. package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js.map +2 -2
  32. package/dist-cjs/lib/editor/managers/ScribbleManager/ScribbleManager.js +7 -0
  33. package/dist-cjs/lib/editor/managers/ScribbleManager/ScribbleManager.js.map +2 -2
  34. package/dist-cjs/lib/editor/managers/SnapManager/BoundsSnaps.js +0 -2
  35. package/dist-cjs/lib/editor/managers/SnapManager/BoundsSnaps.js.map +2 -2
  36. package/dist-cjs/lib/editor/managers/TextManager/TextManager.js +1 -2
  37. package/dist-cjs/lib/editor/managers/TextManager/TextManager.js.map +2 -2
  38. package/dist-cjs/lib/editor/managers/ThemeManager/ThemeManager.js +7 -3
  39. package/dist-cjs/lib/editor/managers/ThemeManager/ThemeManager.js.map +2 -2
  40. package/dist-cjs/lib/editor/managers/TickManager/TickManager.js +6 -3
  41. package/dist-cjs/lib/editor/managers/TickManager/TickManager.js.map +2 -2
  42. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js +12 -1
  43. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +2 -2
  44. package/dist-cjs/lib/editor/overlays/OverlayManager.js +3 -7
  45. package/dist-cjs/lib/editor/overlays/OverlayManager.js.map +2 -2
  46. package/dist-cjs/lib/editor/overlays/strokeShapeIndicators.js +0 -1
  47. package/dist-cjs/lib/editor/overlays/strokeShapeIndicators.js.map +2 -2
  48. package/dist-cjs/lib/editor/shapes/BaseFrameLikeShapeUtil.js +3 -1
  49. package/dist-cjs/lib/editor/shapes/BaseFrameLikeShapeUtil.js.map +2 -2
  50. package/dist-cjs/lib/editor/shapes/shared/getPerfectDashProps.js +1 -0
  51. package/dist-cjs/lib/editor/shapes/shared/getPerfectDashProps.js.map +2 -2
  52. package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js +1 -1
  53. package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js.map +2 -2
  54. package/dist-cjs/lib/editor/tools/StateNode.js +3 -2
  55. package/dist-cjs/lib/editor/tools/StateNode.js.map +2 -2
  56. package/dist-cjs/lib/globals/editors.js +57 -0
  57. package/dist-cjs/lib/globals/editors.js.map +7 -0
  58. package/dist-cjs/lib/globals/menus.js +9 -4
  59. package/dist-cjs/lib/globals/menus.js.map +2 -2
  60. package/dist-cjs/lib/hooks/useCanvasEvents.js +2 -0
  61. package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
  62. package/dist-cjs/lib/hooks/useDocumentEvents.js +39 -9
  63. package/dist-cjs/lib/hooks/useDocumentEvents.js.map +2 -2
  64. package/dist-cjs/lib/hooks/useLocalStore.js +10 -6
  65. package/dist-cjs/lib/hooks/useLocalStore.js.map +2 -2
  66. package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js +1 -0
  67. package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js.map +2 -2
  68. package/dist-cjs/lib/hooks/usePeerIds.js +2 -1
  69. package/dist-cjs/lib/hooks/usePeerIds.js.map +2 -2
  70. package/dist-cjs/lib/license/LicenseManager.js +9 -2
  71. package/dist-cjs/lib/license/LicenseManager.js.map +2 -2
  72. package/dist-cjs/lib/license/LicenseProvider.js +16 -3
  73. package/dist-cjs/lib/license/LicenseProvider.js.map +2 -2
  74. package/dist-cjs/lib/license/useLicenseManagerState.js +5 -4
  75. package/dist-cjs/lib/license/useLicenseManagerState.js.map +2 -2
  76. package/dist-cjs/lib/primitives/Box.js +2 -1
  77. package/dist-cjs/lib/primitives/Box.js.map +2 -2
  78. package/dist-cjs/lib/primitives/Vec.js +4 -3
  79. package/dist-cjs/lib/primitives/Vec.js.map +2 -2
  80. package/dist-cjs/lib/primitives/geometry/Arc2d.js +1 -1
  81. package/dist-cjs/lib/primitives/geometry/Arc2d.js.map +2 -2
  82. package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js +1 -1
  83. package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js.map +2 -2
  84. package/dist-cjs/lib/primitives/geometry/Edge2d.js +1 -1
  85. package/dist-cjs/lib/primitives/geometry/Edge2d.js.map +2 -2
  86. package/dist-cjs/lib/primitives/geometry/Group2d.js +71 -26
  87. package/dist-cjs/lib/primitives/geometry/Group2d.js.map +2 -2
  88. package/dist-cjs/lib/primitives/geometry/Point2d.js +1 -1
  89. package/dist-cjs/lib/primitives/geometry/Point2d.js.map +2 -2
  90. package/dist-cjs/lib/primitives/utils.js +2 -1
  91. package/dist-cjs/lib/primitives/utils.js.map +2 -2
  92. package/dist-cjs/lib/utils/deepLinks.js +5 -1
  93. package/dist-cjs/lib/utils/deepLinks.js.map +2 -2
  94. package/dist-cjs/lib/utils/normalizeWheel.js +2 -0
  95. package/dist-cjs/lib/utils/normalizeWheel.js.map +2 -2
  96. package/dist-cjs/lib/utils/rotation.js +1 -9
  97. package/dist-cjs/lib/utils/rotation.js.map +2 -2
  98. package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js +17 -0
  99. package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js.map +2 -2
  100. package/dist-cjs/lib/utils/sync/alerts.js +1 -1
  101. package/dist-cjs/lib/utils/sync/alerts.js.map +1 -1
  102. package/dist-cjs/version.js +3 -3
  103. package/dist-cjs/version.js.map +1 -1
  104. package/dist-esm/index.d.mts +119 -49
  105. package/dist-esm/index.mjs +9 -2
  106. package/dist-esm/index.mjs.map +2 -2
  107. package/dist-esm/lib/TldrawEditor.mjs +9 -4
  108. package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
  109. package/dist-esm/lib/components/MenuClickCapture.mjs +6 -8
  110. package/dist-esm/lib/components/MenuClickCapture.mjs.map +2 -2
  111. package/dist-esm/lib/components/default-components/CanvasOverlays.mjs +1 -3
  112. package/dist-esm/lib/components/default-components/CanvasOverlays.mjs.map +2 -2
  113. package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs +1 -1
  114. package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs.map +1 -1
  115. package/dist-esm/lib/config/TLUserPreferences.mjs +6 -2
  116. package/dist-esm/lib/config/TLUserPreferences.mjs.map +2 -2
  117. package/dist-esm/lib/config/createTLStore.mjs +1 -1
  118. package/dist-esm/lib/config/createTLStore.mjs.map +2 -2
  119. package/dist-esm/lib/constants.mjs +2 -0
  120. package/dist-esm/lib/constants.mjs.map +2 -2
  121. package/dist-esm/lib/editor/Editor.mjs +199 -301
  122. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  123. package/dist-esm/lib/editor/derivations/shapeIdsInCurrentPage.mjs +7 -3
  124. package/dist-esm/lib/editor/derivations/shapeIdsInCurrentPage.mjs.map +2 -2
  125. package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs +3 -3
  126. package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs.map +2 -2
  127. package/dist-esm/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.mjs +9 -9
  128. package/dist-esm/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.mjs.map +2 -2
  129. package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs +10 -6
  130. package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs.map +2 -2
  131. package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs +2 -1
  132. package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs.map +2 -2
  133. package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs +81 -8
  134. package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs.map +2 -2
  135. package/dist-esm/lib/editor/managers/ScribbleManager/ScribbleManager.mjs +7 -0
  136. package/dist-esm/lib/editor/managers/ScribbleManager/ScribbleManager.mjs.map +2 -2
  137. package/dist-esm/lib/editor/managers/SnapManager/BoundsSnaps.mjs +0 -2
  138. package/dist-esm/lib/editor/managers/SnapManager/BoundsSnaps.mjs.map +2 -2
  139. package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs +2 -3
  140. package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs.map +2 -2
  141. package/dist-esm/lib/editor/managers/ThemeManager/ThemeManager.mjs +7 -3
  142. package/dist-esm/lib/editor/managers/ThemeManager/ThemeManager.mjs.map +2 -2
  143. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs +6 -3
  144. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs.map +2 -2
  145. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs +12 -1
  146. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +2 -2
  147. package/dist-esm/lib/editor/overlays/OverlayManager.mjs +3 -7
  148. package/dist-esm/lib/editor/overlays/OverlayManager.mjs.map +2 -2
  149. package/dist-esm/lib/editor/overlays/strokeShapeIndicators.mjs +0 -1
  150. package/dist-esm/lib/editor/overlays/strokeShapeIndicators.mjs.map +2 -2
  151. package/dist-esm/lib/editor/shapes/BaseFrameLikeShapeUtil.mjs +3 -1
  152. package/dist-esm/lib/editor/shapes/BaseFrameLikeShapeUtil.mjs.map +2 -2
  153. package/dist-esm/lib/editor/shapes/shared/getPerfectDashProps.mjs +1 -0
  154. package/dist-esm/lib/editor/shapes/shared/getPerfectDashProps.mjs.map +2 -2
  155. package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs +1 -1
  156. package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs.map +2 -2
  157. package/dist-esm/lib/editor/tools/StateNode.mjs +3 -2
  158. package/dist-esm/lib/editor/tools/StateNode.mjs.map +2 -2
  159. package/dist-esm/lib/globals/editors.mjs +37 -0
  160. package/dist-esm/lib/globals/editors.mjs.map +7 -0
  161. package/dist-esm/lib/globals/menus.mjs +9 -4
  162. package/dist-esm/lib/globals/menus.mjs.map +2 -2
  163. package/dist-esm/lib/hooks/useCanvasEvents.mjs +2 -0
  164. package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
  165. package/dist-esm/lib/hooks/useDocumentEvents.mjs +40 -10
  166. package/dist-esm/lib/hooks/useDocumentEvents.mjs.map +2 -2
  167. package/dist-esm/lib/hooks/useLocalStore.mjs +11 -7
  168. package/dist-esm/lib/hooks/useLocalStore.mjs.map +2 -2
  169. package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs +1 -0
  170. package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs.map +2 -2
  171. package/dist-esm/lib/hooks/usePeerIds.mjs +2 -1
  172. package/dist-esm/lib/hooks/usePeerIds.mjs.map +2 -2
  173. package/dist-esm/lib/license/LicenseManager.mjs +9 -2
  174. package/dist-esm/lib/license/LicenseManager.mjs.map +2 -2
  175. package/dist-esm/lib/license/LicenseProvider.mjs +16 -3
  176. package/dist-esm/lib/license/LicenseProvider.mjs.map +2 -2
  177. package/dist-esm/lib/license/useLicenseManagerState.mjs +5 -4
  178. package/dist-esm/lib/license/useLicenseManagerState.mjs.map +2 -2
  179. package/dist-esm/lib/primitives/Box.mjs +2 -1
  180. package/dist-esm/lib/primitives/Box.mjs.map +2 -2
  181. package/dist-esm/lib/primitives/Vec.mjs +4 -3
  182. package/dist-esm/lib/primitives/Vec.mjs.map +2 -2
  183. package/dist-esm/lib/primitives/geometry/Arc2d.mjs +1 -1
  184. package/dist-esm/lib/primitives/geometry/Arc2d.mjs.map +2 -2
  185. package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs +1 -1
  186. package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs.map +2 -2
  187. package/dist-esm/lib/primitives/geometry/Edge2d.mjs +1 -1
  188. package/dist-esm/lib/primitives/geometry/Edge2d.mjs.map +2 -2
  189. package/dist-esm/lib/primitives/geometry/Group2d.mjs +75 -27
  190. package/dist-esm/lib/primitives/geometry/Group2d.mjs.map +2 -2
  191. package/dist-esm/lib/primitives/geometry/Point2d.mjs +1 -1
  192. package/dist-esm/lib/primitives/geometry/Point2d.mjs.map +2 -2
  193. package/dist-esm/lib/primitives/utils.mjs +2 -1
  194. package/dist-esm/lib/primitives/utils.mjs.map +2 -2
  195. package/dist-esm/lib/utils/deepLinks.mjs +5 -1
  196. package/dist-esm/lib/utils/deepLinks.mjs.map +2 -2
  197. package/dist-esm/lib/utils/normalizeWheel.mjs +2 -0
  198. package/dist-esm/lib/utils/normalizeWheel.mjs.map +2 -2
  199. package/dist-esm/lib/utils/rotation.mjs +1 -9
  200. package/dist-esm/lib/utils/rotation.mjs.map +2 -2
  201. package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs +17 -0
  202. package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs.map +2 -2
  203. package/dist-esm/lib/utils/sync/alerts.mjs +1 -1
  204. package/dist-esm/lib/utils/sync/alerts.mjs.map +1 -1
  205. package/dist-esm/version.mjs +3 -3
  206. package/dist-esm/version.mjs.map +1 -1
  207. package/package.json +13 -7
  208. package/src/index.ts +6 -1
  209. package/src/lib/TldrawEditor.tsx +16 -4
  210. package/src/lib/components/MenuClickCapture.tsx +19 -18
  211. package/src/lib/components/default-components/CanvasOverlays.tsx +1 -3
  212. package/src/lib/components/default-components/DefaultErrorFallback.tsx +1 -1
  213. package/src/lib/config/TLUserPreferences.test.ts +18 -1
  214. package/src/lib/config/TLUserPreferences.ts +8 -4
  215. package/src/lib/config/createTLStore.ts +1 -1
  216. package/src/lib/constants.ts +8 -0
  217. package/src/lib/editor/Editor.ts +285 -392
  218. package/src/lib/editor/derivations/shapeIdsInCurrentPage.ts +7 -3
  219. package/src/lib/editor/managers/ClickManager/ClickManager.ts +5 -3
  220. package/src/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.ts +9 -9
  221. package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.test.ts +35 -4
  222. package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.ts +13 -7
  223. package/src/lib/editor/managers/FontManager/FontManager.ts +2 -1
  224. package/src/lib/editor/managers/InputsManager/InputsManager.test.ts +199 -3
  225. package/src/lib/editor/managers/InputsManager/InputsManager.ts +128 -9
  226. package/src/lib/editor/managers/ScribbleManager/ScribbleManager.ts +12 -1
  227. package/src/lib/editor/managers/SnapManager/BoundsSnaps.ts +0 -2
  228. package/src/lib/editor/managers/TextManager/TextManager.ts +2 -6
  229. package/src/lib/editor/managers/ThemeManager/ThemeManager.test.ts +53 -0
  230. package/src/lib/editor/managers/ThemeManager/ThemeManager.ts +9 -3
  231. package/src/lib/editor/managers/TickManager/TickManager.test.ts +13 -0
  232. package/src/lib/editor/managers/TickManager/TickManager.ts +9 -3
  233. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +64 -3
  234. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.ts +18 -1
  235. package/src/lib/editor/overlays/OverlayManager.ts +3 -7
  236. package/src/lib/editor/overlays/strokeShapeIndicators.ts +0 -1
  237. package/src/lib/editor/shapes/BaseFrameLikeShapeUtil.tsx +5 -1
  238. package/src/lib/editor/shapes/shared/getPerfectDashProps.ts +3 -0
  239. package/src/lib/editor/tools/BaseBoxShapeTool/children/Pointing.ts +4 -1
  240. package/src/lib/editor/tools/StateNode.test.ts +64 -0
  241. package/src/lib/editor/tools/StateNode.ts +12 -2
  242. package/src/lib/globals/editors.test.ts +76 -0
  243. package/src/lib/globals/editors.ts +53 -0
  244. package/src/lib/globals/menus.test.ts +33 -0
  245. package/src/lib/globals/menus.ts +11 -4
  246. package/src/lib/hooks/useCanvasEvents.ts +2 -0
  247. package/src/lib/hooks/useDocumentEvents.ts +60 -16
  248. package/src/lib/hooks/useLocalStore.ts +16 -8
  249. package/src/lib/hooks/usePassThroughWheelEvents.ts +3 -0
  250. package/src/lib/hooks/usePeerIds.ts +2 -1
  251. package/src/lib/license/LicenseManager.test.ts +160 -0
  252. package/src/lib/license/LicenseManager.ts +13 -4
  253. package/src/lib/license/LicenseProvider.tsx +24 -3
  254. package/src/lib/license/useLicenseManagerState.ts +8 -6
  255. package/src/lib/primitives/Box.test.ts +9 -1
  256. package/src/lib/primitives/Box.ts +3 -1
  257. package/src/lib/primitives/Vec.test.ts +10 -0
  258. package/src/lib/primitives/Vec.ts +4 -4
  259. package/src/lib/primitives/geometry/Arc2d.test.ts +235 -3
  260. package/src/lib/primitives/geometry/Arc2d.ts +1 -1
  261. package/src/lib/primitives/geometry/CubicBezier2d.test.ts +186 -3
  262. package/src/lib/primitives/geometry/CubicBezier2d.ts +1 -1
  263. package/src/lib/primitives/geometry/CubicSpline2d.test.ts +160 -3
  264. package/src/lib/primitives/geometry/Edge2d.test.ts +21 -3
  265. package/src/lib/primitives/geometry/Edge2d.ts +1 -1
  266. package/src/lib/primitives/geometry/Ellipse2d.test.ts +243 -3
  267. package/src/lib/primitives/geometry/Geometry2d.test.ts +553 -1
  268. package/src/lib/primitives/geometry/Group2d.test.ts +419 -3
  269. package/src/lib/primitives/geometry/Group2d.ts +81 -34
  270. package/src/lib/primitives/geometry/Point2d.test.ts +14 -3
  271. package/src/lib/primitives/geometry/Point2d.ts +1 -1
  272. package/src/lib/primitives/geometry/Polyline2d.test.ts +207 -0
  273. package/src/lib/primitives/geometry/Stadium2d.test.ts +191 -3
  274. package/src/lib/primitives/utils.test.ts +10 -1
  275. package/src/lib/primitives/utils.ts +4 -1
  276. package/src/lib/test/useCanvasEvents.test.tsx +74 -0
  277. package/src/lib/test/useDocumentEvents.test.tsx +107 -0
  278. package/src/lib/test/usePassThroughWheelEvents.test.tsx +25 -2
  279. package/src/lib/utils/deepLinks.test.ts +15 -0
  280. package/src/lib/utils/deepLinks.ts +7 -1
  281. package/src/lib/utils/normalizeWheel.test.ts +39 -0
  282. package/src/lib/utils/normalizeWheel.ts +6 -0
  283. package/src/lib/utils/rotation.ts +4 -12
  284. package/src/lib/utils/sync/TLLocalSyncClient.test.ts +56 -0
  285. package/src/lib/utils/sync/TLLocalSyncClient.ts +24 -0
  286. package/src/lib/utils/sync/alerts.ts +1 -1
  287. package/src/version.ts +3 -3
  288. package/src/lib/primitives/geometry/Polyline.test.ts +0 -5
@@ -39,6 +39,7 @@ import {
39
39
  Result,
40
40
  ZERO_INDEX_KEY,
41
41
  annotateError,
42
+ areArraysShallowEqual,
42
43
  assert,
43
44
  assertExists,
44
45
  bind,
@@ -74,6 +75,7 @@ import {
74
75
  import {
75
76
  DEFAULT_ANIMATION_OPTIONS,
76
77
  DEFAULT_CAMERA_OPTIONS,
78
+ FRAME_MS_60HZ,
77
79
  INTERNAL_POINTER_IDS,
78
80
  LEFT_MOUSE_BUTTON,
79
81
  MIDDLE_MOUSE_BUTTON,
@@ -83,6 +85,7 @@ import {
83
85
  import { getOwnerWindow } from "../exports/domUtils.mjs";
84
86
  import { exportToSvg } from "../exports/exportToSvg.mjs";
85
87
  import { getSvgAsImageWithOptions, trimSvgToContent } from "../exports/getSvgAsImage.mjs";
88
+ import { registerMountedEditor, unregisterMountedEditor } from "../globals/editors.mjs";
86
89
  import { tlmenus } from "../globals/menus.mjs";
87
90
  import { tltime } from "../globals/time.mjs";
88
91
  import { defaultTldrawOptions } from "../options.mjs";
@@ -91,7 +94,13 @@ import { EASINGS } from "../primitives/easings.mjs";
91
94
  import { Group2d } from "../primitives/geometry/Group2d.mjs";
92
95
  import { intersectPolygonPolygon } from "../primitives/intersect.mjs";
93
96
  import { Mat } from "../primitives/Mat.mjs";
94
- import { PI, approximately, areAnglesCompatible, clamp, pointInPolygon } from "../primitives/utils.mjs";
97
+ import {
98
+ HALF_PI,
99
+ approximately,
100
+ areAnglesCompatible,
101
+ clamp,
102
+ pointInPolygon
103
+ } from "../primitives/utils.mjs";
95
104
  import { Vec } from "../primitives/Vec.mjs";
96
105
  import { areShapesContentEqual } from "../utils/areShapesContentEqual.mjs";
97
106
  import { dataUrlToFile } from "../utils/assets.mjs";
@@ -289,6 +298,10 @@ class Editor extends EventEmitter {
289
298
  if (!nextPageState) nextPageState = { ...prevPageState };
290
299
  nextPageState.editingShapeId = null;
291
300
  }
301
+ if (prevPageState.croppingShapeId && shapesNoLongerInPage.has(prevPageState.croppingShapeId)) {
302
+ if (!nextPageState) nextPageState = { ...prevPageState };
303
+ nextPageState.croppingShapeId = null;
304
+ }
292
305
  const hintingShapeIds = prevPageState.hintingShapeIds.filter(
293
306
  (id) => !shapesNoLongerInPage.has(id)
294
307
  );
@@ -531,10 +544,11 @@ class Editor extends EventEmitter {
531
544
  this.cancelDoubleClick();
532
545
  }
533
546
  if (prev?.selectedShapeIds !== next?.selectedShapeIds) {
547
+ const selectedShapeIds = new Set(next.selectedShapeIds);
534
548
  const filtered = next.selectedShapeIds.filter((id) => {
535
549
  let parentId = this.getShape(id)?.parentId;
536
550
  while (isShapeId(parentId)) {
537
- if (next.selectedShapeIds.includes(parentId)) {
551
+ if (selectedShapeIds.has(parentId)) {
538
552
  return false;
539
553
  }
540
554
  parentId = this.getShape(parentId)?.parentId;
@@ -604,9 +618,11 @@ class Editor extends EventEmitter {
604
618
  this.on("tick", this._flushEventsForTick);
605
619
  this.on("mount", () => {
606
620
  this._isMounted.set(true);
621
+ registerMountedEditor(this);
607
622
  });
608
623
  this.on("unmount", () => {
609
624
  this._isMounted.set(false);
625
+ unregisterMountedEditor(this);
610
626
  });
611
627
  this.timers.requestAnimationFrame(() => {
612
628
  this._tickManager.start();
@@ -616,7 +632,10 @@ class Editor extends EventEmitter {
616
632
  const mode = this.store.props.collaboration.mode;
617
633
  this.disposables.add(
618
634
  react("update collaboration mode", () => {
619
- this.store.put([{ ...this.getInstanceState(), isReadonly: mode.get() === "readonly" }]);
635
+ const isReadonly = mode.get() === "readonly";
636
+ unsafe__withoutCapture(
637
+ () => this._updateInstanceState({ isReadonly }, { history: "ignore" })
638
+ );
620
639
  })
621
640
  );
622
641
  }
@@ -646,6 +665,14 @@ class Editor extends EventEmitter {
646
665
  );
647
666
  }
648
667
  options;
668
+ /**
669
+ * The license manager whose feature flags apply to this editor. Assigned by `<TldrawEditor />`
670
+ * when it creates the editor, so that UI rendered outside the editor tree can resolve the
671
+ * editor's license through the editor instance. Undefined for editors created directly.
672
+ *
673
+ * @internal
674
+ */
675
+ licenseManager;
649
676
  contextId = uniqueId();
650
677
  /**
651
678
  * The editor's store
@@ -840,6 +867,7 @@ class Editor extends EventEmitter {
840
867
  if (this._isMounted.get()) {
841
868
  this.emit("unmount");
842
869
  }
870
+ unregisterMountedEditor(this);
843
871
  this.stopCameraAnimation();
844
872
  if (this.getInstanceState().followingUserId) {
845
873
  this.stopFollowingUser();
@@ -965,7 +993,7 @@ class Editor extends EventEmitter {
965
993
  getShapeUtil(arg) {
966
994
  const type = typeof arg === "string" ? arg : arg.type;
967
995
  const shapeUtil = getOwnProperty(this.shapeUtils, type);
968
- assert(shapeUtil, `No shape util found for type "${type}"`);
996
+ if (!shapeUtil) throw new Error(`No shape util found for type "${type}"`);
969
997
  return shapeUtil;
970
998
  }
971
999
  hasShapeUtil(arg) {
@@ -1630,7 +1658,8 @@ class Editor extends EventEmitter {
1630
1658
  if (direction === "next" || direction === "prev") {
1631
1659
  const shapeIds = readingOrderShapes.map((shape2) => shape2.id);
1632
1660
  const currentIndex = currentShapeId ? shapeIds.indexOf(currentShapeId) : -1;
1633
- const adjacentIndex = (currentIndex + (direction === "next" ? 1 : -1) + shapeIds.length) % shapeIds.length;
1661
+ const startIndex = currentIndex === -1 && direction === "prev" ? 0 : currentIndex;
1662
+ const adjacentIndex = (startIndex + (direction === "next" ? 1 : -1) + shapeIds.length) % shapeIds.length;
1634
1663
  adjacentShapeId = shapeIds[adjacentIndex];
1635
1664
  } else {
1636
1665
  if (!currentShapeId) return;
@@ -1754,7 +1783,9 @@ class Editor extends EventEmitter {
1754
1783
  const selectedShapes = this.getSelectedShapes();
1755
1784
  if (!selectedShapes.length) return;
1756
1785
  const selectedShape = selectedShapes[0];
1757
- const children = this.getSortedChildIdsForParent(selectedShape.id).map((id) => this.getShape(id)).filter((i) => i);
1786
+ const children = compact(
1787
+ this.getSortedChildIdsForParent(selectedShape.id).map((id) => this.getShape(id))
1788
+ );
1758
1789
  const sortedChildren = this._getShapesInReadingOrder(children);
1759
1790
  if (sortedChildren.length === 0) return;
1760
1791
  this._selectShapesAndZoom([sortedChildren[0].id]);
@@ -1821,18 +1852,13 @@ class Editor extends EventEmitter {
1821
1852
  * @internal
1822
1853
  */
1823
1854
  getShapesSharedRotation(shapeIds) {
1824
- let foundFirst = false;
1825
1855
  let rotation = 0;
1826
1856
  for (let i = 0, n = shapeIds.length; i < n; i++) {
1827
- const pageTransform = this.getShapePageTransform(shapeIds[i]);
1828
- if (!pageTransform) continue;
1829
- if (foundFirst) {
1830
- if (pageTransform.rotation() !== rotation) {
1831
- return 0;
1832
- }
1833
- } else {
1834
- foundFirst = true;
1835
- rotation = pageTransform.rotation();
1857
+ const pageRotation = this.getShapePageTransform(shapeIds[i]).rotation();
1858
+ if (i === 0) {
1859
+ rotation = pageRotation;
1860
+ } else if (pageRotation !== rotation) {
1861
+ return 0;
1836
1862
  }
1837
1863
  }
1838
1864
  return rotation;
@@ -1858,11 +1884,9 @@ class Editor extends EventEmitter {
1858
1884
  return bounds;
1859
1885
  }
1860
1886
  const boxFromRotatedVertices = Box.FromPoints(
1861
- shapeIds.flatMap((id) => {
1862
- const pageTransform = this.getShapePageTransform(id);
1863
- if (!pageTransform) return [];
1864
- return pageTransform.applyToPoints(this.getShapeGeometry(id).bounds.corners);
1865
- }).map((p) => p.rot(-selectionRotation))
1887
+ shapeIds.flatMap(
1888
+ (id) => this.getShapePageTransform(id).applyToPoints(this.getShapeGeometry(id).bounds.corners)
1889
+ ).map((p) => p.rot(-selectionRotation))
1866
1890
  );
1867
1891
  boxFromRotatedVertices.point = boxFromRotatedVertices.point.rot(selectionRotation);
1868
1892
  return boxFromRotatedVertices;
@@ -1873,9 +1897,14 @@ class Editor extends EventEmitter {
1873
1897
  getSelectionRotatedScreenBounds() {
1874
1898
  const bounds = this.getSelectionRotatedPageBounds();
1875
1899
  if (!bounds) return void 0;
1876
- const { x, y } = this.pageToScreen(bounds.point);
1877
- const zoom = this.getZoomLevel();
1878
- return new Box(x, y, bounds.width * zoom, bounds.height * zoom);
1900
+ const screenBounds = this.getViewportScreenBounds();
1901
+ const { x: cx, y: cy, z: zoom } = this.getCamera();
1902
+ return new Box(
1903
+ (bounds.x + cx) * zoom + screenBounds.x,
1904
+ (bounds.y + cy) * zoom + screenBounds.y,
1905
+ bounds.width * zoom,
1906
+ bounds.height * zoom
1907
+ );
1879
1908
  }
1880
1909
  getFocusedGroupId() {
1881
1910
  return this.getCurrentPageState().focusedGroupId ?? this.getCurrentPageId();
@@ -1976,42 +2005,28 @@ class Editor extends EventEmitter {
1976
2005
  */
1977
2006
  setEditingShape(shape) {
1978
2007
  const id = typeof shape === "string" ? shape : shape?.id ?? null;
1979
- if (!id) {
2008
+ if (id && !this.canEditShape(id)) return this;
2009
+ this.run(() => {
2010
+ const prevEditingShapeId = this.getEditingShapeId();
2011
+ if (prevEditingShapeId) {
2012
+ const prevEditingShape = this.getShape(prevEditingShapeId);
2013
+ if (prevEditingShape) {
2014
+ this.getShapeUtil(prevEditingShape).onEditEnd?.(prevEditingShape);
2015
+ }
2016
+ }
1980
2017
  this.run(
1981
2018
  () => {
1982
- const prevEditingShapeId = this.getEditingShapeId();
1983
- if (prevEditingShapeId) {
1984
- const prevEditingShape = this.getShape(prevEditingShapeId);
1985
- if (prevEditingShape) {
1986
- this.getShapeUtil(prevEditingShape).onEditEnd?.(prevEditingShape);
1987
- }
1988
- }
1989
2019
  this._updateCurrentPageState({ editingShapeId: null });
1990
2020
  this._currentRichTextEditor.set(null);
2021
+ if (!id) return;
2022
+ this.select(id);
2023
+ this._updateCurrentPageState({ editingShapeId: id });
2024
+ const nextEditingShape = this.getShape(id);
2025
+ this.getShapeUtil(nextEditingShape).onEditStart?.(nextEditingShape);
1991
2026
  },
1992
2027
  { history: "ignore" }
1993
2028
  );
1994
- return this;
1995
- }
1996
- if (!this.canEditShape(id)) return this;
1997
- this.run(
1998
- () => {
1999
- const prevEditingShapeId = this.getEditingShapeId();
2000
- if (prevEditingShapeId) {
2001
- const prevEditingShape = this.getShape(prevEditingShapeId);
2002
- if (prevEditingShape) {
2003
- this.getShapeUtil(prevEditingShape).onEditEnd?.(prevEditingShape);
2004
- }
2005
- }
2006
- this._updateCurrentPageState({ editingShapeId: null });
2007
- this._currentRichTextEditor.set(null);
2008
- this.select(id);
2009
- this._updateCurrentPageState({ editingShapeId: id });
2010
- const nextEditingShape = this.getShape(id);
2011
- this.getShapeUtil(nextEditingShape).onEditStart?.(nextEditingShape);
2012
- },
2013
- { history: "ignore" }
2014
- );
2029
+ });
2015
2030
  return this;
2016
2031
  }
2017
2032
  // Rich text editor
@@ -2117,19 +2132,12 @@ class Editor extends EventEmitter {
2117
2132
  * @public
2118
2133
  */
2119
2134
  setErasingShapes(shapes) {
2120
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((shape) => shape.id);
2135
+ const ids = typeof shapes[0] === "string" ? shapes.slice() : shapes.map((shape) => shape.id);
2121
2136
  ids.sort();
2122
2137
  const erasingShapeIds = this.getErasingShapeIds();
2123
2138
  this.run(
2124
2139
  () => {
2125
- if (ids.length === erasingShapeIds.length) {
2126
- for (let i = 0; i < ids.length; i++) {
2127
- if (ids[i] !== erasingShapeIds[i]) {
2128
- this._updateCurrentPageState({ erasingShapeIds: ids });
2129
- break;
2130
- }
2131
- }
2132
- } else {
2140
+ if (!areArraysShallowEqual(ids, erasingShapeIds)) {
2133
2141
  this._updateCurrentPageState({ erasingShapeIds: ids });
2134
2142
  }
2135
2143
  },
@@ -2162,6 +2170,7 @@ class Editor extends EventEmitter {
2162
2170
  if (!_shape) return false;
2163
2171
  const util = this.getShapeUtil(_shape);
2164
2172
  if (!util.canCrop(_shape)) return false;
2173
+ if (this.getIsReadonly()) return false;
2165
2174
  if (this.isShapeOrAncestorLocked(_shape)) return false;
2166
2175
  return true;
2167
2176
  }
@@ -2226,11 +2235,11 @@ class Editor extends EventEmitter {
2226
2235
  const collaborators = this.getCollaborators();
2227
2236
  let leaderPresence = null;
2228
2237
  while (targetUserId && !visited.includes(targetUserId)) {
2229
- leaderPresence = collaborators.find((c) => c.userId === targetUserId) ?? null;
2230
- targetUserId = leaderPresence?.followingUserId ?? null;
2231
- if (leaderPresence) {
2232
- visited.push(leaderPresence.userId);
2233
- }
2238
+ const nextPresence = collaborators.find((c) => c.userId === targetUserId);
2239
+ if (!nextPresence) break;
2240
+ leaderPresence = nextPresence;
2241
+ targetUserId = nextPresence.followingUserId ?? null;
2242
+ visited.push(nextPresence.userId);
2234
2243
  }
2235
2244
  return leaderPresence;
2236
2245
  }
@@ -2293,39 +2302,7 @@ class Editor extends EventEmitter {
2293
2302
  *
2294
2303
  * @public */
2295
2304
  getInitialZoom() {
2296
- const cameraOptions = this.getCameraOptions();
2297
- if (!cameraOptions.constraints) return 1;
2298
- if (cameraOptions.constraints.initialZoom === "default") return 1;
2299
- const { zx, zy } = getCameraFitXFitY(this, cameraOptions);
2300
- switch (cameraOptions.constraints.initialZoom) {
2301
- case "fit-min": {
2302
- return Math.max(zx, zy);
2303
- }
2304
- case "fit-max": {
2305
- return Math.min(zx, zy);
2306
- }
2307
- case "fit-x": {
2308
- return zx;
2309
- }
2310
- case "fit-y": {
2311
- return zy;
2312
- }
2313
- case "fit-min-100": {
2314
- return Math.min(1, Math.max(zx, zy));
2315
- }
2316
- case "fit-max-100": {
2317
- return Math.min(1, Math.min(zx, zy));
2318
- }
2319
- case "fit-x-100": {
2320
- return Math.min(1, zx);
2321
- }
2322
- case "fit-y-100": {
2323
- return Math.min(1, zy);
2324
- }
2325
- default: {
2326
- throw exhaustiveSwitchError(cameraOptions.constraints.initialZoom);
2327
- }
2328
- }
2305
+ return this._getFitZoom(this.getCameraOptions().constraints?.initialZoom ?? "default");
2329
2306
  }
2330
2307
  /**
2331
2308
  * Get the camera's base level for calculating actual zoom levels based on the zoom steps.
@@ -2337,11 +2314,13 @@ class Editor extends EventEmitter {
2337
2314
  *
2338
2315
  * @public */
2339
2316
  getBaseZoom() {
2317
+ return this._getFitZoom(this.getCameraOptions().constraints?.baseZoom ?? "default");
2318
+ }
2319
+ _getFitZoom(fit) {
2340
2320
  const cameraOptions = this.getCameraOptions();
2341
- if (!cameraOptions.constraints) return 1;
2342
- if (cameraOptions.constraints.baseZoom === "default") return 1;
2321
+ if (!cameraOptions.constraints || fit === "default") return 1;
2343
2322
  const { zx, zy } = getCameraFitXFitY(this, cameraOptions);
2344
- switch (cameraOptions.constraints.baseZoom) {
2323
+ switch (fit) {
2345
2324
  case "fit-min": {
2346
2325
  return Math.max(zx, zy);
2347
2326
  }
@@ -2367,7 +2346,7 @@ class Editor extends EventEmitter {
2367
2346
  return Math.min(1, zy);
2368
2347
  }
2369
2348
  default: {
2370
- throw exhaustiveSwitchError(cameraOptions.constraints.baseZoom);
2349
+ throw exhaustiveSwitchError(fit);
2371
2350
  }
2372
2351
  }
2373
2352
  }
@@ -2401,7 +2380,7 @@ class Editor extends EventEmitter {
2401
2380
  ...this._cameraOptions.__unsafe__getWithoutCapture(),
2402
2381
  ...opts
2403
2382
  });
2404
- if (next.zoomSteps?.length < 1) next.zoomSteps = [1];
2383
+ if (!next.zoomSteps || next.zoomSteps.length < 1) next.zoomSteps = [1];
2405
2384
  this._cameraOptions.set(next);
2406
2385
  this.setCamera(this.getCamera());
2407
2386
  return this;
@@ -2422,8 +2401,8 @@ class Editor extends EventEmitter {
2422
2401
  const vsb = this.getViewportScreenBounds();
2423
2402
  if (cameraOptions.constraints) {
2424
2403
  const { constraints } = cameraOptions;
2425
- const py = Math.min(constraints.padding.y, vsb.w / 2);
2426
- const px = Math.min(constraints.padding.x, vsb.h / 2);
2404
+ const px = Math.min(constraints.padding.x, vsb.w / 2);
2405
+ const py = Math.min(constraints.padding.y, vsb.h / 2);
2427
2406
  const bounds = Box.From(cameraOptions.constraints.bounds);
2428
2407
  const zx = (vsb.w - px * 2) / bounds.w;
2429
2408
  const zy = (vsb.h - py * 2) / bounds.h;
@@ -2462,12 +2441,12 @@ class Editor extends EventEmitter {
2462
2441
  break;
2463
2442
  }
2464
2443
  case "inside": {
2465
- if (z < zx) x = clamp(x, minX, (vsb.w - px) / z - bounds.w);
2444
+ if (z < zx) x = clamp(x, minX, (vsb.w - px) / z - bounds.w - bounds.x);
2466
2445
  else x = clamp(x, minX + freeW, minX);
2467
2446
  break;
2468
2447
  }
2469
2448
  case "outside": {
2470
- x = clamp(x, px / z - bounds.w, (vsb.w - px) / z);
2449
+ x = clamp(x, px / z - bounds.w - bounds.x, (vsb.w - px) / z - bounds.x);
2471
2450
  break;
2472
2451
  }
2473
2452
  case "free": {
@@ -2488,12 +2467,12 @@ class Editor extends EventEmitter {
2488
2467
  break;
2489
2468
  }
2490
2469
  case "inside": {
2491
- if (z < zy) y = clamp(y, minY, (vsb.h - py) / z - bounds.h);
2470
+ if (z < zy) y = clamp(y, minY, (vsb.h - py) / z - bounds.h - bounds.y);
2492
2471
  else y = clamp(y, minY + freeH, minY);
2493
2472
  break;
2494
2473
  }
2495
2474
  case "outside": {
2496
- y = clamp(y, py / z - bounds.h, (vsb.h - py) / z);
2475
+ y = clamp(y, py / z - bounds.h - bounds.y, (vsb.h - py) / z - bounds.y);
2497
2476
  break;
2498
2477
  }
2499
2478
  case "free": {
@@ -2560,14 +2539,16 @@ class Editor extends EventEmitter {
2560
2539
  setCamera(point, opts) {
2561
2540
  const { isLocked } = this._cameraOptions.__unsafe__getWithoutCapture();
2562
2541
  if (isLocked && !opts?.force) return this;
2542
+ const _point = Vec.Cast(point);
2543
+ if (!Number.isFinite(_point.x) || !Number.isFinite(_point.y) || !Number.isFinite(_point.z)) {
2544
+ throw Error(
2545
+ `Editor.setCamera: expected finite values, got (${_point.x}, ${_point.y}, ${_point.z}).`
2546
+ );
2547
+ }
2563
2548
  this.stopCameraAnimation();
2564
2549
  if (this.getInstanceState().followingUserId) {
2565
2550
  this.stopFollowingUser();
2566
2551
  }
2567
- const _point = Vec.Cast(point);
2568
- if (!Number.isFinite(_point.x)) _point.x = 0;
2569
- if (!Number.isFinite(_point.y)) _point.y = 0;
2570
- if (_point.z === void 0 || !Number.isFinite(_point.z)) point.z = this.getZoomLevel();
2571
2552
  const camera = this.getConstrainedCamera(_point, opts);
2572
2553
  if (opts?.animation) {
2573
2554
  const { width, height } = this.getViewportScreenBounds();
@@ -2861,9 +2842,9 @@ class Editor extends EventEmitter {
2861
2842
  }
2862
2843
  const remaining = duration - elapsed;
2863
2844
  const t = easing(1 - remaining / duration);
2864
- const left = start.minX + (end.minX - start.minX) * t;
2865
- const top = start.minY + (end.minY - start.minY) * t;
2866
- const right = start.maxX + (end.maxX - start.maxX) * t;
2845
+ const left = lerp(start.minX, end.minX, t);
2846
+ const top = lerp(start.minY, end.minY, t);
2847
+ const right = lerp(start.maxX, end.maxX, t);
2867
2848
  this._setCamera(new Vec(-left, -top, this.getViewportScreenBounds().width / (right - left)), {
2868
2849
  force: true
2869
2850
  });
@@ -2941,12 +2922,12 @@ class Editor extends EventEmitter {
2941
2922
  let newCy = cy + dy;
2942
2923
  let newCz = cz;
2943
2924
  if (dirZ !== 0) {
2944
- newCz = cz * (1 + dirZ * currentSpeed * elapsed);
2925
+ newCz = cz * Math.exp(dirZ * currentSpeed * elapsed);
2945
2926
  const center = this.getViewportScreenCenter();
2946
2927
  newCx += center.x / newCz - center.x / cz;
2947
2928
  newCy += center.y / newCz - center.y / cz;
2948
2929
  }
2949
- currentSpeed *= 1 - friction;
2930
+ currentSpeed *= (1 - friction) ** (elapsed / FRAME_MS_60HZ);
2950
2931
  if (currentSpeed < speedThreshold) {
2951
2932
  cancel();
2952
2933
  } else {
@@ -3379,12 +3360,12 @@ class Editor extends EventEmitter {
3379
3360
  const renderingShapes = [];
3380
3361
  let nextIndex = this.options.maxShapesPerPage * 2;
3381
3362
  let nextBackgroundIndex = this.options.maxShapesPerPage;
3382
- const erasingShapeIds = this.getErasingShapeIds();
3363
+ const erasingShapeIds = new Set(this.getErasingShapeIds());
3383
3364
  const addShapeById = (id, opacity, isAncestorErasing) => {
3384
3365
  const shape = this.getShape(id);
3385
3366
  if (!shape) return;
3386
3367
  if (this.isShapeHidden(shape)) {
3387
- const isErasing = isAncestorErasing || erasingShapeIds.includes(id);
3368
+ const isErasing = isAncestorErasing || erasingShapeIds.has(id);
3388
3369
  for (const childId of this.getSortedChildIdsForParent(id)) {
3389
3370
  addShapeById(childId, opacity, isErasing);
3390
3371
  }
@@ -3394,7 +3375,7 @@ class Editor extends EventEmitter {
3394
3375
  let isShapeErasing = false;
3395
3376
  const util = this.getShapeUtil(shape);
3396
3377
  if (useEditorState) {
3397
- isShapeErasing = !isAncestorErasing && erasingShapeIds.includes(id);
3378
+ isShapeErasing = !isAncestorErasing && erasingShapeIds.has(id);
3398
3379
  if (isShapeErasing) {
3399
3380
  opacity *= 0.32;
3400
3381
  }
@@ -3986,10 +3967,8 @@ class Editor extends EventEmitter {
3986
3967
  }
3987
3968
  _getShapePageBoundsCache() {
3988
3969
  return this.store.createComputedCache("pageBoundsCache", (shape) => {
3989
- const pageTransform = this.getShapePageTransform(shape);
3990
- if (!pageTransform) return void 0;
3991
3970
  return Box.FromPoints(
3992
- pageTransform.applyToPoints(this.getShapeGeometry(shape).boundsVertices)
3971
+ this.getShapePageTransform(shape).applyToPoints(this.getShapeGeometry(shape).boundsVertices)
3993
3972
  );
3994
3973
  });
3995
3974
  }
@@ -4107,7 +4086,9 @@ class Editor extends EventEmitter {
4107
4086
  if (pageMask) {
4108
4087
  if (pageMask.length === 0) return void 0;
4109
4088
  const { corners } = pageBounds;
4110
- if (corners.every((p, i) => p && Vec.Equals(p, pageMask[i]))) return pageBounds.clone();
4089
+ if (pageMask.length === corners.length && corners.every((p, i) => Vec.Equals(p, pageMask[i]))) {
4090
+ return pageBounds.clone();
4091
+ }
4111
4092
  const intersection = intersectPolygonPolygon(pageMask, corners);
4112
4093
  if (!intersection) return;
4113
4094
  return Box.FromPoints(intersection);
@@ -4331,23 +4312,22 @@ class Editor extends EventEmitter {
4331
4312
  let inMarginClosestToEdgeHit = null;
4332
4313
  const searchMargin = Math.max(innerMargin, outerMargin, this.getHitTestMargin());
4333
4314
  const candidateIds = this._spatialIndex.getShapeIdsAtPoint(point, searchMargin);
4334
- const shapesToCheck = (opts.renderingOnly ? this.getCurrentPageRenderingShapesSorted() : this.getCurrentPageShapesSorted()).filter((shape) => {
4335
- if (!candidateIds.has(shape.id) && !this.isShapeFrameLike(shape)) return false;
4336
- if (shape.isLocked && !hitLocked || this.isShapeHidden(shape) || this.isShapeOfType(shape, "group"))
4337
- return false;
4338
- const pageMask = this.getShapeMask(shape);
4339
- if (pageMask && !pointInPolygon(point, pageMask)) return false;
4340
- if (filter && !filter(shape)) return false;
4341
- return true;
4342
- });
4315
+ const shapesToCheck = opts.renderingOnly ? this.getCurrentPageRenderingShapesSorted() : this.getCurrentPageShapesSorted();
4343
4316
  for (let i = shapesToCheck.length - 1; i >= 0; i--) {
4344
4317
  const shape = shapesToCheck[i];
4318
+ if (!candidateIds.has(shape.id) && !this.isShapeFrameLike(shape)) continue;
4319
+ if (shape.isLocked && !hitLocked || this.isShapeHidden(shape) || this.isShapeOfType(shape, "group")) {
4320
+ continue;
4321
+ }
4322
+ const pageMask = this.getShapeMask(shape);
4323
+ if (pageMask && !pointInPolygon(point, pageMask)) continue;
4324
+ if (filter && !filter(shape)) continue;
4345
4325
  const geometry = this.getShapeGeometry(shape);
4346
4326
  const isGroup = geometry instanceof Group2d;
4347
4327
  const pointInShapeSpace = this.getPointInShapeSpace(shape, point);
4348
4328
  const shapeUtil = this.getShapeUtil(shape);
4349
4329
  const isShapeFrameLike = this.isShapeFrameLike(shape);
4350
- if (isShapeFrameLike || (this.isShapeOfType(shape, "note") || this.isShapeOfType(shape, "arrow") || this.isShapeOfType(shape, "geo") && shape.props.fill === "none") && shapeUtil.getText(shape)?.trim()) {
4330
+ if (isGroup && (isShapeFrameLike || (this.isShapeOfType(shape, "note") || this.isShapeOfType(shape, "arrow") || this.isShapeOfType(shape, "geo") && shape.props.fill === "none") && shapeUtil.getText(shape)?.trim())) {
4351
4331
  for (const childGeometry of geometry.children) {
4352
4332
  if (childGeometry.isLabel && childGeometry.isPointInBounds(pointInShapeSpace)) {
4353
4333
  return shape;
@@ -4446,11 +4426,15 @@ class Editor extends EventEmitter {
4446
4426
  getShapesAtPoint(point, opts = {}) {
4447
4427
  const margin = opts.margin ?? 0;
4448
4428
  const candidateIds = this._spatialIndex.getShapeIdsAtPoint(point, margin);
4449
- return this.getCurrentPageShapesSorted().filter((shape) => {
4450
- if (this.isShapeHidden(shape)) return false;
4451
- if (!candidateIds.has(shape.id) && !this.isShapeFrameLike(shape)) return false;
4452
- return this.isPointInShape(shape, point, opts);
4453
- }).reverse();
4429
+ const sorted = this.getCurrentPageShapesSorted();
4430
+ const result = [];
4431
+ for (let i = sorted.length - 1; i >= 0; i--) {
4432
+ const shape = sorted[i];
4433
+ if (this.isShapeHidden(shape)) continue;
4434
+ if (!candidateIds.has(shape.id) && !this.isShapeFrameLike(shape)) continue;
4435
+ if (this.isPointInShape(shape, point, opts)) result.push(shape);
4436
+ }
4437
+ return result;
4454
4438
  }
4455
4439
  /**
4456
4440
  * Get shape IDs within the given bounds.
@@ -4535,9 +4519,7 @@ class Editor extends EventEmitter {
4535
4519
  const freshShape = this.getShape(id);
4536
4520
  if (!freshShape) return new Vec(0, 0);
4537
4521
  if (isPageId(freshShape.parentId)) return Vec.From(point);
4538
- const parentTransform = this.getShapePageTransform(freshShape.parentId);
4539
- if (!parentTransform) return Vec.From(point);
4540
- return parentTransform.clone().invert().applyToPoint(point);
4522
+ return this.getShapePageTransform(freshShape.parentId).clone().invert().applyToPoint(point);
4541
4523
  }
4542
4524
  getCurrentPageShapes() {
4543
4525
  return Array.from(this.getCurrentPageShapeIds(), (id) => this.store.get(id));
@@ -4649,23 +4631,7 @@ class Editor extends EventEmitter {
4649
4631
  * @public
4650
4632
  */
4651
4633
  isShapeInPage(shape, pageId = this.getCurrentPageId()) {
4652
- const id = typeof shape === "string" ? shape : shape.id;
4653
- const shapeToCheck = this.getShape(id);
4654
- if (!shapeToCheck) return false;
4655
- let shapeIsInPage = false;
4656
- if (shapeToCheck.parentId === pageId) {
4657
- shapeIsInPage = true;
4658
- } else {
4659
- let parent = this.getShape(shapeToCheck.parentId);
4660
- isInPageSearch: while (parent) {
4661
- if (parent.parentId === pageId) {
4662
- shapeIsInPage = true;
4663
- break isInPageSearch;
4664
- }
4665
- parent = this.getShape(parent.parentId);
4666
- }
4667
- }
4668
- return shapeIsInPage;
4634
+ return this.getAncestorPageId(shape) === pageId;
4669
4635
  }
4670
4636
  /**
4671
4637
  * Get the id of the containing page for a given shape.
@@ -4746,10 +4712,7 @@ class Editor extends EventEmitter {
4746
4712
  for (let i = 0; i < shapesToReparent.length; i++) {
4747
4713
  const shape = shapesToReparent[i];
4748
4714
  const pageTransform = this.getShapePageTransform(shape);
4749
- if (!pageTransform) continue;
4750
- const pagePoint = pageTransform.point();
4751
- if (!pagePoint) continue;
4752
- const newPoint = invertedParentTransform.applyToPoint(pagePoint);
4715
+ const newPoint = invertedParentTransform.applyToPoint(pageTransform.point());
4753
4716
  const newRotation = pageTransform.rotation() - parentPageRotation;
4754
4717
  if (shape.id === parentId) {
4755
4718
  throw Error("Attempted to reparent a shape to itself!");
@@ -4838,7 +4801,7 @@ class Editor extends EventEmitter {
4838
4801
  */
4839
4802
  getShapeAndDescendantIds(ids) {
4840
4803
  const shapeIds = /* @__PURE__ */ new Set();
4841
- for (const shape of ids.map((id) => this.getShape(id)).sort(sortByIndex)) {
4804
+ for (const shape of compact(ids.map((id) => this.getShape(id))).sort(sortByIndex)) {
4842
4805
  shapeIds.add(shape.id);
4843
4806
  this.visitDescendants(shape, (descendantId) => {
4844
4807
  shapeIds.add(descendantId);
@@ -4860,11 +4823,12 @@ class Editor extends EventEmitter {
4860
4823
  const draggingShapes = compact(droppingShapes.map((s) => this.getShape(s))).filter(
4861
4824
  (s) => !s.isLocked && !this.isShapeHidden(s)
4862
4825
  );
4826
+ const excludedIds = this.getShapeAndDescendantIds(draggingShapes.map((s) => s.id));
4863
4827
  const maybeDraggingOverShapes = this.getShapesAtPoint(point, {
4864
4828
  hitInside: true,
4865
4829
  margin: 0
4866
4830
  }).filter(
4867
- (s) => !droppingShapes.includes(s) && !s.isLocked && !this.isShapeHidden(s) && !draggingShapes.includes(s)
4831
+ (s) => !droppingShapes.includes(s) && !s.isLocked && !this.isShapeHidden(s) && !excludedIds.has(s.id)
4868
4832
  );
4869
4833
  for (const maybeDraggingOverShape of maybeDraggingOverShapes) {
4870
4834
  const shapeUtil = this.getShapeUtil(maybeDraggingOverShape);
@@ -5117,8 +5081,7 @@ class Editor extends EventEmitter {
5117
5081
  for (const id of ids) {
5118
5082
  const shape = this.getShape(id);
5119
5083
  const localDelta = Vec.From(offset);
5120
- const parentTransform = this.getShapeParentTransform(shape);
5121
- if (parentTransform) localDelta.rot(-parentTransform.rotation());
5084
+ localDelta.rot(-this.getShapeParentTransform(shape).rotation());
5122
5085
  changes.push(this.getChangesToTranslateShape(shape, localDelta.add(shape)));
5123
5086
  }
5124
5087
  this.updateShapes(changes);
@@ -5173,7 +5136,7 @@ class Editor extends EventEmitter {
5173
5136
  if (!originalShape) continue;
5174
5137
  let ox = 0;
5175
5138
  let oy = 0;
5176
- if (offset && initialIds.has(originalId)) {
5139
+ if (offset && initialIds.has(originalId) && !shapeIdSet.has(originalShape.parentId)) {
5177
5140
  const parentTransform = this.getShapeParentTransform(originalShape);
5178
5141
  const vec = new Vec(offset.x, offset.y).rot(-parentTransform.rotation());
5179
5142
  ox = vec.x;
@@ -5287,26 +5250,17 @@ class Editor extends EventEmitter {
5287
5250
  toggleLock(shapes) {
5288
5251
  const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
5289
5252
  if (this.getIsReadonly() || ids.length === 0) return this;
5290
- let allLocked = true, allUnlocked = true;
5253
+ let allLocked = true;
5291
5254
  const shapesToToggle = [];
5292
5255
  for (const id of ids) {
5293
5256
  const shape = this.getShape(id);
5294
5257
  if (shape) {
5295
5258
  shapesToToggle.push(shape);
5296
- if (shape.isLocked) {
5297
- allUnlocked = false;
5298
- } else {
5299
- allLocked = false;
5300
- }
5259
+ if (!shape.isLocked) allLocked = false;
5301
5260
  }
5302
5261
  }
5303
5262
  this.run(() => {
5304
- if (allUnlocked) {
5305
- this.updateShapes(
5306
- shapesToToggle.map((shape) => ({ id: shape.id, type: shape.type, isLocked: true }))
5307
- );
5308
- this.setSelectedShapes([]);
5309
- } else if (allLocked) {
5263
+ if (allLocked) {
5310
5264
  this.updateShapes(
5311
5265
  shapesToToggle.map((shape) => ({ id: shape.id, type: shape.type, isLocked: false }))
5312
5266
  );
@@ -5314,6 +5268,7 @@ class Editor extends EventEmitter {
5314
5268
  this.updateShapes(
5315
5269
  shapesToToggle.map((shape) => ({ id: shape.id, type: shape.type, isLocked: true }))
5316
5270
  );
5271
+ this.setSelectedShapes([]);
5317
5272
  }
5318
5273
  });
5319
5274
  return this;
@@ -5424,9 +5379,10 @@ class Editor extends EventEmitter {
5424
5379
  const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
5425
5380
  let freshShapes = compact(ids.map((id) => this.getShape(id)));
5426
5381
  if (opts?.filterAxisAligned) {
5427
- freshShapes = freshShapes.filter(
5428
- (s) => this.getShapePageTransform(s)?.rotation() % (PI / 2) === 0
5429
- );
5382
+ freshShapes = freshShapes.filter((s) => {
5383
+ const remainder = Math.abs(this.getShapePageTransform(s).rotation() % HALF_PI);
5384
+ return Math.min(remainder, HALF_PI - remainder) < 1e-9;
5385
+ });
5430
5386
  }
5431
5387
  const clusters = [];
5432
5388
  const allBounds = [];
@@ -5591,9 +5547,9 @@ class Editor extends EventEmitter {
5591
5547
  dim = "height";
5592
5548
  }
5593
5549
  let shapeGap = 0;
5550
+ shapeClustersToStack.sort((a, b) => a.pageBounds[min] - b.pageBounds[min]);
5594
5551
  if (_gap === 0) {
5595
5552
  const gaps = {};
5596
- shapeClustersToStack.sort((a, b) => a.pageBounds[min] - b.pageBounds[min]);
5597
5553
  for (let i = 0; i < len - 1; i++) {
5598
5554
  const currCluster = shapeClustersToStack[i];
5599
5555
  const nextCluster = shapeClustersToStack[i + 1];
@@ -5631,8 +5587,7 @@ class Editor extends EventEmitter {
5631
5587
  const shapeDelta = delta.clone();
5632
5588
  const parent = this.getShapeParent(shape);
5633
5589
  if (parent) {
5634
- const parentTransform = this.getShapePageTransform(parent);
5635
- if (parentTransform) shapeDelta.rot(-parentTransform.rotation());
5590
+ shapeDelta.rot(-this.getShapePageTransform(parent).rotation());
5636
5591
  }
5637
5592
  shapeDelta.add(shape);
5638
5593
  changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
@@ -5670,7 +5625,7 @@ class Editor extends EventEmitter {
5670
5625
  }
5671
5626
  const commonBounds = Box.Common(allBounds);
5672
5627
  const maxWidth = commonBounds.width;
5673
- shapeClustersToPack.sort((a, b) => a.pageBounds.width - b.pageBounds.width).sort((a, b) => a.pageBounds.height - b.pageBounds.height);
5628
+ shapeClustersToPack.sort((a, b) => b.pageBounds.width - a.pageBounds.width).sort((a, b) => b.pageBounds.height - a.pageBounds.height);
5674
5629
  const startWidth = Math.max(Math.ceil(Math.sqrt(area / 0.95)), maxWidth);
5675
5630
  const spaces = [new Box(commonBounds.x, commonBounds.y, startWidth, Infinity)];
5676
5631
  let width = 0;
@@ -5718,8 +5673,7 @@ class Editor extends EventEmitter {
5718
5673
  const shapeDelta = delta.clone();
5719
5674
  const parent = this.getShapeParent(shape);
5720
5675
  if (parent) {
5721
- const parentTransform = this.getShapeParentTransform(shape);
5722
- if (parentTransform) shapeDelta.rot(-parentTransform.rotation());
5676
+ shapeDelta.rot(-this.getShapeParentTransform(shape).rotation());
5723
5677
  }
5724
5678
  shapeDelta.add(shape);
5725
5679
  changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
@@ -5785,8 +5739,7 @@ class Editor extends EventEmitter {
5785
5739
  const shapeDelta = delta.clone();
5786
5740
  const parent = this.getShapeParent(shape);
5787
5741
  if (parent) {
5788
- const parentTransform = this.getShapePageTransform(parent);
5789
- if (parentTransform) shapeDelta.rot(-parentTransform.rotation());
5742
+ shapeDelta.rot(-this.getShapePageTransform(parent).rotation());
5790
5743
  }
5791
5744
  shapeDelta.add(shape);
5792
5745
  changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
@@ -5860,8 +5813,7 @@ class Editor extends EventEmitter {
5860
5813
  const shapeDelta = delta.clone();
5861
5814
  const parent = this.getShapeParent(shape);
5862
5815
  if (parent) {
5863
- const parentTransform = this.getShapePageTransform(parent);
5864
- if (parentTransform) shapeDelta.rot(-parentTransform.rotation());
5816
+ shapeDelta.rot(-this.getShapePageTransform(parent).rotation());
5865
5817
  }
5866
5818
  shapeDelta.add(shape);
5867
5819
  changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
@@ -5916,8 +5868,7 @@ class Editor extends EventEmitter {
5916
5868
  scale[val] = commonBounds[dim] / pageBounds[dim];
5917
5869
  for (const shape of shapes2) {
5918
5870
  const shapeLocalOffset = localOffset.clone();
5919
- const parentTransform = this.getShapeParentTransform(shape);
5920
- if (parentTransform) shapeLocalOffset.rot(-parentTransform.rotation());
5871
+ shapeLocalOffset.rot(-this.getShapeParentTransform(shape).rotation());
5921
5872
  shapeLocalOffset.add(shape);
5922
5873
  const changes = this.getChangesToTranslateShape(shape, shapeLocalOffset);
5923
5874
  this.updateShape(changes);
@@ -5973,8 +5924,7 @@ class Editor extends EventEmitter {
5973
5924
  );
5974
5925
  for (const shape of shapes2) {
5975
5926
  const shapeLocalOffset = localOffset.clone();
5976
- const parentTransform = this.getShapeParentTransform(shape);
5977
- if (parentTransform) shapeLocalOffset.rot(-parentTransform.rotation());
5927
+ shapeLocalOffset.rot(-this.getShapeParentTransform(shape).rotation());
5978
5928
  shapeLocalOffset.add(shape);
5979
5929
  const changes = this.getChangesToTranslateShape(shape, shapeLocalOffset);
5980
5930
  this.updateShape(changes);
@@ -6023,9 +5973,7 @@ class Editor extends EventEmitter {
6023
5973
  const scaleOrigin = opts.scaleOrigin ?? this.getShapePageBounds(id)?.center;
6024
5974
  if (!scaleOrigin) return null;
6025
5975
  const pageTransform = opts.initialPageTransform ? Mat.Cast(opts.initialPageTransform) : this.getShapePageTransform(id);
6026
- if (!pageTransform) return null;
6027
5976
  const pageRotation = pageTransform.rotation();
6028
- if (pageRotation == null) return null;
6029
5977
  const scaleAxisRotation = opts.scaleAxisRotation ?? pageRotation;
6030
5978
  const initialBounds = opts.initialBounds ?? this.getShapeGeometry(id).bounds;
6031
5979
  if (!initialBounds) return null;
@@ -6176,7 +6124,6 @@ class Editor extends EventEmitter {
6176
6124
  const currentLocalBounds = this.getShapeGeometry(id).bounds;
6177
6125
  const currentPageCenter = Mat.applyToPoint(pageTransform, currentLocalBounds.center);
6178
6126
  const shapePageTransformOrigin = pageTransform.point();
6179
- if (!currentPageCenter || !shapePageTransformOrigin) return this;
6180
6127
  const pageDelta = Vec.Sub(postScaleShapePageCenter, currentPageCenter);
6181
6128
  const postScaleShapePagePoint = Vec.Add(shapePageTransformOrigin, pageDelta);
6182
6129
  const { x, y } = this.getPointInParentSpace(id, postScaleShapePagePoint);
@@ -6435,10 +6382,10 @@ class Editor extends EventEmitter {
6435
6382
  if (animationIdForShape !== animationId) continue;
6436
6383
  updates.push({
6437
6384
  ...end,
6438
- x: start.x + (end.x - start.x) * t,
6439
- y: start.y + (end.y - start.y) * t,
6440
- opacity: start.opacity + (end.opacity - start.opacity) * t,
6441
- rotation: start.rotation + (end.rotation - start.rotation) * t,
6385
+ x: lerp(start.x, end.x, t),
6386
+ y: lerp(start.y, end.y, t),
6387
+ opacity: lerp(start.opacity, end.opacity, t),
6388
+ rotation: lerp(start.rotation, end.rotation, t),
6442
6389
  props: this.getShapeUtil(end).getInterpolatedProps?.(start, end, t) ?? end.props
6443
6390
  });
6444
6391
  }
@@ -6564,7 +6511,7 @@ class Editor extends EventEmitter {
6564
6511
  * @public
6565
6512
  */
6566
6513
  updateShapes(partials) {
6567
- const compactedPartials = Array(partials.length);
6514
+ const compactedPartials = [];
6568
6515
  for (let i = 0, n = partials.length; i < n; i++) {
6569
6516
  const partial = partials[i];
6570
6517
  if (!partial) continue;
@@ -6635,28 +6582,32 @@ class Editor extends EventEmitter {
6635
6582
  }
6636
6583
  /* --------------------- Styles --------------------- */
6637
6584
  /**
6638
- * Get all the current styles among the users selected shapes
6585
+ * Groups have no styles of their own: a style read or write on a selection applies to the
6586
+ * non-group shapes beneath each group, however deeply nested. Returns those shapes.
6639
6587
  *
6640
6588
  * @internal
6641
6589
  */
6642
- _extractSharedStyles(shape, sharedStyleMap) {
6643
- if (this.isShapeOfType(shape, "group")) {
6644
- const childIds = this._parentIdsToChildIds.get()[shape.id];
6645
- if (!childIds) return;
6646
- for (let i = 0, n = childIds.length; i < n; i++) {
6647
- this._extractSharedStyles(this.getShape(childIds[i]), sharedStyleMap);
6648
- }
6649
- } else {
6650
- for (const [style, propKey] of this.styleProps[shape.type]) {
6651
- sharedStyleMap.applyValue(style, getOwnProperty(shape.props, propKey));
6590
+ _getStyleableShapes(shapes) {
6591
+ const result = [];
6592
+ const visit = (shape) => {
6593
+ if (this.isShapeOfType(shape, "group")) {
6594
+ for (const childId of this.getSortedChildIdsForParent(shape.id)) {
6595
+ const child = this.getShape(childId);
6596
+ if (child) visit(child);
6597
+ }
6598
+ } else {
6599
+ result.push(shape);
6652
6600
  }
6653
- }
6601
+ };
6602
+ for (const shape of shapes) visit(shape);
6603
+ return result;
6654
6604
  }
6655
6605
  _getSelectionSharedStyles() {
6656
- const selectedShapes = this.getSelectedShapes();
6657
6606
  const sharedStyles = new SharedStyleMap();
6658
- for (const selectedShape of selectedShapes) {
6659
- this._extractSharedStyles(selectedShape, sharedStyles);
6607
+ for (const shape of this._getStyleableShapes(this.getSelectedShapes())) {
6608
+ for (const [style, propKey] of this.styleProps[shape.type]) {
6609
+ sharedStyles.applyValue(style, getOwnProperty(shape.props, propKey));
6610
+ }
6660
6611
  }
6661
6612
  return sharedStyles;
6662
6613
  }
@@ -6688,38 +6639,16 @@ class Editor extends EventEmitter {
6688
6639
  const styles = new SharedStyleMap();
6689
6640
  if (!currentTool) return styles;
6690
6641
  if (currentTool.shapeType) {
6691
- if (currentTool.shapeType === "frame" && !this.getShapeUtil("frame").options.showColors) {
6692
- for (const style of this.styleProps[currentTool.shapeType].keys()) {
6693
- if (style.id === "tldraw:color") continue;
6694
- styles.applyValue(style, this.getStyleForNextShape(style));
6695
- }
6696
- } else {
6697
- for (const style of this.styleProps[currentTool.shapeType].keys()) {
6698
- styles.applyValue(style, this.getStyleForNextShape(style));
6699
- }
6642
+ for (const style of this.styleProps[currentTool.shapeType].keys()) {
6643
+ styles.applyValue(style, this.getStyleForNextShape(style));
6700
6644
  }
6701
6645
  }
6702
6646
  return styles;
6703
6647
  }
6704
6648
  getSharedOpacity() {
6705
6649
  if (this.isIn("select") && this.getSelectedShapeIds().length > 0) {
6706
- const shapesToCheck = [];
6707
- const addShape = (shapeId) => {
6708
- const shape = this.getShape(shapeId);
6709
- if (!shape) return;
6710
- if (this.isShapeOfType(shape, "group")) {
6711
- for (const childId of this.getSortedChildIdsForParent(shape.id)) {
6712
- addShape(childId);
6713
- }
6714
- } else {
6715
- shapesToCheck.push(shape);
6716
- }
6717
- };
6718
- for (const shapeId of this.getSelectedShapeIds()) {
6719
- addShape(shapeId);
6720
- }
6721
6650
  let opacity = null;
6722
- for (const shape of shapesToCheck) {
6651
+ for (const shape of this._getStyleableShapes(this.getSelectedShapes())) {
6723
6652
  if (opacity === null) {
6724
6653
  opacity = shape.opacity;
6725
6654
  } else if (opacity !== shape.opacity) {
@@ -6758,28 +6687,12 @@ class Editor extends EventEmitter {
6758
6687
  setOpacityForSelectedShapes(opacity) {
6759
6688
  const selectedShapes = this.getSelectedShapes();
6760
6689
  if (selectedShapes.length > 0) {
6761
- const shapesToUpdate = [];
6762
- const addShapeById = (shape) => {
6763
- if (this.isShapeOfType(shape, "group")) {
6764
- const childIds = this.getSortedChildIdsForParent(shape);
6765
- for (const childId of childIds) {
6766
- addShapeById(this.getShape(childId));
6767
- }
6768
- } else {
6769
- shapesToUpdate.push(shape);
6770
- }
6771
- };
6772
- for (const id of selectedShapes) {
6773
- addShapeById(id);
6774
- }
6775
6690
  this.updateShapes(
6776
- shapesToUpdate.map((shape) => {
6777
- return {
6778
- id: shape.id,
6779
- type: shape.type,
6780
- opacity
6781
- };
6782
- })
6691
+ this._getStyleableShapes(selectedShapes).map((shape) => ({
6692
+ id: shape.id,
6693
+ type: shape.type,
6694
+ opacity
6695
+ }))
6783
6696
  );
6784
6697
  }
6785
6698
  return this;
@@ -6824,33 +6737,17 @@ class Editor extends EventEmitter {
6824
6737
  const selectedShapes = this.getSelectedShapes();
6825
6738
  if (selectedShapes.length > 0) {
6826
6739
  const updates = [];
6827
- const addShapeById = (shape) => {
6828
- if (this.isShapeOfType(shape, "group")) {
6829
- const childIds = this.getSortedChildIdsForParent(shape.id);
6830
- for (const childId of childIds) {
6831
- addShapeById(this.getShape(childId));
6832
- }
6833
- } else {
6834
- const util = this.getShapeUtil(shape);
6835
- const stylePropKey = this.styleProps[shape.type].get(style);
6836
- if (stylePropKey) {
6837
- const shapePartial = {
6838
- id: shape.id,
6839
- type: shape.type,
6840
- props: { [stylePropKey]: value }
6841
- };
6842
- updates.push({
6843
- util,
6844
- originalShape: shape,
6845
- updatePartial: shapePartial
6846
- });
6847
- }
6740
+ for (const shape of this._getStyleableShapes(selectedShapes)) {
6741
+ const stylePropKey = this.styleProps[shape.type].get(style);
6742
+ if (stylePropKey) {
6743
+ updates.push({
6744
+ id: shape.id,
6745
+ type: shape.type,
6746
+ props: { [stylePropKey]: value }
6747
+ });
6848
6748
  }
6849
- };
6850
- for (const shape of selectedShapes) {
6851
- addShapeById(shape);
6852
6749
  }
6853
- this.updateShapes(updates.map(({ updatePartial }) => updatePartial));
6750
+ this.updateShapes(updates);
6854
6751
  }
6855
6752
  return this;
6856
6753
  }
@@ -7246,7 +7143,6 @@ class Editor extends EventEmitter {
7246
7143
  if (shapeIdMap.has(newShape.parentId)) {
7247
7144
  newShape.parentId = shapeIdMap.get(oldShape.parentId);
7248
7145
  } else {
7249
- rootShapeIds.push(newShape.id);
7250
7146
  newShape.index = index;
7251
7147
  index = getIndexAbove(index);
7252
7148
  }
@@ -7271,8 +7167,11 @@ class Editor extends EventEmitter {
7271
7167
  continue;
7272
7168
  }
7273
7169
  if (asset.type === "image" && asset.props.src?.startsWith("data:image") || asset.type === "video" && asset.props.src?.startsWith("data:video")) {
7274
- assetsToUpdate.push(structuredClone(asset));
7275
- asset.props.src = null;
7170
+ assetsToUpdate.push(asset);
7171
+ const assetWithoutSrc = structuredClone(asset);
7172
+ assetWithoutSrc.props.src = null;
7173
+ assetsToCreate.push(assetWithoutSrc);
7174
+ continue;
7276
7175
  }
7277
7176
  assetsToCreate.push(asset);
7278
7177
  }
@@ -8099,7 +7998,7 @@ class Editor extends EventEmitter {
8099
7998
  if (info.type === "misc") {
8100
7999
  if (info.name === "cancel" || info.name === "complete") {
8101
8000
  this.inputs.setIsDragging(false);
8102
- if (this.inputs.getIsPanning()) {
8001
+ if (this.inputs.getIsPanning() && !this.inputs.keys.has("Space")) {
8103
8002
  this.inputs.setIsPanning(false);
8104
8003
  this.inputs.setIsSpacebarPanning(false);
8105
8004
  this.setCursor({ type: this._prevCursor, rotation: 0 });
@@ -8851,10 +8750,9 @@ function withIsolatedShapes(editor, shapeIds, callback) {
8851
8750
  }
8852
8751
  function getCameraFitXFitY(editor, cameraOptions) {
8853
8752
  if (!cameraOptions.constraints) throw Error("Should have constraints here");
8854
- const {
8855
- padding: { x: px, y: py }
8856
- } = cameraOptions.constraints;
8857
8753
  const vsb = editor.getViewportScreenBounds();
8754
+ const px = Math.min(cameraOptions.constraints.padding.x, vsb.w / 2);
8755
+ const py = Math.min(cameraOptions.constraints.padding.y, vsb.h / 2);
8858
8756
  const bounds = Box.From(cameraOptions.constraints.bounds);
8859
8757
  const zx = (vsb.w - px * 2) / bounds.w;
8860
8758
  const zy = (vsb.h - py * 2) / bounds.h;