@tldraw/editor 4.2.2 → 4.2.3

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 (200) hide show
  1. package/dist-cjs/index.d.ts +155 -498
  2. package/dist-cjs/index.js +1 -6
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/components/ErrorBoundary.js.map +1 -1
  5. package/dist-cjs/lib/components/GeometryDebuggingView.js +17 -1
  6. package/dist-cjs/lib/components/GeometryDebuggingView.js.map +2 -2
  7. package/dist-cjs/lib/components/default-components/DefaultCanvas.js +3 -3
  8. package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
  9. package/dist-cjs/lib/constants.js +3 -1
  10. package/dist-cjs/lib/constants.js.map +2 -2
  11. package/dist-cjs/lib/editor/Editor.js +286 -292
  12. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  13. package/dist-cjs/lib/editor/bindings/BindingUtil.js.map +2 -2
  14. package/dist-cjs/lib/editor/derivations/bindingsIndex.js.map +2 -2
  15. package/dist-cjs/lib/editor/derivations/notVisibleShapes.js +17 -18
  16. package/dist-cjs/lib/editor/derivations/notVisibleShapes.js.map +3 -3
  17. package/dist-cjs/lib/editor/derivations/parentsToChildren.js +3 -12
  18. package/dist-cjs/lib/editor/derivations/parentsToChildren.js.map +2 -2
  19. package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js +1 -1
  20. package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js.map +2 -2
  21. package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js +6 -5
  22. package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js.map +2 -2
  23. package/dist-cjs/lib/editor/managers/SnapManager/SnapManager.js +1 -1
  24. package/dist-cjs/lib/editor/managers/SnapManager/SnapManager.js.map +2 -2
  25. package/dist-cjs/lib/editor/managers/TickManager/TickManager.js +22 -1
  26. package/dist-cjs/lib/editor/managers/TickManager/TickManager.js.map +2 -2
  27. package/dist-cjs/lib/editor/shapes/BaseBoxShapeUtil.js.map +1 -1
  28. package/dist-cjs/lib/editor/shapes/ShapeUtil.js +23 -31
  29. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  30. package/dist-cjs/lib/editor/shapes/group/DashedOutlineBox.js +1 -1
  31. package/dist-cjs/lib/editor/shapes/group/DashedOutlineBox.js.map +2 -2
  32. package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js.map +2 -2
  33. package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/BaseBoxShapeTool.js.map +2 -2
  34. package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js +3 -3
  35. package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js.map +2 -2
  36. package/dist-cjs/lib/editor/types/emit-types.js.map +1 -1
  37. package/dist-cjs/lib/exports/getSvgJsx.js.map +2 -2
  38. package/dist-cjs/lib/exports/parseCss.js +1 -1
  39. package/dist-cjs/lib/exports/parseCss.js.map +2 -2
  40. package/dist-cjs/lib/globals/environment.js +9 -45
  41. package/dist-cjs/lib/globals/environment.js.map +2 -2
  42. package/dist-cjs/lib/globals/menus.js +1 -1
  43. package/dist-cjs/lib/globals/menus.js.map +2 -2
  44. package/dist-cjs/lib/hooks/useCanvasEvents.js +3 -4
  45. package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
  46. package/dist-cjs/lib/hooks/useCoarsePointer.js +29 -14
  47. package/dist-cjs/lib/hooks/useCoarsePointer.js.map +2 -2
  48. package/dist-cjs/lib/hooks/useEvent.js +1 -1
  49. package/dist-cjs/lib/hooks/useEvent.js.map +2 -2
  50. package/dist-cjs/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.js.map +2 -2
  51. package/dist-cjs/lib/hooks/useGestureEvents.js +1 -1
  52. package/dist-cjs/lib/hooks/useGestureEvents.js.map +2 -2
  53. package/dist-cjs/lib/hooks/usePassThroughMouseOverEvents.js.map +2 -2
  54. package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js.map +2 -2
  55. package/dist-cjs/lib/hooks/useScreenBounds.js.map +2 -2
  56. package/dist-cjs/lib/hooks/useStateAttribute.js +1 -4
  57. package/dist-cjs/lib/hooks/useStateAttribute.js.map +2 -2
  58. package/dist-cjs/lib/hooks/useTransform.js.map +1 -1
  59. package/dist-cjs/lib/hooks/useZoomCss.js +8 -4
  60. package/dist-cjs/lib/hooks/useZoomCss.js.map +2 -2
  61. package/dist-cjs/lib/options.js +1 -6
  62. package/dist-cjs/lib/options.js.map +2 -2
  63. package/dist-cjs/lib/primitives/Box.js +0 -3
  64. package/dist-cjs/lib/primitives/Box.js.map +2 -2
  65. package/dist-cjs/lib/primitives/geometry/Geometry2d.js +0 -1
  66. package/dist-cjs/lib/primitives/geometry/Geometry2d.js.map +2 -2
  67. package/dist-cjs/lib/utils/reparenting.js.map +2 -2
  68. package/dist-cjs/lib/utils/rotation.js +1 -1
  69. package/dist-cjs/lib/utils/rotation.js.map +2 -2
  70. package/dist-cjs/version.js +3 -3
  71. package/dist-cjs/version.js.map +1 -1
  72. package/dist-esm/index.d.mts +155 -498
  73. package/dist-esm/index.mjs +2 -7
  74. package/dist-esm/index.mjs.map +2 -2
  75. package/dist-esm/lib/components/ErrorBoundary.mjs.map +1 -1
  76. package/dist-esm/lib/components/GeometryDebuggingView.mjs +17 -1
  77. package/dist-esm/lib/components/GeometryDebuggingView.mjs.map +2 -2
  78. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +3 -3
  79. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
  80. package/dist-esm/lib/constants.mjs +3 -1
  81. package/dist-esm/lib/constants.mjs.map +2 -2
  82. package/dist-esm/lib/editor/Editor.mjs +289 -293
  83. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  84. package/dist-esm/lib/editor/bindings/BindingUtil.mjs.map +2 -2
  85. package/dist-esm/lib/editor/derivations/bindingsIndex.mjs.map +2 -2
  86. package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs +17 -18
  87. package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs.map +3 -3
  88. package/dist-esm/lib/editor/derivations/parentsToChildren.mjs +4 -13
  89. package/dist-esm/lib/editor/derivations/parentsToChildren.mjs.map +2 -2
  90. package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs +1 -1
  91. package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs.map +2 -2
  92. package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs +6 -5
  93. package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs.map +2 -2
  94. package/dist-esm/lib/editor/managers/SnapManager/SnapManager.mjs +1 -1
  95. package/dist-esm/lib/editor/managers/SnapManager/SnapManager.mjs.map +2 -2
  96. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs +22 -1
  97. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs.map +2 -2
  98. package/dist-esm/lib/editor/shapes/BaseBoxShapeUtil.mjs.map +1 -1
  99. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +23 -31
  100. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  101. package/dist-esm/lib/editor/shapes/group/DashedOutlineBox.mjs +1 -1
  102. package/dist-esm/lib/editor/shapes/group/DashedOutlineBox.mjs.map +2 -2
  103. package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs.map +2 -2
  104. package/dist-esm/lib/editor/tools/BaseBoxShapeTool/BaseBoxShapeTool.mjs.map +2 -2
  105. package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs +3 -3
  106. package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs.map +2 -2
  107. package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
  108. package/dist-esm/lib/exports/parseCss.mjs +1 -1
  109. package/dist-esm/lib/exports/parseCss.mjs.map +2 -2
  110. package/dist-esm/lib/globals/environment.mjs +9 -45
  111. package/dist-esm/lib/globals/environment.mjs.map +2 -2
  112. package/dist-esm/lib/globals/menus.mjs +1 -1
  113. package/dist-esm/lib/globals/menus.mjs.map +2 -2
  114. package/dist-esm/lib/hooks/useCanvasEvents.mjs +3 -4
  115. package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
  116. package/dist-esm/lib/hooks/useCoarsePointer.mjs +30 -15
  117. package/dist-esm/lib/hooks/useCoarsePointer.mjs.map +2 -2
  118. package/dist-esm/lib/hooks/useEvent.mjs +1 -1
  119. package/dist-esm/lib/hooks/useEvent.mjs.map +2 -2
  120. package/dist-esm/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.mjs.map +2 -2
  121. package/dist-esm/lib/hooks/useGestureEvents.mjs +1 -1
  122. package/dist-esm/lib/hooks/useGestureEvents.mjs.map +2 -2
  123. package/dist-esm/lib/hooks/usePassThroughMouseOverEvents.mjs.map +2 -2
  124. package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs.map +2 -2
  125. package/dist-esm/lib/hooks/useScreenBounds.mjs.map +2 -2
  126. package/dist-esm/lib/hooks/useStateAttribute.mjs +1 -4
  127. package/dist-esm/lib/hooks/useStateAttribute.mjs.map +2 -2
  128. package/dist-esm/lib/hooks/useTransform.mjs.map +1 -1
  129. package/dist-esm/lib/hooks/useZoomCss.mjs +8 -4
  130. package/dist-esm/lib/hooks/useZoomCss.mjs.map +2 -2
  131. package/dist-esm/lib/options.mjs +1 -6
  132. package/dist-esm/lib/options.mjs.map +2 -2
  133. package/dist-esm/lib/primitives/Box.mjs +0 -3
  134. package/dist-esm/lib/primitives/Box.mjs.map +2 -2
  135. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs +0 -1
  136. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs.map +2 -2
  137. package/dist-esm/lib/utils/reparenting.mjs.map +2 -2
  138. package/dist-esm/lib/utils/rotation.mjs +1 -1
  139. package/dist-esm/lib/utils/rotation.mjs.map +2 -2
  140. package/dist-esm/version.mjs +3 -3
  141. package/dist-esm/version.mjs.map +1 -1
  142. package/editor.css +12 -14
  143. package/package.json +16 -18
  144. package/src/index.ts +1 -4
  145. package/src/lib/components/ErrorBoundary.tsx +1 -1
  146. package/src/lib/components/GeometryDebuggingView.tsx +19 -1
  147. package/src/lib/components/default-components/DefaultCanvas.tsx +3 -4
  148. package/src/lib/constants.ts +2 -0
  149. package/src/lib/editor/Editor.test.ts +10 -150
  150. package/src/lib/editor/Editor.ts +379 -459
  151. package/src/lib/editor/bindings/BindingUtil.ts +9 -15
  152. package/src/lib/editor/derivations/bindingsIndex.ts +2 -2
  153. package/src/lib/editor/derivations/notVisibleShapes.ts +23 -37
  154. package/src/lib/editor/derivations/parentsToChildren.ts +7 -18
  155. package/src/lib/editor/managers/ClickManager/ClickManager.test.ts +31 -17
  156. package/src/lib/editor/managers/ClickManager/ClickManager.ts +1 -1
  157. package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.test.ts +79 -129
  158. package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.ts +6 -10
  159. package/src/lib/editor/managers/FontManager/FontManager.test.ts +4 -14
  160. package/src/lib/editor/managers/ScribbleManager/ScribbleManager.test.ts +4 -0
  161. package/src/lib/editor/managers/SnapManager/SnapManager.test.ts +0 -12
  162. package/src/lib/editor/managers/SnapManager/SnapManager.ts +4 -4
  163. package/src/lib/editor/managers/TickManager/TickManager.test.ts +107 -40
  164. package/src/lib/editor/managers/TickManager/TickManager.ts +32 -2
  165. package/src/lib/editor/shapes/BaseBoxShapeUtil.tsx +2 -2
  166. package/src/lib/editor/shapes/ShapeUtil.ts +32 -72
  167. package/src/lib/editor/shapes/group/DashedOutlineBox.tsx +1 -1
  168. package/src/lib/editor/shapes/group/GroupShapeUtil.tsx +3 -1
  169. package/src/lib/editor/tools/BaseBoxShapeTool/BaseBoxShapeTool.ts +1 -2
  170. package/src/lib/editor/tools/BaseBoxShapeTool/children/Pointing.ts +6 -6
  171. package/src/lib/editor/types/emit-types.ts +1 -3
  172. package/src/lib/exports/getSvgJsx.test.ts +19 -10
  173. package/src/lib/exports/getSvgJsx.tsx +5 -2
  174. package/src/lib/exports/parseCss.test.ts +0 -1
  175. package/src/lib/exports/parseCss.ts +1 -1
  176. package/src/lib/globals/environment.ts +10 -65
  177. package/src/lib/globals/menus.ts +1 -1
  178. package/src/lib/hooks/useCanvasEvents.ts +3 -4
  179. package/src/lib/hooks/useCoarsePointer.ts +59 -16
  180. package/src/lib/hooks/useEvent.tsx +1 -1
  181. package/src/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.ts +1 -1
  182. package/src/lib/hooks/useGestureEvents.ts +2 -2
  183. package/src/lib/hooks/usePassThroughMouseOverEvents.ts +1 -1
  184. package/src/lib/hooks/usePassThroughWheelEvents.ts +1 -1
  185. package/src/lib/hooks/useScreenBounds.ts +1 -1
  186. package/src/lib/hooks/useStateAttribute.ts +1 -4
  187. package/src/lib/hooks/useTransform.ts +1 -1
  188. package/src/lib/hooks/useZoomCss.ts +8 -3
  189. package/src/lib/options.ts +0 -32
  190. package/src/lib/primitives/Box.ts +0 -9
  191. package/src/lib/primitives/geometry/Geometry2d.ts +0 -1
  192. package/src/lib/utils/reparenting.ts +5 -5
  193. package/src/lib/utils/rotation.ts +1 -1
  194. package/src/version.ts +3 -3
  195. package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js +0 -591
  196. package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js.map +0 -7
  197. package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs +0 -573
  198. package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs.map +0 -7
  199. package/src/lib/config/TLUserPreferences.test.ts +0 -40
  200. package/src/lib/editor/managers/InputsManager/InputsManager.ts +0 -566
@@ -21,17 +21,6 @@ describe('TickManager', () => {
21
21
  let tickManager: TickManager
22
22
  let mockEmit: Mock
23
23
  let mockDisposablesAdd: Mock
24
- let mockInputs: {
25
- _currentScreenPoint: Vec
26
- getCurrentScreenPoint(): Vec
27
- currentScreenPoint: Vec
28
- setCurrentScreenPoint(value: Vec): void
29
- _pointerVelocity: Vec
30
- getPointerVelocity(): Vec
31
- pointerVelocity: Vec
32
- setPointerVelocity(value: Vec): void
33
- updatePointerVelocity(elapsed: number): void
34
- }
35
24
 
36
25
  beforeEach(() => {
37
26
  vi.clearAllMocks()
@@ -52,40 +41,16 @@ describe('TickManager', () => {
52
41
  mockEmit = vi.fn()
53
42
  mockDisposablesAdd = vi.fn()
54
43
 
55
- // Create a mock inputs object with getters and setters
56
- mockInputs = {
57
- _currentScreenPoint: new Vec(100, 100),
58
- getCurrentScreenPoint() {
59
- return this._currentScreenPoint
60
- },
61
- get currentScreenPoint() {
62
- return this.getCurrentScreenPoint()
63
- },
64
- setCurrentScreenPoint(value: Vec) {
65
- this._currentScreenPoint = value
66
- },
67
- _pointerVelocity: new Vec(0, 0),
68
- getPointerVelocity() {
69
- return this._pointerVelocity
70
- },
71
- get pointerVelocity() {
72
- return this.getPointerVelocity()
73
- },
74
- setPointerVelocity(value: Vec) {
75
- this._pointerVelocity = value
76
- },
77
- updatePointerVelocity(_elapsed: number) {
78
- // Mock implementation - no-op for tests
79
- },
80
- }
81
-
82
44
  editor = {
83
45
  emit: mockEmit,
84
46
  disposables: {
85
47
  add: mockDisposablesAdd,
86
48
  },
87
- inputs: mockInputs as unknown as Editor['inputs'],
88
- } as unknown as Mocked<Editor>
49
+ inputs: {
50
+ currentScreenPoint: new Vec(100, 100),
51
+ pointerVelocity: new Vec(0, 0),
52
+ },
53
+ } as any
89
54
 
90
55
  tickManager = new TickManager(editor)
91
56
  })
@@ -178,6 +143,16 @@ describe('TickManager', () => {
178
143
  expect(mockEmit).toHaveBeenCalledTimes(2)
179
144
  })
180
145
 
146
+ it('should update pointer velocity', () => {
147
+ const updatePointerVelocitySpy = vi.spyOn(tickManager as any, 'updatePointerVelocity')
148
+ tickManager.now = 1000
149
+ mockDateNow.mockReturnValue(1016)
150
+
151
+ tickManager.tick()
152
+
153
+ expect(updatePointerVelocitySpy).toHaveBeenCalledWith(16)
154
+ })
155
+
181
156
  it('should schedule next RAF', () => {
182
157
  tickManager.tick()
183
158
  expect(mockRequestAnimationFrame).toHaveBeenCalled()
@@ -217,6 +192,98 @@ describe('TickManager', () => {
217
192
  })
218
193
  })
219
194
 
195
+ describe('updatePointerVelocity method', () => {
196
+ let updatePointerVelocity: any
197
+
198
+ beforeEach(() => {
199
+ // Access private method for testing
200
+ updatePointerVelocity = (tickManager as any).updatePointerVelocity.bind(tickManager)
201
+ // Reset the prevPoint to a known state
202
+ ;(tickManager as any).prevPoint = new Vec(50, 50)
203
+ })
204
+
205
+ it('should return early if elapsed time is 0', () => {
206
+ const originalVelocity = editor.inputs.pointerVelocity.clone()
207
+
208
+ updatePointerVelocity(0)
209
+
210
+ expect(editor.inputs.pointerVelocity).toEqual(originalVelocity)
211
+ })
212
+
213
+ it('should calculate velocity based on pointer movement', () => {
214
+ editor.inputs.currentScreenPoint = new Vec(150, 150)
215
+ ;(tickManager as any).prevPoint = new Vec(50, 50)
216
+ editor.inputs.pointerVelocity = new Vec(0, 0)
217
+
218
+ updatePointerVelocity(100) // 100ms elapsed
219
+
220
+ // Delta should be (100, 100), length = ~141.42, direction = (~0.707, ~0.707)
221
+ // Velocity should be length/elapsed = ~1.414 per ms in each direction
222
+ // But with linear interpolation (lrp factor 0.5), it will be halved
223
+ const expectedVelocity = new Vec(0.5, 0.5) // With lrp factor 0.5
224
+ expect(editor.inputs.pointerVelocity.x).toBeCloseTo(expectedVelocity.x, 1)
225
+ expect(editor.inputs.pointerVelocity.y).toBeCloseTo(expectedVelocity.y, 1)
226
+ })
227
+
228
+ it('should update prevPoint to current screen point', () => {
229
+ const newPoint = new Vec(200, 200)
230
+ editor.inputs.currentScreenPoint = newPoint
231
+
232
+ updatePointerVelocity(16)
233
+
234
+ expect((tickManager as any).prevPoint).toEqual(newPoint)
235
+ })
236
+
237
+ it('should use linear interpolation to smooth velocity', () => {
238
+ editor.inputs.currentScreenPoint = new Vec(150, 50)
239
+ ;(tickManager as any).prevPoint = new Vec(50, 50)
240
+ editor.inputs.pointerVelocity = new Vec(2, 2)
241
+
242
+ updatePointerVelocity(100)
243
+
244
+ // Should interpolate between current velocity (2,2) and new velocity (1,0)
245
+ // lrp with factor 0.5 should give us something between them
246
+ expect(editor.inputs.pointerVelocity.x).toBeGreaterThan(0.5)
247
+ expect(editor.inputs.pointerVelocity.x).toBeLessThan(2)
248
+ })
249
+
250
+ it('should set very small velocity components to 0', () => {
251
+ editor.inputs.currentScreenPoint = new Vec(50.005, 50.005)
252
+ ;(tickManager as any).prevPoint = new Vec(50, 50)
253
+ editor.inputs.pointerVelocity = new Vec(0, 0)
254
+
255
+ updatePointerVelocity(1000) // Long elapsed time = very small velocity
256
+
257
+ expect(editor.inputs.pointerVelocity.x).toBe(0)
258
+ expect(editor.inputs.pointerVelocity.y).toBe(0)
259
+ })
260
+
261
+ it('should handle zero movement (stationary pointer)', () => {
262
+ editor.inputs.currentScreenPoint = new Vec(100, 100)
263
+ ;(tickManager as any).prevPoint = new Vec(100, 100)
264
+ editor.inputs.pointerVelocity = new Vec(1, 1)
265
+
266
+ updatePointerVelocity(16)
267
+
268
+ // Should interpolate towards zero velocity
269
+ expect(editor.inputs.pointerVelocity.x).toBeLessThan(1)
270
+ expect(editor.inputs.pointerVelocity.y).toBeLessThan(1)
271
+ })
272
+
273
+ it('should only update pointerVelocity if it actually changes', () => {
274
+ // Setup scenario where velocity won't change
275
+ editor.inputs.currentScreenPoint = new Vec(100, 100)
276
+ ;(tickManager as any).prevPoint = new Vec(100, 100)
277
+ editor.inputs.pointerVelocity = new Vec(0, 0)
278
+
279
+ const originalVelocity = editor.inputs.pointerVelocity
280
+ updatePointerVelocity(16)
281
+
282
+ // Should still be the same object reference since no change occurred
283
+ expect(editor.inputs.pointerVelocity).toBe(originalVelocity)
284
+ })
285
+ })
286
+
220
287
  describe('RAF throttling behavior', () => {
221
288
  it('should use test-specific RAF behavior in test environment', () => {
222
289
  // The TickManager should detect test environment and use requestAnimationFrame directly
@@ -1,4 +1,5 @@
1
1
  import { throttleToNextFrame as _throttleToNextFrame, bind } from '@tldraw/utils'
2
+ import { Vec } from '../../../primitives/Vec'
2
3
  import { Editor } from '../../Editor'
3
4
 
4
5
  const throttleToNextFrame =
@@ -12,7 +13,6 @@ const throttleToNextFrame =
12
13
  }
13
14
  : _throttleToNextFrame
14
15
 
15
- /** @internal */
16
16
  export class TickManager {
17
17
  constructor(public editor: Editor) {
18
18
  this.editor.disposables.add(this.dispose)
@@ -40,7 +40,7 @@ export class TickManager {
40
40
  const elapsed = now - this.now
41
41
  this.now = now
42
42
 
43
- this.editor.inputs.updatePointerVelocity(elapsed)
43
+ this.updatePointerVelocity(elapsed)
44
44
  this.editor.emit('frame', elapsed)
45
45
  this.editor.emit('tick', elapsed)
46
46
  this.cancelRaf = throttleToNextFrame(this.tick)
@@ -53,4 +53,34 @@ export class TickManager {
53
53
 
54
54
  this.cancelRaf?.()
55
55
  }
56
+
57
+ private prevPoint = new Vec()
58
+
59
+ updatePointerVelocity(elapsed: number) {
60
+ const {
61
+ prevPoint,
62
+ editor: {
63
+ inputs: { currentScreenPoint, pointerVelocity },
64
+ },
65
+ } = this
66
+
67
+ if (elapsed === 0) return
68
+
69
+ const delta = Vec.Sub(currentScreenPoint, prevPoint)
70
+ this.prevPoint = currentScreenPoint.clone()
71
+
72
+ const length = delta.len()
73
+ const direction = length ? delta.div(length) : new Vec(0, 0)
74
+
75
+ // consider adjusting this with an easing rather than a linear interpolation
76
+ const next = pointerVelocity.clone().lrp(direction.mul(length / elapsed), 0.5)
77
+
78
+ // if the velocity is very small, just set it to 0
79
+ if (Math.abs(next.x) < 0.01) next.x = 0
80
+ if (Math.abs(next.y) < 0.01) next.y = 0
81
+
82
+ if (!pointerVelocity.equals(next)) {
83
+ this.editor.inputs.pointerVelocity = next
84
+ }
85
+ }
56
86
  }
@@ -1,4 +1,4 @@
1
- import { ExtractShapeByProps } from '@tldraw/tlschema'
1
+ import { TLBaseShape } from '@tldraw/tlschema'
2
2
  import { lerp } from '@tldraw/utils'
3
3
  import { Geometry2d } from '../../primitives/geometry/Geometry2d'
4
4
  import { Rectangle2d } from '../../primitives/geometry/Rectangle2d'
@@ -7,7 +7,7 @@ import { ShapeUtil, TLResizeInfo } from './ShapeUtil'
7
7
  import { resizeBox } from './shared/resizeBox'
8
8
 
9
9
  /** @public */
10
- export type TLBaseBoxShape = ExtractShapeByProps<{ w: number; h: number }>
10
+ export type TLBaseBoxShape = TLBaseShape<string, { w: number; h: number }>
11
11
 
12
12
  /** @public */
13
13
  export abstract class BaseBoxShapeUtil<Shape extends TLBaseBoxShape> extends ShapeUtil<Shape> {
@@ -26,7 +26,10 @@ import { TLClickEventInfo } from '../types/event-types'
26
26
  import { TLResizeHandle } from '../types/selection-types'
27
27
 
28
28
  /** @public */
29
- export interface TLShapeUtilConstructor<T extends TLShape, U extends ShapeUtil<T> = ShapeUtil<T>> {
29
+ export interface TLShapeUtilConstructor<
30
+ T extends TLUnknownShape,
31
+ U extends ShapeUtil<T> = ShapeUtil<T>,
32
+ > {
30
33
  new (editor: Editor): U
31
34
  type: T['type']
32
35
  props?: RecordProps<T>
@@ -39,11 +42,11 @@ export interface TLShapeUtilConstructor<T extends TLShape, U extends ShapeUtil<T
39
42
  *
40
43
  * @public
41
44
  */
42
- export interface TLShapeUtilCanBindOpts<Shape extends TLShape = TLShape> {
45
+ export interface TLShapeUtilCanBindOpts<Shape extends TLUnknownShape = TLUnknownShape> {
43
46
  /** The type of shape referenced by the `fromId` of the binding. */
44
- fromShapeType: TLShape['type']
47
+ fromShapeType: string
45
48
  /** The type of shape referenced by the `toId` of the binding. */
46
- toShapeType: TLShape['type']
49
+ toShapeType: string
47
50
  /** The type of binding. */
48
51
  bindingType: string
49
52
  }
@@ -76,7 +79,7 @@ export interface TLShapeUtilCanvasSvgDef {
76
79
  }
77
80
 
78
81
  /** @public */
79
- export abstract class ShapeUtil<Shape extends TLShape = TLShape> {
82
+ export abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {
80
83
  /** Configure this shape utils {@link ShapeUtil.options | `options`}. */
81
84
  static configure<T extends TLShapeUtilConstructor<any, any>>(
82
85
  this: T,
@@ -190,7 +193,7 @@ export abstract class ShapeUtil<Shape extends TLShape = TLShape> {
190
193
  * @param shape - The shape.
191
194
  * @public
192
195
  */
193
- canSnap(shape: Shape): boolean {
196
+ canSnap(_shape: Shape): boolean {
194
197
  return true
195
198
  }
196
199
 
@@ -200,7 +203,7 @@ export abstract class ShapeUtil<Shape extends TLShape = TLShape> {
200
203
  * @param shape - The shape.
201
204
  * @public
202
205
  */
203
- canTabTo(shape: Shape): boolean {
206
+ canTabTo(_shape: Shape): boolean {
204
207
  return true
205
208
  }
206
209
 
@@ -209,7 +212,7 @@ export abstract class ShapeUtil<Shape extends TLShape = TLShape> {
209
212
  *
210
213
  * @public
211
214
  */
212
- canScroll(shape: Shape): boolean {
215
+ canScroll(_shape: Shape): boolean {
213
216
  return false
214
217
  }
215
218
 
@@ -227,7 +230,7 @@ export abstract class ShapeUtil<Shape extends TLShape = TLShape> {
227
230
  *
228
231
  * @public
229
232
  */
230
- canEdit(shape: Shape, info: TLEditStartInfo): boolean {
233
+ canEdit(_shape: Shape): boolean {
231
234
  return false
232
235
  }
233
236
 
@@ -236,7 +239,7 @@ export abstract class ShapeUtil<Shape extends TLShape = TLShape> {
236
239
  *
237
240
  * @public
238
241
  */
239
- canResize(shape: Shape): boolean {
242
+ canResize(_shape: Shape): boolean {
240
243
  return true
241
244
  }
242
245
 
@@ -245,7 +248,7 @@ export abstract class ShapeUtil<Shape extends TLShape = TLShape> {
245
248
  *
246
249
  * @public
247
250
  */
248
- canResizeChildren(shape: Shape): boolean {
251
+ canResizeChildren(_shape: Shape): boolean {
249
252
  return true
250
253
  }
251
254
 
@@ -254,16 +257,7 @@ export abstract class ShapeUtil<Shape extends TLShape = TLShape> {
254
257
  *
255
258
  * @public
256
259
  */
257
- canEditInReadonly(shape: Shape): boolean {
258
- return false
259
- }
260
-
261
- /**
262
- * Whether the shape can be edited while locked or while an ancestor is locked.
263
- *
264
- * @public
265
- */
266
- canEditWhileLocked(shape: Shape): boolean {
260
+ canEditInReadonly(_shape: Shape): boolean {
267
261
  return false
268
262
  }
269
263
 
@@ -272,7 +266,7 @@ export abstract class ShapeUtil<Shape extends TLShape = TLShape> {
272
266
  *
273
267
  * @public
274
268
  */
275
- canCrop(shape: Shape): boolean {
269
+ canCrop(_shape: Shape): boolean {
276
270
  return false
277
271
  }
278
272
 
@@ -285,7 +279,7 @@ export abstract class ShapeUtil<Shape extends TLShape = TLShape> {
285
279
  *
286
280
  * @public
287
281
  */
288
- canBeLaidOut(shape: Shape, info: TLShapeUtilCanBeLaidOutOpts): boolean {
282
+ canBeLaidOut(_shape: Shape, _info: TLShapeUtilCanBeLaidOutOpts): boolean {
289
283
  return true
290
284
  }
291
285
 
@@ -296,7 +290,7 @@ export abstract class ShapeUtil<Shape extends TLShape = TLShape> {
296
290
  *
297
291
  * @param shape - The shape.
298
292
  */
299
- canCull(shape: Shape): boolean {
293
+ canCull(_shape: Shape): boolean {
300
294
  return true
301
295
  }
302
296
 
@@ -309,33 +303,13 @@ export abstract class ShapeUtil<Shape extends TLShape = TLShape> {
309
303
  *
310
304
  * @internal
311
305
  */
312
- providesBackgroundForChildren(shape: Shape): boolean {
306
+ providesBackgroundForChildren(_shape: Shape): boolean {
313
307
  return false
314
308
  }
315
309
 
316
310
  /**
317
311
  * Get the clip path to apply to this shape's children.
318
312
  *
319
- * The returned points should define the **inner** clip boundary - the area where
320
- * children will be visible. If your shape has a stroke, you should inset the clip
321
- * path by half the stroke width so children are clipped to the inner edge of the
322
- * stroke rather than its center line.
323
- *
324
- * @example
325
- * ```ts
326
- * override getClipPath(shape: MyShape): Vec[] | undefined {
327
- * const strokeWidth = 2
328
- * const inset = strokeWidth / 2
329
- * // Return points inset by half the stroke width
330
- * return [
331
- * new Vec(inset, inset),
332
- * new Vec(shape.props.w - inset, inset),
333
- * new Vec(shape.props.w - inset, shape.props.h - inset),
334
- * new Vec(inset, shape.props.h - inset),
335
- * ]
336
- * }
337
- * ```
338
- *
339
313
  * @param shape - The shape to get the clip path for
340
314
  * @returns Array of points defining the clipping polygon in local coordinates, or undefined if no clipping
341
315
  * @public
@@ -362,14 +336,14 @@ export abstract class ShapeUtil<Shape extends TLShape = TLShape> {
362
336
  * @returns boolean indicating if this shape should hide in the minimap
363
337
  * @public
364
338
  */
365
- hideInMinimap?(shape: Shape): boolean
339
+ hideInMinimap?(_shape: Shape): boolean
366
340
 
367
341
  /**
368
342
  * Whether the shape should hide its resize handles when selected.
369
343
  *
370
344
  * @public
371
345
  */
372
- hideResizeHandles(shape: Shape): boolean {
346
+ hideResizeHandles(_shape: Shape): boolean {
373
347
  return false
374
348
  }
375
349
 
@@ -378,7 +352,7 @@ export abstract class ShapeUtil<Shape extends TLShape = TLShape> {
378
352
  *
379
353
  * @public
380
354
  */
381
- hideRotateHandle(shape: Shape): boolean {
355
+ hideRotateHandle(_shape: Shape): boolean {
382
356
  return false
383
357
  }
384
358
 
@@ -387,7 +361,7 @@ export abstract class ShapeUtil<Shape extends TLShape = TLShape> {
387
361
  *
388
362
  * @public
389
363
  */
390
- hideSelectionBoundsBg(shape: Shape): boolean {
364
+ hideSelectionBoundsBg(_shape: Shape): boolean {
391
365
  return false
392
366
  }
393
367
 
@@ -396,7 +370,7 @@ export abstract class ShapeUtil<Shape extends TLShape = TLShape> {
396
370
  *
397
371
  * @public
398
372
  */
399
- hideSelectionBoundsFg(shape: Shape): boolean {
373
+ hideSelectionBoundsFg(_shape: Shape): boolean {
400
374
  return false
401
375
  }
402
376
 
@@ -405,7 +379,7 @@ export abstract class ShapeUtil<Shape extends TLShape = TLShape> {
405
379
  *
406
380
  * @public
407
381
  */
408
- isAspectRatioLocked(shape: Shape): boolean {
382
+ isAspectRatioLocked(_shape: Shape): boolean {
409
383
  return false
410
384
  }
411
385
 
@@ -416,10 +390,10 @@ export abstract class ShapeUtil<Shape extends TLShape = TLShape> {
416
390
  * useful in cases like annotating on top of an image, where you usually want to avoid extra
417
391
  * padding around the image if you don't need it.
418
392
  *
419
- * @param shape - The shape to check
393
+ * @param _shape - The shape to check
420
394
  * @returns True if this shape should be treated as an export bounds container
421
395
  */
422
- isExportBoundsContainer(shape: Shape): boolean {
396
+ isExportBoundsContainer(_shape: Shape): boolean {
423
397
  return false
424
398
  }
425
399
 
@@ -468,7 +442,7 @@ export abstract class ShapeUtil<Shape extends TLShape = TLShape> {
468
442
  * @param type - The shape type.
469
443
  * @public
470
444
  */
471
- canReceiveNewChildrenOfType(shape: Shape, _type: TLShape['type']) {
445
+ canReceiveNewChildrenOfType(_shape: Shape, _type: TLShape['type']) {
472
446
  return false
473
447
  }
474
448
 
@@ -516,7 +490,7 @@ export abstract class ShapeUtil<Shape extends TLShape = TLShape> {
516
490
  * Get the geometry to use when snapping to this this shape in translate/resize operations. See
517
491
  * {@link BoundsSnapGeometry} for details.
518
492
  */
519
- getBoundsSnapGeometry(shape: Shape): BoundsSnapGeometry {
493
+ getBoundsSnapGeometry(_shape: Shape): BoundsSnapGeometry {
520
494
  return {}
521
495
  }
522
496
 
@@ -524,15 +498,15 @@ export abstract class ShapeUtil<Shape extends TLShape = TLShape> {
524
498
  * Get the geometry to use when snapping handles to this shape. See {@link HandleSnapGeometry}
525
499
  * for details.
526
500
  */
527
- getHandleSnapGeometry(shape: Shape): HandleSnapGeometry {
501
+ getHandleSnapGeometry(_shape: Shape): HandleSnapGeometry {
528
502
  return {}
529
503
  }
530
504
 
531
- getText(shape: Shape): string | undefined {
505
+ getText(_shape: Shape): string | undefined {
532
506
  return undefined
533
507
  }
534
508
 
535
- getAriaDescriptor(shape: Shape): string | undefined {
509
+ getAriaDescriptor(_shape: Shape): string | undefined {
536
510
  return undefined
537
511
  }
538
512
 
@@ -960,17 +934,3 @@ export interface TLHandleDragInfo<T extends TLShape> {
960
934
  isCreatingShape: boolean
961
935
  initial?: T | undefined
962
936
  }
963
-
964
- /* --------------------------------- Editing -------------------------------- */
965
-
966
- /** @public */
967
- export interface TLEditStartInfo {
968
- type:
969
- | 'press_enter'
970
- | 'click'
971
- | 'double-click'
972
- | 'double-click-edge'
973
- | 'double-click-corner'
974
- | 'click-header'
975
- | 'unknown'
976
- }
@@ -6,7 +6,7 @@ import { getPerfectDashProps } from '../shared/getPerfectDashProps'
6
6
  export function DashedOutlineBox({ bounds, className }: { bounds: Box; className: string }) {
7
7
  const editor = useEditor()
8
8
 
9
- const zoomLevel = useValue('zoom level', () => editor.getEfficientZoomLevel(), [editor])
9
+ const zoomLevel = useValue('zoom level', () => editor.getZoomLevel(), [editor])
10
10
 
11
11
  return (
12
12
  <g className={className} pointerEvents="none" strokeLinecap="round" strokeLinejoin="round">
@@ -55,7 +55,9 @@ export class GroupShapeUtil extends ShapeUtil<TLGroupShape> {
55
55
  const isHintingOtherGroup =
56
56
  hintingShapeIds.length > 0 &&
57
57
  hintingShapeIds.some(
58
- (id) => id !== shape.id && this.editor.isShapeOfType(this.editor.getShape(id)!, 'group')
58
+ (id) =>
59
+ id !== shape.id &&
60
+ this.editor.isShapeOfType<TLGroupShape>(this.editor.getShape(id)!, 'group')
59
61
  )
60
62
 
61
63
  const isFocused = this.editor.getCurrentPageState().focusedGroupId !== shape.id
@@ -1,5 +1,4 @@
1
1
  import { TLShape } from '@tldraw/tlschema'
2
- import { TLBaseBoxShape } from '../../shapes/BaseBoxShapeUtil'
3
2
  import { StateNode, TLStateNodeConstructor } from '../StateNode'
4
3
  import { Idle } from './children/Idle'
5
4
  import { Pointing } from './children/Pointing'
@@ -12,7 +11,7 @@ export abstract class BaseBoxShapeTool extends StateNode {
12
11
  return [Idle, Pointing]
13
12
  }
14
13
 
15
- abstract override shapeType: TLBaseBoxShape['type']
14
+ abstract override shapeType: string
16
15
 
17
16
  onCreate?(_shape: TLShape | null): void | null
18
17
  }
@@ -12,8 +12,8 @@ export class Pointing extends StateNode {
12
12
 
13
13
  override onPointerMove(info: TLPointerEventInfo) {
14
14
  const { editor } = this
15
- if (editor.inputs.getIsDragging()) {
16
- const originPagePoint = editor.inputs.getOriginPagePoint()
15
+ if (editor.inputs.isDragging) {
16
+ const { originPagePoint } = editor.inputs
17
17
 
18
18
  const shapeType = (this.parent as BaseBoxShapeTool)!.shapeType
19
19
 
@@ -23,7 +23,7 @@ export class Pointing extends StateNode {
23
23
  const newPoint = maybeSnapToGrid(originPagePoint, editor)
24
24
 
25
25
  // Allow this to trigger the max shapes reached alert
26
- this.editor.createShapes([
26
+ this.editor.createShapes<TLBaseBoxShape>([
27
27
  {
28
28
  id,
29
29
  type: shapeType,
@@ -78,7 +78,7 @@ export class Pointing extends StateNode {
78
78
  }
79
79
 
80
80
  complete() {
81
- const originPagePoint = this.editor.inputs.getOriginPagePoint()
81
+ const { originPagePoint } = this.editor.inputs
82
82
 
83
83
  const shapeType = (this.parent as BaseBoxShapeTool)!.shapeType as TLBaseBoxShape['type']
84
84
 
@@ -88,7 +88,7 @@ export class Pointing extends StateNode {
88
88
 
89
89
  // Allow this to trigger the max shapes reached alert
90
90
  // todo: add scale here when dynamic size is enabled (is this still needed?)
91
- this.editor.createShapes([
91
+ this.editor.createShapes<TLBaseBoxShape>([
92
92
  {
93
93
  id,
94
94
  type: shapeType,
@@ -127,7 +127,7 @@ export class Pointing extends StateNode {
127
127
  ;(next as TLBaseBoxShape & { props: { scale: number } }).props.scale = scale
128
128
  }
129
129
 
130
- this.editor.updateShape(next)
130
+ this.editor.updateShape<TLBaseBoxShape>(next)
131
131
 
132
132
  this.editor.setSelectedShapes([id])
133
133
 
@@ -1,5 +1,5 @@
1
1
  import { HistoryEntry } from '@tldraw/store'
2
- import { BoxModel, TLPageId, TLRecord, TLShapeId } from '@tldraw/tlschema'
2
+ import { TLPageId, TLRecord, TLShapeId } from '@tldraw/tlschema'
3
3
  import { TLEventInfo } from './event-types'
4
4
 
5
5
  /** @public */
@@ -16,14 +16,12 @@ export interface TLEventMap {
16
16
  event: [TLEventInfo]
17
17
  tick: [number]
18
18
  frame: [number]
19
- resize: [BoxModel]
20
19
  'select-all-text': [{ shapeId: TLShapeId }]
21
20
  'place-caret': [{ shapeId: TLShapeId; point: { x: number; y: number } }]
22
21
  'created-shapes': [TLRecord[]]
23
22
  'edited-shapes': [TLRecord[]]
24
23
  'deleted-shapes': [TLShapeId[]]
25
24
  edit: []
26
- dispose: []
27
25
  }
28
26
 
29
27
  /** @public */
@@ -1,21 +1,30 @@
1
- import { Geometry2d, RecordProps, Rectangle2d, ShapeUtil, T, TLShape, createShapeId } from '../..'
1
+ import {
2
+ Geometry2d,
3
+ RecordProps,
4
+ Rectangle2d,
5
+ ShapeUtil,
6
+ T,
7
+ TLBaseShape,
8
+ createShapeId,
9
+ } from '../..'
2
10
  import { createTLStore } from '../config/createTLStore'
3
11
  import { Editor } from '../editor/Editor'
4
12
  import { Box } from '../primitives/Box'
5
13
  import { getExportDefaultBounds } from './getSvgJsx'
6
14
 
7
- const TEST_SHAPE_TYPE = 'test-shape'
8
-
9
- declare module '@tldraw/tlschema' {
10
- export interface TLGlobalShapePropsMap {
11
- [TEST_SHAPE_TYPE]: { w: number; h: number; x: number; y: number; isContainer?: boolean }
15
+ type ITestShape = TLBaseShape<
16
+ 'test-shape',
17
+ {
18
+ w: number
19
+ h: number
20
+ x: number
21
+ y: number
22
+ isContainer?: boolean
12
23
  }
13
- }
14
-
15
- type ITestShape = TLShape<typeof TEST_SHAPE_TYPE>
24
+ >
16
25
 
17
26
  class TestShape extends ShapeUtil<ITestShape> {
18
- static override type = TEST_SHAPE_TYPE
27
+ static override type = 'test-shape' as const
19
28
  static override props: RecordProps<ITestShape> = {
20
29
  w: T.number,
21
30
  h: T.number,