@tldraw/editor 5.4.0-next.ebc95cf01337 → 5.4.0

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 (233) hide show
  1. package/dist-cjs/index.d.ts +67 -1
  2. package/dist-cjs/index.js +4 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/TldrawEditor.js +7 -2
  5. package/dist-cjs/lib/TldrawEditor.js.map +2 -2
  6. package/dist-cjs/lib/components/MenuClickCapture.js +6 -8
  7. package/dist-cjs/lib/components/MenuClickCapture.js.map +2 -2
  8. package/dist-cjs/lib/components/default-components/CanvasOverlays.js +1 -3
  9. package/dist-cjs/lib/components/default-components/CanvasOverlays.js.map +2 -2
  10. package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js +1 -1
  11. package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js.map +1 -1
  12. package/dist-cjs/lib/config/TLUserPreferences.js +6 -2
  13. package/dist-cjs/lib/config/TLUserPreferences.js.map +2 -2
  14. package/dist-cjs/lib/config/createTLStore.js +1 -1
  15. package/dist-cjs/lib/config/createTLStore.js.map +2 -2
  16. package/dist-cjs/lib/editor/Editor.js +164 -280
  17. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  18. package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js +3 -3
  19. package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js.map +2 -2
  20. package/dist-cjs/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.js +8 -8
  21. package/dist-cjs/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.js.map +2 -2
  22. package/dist-cjs/lib/editor/managers/FontManager/FontManager.js +2 -1
  23. package/dist-cjs/lib/editor/managers/FontManager/FontManager.js.map +2 -2
  24. package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js +79 -6
  25. package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js.map +2 -2
  26. package/dist-cjs/lib/editor/managers/ScribbleManager/ScribbleManager.js +7 -0
  27. package/dist-cjs/lib/editor/managers/ScribbleManager/ScribbleManager.js.map +2 -2
  28. package/dist-cjs/lib/editor/managers/SnapManager/BoundsSnaps.js +0 -2
  29. package/dist-cjs/lib/editor/managers/SnapManager/BoundsSnaps.js.map +2 -2
  30. package/dist-cjs/lib/editor/managers/TextManager/TextManager.js +1 -2
  31. package/dist-cjs/lib/editor/managers/TextManager/TextManager.js.map +2 -2
  32. package/dist-cjs/lib/editor/managers/TickManager/TickManager.js +6 -3
  33. package/dist-cjs/lib/editor/managers/TickManager/TickManager.js.map +2 -2
  34. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js +12 -1
  35. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +2 -2
  36. package/dist-cjs/lib/editor/overlays/OverlayManager.js +3 -7
  37. package/dist-cjs/lib/editor/overlays/OverlayManager.js.map +2 -2
  38. package/dist-cjs/lib/editor/overlays/strokeShapeIndicators.js +0 -1
  39. package/dist-cjs/lib/editor/overlays/strokeShapeIndicators.js.map +2 -2
  40. package/dist-cjs/lib/editor/shapes/BaseFrameLikeShapeUtil.js +3 -1
  41. package/dist-cjs/lib/editor/shapes/BaseFrameLikeShapeUtil.js.map +2 -2
  42. package/dist-cjs/lib/editor/shapes/shared/getPerfectDashProps.js +1 -0
  43. package/dist-cjs/lib/editor/shapes/shared/getPerfectDashProps.js.map +2 -2
  44. package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js +1 -1
  45. package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js.map +2 -2
  46. package/dist-cjs/lib/editor/tools/StateNode.js +3 -2
  47. package/dist-cjs/lib/editor/tools/StateNode.js.map +2 -2
  48. package/dist-cjs/lib/globals/editors.js +57 -0
  49. package/dist-cjs/lib/globals/editors.js.map +7 -0
  50. package/dist-cjs/lib/globals/menus.js +9 -4
  51. package/dist-cjs/lib/globals/menus.js.map +2 -2
  52. package/dist-cjs/lib/hooks/useCanvasEvents.js +2 -0
  53. package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
  54. package/dist-cjs/lib/hooks/useDocumentEvents.js +39 -9
  55. package/dist-cjs/lib/hooks/useDocumentEvents.js.map +2 -2
  56. package/dist-cjs/lib/hooks/useLocalStore.js +10 -6
  57. package/dist-cjs/lib/hooks/useLocalStore.js.map +2 -2
  58. package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js +1 -0
  59. package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js.map +2 -2
  60. package/dist-cjs/lib/hooks/usePeerIds.js +2 -1
  61. package/dist-cjs/lib/hooks/usePeerIds.js.map +2 -2
  62. package/dist-cjs/lib/license/LicenseManager.js +9 -2
  63. package/dist-cjs/lib/license/LicenseManager.js.map +2 -2
  64. package/dist-cjs/lib/license/LicenseProvider.js +16 -3
  65. package/dist-cjs/lib/license/LicenseProvider.js.map +2 -2
  66. package/dist-cjs/lib/license/useLicenseManagerState.js +5 -4
  67. package/dist-cjs/lib/license/useLicenseManagerState.js.map +2 -2
  68. package/dist-cjs/lib/primitives/Box.js +2 -1
  69. package/dist-cjs/lib/primitives/Box.js.map +2 -2
  70. package/dist-cjs/lib/primitives/Vec.js +4 -3
  71. package/dist-cjs/lib/primitives/Vec.js.map +2 -2
  72. package/dist-cjs/lib/primitives/utils.js +2 -1
  73. package/dist-cjs/lib/primitives/utils.js.map +2 -2
  74. package/dist-cjs/lib/utils/deepLinks.js +5 -1
  75. package/dist-cjs/lib/utils/deepLinks.js.map +2 -2
  76. package/dist-cjs/lib/utils/rotation.js +1 -9
  77. package/dist-cjs/lib/utils/rotation.js.map +2 -2
  78. package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js +17 -0
  79. package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js.map +2 -2
  80. package/dist-cjs/lib/utils/sync/alerts.js +1 -1
  81. package/dist-cjs/lib/utils/sync/alerts.js.map +1 -1
  82. package/dist-cjs/version.js +3 -3
  83. package/dist-cjs/version.js.map +1 -1
  84. package/dist-esm/index.d.mts +67 -1
  85. package/dist-esm/index.mjs +9 -2
  86. package/dist-esm/index.mjs.map +2 -2
  87. package/dist-esm/lib/TldrawEditor.mjs +9 -4
  88. package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
  89. package/dist-esm/lib/components/MenuClickCapture.mjs +6 -8
  90. package/dist-esm/lib/components/MenuClickCapture.mjs.map +2 -2
  91. package/dist-esm/lib/components/default-components/CanvasOverlays.mjs +1 -3
  92. package/dist-esm/lib/components/default-components/CanvasOverlays.mjs.map +2 -2
  93. package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs +1 -1
  94. package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs.map +1 -1
  95. package/dist-esm/lib/config/TLUserPreferences.mjs +6 -2
  96. package/dist-esm/lib/config/TLUserPreferences.mjs.map +2 -2
  97. package/dist-esm/lib/config/createTLStore.mjs +1 -1
  98. package/dist-esm/lib/config/createTLStore.mjs.map +2 -2
  99. package/dist-esm/lib/editor/Editor.mjs +172 -281
  100. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  101. package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs +3 -3
  102. package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs.map +2 -2
  103. package/dist-esm/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.mjs +9 -9
  104. package/dist-esm/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.mjs.map +2 -2
  105. package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs +2 -1
  106. package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs.map +2 -2
  107. package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs +81 -8
  108. package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs.map +2 -2
  109. package/dist-esm/lib/editor/managers/ScribbleManager/ScribbleManager.mjs +7 -0
  110. package/dist-esm/lib/editor/managers/ScribbleManager/ScribbleManager.mjs.map +2 -2
  111. package/dist-esm/lib/editor/managers/SnapManager/BoundsSnaps.mjs +0 -2
  112. package/dist-esm/lib/editor/managers/SnapManager/BoundsSnaps.mjs.map +2 -2
  113. package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs +2 -3
  114. package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs.map +2 -2
  115. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs +6 -3
  116. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs.map +2 -2
  117. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs +12 -1
  118. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +2 -2
  119. package/dist-esm/lib/editor/overlays/OverlayManager.mjs +3 -7
  120. package/dist-esm/lib/editor/overlays/OverlayManager.mjs.map +2 -2
  121. package/dist-esm/lib/editor/overlays/strokeShapeIndicators.mjs +0 -1
  122. package/dist-esm/lib/editor/overlays/strokeShapeIndicators.mjs.map +2 -2
  123. package/dist-esm/lib/editor/shapes/BaseFrameLikeShapeUtil.mjs +3 -1
  124. package/dist-esm/lib/editor/shapes/BaseFrameLikeShapeUtil.mjs.map +2 -2
  125. package/dist-esm/lib/editor/shapes/shared/getPerfectDashProps.mjs +1 -0
  126. package/dist-esm/lib/editor/shapes/shared/getPerfectDashProps.mjs.map +2 -2
  127. package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs +1 -1
  128. package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs.map +2 -2
  129. package/dist-esm/lib/editor/tools/StateNode.mjs +3 -2
  130. package/dist-esm/lib/editor/tools/StateNode.mjs.map +2 -2
  131. package/dist-esm/lib/globals/editors.mjs +37 -0
  132. package/dist-esm/lib/globals/editors.mjs.map +7 -0
  133. package/dist-esm/lib/globals/menus.mjs +9 -4
  134. package/dist-esm/lib/globals/menus.mjs.map +2 -2
  135. package/dist-esm/lib/hooks/useCanvasEvents.mjs +2 -0
  136. package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
  137. package/dist-esm/lib/hooks/useDocumentEvents.mjs +40 -10
  138. package/dist-esm/lib/hooks/useDocumentEvents.mjs.map +2 -2
  139. package/dist-esm/lib/hooks/useLocalStore.mjs +11 -7
  140. package/dist-esm/lib/hooks/useLocalStore.mjs.map +2 -2
  141. package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs +1 -0
  142. package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs.map +2 -2
  143. package/dist-esm/lib/hooks/usePeerIds.mjs +2 -1
  144. package/dist-esm/lib/hooks/usePeerIds.mjs.map +2 -2
  145. package/dist-esm/lib/license/LicenseManager.mjs +9 -2
  146. package/dist-esm/lib/license/LicenseManager.mjs.map +2 -2
  147. package/dist-esm/lib/license/LicenseProvider.mjs +16 -3
  148. package/dist-esm/lib/license/LicenseProvider.mjs.map +2 -2
  149. package/dist-esm/lib/license/useLicenseManagerState.mjs +5 -4
  150. package/dist-esm/lib/license/useLicenseManagerState.mjs.map +2 -2
  151. package/dist-esm/lib/primitives/Box.mjs +2 -1
  152. package/dist-esm/lib/primitives/Box.mjs.map +2 -2
  153. package/dist-esm/lib/primitives/Vec.mjs +4 -3
  154. package/dist-esm/lib/primitives/Vec.mjs.map +2 -2
  155. package/dist-esm/lib/primitives/utils.mjs +2 -1
  156. package/dist-esm/lib/primitives/utils.mjs.map +2 -2
  157. package/dist-esm/lib/utils/deepLinks.mjs +5 -1
  158. package/dist-esm/lib/utils/deepLinks.mjs.map +2 -2
  159. package/dist-esm/lib/utils/rotation.mjs +1 -9
  160. package/dist-esm/lib/utils/rotation.mjs.map +2 -2
  161. package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs +17 -0
  162. package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs.map +2 -2
  163. package/dist-esm/lib/utils/sync/alerts.mjs +1 -1
  164. package/dist-esm/lib/utils/sync/alerts.mjs.map +1 -1
  165. package/dist-esm/version.mjs +3 -3
  166. package/dist-esm/version.mjs.map +1 -1
  167. package/package.json +13 -7
  168. package/src/index.ts +6 -1
  169. package/src/lib/TldrawEditor.tsx +16 -4
  170. package/src/lib/components/MenuClickCapture.tsx +19 -18
  171. package/src/lib/components/default-components/CanvasOverlays.tsx +1 -3
  172. package/src/lib/components/default-components/DefaultErrorFallback.tsx +1 -1
  173. package/src/lib/config/TLUserPreferences.test.ts +18 -1
  174. package/src/lib/config/TLUserPreferences.ts +8 -4
  175. package/src/lib/config/createTLStore.ts +1 -1
  176. package/src/lib/editor/Editor.ts +241 -359
  177. package/src/lib/editor/managers/ClickManager/ClickManager.ts +5 -3
  178. package/src/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.ts +9 -9
  179. package/src/lib/editor/managers/FontManager/FontManager.ts +2 -1
  180. package/src/lib/editor/managers/InputsManager/InputsManager.test.ts +199 -3
  181. package/src/lib/editor/managers/InputsManager/InputsManager.ts +128 -9
  182. package/src/lib/editor/managers/ScribbleManager/ScribbleManager.ts +12 -1
  183. package/src/lib/editor/managers/SnapManager/BoundsSnaps.ts +0 -2
  184. package/src/lib/editor/managers/TextManager/TextManager.ts +2 -6
  185. package/src/lib/editor/managers/TickManager/TickManager.test.ts +13 -0
  186. package/src/lib/editor/managers/TickManager/TickManager.ts +9 -3
  187. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +64 -3
  188. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.ts +18 -1
  189. package/src/lib/editor/overlays/OverlayManager.ts +3 -7
  190. package/src/lib/editor/overlays/strokeShapeIndicators.ts +0 -1
  191. package/src/lib/editor/shapes/BaseFrameLikeShapeUtil.tsx +5 -1
  192. package/src/lib/editor/shapes/shared/getPerfectDashProps.ts +3 -0
  193. package/src/lib/editor/tools/BaseBoxShapeTool/children/Pointing.ts +4 -1
  194. package/src/lib/editor/tools/StateNode.test.ts +64 -0
  195. package/src/lib/editor/tools/StateNode.ts +12 -2
  196. package/src/lib/globals/editors.test.ts +76 -0
  197. package/src/lib/globals/editors.ts +53 -0
  198. package/src/lib/globals/menus.test.ts +33 -0
  199. package/src/lib/globals/menus.ts +11 -4
  200. package/src/lib/hooks/useCanvasEvents.ts +2 -0
  201. package/src/lib/hooks/useDocumentEvents.ts +60 -16
  202. package/src/lib/hooks/useLocalStore.ts +16 -8
  203. package/src/lib/hooks/usePassThroughWheelEvents.ts +3 -0
  204. package/src/lib/hooks/usePeerIds.ts +2 -1
  205. package/src/lib/license/LicenseManager.test.ts +160 -0
  206. package/src/lib/license/LicenseManager.ts +13 -4
  207. package/src/lib/license/LicenseProvider.tsx +24 -3
  208. package/src/lib/license/useLicenseManagerState.ts +8 -6
  209. package/src/lib/primitives/Box.test.ts +9 -1
  210. package/src/lib/primitives/Box.ts +3 -1
  211. package/src/lib/primitives/Vec.test.ts +10 -0
  212. package/src/lib/primitives/Vec.ts +4 -4
  213. package/src/lib/primitives/geometry/Arc2d.test.ts +226 -3
  214. package/src/lib/primitives/geometry/CubicBezier2d.test.ts +173 -3
  215. package/src/lib/primitives/geometry/CubicSpline2d.test.ts +160 -3
  216. package/src/lib/primitives/geometry/Ellipse2d.test.ts +243 -3
  217. package/src/lib/primitives/geometry/Geometry2d.test.ts +553 -1
  218. package/src/lib/primitives/geometry/Group2d.test.ts +398 -3
  219. package/src/lib/primitives/geometry/Polyline2d.test.ts +207 -0
  220. package/src/lib/primitives/geometry/Stadium2d.test.ts +191 -3
  221. package/src/lib/primitives/utils.test.ts +10 -1
  222. package/src/lib/primitives/utils.ts +4 -1
  223. package/src/lib/test/useCanvasEvents.test.tsx +74 -0
  224. package/src/lib/test/useDocumentEvents.test.tsx +107 -0
  225. package/src/lib/test/usePassThroughWheelEvents.test.tsx +25 -2
  226. package/src/lib/utils/deepLinks.test.ts +15 -0
  227. package/src/lib/utils/deepLinks.ts +7 -1
  228. package/src/lib/utils/rotation.ts +4 -12
  229. package/src/lib/utils/sync/TLLocalSyncClient.test.ts +56 -0
  230. package/src/lib/utils/sync/TLLocalSyncClient.ts +24 -0
  231. package/src/lib/utils/sync/alerts.ts +1 -1
  232. package/src/version.ts +3 -3
  233. package/src/lib/primitives/geometry/Polyline.test.ts +0 -5
package/src/index.ts CHANGED
@@ -290,6 +290,7 @@ export {
290
290
  export { getOwnerDocument, getOwnerWindow } from './lib/exports/domUtils'
291
291
  export { getSvgAsImage } from './lib/exports/getSvgAsImage'
292
292
  export { tlenv, tlenvReactive } from './lib/globals/environment'
293
+ export { tleditors } from './lib/globals/editors'
293
294
  export { tlmenus } from './lib/globals/menus'
294
295
  export { tltime } from './lib/globals/time'
295
296
  export {
@@ -341,7 +342,11 @@ export {
341
342
  type LicenseState,
342
343
  type ValidLicenseKeyResult,
343
344
  } from './lib/license/LicenseManager'
344
- export { LICENSE_TIMEOUT, useLicenseContext } from './lib/license/LicenseProvider'
345
+ export {
346
+ LICENSE_TIMEOUT,
347
+ useLicenseContext,
348
+ useMaybeLicenseManager,
349
+ } from './lib/license/LicenseProvider'
345
350
  export { useLicenseFeatureFlag } from './lib/license/useLicenseManagerState'
346
351
  export {
347
352
  defaultTldrawOptions,
@@ -51,11 +51,11 @@ import { useLocalStore } from './hooks/useLocalStore'
51
51
  import { useRefState } from './hooks/useRefState'
52
52
  import { useStateAttribute } from './hooks/useStateAttribute'
53
53
  import { useZoomCss } from './hooks/useZoomCss'
54
- import { LicenseProvider } from './license/LicenseProvider'
54
+ import { LicenseProvider, useLicenseContext } from './license/LicenseProvider'
55
55
  import { Watermark } from './license/Watermark'
56
56
  import { TldrawOptions } from './options'
57
57
  import { TLDeepLinkOptions } from './utils/deepLinks'
58
- import { getGlobalDocument } from './utils/dom'
58
+ import { getGlobalDocument, getGlobalWindow } from './utils/dom'
59
59
  import { TLTextOptions } from './utils/richText'
60
60
  import { TLStoreWithStatus } from './utils/sync/StoreWithStatus'
61
61
 
@@ -433,11 +433,20 @@ const TldrawEditorWithLoadingStore = memo(function TldrawEditorBeforeLoading({
433
433
  const container = useContainer()
434
434
 
435
435
  useLayoutEffect(() => {
436
- if (user.userPreferences.get().colorScheme === 'dark') {
436
+ // Resolve the scheme the same way UserPreferencesManager.getIsDarkMode will once the
437
+ // editor mounts, so a 'system' user on a dark OS doesn't get a light loading screen that
438
+ // flips dark on mount.
439
+ const scheme = user.userPreferences.get().colorScheme ?? rest.colorScheme ?? 'light'
440
+ const isDark =
441
+ scheme === 'dark' ||
442
+ (scheme === 'system' &&
443
+ typeof window !== 'undefined' &&
444
+ !!getGlobalWindow().matchMedia?.('(prefers-color-scheme: dark)').matches)
445
+ if (isDark) {
437
446
  container.classList.remove('tl-theme__light')
438
447
  container.classList.add('tl-theme__dark')
439
448
  }
440
- }, [container, user])
449
+ }, [container, user, rest.colorScheme])
441
450
 
442
451
  const { LoadingScreen } = useEditorComponents()
443
452
 
@@ -497,6 +506,7 @@ function TldrawEditorWithReadyStore({
497
506
  >) {
498
507
  const { ErrorFallback } = useEditorComponents()
499
508
  const container = useContainer()
509
+ const licenseManager = useLicenseContext()
500
510
 
501
511
  const [editor, setEditor] = useRefState<Editor | null>(null)
502
512
 
@@ -563,6 +573,7 @@ function TldrawEditorWithReadyStore({
563
573
  themes: themes,
564
574
  initialTheme: initialTheme,
565
575
  })
576
+ editor.licenseManager = licenseManager
566
577
 
567
578
  editor.updateViewportScreenBounds(canvasRef.current ?? container)
568
579
 
@@ -598,6 +609,7 @@ function TldrawEditorWithReadyStore({
598
609
  user,
599
610
  setEditor,
600
611
  licenseKey,
612
+ licenseManager,
601
613
  getShapeVisibility,
602
614
  assetUrls,
603
615
  ]
@@ -72,6 +72,22 @@ export function MenuClickCapture() {
72
72
  const button = getPointerEventButton(e)
73
73
  if (button !== 0 && button !== 2) return
74
74
 
75
+ if (button === 2 && editor.options.rightClickPanning) {
76
+ // Forward right-click pointerdown through the canvas's own handler so
77
+ // pointer capture is set on the canvas (load-bearing: without this
78
+ // the context menu briefly flashes closed during consecutive right-clicks).
79
+ // The canvas owns the rest of the gesture, so don't capture or mark
80
+ // pointing here: this element's pointerup would never fire, leaving it
81
+ // mounted over the canvas after the menu closes.
82
+ // We don't clearOpenMenus() — Radix's DismissableLayer closes the menu
83
+ // via outside-click detection, keeping its internal state in sync.
84
+ const canvas =
85
+ editor.getContainer().querySelector<HTMLDivElement>('.tl-canvas') ?? e.currentTarget
86
+ canvasEvents.onPointerDown?.({ ...e, currentTarget: canvas })
87
+ swallowNextNativeContextMenu()
88
+ return
89
+ }
90
+
75
91
  flushSync(() => setIsPointing(true))
76
92
  setPointerCapture(e.currentTarget, e)
77
93
  rPointerState.current = {
@@ -82,23 +98,9 @@ export function MenuClickCapture() {
82
98
  }
83
99
 
84
100
  if (button === 2) {
85
- if (!editor.options.rightClickPanning) {
86
- // Right-click panning off: close the open menu and swallow the native
87
- // contextmenu that would otherwise briefly open a new one (causing a flash).
88
- swallowNextNativeContextMenu()
89
- editor.menus.clearOpenMenus()
90
- return
91
- }
92
- // Forward right-click pointerdown through the canvas's own handler so
93
- // pointer capture is also set on the canvas (load-bearing: without this
94
- // the context menu briefly flashes closed during consecutive right-clicks).
95
- // We don't clearOpenMenus() — Radix's DismissableLayer closes the menu
96
- // via outside-click detection, keeping its internal state in sync.
97
- const canvas =
98
- editor.getContainer().querySelector<HTMLDivElement>('.tl-canvas') ?? e.currentTarget
99
- canvasEvents.onPointerDown?.({ ...e, currentTarget: canvas })
101
+ // Right-click panning off: swallow the native contextmenu that would otherwise
102
+ // briefly open a new menu (causing a flash) once the open one closes below.
100
103
  swallowNextNativeContextMenu()
101
- return
102
104
  }
103
105
 
104
106
  editor.menus.clearOpenMenus()
@@ -113,8 +115,7 @@ export function MenuClickCapture() {
113
115
 
114
116
  // Left-click: wait for the drag threshold before forwarding anything, then
115
117
  // replay pointerdown at the original start so the editor records the
116
- // correct drag origin. Right-click forwards moves immediately (pointerdown
117
- // was already dispatched in handlePointerDown).
118
+ // correct drag origin. Right-click forwards moves immediately.
118
119
  if (state.button !== 2 && !state.isDragging) {
119
120
  if (
120
121
  Vec.Dist2(state.start, new Vec(e.clientX, e.clientY)) <=
@@ -94,11 +94,9 @@ export const CanvasOverlays = memo(function CanvasOverlays() {
94
94
  if (!shape || shape.type === 'group') continue
95
95
 
96
96
  const geometry = editor.getShapeGeometry(shape)
97
- const pageTransform = editor.getShapePageTransform(shape)
98
- if (!pageTransform) continue
97
+ const m = editor.getShapePageTransform(shape)
99
98
 
100
99
  ctx.save()
101
- const m = pageTransform
102
100
  ctx.transform(m.a, m.b, m.c, m.d, m.e, m.f)
103
101
 
104
102
  // Outline
@@ -220,7 +220,7 @@ My browser: ${navigator.userAgent}`
220
220
  Reset data
221
221
  </button>
222
222
  <button className="tlui-button tl-error-boundary__refresh" onClick={refresh}>
223
- Refresh Page
223
+ Refresh page
224
224
  </button>
225
225
  </div>
226
226
  </div>
@@ -1,4 +1,5 @@
1
- import { describe, expect, it } from 'vitest'
1
+ import { deleteFromLocalStorage, setInLocalStorage } from '@tldraw/utils'
2
+ import { afterEach, describe, expect, it, vi } from 'vitest'
2
3
  import { defaultUserPreferences, userTypeValidator } from './TLUserPreferences'
3
4
 
4
5
  describe('TLUserPreferences consistency', () => {
@@ -39,3 +40,19 @@ describe('TLUserPreferences consistency', () => {
39
40
  expect(validatorKeys).toEqual(expected)
40
41
  })
41
42
  })
43
+
44
+ describe('loading stored preferences', () => {
45
+ afterEach(() => {
46
+ deleteFromLocalStorage('TLDRAW_USER_DATA_v3')
47
+ vi.resetModules()
48
+ })
49
+
50
+ it.each([
51
+ ['corrupt json', '{not json'],
52
+ ['a snapshot whose user is not an object', JSON.stringify({ version: 1, user: null })],
53
+ ])('falls back to fresh preferences for %s', async (_label, stored) => {
54
+ setInLocalStorage('TLDRAW_USER_DATA_v3', stored)
55
+ const { getUserPreferences } = await import('./TLUserPreferences')
56
+ expect(getUserPreferences().id).toEqual(expect.any(String))
57
+ })
58
+ })
@@ -201,9 +201,9 @@ function migrateUserPreferences(userData: unknown): TLUserPreferences {
201
201
 
202
202
  const snapshot = structuredClone(userData) as any
203
203
 
204
- migrateSnapshot(snapshot)
205
-
206
204
  try {
205
+ // migration dereferences `user`, which malformed stored data may not have as an object
206
+ migrateSnapshot(snapshot)
207
207
  return userTypeValidator.validate(snapshot.user)
208
208
  } catch {
209
209
  return getFreshUserPreferences()
@@ -211,8 +211,12 @@ function migrateUserPreferences(userData: unknown): TLUserPreferences {
211
211
  }
212
212
 
213
213
  function loadUserPreferences(): TLUserPreferences {
214
- const userData = (JSON.parse(getFromLocalStorage(USER_DATA_KEY) || 'null') ??
215
- null) as null | UserDataSnapshot
214
+ let userData: unknown = null
215
+ try {
216
+ userData = JSON.parse(getFromLocalStorage(USER_DATA_KEY) || 'null')
217
+ } catch {
218
+ // corrupt stored data is treated as absent rather than blocking the editor from mounting
219
+ }
216
220
 
217
221
  return migrateUserPreferences(userData)
218
222
  }
@@ -181,7 +181,7 @@ export function createTLStore({
181
181
  },
182
182
  onMount: (editor) => {
183
183
  assert(editor instanceof Editor)
184
- onMount?.(editor)
184
+ return onMount?.(editor)
185
185
  },
186
186
  collaboration,
187
187
  },