@tldraw/editor 4.6.0-next.fecc64eee134 → 5.0.1

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 (324) hide show
  1. package/dist-cjs/index.d.ts +493 -170
  2. package/dist-cjs/index.js +14 -23
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/TldrawEditor.js +3 -0
  5. package/dist-cjs/lib/TldrawEditor.js.map +2 -2
  6. package/dist-cjs/lib/components/MenuClickCapture.js +93 -47
  7. package/dist-cjs/lib/components/MenuClickCapture.js.map +2 -2
  8. package/dist-cjs/lib/components/default-components/CanvasOverlays.js +180 -0
  9. package/dist-cjs/lib/components/default-components/CanvasOverlays.js.map +7 -0
  10. package/dist-cjs/lib/components/default-components/DefaultCanvas.js +46 -248
  11. package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +3 -3
  12. package/dist-cjs/lib/editor/Editor.js +142 -33
  13. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  14. package/dist-cjs/lib/editor/assets/AssetUtil.js +1 -0
  15. package/dist-cjs/lib/editor/assets/AssetUtil.js.map +1 -1
  16. package/dist-cjs/lib/editor/bindings/BindingUtil.js +1 -0
  17. package/dist-cjs/lib/editor/bindings/BindingUtil.js.map +1 -1
  18. package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js +1 -0
  19. package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js.map +1 -1
  20. package/dist-cjs/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.js +98 -0
  21. package/dist-cjs/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.js.map +7 -0
  22. package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js +1 -0
  23. package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js.map +1 -1
  24. package/dist-cjs/lib/editor/managers/FocusManager/FocusManager.js +1 -0
  25. package/dist-cjs/lib/editor/managers/FocusManager/FocusManager.js.map +1 -1
  26. package/dist-cjs/lib/editor/managers/FontManager/FontManager.js +2 -0
  27. package/dist-cjs/lib/editor/managers/FontManager/FontManager.js.map +1 -1
  28. package/dist-cjs/lib/editor/managers/HistoryManager/HistoryManager.js +2 -0
  29. package/dist-cjs/lib/editor/managers/HistoryManager/HistoryManager.js.map +1 -1
  30. package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js +12 -0
  31. package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js.map +2 -2
  32. package/dist-cjs/lib/editor/managers/ScribbleManager/ScribbleManager.js +1 -0
  33. package/dist-cjs/lib/editor/managers/ScribbleManager/ScribbleManager.js.map +1 -1
  34. package/dist-cjs/lib/editor/managers/SnapManager/BoundsSnaps.js +1 -0
  35. package/dist-cjs/lib/editor/managers/SnapManager/BoundsSnaps.js.map +1 -1
  36. package/dist-cjs/lib/editor/managers/SnapManager/HandleSnaps.js +1 -0
  37. package/dist-cjs/lib/editor/managers/SnapManager/HandleSnaps.js.map +1 -1
  38. package/dist-cjs/lib/editor/managers/SnapManager/SnapManager.js +2 -1
  39. package/dist-cjs/lib/editor/managers/SnapManager/SnapManager.js.map +2 -2
  40. package/dist-cjs/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.js +1 -0
  41. package/dist-cjs/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.js.map +1 -1
  42. package/dist-cjs/lib/editor/managers/TextManager/TextManager.js +1 -0
  43. package/dist-cjs/lib/editor/managers/TextManager/TextManager.js.map +1 -1
  44. package/dist-cjs/lib/editor/managers/ThemeManager/ThemeManager.js +1 -0
  45. package/dist-cjs/lib/editor/managers/ThemeManager/ThemeManager.js.map +1 -1
  46. package/dist-cjs/lib/editor/managers/ThemeManager/defaultThemes.js +14 -0
  47. package/dist-cjs/lib/editor/managers/ThemeManager/defaultThemes.js.map +2 -2
  48. package/dist-cjs/lib/editor/managers/TickManager/TickManager.js +1 -0
  49. package/dist-cjs/lib/editor/managers/TickManager/TickManager.js.map +1 -1
  50. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js +2 -0
  51. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +1 -1
  52. package/dist-cjs/lib/editor/overlays/OverlayManager.js +154 -0
  53. package/dist-cjs/lib/editor/overlays/OverlayManager.js.map +7 -0
  54. package/dist-cjs/lib/editor/overlays/OverlayUtil.js +92 -0
  55. package/dist-cjs/lib/editor/overlays/OverlayUtil.js.map +7 -0
  56. package/dist-cjs/lib/editor/overlays/ShapeIndicatorOverlayUtil.js +161 -0
  57. package/dist-cjs/lib/editor/overlays/ShapeIndicatorOverlayUtil.js.map +7 -0
  58. package/dist-cjs/lib/editor/overlays/getOverlayDisplayValues.js +39 -0
  59. package/dist-cjs/lib/editor/overlays/getOverlayDisplayValues.js.map +7 -0
  60. package/dist-cjs/lib/editor/shapes/BaseFrameLikeShapeUtil.js +79 -0
  61. package/dist-cjs/lib/editor/shapes/BaseFrameLikeShapeUtil.js.map +7 -0
  62. package/dist-cjs/lib/editor/shapes/ShapeUtil.js +36 -23
  63. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  64. package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js +32 -2
  65. package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js.map +2 -2
  66. package/dist-cjs/lib/editor/tools/StateNode.js +1 -0
  67. package/dist-cjs/lib/editor/tools/StateNode.js.map +1 -1
  68. package/dist-cjs/lib/editor/types/event-types.js.map +2 -2
  69. package/dist-cjs/lib/exports/ExportDelay.js +1 -0
  70. package/dist-cjs/lib/exports/ExportDelay.js.map +1 -1
  71. package/dist-cjs/lib/exports/StyleEmbedder.js +1 -0
  72. package/dist-cjs/lib/exports/StyleEmbedder.js.map +1 -1
  73. package/dist-cjs/lib/exports/fetchCache.js +1 -1
  74. package/dist-cjs/lib/exports/fetchCache.js.map +2 -2
  75. package/dist-cjs/lib/exports/getSvgJsx.js +2 -1
  76. package/dist-cjs/lib/exports/getSvgJsx.js.map +2 -2
  77. package/dist-cjs/lib/hooks/EditorComponentsContext.js.map +2 -2
  78. package/dist-cjs/lib/hooks/useCanvasEvents.js +25 -4
  79. package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
  80. package/dist-cjs/lib/hooks/useEditorComponents.js +0 -28
  81. package/dist-cjs/lib/hooks/useEditorComponents.js.map +2 -2
  82. package/dist-cjs/lib/hooks/usePeerIds.js +1 -36
  83. package/dist-cjs/lib/hooks/usePeerIds.js.map +2 -2
  84. package/dist-cjs/lib/hooks/useShapeCulling.js +2 -1
  85. package/dist-cjs/lib/hooks/useShapeCulling.js.map +2 -2
  86. package/dist-cjs/lib/options.js +1 -0
  87. package/dist-cjs/lib/options.js.map +2 -2
  88. package/dist-cjs/lib/primitives/Vec.js +3 -0
  89. package/dist-cjs/lib/primitives/Vec.js.map +1 -1
  90. package/dist-cjs/lib/primitives/geometry/Circle2d.js +1 -0
  91. package/dist-cjs/lib/primitives/geometry/Circle2d.js.map +1 -1
  92. package/dist-cjs/lib/primitives/geometry/Ellipse2d.js +1 -0
  93. package/dist-cjs/lib/primitives/geometry/Ellipse2d.js.map +1 -1
  94. package/dist-cjs/lib/primitives/geometry/Geometry2d.js +2 -0
  95. package/dist-cjs/lib/primitives/geometry/Geometry2d.js.map +1 -1
  96. package/dist-cjs/lib/primitives/geometry/Stadium2d.js +1 -0
  97. package/dist-cjs/lib/primitives/geometry/Stadium2d.js.map +1 -1
  98. package/dist-cjs/lib/utils/EditorAtom.js +2 -0
  99. package/dist-cjs/lib/utils/EditorAtom.js.map +1 -1
  100. package/dist-cjs/lib/utils/reparenting.js +20 -7
  101. package/dist-cjs/lib/utils/reparenting.js.map +2 -2
  102. package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js +5 -0
  103. package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js.map +2 -2
  104. package/dist-cjs/version.js +4 -4
  105. package/dist-cjs/version.js.map +1 -1
  106. package/dist-esm/index.d.mts +493 -170
  107. package/dist-esm/index.mjs +21 -41
  108. package/dist-esm/index.mjs.map +2 -2
  109. package/dist-esm/lib/TldrawEditor.mjs +3 -0
  110. package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
  111. package/dist-esm/lib/components/MenuClickCapture.mjs +94 -48
  112. package/dist-esm/lib/components/MenuClickCapture.mjs.map +2 -2
  113. package/dist-esm/lib/components/default-components/CanvasOverlays.mjs +160 -0
  114. package/dist-esm/lib/components/default-components/CanvasOverlays.mjs.map +7 -0
  115. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +47 -249
  116. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +3 -3
  117. package/dist-esm/lib/editor/Editor.mjs +143 -35
  118. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  119. package/dist-esm/lib/editor/assets/AssetUtil.mjs +1 -0
  120. package/dist-esm/lib/editor/assets/AssetUtil.mjs.map +1 -1
  121. package/dist-esm/lib/editor/bindings/BindingUtil.mjs +1 -0
  122. package/dist-esm/lib/editor/bindings/BindingUtil.mjs.map +1 -1
  123. package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs +1 -0
  124. package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs.map +1 -1
  125. package/dist-esm/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.mjs +83 -0
  126. package/dist-esm/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.mjs.map +7 -0
  127. package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs +1 -0
  128. package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs.map +1 -1
  129. package/dist-esm/lib/editor/managers/FocusManager/FocusManager.mjs +1 -0
  130. package/dist-esm/lib/editor/managers/FocusManager/FocusManager.mjs.map +1 -1
  131. package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs +2 -0
  132. package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs.map +1 -1
  133. package/dist-esm/lib/editor/managers/HistoryManager/HistoryManager.mjs +2 -0
  134. package/dist-esm/lib/editor/managers/HistoryManager/HistoryManager.mjs.map +1 -1
  135. package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs +12 -0
  136. package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs.map +2 -2
  137. package/dist-esm/lib/editor/managers/ScribbleManager/ScribbleManager.mjs +1 -0
  138. package/dist-esm/lib/editor/managers/ScribbleManager/ScribbleManager.mjs.map +1 -1
  139. package/dist-esm/lib/editor/managers/SnapManager/BoundsSnaps.mjs +1 -0
  140. package/dist-esm/lib/editor/managers/SnapManager/BoundsSnaps.mjs.map +1 -1
  141. package/dist-esm/lib/editor/managers/SnapManager/HandleSnaps.mjs +1 -0
  142. package/dist-esm/lib/editor/managers/SnapManager/HandleSnaps.mjs.map +1 -1
  143. package/dist-esm/lib/editor/managers/SnapManager/SnapManager.mjs +2 -1
  144. package/dist-esm/lib/editor/managers/SnapManager/SnapManager.mjs.map +2 -2
  145. package/dist-esm/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.mjs +1 -0
  146. package/dist-esm/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.mjs.map +1 -1
  147. package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs +1 -0
  148. package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs.map +1 -1
  149. package/dist-esm/lib/editor/managers/ThemeManager/ThemeManager.mjs +1 -0
  150. package/dist-esm/lib/editor/managers/ThemeManager/ThemeManager.mjs.map +1 -1
  151. package/dist-esm/lib/editor/managers/ThemeManager/defaultThemes.mjs +14 -0
  152. package/dist-esm/lib/editor/managers/ThemeManager/defaultThemes.mjs.map +2 -2
  153. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs +1 -0
  154. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs.map +1 -1
  155. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs +2 -0
  156. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +1 -1
  157. package/dist-esm/lib/editor/overlays/OverlayManager.mjs +136 -0
  158. package/dist-esm/lib/editor/overlays/OverlayManager.mjs.map +7 -0
  159. package/dist-esm/lib/editor/overlays/OverlayUtil.mjs +72 -0
  160. package/dist-esm/lib/editor/overlays/OverlayUtil.mjs.map +7 -0
  161. package/dist-esm/lib/editor/overlays/ShapeIndicatorOverlayUtil.mjs +141 -0
  162. package/dist-esm/lib/editor/overlays/ShapeIndicatorOverlayUtil.mjs.map +7 -0
  163. package/dist-esm/lib/editor/overlays/getOverlayDisplayValues.mjs +19 -0
  164. package/dist-esm/lib/editor/overlays/getOverlayDisplayValues.mjs.map +7 -0
  165. package/dist-esm/lib/editor/shapes/BaseFrameLikeShapeUtil.mjs +59 -0
  166. package/dist-esm/lib/editor/shapes/BaseFrameLikeShapeUtil.mjs.map +7 -0
  167. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +36 -23
  168. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  169. package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs +32 -2
  170. package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs.map +2 -2
  171. package/dist-esm/lib/editor/tools/StateNode.mjs +1 -0
  172. package/dist-esm/lib/editor/tools/StateNode.mjs.map +1 -1
  173. package/dist-esm/lib/editor/types/event-types.mjs.map +2 -2
  174. package/dist-esm/lib/exports/ExportDelay.mjs +1 -0
  175. package/dist-esm/lib/exports/ExportDelay.mjs.map +1 -1
  176. package/dist-esm/lib/exports/StyleEmbedder.mjs +1 -0
  177. package/dist-esm/lib/exports/StyleEmbedder.mjs.map +1 -1
  178. package/dist-esm/lib/exports/fetchCache.mjs +2 -2
  179. package/dist-esm/lib/exports/fetchCache.mjs.map +2 -2
  180. package/dist-esm/lib/exports/getSvgJsx.mjs +2 -1
  181. package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
  182. package/dist-esm/lib/hooks/EditorComponentsContext.mjs.map +2 -2
  183. package/dist-esm/lib/hooks/useCanvasEvents.mjs +25 -4
  184. package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
  185. package/dist-esm/lib/hooks/useEditorComponents.mjs +0 -28
  186. package/dist-esm/lib/hooks/useEditorComponents.mjs.map +2 -2
  187. package/dist-esm/lib/hooks/usePeerIds.mjs +2 -40
  188. package/dist-esm/lib/hooks/usePeerIds.mjs.map +2 -2
  189. package/dist-esm/lib/hooks/useShapeCulling.mjs +2 -1
  190. package/dist-esm/lib/hooks/useShapeCulling.mjs.map +2 -2
  191. package/dist-esm/lib/options.mjs +1 -0
  192. package/dist-esm/lib/options.mjs.map +2 -2
  193. package/dist-esm/lib/primitives/Vec.mjs +3 -0
  194. package/dist-esm/lib/primitives/Vec.mjs.map +1 -1
  195. package/dist-esm/lib/primitives/geometry/Circle2d.mjs +1 -0
  196. package/dist-esm/lib/primitives/geometry/Circle2d.mjs.map +1 -1
  197. package/dist-esm/lib/primitives/geometry/Ellipse2d.mjs +1 -0
  198. package/dist-esm/lib/primitives/geometry/Ellipse2d.mjs.map +1 -1
  199. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs +2 -0
  200. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs.map +1 -1
  201. package/dist-esm/lib/primitives/geometry/Stadium2d.mjs +1 -0
  202. package/dist-esm/lib/primitives/geometry/Stadium2d.mjs.map +1 -1
  203. package/dist-esm/lib/utils/EditorAtom.mjs +2 -0
  204. package/dist-esm/lib/utils/EditorAtom.mjs.map +1 -1
  205. package/dist-esm/lib/utils/reparenting.mjs +20 -7
  206. package/dist-esm/lib/utils/reparenting.mjs.map +2 -2
  207. package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs +5 -0
  208. package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs.map +2 -2
  209. package/dist-esm/version.mjs +4 -4
  210. package/dist-esm/version.mjs.map +1 -1
  211. package/editor.css +4 -243
  212. package/package.json +7 -7
  213. package/src/index.ts +18 -39
  214. package/src/lib/TldrawEditor.tsx +9 -0
  215. package/src/lib/components/MenuClickCapture.tsx +124 -64
  216. package/src/lib/components/default-components/CanvasOverlays.tsx +208 -0
  217. package/src/lib/components/default-components/DefaultCanvas.tsx +51 -322
  218. package/src/lib/editor/Editor.test.ts +3 -1
  219. package/src/lib/editor/Editor.ts +167 -38
  220. package/src/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.ts +98 -0
  221. package/src/lib/editor/managers/InputsManager/InputsManager.ts +12 -0
  222. package/src/lib/editor/managers/SnapManager/SnapManager.test.ts +13 -2
  223. package/src/lib/editor/managers/SnapManager/SnapManager.ts +1 -1
  224. package/src/lib/editor/managers/ThemeManager/defaultThemes.ts +14 -0
  225. package/src/lib/editor/overlays/OverlayManager.ts +183 -0
  226. package/src/lib/editor/overlays/OverlayUtil.ts +143 -0
  227. package/src/lib/editor/overlays/ShapeIndicatorOverlayUtil.ts +216 -0
  228. package/src/lib/editor/overlays/getOverlayDisplayValues.ts +51 -0
  229. package/src/lib/editor/shapes/BaseFrameLikeShapeUtil.tsx +128 -0
  230. package/src/lib/editor/shapes/ShapeUtil.ts +45 -26
  231. package/src/lib/editor/shapes/group/GroupShapeUtil.tsx +40 -3
  232. package/src/lib/editor/types/event-types.ts +2 -0
  233. package/src/lib/exports/fetchCache.ts +2 -4
  234. package/src/lib/exports/getSvgJsx.test.ts +3 -1
  235. package/src/lib/exports/getSvgJsx.tsx +2 -1
  236. package/src/lib/hooks/EditorComponentsContext.tsx +0 -27
  237. package/src/lib/hooks/useCanvasEvents.ts +45 -3
  238. package/src/lib/hooks/useEditorComponents.tsx +0 -28
  239. package/src/lib/hooks/usePeerIds.ts +6 -55
  240. package/src/lib/hooks/useShapeCulling.tsx +3 -1
  241. package/src/lib/options.ts +7 -0
  242. package/src/lib/utils/reparenting.ts +22 -9
  243. package/src/lib/utils/sync/TLLocalSyncClient.ts +3 -0
  244. package/src/version.ts +4 -4
  245. package/dist-cjs/lib/components/GeometryDebuggingView.js +0 -115
  246. package/dist-cjs/lib/components/GeometryDebuggingView.js.map +0 -7
  247. package/dist-cjs/lib/components/LiveCollaborators.js +0 -151
  248. package/dist-cjs/lib/components/LiveCollaborators.js.map +0 -7
  249. package/dist-cjs/lib/components/default-components/CanvasShapeIndicators.js +0 -227
  250. package/dist-cjs/lib/components/default-components/CanvasShapeIndicators.js.map +0 -7
  251. package/dist-cjs/lib/components/default-components/DefaultBrush.js +0 -38
  252. package/dist-cjs/lib/components/default-components/DefaultBrush.js.map +0 -7
  253. package/dist-cjs/lib/components/default-components/DefaultCollaboratorHint.js +0 -71
  254. package/dist-cjs/lib/components/default-components/DefaultCollaboratorHint.js.map +0 -7
  255. package/dist-cjs/lib/components/default-components/DefaultCursor.js +0 -59
  256. package/dist-cjs/lib/components/default-components/DefaultCursor.js.map +0 -7
  257. package/dist-cjs/lib/components/default-components/DefaultHandle.js +0 -56
  258. package/dist-cjs/lib/components/default-components/DefaultHandle.js.map +0 -7
  259. package/dist-cjs/lib/components/default-components/DefaultHandles.js +0 -28
  260. package/dist-cjs/lib/components/default-components/DefaultHandles.js.map +0 -7
  261. package/dist-cjs/lib/components/default-components/DefaultScribble.js +0 -51
  262. package/dist-cjs/lib/components/default-components/DefaultScribble.js.map +0 -7
  263. package/dist-cjs/lib/components/default-components/DefaultSelectionForeground.js +0 -69
  264. package/dist-cjs/lib/components/default-components/DefaultSelectionForeground.js.map +0 -7
  265. package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js +0 -107
  266. package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js.map +0 -7
  267. package/dist-cjs/lib/components/default-components/DefaultShapeIndicatorErrorFallback.js +0 -28
  268. package/dist-cjs/lib/components/default-components/DefaultShapeIndicatorErrorFallback.js.map +0 -7
  269. package/dist-cjs/lib/components/default-components/DefaultShapeIndicators.js +0 -101
  270. package/dist-cjs/lib/components/default-components/DefaultShapeIndicators.js.map +0 -7
  271. package/dist-cjs/lib/components/default-components/DefaultSnapIndictor.js +0 -170
  272. package/dist-cjs/lib/components/default-components/DefaultSnapIndictor.js.map +0 -7
  273. package/dist-cjs/lib/hooks/useHandleEvents.js +0 -100
  274. package/dist-cjs/lib/hooks/useHandleEvents.js.map +0 -7
  275. package/dist-cjs/lib/hooks/useSelectionEvents.js +0 -98
  276. package/dist-cjs/lib/hooks/useSelectionEvents.js.map +0 -7
  277. package/dist-esm/lib/components/GeometryDebuggingView.mjs +0 -95
  278. package/dist-esm/lib/components/GeometryDebuggingView.mjs.map +0 -7
  279. package/dist-esm/lib/components/LiveCollaborators.mjs +0 -134
  280. package/dist-esm/lib/components/LiveCollaborators.mjs.map +0 -7
  281. package/dist-esm/lib/components/default-components/CanvasShapeIndicators.mjs +0 -207
  282. package/dist-esm/lib/components/default-components/CanvasShapeIndicators.mjs.map +0 -7
  283. package/dist-esm/lib/components/default-components/DefaultBrush.mjs +0 -18
  284. package/dist-esm/lib/components/default-components/DefaultBrush.mjs.map +0 -7
  285. package/dist-esm/lib/components/default-components/DefaultCollaboratorHint.mjs +0 -41
  286. package/dist-esm/lib/components/default-components/DefaultCollaboratorHint.mjs.map +0 -7
  287. package/dist-esm/lib/components/default-components/DefaultCursor.mjs +0 -29
  288. package/dist-esm/lib/components/default-components/DefaultCursor.mjs.map +0 -7
  289. package/dist-esm/lib/components/default-components/DefaultHandle.mjs +0 -26
  290. package/dist-esm/lib/components/default-components/DefaultHandle.mjs.map +0 -7
  291. package/dist-esm/lib/components/default-components/DefaultHandles.mjs +0 -8
  292. package/dist-esm/lib/components/default-components/DefaultHandles.mjs.map +0 -7
  293. package/dist-esm/lib/components/default-components/DefaultScribble.mjs +0 -21
  294. package/dist-esm/lib/components/default-components/DefaultScribble.mjs.map +0 -7
  295. package/dist-esm/lib/components/default-components/DefaultSelectionForeground.mjs +0 -39
  296. package/dist-esm/lib/components/default-components/DefaultSelectionForeground.mjs.map +0 -7
  297. package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs +0 -77
  298. package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs.map +0 -7
  299. package/dist-esm/lib/components/default-components/DefaultShapeIndicatorErrorFallback.mjs +0 -8
  300. package/dist-esm/lib/components/default-components/DefaultShapeIndicatorErrorFallback.mjs.map +0 -7
  301. package/dist-esm/lib/components/default-components/DefaultShapeIndicators.mjs +0 -81
  302. package/dist-esm/lib/components/default-components/DefaultShapeIndicators.mjs.map +0 -7
  303. package/dist-esm/lib/components/default-components/DefaultSnapIndictor.mjs +0 -142
  304. package/dist-esm/lib/components/default-components/DefaultSnapIndictor.mjs.map +0 -7
  305. package/dist-esm/lib/hooks/useHandleEvents.mjs +0 -70
  306. package/dist-esm/lib/hooks/useHandleEvents.mjs.map +0 -7
  307. package/dist-esm/lib/hooks/useSelectionEvents.mjs +0 -78
  308. package/dist-esm/lib/hooks/useSelectionEvents.mjs.map +0 -7
  309. package/src/lib/components/GeometryDebuggingView.tsx +0 -108
  310. package/src/lib/components/LiveCollaborators.tsx +0 -174
  311. package/src/lib/components/default-components/CanvasShapeIndicators.tsx +0 -289
  312. package/src/lib/components/default-components/DefaultBrush.tsx +0 -35
  313. package/src/lib/components/default-components/DefaultCollaboratorHint.tsx +0 -52
  314. package/src/lib/components/default-components/DefaultCursor.tsx +0 -59
  315. package/src/lib/components/default-components/DefaultHandle.tsx +0 -42
  316. package/src/lib/components/default-components/DefaultHandles.tsx +0 -15
  317. package/src/lib/components/default-components/DefaultScribble.tsx +0 -31
  318. package/src/lib/components/default-components/DefaultSelectionForeground.tsx +0 -50
  319. package/src/lib/components/default-components/DefaultShapeIndicator.tsx +0 -104
  320. package/src/lib/components/default-components/DefaultShapeIndicatorErrorFallback.tsx +0 -9
  321. package/src/lib/components/default-components/DefaultShapeIndicators.tsx +0 -116
  322. package/src/lib/components/default-components/DefaultSnapIndictor.tsx +0 -174
  323. package/src/lib/hooks/useHandleEvents.ts +0 -88
  324. package/src/lib/hooks/useSelectionEvents.ts +0 -97
@@ -10,10 +10,10 @@ import {
10
10
  import {
11
11
  ComputedCache,
12
12
  RecordType,
13
+ StoreSideEffects,
14
+ StoreSnapshot,
13
15
  UnknownRecord,
14
16
  reverseRecordsDiff,
15
- StoreSnapshot,
16
- StoreSideEffects,
17
17
  } from '@tldraw/store'
18
18
  import {
19
19
  CameraRecordType,
@@ -89,7 +89,6 @@ import {
89
89
  hasOwnProperty,
90
90
  last,
91
91
  lerp,
92
- maxBy,
93
92
  minBy,
94
93
  sortById,
95
94
  sortByIndex,
@@ -152,6 +151,7 @@ import { notVisibleShapes } from './derivations/notVisibleShapes'
152
151
  import { parentsToChildren } from './derivations/parentsToChildren'
153
152
  import { deriveShapeIdsInCurrentPage } from './derivations/shapeIdsInCurrentPage'
154
153
  import { ClickManager } from './managers/ClickManager/ClickManager'
154
+ import { CollaboratorsManager } from './managers/CollaboratorsManager/CollaboratorsManager'
155
155
  import { EdgeScrollManager } from './managers/EdgeScrollManager/EdgeScrollManager'
156
156
  import { FocusManager } from './managers/FocusManager/FocusManager'
157
157
  import { FontManager } from './managers/FontManager/FontManager'
@@ -162,9 +162,11 @@ import { ScribbleManager } from './managers/ScribbleManager/ScribbleManager'
162
162
  import { SnapManager } from './managers/SnapManager/SnapManager'
163
163
  import { SpatialIndexManager } from './managers/SpatialIndexManager/SpatialIndexManager'
164
164
  import { TextManager } from './managers/TextManager/TextManager'
165
- import { resolveThemes, ThemeManager } from './managers/ThemeManager/ThemeManager'
165
+ import { ThemeManager, resolveThemes } from './managers/ThemeManager/ThemeManager'
166
166
  import { TickManager } from './managers/TickManager/TickManager'
167
167
  import { UserPreferencesManager } from './managers/UserPreferencesManager/UserPreferencesManager'
168
+ import { OverlayManager } from './overlays/OverlayManager'
169
+ import { TLAnyOverlayUtilConstructor } from './overlays/OverlayUtil'
168
170
  import {
169
171
  ShapeUtil,
170
172
  TLEditStartInfo,
@@ -224,6 +226,11 @@ export interface TLEditorOptions {
224
226
  * An array of asset utils to use in the editor. These will be used to handle asset-type-specific behavior.
225
227
  */
226
228
  assetUtils?: readonly TLAnyAssetUtilConstructor[]
229
+ /**
230
+ * An array of overlay utils to use in the editor. These define canvas overlay UI elements
231
+ * like selection handles, rotation corners, shape handles, etc.
232
+ */
233
+ overlayUtils?: readonly TLAnyOverlayUtilConstructor[]
227
234
  /**
228
235
  * An array of tools to use in the editor. These will be used to handle events and manage user interactions in the editor.
229
236
  */
@@ -330,6 +337,7 @@ export class Editor extends EventEmitter<TLEventMap> {
330
337
  shapeUtils,
331
338
  bindingUtils,
332
339
  assetUtils: assetUtilConstructors,
340
+ overlayUtils: overlayUtilConstructors,
333
341
  tools,
334
342
  getContainer,
335
343
  // needs to be here for backwards compatibility with TldrawEditor
@@ -410,6 +418,7 @@ export class Editor extends EventEmitter<TLEventMap> {
410
418
  this.inputs = new InputsManager(this)
411
419
  this.performance = new PerformanceManager(this)
412
420
  this.disposables.add(() => this.performance.dispose())
421
+ this.collaborators = new CollaboratorsManager(this)
413
422
 
414
423
  class NewRoot extends RootState {
415
424
  static override initial = initialState ?? ''
@@ -489,6 +498,15 @@ export class Editor extends EventEmitter<TLEventMap> {
489
498
 
490
499
  this.scribbles = new ScribbleManager(this)
491
500
 
501
+ // Overlay utils
502
+ this.overlays = new OverlayManager(this)
503
+ if (overlayUtilConstructors) {
504
+ for (const Util of overlayUtilConstructors) {
505
+ const util = new Util(this)
506
+ this.overlays.registerUtil(util)
507
+ }
508
+ }
509
+
492
510
  // Cleanup
493
511
 
494
512
  const cleanupInstancePageState = (
@@ -1033,6 +1051,13 @@ export class Editor extends EventEmitter<TLEventMap> {
1033
1051
  */
1034
1052
  readonly timers = tltime.forContext(this.contextId)
1035
1053
 
1054
+ /**
1055
+ * A manager for remote peer collaborators connected to this editor.
1056
+ *
1057
+ * @public
1058
+ */
1059
+ readonly collaborators: CollaboratorsManager
1060
+
1036
1061
  /**
1037
1062
  * A manager for the user and their preferences.
1038
1063
  *
@@ -1068,6 +1093,13 @@ export class Editor extends EventEmitter<TLEventMap> {
1068
1093
  */
1069
1094
  readonly scribbles: ScribbleManager
1070
1095
 
1096
+ /**
1097
+ * A manager for canvas overlay UI elements (selection handles, shape handles, etc.).
1098
+ *
1099
+ * @public
1100
+ */
1101
+ readonly overlays: OverlayManager
1102
+
1071
1103
  /**
1072
1104
  * A manager for side effects and correct state enforcement. See {@link @tldraw/store#StoreSideEffects} for details.
1073
1105
  *
@@ -1912,11 +1944,23 @@ export class Editor extends EventEmitter<TLEventMap> {
1912
1944
  /**
1913
1945
  * Set the cursor.
1914
1946
  *
1947
+ * No-op when the partial wouldn't change the current cursor — `setCursor`
1948
+ * is called from pointer-move hot paths (see `updateHoveredOverlayId`,
1949
+ * various tool states) and skipping redundant writes avoids needlessly
1950
+ * dirtying instance state.
1951
+ *
1915
1952
  * @param cursor - The cursor to set.
1916
1953
  * @public
1917
1954
  */
1918
1955
  setCursor(cursor: Partial<TLCursor>) {
1919
- this.updateInstanceState({ cursor: { ...this.getInstanceState().cursor, ...cursor } })
1956
+ const current = this.getInstanceState().cursor
1957
+ if (
1958
+ (cursor.type === undefined || cursor.type === current.type) &&
1959
+ (cursor.rotation === undefined || cursor.rotation === current.rotation)
1960
+ ) {
1961
+ return this
1962
+ }
1963
+ this.updateInstanceState({ cursor: { ...current, ...cursor } })
1920
1964
  return this
1921
1965
  }
1922
1966
 
@@ -4222,43 +4266,55 @@ export class Editor extends EventEmitter<TLEventMap> {
4222
4266
  }
4223
4267
  // Collaborators
4224
4268
 
4225
- @computed
4226
- private _getCollaboratorsQuery() {
4227
- return this.store.query.records('instance_presence', () => ({
4228
- userId: { neq: this.user.getId() },
4229
- }))
4230
- }
4231
-
4232
4269
  /**
4233
4270
  * Returns a list of presence records for all peer collaborators.
4234
4271
  * This will return the latest presence record for each connected user.
4235
4272
  *
4273
+ * Convenience wrapper for {@link CollaboratorsManager.getCollaborators}.
4274
+ *
4236
4275
  * @public
4237
4276
  */
4238
- @computed
4239
4277
  getCollaborators() {
4240
- const allPresenceRecords = this._getCollaboratorsQuery().get()
4241
- if (!allPresenceRecords.length) return EMPTY_ARRAY
4242
- const userIds = [...new Set(allPresenceRecords.map((c) => c.userId))].sort()
4243
- return userIds.map((id) => {
4244
- const latestPresence = maxBy(
4245
- allPresenceRecords.filter((c) => c.userId === id),
4246
- (p) => p.lastActivityTimestamp ?? 0
4247
- )
4248
- return latestPresence!
4249
- })
4278
+ return this.collaborators.getCollaborators()
4250
4279
  }
4251
4280
 
4252
4281
  /**
4253
4282
  * Returns a list of presence records for all peer collaborators on the current page.
4254
4283
  * This will return the latest presence record for each connected user.
4255
4284
  *
4285
+ * Convenience wrapper for {@link CollaboratorsManager.getCollaboratorsOnCurrentPage}.
4286
+ *
4256
4287
  * @public
4257
4288
  */
4258
- @computed
4259
4289
  getCollaboratorsOnCurrentPage() {
4260
- const currentPageId = this.getCurrentPageId()
4261
- return this.getCollaborators().filter((c) => c.currentPageId === currentPageId)
4290
+ return this.collaborators.getCollaboratorsOnCurrentPage()
4291
+ }
4292
+
4293
+ /**
4294
+ * Returns a list of presence records for peer collaborators who should currently be
4295
+ * shown in the UI. Filters {@link Editor.getCollaborators} by activity state
4296
+ * (active / idle / inactive) and visibility rules such as following and highlighted
4297
+ * users. Re-evaluates on the collaborator visibility clock, so callers don't need to
4298
+ * drive their own activity timer.
4299
+ *
4300
+ * Convenience wrapper for {@link CollaboratorsManager.getVisibleCollaborators}.
4301
+ *
4302
+ * @public
4303
+ */
4304
+ getVisibleCollaborators() {
4305
+ return this.collaborators.getVisibleCollaborators()
4306
+ }
4307
+
4308
+ /**
4309
+ * Returns a list of presence records for peer collaborators who should currently be
4310
+ * shown in the UI, filtered to those on the current page.
4311
+ *
4312
+ * Convenience wrapper for {@link CollaboratorsManager.getVisibleCollaboratorsOnCurrentPage}.
4313
+ *
4314
+ * @public
4315
+ */
4316
+ getVisibleCollaboratorsOnCurrentPage() {
4317
+ return this.collaborators.getVisibleCollaboratorsOnCurrentPage()
4262
4318
  }
4263
4319
 
4264
4320
  // Attribution
@@ -5112,7 +5168,7 @@ export class Editor extends EventEmitter<TLEventMap> {
5112
5168
  const zoomStepFunction = (zoom: number) => Math.pow(2, Math.ceil(Math.log2(zoom)))
5113
5169
  const steppedScreenScale = zoomStepFunction(screenScale)
5114
5170
  const networkEffectiveType: string | null =
5115
- 'connection' in navigator ? (navigator as any).connection.effectiveType : null
5171
+ 'connection' in navigator ? ((navigator as any).connection?.effectiveType ?? null) : null
5116
5172
 
5117
5173
  return await this.store.props.assets.resolve(asset, {
5118
5174
  screenScale: screenScale || 1,
@@ -5703,8 +5759,8 @@ export class Editor extends EventEmitter<TLEventMap> {
5703
5759
  ? this.getCurrentPageRenderingShapesSorted()
5704
5760
  : this.getCurrentPageShapesSorted()
5705
5761
  ).filter((shape) => {
5706
- // Frames have labels positioned above the shape (outside bounds), so always include them
5707
- if (!candidateIds.has(shape.id) && !this.isShapeOfType(shape, 'frame')) return false
5762
+ // Frame-like shapes have labels positioned above the shape (outside bounds), so always include them
5763
+ if (!candidateIds.has(shape.id) && !this.isShapeFrameLike(shape)) return false
5708
5764
 
5709
5765
  if (
5710
5766
  (shape.isLocked && !hitLocked) ||
@@ -5726,12 +5782,14 @@ export class Editor extends EventEmitter<TLEventMap> {
5726
5782
  const pointInShapeSpace = this.getPointInShapeSpace(shape, point)
5727
5783
 
5728
5784
  // Check labels first
5785
+ const shapeUtil = this.getShapeUtil(shape)
5786
+ const isShapeFrameLike = this.isShapeFrameLike(shape)
5729
5787
  if (
5730
- this.isShapeOfType(shape, 'frame') ||
5788
+ isShapeFrameLike ||
5731
5789
  ((this.isShapeOfType(shape, 'note') ||
5732
5790
  this.isShapeOfType(shape, 'arrow') ||
5733
5791
  (this.isShapeOfType(shape, 'geo') && shape.props.fill === 'none')) &&
5734
- this.getShapeUtil(shape).getText(shape)?.trim())
5792
+ shapeUtil.getText(shape)?.trim())
5735
5793
  ) {
5736
5794
  for (const childGeometry of (geometry as Group2d).children) {
5737
5795
  if (childGeometry.isLabel && childGeometry.isPointInBounds(pointInShapeSpace)) {
@@ -5740,8 +5798,8 @@ export class Editor extends EventEmitter<TLEventMap> {
5740
5798
  }
5741
5799
  }
5742
5800
 
5743
- if (this.isShapeOfType(shape, 'frame')) {
5744
- // On the rare case that we've hit a frame (not its label), test again hitInside to be forced true;
5801
+ if (isShapeFrameLike) {
5802
+ // On the rare case that we've hit a frame-like shape (not its label), test again hitInside to be forced true;
5745
5803
  // this prevents clicks from passing through the body of a frame to shapes behind it.
5746
5804
 
5747
5805
  // If the hit is within the frame's outer margin, then select the frame
@@ -5900,11 +5958,11 @@ export class Editor extends EventEmitter<TLEventMap> {
5900
5958
  const candidateIds = this._spatialIndex.getShapeIdsAtPoint(point, margin)
5901
5959
 
5902
5960
  // Get all page shapes in z-index order and filter to candidates that pass isPointInShape
5903
- // Frames are always checked because their labels can be outside their bounds
5961
+ // Frame-like shapes are always checked because their labels can be outside their bounds
5904
5962
  return this.getCurrentPageShapesSorted()
5905
5963
  .filter((shape) => {
5906
5964
  if (this.isShapeHidden(shape)) return false
5907
- if (!candidateIds.has(shape.id) && !this.isShapeOfType(shape, 'frame')) return false
5965
+ if (!candidateIds.has(shape.id) && !this.isShapeFrameLike(shape)) return false
5908
5966
  return this.isPointInShape(shape, point, opts)
5909
5967
  })
5910
5968
  .reverse()
@@ -6079,6 +6137,25 @@ export class Editor extends EventEmitter<TLEventMap> {
6079
6137
  return shape.type === type
6080
6138
  }
6081
6139
 
6140
+ /**
6141
+ * Get whether a shape behaves like a frame — a container that has child
6142
+ * shapes, requires full-brush selection, blocks erasure from inside, etc.
6143
+ *
6144
+ * @example
6145
+ * ```ts
6146
+ * const isFrameLike = editor.isShapeFrameLike(someShape)
6147
+ * ```
6148
+ *
6149
+ * @param shape - The shape (or shape id) to test.
6150
+ *
6151
+ * @public
6152
+ */
6153
+ isShapeFrameLike(shape: TLShape | TLShapeId): boolean {
6154
+ const _shape = typeof shape === 'string' ? this.getShape(shape) : shape
6155
+ if (!_shape) return false
6156
+ return this.getShapeUtil(_shape).isFrameLike(_shape)
6157
+ }
6158
+
6082
6159
  /**
6083
6160
  * Get a shape by its id.
6084
6161
  *
@@ -11036,6 +11113,10 @@ export class Editor extends EventEmitter<TLEventMap> {
11036
11113
  }
11037
11114
  this.inputs.setIsPanning(true)
11038
11115
  clearTimeout(this._longPressTimeout)
11116
+ } else if (info.button === RIGHT_MOUSE_BUTTON && this.options.rightClickPanning) {
11117
+ this.inputs.setIsRightPointing(true)
11118
+ clearTimeout(this._longPressTimeout)
11119
+ return this
11039
11120
  }
11040
11121
 
11041
11122
  // We might be panning because we did a middle mouse click, or because we're holding spacebar and started a regular click
@@ -11054,9 +11135,31 @@ export class Editor extends EventEmitter<TLEventMap> {
11054
11135
 
11055
11136
  const { x: cx, y: cy, z: cz } = unsafe__withoutCapture(() => this.getCamera())
11056
11137
 
11138
+ // Right-click pointing: waiting to see if this becomes a drag
11139
+ if (this.inputs.getIsRightPointing() && !this.inputs.getIsPanning()) {
11140
+ const currentScreenPoint = this.inputs.getCurrentScreenPoint()
11141
+ const originScreenPoint = this.inputs.getOriginScreenPoint()
11142
+ if (
11143
+ Vec.Dist2(originScreenPoint, currentScreenPoint) > this.options.dragDistanceSquared
11144
+ ) {
11145
+ // Passed the drag threshold—transition to panning
11146
+ this._prevCursor = this.getInstanceState().cursor.type
11147
+ this.inputs.setIsPanning(true)
11148
+ this.setCursor({ type: 'grabbing', rotation: 0 })
11149
+ this.stopCameraAnimation()
11150
+ // Apply the initial delta from the pointer down origin
11151
+ const offset = Vec.Sub(currentScreenPoint, originScreenPoint)
11152
+ this.setCamera(new Vec(cx + offset.x / cz, cy + offset.y / cz, cz), {
11153
+ immediate: true,
11154
+ })
11155
+ this.maybeTrackPerformance('Panning')
11156
+ }
11157
+ return
11158
+ }
11159
+
11057
11160
  // If we've started panning, then clear any long press timeout
11058
11161
  if (this.inputs.getIsPanning() && this.inputs.getIsPointing()) {
11059
- // Handle spacebar / middle mouse button panning
11162
+ // Handle spacebar / middle mouse button / right-click panning
11060
11163
  const currentScreenPoint = this.inputs.getCurrentScreenPoint()
11061
11164
  const previousScreenPoint = this.inputs.getPreviousScreenPoint()
11062
11165
  const offset = Vec.Sub(currentScreenPoint, previousScreenPoint)
@@ -11089,13 +11192,24 @@ export class Editor extends EventEmitter<TLEventMap> {
11089
11192
  inputs.setIsDragging(false)
11090
11193
  inputs.setIsPointing(false)
11091
11194
  clearTimeout(this._longPressTimeout)
11092
-
11093
11195
  // Remove the button from the buttons set
11094
11196
  inputs.buttons.delete(info.button)
11095
11197
 
11096
11198
  // If we're in pen mode and we're not using a pen, stop here
11097
11199
  if (instanceState.isPenMode && !isPen) return
11098
11200
 
11201
+ // Right-click pointing ended without dragging—this is a static
11202
+ // right-click, so let it through to the state chart as right_click.
11203
+ // Check isPanning first: if we transitioned to panning, isRightPointing
11204
+ // is still true but we want the panning cleanup path instead.
11205
+ if (this.inputs.getIsRightPointing() && !this.inputs.getIsPanning()) {
11206
+ this.inputs.setIsRightPointing(false)
11207
+ this._selectedShapeIdsAtPointerDown = []
11208
+ break // fall through to state chart dispatch as right_click
11209
+ }
11210
+
11211
+ this.inputs.setIsRightPointing(false)
11212
+
11099
11213
  // Firefox bug fix...
11100
11214
  // If it's the same pointer that we stored earlier...
11101
11215
  // ... then it's probably still a left-mouse-click!
@@ -11118,11 +11232,26 @@ export class Editor extends EventEmitter<TLEventMap> {
11118
11232
  break
11119
11233
  }
11120
11234
  case MIDDLE_MOUSE_BUTTON: {
11121
- if (this.inputs.keys.has(' ')) {
11235
+ if (this.inputs.keys.has('Space')) {
11236
+ this.setCursor({ type: 'grab', rotation: 0 })
11237
+ } else {
11238
+ this.setCursor({ type: this._prevCursor, rotation: 0 })
11239
+ }
11240
+ break
11241
+ }
11242
+ case RIGHT_MOUSE_BUTTON: {
11243
+ if (this.inputs.keys.has('Space')) {
11122
11244
  this.setCursor({ type: 'grab', rotation: 0 })
11123
11245
  } else {
11124
11246
  this.setCursor({ type: this._prevCursor, rotation: 0 })
11125
11247
  }
11248
+ // Don't pass right-click panning events to the state chart
11249
+ // as it causes unintended shape selection on release
11250
+ if (slideSpeed > 0) {
11251
+ this.slideCamera({ speed: slideSpeed, direction: slideDirection })
11252
+ }
11253
+ this._selectedShapeIdsAtPointerDown = []
11254
+ return this
11126
11255
  }
11127
11256
  }
11128
11257
 
@@ -0,0 +1,98 @@
1
+ import { EMPTY_ARRAY, atom, computed } from '@tldraw/state'
2
+ import { TLInstancePresence } from '@tldraw/tlschema'
3
+ import { maxBy } from '@tldraw/utils'
4
+ import {
5
+ getCollaboratorStateFromElapsedTime,
6
+ shouldShowCollaborator,
7
+ } from '../../../utils/collaboratorState'
8
+ import type { Editor } from '../../Editor'
9
+
10
+ /**
11
+ * Tracks remote peers and exposes the collaborator-related queries used by the
12
+ * editor and its overlays. Encapsulates the visibility clock that periodically
13
+ * re-evaluates which collaborators should be visible based on activity.
14
+ *
15
+ * Accessed via {@link Editor.collaborators}.
16
+ *
17
+ * @public
18
+ */
19
+ export class CollaboratorsManager {
20
+ constructor(private readonly editor: Editor) {
21
+ // Editor disposes `editor.timers` on its own teardown, so the interval is
22
+ // automatically cleared when the editor is disposed.
23
+ editor.timers.setInterval(() => {
24
+ this._visibilityClock.set(Date.now())
25
+ }, editor.options.collaboratorCheckIntervalMs)
26
+ }
27
+
28
+ /**
29
+ * Drives reactive re-evaluation of {@link CollaboratorsManager.getVisibleCollaborators}.
30
+ * Ticked on a fixed interval so callers don't need to manage their own activity timers.
31
+ */
32
+ private readonly _visibilityClock = atom('collaboratorVisibilityClock', Date.now())
33
+
34
+ @computed
35
+ private _getCollaboratorsQuery() {
36
+ return this.editor.store.query.records('instance_presence', () => ({
37
+ userId: { neq: this.editor.user.getId() },
38
+ }))
39
+ }
40
+
41
+ /**
42
+ * Returns a list of presence records for all peer collaborators.
43
+ * This will return the latest presence record for each connected user.
44
+ */
45
+ @computed
46
+ getCollaborators(): TLInstancePresence[] {
47
+ const allPresenceRecords = this._getCollaboratorsQuery().get()
48
+ if (!allPresenceRecords.length) return EMPTY_ARRAY
49
+ const userIds = [...new Set(allPresenceRecords.map((c) => c.userId))].sort()
50
+ return userIds.map((id) => {
51
+ const latestPresence = maxBy(
52
+ allPresenceRecords.filter((c) => c.userId === id),
53
+ (p) => p.lastActivityTimestamp ?? 0
54
+ )
55
+ return latestPresence!
56
+ })
57
+ }
58
+
59
+ /**
60
+ * Returns a list of presence records for all peer collaborators on the current page.
61
+ * This will return the latest presence record for each connected user.
62
+ */
63
+ @computed
64
+ getCollaboratorsOnCurrentPage(): TLInstancePresence[] {
65
+ const currentPageId = this.editor.getCurrentPageId()
66
+ return this.getCollaborators().filter((c) => c.currentPageId === currentPageId)
67
+ }
68
+
69
+ /**
70
+ * Returns a list of presence records for peer collaborators who should currently be
71
+ * shown in the UI. Filters {@link CollaboratorsManager.getCollaborators} by activity
72
+ * state (active / idle / inactive) and visibility rules such as following and
73
+ * highlighted users. Re-evaluates on the visibility clock, so callers don't need to
74
+ * drive their own activity timer.
75
+ */
76
+ @computed
77
+ getVisibleCollaborators(): TLInstancePresence[] {
78
+ this._visibilityClock.get()
79
+ const now = Date.now()
80
+ return this.getCollaborators().filter((presence) => {
81
+ // Treat a missing `lastActivityTimestamp` as "active right now" (elapsed = 0)
82
+ // so newly-joined peers aren't immediately classified as idle/inactive.
83
+ const elapsed = Math.max(0, now - (presence.lastActivityTimestamp ?? now))
84
+ const state = getCollaboratorStateFromElapsedTime(this.editor, elapsed)
85
+ return shouldShowCollaborator(this.editor, presence, state)
86
+ })
87
+ }
88
+
89
+ /**
90
+ * Returns a list of presence records for peer collaborators who should currently be
91
+ * shown in the UI, filtered to those on the current page.
92
+ */
93
+ @computed
94
+ getVisibleCollaboratorsOnCurrentPage(): TLInstancePresence[] {
95
+ const currentPageId = this.editor.getCurrentPageId()
96
+ return this.getVisibleCollaborators().filter((c) => c.currentPageId === currentPageId)
97
+ }
98
+ }
@@ -331,6 +331,18 @@ export class InputsManager {
331
331
  this._isPointing.set(isPointing)
332
332
  }
333
333
 
334
+ private _isRightPointing = atom<boolean>('isRightPointing', false)
335
+ /**
336
+ * Whether the user is right-click pointing (before drag threshold).
337
+ */
338
+ getIsRightPointing() {
339
+ return this._isRightPointing.get()
340
+ }
341
+ /** @internal */
342
+ setIsRightPointing(isRightPointing: boolean) {
343
+ this._isRightPointing.set(isRightPointing)
344
+ }
345
+
334
346
  private _isPinching = atom<boolean>('isPinching', false)
335
347
  /**
336
348
  * Whether the user is pinching.
@@ -77,8 +77,13 @@ describe('SnapManager', () => {
77
77
  })),
78
78
  getShapePageBounds: vi.fn(),
79
79
  isShapeOfType: vi.fn(),
80
+ isShapeFrameLike: vi.fn(() => false),
80
81
  } as any
81
82
 
83
+ editor.getShapeUtil.mockReturnValue({
84
+ canSnap: vi.fn(() => true),
85
+ } as any)
86
+
82
87
  snapManager = new SnapManager(editor)
83
88
  })
84
89
 
@@ -351,7 +356,10 @@ describe('SnapManager', () => {
351
356
 
352
357
  editor.getSortedChildIdsForParent.mockReturnValue([frameId])
353
358
  editor.getShape.mockReturnValue(frameShape)
354
- editor.isShapeOfType.mockImplementation((_shape, type) => type === 'frame')
359
+ editor.getShapeUtil.mockReturnValue({
360
+ canSnap: vi.fn(() => true),
361
+ } as any)
362
+ editor.isShapeFrameLike.mockReturnValue(true)
355
363
  editor.getShapePageBounds.mockReturnValue(new Box(10, 10, 50, 50))
356
364
 
357
365
  const result = snapManager.getSnappableShapes()
@@ -401,7 +409,10 @@ describe('SnapManager', () => {
401
409
  return undefined
402
410
  })
403
411
 
404
- editor.isShapeOfType.mockImplementation((shape: any, type: any) => type === 'frame')
412
+ editor.getShapeUtil.mockReturnValue({
413
+ canSnap: vi.fn(() => true),
414
+ } as any)
415
+ editor.isShapeFrameLike.mockReturnValue(true)
405
416
  editor.getShapePageBounds.mockReturnValue(new Box(10, 10, 50, 50))
406
417
 
407
418
  const result = snapManager.getSnappableShapes()
@@ -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(parent, 'frame')) {
75
+ if (parent && editor.isShapeFrameLike(parent)) {
76
76
  snappableShapes.add(parentId)
77
77
  }
78
78
  }
@@ -136,6 +136,13 @@ export const DEFAULT_THEME: TLTheme = {
136
136
  solid: '#fcfffe',
137
137
  cursor: 'black',
138
138
  noteBorder: 'rgb(144, 144, 144)',
139
+ snap: 'hsl(0, 76%, 60%)',
140
+ selectionStroke: 'hsl(214, 84%, 56%)',
141
+ selectionFill: 'hsl(210, 100%, 56%, 24%)',
142
+ brushFill: 'hsl(0, 0%, 56%, 10.2%)',
143
+ brushStroke: 'hsl(0, 0%, 56%, 25.1%)',
144
+ selectedContrast: '#ffffff',
145
+ laser: 'hsl(0, 100%, 50%)',
139
146
  black: {
140
147
  solid: '#1d1d1d',
141
148
  fill: '#1d1d1d',
@@ -351,6 +358,13 @@ export const DEFAULT_THEME: TLTheme = {
351
358
  negativeSpace: 'hsl(240, 5%, 6.5%)',
352
359
  solid: '#010403',
353
360
  cursor: 'white',
361
+ snap: 'hsl(0, 76%, 60%)',
362
+ selectionStroke: 'hsl(214, 84%, 56%)',
363
+ selectionFill: 'hsl(209, 100%, 57%, 20%)',
364
+ brushFill: 'hsl(0, 0%, 56%, 10.2%)',
365
+ brushStroke: 'hsl(0, 0%, 56%, 25.1%)',
366
+ selectedContrast: '#ffffff',
367
+ laser: 'hsl(0, 100%, 50%)',
354
368
  noteBorder: 'rgb(20, 20, 20)',
355
369
 
356
370
  black: {