@tldraw/editor 5.2.0-next.ee0fa4d6244f → 5.2.0

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 (189) hide show
  1. package/README.md +7 -1
  2. package/dist-cjs/index.d.ts +117 -60
  3. package/dist-cjs/index.js +7 -8
  4. package/dist-cjs/index.js.map +2 -2
  5. package/dist-cjs/lib/components/MenuClickCapture.js +8 -5
  6. package/dist-cjs/lib/components/MenuClickCapture.js.map +2 -2
  7. package/dist-cjs/lib/components/Shape.js +47 -1
  8. package/dist-cjs/lib/components/Shape.js.map +3 -3
  9. package/dist-cjs/lib/components/default-components/DefaultCanvas.js +1 -16
  10. package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
  11. package/dist-cjs/lib/editor/Editor.js +131 -57
  12. package/dist-cjs/lib/editor/Editor.js.map +3 -3
  13. package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js +8 -58
  14. package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js.map +2 -2
  15. package/dist-cjs/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.js +3 -3
  16. package/dist-cjs/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.js.map +2 -2
  17. package/dist-cjs/lib/editor/managers/FocusManager/FocusManager.js +1 -2
  18. package/dist-cjs/lib/editor/managers/FocusManager/FocusManager.js.map +2 -2
  19. package/dist-cjs/lib/editor/managers/FontManager/FontManager.js +28 -4
  20. package/dist-cjs/lib/editor/managers/FontManager/FontManager.js.map +3 -3
  21. package/dist-cjs/lib/editor/managers/HistoryManager/HistoryManager.js +24 -2
  22. package/dist-cjs/lib/editor/managers/HistoryManager/HistoryManager.js.map +2 -2
  23. package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js +14 -3
  24. package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js.map +2 -2
  25. package/dist-cjs/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.js +4 -2
  26. package/dist-cjs/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.js.map +2 -2
  27. package/dist-cjs/lib/editor/managers/TextManager/TextManager.js +7 -3
  28. package/dist-cjs/lib/editor/managers/TextManager/TextManager.js.map +2 -2
  29. package/dist-cjs/lib/editor/managers/TickManager/TickManager.js +0 -1
  30. package/dist-cjs/lib/editor/managers/TickManager/TickManager.js.map +2 -2
  31. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js +15 -2
  32. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +2 -2
  33. package/dist-cjs/lib/editor/overlays/strokeShapeIndicators.js +79 -0
  34. package/dist-cjs/lib/editor/overlays/strokeShapeIndicators.js.map +7 -0
  35. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  36. package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js +4 -0
  37. package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js.map +2 -2
  38. package/dist-cjs/lib/editor/tools/StateNode.js.map +2 -2
  39. package/dist-cjs/lib/{hooks/useTransform.js → editor/tools/cancelShapeCreationOnLongPress.js} +8 -23
  40. package/dist-cjs/lib/editor/tools/cancelShapeCreationOnLongPress.js.map +7 -0
  41. package/dist-cjs/lib/editor/types/event-types.js +0 -2
  42. package/dist-cjs/lib/editor/types/event-types.js.map +2 -2
  43. package/dist-cjs/lib/globals/environment.js +6 -1
  44. package/dist-cjs/lib/globals/environment.js.map +2 -2
  45. package/dist-cjs/lib/hooks/EditorComponentsContext.js.map +2 -2
  46. package/dist-cjs/lib/hooks/useCanvasEvents.js +17 -8
  47. package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
  48. package/dist-cjs/lib/hooks/useEditorComponents.js +0 -1
  49. package/dist-cjs/lib/hooks/useEditorComponents.js.map +2 -2
  50. package/dist-cjs/lib/hooks/usePresence.js.map +2 -2
  51. package/dist-cjs/lib/hooks/useScreenBounds.js +1 -0
  52. package/dist-cjs/lib/hooks/useScreenBounds.js.map +2 -2
  53. package/dist-cjs/lib/license/LicenseManager.js +5 -1
  54. package/dist-cjs/lib/license/LicenseManager.js.map +2 -2
  55. package/dist-cjs/lib/primitives/Vec.js +3 -2
  56. package/dist-cjs/lib/primitives/Vec.js.map +2 -2
  57. package/dist-cjs/lib/utils/dom.js +12 -0
  58. package/dist-cjs/lib/utils/dom.js.map +2 -2
  59. package/dist-cjs/lib/utils/getPointerInfo.js +2 -1
  60. package/dist-cjs/lib/utils/getPointerInfo.js.map +2 -2
  61. package/dist-cjs/lib/utils/pointer.js +37 -0
  62. package/dist-cjs/lib/utils/pointer.js.map +7 -0
  63. package/dist-cjs/version.js +3 -3
  64. package/dist-cjs/version.js.map +1 -1
  65. package/dist-esm/index.d.mts +117 -60
  66. package/dist-esm/index.mjs +9 -13
  67. package/dist-esm/index.mjs.map +2 -2
  68. package/dist-esm/lib/components/MenuClickCapture.mjs +8 -5
  69. package/dist-esm/lib/components/MenuClickCapture.mjs.map +2 -2
  70. package/dist-esm/lib/components/Shape.mjs +48 -2
  71. package/dist-esm/lib/components/Shape.mjs.map +3 -3
  72. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +1 -16
  73. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
  74. package/dist-esm/lib/editor/Editor.mjs +131 -57
  75. package/dist-esm/lib/editor/Editor.mjs.map +3 -3
  76. package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs +8 -58
  77. package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs.map +2 -2
  78. package/dist-esm/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.mjs +3 -3
  79. package/dist-esm/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.mjs.map +2 -2
  80. package/dist-esm/lib/editor/managers/FocusManager/FocusManager.mjs +1 -2
  81. package/dist-esm/lib/editor/managers/FocusManager/FocusManager.mjs.map +2 -2
  82. package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs +28 -4
  83. package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs.map +3 -3
  84. package/dist-esm/lib/editor/managers/HistoryManager/HistoryManager.mjs +24 -2
  85. package/dist-esm/lib/editor/managers/HistoryManager/HistoryManager.mjs.map +2 -2
  86. package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs +14 -3
  87. package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs.map +2 -2
  88. package/dist-esm/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.mjs +4 -2
  89. package/dist-esm/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.mjs.map +2 -2
  90. package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs +7 -3
  91. package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs.map +2 -2
  92. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs +0 -1
  93. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs.map +2 -2
  94. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs +15 -2
  95. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +2 -2
  96. package/dist-esm/lib/editor/overlays/strokeShapeIndicators.mjs +59 -0
  97. package/dist-esm/lib/editor/overlays/strokeShapeIndicators.mjs.map +7 -0
  98. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  99. package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs +4 -0
  100. package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs.map +2 -2
  101. package/dist-esm/lib/editor/tools/StateNode.mjs.map +2 -2
  102. package/dist-esm/lib/editor/tools/cancelShapeCreationOnLongPress.mjs +8 -0
  103. package/dist-esm/lib/editor/tools/cancelShapeCreationOnLongPress.mjs.map +7 -0
  104. package/dist-esm/lib/editor/types/event-types.mjs +0 -2
  105. package/dist-esm/lib/editor/types/event-types.mjs.map +2 -2
  106. package/dist-esm/lib/globals/environment.mjs +6 -1
  107. package/dist-esm/lib/globals/environment.mjs.map +2 -2
  108. package/dist-esm/lib/hooks/EditorComponentsContext.mjs.map +2 -2
  109. package/dist-esm/lib/hooks/useCanvasEvents.mjs +17 -8
  110. package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
  111. package/dist-esm/lib/hooks/useEditorComponents.mjs +0 -1
  112. package/dist-esm/lib/hooks/useEditorComponents.mjs.map +2 -2
  113. package/dist-esm/lib/hooks/usePresence.mjs.map +2 -2
  114. package/dist-esm/lib/hooks/useScreenBounds.mjs +1 -0
  115. package/dist-esm/lib/hooks/useScreenBounds.mjs.map +2 -2
  116. package/dist-esm/lib/license/LicenseManager.mjs +5 -1
  117. package/dist-esm/lib/license/LicenseManager.mjs.map +2 -2
  118. package/dist-esm/lib/primitives/Vec.mjs +3 -2
  119. package/dist-esm/lib/primitives/Vec.mjs.map +2 -2
  120. package/dist-esm/lib/utils/dom.mjs +12 -0
  121. package/dist-esm/lib/utils/dom.mjs.map +2 -2
  122. package/dist-esm/lib/utils/getPointerInfo.mjs +2 -1
  123. package/dist-esm/lib/utils/getPointerInfo.mjs.map +2 -2
  124. package/dist-esm/lib/utils/pointer.mjs +17 -0
  125. package/dist-esm/lib/utils/pointer.mjs.map +7 -0
  126. package/dist-esm/version.mjs +3 -3
  127. package/dist-esm/version.mjs.map +1 -1
  128. package/editor.css +15 -3
  129. package/package.json +11 -8
  130. package/src/index.ts +4 -10
  131. package/src/lib/components/MenuClickCapture.tsx +8 -4
  132. package/src/lib/components/Shape.tsx +88 -1
  133. package/src/lib/components/default-components/DefaultCanvas.tsx +1 -17
  134. package/src/lib/editor/Editor.test.ts +36 -0
  135. package/src/lib/editor/Editor.ts +204 -75
  136. package/src/lib/editor/managers/ClickManager/ClickManager.test.ts +54 -74
  137. package/src/lib/editor/managers/ClickManager/ClickManager.ts +15 -65
  138. package/src/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.test.ts +43 -16
  139. package/src/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.ts +8 -5
  140. package/src/lib/editor/managers/FocusManager/FocusManager.test.ts +4 -4
  141. package/src/lib/editor/managers/FocusManager/FocusManager.ts +1 -2
  142. package/src/lib/editor/managers/FontManager/FontManager.test.ts +37 -10
  143. package/src/lib/editor/managers/FontManager/FontManager.ts +46 -1
  144. package/src/lib/editor/managers/HistoryManager/HistoryManager.test.ts +32 -0
  145. package/src/lib/editor/managers/HistoryManager/HistoryManager.ts +34 -4
  146. package/src/lib/editor/managers/InputsManager/InputsManager.test.ts +61 -0
  147. package/src/lib/editor/managers/InputsManager/InputsManager.ts +16 -4
  148. package/src/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.ts +9 -2
  149. package/src/lib/editor/managers/TextManager/TextManager.test.ts +16 -14
  150. package/src/lib/editor/managers/TextManager/TextManager.ts +17 -2
  151. package/src/lib/editor/managers/TickManager/TickManager.test.ts +0 -40
  152. package/src/lib/editor/managers/TickManager/TickManager.ts +0 -1
  153. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +12 -2
  154. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.ts +27 -2
  155. package/src/lib/editor/overlays/strokeShapeIndicators.ts +86 -0
  156. package/src/lib/editor/shapes/ShapeUtil.ts +41 -0
  157. package/src/lib/editor/tools/BaseBoxShapeTool/children/Pointing.ts +5 -0
  158. package/src/lib/editor/tools/StateNode.ts +0 -2
  159. package/src/lib/editor/tools/cancelShapeCreationOnLongPress.ts +31 -0
  160. package/src/lib/editor/types/event-types.ts +9 -6
  161. package/src/lib/globals/environment.ts +7 -0
  162. package/src/lib/hooks/EditorComponentsContext.tsx +0 -2
  163. package/src/lib/hooks/useCanvasEvents.ts +24 -12
  164. package/src/lib/hooks/useEditorComponents.tsx +0 -1
  165. package/src/lib/hooks/usePresence.ts +2 -2
  166. package/src/lib/hooks/useScreenBounds.ts +3 -0
  167. package/src/lib/license/LicenseManager.test.ts +24 -16
  168. package/src/lib/license/LicenseManager.ts +8 -2
  169. package/src/lib/primitives/Vec.ts +5 -2
  170. package/src/lib/test/ContentElementSlot.test.tsx +201 -0
  171. package/src/lib/utils/dom.ts +34 -0
  172. package/src/lib/utils/getPointerInfo.ts +2 -1
  173. package/src/lib/utils/pointer.test.ts +76 -0
  174. package/src/lib/utils/pointer.ts +42 -0
  175. package/src/version.ts +3 -3
  176. package/dist-cjs/lib/components/default-components/DefaultSelectionBackground.js +0 -49
  177. package/dist-cjs/lib/components/default-components/DefaultSelectionBackground.js.map +0 -7
  178. package/dist-cjs/lib/editor/overlays/ShapeIndicatorOverlayUtil.js +0 -161
  179. package/dist-cjs/lib/editor/overlays/ShapeIndicatorOverlayUtil.js.map +0 -7
  180. package/dist-cjs/lib/hooks/useTransform.js.map +0 -7
  181. package/dist-esm/lib/components/default-components/DefaultSelectionBackground.mjs +0 -19
  182. package/dist-esm/lib/components/default-components/DefaultSelectionBackground.mjs.map +0 -7
  183. package/dist-esm/lib/editor/overlays/ShapeIndicatorOverlayUtil.mjs +0 -141
  184. package/dist-esm/lib/editor/overlays/ShapeIndicatorOverlayUtil.mjs.map +0 -7
  185. package/dist-esm/lib/hooks/useTransform.mjs +0 -23
  186. package/dist-esm/lib/hooks/useTransform.mjs.map +0 -7
  187. package/src/lib/components/default-components/DefaultSelectionBackground.tsx +0 -25
  188. package/src/lib/editor/overlays/ShapeIndicatorOverlayUtil.ts +0 -216
  189. package/src/lib/hooks/useTransform.ts +0 -30
@@ -188,12 +188,12 @@ class Editor extends EventEmitter {
188
188
  this._tickManager = new TickManager(this);
189
189
  this.disposables.add(() => this._tickManager.dispose());
190
190
  this.disposables.add(() => {
191
- this.off("tick", this._decayCameraStateTimeout);
192
191
  this._setCameraState("idle");
193
192
  });
194
193
  this.fonts = new FontManager(this, fontAssetUrls);
195
194
  this.disposables.add(() => this.fonts.dispose());
196
195
  this.inputs = new InputsManager(this);
196
+ this.disposables.add(() => this.inputs.dispose());
197
197
  this.performance = new PerformanceManager(this);
198
198
  this.disposables.add(() => this.performance.dispose());
199
199
  this.collaborators = new CollaboratorsManager(this);
@@ -822,6 +822,7 @@ class Editor extends EventEmitter {
822
822
  this.store.dispose();
823
823
  this.isDisposed = true;
824
824
  this.emit("dispose");
825
+ this.removeAllListeners();
825
826
  }
826
827
  /* ------------------ Themes (shadowing the theme manager) ------------------ */
827
828
  /**
@@ -1091,6 +1092,14 @@ class Editor extends EventEmitter {
1091
1092
  getMarkIdMatching(idSubstring) {
1092
1093
  return this.history.getMarkIdMatching(idSubstring);
1093
1094
  }
1095
+ /**
1096
+ * Whether the editor is currently replaying history (i.e. an undo or redo is being applied).
1097
+ *
1098
+ * @internal
1099
+ */
1100
+ isReplayingHistory() {
1101
+ return this.history.isReplaying();
1102
+ }
1094
1103
  /**
1095
1104
  * Coalesces all changes since the given mark into a single change, removing any intermediate marks.
1096
1105
  *
@@ -2185,7 +2194,7 @@ class Editor extends EventEmitter {
2185
2194
  return baseCamera;
2186
2195
  }
2187
2196
  _getFollowingPresence(targetUserId) {
2188
- const visited = [this.user.getId()];
2197
+ const visited = [this.user.getRecordId()];
2189
2198
  const collaborators = this.getCollaborators();
2190
2199
  let leaderPresence = null;
2191
2200
  while (targetUserId && !visited.includes(targetUserId)) {
@@ -2373,6 +2382,11 @@ class Editor extends EventEmitter {
2373
2382
  getConstrainedCamera(point, opts) {
2374
2383
  const currentCamera = this.getCamera();
2375
2384
  let { x, y, z = currentCamera.z } = point;
2385
+ const preserveFocalPoint = (current, requested, rz, z2) => {
2386
+ const cz = currentCamera.z;
2387
+ if (rz === cz) return current;
2388
+ return current + (requested - current) * (1 / z2 - 1 / cz) / (1 / rz - 1 / cz);
2389
+ };
2376
2390
  if (!opts?.force) {
2377
2391
  const cameraOptions = this.getCameraOptions();
2378
2392
  const zoomMin = cameraOptions.zoomSteps[0];
@@ -2392,14 +2406,10 @@ class Editor extends EventEmitter {
2392
2406
  z = this.getInitialZoom();
2393
2407
  }
2394
2408
  if (z < minZ || z > maxZ) {
2395
- const { x: cx, y: cy, z: cz } = currentCamera;
2396
- const cxA = -cx + vsb.w / cz / 2;
2397
- const cyA = -cy + vsb.h / cz / 2;
2409
+ const rz = z;
2398
2410
  z = clamp(z, minZ, maxZ);
2399
- const cxB = -cx + vsb.w / z / 2;
2400
- const cyB = -cy + vsb.h / z / 2;
2401
- x = cx + cxB - cxA;
2402
- y = cy + cyB - cyA;
2411
+ x = preserveFocalPoint(currentCamera.x, x, rz, z);
2412
+ y = preserveFocalPoint(currentCamera.y, y, rz, z);
2403
2413
  }
2404
2414
  const minX = px / z - bounds.x;
2405
2415
  const minY = py / z - bounds.y;
@@ -2468,10 +2478,10 @@ class Editor extends EventEmitter {
2468
2478
  }
2469
2479
  } else {
2470
2480
  if (z > zoomMax || z < zoomMin) {
2471
- const { x: cx, y: cy, z: cz } = currentCamera;
2481
+ const rz = z;
2472
2482
  z = clamp(z, zoomMin, zoomMax);
2473
- x = cx + (-cx + vsb.w / z / 2) - (-cx + vsb.w / cz / 2);
2474
- y = cy + (-cy + vsb.h / z / 2) - (-cy + vsb.h / cz / 2);
2483
+ x = preserveFocalPoint(currentCamera.x, x, rz, z);
2484
+ y = preserveFocalPoint(currentCamera.y, y, rz, z);
2475
2485
  }
2476
2486
  }
2477
2487
  }
@@ -2894,14 +2904,25 @@ class Editor extends EventEmitter {
2894
2904
  this.off("stop-camera-animation", cancel);
2895
2905
  };
2896
2906
  this.once("stop-camera-animation", cancel);
2907
+ const dirZ = direction.z ?? 0;
2897
2908
  const moveCamera = (elapsed) => {
2898
2909
  const { x: cx, y: cy, z: cz } = this.getCamera();
2899
- const movementVec = Vec.Mul(direction, currentSpeed * elapsed / cz);
2910
+ const dx = direction.x * (currentSpeed * elapsed) / cz;
2911
+ const dy = direction.y * (currentSpeed * elapsed) / cz;
2912
+ let newCx = cx + dx;
2913
+ let newCy = cy + dy;
2914
+ let newCz = cz;
2915
+ if (dirZ !== 0) {
2916
+ newCz = cz * (1 + dirZ * currentSpeed * elapsed);
2917
+ const center = this.getViewportScreenCenter();
2918
+ newCx += center.x / newCz - center.x / cz;
2919
+ newCy += center.y / newCz - center.y / cz;
2920
+ }
2900
2921
  currentSpeed *= 1 - friction;
2901
2922
  if (currentSpeed < speedThreshold) {
2902
2923
  cancel();
2903
2924
  } else {
2904
- this._setCamera(new Vec(cx + movementVec.x, cy + movementVec.y, cz));
2925
+ this._setCamera(new Vec(newCx, newCy, newCz));
2905
2926
  }
2906
2927
  };
2907
2928
  this.on("tick", moveCamera);
@@ -2981,6 +3002,7 @@ class Editor extends EventEmitter {
2981
3002
  screenBounds.height = Math.max(screenBounds.height, 1);
2982
3003
  }
2983
3004
  const doc = this.getContainerDocument();
3005
+ if (!doc.body) return this;
2984
3006
  const insets = [
2985
3007
  // top
2986
3008
  screenBounds.minY !== 0,
@@ -3190,7 +3212,7 @@ class Editor extends EventEmitter {
3190
3212
  }
3191
3213
  /**
3192
3214
  * Collect user IDs referenced by a set of shapes via shape-specific props
3193
- * (e.g. `textFirstEditedBy` on notes).
3215
+ * (e.g. `textLastEditedBy` on notes).
3194
3216
  *
3195
3217
  * @internal
3196
3218
  */
@@ -3221,7 +3243,7 @@ class Editor extends EventEmitter {
3221
3243
  */
3222
3244
  startFollowingUser(userId) {
3223
3245
  this.stopFollowingUser();
3224
- const thisUserId = this.user.getId();
3246
+ const thisUserId = this.user.getExternalId();
3225
3247
  if (!thisUserId) {
3226
3248
  console.warn("You should set the userId for the current instance before following a user");
3227
3249
  }
@@ -5925,24 +5947,40 @@ class Editor extends EventEmitter {
5925
5947
  * @public
5926
5948
  */
5927
5949
  resizeShape(shape, scale, opts = {}) {
5950
+ const partial = this.getResizeShapePartial(shape, scale, opts);
5951
+ if (partial) this.updateShapes([partial]);
5952
+ return this;
5953
+ }
5954
+ /**
5955
+ * Get the update for a resized shape without committing it to the store. Interactions that
5956
+ * resize many shapes at once use this to collect all of the updates and commit them in a
5957
+ * single batch. Returns null when there is nothing to update.
5958
+ *
5959
+ * Shapes that are rotated out of alignment with the scale axis cannot be resized with a
5960
+ * single update; those shapes are resized immediately (as `resizeShape` would do) and null
5961
+ * is returned.
5962
+ *
5963
+ * @internal
5964
+ */
5965
+ getResizeShapePartial(shape, scale, opts = {}) {
5928
5966
  const id = typeof shape === "string" ? shape : shape.id;
5929
- if (this.getIsReadonly()) return this;
5967
+ if (this.getIsReadonly()) return null;
5930
5968
  if (!Number.isFinite(scale.x)) scale = new Vec(1, scale.y);
5931
5969
  if (!Number.isFinite(scale.y)) scale = new Vec(scale.x, 1);
5932
5970
  const initialShape = opts.initialShape ?? this.getShape(id);
5933
- if (!initialShape) return this;
5971
+ if (!initialShape) return null;
5934
5972
  const scaleOrigin = opts.scaleOrigin ?? this.getShapePageBounds(id)?.center;
5935
- if (!scaleOrigin) return this;
5973
+ if (!scaleOrigin) return null;
5936
5974
  const pageTransform = opts.initialPageTransform ? Mat.Cast(opts.initialPageTransform) : this.getShapePageTransform(id);
5937
- if (!pageTransform) return this;
5975
+ if (!pageTransform) return null;
5938
5976
  const pageRotation = pageTransform.rotation();
5939
- if (pageRotation == null) return this;
5977
+ if (pageRotation == null) return null;
5940
5978
  const scaleAxisRotation = opts.scaleAxisRotation ?? pageRotation;
5941
5979
  const initialBounds = opts.initialBounds ?? this.getShapeGeometry(id).bounds;
5942
- if (!initialBounds) return this;
5980
+ if (!initialBounds) return null;
5943
5981
  const isAspectRatioLocked = opts.isAspectRatioLocked ?? this.getShapeUtil(initialShape).isAspectRatioLocked(initialShape);
5944
5982
  if (!areAnglesCompatible(pageRotation, scaleAxisRotation)) {
5945
- return this._resizeUnalignedShape(id, scale, {
5983
+ this._resizeUnalignedShape(id, scale, {
5946
5984
  ...opts,
5947
5985
  initialBounds,
5948
5986
  scaleOrigin,
@@ -5951,6 +5989,7 @@ class Editor extends EventEmitter {
5951
5989
  isAspectRatioLocked,
5952
5990
  initialShape
5953
5991
  });
5992
+ return null;
5954
5993
  }
5955
5994
  const util = this.getShapeUtil(initialShape);
5956
5995
  if (isAspectRatioLocked) {
@@ -5960,7 +5999,7 @@ class Editor extends EventEmitter {
5960
5999
  scale = new Vec(Math.sign(scale.x) * Math.abs(scale.y), scale.y);
5961
6000
  }
5962
6001
  }
5963
- let didResize = false;
6002
+ let workingShape = null;
5964
6003
  if (util.onResize && util.canResize(initialShape)) {
5965
6004
  const newPagePoint = this._scalePagePoint(
5966
6005
  Mat.applyToPoint(pageTransform, new Vec(0, 0)),
@@ -5978,7 +6017,7 @@ class Editor extends EventEmitter {
5978
6017
  myScale.y = areWidthAndHeightAlignedWithCorrectAxis ? scale.y : scale.x;
5979
6018
  const initialPagePoint = Mat.applyToPoint(pageTransform, new Vec());
5980
6019
  const { x, y } = this.getPointInParentSpace(initialShape.id, initialPagePoint);
5981
- let workingShape = initialShape;
6020
+ workingShape = initialShape;
5982
6021
  if (!opts.skipStartAndEndCallbacks) {
5983
6022
  workingShape = applyPartialToRecordWithProps(
5984
6023
  initialShape,
@@ -5998,9 +6037,6 @@ class Editor extends EventEmitter {
5998
6037
  initialShape
5999
6038
  }
6000
6039
  );
6001
- if (resizedShape) {
6002
- didResize = true;
6003
- }
6004
6040
  workingShape = applyPartialToRecordWithProps(workingShape, {
6005
6041
  id,
6006
6042
  type: initialShape.type,
@@ -6014,32 +6050,36 @@ class Editor extends EventEmitter {
6014
6050
  util.onResizeEnd?.(initialShape, workingShape) ?? void 0
6015
6051
  );
6016
6052
  }
6017
- this.updateShapes([workingShape]);
6053
+ if (resizedShape) {
6054
+ return workingShape;
6055
+ }
6018
6056
  }
6019
- if (!didResize) {
6020
- const initialPageCenter = Mat.applyToPoint(pageTransform, initialBounds.center);
6021
- const newPageCenter = this._scalePagePoint(
6022
- initialPageCenter,
6023
- scaleOrigin,
6024
- scale,
6025
- scaleAxisRotation
6026
- );
6027
- const initialPageCenterInParentSpace = this.getPointInParentSpace(
6028
- initialShape.id,
6029
- initialPageCenter
6030
- );
6031
- const newPageCenterInParentSpace = this.getPointInParentSpace(initialShape.id, newPageCenter);
6032
- const delta = Vec.Sub(newPageCenterInParentSpace, initialPageCenterInParentSpace);
6033
- this.updateShapes([
6034
- {
6035
- id,
6036
- type: initialShape.type,
6037
- x: initialShape.x + delta.x,
6038
- y: initialShape.y + delta.y
6039
- }
6040
- ]);
6057
+ const initialPageCenter = Mat.applyToPoint(pageTransform, initialBounds.center);
6058
+ const newPageCenter = this._scalePagePoint(
6059
+ initialPageCenter,
6060
+ scaleOrigin,
6061
+ scale,
6062
+ scaleAxisRotation
6063
+ );
6064
+ const initialPageCenterInParentSpace = this.getPointInParentSpace(
6065
+ initialShape.id,
6066
+ initialPageCenter
6067
+ );
6068
+ const newPageCenterInParentSpace = this.getPointInParentSpace(initialShape.id, newPageCenter);
6069
+ const delta = Vec.Sub(newPageCenterInParentSpace, initialPageCenterInParentSpace);
6070
+ if (workingShape) {
6071
+ return {
6072
+ ...workingShape,
6073
+ x: initialShape.x + delta.x,
6074
+ y: initialShape.y + delta.y
6075
+ };
6041
6076
  }
6042
- return this;
6077
+ return {
6078
+ id,
6079
+ type: initialShape.type,
6080
+ x: initialShape.x + delta.x,
6081
+ y: initialShape.y + delta.y
6082
+ };
6043
6083
  }
6044
6084
  /** @internal */
6045
6085
  _scalePagePoint(point, scaleOrigin, scale, scaleAxisRotation) {
@@ -7293,6 +7333,7 @@ class Editor extends EventEmitter {
7293
7333
  async getSvgElement(shapes, opts = {}) {
7294
7334
  const ids = shapes.length === 0 ? this.getCurrentPageShapeIdsSorted() : typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
7295
7335
  if (ids.length === 0) return void 0;
7336
+ await this.fonts.loadRequiredFontsForCurrentPage(this.options.maxFontsToLoadBeforeRender);
7296
7337
  return exportToSvg(this, ids, opts);
7297
7338
  }
7298
7339
  /**
@@ -7867,6 +7908,15 @@ class Editor extends EventEmitter {
7867
7908
  _didPinch = false;
7868
7909
  /** @internal */
7869
7910
  _selectedShapeIdsAtPointerDown = [];
7911
+ /**
7912
+ * Whether `_selectedShapeIdsAtPointerDown` holds a pre-gesture selection
7913
+ * captured by a `pointer_down` (the touch path) that a following pinch
7914
+ * should restore. False when no pointer_down preceded the pinch (the
7915
+ * Safari trackpad path uses gesture events), in which case `pinch_start`
7916
+ * captures the live selection instead.
7917
+ * @internal
7918
+ */
7919
+ _didCaptureSelectionAtPointerDown = false;
7870
7920
  /** @internal */
7871
7921
  _longPressTimeout = -1;
7872
7922
  /** @internal */
@@ -7999,10 +8049,15 @@ class Editor extends EventEmitter {
7999
8049
  case "pinch_start": {
8000
8050
  if (inputs.getIsPinching()) return;
8001
8051
  if (!inputs.getIsEditing()) {
8002
- this._selectedShapeIdsAtPointerDown = [...pageState.selectedShapeIds];
8052
+ if (!this._didCaptureSelectionAtPointerDown) {
8053
+ this._selectedShapeIdsAtPointerDown = [...pageState.selectedShapeIds];
8054
+ }
8003
8055
  this._didPinch = true;
8004
8056
  inputs.setIsPinching(true);
8005
8057
  this.interrupt();
8058
+ if (this._didCaptureSelectionAtPointerDown) {
8059
+ this.setSelectedShapes(this._selectedShapeIdsAtPointerDown);
8060
+ }
8006
8061
  }
8007
8062
  this.emit("event", info);
8008
8063
  return;
@@ -8042,6 +8097,7 @@ class Editor extends EventEmitter {
8042
8097
  const { _selectedShapeIdsAtPointerDown: shapesToReselect } = this;
8043
8098
  this.setSelectedShapes(this._selectedShapeIdsAtPointerDown);
8044
8099
  this._selectedShapeIdsAtPointerDown = [];
8100
+ this._didCaptureSelectionAtPointerDown = false;
8045
8101
  if (this._didPinch) {
8046
8102
  this._didPinch = false;
8047
8103
  if (shapesToReselect.length > 0) {
@@ -8135,12 +8191,18 @@ class Editor extends EventEmitter {
8135
8191
  });
8136
8192
  }, this.options.longPressDurationMs);
8137
8193
  }
8138
- this._selectedShapeIdsAtPointerDown = this.getSelectedShapeIds();
8194
+ if (!this._didCaptureSelectionAtPointerDown) {
8195
+ this._selectedShapeIdsAtPointerDown = this.getSelectedShapeIds();
8196
+ this._didCaptureSelectionAtPointerDown = true;
8197
+ }
8139
8198
  if (info.button === LEFT_MOUSE_BUTTON) this.capturedPointerId = info.pointerId;
8140
8199
  inputs.buttons.add(info.button);
8141
8200
  inputs.setIsPointing(true);
8142
8201
  inputs.setIsDragging(false);
8143
- if (!isPenMode && isPen) this.updateInstanceState({ isPenMode: true });
8202
+ if (!isPenMode && info.isPenDirect) {
8203
+ this.updateInstanceState({ isPenMode: true });
8204
+ this.interrupt();
8205
+ }
8144
8206
  if (info.button === STYLUS_ERASER_BUTTON) {
8145
8207
  this._restoreToolId = this.getCurrentToolId();
8146
8208
  this.complete();
@@ -8208,6 +8270,7 @@ class Editor extends EventEmitter {
8208
8270
  if (this.inputs.getIsRightPointing() && !this.inputs.getIsPanning()) {
8209
8271
  this.inputs.setIsRightPointing(false);
8210
8272
  this._selectedShapeIdsAtPointerDown = [];
8273
+ this._didCaptureSelectionAtPointerDown = false;
8211
8274
  break;
8212
8275
  }
8213
8276
  this.inputs.setIsRightPointing(false);
@@ -8242,14 +8305,21 @@ class Editor extends EventEmitter {
8242
8305
  this.setCursor({ type: this._prevCursor, rotation: 0 });
8243
8306
  }
8244
8307
  if (slideSpeed > 0) {
8245
- this.slideCamera({ speed: slideSpeed, direction: slideDirection });
8308
+ this.slideCamera({
8309
+ speed: slideSpeed,
8310
+ direction: { x: slideDirection.x, y: slideDirection.y, z: 0 }
8311
+ });
8246
8312
  }
8247
8313
  this._selectedShapeIdsAtPointerDown = [];
8314
+ this._didCaptureSelectionAtPointerDown = false;
8248
8315
  return this;
8249
8316
  }
8250
8317
  }
8251
8318
  if (slideSpeed > 0) {
8252
- this.slideCamera({ speed: slideSpeed, direction: slideDirection });
8319
+ this.slideCamera({
8320
+ speed: slideSpeed,
8321
+ direction: { x: slideDirection.x, y: slideDirection.y, z: 0 }
8322
+ });
8253
8323
  }
8254
8324
  } else {
8255
8325
  if (info.button === STYLUS_ERASER_BUTTON) {
@@ -8258,6 +8328,7 @@ class Editor extends EventEmitter {
8258
8328
  }
8259
8329
  }
8260
8330
  this._selectedShapeIdsAtPointerDown = [];
8331
+ this._didCaptureSelectionAtPointerDown = false;
8261
8332
  break;
8262
8333
  }
8263
8334
  }
@@ -8340,6 +8411,9 @@ class Editor extends EventEmitter {
8340
8411
  } else if (info.button === RIGHT_MOUSE_BUTTON) {
8341
8412
  info.name = "right_click";
8342
8413
  }
8414
+ if (info.name === "right_click" && this.getCurrentToolId() !== "select" && this.getStateDescendant("select")) {
8415
+ this.setCurrentTool("select");
8416
+ }
8343
8417
  const { isPenMode } = this.store.unsafeGetWithoutCapture(TLINSTANCE_ID);
8344
8418
  if (info.isPen === isPenMode) {
8345
8419
  const clickInfo = this._clickManager.handlePointerEvent(info);