@tldraw/editor 5.4.0-canary.042da660fa66 → 5.4.0-canary.059d049864ed

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 (155) hide show
  1. package/dist-cjs/index.d.ts +31 -1
  2. package/dist-cjs/index.js +1 -1
  3. package/dist-cjs/lib/components/MenuClickCapture.js +6 -8
  4. package/dist-cjs/lib/components/MenuClickCapture.js.map +2 -2
  5. package/dist-cjs/lib/components/default-components/CanvasOverlays.js +1 -3
  6. package/dist-cjs/lib/components/default-components/CanvasOverlays.js.map +2 -2
  7. package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js +1 -1
  8. package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js.map +1 -1
  9. package/dist-cjs/lib/config/TLUserPreferences.js +6 -2
  10. package/dist-cjs/lib/config/TLUserPreferences.js.map +2 -2
  11. package/dist-cjs/lib/config/createTLStore.js +1 -1
  12. package/dist-cjs/lib/config/createTLStore.js.map +2 -2
  13. package/dist-cjs/lib/editor/Editor.js +139 -268
  14. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  15. package/dist-cjs/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.js +8 -8
  16. package/dist-cjs/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.js.map +2 -2
  17. package/dist-cjs/lib/editor/managers/FontManager/FontManager.js +2 -1
  18. package/dist-cjs/lib/editor/managers/FontManager/FontManager.js.map +2 -2
  19. package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js +79 -6
  20. package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js.map +2 -2
  21. package/dist-cjs/lib/editor/managers/SnapManager/BoundsSnaps.js +0 -2
  22. package/dist-cjs/lib/editor/managers/SnapManager/BoundsSnaps.js.map +2 -2
  23. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js +1 -0
  24. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +2 -2
  25. package/dist-cjs/lib/editor/overlays/OverlayManager.js +3 -7
  26. package/dist-cjs/lib/editor/overlays/OverlayManager.js.map +2 -2
  27. package/dist-cjs/lib/editor/overlays/strokeShapeIndicators.js +0 -1
  28. package/dist-cjs/lib/editor/overlays/strokeShapeIndicators.js.map +2 -2
  29. package/dist-cjs/lib/editor/shapes/BaseFrameLikeShapeUtil.js +3 -1
  30. package/dist-cjs/lib/editor/shapes/BaseFrameLikeShapeUtil.js.map +2 -2
  31. package/dist-cjs/lib/editor/shapes/shared/getPerfectDashProps.js +1 -0
  32. package/dist-cjs/lib/editor/shapes/shared/getPerfectDashProps.js.map +2 -2
  33. package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js +1 -1
  34. package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js.map +2 -2
  35. package/dist-cjs/lib/editor/tools/StateNode.js +3 -2
  36. package/dist-cjs/lib/editor/tools/StateNode.js.map +2 -2
  37. package/dist-cjs/lib/globals/menus.js +9 -4
  38. package/dist-cjs/lib/globals/menus.js.map +2 -2
  39. package/dist-cjs/lib/hooks/useCanvasEvents.js +2 -0
  40. package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
  41. package/dist-cjs/lib/hooks/useDocumentEvents.js +9 -7
  42. package/dist-cjs/lib/hooks/useDocumentEvents.js.map +2 -2
  43. package/dist-cjs/lib/hooks/useLocalStore.js +10 -6
  44. package/dist-cjs/lib/hooks/useLocalStore.js.map +2 -2
  45. package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js +1 -0
  46. package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js.map +2 -2
  47. package/dist-cjs/lib/hooks/usePeerIds.js +2 -1
  48. package/dist-cjs/lib/hooks/usePeerIds.js.map +2 -2
  49. package/dist-cjs/lib/primitives/Box.js +2 -1
  50. package/dist-cjs/lib/primitives/Box.js.map +2 -2
  51. package/dist-cjs/lib/primitives/Vec.js +4 -3
  52. package/dist-cjs/lib/primitives/Vec.js.map +2 -2
  53. package/dist-cjs/lib/primitives/utils.js +2 -1
  54. package/dist-cjs/lib/primitives/utils.js.map +2 -2
  55. package/dist-cjs/lib/utils/rotation.js +1 -9
  56. package/dist-cjs/lib/utils/rotation.js.map +2 -2
  57. package/dist-cjs/version.js +3 -3
  58. package/dist-cjs/version.js.map +1 -1
  59. package/dist-esm/index.d.mts +31 -1
  60. package/dist-esm/index.mjs +1 -1
  61. package/dist-esm/lib/components/MenuClickCapture.mjs +6 -8
  62. package/dist-esm/lib/components/MenuClickCapture.mjs.map +2 -2
  63. package/dist-esm/lib/components/default-components/CanvasOverlays.mjs +1 -3
  64. package/dist-esm/lib/components/default-components/CanvasOverlays.mjs.map +2 -2
  65. package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs +1 -1
  66. package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs.map +1 -1
  67. package/dist-esm/lib/config/TLUserPreferences.mjs +6 -2
  68. package/dist-esm/lib/config/TLUserPreferences.mjs.map +2 -2
  69. package/dist-esm/lib/config/createTLStore.mjs +1 -1
  70. package/dist-esm/lib/config/createTLStore.mjs.map +2 -2
  71. package/dist-esm/lib/editor/Editor.mjs +147 -269
  72. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  73. package/dist-esm/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.mjs +9 -9
  74. package/dist-esm/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.mjs.map +2 -2
  75. package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs +2 -1
  76. package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs.map +2 -2
  77. package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs +81 -8
  78. package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs.map +2 -2
  79. package/dist-esm/lib/editor/managers/SnapManager/BoundsSnaps.mjs +0 -2
  80. package/dist-esm/lib/editor/managers/SnapManager/BoundsSnaps.mjs.map +2 -2
  81. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs +1 -0
  82. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +2 -2
  83. package/dist-esm/lib/editor/overlays/OverlayManager.mjs +3 -7
  84. package/dist-esm/lib/editor/overlays/OverlayManager.mjs.map +2 -2
  85. package/dist-esm/lib/editor/overlays/strokeShapeIndicators.mjs +0 -1
  86. package/dist-esm/lib/editor/overlays/strokeShapeIndicators.mjs.map +2 -2
  87. package/dist-esm/lib/editor/shapes/BaseFrameLikeShapeUtil.mjs +3 -1
  88. package/dist-esm/lib/editor/shapes/BaseFrameLikeShapeUtil.mjs.map +2 -2
  89. package/dist-esm/lib/editor/shapes/shared/getPerfectDashProps.mjs +1 -0
  90. package/dist-esm/lib/editor/shapes/shared/getPerfectDashProps.mjs.map +2 -2
  91. package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs +1 -1
  92. package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs.map +2 -2
  93. package/dist-esm/lib/editor/tools/StateNode.mjs +3 -2
  94. package/dist-esm/lib/editor/tools/StateNode.mjs.map +2 -2
  95. package/dist-esm/lib/globals/menus.mjs +9 -4
  96. package/dist-esm/lib/globals/menus.mjs.map +2 -2
  97. package/dist-esm/lib/hooks/useCanvasEvents.mjs +2 -0
  98. package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
  99. package/dist-esm/lib/hooks/useDocumentEvents.mjs +9 -7
  100. package/dist-esm/lib/hooks/useDocumentEvents.mjs.map +2 -2
  101. package/dist-esm/lib/hooks/useLocalStore.mjs +11 -7
  102. package/dist-esm/lib/hooks/useLocalStore.mjs.map +2 -2
  103. package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs +1 -0
  104. package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs.map +2 -2
  105. package/dist-esm/lib/hooks/usePeerIds.mjs +2 -1
  106. package/dist-esm/lib/hooks/usePeerIds.mjs.map +2 -2
  107. package/dist-esm/lib/primitives/Box.mjs +2 -1
  108. package/dist-esm/lib/primitives/Box.mjs.map +2 -2
  109. package/dist-esm/lib/primitives/Vec.mjs +4 -3
  110. package/dist-esm/lib/primitives/Vec.mjs.map +2 -2
  111. package/dist-esm/lib/primitives/utils.mjs +2 -1
  112. package/dist-esm/lib/primitives/utils.mjs.map +2 -2
  113. package/dist-esm/lib/utils/rotation.mjs +1 -9
  114. package/dist-esm/lib/utils/rotation.mjs.map +2 -2
  115. package/dist-esm/version.mjs +3 -3
  116. package/dist-esm/version.mjs.map +1 -1
  117. package/package.json +7 -7
  118. package/src/lib/components/MenuClickCapture.tsx +19 -18
  119. package/src/lib/components/default-components/CanvasOverlays.tsx +1 -3
  120. package/src/lib/components/default-components/DefaultErrorFallback.tsx +1 -1
  121. package/src/lib/config/TLUserPreferences.test.ts +18 -1
  122. package/src/lib/config/TLUserPreferences.ts +8 -4
  123. package/src/lib/config/createTLStore.ts +1 -1
  124. package/src/lib/editor/Editor.ts +196 -341
  125. package/src/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.ts +9 -9
  126. package/src/lib/editor/managers/FontManager/FontManager.ts +2 -1
  127. package/src/lib/editor/managers/InputsManager/InputsManager.test.ts +199 -3
  128. package/src/lib/editor/managers/InputsManager/InputsManager.ts +128 -9
  129. package/src/lib/editor/managers/SnapManager/BoundsSnaps.ts +0 -2
  130. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.ts +1 -0
  131. package/src/lib/editor/overlays/OverlayManager.ts +3 -7
  132. package/src/lib/editor/overlays/strokeShapeIndicators.ts +0 -1
  133. package/src/lib/editor/shapes/BaseFrameLikeShapeUtil.tsx +5 -1
  134. package/src/lib/editor/shapes/shared/getPerfectDashProps.ts +3 -0
  135. package/src/lib/editor/tools/BaseBoxShapeTool/children/Pointing.ts +4 -1
  136. package/src/lib/editor/tools/StateNode.test.ts +64 -0
  137. package/src/lib/editor/tools/StateNode.ts +12 -2
  138. package/src/lib/globals/menus.test.ts +33 -0
  139. package/src/lib/globals/menus.ts +11 -4
  140. package/src/lib/hooks/useCanvasEvents.ts +2 -0
  141. package/src/lib/hooks/useDocumentEvents.ts +14 -7
  142. package/src/lib/hooks/useLocalStore.ts +16 -8
  143. package/src/lib/hooks/usePassThroughWheelEvents.ts +3 -0
  144. package/src/lib/hooks/usePeerIds.ts +2 -1
  145. package/src/lib/primitives/Box.test.ts +9 -1
  146. package/src/lib/primitives/Box.ts +3 -1
  147. package/src/lib/primitives/Vec.test.ts +10 -0
  148. package/src/lib/primitives/Vec.ts +4 -4
  149. package/src/lib/primitives/utils.test.ts +10 -1
  150. package/src/lib/primitives/utils.ts +4 -1
  151. package/src/lib/test/useCanvasEvents.test.tsx +74 -0
  152. package/src/lib/test/useDocumentEvents.test.tsx +31 -0
  153. package/src/lib/test/usePassThroughWheelEvents.test.tsx +25 -2
  154. package/src/lib/utils/rotation.ts +4 -12
  155. package/src/version.ts +3 -3
@@ -257,6 +257,10 @@ class Editor extends import_eventemitter3.default {
257
257
  if (!nextPageState) nextPageState = { ...prevPageState };
258
258
  nextPageState.editingShapeId = null;
259
259
  }
260
+ if (prevPageState.croppingShapeId && shapesNoLongerInPage.has(prevPageState.croppingShapeId)) {
261
+ if (!nextPageState) nextPageState = { ...prevPageState };
262
+ nextPageState.croppingShapeId = null;
263
+ }
260
264
  const hintingShapeIds = prevPageState.hintingShapeIds.filter(
261
265
  (id) => !shapesNoLongerInPage.has(id)
262
266
  );
@@ -499,10 +503,11 @@ class Editor extends import_eventemitter3.default {
499
503
  this.cancelDoubleClick();
500
504
  }
501
505
  if (prev?.selectedShapeIds !== next?.selectedShapeIds) {
506
+ const selectedShapeIds = new Set(next.selectedShapeIds);
502
507
  const filtered = next.selectedShapeIds.filter((id) => {
503
508
  let parentId = this.getShape(id)?.parentId;
504
509
  while ((0, import_tlschema.isShapeId)(parentId)) {
505
- if (next.selectedShapeIds.includes(parentId)) {
510
+ if (selectedShapeIds.has(parentId)) {
506
511
  return false;
507
512
  }
508
513
  parentId = this.getShape(parentId)?.parentId;
@@ -586,7 +591,10 @@ class Editor extends import_eventemitter3.default {
586
591
  const mode = this.store.props.collaboration.mode;
587
592
  this.disposables.add(
588
593
  (0, import_state.react)("update collaboration mode", () => {
589
- 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
+ );
590
598
  })
591
599
  );
592
600
  }
@@ -944,7 +952,7 @@ class Editor extends import_eventemitter3.default {
944
952
  getShapeUtil(arg) {
945
953
  const type = typeof arg === "string" ? arg : arg.type;
946
954
  const shapeUtil = (0, import_utils.getOwnProperty)(this.shapeUtils, type);
947
- (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}"`);
948
956
  return shapeUtil;
949
957
  }
950
958
  hasShapeUtil(arg) {
@@ -1733,7 +1741,9 @@ class Editor extends import_eventemitter3.default {
1733
1741
  const selectedShapes = this.getSelectedShapes();
1734
1742
  if (!selectedShapes.length) return;
1735
1743
  const selectedShape = selectedShapes[0];
1736
- 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
+ );
1737
1747
  const sortedChildren = this._getShapesInReadingOrder(children);
1738
1748
  if (sortedChildren.length === 0) return;
1739
1749
  this._selectShapesAndZoom([sortedChildren[0].id]);
@@ -1800,18 +1810,13 @@ class Editor extends import_eventemitter3.default {
1800
1810
  * @internal
1801
1811
  */
1802
1812
  getShapesSharedRotation(shapeIds) {
1803
- let foundFirst = false;
1804
1813
  let rotation = 0;
1805
1814
  for (let i = 0, n = shapeIds.length; i < n; i++) {
1806
- const pageTransform = this.getShapePageTransform(shapeIds[i]);
1807
- if (!pageTransform) continue;
1808
- if (foundFirst) {
1809
- if (pageTransform.rotation() !== rotation) {
1810
- return 0;
1811
- }
1812
- } else {
1813
- foundFirst = true;
1814
- 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;
1815
1820
  }
1816
1821
  }
1817
1822
  return rotation;
@@ -1837,11 +1842,9 @@ class Editor extends import_eventemitter3.default {
1837
1842
  return bounds;
1838
1843
  }
1839
1844
  const boxFromRotatedVertices = import_Box.Box.FromPoints(
1840
- shapeIds.flatMap((id) => {
1841
- const pageTransform = this.getShapePageTransform(id);
1842
- if (!pageTransform) return [];
1843
- return pageTransform.applyToPoints(this.getShapeGeometry(id).bounds.corners);
1844
- }).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))
1845
1848
  );
1846
1849
  boxFromRotatedVertices.point = boxFromRotatedVertices.point.rot(selectionRotation);
1847
1850
  return boxFromRotatedVertices;
@@ -1852,9 +1855,14 @@ class Editor extends import_eventemitter3.default {
1852
1855
  getSelectionRotatedScreenBounds() {
1853
1856
  const bounds = this.getSelectionRotatedPageBounds();
1854
1857
  if (!bounds) return void 0;
1855
- const { x, y } = this.pageToScreen(bounds.point);
1856
- const zoom = this.getZoomLevel();
1857
- 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
+ );
1858
1866
  }
1859
1867
  getFocusedGroupId() {
1860
1868
  return this.getCurrentPageState().focusedGroupId ?? this.getCurrentPageId();
@@ -1955,42 +1963,28 @@ class Editor extends import_eventemitter3.default {
1955
1963
  */
1956
1964
  setEditingShape(shape) {
1957
1965
  const id = typeof shape === "string" ? shape : shape?.id ?? null;
1958
- 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
+ }
1959
1975
  this.run(
1960
1976
  () => {
1961
- const prevEditingShapeId = this.getEditingShapeId();
1962
- if (prevEditingShapeId) {
1963
- const prevEditingShape = this.getShape(prevEditingShapeId);
1964
- if (prevEditingShape) {
1965
- this.getShapeUtil(prevEditingShape).onEditEnd?.(prevEditingShape);
1966
- }
1967
- }
1968
1977
  this._updateCurrentPageState({ editingShapeId: null });
1969
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);
1970
1984
  },
1971
1985
  { history: "ignore" }
1972
1986
  );
1973
- return this;
1974
- }
1975
- if (!this.canEditShape(id)) return this;
1976
- this.run(
1977
- () => {
1978
- const prevEditingShapeId = this.getEditingShapeId();
1979
- if (prevEditingShapeId) {
1980
- const prevEditingShape = this.getShape(prevEditingShapeId);
1981
- if (prevEditingShape) {
1982
- this.getShapeUtil(prevEditingShape).onEditEnd?.(prevEditingShape);
1983
- }
1984
- }
1985
- this._updateCurrentPageState({ editingShapeId: null });
1986
- this._currentRichTextEditor.set(null);
1987
- this.select(id);
1988
- this._updateCurrentPageState({ editingShapeId: id });
1989
- const nextEditingShape = this.getShape(id);
1990
- this.getShapeUtil(nextEditingShape).onEditStart?.(nextEditingShape);
1991
- },
1992
- { history: "ignore" }
1993
- );
1987
+ });
1994
1988
  return this;
1995
1989
  }
1996
1990
  // Rich text editor
@@ -2096,19 +2090,12 @@ class Editor extends import_eventemitter3.default {
2096
2090
  * @public
2097
2091
  */
2098
2092
  setErasingShapes(shapes) {
2099
- 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);
2100
2094
  ids.sort();
2101
2095
  const erasingShapeIds = this.getErasingShapeIds();
2102
2096
  this.run(
2103
2097
  () => {
2104
- if (ids.length === erasingShapeIds.length) {
2105
- for (let i = 0; i < ids.length; i++) {
2106
- if (ids[i] !== erasingShapeIds[i]) {
2107
- this._updateCurrentPageState({ erasingShapeIds: ids });
2108
- break;
2109
- }
2110
- }
2111
- } else {
2098
+ if (!(0, import_utils.areArraysShallowEqual)(ids, erasingShapeIds)) {
2112
2099
  this._updateCurrentPageState({ erasingShapeIds: ids });
2113
2100
  }
2114
2101
  },
@@ -2141,6 +2128,7 @@ class Editor extends import_eventemitter3.default {
2141
2128
  if (!_shape) return false;
2142
2129
  const util = this.getShapeUtil(_shape);
2143
2130
  if (!util.canCrop(_shape)) return false;
2131
+ if (this.getIsReadonly()) return false;
2144
2132
  if (this.isShapeOrAncestorLocked(_shape)) return false;
2145
2133
  return true;
2146
2134
  }
@@ -2205,11 +2193,11 @@ class Editor extends import_eventemitter3.default {
2205
2193
  const collaborators = this.getCollaborators();
2206
2194
  let leaderPresence = null;
2207
2195
  while (targetUserId && !visited.includes(targetUserId)) {
2208
- leaderPresence = collaborators.find((c) => c.userId === targetUserId) ?? null;
2209
- targetUserId = leaderPresence?.followingUserId ?? null;
2210
- if (leaderPresence) {
2211
- visited.push(leaderPresence.userId);
2212
- }
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);
2213
2201
  }
2214
2202
  return leaderPresence;
2215
2203
  }
@@ -2272,39 +2260,7 @@ class Editor extends import_eventemitter3.default {
2272
2260
  *
2273
2261
  * @public */
2274
2262
  getInitialZoom() {
2275
- const cameraOptions = this.getCameraOptions();
2276
- if (!cameraOptions.constraints) return 1;
2277
- if (cameraOptions.constraints.initialZoom === "default") return 1;
2278
- const { zx, zy } = getCameraFitXFitY(this, cameraOptions);
2279
- switch (cameraOptions.constraints.initialZoom) {
2280
- case "fit-min": {
2281
- return Math.max(zx, zy);
2282
- }
2283
- case "fit-max": {
2284
- return Math.min(zx, zy);
2285
- }
2286
- case "fit-x": {
2287
- return zx;
2288
- }
2289
- case "fit-y": {
2290
- return zy;
2291
- }
2292
- case "fit-min-100": {
2293
- return Math.min(1, Math.max(zx, zy));
2294
- }
2295
- case "fit-max-100": {
2296
- return Math.min(1, Math.min(zx, zy));
2297
- }
2298
- case "fit-x-100": {
2299
- return Math.min(1, zx);
2300
- }
2301
- case "fit-y-100": {
2302
- return Math.min(1, zy);
2303
- }
2304
- default: {
2305
- throw (0, import_utils.exhaustiveSwitchError)(cameraOptions.constraints.initialZoom);
2306
- }
2307
- }
2263
+ return this._getFitZoom(this.getCameraOptions().constraints?.initialZoom ?? "default");
2308
2264
  }
2309
2265
  /**
2310
2266
  * Get the camera's base level for calculating actual zoom levels based on the zoom steps.
@@ -2316,11 +2272,13 @@ class Editor extends import_eventemitter3.default {
2316
2272
  *
2317
2273
  * @public */
2318
2274
  getBaseZoom() {
2275
+ return this._getFitZoom(this.getCameraOptions().constraints?.baseZoom ?? "default");
2276
+ }
2277
+ _getFitZoom(fit) {
2319
2278
  const cameraOptions = this.getCameraOptions();
2320
- if (!cameraOptions.constraints) return 1;
2321
- if (cameraOptions.constraints.baseZoom === "default") return 1;
2279
+ if (!cameraOptions.constraints || fit === "default") return 1;
2322
2280
  const { zx, zy } = getCameraFitXFitY(this, cameraOptions);
2323
- switch (cameraOptions.constraints.baseZoom) {
2281
+ switch (fit) {
2324
2282
  case "fit-min": {
2325
2283
  return Math.max(zx, zy);
2326
2284
  }
@@ -2346,7 +2304,7 @@ class Editor extends import_eventemitter3.default {
2346
2304
  return Math.min(1, zy);
2347
2305
  }
2348
2306
  default: {
2349
- throw (0, import_utils.exhaustiveSwitchError)(cameraOptions.constraints.baseZoom);
2307
+ throw (0, import_utils.exhaustiveSwitchError)(fit);
2350
2308
  }
2351
2309
  }
2352
2310
  }
@@ -2380,7 +2338,7 @@ class Editor extends import_eventemitter3.default {
2380
2338
  ...this._cameraOptions.__unsafe__getWithoutCapture(),
2381
2339
  ...opts
2382
2340
  });
2383
- if (next.zoomSteps?.length < 1) next.zoomSteps = [1];
2341
+ if (!next.zoomSteps || next.zoomSteps.length < 1) next.zoomSteps = [1];
2384
2342
  this._cameraOptions.set(next);
2385
2343
  this.setCamera(this.getCamera());
2386
2344
  return this;
@@ -2840,9 +2798,9 @@ class Editor extends import_eventemitter3.default {
2840
2798
  }
2841
2799
  const remaining = duration - elapsed;
2842
2800
  const t = easing(1 - remaining / duration);
2843
- const left = start.minX + (end.minX - start.minX) * t;
2844
- const top = start.minY + (end.minY - start.minY) * t;
2845
- 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);
2846
2804
  this._setCamera(new import_Vec.Vec(-left, -top, this.getViewportScreenBounds().width / (right - left)), {
2847
2805
  force: true
2848
2806
  });
@@ -2920,7 +2878,7 @@ class Editor extends import_eventemitter3.default {
2920
2878
  let newCy = cy + dy;
2921
2879
  let newCz = cz;
2922
2880
  if (dirZ !== 0) {
2923
- newCz = cz * (1 + dirZ * currentSpeed * elapsed);
2881
+ newCz = cz * Math.exp(dirZ * currentSpeed * elapsed);
2924
2882
  const center = this.getViewportScreenCenter();
2925
2883
  newCx += center.x / newCz - center.x / cz;
2926
2884
  newCy += center.y / newCz - center.y / cz;
@@ -3358,12 +3316,12 @@ class Editor extends import_eventemitter3.default {
3358
3316
  const renderingShapes = [];
3359
3317
  let nextIndex = this.options.maxShapesPerPage * 2;
3360
3318
  let nextBackgroundIndex = this.options.maxShapesPerPage;
3361
- const erasingShapeIds = this.getErasingShapeIds();
3319
+ const erasingShapeIds = new Set(this.getErasingShapeIds());
3362
3320
  const addShapeById = (id, opacity, isAncestorErasing) => {
3363
3321
  const shape = this.getShape(id);
3364
3322
  if (!shape) return;
3365
3323
  if (this.isShapeHidden(shape)) {
3366
- const isErasing = isAncestorErasing || erasingShapeIds.includes(id);
3324
+ const isErasing = isAncestorErasing || erasingShapeIds.has(id);
3367
3325
  for (const childId of this.getSortedChildIdsForParent(id)) {
3368
3326
  addShapeById(childId, opacity, isErasing);
3369
3327
  }
@@ -3373,7 +3331,7 @@ class Editor extends import_eventemitter3.default {
3373
3331
  let isShapeErasing = false;
3374
3332
  const util = this.getShapeUtil(shape);
3375
3333
  if (useEditorState) {
3376
- isShapeErasing = !isAncestorErasing && erasingShapeIds.includes(id);
3334
+ isShapeErasing = !isAncestorErasing && erasingShapeIds.has(id);
3377
3335
  if (isShapeErasing) {
3378
3336
  opacity *= 0.32;
3379
3337
  }
@@ -3965,10 +3923,8 @@ class Editor extends import_eventemitter3.default {
3965
3923
  }
3966
3924
  _getShapePageBoundsCache() {
3967
3925
  return this.store.createComputedCache("pageBoundsCache", (shape) => {
3968
- const pageTransform = this.getShapePageTransform(shape);
3969
- if (!pageTransform) return void 0;
3970
3926
  return import_Box.Box.FromPoints(
3971
- pageTransform.applyToPoints(this.getShapeGeometry(shape).boundsVertices)
3927
+ this.getShapePageTransform(shape).applyToPoints(this.getShapeGeometry(shape).boundsVertices)
3972
3928
  );
3973
3929
  });
3974
3930
  }
@@ -4517,9 +4473,7 @@ class Editor extends import_eventemitter3.default {
4517
4473
  const freshShape = this.getShape(id);
4518
4474
  if (!freshShape) return new import_Vec.Vec(0, 0);
4519
4475
  if ((0, import_tlschema.isPageId)(freshShape.parentId)) return import_Vec.Vec.From(point);
4520
- const parentTransform = this.getShapePageTransform(freshShape.parentId);
4521
- if (!parentTransform) return import_Vec.Vec.From(point);
4522
- return parentTransform.clone().invert().applyToPoint(point);
4476
+ return this.getShapePageTransform(freshShape.parentId).clone().invert().applyToPoint(point);
4523
4477
  }
4524
4478
  getCurrentPageShapes() {
4525
4479
  return Array.from(this.getCurrentPageShapeIds(), (id) => this.store.get(id));
@@ -4631,23 +4585,7 @@ class Editor extends import_eventemitter3.default {
4631
4585
  * @public
4632
4586
  */
4633
4587
  isShapeInPage(shape, pageId = this.getCurrentPageId()) {
4634
- const id = typeof shape === "string" ? shape : shape.id;
4635
- const shapeToCheck = this.getShape(id);
4636
- if (!shapeToCheck) return false;
4637
- let shapeIsInPage = false;
4638
- if (shapeToCheck.parentId === pageId) {
4639
- shapeIsInPage = true;
4640
- } else {
4641
- let parent = this.getShape(shapeToCheck.parentId);
4642
- isInPageSearch: while (parent) {
4643
- if (parent.parentId === pageId) {
4644
- shapeIsInPage = true;
4645
- break isInPageSearch;
4646
- }
4647
- parent = this.getShape(parent.parentId);
4648
- }
4649
- }
4650
- return shapeIsInPage;
4588
+ return this.getAncestorPageId(shape) === pageId;
4651
4589
  }
4652
4590
  /**
4653
4591
  * Get the id of the containing page for a given shape.
@@ -4728,10 +4666,7 @@ class Editor extends import_eventemitter3.default {
4728
4666
  for (let i = 0; i < shapesToReparent.length; i++) {
4729
4667
  const shape = shapesToReparent[i];
4730
4668
  const pageTransform = this.getShapePageTransform(shape);
4731
- if (!pageTransform) continue;
4732
- const pagePoint = pageTransform.point();
4733
- if (!pagePoint) continue;
4734
- const newPoint = invertedParentTransform.applyToPoint(pagePoint);
4669
+ const newPoint = invertedParentTransform.applyToPoint(pageTransform.point());
4735
4670
  const newRotation = pageTransform.rotation() - parentPageRotation;
4736
4671
  if (shape.id === parentId) {
4737
4672
  throw Error("Attempted to reparent a shape to itself!");
@@ -4820,7 +4755,7 @@ class Editor extends import_eventemitter3.default {
4820
4755
  */
4821
4756
  getShapeAndDescendantIds(ids) {
4822
4757
  const shapeIds = /* @__PURE__ */ new Set();
4823
- for (const shape of ids.map((id) => this.getShape(id)).sort(import_utils.sortByIndex)) {
4758
+ for (const shape of (0, import_utils.compact)(ids.map((id) => this.getShape(id))).sort(import_utils.sortByIndex)) {
4824
4759
  shapeIds.add(shape.id);
4825
4760
  this.visitDescendants(shape, (descendantId) => {
4826
4761
  shapeIds.add(descendantId);
@@ -4842,11 +4777,12 @@ class Editor extends import_eventemitter3.default {
4842
4777
  const draggingShapes = (0, import_utils.compact)(droppingShapes.map((s) => this.getShape(s))).filter(
4843
4778
  (s) => !s.isLocked && !this.isShapeHidden(s)
4844
4779
  );
4780
+ const excludedIds = this.getShapeAndDescendantIds(draggingShapes.map((s) => s.id));
4845
4781
  const maybeDraggingOverShapes = this.getShapesAtPoint(point, {
4846
4782
  hitInside: true,
4847
4783
  margin: 0
4848
4784
  }).filter(
4849
- (s) => !droppingShapes.includes(s) && !s.isLocked && !this.isShapeHidden(s) && !draggingShapes.includes(s)
4785
+ (s) => !droppingShapes.includes(s) && !s.isLocked && !this.isShapeHidden(s) && !excludedIds.has(s.id)
4850
4786
  );
4851
4787
  for (const maybeDraggingOverShape of maybeDraggingOverShapes) {
4852
4788
  const shapeUtil = this.getShapeUtil(maybeDraggingOverShape);
@@ -5099,8 +5035,7 @@ class Editor extends import_eventemitter3.default {
5099
5035
  for (const id of ids) {
5100
5036
  const shape = this.getShape(id);
5101
5037
  const localDelta = import_Vec.Vec.From(offset);
5102
- const parentTransform = this.getShapeParentTransform(shape);
5103
- if (parentTransform) localDelta.rot(-parentTransform.rotation());
5038
+ localDelta.rot(-this.getShapeParentTransform(shape).rotation());
5104
5039
  changes.push(this.getChangesToTranslateShape(shape, localDelta.add(shape)));
5105
5040
  }
5106
5041
  this.updateShapes(changes);
@@ -5155,7 +5090,7 @@ class Editor extends import_eventemitter3.default {
5155
5090
  if (!originalShape) continue;
5156
5091
  let ox = 0;
5157
5092
  let oy = 0;
5158
- if (offset && initialIds.has(originalId)) {
5093
+ if (offset && initialIds.has(originalId) && !shapeIdSet.has(originalShape.parentId)) {
5159
5094
  const parentTransform = this.getShapeParentTransform(originalShape);
5160
5095
  const vec = new import_Vec.Vec(offset.x, offset.y).rot(-parentTransform.rotation());
5161
5096
  ox = vec.x;
@@ -5269,26 +5204,17 @@ class Editor extends import_eventemitter3.default {
5269
5204
  toggleLock(shapes) {
5270
5205
  const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
5271
5206
  if (this.getIsReadonly() || ids.length === 0) return this;
5272
- let allLocked = true, allUnlocked = true;
5207
+ let allLocked = true;
5273
5208
  const shapesToToggle = [];
5274
5209
  for (const id of ids) {
5275
5210
  const shape = this.getShape(id);
5276
5211
  if (shape) {
5277
5212
  shapesToToggle.push(shape);
5278
- if (shape.isLocked) {
5279
- allUnlocked = false;
5280
- } else {
5281
- allLocked = false;
5282
- }
5213
+ if (!shape.isLocked) allLocked = false;
5283
5214
  }
5284
5215
  }
5285
5216
  this.run(() => {
5286
- if (allUnlocked) {
5287
- this.updateShapes(
5288
- shapesToToggle.map((shape) => ({ id: shape.id, type: shape.type, isLocked: true }))
5289
- );
5290
- this.setSelectedShapes([]);
5291
- } else if (allLocked) {
5217
+ if (allLocked) {
5292
5218
  this.updateShapes(
5293
5219
  shapesToToggle.map((shape) => ({ id: shape.id, type: shape.type, isLocked: false }))
5294
5220
  );
@@ -5296,6 +5222,7 @@ class Editor extends import_eventemitter3.default {
5296
5222
  this.updateShapes(
5297
5223
  shapesToToggle.map((shape) => ({ id: shape.id, type: shape.type, isLocked: true }))
5298
5224
  );
5225
+ this.setSelectedShapes([]);
5299
5226
  }
5300
5227
  });
5301
5228
  return this;
@@ -5406,9 +5333,10 @@ class Editor extends import_eventemitter3.default {
5406
5333
  const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
5407
5334
  let freshShapes = (0, import_utils.compact)(ids.map((id) => this.getShape(id)));
5408
5335
  if (opts?.filterAxisAligned) {
5409
- freshShapes = freshShapes.filter(
5410
- (s) => this.getShapePageTransform(s)?.rotation() % (import_utils2.PI / 2) === 0
5411
- );
5336
+ freshShapes = freshShapes.filter((s) => {
5337
+ const remainder = Math.abs(this.getShapePageTransform(s).rotation() % import_utils2.HALF_PI);
5338
+ return Math.min(remainder, import_utils2.HALF_PI - remainder) < 1e-9;
5339
+ });
5412
5340
  }
5413
5341
  const clusters = [];
5414
5342
  const allBounds = [];
@@ -5573,9 +5501,9 @@ class Editor extends import_eventemitter3.default {
5573
5501
  dim = "height";
5574
5502
  }
5575
5503
  let shapeGap = 0;
5504
+ shapeClustersToStack.sort((a, b) => a.pageBounds[min] - b.pageBounds[min]);
5576
5505
  if (_gap === 0) {
5577
5506
  const gaps = {};
5578
- shapeClustersToStack.sort((a, b) => a.pageBounds[min] - b.pageBounds[min]);
5579
5507
  for (let i = 0; i < len - 1; i++) {
5580
5508
  const currCluster = shapeClustersToStack[i];
5581
5509
  const nextCluster = shapeClustersToStack[i + 1];
@@ -5613,8 +5541,7 @@ class Editor extends import_eventemitter3.default {
5613
5541
  const shapeDelta = delta.clone();
5614
5542
  const parent = this.getShapeParent(shape);
5615
5543
  if (parent) {
5616
- const parentTransform = this.getShapePageTransform(parent);
5617
- if (parentTransform) shapeDelta.rot(-parentTransform.rotation());
5544
+ shapeDelta.rot(-this.getShapePageTransform(parent).rotation());
5618
5545
  }
5619
5546
  shapeDelta.add(shape);
5620
5547
  changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
@@ -5652,7 +5579,7 @@ class Editor extends import_eventemitter3.default {
5652
5579
  }
5653
5580
  const commonBounds = import_Box.Box.Common(allBounds);
5654
5581
  const maxWidth = commonBounds.width;
5655
- shapeClustersToPack.sort((a, b) => a.pageBounds.width - b.pageBounds.width).sort((a, b) => a.pageBounds.height - b.pageBounds.height);
5582
+ shapeClustersToPack.sort((a, b) => b.pageBounds.width - a.pageBounds.width).sort((a, b) => b.pageBounds.height - a.pageBounds.height);
5656
5583
  const startWidth = Math.max(Math.ceil(Math.sqrt(area / 0.95)), maxWidth);
5657
5584
  const spaces = [new import_Box.Box(commonBounds.x, commonBounds.y, startWidth, Infinity)];
5658
5585
  let width = 0;
@@ -5700,8 +5627,7 @@ class Editor extends import_eventemitter3.default {
5700
5627
  const shapeDelta = delta.clone();
5701
5628
  const parent = this.getShapeParent(shape);
5702
5629
  if (parent) {
5703
- const parentTransform = this.getShapeParentTransform(shape);
5704
- if (parentTransform) shapeDelta.rot(-parentTransform.rotation());
5630
+ shapeDelta.rot(-this.getShapeParentTransform(shape).rotation());
5705
5631
  }
5706
5632
  shapeDelta.add(shape);
5707
5633
  changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
@@ -5767,8 +5693,7 @@ class Editor extends import_eventemitter3.default {
5767
5693
  const shapeDelta = delta.clone();
5768
5694
  const parent = this.getShapeParent(shape);
5769
5695
  if (parent) {
5770
- const parentTransform = this.getShapePageTransform(parent);
5771
- if (parentTransform) shapeDelta.rot(-parentTransform.rotation());
5696
+ shapeDelta.rot(-this.getShapePageTransform(parent).rotation());
5772
5697
  }
5773
5698
  shapeDelta.add(shape);
5774
5699
  changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
@@ -5842,8 +5767,7 @@ class Editor extends import_eventemitter3.default {
5842
5767
  const shapeDelta = delta.clone();
5843
5768
  const parent = this.getShapeParent(shape);
5844
5769
  if (parent) {
5845
- const parentTransform = this.getShapePageTransform(parent);
5846
- if (parentTransform) shapeDelta.rot(-parentTransform.rotation());
5770
+ shapeDelta.rot(-this.getShapePageTransform(parent).rotation());
5847
5771
  }
5848
5772
  shapeDelta.add(shape);
5849
5773
  changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
@@ -5898,8 +5822,7 @@ class Editor extends import_eventemitter3.default {
5898
5822
  scale[val] = commonBounds[dim] / pageBounds[dim];
5899
5823
  for (const shape of shapes2) {
5900
5824
  const shapeLocalOffset = localOffset.clone();
5901
- const parentTransform = this.getShapeParentTransform(shape);
5902
- if (parentTransform) shapeLocalOffset.rot(-parentTransform.rotation());
5825
+ shapeLocalOffset.rot(-this.getShapeParentTransform(shape).rotation());
5903
5826
  shapeLocalOffset.add(shape);
5904
5827
  const changes = this.getChangesToTranslateShape(shape, shapeLocalOffset);
5905
5828
  this.updateShape(changes);
@@ -5955,8 +5878,7 @@ class Editor extends import_eventemitter3.default {
5955
5878
  );
5956
5879
  for (const shape of shapes2) {
5957
5880
  const shapeLocalOffset = localOffset.clone();
5958
- const parentTransform = this.getShapeParentTransform(shape);
5959
- if (parentTransform) shapeLocalOffset.rot(-parentTransform.rotation());
5881
+ shapeLocalOffset.rot(-this.getShapeParentTransform(shape).rotation());
5960
5882
  shapeLocalOffset.add(shape);
5961
5883
  const changes = this.getChangesToTranslateShape(shape, shapeLocalOffset);
5962
5884
  this.updateShape(changes);
@@ -6005,9 +5927,7 @@ class Editor extends import_eventemitter3.default {
6005
5927
  const scaleOrigin = opts.scaleOrigin ?? this.getShapePageBounds(id)?.center;
6006
5928
  if (!scaleOrigin) return null;
6007
5929
  const pageTransform = opts.initialPageTransform ? import_Mat.Mat.Cast(opts.initialPageTransform) : this.getShapePageTransform(id);
6008
- if (!pageTransform) return null;
6009
5930
  const pageRotation = pageTransform.rotation();
6010
- if (pageRotation == null) return null;
6011
5931
  const scaleAxisRotation = opts.scaleAxisRotation ?? pageRotation;
6012
5932
  const initialBounds = opts.initialBounds ?? this.getShapeGeometry(id).bounds;
6013
5933
  if (!initialBounds) return null;
@@ -6158,7 +6078,6 @@ class Editor extends import_eventemitter3.default {
6158
6078
  const currentLocalBounds = this.getShapeGeometry(id).bounds;
6159
6079
  const currentPageCenter = import_Mat.Mat.applyToPoint(pageTransform, currentLocalBounds.center);
6160
6080
  const shapePageTransformOrigin = pageTransform.point();
6161
- if (!currentPageCenter || !shapePageTransformOrigin) return this;
6162
6081
  const pageDelta = import_Vec.Vec.Sub(postScaleShapePageCenter, currentPageCenter);
6163
6082
  const postScaleShapePagePoint = import_Vec.Vec.Add(shapePageTransformOrigin, pageDelta);
6164
6083
  const { x, y } = this.getPointInParentSpace(id, postScaleShapePagePoint);
@@ -6417,10 +6336,10 @@ class Editor extends import_eventemitter3.default {
6417
6336
  if (animationIdForShape !== animationId) continue;
6418
6337
  updates.push({
6419
6338
  ...end,
6420
- x: start.x + (end.x - start.x) * t,
6421
- y: start.y + (end.y - start.y) * t,
6422
- opacity: start.opacity + (end.opacity - start.opacity) * t,
6423
- rotation: start.rotation + (end.rotation - start.rotation) * t,
6339
+ x: (0, import_utils.lerp)(start.x, end.x, t),
6340
+ y: (0, import_utils.lerp)(start.y, end.y, t),
6341
+ opacity: (0, import_utils.lerp)(start.opacity, end.opacity, t),
6342
+ rotation: (0, import_utils.lerp)(start.rotation, end.rotation, t),
6424
6343
  props: this.getShapeUtil(end).getInterpolatedProps?.(start, end, t) ?? end.props
6425
6344
  });
6426
6345
  }
@@ -6546,7 +6465,7 @@ class Editor extends import_eventemitter3.default {
6546
6465
  * @public
6547
6466
  */
6548
6467
  updateShapes(partials) {
6549
- const compactedPartials = Array(partials.length);
6468
+ const compactedPartials = [];
6550
6469
  for (let i = 0, n = partials.length; i < n; i++) {
6551
6470
  const partial = partials[i];
6552
6471
  if (!partial) continue;
@@ -6617,28 +6536,32 @@ class Editor extends import_eventemitter3.default {
6617
6536
  }
6618
6537
  /* --------------------- Styles --------------------- */
6619
6538
  /**
6620
- * Get all the current styles among the users selected shapes
6539
+ * Groups have no styles of their own: a style read or write on a selection applies to the
6540
+ * non-group shapes beneath each group, however deeply nested. Returns those shapes.
6621
6541
  *
6622
6542
  * @internal
6623
6543
  */
6624
- _extractSharedStyles(shape, sharedStyleMap) {
6625
- if (this.isShapeOfType(shape, "group")) {
6626
- const childIds = this._parentIdsToChildIds.get()[shape.id];
6627
- if (!childIds) return;
6628
- for (let i = 0, n = childIds.length; i < n; i++) {
6629
- this._extractSharedStyles(this.getShape(childIds[i]), sharedStyleMap);
6630
- }
6631
- } else {
6632
- for (const [style, propKey] of this.styleProps[shape.type]) {
6633
- sharedStyleMap.applyValue(style, (0, import_utils.getOwnProperty)(shape.props, propKey));
6544
+ _getStyleableShapes(shapes) {
6545
+ const result = [];
6546
+ const visit = (shape) => {
6547
+ if (this.isShapeOfType(shape, "group")) {
6548
+ for (const childId of this.getSortedChildIdsForParent(shape.id)) {
6549
+ const child = this.getShape(childId);
6550
+ if (child) visit(child);
6551
+ }
6552
+ } else {
6553
+ result.push(shape);
6634
6554
  }
6635
- }
6555
+ };
6556
+ for (const shape of shapes) visit(shape);
6557
+ return result;
6636
6558
  }
6637
6559
  _getSelectionSharedStyles() {
6638
- const selectedShapes = this.getSelectedShapes();
6639
6560
  const sharedStyles = new import_SharedStylesMap.SharedStyleMap();
6640
- for (const selectedShape of selectedShapes) {
6641
- this._extractSharedStyles(selectedShape, sharedStyles);
6561
+ for (const shape of this._getStyleableShapes(this.getSelectedShapes())) {
6562
+ for (const [style, propKey] of this.styleProps[shape.type]) {
6563
+ sharedStyles.applyValue(style, (0, import_utils.getOwnProperty)(shape.props, propKey));
6564
+ }
6642
6565
  }
6643
6566
  return sharedStyles;
6644
6567
  }
@@ -6670,38 +6593,16 @@ class Editor extends import_eventemitter3.default {
6670
6593
  const styles = new import_SharedStylesMap.SharedStyleMap();
6671
6594
  if (!currentTool) return styles;
6672
6595
  if (currentTool.shapeType) {
6673
- if (currentTool.shapeType === "frame" && !this.getShapeUtil("frame").options.showColors) {
6674
- for (const style of this.styleProps[currentTool.shapeType].keys()) {
6675
- if (style.id === "tldraw:color") continue;
6676
- styles.applyValue(style, this.getStyleForNextShape(style));
6677
- }
6678
- } else {
6679
- for (const style of this.styleProps[currentTool.shapeType].keys()) {
6680
- styles.applyValue(style, this.getStyleForNextShape(style));
6681
- }
6596
+ for (const style of this.styleProps[currentTool.shapeType].keys()) {
6597
+ styles.applyValue(style, this.getStyleForNextShape(style));
6682
6598
  }
6683
6599
  }
6684
6600
  return styles;
6685
6601
  }
6686
6602
  getSharedOpacity() {
6687
6603
  if (this.isIn("select") && this.getSelectedShapeIds().length > 0) {
6688
- const shapesToCheck = [];
6689
- const addShape = (shapeId) => {
6690
- const shape = this.getShape(shapeId);
6691
- if (!shape) return;
6692
- if (this.isShapeOfType(shape, "group")) {
6693
- for (const childId of this.getSortedChildIdsForParent(shape.id)) {
6694
- addShape(childId);
6695
- }
6696
- } else {
6697
- shapesToCheck.push(shape);
6698
- }
6699
- };
6700
- for (const shapeId of this.getSelectedShapeIds()) {
6701
- addShape(shapeId);
6702
- }
6703
6604
  let opacity = null;
6704
- for (const shape of shapesToCheck) {
6605
+ for (const shape of this._getStyleableShapes(this.getSelectedShapes())) {
6705
6606
  if (opacity === null) {
6706
6607
  opacity = shape.opacity;
6707
6608
  } else if (opacity !== shape.opacity) {
@@ -6740,28 +6641,12 @@ class Editor extends import_eventemitter3.default {
6740
6641
  setOpacityForSelectedShapes(opacity) {
6741
6642
  const selectedShapes = this.getSelectedShapes();
6742
6643
  if (selectedShapes.length > 0) {
6743
- const shapesToUpdate = [];
6744
- const addShapeById = (shape) => {
6745
- if (this.isShapeOfType(shape, "group")) {
6746
- const childIds = this.getSortedChildIdsForParent(shape);
6747
- for (const childId of childIds) {
6748
- addShapeById(this.getShape(childId));
6749
- }
6750
- } else {
6751
- shapesToUpdate.push(shape);
6752
- }
6753
- };
6754
- for (const id of selectedShapes) {
6755
- addShapeById(id);
6756
- }
6757
6644
  this.updateShapes(
6758
- shapesToUpdate.map((shape) => {
6759
- return {
6760
- id: shape.id,
6761
- type: shape.type,
6762
- opacity
6763
- };
6764
- })
6645
+ this._getStyleableShapes(selectedShapes).map((shape) => ({
6646
+ id: shape.id,
6647
+ type: shape.type,
6648
+ opacity
6649
+ }))
6765
6650
  );
6766
6651
  }
6767
6652
  return this;
@@ -6806,33 +6691,17 @@ class Editor extends import_eventemitter3.default {
6806
6691
  const selectedShapes = this.getSelectedShapes();
6807
6692
  if (selectedShapes.length > 0) {
6808
6693
  const updates = [];
6809
- const addShapeById = (shape) => {
6810
- if (this.isShapeOfType(shape, "group")) {
6811
- const childIds = this.getSortedChildIdsForParent(shape.id);
6812
- for (const childId of childIds) {
6813
- addShapeById(this.getShape(childId));
6814
- }
6815
- } else {
6816
- const util = this.getShapeUtil(shape);
6817
- const stylePropKey = this.styleProps[shape.type].get(style);
6818
- if (stylePropKey) {
6819
- const shapePartial = {
6820
- id: shape.id,
6821
- type: shape.type,
6822
- props: { [stylePropKey]: value }
6823
- };
6824
- updates.push({
6825
- util,
6826
- originalShape: shape,
6827
- updatePartial: shapePartial
6828
- });
6829
- }
6694
+ for (const shape of this._getStyleableShapes(selectedShapes)) {
6695
+ const stylePropKey = this.styleProps[shape.type].get(style);
6696
+ if (stylePropKey) {
6697
+ updates.push({
6698
+ id: shape.id,
6699
+ type: shape.type,
6700
+ props: { [stylePropKey]: value }
6701
+ });
6830
6702
  }
6831
- };
6832
- for (const shape of selectedShapes) {
6833
- addShapeById(shape);
6834
6703
  }
6835
- this.updateShapes(updates.map(({ updatePartial }) => updatePartial));
6704
+ this.updateShapes(updates);
6836
6705
  }
6837
6706
  return this;
6838
6707
  }
@@ -7228,7 +7097,6 @@ class Editor extends import_eventemitter3.default {
7228
7097
  if (shapeIdMap.has(newShape.parentId)) {
7229
7098
  newShape.parentId = shapeIdMap.get(oldShape.parentId);
7230
7099
  } else {
7231
- rootShapeIds.push(newShape.id);
7232
7100
  newShape.index = index;
7233
7101
  index = (0, import_utils.getIndexAbove)(index);
7234
7102
  }
@@ -7253,8 +7121,11 @@ class Editor extends import_eventemitter3.default {
7253
7121
  continue;
7254
7122
  }
7255
7123
  if (asset.type === "image" && asset.props.src?.startsWith("data:image") || asset.type === "video" && asset.props.src?.startsWith("data:video")) {
7256
- assetsToUpdate.push((0, import_utils.structuredClone)(asset));
7257
- asset.props.src = null;
7124
+ assetsToUpdate.push(asset);
7125
+ const assetWithoutSrc = (0, import_utils.structuredClone)(asset);
7126
+ assetWithoutSrc.props.src = null;
7127
+ assetsToCreate.push(assetWithoutSrc);
7128
+ continue;
7258
7129
  }
7259
7130
  assetsToCreate.push(asset);
7260
7131
  }