@tldraw/editor 3.16.0-internal.51e99e128bd4 → 3.16.0-internal.a478398270c6

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 (217) hide show
  1. package/dist-cjs/index.d.ts +16 -217
  2. package/dist-cjs/index.js +1 -8
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/TldrawEditor.js +1 -3
  5. package/dist-cjs/lib/TldrawEditor.js.map +2 -2
  6. package/dist-cjs/lib/components/MenuClickCapture.js +5 -0
  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 +26 -4
  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 +1 -1
  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 +1 -9
  29. package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js.map +2 -2
  30. package/dist-cjs/lib/components/default-components/DefaultSnapIndictor.js +1 -1
  31. package/dist-cjs/lib/components/default-components/DefaultSnapIndictor.js.map +2 -2
  32. package/dist-cjs/lib/components/default-components/DefaultSpinner.js +15 -27
  33. package/dist-cjs/lib/components/default-components/DefaultSpinner.js.map +3 -3
  34. package/dist-cjs/lib/config/TLUserPreferences.js +3 -15
  35. package/dist-cjs/lib/config/TLUserPreferences.js.map +2 -2
  36. package/dist-cjs/lib/editor/Editor.js +67 -134
  37. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  38. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js +4 -14
  39. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +2 -2
  40. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  41. package/dist-cjs/lib/editor/tools/StateNode.js +1 -20
  42. package/dist-cjs/lib/editor/tools/StateNode.js.map +2 -2
  43. package/dist-cjs/lib/editor/types/misc-types.js.map +1 -1
  44. package/dist-cjs/lib/exports/getSvgJsx.js +2 -1
  45. package/dist-cjs/lib/exports/getSvgJsx.js.map +2 -2
  46. package/dist-cjs/lib/hooks/useCanvasEvents.js +20 -24
  47. package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
  48. package/dist-cjs/lib/hooks/useEditor.js +4 -1
  49. package/dist-cjs/lib/hooks/useEditor.js.map +2 -2
  50. package/dist-cjs/lib/hooks/useEditorComponents.js +0 -2
  51. package/dist-cjs/lib/hooks/useEditorComponents.js.map +2 -2
  52. package/dist-cjs/lib/license/Watermark.js +8 -8
  53. package/dist-cjs/lib/license/Watermark.js.map +2 -2
  54. package/dist-cjs/lib/options.js +0 -7
  55. package/dist-cjs/lib/options.js.map +2 -2
  56. package/dist-cjs/lib/primitives/geometry/Arc2d.js +1 -1
  57. package/dist-cjs/lib/primitives/geometry/Arc2d.js.map +2 -2
  58. package/dist-cjs/lib/primitives/geometry/Circle2d.js +1 -1
  59. package/dist-cjs/lib/primitives/geometry/Circle2d.js.map +2 -2
  60. package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js +1 -3
  61. package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js.map +2 -2
  62. package/dist-cjs/lib/primitives/geometry/Ellipse2d.js +1 -1
  63. package/dist-cjs/lib/primitives/geometry/Ellipse2d.js.map +2 -2
  64. package/dist-cjs/lib/primitives/geometry/geometry-constants.js +2 -2
  65. package/dist-cjs/lib/primitives/geometry/geometry-constants.js.map +2 -2
  66. package/dist-cjs/lib/primitives/intersect.js +4 -4
  67. package/dist-cjs/lib/primitives/intersect.js.map +2 -2
  68. package/dist-cjs/lib/primitives/utils.js +0 -4
  69. package/dist-cjs/lib/primitives/utils.js.map +2 -2
  70. package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js +1 -0
  71. package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js.map +2 -2
  72. package/dist-cjs/version.js +3 -3
  73. package/dist-cjs/version.js.map +1 -1
  74. package/dist-esm/index.d.mts +16 -217
  75. package/dist-esm/index.mjs +2 -16
  76. package/dist-esm/index.mjs.map +2 -2
  77. package/dist-esm/lib/TldrawEditor.mjs +1 -3
  78. package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
  79. package/dist-esm/lib/components/MenuClickCapture.mjs +5 -0
  80. package/dist-esm/lib/components/MenuClickCapture.mjs.map +2 -2
  81. package/dist-esm/lib/components/SVGContainer.mjs +1 -1
  82. package/dist-esm/lib/components/SVGContainer.mjs.map +2 -2
  83. package/dist-esm/lib/components/Shape.mjs +26 -4
  84. package/dist-esm/lib/components/Shape.mjs.map +2 -2
  85. package/dist-esm/lib/components/default-components/DefaultBrush.mjs +1 -1
  86. package/dist-esm/lib/components/default-components/DefaultBrush.mjs.map +2 -2
  87. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +1 -1
  88. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
  89. package/dist-esm/lib/components/default-components/DefaultCollaboratorHint.mjs +2 -2
  90. package/dist-esm/lib/components/default-components/DefaultCollaboratorHint.mjs.map +2 -2
  91. package/dist-esm/lib/components/default-components/DefaultCursor.mjs +1 -1
  92. package/dist-esm/lib/components/default-components/DefaultCursor.mjs.map +2 -2
  93. package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs +1 -1
  94. package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs.map +2 -2
  95. package/dist-esm/lib/components/default-components/DefaultGrid.mjs +1 -1
  96. package/dist-esm/lib/components/default-components/DefaultGrid.mjs.map +2 -2
  97. package/dist-esm/lib/components/default-components/DefaultHandles.mjs +1 -1
  98. package/dist-esm/lib/components/default-components/DefaultHandles.mjs.map +2 -2
  99. package/dist-esm/lib/components/default-components/DefaultScribble.mjs +1 -1
  100. package/dist-esm/lib/components/default-components/DefaultScribble.mjs.map +2 -2
  101. package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs +1 -9
  102. package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs.map +2 -2
  103. package/dist-esm/lib/components/default-components/DefaultSnapIndictor.mjs +1 -1
  104. package/dist-esm/lib/components/default-components/DefaultSnapIndictor.mjs.map +2 -2
  105. package/dist-esm/lib/components/default-components/DefaultSpinner.mjs +15 -17
  106. package/dist-esm/lib/components/default-components/DefaultSpinner.mjs.map +2 -2
  107. package/dist-esm/lib/config/TLUserPreferences.mjs +3 -15
  108. package/dist-esm/lib/config/TLUserPreferences.mjs.map +2 -2
  109. package/dist-esm/lib/editor/Editor.mjs +67 -134
  110. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  111. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs +4 -14
  112. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +2 -2
  113. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  114. package/dist-esm/lib/editor/tools/StateNode.mjs +1 -20
  115. package/dist-esm/lib/editor/tools/StateNode.mjs.map +2 -2
  116. package/dist-esm/lib/exports/getSvgJsx.mjs +2 -2
  117. package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
  118. package/dist-esm/lib/hooks/useCanvasEvents.mjs +21 -25
  119. package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
  120. package/dist-esm/lib/hooks/useEditor.mjs +4 -1
  121. package/dist-esm/lib/hooks/useEditor.mjs.map +2 -2
  122. package/dist-esm/lib/hooks/useEditorComponents.mjs +0 -4
  123. package/dist-esm/lib/hooks/useEditorComponents.mjs.map +2 -2
  124. package/dist-esm/lib/license/Watermark.mjs +8 -8
  125. package/dist-esm/lib/license/Watermark.mjs.map +2 -2
  126. package/dist-esm/lib/options.mjs +0 -7
  127. package/dist-esm/lib/options.mjs.map +2 -2
  128. package/dist-esm/lib/primitives/geometry/Arc2d.mjs +2 -2
  129. package/dist-esm/lib/primitives/geometry/Arc2d.mjs.map +2 -2
  130. package/dist-esm/lib/primitives/geometry/Circle2d.mjs +2 -2
  131. package/dist-esm/lib/primitives/geometry/Circle2d.mjs.map +2 -2
  132. package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs +1 -3
  133. package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs.map +2 -2
  134. package/dist-esm/lib/primitives/geometry/Ellipse2d.mjs +2 -2
  135. package/dist-esm/lib/primitives/geometry/Ellipse2d.mjs.map +2 -2
  136. package/dist-esm/lib/primitives/geometry/geometry-constants.mjs +2 -2
  137. package/dist-esm/lib/primitives/geometry/geometry-constants.mjs.map +2 -2
  138. package/dist-esm/lib/primitives/intersect.mjs +5 -5
  139. package/dist-esm/lib/primitives/intersect.mjs.map +2 -2
  140. package/dist-esm/lib/primitives/utils.mjs +0 -4
  141. package/dist-esm/lib/primitives/utils.mjs.map +2 -2
  142. package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs +1 -0
  143. package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs.map +2 -2
  144. package/dist-esm/version.mjs +3 -3
  145. package/dist-esm/version.mjs.map +1 -1
  146. package/editor.css +313 -312
  147. package/package.json +38 -16
  148. package/src/index.ts +1 -15
  149. package/src/lib/TldrawEditor.tsx +5 -7
  150. package/src/lib/components/MenuClickCapture.tsx +8 -0
  151. package/src/lib/components/SVGContainer.tsx +1 -1
  152. package/src/lib/components/Shape.tsx +21 -6
  153. package/src/lib/components/default-components/DefaultBrush.tsx +1 -1
  154. package/src/lib/components/default-components/DefaultCanvas.tsx +1 -1
  155. package/src/lib/components/default-components/DefaultCollaboratorHint.tsx +2 -2
  156. package/src/lib/components/default-components/DefaultCursor.tsx +1 -1
  157. package/src/lib/components/default-components/DefaultErrorFallback.tsx +1 -1
  158. package/src/lib/components/default-components/DefaultGrid.tsx +1 -1
  159. package/src/lib/components/default-components/DefaultHandles.tsx +1 -5
  160. package/src/lib/components/default-components/DefaultScribble.tsx +1 -1
  161. package/src/lib/components/default-components/DefaultShapeIndicator.tsx +2 -6
  162. package/src/lib/components/default-components/DefaultSnapIndictor.tsx +1 -1
  163. package/src/lib/components/default-components/DefaultSpinner.tsx +12 -12
  164. package/src/lib/config/TLUserPreferences.ts +1 -15
  165. package/src/lib/editor/Editor.test.ts +8 -416
  166. package/src/lib/editor/Editor.ts +92 -177
  167. package/src/lib/editor/managers/ClickManager/ClickManager.test.ts +14 -15
  168. package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.test.ts +15 -16
  169. package/src/lib/editor/managers/FocusManager/FocusManager.test.ts +48 -49
  170. package/src/lib/editor/managers/FontManager/FontManager.test.ts +23 -24
  171. package/src/lib/editor/managers/HistoryManager/HistoryManager.test.ts +6 -7
  172. package/src/lib/editor/managers/ScribbleManager/ScribbleManager.test.ts +11 -12
  173. package/src/lib/editor/managers/SnapManager/SnapManager.test.ts +50 -57
  174. package/src/lib/editor/managers/TextManager/TextManager.test.ts +26 -51
  175. package/src/lib/editor/managers/TickManager/TickManager.test.ts +13 -14
  176. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +26 -55
  177. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.ts +1 -14
  178. package/src/lib/editor/shapes/ShapeUtil.ts +0 -57
  179. package/src/lib/editor/tools/StateNode.ts +1 -27
  180. package/src/lib/editor/types/misc-types.ts +1 -73
  181. package/src/lib/exports/getSvgJsx.tsx +2 -2
  182. package/src/lib/hooks/useCanvasEvents.ts +32 -39
  183. package/src/lib/hooks/useEditor.tsx +5 -6
  184. package/src/lib/hooks/useEditorComponents.tsx +2 -8
  185. package/src/lib/license/LicenseManager.test.ts +1 -3
  186. package/src/lib/license/Watermark.test.tsx +1 -2
  187. package/src/lib/license/Watermark.tsx +8 -8
  188. package/src/lib/options.ts +0 -8
  189. package/src/lib/primitives/geometry/Arc2d.ts +2 -2
  190. package/src/lib/primitives/geometry/Circle2d.ts +2 -2
  191. package/src/lib/primitives/geometry/CubicBezier2d.ts +1 -4
  192. package/src/lib/primitives/geometry/Ellipse2d.ts +2 -2
  193. package/src/lib/primitives/geometry/geometry-constants.ts +1 -2
  194. package/src/lib/primitives/intersect.ts +5 -12
  195. package/src/lib/primitives/utils.ts +0 -11
  196. package/src/lib/test/currentToolIdMask.test.ts +49 -0
  197. package/src/lib/utils/sync/LocalIndexedDb.test.ts +1 -2
  198. package/src/lib/utils/sync/TLLocalSyncClient.test.ts +15 -15
  199. package/src/lib/utils/sync/TLLocalSyncClient.ts +1 -0
  200. package/src/version.ts +3 -3
  201. package/dist-cjs/lib/components/default-components/DefaultShapeWrapper.js +0 -53
  202. package/dist-cjs/lib/components/default-components/DefaultShapeWrapper.js.map +0 -7
  203. package/dist-cjs/lib/hooks/useStateAttribute.js +0 -35
  204. package/dist-cjs/lib/hooks/useStateAttribute.js.map +0 -7
  205. package/dist-cjs/lib/utils/EditorAtom.js +0 -45
  206. package/dist-cjs/lib/utils/EditorAtom.js.map +0 -7
  207. package/dist-esm/lib/components/default-components/DefaultShapeWrapper.mjs +0 -23
  208. package/dist-esm/lib/components/default-components/DefaultShapeWrapper.mjs.map +0 -7
  209. package/dist-esm/lib/hooks/useStateAttribute.mjs +0 -15
  210. package/dist-esm/lib/hooks/useStateAttribute.mjs.map +0 -7
  211. package/dist-esm/lib/utils/EditorAtom.mjs +0 -25
  212. package/dist-esm/lib/utils/EditorAtom.mjs.map +0 -7
  213. package/src/lib/components/default-components/DefaultShapeWrapper.tsx +0 -35
  214. package/src/lib/editor/tools/StateNode.test.ts +0 -285
  215. package/src/lib/hooks/useStateAttribute.ts +0 -15
  216. package/src/lib/primitives/intersect.test.ts +0 -946
  217. package/src/lib/utils/EditorAtom.ts +0 -37
@@ -176,10 +176,8 @@ import {
176
176
  RequiredKeys,
177
177
  TLCameraMoveOptions,
178
178
  TLCameraOptions,
179
- TLGetShapeAtPointOptions,
180
179
  TLImageExportOptions,
181
180
  TLSvgExportOptions,
182
- TLUpdatePointerOptions,
183
181
  } from './types/misc-types'
184
182
  import { TLAdjacentDirection, TLResizeHandle } from './types/selection-types'
185
183
 
@@ -1805,9 +1803,7 @@ export class Editor extends EventEmitter<TLEventMap> {
1805
1803
  }
1806
1804
 
1807
1805
  /**
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.
1806
+ * Select all direct children of the current page.
1811
1807
  *
1812
1808
  * @example
1813
1809
  * ```ts
@@ -1817,34 +1813,11 @@ export class Editor extends EventEmitter<TLEventMap> {
1817
1813
  * @public
1818
1814
  */
1819
1815
  selectAll(): this {
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)
1816
+ const ids = this.getSortedChildIdsForParent(this.getCurrentPageId())
1817
+ // page might have no shapes
1846
1818
  if (ids.length <= 0) return this
1847
1819
  this.setSelectedShapes(this._getUnlockedShapeIds(ids))
1820
+
1848
1821
  return this
1849
1822
  }
1850
1823
 
@@ -1865,11 +1838,10 @@ export class Editor extends EventEmitter<TLEventMap> {
1865
1838
  firstParentId &&
1866
1839
  selectedShapeIds.every((shapeId) => this.getShape(shapeId)?.parentId === firstParentId) &&
1867
1840
  !isPageId(firstParentId)
1868
- const filteredShapes = isSelectedWithinContainer
1869
- ? this.getCurrentPageShapes().filter((shape) => shape.parentId === firstParentId)
1870
- : this.getCurrentPageShapes().filter((shape) => isPageId(shape.parentId))
1871
1841
  const readingOrderShapes = isSelectedWithinContainer
1872
- ? this._getShapesInReadingOrder(filteredShapes)
1842
+ ? this._getShapesInReadingOrder(
1843
+ this.getCurrentPageShapes().filter((shape) => shape.parentId === firstParentId)
1844
+ )
1873
1845
  : this.getCurrentPageShapesInReadingOrder()
1874
1846
  const currentShapeId: TLShapeId | undefined =
1875
1847
  selectedShapeIds.length === 1
@@ -1886,7 +1858,7 @@ export class Editor extends EventEmitter<TLEventMap> {
1886
1858
  adjacentShapeId = shapeIds[adjacentIndex]
1887
1859
  } else {
1888
1860
  if (!currentShapeId) return
1889
- adjacentShapeId = this.getNearestAdjacentShape(filteredShapes, currentShapeId, direction)
1861
+ adjacentShapeId = this.getNearestAdjacentShape(currentShapeId, direction)
1890
1862
  }
1891
1863
 
1892
1864
  const shape = this.getShape(adjacentShapeId)
@@ -1985,7 +1957,6 @@ export class Editor extends EventEmitter<TLEventMap> {
1985
1957
  * @public
1986
1958
  */
1987
1959
  getNearestAdjacentShape(
1988
- shapes: TLShape[],
1989
1960
  currentShapeId: TLShapeId,
1990
1961
  direction: 'left' | 'right' | 'up' | 'down'
1991
1962
  ): TLShapeId {
@@ -1993,6 +1964,7 @@ export class Editor extends EventEmitter<TLEventMap> {
1993
1964
  const currentShape = this.getShape(currentShapeId)
1994
1965
  if (!currentShape) return currentShapeId
1995
1966
 
1967
+ const shapes = this.getCurrentPageShapes()
1996
1968
  const tabbableShapes = shapes.filter(
1997
1969
  (shape) => this.getShapeUtil(shape).canTabTo(shape) && shape.id !== currentShapeId
1998
1970
  )
@@ -3074,6 +3046,7 @@ export class Editor extends EventEmitter<TLEventMap> {
3074
3046
  // Dispatch a new pointer move because the pointer's page will have changed
3075
3047
  // (its screen position will compute to a new page position given the new camera position)
3076
3048
  const { currentScreenPoint, currentPagePoint } = this.inputs
3049
+ const { screenBounds } = this.store.unsafeGetWithoutCapture(TLINSTANCE_ID)!
3077
3050
 
3078
3051
  // compare the next page point (derived from the current camera) to the current page point
3079
3052
  if (
@@ -3081,10 +3054,27 @@ export class Editor extends EventEmitter<TLEventMap> {
3081
3054
  currentScreenPoint.y / z - y !== currentPagePoint.y
3082
3055
  ) {
3083
3056
  // If it's changed, dispatch a pointer event
3084
- this.updatePointer({
3085
- immediate: opts?.immediate,
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),
3086
3063
  pointerId: INTERNAL_POINTER_IDS.CAMERA_MOVE,
3087
- })
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
+ }
3088
3078
  }
3089
3079
 
3090
3080
  this._tickCameraState()
@@ -4405,28 +4395,21 @@ export class Editor extends EventEmitter<TLEventMap> {
4405
4395
  */
4406
4396
  deletePage(page: TLPageId | TLPage): this {
4407
4397
  const id = typeof page === 'string' ? page : page.id
4408
- this.run(
4409
- () => {
4410
- if (this.getIsReadonly()) return
4411
- const pages = this.getPages()
4412
- if (pages.length === 1) return
4413
-
4414
- const deletedPage = this.getPage(id)
4415
- if (!deletedPage) return
4416
-
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
- }
4398
+ this.run(() => {
4399
+ if (this.getIsReadonly()) return
4400
+ const pages = this.getPages()
4401
+ if (pages.length === 1) return
4422
4402
 
4423
- const shapes = this.getSortedChildIdsForParent(deletedPage.id)
4424
- this.deleteShapes(shapes)
4403
+ const deletedPage = this.getPage(id)
4404
+ if (!deletedPage) return
4425
4405
 
4426
- this.store.remove([deletedPage.id])
4427
- },
4428
- { ignoreShapeLock: true }
4429
- )
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
+ })
4430
4413
  return this
4431
4414
  }
4432
4415
 
@@ -5155,7 +5138,20 @@ export class Editor extends EventEmitter<TLEventMap> {
5155
5138
  *
5156
5139
  * @returns The shape at the given point, or undefined if there is no shape at the point.
5157
5140
  */
5158
- getShapeAtPoint(point: VecLike, opts: TLGetShapeAtPointOptions = {}): TLShape | undefined {
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 {
5159
5155
  const zoomLevel = this.getZoomLevel()
5160
5156
  const viewportPageBounds = this.getViewportPageBounds()
5161
5157
  const {
@@ -5167,8 +5163,6 @@ export class Editor extends EventEmitter<TLEventMap> {
5167
5163
  hitFrameInside = false,
5168
5164
  } = opts
5169
5165
 
5170
- const [innerMargin, outerMargin] = Array.isArray(margin) ? margin : [margin, margin]
5171
-
5172
5166
  let inHollowSmallestArea = Infinity
5173
5167
  let inHollowSmallestAreaHit: TLShape | null = null
5174
5168
 
@@ -5188,7 +5182,7 @@ export class Editor extends EventEmitter<TLEventMap> {
5188
5182
  return false
5189
5183
  const pageMask = this.getShapeMask(shape)
5190
5184
  if (pageMask && !pointInPolygon(point, pageMask)) return false
5191
- if (filter && !filter(shape)) return false
5185
+ if (filter) return filter(shape)
5192
5186
  return true
5193
5187
  })
5194
5188
 
@@ -5202,8 +5196,8 @@ export class Editor extends EventEmitter<TLEventMap> {
5202
5196
  // Check labels first
5203
5197
  if (
5204
5198
  this.isShapeOfType<TLFrameShape>(shape, 'frame') ||
5199
+ (this.isShapeOfType<TLArrowShape>(shape, 'arrow') && shape.props.text.trim()) ||
5205
5200
  ((this.isShapeOfType<TLNoteShape>(shape, 'note') ||
5206
- this.isShapeOfType<TLArrowShape>(shape, 'arrow') ||
5207
5201
  (this.isShapeOfType<TLGeoShape>(shape, 'geo') && shape.props.fill === 'none')) &&
5208
5202
  this.getShapeUtil(shape).getText(shape)?.trim())
5209
5203
  ) {
@@ -5214,18 +5208,13 @@ export class Editor extends EventEmitter<TLEventMap> {
5214
5208
  }
5215
5209
  }
5216
5210
 
5217
- if (this.isShapeOfType<TLFrameShape>(shape, 'frame')) {
5211
+ if (this.isShapeOfType(shape, 'frame')) {
5218
5212
  // On the rare case that we've hit a frame (not its label), test again hitInside to be forced true;
5219
5213
  // this prevents clicks from passing through the body of a frame to shapes behind it.
5220
5214
 
5221
5215
  // If the hit is within the frame's outer margin, then select the frame
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
- ) {
5216
+ const distance = geometry.distanceToPoint(pointInShapeSpace, hitInside)
5217
+ if (Math.abs(distance) <= margin) {
5229
5218
  return inMarginClosestToEdgeHit || shape
5230
5219
  }
5231
5220
 
@@ -5264,11 +5253,11 @@ export class Editor extends EventEmitter<TLEventMap> {
5264
5253
  // If the margin is zero and the geometry has a very small width or height,
5265
5254
  // then check the actual distance. This is to prevent a bug where straight
5266
5255
  // lines would never pass the broad phase (point-in-bounds) check.
5267
- if (outerMargin === 0 && (geometry.bounds.w < 1 || geometry.bounds.h < 1)) {
5256
+ if (margin === 0 && (geometry.bounds.w < 1 || geometry.bounds.h < 1)) {
5268
5257
  distance = geometry.distanceToPoint(pointInShapeSpace, hitInside)
5269
5258
  } else {
5270
5259
  // Broad phase
5271
- if (geometry.bounds.containsPoint(pointInShapeSpace, outerMargin)) {
5260
+ if (geometry.bounds.containsPoint(pointInShapeSpace, margin)) {
5272
5261
  // Narrow phase (actual distance)
5273
5262
  distance = geometry.distanceToPoint(pointInShapeSpace, hitInside)
5274
5263
  } else {
@@ -5283,8 +5272,7 @@ export class Editor extends EventEmitter<TLEventMap> {
5283
5272
  // the shape or negative if inside of the shape. If the distance
5284
5273
  // is greater than the margin, then it's a miss. Otherwise...
5285
5274
 
5286
- // Are we close to the shape's edge?
5287
- if (distance <= outerMargin || (hitInside && distance <= 0 && distance > -innerMargin)) {
5275
+ if (distance <= margin) {
5288
5276
  if (geometry.isFilled || (isGroup && geometry.children[0].isFilled)) {
5289
5277
  // If the shape is filled, then it's a hit. Remember, we're
5290
5278
  // starting from the TOP-MOST shape in z-index order, so any
@@ -5294,21 +5282,11 @@ export class Editor extends EventEmitter<TLEventMap> {
5294
5282
  // If the shape is bigger than the viewport, then skip it.
5295
5283
  if (this.getShapePageBounds(shape)!.contains(viewportPageBounds)) continue
5296
5284
 
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
- ) {
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.
5312
5290
  if (Math.abs(distance) < inMarginClosestToEdgeDistance) {
5313
5291
  inMarginClosestToEdgeDistance = Math.abs(distance)
5314
5292
  inMarginClosestToEdgeHit = shape
@@ -5330,7 +5308,6 @@ export class Editor extends EventEmitter<TLEventMap> {
5330
5308
  } else {
5331
5309
  // For open shapes (e.g. lines or draw shapes) always use the margin.
5332
5310
  // If the distance is less than the margin, return the shape as the hit.
5333
- // Use the editor's configurable hit test margin.
5334
5311
  if (distance < this.options.hitTestMargin / zoomLevel) {
5335
5312
  return shape
5336
5313
  }
@@ -6333,17 +6310,7 @@ export class Editor extends EventEmitter<TLEventMap> {
6333
6310
 
6334
6311
  this.createShapes(shapesToCreate)
6335
6312
  this.createBindings(bindingsToCreate)
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
- )
6313
+ this.setSelectedShapes(compact(ids.map((id) => shapeIds.get(id))))
6347
6314
 
6348
6315
  if (offset !== undefined) {
6349
6316
  // If we've offset the duplicated shapes, check to see whether their new bounds is entirely
@@ -7397,6 +7364,7 @@ export class Editor extends EventEmitter<TLEventMap> {
7397
7364
  if (
7398
7365
  !this.getShapeUtil(shape).canBeLaidOut?.(shape, {
7399
7366
  type: 'stretch',
7367
+ shapes: shapesToStretchFirstPass,
7400
7368
  })
7401
7369
  ) {
7402
7370
  continue
@@ -7867,38 +7835,31 @@ export class Editor extends EventEmitter<TLEventMap> {
7867
7835
  ) {
7868
7836
  let parentId: TLParentId = this.getFocusedGroupId()
7869
7837
 
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
- }
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
7896
7857
  }
7897
7858
  }
7898
7859
 
7899
7860
  const prevParentId = partial.parentId
7900
7861
 
7901
- // a shape cannot be its own parent. This was a rare issue with frames/groups in the syncFuzz tests.
7862
+ // a shape cannot be it's own parent. This was a rare issue with frames/groups in the syncFuzz tests.
7902
7863
  if (parentId === partial.id) {
7903
7864
  parentId = focusedGroupId
7904
7865
  }
@@ -9686,52 +9647,6 @@ export class Editor extends EventEmitter<TLEventMap> {
9686
9647
  return this
9687
9648
  }
9688
9649
 
9689
- /**
9690
- * Dispatch a pointer move event in the current position of the pointer. This is useful when
9691
- * external circumstances have changed (e.g. the camera moved or a shape was moved) and you want
9692
- * the current interaction to respond to that change.
9693
- *
9694
- * @example
9695
- * ```ts
9696
- * editor.updatePointer()
9697
- * ```
9698
- *
9699
- * @param options - The options for updating the pointer.
9700
- * @returns The editor instance.
9701
- * @public
9702
- */
9703
- updatePointer(options?: TLUpdatePointerOptions): this {
9704
- const event: TLPointerEventInfo = {
9705
- type: 'pointer',
9706
- target: 'canvas',
9707
- name: 'pointer_move',
9708
- point:
9709
- options?.point ??
9710
- // weird but true: what `inputs` calls screen-space is actually viewport space. so
9711
- // we need to convert back into true screen space first. we should fix this...
9712
- Vec.Add(
9713
- this.inputs.currentScreenPoint,
9714
- this.store.unsafeGetWithoutCapture(TLINSTANCE_ID)!.screenBounds
9715
- ),
9716
- pointerId: options?.pointerId ?? 0,
9717
- button: options?.button ?? 0,
9718
- isPen: options?.isPen ?? this.inputs.isPen,
9719
- shiftKey: options?.shiftKey ?? this.inputs.shiftKey,
9720
- altKey: options?.altKey ?? this.inputs.altKey,
9721
- ctrlKey: options?.ctrlKey ?? this.inputs.ctrlKey,
9722
- metaKey: options?.metaKey ?? this.inputs.metaKey,
9723
- accelKey: options?.accelKey ?? isAccelKey(this.inputs),
9724
- }
9725
-
9726
- if (options?.immediate) {
9727
- this._flushEventForTick(event)
9728
- } else {
9729
- this.dispatch(event)
9730
- }
9731
-
9732
- return this
9733
- }
9734
-
9735
9650
  /**
9736
9651
  * Puts the editor into focused mode.
9737
9652
  *
@@ -1,13 +1,12 @@
1
- import { Mocked, vi } from 'vitest'
2
1
  import { Editor } from '../../Editor'
3
2
  import { TLClickEventInfo, TLPointerEventInfo } from '../../types/event-types'
4
3
  import { ClickManager } from './ClickManager'
5
4
 
6
5
  // Mock the Editor class
7
- vi.mock('../../Editor')
6
+ jest.mock('../../Editor')
8
7
 
9
8
  describe('ClickManager', () => {
10
- let editor: Mocked<Editor>
9
+ let editor: jest.Mocked<Editor>
11
10
  let clickManager: ClickManager
12
11
  let mockTimers: any
13
12
 
@@ -30,14 +29,14 @@ describe('ClickManager', () => {
30
29
  })
31
30
 
32
31
  beforeEach(() => {
33
- vi.useFakeTimers()
32
+ jest.useFakeTimers()
34
33
  mockTimers = {
35
- setTimeout: vi.fn((fn, delay) => setTimeout(fn, delay)),
34
+ setTimeout: jest.fn((fn, delay) => setTimeout(fn, delay)),
36
35
  }
37
36
 
38
37
  editor = {
39
38
  timers: mockTimers,
40
- dispatch: vi.fn(),
39
+ dispatch: jest.fn(),
41
40
  options: {
42
41
  doubleClickDurationMs: 300,
43
42
  multiClickDurationMs: 300,
@@ -47,7 +46,7 @@ describe('ClickManager', () => {
47
46
  inputs: {
48
47
  currentScreenPoint: { x: 0, y: 0 },
49
48
  },
50
- getInstanceState: vi.fn(() => ({
49
+ getInstanceState: jest.fn(() => ({
51
50
  isCoarsePointer: false,
52
51
  })),
53
52
  } as any
@@ -56,8 +55,8 @@ describe('ClickManager', () => {
56
55
  })
57
56
 
58
57
  afterEach(() => {
59
- vi.useRealTimers()
60
- vi.clearAllMocks()
58
+ jest.useRealTimers()
59
+ jest.clearAllMocks()
61
60
  })
62
61
 
63
62
  describe('constructor and initial state', () => {
@@ -101,7 +100,7 @@ describe('ClickManager', () => {
101
100
  clickManager.handlePointerEvent(pointerEvent)
102
101
  expect(clickManager.clickState).toBe('pendingDouble')
103
102
 
104
- vi.advanceTimersByTime(350)
103
+ jest.advanceTimersByTime(350)
105
104
 
106
105
  expect(clickManager.clickState).toBe('idle')
107
106
  })
@@ -142,7 +141,7 @@ describe('ClickManager', () => {
142
141
  clickManager.handlePointerEvent(firstDown)
143
142
  clickManager.handlePointerEvent(secondDown)
144
143
 
145
- vi.advanceTimersByTime(350)
144
+ jest.advanceTimersByTime(350)
146
145
 
147
146
  expect(editor.dispatch).toHaveBeenCalledWith(
148
147
  expect.objectContaining({
@@ -236,7 +235,7 @@ describe('ClickManager', () => {
236
235
  clickManager.handlePointerEvent(pointerDown) // second
237
236
  clickManager.handlePointerEvent(pointerDown) // third
238
237
 
239
- vi.advanceTimersByTime(350)
238
+ jest.advanceTimersByTime(350)
240
239
 
241
240
  expect(editor.dispatch).toHaveBeenCalledWith(
242
241
  expect.objectContaining({
@@ -256,7 +255,7 @@ describe('ClickManager', () => {
256
255
  clickManager.handlePointerEvent(pointerDown) // third
257
256
  clickManager.handlePointerEvent(pointerDown) // fourth
258
257
 
259
- vi.advanceTimersByTime(350)
258
+ jest.advanceTimersByTime(350)
260
259
 
261
260
  expect(editor.dispatch).toHaveBeenCalledWith(
262
261
  expect.objectContaining({
@@ -278,7 +277,7 @@ describe('ClickManager', () => {
278
277
  editor.options.doubleClickDurationMs
279
278
  )
280
279
 
281
- vi.clearAllMocks()
280
+ jest.clearAllMocks()
282
281
 
283
282
  // Second click - should use multiClickDurationMs
284
283
  clickManager.handlePointerEvent(pointerDown)
@@ -393,7 +392,7 @@ describe('ClickManager', () => {
393
392
  clickManager.cancelDoubleClickTimeout()
394
393
 
395
394
  // Advance time - should not dispatch settle event
396
- vi.advanceTimersByTime(350)
395
+ jest.advanceTimersByTime(350)
397
396
 
398
397
  expect(editor.dispatch).not.toHaveBeenCalled()
399
398
  expect(clickManager.clickState).toBe('idle')
@@ -1,20 +1,19 @@
1
- import { Mock, Mocked, vi } from 'vitest'
2
1
  import { Box } from '../../../primitives/Box'
3
2
  import { Vec } from '../../../primitives/Vec'
4
3
  import { Editor } from '../../Editor'
5
4
  import { EdgeScrollManager } from './EdgeScrollManager'
6
5
 
7
6
  // Mock the Editor class
8
- vi.mock('../../Editor')
7
+ jest.mock('../../Editor')
9
8
 
10
9
  describe('EdgeScrollManager', () => {
11
- let editor: Mocked<
10
+ let editor: jest.Mocked<
12
11
  Editor & {
13
- user: { getEdgeScrollSpeed: Mock }
14
- getCamera: Mock
15
- getCameraOptions: Mock
16
- getZoomLevel: Mock
17
- getViewportScreenBounds: Mock
12
+ user: { getEdgeScrollSpeed: jest.Mock }
13
+ getCamera: jest.Mock
14
+ getCameraOptions: jest.Mock
15
+ getZoomLevel: jest.Mock
16
+ getViewportScreenBounds: jest.Mock
18
17
  }
19
18
  >
20
19
  let edgeScrollManager: EdgeScrollManager
@@ -34,33 +33,33 @@ describe('EdgeScrollManager', () => {
34
33
  isPanning: false,
35
34
  },
36
35
  user: {
37
- getEdgeScrollSpeed: vi.fn(() => 1),
36
+ getEdgeScrollSpeed: jest.fn(() => 1),
38
37
  },
39
- getViewportScreenBounds: vi.fn(() => new Box(0, 0, 1000, 600)),
40
- getInstanceState: vi.fn(
38
+ getViewportScreenBounds: jest.fn(() => new Box(0, 0, 1000, 600)),
39
+ getInstanceState: jest.fn(
41
40
  () =>
42
41
  ({
43
42
  isCoarsePointer: false,
44
43
  insets: [false, false, false, false], // [top, right, bottom, left]
45
44
  }) as any
46
45
  ),
47
- getCameraOptions: vi.fn(() => ({
46
+ getCameraOptions: jest.fn(() => ({
48
47
  isLocked: false,
49
48
  panSpeed: 1,
50
49
  zoomSpeed: 1,
51
50
  zoomSteps: [1],
52
51
  wheelBehavior: 'pan' as const,
53
52
  })),
54
- getZoomLevel: vi.fn(() => 1),
55
- getCamera: vi.fn(() => new Vec(0, 0, 1)),
56
- setCamera: vi.fn(),
53
+ getZoomLevel: jest.fn(() => 1),
54
+ getCamera: jest.fn(() => new Vec(0, 0, 1)),
55
+ setCamera: jest.fn(),
57
56
  } as any
58
57
 
59
58
  edgeScrollManager = new EdgeScrollManager(editor as any)
60
59
  })
61
60
 
62
61
  afterEach(() => {
63
- vi.clearAllMocks()
62
+ jest.clearAllMocks()
64
63
  })
65
64
 
66
65
  describe('constructor and initialization', () => {