@tldraw/editor 3.16.0-internal.a478398270c6 → 3.16.0-next.15f085081fd5

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 (235) hide show
  1. package/dist-cjs/index.d.ts +243 -16
  2. package/dist-cjs/index.js +8 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/TldrawEditor.js +8 -2
  5. package/dist-cjs/lib/TldrawEditor.js.map +2 -2
  6. package/dist-cjs/lib/components/MenuClickCapture.js +0 -5
  7. package/dist-cjs/lib/components/MenuClickCapture.js.map +2 -2
  8. package/dist-cjs/lib/components/SVGContainer.js +1 -1
  9. package/dist-cjs/lib/components/SVGContainer.js.map +2 -2
  10. package/dist-cjs/lib/components/Shape.js +11 -36
  11. package/dist-cjs/lib/components/Shape.js.map +2 -2
  12. package/dist-cjs/lib/components/default-components/DefaultBrush.js +1 -1
  13. package/dist-cjs/lib/components/default-components/DefaultBrush.js.map +2 -2
  14. package/dist-cjs/lib/components/default-components/DefaultCanvas.js +5 -24
  15. package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
  16. package/dist-cjs/lib/components/default-components/DefaultCollaboratorHint.js +2 -2
  17. package/dist-cjs/lib/components/default-components/DefaultCollaboratorHint.js.map +2 -2
  18. package/dist-cjs/lib/components/default-components/DefaultCursor.js +1 -1
  19. package/dist-cjs/lib/components/default-components/DefaultCursor.js.map +2 -2
  20. package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js +1 -1
  21. package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js.map +2 -2
  22. package/dist-cjs/lib/components/default-components/DefaultGrid.js +1 -1
  23. package/dist-cjs/lib/components/default-components/DefaultGrid.js.map +2 -2
  24. package/dist-cjs/lib/components/default-components/DefaultHandles.js +1 -1
  25. package/dist-cjs/lib/components/default-components/DefaultHandles.js.map +2 -2
  26. package/dist-cjs/lib/components/default-components/DefaultScribble.js +1 -1
  27. package/dist-cjs/lib/components/default-components/DefaultScribble.js.map +2 -2
  28. package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js +9 -1
  29. package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js.map +2 -2
  30. package/dist-cjs/lib/components/default-components/DefaultShapeWrapper.js +53 -0
  31. package/dist-cjs/lib/components/default-components/DefaultShapeWrapper.js.map +7 -0
  32. package/dist-cjs/lib/components/default-components/DefaultSnapIndictor.js +1 -1
  33. package/dist-cjs/lib/components/default-components/DefaultSnapIndictor.js.map +2 -2
  34. package/dist-cjs/lib/components/default-components/DefaultSpinner.js +27 -15
  35. package/dist-cjs/lib/components/default-components/DefaultSpinner.js.map +3 -3
  36. package/dist-cjs/lib/config/TLUserPreferences.js +15 -3
  37. package/dist-cjs/lib/config/TLUserPreferences.js.map +2 -2
  38. package/dist-cjs/lib/editor/Editor.js +151 -67
  39. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  40. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js +14 -4
  41. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +2 -2
  42. package/dist-cjs/lib/editor/shapes/ShapeUtil.js +13 -0
  43. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  44. package/dist-cjs/lib/editor/tools/StateNode.js +20 -1
  45. package/dist-cjs/lib/editor/tools/StateNode.js.map +2 -2
  46. package/dist-cjs/lib/editor/types/misc-types.js.map +1 -1
  47. package/dist-cjs/lib/exports/getSvgJsx.js +35 -16
  48. package/dist-cjs/lib/exports/getSvgJsx.js.map +2 -2
  49. package/dist-cjs/lib/hooks/useCanvasEvents.js +31 -25
  50. package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
  51. package/dist-cjs/lib/hooks/useEditor.js +1 -4
  52. package/dist-cjs/lib/hooks/useEditor.js.map +2 -2
  53. package/dist-cjs/lib/hooks/useEditorComponents.js +2 -0
  54. package/dist-cjs/lib/hooks/useEditorComponents.js.map +2 -2
  55. package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js +4 -1
  56. package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js.map +2 -2
  57. package/dist-cjs/lib/{utils/nearestMultiple.js → hooks/useStateAttribute.js} +15 -14
  58. package/dist-cjs/lib/hooks/useStateAttribute.js.map +7 -0
  59. package/dist-cjs/lib/license/Watermark.js +8 -8
  60. package/dist-cjs/lib/license/Watermark.js.map +2 -2
  61. package/dist-cjs/lib/options.js +7 -0
  62. package/dist-cjs/lib/options.js.map +2 -2
  63. package/dist-cjs/lib/primitives/Box.js +3 -0
  64. package/dist-cjs/lib/primitives/Box.js.map +2 -2
  65. package/dist-cjs/lib/primitives/geometry/Arc2d.js +1 -1
  66. package/dist-cjs/lib/primitives/geometry/Arc2d.js.map +2 -2
  67. package/dist-cjs/lib/primitives/geometry/Circle2d.js +1 -1
  68. package/dist-cjs/lib/primitives/geometry/Circle2d.js.map +2 -2
  69. package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js +3 -1
  70. package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js.map +2 -2
  71. package/dist-cjs/lib/primitives/geometry/Ellipse2d.js +1 -1
  72. package/dist-cjs/lib/primitives/geometry/Ellipse2d.js.map +2 -2
  73. package/dist-cjs/lib/primitives/geometry/geometry-constants.js +2 -2
  74. package/dist-cjs/lib/primitives/geometry/geometry-constants.js.map +2 -2
  75. package/dist-cjs/lib/primitives/intersect.js +4 -4
  76. package/dist-cjs/lib/primitives/intersect.js.map +2 -2
  77. package/dist-cjs/lib/primitives/utils.js +4 -0
  78. package/dist-cjs/lib/primitives/utils.js.map +2 -2
  79. package/dist-cjs/lib/utils/EditorAtom.js +45 -0
  80. package/dist-cjs/lib/utils/EditorAtom.js.map +7 -0
  81. package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js +0 -1
  82. package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js.map +2 -2
  83. package/dist-cjs/version.js +3 -3
  84. package/dist-cjs/version.js.map +1 -1
  85. package/dist-esm/index.d.mts +243 -16
  86. package/dist-esm/index.mjs +16 -2
  87. package/dist-esm/index.mjs.map +2 -2
  88. package/dist-esm/lib/TldrawEditor.mjs +8 -2
  89. package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
  90. package/dist-esm/lib/components/MenuClickCapture.mjs +0 -5
  91. package/dist-esm/lib/components/MenuClickCapture.mjs.map +2 -2
  92. package/dist-esm/lib/components/SVGContainer.mjs +1 -1
  93. package/dist-esm/lib/components/SVGContainer.mjs.map +2 -2
  94. package/dist-esm/lib/components/Shape.mjs +11 -36
  95. package/dist-esm/lib/components/Shape.mjs.map +2 -2
  96. package/dist-esm/lib/components/default-components/DefaultBrush.mjs +1 -1
  97. package/dist-esm/lib/components/default-components/DefaultBrush.mjs.map +2 -2
  98. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +5 -24
  99. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
  100. package/dist-esm/lib/components/default-components/DefaultCollaboratorHint.mjs +2 -2
  101. package/dist-esm/lib/components/default-components/DefaultCollaboratorHint.mjs.map +2 -2
  102. package/dist-esm/lib/components/default-components/DefaultCursor.mjs +1 -1
  103. package/dist-esm/lib/components/default-components/DefaultCursor.mjs.map +2 -2
  104. package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs +1 -1
  105. package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs.map +2 -2
  106. package/dist-esm/lib/components/default-components/DefaultGrid.mjs +1 -1
  107. package/dist-esm/lib/components/default-components/DefaultGrid.mjs.map +2 -2
  108. package/dist-esm/lib/components/default-components/DefaultHandles.mjs +1 -1
  109. package/dist-esm/lib/components/default-components/DefaultHandles.mjs.map +2 -2
  110. package/dist-esm/lib/components/default-components/DefaultScribble.mjs +1 -1
  111. package/dist-esm/lib/components/default-components/DefaultScribble.mjs.map +2 -2
  112. package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs +9 -1
  113. package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs.map +2 -2
  114. package/dist-esm/lib/components/default-components/DefaultShapeWrapper.mjs +23 -0
  115. package/dist-esm/lib/components/default-components/DefaultShapeWrapper.mjs.map +7 -0
  116. package/dist-esm/lib/components/default-components/DefaultSnapIndictor.mjs +1 -1
  117. package/dist-esm/lib/components/default-components/DefaultSnapIndictor.mjs.map +2 -2
  118. package/dist-esm/lib/components/default-components/DefaultSpinner.mjs +17 -15
  119. package/dist-esm/lib/components/default-components/DefaultSpinner.mjs.map +2 -2
  120. package/dist-esm/lib/config/TLUserPreferences.mjs +15 -3
  121. package/dist-esm/lib/config/TLUserPreferences.mjs.map +2 -2
  122. package/dist-esm/lib/editor/Editor.mjs +151 -67
  123. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  124. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs +14 -4
  125. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +2 -2
  126. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +13 -0
  127. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  128. package/dist-esm/lib/editor/tools/StateNode.mjs +20 -1
  129. package/dist-esm/lib/editor/tools/StateNode.mjs.map +2 -2
  130. package/dist-esm/lib/exports/getSvgJsx.mjs +36 -16
  131. package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
  132. package/dist-esm/lib/hooks/useCanvasEvents.mjs +32 -26
  133. package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
  134. package/dist-esm/lib/hooks/useEditor.mjs +1 -4
  135. package/dist-esm/lib/hooks/useEditor.mjs.map +2 -2
  136. package/dist-esm/lib/hooks/useEditorComponents.mjs +4 -0
  137. package/dist-esm/lib/hooks/useEditorComponents.mjs.map +2 -2
  138. package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs +4 -1
  139. package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs.map +2 -2
  140. package/dist-esm/lib/hooks/useStateAttribute.mjs +15 -0
  141. package/dist-esm/lib/hooks/useStateAttribute.mjs.map +7 -0
  142. package/dist-esm/lib/license/Watermark.mjs +8 -8
  143. package/dist-esm/lib/license/Watermark.mjs.map +2 -2
  144. package/dist-esm/lib/options.mjs +7 -0
  145. package/dist-esm/lib/options.mjs.map +2 -2
  146. package/dist-esm/lib/primitives/Box.mjs +4 -1
  147. package/dist-esm/lib/primitives/Box.mjs.map +2 -2
  148. package/dist-esm/lib/primitives/geometry/Arc2d.mjs +2 -2
  149. package/dist-esm/lib/primitives/geometry/Arc2d.mjs.map +2 -2
  150. package/dist-esm/lib/primitives/geometry/Circle2d.mjs +2 -2
  151. package/dist-esm/lib/primitives/geometry/Circle2d.mjs.map +2 -2
  152. package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs +3 -1
  153. package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs.map +2 -2
  154. package/dist-esm/lib/primitives/geometry/Ellipse2d.mjs +2 -2
  155. package/dist-esm/lib/primitives/geometry/Ellipse2d.mjs.map +2 -2
  156. package/dist-esm/lib/primitives/geometry/geometry-constants.mjs +2 -2
  157. package/dist-esm/lib/primitives/geometry/geometry-constants.mjs.map +2 -2
  158. package/dist-esm/lib/primitives/intersect.mjs +5 -5
  159. package/dist-esm/lib/primitives/intersect.mjs.map +2 -2
  160. package/dist-esm/lib/primitives/utils.mjs +4 -0
  161. package/dist-esm/lib/primitives/utils.mjs.map +2 -2
  162. package/dist-esm/lib/utils/EditorAtom.mjs +25 -0
  163. package/dist-esm/lib/utils/EditorAtom.mjs.map +7 -0
  164. package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs +0 -1
  165. package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs.map +2 -2
  166. package/dist-esm/version.mjs +3 -3
  167. package/dist-esm/version.mjs.map +1 -1
  168. package/editor.css +320 -313
  169. package/package.json +16 -38
  170. package/src/index.ts +15 -1
  171. package/src/lib/TldrawEditor.tsx +13 -6
  172. package/src/lib/components/MenuClickCapture.tsx +0 -8
  173. package/src/lib/components/SVGContainer.tsx +1 -1
  174. package/src/lib/components/Shape.tsx +12 -33
  175. package/src/lib/components/default-components/DefaultBrush.tsx +1 -1
  176. package/src/lib/components/default-components/DefaultCanvas.tsx +6 -23
  177. package/src/lib/components/default-components/DefaultCollaboratorHint.tsx +2 -2
  178. package/src/lib/components/default-components/DefaultCursor.tsx +1 -1
  179. package/src/lib/components/default-components/DefaultErrorFallback.tsx +1 -1
  180. package/src/lib/components/default-components/DefaultGrid.tsx +1 -1
  181. package/src/lib/components/default-components/DefaultHandles.tsx +5 -1
  182. package/src/lib/components/default-components/DefaultScribble.tsx +1 -1
  183. package/src/lib/components/default-components/DefaultShapeIndicator.tsx +6 -2
  184. package/src/lib/components/default-components/DefaultShapeWrapper.tsx +35 -0
  185. package/src/lib/components/default-components/DefaultSnapIndictor.tsx +1 -1
  186. package/src/lib/components/default-components/DefaultSpinner.tsx +12 -12
  187. package/src/lib/config/TLUserPreferences.ts +15 -1
  188. package/src/lib/editor/Editor.test.ts +416 -8
  189. package/src/lib/editor/Editor.ts +195 -92
  190. package/src/lib/editor/managers/ClickManager/ClickManager.test.ts +15 -14
  191. package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.test.ts +16 -15
  192. package/src/lib/editor/managers/FocusManager/FocusManager.test.ts +49 -48
  193. package/src/lib/editor/managers/FontManager/FontManager.test.ts +24 -23
  194. package/src/lib/editor/managers/HistoryManager/HistoryManager.test.ts +7 -6
  195. package/src/lib/editor/managers/ScribbleManager/ScribbleManager.test.ts +12 -11
  196. package/src/lib/editor/managers/SnapManager/SnapManager.test.ts +57 -50
  197. package/src/lib/editor/managers/TextManager/TextManager.test.ts +51 -26
  198. package/src/lib/editor/managers/TickManager/TickManager.test.ts +14 -13
  199. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +55 -26
  200. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.ts +14 -1
  201. package/src/lib/editor/shapes/ShapeUtil.ts +71 -0
  202. package/src/lib/editor/tools/StateNode.test.ts +285 -0
  203. package/src/lib/editor/tools/StateNode.ts +27 -1
  204. package/src/lib/editor/types/misc-types.ts +73 -1
  205. package/src/lib/exports/getSvgJsx.test.ts +868 -0
  206. package/src/lib/exports/getSvgJsx.tsx +78 -21
  207. package/src/lib/hooks/useCanvasEvents.ts +45 -38
  208. package/src/lib/hooks/useEditor.tsx +6 -5
  209. package/src/lib/hooks/useEditorComponents.tsx +8 -2
  210. package/src/lib/hooks/usePassThroughWheelEvents.ts +6 -1
  211. package/src/lib/hooks/useStateAttribute.ts +15 -0
  212. package/src/lib/license/LicenseManager.test.ts +3 -1
  213. package/src/lib/license/Watermark.test.tsx +2 -1
  214. package/src/lib/license/Watermark.tsx +8 -8
  215. package/src/lib/options.ts +8 -0
  216. package/src/lib/primitives/Box.test.ts +126 -0
  217. package/src/lib/primitives/Box.ts +10 -1
  218. package/src/lib/primitives/geometry/Arc2d.ts +2 -2
  219. package/src/lib/primitives/geometry/Circle2d.ts +2 -2
  220. package/src/lib/primitives/geometry/CubicBezier2d.ts +4 -1
  221. package/src/lib/primitives/geometry/Ellipse2d.ts +2 -2
  222. package/src/lib/primitives/geometry/geometry-constants.ts +2 -1
  223. package/src/lib/primitives/intersect.test.ts +946 -0
  224. package/src/lib/primitives/intersect.ts +12 -5
  225. package/src/lib/primitives/utils.ts +11 -0
  226. package/src/lib/utils/EditorAtom.ts +37 -0
  227. package/src/lib/utils/sync/LocalIndexedDb.test.ts +2 -1
  228. package/src/lib/utils/sync/TLLocalSyncClient.test.ts +15 -15
  229. package/src/lib/utils/sync/TLLocalSyncClient.ts +0 -1
  230. package/src/version.ts +3 -3
  231. package/dist-cjs/lib/utils/nearestMultiple.js.map +0 -7
  232. package/dist-esm/lib/utils/nearestMultiple.mjs +0 -14
  233. package/dist-esm/lib/utils/nearestMultiple.mjs.map +0 -7
  234. package/src/lib/test/currentToolIdMask.test.ts +0 -49
  235. package/src/lib/utils/nearestMultiple.ts +0 -13
@@ -176,8 +176,10 @@ import {
176
176
  RequiredKeys,
177
177
  TLCameraMoveOptions,
178
178
  TLCameraOptions,
179
+ TLGetShapeAtPointOptions,
179
180
  TLImageExportOptions,
180
181
  TLSvgExportOptions,
182
+ TLUpdatePointerOptions,
181
183
  } from './types/misc-types'
182
184
  import { TLAdjacentDirection, TLResizeHandle } from './types/selection-types'
183
185
 
@@ -1803,7 +1805,9 @@ export class Editor extends EventEmitter<TLEventMap> {
1803
1805
  }
1804
1806
 
1805
1807
  /**
1806
- * Select all direct children of the current page.
1808
+ * Select all shapes. If the user has selected shapes that share a parent,
1809
+ * select all shapes within that parent. If the user has not selected any shapes,
1810
+ * or if the shapes shapes are only on select all shapes on the current page.
1807
1811
  *
1808
1812
  * @example
1809
1813
  * ```ts
@@ -1813,11 +1817,34 @@ export class Editor extends EventEmitter<TLEventMap> {
1813
1817
  * @public
1814
1818
  */
1815
1819
  selectAll(): this {
1816
- const ids = this.getSortedChildIdsForParent(this.getCurrentPageId())
1817
- // page might have no shapes
1820
+ let parentToSelectWithinId: TLParentId | null = null
1821
+
1822
+ const selectedShapeIds = this.getSelectedShapeIds()
1823
+
1824
+ // If we have selected shapes, try to find a parent to select within
1825
+ if (selectedShapeIds.length > 0) {
1826
+ for (const id of selectedShapeIds) {
1827
+ const shape = this.getShape(id)
1828
+ if (!shape) continue
1829
+ if (parentToSelectWithinId === null) {
1830
+ // If we haven't found a parent yet, set this parent as the parent to select within
1831
+ parentToSelectWithinId = shape.parentId
1832
+ } else if (parentToSelectWithinId !== shape.parentId) {
1833
+ // If we've found two different parents, we can't select all, do nothing
1834
+ return this
1835
+ }
1836
+ }
1837
+ }
1838
+
1839
+ // If we haven't found a parent from our selected shapes, select the current page
1840
+ if (!parentToSelectWithinId) {
1841
+ parentToSelectWithinId = this.getCurrentPageId()
1842
+ }
1843
+
1844
+ // Select all the unlocked shapes within the parent
1845
+ const ids = this.getSortedChildIdsForParent(parentToSelectWithinId)
1818
1846
  if (ids.length <= 0) return this
1819
1847
  this.setSelectedShapes(this._getUnlockedShapeIds(ids))
1820
-
1821
1848
  return this
1822
1849
  }
1823
1850
 
@@ -1838,10 +1865,11 @@ export class Editor extends EventEmitter<TLEventMap> {
1838
1865
  firstParentId &&
1839
1866
  selectedShapeIds.every((shapeId) => this.getShape(shapeId)?.parentId === firstParentId) &&
1840
1867
  !isPageId(firstParentId)
1868
+ const filteredShapes = isSelectedWithinContainer
1869
+ ? this.getCurrentPageShapes().filter((shape) => shape.parentId === firstParentId)
1870
+ : this.getCurrentPageShapes().filter((shape) => isPageId(shape.parentId))
1841
1871
  const readingOrderShapes = isSelectedWithinContainer
1842
- ? this._getShapesInReadingOrder(
1843
- this.getCurrentPageShapes().filter((shape) => shape.parentId === firstParentId)
1844
- )
1872
+ ? this._getShapesInReadingOrder(filteredShapes)
1845
1873
  : this.getCurrentPageShapesInReadingOrder()
1846
1874
  const currentShapeId: TLShapeId | undefined =
1847
1875
  selectedShapeIds.length === 1
@@ -1858,7 +1886,7 @@ export class Editor extends EventEmitter<TLEventMap> {
1858
1886
  adjacentShapeId = shapeIds[adjacentIndex]
1859
1887
  } else {
1860
1888
  if (!currentShapeId) return
1861
- adjacentShapeId = this.getNearestAdjacentShape(currentShapeId, direction)
1889
+ adjacentShapeId = this.getNearestAdjacentShape(filteredShapes, currentShapeId, direction)
1862
1890
  }
1863
1891
 
1864
1892
  const shape = this.getShape(adjacentShapeId)
@@ -1957,6 +1985,7 @@ export class Editor extends EventEmitter<TLEventMap> {
1957
1985
  * @public
1958
1986
  */
1959
1987
  getNearestAdjacentShape(
1988
+ shapes: TLShape[],
1960
1989
  currentShapeId: TLShapeId,
1961
1990
  direction: 'left' | 'right' | 'up' | 'down'
1962
1991
  ): TLShapeId {
@@ -1964,7 +1993,6 @@ export class Editor extends EventEmitter<TLEventMap> {
1964
1993
  const currentShape = this.getShape(currentShapeId)
1965
1994
  if (!currentShape) return currentShapeId
1966
1995
 
1967
- const shapes = this.getCurrentPageShapes()
1968
1996
  const tabbableShapes = shapes.filter(
1969
1997
  (shape) => this.getShapeUtil(shape).canTabTo(shape) && shape.id !== currentShapeId
1970
1998
  )
@@ -3046,7 +3074,6 @@ export class Editor extends EventEmitter<TLEventMap> {
3046
3074
  // Dispatch a new pointer move because the pointer's page will have changed
3047
3075
  // (its screen position will compute to a new page position given the new camera position)
3048
3076
  const { currentScreenPoint, currentPagePoint } = this.inputs
3049
- const { screenBounds } = this.store.unsafeGetWithoutCapture(TLINSTANCE_ID)!
3050
3077
 
3051
3078
  // compare the next page point (derived from the current camera) to the current page point
3052
3079
  if (
@@ -3054,27 +3081,10 @@ export class Editor extends EventEmitter<TLEventMap> {
3054
3081
  currentScreenPoint.y / z - y !== currentPagePoint.y
3055
3082
  ) {
3056
3083
  // If it's changed, dispatch a pointer event
3057
- const event: TLPointerEventInfo = {
3058
- type: 'pointer',
3059
- target: 'canvas',
3060
- name: 'pointer_move',
3061
- // weird but true: we need to put the screen point back into client space
3062
- point: Vec.AddXY(currentScreenPoint, screenBounds.x, screenBounds.y),
3084
+ this.updatePointer({
3085
+ immediate: opts?.immediate,
3063
3086
  pointerId: INTERNAL_POINTER_IDS.CAMERA_MOVE,
3064
- ctrlKey: this.inputs.ctrlKey,
3065
- altKey: this.inputs.altKey,
3066
- shiftKey: this.inputs.shiftKey,
3067
- metaKey: this.inputs.metaKey,
3068
- accelKey: isAccelKey(this.inputs),
3069
- button: 0,
3070
- isPen: this.getInstanceState().isPenMode ?? false,
3071
- }
3072
-
3073
- if (opts?.immediate) {
3074
- this._flushEventForTick(event)
3075
- } else {
3076
- this.dispatch(event)
3077
- }
3087
+ })
3078
3088
  }
3079
3089
 
3080
3090
  this._tickCameraState()
@@ -4395,21 +4405,28 @@ export class Editor extends EventEmitter<TLEventMap> {
4395
4405
  */
4396
4406
  deletePage(page: TLPageId | TLPage): this {
4397
4407
  const id = typeof page === 'string' ? page : page.id
4398
- this.run(() => {
4399
- if (this.getIsReadonly()) return
4400
- const pages = this.getPages()
4401
- if (pages.length === 1) return
4408
+ this.run(
4409
+ () => {
4410
+ if (this.getIsReadonly()) return
4411
+ const pages = this.getPages()
4412
+ if (pages.length === 1) return
4402
4413
 
4403
- const deletedPage = this.getPage(id)
4404
- if (!deletedPage) return
4414
+ const deletedPage = this.getPage(id)
4415
+ if (!deletedPage) return
4405
4416
 
4406
- if (id === this.getCurrentPageId()) {
4407
- const index = pages.findIndex((page) => page.id === id)
4408
- const next = pages[index - 1] ?? pages[index + 1]
4409
- this.setCurrentPage(next.id)
4410
- }
4411
- this.store.remove([deletedPage.id])
4412
- })
4417
+ if (id === this.getCurrentPageId()) {
4418
+ const index = pages.findIndex((page) => page.id === id)
4419
+ const next = pages[index - 1] ?? pages[index + 1]
4420
+ this.setCurrentPage(next.id)
4421
+ }
4422
+
4423
+ const shapes = this.getSortedChildIdsForParent(deletedPage.id)
4424
+ this.deleteShapes(shapes)
4425
+
4426
+ this.store.remove([deletedPage.id])
4427
+ },
4428
+ { ignoreShapeLock: true }
4429
+ )
4413
4430
  return this
4414
4431
  }
4415
4432
 
@@ -5138,20 +5155,7 @@ export class Editor extends EventEmitter<TLEventMap> {
5138
5155
  *
5139
5156
  * @returns The shape at the given point, or undefined if there is no shape at the point.
5140
5157
  */
5141
- getShapeAtPoint(
5142
- point: VecLike,
5143
- opts = {} as {
5144
- renderingOnly?: boolean
5145
- margin?: number
5146
- hitInside?: boolean
5147
- hitLocked?: boolean
5148
- // TODO: we probably need to rename this, we don't quite _always_
5149
- // respect this esp. in the part below that does "Check labels first"
5150
- hitLabels?: boolean
5151
- hitFrameInside?: boolean
5152
- filter?(shape: TLShape): boolean
5153
- }
5154
- ): TLShape | undefined {
5158
+ getShapeAtPoint(point: VecLike, opts: TLGetShapeAtPointOptions = {}): TLShape | undefined {
5155
5159
  const zoomLevel = this.getZoomLevel()
5156
5160
  const viewportPageBounds = this.getViewportPageBounds()
5157
5161
  const {
@@ -5163,6 +5167,8 @@ export class Editor extends EventEmitter<TLEventMap> {
5163
5167
  hitFrameInside = false,
5164
5168
  } = opts
5165
5169
 
5170
+ const [innerMargin, outerMargin] = Array.isArray(margin) ? margin : [margin, margin]
5171
+
5166
5172
  let inHollowSmallestArea = Infinity
5167
5173
  let inHollowSmallestAreaHit: TLShape | null = null
5168
5174
 
@@ -5182,7 +5188,7 @@ export class Editor extends EventEmitter<TLEventMap> {
5182
5188
  return false
5183
5189
  const pageMask = this.getShapeMask(shape)
5184
5190
  if (pageMask && !pointInPolygon(point, pageMask)) return false
5185
- if (filter) return filter(shape)
5191
+ if (filter && !filter(shape)) return false
5186
5192
  return true
5187
5193
  })
5188
5194
 
@@ -5196,8 +5202,8 @@ export class Editor extends EventEmitter<TLEventMap> {
5196
5202
  // Check labels first
5197
5203
  if (
5198
5204
  this.isShapeOfType<TLFrameShape>(shape, 'frame') ||
5199
- (this.isShapeOfType<TLArrowShape>(shape, 'arrow') && shape.props.text.trim()) ||
5200
5205
  ((this.isShapeOfType<TLNoteShape>(shape, 'note') ||
5206
+ this.isShapeOfType<TLArrowShape>(shape, 'arrow') ||
5201
5207
  (this.isShapeOfType<TLGeoShape>(shape, 'geo') && shape.props.fill === 'none')) &&
5202
5208
  this.getShapeUtil(shape).getText(shape)?.trim())
5203
5209
  ) {
@@ -5208,13 +5214,18 @@ export class Editor extends EventEmitter<TLEventMap> {
5208
5214
  }
5209
5215
  }
5210
5216
 
5211
- if (this.isShapeOfType(shape, 'frame')) {
5217
+ if (this.isShapeOfType<TLFrameShape>(shape, 'frame')) {
5212
5218
  // On the rare case that we've hit a frame (not its label), test again hitInside to be forced true;
5213
5219
  // this prevents clicks from passing through the body of a frame to shapes behind it.
5214
5220
 
5215
5221
  // If the hit is within the frame's outer margin, then select the frame
5216
- const distance = geometry.distanceToPoint(pointInShapeSpace, hitInside)
5217
- if (Math.abs(distance) <= margin) {
5222
+ const distance = geometry.distanceToPoint(pointInShapeSpace, hitFrameInside)
5223
+ if (
5224
+ hitFrameInside
5225
+ ? (distance > 0 && distance <= outerMargin) ||
5226
+ (distance <= 0 && distance > -innerMargin)
5227
+ : distance > 0 && distance <= outerMargin
5228
+ ) {
5218
5229
  return inMarginClosestToEdgeHit || shape
5219
5230
  }
5220
5231
 
@@ -5253,11 +5264,11 @@ export class Editor extends EventEmitter<TLEventMap> {
5253
5264
  // If the margin is zero and the geometry has a very small width or height,
5254
5265
  // then check the actual distance. This is to prevent a bug where straight
5255
5266
  // lines would never pass the broad phase (point-in-bounds) check.
5256
- if (margin === 0 && (geometry.bounds.w < 1 || geometry.bounds.h < 1)) {
5267
+ if (outerMargin === 0 && (geometry.bounds.w < 1 || geometry.bounds.h < 1)) {
5257
5268
  distance = geometry.distanceToPoint(pointInShapeSpace, hitInside)
5258
5269
  } else {
5259
5270
  // Broad phase
5260
- if (geometry.bounds.containsPoint(pointInShapeSpace, margin)) {
5271
+ if (geometry.bounds.containsPoint(pointInShapeSpace, outerMargin)) {
5261
5272
  // Narrow phase (actual distance)
5262
5273
  distance = geometry.distanceToPoint(pointInShapeSpace, hitInside)
5263
5274
  } else {
@@ -5272,7 +5283,8 @@ export class Editor extends EventEmitter<TLEventMap> {
5272
5283
  // the shape or negative if inside of the shape. If the distance
5273
5284
  // is greater than the margin, then it's a miss. Otherwise...
5274
5285
 
5275
- if (distance <= margin) {
5286
+ // Are we close to the shape's edge?
5287
+ if (distance <= outerMargin || (hitInside && distance <= 0 && distance > -innerMargin)) {
5276
5288
  if (geometry.isFilled || (isGroup && geometry.children[0].isFilled)) {
5277
5289
  // If the shape is filled, then it's a hit. Remember, we're
5278
5290
  // starting from the TOP-MOST shape in z-index order, so any
@@ -5282,11 +5294,21 @@ export class Editor extends EventEmitter<TLEventMap> {
5282
5294
  // If the shape is bigger than the viewport, then skip it.
5283
5295
  if (this.getShapePageBounds(shape)!.contains(viewportPageBounds)) continue
5284
5296
 
5285
- // For hollow shapes...
5286
- if (Math.abs(distance) < margin) {
5287
- // We want to preference shapes where we're inside of the
5288
- // shape margin; and we would want to hit the shape with the
5289
- // edge closest to the point.
5297
+ // If we're close to the edge of the shape, and if it's the closest edge among
5298
+ // all the edges that we've gotten close to so far, then we will want to hit the
5299
+ // shape unless we hit something else or closer in later iterations.
5300
+ if (
5301
+ hitInside
5302
+ ? // On hitInside, the distance will be negative for hits inside
5303
+ // If the distance is positive, check against the outer margin
5304
+ (distance > 0 && distance <= outerMargin) ||
5305
+ // If the distance is negative, check against the inner margin
5306
+ (distance <= 0 && distance > -innerMargin)
5307
+ : // If hitInside is false, then sadly _we do not know_ whether the
5308
+ // point is inside or outside of the shape, so we check against
5309
+ // the max of the two margins
5310
+ Math.abs(distance) <= Math.max(innerMargin, outerMargin)
5311
+ ) {
5290
5312
  if (Math.abs(distance) < inMarginClosestToEdgeDistance) {
5291
5313
  inMarginClosestToEdgeDistance = Math.abs(distance)
5292
5314
  inMarginClosestToEdgeHit = shape
@@ -5308,6 +5330,7 @@ export class Editor extends EventEmitter<TLEventMap> {
5308
5330
  } else {
5309
5331
  // For open shapes (e.g. lines or draw shapes) always use the margin.
5310
5332
  // If the distance is less than the margin, return the shape as the hit.
5333
+ // Use the editor's configurable hit test margin.
5311
5334
  if (distance < this.options.hitTestMargin / zoomLevel) {
5312
5335
  return shape
5313
5336
  }
@@ -6310,7 +6333,17 @@ export class Editor extends EventEmitter<TLEventMap> {
6310
6333
 
6311
6334
  this.createShapes(shapesToCreate)
6312
6335
  this.createBindings(bindingsToCreate)
6313
- this.setSelectedShapes(compact(ids.map((id) => shapeIds.get(id))))
6336
+
6337
+ this.setSelectedShapes(
6338
+ compact(
6339
+ ids.map((oldId) => {
6340
+ const newId = shapeIds.get(oldId)
6341
+ if (!newId) return null
6342
+ if (!this.getShape(newId)) return null
6343
+ return newId
6344
+ })
6345
+ )
6346
+ )
6314
6347
 
6315
6348
  if (offset !== undefined) {
6316
6349
  // If we've offset the duplicated shapes, check to see whether their new bounds is entirely
@@ -7364,7 +7397,6 @@ export class Editor extends EventEmitter<TLEventMap> {
7364
7397
  if (
7365
7398
  !this.getShapeUtil(shape).canBeLaidOut?.(shape, {
7366
7399
  type: 'stretch',
7367
- shapes: shapesToStretchFirstPass,
7368
7400
  })
7369
7401
  ) {
7370
7402
  continue
@@ -7835,31 +7867,38 @@ export class Editor extends EventEmitter<TLEventMap> {
7835
7867
  ) {
7836
7868
  let parentId: TLParentId = this.getFocusedGroupId()
7837
7869
 
7838
- for (let i = currentPageShapesSorted.length - 1; i >= 0; i--) {
7839
- const parent = currentPageShapesSorted[i]
7840
- const util = this.getShapeUtil(parent)
7841
- if (
7842
- util.canReceiveNewChildrenOfType(parent, partial.type) &&
7843
- !this.isShapeHidden(parent) &&
7844
- this.isPointInShape(
7845
- parent,
7846
- // If no parent is provided, then we can treat the
7847
- // shape's provided x/y as being in the page's space.
7848
- { x: partial.x ?? 0, y: partial.y ?? 0 },
7849
- {
7850
- margin: 0,
7851
- hitInside: true,
7852
- }
7853
- )
7854
- ) {
7855
- parentId = parent.id
7856
- break
7870
+ const isPositioned = partial.x !== undefined && partial.y !== undefined
7871
+
7872
+ // If the shape has been explicitly positioned, we'll try to find a parent at
7873
+ // that position. If not, we'll assume the user isn't deliberately placing the
7874
+ // shape and the positioning will be handled later by another system.
7875
+ if (isPositioned) {
7876
+ for (let i = currentPageShapesSorted.length - 1; i >= 0; i--) {
7877
+ const parent = currentPageShapesSorted[i]
7878
+ const util = this.getShapeUtil(parent)
7879
+ if (
7880
+ util.canReceiveNewChildrenOfType(parent, partial.type) &&
7881
+ !this.isShapeHidden(parent) &&
7882
+ this.isPointInShape(
7883
+ parent,
7884
+ // If no parent is provided, then we can treat the
7885
+ // shape's provided x/y as being in the page's space.
7886
+ { x: partial.x ?? 0, y: partial.y ?? 0 },
7887
+ {
7888
+ margin: 0,
7889
+ hitInside: true,
7890
+ }
7891
+ )
7892
+ ) {
7893
+ parentId = parent.id
7894
+ break
7895
+ }
7857
7896
  }
7858
7897
  }
7859
7898
 
7860
7899
  const prevParentId = partial.parentId
7861
7900
 
7862
- // a shape cannot be it's own parent. This was a rare issue with frames/groups in the syncFuzz tests.
7901
+ // a shape cannot be its own parent. This was a rare issue with frames/groups in the syncFuzz tests.
7863
7902
  if (parentId === partial.id) {
7864
7903
  parentId = focusedGroupId
7865
7904
  }
@@ -9480,6 +9519,24 @@ export class Editor extends EventEmitter<TLEventMap> {
9480
9519
  }
9481
9520
  }
9482
9521
 
9522
+ /**
9523
+ * Get an exported image of the given shapes as a data URL.
9524
+ *
9525
+ * @param shapes - The shapes (or shape ids) to export.
9526
+ * @param opts - Options for the export.
9527
+ *
9528
+ * @returns A data URL of the image.
9529
+ * @public
9530
+ */
9531
+ async toImageDataUrl(shapes: TLShapeId[] | TLShape[], opts: TLImageExportOptions = {}) {
9532
+ const { blob, width, height } = await this.toImage(shapes, opts)
9533
+ return {
9534
+ url: await FileHelpers.blobToDataUrl(blob),
9535
+ width,
9536
+ height,
9537
+ }
9538
+ }
9539
+
9483
9540
  /* --------------------- Events --------------------- */
9484
9541
 
9485
9542
  /**
@@ -9647,6 +9704,52 @@ export class Editor extends EventEmitter<TLEventMap> {
9647
9704
  return this
9648
9705
  }
9649
9706
 
9707
+ /**
9708
+ * Dispatch a pointer move event in the current position of the pointer. This is useful when
9709
+ * external circumstances have changed (e.g. the camera moved or a shape was moved) and you want
9710
+ * the current interaction to respond to that change.
9711
+ *
9712
+ * @example
9713
+ * ```ts
9714
+ * editor.updatePointer()
9715
+ * ```
9716
+ *
9717
+ * @param options - The options for updating the pointer.
9718
+ * @returns The editor instance.
9719
+ * @public
9720
+ */
9721
+ updatePointer(options?: TLUpdatePointerOptions): this {
9722
+ const event: TLPointerEventInfo = {
9723
+ type: 'pointer',
9724
+ target: 'canvas',
9725
+ name: 'pointer_move',
9726
+ point:
9727
+ options?.point ??
9728
+ // weird but true: what `inputs` calls screen-space is actually viewport space. so
9729
+ // we need to convert back into true screen space first. we should fix this...
9730
+ Vec.Add(
9731
+ this.inputs.currentScreenPoint,
9732
+ this.store.unsafeGetWithoutCapture(TLINSTANCE_ID)!.screenBounds
9733
+ ),
9734
+ pointerId: options?.pointerId ?? 0,
9735
+ button: options?.button ?? 0,
9736
+ isPen: options?.isPen ?? this.inputs.isPen,
9737
+ shiftKey: options?.shiftKey ?? this.inputs.shiftKey,
9738
+ altKey: options?.altKey ?? this.inputs.altKey,
9739
+ ctrlKey: options?.ctrlKey ?? this.inputs.ctrlKey,
9740
+ metaKey: options?.metaKey ?? this.inputs.metaKey,
9741
+ accelKey: options?.accelKey ?? isAccelKey(this.inputs),
9742
+ }
9743
+
9744
+ if (options?.immediate) {
9745
+ this._flushEventForTick(event)
9746
+ } else {
9747
+ this.dispatch(event)
9748
+ }
9749
+
9750
+ return this
9751
+ }
9752
+
9650
9753
  /**
9651
9754
  * Puts the editor into focused mode.
9652
9755
  *
@@ -1,12 +1,13 @@
1
+ import { Mocked, vi } from 'vitest'
1
2
  import { Editor } from '../../Editor'
2
3
  import { TLClickEventInfo, TLPointerEventInfo } from '../../types/event-types'
3
4
  import { ClickManager } from './ClickManager'
4
5
 
5
6
  // Mock the Editor class
6
- jest.mock('../../Editor')
7
+ vi.mock('../../Editor')
7
8
 
8
9
  describe('ClickManager', () => {
9
- let editor: jest.Mocked<Editor>
10
+ let editor: Mocked<Editor>
10
11
  let clickManager: ClickManager
11
12
  let mockTimers: any
12
13
 
@@ -29,14 +30,14 @@ describe('ClickManager', () => {
29
30
  })
30
31
 
31
32
  beforeEach(() => {
32
- jest.useFakeTimers()
33
+ vi.useFakeTimers()
33
34
  mockTimers = {
34
- setTimeout: jest.fn((fn, delay) => setTimeout(fn, delay)),
35
+ setTimeout: vi.fn((fn, delay) => setTimeout(fn, delay)),
35
36
  }
36
37
 
37
38
  editor = {
38
39
  timers: mockTimers,
39
- dispatch: jest.fn(),
40
+ dispatch: vi.fn(),
40
41
  options: {
41
42
  doubleClickDurationMs: 300,
42
43
  multiClickDurationMs: 300,
@@ -46,7 +47,7 @@ describe('ClickManager', () => {
46
47
  inputs: {
47
48
  currentScreenPoint: { x: 0, y: 0 },
48
49
  },
49
- getInstanceState: jest.fn(() => ({
50
+ getInstanceState: vi.fn(() => ({
50
51
  isCoarsePointer: false,
51
52
  })),
52
53
  } as any
@@ -55,8 +56,8 @@ describe('ClickManager', () => {
55
56
  })
56
57
 
57
58
  afterEach(() => {
58
- jest.useRealTimers()
59
- jest.clearAllMocks()
59
+ vi.useRealTimers()
60
+ vi.clearAllMocks()
60
61
  })
61
62
 
62
63
  describe('constructor and initial state', () => {
@@ -100,7 +101,7 @@ describe('ClickManager', () => {
100
101
  clickManager.handlePointerEvent(pointerEvent)
101
102
  expect(clickManager.clickState).toBe('pendingDouble')
102
103
 
103
- jest.advanceTimersByTime(350)
104
+ vi.advanceTimersByTime(350)
104
105
 
105
106
  expect(clickManager.clickState).toBe('idle')
106
107
  })
@@ -141,7 +142,7 @@ describe('ClickManager', () => {
141
142
  clickManager.handlePointerEvent(firstDown)
142
143
  clickManager.handlePointerEvent(secondDown)
143
144
 
144
- jest.advanceTimersByTime(350)
145
+ vi.advanceTimersByTime(350)
145
146
 
146
147
  expect(editor.dispatch).toHaveBeenCalledWith(
147
148
  expect.objectContaining({
@@ -235,7 +236,7 @@ describe('ClickManager', () => {
235
236
  clickManager.handlePointerEvent(pointerDown) // second
236
237
  clickManager.handlePointerEvent(pointerDown) // third
237
238
 
238
- jest.advanceTimersByTime(350)
239
+ vi.advanceTimersByTime(350)
239
240
 
240
241
  expect(editor.dispatch).toHaveBeenCalledWith(
241
242
  expect.objectContaining({
@@ -255,7 +256,7 @@ describe('ClickManager', () => {
255
256
  clickManager.handlePointerEvent(pointerDown) // third
256
257
  clickManager.handlePointerEvent(pointerDown) // fourth
257
258
 
258
- jest.advanceTimersByTime(350)
259
+ vi.advanceTimersByTime(350)
259
260
 
260
261
  expect(editor.dispatch).toHaveBeenCalledWith(
261
262
  expect.objectContaining({
@@ -277,7 +278,7 @@ describe('ClickManager', () => {
277
278
  editor.options.doubleClickDurationMs
278
279
  )
279
280
 
280
- jest.clearAllMocks()
281
+ vi.clearAllMocks()
281
282
 
282
283
  // Second click - should use multiClickDurationMs
283
284
  clickManager.handlePointerEvent(pointerDown)
@@ -392,7 +393,7 @@ describe('ClickManager', () => {
392
393
  clickManager.cancelDoubleClickTimeout()
393
394
 
394
395
  // Advance time - should not dispatch settle event
395
- jest.advanceTimersByTime(350)
396
+ vi.advanceTimersByTime(350)
396
397
 
397
398
  expect(editor.dispatch).not.toHaveBeenCalled()
398
399
  expect(clickManager.clickState).toBe('idle')
@@ -1,19 +1,20 @@
1
+ import { Mock, Mocked, vi } from 'vitest'
1
2
  import { Box } from '../../../primitives/Box'
2
3
  import { Vec } from '../../../primitives/Vec'
3
4
  import { Editor } from '../../Editor'
4
5
  import { EdgeScrollManager } from './EdgeScrollManager'
5
6
 
6
7
  // Mock the Editor class
7
- jest.mock('../../Editor')
8
+ vi.mock('../../Editor')
8
9
 
9
10
  describe('EdgeScrollManager', () => {
10
- let editor: jest.Mocked<
11
+ let editor: Mocked<
11
12
  Editor & {
12
- user: { getEdgeScrollSpeed: jest.Mock }
13
- getCamera: jest.Mock
14
- getCameraOptions: jest.Mock
15
- getZoomLevel: jest.Mock
16
- getViewportScreenBounds: jest.Mock
13
+ user: { getEdgeScrollSpeed: Mock }
14
+ getCamera: Mock
15
+ getCameraOptions: Mock
16
+ getZoomLevel: Mock
17
+ getViewportScreenBounds: Mock
17
18
  }
18
19
  >
19
20
  let edgeScrollManager: EdgeScrollManager
@@ -33,33 +34,33 @@ describe('EdgeScrollManager', () => {
33
34
  isPanning: false,
34
35
  },
35
36
  user: {
36
- getEdgeScrollSpeed: jest.fn(() => 1),
37
+ getEdgeScrollSpeed: vi.fn(() => 1),
37
38
  },
38
- getViewportScreenBounds: jest.fn(() => new Box(0, 0, 1000, 600)),
39
- getInstanceState: jest.fn(
39
+ getViewportScreenBounds: vi.fn(() => new Box(0, 0, 1000, 600)),
40
+ getInstanceState: vi.fn(
40
41
  () =>
41
42
  ({
42
43
  isCoarsePointer: false,
43
44
  insets: [false, false, false, false], // [top, right, bottom, left]
44
45
  }) as any
45
46
  ),
46
- getCameraOptions: jest.fn(() => ({
47
+ getCameraOptions: vi.fn(() => ({
47
48
  isLocked: false,
48
49
  panSpeed: 1,
49
50
  zoomSpeed: 1,
50
51
  zoomSteps: [1],
51
52
  wheelBehavior: 'pan' as const,
52
53
  })),
53
- getZoomLevel: jest.fn(() => 1),
54
- getCamera: jest.fn(() => new Vec(0, 0, 1)),
55
- setCamera: jest.fn(),
54
+ getZoomLevel: vi.fn(() => 1),
55
+ getCamera: vi.fn(() => new Vec(0, 0, 1)),
56
+ setCamera: vi.fn(),
56
57
  } as any
57
58
 
58
59
  edgeScrollManager = new EdgeScrollManager(editor as any)
59
60
  })
60
61
 
61
62
  afterEach(() => {
62
- jest.clearAllMocks()
63
+ vi.clearAllMocks()
63
64
  })
64
65
 
65
66
  describe('constructor and initialization', () => {