@tldraw/editor 4.3.0-next.f4772c19540d → 4.4.0-canary.1e3b436e33e4

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 (206) hide show
  1. package/README.md +1 -1
  2. package/dist-cjs/index.d.ts +503 -155
  3. package/dist-cjs/index.js +8 -1
  4. package/dist-cjs/index.js.map +2 -2
  5. package/dist-cjs/lib/components/ErrorBoundary.js.map +1 -1
  6. package/dist-cjs/lib/components/GeometryDebuggingView.js +1 -17
  7. package/dist-cjs/lib/components/GeometryDebuggingView.js.map +2 -2
  8. package/dist-cjs/lib/components/default-components/DefaultCanvas.js +4 -5
  9. package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
  10. package/dist-cjs/lib/constants.js +1 -3
  11. package/dist-cjs/lib/constants.js.map +2 -2
  12. package/dist-cjs/lib/editor/Editor.js +346 -291
  13. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  14. package/dist-cjs/lib/editor/bindings/BindingUtil.js.map +2 -2
  15. package/dist-cjs/lib/editor/derivations/bindingsIndex.js.map +2 -2
  16. package/dist-cjs/lib/editor/derivations/notVisibleShapes.js +16 -23
  17. package/dist-cjs/lib/editor/derivations/notVisibleShapes.js.map +3 -3
  18. package/dist-cjs/lib/editor/derivations/parentsToChildren.js +12 -3
  19. package/dist-cjs/lib/editor/derivations/parentsToChildren.js.map +2 -2
  20. package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js +1 -1
  21. package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js.map +2 -2
  22. package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js +5 -6
  23. package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js.map +2 -2
  24. package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js +591 -0
  25. package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js.map +7 -0
  26. package/dist-cjs/lib/editor/managers/SnapManager/SnapManager.js +1 -1
  27. package/dist-cjs/lib/editor/managers/SnapManager/SnapManager.js.map +2 -2
  28. package/dist-cjs/lib/editor/managers/SpatialIndexManager/RBushIndex.js +144 -0
  29. package/dist-cjs/lib/editor/managers/SpatialIndexManager/RBushIndex.js.map +7 -0
  30. package/dist-cjs/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.js +181 -0
  31. package/dist-cjs/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.js.map +7 -0
  32. package/dist-cjs/lib/editor/managers/TickManager/TickManager.js +1 -22
  33. package/dist-cjs/lib/editor/managers/TickManager/TickManager.js.map +2 -2
  34. package/dist-cjs/lib/editor/shapes/BaseBoxShapeUtil.js.map +1 -1
  35. package/dist-cjs/lib/editor/shapes/ShapeUtil.js +31 -23
  36. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  37. package/dist-cjs/lib/editor/shapes/group/DashedOutlineBox.js +1 -1
  38. package/dist-cjs/lib/editor/shapes/group/DashedOutlineBox.js.map +2 -2
  39. package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js.map +2 -2
  40. package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/BaseBoxShapeTool.js.map +2 -2
  41. package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js +3 -3
  42. package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js.map +2 -2
  43. package/dist-cjs/lib/editor/types/emit-types.js.map +1 -1
  44. package/dist-cjs/lib/exports/getSvgJsx.js.map +2 -2
  45. package/dist-cjs/lib/exports/parseCss.js +1 -1
  46. package/dist-cjs/lib/exports/parseCss.js.map +2 -2
  47. package/dist-cjs/lib/globals/environment.js +45 -9
  48. package/dist-cjs/lib/globals/environment.js.map +2 -2
  49. package/dist-cjs/lib/globals/menus.js +1 -1
  50. package/dist-cjs/lib/globals/menus.js.map +2 -2
  51. package/dist-cjs/lib/hooks/useCoarsePointer.js +14 -29
  52. package/dist-cjs/lib/hooks/useCoarsePointer.js.map +2 -2
  53. package/dist-cjs/lib/hooks/useEvent.js +1 -1
  54. package/dist-cjs/lib/hooks/useEvent.js.map +2 -2
  55. package/dist-cjs/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.js.map +2 -2
  56. package/dist-cjs/lib/hooks/useGestureEvents.js +1 -1
  57. package/dist-cjs/lib/hooks/useGestureEvents.js.map +2 -2
  58. package/dist-cjs/lib/hooks/usePassThroughMouseOverEvents.js.map +2 -2
  59. package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js.map +2 -2
  60. package/dist-cjs/lib/hooks/useScreenBounds.js.map +2 -2
  61. package/dist-cjs/lib/hooks/useStateAttribute.js +4 -1
  62. package/dist-cjs/lib/hooks/useStateAttribute.js.map +2 -2
  63. package/dist-cjs/lib/hooks/useTransform.js.map +1 -1
  64. package/dist-cjs/lib/hooks/useZoomCss.js +4 -8
  65. package/dist-cjs/lib/hooks/useZoomCss.js.map +2 -2
  66. package/dist-cjs/lib/options.js +6 -1
  67. package/dist-cjs/lib/options.js.map +2 -2
  68. package/dist-cjs/lib/primitives/Box.js +3 -0
  69. package/dist-cjs/lib/primitives/Box.js.map +2 -2
  70. package/dist-cjs/lib/primitives/geometry/Geometry2d.js +1 -0
  71. package/dist-cjs/lib/primitives/geometry/Geometry2d.js.map +2 -2
  72. package/dist-cjs/lib/utils/reparenting.js.map +2 -2
  73. package/dist-cjs/lib/utils/rotation.js +1 -1
  74. package/dist-cjs/lib/utils/rotation.js.map +2 -2
  75. package/dist-cjs/version.js +3 -3
  76. package/dist-cjs/version.js.map +1 -1
  77. package/dist-esm/index.d.mts +503 -155
  78. package/dist-esm/index.mjs +9 -2
  79. package/dist-esm/index.mjs.map +2 -2
  80. package/dist-esm/lib/components/ErrorBoundary.mjs.map +1 -1
  81. package/dist-esm/lib/components/GeometryDebuggingView.mjs +1 -17
  82. package/dist-esm/lib/components/GeometryDebuggingView.mjs.map +2 -2
  83. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +4 -5
  84. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
  85. package/dist-esm/lib/constants.mjs +1 -3
  86. package/dist-esm/lib/constants.mjs.map +2 -2
  87. package/dist-esm/lib/editor/Editor.mjs +347 -294
  88. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  89. package/dist-esm/lib/editor/bindings/BindingUtil.mjs.map +2 -2
  90. package/dist-esm/lib/editor/derivations/bindingsIndex.mjs.map +2 -2
  91. package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs +16 -23
  92. package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs.map +3 -3
  93. package/dist-esm/lib/editor/derivations/parentsToChildren.mjs +13 -4
  94. package/dist-esm/lib/editor/derivations/parentsToChildren.mjs.map +2 -2
  95. package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs +1 -1
  96. package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs.map +2 -2
  97. package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs +5 -6
  98. package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs.map +2 -2
  99. package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs +573 -0
  100. package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs.map +7 -0
  101. package/dist-esm/lib/editor/managers/SnapManager/SnapManager.mjs +1 -1
  102. package/dist-esm/lib/editor/managers/SnapManager/SnapManager.mjs.map +2 -2
  103. package/dist-esm/lib/editor/managers/SpatialIndexManager/RBushIndex.mjs +114 -0
  104. package/dist-esm/lib/editor/managers/SpatialIndexManager/RBushIndex.mjs.map +7 -0
  105. package/dist-esm/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.mjs +161 -0
  106. package/dist-esm/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.mjs.map +7 -0
  107. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs +1 -22
  108. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs.map +2 -2
  109. package/dist-esm/lib/editor/shapes/BaseBoxShapeUtil.mjs.map +1 -1
  110. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +31 -23
  111. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  112. package/dist-esm/lib/editor/shapes/group/DashedOutlineBox.mjs +1 -1
  113. package/dist-esm/lib/editor/shapes/group/DashedOutlineBox.mjs.map +2 -2
  114. package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs.map +2 -2
  115. package/dist-esm/lib/editor/tools/BaseBoxShapeTool/BaseBoxShapeTool.mjs.map +2 -2
  116. package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs +3 -3
  117. package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs.map +2 -2
  118. package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
  119. package/dist-esm/lib/exports/parseCss.mjs +1 -1
  120. package/dist-esm/lib/exports/parseCss.mjs.map +2 -2
  121. package/dist-esm/lib/globals/environment.mjs +45 -9
  122. package/dist-esm/lib/globals/environment.mjs.map +2 -2
  123. package/dist-esm/lib/globals/menus.mjs +1 -1
  124. package/dist-esm/lib/globals/menus.mjs.map +2 -2
  125. package/dist-esm/lib/hooks/useCoarsePointer.mjs +15 -30
  126. package/dist-esm/lib/hooks/useCoarsePointer.mjs.map +2 -2
  127. package/dist-esm/lib/hooks/useEvent.mjs +1 -1
  128. package/dist-esm/lib/hooks/useEvent.mjs.map +2 -2
  129. package/dist-esm/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.mjs.map +2 -2
  130. package/dist-esm/lib/hooks/useGestureEvents.mjs +1 -1
  131. package/dist-esm/lib/hooks/useGestureEvents.mjs.map +2 -2
  132. package/dist-esm/lib/hooks/usePassThroughMouseOverEvents.mjs.map +2 -2
  133. package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs.map +2 -2
  134. package/dist-esm/lib/hooks/useScreenBounds.mjs.map +2 -2
  135. package/dist-esm/lib/hooks/useStateAttribute.mjs +4 -1
  136. package/dist-esm/lib/hooks/useStateAttribute.mjs.map +2 -2
  137. package/dist-esm/lib/hooks/useTransform.mjs.map +1 -1
  138. package/dist-esm/lib/hooks/useZoomCss.mjs +4 -8
  139. package/dist-esm/lib/hooks/useZoomCss.mjs.map +2 -2
  140. package/dist-esm/lib/options.mjs +6 -1
  141. package/dist-esm/lib/options.mjs.map +2 -2
  142. package/dist-esm/lib/primitives/Box.mjs +3 -0
  143. package/dist-esm/lib/primitives/Box.mjs.map +2 -2
  144. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs +1 -0
  145. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs.map +2 -2
  146. package/dist-esm/lib/utils/reparenting.mjs.map +2 -2
  147. package/dist-esm/lib/utils/rotation.mjs +1 -1
  148. package/dist-esm/lib/utils/rotation.mjs.map +2 -2
  149. package/dist-esm/version.mjs +3 -3
  150. package/dist-esm/version.mjs.map +1 -1
  151. package/editor.css +14 -12
  152. package/package.json +21 -17
  153. package/src/index.ts +5 -1
  154. package/src/lib/components/ErrorBoundary.tsx +1 -1
  155. package/src/lib/components/GeometryDebuggingView.tsx +1 -19
  156. package/src/lib/components/default-components/DefaultCanvas.tsx +5 -8
  157. package/src/lib/config/TLUserPreferences.test.ts +40 -0
  158. package/src/lib/constants.ts +0 -2
  159. package/src/lib/editor/Editor.test.ts +150 -10
  160. package/src/lib/editor/Editor.ts +533 -384
  161. package/src/lib/editor/bindings/BindingUtil.ts +15 -9
  162. package/src/lib/editor/derivations/bindingsIndex.ts +2 -2
  163. package/src/lib/editor/derivations/notVisibleShapes.ts +21 -33
  164. package/src/lib/editor/derivations/parentsToChildren.ts +18 -7
  165. package/src/lib/editor/managers/ClickManager/ClickManager.test.ts +17 -31
  166. package/src/lib/editor/managers/ClickManager/ClickManager.ts +1 -1
  167. package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.test.ts +129 -79
  168. package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.ts +10 -6
  169. package/src/lib/editor/managers/FontManager/FontManager.test.ts +14 -4
  170. package/src/lib/editor/managers/InputsManager/InputsManager.ts +566 -0
  171. package/src/lib/editor/managers/ScribbleManager/ScribbleManager.test.ts +0 -4
  172. package/src/lib/editor/managers/SnapManager/SnapManager.test.ts +12 -0
  173. package/src/lib/editor/managers/SnapManager/SnapManager.ts +4 -4
  174. package/src/lib/editor/managers/SpatialIndexManager/RBushIndex.ts +144 -0
  175. package/src/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.ts +215 -0
  176. package/src/lib/editor/managers/TickManager/TickManager.test.ts +40 -107
  177. package/src/lib/editor/managers/TickManager/TickManager.ts +2 -32
  178. package/src/lib/editor/shapes/BaseBoxShapeUtil.tsx +2 -2
  179. package/src/lib/editor/shapes/ShapeUtil.ts +72 -32
  180. package/src/lib/editor/shapes/group/DashedOutlineBox.tsx +1 -1
  181. package/src/lib/editor/shapes/group/GroupShapeUtil.tsx +1 -3
  182. package/src/lib/editor/tools/BaseBoxShapeTool/BaseBoxShapeTool.ts +2 -1
  183. package/src/lib/editor/tools/BaseBoxShapeTool/children/Pointing.ts +6 -6
  184. package/src/lib/editor/types/emit-types.ts +3 -1
  185. package/src/lib/exports/getSvgJsx.test.ts +10 -19
  186. package/src/lib/exports/getSvgJsx.tsx +2 -5
  187. package/src/lib/exports/parseCss.test.ts +1 -0
  188. package/src/lib/exports/parseCss.ts +1 -1
  189. package/src/lib/globals/environment.ts +65 -10
  190. package/src/lib/globals/menus.ts +1 -1
  191. package/src/lib/hooks/useCoarsePointer.ts +16 -59
  192. package/src/lib/hooks/useEvent.tsx +1 -1
  193. package/src/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.ts +1 -1
  194. package/src/lib/hooks/useGestureEvents.ts +2 -2
  195. package/src/lib/hooks/usePassThroughMouseOverEvents.ts +1 -1
  196. package/src/lib/hooks/usePassThroughWheelEvents.ts +1 -1
  197. package/src/lib/hooks/useScreenBounds.ts +1 -1
  198. package/src/lib/hooks/useStateAttribute.ts +4 -1
  199. package/src/lib/hooks/useTransform.ts +1 -1
  200. package/src/lib/hooks/useZoomCss.ts +3 -8
  201. package/src/lib/options.ts +32 -0
  202. package/src/lib/primitives/Box.ts +9 -0
  203. package/src/lib/primitives/geometry/Geometry2d.ts +1 -0
  204. package/src/lib/utils/reparenting.ts +5 -5
  205. package/src/lib/utils/rotation.ts +1 -1
  206. package/src/version.ts +3 -3
@@ -0,0 +1,566 @@
1
+ import { atom, computed, unsafe__withoutCapture } from '@tldraw/state'
2
+ import { AtomSet } from '@tldraw/store'
3
+ import { TLINSTANCE_ID, TLPOINTER_ID } from '@tldraw/tlschema'
4
+ import { INTERNAL_POINTER_IDS } from '../../../constants'
5
+ import { Vec } from '../../../primitives/Vec'
6
+ import { isAccelKey } from '../../../utils/keyboard'
7
+ import { Editor } from '../../Editor'
8
+ import { TLPinchEventInfo, TLPointerEventInfo, TLWheelEventInfo } from '../../types/event-types'
9
+
10
+ /** @public */
11
+ export class InputsManager {
12
+ constructor(private readonly editor: Editor) {}
13
+
14
+ private _originPagePoint = atom<Vec>('originPagePoint', new Vec())
15
+ /**
16
+ * The most recent pointer down's position in the current page space.
17
+ */
18
+ getOriginPagePoint() {
19
+ return this._originPagePoint.get()
20
+ }
21
+ /**
22
+ * @deprecated Use `getOriginPagePoint()` instead.
23
+ */
24
+ // eslint-disable-next-line no-restricted-syntax
25
+ get originPagePoint() {
26
+ return this.getOriginPagePoint()
27
+ }
28
+
29
+ private _originScreenPoint = atom<Vec>('originScreenPoint', new Vec())
30
+ /**
31
+ * The most recent pointer down's position in screen space.
32
+ */
33
+ getOriginScreenPoint() {
34
+ return this._originScreenPoint.get()
35
+ }
36
+ /**
37
+ * @deprecated Use `getOriginScreenPoint()` instead.
38
+ */
39
+ // eslint-disable-next-line no-restricted-syntax
40
+ get originScreenPoint() {
41
+ return this.getOriginScreenPoint()
42
+ }
43
+
44
+ private _previousPagePoint = atom<Vec>('previousPagePoint', new Vec())
45
+ /**
46
+ * The previous pointer position in the current page space.
47
+ */
48
+ getPreviousPagePoint() {
49
+ return this._previousPagePoint.get()
50
+ }
51
+ /**
52
+ * @deprecated Use `getPreviousPagePoint()` instead.
53
+ */
54
+ // eslint-disable-next-line no-restricted-syntax
55
+ get previousPagePoint() {
56
+ return this.getPreviousPagePoint()
57
+ }
58
+
59
+ private _previousScreenPoint = atom<Vec>('previousScreenPoint', new Vec())
60
+ /**
61
+ * The previous pointer position in screen space.
62
+ */
63
+ getPreviousScreenPoint() {
64
+ return this._previousScreenPoint.get()
65
+ }
66
+ /**
67
+ * @deprecated Use `getPreviousScreenPoint()` instead.
68
+ */
69
+ // eslint-disable-next-line no-restricted-syntax
70
+ get previousScreenPoint() {
71
+ return this.getPreviousScreenPoint()
72
+ }
73
+
74
+ private _currentPagePoint = atom<Vec>('currentPagePoint', new Vec())
75
+ /**
76
+ * The most recent pointer position in the current page space.
77
+ */
78
+ getCurrentPagePoint() {
79
+ return this._currentPagePoint.get()
80
+ }
81
+ /**
82
+ * @deprecated Use `getCurrentPagePoint()` instead.
83
+ */
84
+ // eslint-disable-next-line no-restricted-syntax
85
+ get currentPagePoint() {
86
+ return this.getCurrentPagePoint()
87
+ }
88
+
89
+ private _currentScreenPoint = atom<Vec>('currentScreenPoint', new Vec())
90
+ /**
91
+ * The most recent pointer position in screen space.
92
+ */
93
+ getCurrentScreenPoint() {
94
+ return this._currentScreenPoint.get()
95
+ }
96
+ /**
97
+ * @deprecated Use `getCurrentScreenPoint()` instead.
98
+ */
99
+ // eslint-disable-next-line no-restricted-syntax
100
+ get currentScreenPoint() {
101
+ return this.getCurrentScreenPoint()
102
+ }
103
+
104
+ private _pointerVelocity = atom<Vec>('pointerVelocity', new Vec())
105
+ /**
106
+ * Velocity of mouse pointer, in pixels per millisecond.
107
+ */
108
+ getPointerVelocity() {
109
+ return this._pointerVelocity.get()
110
+ }
111
+ /**
112
+ * @deprecated Use `getPointerVelocity()` instead.
113
+ */
114
+ // eslint-disable-next-line no-restricted-syntax
115
+ get pointerVelocity() {
116
+ return this.getPointerVelocity()
117
+ }
118
+
119
+ /**
120
+ * Normally you shouldn't need to set the pointer velocity directly, this is set by the tick manager.
121
+ * However, this is currently used in tests to fake pointer velocity.
122
+ * @param pointerVelocity - The pointer velocity.
123
+ * @internal
124
+ */
125
+ setPointerVelocity(pointerVelocity: Vec) {
126
+ this._pointerVelocity.set(pointerVelocity)
127
+ }
128
+
129
+ /**
130
+ * A set containing the currently pressed keys.
131
+ */
132
+ readonly keys = new AtomSet<string>('keys')
133
+
134
+ /**
135
+ * A set containing the currently pressed buttons.
136
+ */
137
+ readonly buttons = new AtomSet<number>('buttons')
138
+
139
+ private _isPen = atom<boolean>('isPen', false)
140
+
141
+ /**
142
+ * Whether the input is from a pen.
143
+ */
144
+ getIsPen() {
145
+ return this._isPen.get()
146
+ }
147
+ /**
148
+ * @deprecated Use `getIsPen()` instead.
149
+ */
150
+ // eslint-disable-next-line no-restricted-syntax
151
+ get isPen() {
152
+ return this.getIsPen()
153
+ }
154
+ // eslint-disable-next-line no-restricted-syntax
155
+ set isPen(isPen: boolean) {
156
+ this.setIsPen(isPen)
157
+ }
158
+ /**
159
+ * @param isPen - Whether the input is from a pen.
160
+ */
161
+ setIsPen(isPen: boolean) {
162
+ this._isPen.set(isPen)
163
+ }
164
+
165
+ private _shiftKey = atom<boolean>('shiftKey', false)
166
+ /**
167
+ * Whether the shift key is currently pressed.
168
+ */
169
+ getShiftKey() {
170
+ return this._shiftKey.get()
171
+ }
172
+ /**
173
+ * @deprecated Use `getShiftKey()` instead.
174
+ */
175
+ // eslint-disable-next-line no-restricted-syntax
176
+ get shiftKey() {
177
+ return this.getShiftKey()
178
+ }
179
+ // eslint-disable-next-line no-restricted-syntax
180
+ set shiftKey(shiftKey: boolean) {
181
+ this.setShiftKey(shiftKey)
182
+ }
183
+ /**
184
+ * @param shiftKey - Whether the shift key is pressed.
185
+ * @internal
186
+ */
187
+ setShiftKey(shiftKey: boolean) {
188
+ this._shiftKey.set(shiftKey)
189
+ }
190
+
191
+ private _metaKey = atom<boolean>('metaKey', false)
192
+ /**
193
+ * Whether the meta key is currently pressed.
194
+ */
195
+ getMetaKey() {
196
+ return this._metaKey.get()
197
+ }
198
+ /**
199
+ * @deprecated Use `getMetaKey()` instead.
200
+ */
201
+ // eslint-disable-next-line no-restricted-syntax
202
+ get metaKey() {
203
+ return this.getMetaKey()
204
+ }
205
+ // eslint-disable-next-line no-restricted-syntax
206
+ set metaKey(metaKey: boolean) {
207
+ this.setMetaKey(metaKey)
208
+ }
209
+ /**
210
+ * @param metaKey - Whether the meta key is pressed.
211
+ * @internal
212
+ */
213
+ setMetaKey(metaKey: boolean) {
214
+ this._metaKey.set(metaKey)
215
+ }
216
+
217
+ private _ctrlKey = atom<boolean>('ctrlKey', false)
218
+ /**
219
+ * Whether the ctrl or command key is currently pressed.
220
+ */
221
+ getCtrlKey() {
222
+ return this._ctrlKey.get()
223
+ }
224
+ /**
225
+ * @deprecated Use `getCtrlKey()` instead.
226
+ */
227
+ // eslint-disable-next-line no-restricted-syntax
228
+ get ctrlKey() {
229
+ return this.getCtrlKey()
230
+ }
231
+ // eslint-disable-next-line no-restricted-syntax
232
+ set ctrlKey(ctrlKey: boolean) {
233
+ this.setCtrlKey(ctrlKey)
234
+ }
235
+ /**
236
+ * @param ctrlKey - Whether the ctrl key is pressed.
237
+ * @internal
238
+ */
239
+ setCtrlKey(ctrlKey: boolean) {
240
+ this._ctrlKey.set(ctrlKey)
241
+ }
242
+
243
+ private _altKey = atom<boolean>('altKey', false)
244
+ /**
245
+ * Whether the alt or option key is currently pressed.
246
+ */
247
+ getAltKey() {
248
+ return this._altKey.get()
249
+ }
250
+ /**
251
+ * @deprecated Use `getAltKey()` instead.
252
+ */
253
+ // eslint-disable-next-line no-restricted-syntax
254
+ get altKey() {
255
+ return this.getAltKey()
256
+ }
257
+ // eslint-disable-next-line no-restricted-syntax
258
+ set altKey(altKey: boolean) {
259
+ this.setAltKey(altKey)
260
+ }
261
+ /**
262
+ * @param altKey - Whether the alt key is pressed.
263
+ * @internal
264
+ */
265
+ setAltKey(altKey: boolean) {
266
+ this._altKey.set(altKey)
267
+ }
268
+
269
+ /**
270
+ * Is the accelerator key (cmd on mac, ctrl elsewhere) currently pressed.
271
+ */
272
+ getAccelKey() {
273
+ return isAccelKey({ metaKey: this.getMetaKey(), ctrlKey: this.getCtrlKey() })
274
+ }
275
+ /**
276
+ * @deprecated Use `getAccelKey()` instead.
277
+ */
278
+ // eslint-disable-next-line no-restricted-syntax
279
+ get accelKey() {
280
+ return this.getAccelKey()
281
+ }
282
+
283
+ private _isDragging = atom<boolean>('isDragging', false)
284
+ /**
285
+ * Whether the user is dragging.
286
+ */
287
+ getIsDragging() {
288
+ return this._isDragging.get()
289
+ }
290
+ /**
291
+ * Soon to be deprecated, use `getIsDragging()` instead.
292
+ */
293
+ // eslint-disable-next-line no-restricted-syntax
294
+ get isDragging() {
295
+ return this.getIsDragging()
296
+ }
297
+ // eslint-disable-next-line no-restricted-syntax
298
+ set isDragging(isDragging: boolean) {
299
+ this.setIsDragging(isDragging)
300
+ }
301
+ /**
302
+ * @param isDragging - Whether the user is dragging.
303
+ */
304
+ setIsDragging(isDragging: boolean) {
305
+ this._isDragging.set(isDragging)
306
+ }
307
+
308
+ private _isPointing = atom<boolean>('isPointing', false)
309
+ /**
310
+ * Whether the user is pointing.
311
+ */
312
+ getIsPointing() {
313
+ return this._isPointing.get()
314
+ }
315
+ /**
316
+ * @deprecated Use `getIsPointing()` instead.
317
+ */
318
+ // eslint-disable-next-line no-restricted-syntax
319
+ get isPointing() {
320
+ return this.getIsPointing()
321
+ }
322
+ // eslint-disable-next-line no-restricted-syntax
323
+ set isPointing(isPointing: boolean) {
324
+ this.setIsPointing(isPointing)
325
+ }
326
+ /**
327
+ * @param isPointing - Whether the user is pointing.
328
+ * @internal
329
+ */
330
+ setIsPointing(isPointing: boolean) {
331
+ this._isPointing.set(isPointing)
332
+ }
333
+
334
+ private _isPinching = atom<boolean>('isPinching', false)
335
+ /**
336
+ * Whether the user is pinching.
337
+ */
338
+ getIsPinching() {
339
+ return this._isPinching.get()
340
+ }
341
+ /**
342
+ * @deprecated Use `getIsPinching()` instead.
343
+ */
344
+ // eslint-disable-next-line no-restricted-syntax
345
+ get isPinching() {
346
+ return this.getIsPinching()
347
+ }
348
+ // eslint-disable-next-line no-restricted-syntax
349
+ set isPinching(isPinching: boolean) {
350
+ this.setIsPinching(isPinching)
351
+ }
352
+ /**
353
+ * @param isPinching - Whether the user is pinching.
354
+ * @internal
355
+ */
356
+ setIsPinching(isPinching: boolean) {
357
+ this._isPinching.set(isPinching)
358
+ }
359
+
360
+ private _isEditing = atom<boolean>('isEditing', false)
361
+ /**
362
+ * Whether the user is editing.
363
+ */
364
+ getIsEditing() {
365
+ return this._isEditing.get()
366
+ }
367
+ /**
368
+ * @deprecated Use `getIsEditing()` instead.
369
+ */
370
+ // eslint-disable-next-line no-restricted-syntax
371
+ get isEditing() {
372
+ return this.getIsEditing()
373
+ }
374
+ // eslint-disable-next-line no-restricted-syntax
375
+ set isEditing(isEditing: boolean) {
376
+ this.setIsEditing(isEditing)
377
+ }
378
+ /**
379
+ * @param isEditing - Whether the user is editing.
380
+ */
381
+ setIsEditing(isEditing: boolean) {
382
+ this._isEditing.set(isEditing)
383
+ }
384
+
385
+ private _isPanning = atom<boolean>('isPanning', false)
386
+ /**
387
+ * Whether the user is panning.
388
+ */
389
+ getIsPanning() {
390
+ return this._isPanning.get()
391
+ }
392
+ /**
393
+ * @deprecated Use `getIsPanning()` instead.
394
+ */
395
+ // eslint-disable-next-line no-restricted-syntax
396
+ get isPanning() {
397
+ return this.getIsPanning()
398
+ }
399
+ // eslint-disable-next-line no-restricted-syntax
400
+ set isPanning(isPanning: boolean) {
401
+ this.setIsPanning(isPanning)
402
+ }
403
+ /**
404
+ * @param isPanning - Whether the user is panning.
405
+ * @internal
406
+ */
407
+ setIsPanning(isPanning: boolean) {
408
+ this._isPanning.set(isPanning)
409
+ }
410
+
411
+ private _isSpacebarPanning = atom<boolean>('isSpacebarPanning', false)
412
+ /**
413
+ * Whether the user is spacebar panning.
414
+ */
415
+ getIsSpacebarPanning() {
416
+ return this._isSpacebarPanning.get()
417
+ }
418
+ /**
419
+ * @deprecated Use `getIsSpacebarPanning()` instead.
420
+ */
421
+ // eslint-disable-next-line no-restricted-syntax
422
+ get isSpacebarPanning() {
423
+ return this.getIsSpacebarPanning()
424
+ }
425
+ // eslint-disable-next-line no-restricted-syntax
426
+ set isSpacebarPanning(isSpacebarPanning: boolean) {
427
+ this.setIsSpacebarPanning(isSpacebarPanning)
428
+ }
429
+ /**
430
+ * @param isSpacebarPanning - Whether the user is spacebar panning.
431
+ * @internal
432
+ */
433
+ setIsSpacebarPanning(isSpacebarPanning: boolean) {
434
+ this._isSpacebarPanning.set(isSpacebarPanning)
435
+ }
436
+
437
+ @computed private _getHasCollaborators() {
438
+ return this.editor.getCollaborators().length > 0 // could we do this more efficiently?
439
+ }
440
+
441
+ /**
442
+ * The previous point used for velocity calculation (updated each tick, not each pointer event).
443
+ * @internal
444
+ */
445
+ private _velocityPrevPoint = new Vec()
446
+
447
+ /**
448
+ * Update the pointer velocity based on elapsed time. Called by the tick manager.
449
+ * @param elapsed - The time elapsed since the last tick in milliseconds.
450
+ * @internal
451
+ */
452
+ updatePointerVelocity(elapsed: number) {
453
+ const currentScreenPoint = this.getCurrentScreenPoint()
454
+ const pointerVelocity = this.getPointerVelocity()
455
+
456
+ if (elapsed === 0) return
457
+
458
+ const delta = Vec.Sub(currentScreenPoint, this._velocityPrevPoint)
459
+ this._velocityPrevPoint = currentScreenPoint.clone()
460
+
461
+ const length = delta.len()
462
+ const direction = length ? delta.div(length) : new Vec(0, 0)
463
+
464
+ // consider adjusting this with an easing rather than a linear interpolation
465
+ const next = pointerVelocity.clone().lrp(direction.mul(length / elapsed), 0.5)
466
+
467
+ // if the velocity is very small, just set it to 0
468
+ if (Math.abs(next.x) < 0.01) next.x = 0
469
+ if (Math.abs(next.y) < 0.01) next.y = 0
470
+
471
+ if (!pointerVelocity.equals(next)) {
472
+ this._pointerVelocity.set(next)
473
+ }
474
+ }
475
+
476
+ /**
477
+ * Update the input points from a pointer, pinch, or wheel event.
478
+ *
479
+ * @param info - The event info.
480
+ * @internal
481
+ */
482
+ updateFromEvent(info: TLPointerEventInfo | TLPinchEventInfo | TLWheelEventInfo): void {
483
+ const currentScreenPoint = this._currentScreenPoint.__unsafe__getWithoutCapture()
484
+ const currentPagePoint = this._currentPagePoint.__unsafe__getWithoutCapture()
485
+ const isPinching = this._isPinching.__unsafe__getWithoutCapture()
486
+ const { screenBounds } = this.editor.store.unsafeGetWithoutCapture(TLINSTANCE_ID)!
487
+ const { x: cx, y: cy, z: cz } = unsafe__withoutCapture(() => this.editor.getCamera())
488
+
489
+ const sx = info.point.x - screenBounds.x
490
+ const sy = info.point.y - screenBounds.y
491
+ const sz = info.point.z ?? 0.5
492
+
493
+ this._previousScreenPoint.set(currentScreenPoint)
494
+ this._previousPagePoint.set(currentPagePoint)
495
+
496
+ // The "screen bounds" is relative to the user's actual screen.
497
+ // The "screen point" is relative to the "screen bounds";
498
+ // it will be 0,0 when its actual screen position is equal
499
+ // to screenBounds.point. This is confusing!
500
+ this._currentScreenPoint.set(new Vec(sx, sy))
501
+ const nx = sx / cz - cx
502
+ const ny = sy / cz - cy
503
+ if (isFinite(nx) && isFinite(ny)) {
504
+ this._currentPagePoint.set(new Vec(nx, ny, sz))
505
+ }
506
+
507
+ this._isPen.set(info.type === 'pointer' && info.isPen)
508
+
509
+ // Reset velocity on pointer down, or when a pinch starts or ends
510
+ if (info.name === 'pointer_down' || isPinching) {
511
+ this._pointerVelocity.set(new Vec())
512
+ this._originScreenPoint.set(this._currentScreenPoint.__unsafe__getWithoutCapture())
513
+ this._originPagePoint.set(this._currentPagePoint.__unsafe__getWithoutCapture())
514
+ }
515
+
516
+ if (this._getHasCollaborators()) {
517
+ this.editor.run(
518
+ () => {
519
+ const pagePoint = this._currentPagePoint.__unsafe__getWithoutCapture()
520
+ this.editor.store.put([
521
+ {
522
+ id: TLPOINTER_ID,
523
+ typeName: 'pointer',
524
+ x: pagePoint.x,
525
+ y: pagePoint.y,
526
+ lastActivityTimestamp:
527
+ // If our pointer moved only because we're following some other user, then don't
528
+ // update our last activity timestamp; otherwise, update it to the current timestamp.
529
+ info.type === 'pointer' && info.pointerId === INTERNAL_POINTER_IDS.CAMERA_MOVE
530
+ ? (this.editor.store.unsafeGetWithoutCapture(TLPOINTER_ID)
531
+ ?.lastActivityTimestamp ?? Date.now())
532
+ : Date.now(),
533
+ meta: {},
534
+ },
535
+ ])
536
+ },
537
+ { history: 'ignore' }
538
+ )
539
+ }
540
+ }
541
+
542
+ toJson() {
543
+ return {
544
+ originPagePoint: this._originPagePoint.get().toJson(),
545
+ originScreenPoint: this._originScreenPoint.get().toJson(),
546
+ previousPagePoint: this._previousPagePoint.get().toJson(),
547
+ previousScreenPoint: this._previousScreenPoint.get().toJson(),
548
+ currentPagePoint: this._currentPagePoint.get().toJson(),
549
+ currentScreenPoint: this._currentScreenPoint.get().toJson(),
550
+ pointerVelocity: this._pointerVelocity.get().toJson(),
551
+ shiftKey: this._shiftKey.get(),
552
+ metaKey: this._metaKey.get(),
553
+ ctrlKey: this._ctrlKey.get(),
554
+ altKey: this._altKey.get(),
555
+ isPen: this._isPen.get(),
556
+ isDragging: this._isDragging.get(),
557
+ isPointing: this._isPointing.get(),
558
+ isPinching: this._isPinching.get(),
559
+ isEditing: this._isEditing.get(),
560
+ isPanning: this._isPanning.get(),
561
+ isSpacebarPanning: this._isSpacebarPanning.get(),
562
+ keys: Array.from(this.keys.keys()),
563
+ buttons: Array.from(this.buttons.keys()),
564
+ }
565
+ }
566
+ }
@@ -36,10 +36,6 @@ describe('ScribbleManager', () => {
36
36
  expect(scribbleManager.scribbleItems.size).toBe(0)
37
37
  expect(scribbleManager.state).toBe('paused')
38
38
  })
39
-
40
- it('should store reference to editor', () => {
41
- expect((scribbleManager as any).editor).toBe(editor)
42
- })
43
39
  })
44
40
 
45
41
  describe('addScribble', () => {
@@ -64,6 +64,7 @@ describe('SnapManager', () => {
64
64
  beforeEach(() => {
65
65
  editor = {
66
66
  getZoomLevel: vi.fn(() => 1),
67
+ options: { snapThreshold: 8 },
67
68
  getViewportPageBounds: vi.fn(() => new Box(0, 0, 1000, 1000)),
68
69
  getSelectedShapeIds: vi.fn(() => []),
69
70
  getSelectedShapes: vi.fn(() => []),
@@ -248,6 +249,17 @@ describe('SnapManager', () => {
248
249
  editor.getZoomLevel.mockReturnValue(10)
249
250
  expect(snapManager.getSnapThreshold()).toBe(0.8)
250
251
  })
252
+
253
+ it('should use custom snap threshold when configured', () => {
254
+ ;(editor as any).options = { snapThreshold: 16 }
255
+ editor.getZoomLevel.mockReturnValue(1)
256
+ const customSnapManager = new SnapManager(editor)
257
+ expect(customSnapManager.getSnapThreshold()).toBe(16)
258
+
259
+ editor.getZoomLevel.mockReturnValue(2)
260
+ const customSnapManager2 = new SnapManager(editor)
261
+ expect(customSnapManager2.getSnapThreshold()).toBe(8)
262
+ })
251
263
  })
252
264
 
253
265
  describe('getCurrentCommonAncestor', () => {
@@ -1,5 +1,5 @@
1
1
  import { EMPTY_ARRAY, atom, computed } from '@tldraw/state'
2
- import { TLFrameShape, TLGroupShape, TLParentId, TLShapeId, isShapeId } from '@tldraw/tlschema'
2
+ import { TLParentId, TLShapeId, isShapeId } from '@tldraw/tlschema'
3
3
  import { Vec, VecLike } from '../../../primitives/Vec'
4
4
  import type { Editor } from '../../Editor'
5
5
  import { BoundsSnaps } from './BoundsSnaps'
@@ -58,7 +58,7 @@ export class SnapManager {
58
58
  }
59
59
 
60
60
  @computed getSnapThreshold() {
61
- return 8 / this.editor.getZoomLevel()
61
+ return this.editor.options.snapThreshold / this.editor.getZoomLevel()
62
62
  }
63
63
 
64
64
  // TODO: make this an incremental derivation
@@ -72,7 +72,7 @@ export class SnapManager {
72
72
  const collectSnappableShapesFromParent = (parentId: TLParentId) => {
73
73
  if (isShapeId(parentId)) {
74
74
  const parent = editor.getShape(parentId)
75
- if (parent && editor.isShapeOfType<TLFrameShape>(parent, 'frame')) {
75
+ if (parent && editor.isShapeOfType(parent, 'frame')) {
76
76
  snappableShapes.add(parentId)
77
77
  }
78
78
  }
@@ -89,7 +89,7 @@ export class SnapManager {
89
89
  const pageBounds = editor.getShapePageBounds(childId)
90
90
  if (!(pageBounds && renderingBounds.includes(pageBounds))) continue
91
91
  // Snap to children of groups but not group itself
92
- if (editor.isShapeOfType<TLGroupShape>(childShape, 'group')) {
92
+ if (editor.isShapeOfType(childShape, 'group')) {
93
93
  collectSnappableShapesFromParent(childId)
94
94
  continue
95
95
  }