tldraw 4.3.0-canary.da35795ba8e2 → 4.3.0-canary.e1766dd4eab3

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 (310) hide show
  1. package/dist-cjs/index.d.ts +26 -5
  2. package/dist-cjs/index.js +2 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/bindings/arrow/ArrowBindingUtil.js.map +2 -2
  5. package/dist-cjs/lib/canvas/TldrawSelectionForeground.js +2 -2
  6. package/dist-cjs/lib/canvas/TldrawSelectionForeground.js.map +2 -2
  7. package/dist-cjs/lib/defaultExternalContentHandlers.js.map +2 -2
  8. package/dist-cjs/lib/shapes/arrow/ArrowShapeUtil.js +9 -12
  9. package/dist-cjs/lib/shapes/arrow/ArrowShapeUtil.js.map +2 -2
  10. package/dist-cjs/lib/shapes/arrow/arrow-types.js.map +1 -1
  11. package/dist-cjs/lib/shapes/arrow/arrowLabel.js.map +2 -2
  12. package/dist-cjs/lib/shapes/arrow/arrowTargetState.js.map +2 -2
  13. package/dist-cjs/lib/shapes/arrow/elbow/elbowArrowSnapLines.js.map +2 -2
  14. package/dist-cjs/lib/shapes/arrow/shared.js.map +2 -2
  15. package/dist-cjs/lib/shapes/arrow/toolStates/Pointing.js.map +2 -2
  16. package/dist-cjs/lib/shapes/bookmark/bookmarks.js.map +2 -2
  17. package/dist-cjs/lib/shapes/draw/DrawShapeUtil.js +3 -3
  18. package/dist-cjs/lib/shapes/draw/DrawShapeUtil.js.map +2 -2
  19. package/dist-cjs/lib/shapes/draw/toolStates/Drawing.js.map +2 -2
  20. package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js.map +2 -2
  21. package/dist-cjs/lib/shapes/frame/FrameShapeTool.js.map +1 -1
  22. package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js +1 -1
  23. package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js.map +2 -2
  24. package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js +10 -6
  25. package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js.map +2 -2
  26. package/dist-cjs/lib/shapes/geo/toolStates/Pointing.js.map +2 -2
  27. package/dist-cjs/lib/shapes/highlight/HighlightShapeUtil.js +1 -1
  28. package/dist-cjs/lib/shapes/highlight/HighlightShapeUtil.js.map +2 -2
  29. package/dist-cjs/lib/shapes/line/toolStates/Pointing.js.map +2 -2
  30. package/dist-cjs/lib/shapes/note/NoteShapeUtil.js +5 -5
  31. package/dist-cjs/lib/shapes/note/NoteShapeUtil.js.map +2 -2
  32. package/dist-cjs/lib/shapes/note/noteHelpers.js.map +2 -2
  33. package/dist-cjs/lib/shapes/shared/HyperlinkButton.js +2 -1
  34. package/dist-cjs/lib/shapes/shared/HyperlinkButton.js.map +2 -2
  35. package/dist-cjs/lib/shapes/shared/PlainTextLabel.js +14 -2
  36. package/dist-cjs/lib/shapes/shared/PlainTextLabel.js.map +3 -3
  37. package/dist-cjs/lib/shapes/shared/RichTextLabel.js +11 -3
  38. package/dist-cjs/lib/shapes/shared/RichTextLabel.js.map +3 -3
  39. package/dist-cjs/lib/shapes/shared/ShapeFill.js +2 -2
  40. package/dist-cjs/lib/shapes/shared/ShapeFill.js.map +2 -2
  41. package/dist-cjs/lib/shapes/shared/crop.js +1 -0
  42. package/dist-cjs/lib/shapes/shared/crop.js.map +2 -2
  43. package/dist-cjs/lib/shapes/shared/useEditablePlainText.js.map +2 -2
  44. package/dist-cjs/lib/shapes/shared/useEditableRichText.js.map +2 -2
  45. package/dist-cjs/lib/shapes/shared/{useForceSolid.js → useEfficientZoomThreshold.js} +10 -7
  46. package/dist-cjs/lib/shapes/shared/useEfficientZoomThreshold.js.map +7 -0
  47. package/dist-cjs/lib/shapes/shared/useImageOrVideoAsset.js +1 -1
  48. package/dist-cjs/lib/shapes/shared/useImageOrVideoAsset.js.map +2 -2
  49. package/dist-cjs/lib/shapes/text/TextShapeUtil.js +5 -2
  50. package/dist-cjs/lib/shapes/text/TextShapeUtil.js.map +2 -2
  51. package/dist-cjs/lib/shapes/text/toolStates/Pointing.js.map +2 -2
  52. package/dist-cjs/lib/shapes/video/VideoShapeUtil.js +1 -1
  53. package/dist-cjs/lib/shapes/video/VideoShapeUtil.js.map +2 -2
  54. package/dist-cjs/lib/tools/EraserTool/childStates/Erasing.js.map +2 -2
  55. package/dist-cjs/lib/tools/EraserTool/childStates/Pointing.js.map +2 -2
  56. package/dist-cjs/lib/tools/SelectTool/DragAndDropManager.js +1 -4
  57. package/dist-cjs/lib/tools/SelectTool/DragAndDropManager.js.map +2 -2
  58. package/dist-cjs/lib/tools/SelectTool/childStates/Brushing.js.map +2 -2
  59. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/Idle.js.map +2 -2
  60. package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js +1 -1
  61. package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js.map +2 -2
  62. package/dist-cjs/lib/tools/SelectTool/childStates/EditingShape.js +30 -10
  63. package/dist-cjs/lib/tools/SelectTool/childStates/EditingShape.js.map +2 -2
  64. package/dist-cjs/lib/tools/SelectTool/childStates/Idle.js.map +2 -2
  65. package/dist-cjs/lib/tools/SelectTool/childStates/PointingArrowLabel.js.map +2 -2
  66. package/dist-cjs/lib/tools/SelectTool/childStates/PointingHandle.js.map +2 -2
  67. package/dist-cjs/lib/tools/SelectTool/childStates/PointingSelection.js.map +2 -2
  68. package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js.map +2 -2
  69. package/dist-cjs/lib/tools/SelectTool/childStates/ScribbleBrushing.js.map +2 -2
  70. package/dist-cjs/lib/tools/SelectTool/childStates/Translating.js.map +2 -2
  71. package/dist-cjs/lib/ui/components/ActionsMenu/DefaultActionsMenuContent.js +3 -9
  72. package/dist-cjs/lib/ui/components/ActionsMenu/DefaultActionsMenuContent.js.map +2 -2
  73. package/dist-cjs/lib/ui/components/EditLinkDialog.js +11 -1
  74. package/dist-cjs/lib/ui/components/EditLinkDialog.js.map +2 -2
  75. package/dist-cjs/lib/ui/components/Toolbar/AltTextEditor.js.map +2 -2
  76. package/dist-cjs/lib/ui/components/ZoomMenu/DefaultZoomMenu.js +1 -1
  77. package/dist-cjs/lib/ui/components/ZoomMenu/DefaultZoomMenu.js.map +2 -2
  78. package/dist-cjs/lib/ui/components/menu-items.js +3 -1
  79. package/dist-cjs/lib/ui/components/menu-items.js.map +2 -2
  80. package/dist-cjs/lib/ui/components/primitives/TldrawUiSlider.js +1 -1
  81. package/dist-cjs/lib/ui/components/primitives/TldrawUiSlider.js.map +2 -2
  82. package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js +143 -88
  83. package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js.map +2 -2
  84. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js +1 -1
  85. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js.map +2 -2
  86. package/dist-cjs/lib/ui/context/actions.js +1 -2
  87. package/dist-cjs/lib/ui/context/actions.js.map +2 -2
  88. package/dist-cjs/lib/ui/hooks/menu-hooks.js.map +2 -2
  89. package/dist-cjs/lib/ui/hooks/useFlatten.js.map +2 -2
  90. package/dist-cjs/lib/ui/hooks/useTools.js.map +2 -2
  91. package/dist-cjs/lib/ui/version.js +3 -3
  92. package/dist-cjs/lib/ui/version.js.map +1 -1
  93. package/dist-cjs/lib/utils/excalidraw/putExcalidrawContent.js +8 -0
  94. package/dist-cjs/lib/utils/excalidraw/putExcalidrawContent.js.map +2 -2
  95. package/dist-cjs/lib/utils/export/exportAs.js.map +2 -2
  96. package/dist-cjs/lib/utils/frames/frames.js.map +2 -2
  97. package/dist-cjs/lib/utils/text/richText.js +7 -17
  98. package/dist-cjs/lib/utils/text/richText.js.map +3 -3
  99. package/dist-cjs/lib/utils/tldr/buildFromV1Document.js.map +2 -2
  100. package/dist-esm/index.d.mts +26 -5
  101. package/dist-esm/index.mjs +3 -1
  102. package/dist-esm/index.mjs.map +2 -2
  103. package/dist-esm/lib/bindings/arrow/ArrowBindingUtil.mjs.map +2 -2
  104. package/dist-esm/lib/canvas/TldrawSelectionForeground.mjs +2 -2
  105. package/dist-esm/lib/canvas/TldrawSelectionForeground.mjs.map +2 -2
  106. package/dist-esm/lib/defaultExternalContentHandlers.mjs.map +2 -2
  107. package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs +10 -14
  108. package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs.map +2 -2
  109. package/dist-esm/lib/shapes/arrow/arrowLabel.mjs.map +2 -2
  110. package/dist-esm/lib/shapes/arrow/arrowTargetState.mjs.map +2 -2
  111. package/dist-esm/lib/shapes/arrow/elbow/elbowArrowSnapLines.mjs.map +2 -2
  112. package/dist-esm/lib/shapes/arrow/shared.mjs.map +2 -2
  113. package/dist-esm/lib/shapes/arrow/toolStates/Pointing.mjs.map +2 -2
  114. package/dist-esm/lib/shapes/bookmark/bookmarks.mjs.map +2 -2
  115. package/dist-esm/lib/shapes/draw/DrawShapeUtil.mjs +3 -3
  116. package/dist-esm/lib/shapes/draw/DrawShapeUtil.mjs.map +2 -2
  117. package/dist-esm/lib/shapes/draw/toolStates/Drawing.mjs.map +2 -2
  118. package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs.map +2 -2
  119. package/dist-esm/lib/shapes/frame/FrameShapeTool.mjs.map +1 -1
  120. package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs +1 -1
  121. package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs.map +2 -2
  122. package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs +10 -6
  123. package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs.map +2 -2
  124. package/dist-esm/lib/shapes/geo/toolStates/Pointing.mjs.map +2 -2
  125. package/dist-esm/lib/shapes/highlight/HighlightShapeUtil.mjs +1 -1
  126. package/dist-esm/lib/shapes/highlight/HighlightShapeUtil.mjs.map +2 -2
  127. package/dist-esm/lib/shapes/line/toolStates/Pointing.mjs.map +2 -2
  128. package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs +5 -5
  129. package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs.map +2 -2
  130. package/dist-esm/lib/shapes/note/noteHelpers.mjs.map +2 -2
  131. package/dist-esm/lib/shapes/shared/HyperlinkButton.mjs +3 -2
  132. package/dist-esm/lib/shapes/shared/HyperlinkButton.mjs.map +2 -2
  133. package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs +14 -2
  134. package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs.map +2 -2
  135. package/dist-esm/lib/shapes/shared/RichTextLabel.mjs +11 -3
  136. package/dist-esm/lib/shapes/shared/RichTextLabel.mjs.map +2 -2
  137. package/dist-esm/lib/shapes/shared/ShapeFill.mjs +2 -2
  138. package/dist-esm/lib/shapes/shared/ShapeFill.mjs.map +2 -2
  139. package/dist-esm/lib/shapes/shared/crop.mjs +1 -0
  140. package/dist-esm/lib/shapes/shared/crop.mjs.map +2 -2
  141. package/dist-esm/lib/shapes/shared/useEditablePlainText.mjs.map +2 -2
  142. package/dist-esm/lib/shapes/shared/useEditableRichText.mjs.map +2 -2
  143. package/dist-esm/lib/shapes/shared/useEfficientZoomThreshold.mjs +12 -0
  144. package/dist-esm/lib/shapes/shared/useEfficientZoomThreshold.mjs.map +7 -0
  145. package/dist-esm/lib/shapes/shared/useImageOrVideoAsset.mjs +1 -1
  146. package/dist-esm/lib/shapes/shared/useImageOrVideoAsset.mjs.map +2 -2
  147. package/dist-esm/lib/shapes/text/TextShapeUtil.mjs +5 -2
  148. package/dist-esm/lib/shapes/text/TextShapeUtil.mjs.map +2 -2
  149. package/dist-esm/lib/shapes/text/toolStates/Pointing.mjs.map +2 -2
  150. package/dist-esm/lib/shapes/video/VideoShapeUtil.mjs +1 -1
  151. package/dist-esm/lib/shapes/video/VideoShapeUtil.mjs.map +2 -2
  152. package/dist-esm/lib/tools/EraserTool/childStates/Erasing.mjs.map +2 -2
  153. package/dist-esm/lib/tools/EraserTool/childStates/Pointing.mjs +1 -4
  154. package/dist-esm/lib/tools/EraserTool/childStates/Pointing.mjs.map +2 -2
  155. package/dist-esm/lib/tools/SelectTool/DragAndDropManager.mjs +1 -4
  156. package/dist-esm/lib/tools/SelectTool/DragAndDropManager.mjs.map +2 -2
  157. package/dist-esm/lib/tools/SelectTool/childStates/Brushing.mjs.map +2 -2
  158. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/Idle.mjs.map +2 -2
  159. package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs +1 -1
  160. package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs.map +2 -2
  161. package/dist-esm/lib/tools/SelectTool/childStates/EditingShape.mjs +30 -10
  162. package/dist-esm/lib/tools/SelectTool/childStates/EditingShape.mjs.map +2 -2
  163. package/dist-esm/lib/tools/SelectTool/childStates/Idle.mjs.map +2 -2
  164. package/dist-esm/lib/tools/SelectTool/childStates/PointingArrowLabel.mjs.map +2 -2
  165. package/dist-esm/lib/tools/SelectTool/childStates/PointingHandle.mjs +1 -4
  166. package/dist-esm/lib/tools/SelectTool/childStates/PointingHandle.mjs.map +2 -2
  167. package/dist-esm/lib/tools/SelectTool/childStates/PointingSelection.mjs.map +2 -2
  168. package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs.map +2 -2
  169. package/dist-esm/lib/tools/SelectTool/childStates/ScribbleBrushing.mjs.map +2 -2
  170. package/dist-esm/lib/tools/SelectTool/childStates/Translating.mjs.map +2 -2
  171. package/dist-esm/lib/ui/components/ActionsMenu/DefaultActionsMenuContent.mjs +2 -8
  172. package/dist-esm/lib/ui/components/ActionsMenu/DefaultActionsMenuContent.mjs.map +2 -2
  173. package/dist-esm/lib/ui/components/EditLinkDialog.mjs +11 -1
  174. package/dist-esm/lib/ui/components/EditLinkDialog.mjs.map +2 -2
  175. package/dist-esm/lib/ui/components/Toolbar/AltTextEditor.mjs.map +2 -2
  176. package/dist-esm/lib/ui/components/ZoomMenu/DefaultZoomMenu.mjs +1 -1
  177. package/dist-esm/lib/ui/components/ZoomMenu/DefaultZoomMenu.mjs.map +2 -2
  178. package/dist-esm/lib/ui/components/menu-items.mjs +4 -5
  179. package/dist-esm/lib/ui/components/menu-items.mjs.map +2 -2
  180. package/dist-esm/lib/ui/components/primitives/TldrawUiSlider.mjs +2 -2
  181. package/dist-esm/lib/ui/components/primitives/TldrawUiSlider.mjs.map +2 -2
  182. package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs +151 -90
  183. package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs.map +2 -2
  184. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs +2 -2
  185. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs.map +2 -2
  186. package/dist-esm/lib/ui/context/actions.mjs +1 -2
  187. package/dist-esm/lib/ui/context/actions.mjs.map +2 -2
  188. package/dist-esm/lib/ui/hooks/menu-hooks.mjs +1 -4
  189. package/dist-esm/lib/ui/hooks/menu-hooks.mjs.map +2 -2
  190. package/dist-esm/lib/ui/hooks/useFlatten.mjs.map +2 -2
  191. package/dist-esm/lib/ui/hooks/useTools.mjs.map +2 -2
  192. package/dist-esm/lib/ui/version.mjs +3 -3
  193. package/dist-esm/lib/ui/version.mjs.map +1 -1
  194. package/dist-esm/lib/utils/excalidraw/putExcalidrawContent.mjs +8 -0
  195. package/dist-esm/lib/utils/excalidraw/putExcalidrawContent.mjs.map +2 -2
  196. package/dist-esm/lib/utils/export/exportAs.mjs +1 -3
  197. package/dist-esm/lib/utils/export/exportAs.mjs.map +2 -2
  198. package/dist-esm/lib/utils/frames/frames.mjs.map +2 -2
  199. package/dist-esm/lib/utils/text/richText.mjs +3 -3
  200. package/dist-esm/lib/utils/text/richText.mjs.map +2 -2
  201. package/dist-esm/lib/utils/tldr/buildFromV1Document.mjs.map +2 -2
  202. package/package.json +10 -10
  203. package/src/index.ts +1 -0
  204. package/src/lib/bindings/arrow/ArrowBindingUtil.ts +1 -1
  205. package/src/lib/canvas/TldrawSelectionForeground.tsx +6 -11
  206. package/src/lib/defaultExternalContentHandlers.ts +3 -4
  207. package/src/lib/shapes/arrow/ArrowShapeUtil.test.ts +2 -2
  208. package/src/lib/shapes/arrow/ArrowShapeUtil.tsx +11 -13
  209. package/src/lib/shapes/arrow/arrow-types.ts +2 -0
  210. package/src/lib/shapes/arrow/arrowLabel.ts +1 -1
  211. package/src/lib/shapes/arrow/arrowTargetState.ts +1 -1
  212. package/src/lib/shapes/arrow/elbow/elbowArrowSnapLines.tsx +3 -3
  213. package/src/lib/shapes/arrow/shared.ts +4 -4
  214. package/src/lib/shapes/arrow/toolStates/Pointing.tsx +1 -1
  215. package/src/lib/shapes/bookmark/bookmarks.ts +3 -3
  216. package/src/lib/shapes/draw/DrawShapeUtil.tsx +3 -3
  217. package/src/lib/shapes/draw/toolStates/Drawing.ts +4 -4
  218. package/src/lib/shapes/embed/EmbedShapeUtil.tsx +1 -1
  219. package/src/lib/shapes/frame/FrameShapeTool.ts +1 -1
  220. package/src/lib/shapes/frame/FrameShapeUtil.tsx +1 -1
  221. package/src/lib/shapes/geo/GeoShapeUtil.test.tsx +10 -2
  222. package/src/lib/shapes/geo/GeoShapeUtil.tsx +9 -4
  223. package/src/lib/shapes/geo/toolStates/Pointing.ts +3 -3
  224. package/src/lib/shapes/highlight/HighlightShapeUtil.tsx +1 -1
  225. package/src/lib/shapes/line/LineShapeTool.test.ts +6 -6
  226. package/src/lib/shapes/line/LineShapeUtil.test.tsx +5 -5
  227. package/src/lib/shapes/line/toolStates/Pointing.ts +1 -1
  228. package/src/lib/shapes/note/NoteShapeTool.test.ts +2 -1
  229. package/src/lib/shapes/note/NoteShapeUtil.tsx +7 -8
  230. package/src/lib/shapes/note/noteHelpers.ts +2 -2
  231. package/src/lib/shapes/shared/HyperlinkButton.tsx +3 -2
  232. package/src/lib/shapes/shared/PlainTextLabel.tsx +12 -2
  233. package/src/lib/shapes/shared/RichTextLabel.tsx +13 -3
  234. package/src/lib/shapes/shared/ShapeFill.tsx +2 -2
  235. package/src/lib/shapes/shared/crop.ts +1 -0
  236. package/src/lib/shapes/shared/useEditablePlainText.ts +7 -3
  237. package/src/lib/shapes/shared/useEditableRichText.ts +7 -3
  238. package/src/lib/shapes/shared/useEfficientZoomThreshold.ts +10 -0
  239. package/src/lib/shapes/shared/useImageOrVideoAsset.ts +1 -1
  240. package/src/lib/shapes/text/TextShapeTool.test.ts +4 -4
  241. package/src/lib/shapes/text/TextShapeUtil.tsx +5 -0
  242. package/src/lib/shapes/text/toolStates/Pointing.ts +1 -1
  243. package/src/lib/shapes/video/VideoShapeUtil.tsx +2 -1
  244. package/src/lib/tools/EraserTool/childStates/Erasing.ts +3 -5
  245. package/src/lib/tools/EraserTool/childStates/Pointing.ts +3 -16
  246. package/src/lib/tools/SelectTool/DragAndDropManager.ts +2 -4
  247. package/src/lib/tools/SelectTool/childStates/Brushing.ts +2 -6
  248. package/src/lib/tools/SelectTool/childStates/Crop/children/Idle.ts +2 -3
  249. package/src/lib/tools/SelectTool/childStates/DraggingHandle.tsx +4 -7
  250. package/src/lib/tools/SelectTool/childStates/EditingShape.ts +46 -15
  251. package/src/lib/tools/SelectTool/childStates/Idle.ts +6 -10
  252. package/src/lib/tools/SelectTool/childStates/PointingArrowLabel.ts +1 -1
  253. package/src/lib/tools/SelectTool/childStates/PointingHandle.ts +4 -12
  254. package/src/lib/tools/SelectTool/childStates/PointingSelection.ts +2 -2
  255. package/src/lib/tools/SelectTool/childStates/Resizing.ts +2 -4
  256. package/src/lib/tools/SelectTool/childStates/ScribbleBrushing.ts +2 -4
  257. package/src/lib/tools/SelectTool/childStates/Translating.ts +1 -3
  258. package/src/lib/ui/components/ActionsMenu/DefaultActionsMenuContent.tsx +1 -9
  259. package/src/lib/ui/components/EditLinkDialog.tsx +16 -6
  260. package/src/lib/ui/components/Toolbar/AltTextEditor.tsx +2 -2
  261. package/src/lib/ui/components/ZoomMenu/DefaultZoomMenu.tsx +1 -1
  262. package/src/lib/ui/components/menu-items.tsx +9 -15
  263. package/src/lib/ui/components/primitives/TldrawUiSlider.tsx +2 -2
  264. package/src/lib/ui/components/primitives/TldrawUiTooltip.tsx +196 -108
  265. package/src/lib/ui/components/primitives/menus/TldrawUiMenuItem.tsx +2 -2
  266. package/src/lib/ui/context/actions.tsx +9 -13
  267. package/src/lib/ui/hooks/menu-hooks.ts +9 -19
  268. package/src/lib/ui/hooks/useFlatten.ts +1 -2
  269. package/src/lib/ui/hooks/useTools.tsx +1 -2
  270. package/src/lib/ui/version.ts +3 -3
  271. package/src/lib/utils/excalidraw/putExcalidrawContent.ts +8 -0
  272. package/src/lib/utils/export/exportAs.ts +2 -9
  273. package/src/lib/utils/frames/frames.ts +1 -1
  274. package/src/lib/utils/text/richText.ts +3 -3
  275. package/src/lib/utils/tldr/buildFromV1Document.ts +12 -17
  276. package/src/test/Editor.test.tsx +38 -12
  277. package/src/test/SelectTool.test.ts +11 -19
  278. package/src/test/TestEditor.ts +1 -4
  279. package/src/test/TldrawEditor.test.tsx +21 -18
  280. package/src/test/bindings.test.tsx +29 -25
  281. package/src/test/bindingsIndex.test.tsx +4 -4
  282. package/src/test/commands/__snapshots__/getSvgString.test.ts.snap +2 -2
  283. package/src/test/commands/cameraState.test.ts +299 -0
  284. package/src/test/commands/createShape.test.ts +64 -0
  285. package/src/test/commands/createShapes.test.ts +15 -1
  286. package/src/test/commands/getSvgString.test.ts +2 -2
  287. package/src/test/commands/isShapeOfType.test.ts +44 -0
  288. package/src/test/commands/putContent.test.ts +80 -1
  289. package/src/test/commands/updateShape.test.ts +67 -0
  290. package/src/test/commands/updateShapes.test.ts +21 -5
  291. package/src/test/custom-clipping.test.ts +36 -35
  292. package/src/test/customSnapping.test.tsx +77 -62
  293. package/src/test/duplicate.test.ts +1 -1
  294. package/src/test/frames.test.ts +2 -2
  295. package/src/test/getCulledShapes.test.tsx +11 -3
  296. package/src/test/getShapeAtPoint.test.ts +2 -2
  297. package/src/test/groups.test.tsx +6 -3
  298. package/src/test/resizing.test.ts +9 -13
  299. package/src/test/selection-omnibus.test.ts +11 -11
  300. package/src/test/shapeutils.test.ts +1 -1
  301. package/src/test/styles2.test.tsx +1 -1
  302. package/src/test/styles3.test.ts +5 -5
  303. package/src/test/test-jsx.tsx +69 -57
  304. package/src/test/text.test.ts +15 -17
  305. package/src/test/translating.test.ts +6 -8
  306. package/tldraw.css +8 -4
  307. package/dist-cjs/lib/shapes/shared/useForceSolid.js.map +0 -7
  308. package/dist-esm/lib/shapes/shared/useForceSolid.mjs +0 -9
  309. package/dist-esm/lib/shapes/shared/useForceSolid.mjs.map +0 -7
  310. package/src/lib/shapes/shared/useForceSolid.ts +0 -6
@@ -6,8 +6,8 @@ import {
6
6
  BindingOnShapeDeleteOptions,
7
7
  BindingOnShapeIsolateOptions,
8
8
  BindingUtil,
9
+ TLBinding,
9
10
  TLShapeId,
10
- TLUnknownBinding,
11
11
  createBindingId,
12
12
  createShapeId,
13
13
  } from '@tldraw/editor'
@@ -40,7 +40,7 @@ const mockOnAfterChangeToShape = vi.fn()
40
40
 
41
41
  const calls: string[] = []
42
42
 
43
- const registerCall = (method: string, binding: TLUnknownBinding) => {
43
+ const registerCall = (method: string, binding: TLBinding) => {
44
44
  calls.push(
45
45
  `${method}: ${binding.fromId.slice('shape:'.length)}->${binding.toId.slice('shape:'.length)}`
46
46
  )
@@ -60,62 +60,62 @@ class TestBindingUtil extends BindingUtil {
60
60
  mockOnOperationComplete()
61
61
  }
62
62
 
63
- override onBeforeDelete(options: BindingOnDeleteOptions<TLUnknownBinding>): void {
63
+ override onBeforeDelete(options: BindingOnDeleteOptions): void {
64
64
  registerCall('onBeforeDelete', options.binding)
65
65
  mockOnBeforeDelete(options)
66
66
  }
67
67
 
68
- override onAfterDelete(options: BindingOnDeleteOptions<TLUnknownBinding>): void {
68
+ override onAfterDelete(options: BindingOnDeleteOptions): void {
69
69
  registerCall('onAfterDelete', options.binding)
70
70
  mockOnAfterDelete(options)
71
71
  }
72
72
 
73
- override onBeforeDeleteFromShape(options: BindingOnShapeDeleteOptions<TLUnknownBinding>): void {
73
+ override onBeforeDeleteFromShape(options: BindingOnShapeDeleteOptions): void {
74
74
  registerCall('onBeforeDeleteFromShape', options.binding)
75
75
  mockOnBeforeFromShapeDelete(options)
76
76
  }
77
77
 
78
- override onBeforeDeleteToShape(options: BindingOnShapeDeleteOptions<TLUnknownBinding>): void {
78
+ override onBeforeDeleteToShape(options: BindingOnShapeDeleteOptions): void {
79
79
  registerCall('onBeforeDeleteToShape', options.binding)
80
80
  mockOnBeforeToShapeDelete(options)
81
81
  }
82
82
 
83
- override onBeforeIsolateFromShape(options: BindingOnShapeIsolateOptions<TLUnknownBinding>): void {
83
+ override onBeforeIsolateFromShape(options: BindingOnShapeIsolateOptions): void {
84
84
  registerCall('onBeforeIsolateFromShape', options.binding)
85
85
  mockOnBeforeFromShapeIsolate(options)
86
86
  }
87
87
 
88
- override onBeforeIsolateToShape(options: BindingOnShapeIsolateOptions<TLUnknownBinding>): void {
88
+ override onBeforeIsolateToShape(options: BindingOnShapeIsolateOptions): void {
89
89
  registerCall('onBeforeIsolateToShape', options.binding)
90
90
  mockOnBeforeToShapeIsolate(options)
91
91
  }
92
92
 
93
- override onBeforeCreate(options: BindingOnCreateOptions<TLUnknownBinding>): void {
93
+ override onBeforeCreate(options: BindingOnCreateOptions): void {
94
94
  registerCall('onBeforeCreate', options.binding)
95
95
  mockOnBeforeCreate(options)
96
96
  }
97
97
 
98
- override onAfterCreate(options: BindingOnCreateOptions<TLUnknownBinding>): void {
98
+ override onAfterCreate(options: BindingOnCreateOptions): void {
99
99
  registerCall('onAfterCreate', options.binding)
100
100
  mockOnAfterCreate(options)
101
101
  }
102
102
 
103
- override onBeforeChange(options: BindingOnChangeOptions<TLUnknownBinding>): void {
103
+ override onBeforeChange(options: BindingOnChangeOptions): void {
104
104
  registerCall('onBeforeChange', options.bindingAfter)
105
105
  mockOnBeforeChange(options)
106
106
  }
107
107
 
108
- override onAfterChange(options: BindingOnChangeOptions<TLUnknownBinding>): void {
108
+ override onAfterChange(options: BindingOnChangeOptions): void {
109
109
  registerCall('onAfterChange', options.bindingAfter)
110
110
  mockOnAfterChange(options)
111
111
  }
112
112
 
113
- override onAfterChangeFromShape(options: BindingOnShapeChangeOptions<TLUnknownBinding>): void {
113
+ override onAfterChangeFromShape(options: BindingOnShapeChangeOptions): void {
114
114
  registerCall('onAfterChangeFromShape', options.binding)
115
115
  mockOnAfterChangeFromShape(options)
116
116
  }
117
117
 
118
- override onAfterChangeToShape(options: BindingOnShapeChangeOptions<TLUnknownBinding>): void {
118
+ override onAfterChangeToShape(options: BindingOnShapeChangeOptions): void {
119
119
  registerCall('onAfterChangeToShape', options.binding)
120
120
  mockOnAfterChangeToShape(options)
121
121
  }
@@ -146,11 +146,19 @@ beforeEach(() => {
146
146
  mockOnAfterChangeToShape.mockReset()
147
147
  })
148
148
 
149
+ const TEST_TYPE = 'test'
150
+
151
+ declare module '@tldraw/tlschema' {
152
+ export interface TLGlobalBindingPropsMap {
153
+ [TEST_TYPE]: Record<string, never>
154
+ }
155
+ }
156
+
149
157
  function bindShapes(fromId: TLShapeId, toId: TLShapeId) {
150
158
  const bindingId = createBindingId()
151
159
  editor.createBinding({
152
160
  id: bindingId,
153
- type: 'test',
161
+ type: TEST_TYPE,
154
162
  fromId,
155
163
  toId,
156
164
  })
@@ -256,11 +264,9 @@ test('copying the to shape on its own does trigger the unbind operation', () =>
256
264
  })
257
265
 
258
266
  test('cascading deletes in beforeFromShapeDelete are handled correctly', () => {
259
- mockOnBeforeFromShapeDelete.mockImplementation(
260
- (options: BindingOnShapeDeleteOptions<TLUnknownBinding>) => {
261
- editor.deleteShape(options.binding.toId)
262
- }
263
- )
267
+ mockOnBeforeFromShapeDelete.mockImplementation((options: BindingOnShapeDeleteOptions) => {
268
+ editor.deleteShape(options.binding.toId)
269
+ })
264
270
 
265
271
  bindShapes(ids.box1, ids.box2)
266
272
  bindShapes(ids.box2, ids.box3)
@@ -301,11 +307,9 @@ test('cascading deletes in beforeFromShapeDelete are handled correctly', () => {
301
307
  })
302
308
 
303
309
  test('cascading deletes in beforeToShapeDelete are handled correctly', () => {
304
- mockOnBeforeToShapeDelete.mockImplementation(
305
- (options: BindingOnShapeDeleteOptions<TLUnknownBinding>) => {
306
- editor.deleteShape(options.binding.fromId)
307
- }
308
- )
310
+ mockOnBeforeToShapeDelete.mockImplementation((options: BindingOnShapeDeleteOptions) => {
311
+ editor.deleteShape(options.binding.fromId)
312
+ })
309
313
 
310
314
  bindShapes(ids.box1, ids.box2)
311
315
  bindShapes(ids.box2, ids.box3)
@@ -1,4 +1,4 @@
1
- import { TLArrowBinding, TLGeoShape, TLShapeId, createShapeId } from '@tldraw/editor'
1
+ import { TLShapeId, createShapeId } from '@tldraw/editor'
2
2
  import { TestEditor } from './TestEditor'
3
3
  import { TL } from './test-jsx'
4
4
 
@@ -219,7 +219,7 @@ describe('bindingsIndex', () => {
219
219
 
220
220
  const [box1Clone, box2Clone] = editor
221
221
  .getSelectedShapes()
222
- .filter((shape) => editor.isShapeOfType<TLGeoShape>(shape, 'geo'))
222
+ .filter((shape) => editor.isShapeOfType(shape, 'geo'))
223
223
  .sort((a, b) => a.x - b.x)
224
224
 
225
225
  expect(editor.getArrowsBoundTo(box2Clone.id)).toHaveLength(3)
@@ -248,9 +248,9 @@ describe('bindingsIndex', () => {
248
248
 
249
249
  // move arrowA end from box2 to box3
250
250
  const binding = editor
251
- .getBindingsInvolvingShape<TLArrowBinding>(ids.box2, 'arrow')
251
+ .getBindingsInvolvingShape(ids.box2, 'arrow')
252
252
  .find((b) => b.props.terminal === 'end')!
253
- editor.updateBinding({ ...binding, toId: box3 } satisfies TLArrowBinding)
253
+ editor.updateBinding({ ...binding, toId: box3 })
254
254
 
255
255
  expect(editor.getArrowsBoundTo(ids.box2)).toHaveLength(2)
256
256
  expect(editor.getArrowsBoundTo(ids.box1)).toHaveLength(3)
@@ -83,7 +83,7 @@ exports[`Matches a snapshot > Basic SVG 1`] = `
83
83
  stroke-width="3.5"
84
84
  />
85
85
  <foreignobject
86
- class="tl-export-embed-styles tl-rich-text tl-rich-text-svg"
86
+ class="tl-export-embed-styles tl-rich-text tl-rich-text-svg tl-text__outline"
87
87
  height="100"
88
88
  width="100"
89
89
  x="0"
@@ -223,7 +223,7 @@ exports[`Returns all shapes when no ids are provided > All shapes 1`] = `
223
223
  stroke-width="3.5"
224
224
  />
225
225
  <foreignobject
226
- class="tl-export-embed-styles tl-rich-text tl-rich-text-svg"
226
+ class="tl-export-embed-styles tl-rich-text tl-rich-text-svg tl-text__outline"
227
227
  height="100"
228
228
  width="100"
229
229
  x="0"
@@ -0,0 +1,299 @@
1
+ import { Box } from '@tldraw/editor'
2
+ import { TestEditor } from '../TestEditor'
3
+
4
+ let editor: TestEditor
5
+
6
+ beforeEach(() => {
7
+ editor = new TestEditor()
8
+ editor.updateViewportScreenBounds(new Box(0, 0, 1600, 900))
9
+ })
10
+
11
+ describe('getCameraState', () => {
12
+ it('starts as idle', () => {
13
+ expect(editor.getCameraState()).toBe('idle')
14
+ })
15
+
16
+ it('becomes moving when the camera changes via setCamera', () => {
17
+ expect(editor.getCameraState()).toBe('idle')
18
+ editor.setCamera({ x: 100, y: 100, z: 1 })
19
+ expect(editor.getCameraState()).toBe('moving')
20
+ })
21
+
22
+ it('becomes moving when the camera changes via pan', () => {
23
+ expect(editor.getCameraState()).toBe('idle')
24
+ editor.pan({ x: 100, y: 100 })
25
+ expect(editor.getCameraState()).toBe('moving')
26
+ })
27
+
28
+ it('becomes moving when the camera changes via zoomIn', () => {
29
+ expect(editor.getCameraState()).toBe('idle')
30
+ editor.zoomIn(undefined, { immediate: true })
31
+ expect(editor.getCameraState()).toBe('moving')
32
+ })
33
+
34
+ it('returns to idle after the timeout elapses', () => {
35
+ expect(editor.getCameraState()).toBe('idle')
36
+ editor.setCamera({ x: 100, y: 100, z: 1 })
37
+ expect(editor.getCameraState()).toBe('moving')
38
+
39
+ // The default timeout is 64ms (options.cameraMovingTimeoutMs)
40
+ // Each tick is 16ms, so we need ~4 ticks to elapse
41
+ editor.forceTick(5)
42
+ expect(editor.getCameraState()).toBe('idle')
43
+ })
44
+
45
+ it('stays moving while camera continues to change', () => {
46
+ expect(editor.getCameraState()).toBe('idle')
47
+ editor.setCamera({ x: 100, y: 100, z: 1 })
48
+ expect(editor.getCameraState()).toBe('moving')
49
+
50
+ // Move again before timeout elapses
51
+ editor.forceTick(2)
52
+ editor.setCamera({ x: 200, y: 200, z: 1 })
53
+ expect(editor.getCameraState()).toBe('moving')
54
+
55
+ // Move again
56
+ editor.forceTick(2)
57
+ editor.setCamera({ x: 300, y: 300, z: 1 })
58
+ expect(editor.getCameraState()).toBe('moving')
59
+
60
+ // Now let it settle
61
+ editor.forceTick(5)
62
+ expect(editor.getCameraState()).toBe('idle')
63
+ })
64
+
65
+ it('stays idle when camera position does not actually change', () => {
66
+ expect(editor.getCameraState()).toBe('idle')
67
+
68
+ // Setting the same camera position should not trigger moving state
69
+ const currentCamera = editor.getCamera()
70
+ editor.setCamera({ x: currentCamera.x, y: currentCamera.y, z: currentCamera.z })
71
+ expect(editor.getCameraState()).toBe('idle')
72
+ })
73
+
74
+ it('does not add multiple tick listeners when camera changes rapidly', () => {
75
+ // This test verifies the fix: we should not have redundant listeners
76
+ expect(editor.getCameraState()).toBe('idle')
77
+
78
+ // Change camera multiple times rapidly
79
+ editor.setCamera({ x: 100, y: 100, z: 1 })
80
+ editor.setCamera({ x: 200, y: 200, z: 1 })
81
+ editor.setCamera({ x: 300, y: 300, z: 1 })
82
+
83
+ expect(editor.getCameraState()).toBe('moving')
84
+
85
+ // After timeout, should return to idle exactly once
86
+ // If there were multiple listeners, the state might behave unexpectedly
87
+ editor.forceTick(5)
88
+ expect(editor.getCameraState()).toBe('idle')
89
+ })
90
+
91
+ it('resets timeout when camera changes while already moving', () => {
92
+ expect(editor.getCameraState()).toBe('idle')
93
+ editor.setCamera({ x: 100, y: 100, z: 1 })
94
+ expect(editor.getCameraState()).toBe('moving')
95
+
96
+ // Wait almost until timeout
97
+ editor.forceTick(3)
98
+ expect(editor.getCameraState()).toBe('moving')
99
+
100
+ // Change camera again - should reset timeout
101
+ editor.setCamera({ x: 200, y: 200, z: 1 })
102
+ expect(editor.getCameraState()).toBe('moving')
103
+
104
+ // Wait 3 more ticks - would have been idle if timeout wasn't reset
105
+ editor.forceTick(3)
106
+ expect(editor.getCameraState()).toBe('moving')
107
+
108
+ // Now let it fully settle
109
+ editor.forceTick(3)
110
+ expect(editor.getCameraState()).toBe('idle')
111
+ })
112
+ })
113
+
114
+ describe('camera state with zoom', () => {
115
+ it('becomes moving on zoomOut', () => {
116
+ expect(editor.getCameraState()).toBe('idle')
117
+ editor.zoomOut(undefined, { immediate: true })
118
+ expect(editor.getCameraState()).toBe('moving')
119
+ })
120
+
121
+ it('becomes moving on centerOnPoint', () => {
122
+ expect(editor.getCameraState()).toBe('idle')
123
+ editor.centerOnPoint({ x: 500, y: 500 })
124
+ expect(editor.getCameraState()).toBe('moving')
125
+ })
126
+
127
+ it('becomes moving on zoomToFit', () => {
128
+ // Create a shape so zoomToFit has something to fit
129
+ editor.createShape({ type: 'geo', x: 100, y: 100, props: { w: 200, h: 200 } })
130
+ expect(editor.getCameraState()).toBe('idle')
131
+ editor.zoomToFit({ immediate: true })
132
+ expect(editor.getCameraState()).toBe('moving')
133
+ })
134
+ })
135
+
136
+ describe('getDebouncedZoomLevel', () => {
137
+ it('returns the current zoom level when camera is idle', () => {
138
+ expect(editor.getCameraState()).toBe('idle')
139
+ expect(editor.getDebouncedZoomLevel()).toBe(editor.getZoomLevel())
140
+
141
+ // Change zoom and let it settle
142
+ editor.zoomIn(undefined, { immediate: true })
143
+ editor.forceTick(5)
144
+ expect(editor.getCameraState()).toBe('idle')
145
+ expect(editor.getDebouncedZoomLevel()).toBe(editor.getZoomLevel())
146
+ })
147
+
148
+ it('captures zoom when camera starts moving', () => {
149
+ expect(editor.getCameraState()).toBe('idle')
150
+
151
+ // Start zooming - the debounced zoom is captured when movement starts
152
+ editor.zoomIn(undefined, { immediate: true })
153
+ expect(editor.getCameraState()).toBe('moving')
154
+
155
+ // The debounced zoom is captured at the moment movement starts (after first change)
156
+ const capturedZoom = editor.getDebouncedZoomLevel()
157
+ expect(capturedZoom).toBe(editor.getZoomLevel())
158
+ })
159
+
160
+ it('keeps captured zoom during continued camera movement', () => {
161
+ // Start zooming
162
+ editor.zoomIn(undefined, { immediate: true })
163
+ const capturedZoom = editor.getDebouncedZoomLevel()
164
+ expect(editor.getCameraState()).toBe('moving')
165
+
166
+ // Zoom again while still moving - debounced value should stay the same
167
+ editor.zoomIn(undefined, { immediate: true })
168
+ expect(editor.getCameraState()).toBe('moving')
169
+ expect(editor.getDebouncedZoomLevel()).toBe(capturedZoom)
170
+
171
+ // But current zoom should have changed
172
+ expect(editor.getZoomLevel()).not.toBe(capturedZoom)
173
+ })
174
+
175
+ it('updates debounced zoom when camera becomes idle again', () => {
176
+ // Start zooming
177
+ editor.zoomIn(undefined, { immediate: true })
178
+ const capturedZoom = editor.getDebouncedZoomLevel()
179
+
180
+ // Zoom again while moving to change the current zoom
181
+ editor.zoomIn(undefined, { immediate: true })
182
+ expect(editor.getDebouncedZoomLevel()).toBe(capturedZoom)
183
+
184
+ // Let camera settle
185
+ editor.forceTick(5)
186
+ expect(editor.getCameraState()).toBe('idle')
187
+
188
+ // Debounced zoom should now match current zoom
189
+ expect(editor.getDebouncedZoomLevel()).toBe(editor.getZoomLevel())
190
+ })
191
+
192
+ it('captures new zoom at the start of each new movement', () => {
193
+ // First zoom and settle
194
+ editor.zoomIn(undefined, { immediate: true })
195
+ const firstCapturedZoom = editor.getDebouncedZoomLevel()
196
+ editor.forceTick(5)
197
+ expect(editor.getCameraState()).toBe('idle')
198
+
199
+ // Second zoom - should capture new zoom level
200
+ editor.zoomIn(undefined, { immediate: true })
201
+ expect(editor.getCameraState()).toBe('moving')
202
+ // The captured zoom should be different from the first capture
203
+ expect(editor.getDebouncedZoomLevel()).not.toBe(firstCapturedZoom)
204
+ // And it should match the current zoom (since we just started moving)
205
+ expect(editor.getDebouncedZoomLevel()).toBe(editor.getZoomLevel())
206
+ })
207
+
208
+ describe('with debouncedZoom option disabled', () => {
209
+ let editorWithoutDebouncedZoom: TestEditor
210
+
211
+ beforeEach(() => {
212
+ editorWithoutDebouncedZoom = new TestEditor({
213
+ options: { debouncedZoom: false },
214
+ })
215
+ editorWithoutDebouncedZoom.updateViewportScreenBounds(new Box(0, 0, 1600, 900))
216
+ })
217
+
218
+ it('always returns the current zoom level even when camera is moving', () => {
219
+ const initialZoom = editorWithoutDebouncedZoom.getZoomLevel()
220
+
221
+ editorWithoutDebouncedZoom.zoomIn(undefined, { immediate: true })
222
+ expect(editorWithoutDebouncedZoom.getCameraState()).toBe('moving')
223
+
224
+ // Should return the current zoom, not the captured one
225
+ expect(editorWithoutDebouncedZoom.getDebouncedZoomLevel()).toBe(
226
+ editorWithoutDebouncedZoom.getZoomLevel()
227
+ )
228
+ expect(editorWithoutDebouncedZoom.getDebouncedZoomLevel()).not.toBe(initialZoom)
229
+ })
230
+ })
231
+ })
232
+
233
+ describe('getEfficientZoomLevel', () => {
234
+ it('returns current zoom level when below shape threshold', () => {
235
+ // Default threshold is 500 shapes, we have 0
236
+ expect(editor.getZoomLevel()).toBe(editor.getEfficientZoomLevel())
237
+
238
+ // Add a few shapes - still below threshold
239
+ for (let i = 0; i < 10; i++) {
240
+ editor.createShape({ type: 'geo', x: i * 100, y: 0, props: { w: 50, h: 50 } })
241
+ }
242
+ expect(editor.getCurrentPageShapeIds().size).toBe(10)
243
+
244
+ // Start zooming
245
+ editor.zoomIn(undefined, { immediate: true })
246
+ expect(editor.getCameraState()).toBe('moving')
247
+
248
+ // Should still return current zoom because we're below threshold
249
+ expect(editor.getEfficientZoomLevel()).toBe(editor.getZoomLevel())
250
+ })
251
+
252
+ describe('with many shapes above threshold', () => {
253
+ let editorWithManyShapes: TestEditor
254
+
255
+ beforeEach(() => {
256
+ // Use a lower threshold for testing
257
+ editorWithManyShapes = new TestEditor({
258
+ options: { debouncedZoomThreshold: 5 },
259
+ })
260
+ editorWithManyShapes.updateViewportScreenBounds(new Box(0, 0, 1600, 900))
261
+
262
+ // Add shapes above the threshold
263
+ for (let i = 0; i < 10; i++) {
264
+ editorWithManyShapes.createShape({
265
+ type: 'geo',
266
+ x: i * 100,
267
+ y: 0,
268
+ props: { w: 50, h: 50 },
269
+ })
270
+ }
271
+ })
272
+
273
+ it('returns debounced zoom level when above shape threshold and camera is moving', () => {
274
+ // First zoom to capture a debounced value
275
+ editorWithManyShapes.zoomIn(undefined, { immediate: true })
276
+ const capturedZoom = editorWithManyShapes.getEfficientZoomLevel()
277
+ expect(editorWithManyShapes.getCameraState()).toBe('moving')
278
+
279
+ // Zoom again while still moving
280
+ editorWithManyShapes.zoomIn(undefined, { immediate: true })
281
+ expect(editorWithManyShapes.getCameraState()).toBe('moving')
282
+
283
+ // Should return the captured zoom, not the current zoom
284
+ expect(editorWithManyShapes.getEfficientZoomLevel()).toBe(capturedZoom)
285
+ expect(editorWithManyShapes.getEfficientZoomLevel()).not.toBe(
286
+ editorWithManyShapes.getZoomLevel()
287
+ )
288
+ })
289
+
290
+ it('returns current zoom level when above threshold but camera is idle', () => {
291
+ editorWithManyShapes.zoomIn(undefined, { immediate: true })
292
+ editorWithManyShapes.forceTick(5)
293
+ expect(editorWithManyShapes.getCameraState()).toBe('idle')
294
+
295
+ // Should return current zoom because camera is idle
296
+ expect(editorWithManyShapes.getEfficientZoomLevel()).toBe(editorWithManyShapes.getZoomLevel())
297
+ })
298
+ })
299
+ })
@@ -0,0 +1,64 @@
1
+ import { TLArrowShape, TLGeoShape, createShapeId } from '@tldraw/editor'
2
+ import { TestEditor } from '../TestEditor'
3
+
4
+ let editor: TestEditor
5
+
6
+ const ids = {
7
+ box1: createShapeId('box1'),
8
+ }
9
+
10
+ beforeEach(() => {
11
+ editor = new TestEditor()
12
+ })
13
+
14
+ it('Uses typescript generics', () => {
15
+ expect(() => {
16
+ editor.createShape(
17
+ //@ts-expect-error Yep error because we are giving the wrong props to the shape
18
+ {
19
+ id: ids.box1,
20
+ type: 'geo',
21
+ props: { w: 'OH NO' },
22
+ }
23
+ )
24
+
25
+ // Errors when creating a shape with unknown props
26
+ editor.createShape({
27
+ id: ids.box1,
28
+ type: 'geo',
29
+ props: {
30
+ // @ts-expect-error
31
+ foo: 'bar',
32
+ },
33
+ })
34
+
35
+ // Yep error here because we are giving the wrong props to the shape
36
+ editor.createShape<TLGeoShape>({
37
+ id: ids.box1,
38
+ type: 'geo',
39
+ //@ts-expect-error
40
+ props: { w: 'OH NO' },
41
+ })
42
+
43
+ // Yep error here because we are giving the wrong generic
44
+ editor.createShape<TLArrowShape>({
45
+ id: ids.box1,
46
+ //@ts-expect-error
47
+ type: 'geo',
48
+ //@ts-expect-error
49
+ props: { w: 'OH NO' },
50
+ })
51
+
52
+ // All good, correct match of generic and shape type
53
+ editor.createShape<TLGeoShape>({
54
+ id: ids.box1,
55
+ type: 'geo',
56
+ props: { w: 100 },
57
+ })
58
+
59
+ editor.createShape<TLGeoShape>({
60
+ id: ids.box1,
61
+ type: 'geo',
62
+ })
63
+ }).toThrow()
64
+ })
@@ -19,14 +19,28 @@ beforeEach(() => {
19
19
 
20
20
  it('Uses typescript generics', () => {
21
21
  expect(() => {
22
- // No error here because no generic, the editor doesn't know what this guy is
22
+ // Yep error because we are giving the wrong props to the shape
23
23
  editor.createShapes([
24
+ //@ts-expect-error
24
25
  {
25
26
  id: ids.box1,
26
27
  type: 'geo',
27
28
  props: { w: 'OH NO' },
28
29
  },
29
30
  ])
31
+
32
+ // Errors when creating shapes with unknown props
33
+ editor.createShapes([
34
+ {
35
+ id: ids.box1,
36
+ type: 'geo',
37
+ props: {
38
+ // @ts-expect-error
39
+ foo: 'bar',
40
+ },
41
+ },
42
+ ])
43
+
30
44
  // Yep error here because we are giving the wrong props to the shape
31
45
  editor.createShapes<TLGeoShape>([
32
46
  {
@@ -1,4 +1,4 @@
1
- import { DefaultDashStyle, TLGeoShape, createShapeId, toRichText } from '@tldraw/editor'
1
+ import { DefaultDashStyle, createShapeId, toRichText } from '@tldraw/editor'
2
2
  import { vi } from 'vitest'
3
3
  import { TestEditor } from '../TestEditor'
4
4
 
@@ -21,7 +21,7 @@ beforeEach(() => {
21
21
  editor = new TestEditor()
22
22
  editor.setStyleForNextShapes(DefaultDashStyle, 'solid')
23
23
  editor.setStyleForSelectedShapes(DefaultDashStyle, 'solid')
24
- editor.createShapes<TLGeoShape>([
24
+ editor.createShapes([
25
25
  {
26
26
  id: ids.boxA,
27
27
  type: 'geo',
@@ -0,0 +1,44 @@
1
+ import { TLArrowShape, createShapeId } from '@tldraw/editor'
2
+ import { TestEditor } from '../TestEditor'
3
+
4
+ let editor: TestEditor
5
+
6
+ beforeEach(() => {
7
+ editor = new TestEditor()
8
+ })
9
+
10
+ export declare function assertNever(x: never): never
11
+
12
+ it('narrows down the shape type', () => {
13
+ const id = createShapeId('arrow1')
14
+ editor.createShape({ type: 'arrow', id, x: 0, y: 0 })
15
+
16
+ const shape = editor.getShape(id)!
17
+ if (editor.isShapeOfType(shape, 'arrow')) {
18
+ expect(shape.type === 'arrow').toBe(true)
19
+ expect(
20
+ // @ts-expect-error This comparison appears to be unintentional because the types '"arrow"' and '"card"' have no overlap.
21
+ shape.type === 'card'
22
+ ).toBe(false)
23
+ }
24
+ })
25
+
26
+ it('narrows down the shape type with generic', () => {
27
+ const id = createShapeId('arrow1')
28
+ editor.createShape({ type: 'arrow', id, x: 0, y: 0 })
29
+
30
+ const shape = editor.getShape(id)!
31
+ if (editor.isShapeOfType<TLArrowShape>(shape, 'arrow')) {
32
+ expect(shape.type === 'arrow').toBe(true)
33
+
34
+ expect(
35
+ // @ts-expect-error This comparison appears to be unintentional because the types '"arrow"' and '"card"' have no overlap.
36
+ shape.type === 'card'
37
+ ).toBe(false)
38
+ }
39
+
40
+ // @ts-expect-error mismatch between the generic and the shape type
41
+ if (editor.isShapeOfType<TLArrowShape>(shape, 'card')) {
42
+ assertNever(shape)
43
+ }
44
+ })