@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
@@ -0,0 +1,128 @@
1
+ import { TLShape, TLShapeId } from '@tldraw/tlschema'
2
+ import { IndexKey, compact } from '@tldraw/utils'
3
+ import { Vec } from '../../primitives/Vec'
4
+ import { BaseBoxShapeUtil, TLBaseBoxShape } from './BaseBoxShapeUtil'
5
+ import { TLDragShapesInInfo, TLDragShapesOutInfo } from './ShapeUtil'
6
+
7
+ /**
8
+ * A base class for frame-like shapes — containers that clip their children,
9
+ * require full-brush selection, block erasure from inside, and support
10
+ * drag-and-drop reparenting.
11
+ *
12
+ * Extending this class is the easiest way to create a custom frame-like shape.
13
+ * It provides sensible defaults for all frame-like behaviors:
14
+ *
15
+ * - `isFrameLike()` returns `true`
16
+ * - `providesBackgroundForChildren()` returns `true`
17
+ * - `canReceiveNewChildrenOfType()` returns `true` unless the container is locked
18
+ * - `canRemoveChildrenOfType()` returns `true` unless the container is locked
19
+ * - `getClipPath()` returns the shape geometry's vertices
20
+ * - `onDragShapesIn()` reparents shapes into the frame (with index restoration)
21
+ * - `onDragShapesOut()` reparents shapes back to the page
22
+ *
23
+ * All methods can be overridden for custom behavior.
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * class MyContainerUtil extends BaseFrameLikeShapeUtil<MyContainerShape> {
28
+ * static override type = 'my-container' as const
29
+ * static override props = myContainerShapeProps
30
+ *
31
+ * override getDefaultProps() {
32
+ * return { w: 300, h: 200 }
33
+ * }
34
+ *
35
+ * override component(shape: MyContainerShape) {
36
+ * return <SVGContainer>...</SVGContainer>
37
+ * }
38
+ *
39
+ * override getIndicatorPath(shape: MyContainerShape) {
40
+ * const path = new Path2D()
41
+ * path.rect(0, 0, shape.props.w, shape.props.h)
42
+ * return path
43
+ * }
44
+ * }
45
+ * ```
46
+ *
47
+ * @public
48
+ */
49
+ export abstract class BaseFrameLikeShapeUtil<
50
+ Shape extends TLBaseBoxShape,
51
+ > extends BaseBoxShapeUtil<Shape> {
52
+ override isFrameLike(_shape: Shape): boolean {
53
+ return true
54
+ }
55
+
56
+ override providesBackgroundForChildren(): boolean {
57
+ return true
58
+ }
59
+
60
+ override canReceiveNewChildrenOfType(shape: Shape, _type: TLShape['type']): boolean {
61
+ return !shape.isLocked
62
+ }
63
+
64
+ override canRemoveChildrenOfType(shape: Shape, _type: TLShape['type']): boolean {
65
+ return !shape.isLocked
66
+ }
67
+
68
+ override getClipPath(shape: Shape): Vec[] | undefined {
69
+ return this.editor.getShapeGeometry(shape.id).vertices
70
+ }
71
+
72
+ override onDragShapesIn(
73
+ shape: Shape,
74
+ draggingShapes: TLShape[],
75
+ { initialParentIds, initialIndices }: TLDragShapesInInfo
76
+ ): void {
77
+ const { editor } = this
78
+
79
+ if (draggingShapes.every((s) => s.parentId === shape.id)) return
80
+
81
+ // Check to see whether any of the shapes can have their old index restored
82
+ let canRestoreOriginalIndices = false
83
+ const previousChildren = draggingShapes.filter(
84
+ (s) => shape.id === (initialParentIds.get(s.id) as TLShapeId)
85
+ )
86
+
87
+ if (previousChildren.length > 0) {
88
+ const currentChildren = compact(
89
+ editor.getSortedChildIdsForParent(shape).map((id) => editor.getShape(id))
90
+ )
91
+ if (previousChildren.every((s) => !currentChildren.find((c) => c.index === s.index))) {
92
+ canRestoreOriginalIndices = true
93
+ }
94
+ }
95
+
96
+ // If any of the children are the ancestor of the frame, quit here
97
+ if (draggingShapes.some((s) => editor.hasAncestor(shape, s.id))) return
98
+
99
+ editor.reparentShapes(draggingShapes, shape.id)
100
+
101
+ if (canRestoreOriginalIndices) {
102
+ for (const s of previousChildren) {
103
+ editor.updateShape({
104
+ id: s.id,
105
+ type: s.type,
106
+ index: initialIndices.get(s.id) as IndexKey,
107
+ })
108
+ }
109
+ }
110
+ }
111
+
112
+ override onDragShapesOut(
113
+ shape: Shape,
114
+ draggingShapes: TLShape[],
115
+ info: TLDragShapesOutInfo
116
+ ): void {
117
+ const { editor } = this
118
+ // When a user drags shapes out and we're not dragging into a new shape,
119
+ // reparent the dragging shapes onto the current page instead
120
+ if (!info.nextDraggingOverShapeId) {
121
+ // Locked shapes are already filtered out upstream by DragAndDropManager.
122
+ editor.reparentShapes(
123
+ draggingShapes.filter((s) => s.parentId === shape.id),
124
+ editor.getCurrentPageId()
125
+ )
126
+ }
127
+ }
128
+ }
@@ -218,42 +218,32 @@ export abstract class ShapeUtil<Shape extends TLShape = TLShape> {
218
218
  abstract component(shape: Shape): any
219
219
 
220
220
  /**
221
- * Get JSX describing the shape's indicator (as an SVG element).
221
+ * Get a Path2D (or a richer object with clip/additional paths) for rendering the
222
+ * shape's indicator on the canvas. Shapes that return `undefined` will not render
223
+ * an indicator.
222
224
  *
223
- * @param shape - The shape.
224
- * @public
225
- */
226
- abstract indicator(shape: Shape): any
227
-
228
- /**
229
- * Whether to use the legacy React-based indicator rendering.
230
- *
231
- * Override this to return `false` if your shape implements {@link ShapeUtil.getIndicatorPath}
232
- * for canvas-based indicator rendering.
225
+ * For complex indicators that need clipping (e.g., arrows with labels), return an
226
+ * object with `path`, `clipPath`, and `additionalPaths` properties.
233
227
  *
234
- * @returns `true` to use SVG indicators (default), `false` to use canvas indicators.
228
+ * @param shape - The shape.
229
+ * @returns A Path2D to stroke, or an object with clipping info, or undefined to skip.
235
230
  * @public
236
231
  */
237
- useLegacyIndicator(): boolean {
238
- return true
239
- }
232
+ abstract getIndicatorPath(shape: Shape): TLIndicatorPath | undefined
240
233
 
241
234
  /**
242
- * Get a Path2D for rendering the shape's indicator on the canvas.
243
- *
244
- * When implemented, this is used instead of {@link ShapeUtil.indicator} for more
245
- * efficient canvas-based indicator rendering. Shapes that return `undefined` will
246
- * fall back to SVG-based rendering via {@link ShapeUtil.indicator}.
235
+ * Get JSX describing the shape's indicator (as an SVG element).
247
236
  *
248
- * For complex indicators that need clipping (e.g., arrows with labels), return an
249
- * object with `path`, `clipPath`, and `additionalPaths` properties.
237
+ * @deprecated SVG indicators are no longer rendered. Override
238
+ * {@link ShapeUtil.getIndicatorPath} instead. This stub is retained so legacy
239
+ * subclasses that still call `super.indicator()` keep type-checking; new shapes
240
+ * should not implement it.
250
241
  *
251
242
  * @param shape - The shape.
252
- * @returns A Path2D to stroke, or an object with clipping info, or undefined to use SVG fallback.
253
243
  * @public
254
244
  */
255
- getIndicatorPath(shape: Shape): TLIndicatorPath | undefined {
256
- return undefined
245
+ indicator(_shape: Shape): any {
246
+ return null
257
247
  }
258
248
 
259
249
  /**
@@ -492,6 +482,17 @@ export abstract class ShapeUtil<Shape extends TLShape = TLShape> {
492
482
  return false
493
483
  }
494
484
 
485
+ /**
486
+ * Whether the shape behaves like a frame — a container that has child shapes,
487
+ * requires full-brush selection, blocks erasure from inside, etc.
488
+ *
489
+ * @param shape - The shape.
490
+ * @public
491
+ */
492
+ isFrameLike(_shape: Shape): boolean {
493
+ return false
494
+ }
495
+
495
496
  /**
496
497
  * By default, the bounds of an image export are the bounds of all the shapes it contains, plus
497
498
  * some padding. If an export includes a shape where `isExportBoundsContainer` is true, then the
@@ -545,7 +546,9 @@ export abstract class ShapeUtil<Shape extends TLShape = TLShape> {
545
546
  getHandles?(shape: Shape): TLHandle[]
546
547
 
547
548
  /**
548
- * Get whether the shape can receive children of a given type.
549
+ * Get whether the shape can receive children of a given type. Used by the drag and drop system
550
+ * to decide whether {@link ShapeUtil.onDragShapesIn} should fire when a shape of the given type
551
+ * is dragged over this one.
549
552
  *
550
553
  * @param shape - The shape.
551
554
  * @param type - The shape type.
@@ -555,6 +558,22 @@ export abstract class ShapeUtil<Shape extends TLShape = TLShape> {
555
558
  return false
556
559
  }
557
560
 
561
+ /**
562
+ * Get whether children of a given type can be removed from this shape. Used by the drag and
563
+ * drop system to decide whether {@link ShapeUtil.onDragShapesOut} should fire when a child of
564
+ * the given type is dragged out of this shape, and by `kickoutOccludedShapes` to decide
565
+ * whether to auto-reparent a child of the given type when it has moved outside this shape's
566
+ * geometry. Returning `false` therefore "pins" matching children — they stay parented to this
567
+ * shape even when dragged or moved outside it. Defaults to `true`.
568
+ *
569
+ * @param shape - The shape.
570
+ * @param type - The shape type.
571
+ * @public
572
+ */
573
+ canRemoveChildrenOfType(shape: Shape, type: TLShape['type']) {
574
+ return true
575
+ }
576
+
558
577
  /**
559
578
  * Get the shape as an SVG object.
560
579
  *
@@ -4,6 +4,7 @@ import { Geometry2d } from '../../../primitives/geometry/Geometry2d'
4
4
  import { Group2d } from '../../../primitives/geometry/Group2d'
5
5
  import { Rectangle2d } from '../../../primitives/geometry/Rectangle2d'
6
6
  import { ShapeUtil } from '../ShapeUtil'
7
+ import { getPerfectDashProps } from '../shared/getPerfectDashProps'
7
8
  import { DashedOutlineBox } from './DashedOutlineBox'
8
9
 
9
10
  /** @public */
@@ -78,10 +79,46 @@ export class GroupShapeUtil extends ShapeUtil<TLGroupShape> {
78
79
  )
79
80
  }
80
81
 
81
- indicator(shape: TLGroupShape) {
82
- // Not a class component, but eslint can't tell that :(
82
+ override getIndicatorPath(shape: TLGroupShape): Path2D {
83
83
  const bounds = this.editor.getShapeGeometry(shape).bounds
84
- return <DashedOutlineBox className="" bounds={bounds} />
84
+ const zoomLevel = this.editor.getEfficientZoomLevel()
85
+ const path = new Path2D()
86
+
87
+ for (const side of bounds.sides) {
88
+ const [start, end] = side
89
+ const length = start.dist(end)
90
+ if (length <= 0) continue
91
+
92
+ const { strokeDasharray, strokeDashoffset } = getPerfectDashProps(length, 1 / zoomLevel, {
93
+ style: 'dashed',
94
+ lengthRatio: 4,
95
+ })
96
+
97
+ if (strokeDasharray === 'none') {
98
+ path.moveTo(start.x, start.y)
99
+ path.lineTo(end.x, end.y)
100
+ continue
101
+ }
102
+
103
+ const [dashLength, gapLength] = strokeDasharray.split(' ').map(Number)
104
+ const dashOffset = Number(strokeDashoffset)
105
+ const period = dashLength + gapLength
106
+ if (!Number.isFinite(period) || period <= 0) continue
107
+
108
+ const dx = (end.x - start.x) / length
109
+ const dy = (end.y - start.y) / length
110
+
111
+ for (let dashStart = -dashOffset; dashStart < length; dashStart += period) {
112
+ const dashEnd = Math.min(length, dashStart + dashLength)
113
+ const clippedDashStart = Math.max(0, dashStart)
114
+ if (dashEnd <= clippedDashStart) continue
115
+
116
+ path.moveTo(start.x + dx * clippedDashStart, start.y + dy * clippedDashStart)
117
+ path.lineTo(start.x + dx * dashEnd, start.y + dy * dashEnd)
118
+ }
119
+ }
120
+
121
+ return path
85
122
  }
86
123
 
87
124
  override onChildrenChange(group: TLGroupShape) {
@@ -1,5 +1,6 @@
1
1
  import { TLHandle, TLShape, VecModel } from '@tldraw/tlschema'
2
2
  import { VecLike } from '../../primitives/Vec'
3
+ import { TLOverlay } from '../overlays/OverlayUtil'
3
4
  import { TLSelectionHandle } from './selection-types'
4
5
 
5
6
  /** @public */
@@ -11,6 +12,7 @@ export type TLPointerEventTarget =
11
12
  | { target: 'selection'; handle?: TLSelectionHandle; shape?: undefined }
12
13
  | { target: 'shape'; shape: TLShape }
13
14
  | { target: 'handle'; shape: TLShape; handle: TLHandle }
15
+ | { target: 'overlay'; overlay: TLOverlay; shape?: undefined }
14
16
 
15
17
  /** @public */
16
18
  export type TLPointerEventName =
@@ -1,9 +1,7 @@
1
- import { FileHelpers, assert, fetch } from '@tldraw/utils'
1
+ import { FileHelpers, LruCache, assert, fetch } from '@tldraw/utils'
2
2
 
3
- // TODO(alex): currently, this cache will grow unbounded. we should come up with a better strategy
4
- // for clearing items from the cache over time.
5
3
  export function fetchCache<T>(cb: (response: Response) => Promise<T>, init?: RequestInit) {
6
- const cache = new Map<string, Promise<T | null>>()
4
+ const cache = new LruCache<string, Promise<T | null>>(100)
7
5
 
8
6
  return async function fetchCached(url: string): Promise<T | null> {
9
7
  const existing = cache.get(url)
@@ -46,7 +46,9 @@ class TestShape extends ShapeUtil<ITestShape> {
46
46
  return shape.props.isContainer ?? false
47
47
  }
48
48
 
49
- indicator() {}
49
+ getIndicatorPath() {
50
+ return undefined
51
+ }
50
52
  component() {}
51
53
  }
52
54
 
@@ -60,8 +60,9 @@ export function getSvgJsx(editor: Editor, ids: TLShapeId[], opts: TLImageExportO
60
60
  .filter(({ id }) => shapeIdsToInclude.has(id))
61
61
 
62
62
  // --- Common bounding box of all shapes
63
+ const singleFrameShape = ids.length === 1 ? editor.getShape(ids[0]) : null
63
64
  const singleFrameShapeId =
64
- ids.length === 1 && editor.isShapeOfType(editor.getShape(ids[0])!, 'frame') ? ids[0] : null
65
+ singleFrameShape && editor.isShapeFrameLike(singleFrameShape) ? ids[0] : null
65
66
 
66
67
  let bbox: null | Box = null
67
68
  let paddingWasApplied = false
@@ -1,54 +1,27 @@
1
1
  import { ComponentType, RefAttributes, createContext, useContext } from 'react'
2
- import type { TLBrushProps } from '../components/default-components/DefaultBrush'
3
2
  import type { TLCanvasComponentProps } from '../components/default-components/DefaultCanvas'
4
- import type { TLCollaboratorHintProps } from '../components/default-components/DefaultCollaboratorHint'
5
- import type { TLCursorProps } from '../components/default-components/DefaultCursor'
6
3
  import type { TLErrorFallbackComponent } from '../components/default-components/DefaultErrorFallback'
7
4
  import type { TLGridProps } from '../components/default-components/DefaultGrid'
8
- import type { TLHandleProps } from '../components/default-components/DefaultHandle'
9
- import type { TLHandlesProps } from '../components/default-components/DefaultHandles'
10
- import type { TLScribbleProps } from '../components/default-components/DefaultScribble'
11
5
  import type { TLSelectionBackgroundProps } from '../components/default-components/DefaultSelectionBackground'
12
- import type { TLSelectionForegroundProps } from '../components/default-components/DefaultSelectionForeground'
13
6
  import type { TLShapeErrorFallbackComponent } from '../components/default-components/DefaultShapeErrorFallback'
14
- import type { TLShapeIndicatorProps } from '../components/default-components/DefaultShapeIndicator'
15
- import type { TLShapeIndicatorErrorFallbackComponent } from '../components/default-components/DefaultShapeIndicatorErrorFallback'
16
7
  import type { TLShapeWrapperProps } from '../components/default-components/DefaultShapeWrapper'
17
- import type { TLSnapIndicatorProps } from '../components/default-components/DefaultSnapIndictor'
18
8
 
19
9
  /** @public */
20
10
  export interface TLEditorComponents {
21
11
  Background?: ComponentType | null
22
- Brush?: ComponentType<TLBrushProps> | null
23
12
  Canvas?: ComponentType<TLCanvasComponentProps> | null
24
- CollaboratorBrush?: ComponentType<TLBrushProps> | null
25
- CollaboratorCursor?: ComponentType<TLCursorProps> | null
26
- CollaboratorHint?: ComponentType<TLCollaboratorHintProps> | null
27
- CollaboratorScribble?: ComponentType<TLScribbleProps> | null
28
- CollaboratorShapeIndicator?: ComponentType<TLShapeIndicatorProps> | null
29
- Cursor?: ComponentType<TLCursorProps> | null
30
13
  Grid?: ComponentType<TLGridProps> | null
31
- Handle?: ComponentType<TLHandleProps> | null
32
- Handles?: ComponentType<TLHandlesProps> | null
33
14
  InFrontOfTheCanvas?: ComponentType | null
34
15
  LoadingScreen?: ComponentType | null
35
16
  OnTheCanvas?: ComponentType | null
36
- Overlays?: ComponentType | null
37
- Scribble?: ComponentType<TLScribbleProps> | null
38
17
  SelectionBackground?: ComponentType<TLSelectionBackgroundProps> | null
39
- SelectionForeground?: ComponentType<TLSelectionForegroundProps> | null
40
- ShapeIndicator?: ComponentType<TLShapeIndicatorProps> | null
41
- ShapeIndicators?: ComponentType | null
42
18
  ShapeWrapper?: ComponentType<TLShapeWrapperProps & RefAttributes<HTMLDivElement>> | null
43
- SnapIndicator?: ComponentType<TLSnapIndicatorProps> | null
44
19
  Spinner?: ComponentType<React.SVGProps<SVGSVGElement>> | null
45
20
  SvgDefs?: ComponentType | null
46
- ZoomBrush?: ComponentType<TLBrushProps> | null
47
21
 
48
22
  // These will always have defaults
49
23
  ErrorFallback?: TLErrorFallbackComponent
50
24
  ShapeErrorFallback?: TLShapeErrorFallbackComponent
51
- ShapeIndicatorErrorFallback?: TLShapeIndicatorErrorFallbackComponent
52
25
  }
53
26
 
54
27
  export const EditorComponentsContext = createContext<null | Required<TLEditorComponents>>(null)
@@ -1,6 +1,5 @@
1
1
  import { useValue } from '@tldraw/state-react'
2
2
  import React, { useEffect, useMemo } from 'react'
3
- import { RIGHT_MOUSE_BUTTON } from '../constants'
4
3
  import { tlenv } from '../globals/environment'
5
4
  import {
6
5
  elementShouldCaptureKeys,
@@ -21,7 +20,9 @@ export function useCanvasEvents() {
21
20
  function onPointerDown(e: React.PointerEvent) {
22
21
  if (editor.wasEventAlreadyHandled(e)) return
23
22
 
24
- if (e.button === RIGHT_MOUSE_BUTTON) {
23
+ // With right-click panning disabled, fire right_click on press and let the
24
+ // native contextmenu through so the menu opens at the pointer-down location.
25
+ if (e.button === 2 && !editor.options.rightClickPanning) {
25
26
  editor.dispatch({
26
27
  type: 'pointer',
27
28
  target: 'canvas',
@@ -31,7 +32,7 @@ export function useCanvasEvents() {
31
32
  return
32
33
  }
33
34
 
34
- if (e.button !== 0 && e.button !== 1 && e.button !== 5) return
35
+ if (e.button !== 0 && e.button !== 1 && e.button !== 2 && e.button !== 5) return
35
36
 
36
37
  setPointerCapture(e.currentTarget, e)
37
38
 
@@ -47,6 +48,11 @@ export function useCanvasEvents() {
47
48
  if (editor.wasEventAlreadyHandled(e)) return
48
49
  if (e.button !== 0 && e.button !== 1 && e.button !== 2 && e.button !== 5) return
49
50
 
51
+ const rightClickPanning = editor.options.rightClickPanning
52
+ // Check before dispatch (which resets isPanning)
53
+ const wasRightClickPanning =
54
+ rightClickPanning && e.button === 2 && editor.inputs.getIsPanning()
55
+
50
56
  releasePointerCapture(e.currentTarget, e)
51
57
 
52
58
  editor.dispatch({
@@ -55,6 +61,21 @@ export function useCanvasEvents() {
55
61
  name: 'pointer_up',
56
62
  ...getPointerInfo(editor, e),
57
63
  })
64
+
65
+ // Static right-click: fire contextmenu at the pointer-up location
66
+ if (rightClickPanning && e.button === 2 && !wasRightClickPanning) {
67
+ const contextMenuEvent = new PointerEvent('contextmenu', {
68
+ bubbles: true,
69
+ clientX: e.clientX,
70
+ clientY: e.clientY,
71
+ button: 2,
72
+ buttons: 0,
73
+ pointerId: e.pointerId,
74
+ pointerType: e.pointerType,
75
+ isPrimary: e.isPrimary,
76
+ })
77
+ e.currentTarget.dispatchEvent(contextMenuEvent)
78
+ }
58
79
  }
59
80
 
60
81
  function onPointerEnter(e: React.PointerEvent) {
@@ -143,6 +164,26 @@ export function useCanvasEvents() {
143
164
  e.stopPropagation()
144
165
  }
145
166
 
167
+ function onContextMenu(e: React.MouseEvent) {
168
+ // With right-click panning disabled, let the native contextmenu through so the
169
+ // menu opens on press.
170
+ if (!editor.options.rightClickPanning) return
171
+ // Synthetic events — our own dispatch from onPointerUp, or tests using
172
+ // fireEvent.contextMenu — pass through so Radix can open the menu.
173
+ if (!e.nativeEvent.isTrusted) return
174
+ // Only suppress the native browser contextmenu when it follows a real
175
+ // right-click (button=2 with no ctrl modifier). For those, our pointer
176
+ // handling has already decided what to do (either we'll dispatch a
177
+ // synthetic contextmenu on pointerup to open the menu at the release
178
+ // position, or we panned and don't want a menu at all).
179
+ //
180
+ // Other contextmenu sources must reach Radix so the menu opens:
181
+ // - ctrl+click on macOS (button=0, or button=2 with ctrlKey=true)
182
+ // - long-press on touch devices (button=0, pointerType=touch)
183
+ if (e.button !== 2 || e.ctrlKey) return
184
+ preventDefault(e)
185
+ }
186
+
146
187
  return {
147
188
  onPointerDown,
148
189
  onPointerUp,
@@ -153,6 +194,7 @@ export function useCanvasEvents() {
153
194
  onTouchStart,
154
195
  onTouchEnd,
155
196
  onClick,
197
+ onContextMenu,
156
198
  }
157
199
  },
158
200
  [editor]
@@ -1,22 +1,11 @@
1
1
  import { ReactNode, useMemo } from 'react'
2
2
  import { DefaultBackground } from '../components/default-components/DefaultBackground'
3
- import { DefaultBrush } from '../components/default-components/DefaultBrush'
4
3
  import { DefaultCanvas } from '../components/default-components/DefaultCanvas'
5
- import { DefaultCollaboratorHint } from '../components/default-components/DefaultCollaboratorHint'
6
- import { DefaultCursor } from '../components/default-components/DefaultCursor'
7
4
  import { DefaultErrorFallback } from '../components/default-components/DefaultErrorFallback'
8
5
  import { DefaultGrid } from '../components/default-components/DefaultGrid'
9
- import { DefaultHandle } from '../components/default-components/DefaultHandle'
10
- import { DefaultHandles } from '../components/default-components/DefaultHandles'
11
6
  import { DefaultLoadingScreen } from '../components/default-components/DefaultLoadingScreen'
12
- import { DefaultScribble } from '../components/default-components/DefaultScribble'
13
- import { DefaultSelectionForeground } from '../components/default-components/DefaultSelectionForeground'
14
7
  import { DefaultShapeErrorFallback } from '../components/default-components/DefaultShapeErrorFallback'
15
- import { DefaultShapeIndicator } from '../components/default-components/DefaultShapeIndicator'
16
- import { DefaultShapeIndicatorErrorFallback } from '../components/default-components/DefaultShapeIndicatorErrorFallback'
17
- import { DefaultShapeIndicators } from '../components/default-components/DefaultShapeIndicators'
18
8
  import { DefaultShapeWrapper } from '../components/default-components/DefaultShapeWrapper'
19
- import { DefaultSnapIndicator } from '../components/default-components/DefaultSnapIndictor'
20
9
  import { DefaultSpinner } from '../components/default-components/DefaultSpinner'
21
10
  import { DefaultSvgDefs } from '../components/default-components/DefaultSvgDefs'
22
11
  import { EditorComponentsContext } from './EditorComponentsContext'
@@ -39,35 +28,18 @@ export function EditorComponentsProvider({
39
28
  const value = useMemo(
40
29
  (): Required<TLEditorComponents> => ({
41
30
  Background: DefaultBackground,
42
- Brush: DefaultBrush,
43
31
  Canvas: DefaultCanvas,
44
- CollaboratorBrush: DefaultBrush,
45
- CollaboratorCursor: DefaultCursor,
46
- CollaboratorHint: DefaultCollaboratorHint,
47
- CollaboratorScribble: DefaultScribble,
48
- CollaboratorShapeIndicator: DefaultShapeIndicator,
49
- Cursor: DefaultCursor,
50
32
  Grid: DefaultGrid,
51
- Handle: DefaultHandle,
52
- Handles: DefaultHandles,
53
33
  InFrontOfTheCanvas: null,
54
34
  LoadingScreen: DefaultLoadingScreen,
55
35
  OnTheCanvas: null,
56
- Overlays: null,
57
- Scribble: DefaultScribble,
58
36
  SelectionBackground: null,
59
- SelectionForeground: DefaultSelectionForeground,
60
- ShapeIndicator: DefaultShapeIndicator,
61
- ShapeIndicators: DefaultShapeIndicators,
62
37
  ShapeWrapper: DefaultShapeWrapper,
63
- SnapIndicator: DefaultSnapIndicator,
64
38
  Spinner: DefaultSpinner,
65
39
  SvgDefs: DefaultSvgDefs,
66
- ZoomBrush: DefaultBrush,
67
40
 
68
41
  ErrorFallback: DefaultErrorFallback,
69
42
  ShapeErrorFallback: DefaultShapeErrorFallback,
70
- ShapeIndicatorErrorFallback: DefaultShapeIndicatorErrorFallback,
71
43
 
72
44
  ..._overrides,
73
45
  }),
@@ -1,22 +1,12 @@
1
- import { useAtom, useComputed, useValue } from '@tldraw/state-react'
2
- import { useEffect } from 'react'
3
- import {
4
- getCollaboratorStateFromElapsedTime,
5
- shouldShowCollaborator,
6
- } from '../utils/collaboratorState'
1
+ import { useComputed, useValue } from '@tldraw/state-react'
7
2
  import { uniq } from '../utils/uniq'
8
3
  import { useEditor } from './useEditor'
9
4
 
10
- function setsEqual<T>(a: Set<T>, b: Set<T>): boolean {
11
- if (a.size !== b.size) return false
12
- for (const item of a) {
13
- if (!b.has(item)) return false
14
- }
15
- return true
16
- }
17
-
18
- // TODO: maybe move this to a computed property on the App class?
19
5
  /**
6
+ * Reactive list of peer user IDs for collaborators currently shown in the UI.
7
+ * Mirrors {@link Editor.getVisibleCollaborators} — peers fade out as they
8
+ * transition to idle/inactive, without requiring a manual re-render.
9
+ *
20
10
  * @returns The list of peer UserIDs
21
11
  * @public
22
12
  */
@@ -25,49 +15,10 @@ export function usePeerIds() {
25
15
 
26
16
  const $userIds = useComputed(
27
17
  'userIds',
28
- () => uniq(editor.getCollaborators().map((p) => p.userId)).sort(),
18
+ () => uniq(editor.getVisibleCollaborators().map((p) => p.userId)).sort(),
29
19
  { isEqual: (a, b) => a.join(',') === b.join?.(',') },
30
20
  [editor]
31
21
  )
32
22
 
33
23
  return useValue($userIds)
34
24
  }
35
-
36
- /**
37
- * Returns a computed signal of active peer user IDs that should be shown.
38
- * Automatically re-evaluates on an interval to handle time-based state transitions
39
- * (active -> idle -> inactive).
40
- *
41
- * @returns A computed signal containing a Set of active peer user IDs
42
- * @internal
43
- */
44
- export function useActivePeerIds$() {
45
- const $time = useAtom('peerIdsTime', Date.now())
46
- const editor = useEditor()
47
- useEffect(() => {
48
- const interval = editor.timers.setInterval(() => {
49
- $time.set(Date.now())
50
- }, editor.options.collaboratorCheckIntervalMs)
51
-
52
- return () => clearInterval(interval)
53
- }, [editor, $time])
54
-
55
- return useComputed(
56
- 'activePeerIds',
57
- () => {
58
- const now = $time.get()
59
- return new Set(
60
- editor
61
- .getCollaborators()
62
- .filter((p) => {
63
- const elapsed = Math.max(0, now - (p.lastActivityTimestamp ?? Infinity))
64
- const state = getCollaboratorStateFromElapsedTime(editor, elapsed)
65
- return shouldShowCollaborator(editor, p, state)
66
- })
67
- .map((p) => p.userId)
68
- )
69
- },
70
- { isEqual: setsEqual },
71
- [editor]
72
- )
73
- }
@@ -61,8 +61,10 @@ export function ShapeCullingProvider({ children }: ShapeCullingProviderProps) {
61
61
  }, [])
62
62
 
63
63
  const updateCulling = useCallback((culledShapes: Set<TLShapeId>) => {
64
+ let shouldBeCulled: boolean
65
+
64
66
  for (const [id, entry] of containersRef.current) {
65
- const shouldBeCulled = culledShapes.has(id)
67
+ shouldBeCulled = culledShapes.has(id)
66
68
  if (shouldBeCulled !== entry.isCulled) {
67
69
  const display = shouldBeCulled ? 'none' : 'block'
68
70
  setStyleProperty(entry.container, 'display', display)
@@ -154,6 +154,12 @@ export interface TldrawOptions {
154
154
  * When false, the spacebar will not pan the camera.
155
155
  */
156
156
  readonly spacebarPanning: boolean
157
+ /**
158
+ * Whether to allow right-click + drag to pan the camera. When true, right-click + drag pans the
159
+ * camera and a static right-click opens the context menu at the release position. When false,
160
+ * right-click opens the context menu on press (no drag-to-pan).
161
+ */
162
+ readonly rightClickPanning: boolean
157
163
  /**
158
164
  * The default padding (in pixels) used when zooming to fit content in the viewport.
159
165
  * This affects methods like `zoomToFit()`, `zoomToSelection()`, and `zoomToBounds()`.
@@ -326,6 +332,7 @@ export const defaultTldrawOptions = {
326
332
  debouncedZoom: true,
327
333
  debouncedZoomThreshold: 500,
328
334
  spacebarPanning: true,
335
+ rightClickPanning: true,
329
336
  zoomToFitPadding: 128,
330
337
  snapThreshold: 8,
331
338
  camera: DEFAULT_CAMERA_OPTIONS,