@tldraw/editor 3.16.0-internal.a478398270c6 → 3.16.0-internal.f8b97f0c414f

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 (325) hide show
  1. package/dist-cjs/index.d.ts +350 -142
  2. package/dist-cjs/index.js +13 -6
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/TldrawEditor.js +10 -8
  5. package/dist-cjs/lib/TldrawEditor.js.map +2 -2
  6. package/dist-cjs/lib/components/MenuClickCapture.js +0 -5
  7. package/dist-cjs/lib/components/MenuClickCapture.js.map +2 -2
  8. package/dist-cjs/lib/components/SVGContainer.js +1 -1
  9. package/dist-cjs/lib/components/SVGContainer.js.map +2 -2
  10. package/dist-cjs/lib/components/Shape.js +11 -36
  11. package/dist-cjs/lib/components/Shape.js.map +2 -2
  12. package/dist-cjs/lib/components/default-components/DefaultBrush.js +1 -1
  13. package/dist-cjs/lib/components/default-components/DefaultBrush.js.map +2 -2
  14. package/dist-cjs/lib/components/default-components/DefaultCanvas.js +15 -24
  15. package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
  16. package/dist-cjs/lib/components/default-components/DefaultCollaboratorHint.js +2 -2
  17. package/dist-cjs/lib/components/default-components/DefaultCollaboratorHint.js.map +2 -2
  18. package/dist-cjs/lib/components/default-components/DefaultCursor.js +1 -1
  19. package/dist-cjs/lib/components/default-components/DefaultCursor.js.map +2 -2
  20. package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js +1 -1
  21. package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js.map +2 -2
  22. package/dist-cjs/lib/components/default-components/DefaultGrid.js +1 -1
  23. package/dist-cjs/lib/components/default-components/DefaultGrid.js.map +2 -2
  24. package/dist-cjs/lib/components/default-components/DefaultHandles.js +1 -1
  25. package/dist-cjs/lib/components/default-components/DefaultHandles.js.map +2 -2
  26. package/dist-cjs/lib/components/default-components/DefaultScribble.js +1 -1
  27. package/dist-cjs/lib/components/default-components/DefaultScribble.js.map +2 -2
  28. package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js +9 -1
  29. package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js.map +2 -2
  30. package/dist-cjs/lib/components/default-components/DefaultShapeWrapper.js +53 -0
  31. package/dist-cjs/lib/components/default-components/DefaultShapeWrapper.js.map +7 -0
  32. package/dist-cjs/lib/components/default-components/DefaultSnapIndictor.js +1 -1
  33. package/dist-cjs/lib/components/default-components/DefaultSnapIndictor.js.map +2 -2
  34. package/dist-cjs/lib/components/default-components/DefaultSpinner.js +27 -15
  35. package/dist-cjs/lib/components/default-components/DefaultSpinner.js.map +3 -3
  36. package/dist-cjs/lib/config/TLUserPreferences.js +15 -3
  37. package/dist-cjs/lib/config/TLUserPreferences.js.map +2 -2
  38. package/dist-cjs/lib/editor/Editor.js +174 -180
  39. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  40. package/dist-cjs/lib/editor/derivations/notVisibleShapes.js +4 -0
  41. package/dist-cjs/lib/editor/derivations/notVisibleShapes.js.map +2 -2
  42. package/dist-cjs/lib/editor/derivations/parentsToChildren.js.map +2 -2
  43. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js +14 -4
  44. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +2 -2
  45. package/dist-cjs/lib/editor/shapes/BaseBoxShapeUtil.js.map +1 -1
  46. package/dist-cjs/lib/editor/shapes/ShapeUtil.js +23 -0
  47. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  48. package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/BaseBoxShapeTool.js.map +2 -2
  49. package/dist-cjs/lib/editor/tools/StateNode.js +20 -1
  50. package/dist-cjs/lib/editor/tools/StateNode.js.map +2 -2
  51. package/dist-cjs/lib/editor/types/misc-types.js.map +1 -1
  52. package/dist-cjs/lib/exports/getSvgJsx.js +35 -16
  53. package/dist-cjs/lib/exports/getSvgJsx.js.map +2 -2
  54. package/dist-cjs/lib/hooks/useCanvasEvents.js +44 -35
  55. package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
  56. package/dist-cjs/lib/hooks/useDocumentEvents.js +5 -5
  57. package/dist-cjs/lib/hooks/useDocumentEvents.js.map +2 -2
  58. package/dist-cjs/lib/hooks/useEditor.js +1 -4
  59. package/dist-cjs/lib/hooks/useEditor.js.map +2 -2
  60. package/dist-cjs/lib/hooks/useEditorComponents.js +2 -0
  61. package/dist-cjs/lib/hooks/useEditorComponents.js.map +2 -2
  62. package/dist-cjs/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.js +1 -2
  63. package/dist-cjs/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.js.map +2 -2
  64. package/dist-cjs/lib/hooks/useGestureEvents.js +1 -1
  65. package/dist-cjs/lib/hooks/useGestureEvents.js.map +2 -2
  66. package/dist-cjs/lib/hooks/useHandleEvents.js +3 -3
  67. package/dist-cjs/lib/hooks/useHandleEvents.js.map +2 -2
  68. package/dist-cjs/lib/hooks/usePassThroughMouseOverEvents.js +4 -1
  69. package/dist-cjs/lib/hooks/usePassThroughMouseOverEvents.js.map +2 -2
  70. package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js +4 -1
  71. package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js.map +2 -2
  72. package/dist-cjs/lib/hooks/useSelectionEvents.js +4 -4
  73. package/dist-cjs/lib/hooks/useSelectionEvents.js.map +2 -2
  74. package/dist-cjs/lib/{utils/nearestMultiple.js → hooks/useStateAttribute.js} +15 -14
  75. package/dist-cjs/lib/hooks/useStateAttribute.js.map +7 -0
  76. package/dist-cjs/lib/license/LicenseManager.js +140 -53
  77. package/dist-cjs/lib/license/LicenseManager.js.map +2 -2
  78. package/dist-cjs/lib/license/LicenseProvider.js +39 -1
  79. package/dist-cjs/lib/license/LicenseProvider.js.map +2 -2
  80. package/dist-cjs/lib/license/Watermark.js +75 -13
  81. package/dist-cjs/lib/license/Watermark.js.map +3 -3
  82. package/dist-cjs/lib/license/useLicenseManagerState.js.map +2 -2
  83. package/dist-cjs/lib/options.js +7 -0
  84. package/dist-cjs/lib/options.js.map +2 -2
  85. package/dist-cjs/lib/primitives/Box.js +3 -0
  86. package/dist-cjs/lib/primitives/Box.js.map +2 -2
  87. package/dist-cjs/lib/primitives/Vec.js +0 -4
  88. package/dist-cjs/lib/primitives/Vec.js.map +2 -2
  89. package/dist-cjs/lib/primitives/geometry/Arc2d.js +1 -1
  90. package/dist-cjs/lib/primitives/geometry/Arc2d.js.map +2 -2
  91. package/dist-cjs/lib/primitives/geometry/Circle2d.js +1 -1
  92. package/dist-cjs/lib/primitives/geometry/Circle2d.js.map +2 -2
  93. package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js +3 -1
  94. package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js.map +2 -2
  95. package/dist-cjs/lib/primitives/geometry/Ellipse2d.js +1 -1
  96. package/dist-cjs/lib/primitives/geometry/Ellipse2d.js.map +2 -2
  97. package/dist-cjs/lib/primitives/geometry/Geometry2d.js +50 -20
  98. package/dist-cjs/lib/primitives/geometry/Geometry2d.js.map +2 -2
  99. package/dist-cjs/lib/primitives/geometry/Group2d.js +8 -1
  100. package/dist-cjs/lib/primitives/geometry/Group2d.js.map +2 -2
  101. package/dist-cjs/lib/primitives/geometry/geometry-constants.js +2 -2
  102. package/dist-cjs/lib/primitives/geometry/geometry-constants.js.map +2 -2
  103. package/dist-cjs/lib/primitives/intersect.js +4 -4
  104. package/dist-cjs/lib/primitives/intersect.js.map +2 -2
  105. package/dist-cjs/lib/primitives/utils.js +4 -0
  106. package/dist-cjs/lib/primitives/utils.js.map +2 -2
  107. package/dist-cjs/lib/utils/EditorAtom.js +45 -0
  108. package/dist-cjs/lib/utils/EditorAtom.js.map +7 -0
  109. package/dist-cjs/lib/utils/dom.js +12 -1
  110. package/dist-cjs/lib/utils/dom.js.map +2 -2
  111. package/dist-cjs/lib/utils/getPointerInfo.js +2 -2
  112. package/dist-cjs/lib/utils/getPointerInfo.js.map +2 -2
  113. package/dist-cjs/lib/utils/reparenting.js +2 -35
  114. package/dist-cjs/lib/utils/reparenting.js.map +3 -3
  115. package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js +0 -1
  116. package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js.map +2 -2
  117. package/dist-cjs/version.js +3 -3
  118. package/dist-cjs/version.js.map +1 -1
  119. package/dist-esm/index.d.mts +350 -142
  120. package/dist-esm/index.mjs +24 -8
  121. package/dist-esm/index.mjs.map +2 -2
  122. package/dist-esm/lib/TldrawEditor.mjs +11 -9
  123. package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
  124. package/dist-esm/lib/components/MenuClickCapture.mjs +0 -5
  125. package/dist-esm/lib/components/MenuClickCapture.mjs.map +2 -2
  126. package/dist-esm/lib/components/SVGContainer.mjs +1 -1
  127. package/dist-esm/lib/components/SVGContainer.mjs.map +2 -2
  128. package/dist-esm/lib/components/Shape.mjs +11 -36
  129. package/dist-esm/lib/components/Shape.mjs.map +2 -2
  130. package/dist-esm/lib/components/default-components/DefaultBrush.mjs +1 -1
  131. package/dist-esm/lib/components/default-components/DefaultBrush.mjs.map +2 -2
  132. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +16 -25
  133. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
  134. package/dist-esm/lib/components/default-components/DefaultCollaboratorHint.mjs +2 -2
  135. package/dist-esm/lib/components/default-components/DefaultCollaboratorHint.mjs.map +2 -2
  136. package/dist-esm/lib/components/default-components/DefaultCursor.mjs +1 -1
  137. package/dist-esm/lib/components/default-components/DefaultCursor.mjs.map +2 -2
  138. package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs +1 -1
  139. package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs.map +2 -2
  140. package/dist-esm/lib/components/default-components/DefaultGrid.mjs +1 -1
  141. package/dist-esm/lib/components/default-components/DefaultGrid.mjs.map +2 -2
  142. package/dist-esm/lib/components/default-components/DefaultHandles.mjs +1 -1
  143. package/dist-esm/lib/components/default-components/DefaultHandles.mjs.map +2 -2
  144. package/dist-esm/lib/components/default-components/DefaultScribble.mjs +1 -1
  145. package/dist-esm/lib/components/default-components/DefaultScribble.mjs.map +2 -2
  146. package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs +9 -1
  147. package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs.map +2 -2
  148. package/dist-esm/lib/components/default-components/DefaultShapeWrapper.mjs +23 -0
  149. package/dist-esm/lib/components/default-components/DefaultShapeWrapper.mjs.map +7 -0
  150. package/dist-esm/lib/components/default-components/DefaultSnapIndictor.mjs +1 -1
  151. package/dist-esm/lib/components/default-components/DefaultSnapIndictor.mjs.map +2 -2
  152. package/dist-esm/lib/components/default-components/DefaultSpinner.mjs +17 -15
  153. package/dist-esm/lib/components/default-components/DefaultSpinner.mjs.map +2 -2
  154. package/dist-esm/lib/config/TLUserPreferences.mjs +15 -3
  155. package/dist-esm/lib/config/TLUserPreferences.mjs.map +2 -2
  156. package/dist-esm/lib/editor/Editor.mjs +174 -180
  157. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  158. package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs +4 -0
  159. package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs.map +2 -2
  160. package/dist-esm/lib/editor/derivations/parentsToChildren.mjs.map +2 -2
  161. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs +14 -4
  162. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +2 -2
  163. package/dist-esm/lib/editor/shapes/BaseBoxShapeUtil.mjs.map +1 -1
  164. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +23 -0
  165. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  166. package/dist-esm/lib/editor/tools/BaseBoxShapeTool/BaseBoxShapeTool.mjs.map +2 -2
  167. package/dist-esm/lib/editor/tools/StateNode.mjs +20 -1
  168. package/dist-esm/lib/editor/tools/StateNode.mjs.map +2 -2
  169. package/dist-esm/lib/exports/getSvgJsx.mjs +36 -16
  170. package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
  171. package/dist-esm/lib/hooks/useCanvasEvents.mjs +47 -37
  172. package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
  173. package/dist-esm/lib/hooks/useDocumentEvents.mjs +11 -6
  174. package/dist-esm/lib/hooks/useDocumentEvents.mjs.map +2 -2
  175. package/dist-esm/lib/hooks/useEditor.mjs +1 -4
  176. package/dist-esm/lib/hooks/useEditor.mjs.map +2 -2
  177. package/dist-esm/lib/hooks/useEditorComponents.mjs +4 -0
  178. package/dist-esm/lib/hooks/useEditorComponents.mjs.map +2 -2
  179. package/dist-esm/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.mjs +2 -3
  180. package/dist-esm/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.mjs.map +2 -2
  181. package/dist-esm/lib/hooks/useGestureEvents.mjs +2 -2
  182. package/dist-esm/lib/hooks/useGestureEvents.mjs.map +2 -2
  183. package/dist-esm/lib/hooks/useHandleEvents.mjs +9 -4
  184. package/dist-esm/lib/hooks/useHandleEvents.mjs.map +2 -2
  185. package/dist-esm/lib/hooks/usePassThroughMouseOverEvents.mjs +4 -1
  186. package/dist-esm/lib/hooks/usePassThroughMouseOverEvents.mjs.map +2 -2
  187. package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs +4 -1
  188. package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs.map +2 -2
  189. package/dist-esm/lib/hooks/useSelectionEvents.mjs +6 -5
  190. package/dist-esm/lib/hooks/useSelectionEvents.mjs.map +2 -2
  191. package/dist-esm/lib/hooks/useStateAttribute.mjs +15 -0
  192. package/dist-esm/lib/hooks/useStateAttribute.mjs.map +7 -0
  193. package/dist-esm/lib/license/LicenseManager.mjs +141 -54
  194. package/dist-esm/lib/license/LicenseManager.mjs.map +2 -2
  195. package/dist-esm/lib/license/LicenseProvider.mjs +39 -2
  196. package/dist-esm/lib/license/LicenseProvider.mjs.map +2 -2
  197. package/dist-esm/lib/license/Watermark.mjs +76 -14
  198. package/dist-esm/lib/license/Watermark.mjs.map +3 -3
  199. package/dist-esm/lib/license/useLicenseManagerState.mjs.map +2 -2
  200. package/dist-esm/lib/options.mjs +7 -0
  201. package/dist-esm/lib/options.mjs.map +2 -2
  202. package/dist-esm/lib/primitives/Box.mjs +4 -1
  203. package/dist-esm/lib/primitives/Box.mjs.map +2 -2
  204. package/dist-esm/lib/primitives/Vec.mjs +0 -4
  205. package/dist-esm/lib/primitives/Vec.mjs.map +2 -2
  206. package/dist-esm/lib/primitives/geometry/Arc2d.mjs +2 -2
  207. package/dist-esm/lib/primitives/geometry/Arc2d.mjs.map +2 -2
  208. package/dist-esm/lib/primitives/geometry/Circle2d.mjs +2 -2
  209. package/dist-esm/lib/primitives/geometry/Circle2d.mjs.map +2 -2
  210. package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs +3 -1
  211. package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs.map +2 -2
  212. package/dist-esm/lib/primitives/geometry/Ellipse2d.mjs +2 -2
  213. package/dist-esm/lib/primitives/geometry/Ellipse2d.mjs.map +2 -2
  214. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs +53 -21
  215. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs.map +2 -2
  216. package/dist-esm/lib/primitives/geometry/Group2d.mjs +8 -1
  217. package/dist-esm/lib/primitives/geometry/Group2d.mjs.map +2 -2
  218. package/dist-esm/lib/primitives/geometry/geometry-constants.mjs +2 -2
  219. package/dist-esm/lib/primitives/geometry/geometry-constants.mjs.map +2 -2
  220. package/dist-esm/lib/primitives/intersect.mjs +5 -5
  221. package/dist-esm/lib/primitives/intersect.mjs.map +2 -2
  222. package/dist-esm/lib/primitives/utils.mjs +4 -0
  223. package/dist-esm/lib/primitives/utils.mjs.map +2 -2
  224. package/dist-esm/lib/utils/EditorAtom.mjs +25 -0
  225. package/dist-esm/lib/utils/EditorAtom.mjs.map +7 -0
  226. package/dist-esm/lib/utils/dom.mjs +12 -1
  227. package/dist-esm/lib/utils/dom.mjs.map +2 -2
  228. package/dist-esm/lib/utils/getPointerInfo.mjs +2 -2
  229. package/dist-esm/lib/utils/getPointerInfo.mjs.map +2 -2
  230. package/dist-esm/lib/utils/reparenting.mjs +3 -40
  231. package/dist-esm/lib/utils/reparenting.mjs.map +2 -2
  232. package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs +0 -1
  233. package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs.map +2 -2
  234. package/dist-esm/version.mjs +3 -3
  235. package/dist-esm/version.mjs.map +1 -1
  236. package/editor.css +327 -315
  237. package/package.json +16 -38
  238. package/src/index.ts +19 -10
  239. package/src/lib/TldrawEditor.tsx +16 -21
  240. package/src/lib/components/MenuClickCapture.tsx +0 -8
  241. package/src/lib/components/SVGContainer.tsx +1 -1
  242. package/src/lib/components/Shape.tsx +12 -33
  243. package/src/lib/components/default-components/DefaultBrush.tsx +1 -1
  244. package/src/lib/components/default-components/DefaultCanvas.tsx +13 -24
  245. package/src/lib/components/default-components/DefaultCollaboratorHint.tsx +2 -2
  246. package/src/lib/components/default-components/DefaultCursor.tsx +1 -1
  247. package/src/lib/components/default-components/DefaultErrorFallback.tsx +1 -1
  248. package/src/lib/components/default-components/DefaultGrid.tsx +1 -1
  249. package/src/lib/components/default-components/DefaultHandles.tsx +5 -1
  250. package/src/lib/components/default-components/DefaultScribble.tsx +1 -1
  251. package/src/lib/components/default-components/DefaultShapeIndicator.tsx +6 -2
  252. package/src/lib/components/default-components/DefaultShapeWrapper.tsx +35 -0
  253. package/src/lib/components/default-components/DefaultSnapIndictor.tsx +1 -1
  254. package/src/lib/components/default-components/DefaultSpinner.tsx +12 -12
  255. package/src/lib/config/TLUserPreferences.ts +15 -1
  256. package/src/lib/editor/Editor.test.ts +512 -8
  257. package/src/lib/editor/Editor.ts +252 -267
  258. package/src/lib/editor/derivations/notVisibleShapes.ts +6 -0
  259. package/src/lib/editor/derivations/parentsToChildren.ts +1 -1
  260. package/src/lib/editor/managers/ClickManager/ClickManager.test.ts +15 -14
  261. package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.test.ts +16 -15
  262. package/src/lib/editor/managers/FocusManager/FocusManager.test.ts +49 -48
  263. package/src/lib/editor/managers/FontManager/FontManager.test.ts +38 -27
  264. package/src/lib/editor/managers/HistoryManager/HistoryManager.test.ts +7 -6
  265. package/src/lib/editor/managers/ScribbleManager/ScribbleManager.test.ts +12 -11
  266. package/src/lib/editor/managers/SnapManager/SnapManager.test.ts +57 -50
  267. package/src/lib/editor/managers/TextManager/TextManager.test.ts +51 -26
  268. package/src/lib/editor/managers/TickManager/TickManager.test.ts +14 -13
  269. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +55 -26
  270. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.ts +14 -1
  271. package/src/lib/editor/shapes/BaseBoxShapeUtil.tsx +2 -2
  272. package/src/lib/editor/shapes/ShapeUtil.ts +108 -8
  273. package/src/lib/editor/tools/BaseBoxShapeTool/BaseBoxShapeTool.ts +2 -1
  274. package/src/lib/editor/tools/StateNode.test.ts +285 -0
  275. package/src/lib/editor/tools/StateNode.ts +27 -1
  276. package/src/lib/editor/types/misc-types.ts +73 -7
  277. package/src/lib/exports/getSvgJsx.test.ts +874 -0
  278. package/src/lib/exports/getSvgJsx.tsx +78 -21
  279. package/src/lib/hooks/useCanvasEvents.ts +60 -47
  280. package/src/lib/hooks/useDocumentEvents.ts +11 -6
  281. package/src/lib/hooks/useEditor.tsx +6 -5
  282. package/src/lib/hooks/useEditorComponents.tsx +8 -2
  283. package/src/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.ts +2 -2
  284. package/src/lib/hooks/useGestureEvents.ts +2 -2
  285. package/src/lib/hooks/useHandleEvents.ts +9 -4
  286. package/src/lib/hooks/usePassThroughMouseOverEvents.ts +4 -1
  287. package/src/lib/hooks/usePassThroughWheelEvents.ts +6 -1
  288. package/src/lib/hooks/useSelectionEvents.ts +6 -5
  289. package/src/lib/hooks/useStateAttribute.ts +15 -0
  290. package/src/lib/license/LicenseManager.test.ts +724 -383
  291. package/src/lib/license/LicenseManager.ts +201 -58
  292. package/src/lib/license/LicenseProvider.tsx +74 -2
  293. package/src/lib/license/Watermark.test.tsx +2 -1
  294. package/src/lib/license/Watermark.tsx +81 -14
  295. package/src/lib/license/useLicenseManagerState.ts +2 -2
  296. package/src/lib/options.ts +8 -0
  297. package/src/lib/primitives/Box.test.ts +126 -0
  298. package/src/lib/primitives/Box.ts +10 -1
  299. package/src/lib/primitives/Vec.ts +0 -5
  300. package/src/lib/primitives/geometry/Arc2d.ts +2 -2
  301. package/src/lib/primitives/geometry/Circle2d.ts +2 -2
  302. package/src/lib/primitives/geometry/CubicBezier2d.ts +4 -1
  303. package/src/lib/primitives/geometry/Ellipse2d.ts +2 -2
  304. package/src/lib/primitives/geometry/Geometry2d.test.ts +420 -0
  305. package/src/lib/primitives/geometry/Geometry2d.ts +78 -21
  306. package/src/lib/primitives/geometry/Group2d.ts +10 -1
  307. package/src/lib/primitives/geometry/geometry-constants.ts +2 -1
  308. package/src/lib/primitives/intersect.test.ts +946 -0
  309. package/src/lib/primitives/intersect.ts +12 -5
  310. package/src/lib/primitives/utils.ts +11 -0
  311. package/src/lib/test/InFrontOfTheCanvas.test.tsx +187 -0
  312. package/src/lib/utils/EditorAtom.ts +37 -0
  313. package/src/lib/utils/dom.test.ts +94 -0
  314. package/src/lib/utils/dom.ts +38 -1
  315. package/src/lib/utils/getPointerInfo.ts +2 -1
  316. package/src/lib/utils/reparenting.ts +3 -69
  317. package/src/lib/utils/sync/LocalIndexedDb.test.ts +2 -1
  318. package/src/lib/utils/sync/TLLocalSyncClient.test.ts +15 -15
  319. package/src/lib/utils/sync/TLLocalSyncClient.ts +0 -1
  320. package/src/version.ts +3 -3
  321. package/dist-cjs/lib/utils/nearestMultiple.js.map +0 -7
  322. package/dist-esm/lib/utils/nearestMultiple.mjs +0 -14
  323. package/dist-esm/lib/utils/nearestMultiple.mjs.map +0 -7
  324. package/src/lib/test/currentToolIdMask.test.ts +0 -49
  325. package/src/lib/utils/nearestMultiple.ts +0 -13
@@ -55,7 +55,6 @@ import {
55
55
  TLStore,
56
56
  TLStoreSnapshot,
57
57
  TLUnknownBinding,
58
- TLUnknownShape,
59
58
  TLVideoAsset,
60
59
  createBindingId,
61
60
  createShapeId,
@@ -116,7 +115,6 @@ import {
116
115
  } from '../constants'
117
116
  import { exportToSvg } from '../exports/exportToSvg'
118
117
  import { getSvgAsImage } from '../exports/getSvgAsImage'
119
- import { tlenv } from '../globals/environment'
120
118
  import { tlmenus } from '../globals/menus'
121
119
  import { tltime } from '../globals/time'
122
120
  import { TldrawOptions, defaultTldrawOptions } from '../options'
@@ -176,8 +174,10 @@ import {
176
174
  RequiredKeys,
177
175
  TLCameraMoveOptions,
178
176
  TLCameraOptions,
177
+ TLGetShapeAtPointOptions,
179
178
  TLImageExportOptions,
180
179
  TLSvgExportOptions,
180
+ TLUpdatePointerOptions,
181
181
  } from './types/misc-types'
182
182
  import { TLAdjacentDirection, TLResizeHandle } from './types/selection-types'
183
183
 
@@ -242,16 +242,6 @@ export interface TLEditorOptions {
242
242
  options?: Partial<TldrawOptions>
243
243
  licenseKey?: string
244
244
  fontAssetUrls?: { [key: string]: string | undefined }
245
- /**
246
- * A predicate that should return true if the given shape should be hidden.
247
- *
248
- * @deprecated Use {@link Editor#getShapeVisibility} instead.
249
- *
250
- * @param shape - The shape to check.
251
- * @param editor - The editor instance.
252
- */
253
- isShapeHidden?(shape: TLShape, editor: Editor): boolean
254
-
255
245
  /**
256
246
  * Provides a way to hide shapes.
257
247
  *
@@ -307,21 +297,12 @@ export class Editor extends EventEmitter<TLEventMap> {
307
297
  autoFocus,
308
298
  inferDarkMode,
309
299
  options,
310
- // eslint-disable-next-line @typescript-eslint/no-deprecated
311
- isShapeHidden,
312
300
  getShapeVisibility,
313
301
  fontAssetUrls,
314
302
  }: TLEditorOptions) {
315
303
  super()
316
- assert(
317
- !(isShapeHidden && getShapeVisibility),
318
- 'Cannot use both isShapeHidden and getShapeVisibility'
319
- )
320
304
 
321
- this._getShapeVisibility = isShapeHidden
322
- ? // eslint-disable-next-line @typescript-eslint/no-deprecated
323
- (shape: TLShape, editor: Editor) => (isShapeHidden(shape, editor) ? 'hidden' : 'inherit')
324
- : getShapeVisibility
305
+ this._getShapeVisibility = getShapeVisibility
325
306
 
326
307
  this.options = { ...defaultTldrawOptions, ...options }
327
308
 
@@ -905,14 +886,6 @@ export class Editor extends EventEmitter<TLEventMap> {
905
886
  */
906
887
  readonly fonts: FontManager
907
888
 
908
- /**
909
- * A manager for the editor's environment.
910
- *
911
- * @deprecated This is deprecated and will be removed in a future version. Use the `tlenv` global export instead.
912
- * @public
913
- */
914
- readonly environment = tlenv
915
-
916
889
  /**
917
890
  * A manager for the editor's scribbles.
918
891
  *
@@ -972,7 +945,7 @@ export class Editor extends EventEmitter<TLEventMap> {
972
945
  *
973
946
  * @public
974
947
  */
975
- shapeUtils: { readonly [K in string]?: ShapeUtil<TLUnknownShape> }
948
+ shapeUtils: { readonly [K in string]?: ShapeUtil<TLShape> }
976
949
 
977
950
  styleProps: { [key: string]: Map<StyleProp<any>, string> }
978
951
 
@@ -991,8 +964,8 @@ export class Editor extends EventEmitter<TLEventMap> {
991
964
  *
992
965
  * @public
993
966
  */
994
- getShapeUtil<S extends TLUnknownShape>(shape: S | TLShapePartial<S>): ShapeUtil<S>
995
- getShapeUtil<S extends TLUnknownShape>(type: S['type']): ShapeUtil<S>
967
+ getShapeUtil<S extends TLShape>(shape: S | TLShapePartial<S>): ShapeUtil<S>
968
+ getShapeUtil<S extends TLShape>(type: S['type']): ShapeUtil<S>
996
969
  getShapeUtil<T extends ShapeUtil>(type: T extends ShapeUtil<infer R> ? R['type'] : string): T
997
970
  getShapeUtil(arg: string | { type: string }) {
998
971
  const type = typeof arg === 'string' ? arg : arg.type
@@ -1006,8 +979,8 @@ export class Editor extends EventEmitter<TLEventMap> {
1006
979
  *
1007
980
  * @param shape - A shape, shape partial, or shape type.
1008
981
  */
1009
- hasShapeUtil<S extends TLUnknownShape>(shape: S | TLShapePartial<S>): boolean
1010
- hasShapeUtil<S extends TLUnknownShape>(type: S['type']): boolean
982
+ hasShapeUtil(shape: TLShape | TLShapePartial<TLShape>): boolean
983
+ hasShapeUtil(type: TLShape['type']): boolean
1011
984
  hasShapeUtil<T extends ShapeUtil>(
1012
985
  type: T extends ShapeUtil<infer R> ? R['type'] : string
1013
986
  ): boolean
@@ -1117,35 +1090,6 @@ export class Editor extends EventEmitter<TLEventMap> {
1117
1090
  return this.history.getNumRedos() > 0
1118
1091
  }
1119
1092
 
1120
- /**
1121
- * Create a new "mark", or stopping point, in the undo redo history. Creating a mark will clear
1122
- * any redos.
1123
- *
1124
- * @example
1125
- * ```ts
1126
- * editor.mark()
1127
- * editor.mark('flip shapes')
1128
- * ```
1129
- *
1130
- * @param markId - The mark's id, usually the reason for adding the mark.
1131
- *
1132
- * @public
1133
- * @deprecated use {@link Editor.markHistoryStoppingPoint} instead
1134
- */
1135
- mark(markId?: string): this {
1136
- if (typeof markId === 'string') {
1137
- console.warn(
1138
- `[tldraw] \`editor.history.mark("${markId}")\` is deprecated. Please use \`const myMarkId = editor.markHistoryStoppingPoint()\` instead.`
1139
- )
1140
- } else {
1141
- console.warn(
1142
- '[tldraw] `editor.mark()` is deprecated. Use `editor.markHistoryStoppingPoint()` instead.'
1143
- )
1144
- }
1145
- this.history._mark(markId ?? uniqueId())
1146
- return this
1147
- }
1148
-
1149
1093
  /**
1150
1094
  * Create a new "mark", or stopping point, in the undo redo history. Creating a mark will clear
1151
1095
  * any redos. You typically want to do this just before a user interaction begins or is handled.
@@ -1270,13 +1214,6 @@ export class Editor extends EventEmitter<TLEventMap> {
1270
1214
  return this
1271
1215
  }
1272
1216
 
1273
- /**
1274
- * @deprecated Use `Editor.run` instead.
1275
- */
1276
- batch(fn: () => void, opts?: TLEditorRunOptions): this {
1277
- return this.run(fn, opts)
1278
- }
1279
-
1280
1217
  /* --------------------- Errors --------------------- */
1281
1218
 
1282
1219
  /** @internal */
@@ -1578,54 +1515,6 @@ export class Editor extends EventEmitter<TLEventMap> {
1578
1515
 
1579
1516
  menus = tlmenus.forContext(this.contextId)
1580
1517
 
1581
- /**
1582
- * @deprecated Use `editor.menus.getOpenMenus` instead.
1583
- *
1584
- * @public
1585
- */
1586
- @computed getOpenMenus(): string[] {
1587
- return this.menus.getOpenMenus()
1588
- }
1589
-
1590
- /**
1591
- * @deprecated Use `editor.menus.addOpenMenu` instead.
1592
- *
1593
- * @public
1594
- */
1595
- addOpenMenu(id: string): this {
1596
- this.menus.addOpenMenu(id)
1597
- return this
1598
- }
1599
-
1600
- /**
1601
- * @deprecated Use `editor.menus.deleteOpenMenu` instead.
1602
- *
1603
- * @public
1604
- */
1605
- deleteOpenMenu(id: string): this {
1606
- this.menus.deleteOpenMenu(id)
1607
- return this
1608
- }
1609
-
1610
- /**
1611
- * @deprecated Use `editor.menus.clearOpenMenus` instead.
1612
- *
1613
- * @public
1614
- */
1615
- clearOpenMenus(): this {
1616
- this.menus.clearOpenMenus()
1617
- return this
1618
- }
1619
-
1620
- /**
1621
- * @deprecated Use `editor.menus.hasAnyOpenMenus` instead.
1622
- *
1623
- * @public
1624
- */
1625
- @computed getIsMenuOpen(): boolean {
1626
- return this.menus.hasAnyOpenMenus()
1627
- }
1628
-
1629
1518
  /* --------------------- Cursor --------------------- */
1630
1519
 
1631
1520
  /**
@@ -1803,7 +1692,9 @@ export class Editor extends EventEmitter<TLEventMap> {
1803
1692
  }
1804
1693
 
1805
1694
  /**
1806
- * Select all direct children of the current page.
1695
+ * Select all shapes. If the user has selected shapes that share a parent,
1696
+ * select all shapes within that parent. If the user has not selected any shapes,
1697
+ * or if the shapes shapes are only on select all shapes on the current page.
1807
1698
  *
1808
1699
  * @example
1809
1700
  * ```ts
@@ -1813,11 +1704,34 @@ export class Editor extends EventEmitter<TLEventMap> {
1813
1704
  * @public
1814
1705
  */
1815
1706
  selectAll(): this {
1816
- const ids = this.getSortedChildIdsForParent(this.getCurrentPageId())
1817
- // page might have no shapes
1707
+ let parentToSelectWithinId: TLParentId | null = null
1708
+
1709
+ const selectedShapeIds = this.getSelectedShapeIds()
1710
+
1711
+ // If we have selected shapes, try to find a parent to select within
1712
+ if (selectedShapeIds.length > 0) {
1713
+ for (const id of selectedShapeIds) {
1714
+ const shape = this.getShape(id)
1715
+ if (!shape) continue
1716
+ if (parentToSelectWithinId === null) {
1717
+ // If we haven't found a parent yet, set this parent as the parent to select within
1718
+ parentToSelectWithinId = shape.parentId
1719
+ } else if (parentToSelectWithinId !== shape.parentId) {
1720
+ // If we've found two different parents, we can't select all, do nothing
1721
+ return this
1722
+ }
1723
+ }
1724
+ }
1725
+
1726
+ // If we haven't found a parent from our selected shapes, select the current page
1727
+ if (!parentToSelectWithinId) {
1728
+ parentToSelectWithinId = this.getCurrentPageId()
1729
+ }
1730
+
1731
+ // Select all the unlocked shapes within the parent
1732
+ const ids = this.getSortedChildIdsForParent(parentToSelectWithinId)
1818
1733
  if (ids.length <= 0) return this
1819
1734
  this.setSelectedShapes(this._getUnlockedShapeIds(ids))
1820
-
1821
1735
  return this
1822
1736
  }
1823
1737
 
@@ -1838,10 +1752,11 @@ export class Editor extends EventEmitter<TLEventMap> {
1838
1752
  firstParentId &&
1839
1753
  selectedShapeIds.every((shapeId) => this.getShape(shapeId)?.parentId === firstParentId) &&
1840
1754
  !isPageId(firstParentId)
1755
+ const filteredShapes = isSelectedWithinContainer
1756
+ ? this.getCurrentPageShapes().filter((shape) => shape.parentId === firstParentId)
1757
+ : this.getCurrentPageShapes().filter((shape) => isPageId(shape.parentId))
1841
1758
  const readingOrderShapes = isSelectedWithinContainer
1842
- ? this._getShapesInReadingOrder(
1843
- this.getCurrentPageShapes().filter((shape) => shape.parentId === firstParentId)
1844
- )
1759
+ ? this._getShapesInReadingOrder(filteredShapes)
1845
1760
  : this.getCurrentPageShapesInReadingOrder()
1846
1761
  const currentShapeId: TLShapeId | undefined =
1847
1762
  selectedShapeIds.length === 1
@@ -1858,7 +1773,7 @@ export class Editor extends EventEmitter<TLEventMap> {
1858
1773
  adjacentShapeId = shapeIds[adjacentIndex]
1859
1774
  } else {
1860
1775
  if (!currentShapeId) return
1861
- adjacentShapeId = this.getNearestAdjacentShape(currentShapeId, direction)
1776
+ adjacentShapeId = this.getNearestAdjacentShape(filteredShapes, currentShapeId, direction)
1862
1777
  }
1863
1778
 
1864
1779
  const shape = this.getShape(adjacentShapeId)
@@ -1957,6 +1872,7 @@ export class Editor extends EventEmitter<TLEventMap> {
1957
1872
  * @public
1958
1873
  */
1959
1874
  getNearestAdjacentShape(
1875
+ shapes: TLShape[],
1960
1876
  currentShapeId: TLShapeId,
1961
1877
  direction: 'left' | 'right' | 'up' | 'down'
1962
1878
  ): TLShapeId {
@@ -1964,7 +1880,6 @@ export class Editor extends EventEmitter<TLEventMap> {
1964
1880
  const currentShape = this.getShape(currentShapeId)
1965
1881
  if (!currentShape) return currentShapeId
1966
1882
 
1967
- const shapes = this.getCurrentPageShapes()
1968
1883
  const tabbableShapes = shapes.filter(
1969
1884
  (shape) => this.getShapeUtil(shape).canTabTo(shape) && shape.id !== currentShapeId
1970
1885
  )
@@ -3046,7 +2961,6 @@ export class Editor extends EventEmitter<TLEventMap> {
3046
2961
  // Dispatch a new pointer move because the pointer's page will have changed
3047
2962
  // (its screen position will compute to a new page position given the new camera position)
3048
2963
  const { currentScreenPoint, currentPagePoint } = this.inputs
3049
- const { screenBounds } = this.store.unsafeGetWithoutCapture(TLINSTANCE_ID)!
3050
2964
 
3051
2965
  // compare the next page point (derived from the current camera) to the current page point
3052
2966
  if (
@@ -3054,27 +2968,10 @@ export class Editor extends EventEmitter<TLEventMap> {
3054
2968
  currentScreenPoint.y / z - y !== currentPagePoint.y
3055
2969
  ) {
3056
2970
  // If it's changed, dispatch a pointer event
3057
- const event: TLPointerEventInfo = {
3058
- type: 'pointer',
3059
- target: 'canvas',
3060
- name: 'pointer_move',
3061
- // weird but true: we need to put the screen point back into client space
3062
- point: Vec.AddXY(currentScreenPoint, screenBounds.x, screenBounds.y),
2971
+ this.updatePointer({
2972
+ immediate: opts?.immediate,
3063
2973
  pointerId: INTERNAL_POINTER_IDS.CAMERA_MOVE,
3064
- ctrlKey: this.inputs.ctrlKey,
3065
- altKey: this.inputs.altKey,
3066
- shiftKey: this.inputs.shiftKey,
3067
- metaKey: this.inputs.metaKey,
3068
- accelKey: isAccelKey(this.inputs),
3069
- button: 0,
3070
- isPen: this.getInstanceState().isPenMode ?? false,
3071
- }
3072
-
3073
- if (opts?.immediate) {
3074
- this._flushEventForTick(event)
3075
- } else {
3076
- this.dispatch(event)
3077
- }
2974
+ })
3078
2975
  }
3079
2976
 
3080
2977
  this._tickCameraState()
@@ -4395,21 +4292,28 @@ export class Editor extends EventEmitter<TLEventMap> {
4395
4292
  */
4396
4293
  deletePage(page: TLPageId | TLPage): this {
4397
4294
  const id = typeof page === 'string' ? page : page.id
4398
- this.run(() => {
4399
- if (this.getIsReadonly()) return
4400
- const pages = this.getPages()
4401
- if (pages.length === 1) return
4295
+ this.run(
4296
+ () => {
4297
+ if (this.getIsReadonly()) return
4298
+ const pages = this.getPages()
4299
+ if (pages.length === 1) return
4402
4300
 
4403
- const deletedPage = this.getPage(id)
4404
- if (!deletedPage) return
4301
+ const deletedPage = this.getPage(id)
4302
+ if (!deletedPage) return
4405
4303
 
4406
- if (id === this.getCurrentPageId()) {
4407
- const index = pages.findIndex((page) => page.id === id)
4408
- const next = pages[index - 1] ?? pages[index + 1]
4409
- this.setCurrentPage(next.id)
4410
- }
4411
- this.store.remove([deletedPage.id])
4412
- })
4304
+ if (id === this.getCurrentPageId()) {
4305
+ const index = pages.findIndex((page) => page.id === id)
4306
+ const next = pages[index - 1] ?? pages[index + 1]
4307
+ this.setCurrentPage(next.id)
4308
+ }
4309
+
4310
+ const shapes = this.getSortedChildIdsForParent(deletedPage.id)
4311
+ this.deleteShapes(shapes)
4312
+
4313
+ this.store.remove([deletedPage.id])
4314
+ },
4315
+ { ignoreShapeLock: true }
4316
+ )
4413
4317
  return this
4414
4318
  }
4415
4319
 
@@ -4775,8 +4679,10 @@ export class Editor extends EventEmitter<TLEventMap> {
4775
4679
  return this.store.createComputedCache<Box, TLShape>('pageBoundsCache', (shape) => {
4776
4680
  const pageTransform = this.getShapePageTransform(shape)
4777
4681
  if (!pageTransform) return undefined
4778
- const geometry = this.getShapeGeometry(shape)
4779
- return Box.FromPoints(pageTransform.applyToPoints(geometry.vertices))
4682
+
4683
+ return Box.FromPoints(
4684
+ pageTransform.applyToPoints(this.getShapeGeometry(shape).boundsVertices)
4685
+ )
4780
4686
  })
4781
4687
  }
4782
4688
 
@@ -4843,27 +4749,25 @@ export class Editor extends EventEmitter<TLEventMap> {
4843
4749
  return this.store.createComputedCache('pageMaskCache', (shape) => {
4844
4750
  if (isPageId(shape.parentId)) return undefined
4845
4751
 
4846
- const frameAncestors = this.getShapeAncestors(shape.id).filter((shape) =>
4847
- this.isShapeOfType<TLFrameShape>(shape, 'frame')
4848
- )
4849
-
4850
- if (frameAncestors.length === 0) return undefined
4851
-
4852
- const pageMask = frameAncestors
4853
- .map<Vec[] | undefined>((s) => {
4854
- // Apply the frame transform to the frame outline to get the frame outline in the current page space
4855
- const geometry = this.getShapeGeometry(s.id)
4856
- const pageTransform = this.getShapePageTransform(s.id)
4857
- return pageTransform.applyToPoints(geometry.vertices)
4858
- })
4859
- .reduce((acc, b) => {
4860
- if (!(b && acc)) return undefined
4861
- const intersection = intersectPolygonPolygon(acc, b)
4862
- if (intersection) {
4863
- return intersection.map(Vec.Cast)
4864
- }
4865
- return []
4866
- })
4752
+ const clipPaths: Vec[][] = []
4753
+ // Get all ancestors that can potentially clip this shape
4754
+ for (const ancestor of this.getShapeAncestors(shape.id)) {
4755
+ const util = this.getShapeUtil(ancestor)
4756
+ const clipPath = util.getClipPath?.(ancestor)
4757
+ if (!clipPath) continue
4758
+ if (util.shouldClipChild?.(shape) === false) continue
4759
+ const pageTransform = this.getShapePageTransform(ancestor.id)
4760
+ clipPaths.push(pageTransform.applyToPoints(clipPath))
4761
+ }
4762
+ if (clipPaths.length === 0) return undefined
4763
+
4764
+ const pageMask = clipPaths.reduce((acc, b) => {
4765
+ const intersection = intersectPolygonPolygon(acc, b)
4766
+ if (intersection) {
4767
+ return intersection.map(Vec.Cast)
4768
+ }
4769
+ return []
4770
+ })
4867
4771
 
4868
4772
  return pageMask
4869
4773
  })
@@ -5138,20 +5042,7 @@ export class Editor extends EventEmitter<TLEventMap> {
5138
5042
  *
5139
5043
  * @returns The shape at the given point, or undefined if there is no shape at the point.
5140
5044
  */
5141
- getShapeAtPoint(
5142
- point: VecLike,
5143
- opts = {} as {
5144
- renderingOnly?: boolean
5145
- margin?: number
5146
- hitInside?: boolean
5147
- hitLocked?: boolean
5148
- // TODO: we probably need to rename this, we don't quite _always_
5149
- // respect this esp. in the part below that does "Check labels first"
5150
- hitLabels?: boolean
5151
- hitFrameInside?: boolean
5152
- filter?(shape: TLShape): boolean
5153
- }
5154
- ): TLShape | undefined {
5045
+ getShapeAtPoint(point: VecLike, opts: TLGetShapeAtPointOptions = {}): TLShape | undefined {
5155
5046
  const zoomLevel = this.getZoomLevel()
5156
5047
  const viewportPageBounds = this.getViewportPageBounds()
5157
5048
  const {
@@ -5163,6 +5054,8 @@ export class Editor extends EventEmitter<TLEventMap> {
5163
5054
  hitFrameInside = false,
5164
5055
  } = opts
5165
5056
 
5057
+ const [innerMargin, outerMargin] = Array.isArray(margin) ? margin : [margin, margin]
5058
+
5166
5059
  let inHollowSmallestArea = Infinity
5167
5060
  let inHollowSmallestAreaHit: TLShape | null = null
5168
5061
 
@@ -5182,7 +5075,7 @@ export class Editor extends EventEmitter<TLEventMap> {
5182
5075
  return false
5183
5076
  const pageMask = this.getShapeMask(shape)
5184
5077
  if (pageMask && !pointInPolygon(point, pageMask)) return false
5185
- if (filter) return filter(shape)
5078
+ if (filter && !filter(shape)) return false
5186
5079
  return true
5187
5080
  })
5188
5081
 
@@ -5196,8 +5089,8 @@ export class Editor extends EventEmitter<TLEventMap> {
5196
5089
  // Check labels first
5197
5090
  if (
5198
5091
  this.isShapeOfType<TLFrameShape>(shape, 'frame') ||
5199
- (this.isShapeOfType<TLArrowShape>(shape, 'arrow') && shape.props.text.trim()) ||
5200
5092
  ((this.isShapeOfType<TLNoteShape>(shape, 'note') ||
5093
+ this.isShapeOfType<TLArrowShape>(shape, 'arrow') ||
5201
5094
  (this.isShapeOfType<TLGeoShape>(shape, 'geo') && shape.props.fill === 'none')) &&
5202
5095
  this.getShapeUtil(shape).getText(shape)?.trim())
5203
5096
  ) {
@@ -5208,13 +5101,18 @@ export class Editor extends EventEmitter<TLEventMap> {
5208
5101
  }
5209
5102
  }
5210
5103
 
5211
- if (this.isShapeOfType(shape, 'frame')) {
5104
+ if (this.isShapeOfType<TLFrameShape>(shape, 'frame')) {
5212
5105
  // On the rare case that we've hit a frame (not its label), test again hitInside to be forced true;
5213
5106
  // this prevents clicks from passing through the body of a frame to shapes behind it.
5214
5107
 
5215
5108
  // If the hit is within the frame's outer margin, then select the frame
5216
- const distance = geometry.distanceToPoint(pointInShapeSpace, hitInside)
5217
- if (Math.abs(distance) <= margin) {
5109
+ const distance = geometry.distanceToPoint(pointInShapeSpace, hitFrameInside)
5110
+ if (
5111
+ hitFrameInside
5112
+ ? (distance > 0 && distance <= outerMargin) ||
5113
+ (distance <= 0 && distance > -innerMargin)
5114
+ : distance > 0 && distance <= outerMargin
5115
+ ) {
5218
5116
  return inMarginClosestToEdgeHit || shape
5219
5117
  }
5220
5118
 
@@ -5253,11 +5151,11 @@ export class Editor extends EventEmitter<TLEventMap> {
5253
5151
  // If the margin is zero and the geometry has a very small width or height,
5254
5152
  // then check the actual distance. This is to prevent a bug where straight
5255
5153
  // lines would never pass the broad phase (point-in-bounds) check.
5256
- if (margin === 0 && (geometry.bounds.w < 1 || geometry.bounds.h < 1)) {
5154
+ if (outerMargin === 0 && (geometry.bounds.w < 1 || geometry.bounds.h < 1)) {
5257
5155
  distance = geometry.distanceToPoint(pointInShapeSpace, hitInside)
5258
5156
  } else {
5259
5157
  // Broad phase
5260
- if (geometry.bounds.containsPoint(pointInShapeSpace, margin)) {
5158
+ if (geometry.bounds.containsPoint(pointInShapeSpace, outerMargin)) {
5261
5159
  // Narrow phase (actual distance)
5262
5160
  distance = geometry.distanceToPoint(pointInShapeSpace, hitInside)
5263
5161
  } else {
@@ -5272,7 +5170,8 @@ export class Editor extends EventEmitter<TLEventMap> {
5272
5170
  // the shape or negative if inside of the shape. If the distance
5273
5171
  // is greater than the margin, then it's a miss. Otherwise...
5274
5172
 
5275
- if (distance <= margin) {
5173
+ // Are we close to the shape's edge?
5174
+ if (distance <= outerMargin || (hitInside && distance <= 0 && distance > -innerMargin)) {
5276
5175
  if (geometry.isFilled || (isGroup && geometry.children[0].isFilled)) {
5277
5176
  // If the shape is filled, then it's a hit. Remember, we're
5278
5177
  // starting from the TOP-MOST shape in z-index order, so any
@@ -5282,11 +5181,21 @@ export class Editor extends EventEmitter<TLEventMap> {
5282
5181
  // If the shape is bigger than the viewport, then skip it.
5283
5182
  if (this.getShapePageBounds(shape)!.contains(viewportPageBounds)) continue
5284
5183
 
5285
- // For hollow shapes...
5286
- if (Math.abs(distance) < margin) {
5287
- // We want to preference shapes where we're inside of the
5288
- // shape margin; and we would want to hit the shape with the
5289
- // edge closest to the point.
5184
+ // If we're close to the edge of the shape, and if it's the closest edge among
5185
+ // all the edges that we've gotten close to so far, then we will want to hit the
5186
+ // shape unless we hit something else or closer in later iterations.
5187
+ if (
5188
+ hitInside
5189
+ ? // On hitInside, the distance will be negative for hits inside
5190
+ // If the distance is positive, check against the outer margin
5191
+ (distance > 0 && distance <= outerMargin) ||
5192
+ // If the distance is negative, check against the inner margin
5193
+ (distance <= 0 && distance > -innerMargin)
5194
+ : // If hitInside is false, then sadly _we do not know_ whether the
5195
+ // point is inside or outside of the shape, so we check against
5196
+ // the max of the two margins
5197
+ Math.abs(distance) <= Math.max(innerMargin, outerMargin)
5198
+ ) {
5290
5199
  if (Math.abs(distance) < inMarginClosestToEdgeDistance) {
5291
5200
  inMarginClosestToEdgeDistance = Math.abs(distance)
5292
5201
  inMarginClosestToEdgeHit = shape
@@ -5308,6 +5217,7 @@ export class Editor extends EventEmitter<TLEventMap> {
5308
5217
  } else {
5309
5218
  // For open shapes (e.g. lines or draw shapes) always use the margin.
5310
5219
  // If the distance is less than the margin, return the shape as the hit.
5220
+ // Use the editor's configurable hit test margin.
5311
5221
  if (distance < this.options.hitTestMargin / zoomLevel) {
5312
5222
  return shape
5313
5223
  }
@@ -5480,15 +5390,9 @@ export class Editor extends EventEmitter<TLEventMap> {
5480
5390
  *
5481
5391
  * @public
5482
5392
  */
5483
- isShapeOfType<T extends TLUnknownShape>(shape: TLUnknownShape, type: T['type']): shape is T
5484
- isShapeOfType<T extends TLUnknownShape>(
5485
- shapeId: TLUnknownShape['id'],
5486
- type: T['type']
5487
- ): shapeId is T['id']
5488
- isShapeOfType<T extends TLUnknownShape>(
5489
- arg: TLUnknownShape | TLUnknownShape['id'],
5490
- type: T['type']
5491
- ) {
5393
+ isShapeOfType<T extends TLShape>(shape: TLShape, type: T['type']): shape is T
5394
+ isShapeOfType<T extends TLShape = TLShape>(shapeId: TLShapeId, type: T['type']): boolean
5395
+ isShapeOfType(arg: TLShape | TLShapeId, type: TLShape['type']) {
5492
5396
  const shape = typeof arg === 'string' ? this.getShape(arg) : arg
5493
5397
  if (!shape) return false
5494
5398
  return shape.type === type
@@ -5818,11 +5722,6 @@ export class Editor extends EventEmitter<TLEventMap> {
5818
5722
  return shapeIds
5819
5723
  }
5820
5724
 
5821
- /** @deprecated Use {@link Editor.getDraggingOverShape} instead */
5822
- getDroppingOverShape(point: Vec, droppingShapes: TLShape[]): TLShape | undefined {
5823
- return this.getDraggingOverShape(point, droppingShapes)
5824
- }
5825
-
5826
5725
  /**
5827
5726
  * Get the shape that some shapes should be dropped on at a given point.
5828
5727
  *
@@ -6310,7 +6209,17 @@ export class Editor extends EventEmitter<TLEventMap> {
6310
6209
 
6311
6210
  this.createShapes(shapesToCreate)
6312
6211
  this.createBindings(bindingsToCreate)
6313
- this.setSelectedShapes(compact(ids.map((id) => shapeIds.get(id))))
6212
+
6213
+ this.setSelectedShapes(
6214
+ compact(
6215
+ ids.map((oldId) => {
6216
+ const newId = shapeIds.get(oldId)
6217
+ if (!newId) return null
6218
+ if (!this.getShape(newId)) return null
6219
+ return newId
6220
+ })
6221
+ )
6222
+ )
6314
6223
 
6315
6224
  if (offset !== undefined) {
6316
6225
  // If we've offset the duplicated shapes, check to see whether their new bounds is entirely
@@ -7364,7 +7273,6 @@ export class Editor extends EventEmitter<TLEventMap> {
7364
7273
  if (
7365
7274
  !this.getShapeUtil(shape).canBeLaidOut?.(shape, {
7366
7275
  type: 'stretch',
7367
- shapes: shapesToStretchFirstPass,
7368
7276
  })
7369
7277
  ) {
7370
7278
  continue
@@ -7739,9 +7647,7 @@ export class Editor extends EventEmitter<TLEventMap> {
7739
7647
  *
7740
7648
  * @public
7741
7649
  */
7742
- canCreateShape<T extends TLUnknownShape>(
7743
- shape: OptionalKeys<TLShapePartial<T>, 'id'> | T['id']
7744
- ): boolean {
7650
+ canCreateShape(shape: OptionalKeys<TLShapePartial<TLShape>, 'id'> | TLShape['id']): boolean {
7745
7651
  return this.canCreateShapes([shape])
7746
7652
  }
7747
7653
 
@@ -7752,8 +7658,8 @@ export class Editor extends EventEmitter<TLEventMap> {
7752
7658
  *
7753
7659
  * @public
7754
7660
  */
7755
- canCreateShapes<T extends TLUnknownShape>(
7756
- shapes: (T['id'] | OptionalKeys<TLShapePartial<T>, 'id'>)[]
7661
+ canCreateShapes(
7662
+ shapes: (TLShape['id'] | OptionalKeys<TLShapePartial<TLShape>, 'id'>)[]
7757
7663
  ): boolean {
7758
7664
  return shapes.length + this.getCurrentPageShapeIds().size <= this.options.maxShapesPerPage
7759
7665
  }
@@ -7771,7 +7677,7 @@ export class Editor extends EventEmitter<TLEventMap> {
7771
7677
  *
7772
7678
  * @public
7773
7679
  */
7774
- createShape<T extends TLUnknownShape>(shape: OptionalKeys<TLShapePartial<T>, 'id'>): this {
7680
+ createShape<TShape extends TLShape>(shape: OptionalKeys<TLShapePartial<TShape>, 'id'>): this {
7775
7681
  this.createShapes([shape])
7776
7682
  return this
7777
7683
  }
@@ -7789,7 +7695,9 @@ export class Editor extends EventEmitter<TLEventMap> {
7789
7695
  *
7790
7696
  * @public
7791
7697
  */
7792
- createShapes<T extends TLUnknownShape>(shapes: OptionalKeys<TLShapePartial<T>, 'id'>[]): this {
7698
+ createShapes<TShape extends TLShape = TLShape>(
7699
+ shapes: OptionalKeys<TLShapePartial<TShape>, 'id'>[]
7700
+ ): this {
7793
7701
  if (!Array.isArray(shapes)) {
7794
7702
  throw Error('Editor.createShapes: must provide an array of shapes or shape partials')
7795
7703
  }
@@ -7835,31 +7743,38 @@ export class Editor extends EventEmitter<TLEventMap> {
7835
7743
  ) {
7836
7744
  let parentId: TLParentId = this.getFocusedGroupId()
7837
7745
 
7838
- for (let i = currentPageShapesSorted.length - 1; i >= 0; i--) {
7839
- const parent = currentPageShapesSorted[i]
7840
- const util = this.getShapeUtil(parent)
7841
- if (
7842
- util.canReceiveNewChildrenOfType(parent, partial.type) &&
7843
- !this.isShapeHidden(parent) &&
7844
- this.isPointInShape(
7845
- parent,
7846
- // If no parent is provided, then we can treat the
7847
- // shape's provided x/y as being in the page's space.
7848
- { x: partial.x ?? 0, y: partial.y ?? 0 },
7849
- {
7850
- margin: 0,
7851
- hitInside: true,
7852
- }
7853
- )
7854
- ) {
7855
- parentId = parent.id
7856
- break
7746
+ const isPositioned = partial.x !== undefined && partial.y !== undefined
7747
+
7748
+ // If the shape has been explicitly positioned, we'll try to find a parent at
7749
+ // that position. If not, we'll assume the user isn't deliberately placing the
7750
+ // shape and the positioning will be handled later by another system.
7751
+ if (isPositioned) {
7752
+ for (let i = currentPageShapesSorted.length - 1; i >= 0; i--) {
7753
+ const parent = currentPageShapesSorted[i]
7754
+ const util = this.getShapeUtil(parent)
7755
+ if (
7756
+ util.canReceiveNewChildrenOfType(parent, partial.type) &&
7757
+ !this.isShapeHidden(parent) &&
7758
+ this.isPointInShape(
7759
+ parent,
7760
+ // If no parent is provided, then we can treat the
7761
+ // shape's provided x/y as being in the page's space.
7762
+ { x: partial.x ?? 0, y: partial.y ?? 0 },
7763
+ {
7764
+ margin: 0,
7765
+ hitInside: true,
7766
+ }
7767
+ )
7768
+ ) {
7769
+ parentId = parent.id
7770
+ break
7771
+ }
7857
7772
  }
7858
7773
  }
7859
7774
 
7860
7775
  const prevParentId = partial.parentId
7861
7776
 
7862
- // a shape cannot be it's own parent. This was a rare issue with frames/groups in the syncFuzz tests.
7777
+ // a shape cannot be its own parent. This was a rare issue with frames/groups in the syncFuzz tests.
7863
7778
  if (parentId === partial.id) {
7864
7779
  parentId = focusedGroupId
7865
7780
  }
@@ -8279,7 +8194,7 @@ export class Editor extends EventEmitter<TLEventMap> {
8279
8194
  *
8280
8195
  * @public
8281
8196
  */
8282
- updateShape<T extends TLUnknownShape>(partial: TLShapePartial<T> | null | undefined) {
8197
+ updateShape<T extends TLShape = TLShape>(partial: TLShapePartial<T> | null | undefined) {
8283
8198
  this.updateShapes([partial])
8284
8199
  return this
8285
8200
  }
@@ -8296,7 +8211,7 @@ export class Editor extends EventEmitter<TLEventMap> {
8296
8211
  *
8297
8212
  * @public
8298
8213
  */
8299
- updateShapes<T extends TLUnknownShape>(partials: (TLShapePartial<T> | null | undefined)[]) {
8214
+ updateShapes<T extends TLShape>(partials: (TLShapePartial<T> | null | undefined)[]) {
8300
8215
  const compactedPartials: TLShapePartial<T>[] = Array(partials.length)
8301
8216
 
8302
8217
  for (let i = 0, n = partials.length; i < n; i++) {
@@ -8911,8 +8826,13 @@ export class Editor extends EventEmitter<TLEventMap> {
8911
8826
  * Handle external content, such as files, urls, embeds, or plain text which has been put into the app, for example by pasting external text or dropping external images onto canvas.
8912
8827
  *
8913
8828
  * @param info - Info about the external content.
8829
+ * @param opts - Options for handling external content, including force flag to bypass readonly checks.
8914
8830
  */
8915
- async putExternalContent<E>(info: TLExternalContent<E>): Promise<void> {
8831
+ async putExternalContent<E>(
8832
+ info: TLExternalContent<E>,
8833
+ opts = {} as { force?: boolean }
8834
+ ): Promise<void> {
8835
+ if (!opts.force && this.getIsReadonly()) return
8916
8836
  return this.externalContentHandlers[info.type]?.(info as any)
8917
8837
  }
8918
8838
 
@@ -8920,8 +8840,13 @@ export class Editor extends EventEmitter<TLEventMap> {
8920
8840
  * Handle replacing external content.
8921
8841
  *
8922
8842
  * @param info - Info about the external content.
8843
+ * @param opts - Options for handling external content, including force flag to bypass readonly checks.
8923
8844
  */
8924
- async replaceExternalContent<E>(info: TLExternalContent<E>): Promise<void> {
8845
+ async replaceExternalContent<E>(
8846
+ info: TLExternalContent<E>,
8847
+ opts = {} as { force?: boolean }
8848
+ ): Promise<void> {
8849
+ if (!opts.force && this.getIsReadonly()) return
8925
8850
  return this.externalContentHandlers[info.type]?.(info as any)
8926
8851
  }
8927
8852
 
@@ -9422,13 +9347,6 @@ export class Editor extends EventEmitter<TLEventMap> {
9422
9347
  }
9423
9348
  }
9424
9349
 
9425
- /** @deprecated Use {@link Editor.getSvgString} or {@link Editor.getSvgElement} instead. */
9426
- async getSvg(shapes: TLShapeId[] | TLShape[], opts: TLSvgExportOptions = {}) {
9427
- const result = await this.getSvgElement(shapes, opts)
9428
- if (!result) return undefined
9429
- return result.svg
9430
- }
9431
-
9432
9350
  /**
9433
9351
  * Get an exported image of the given shapes.
9434
9352
  *
@@ -9480,6 +9398,24 @@ export class Editor extends EventEmitter<TLEventMap> {
9480
9398
  }
9481
9399
  }
9482
9400
 
9401
+ /**
9402
+ * Get an exported image of the given shapes as a data URL.
9403
+ *
9404
+ * @param shapes - The shapes (or shape ids) to export.
9405
+ * @param opts - Options for the export.
9406
+ *
9407
+ * @returns A data URL of the image.
9408
+ * @public
9409
+ */
9410
+ async toImageDataUrl(shapes: TLShapeId[] | TLShape[], opts: TLImageExportOptions = {}) {
9411
+ const { blob, width, height } = await this.toImage(shapes, opts)
9412
+ return {
9413
+ url: await FileHelpers.blobToDataUrl(blob),
9414
+ width,
9415
+ height,
9416
+ }
9417
+ }
9418
+
9483
9419
  /* --------------------- Events --------------------- */
9484
9420
 
9485
9421
  /**
@@ -9647,6 +9583,52 @@ export class Editor extends EventEmitter<TLEventMap> {
9647
9583
  return this
9648
9584
  }
9649
9585
 
9586
+ /**
9587
+ * Dispatch a pointer move event in the current position of the pointer. This is useful when
9588
+ * external circumstances have changed (e.g. the camera moved or a shape was moved) and you want
9589
+ * the current interaction to respond to that change.
9590
+ *
9591
+ * @example
9592
+ * ```ts
9593
+ * editor.updatePointer()
9594
+ * ```
9595
+ *
9596
+ * @param options - The options for updating the pointer.
9597
+ * @returns The editor instance.
9598
+ * @public
9599
+ */
9600
+ updatePointer(options?: TLUpdatePointerOptions): this {
9601
+ const event: TLPointerEventInfo = {
9602
+ type: 'pointer',
9603
+ target: 'canvas',
9604
+ name: 'pointer_move',
9605
+ point:
9606
+ options?.point ??
9607
+ // weird but true: what `inputs` calls screen-space is actually viewport space. so
9608
+ // we need to convert back into true screen space first. we should fix this...
9609
+ Vec.Add(
9610
+ this.inputs.currentScreenPoint,
9611
+ this.store.unsafeGetWithoutCapture(TLINSTANCE_ID)!.screenBounds
9612
+ ),
9613
+ pointerId: options?.pointerId ?? 0,
9614
+ button: options?.button ?? 0,
9615
+ isPen: options?.isPen ?? this.inputs.isPen,
9616
+ shiftKey: options?.shiftKey ?? this.inputs.shiftKey,
9617
+ altKey: options?.altKey ?? this.inputs.altKey,
9618
+ ctrlKey: options?.ctrlKey ?? this.inputs.ctrlKey,
9619
+ metaKey: options?.metaKey ?? this.inputs.metaKey,
9620
+ accelKey: options?.accelKey ?? isAccelKey(this.inputs),
9621
+ }
9622
+
9623
+ if (options?.immediate) {
9624
+ this._flushEventForTick(event)
9625
+ } else {
9626
+ this.dispatch(event)
9627
+ }
9628
+
9629
+ return this
9630
+ }
9631
+
9650
9632
  /**
9651
9633
  * Puts the editor into focused mode.
9652
9634
  *
@@ -10665,7 +10647,10 @@ function alertMaxShapes(editor: Editor, pageId = editor.getCurrentPageId()) {
10665
10647
 
10666
10648
  function applyPartialToRecordWithProps<
10667
10649
  T extends UnknownRecord & { type: string; props: object; meta: object },
10668
- >(prev: T, partial?: Partial<T> & { props?: Partial<T['props']> }): T {
10650
+ >(
10651
+ prev: T,
10652
+ partial?: T extends T ? Omit<Partial<T>, 'props'> & { props?: Partial<T['props']> } : never
10653
+ ): T {
10669
10654
  if (!partial) return prev
10670
10655
  let next = null as null | T
10671
10656
  const entries = Object.entries(partial)