@tldraw/editor 4.6.0-next.30b99cd52fc8 → 4.6.0-next.35cf541abcf9

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 (284) hide show
  1. package/dist-cjs/index.d.ts +758 -96
  2. package/dist-cjs/index.js +18 -3
  3. package/dist-cjs/index.js.map +3 -3
  4. package/dist-cjs/lib/TldrawEditor.js +55 -12
  5. package/dist-cjs/lib/TldrawEditor.js.map +3 -3
  6. package/dist-cjs/lib/components/LiveCollaborators.js +1 -0
  7. package/dist-cjs/lib/components/LiveCollaborators.js.map +2 -2
  8. package/dist-cjs/lib/components/MenuClickCapture.js +99 -38
  9. package/dist-cjs/lib/components/MenuClickCapture.js.map +2 -2
  10. package/dist-cjs/lib/components/default-components/CanvasShapeIndicators.js +10 -3
  11. package/dist-cjs/lib/components/default-components/CanvasShapeIndicators.js.map +3 -3
  12. package/dist-cjs/lib/components/default-components/DefaultCanvas.js +5 -2
  13. package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
  14. package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js +1 -1
  15. package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js.map +2 -2
  16. package/dist-cjs/lib/components/default-components/DefaultShapeIndicators.js +1 -0
  17. package/dist-cjs/lib/components/default-components/DefaultShapeIndicators.js.map +2 -2
  18. package/dist-cjs/lib/config/createTLStore.js +7 -0
  19. package/dist-cjs/lib/config/createTLStore.js.map +2 -2
  20. package/dist-cjs/lib/config/defaultAssets.js +36 -0
  21. package/dist-cjs/lib/config/defaultAssets.js.map +7 -0
  22. package/dist-cjs/lib/editor/Editor.js +279 -10
  23. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  24. package/dist-cjs/lib/editor/assets/AssetUtil.js +67 -0
  25. package/dist-cjs/lib/editor/assets/AssetUtil.js.map +7 -0
  26. package/dist-cjs/lib/editor/bindings/BindingUtil.js +1 -0
  27. package/dist-cjs/lib/editor/bindings/BindingUtil.js.map +1 -1
  28. package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js +1 -0
  29. package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js.map +1 -1
  30. package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js +1 -0
  31. package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js.map +1 -1
  32. package/dist-cjs/lib/editor/managers/FocusManager/FocusManager.js +1 -0
  33. package/dist-cjs/lib/editor/managers/FocusManager/FocusManager.js.map +1 -1
  34. package/dist-cjs/lib/editor/managers/FontManager/FontManager.js +2 -0
  35. package/dist-cjs/lib/editor/managers/FontManager/FontManager.js.map +2 -2
  36. package/dist-cjs/lib/editor/managers/HistoryManager/HistoryManager.js +2 -0
  37. package/dist-cjs/lib/editor/managers/HistoryManager/HistoryManager.js.map +1 -1
  38. package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js +12 -0
  39. package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js.map +2 -2
  40. package/dist-cjs/lib/editor/managers/PerformanceManager/PerformanceApiAdapter.js +80 -0
  41. package/dist-cjs/lib/editor/managers/PerformanceManager/PerformanceApiAdapter.js.map +7 -0
  42. package/dist-cjs/lib/editor/managers/PerformanceManager/PerformanceManager.js +466 -0
  43. package/dist-cjs/lib/editor/managers/PerformanceManager/PerformanceManager.js.map +7 -0
  44. package/dist-cjs/lib/editor/managers/PerformanceManager/perf-types.js +17 -0
  45. package/dist-cjs/lib/editor/managers/PerformanceManager/perf-types.js.map +7 -0
  46. package/dist-cjs/lib/editor/managers/ScribbleManager/ScribbleManager.js +1 -0
  47. package/dist-cjs/lib/editor/managers/ScribbleManager/ScribbleManager.js.map +1 -1
  48. package/dist-cjs/lib/editor/managers/SnapManager/BoundsSnaps.js +1 -0
  49. package/dist-cjs/lib/editor/managers/SnapManager/BoundsSnaps.js.map +1 -1
  50. package/dist-cjs/lib/editor/managers/SnapManager/HandleSnaps.js +1 -0
  51. package/dist-cjs/lib/editor/managers/SnapManager/HandleSnaps.js.map +1 -1
  52. package/dist-cjs/lib/editor/managers/SnapManager/SnapManager.js +2 -1
  53. package/dist-cjs/lib/editor/managers/SnapManager/SnapManager.js.map +2 -2
  54. package/dist-cjs/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.js +1 -0
  55. package/dist-cjs/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.js.map +1 -1
  56. package/dist-cjs/lib/editor/managers/TextManager/TextManager.js +1 -0
  57. package/dist-cjs/lib/editor/managers/TextManager/TextManager.js.map +1 -1
  58. package/dist-cjs/lib/editor/managers/ThemeManager/ThemeManager.js +107 -0
  59. package/dist-cjs/lib/editor/managers/ThemeManager/ThemeManager.js.map +7 -0
  60. package/dist-cjs/lib/editor/managers/ThemeManager/defaultThemes.js +586 -0
  61. package/dist-cjs/lib/editor/managers/ThemeManager/defaultThemes.js.map +7 -0
  62. package/dist-cjs/lib/editor/managers/TickManager/TickManager.js +1 -0
  63. package/dist-cjs/lib/editor/managers/TickManager/TickManager.js.map +1 -1
  64. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js +8 -4
  65. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +2 -2
  66. package/dist-cjs/lib/editor/shapes/BaseFrameLikeShapeUtil.js +76 -0
  67. package/dist-cjs/lib/editor/shapes/BaseFrameLikeShapeUtil.js.map +7 -0
  68. package/dist-cjs/lib/editor/shapes/ShapeUtil.js +22 -2
  69. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  70. package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js +1 -1
  71. package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js.map +2 -2
  72. package/dist-cjs/lib/editor/shapes/shared/getPerfectDashProps.js +6 -0
  73. package/dist-cjs/lib/editor/shapes/shared/getPerfectDashProps.js.map +2 -2
  74. package/dist-cjs/lib/editor/tools/StateNode.js +15 -17
  75. package/dist-cjs/lib/editor/tools/StateNode.js.map +2 -2
  76. package/dist-cjs/lib/editor/types/SvgExportContext.js.map +2 -2
  77. package/dist-cjs/lib/editor/types/external-content.js.map +1 -1
  78. package/dist-cjs/lib/exports/ExportDelay.js +1 -0
  79. package/dist-cjs/lib/exports/ExportDelay.js.map +1 -1
  80. package/dist-cjs/lib/exports/StyleEmbedder.js +1 -0
  81. package/dist-cjs/lib/exports/StyleEmbedder.js.map +1 -1
  82. package/dist-cjs/lib/exports/getSvgJsx.js +14 -8
  83. package/dist-cjs/lib/exports/getSvgJsx.js.map +2 -2
  84. package/dist-cjs/lib/globals/environment.js +18 -1
  85. package/dist-cjs/lib/globals/environment.js.map +2 -2
  86. package/dist-cjs/lib/hooks/useCanvasEvents.js +25 -4
  87. package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
  88. package/dist-cjs/lib/hooks/{useIsDarkMode.js → useColorMode.js} +14 -10
  89. package/dist-cjs/lib/hooks/useColorMode.js.map +7 -0
  90. package/dist-cjs/lib/hooks/useCursor.js +3 -7
  91. package/dist-cjs/lib/hooks/useCursor.js.map +2 -2
  92. package/dist-cjs/lib/hooks/useDarkMode.js +4 -4
  93. package/dist-cjs/lib/hooks/useDarkMode.js.map +2 -2
  94. package/dist-cjs/lib/options.js +2 -0
  95. package/dist-cjs/lib/options.js.map +2 -2
  96. package/dist-cjs/lib/primitives/Vec.js +3 -0
  97. package/dist-cjs/lib/primitives/Vec.js.map +1 -1
  98. package/dist-cjs/lib/primitives/geometry/Circle2d.js +1 -0
  99. package/dist-cjs/lib/primitives/geometry/Circle2d.js.map +1 -1
  100. package/dist-cjs/lib/primitives/geometry/Ellipse2d.js +1 -0
  101. package/dist-cjs/lib/primitives/geometry/Ellipse2d.js.map +1 -1
  102. package/dist-cjs/lib/primitives/geometry/Geometry2d.js +2 -0
  103. package/dist-cjs/lib/primitives/geometry/Geometry2d.js.map +1 -1
  104. package/dist-cjs/lib/primitives/geometry/Stadium2d.js +1 -0
  105. package/dist-cjs/lib/primitives/geometry/Stadium2d.js.map +1 -1
  106. package/dist-cjs/lib/utils/EditorAtom.js +2 -0
  107. package/dist-cjs/lib/utils/EditorAtom.js.map +1 -1
  108. package/dist-cjs/lib/utils/reparenting.js +2 -1
  109. package/dist-cjs/lib/utils/reparenting.js.map +2 -2
  110. package/dist-cjs/lib/utils/richText.js.map +2 -2
  111. package/dist-cjs/lib/utils/runtime.js +2 -1
  112. package/dist-cjs/lib/utils/runtime.js.map +2 -2
  113. package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js +2 -0
  114. package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js.map +1 -1
  115. package/dist-cjs/lib/utils/sync/hardReset.js +0 -8
  116. package/dist-cjs/lib/utils/sync/hardReset.js.map +2 -2
  117. package/dist-cjs/version.js +3 -3
  118. package/dist-cjs/version.js.map +1 -1
  119. package/dist-esm/index.d.mts +758 -96
  120. package/dist-esm/index.mjs +19 -6
  121. package/dist-esm/index.mjs.map +2 -2
  122. package/dist-esm/lib/TldrawEditor.mjs +58 -12
  123. package/dist-esm/lib/TldrawEditor.mjs.map +3 -3
  124. package/dist-esm/lib/components/LiveCollaborators.mjs +1 -0
  125. package/dist-esm/lib/components/LiveCollaborators.mjs.map +2 -2
  126. package/dist-esm/lib/components/MenuClickCapture.mjs +100 -39
  127. package/dist-esm/lib/components/MenuClickCapture.mjs.map +2 -2
  128. package/dist-esm/lib/components/default-components/CanvasShapeIndicators.mjs +10 -3
  129. package/dist-esm/lib/components/default-components/CanvasShapeIndicators.mjs.map +3 -3
  130. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +5 -2
  131. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
  132. package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs +1 -1
  133. package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs.map +2 -2
  134. package/dist-esm/lib/components/default-components/DefaultShapeIndicators.mjs +1 -0
  135. package/dist-esm/lib/components/default-components/DefaultShapeIndicators.mjs.map +2 -2
  136. package/dist-esm/lib/config/createTLStore.mjs +10 -1
  137. package/dist-esm/lib/config/createTLStore.mjs.map +2 -2
  138. package/dist-esm/lib/config/defaultAssets.mjs +16 -0
  139. package/dist-esm/lib/config/defaultAssets.mjs.map +7 -0
  140. package/dist-esm/lib/editor/Editor.mjs +279 -10
  141. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  142. package/dist-esm/lib/editor/assets/AssetUtil.mjs +47 -0
  143. package/dist-esm/lib/editor/assets/AssetUtil.mjs.map +7 -0
  144. package/dist-esm/lib/editor/bindings/BindingUtil.mjs +1 -0
  145. package/dist-esm/lib/editor/bindings/BindingUtil.mjs.map +1 -1
  146. package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs +1 -0
  147. package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs.map +1 -1
  148. package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs +1 -0
  149. package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs.map +1 -1
  150. package/dist-esm/lib/editor/managers/FocusManager/FocusManager.mjs +1 -0
  151. package/dist-esm/lib/editor/managers/FocusManager/FocusManager.mjs.map +1 -1
  152. package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs +2 -0
  153. package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs.map +2 -2
  154. package/dist-esm/lib/editor/managers/HistoryManager/HistoryManager.mjs +2 -0
  155. package/dist-esm/lib/editor/managers/HistoryManager/HistoryManager.mjs.map +1 -1
  156. package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs +12 -0
  157. package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs.map +2 -2
  158. package/dist-esm/lib/editor/managers/PerformanceManager/PerformanceApiAdapter.mjs +60 -0
  159. package/dist-esm/lib/editor/managers/PerformanceManager/PerformanceApiAdapter.mjs.map +7 -0
  160. package/dist-esm/lib/editor/managers/PerformanceManager/PerformanceManager.mjs +438 -0
  161. package/dist-esm/lib/editor/managers/PerformanceManager/PerformanceManager.mjs.map +7 -0
  162. package/dist-esm/lib/editor/managers/PerformanceManager/perf-types.mjs +1 -0
  163. package/dist-esm/lib/editor/managers/PerformanceManager/perf-types.mjs.map +7 -0
  164. package/dist-esm/lib/editor/managers/ScribbleManager/ScribbleManager.mjs +1 -0
  165. package/dist-esm/lib/editor/managers/ScribbleManager/ScribbleManager.mjs.map +1 -1
  166. package/dist-esm/lib/editor/managers/SnapManager/BoundsSnaps.mjs +1 -0
  167. package/dist-esm/lib/editor/managers/SnapManager/BoundsSnaps.mjs.map +1 -1
  168. package/dist-esm/lib/editor/managers/SnapManager/HandleSnaps.mjs +1 -0
  169. package/dist-esm/lib/editor/managers/SnapManager/HandleSnaps.mjs.map +1 -1
  170. package/dist-esm/lib/editor/managers/SnapManager/SnapManager.mjs +2 -1
  171. package/dist-esm/lib/editor/managers/SnapManager/SnapManager.mjs.map +2 -2
  172. package/dist-esm/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.mjs +1 -0
  173. package/dist-esm/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.mjs.map +1 -1
  174. package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs +1 -0
  175. package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs.map +1 -1
  176. package/dist-esm/lib/editor/managers/ThemeManager/ThemeManager.mjs +89 -0
  177. package/dist-esm/lib/editor/managers/ThemeManager/ThemeManager.mjs.map +7 -0
  178. package/dist-esm/lib/editor/managers/ThemeManager/defaultThemes.mjs +568 -0
  179. package/dist-esm/lib/editor/managers/ThemeManager/defaultThemes.mjs.map +7 -0
  180. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs +1 -0
  181. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs.map +1 -1
  182. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs +8 -4
  183. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +2 -2
  184. package/dist-esm/lib/editor/shapes/BaseFrameLikeShapeUtil.mjs +56 -0
  185. package/dist-esm/lib/editor/shapes/BaseFrameLikeShapeUtil.mjs.map +7 -0
  186. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +22 -2
  187. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  188. package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs +1 -1
  189. package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs.map +2 -2
  190. package/dist-esm/lib/editor/shapes/shared/getPerfectDashProps.mjs +6 -0
  191. package/dist-esm/lib/editor/shapes/shared/getPerfectDashProps.mjs.map +2 -2
  192. package/dist-esm/lib/editor/tools/StateNode.mjs +15 -17
  193. package/dist-esm/lib/editor/tools/StateNode.mjs.map +2 -2
  194. package/dist-esm/lib/editor/types/SvgExportContext.mjs.map +2 -2
  195. package/dist-esm/lib/exports/ExportDelay.mjs +1 -0
  196. package/dist-esm/lib/exports/ExportDelay.mjs.map +1 -1
  197. package/dist-esm/lib/exports/StyleEmbedder.mjs +1 -0
  198. package/dist-esm/lib/exports/StyleEmbedder.mjs.map +1 -1
  199. package/dist-esm/lib/exports/getSvgJsx.mjs +14 -11
  200. package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
  201. package/dist-esm/lib/globals/environment.mjs +18 -1
  202. package/dist-esm/lib/globals/environment.mjs.map +2 -2
  203. package/dist-esm/lib/hooks/useCanvasEvents.mjs +25 -4
  204. package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
  205. package/dist-esm/lib/hooks/useColorMode.mjs +19 -0
  206. package/dist-esm/lib/hooks/useColorMode.mjs.map +7 -0
  207. package/dist-esm/lib/hooks/useCursor.mjs +3 -7
  208. package/dist-esm/lib/hooks/useCursor.mjs.map +2 -2
  209. package/dist-esm/lib/hooks/useDarkMode.mjs +4 -4
  210. package/dist-esm/lib/hooks/useDarkMode.mjs.map +2 -2
  211. package/dist-esm/lib/options.mjs +2 -0
  212. package/dist-esm/lib/options.mjs.map +2 -2
  213. package/dist-esm/lib/primitives/Vec.mjs +3 -0
  214. package/dist-esm/lib/primitives/Vec.mjs.map +1 -1
  215. package/dist-esm/lib/primitives/geometry/Circle2d.mjs +1 -0
  216. package/dist-esm/lib/primitives/geometry/Circle2d.mjs.map +1 -1
  217. package/dist-esm/lib/primitives/geometry/Ellipse2d.mjs +1 -0
  218. package/dist-esm/lib/primitives/geometry/Ellipse2d.mjs.map +1 -1
  219. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs +2 -0
  220. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs.map +1 -1
  221. package/dist-esm/lib/primitives/geometry/Stadium2d.mjs +1 -0
  222. package/dist-esm/lib/primitives/geometry/Stadium2d.mjs.map +1 -1
  223. package/dist-esm/lib/utils/EditorAtom.mjs +2 -0
  224. package/dist-esm/lib/utils/EditorAtom.mjs.map +1 -1
  225. package/dist-esm/lib/utils/reparenting.mjs +2 -1
  226. package/dist-esm/lib/utils/reparenting.mjs.map +2 -2
  227. package/dist-esm/lib/utils/richText.mjs.map +2 -2
  228. package/dist-esm/lib/utils/runtime.mjs +2 -1
  229. package/dist-esm/lib/utils/runtime.mjs.map +2 -2
  230. package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs +2 -0
  231. package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs.map +1 -1
  232. package/dist-esm/lib/utils/sync/hardReset.mjs +0 -8
  233. package/dist-esm/lib/utils/sync/hardReset.mjs.map +2 -2
  234. package/dist-esm/version.mjs +3 -3
  235. package/dist-esm/version.mjs.map +1 -1
  236. package/editor.css +0 -37
  237. package/package.json +7 -7
  238. package/src/index.ts +24 -6
  239. package/src/lib/TldrawEditor.tsx +90 -13
  240. package/src/lib/components/LiveCollaborators.tsx +8 -2
  241. package/src/lib/components/MenuClickCapture.tsx +129 -49
  242. package/src/lib/components/default-components/CanvasShapeIndicators.tsx +14 -3
  243. package/src/lib/components/default-components/DefaultCanvas.tsx +6 -2
  244. package/src/lib/components/default-components/DefaultShapeIndicator.tsx +2 -2
  245. package/src/lib/components/default-components/DefaultShapeIndicators.tsx +2 -0
  246. package/src/lib/config/createTLStore.ts +22 -1
  247. package/src/lib/config/defaultAssets.ts +19 -0
  248. package/src/lib/editor/Editor.ts +387 -40
  249. package/src/lib/editor/assets/AssetUtil.ts +85 -0
  250. package/src/lib/editor/managers/FontManager/FontManager.test.ts +9 -2
  251. package/src/lib/editor/managers/FontManager/FontManager.ts +1 -67
  252. package/src/lib/editor/managers/InputsManager/InputsManager.ts +12 -0
  253. package/src/lib/editor/managers/PerformanceManager/PerformanceApiAdapter.ts +82 -0
  254. package/src/lib/editor/managers/PerformanceManager/PerformanceManager.test.ts +522 -0
  255. package/src/lib/editor/managers/PerformanceManager/PerformanceManager.ts +583 -0
  256. package/src/lib/editor/managers/PerformanceManager/perf-types.ts +196 -0
  257. package/src/lib/editor/managers/SnapManager/SnapManager.test.ts +13 -2
  258. package/src/lib/editor/managers/SnapManager/SnapManager.ts +1 -1
  259. package/src/lib/editor/managers/ThemeManager/ThemeManager.ts +116 -0
  260. package/src/lib/editor/managers/ThemeManager/defaultThemes.ts +605 -0
  261. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +23 -29
  262. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.ts +5 -3
  263. package/src/lib/editor/shapes/BaseFrameLikeShapeUtil.tsx +121 -0
  264. package/src/lib/editor/shapes/ShapeUtil.ts +39 -3
  265. package/src/lib/editor/shapes/group/GroupShapeUtil.tsx +1 -1
  266. package/src/lib/editor/shapes/shared/getPerfectDashProps.ts +7 -0
  267. package/src/lib/editor/tools/StateNode.ts +16 -18
  268. package/src/lib/editor/types/SvgExportContext.tsx +5 -0
  269. package/src/lib/editor/types/external-content.ts +1 -0
  270. package/src/lib/exports/getSvgJsx.tsx +23 -16
  271. package/src/lib/globals/environment.ts +18 -0
  272. package/src/lib/hooks/useCanvasEvents.ts +40 -3
  273. package/src/lib/hooks/{useIsDarkMode.ts → useColorMode.ts} +9 -5
  274. package/src/lib/hooks/useCursor.ts +3 -7
  275. package/src/lib/hooks/useDarkMode.ts +4 -4
  276. package/src/lib/options.ts +14 -0
  277. package/src/lib/utils/reparenting.ts +6 -2
  278. package/src/lib/utils/richText.ts +1 -1
  279. package/src/lib/utils/runtime.ts +3 -1
  280. package/src/lib/utils/sync/hardReset.ts +0 -8
  281. package/src/version.ts +3 -3
  282. package/dist-cjs/lib/hooks/useIsDarkMode.js.map +0 -7
  283. package/dist-esm/lib/hooks/useIsDarkMode.mjs +0 -15
  284. package/dist-esm/lib/hooks/useIsDarkMode.mjs.map +0 -7
@@ -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,
@@ -50,6 +50,9 @@ import {
50
50
  TLShapePartial,
51
51
  TLStore,
52
52
  TLStoreSnapshot,
53
+ TLTheme,
54
+ TLThemeId,
55
+ TLThemes,
53
56
  TLUser,
54
57
  TLUserId,
55
58
  TLVideoAsset,
@@ -95,6 +98,7 @@ import {
95
98
  } from '@tldraw/utils'
96
99
  import EventEmitter from 'eventemitter3'
97
100
  import { TLCurrentUser, createTLCurrentUser } from '../config/createTLCurrentUser'
101
+ import { TLAnyAssetUtilConstructor, checkAssets } from '../config/defaultAssets'
98
102
  import { TLAnyBindingUtilConstructor, checkBindings } from '../config/defaultBindings'
99
103
  import { TLAnyShapeUtilConstructor, checkShapesAndAddCore } from '../config/defaultShapes'
100
104
  import {
@@ -141,6 +145,7 @@ import { getDroppedShapesToNewParents, kickoutOccludedShapes } from '../utils/re
141
145
  import { TLTextOptions, TiptapEditor } from '../utils/richText'
142
146
  import { applyRotationToSnapshotShapes, getRotationSnapshot } from '../utils/rotation'
143
147
  import { ReadonlySharedStyleMap, SharedStyle, SharedStyleMap } from '../utils/SharedStylesMap'
148
+ import { AssetUtil } from './assets/AssetUtil'
144
149
  import { BindingOnDeleteOptions, BindingUtil } from './bindings/BindingUtil'
145
150
  import { bindingsIndex } from './derivations/bindingsIndex'
146
151
  import { notVisibleShapes } from './derivations/notVisibleShapes'
@@ -152,10 +157,12 @@ import { FocusManager } from './managers/FocusManager/FocusManager'
152
157
  import { FontManager } from './managers/FontManager/FontManager'
153
158
  import { HistoryManager } from './managers/HistoryManager/HistoryManager'
154
159
  import { InputsManager } from './managers/InputsManager/InputsManager'
160
+ import { PerformanceManager } from './managers/PerformanceManager/PerformanceManager'
155
161
  import { ScribbleManager } from './managers/ScribbleManager/ScribbleManager'
156
162
  import { SnapManager } from './managers/SnapManager/SnapManager'
157
163
  import { SpatialIndexManager } from './managers/SpatialIndexManager/SpatialIndexManager'
158
164
  import { TextManager } from './managers/TextManager/TextManager'
165
+ import { ThemeManager, resolveThemes } from './managers/ThemeManager/ThemeManager'
159
166
  import { TickManager } from './managers/TickManager/TickManager'
160
167
  import { UserPreferencesManager } from './managers/UserPreferencesManager/UserPreferencesManager'
161
168
  import {
@@ -214,14 +221,13 @@ export interface TLEditorOptions {
214
221
  */
215
222
  bindingUtils: readonly TLAnyBindingUtilConstructor[]
216
223
  /**
217
- * An array of tools to use in the editor. These will be used to handle events and manage user interactions in the editor.
224
+ * An array of asset utils to use in the editor. These will be used to handle asset-type-specific behavior.
218
225
  */
219
- tools: readonly TLStateNodeConstructor[]
226
+ assetUtils?: readonly TLAnyAssetUtilConstructor[]
220
227
  /**
221
- * Should return a containing html element which has all the styles applied to the editor. If not
222
- * given, the body element will be used.
228
+ * An array of tools to use in the editor. These will be used to handle events and manage user interactions in the editor.
223
229
  */
224
- getContainer(): HTMLElement
230
+ tools: readonly TLStateNodeConstructor[]
225
231
  /**
226
232
  * A user defined externally to replace the default user.
227
233
  */
@@ -234,25 +240,13 @@ export interface TLEditorOptions {
234
240
  * Whether to automatically focus the editor when it mounts.
235
241
  */
236
242
  autoFocus?: boolean
237
- /**
238
- * Whether to infer dark mode from the user's system preferences. Defaults to false.
239
- */
240
- inferDarkMode?: boolean
241
- /**
242
- * Options for the editor's camera.
243
- *
244
- * @deprecated Use `options.cameraOptions` instead. This will be removed in a future release.
245
- */
246
- cameraOptions?: Partial<TLCameraOptions>
247
- options?: Partial<TldrawOptions>
248
- /**
249
- * Text options for the editor.
250
- *
251
- * @deprecated Use `options.text` instead. This prop will be removed in a future release.
252
- */
253
- textOptions?: TLTextOptions
254
243
  licenseKey?: string
255
244
  fontAssetUrls?: { [key: string]: string | undefined }
245
+ /**
246
+ * Should return a containing html element which has all the styles applied to the editor. If not
247
+ * given, the body element will be used.
248
+ */
249
+ getContainer(): HTMLElement
256
250
  /**
257
251
  * Provides a way to hide shapes.
258
252
  *
@@ -272,6 +266,41 @@ export interface TLEditorOptions {
272
266
  shape: TLShape,
273
267
  editor: Editor
274
268
  ): 'visible' | 'hidden' | 'inherit' | null | undefined
269
+ /**
270
+ * Named theme definitions for the editor. Each theme contains shared
271
+ * properties (font size, line height, stroke width) and color palettes
272
+ * for both light and dark modes.
273
+ */
274
+ themes?: Partial<TLThemes>
275
+ /**
276
+ * The id of the initially active theme. Defaults to `'default'`.
277
+ */
278
+ initialTheme?: TLThemeId
279
+ /**
280
+ * The editor's color scheme preference, controls the default color mode. Defaults to `'light'`.
281
+ *
282
+ * - `'light'` - Always use light mode.
283
+ * - `'dark'` - Always use dark mode.
284
+ * - `'system'` - Follow the OS color scheme preference.
285
+ */
286
+ colorScheme?: 'light' | 'dark' | 'system'
287
+ /**
288
+ * Additional configuration options for the tldraw editor.
289
+ */
290
+ options?: Partial<TldrawOptions>
291
+ // --- Deprecated ----
292
+ /**
293
+ * Options for the editor's camera.
294
+ *
295
+ * @deprecated Use `options.cameraOptions` instead. This will be removed in a future release.
296
+ */
297
+ cameraOptions?: Partial<TLCameraOptions>
298
+ /**
299
+ * Text options for the editor.
300
+ *
301
+ * @deprecated Use `options.text` instead. This prop will be removed in a future release.
302
+ */
303
+ textOptions?: TLTextOptions
275
304
  }
276
305
 
277
306
  /**
@@ -300,6 +329,7 @@ export class Editor extends EventEmitter<TLEventMap> {
300
329
  user,
301
330
  shapeUtils,
302
331
  bindingUtils,
332
+ assetUtils: assetUtilConstructors,
303
333
  tools,
304
334
  getContainer,
305
335
  // needs to be here for backwards compatibility with TldrawEditor
@@ -307,13 +337,15 @@ export class Editor extends EventEmitter<TLEventMap> {
307
337
  cameraOptions,
308
338
  initialState,
309
339
  autoFocus,
310
- inferDarkMode,
311
340
  options: _options,
312
341
  // needs to be here for backwards compatibility with TldrawEditor
313
342
  // eslint-disable-next-line @typescript-eslint/no-deprecated
314
343
  textOptions: _textOptions,
315
344
  getShapeVisibility,
345
+ colorScheme,
316
346
  fontAssetUrls,
347
+ themes,
348
+ initialTheme,
317
349
  }: TLEditorOptions) {
318
350
  super()
319
351
 
@@ -348,19 +380,24 @@ export class Editor extends EventEmitter<TLEventMap> {
348
380
  ...options?.camera,
349
381
  })
350
382
 
383
+ this.getContainer = getContainer
384
+
351
385
  this._textOptions = atom('text options', options?.text ?? null)
352
386
 
353
- this.user = new UserPreferencesManager(user ?? createTLCurrentUser(), inferDarkMode ?? false)
387
+ this.user = new UserPreferencesManager(user ?? createTLCurrentUser(), colorScheme ?? 'light')
354
388
  this.disposables.add(() => this.user.dispose())
355
389
 
356
- this.getContainer = getContainer
357
-
358
390
  this.textMeasure = new TextManager(this)
359
391
  this.disposables.add(() => this.textMeasure.dispose())
360
392
 
361
- this.fonts = new FontManager(this, fontAssetUrls)
393
+ this._themeManager = new ThemeManager(this, {
394
+ themes: resolveThemes(themes),
395
+ initial: initialTheme ?? 'default',
396
+ })
397
+ this.disposables.add(() => this._themeManager.dispose())
362
398
 
363
399
  this._tickManager = new TickManager(this)
400
+ this.disposables.add(() => this._tickManager.dispose())
364
401
  this.disposables.add(() => {
365
402
  // Reset camera state to 'idle' so the store isn't left stuck at 'moving'
366
403
  // when tick events stop (e.g. React strict mode disposes while camera is moving)
@@ -368,7 +405,11 @@ export class Editor extends EventEmitter<TLEventMap> {
368
405
  this._setCameraState('idle')
369
406
  })
370
407
 
408
+ this.fonts = new FontManager(this, fontAssetUrls)
409
+
371
410
  this.inputs = new InputsManager(this)
411
+ this.performance = new PerformanceManager(this)
412
+ this.disposables.add(() => this.performance.dispose())
372
413
 
373
414
  class NewRoot extends RootState {
374
415
  static override initial = initialState ?? ''
@@ -406,6 +447,17 @@ export class Editor extends EventEmitter<TLEventMap> {
406
447
  this.shapeUtils = _shapeUtils
407
448
  this.styleProps = _styleProps
408
449
 
450
+ const _shapeUtilsByAssetType = {} as Record<string, ShapeUtil<any>>
451
+ for (const Util of allShapeUtils) {
452
+ const assetTypes = Util.handledAssetTypes
453
+ if (assetTypes) {
454
+ for (const assetType of assetTypes) {
455
+ _shapeUtilsByAssetType[assetType] = _shapeUtils[Util.type]
456
+ }
457
+ }
458
+ }
459
+ this._shapeUtilsByAssetType = _shapeUtilsByAssetType
460
+
409
461
  const allBindingUtils = checkBindings(bindingUtils)
410
462
  const _bindingUtils = {} as Record<string, BindingUtil<any>>
411
463
  for (const Util of allBindingUtils) {
@@ -414,6 +466,17 @@ export class Editor extends EventEmitter<TLEventMap> {
414
466
  }
415
467
  this.bindingUtils = _bindingUtils
416
468
 
469
+ // Asset utils
470
+ if (assetUtilConstructors) {
471
+ const allAssetUtils = checkAssets(assetUtilConstructors)
472
+ const _assetUtils = {} as Record<string, AssetUtil<any>>
473
+ for (const Util of allAssetUtils) {
474
+ const util = new Util(this)
475
+ _assetUtils[Util.type] = util
476
+ }
477
+ this.assetUtils = _assetUtils
478
+ }
479
+
417
480
  // Tools.
418
481
  // Accept tools from constructor parameters which may not conflict with the root note's default or
419
482
  // "baked in" tools, select and zoom.
@@ -948,6 +1011,18 @@ export class Editor extends EventEmitter<TLEventMap> {
948
1011
  */
949
1012
  readonly snaps: SnapManager
950
1013
 
1014
+ /**
1015
+ * A manager for performance measurement hooks.
1016
+ *
1017
+ * @public
1018
+ */
1019
+ readonly performance: PerformanceManager
1020
+
1021
+ /**
1022
+ * A manager for the spatial index, tracking where shapes exist on the canvas.
1023
+ *
1024
+ * @internal
1025
+ */
951
1026
  private readonly _spatialIndex: SpatialIndexManager
952
1027
 
953
1028
  /**
@@ -965,6 +1040,13 @@ export class Editor extends EventEmitter<TLEventMap> {
965
1040
  */
966
1041
  readonly user: UserPreferencesManager
967
1042
 
1043
+ /**
1044
+ * A manager for the editor's themes.
1045
+ *
1046
+ * @internal
1047
+ */
1048
+ private readonly _themeManager: ThemeManager
1049
+
968
1050
  /**
969
1051
  * A helper for measuring text.
970
1052
  *
@@ -1045,13 +1127,134 @@ export class Editor extends EventEmitter<TLEventMap> {
1045
1127
  * @public
1046
1128
  */
1047
1129
  dispose() {
1130
+ // Stop any in-progress camera animations and following before
1131
+ // running disposables, so their cleanup listeners fire first
1132
+ this.stopCameraAnimation()
1133
+ if (this.getInstanceState().followingUserId) {
1134
+ this.stopFollowingUser()
1135
+ }
1136
+
1048
1137
  this.disposables.forEach((dispose) => dispose())
1049
1138
  this.disposables.clear()
1139
+
1140
+ // Clear any open menus for this editor's context
1141
+ this.menus.clearOpenMenus()
1142
+
1050
1143
  this.store.dispose()
1051
1144
  this.isDisposed = true
1052
1145
  this.emit('dispose')
1053
1146
  }
1054
1147
 
1148
+ /* ------------------ Themes (shadowing the theme manager) ------------------ */
1149
+
1150
+ /**
1151
+ * Get the current color mode (`'light'` or `'dark'`), based on the user's dark mode preference.
1152
+ *
1153
+ * @public
1154
+ */
1155
+ getColorMode(): 'light' | 'dark' {
1156
+ return this._themeManager.getColorMode()
1157
+ }
1158
+
1159
+ /**
1160
+ * Set the color mode. Note that this is a convenience method that passes the mode to
1161
+ * `user.updateUserPreferences`, which is the source of truth for the user's color mode preference.
1162
+ *
1163
+ * @public
1164
+ */
1165
+ setColorMode(mode: 'light' | 'dark') {
1166
+ this.user.updateUserPreferences({ colorScheme: mode })
1167
+ return this
1168
+ }
1169
+
1170
+ /**
1171
+ * Get the id of the current theme.
1172
+ *
1173
+ * @public
1174
+ */
1175
+ getCurrentThemeId(): TLThemeId {
1176
+ return this._themeManager.getCurrentThemeId()
1177
+ }
1178
+
1179
+ /**
1180
+ * Get the current theme definition.
1181
+ *
1182
+ * @public
1183
+ */
1184
+ getCurrentTheme(): TLTheme {
1185
+ return this._themeManager.getCurrentTheme()
1186
+ }
1187
+
1188
+ /**
1189
+ * Set the current theme by id.
1190
+ *
1191
+ * @public
1192
+ */
1193
+ setCurrentTheme(id: TLThemeId) {
1194
+ this._themeManager.setCurrentTheme(id)
1195
+ return this
1196
+ }
1197
+
1198
+ /**
1199
+ * Get all registered theme definitions.
1200
+ *
1201
+ * @public
1202
+ */
1203
+ getThemes(): TLThemes {
1204
+ return this._themeManager.getThemes()
1205
+ }
1206
+
1207
+ /**
1208
+ * Get a single theme definition by id.
1209
+ *
1210
+ * @public
1211
+ */
1212
+ getTheme(id: TLThemeId): TLTheme | undefined {
1213
+ return this._themeManager.getTheme(id)
1214
+ }
1215
+
1216
+ /**
1217
+ * Replace all theme definitions, or update them via a callback that receives a deep copy.
1218
+ * The `'default'` theme must always be present in the result.
1219
+ *
1220
+ * @example
1221
+ * ```ts
1222
+ * // Replace all themes
1223
+ * editor.updateThemes({ default: myDefaultTheme, ocean: myOceanTheme })
1224
+ *
1225
+ * // Update via callback
1226
+ * editor.updateThemes((themes) => {
1227
+ * delete themes.ocean
1228
+ * return themes
1229
+ * })
1230
+ * ```
1231
+ *
1232
+ * @public
1233
+ */
1234
+ updateThemes(themes: TLThemes | ((themes: TLThemes) => TLThemes)) {
1235
+ this._themeManager.updateThemes(themes)
1236
+ return this
1237
+ }
1238
+
1239
+ /**
1240
+ * Register or update a single theme definition. The theme is keyed by its `id` property.
1241
+ *
1242
+ * @example
1243
+ * ```ts
1244
+ * // Override a property on the default theme
1245
+ * editor.updateTheme({ ...editor.getTheme('default')!, fontSize: 24 })
1246
+ *
1247
+ * // Register a new theme
1248
+ * editor.updateTheme({ id: 'ocean', ...myOceanTheme })
1249
+ * ```
1250
+ *
1251
+ * @public
1252
+ */
1253
+ updateTheme(theme: TLTheme) {
1254
+ this._themeManager.updateTheme(theme)
1255
+ return this
1256
+ }
1257
+
1055
1258
  /* ------------------- Shape Utils ------------------ */
1056
1259
 
1057
1260
  /**
@@ -1061,6 +1264,9 @@ export class Editor extends EventEmitter<TLEventMap> {
1061
1264
  */
1062
1265
  shapeUtils: { readonly [K in string]?: ShapeUtil<TLShape> }
1063
1266
 
1267
+ /** @internal */
1268
+ private _shapeUtilsByAssetType: { readonly [K in string]?: ShapeUtil<TLShape> } = {}
1269
+
1064
1270
  styleProps: { [key: string]: Map<StyleProp<any>, string> }
1065
1271
 
1066
1272
  /**
@@ -1103,6 +1309,18 @@ export class Editor extends EventEmitter<TLEventMap> {
1103
1309
  return hasOwnProperty(this.shapeUtils, type)
1104
1310
  }
1105
1311
 
1312
+ /**
1313
+ * Get the shape util that handles the given asset type.
1314
+ * Returns the shape util whose {@link ShapeUtil.handledAssetTypes} includes
1315
+ * the given asset type, or undefined if none matches.
1316
+ *
1317
+ * @param assetType - The asset type string.
1318
+ * @public
1319
+ */
1320
+ getShapeUtilForAssetType(assetType: string): ShapeUtil | undefined {
1321
+ return getOwnProperty(this._shapeUtilsByAssetType, assetType)
1322
+ }
1323
+
1106
1324
  /* ------------------- Binding Utils ------------------ */
1107
1325
  /**
1108
1326
  * A map of shape utility classes (TLShapeUtils) by shape type.
@@ -1138,6 +1356,56 @@ export class Editor extends EventEmitter<TLEventMap> {
1138
1356
  return bindingUtil
1139
1357
  }
1140
1358
 
1359
+ /* ------------------- Asset Utils ------------------ */
1360
+
1361
+ /**
1362
+ * A map of asset utility classes by asset type.
1363
+ *
1364
+ * @public
1365
+ */
1366
+ assetUtils: { readonly [K in string]?: AssetUtil<TLAsset> } = {}
1367
+
1368
+ /**
1369
+ * Get an asset util from an asset or asset type.
1370
+ *
1371
+ * @param arg - An asset, asset type string, or object with type.
1372
+ *
1373
+ * @public
1374
+ */
1375
+ getAssetUtil<S extends TLAsset>(asset: S | { type: S['type'] }): AssetUtil<S>
1376
+ getAssetUtil(type: string): AssetUtil
1377
+ getAssetUtil(arg: string | { type: string }) {
1378
+ const type = typeof arg === 'string' ? arg : arg.type
1379
+ const assetUtil = getOwnProperty(this.assetUtils, type)
1380
+ assert(assetUtil, `No asset util found for type "${type}"`)
1381
+ return assetUtil
1382
+ }
1383
+
1384
+ /**
1385
+ * Returns true if the editor has an asset util for the given asset type.
1386
+ *
1387
+ * @public
1388
+ */
1389
+ hasAssetUtil(arg: string | { type: string }): boolean {
1390
+ const type = typeof arg === 'string' ? arg : arg.type
1391
+ return hasOwnProperty(this.assetUtils, type)
1392
+ }
1393
+
1394
+ /**
1395
+ * Get the asset util that accepts the given MIME type.
1396
+ * Returns null if no registered asset util accepts the MIME type.
1397
+ *
1398
+ * @public
1399
+ */
1400
+ getAssetUtilForMimeType(mimeType: string): AssetUtil | null {
1401
+ for (const util of Object.values(this.assetUtils)) {
1402
+ if (util && util.acceptsMimeType(mimeType)) {
1403
+ return util
1404
+ }
1405
+ }
1406
+ return null
1407
+ }
1408
+
1141
1409
  /* --------------------- History -------------------- */
1142
1410
 
1143
1411
  /**
@@ -1161,6 +1429,7 @@ export class Editor extends EventEmitter<TLEventMap> {
1161
1429
  this._flushEventsForTick(0)
1162
1430
  this.complete()
1163
1431
  this.history.undo()
1432
+ this.performance._notifyUndoRedo('undo', this.history.getNumUndos(), this.history.getNumRedos())
1164
1433
  return this
1165
1434
  }
1166
1435
 
@@ -1191,6 +1460,7 @@ export class Editor extends EventEmitter<TLEventMap> {
1191
1460
  this._flushEventsForTick(0)
1192
1461
  this.complete()
1193
1462
  this.history.redo()
1463
+ this.performance._notifyUndoRedo('redo', this.history.getNumUndos(), this.history.getNumRedos())
1194
1464
  return this
1195
1465
  }
1196
1466
 
@@ -5433,8 +5703,8 @@ export class Editor extends EventEmitter<TLEventMap> {
5433
5703
  ? this.getCurrentPageRenderingShapesSorted()
5434
5704
  : this.getCurrentPageShapesSorted()
5435
5705
  ).filter((shape) => {
5436
- // Frames have labels positioned above the shape (outside bounds), so always include them
5437
- if (!candidateIds.has(shape.id) && !this.isShapeOfType(shape, 'frame')) return false
5706
+ // Frame-like shapes have labels positioned above the shape (outside bounds), so always include them
5707
+ if (!candidateIds.has(shape.id) && !this.isShapeFrameLike(shape)) return false
5438
5708
 
5439
5709
  if (
5440
5710
  (shape.isLocked && !hitLocked) ||
@@ -5456,12 +5726,14 @@ export class Editor extends EventEmitter<TLEventMap> {
5456
5726
  const pointInShapeSpace = this.getPointInShapeSpace(shape, point)
5457
5727
 
5458
5728
  // Check labels first
5729
+ const shapeUtil = this.getShapeUtil(shape)
5730
+ const isShapeFrameLike = this.isShapeFrameLike(shape)
5459
5731
  if (
5460
- this.isShapeOfType(shape, 'frame') ||
5732
+ isShapeFrameLike ||
5461
5733
  ((this.isShapeOfType(shape, 'note') ||
5462
5734
  this.isShapeOfType(shape, 'arrow') ||
5463
5735
  (this.isShapeOfType(shape, 'geo') && shape.props.fill === 'none')) &&
5464
- this.getShapeUtil(shape).getText(shape)?.trim())
5736
+ shapeUtil.getText(shape)?.trim())
5465
5737
  ) {
5466
5738
  for (const childGeometry of (geometry as Group2d).children) {
5467
5739
  if (childGeometry.isLabel && childGeometry.isPointInBounds(pointInShapeSpace)) {
@@ -5470,8 +5742,8 @@ export class Editor extends EventEmitter<TLEventMap> {
5470
5742
  }
5471
5743
  }
5472
5744
 
5473
- if (this.isShapeOfType(shape, 'frame')) {
5474
- // On the rare case that we've hit a frame (not its label), test again hitInside to be forced true;
5745
+ if (isShapeFrameLike) {
5746
+ // On the rare case that we've hit a frame-like shape (not its label), test again hitInside to be forced true;
5475
5747
  // this prevents clicks from passing through the body of a frame to shapes behind it.
5476
5748
 
5477
5749
  // If the hit is within the frame's outer margin, then select the frame
@@ -5630,11 +5902,11 @@ export class Editor extends EventEmitter<TLEventMap> {
5630
5902
  const candidateIds = this._spatialIndex.getShapeIdsAtPoint(point, margin)
5631
5903
 
5632
5904
  // Get all page shapes in z-index order and filter to candidates that pass isPointInShape
5633
- // Frames are always checked because their labels can be outside their bounds
5905
+ // Frame-like shapes are always checked because their labels can be outside their bounds
5634
5906
  return this.getCurrentPageShapesSorted()
5635
5907
  .filter((shape) => {
5636
5908
  if (this.isShapeHidden(shape)) return false
5637
- if (!candidateIds.has(shape.id) && !this.isShapeOfType(shape, 'frame')) return false
5909
+ if (!candidateIds.has(shape.id) && !this.isShapeFrameLike(shape)) return false
5638
5910
  return this.isPointInShape(shape, point, opts)
5639
5911
  })
5640
5912
  .reverse()
@@ -5809,6 +6081,25 @@ export class Editor extends EventEmitter<TLEventMap> {
5809
6081
  return shape.type === type
5810
6082
  }
5811
6083
 
6084
+ /**
6085
+ * Get whether a shape behaves like a frame — a container that has child
6086
+ * shapes, requires full-brush selection, blocks erasure from inside, etc.
6087
+ *
6088
+ * @example
6089
+ * ```ts
6090
+ * const isFrameLike = editor.isShapeFrameLike(someShape)
6091
+ * ```
6092
+ *
6093
+ * @param shape - The shape (or shape id) to test.
6094
+ *
6095
+ * @public
6096
+ */
6097
+ isShapeFrameLike(shape: TLShape | TLShapeId): boolean {
6098
+ const _shape = typeof shape === 'string' ? this.getShape(shape) : shape
6099
+ if (!_shape) return false
6100
+ return this.getShapeUtil(_shape).isFrameLike(_shape)
6101
+ }
6102
+
5812
6103
  /**
5813
6104
  * Get a shape by its id.
5814
6105
  *
@@ -10595,6 +10886,7 @@ export class Editor extends EventEmitter<TLEventMap> {
10595
10886
  { immediate: true }
10596
10887
  )
10597
10888
 
10889
+ this.performance._notifyCameraOperation('zooming')
10598
10890
  this.emit('event', info)
10599
10891
  return // Stop here!
10600
10892
  }
@@ -10687,6 +10979,7 @@ export class Editor extends EventEmitter<TLEventMap> {
10687
10979
  immediate: true,
10688
10980
  })
10689
10981
  this.maybeTrackPerformance('Zooming')
10982
+ this.performance._notifyCameraOperation('zooming')
10690
10983
  this.root.handleEvent(info)
10691
10984
  this.emit('event', info)
10692
10985
  return
@@ -10697,6 +10990,7 @@ export class Editor extends EventEmitter<TLEventMap> {
10697
10990
  immediate: true,
10698
10991
  })
10699
10992
  this.maybeTrackPerformance('Panning')
10993
+ this.performance._notifyCameraOperation('panning')
10700
10994
  this.root.handleEvent(info)
10701
10995
  this.emit('event', info)
10702
10996
  return
@@ -10763,6 +11057,10 @@ export class Editor extends EventEmitter<TLEventMap> {
10763
11057
  }
10764
11058
  this.inputs.setIsPanning(true)
10765
11059
  clearTimeout(this._longPressTimeout)
11060
+ } else if (info.button === RIGHT_MOUSE_BUTTON && this.options.rightClickPanning) {
11061
+ this.inputs.setIsRightPointing(true)
11062
+ clearTimeout(this._longPressTimeout)
11063
+ return this
10766
11064
  }
10767
11065
 
10768
11066
  // We might be panning because we did a middle mouse click, or because we're holding spacebar and started a regular click
@@ -10781,9 +11079,31 @@ export class Editor extends EventEmitter<TLEventMap> {
10781
11079
 
10782
11080
  const { x: cx, y: cy, z: cz } = unsafe__withoutCapture(() => this.getCamera())
10783
11081
 
11082
+ // Right-click pointing: waiting to see if this becomes a drag
11083
+ if (this.inputs.getIsRightPointing() && !this.inputs.getIsPanning()) {
11084
+ const currentScreenPoint = this.inputs.getCurrentScreenPoint()
11085
+ const originScreenPoint = this.inputs.getOriginScreenPoint()
11086
+ if (
11087
+ Vec.Dist2(originScreenPoint, currentScreenPoint) > this.options.dragDistanceSquared
11088
+ ) {
11089
+ // Passed the drag threshold—transition to panning
11090
+ this._prevCursor = this.getInstanceState().cursor.type
11091
+ this.inputs.setIsPanning(true)
11092
+ this.setCursor({ type: 'grabbing', rotation: 0 })
11093
+ this.stopCameraAnimation()
11094
+ // Apply the initial delta from the pointer down origin
11095
+ const offset = Vec.Sub(currentScreenPoint, originScreenPoint)
11096
+ this.setCamera(new Vec(cx + offset.x / cz, cy + offset.y / cz, cz), {
11097
+ immediate: true,
11098
+ })
11099
+ this.maybeTrackPerformance('Panning')
11100
+ }
11101
+ return
11102
+ }
11103
+
10784
11104
  // If we've started panning, then clear any long press timeout
10785
11105
  if (this.inputs.getIsPanning() && this.inputs.getIsPointing()) {
10786
- // Handle spacebar / middle mouse button panning
11106
+ // Handle spacebar / middle mouse button / right-click panning
10787
11107
  const currentScreenPoint = this.inputs.getCurrentScreenPoint()
10788
11108
  const previousScreenPoint = this.inputs.getPreviousScreenPoint()
10789
11109
  const offset = Vec.Sub(currentScreenPoint, previousScreenPoint)
@@ -10791,6 +11111,7 @@ export class Editor extends EventEmitter<TLEventMap> {
10791
11111
  immediate: true,
10792
11112
  })
10793
11113
  this.maybeTrackPerformance('Panning')
11114
+ this.performance._notifyCameraOperation('panning')
10794
11115
  return
10795
11116
  }
10796
11117
 
@@ -10815,13 +11136,24 @@ export class Editor extends EventEmitter<TLEventMap> {
10815
11136
  inputs.setIsDragging(false)
10816
11137
  inputs.setIsPointing(false)
10817
11138
  clearTimeout(this._longPressTimeout)
10818
-
10819
11139
  // Remove the button from the buttons set
10820
11140
  inputs.buttons.delete(info.button)
10821
11141
 
10822
11142
  // If we're in pen mode and we're not using a pen, stop here
10823
11143
  if (instanceState.isPenMode && !isPen) return
10824
11144
 
11145
+ // Right-click pointing ended without dragging—this is a static
11146
+ // right-click, so let it through to the state chart as right_click.
11147
+ // Check isPanning first: if we transitioned to panning, isRightPointing
11148
+ // is still true but we want the panning cleanup path instead.
11149
+ if (this.inputs.getIsRightPointing() && !this.inputs.getIsPanning()) {
11150
+ this.inputs.setIsRightPointing(false)
11151
+ this._selectedShapeIdsAtPointerDown = []
11152
+ break // fall through to state chart dispatch as right_click
11153
+ }
11154
+
11155
+ this.inputs.setIsRightPointing(false)
11156
+
10825
11157
  // Firefox bug fix...
10826
11158
  // If it's the same pointer that we stored earlier...
10827
11159
  // ... then it's probably still a left-mouse-click!
@@ -10844,11 +11176,26 @@ export class Editor extends EventEmitter<TLEventMap> {
10844
11176
  break
10845
11177
  }
10846
11178
  case MIDDLE_MOUSE_BUTTON: {
10847
- if (this.inputs.keys.has(' ')) {
11179
+ if (this.inputs.keys.has('Space')) {
10848
11180
  this.setCursor({ type: 'grab', rotation: 0 })
10849
11181
  } else {
10850
11182
  this.setCursor({ type: this._prevCursor, rotation: 0 })
10851
11183
  }
11184
+ break
11185
+ }
11186
+ case RIGHT_MOUSE_BUTTON: {
11187
+ if (this.inputs.keys.has('Space')) {
11188
+ this.setCursor({ type: 'grab', rotation: 0 })
11189
+ } else {
11190
+ this.setCursor({ type: this._prevCursor, rotation: 0 })
11191
+ }
11192
+ // Don't pass right-click panning events to the state chart
11193
+ // as it causes unintended shape selection on release
11194
+ if (slideSpeed > 0) {
11195
+ this.slideCamera({ speed: slideSpeed, direction: slideDirection })
11196
+ }
11197
+ this._selectedShapeIdsAtPointerDown = []
11198
+ return this
10852
11199
  }
10853
11200
  }
10854
11201