@tldraw/editor 4.3.0-next.f4772c19540d → 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/dist-cjs/index.d.ts +498 -155
  2. package/dist-cjs/index.js +6 -1
  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 +1 -17
  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 +1 -3
  10. package/dist-cjs/lib/constants.js.map +2 -2
  11. package/dist-cjs/lib/editor/Editor.js +292 -286
  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 +18 -17
  16. package/dist-cjs/lib/editor/derivations/notVisibleShapes.js.map +3 -3
  17. package/dist-cjs/lib/editor/derivations/parentsToChildren.js +12 -3
  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 +5 -6
  22. package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js.map +2 -2
  23. package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js +591 -0
  24. package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js.map +7 -0
  25. package/dist-cjs/lib/editor/managers/SnapManager/SnapManager.js +1 -1
  26. package/dist-cjs/lib/editor/managers/SnapManager/SnapManager.js.map +2 -2
  27. package/dist-cjs/lib/editor/managers/TickManager/TickManager.js +1 -22
  28. package/dist-cjs/lib/editor/managers/TickManager/TickManager.js.map +2 -2
  29. package/dist-cjs/lib/editor/shapes/BaseBoxShapeUtil.js.map +1 -1
  30. package/dist-cjs/lib/editor/shapes/ShapeUtil.js +31 -23
  31. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  32. package/dist-cjs/lib/editor/shapes/group/DashedOutlineBox.js +1 -1
  33. package/dist-cjs/lib/editor/shapes/group/DashedOutlineBox.js.map +2 -2
  34. package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js.map +2 -2
  35. package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/BaseBoxShapeTool.js.map +2 -2
  36. package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js +3 -3
  37. package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js.map +2 -2
  38. package/dist-cjs/lib/editor/types/emit-types.js.map +1 -1
  39. package/dist-cjs/lib/exports/getSvgJsx.js.map +2 -2
  40. package/dist-cjs/lib/exports/parseCss.js +1 -1
  41. package/dist-cjs/lib/exports/parseCss.js.map +2 -2
  42. package/dist-cjs/lib/globals/environment.js +45 -9
  43. package/dist-cjs/lib/globals/environment.js.map +2 -2
  44. package/dist-cjs/lib/globals/menus.js +1 -1
  45. package/dist-cjs/lib/globals/menus.js.map +2 -2
  46. package/dist-cjs/lib/hooks/useCoarsePointer.js +14 -29
  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 +4 -1
  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 +4 -8
  60. package/dist-cjs/lib/hooks/useZoomCss.js.map +2 -2
  61. package/dist-cjs/lib/options.js +6 -1
  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/Geometry2d.js +1 -0
  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 +498 -155
  73. package/dist-esm/index.mjs +7 -2
  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 +1 -17
  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 +1 -3
  81. package/dist-esm/lib/constants.mjs.map +2 -2
  82. package/dist-esm/lib/editor/Editor.mjs +293 -289
  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 +18 -17
  87. package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs.map +3 -3
  88. package/dist-esm/lib/editor/derivations/parentsToChildren.mjs +13 -4
  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 +5 -6
  93. package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs.map +2 -2
  94. package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs +573 -0
  95. package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs.map +7 -0
  96. package/dist-esm/lib/editor/managers/SnapManager/SnapManager.mjs +1 -1
  97. package/dist-esm/lib/editor/managers/SnapManager/SnapManager.mjs.map +2 -2
  98. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs +1 -22
  99. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs.map +2 -2
  100. package/dist-esm/lib/editor/shapes/BaseBoxShapeUtil.mjs.map +1 -1
  101. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +31 -23
  102. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  103. package/dist-esm/lib/editor/shapes/group/DashedOutlineBox.mjs +1 -1
  104. package/dist-esm/lib/editor/shapes/group/DashedOutlineBox.mjs.map +2 -2
  105. package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs.map +2 -2
  106. package/dist-esm/lib/editor/tools/BaseBoxShapeTool/BaseBoxShapeTool.mjs.map +2 -2
  107. package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs +3 -3
  108. package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs.map +2 -2
  109. package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
  110. package/dist-esm/lib/exports/parseCss.mjs +1 -1
  111. package/dist-esm/lib/exports/parseCss.mjs.map +2 -2
  112. package/dist-esm/lib/globals/environment.mjs +45 -9
  113. package/dist-esm/lib/globals/environment.mjs.map +2 -2
  114. package/dist-esm/lib/globals/menus.mjs +1 -1
  115. package/dist-esm/lib/globals/menus.mjs.map +2 -2
  116. package/dist-esm/lib/hooks/useCoarsePointer.mjs +15 -30
  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 +4 -1
  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 +4 -8
  130. package/dist-esm/lib/hooks/useZoomCss.mjs.map +2 -2
  131. package/dist-esm/lib/options.mjs +6 -1
  132. package/dist-esm/lib/options.mjs.map +2 -2
  133. package/dist-esm/lib/primitives/Box.mjs +3 -0
  134. package/dist-esm/lib/primitives/Box.mjs.map +2 -2
  135. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs +1 -0
  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 +14 -12
  143. package/package.json +18 -16
  144. package/src/index.ts +4 -1
  145. package/src/lib/components/ErrorBoundary.tsx +1 -1
  146. package/src/lib/components/GeometryDebuggingView.tsx +1 -19
  147. package/src/lib/components/default-components/DefaultCanvas.tsx +4 -3
  148. package/src/lib/config/TLUserPreferences.test.ts +40 -0
  149. package/src/lib/constants.ts +0 -2
  150. package/src/lib/editor/Editor.test.ts +150 -10
  151. package/src/lib/editor/Editor.ts +459 -379
  152. package/src/lib/editor/bindings/BindingUtil.ts +15 -9
  153. package/src/lib/editor/derivations/bindingsIndex.ts +2 -2
  154. package/src/lib/editor/derivations/notVisibleShapes.ts +37 -23
  155. package/src/lib/editor/derivations/parentsToChildren.ts +18 -7
  156. package/src/lib/editor/managers/ClickManager/ClickManager.test.ts +17 -31
  157. package/src/lib/editor/managers/ClickManager/ClickManager.ts +1 -1
  158. package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.test.ts +129 -79
  159. package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.ts +10 -6
  160. package/src/lib/editor/managers/FontManager/FontManager.test.ts +14 -4
  161. package/src/lib/editor/managers/InputsManager/InputsManager.ts +566 -0
  162. package/src/lib/editor/managers/ScribbleManager/ScribbleManager.test.ts +0 -4
  163. package/src/lib/editor/managers/SnapManager/SnapManager.test.ts +12 -0
  164. package/src/lib/editor/managers/SnapManager/SnapManager.ts +4 -4
  165. package/src/lib/editor/managers/TickManager/TickManager.test.ts +40 -107
  166. package/src/lib/editor/managers/TickManager/TickManager.ts +2 -32
  167. package/src/lib/editor/shapes/BaseBoxShapeUtil.tsx +2 -2
  168. package/src/lib/editor/shapes/ShapeUtil.ts +72 -32
  169. package/src/lib/editor/shapes/group/DashedOutlineBox.tsx +1 -1
  170. package/src/lib/editor/shapes/group/GroupShapeUtil.tsx +1 -3
  171. package/src/lib/editor/tools/BaseBoxShapeTool/BaseBoxShapeTool.ts +2 -1
  172. package/src/lib/editor/tools/BaseBoxShapeTool/children/Pointing.ts +6 -6
  173. package/src/lib/editor/types/emit-types.ts +3 -1
  174. package/src/lib/exports/getSvgJsx.test.ts +10 -19
  175. package/src/lib/exports/getSvgJsx.tsx +2 -5
  176. package/src/lib/exports/parseCss.test.ts +1 -0
  177. package/src/lib/exports/parseCss.ts +1 -1
  178. package/src/lib/globals/environment.ts +65 -10
  179. package/src/lib/globals/menus.ts +1 -1
  180. package/src/lib/hooks/useCoarsePointer.ts +16 -59
  181. package/src/lib/hooks/useEvent.tsx +1 -1
  182. package/src/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.ts +1 -1
  183. package/src/lib/hooks/useGestureEvents.ts +2 -2
  184. package/src/lib/hooks/usePassThroughMouseOverEvents.ts +1 -1
  185. package/src/lib/hooks/usePassThroughWheelEvents.ts +1 -1
  186. package/src/lib/hooks/useScreenBounds.ts +1 -1
  187. package/src/lib/hooks/useStateAttribute.ts +4 -1
  188. package/src/lib/hooks/useTransform.ts +1 -1
  189. package/src/lib/hooks/useZoomCss.ts +3 -8
  190. package/src/lib/options.ts +32 -0
  191. package/src/lib/primitives/Box.ts +9 -0
  192. package/src/lib/primitives/geometry/Geometry2d.ts +1 -0
  193. package/src/lib/utils/reparenting.ts +5 -5
  194. package/src/lib/utils/rotation.ts +1 -1
  195. package/src/version.ts +3 -3
@@ -18,8 +18,56 @@ describe('EdgeScrollManager', () => {
18
18
  }
19
19
  >
20
20
  let edgeScrollManager: EdgeScrollManager
21
+ let mockInputs: {
22
+ _currentScreenPoint: Vec
23
+ currentScreenPoint: Vec
24
+ getCurrentScreenPoint(): Vec
25
+ setCurrentScreenPoint(value: Vec): void
26
+ _isDragging: boolean
27
+ isDragging: boolean
28
+ getIsDragging(): boolean
29
+ setIsDragging(value: boolean): void
30
+ _isPanning: boolean
31
+ isPanning: boolean
32
+ getIsPanning(): boolean
33
+ setIsPanning(value: boolean): void
34
+ }
21
35
 
22
36
  beforeEach(() => {
37
+ // Create a mock inputs object with writable properties and getters
38
+ mockInputs = {
39
+ _currentScreenPoint: new Vec(500, 300),
40
+ get currentScreenPoint() {
41
+ return this._currentScreenPoint
42
+ },
43
+ getCurrentScreenPoint() {
44
+ return this._currentScreenPoint
45
+ },
46
+ setCurrentScreenPoint(value: Vec) {
47
+ this._currentScreenPoint = value
48
+ },
49
+ _isDragging: true,
50
+ get isDragging() {
51
+ return this._isDragging
52
+ },
53
+ getIsDragging() {
54
+ return this._isDragging
55
+ },
56
+ setIsDragging(value: boolean) {
57
+ this._isDragging = value
58
+ },
59
+ _isPanning: false,
60
+ get isPanning() {
61
+ return this._isPanning
62
+ },
63
+ getIsPanning() {
64
+ return this._isPanning
65
+ },
66
+ setIsPanning(value: boolean) {
67
+ this._isPanning = value
68
+ },
69
+ }
70
+
23
71
  editor = {
24
72
  options: {
25
73
  edgeScrollDelay: 200,
@@ -28,22 +76,15 @@ describe('EdgeScrollManager', () => {
28
76
  edgeScrollDistance: 8,
29
77
  coarsePointerWidth: 12,
30
78
  },
31
- inputs: {
32
- currentScreenPoint: new Vec(500, 300),
33
- isDragging: true,
34
- isPanning: false,
35
- },
79
+ inputs: mockInputs as unknown as Editor['inputs'],
36
80
  user: {
37
81
  getEdgeScrollSpeed: vi.fn(() => 1),
38
82
  },
39
83
  getViewportScreenBounds: vi.fn(() => new Box(0, 0, 1000, 600)),
40
- getInstanceState: vi.fn(
41
- () =>
42
- ({
43
- isCoarsePointer: false,
44
- insets: [false, false, false, false], // [top, right, bottom, left]
45
- }) as any
46
- ),
84
+ getInstanceState: vi.fn(() => ({
85
+ isCoarsePointer: false,
86
+ insets: [false, false, false, false], // [top, right, bottom, left]
87
+ })),
47
88
  getCameraOptions: vi.fn(() => ({
48
89
  isLocked: false,
49
90
  panSpeed: 1,
@@ -54,9 +95,17 @@ describe('EdgeScrollManager', () => {
54
95
  getZoomLevel: vi.fn(() => 1),
55
96
  getCamera: vi.fn(() => new Vec(0, 0, 1)),
56
97
  setCamera: vi.fn(),
57
- } as any
98
+ } as unknown as Mocked<
99
+ Editor & {
100
+ user: { getEdgeScrollSpeed: Mock }
101
+ getCamera: Mock
102
+ getCameraOptions: Mock
103
+ getZoomLevel: Mock
104
+ getViewportScreenBounds: Mock
105
+ }
106
+ >
58
107
 
59
- edgeScrollManager = new EdgeScrollManager(editor as any)
108
+ edgeScrollManager = new EdgeScrollManager(editor)
60
109
  })
61
110
 
62
111
  afterEach(() => {
@@ -67,49 +116,45 @@ describe('EdgeScrollManager', () => {
67
116
  it('should initialize with editor reference', () => {
68
117
  expect(edgeScrollManager.editor).toBe(editor)
69
118
  })
70
-
71
- it('should initialize edge scrolling state as false', () => {
72
- // Access private properties for testing
73
- expect((edgeScrollManager as any)._isEdgeScrolling).toBe(false)
74
- expect((edgeScrollManager as any)._edgeScrollDuration).toBe(-1)
75
- })
76
119
  })
77
120
 
78
121
  describe('basic edge scrolling behavior', () => {
79
122
  it('should not trigger edge scrolling when pointer is in center', () => {
80
- editor.inputs.currentScreenPoint = new Vec(500, 300)
123
+ mockInputs.setCurrentScreenPoint(new Vec(500, 300))
81
124
 
82
125
  edgeScrollManager.updateEdgeScrolling(16)
83
126
 
84
- expect((edgeScrollManager as any)._isEdgeScrolling).toBe(false)
85
127
  expect(editor.setCamera).not.toHaveBeenCalled()
86
128
  })
87
129
 
88
130
  it('should start edge scrolling when pointer is near edge', () => {
89
- editor.inputs.currentScreenPoint = new Vec(5, 300)
131
+ mockInputs.setCurrentScreenPoint(new Vec(5, 300))
90
132
 
133
+ // Should not scroll immediately due to delay
91
134
  edgeScrollManager.updateEdgeScrolling(16)
135
+ expect(editor.setCamera).not.toHaveBeenCalled()
92
136
 
93
- expect((edgeScrollManager as any)._isEdgeScrolling).toBe(true)
94
- expect((edgeScrollManager as any)._edgeScrollDuration).toBe(16)
137
+ // Should scroll after delay
138
+ edgeScrollManager.updateEdgeScrolling(200)
139
+ expect(editor.setCamera).toHaveBeenCalled()
95
140
  })
96
141
 
97
142
  it('should stop edge scrolling when pointer moves away from edge', () => {
98
- // Start edge scrolling
99
- editor.inputs.currentScreenPoint = new Vec(5, 300)
100
- edgeScrollManager.updateEdgeScrolling(16)
101
- expect((edgeScrollManager as any)._isEdgeScrolling).toBe(true)
143
+ // Start edge scrolling near edge
144
+ mockInputs.setCurrentScreenPoint(new Vec(5, 300))
145
+ edgeScrollManager.updateEdgeScrolling(300)
146
+ expect(editor.setCamera).toHaveBeenCalled()
102
147
 
103
- // Move pointer to center
104
- editor.inputs.currentScreenPoint = new Vec(500, 300)
148
+ // Move pointer to center - should stop scrolling
149
+ editor.setCamera.mockClear()
150
+ mockInputs.setCurrentScreenPoint(new Vec(500, 300))
105
151
  edgeScrollManager.updateEdgeScrolling(16)
106
152
 
107
- expect((edgeScrollManager as any)._isEdgeScrolling).toBe(false)
108
- expect((edgeScrollManager as any)._edgeScrollDuration).toBe(0)
153
+ expect(editor.setCamera).not.toHaveBeenCalled()
109
154
  })
110
155
 
111
156
  it('should respect edge scroll delay', () => {
112
- editor.inputs.currentScreenPoint = new Vec(5, 300)
157
+ mockInputs.setCurrentScreenPoint(new Vec(5, 300))
113
158
 
114
159
  // First update - should not scroll yet due to delay
115
160
  edgeScrollManager.updateEdgeScrolling(100)
@@ -123,7 +168,7 @@ describe('EdgeScrollManager', () => {
123
168
 
124
169
  describe('edge proximity detection', () => {
125
170
  it('should detect left edge proximity', () => {
126
- editor.inputs.currentScreenPoint = new Vec(5, 300)
171
+ mockInputs.setCurrentScreenPoint(new Vec(5, 300))
127
172
  edgeScrollManager.updateEdgeScrolling(300) // Enough to trigger after delay
128
173
 
129
174
  expect(editor.setCamera).toHaveBeenCalled()
@@ -132,7 +177,7 @@ describe('EdgeScrollManager', () => {
132
177
  })
133
178
 
134
179
  it('should detect right edge proximity', () => {
135
- editor.inputs.currentScreenPoint = new Vec(995, 300)
180
+ mockInputs.setCurrentScreenPoint(new Vec(995, 300))
136
181
  edgeScrollManager.updateEdgeScrolling(300)
137
182
 
138
183
  expect(editor.setCamera).toHaveBeenCalled()
@@ -141,7 +186,7 @@ describe('EdgeScrollManager', () => {
141
186
  })
142
187
 
143
188
  it('should detect top edge proximity', () => {
144
- editor.inputs.currentScreenPoint = new Vec(500, 5)
189
+ mockInputs.setCurrentScreenPoint(new Vec(500, 5))
145
190
  edgeScrollManager.updateEdgeScrolling(300)
146
191
 
147
192
  expect(editor.setCamera).toHaveBeenCalled()
@@ -150,7 +195,7 @@ describe('EdgeScrollManager', () => {
150
195
  })
151
196
 
152
197
  it('should detect bottom edge proximity', () => {
153
- editor.inputs.currentScreenPoint = new Vec(500, 595)
198
+ mockInputs.setCurrentScreenPoint(new Vec(500, 595))
154
199
  edgeScrollManager.updateEdgeScrolling(300)
155
200
 
156
201
  expect(editor.setCamera).toHaveBeenCalled()
@@ -159,7 +204,7 @@ describe('EdgeScrollManager', () => {
159
204
  })
160
205
 
161
206
  it('should handle corner proximity (both x and y)', () => {
162
- editor.inputs.currentScreenPoint = new Vec(5, 5)
207
+ mockInputs.setCurrentScreenPoint(new Vec(5, 5))
163
208
  edgeScrollManager.updateEdgeScrolling(300)
164
209
 
165
210
  expect(editor.setCamera).toHaveBeenCalled()
@@ -172,11 +217,11 @@ describe('EdgeScrollManager', () => {
172
217
  describe('coarse pointer handling', () => {
173
218
  it('should account for coarse pointer width', () => {
174
219
  editor.getInstanceState.mockReturnValue({
220
+ ...editor.getInstanceState(),
175
221
  isCoarsePointer: true,
176
222
  insets: [false, false, false, false],
177
- } as any)
178
-
179
- editor.inputs.currentScreenPoint = new Vec(15, 300)
223
+ })
224
+ mockInputs.setCurrentScreenPoint(new Vec(15, 300))
180
225
  edgeScrollManager.updateEdgeScrolling(300)
181
226
 
182
227
  expect(editor.setCamera).toHaveBeenCalled()
@@ -184,11 +229,11 @@ describe('EdgeScrollManager', () => {
184
229
 
185
230
  it('should not trigger edge scrolling for fine pointer at same position', () => {
186
231
  editor.getInstanceState.mockReturnValue({
232
+ ...editor.getInstanceState(),
187
233
  isCoarsePointer: false,
188
234
  insets: [false, false, false, false],
189
- } as any)
190
-
191
- editor.inputs.currentScreenPoint = new Vec(15, 300)
235
+ })
236
+ mockInputs.setCurrentScreenPoint(new Vec(15, 300))
192
237
  edgeScrollManager.updateEdgeScrolling(300)
193
238
 
194
239
  expect(editor.setCamera).not.toHaveBeenCalled()
@@ -197,8 +242,8 @@ describe('EdgeScrollManager', () => {
197
242
 
198
243
  describe('camera movement conditions', () => {
199
244
  it('should not move camera when not dragging', () => {
200
- editor.inputs.isDragging = false
201
- editor.inputs.currentScreenPoint = new Vec(5, 300)
245
+ editor.inputs.setIsDragging(false)
246
+ mockInputs.setCurrentScreenPoint(new Vec(5, 300))
202
247
 
203
248
  edgeScrollManager.updateEdgeScrolling(300)
204
249
 
@@ -206,8 +251,8 @@ describe('EdgeScrollManager', () => {
206
251
  })
207
252
 
208
253
  it('should not move camera when panning', () => {
209
- editor.inputs.isPanning = true
210
- editor.inputs.currentScreenPoint = new Vec(5, 300)
254
+ editor.inputs.setIsPanning(true)
255
+ mockInputs.setCurrentScreenPoint(new Vec(5, 300))
211
256
 
212
257
  edgeScrollManager.updateEdgeScrolling(300)
213
258
 
@@ -222,7 +267,7 @@ describe('EdgeScrollManager', () => {
222
267
  zoomSteps: [1],
223
268
  wheelBehavior: 'pan' as const,
224
269
  })
225
- editor.inputs.currentScreenPoint = new Vec(5, 300)
270
+ mockInputs.setCurrentScreenPoint(new Vec(5, 300))
226
271
 
227
272
  edgeScrollManager.updateEdgeScrolling(300)
228
273
 
@@ -233,7 +278,7 @@ describe('EdgeScrollManager', () => {
233
278
  describe('camera movement calculation', () => {
234
279
  it('should calculate scroll speed based on user preference', () => {
235
280
  editor.user.getEdgeScrollSpeed.mockReturnValue(2)
236
- editor.inputs.currentScreenPoint = new Vec(5, 300)
281
+ mockInputs.setCurrentScreenPoint(new Vec(5, 300))
237
282
 
238
283
  edgeScrollManager.updateEdgeScrolling(300)
239
284
 
@@ -244,7 +289,7 @@ describe('EdgeScrollManager', () => {
244
289
 
245
290
  it('should apply screen size factor for small screens', () => {
246
291
  editor.getViewportScreenBounds.mockReturnValue(new Box(0, 0, 800, 600))
247
- editor.inputs.currentScreenPoint = new Vec(5, 300)
292
+ mockInputs.setCurrentScreenPoint(new Vec(5, 300))
248
293
 
249
294
  edgeScrollManager.updateEdgeScrolling(300)
250
295
 
@@ -253,7 +298,7 @@ describe('EdgeScrollManager', () => {
253
298
 
254
299
  it('should adjust scroll speed based on zoom level', () => {
255
300
  editor.getZoomLevel.mockReturnValue(2)
256
- editor.inputs.currentScreenPoint = new Vec(5, 300)
301
+ mockInputs.setCurrentScreenPoint(new Vec(5, 300))
257
302
 
258
303
  edgeScrollManager.updateEdgeScrolling(300)
259
304
 
@@ -266,7 +311,7 @@ describe('EdgeScrollManager', () => {
266
311
  it('should add scroll delta to current camera position', () => {
267
312
  const currentCamera = new Vec(100, 200, 1)
268
313
  editor.getCamera.mockReturnValue(currentCamera)
269
- editor.inputs.currentScreenPoint = new Vec(5, 5)
314
+ mockInputs.setCurrentScreenPoint(new Vec(5, 5))
270
315
 
271
316
  edgeScrollManager.updateEdgeScrolling(300)
272
317
 
@@ -280,14 +325,14 @@ describe('EdgeScrollManager', () => {
280
325
 
281
326
  describe('proximity factor calculation', () => {
282
327
  it('should return 0 when not near any edge', () => {
283
- editor.inputs.currentScreenPoint = new Vec(500, 300)
328
+ mockInputs.setCurrentScreenPoint(new Vec(500, 300))
284
329
  edgeScrollManager.updateEdgeScrolling(16)
285
330
 
286
331
  expect(editor.setCamera).not.toHaveBeenCalled()
287
332
  })
288
333
 
289
334
  it('should cap proximity factor at 1', () => {
290
- editor.inputs.currentScreenPoint = new Vec(0, 300)
335
+ mockInputs.setCurrentScreenPoint(new Vec(0, 300))
291
336
  edgeScrollManager.updateEdgeScrolling(300)
292
337
 
293
338
  expect(editor.setCamera).toHaveBeenCalled()
@@ -297,20 +342,20 @@ describe('EdgeScrollManager', () => {
297
342
 
298
343
  describe('edge cases and error handling', () => {
299
344
  it('should handle negative elapsed time', () => {
300
- editor.inputs.currentScreenPoint = new Vec(5, 300)
345
+ mockInputs.setCurrentScreenPoint(new Vec(5, 300))
301
346
 
302
347
  expect(() => edgeScrollManager.updateEdgeScrolling(-16)).not.toThrow()
303
348
  })
304
349
 
305
350
  it('should handle very large elapsed time', () => {
306
- editor.inputs.currentScreenPoint = new Vec(5, 300)
351
+ mockInputs.setCurrentScreenPoint(new Vec(5, 300))
307
352
 
308
353
  expect(() => edgeScrollManager.updateEdgeScrolling(100000)).not.toThrow()
309
354
  })
310
355
 
311
356
  it('should handle zero user edge scroll speed', () => {
312
357
  editor.user.getEdgeScrollSpeed.mockReturnValue(0)
313
- editor.inputs.currentScreenPoint = new Vec(5, 300)
358
+ mockInputs.setCurrentScreenPoint(new Vec(5, 300))
314
359
 
315
360
  edgeScrollManager.updateEdgeScrolling(300)
316
361
 
@@ -322,7 +367,7 @@ describe('EdgeScrollManager', () => {
322
367
  })
323
368
 
324
369
  it('should handle extreme zoom levels', () => {
325
- editor.inputs.currentScreenPoint = new Vec(5, 300)
370
+ mockInputs.setCurrentScreenPoint(new Vec(5, 300))
326
371
 
327
372
  editor.getZoomLevel.mockReturnValue(0.01) // Very zoomed out
328
373
  expect(() => edgeScrollManager.updateEdgeScrolling(300)).not.toThrow()
@@ -335,41 +380,46 @@ describe('EdgeScrollManager', () => {
335
380
  describe('state transitions', () => {
336
381
  it('should properly transition from not scrolling to scrolling', () => {
337
382
  // Start with no edge scrolling
338
- editor.inputs.currentScreenPoint = new Vec(500, 300)
383
+ mockInputs.setCurrentScreenPoint(new Vec(500, 300))
339
384
  edgeScrollManager.updateEdgeScrolling(16)
340
- expect((edgeScrollManager as any)._isEdgeScrolling).toBe(false)
385
+ expect(editor.setCamera).not.toHaveBeenCalled()
341
386
 
342
- // Move to edge
343
- editor.inputs.currentScreenPoint = new Vec(5, 300)
387
+ // Move to edge - should start scrolling after delay
388
+ mockInputs.setCurrentScreenPoint(new Vec(5, 300))
344
389
  edgeScrollManager.updateEdgeScrolling(16)
345
- expect((edgeScrollManager as any)._isEdgeScrolling).toBe(true)
346
- expect((edgeScrollManager as any)._edgeScrollDuration).toBe(16)
390
+ expect(editor.setCamera).not.toHaveBeenCalled() // Not yet, due to delay
391
+
392
+ edgeScrollManager.updateEdgeScrolling(200)
393
+ expect(editor.setCamera).toHaveBeenCalled()
347
394
  })
348
395
 
349
396
  it('should accumulate edge scroll duration over multiple updates', () => {
350
- editor.inputs.currentScreenPoint = new Vec(5, 300)
397
+ mockInputs.setCurrentScreenPoint(new Vec(5, 300))
351
398
 
399
+ // First update - not enough time
352
400
  edgeScrollManager.updateEdgeScrolling(50)
353
- expect((edgeScrollManager as any)._edgeScrollDuration).toBe(50)
401
+ expect(editor.setCamera).not.toHaveBeenCalled()
354
402
 
355
- edgeScrollManager.updateEdgeScrolling(30)
356
- expect((edgeScrollManager as any)._edgeScrollDuration).toBe(80)
403
+ // Second update - still not enough
404
+ edgeScrollManager.updateEdgeScrolling(50)
405
+ expect(editor.setCamera).not.toHaveBeenCalled()
357
406
 
358
- edgeScrollManager.updateEdgeScrolling(25)
359
- expect((edgeScrollManager as any)._edgeScrollDuration).toBe(105)
407
+ // Third update - now should trigger (50 + 50 + 101 = 201ms > 200ms delay)
408
+ edgeScrollManager.updateEdgeScrolling(101)
409
+ expect(editor.setCamera).toHaveBeenCalled()
360
410
  })
361
411
 
362
412
  it('should reset duration when stopping edge scroll', () => {
363
413
  // Start edge scrolling
364
- editor.inputs.currentScreenPoint = new Vec(5, 300)
365
- edgeScrollManager.updateEdgeScrolling(100)
366
- expect((edgeScrollManager as any)._edgeScrollDuration).toBe(100)
414
+ mockInputs.setCurrentScreenPoint(new Vec(5, 300))
415
+ edgeScrollManager.updateEdgeScrolling(300)
416
+ expect(editor.setCamera).toHaveBeenCalled()
367
417
 
368
- // Stop edge scrolling
369
- editor.inputs.currentScreenPoint = new Vec(500, 300)
418
+ // Stop edge scrolling - move away
419
+ editor.setCamera.mockClear()
420
+ mockInputs.setCurrentScreenPoint(new Vec(500, 300))
370
421
  edgeScrollManager.updateEdgeScrolling(16)
371
- expect((edgeScrollManager as any)._isEdgeScrolling).toBe(false)
372
- expect((edgeScrollManager as any)._edgeScrollDuration).toBe(0)
422
+ expect(editor.setCamera).not.toHaveBeenCalled()
373
423
  })
374
424
  })
375
425
  })
@@ -9,6 +9,10 @@ export class EdgeScrollManager {
9
9
  private _isEdgeScrolling = false
10
10
  private _edgeScrollDuration = -1
11
11
 
12
+ getIsEdgeScrolling() {
13
+ return this._isEdgeScrolling
14
+ }
15
+
12
16
  /**
13
17
  * Update the camera position when the mouse is close to the edge of the screen.
14
18
  * Run this on every tick when in a state where edge scrolling is enabled.
@@ -81,11 +85,7 @@ export class EdgeScrollManager {
81
85
 
82
86
  private getEdgeScroll() {
83
87
  const { editor } = this
84
- const {
85
- inputs: {
86
- currentScreenPoint: { x, y },
87
- },
88
- } = editor
88
+ const { x, y } = editor.inputs.getCurrentScreenPoint()
89
89
  const screenBounds = editor.getViewportScreenBounds()
90
90
 
91
91
  const {
@@ -107,7 +107,11 @@ export class EdgeScrollManager {
107
107
  */
108
108
  private moveCameraWhenCloseToEdge(proximityFactor: { x: number; y: number }) {
109
109
  const { editor } = this
110
- if (!editor.inputs.isDragging || editor.inputs.isPanning || editor.getCameraOptions().isLocked)
110
+ if (
111
+ !editor.inputs.getIsDragging() ||
112
+ editor.inputs.getIsPanning() ||
113
+ editor.getCameraOptions().isLocked
114
+ )
111
115
  return
112
116
 
113
117
  if (proximityFactor.x === 0 && proximityFactor.y === 0) return
@@ -1,4 +1,5 @@
1
- import { TLShape, TLShapeId, createShapeId } from '@tldraw/tlschema'
1
+ import { TLParentId, TLShape, TLShapeId, createShapeId, toRichText } from '@tldraw/tlschema'
2
+ import { IndexKey } from '@tldraw/utils'
2
3
  import { Mock, Mocked, vi } from 'vitest'
3
4
  import { Editor } from '../../Editor'
4
5
  import { FontManager, TLFontFace } from './FontManager'
@@ -41,12 +42,21 @@ describe('FontManager', () => {
41
42
  x: 0,
42
43
  y: 0,
43
44
  rotation: 0,
44
- index: 'a1' as any,
45
- parentId: 'page:page' as any,
45
+ index: 'a1' as IndexKey,
46
+ parentId: 'page:page' as TLParentId,
46
47
  opacity: 1,
47
48
  isLocked: false,
48
49
  meta: {},
49
- props: {},
50
+ props: {
51
+ color: 'black',
52
+ size: 'xl',
53
+ font: 'serif',
54
+ textAlign: 'middle',
55
+ w: 100,
56
+ richText: toRichText('❤️'),
57
+ scale: 2,
58
+ autoSize: true,
59
+ },
50
60
  typeName: 'shape' as const,
51
61
  })
52
62