@tldraw/editor 5.4.0-canary.b8ec00e16e3a → 5.4.0-canary.b902bfb68544

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 (233) hide show
  1. package/dist-cjs/index.d.ts +114 -47
  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/editor/Editor.js +164 -280
  17. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  18. package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js +3 -3
  19. package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js.map +2 -2
  20. package/dist-cjs/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.js +8 -8
  21. package/dist-cjs/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.js.map +2 -2
  22. package/dist-cjs/lib/editor/managers/FontManager/FontManager.js +2 -1
  23. package/dist-cjs/lib/editor/managers/FontManager/FontManager.js.map +2 -2
  24. package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js +79 -6
  25. package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js.map +2 -2
  26. package/dist-cjs/lib/editor/managers/ScribbleManager/ScribbleManager.js +7 -0
  27. package/dist-cjs/lib/editor/managers/ScribbleManager/ScribbleManager.js.map +2 -2
  28. package/dist-cjs/lib/editor/managers/SnapManager/BoundsSnaps.js +0 -2
  29. package/dist-cjs/lib/editor/managers/SnapManager/BoundsSnaps.js.map +2 -2
  30. package/dist-cjs/lib/editor/managers/TextManager/TextManager.js +1 -2
  31. package/dist-cjs/lib/editor/managers/TextManager/TextManager.js.map +2 -2
  32. package/dist-cjs/lib/editor/managers/TickManager/TickManager.js +6 -3
  33. package/dist-cjs/lib/editor/managers/TickManager/TickManager.js.map +2 -2
  34. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js +12 -1
  35. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +2 -2
  36. package/dist-cjs/lib/editor/overlays/OverlayManager.js +3 -7
  37. package/dist-cjs/lib/editor/overlays/OverlayManager.js.map +2 -2
  38. package/dist-cjs/lib/editor/overlays/strokeShapeIndicators.js +0 -1
  39. package/dist-cjs/lib/editor/overlays/strokeShapeIndicators.js.map +2 -2
  40. package/dist-cjs/lib/editor/shapes/BaseFrameLikeShapeUtil.js +3 -1
  41. package/dist-cjs/lib/editor/shapes/BaseFrameLikeShapeUtil.js.map +2 -2
  42. package/dist-cjs/lib/editor/shapes/shared/getPerfectDashProps.js +1 -0
  43. package/dist-cjs/lib/editor/shapes/shared/getPerfectDashProps.js.map +2 -2
  44. package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js +1 -1
  45. package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js.map +2 -2
  46. package/dist-cjs/lib/editor/tools/StateNode.js +3 -2
  47. package/dist-cjs/lib/editor/tools/StateNode.js.map +2 -2
  48. package/dist-cjs/lib/globals/editors.js +57 -0
  49. package/dist-cjs/lib/globals/editors.js.map +7 -0
  50. package/dist-cjs/lib/globals/menus.js +9 -4
  51. package/dist-cjs/lib/globals/menus.js.map +2 -2
  52. package/dist-cjs/lib/hooks/useCanvasEvents.js +2 -0
  53. package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
  54. package/dist-cjs/lib/hooks/useDocumentEvents.js +39 -9
  55. package/dist-cjs/lib/hooks/useDocumentEvents.js.map +2 -2
  56. package/dist-cjs/lib/hooks/useLocalStore.js +10 -6
  57. package/dist-cjs/lib/hooks/useLocalStore.js.map +2 -2
  58. package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js +1 -0
  59. package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js.map +2 -2
  60. package/dist-cjs/lib/hooks/usePeerIds.js +2 -1
  61. package/dist-cjs/lib/hooks/usePeerIds.js.map +2 -2
  62. package/dist-cjs/lib/license/LicenseManager.js +9 -2
  63. package/dist-cjs/lib/license/LicenseManager.js.map +2 -2
  64. package/dist-cjs/lib/license/LicenseProvider.js +16 -3
  65. package/dist-cjs/lib/license/LicenseProvider.js.map +2 -2
  66. package/dist-cjs/lib/license/useLicenseManagerState.js +5 -4
  67. package/dist-cjs/lib/license/useLicenseManagerState.js.map +2 -2
  68. package/dist-cjs/lib/primitives/Box.js +2 -1
  69. package/dist-cjs/lib/primitives/Box.js.map +2 -2
  70. package/dist-cjs/lib/primitives/Vec.js +4 -3
  71. package/dist-cjs/lib/primitives/Vec.js.map +2 -2
  72. package/dist-cjs/lib/primitives/utils.js +2 -1
  73. package/dist-cjs/lib/primitives/utils.js.map +2 -2
  74. package/dist-cjs/lib/utils/deepLinks.js +5 -1
  75. package/dist-cjs/lib/utils/deepLinks.js.map +2 -2
  76. package/dist-cjs/lib/utils/rotation.js +1 -9
  77. package/dist-cjs/lib/utils/rotation.js.map +2 -2
  78. package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js +17 -0
  79. package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js.map +2 -2
  80. package/dist-cjs/lib/utils/sync/alerts.js +1 -1
  81. package/dist-cjs/lib/utils/sync/alerts.js.map +1 -1
  82. package/dist-cjs/version.js +3 -3
  83. package/dist-cjs/version.js.map +1 -1
  84. package/dist-esm/index.d.mts +114 -47
  85. package/dist-esm/index.mjs +9 -2
  86. package/dist-esm/index.mjs.map +2 -2
  87. package/dist-esm/lib/TldrawEditor.mjs +9 -4
  88. package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
  89. package/dist-esm/lib/components/MenuClickCapture.mjs +6 -8
  90. package/dist-esm/lib/components/MenuClickCapture.mjs.map +2 -2
  91. package/dist-esm/lib/components/default-components/CanvasOverlays.mjs +1 -3
  92. package/dist-esm/lib/components/default-components/CanvasOverlays.mjs.map +2 -2
  93. package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs +1 -1
  94. package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs.map +1 -1
  95. package/dist-esm/lib/config/TLUserPreferences.mjs +6 -2
  96. package/dist-esm/lib/config/TLUserPreferences.mjs.map +2 -2
  97. package/dist-esm/lib/config/createTLStore.mjs +1 -1
  98. package/dist-esm/lib/config/createTLStore.mjs.map +2 -2
  99. package/dist-esm/lib/editor/Editor.mjs +172 -281
  100. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  101. package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs +3 -3
  102. package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs.map +2 -2
  103. package/dist-esm/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.mjs +9 -9
  104. package/dist-esm/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.mjs.map +2 -2
  105. package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs +2 -1
  106. package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs.map +2 -2
  107. package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs +81 -8
  108. package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs.map +2 -2
  109. package/dist-esm/lib/editor/managers/ScribbleManager/ScribbleManager.mjs +7 -0
  110. package/dist-esm/lib/editor/managers/ScribbleManager/ScribbleManager.mjs.map +2 -2
  111. package/dist-esm/lib/editor/managers/SnapManager/BoundsSnaps.mjs +0 -2
  112. package/dist-esm/lib/editor/managers/SnapManager/BoundsSnaps.mjs.map +2 -2
  113. package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs +2 -3
  114. package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs.map +2 -2
  115. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs +6 -3
  116. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs.map +2 -2
  117. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs +12 -1
  118. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +2 -2
  119. package/dist-esm/lib/editor/overlays/OverlayManager.mjs +3 -7
  120. package/dist-esm/lib/editor/overlays/OverlayManager.mjs.map +2 -2
  121. package/dist-esm/lib/editor/overlays/strokeShapeIndicators.mjs +0 -1
  122. package/dist-esm/lib/editor/overlays/strokeShapeIndicators.mjs.map +2 -2
  123. package/dist-esm/lib/editor/shapes/BaseFrameLikeShapeUtil.mjs +3 -1
  124. package/dist-esm/lib/editor/shapes/BaseFrameLikeShapeUtil.mjs.map +2 -2
  125. package/dist-esm/lib/editor/shapes/shared/getPerfectDashProps.mjs +1 -0
  126. package/dist-esm/lib/editor/shapes/shared/getPerfectDashProps.mjs.map +2 -2
  127. package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs +1 -1
  128. package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs.map +2 -2
  129. package/dist-esm/lib/editor/tools/StateNode.mjs +3 -2
  130. package/dist-esm/lib/editor/tools/StateNode.mjs.map +2 -2
  131. package/dist-esm/lib/globals/editors.mjs +37 -0
  132. package/dist-esm/lib/globals/editors.mjs.map +7 -0
  133. package/dist-esm/lib/globals/menus.mjs +9 -4
  134. package/dist-esm/lib/globals/menus.mjs.map +2 -2
  135. package/dist-esm/lib/hooks/useCanvasEvents.mjs +2 -0
  136. package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
  137. package/dist-esm/lib/hooks/useDocumentEvents.mjs +40 -10
  138. package/dist-esm/lib/hooks/useDocumentEvents.mjs.map +2 -2
  139. package/dist-esm/lib/hooks/useLocalStore.mjs +11 -7
  140. package/dist-esm/lib/hooks/useLocalStore.mjs.map +2 -2
  141. package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs +1 -0
  142. package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs.map +2 -2
  143. package/dist-esm/lib/hooks/usePeerIds.mjs +2 -1
  144. package/dist-esm/lib/hooks/usePeerIds.mjs.map +2 -2
  145. package/dist-esm/lib/license/LicenseManager.mjs +9 -2
  146. package/dist-esm/lib/license/LicenseManager.mjs.map +2 -2
  147. package/dist-esm/lib/license/LicenseProvider.mjs +16 -3
  148. package/dist-esm/lib/license/LicenseProvider.mjs.map +2 -2
  149. package/dist-esm/lib/license/useLicenseManagerState.mjs +5 -4
  150. package/dist-esm/lib/license/useLicenseManagerState.mjs.map +2 -2
  151. package/dist-esm/lib/primitives/Box.mjs +2 -1
  152. package/dist-esm/lib/primitives/Box.mjs.map +2 -2
  153. package/dist-esm/lib/primitives/Vec.mjs +4 -3
  154. package/dist-esm/lib/primitives/Vec.mjs.map +2 -2
  155. package/dist-esm/lib/primitives/utils.mjs +2 -1
  156. package/dist-esm/lib/primitives/utils.mjs.map +2 -2
  157. package/dist-esm/lib/utils/deepLinks.mjs +5 -1
  158. package/dist-esm/lib/utils/deepLinks.mjs.map +2 -2
  159. package/dist-esm/lib/utils/rotation.mjs +1 -9
  160. package/dist-esm/lib/utils/rotation.mjs.map +2 -2
  161. package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs +17 -0
  162. package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs.map +2 -2
  163. package/dist-esm/lib/utils/sync/alerts.mjs +1 -1
  164. package/dist-esm/lib/utils/sync/alerts.mjs.map +1 -1
  165. package/dist-esm/version.mjs +3 -3
  166. package/dist-esm/version.mjs.map +1 -1
  167. package/package.json +13 -7
  168. package/src/index.ts +6 -1
  169. package/src/lib/TldrawEditor.tsx +16 -4
  170. package/src/lib/components/MenuClickCapture.tsx +19 -18
  171. package/src/lib/components/default-components/CanvasOverlays.tsx +1 -3
  172. package/src/lib/components/default-components/DefaultErrorFallback.tsx +1 -1
  173. package/src/lib/config/TLUserPreferences.test.ts +18 -1
  174. package/src/lib/config/TLUserPreferences.ts +8 -4
  175. package/src/lib/config/createTLStore.ts +1 -1
  176. package/src/lib/editor/Editor.ts +241 -359
  177. package/src/lib/editor/managers/ClickManager/ClickManager.ts +5 -3
  178. package/src/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.ts +9 -9
  179. package/src/lib/editor/managers/FontManager/FontManager.ts +2 -1
  180. package/src/lib/editor/managers/InputsManager/InputsManager.test.ts +199 -3
  181. package/src/lib/editor/managers/InputsManager/InputsManager.ts +128 -9
  182. package/src/lib/editor/managers/ScribbleManager/ScribbleManager.ts +12 -1
  183. package/src/lib/editor/managers/SnapManager/BoundsSnaps.ts +0 -2
  184. package/src/lib/editor/managers/TextManager/TextManager.ts +2 -6
  185. package/src/lib/editor/managers/TickManager/TickManager.test.ts +13 -0
  186. package/src/lib/editor/managers/TickManager/TickManager.ts +9 -3
  187. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +64 -3
  188. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.ts +18 -1
  189. package/src/lib/editor/overlays/OverlayManager.ts +3 -7
  190. package/src/lib/editor/overlays/strokeShapeIndicators.ts +0 -1
  191. package/src/lib/editor/shapes/BaseFrameLikeShapeUtil.tsx +5 -1
  192. package/src/lib/editor/shapes/shared/getPerfectDashProps.ts +3 -0
  193. package/src/lib/editor/tools/BaseBoxShapeTool/children/Pointing.ts +4 -1
  194. package/src/lib/editor/tools/StateNode.test.ts +64 -0
  195. package/src/lib/editor/tools/StateNode.ts +12 -2
  196. package/src/lib/globals/editors.test.ts +76 -0
  197. package/src/lib/globals/editors.ts +53 -0
  198. package/src/lib/globals/menus.test.ts +33 -0
  199. package/src/lib/globals/menus.ts +11 -4
  200. package/src/lib/hooks/useCanvasEvents.ts +2 -0
  201. package/src/lib/hooks/useDocumentEvents.ts +60 -16
  202. package/src/lib/hooks/useLocalStore.ts +16 -8
  203. package/src/lib/hooks/usePassThroughWheelEvents.ts +3 -0
  204. package/src/lib/hooks/usePeerIds.ts +2 -1
  205. package/src/lib/license/LicenseManager.test.ts +160 -0
  206. package/src/lib/license/LicenseManager.ts +13 -4
  207. package/src/lib/license/LicenseProvider.tsx +24 -3
  208. package/src/lib/license/useLicenseManagerState.ts +8 -6
  209. package/src/lib/primitives/Box.test.ts +9 -1
  210. package/src/lib/primitives/Box.ts +3 -1
  211. package/src/lib/primitives/Vec.test.ts +10 -0
  212. package/src/lib/primitives/Vec.ts +4 -4
  213. package/src/lib/primitives/geometry/Arc2d.test.ts +226 -3
  214. package/src/lib/primitives/geometry/CubicBezier2d.test.ts +173 -3
  215. package/src/lib/primitives/geometry/CubicSpline2d.test.ts +160 -3
  216. package/src/lib/primitives/geometry/Ellipse2d.test.ts +243 -3
  217. package/src/lib/primitives/geometry/Geometry2d.test.ts +553 -1
  218. package/src/lib/primitives/geometry/Group2d.test.ts +398 -3
  219. package/src/lib/primitives/geometry/Polyline2d.test.ts +207 -0
  220. package/src/lib/primitives/geometry/Stadium2d.test.ts +191 -3
  221. package/src/lib/primitives/utils.test.ts +10 -1
  222. package/src/lib/primitives/utils.ts +4 -1
  223. package/src/lib/test/useCanvasEvents.test.tsx +74 -0
  224. package/src/lib/test/useDocumentEvents.test.tsx +107 -0
  225. package/src/lib/test/usePassThroughWheelEvents.test.tsx +25 -2
  226. package/src/lib/utils/deepLinks.test.ts +15 -0
  227. package/src/lib/utils/deepLinks.ts +7 -1
  228. package/src/lib/utils/rotation.ts +4 -12
  229. package/src/lib/utils/sync/TLLocalSyncClient.test.ts +56 -0
  230. package/src/lib/utils/sync/TLLocalSyncClient.ts +24 -0
  231. package/src/lib/utils/sync/alerts.ts +1 -1
  232. package/src/version.ts +3 -3
  233. package/src/lib/primitives/geometry/Polyline.test.ts +0 -5
@@ -53,6 +53,7 @@ var import_constants = require("../constants");
53
53
  var import_domUtils = require("../exports/domUtils");
54
54
  var import_exportToSvg = require("../exports/exportToSvg");
55
55
  var import_getSvgAsImage = require("../exports/getSvgAsImage");
56
+ var import_editors = require("../globals/editors");
56
57
  var import_menus = require("../globals/menus");
57
58
  var import_time = require("../globals/time");
58
59
  var import_options = require("../options");
@@ -256,6 +257,10 @@ class Editor extends import_eventemitter3.default {
256
257
  if (!nextPageState) nextPageState = { ...prevPageState };
257
258
  nextPageState.editingShapeId = null;
258
259
  }
260
+ if (prevPageState.croppingShapeId && shapesNoLongerInPage.has(prevPageState.croppingShapeId)) {
261
+ if (!nextPageState) nextPageState = { ...prevPageState };
262
+ nextPageState.croppingShapeId = null;
263
+ }
259
264
  const hintingShapeIds = prevPageState.hintingShapeIds.filter(
260
265
  (id) => !shapesNoLongerInPage.has(id)
261
266
  );
@@ -498,10 +503,11 @@ class Editor extends import_eventemitter3.default {
498
503
  this.cancelDoubleClick();
499
504
  }
500
505
  if (prev?.selectedShapeIds !== next?.selectedShapeIds) {
506
+ const selectedShapeIds = new Set(next.selectedShapeIds);
501
507
  const filtered = next.selectedShapeIds.filter((id) => {
502
508
  let parentId = this.getShape(id)?.parentId;
503
509
  while ((0, import_tlschema.isShapeId)(parentId)) {
504
- if (next.selectedShapeIds.includes(parentId)) {
510
+ if (selectedShapeIds.has(parentId)) {
505
511
  return false;
506
512
  }
507
513
  parentId = this.getShape(parentId)?.parentId;
@@ -571,9 +577,11 @@ class Editor extends import_eventemitter3.default {
571
577
  this.on("tick", this._flushEventsForTick);
572
578
  this.on("mount", () => {
573
579
  this._isMounted.set(true);
580
+ (0, import_editors.registerMountedEditor)(this);
574
581
  });
575
582
  this.on("unmount", () => {
576
583
  this._isMounted.set(false);
584
+ (0, import_editors.unregisterMountedEditor)(this);
577
585
  });
578
586
  this.timers.requestAnimationFrame(() => {
579
587
  this._tickManager.start();
@@ -583,7 +591,10 @@ class Editor extends import_eventemitter3.default {
583
591
  const mode = this.store.props.collaboration.mode;
584
592
  this.disposables.add(
585
593
  (0, import_state.react)("update collaboration mode", () => {
586
- this.store.put([{ ...this.getInstanceState(), isReadonly: mode.get() === "readonly" }]);
594
+ const isReadonly = mode.get() === "readonly";
595
+ (0, import_state.unsafe__withoutCapture)(
596
+ () => this._updateInstanceState({ isReadonly }, { history: "ignore" })
597
+ );
587
598
  })
588
599
  );
589
600
  }
@@ -613,6 +624,14 @@ class Editor extends import_eventemitter3.default {
613
624
  );
614
625
  }
615
626
  options;
627
+ /**
628
+ * The license manager whose feature flags apply to this editor. Assigned by `<TldrawEditor />`
629
+ * when it creates the editor, so that UI rendered outside the editor tree can resolve the
630
+ * editor's license through the editor instance. Undefined for editors created directly.
631
+ *
632
+ * @internal
633
+ */
634
+ licenseManager;
616
635
  contextId = (0, import_utils.uniqueId)();
617
636
  /**
618
637
  * The editor's store
@@ -807,6 +826,7 @@ class Editor extends import_eventemitter3.default {
807
826
  if (this._isMounted.get()) {
808
827
  this.emit("unmount");
809
828
  }
829
+ (0, import_editors.unregisterMountedEditor)(this);
810
830
  this.stopCameraAnimation();
811
831
  if (this.getInstanceState().followingUserId) {
812
832
  this.stopFollowingUser();
@@ -932,7 +952,7 @@ class Editor extends import_eventemitter3.default {
932
952
  getShapeUtil(arg) {
933
953
  const type = typeof arg === "string" ? arg : arg.type;
934
954
  const shapeUtil = (0, import_utils.getOwnProperty)(this.shapeUtils, type);
935
- (0, import_utils.assert)(shapeUtil, `No shape util found for type "${type}"`);
955
+ if (!shapeUtil) throw new Error(`No shape util found for type "${type}"`);
936
956
  return shapeUtil;
937
957
  }
938
958
  hasShapeUtil(arg) {
@@ -1721,7 +1741,9 @@ class Editor extends import_eventemitter3.default {
1721
1741
  const selectedShapes = this.getSelectedShapes();
1722
1742
  if (!selectedShapes.length) return;
1723
1743
  const selectedShape = selectedShapes[0];
1724
- const children = this.getSortedChildIdsForParent(selectedShape.id).map((id) => this.getShape(id)).filter((i) => i);
1744
+ const children = (0, import_utils.compact)(
1745
+ this.getSortedChildIdsForParent(selectedShape.id).map((id) => this.getShape(id))
1746
+ );
1725
1747
  const sortedChildren = this._getShapesInReadingOrder(children);
1726
1748
  if (sortedChildren.length === 0) return;
1727
1749
  this._selectShapesAndZoom([sortedChildren[0].id]);
@@ -1788,18 +1810,13 @@ class Editor extends import_eventemitter3.default {
1788
1810
  * @internal
1789
1811
  */
1790
1812
  getShapesSharedRotation(shapeIds) {
1791
- let foundFirst = false;
1792
1813
  let rotation = 0;
1793
1814
  for (let i = 0, n = shapeIds.length; i < n; i++) {
1794
- const pageTransform = this.getShapePageTransform(shapeIds[i]);
1795
- if (!pageTransform) continue;
1796
- if (foundFirst) {
1797
- if (pageTransform.rotation() !== rotation) {
1798
- return 0;
1799
- }
1800
- } else {
1801
- foundFirst = true;
1802
- rotation = pageTransform.rotation();
1815
+ const pageRotation = this.getShapePageTransform(shapeIds[i]).rotation();
1816
+ if (i === 0) {
1817
+ rotation = pageRotation;
1818
+ } else if (pageRotation !== rotation) {
1819
+ return 0;
1803
1820
  }
1804
1821
  }
1805
1822
  return rotation;
@@ -1825,11 +1842,9 @@ class Editor extends import_eventemitter3.default {
1825
1842
  return bounds;
1826
1843
  }
1827
1844
  const boxFromRotatedVertices = import_Box.Box.FromPoints(
1828
- shapeIds.flatMap((id) => {
1829
- const pageTransform = this.getShapePageTransform(id);
1830
- if (!pageTransform) return [];
1831
- return pageTransform.applyToPoints(this.getShapeGeometry(id).bounds.corners);
1832
- }).map((p) => p.rot(-selectionRotation))
1845
+ shapeIds.flatMap(
1846
+ (id) => this.getShapePageTransform(id).applyToPoints(this.getShapeGeometry(id).bounds.corners)
1847
+ ).map((p) => p.rot(-selectionRotation))
1833
1848
  );
1834
1849
  boxFromRotatedVertices.point = boxFromRotatedVertices.point.rot(selectionRotation);
1835
1850
  return boxFromRotatedVertices;
@@ -1840,9 +1855,14 @@ class Editor extends import_eventemitter3.default {
1840
1855
  getSelectionRotatedScreenBounds() {
1841
1856
  const bounds = this.getSelectionRotatedPageBounds();
1842
1857
  if (!bounds) return void 0;
1843
- const { x, y } = this.pageToScreen(bounds.point);
1844
- const zoom = this.getZoomLevel();
1845
- return new import_Box.Box(x, y, bounds.width * zoom, bounds.height * zoom);
1858
+ const screenBounds = this.getViewportScreenBounds();
1859
+ const { x: cx, y: cy, z: zoom } = this.getCamera();
1860
+ return new import_Box.Box(
1861
+ (bounds.x + cx) * zoom + screenBounds.x,
1862
+ (bounds.y + cy) * zoom + screenBounds.y,
1863
+ bounds.width * zoom,
1864
+ bounds.height * zoom
1865
+ );
1846
1866
  }
1847
1867
  getFocusedGroupId() {
1848
1868
  return this.getCurrentPageState().focusedGroupId ?? this.getCurrentPageId();
@@ -1943,42 +1963,28 @@ class Editor extends import_eventemitter3.default {
1943
1963
  */
1944
1964
  setEditingShape(shape) {
1945
1965
  const id = typeof shape === "string" ? shape : shape?.id ?? null;
1946
- if (!id) {
1966
+ if (id && !this.canEditShape(id)) return this;
1967
+ this.run(() => {
1968
+ const prevEditingShapeId = this.getEditingShapeId();
1969
+ if (prevEditingShapeId) {
1970
+ const prevEditingShape = this.getShape(prevEditingShapeId);
1971
+ if (prevEditingShape) {
1972
+ this.getShapeUtil(prevEditingShape).onEditEnd?.(prevEditingShape);
1973
+ }
1974
+ }
1947
1975
  this.run(
1948
1976
  () => {
1949
- const prevEditingShapeId = this.getEditingShapeId();
1950
- if (prevEditingShapeId) {
1951
- const prevEditingShape = this.getShape(prevEditingShapeId);
1952
- if (prevEditingShape) {
1953
- this.getShapeUtil(prevEditingShape).onEditEnd?.(prevEditingShape);
1954
- }
1955
- }
1956
1977
  this._updateCurrentPageState({ editingShapeId: null });
1957
1978
  this._currentRichTextEditor.set(null);
1979
+ if (!id) return;
1980
+ this.select(id);
1981
+ this._updateCurrentPageState({ editingShapeId: id });
1982
+ const nextEditingShape = this.getShape(id);
1983
+ this.getShapeUtil(nextEditingShape).onEditStart?.(nextEditingShape);
1958
1984
  },
1959
1985
  { history: "ignore" }
1960
1986
  );
1961
- return this;
1962
- }
1963
- if (!this.canEditShape(id)) return this;
1964
- this.run(
1965
- () => {
1966
- const prevEditingShapeId = this.getEditingShapeId();
1967
- if (prevEditingShapeId) {
1968
- const prevEditingShape = this.getShape(prevEditingShapeId);
1969
- if (prevEditingShape) {
1970
- this.getShapeUtil(prevEditingShape).onEditEnd?.(prevEditingShape);
1971
- }
1972
- }
1973
- this._updateCurrentPageState({ editingShapeId: null });
1974
- this._currentRichTextEditor.set(null);
1975
- this.select(id);
1976
- this._updateCurrentPageState({ editingShapeId: id });
1977
- const nextEditingShape = this.getShape(id);
1978
- this.getShapeUtil(nextEditingShape).onEditStart?.(nextEditingShape);
1979
- },
1980
- { history: "ignore" }
1981
- );
1987
+ });
1982
1988
  return this;
1983
1989
  }
1984
1990
  // Rich text editor
@@ -2084,19 +2090,12 @@ class Editor extends import_eventemitter3.default {
2084
2090
  * @public
2085
2091
  */
2086
2092
  setErasingShapes(shapes) {
2087
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((shape) => shape.id);
2093
+ const ids = typeof shapes[0] === "string" ? shapes.slice() : shapes.map((shape) => shape.id);
2088
2094
  ids.sort();
2089
2095
  const erasingShapeIds = this.getErasingShapeIds();
2090
2096
  this.run(
2091
2097
  () => {
2092
- if (ids.length === erasingShapeIds.length) {
2093
- for (let i = 0; i < ids.length; i++) {
2094
- if (ids[i] !== erasingShapeIds[i]) {
2095
- this._updateCurrentPageState({ erasingShapeIds: ids });
2096
- break;
2097
- }
2098
- }
2099
- } else {
2098
+ if (!(0, import_utils.areArraysShallowEqual)(ids, erasingShapeIds)) {
2100
2099
  this._updateCurrentPageState({ erasingShapeIds: ids });
2101
2100
  }
2102
2101
  },
@@ -2129,6 +2128,7 @@ class Editor extends import_eventemitter3.default {
2129
2128
  if (!_shape) return false;
2130
2129
  const util = this.getShapeUtil(_shape);
2131
2130
  if (!util.canCrop(_shape)) return false;
2131
+ if (this.getIsReadonly()) return false;
2132
2132
  if (this.isShapeOrAncestorLocked(_shape)) return false;
2133
2133
  return true;
2134
2134
  }
@@ -2193,11 +2193,11 @@ class Editor extends import_eventemitter3.default {
2193
2193
  const collaborators = this.getCollaborators();
2194
2194
  let leaderPresence = null;
2195
2195
  while (targetUserId && !visited.includes(targetUserId)) {
2196
- leaderPresence = collaborators.find((c) => c.userId === targetUserId) ?? null;
2197
- targetUserId = leaderPresence?.followingUserId ?? null;
2198
- if (leaderPresence) {
2199
- visited.push(leaderPresence.userId);
2200
- }
2196
+ const nextPresence = collaborators.find((c) => c.userId === targetUserId);
2197
+ if (!nextPresence) break;
2198
+ leaderPresence = nextPresence;
2199
+ targetUserId = nextPresence.followingUserId ?? null;
2200
+ visited.push(nextPresence.userId);
2201
2201
  }
2202
2202
  return leaderPresence;
2203
2203
  }
@@ -2260,39 +2260,7 @@ class Editor extends import_eventemitter3.default {
2260
2260
  *
2261
2261
  * @public */
2262
2262
  getInitialZoom() {
2263
- const cameraOptions = this.getCameraOptions();
2264
- if (!cameraOptions.constraints) return 1;
2265
- if (cameraOptions.constraints.initialZoom === "default") return 1;
2266
- const { zx, zy } = getCameraFitXFitY(this, cameraOptions);
2267
- switch (cameraOptions.constraints.initialZoom) {
2268
- case "fit-min": {
2269
- return Math.max(zx, zy);
2270
- }
2271
- case "fit-max": {
2272
- return Math.min(zx, zy);
2273
- }
2274
- case "fit-x": {
2275
- return zx;
2276
- }
2277
- case "fit-y": {
2278
- return zy;
2279
- }
2280
- case "fit-min-100": {
2281
- return Math.min(1, Math.max(zx, zy));
2282
- }
2283
- case "fit-max-100": {
2284
- return Math.min(1, Math.min(zx, zy));
2285
- }
2286
- case "fit-x-100": {
2287
- return Math.min(1, zx);
2288
- }
2289
- case "fit-y-100": {
2290
- return Math.min(1, zy);
2291
- }
2292
- default: {
2293
- throw (0, import_utils.exhaustiveSwitchError)(cameraOptions.constraints.initialZoom);
2294
- }
2295
- }
2263
+ return this._getFitZoom(this.getCameraOptions().constraints?.initialZoom ?? "default");
2296
2264
  }
2297
2265
  /**
2298
2266
  * Get the camera's base level for calculating actual zoom levels based on the zoom steps.
@@ -2304,11 +2272,13 @@ class Editor extends import_eventemitter3.default {
2304
2272
  *
2305
2273
  * @public */
2306
2274
  getBaseZoom() {
2275
+ return this._getFitZoom(this.getCameraOptions().constraints?.baseZoom ?? "default");
2276
+ }
2277
+ _getFitZoom(fit) {
2307
2278
  const cameraOptions = this.getCameraOptions();
2308
- if (!cameraOptions.constraints) return 1;
2309
- if (cameraOptions.constraints.baseZoom === "default") return 1;
2279
+ if (!cameraOptions.constraints || fit === "default") return 1;
2310
2280
  const { zx, zy } = getCameraFitXFitY(this, cameraOptions);
2311
- switch (cameraOptions.constraints.baseZoom) {
2281
+ switch (fit) {
2312
2282
  case "fit-min": {
2313
2283
  return Math.max(zx, zy);
2314
2284
  }
@@ -2334,7 +2304,7 @@ class Editor extends import_eventemitter3.default {
2334
2304
  return Math.min(1, zy);
2335
2305
  }
2336
2306
  default: {
2337
- throw (0, import_utils.exhaustiveSwitchError)(cameraOptions.constraints.baseZoom);
2307
+ throw (0, import_utils.exhaustiveSwitchError)(fit);
2338
2308
  }
2339
2309
  }
2340
2310
  }
@@ -2368,7 +2338,7 @@ class Editor extends import_eventemitter3.default {
2368
2338
  ...this._cameraOptions.__unsafe__getWithoutCapture(),
2369
2339
  ...opts
2370
2340
  });
2371
- if (next.zoomSteps?.length < 1) next.zoomSteps = [1];
2341
+ if (!next.zoomSteps || next.zoomSteps.length < 1) next.zoomSteps = [1];
2372
2342
  this._cameraOptions.set(next);
2373
2343
  this.setCamera(this.getCamera());
2374
2344
  return this;
@@ -2389,8 +2359,8 @@ class Editor extends import_eventemitter3.default {
2389
2359
  const vsb = this.getViewportScreenBounds();
2390
2360
  if (cameraOptions.constraints) {
2391
2361
  const { constraints } = cameraOptions;
2392
- const py = Math.min(constraints.padding.y, vsb.w / 2);
2393
- const px = Math.min(constraints.padding.x, vsb.h / 2);
2362
+ const px = Math.min(constraints.padding.x, vsb.w / 2);
2363
+ const py = Math.min(constraints.padding.y, vsb.h / 2);
2394
2364
  const bounds = import_Box.Box.From(cameraOptions.constraints.bounds);
2395
2365
  const zx = (vsb.w - px * 2) / bounds.w;
2396
2366
  const zy = (vsb.h - py * 2) / bounds.h;
@@ -2429,12 +2399,12 @@ class Editor extends import_eventemitter3.default {
2429
2399
  break;
2430
2400
  }
2431
2401
  case "inside": {
2432
- if (z < zx) x = (0, import_utils2.clamp)(x, minX, (vsb.w - px) / z - bounds.w);
2402
+ if (z < zx) x = (0, import_utils2.clamp)(x, minX, (vsb.w - px) / z - bounds.w - bounds.x);
2433
2403
  else x = (0, import_utils2.clamp)(x, minX + freeW, minX);
2434
2404
  break;
2435
2405
  }
2436
2406
  case "outside": {
2437
- x = (0, import_utils2.clamp)(x, px / z - bounds.w, (vsb.w - px) / z);
2407
+ x = (0, import_utils2.clamp)(x, px / z - bounds.w - bounds.x, (vsb.w - px) / z - bounds.x);
2438
2408
  break;
2439
2409
  }
2440
2410
  case "free": {
@@ -2455,12 +2425,12 @@ class Editor extends import_eventemitter3.default {
2455
2425
  break;
2456
2426
  }
2457
2427
  case "inside": {
2458
- if (z < zy) y = (0, import_utils2.clamp)(y, minY, (vsb.h - py) / z - bounds.h);
2428
+ if (z < zy) y = (0, import_utils2.clamp)(y, minY, (vsb.h - py) / z - bounds.h - bounds.y);
2459
2429
  else y = (0, import_utils2.clamp)(y, minY + freeH, minY);
2460
2430
  break;
2461
2431
  }
2462
2432
  case "outside": {
2463
- y = (0, import_utils2.clamp)(y, py / z - bounds.h, (vsb.h - py) / z);
2433
+ y = (0, import_utils2.clamp)(y, py / z - bounds.h - bounds.y, (vsb.h - py) / z - bounds.y);
2464
2434
  break;
2465
2435
  }
2466
2436
  case "free": {
@@ -2828,9 +2798,9 @@ class Editor extends import_eventemitter3.default {
2828
2798
  }
2829
2799
  const remaining = duration - elapsed;
2830
2800
  const t = easing(1 - remaining / duration);
2831
- const left = start.minX + (end.minX - start.minX) * t;
2832
- const top = start.minY + (end.minY - start.minY) * t;
2833
- const right = start.maxX + (end.maxX - start.maxX) * t;
2801
+ const left = (0, import_utils.lerp)(start.minX, end.minX, t);
2802
+ const top = (0, import_utils.lerp)(start.minY, end.minY, t);
2803
+ const right = (0, import_utils.lerp)(start.maxX, end.maxX, t);
2834
2804
  this._setCamera(new import_Vec.Vec(-left, -top, this.getViewportScreenBounds().width / (right - left)), {
2835
2805
  force: true
2836
2806
  });
@@ -2908,7 +2878,7 @@ class Editor extends import_eventemitter3.default {
2908
2878
  let newCy = cy + dy;
2909
2879
  let newCz = cz;
2910
2880
  if (dirZ !== 0) {
2911
- newCz = cz * (1 + dirZ * currentSpeed * elapsed);
2881
+ newCz = cz * Math.exp(dirZ * currentSpeed * elapsed);
2912
2882
  const center = this.getViewportScreenCenter();
2913
2883
  newCx += center.x / newCz - center.x / cz;
2914
2884
  newCy += center.y / newCz - center.y / cz;
@@ -3346,12 +3316,12 @@ class Editor extends import_eventemitter3.default {
3346
3316
  const renderingShapes = [];
3347
3317
  let nextIndex = this.options.maxShapesPerPage * 2;
3348
3318
  let nextBackgroundIndex = this.options.maxShapesPerPage;
3349
- const erasingShapeIds = this.getErasingShapeIds();
3319
+ const erasingShapeIds = new Set(this.getErasingShapeIds());
3350
3320
  const addShapeById = (id, opacity, isAncestorErasing) => {
3351
3321
  const shape = this.getShape(id);
3352
3322
  if (!shape) return;
3353
3323
  if (this.isShapeHidden(shape)) {
3354
- const isErasing = isAncestorErasing || erasingShapeIds.includes(id);
3324
+ const isErasing = isAncestorErasing || erasingShapeIds.has(id);
3355
3325
  for (const childId of this.getSortedChildIdsForParent(id)) {
3356
3326
  addShapeById(childId, opacity, isErasing);
3357
3327
  }
@@ -3361,7 +3331,7 @@ class Editor extends import_eventemitter3.default {
3361
3331
  let isShapeErasing = false;
3362
3332
  const util = this.getShapeUtil(shape);
3363
3333
  if (useEditorState) {
3364
- isShapeErasing = !isAncestorErasing && erasingShapeIds.includes(id);
3334
+ isShapeErasing = !isAncestorErasing && erasingShapeIds.has(id);
3365
3335
  if (isShapeErasing) {
3366
3336
  opacity *= 0.32;
3367
3337
  }
@@ -3953,10 +3923,8 @@ class Editor extends import_eventemitter3.default {
3953
3923
  }
3954
3924
  _getShapePageBoundsCache() {
3955
3925
  return this.store.createComputedCache("pageBoundsCache", (shape) => {
3956
- const pageTransform = this.getShapePageTransform(shape);
3957
- if (!pageTransform) return void 0;
3958
3926
  return import_Box.Box.FromPoints(
3959
- pageTransform.applyToPoints(this.getShapeGeometry(shape).boundsVertices)
3927
+ this.getShapePageTransform(shape).applyToPoints(this.getShapeGeometry(shape).boundsVertices)
3960
3928
  );
3961
3929
  });
3962
3930
  }
@@ -4074,7 +4042,9 @@ class Editor extends import_eventemitter3.default {
4074
4042
  if (pageMask) {
4075
4043
  if (pageMask.length === 0) return void 0;
4076
4044
  const { corners } = pageBounds;
4077
- if (corners.every((p, i) => p && import_Vec.Vec.Equals(p, pageMask[i]))) return pageBounds.clone();
4045
+ if (pageMask.length === corners.length && corners.every((p, i) => import_Vec.Vec.Equals(p, pageMask[i]))) {
4046
+ return pageBounds.clone();
4047
+ }
4078
4048
  const intersection = (0, import_intersect.intersectPolygonPolygon)(pageMask, corners);
4079
4049
  if (!intersection) return;
4080
4050
  return import_Box.Box.FromPoints(intersection);
@@ -4313,7 +4283,7 @@ class Editor extends import_eventemitter3.default {
4313
4283
  const pointInShapeSpace = this.getPointInShapeSpace(shape, point);
4314
4284
  const shapeUtil = this.getShapeUtil(shape);
4315
4285
  const isShapeFrameLike = this.isShapeFrameLike(shape);
4316
- if (isShapeFrameLike || (this.isShapeOfType(shape, "note") || this.isShapeOfType(shape, "arrow") || this.isShapeOfType(shape, "geo") && shape.props.fill === "none") && shapeUtil.getText(shape)?.trim()) {
4286
+ if (isGroup && (isShapeFrameLike || (this.isShapeOfType(shape, "note") || this.isShapeOfType(shape, "arrow") || this.isShapeOfType(shape, "geo") && shape.props.fill === "none") && shapeUtil.getText(shape)?.trim())) {
4317
4287
  for (const childGeometry of geometry.children) {
4318
4288
  if (childGeometry.isLabel && childGeometry.isPointInBounds(pointInShapeSpace)) {
4319
4289
  return shape;
@@ -4505,9 +4475,7 @@ class Editor extends import_eventemitter3.default {
4505
4475
  const freshShape = this.getShape(id);
4506
4476
  if (!freshShape) return new import_Vec.Vec(0, 0);
4507
4477
  if ((0, import_tlschema.isPageId)(freshShape.parentId)) return import_Vec.Vec.From(point);
4508
- const parentTransform = this.getShapePageTransform(freshShape.parentId);
4509
- if (!parentTransform) return import_Vec.Vec.From(point);
4510
- return parentTransform.clone().invert().applyToPoint(point);
4478
+ return this.getShapePageTransform(freshShape.parentId).clone().invert().applyToPoint(point);
4511
4479
  }
4512
4480
  getCurrentPageShapes() {
4513
4481
  return Array.from(this.getCurrentPageShapeIds(), (id) => this.store.get(id));
@@ -4619,23 +4587,7 @@ class Editor extends import_eventemitter3.default {
4619
4587
  * @public
4620
4588
  */
4621
4589
  isShapeInPage(shape, pageId = this.getCurrentPageId()) {
4622
- const id = typeof shape === "string" ? shape : shape.id;
4623
- const shapeToCheck = this.getShape(id);
4624
- if (!shapeToCheck) return false;
4625
- let shapeIsInPage = false;
4626
- if (shapeToCheck.parentId === pageId) {
4627
- shapeIsInPage = true;
4628
- } else {
4629
- let parent = this.getShape(shapeToCheck.parentId);
4630
- isInPageSearch: while (parent) {
4631
- if (parent.parentId === pageId) {
4632
- shapeIsInPage = true;
4633
- break isInPageSearch;
4634
- }
4635
- parent = this.getShape(parent.parentId);
4636
- }
4637
- }
4638
- return shapeIsInPage;
4590
+ return this.getAncestorPageId(shape) === pageId;
4639
4591
  }
4640
4592
  /**
4641
4593
  * Get the id of the containing page for a given shape.
@@ -4716,10 +4668,7 @@ class Editor extends import_eventemitter3.default {
4716
4668
  for (let i = 0; i < shapesToReparent.length; i++) {
4717
4669
  const shape = shapesToReparent[i];
4718
4670
  const pageTransform = this.getShapePageTransform(shape);
4719
- if (!pageTransform) continue;
4720
- const pagePoint = pageTransform.point();
4721
- if (!pagePoint) continue;
4722
- const newPoint = invertedParentTransform.applyToPoint(pagePoint);
4671
+ const newPoint = invertedParentTransform.applyToPoint(pageTransform.point());
4723
4672
  const newRotation = pageTransform.rotation() - parentPageRotation;
4724
4673
  if (shape.id === parentId) {
4725
4674
  throw Error("Attempted to reparent a shape to itself!");
@@ -4808,7 +4757,7 @@ class Editor extends import_eventemitter3.default {
4808
4757
  */
4809
4758
  getShapeAndDescendantIds(ids) {
4810
4759
  const shapeIds = /* @__PURE__ */ new Set();
4811
- for (const shape of ids.map((id) => this.getShape(id)).sort(import_utils.sortByIndex)) {
4760
+ for (const shape of (0, import_utils.compact)(ids.map((id) => this.getShape(id))).sort(import_utils.sortByIndex)) {
4812
4761
  shapeIds.add(shape.id);
4813
4762
  this.visitDescendants(shape, (descendantId) => {
4814
4763
  shapeIds.add(descendantId);
@@ -4830,11 +4779,12 @@ class Editor extends import_eventemitter3.default {
4830
4779
  const draggingShapes = (0, import_utils.compact)(droppingShapes.map((s) => this.getShape(s))).filter(
4831
4780
  (s) => !s.isLocked && !this.isShapeHidden(s)
4832
4781
  );
4782
+ const excludedIds = this.getShapeAndDescendantIds(draggingShapes.map((s) => s.id));
4833
4783
  const maybeDraggingOverShapes = this.getShapesAtPoint(point, {
4834
4784
  hitInside: true,
4835
4785
  margin: 0
4836
4786
  }).filter(
4837
- (s) => !droppingShapes.includes(s) && !s.isLocked && !this.isShapeHidden(s) && !draggingShapes.includes(s)
4787
+ (s) => !droppingShapes.includes(s) && !s.isLocked && !this.isShapeHidden(s) && !excludedIds.has(s.id)
4838
4788
  );
4839
4789
  for (const maybeDraggingOverShape of maybeDraggingOverShapes) {
4840
4790
  const shapeUtil = this.getShapeUtil(maybeDraggingOverShape);
@@ -5087,8 +5037,7 @@ class Editor extends import_eventemitter3.default {
5087
5037
  for (const id of ids) {
5088
5038
  const shape = this.getShape(id);
5089
5039
  const localDelta = import_Vec.Vec.From(offset);
5090
- const parentTransform = this.getShapeParentTransform(shape);
5091
- if (parentTransform) localDelta.rot(-parentTransform.rotation());
5040
+ localDelta.rot(-this.getShapeParentTransform(shape).rotation());
5092
5041
  changes.push(this.getChangesToTranslateShape(shape, localDelta.add(shape)));
5093
5042
  }
5094
5043
  this.updateShapes(changes);
@@ -5143,7 +5092,7 @@ class Editor extends import_eventemitter3.default {
5143
5092
  if (!originalShape) continue;
5144
5093
  let ox = 0;
5145
5094
  let oy = 0;
5146
- if (offset && initialIds.has(originalId)) {
5095
+ if (offset && initialIds.has(originalId) && !shapeIdSet.has(originalShape.parentId)) {
5147
5096
  const parentTransform = this.getShapeParentTransform(originalShape);
5148
5097
  const vec = new import_Vec.Vec(offset.x, offset.y).rot(-parentTransform.rotation());
5149
5098
  ox = vec.x;
@@ -5257,26 +5206,17 @@ class Editor extends import_eventemitter3.default {
5257
5206
  toggleLock(shapes) {
5258
5207
  const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
5259
5208
  if (this.getIsReadonly() || ids.length === 0) return this;
5260
- let allLocked = true, allUnlocked = true;
5209
+ let allLocked = true;
5261
5210
  const shapesToToggle = [];
5262
5211
  for (const id of ids) {
5263
5212
  const shape = this.getShape(id);
5264
5213
  if (shape) {
5265
5214
  shapesToToggle.push(shape);
5266
- if (shape.isLocked) {
5267
- allUnlocked = false;
5268
- } else {
5269
- allLocked = false;
5270
- }
5215
+ if (!shape.isLocked) allLocked = false;
5271
5216
  }
5272
5217
  }
5273
5218
  this.run(() => {
5274
- if (allUnlocked) {
5275
- this.updateShapes(
5276
- shapesToToggle.map((shape) => ({ id: shape.id, type: shape.type, isLocked: true }))
5277
- );
5278
- this.setSelectedShapes([]);
5279
- } else if (allLocked) {
5219
+ if (allLocked) {
5280
5220
  this.updateShapes(
5281
5221
  shapesToToggle.map((shape) => ({ id: shape.id, type: shape.type, isLocked: false }))
5282
5222
  );
@@ -5284,6 +5224,7 @@ class Editor extends import_eventemitter3.default {
5284
5224
  this.updateShapes(
5285
5225
  shapesToToggle.map((shape) => ({ id: shape.id, type: shape.type, isLocked: true }))
5286
5226
  );
5227
+ this.setSelectedShapes([]);
5287
5228
  }
5288
5229
  });
5289
5230
  return this;
@@ -5394,9 +5335,10 @@ class Editor extends import_eventemitter3.default {
5394
5335
  const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
5395
5336
  let freshShapes = (0, import_utils.compact)(ids.map((id) => this.getShape(id)));
5396
5337
  if (opts?.filterAxisAligned) {
5397
- freshShapes = freshShapes.filter(
5398
- (s) => this.getShapePageTransform(s)?.rotation() % (import_utils2.PI / 2) === 0
5399
- );
5338
+ freshShapes = freshShapes.filter((s) => {
5339
+ const remainder = Math.abs(this.getShapePageTransform(s).rotation() % import_utils2.HALF_PI);
5340
+ return Math.min(remainder, import_utils2.HALF_PI - remainder) < 1e-9;
5341
+ });
5400
5342
  }
5401
5343
  const clusters = [];
5402
5344
  const allBounds = [];
@@ -5561,9 +5503,9 @@ class Editor extends import_eventemitter3.default {
5561
5503
  dim = "height";
5562
5504
  }
5563
5505
  let shapeGap = 0;
5506
+ shapeClustersToStack.sort((a, b) => a.pageBounds[min] - b.pageBounds[min]);
5564
5507
  if (_gap === 0) {
5565
5508
  const gaps = {};
5566
- shapeClustersToStack.sort((a, b) => a.pageBounds[min] - b.pageBounds[min]);
5567
5509
  for (let i = 0; i < len - 1; i++) {
5568
5510
  const currCluster = shapeClustersToStack[i];
5569
5511
  const nextCluster = shapeClustersToStack[i + 1];
@@ -5601,8 +5543,7 @@ class Editor extends import_eventemitter3.default {
5601
5543
  const shapeDelta = delta.clone();
5602
5544
  const parent = this.getShapeParent(shape);
5603
5545
  if (parent) {
5604
- const parentTransform = this.getShapePageTransform(parent);
5605
- if (parentTransform) shapeDelta.rot(-parentTransform.rotation());
5546
+ shapeDelta.rot(-this.getShapePageTransform(parent).rotation());
5606
5547
  }
5607
5548
  shapeDelta.add(shape);
5608
5549
  changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
@@ -5640,7 +5581,7 @@ class Editor extends import_eventemitter3.default {
5640
5581
  }
5641
5582
  const commonBounds = import_Box.Box.Common(allBounds);
5642
5583
  const maxWidth = commonBounds.width;
5643
- shapeClustersToPack.sort((a, b) => a.pageBounds.width - b.pageBounds.width).sort((a, b) => a.pageBounds.height - b.pageBounds.height);
5584
+ shapeClustersToPack.sort((a, b) => b.pageBounds.width - a.pageBounds.width).sort((a, b) => b.pageBounds.height - a.pageBounds.height);
5644
5585
  const startWidth = Math.max(Math.ceil(Math.sqrt(area / 0.95)), maxWidth);
5645
5586
  const spaces = [new import_Box.Box(commonBounds.x, commonBounds.y, startWidth, Infinity)];
5646
5587
  let width = 0;
@@ -5688,8 +5629,7 @@ class Editor extends import_eventemitter3.default {
5688
5629
  const shapeDelta = delta.clone();
5689
5630
  const parent = this.getShapeParent(shape);
5690
5631
  if (parent) {
5691
- const parentTransform = this.getShapeParentTransform(shape);
5692
- if (parentTransform) shapeDelta.rot(-parentTransform.rotation());
5632
+ shapeDelta.rot(-this.getShapeParentTransform(shape).rotation());
5693
5633
  }
5694
5634
  shapeDelta.add(shape);
5695
5635
  changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
@@ -5755,8 +5695,7 @@ class Editor extends import_eventemitter3.default {
5755
5695
  const shapeDelta = delta.clone();
5756
5696
  const parent = this.getShapeParent(shape);
5757
5697
  if (parent) {
5758
- const parentTransform = this.getShapePageTransform(parent);
5759
- if (parentTransform) shapeDelta.rot(-parentTransform.rotation());
5698
+ shapeDelta.rot(-this.getShapePageTransform(parent).rotation());
5760
5699
  }
5761
5700
  shapeDelta.add(shape);
5762
5701
  changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
@@ -5830,8 +5769,7 @@ class Editor extends import_eventemitter3.default {
5830
5769
  const shapeDelta = delta.clone();
5831
5770
  const parent = this.getShapeParent(shape);
5832
5771
  if (parent) {
5833
- const parentTransform = this.getShapePageTransform(parent);
5834
- if (parentTransform) shapeDelta.rot(-parentTransform.rotation());
5772
+ shapeDelta.rot(-this.getShapePageTransform(parent).rotation());
5835
5773
  }
5836
5774
  shapeDelta.add(shape);
5837
5775
  changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
@@ -5886,8 +5824,7 @@ class Editor extends import_eventemitter3.default {
5886
5824
  scale[val] = commonBounds[dim] / pageBounds[dim];
5887
5825
  for (const shape of shapes2) {
5888
5826
  const shapeLocalOffset = localOffset.clone();
5889
- const parentTransform = this.getShapeParentTransform(shape);
5890
- if (parentTransform) shapeLocalOffset.rot(-parentTransform.rotation());
5827
+ shapeLocalOffset.rot(-this.getShapeParentTransform(shape).rotation());
5891
5828
  shapeLocalOffset.add(shape);
5892
5829
  const changes = this.getChangesToTranslateShape(shape, shapeLocalOffset);
5893
5830
  this.updateShape(changes);
@@ -5943,8 +5880,7 @@ class Editor extends import_eventemitter3.default {
5943
5880
  );
5944
5881
  for (const shape of shapes2) {
5945
5882
  const shapeLocalOffset = localOffset.clone();
5946
- const parentTransform = this.getShapeParentTransform(shape);
5947
- if (parentTransform) shapeLocalOffset.rot(-parentTransform.rotation());
5883
+ shapeLocalOffset.rot(-this.getShapeParentTransform(shape).rotation());
5948
5884
  shapeLocalOffset.add(shape);
5949
5885
  const changes = this.getChangesToTranslateShape(shape, shapeLocalOffset);
5950
5886
  this.updateShape(changes);
@@ -5993,9 +5929,7 @@ class Editor extends import_eventemitter3.default {
5993
5929
  const scaleOrigin = opts.scaleOrigin ?? this.getShapePageBounds(id)?.center;
5994
5930
  if (!scaleOrigin) return null;
5995
5931
  const pageTransform = opts.initialPageTransform ? import_Mat.Mat.Cast(opts.initialPageTransform) : this.getShapePageTransform(id);
5996
- if (!pageTransform) return null;
5997
5932
  const pageRotation = pageTransform.rotation();
5998
- if (pageRotation == null) return null;
5999
5933
  const scaleAxisRotation = opts.scaleAxisRotation ?? pageRotation;
6000
5934
  const initialBounds = opts.initialBounds ?? this.getShapeGeometry(id).bounds;
6001
5935
  if (!initialBounds) return null;
@@ -6146,7 +6080,6 @@ class Editor extends import_eventemitter3.default {
6146
6080
  const currentLocalBounds = this.getShapeGeometry(id).bounds;
6147
6081
  const currentPageCenter = import_Mat.Mat.applyToPoint(pageTransform, currentLocalBounds.center);
6148
6082
  const shapePageTransformOrigin = pageTransform.point();
6149
- if (!currentPageCenter || !shapePageTransformOrigin) return this;
6150
6083
  const pageDelta = import_Vec.Vec.Sub(postScaleShapePageCenter, currentPageCenter);
6151
6084
  const postScaleShapePagePoint = import_Vec.Vec.Add(shapePageTransformOrigin, pageDelta);
6152
6085
  const { x, y } = this.getPointInParentSpace(id, postScaleShapePagePoint);
@@ -6405,10 +6338,10 @@ class Editor extends import_eventemitter3.default {
6405
6338
  if (animationIdForShape !== animationId) continue;
6406
6339
  updates.push({
6407
6340
  ...end,
6408
- x: start.x + (end.x - start.x) * t,
6409
- y: start.y + (end.y - start.y) * t,
6410
- opacity: start.opacity + (end.opacity - start.opacity) * t,
6411
- rotation: start.rotation + (end.rotation - start.rotation) * t,
6341
+ x: (0, import_utils.lerp)(start.x, end.x, t),
6342
+ y: (0, import_utils.lerp)(start.y, end.y, t),
6343
+ opacity: (0, import_utils.lerp)(start.opacity, end.opacity, t),
6344
+ rotation: (0, import_utils.lerp)(start.rotation, end.rotation, t),
6412
6345
  props: this.getShapeUtil(end).getInterpolatedProps?.(start, end, t) ?? end.props
6413
6346
  });
6414
6347
  }
@@ -6534,7 +6467,7 @@ class Editor extends import_eventemitter3.default {
6534
6467
  * @public
6535
6468
  */
6536
6469
  updateShapes(partials) {
6537
- const compactedPartials = Array(partials.length);
6470
+ const compactedPartials = [];
6538
6471
  for (let i = 0, n = partials.length; i < n; i++) {
6539
6472
  const partial = partials[i];
6540
6473
  if (!partial) continue;
@@ -6605,28 +6538,32 @@ class Editor extends import_eventemitter3.default {
6605
6538
  }
6606
6539
  /* --------------------- Styles --------------------- */
6607
6540
  /**
6608
- * Get all the current styles among the users selected shapes
6541
+ * Groups have no styles of their own: a style read or write on a selection applies to the
6542
+ * non-group shapes beneath each group, however deeply nested. Returns those shapes.
6609
6543
  *
6610
6544
  * @internal
6611
6545
  */
6612
- _extractSharedStyles(shape, sharedStyleMap) {
6613
- if (this.isShapeOfType(shape, "group")) {
6614
- const childIds = this._parentIdsToChildIds.get()[shape.id];
6615
- if (!childIds) return;
6616
- for (let i = 0, n = childIds.length; i < n; i++) {
6617
- this._extractSharedStyles(this.getShape(childIds[i]), sharedStyleMap);
6618
- }
6619
- } else {
6620
- for (const [style, propKey] of this.styleProps[shape.type]) {
6621
- sharedStyleMap.applyValue(style, (0, import_utils.getOwnProperty)(shape.props, propKey));
6546
+ _getStyleableShapes(shapes) {
6547
+ const result = [];
6548
+ const visit = (shape) => {
6549
+ if (this.isShapeOfType(shape, "group")) {
6550
+ for (const childId of this.getSortedChildIdsForParent(shape.id)) {
6551
+ const child = this.getShape(childId);
6552
+ if (child) visit(child);
6553
+ }
6554
+ } else {
6555
+ result.push(shape);
6622
6556
  }
6623
- }
6557
+ };
6558
+ for (const shape of shapes) visit(shape);
6559
+ return result;
6624
6560
  }
6625
6561
  _getSelectionSharedStyles() {
6626
- const selectedShapes = this.getSelectedShapes();
6627
6562
  const sharedStyles = new import_SharedStylesMap.SharedStyleMap();
6628
- for (const selectedShape of selectedShapes) {
6629
- this._extractSharedStyles(selectedShape, sharedStyles);
6563
+ for (const shape of this._getStyleableShapes(this.getSelectedShapes())) {
6564
+ for (const [style, propKey] of this.styleProps[shape.type]) {
6565
+ sharedStyles.applyValue(style, (0, import_utils.getOwnProperty)(shape.props, propKey));
6566
+ }
6630
6567
  }
6631
6568
  return sharedStyles;
6632
6569
  }
@@ -6658,38 +6595,16 @@ class Editor extends import_eventemitter3.default {
6658
6595
  const styles = new import_SharedStylesMap.SharedStyleMap();
6659
6596
  if (!currentTool) return styles;
6660
6597
  if (currentTool.shapeType) {
6661
- if (currentTool.shapeType === "frame" && !this.getShapeUtil("frame").options.showColors) {
6662
- for (const style of this.styleProps[currentTool.shapeType].keys()) {
6663
- if (style.id === "tldraw:color") continue;
6664
- styles.applyValue(style, this.getStyleForNextShape(style));
6665
- }
6666
- } else {
6667
- for (const style of this.styleProps[currentTool.shapeType].keys()) {
6668
- styles.applyValue(style, this.getStyleForNextShape(style));
6669
- }
6598
+ for (const style of this.styleProps[currentTool.shapeType].keys()) {
6599
+ styles.applyValue(style, this.getStyleForNextShape(style));
6670
6600
  }
6671
6601
  }
6672
6602
  return styles;
6673
6603
  }
6674
6604
  getSharedOpacity() {
6675
6605
  if (this.isIn("select") && this.getSelectedShapeIds().length > 0) {
6676
- const shapesToCheck = [];
6677
- const addShape = (shapeId) => {
6678
- const shape = this.getShape(shapeId);
6679
- if (!shape) return;
6680
- if (this.isShapeOfType(shape, "group")) {
6681
- for (const childId of this.getSortedChildIdsForParent(shape.id)) {
6682
- addShape(childId);
6683
- }
6684
- } else {
6685
- shapesToCheck.push(shape);
6686
- }
6687
- };
6688
- for (const shapeId of this.getSelectedShapeIds()) {
6689
- addShape(shapeId);
6690
- }
6691
6606
  let opacity = null;
6692
- for (const shape of shapesToCheck) {
6607
+ for (const shape of this._getStyleableShapes(this.getSelectedShapes())) {
6693
6608
  if (opacity === null) {
6694
6609
  opacity = shape.opacity;
6695
6610
  } else if (opacity !== shape.opacity) {
@@ -6728,28 +6643,12 @@ class Editor extends import_eventemitter3.default {
6728
6643
  setOpacityForSelectedShapes(opacity) {
6729
6644
  const selectedShapes = this.getSelectedShapes();
6730
6645
  if (selectedShapes.length > 0) {
6731
- const shapesToUpdate = [];
6732
- const addShapeById = (shape) => {
6733
- if (this.isShapeOfType(shape, "group")) {
6734
- const childIds = this.getSortedChildIdsForParent(shape);
6735
- for (const childId of childIds) {
6736
- addShapeById(this.getShape(childId));
6737
- }
6738
- } else {
6739
- shapesToUpdate.push(shape);
6740
- }
6741
- };
6742
- for (const id of selectedShapes) {
6743
- addShapeById(id);
6744
- }
6745
6646
  this.updateShapes(
6746
- shapesToUpdate.map((shape) => {
6747
- return {
6748
- id: shape.id,
6749
- type: shape.type,
6750
- opacity
6751
- };
6752
- })
6647
+ this._getStyleableShapes(selectedShapes).map((shape) => ({
6648
+ id: shape.id,
6649
+ type: shape.type,
6650
+ opacity
6651
+ }))
6753
6652
  );
6754
6653
  }
6755
6654
  return this;
@@ -6794,33 +6693,17 @@ class Editor extends import_eventemitter3.default {
6794
6693
  const selectedShapes = this.getSelectedShapes();
6795
6694
  if (selectedShapes.length > 0) {
6796
6695
  const updates = [];
6797
- const addShapeById = (shape) => {
6798
- if (this.isShapeOfType(shape, "group")) {
6799
- const childIds = this.getSortedChildIdsForParent(shape.id);
6800
- for (const childId of childIds) {
6801
- addShapeById(this.getShape(childId));
6802
- }
6803
- } else {
6804
- const util = this.getShapeUtil(shape);
6805
- const stylePropKey = this.styleProps[shape.type].get(style);
6806
- if (stylePropKey) {
6807
- const shapePartial = {
6808
- id: shape.id,
6809
- type: shape.type,
6810
- props: { [stylePropKey]: value }
6811
- };
6812
- updates.push({
6813
- util,
6814
- originalShape: shape,
6815
- updatePartial: shapePartial
6816
- });
6817
- }
6696
+ for (const shape of this._getStyleableShapes(selectedShapes)) {
6697
+ const stylePropKey = this.styleProps[shape.type].get(style);
6698
+ if (stylePropKey) {
6699
+ updates.push({
6700
+ id: shape.id,
6701
+ type: shape.type,
6702
+ props: { [stylePropKey]: value }
6703
+ });
6818
6704
  }
6819
- };
6820
- for (const shape of selectedShapes) {
6821
- addShapeById(shape);
6822
6705
  }
6823
- this.updateShapes(updates.map(({ updatePartial }) => updatePartial));
6706
+ this.updateShapes(updates);
6824
6707
  }
6825
6708
  return this;
6826
6709
  }
@@ -7216,7 +7099,6 @@ class Editor extends import_eventemitter3.default {
7216
7099
  if (shapeIdMap.has(newShape.parentId)) {
7217
7100
  newShape.parentId = shapeIdMap.get(oldShape.parentId);
7218
7101
  } else {
7219
- rootShapeIds.push(newShape.id);
7220
7102
  newShape.index = index;
7221
7103
  index = (0, import_utils.getIndexAbove)(index);
7222
7104
  }
@@ -7241,8 +7123,11 @@ class Editor extends import_eventemitter3.default {
7241
7123
  continue;
7242
7124
  }
7243
7125
  if (asset.type === "image" && asset.props.src?.startsWith("data:image") || asset.type === "video" && asset.props.src?.startsWith("data:video")) {
7244
- assetsToUpdate.push((0, import_utils.structuredClone)(asset));
7245
- asset.props.src = null;
7126
+ assetsToUpdate.push(asset);
7127
+ const assetWithoutSrc = (0, import_utils.structuredClone)(asset);
7128
+ assetWithoutSrc.props.src = null;
7129
+ assetsToCreate.push(assetWithoutSrc);
7130
+ continue;
7246
7131
  }
7247
7132
  assetsToCreate.push(asset);
7248
7133
  }
@@ -8069,7 +7954,7 @@ class Editor extends import_eventemitter3.default {
8069
7954
  if (info.type === "misc") {
8070
7955
  if (info.name === "cancel" || info.name === "complete") {
8071
7956
  this.inputs.setIsDragging(false);
8072
- if (this.inputs.getIsPanning()) {
7957
+ if (this.inputs.getIsPanning() && !this.inputs.keys.has("Space")) {
8073
7958
  this.inputs.setIsPanning(false);
8074
7959
  this.inputs.setIsSpacebarPanning(false);
8075
7960
  this.setCursor({ type: this._prevCursor, rotation: 0 });
@@ -8821,10 +8706,9 @@ function withIsolatedShapes(editor, shapeIds, callback) {
8821
8706
  }
8822
8707
  function getCameraFitXFitY(editor, cameraOptions) {
8823
8708
  if (!cameraOptions.constraints) throw Error("Should have constraints here");
8824
- const {
8825
- padding: { x: px, y: py }
8826
- } = cameraOptions.constraints;
8827
8709
  const vsb = editor.getViewportScreenBounds();
8710
+ const px = Math.min(cameraOptions.constraints.padding.x, vsb.w / 2);
8711
+ const py = Math.min(cameraOptions.constraints.padding.y, vsb.h / 2);
8828
8712
  const bounds = import_Box.Box.From(cameraOptions.constraints.bounds);
8829
8713
  const zx = (vsb.w - px * 2) / bounds.w;
8830
8714
  const zy = (vsb.h - py * 2) / bounds.h;