tldraw 4.3.0-canary.e52fa5385f86 → 4.3.0-canary.ea88b223b83a

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 (548) hide show
  1. package/dist-cjs/index.d.ts +294 -237
  2. package/dist-cjs/index.js +12 -5
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/canvas/TldrawSelectionForeground.js +2 -2
  5. package/dist-cjs/lib/canvas/TldrawSelectionForeground.js.map +2 -2
  6. package/dist-cjs/lib/defaultEmbedDefinitions.js +1 -1
  7. package/dist-cjs/lib/defaultEmbedDefinitions.js.map +2 -2
  8. package/dist-cjs/lib/defaultExternalContentHandlers.js +5 -5
  9. package/dist-cjs/lib/defaultExternalContentHandlers.js.map +2 -2
  10. package/dist-cjs/lib/defaultSideEffects.js +6 -1
  11. package/dist-cjs/lib/defaultSideEffects.js.map +2 -2
  12. package/dist-cjs/lib/shapes/arrow/ArrowShapeUtil.js +14 -13
  13. package/dist-cjs/lib/shapes/arrow/ArrowShapeUtil.js.map +2 -2
  14. package/dist-cjs/lib/shapes/arrow/arrow-types.js.map +1 -1
  15. package/dist-cjs/lib/shapes/arrow/arrowLabel.js +1 -1
  16. package/dist-cjs/lib/shapes/arrow/arrowLabel.js.map +2 -2
  17. package/dist-cjs/lib/shapes/arrow/arrowTargetState.js +1 -1
  18. package/dist-cjs/lib/shapes/arrow/arrowTargetState.js.map +2 -2
  19. package/dist-cjs/lib/shapes/arrow/elbow/getElbowArrowInfo.js +1 -1
  20. package/dist-cjs/lib/shapes/arrow/elbow/getElbowArrowInfo.js.map +2 -2
  21. package/dist-cjs/lib/shapes/arrow/toolStates/Idle.js +4 -10
  22. package/dist-cjs/lib/shapes/arrow/toolStates/Idle.js.map +2 -2
  23. package/dist-cjs/lib/shapes/arrow/toolStates/Pointing.js +7 -4
  24. package/dist-cjs/lib/shapes/arrow/toolStates/Pointing.js.map +2 -2
  25. package/dist-cjs/lib/shapes/bookmark/BookmarkShapeUtil.js +1 -1
  26. package/dist-cjs/lib/shapes/bookmark/BookmarkShapeUtil.js.map +2 -2
  27. package/dist-cjs/lib/shapes/draw/DrawShapeUtil.js +25 -23
  28. package/dist-cjs/lib/shapes/draw/DrawShapeUtil.js.map +2 -2
  29. package/dist-cjs/lib/shapes/draw/getPath.js +20 -11
  30. package/dist-cjs/lib/shapes/draw/getPath.js.map +2 -2
  31. package/dist-cjs/lib/shapes/draw/toolStates/Drawing.js +82 -86
  32. package/dist-cjs/lib/shapes/draw/toolStates/Drawing.js.map +3 -3
  33. package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js +6 -0
  34. package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js.map +2 -2
  35. package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js +6 -5
  36. package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js.map +2 -2
  37. package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js +146 -142
  38. package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js.map +2 -2
  39. package/dist-cjs/lib/shapes/geo/toolStates/Idle.js +5 -10
  40. package/dist-cjs/lib/shapes/geo/toolStates/Idle.js.map +2 -2
  41. package/dist-cjs/lib/shapes/geo/toolStates/Pointing.js +3 -3
  42. package/dist-cjs/lib/shapes/geo/toolStates/Pointing.js.map +2 -2
  43. package/dist-cjs/lib/shapes/highlight/HighlightShapeUtil.js +23 -21
  44. package/dist-cjs/lib/shapes/highlight/HighlightShapeUtil.js.map +2 -2
  45. package/dist-cjs/lib/shapes/line/toolStates/Pointing.js +3 -3
  46. package/dist-cjs/lib/shapes/line/toolStates/Pointing.js.map +2 -2
  47. package/dist-cjs/lib/shapes/note/NoteShapeUtil.js +6 -11
  48. package/dist-cjs/lib/shapes/note/NoteShapeUtil.js.map +2 -2
  49. package/dist-cjs/lib/shapes/note/toolStates/Pointing.js +5 -10
  50. package/dist-cjs/lib/shapes/note/toolStates/Pointing.js.map +2 -2
  51. package/dist-cjs/lib/shapes/shared/HyperlinkButton.js +3 -2
  52. package/dist-cjs/lib/shapes/shared/HyperlinkButton.js.map +2 -2
  53. package/dist-cjs/lib/shapes/shared/PlainTextLabel.js +14 -2
  54. package/dist-cjs/lib/shapes/shared/PlainTextLabel.js.map +3 -3
  55. package/dist-cjs/lib/shapes/shared/RichTextLabel.js +12 -4
  56. package/dist-cjs/lib/shapes/shared/RichTextLabel.js.map +3 -3
  57. package/dist-cjs/lib/shapes/shared/ShapeFill.js +2 -2
  58. package/dist-cjs/lib/shapes/shared/ShapeFill.js.map +2 -2
  59. package/dist-cjs/lib/shapes/shared/interpolate-props.js +3 -3
  60. package/dist-cjs/lib/shapes/shared/interpolate-props.js.map +2 -2
  61. package/dist-cjs/lib/shapes/shared/{useForceSolid.js → useEfficientZoomThreshold.js} +10 -7
  62. package/dist-cjs/lib/shapes/shared/useEfficientZoomThreshold.js.map +7 -0
  63. package/dist-cjs/lib/shapes/shared/useImageOrVideoAsset.js +1 -1
  64. package/dist-cjs/lib/shapes/shared/useImageOrVideoAsset.js.map +2 -2
  65. package/dist-cjs/lib/shapes/text/RichTextArea.js +5 -0
  66. package/dist-cjs/lib/shapes/text/RichTextArea.js.map +2 -2
  67. package/dist-cjs/lib/shapes/text/TextShapeUtil.js +5 -2
  68. package/dist-cjs/lib/shapes/text/TextShapeUtil.js.map +2 -2
  69. package/dist-cjs/lib/shapes/text/toolStates/Idle.js +4 -10
  70. package/dist-cjs/lib/shapes/text/toolStates/Idle.js.map +2 -2
  71. package/dist-cjs/lib/shapes/text/toolStates/Pointing.js +7 -5
  72. package/dist-cjs/lib/shapes/text/toolStates/Pointing.js.map +2 -2
  73. package/dist-cjs/lib/shapes/video/VideoShapeUtil.js +1 -1
  74. package/dist-cjs/lib/shapes/video/VideoShapeUtil.js.map +2 -2
  75. package/dist-cjs/lib/tools/EraserTool/childStates/Erasing.js +4 -5
  76. package/dist-cjs/lib/tools/EraserTool/childStates/Erasing.js.map +2 -2
  77. package/dist-cjs/lib/tools/EraserTool/childStates/Pointing.js +2 -4
  78. package/dist-cjs/lib/tools/EraserTool/childStates/Pointing.js.map +2 -2
  79. package/dist-cjs/lib/tools/HandTool/HandTool.js +3 -5
  80. package/dist-cjs/lib/tools/HandTool/HandTool.js.map +2 -2
  81. package/dist-cjs/lib/tools/HandTool/childStates/Dragging.js +3 -2
  82. package/dist-cjs/lib/tools/HandTool/childStates/Dragging.js.map +2 -2
  83. package/dist-cjs/lib/tools/HandTool/childStates/Pointing.js +1 -1
  84. package/dist-cjs/lib/tools/HandTool/childStates/Pointing.js.map +2 -2
  85. package/dist-cjs/lib/tools/LaserTool/childStates/Lasering.js +1 -1
  86. package/dist-cjs/lib/tools/LaserTool/childStates/Lasering.js.map +2 -2
  87. package/dist-cjs/lib/tools/SelectTool/DragAndDropManager.js +9 -7
  88. package/dist-cjs/lib/tools/SelectTool/DragAndDropManager.js.map +2 -2
  89. package/dist-cjs/lib/tools/SelectTool/childStates/Brushing.js +6 -5
  90. package/dist-cjs/lib/tools/SelectTool/childStates/Brushing.js.map +2 -2
  91. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/Cropping.js +4 -6
  92. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/Cropping.js.map +2 -2
  93. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/Idle.js +1 -1
  94. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/Idle.js.map +2 -2
  95. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/PointingCrop.js +1 -1
  96. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/PointingCrop.js.map +2 -2
  97. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.js +1 -1
  98. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.js.map +2 -2
  99. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/TranslatingCrop.js +2 -1
  100. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/TranslatingCrop.js.map +2 -2
  101. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/crop_helpers.js +1 -1
  102. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/crop_helpers.js.map +2 -2
  103. package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js +7 -5
  104. package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js.map +2 -2
  105. package/dist-cjs/lib/tools/SelectTool/childStates/EditingShape.js +38 -11
  106. package/dist-cjs/lib/tools/SelectTool/childStates/EditingShape.js.map +2 -2
  107. package/dist-cjs/lib/tools/SelectTool/childStates/Idle.js +42 -50
  108. package/dist-cjs/lib/tools/SelectTool/childStates/Idle.js.map +2 -2
  109. package/dist-cjs/lib/tools/SelectTool/childStates/PointingArrowLabel.js +6 -6
  110. package/dist-cjs/lib/tools/SelectTool/childStates/PointingArrowLabel.js.map +2 -2
  111. package/dist-cjs/lib/tools/SelectTool/childStates/PointingCanvas.js +1 -1
  112. package/dist-cjs/lib/tools/SelectTool/childStates/PointingCanvas.js.map +2 -2
  113. package/dist-cjs/lib/tools/SelectTool/childStates/PointingHandle.js +4 -14
  114. package/dist-cjs/lib/tools/SelectTool/childStates/PointingHandle.js.map +2 -2
  115. package/dist-cjs/lib/tools/SelectTool/childStates/PointingResizeHandle.js +1 -1
  116. package/dist-cjs/lib/tools/SelectTool/childStates/PointingResizeHandle.js.map +2 -2
  117. package/dist-cjs/lib/tools/SelectTool/childStates/PointingRotateHandle.js +1 -1
  118. package/dist-cjs/lib/tools/SelectTool/childStates/PointingRotateHandle.js.map +2 -2
  119. package/dist-cjs/lib/tools/SelectTool/childStates/PointingSelection.js +2 -2
  120. package/dist-cjs/lib/tools/SelectTool/childStates/PointingSelection.js.map +2 -2
  121. package/dist-cjs/lib/tools/SelectTool/childStates/PointingShape.js +4 -13
  122. package/dist-cjs/lib/tools/SelectTool/childStates/PointingShape.js.map +2 -2
  123. package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js +5 -6
  124. package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js.map +2 -2
  125. package/dist-cjs/lib/tools/SelectTool/childStates/Rotating.js +2 -3
  126. package/dist-cjs/lib/tools/SelectTool/childStates/Rotating.js.map +2 -2
  127. package/dist-cjs/lib/tools/SelectTool/childStates/ScribbleBrushing.js +7 -6
  128. package/dist-cjs/lib/tools/SelectTool/childStates/ScribbleBrushing.js.map +2 -2
  129. package/dist-cjs/lib/tools/SelectTool/childStates/Translating.js +13 -11
  130. package/dist-cjs/lib/tools/SelectTool/childStates/Translating.js.map +2 -2
  131. package/dist-cjs/lib/tools/SelectTool/selectHelpers.js +15 -4
  132. package/dist-cjs/lib/tools/SelectTool/selectHelpers.js.map +2 -2
  133. package/dist-cjs/lib/tools/ZoomTool/ZoomTool.js +1 -1
  134. package/dist-cjs/lib/tools/ZoomTool/ZoomTool.js.map +2 -2
  135. package/dist-cjs/lib/tools/ZoomTool/childStates/Pointing.js +3 -3
  136. package/dist-cjs/lib/tools/ZoomTool/childStates/Pointing.js.map +2 -2
  137. package/dist-cjs/lib/tools/ZoomTool/childStates/ZoomBrushing.js +5 -6
  138. package/dist-cjs/lib/tools/ZoomTool/childStates/ZoomBrushing.js.map +2 -2
  139. package/dist-cjs/lib/tools/selection-logic/getHitShapeOnCanvasPointerDown.js +1 -3
  140. package/dist-cjs/lib/tools/selection-logic/getHitShapeOnCanvasPointerDown.js.map +2 -2
  141. package/dist-cjs/lib/tools/selection-logic/selectOnCanvasPointerUp.js +1 -1
  142. package/dist-cjs/lib/tools/selection-logic/selectOnCanvasPointerUp.js.map +2 -2
  143. package/dist-cjs/lib/tools/selection-logic/updateHoveredShapeId.js +1 -1
  144. package/dist-cjs/lib/tools/selection-logic/updateHoveredShapeId.js.map +2 -2
  145. package/dist-cjs/lib/ui/TldrawUi.js +2 -2
  146. package/dist-cjs/lib/ui/TldrawUi.js.map +2 -2
  147. package/dist-cjs/lib/ui/components/ActionsMenu/DefaultActionsMenuContent.js +3 -9
  148. package/dist-cjs/lib/ui/components/ActionsMenu/DefaultActionsMenuContent.js.map +2 -2
  149. package/dist-cjs/lib/ui/components/ContextMenu/DefaultContextMenu.js +1 -3
  150. package/dist-cjs/lib/ui/components/ContextMenu/DefaultContextMenu.js.map +2 -2
  151. package/dist-cjs/lib/ui/components/CursorChatBubble.js +1 -1
  152. package/dist-cjs/lib/ui/components/CursorChatBubble.js.map +2 -2
  153. package/dist-cjs/lib/ui/components/DefaultDebugPanel.js +1 -21
  154. package/dist-cjs/lib/ui/components/DefaultDebugPanel.js.map +2 -2
  155. package/dist-cjs/lib/ui/components/HelperButtons/StopFollowing.js.map +2 -2
  156. package/dist-cjs/lib/ui/components/Minimap/DefaultMinimap.js +1 -1
  157. package/dist-cjs/lib/ui/components/Minimap/DefaultMinimap.js.map +2 -2
  158. package/dist-cjs/lib/ui/components/OfflineIndicator/OfflineIndicator.js +2 -15
  159. package/dist-cjs/lib/ui/components/OfflineIndicator/OfflineIndicator.js.map +3 -3
  160. package/dist-cjs/lib/ui/components/PageMenu/PageItemInput.js +3 -1
  161. package/dist-cjs/lib/ui/components/PageMenu/PageItemInput.js.map +2 -2
  162. package/dist-cjs/lib/ui/components/SharePanel/PeopleMenu.js +6 -0
  163. package/dist-cjs/lib/ui/components/SharePanel/PeopleMenu.js.map +2 -2
  164. package/dist-cjs/lib/ui/components/Toolbar/DefaultImageToolbar.js +1 -1
  165. package/dist-cjs/lib/ui/components/Toolbar/DefaultImageToolbar.js.map +2 -2
  166. package/dist-cjs/lib/ui/components/Toolbar/DefaultRichTextToolbar.js +1 -1
  167. package/dist-cjs/lib/ui/components/Toolbar/DefaultRichTextToolbar.js.map +2 -2
  168. package/dist-cjs/lib/ui/components/TopPanel/CenteredTopPanelContainer.js.map +1 -1
  169. package/dist-cjs/lib/ui/components/menu-items.js +3 -1
  170. package/dist-cjs/lib/ui/components/menu-items.js.map +2 -2
  171. package/dist-cjs/lib/ui/components/primitives/TldrawUiContextualToolbar.js +3 -1
  172. package/dist-cjs/lib/ui/components/primitives/TldrawUiContextualToolbar.js.map +2 -2
  173. package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js +6 -5
  174. package/dist-cjs/lib/ui/components/primitives/TldrawUiTooltip.js.map +2 -2
  175. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuActionCheckboxItem.js.map +2 -2
  176. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuActionItem.js.map +2 -2
  177. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js +1 -2
  178. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js.map +2 -2
  179. package/dist-cjs/lib/ui/context/actions.js +6 -6
  180. package/dist-cjs/lib/ui/context/actions.js.map +2 -2
  181. package/dist-cjs/lib/ui/context/components.js +1 -2
  182. package/dist-cjs/lib/ui/context/components.js.map +2 -2
  183. package/dist-cjs/lib/ui/hooks/useClipboardEvents.js +2 -2
  184. package/dist-cjs/lib/ui/hooks/useClipboardEvents.js.map +2 -2
  185. package/dist-cjs/lib/ui/hooks/useKeyboardShortcuts.js +2 -2
  186. package/dist-cjs/lib/ui/hooks/useKeyboardShortcuts.js.map +2 -2
  187. package/dist-cjs/lib/ui/hooks/useTools.js +4 -5
  188. package/dist-cjs/lib/ui/hooks/useTools.js.map +2 -2
  189. package/dist-cjs/lib/ui/version.js +3 -3
  190. package/dist-cjs/lib/ui/version.js.map +1 -1
  191. package/dist-cjs/lib/utils/excalidraw/putExcalidrawContent.js +8 -6
  192. package/dist-cjs/lib/utils/excalidraw/putExcalidrawContent.js.map +2 -2
  193. package/dist-cjs/lib/{tools/selection-logic/getShouldEnterCropModeOnPointerDown.js → utils/test-helpers.js} +21 -8
  194. package/dist-cjs/lib/utils/test-helpers.js.map +7 -0
  195. package/dist-cjs/lib/utils/text/richText.js +4 -2
  196. package/dist-cjs/lib/utils/text/richText.js.map +2 -2
  197. package/dist-cjs/lib/utils/tldr/buildFromV1Document.js +7 -2
  198. package/dist-cjs/lib/utils/tldr/buildFromV1Document.js.map +2 -2
  199. package/dist-esm/index.d.mts +294 -237
  200. package/dist-esm/index.mjs +12 -5
  201. package/dist-esm/index.mjs.map +2 -2
  202. package/dist-esm/lib/canvas/TldrawSelectionForeground.mjs +2 -2
  203. package/dist-esm/lib/canvas/TldrawSelectionForeground.mjs.map +2 -2
  204. package/dist-esm/lib/defaultEmbedDefinitions.mjs +1 -1
  205. package/dist-esm/lib/defaultEmbedDefinitions.mjs.map +2 -2
  206. package/dist-esm/lib/defaultExternalContentHandlers.mjs +5 -5
  207. package/dist-esm/lib/defaultExternalContentHandlers.mjs.map +2 -2
  208. package/dist-esm/lib/defaultSideEffects.mjs +6 -1
  209. package/dist-esm/lib/defaultSideEffects.mjs.map +2 -2
  210. package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs +15 -15
  211. package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs.map +2 -2
  212. package/dist-esm/lib/shapes/arrow/arrowLabel.mjs +1 -1
  213. package/dist-esm/lib/shapes/arrow/arrowLabel.mjs.map +2 -2
  214. package/dist-esm/lib/shapes/arrow/arrowTargetState.mjs +1 -1
  215. package/dist-esm/lib/shapes/arrow/arrowTargetState.mjs.map +2 -2
  216. package/dist-esm/lib/shapes/arrow/elbow/getElbowArrowInfo.mjs +1 -1
  217. package/dist-esm/lib/shapes/arrow/elbow/getElbowArrowInfo.mjs.map +2 -2
  218. package/dist-esm/lib/shapes/arrow/toolStates/Idle.mjs +4 -10
  219. package/dist-esm/lib/shapes/arrow/toolStates/Idle.mjs.map +2 -2
  220. package/dist-esm/lib/shapes/arrow/toolStates/Pointing.mjs +7 -4
  221. package/dist-esm/lib/shapes/arrow/toolStates/Pointing.mjs.map +2 -2
  222. package/dist-esm/lib/shapes/bookmark/BookmarkShapeUtil.mjs +1 -1
  223. package/dist-esm/lib/shapes/bookmark/BookmarkShapeUtil.mjs.map +2 -2
  224. package/dist-esm/lib/shapes/draw/DrawShapeUtil.mjs +30 -25
  225. package/dist-esm/lib/shapes/draw/DrawShapeUtil.mjs.map +2 -2
  226. package/dist-esm/lib/shapes/draw/getPath.mjs +21 -11
  227. package/dist-esm/lib/shapes/draw/getPath.mjs.map +2 -2
  228. package/dist-esm/lib/shapes/draw/toolStates/Drawing.mjs +83 -86
  229. package/dist-esm/lib/shapes/draw/toolStates/Drawing.mjs.map +3 -3
  230. package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs +6 -0
  231. package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs.map +2 -2
  232. package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs +6 -5
  233. package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs.map +2 -2
  234. package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs +147 -142
  235. package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs.map +2 -2
  236. package/dist-esm/lib/shapes/geo/toolStates/Idle.mjs +5 -10
  237. package/dist-esm/lib/shapes/geo/toolStates/Idle.mjs.map +2 -2
  238. package/dist-esm/lib/shapes/geo/toolStates/Pointing.mjs +3 -3
  239. package/dist-esm/lib/shapes/geo/toolStates/Pointing.mjs.map +2 -2
  240. package/dist-esm/lib/shapes/highlight/HighlightShapeUtil.mjs +24 -22
  241. package/dist-esm/lib/shapes/highlight/HighlightShapeUtil.mjs.map +2 -2
  242. package/dist-esm/lib/shapes/line/toolStates/Pointing.mjs +3 -3
  243. package/dist-esm/lib/shapes/line/toolStates/Pointing.mjs.map +2 -2
  244. package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs +7 -12
  245. package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs.map +2 -2
  246. package/dist-esm/lib/shapes/note/toolStates/Pointing.mjs +5 -10
  247. package/dist-esm/lib/shapes/note/toolStates/Pointing.mjs.map +2 -2
  248. package/dist-esm/lib/shapes/shared/HyperlinkButton.mjs +4 -3
  249. package/dist-esm/lib/shapes/shared/HyperlinkButton.mjs.map +2 -2
  250. package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs +14 -2
  251. package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs.map +2 -2
  252. package/dist-esm/lib/shapes/shared/RichTextLabel.mjs +12 -4
  253. package/dist-esm/lib/shapes/shared/RichTextLabel.mjs.map +2 -2
  254. package/dist-esm/lib/shapes/shared/ShapeFill.mjs +2 -2
  255. package/dist-esm/lib/shapes/shared/ShapeFill.mjs.map +2 -2
  256. package/dist-esm/lib/shapes/shared/interpolate-props.mjs +4 -4
  257. package/dist-esm/lib/shapes/shared/interpolate-props.mjs.map +2 -2
  258. package/dist-esm/lib/shapes/shared/useEfficientZoomThreshold.mjs +12 -0
  259. package/dist-esm/lib/shapes/shared/useEfficientZoomThreshold.mjs.map +7 -0
  260. package/dist-esm/lib/shapes/shared/useImageOrVideoAsset.mjs +1 -1
  261. package/dist-esm/lib/shapes/shared/useImageOrVideoAsset.mjs.map +2 -2
  262. package/dist-esm/lib/shapes/text/RichTextArea.mjs +5 -0
  263. package/dist-esm/lib/shapes/text/RichTextArea.mjs.map +2 -2
  264. package/dist-esm/lib/shapes/text/TextShapeUtil.mjs +5 -2
  265. package/dist-esm/lib/shapes/text/TextShapeUtil.mjs.map +2 -2
  266. package/dist-esm/lib/shapes/text/toolStates/Idle.mjs +4 -10
  267. package/dist-esm/lib/shapes/text/toolStates/Idle.mjs.map +2 -2
  268. package/dist-esm/lib/shapes/text/toolStates/Pointing.mjs +7 -5
  269. package/dist-esm/lib/shapes/text/toolStates/Pointing.mjs.map +2 -2
  270. package/dist-esm/lib/shapes/video/VideoShapeUtil.mjs +1 -1
  271. package/dist-esm/lib/shapes/video/VideoShapeUtil.mjs.map +2 -2
  272. package/dist-esm/lib/tools/EraserTool/childStates/Erasing.mjs +4 -5
  273. package/dist-esm/lib/tools/EraserTool/childStates/Erasing.mjs.map +2 -2
  274. package/dist-esm/lib/tools/EraserTool/childStates/Pointing.mjs +2 -4
  275. package/dist-esm/lib/tools/EraserTool/childStates/Pointing.mjs.map +2 -2
  276. package/dist-esm/lib/tools/HandTool/HandTool.mjs +3 -5
  277. package/dist-esm/lib/tools/HandTool/HandTool.mjs.map +2 -2
  278. package/dist-esm/lib/tools/HandTool/childStates/Dragging.mjs +3 -2
  279. package/dist-esm/lib/tools/HandTool/childStates/Dragging.mjs.map +2 -2
  280. package/dist-esm/lib/tools/HandTool/childStates/Pointing.mjs +1 -1
  281. package/dist-esm/lib/tools/HandTool/childStates/Pointing.mjs.map +2 -2
  282. package/dist-esm/lib/tools/LaserTool/childStates/Lasering.mjs +1 -1
  283. package/dist-esm/lib/tools/LaserTool/childStates/Lasering.mjs.map +2 -2
  284. package/dist-esm/lib/tools/SelectTool/DragAndDropManager.mjs +9 -7
  285. package/dist-esm/lib/tools/SelectTool/DragAndDropManager.mjs.map +2 -2
  286. package/dist-esm/lib/tools/SelectTool/childStates/Brushing.mjs +6 -5
  287. package/dist-esm/lib/tools/SelectTool/childStates/Brushing.mjs.map +2 -2
  288. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/Cropping.mjs +4 -6
  289. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/Cropping.mjs.map +2 -2
  290. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/Idle.mjs +1 -1
  291. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/Idle.mjs.map +2 -2
  292. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/PointingCrop.mjs +1 -1
  293. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/PointingCrop.mjs.map +2 -2
  294. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.mjs +1 -1
  295. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.mjs.map +2 -2
  296. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/TranslatingCrop.mjs +2 -1
  297. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/TranslatingCrop.mjs.map +2 -2
  298. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/crop_helpers.mjs +1 -1
  299. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/crop_helpers.mjs.map +2 -2
  300. package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs +7 -5
  301. package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs.map +2 -2
  302. package/dist-esm/lib/tools/SelectTool/childStates/EditingShape.mjs +38 -11
  303. package/dist-esm/lib/tools/SelectTool/childStates/EditingShape.mjs.map +2 -2
  304. package/dist-esm/lib/tools/SelectTool/childStates/Idle.mjs +43 -51
  305. package/dist-esm/lib/tools/SelectTool/childStates/Idle.mjs.map +2 -2
  306. package/dist-esm/lib/tools/SelectTool/childStates/PointingArrowLabel.mjs +6 -6
  307. package/dist-esm/lib/tools/SelectTool/childStates/PointingArrowLabel.mjs.map +2 -2
  308. package/dist-esm/lib/tools/SelectTool/childStates/PointingCanvas.mjs +1 -1
  309. package/dist-esm/lib/tools/SelectTool/childStates/PointingCanvas.mjs.map +2 -2
  310. package/dist-esm/lib/tools/SelectTool/childStates/PointingHandle.mjs +5 -15
  311. package/dist-esm/lib/tools/SelectTool/childStates/PointingHandle.mjs.map +2 -2
  312. package/dist-esm/lib/tools/SelectTool/childStates/PointingResizeHandle.mjs +1 -1
  313. package/dist-esm/lib/tools/SelectTool/childStates/PointingResizeHandle.mjs.map +2 -2
  314. package/dist-esm/lib/tools/SelectTool/childStates/PointingRotateHandle.mjs +1 -1
  315. package/dist-esm/lib/tools/SelectTool/childStates/PointingRotateHandle.mjs.map +2 -2
  316. package/dist-esm/lib/tools/SelectTool/childStates/PointingSelection.mjs +2 -2
  317. package/dist-esm/lib/tools/SelectTool/childStates/PointingSelection.mjs.map +2 -2
  318. package/dist-esm/lib/tools/SelectTool/childStates/PointingShape.mjs +4 -13
  319. package/dist-esm/lib/tools/SelectTool/childStates/PointingShape.mjs.map +2 -2
  320. package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs +5 -6
  321. package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs.map +2 -2
  322. package/dist-esm/lib/tools/SelectTool/childStates/Rotating.mjs +2 -3
  323. package/dist-esm/lib/tools/SelectTool/childStates/Rotating.mjs.map +2 -2
  324. package/dist-esm/lib/tools/SelectTool/childStates/ScribbleBrushing.mjs +7 -6
  325. package/dist-esm/lib/tools/SelectTool/childStates/ScribbleBrushing.mjs.map +2 -2
  326. package/dist-esm/lib/tools/SelectTool/childStates/Translating.mjs +13 -11
  327. package/dist-esm/lib/tools/SelectTool/childStates/Translating.mjs.map +2 -2
  328. package/dist-esm/lib/tools/SelectTool/selectHelpers.mjs +17 -4
  329. package/dist-esm/lib/tools/SelectTool/selectHelpers.mjs.map +2 -2
  330. package/dist-esm/lib/tools/ZoomTool/ZoomTool.mjs +1 -1
  331. package/dist-esm/lib/tools/ZoomTool/ZoomTool.mjs.map +2 -2
  332. package/dist-esm/lib/tools/ZoomTool/childStates/Pointing.mjs +3 -3
  333. package/dist-esm/lib/tools/ZoomTool/childStates/Pointing.mjs.map +2 -2
  334. package/dist-esm/lib/tools/ZoomTool/childStates/ZoomBrushing.mjs +5 -6
  335. package/dist-esm/lib/tools/ZoomTool/childStates/ZoomBrushing.mjs.map +2 -2
  336. package/dist-esm/lib/tools/selection-logic/getHitShapeOnCanvasPointerDown.mjs +1 -3
  337. package/dist-esm/lib/tools/selection-logic/getHitShapeOnCanvasPointerDown.mjs.map +2 -2
  338. package/dist-esm/lib/tools/selection-logic/selectOnCanvasPointerUp.mjs +1 -1
  339. package/dist-esm/lib/tools/selection-logic/selectOnCanvasPointerUp.mjs.map +2 -2
  340. package/dist-esm/lib/tools/selection-logic/updateHoveredShapeId.mjs +1 -1
  341. package/dist-esm/lib/tools/selection-logic/updateHoveredShapeId.mjs.map +2 -2
  342. package/dist-esm/lib/ui/TldrawUi.mjs +2 -2
  343. package/dist-esm/lib/ui/TldrawUi.mjs.map +2 -2
  344. package/dist-esm/lib/ui/components/ActionsMenu/DefaultActionsMenuContent.mjs +2 -8
  345. package/dist-esm/lib/ui/components/ActionsMenu/DefaultActionsMenuContent.mjs.map +2 -2
  346. package/dist-esm/lib/ui/components/ContextMenu/DefaultContextMenu.mjs +1 -3
  347. package/dist-esm/lib/ui/components/ContextMenu/DefaultContextMenu.mjs.map +2 -2
  348. package/dist-esm/lib/ui/components/CursorChatBubble.mjs +1 -1
  349. package/dist-esm/lib/ui/components/CursorChatBubble.mjs.map +2 -2
  350. package/dist-esm/lib/ui/components/DefaultDebugPanel.mjs +3 -30
  351. package/dist-esm/lib/ui/components/DefaultDebugPanel.mjs.map +2 -2
  352. package/dist-esm/lib/ui/components/HelperButtons/StopFollowing.mjs.map +2 -2
  353. package/dist-esm/lib/ui/components/Minimap/DefaultMinimap.mjs +1 -1
  354. package/dist-esm/lib/ui/components/Minimap/DefaultMinimap.mjs.map +2 -2
  355. package/dist-esm/lib/ui/components/OfflineIndicator/OfflineIndicator.mjs +3 -6
  356. package/dist-esm/lib/ui/components/OfflineIndicator/OfflineIndicator.mjs.map +2 -2
  357. package/dist-esm/lib/ui/components/PageMenu/PageItemInput.mjs +3 -1
  358. package/dist-esm/lib/ui/components/PageMenu/PageItemInput.mjs.map +2 -2
  359. package/dist-esm/lib/ui/components/SharePanel/PeopleMenu.mjs +6 -0
  360. package/dist-esm/lib/ui/components/SharePanel/PeopleMenu.mjs.map +2 -2
  361. package/dist-esm/lib/ui/components/Toolbar/DefaultImageToolbar.mjs +1 -1
  362. package/dist-esm/lib/ui/components/Toolbar/DefaultImageToolbar.mjs.map +2 -2
  363. package/dist-esm/lib/ui/components/Toolbar/DefaultRichTextToolbar.mjs +1 -1
  364. package/dist-esm/lib/ui/components/Toolbar/DefaultRichTextToolbar.mjs.map +2 -2
  365. package/dist-esm/lib/ui/components/TopPanel/CenteredTopPanelContainer.mjs.map +1 -1
  366. package/dist-esm/lib/ui/components/menu-items.mjs +3 -1
  367. package/dist-esm/lib/ui/components/menu-items.mjs.map +2 -2
  368. package/dist-esm/lib/ui/components/primitives/TldrawUiContextualToolbar.mjs +3 -1
  369. package/dist-esm/lib/ui/components/primitives/TldrawUiContextualToolbar.mjs.map +2 -2
  370. package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs +6 -5
  371. package/dist-esm/lib/ui/components/primitives/TldrawUiTooltip.mjs.map +2 -2
  372. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuActionCheckboxItem.mjs.map +2 -2
  373. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuActionItem.mjs.map +2 -2
  374. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs +1 -2
  375. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs.map +2 -2
  376. package/dist-esm/lib/ui/context/actions.mjs +6 -6
  377. package/dist-esm/lib/ui/context/actions.mjs.map +2 -2
  378. package/dist-esm/lib/ui/context/components.mjs +1 -2
  379. package/dist-esm/lib/ui/context/components.mjs.map +2 -2
  380. package/dist-esm/lib/ui/hooks/useClipboardEvents.mjs +2 -2
  381. package/dist-esm/lib/ui/hooks/useClipboardEvents.mjs.map +2 -2
  382. package/dist-esm/lib/ui/hooks/useKeyboardShortcuts.mjs +2 -2
  383. package/dist-esm/lib/ui/hooks/useKeyboardShortcuts.mjs.map +2 -2
  384. package/dist-esm/lib/ui/hooks/useTools.mjs +4 -5
  385. package/dist-esm/lib/ui/hooks/useTools.mjs.map +2 -2
  386. package/dist-esm/lib/ui/version.mjs +3 -3
  387. package/dist-esm/lib/ui/version.mjs.map +1 -1
  388. package/dist-esm/lib/utils/excalidraw/putExcalidrawContent.mjs +9 -6
  389. package/dist-esm/lib/utils/excalidraw/putExcalidrawContent.mjs.map +2 -2
  390. package/dist-esm/lib/utils/test-helpers.mjs +21 -0
  391. package/dist-esm/lib/utils/test-helpers.mjs.map +7 -0
  392. package/dist-esm/lib/utils/text/richText.mjs +5 -2
  393. package/dist-esm/lib/utils/text/richText.mjs.map +2 -2
  394. package/dist-esm/lib/utils/tldr/buildFromV1Document.mjs +8 -2
  395. package/dist-esm/lib/utils/tldr/buildFromV1Document.mjs.map +2 -2
  396. package/package.json +18 -16
  397. package/src/index.ts +5 -2
  398. package/src/lib/Tldraw.test.tsx +46 -1
  399. package/src/lib/canvas/TldrawSelectionForeground.tsx +2 -2
  400. package/src/lib/defaultEmbedDefinitions.ts +2 -1
  401. package/src/lib/defaultExternalContentHandlers.ts +10 -10
  402. package/src/lib/defaultSideEffects.ts +6 -1
  403. package/src/lib/shapes/arrow/ArrowShapeOptions.test.ts +40 -133
  404. package/src/lib/shapes/arrow/ArrowShapeTool.test.ts +8 -8
  405. package/src/lib/shapes/arrow/ArrowShapeUtil.tsx +15 -15
  406. package/src/lib/shapes/arrow/arrow-types.ts +2 -0
  407. package/src/lib/shapes/arrow/arrowLabel.ts +1 -1
  408. package/src/lib/shapes/arrow/arrowTargetState.ts +1 -1
  409. package/src/lib/shapes/arrow/elbow/getElbowArrowInfo.test.ts +80 -0
  410. package/src/lib/shapes/arrow/elbow/getElbowArrowInfo.tsx +1 -1
  411. package/src/lib/shapes/arrow/toolStates/Idle.tsx +4 -14
  412. package/src/lib/shapes/arrow/toolStates/Pointing.tsx +7 -4
  413. package/src/lib/shapes/bookmark/BookmarkShapeUtil.tsx +1 -1
  414. package/src/lib/shapes/draw/DrawShapeUtil.test.ts +146 -0
  415. package/src/lib/shapes/draw/DrawShapeUtil.tsx +31 -27
  416. package/src/lib/shapes/draw/getPath.ts +31 -10
  417. package/src/lib/shapes/draw/toolStates/Drawing.ts +96 -86
  418. package/src/lib/shapes/embed/EmbedShapeUtil.tsx +7 -0
  419. package/src/lib/shapes/frame/FrameShapeUtil.tsx +10 -4
  420. package/src/lib/shapes/geo/GeoShapeUtil.tsx +228 -176
  421. package/src/lib/shapes/geo/toolStates/Idle.ts +5 -15
  422. package/src/lib/shapes/geo/toolStates/Pointing.ts +3 -3
  423. package/src/lib/shapes/highlight/HighlightShapeUtil.test.ts +146 -0
  424. package/src/lib/shapes/highlight/HighlightShapeUtil.tsx +25 -24
  425. package/src/lib/shapes/line/toolStates/Pointing.ts +3 -3
  426. package/src/lib/shapes/note/NoteShapeUtil.tsx +9 -10
  427. package/src/lib/shapes/note/noteCloning.test.ts +3 -1
  428. package/src/lib/shapes/note/toolStates/Pointing.ts +5 -10
  429. package/src/lib/shapes/shared/HyperlinkButton.tsx +4 -3
  430. package/src/lib/shapes/shared/PlainTextLabel.tsx +10 -1
  431. package/src/lib/shapes/shared/RichTextLabel.tsx +12 -3
  432. package/src/lib/shapes/shared/ShapeFill.tsx +2 -2
  433. package/src/lib/shapes/shared/interpolate-props.ts +4 -4
  434. package/src/lib/shapes/shared/useEfficientZoomThreshold.ts +10 -0
  435. package/src/lib/shapes/shared/useImageOrVideoAsset.ts +1 -1
  436. package/src/lib/shapes/text/RichTextArea.tsx +5 -0
  437. package/src/lib/shapes/text/TextShapeUtil.tsx +5 -0
  438. package/src/lib/shapes/text/toolStates/Idle.ts +4 -14
  439. package/src/lib/shapes/text/toolStates/Pointing.ts +7 -7
  440. package/src/lib/shapes/video/VideoShapeUtil.tsx +2 -1
  441. package/src/lib/tools/EraserTool/childStates/Erasing.ts +4 -5
  442. package/src/lib/tools/EraserTool/childStates/Pointing.ts +2 -4
  443. package/src/lib/tools/HandTool/HandTool.ts +3 -5
  444. package/src/lib/tools/HandTool/childStates/Dragging.ts +3 -2
  445. package/src/lib/tools/HandTool/childStates/Pointing.ts +1 -1
  446. package/src/lib/tools/LaserTool/childStates/Lasering.ts +1 -1
  447. package/src/lib/tools/SelectTool/DragAndDropManager.ts +12 -7
  448. package/src/lib/tools/SelectTool/childStates/Brushing.ts +6 -5
  449. package/src/lib/tools/SelectTool/childStates/Crop/children/Cropping.ts +7 -6
  450. package/src/lib/tools/SelectTool/childStates/Crop/children/Idle.ts +1 -1
  451. package/src/lib/tools/SelectTool/childStates/Crop/children/PointingCrop.ts +1 -1
  452. package/src/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.ts +1 -1
  453. package/src/lib/tools/SelectTool/childStates/Crop/children/TranslatingCrop.ts +2 -1
  454. package/src/lib/tools/SelectTool/childStates/Crop/children/crop_helpers.ts +1 -1
  455. package/src/lib/tools/SelectTool/childStates/DraggingHandle.tsx +7 -5
  456. package/src/lib/tools/SelectTool/childStates/EditingShape.ts +55 -12
  457. package/src/lib/tools/SelectTool/childStates/Idle.ts +58 -71
  458. package/src/lib/tools/SelectTool/childStates/PointingArrowLabel.ts +6 -7
  459. package/src/lib/tools/SelectTool/childStates/PointingCanvas.ts +1 -1
  460. package/src/lib/tools/SelectTool/childStates/PointingHandle.ts +5 -5
  461. package/src/lib/tools/SelectTool/childStates/PointingResizeHandle.ts +1 -1
  462. package/src/lib/tools/SelectTool/childStates/PointingRotateHandle.ts +1 -1
  463. package/src/lib/tools/SelectTool/childStates/PointingSelection.ts +2 -2
  464. package/src/lib/tools/SelectTool/childStates/PointingShape.ts +4 -14
  465. package/src/lib/tools/SelectTool/childStates/Resizing.ts +6 -6
  466. package/src/lib/tools/SelectTool/childStates/Rotating.ts +2 -3
  467. package/src/lib/tools/SelectTool/childStates/ScribbleBrushing.ts +7 -6
  468. package/src/lib/tools/SelectTool/childStates/Translating.ts +15 -12
  469. package/src/lib/tools/SelectTool/selectHelpers.ts +39 -4
  470. package/src/lib/tools/ZoomTool/ZoomTool.ts +1 -1
  471. package/src/lib/tools/ZoomTool/childStates/Pointing.ts +3 -3
  472. package/src/lib/tools/ZoomTool/childStates/ZoomBrushing.ts +5 -6
  473. package/src/lib/tools/selection-logic/getHitShapeOnCanvasPointerDown.ts +1 -3
  474. package/src/lib/tools/selection-logic/selectOnCanvasPointerUp.ts +1 -1
  475. package/src/lib/tools/selection-logic/updateHoveredShapeId.ts +1 -1
  476. package/src/lib/ui/TldrawUi.tsx +5 -2
  477. package/src/lib/ui/components/ActionsMenu/DefaultActionsMenuContent.tsx +1 -9
  478. package/src/lib/ui/components/ContextMenu/DefaultContextMenu.tsx +1 -3
  479. package/src/lib/ui/components/CursorChatBubble.tsx +2 -2
  480. package/src/lib/ui/components/DefaultDebugPanel.tsx +3 -42
  481. package/src/lib/ui/components/HelperButtons/StopFollowing.tsx +2 -2
  482. package/src/lib/ui/components/Minimap/DefaultMinimap.tsx +1 -1
  483. package/src/lib/ui/components/OfflineIndicator/OfflineIndicator.tsx +6 -5
  484. package/src/lib/ui/components/PageMenu/PageItemInput.tsx +3 -1
  485. package/src/lib/ui/components/SharePanel/PeopleMenu.tsx +8 -0
  486. package/src/lib/ui/components/Toolbar/DefaultImageToolbar.tsx +1 -1
  487. package/src/lib/ui/components/Toolbar/DefaultRichTextToolbar.tsx +1 -1
  488. package/src/lib/ui/components/TopPanel/CenteredTopPanelContainer.tsx +1 -1
  489. package/src/lib/ui/components/menu-items.tsx +3 -1
  490. package/src/lib/ui/components/primitives/TldrawUiContextualToolbar.tsx +5 -3
  491. package/src/lib/ui/components/primitives/TldrawUiTooltip.tsx +12 -5
  492. package/src/lib/ui/components/primitives/menus/TldrawUiMenuActionCheckboxItem.tsx +1 -1
  493. package/src/lib/ui/components/primitives/menus/TldrawUiMenuActionItem.tsx +1 -1
  494. package/src/lib/ui/components/primitives/menus/TldrawUiMenuItem.tsx +0 -1
  495. package/src/lib/ui/context/actions.tsx +6 -6
  496. package/src/lib/ui/context/components.tsx +1 -2
  497. package/src/lib/ui/hooks/useClipboardEvents.ts +2 -2
  498. package/src/lib/ui/hooks/useKeyboardShortcuts.ts +2 -2
  499. package/src/lib/ui/hooks/useTools.tsx +4 -5
  500. package/src/lib/ui/version.ts +3 -3
  501. package/src/lib/ui.css +27 -23
  502. package/src/lib/utils/excalidraw/__snapshots__/putExcalidrawContent.test.tsx.snap +12 -48
  503. package/src/lib/utils/excalidraw/putExcalidrawContent.ts +11 -6
  504. package/src/lib/utils/test-helpers.ts +60 -0
  505. package/src/lib/utils/text/richText.ts +6 -5
  506. package/src/lib/utils/tldr/buildFromV1Document.ts +9 -2
  507. package/src/test/Editor.test.tsx +40 -29
  508. package/src/test/EraserTool.test.ts +10 -12
  509. package/src/test/TestEditor.ts +48 -47
  510. package/src/test/TldrawEditor.test.tsx +3 -2
  511. package/src/test/__snapshots__/drawing.test.ts.snap +3 -1257
  512. package/src/test/__snapshots__/resizing.test.ts.snap +3 -12
  513. package/src/test/arrows-megabus.test.tsx +1 -1
  514. package/src/test/commands/__snapshots__/getSvgString.test.ts.snap +10 -10
  515. package/src/test/commands/cameraState.test.ts +299 -0
  516. package/src/test/commands/setCamera.test.ts +13 -11
  517. package/src/test/commands/stackShapes.test.ts +34 -8
  518. package/src/test/commands/zoomToBounds.test.ts +19 -3
  519. package/src/test/commands/zoomToSelection.test.ts +14 -3
  520. package/src/test/custom-clipping.test.ts +16 -9
  521. package/src/test/drawing.test.ts +17 -10
  522. package/src/test/flipShapes.test.ts +33 -0
  523. package/src/test/frames.test.ts +92 -0
  524. package/src/test/groups.test.tsx +1 -1
  525. package/src/test/modifiers.test.ts +6 -6
  526. package/src/test/resizing.test.ts +7 -9
  527. package/src/test/selection-omnibus.test.ts +2 -2
  528. package/src/test/spacebarPanning.test.ts +28 -10
  529. package/src/test/test-jsx.tsx +3 -0
  530. package/tldraw.css +41 -35
  531. package/dist-cjs/lib/shapes/shared/useForceSolid.js.map +0 -7
  532. package/dist-cjs/lib/tools/selection-logic/getShouldEnterCropModeOnPointerDown.js.map +0 -7
  533. package/dist-cjs/lib/ui/components/TopPanel/DefaultTopPanel.js +0 -32
  534. package/dist-cjs/lib/ui/components/TopPanel/DefaultTopPanel.js.map +0 -7
  535. package/dist-cjs/lib/utils/text/textDirection.js +0 -51
  536. package/dist-cjs/lib/utils/text/textDirection.js.map +0 -7
  537. package/dist-esm/lib/shapes/shared/useForceSolid.mjs +0 -9
  538. package/dist-esm/lib/shapes/shared/useForceSolid.mjs.map +0 -7
  539. package/dist-esm/lib/tools/selection-logic/getShouldEnterCropModeOnPointerDown.mjs +0 -8
  540. package/dist-esm/lib/tools/selection-logic/getShouldEnterCropModeOnPointerDown.mjs.map +0 -7
  541. package/dist-esm/lib/ui/components/TopPanel/DefaultTopPanel.mjs +0 -12
  542. package/dist-esm/lib/ui/components/TopPanel/DefaultTopPanel.mjs.map +0 -7
  543. package/dist-esm/lib/utils/text/textDirection.mjs +0 -31
  544. package/dist-esm/lib/utils/text/textDirection.mjs.map +0 -7
  545. package/src/lib/shapes/shared/useForceSolid.ts +0 -6
  546. package/src/lib/tools/selection-logic/getShouldEnterCropModeOnPointerDown.ts +0 -10
  547. package/src/lib/ui/components/TopPanel/DefaultTopPanel.tsx +0 -10
  548. package/src/lib/utils/text/textDirection.ts +0 -32
@@ -16,20 +16,11 @@ exports[`When resizing a shape with children > Resizes a rotated draw shape > dr
16
16
  "isComplete": false,
17
17
  "isPen": false,
18
18
  "scale": 1,
19
+ "scaleX": 1.1,
20
+ "scaleY": 1.1,
19
21
  "segments": [
20
22
  {
21
- "points": [
22
- {
23
- "x": 0,
24
- "y": 0,
25
- "z": 0.5,
26
- },
27
- {
28
- "x": 110,
29
- "y": 110,
30
- "z": 0.5,
31
- },
32
- ],
23
+ "points": "AAAAAAA4QFZAVgA4",
33
24
  "type": "free",
34
25
  },
35
26
  ],
@@ -33,7 +33,7 @@ it('requires a move to begin drawing', () => {
33
33
  editor.pointerDown()
34
34
  editor.pointerMove(2, 0)
35
35
 
36
- expect(editor.inputs.isDragging).toBe(false)
36
+ expect(editor.inputs.getIsDragging()).toBe(false)
37
37
  })
38
38
 
39
39
  describe('Making an arrow on the page', () => {
@@ -16,7 +16,7 @@ exports[`Matches a snapshot > Basic SVG 1`] = `
16
16
  >
17
17
  <defs>
18
18
  <mask
19
- id="_export_4_rb_"
19
+ id="_export_4_r_b_"
20
20
  >
21
21
  <rect
22
22
  fill="white"
@@ -51,14 +51,14 @@ exports[`Matches a snapshot > Basic SVG 1`] = `
51
51
  </mask>
52
52
  <pattern
53
53
  height="8"
54
- id="_export_4_ra__hash_pattern_light_0"
54
+ id="_export_4_r_a__hash_pattern_light_0"
55
55
  patternUnits="userSpaceOnUse"
56
56
  width="8"
57
57
  >
58
58
  <rect
59
59
  fill="#fcfffe"
60
60
  height="8"
61
- mask="url(#_export_4_rb_)"
61
+ mask="url(#_export_4_r_b_)"
62
62
  width="8"
63
63
  x="0"
64
64
  y="0"
@@ -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"
@@ -127,7 +127,7 @@ exports[`Matches a snapshot > Basic SVG 1`] = `
127
127
  />
128
128
  <path
129
129
  d="M 0 0 L 100 0 L 100 100 L 0 100 Z"
130
- fill="url(#_export_4_ra__hash_pattern_light_0)"
130
+ fill="url(#_export_4_r_a__hash_pattern_light_0)"
131
131
  />
132
132
  <path
133
133
  d="M 0 0 L 100 0 L 100 100 L 0 100 Z"
@@ -156,7 +156,7 @@ exports[`Returns all shapes when no ids are provided > All shapes 1`] = `
156
156
  >
157
157
  <defs>
158
158
  <mask
159
- id="_export_2_r5_"
159
+ id="_export_2_r_5_"
160
160
  >
161
161
  <rect
162
162
  fill="white"
@@ -191,14 +191,14 @@ exports[`Returns all shapes when no ids are provided > All shapes 1`] = `
191
191
  </mask>
192
192
  <pattern
193
193
  height="8"
194
- id="_export_2_r4__hash_pattern_light_0"
194
+ id="_export_2_r_4__hash_pattern_light_0"
195
195
  patternUnits="userSpaceOnUse"
196
196
  width="8"
197
197
  >
198
198
  <rect
199
199
  fill="#fcfffe"
200
200
  height="8"
201
- mask="url(#_export_2_r5_)"
201
+ mask="url(#_export_2_r_5_)"
202
202
  width="8"
203
203
  x="0"
204
204
  y="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"
@@ -267,7 +267,7 @@ exports[`Returns all shapes when no ids are provided > All shapes 1`] = `
267
267
  />
268
268
  <path
269
269
  d="M 0 0 L 100 0 L 100 100 L 0 100 Z"
270
- fill="url(#_export_2_r4__hash_pattern_light_0)"
270
+ fill="url(#_export_2_r_4__hash_pattern_light_0)"
271
271
  />
272
272
  <path
273
273
  d="M 0 0 L 100 0 L 100 100 L 0 100 Z"
@@ -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
+ })
@@ -13,6 +13,8 @@ beforeEach(() => {
13
13
  })
14
14
  editor.updateViewportScreenBounds(new Box(0, 0, 1600, 900))
15
15
  editor.user.updateUserPreferences({ inputMode: null })
16
+ editor._transformPointerDownSpy.mockRestore()
17
+ editor._transformPointerUpSpy.mockRestore()
16
18
  })
17
19
 
18
20
  const wheelEvent = {
@@ -255,8 +257,8 @@ describe('CameraOptions.panSpeed', () => {
255
257
 
256
258
  it('Does not affect hand tool panning', () => {
257
259
  editor.setCameraOptions({ ...DEFAULT_CAMERA_OPTIONS, panSpeed: 2 })
258
- editor.setCurrentTool('hand').pointerDown(0, 0).pointerMove(5, 10).forceTick()
259
- expect(editor.getCamera()).toMatchObject({ x: 5, y: 10, z: 1 })
260
+ editor.setCurrentTool('hand').pointerDown(0, 0).pointerMove(50, 50)
261
+ expect(editor.getCamera()).toMatchObject({ x: 50, y: 50, z: 1 })
260
262
  })
261
263
 
262
264
  it('Does not affect spacebar panning (2x)', () => {
@@ -288,12 +290,12 @@ describe('CameraOptions.panSpeed', () => {
288
290
  .createShape({ id: shapeId, type: 'geo', x: 10, y: 10 })
289
291
  .select(shapeId)
290
292
  const shape = editor.getSelectedShapes()[0]
291
- editor.selectNone()
292
293
  // Move shape far beyond bounds to trigger edge scrolling at maximum speed
293
294
  expect(editor.getCamera()).toMatchObject({ x: 0, y: 0, z: 1 })
294
- editor.pointerDown(shape.x, shape.y).pointerMove(-5000, -5000)
295
- // At maximum speed and a zoom level of 1, the camera should move by 40px per tick if the screen
296
- // is wider than 1000 pixels, or by 40 * 0.612px if it is smaller.
295
+ // pointerMove calls forceTick() internally, so we don't need an extra forceTick() call
296
+ editor.pointerDown(shape.x, shape.y, shapeId).forceTick().pointerMove(-5000, -5000)
297
+ // At maximum speed and a zoom level of 1, the camera should move by 25px per tick if the screen
298
+ // is wider than 1000 pixels, or by 25 * 0.612px if it is smaller.
297
299
  const newX = viewportScreenBounds.w < 1000 ? 25 * 0.612 : 25
298
300
  const newY = viewportScreenBounds.h < 1000 ? 25 * 0.612 : 25
299
301
  expect(editor.getCamera()).toMatchObject({ x: newX, y: newY, z: 1 })
@@ -557,11 +559,11 @@ describe('When constraints are free', () => {
557
559
 
558
560
  it('zooms onto mouse position', () => {
559
561
  editor.pointerMove(100, 100)
560
- expect(editor.inputs.currentPagePoint).toMatchObject({ x: 100, y: 100 })
561
- editor.zoomIn(editor.inputs.currentScreenPoint, { immediate: true })
562
- expect(editor.inputs.currentPagePoint).toMatchObject({ x: 100, y: 100 })
563
- editor.zoomOut(editor.inputs.currentScreenPoint, { immediate: true })
564
- expect(editor.inputs.currentPagePoint).toMatchObject({ x: 100, y: 100 })
562
+ expect(editor.inputs.getCurrentPagePoint()).toMatchObject({ x: 100, y: 100 })
563
+ editor.zoomIn(editor.inputs.getCurrentScreenPoint(), { immediate: true })
564
+ expect(editor.inputs.getCurrentPagePoint()).toMatchObject({ x: 100, y: 100 })
565
+ editor.zoomOut(editor.inputs.getCurrentScreenPoint(), { immediate: true })
566
+ expect(editor.inputs.getCurrentPagePoint()).toMatchObject({ x: 100, y: 100 })
565
567
  })
566
568
  })
567
569
 
@@ -62,28 +62,54 @@ describe('distributeShapes command', () => {
62
62
 
63
63
  describe('when stacking horizontally', () => {
64
64
  it('stacks the shapes based on the editors adjacentShapeMargin', () => {
65
- editor.setSelectedShapes([ids.boxA, ids.boxB, ids.boxC, ids.boxD])
66
- // @ts-expect-error
67
- editor.options.adjacentShapeMargin = 1
68
- editor.stackShapes(editor.getSelectedShapeIds(), 'horizontal')
65
+ // Create a new editor with custom adjacentShapeMargin option
66
+ const customEditor = new TestEditor({ options: { adjacentShapeMargin: 1 } })
67
+ customEditor.createShapes([
68
+ {
69
+ id: ids.boxA,
70
+ type: 'geo',
71
+ x: 0,
72
+ y: 0,
73
+ },
74
+ {
75
+ id: ids.boxB,
76
+ type: 'geo',
77
+ x: 100,
78
+ y: 100,
79
+ },
80
+ {
81
+ id: ids.boxC,
82
+ type: 'geo',
83
+ x: 400,
84
+ y: 400,
85
+ },
86
+ {
87
+ id: ids.boxD,
88
+ type: 'geo',
89
+ x: 700,
90
+ y: 700,
91
+ },
92
+ ])
93
+ customEditor.setSelectedShapes([ids.boxA, ids.boxB, ids.boxC, ids.boxD])
94
+ customEditor.stackShapes(customEditor.getSelectedShapeIds(), 'horizontal')
69
95
  vi.advanceTimersByTime(1000)
70
96
  // 200 distance gap between c and d
71
- editor.expectShapeToMatch({
97
+ customEditor.expectShapeToMatch({
72
98
  id: ids.boxA,
73
99
  x: 0,
74
100
  y: 0,
75
101
  })
76
- editor.expectShapeToMatch({
102
+ customEditor.expectShapeToMatch({
77
103
  id: ids.boxB,
78
104
  x: 101,
79
105
  y: 100,
80
106
  })
81
- editor.expectShapeToMatch({
107
+ customEditor.expectShapeToMatch({
82
108
  id: ids.boxC,
83
109
  x: 202,
84
110
  y: 400,
85
111
  })
86
- editor.expectShapeToMatch({
112
+ customEditor.expectShapeToMatch({
87
113
  id: ids.boxD,
88
114
  x: 303,
89
115
  y: 700,
@@ -24,10 +24,16 @@ describe('When zooming to bounds', () => {
24
24
  h: 1000,
25
25
  })
26
26
 
27
+ const padding = editor.options.zoomToFitPadding
28
+
27
29
  editor.zoomToBounds(new Box(200, 300, 300, 300))
28
- expect(editor.getCamera().z).toCloselyMatchObject((1000 - 128) / 300)
29
- expect(editor.getViewportPageBounds().width).toCloselyMatchObject(1000 / ((1000 - 128) / 300))
30
- expect(editor.getViewportPageBounds().height).toCloselyMatchObject(1000 / ((1000 - 128) / 300))
30
+ expect(editor.getCamera().z).toCloselyMatchObject((1000 - padding) / 300)
31
+ expect(editor.getViewportPageBounds().width).toCloselyMatchObject(
32
+ 1000 / ((1000 - padding) / 300)
33
+ )
34
+ expect(editor.getViewportPageBounds().height).toCloselyMatchObject(
35
+ 1000 / ((1000 - padding) / 300)
36
+ )
31
37
  })
32
38
  })
33
39
 
@@ -55,3 +61,13 @@ it('is ignored by undo/redo', () => {
55
61
  editor.undo()
56
62
  expect(editor.getViewportPageCenter().toJson()).toCloselyMatchObject({ x: 350, y: 450 })
57
63
  })
64
+
65
+ it('respects custom zoomToFitPadding option', () => {
66
+ const customPadding = 64
67
+ editor = new TestEditor({ options: { zoomToFitPadding: customPadding } })
68
+ editor.setScreenBounds({ x: 0, y: 0, w: 1000, h: 1000 })
69
+ editor.setCamera({ x: 0, y: 0, z: 1 })
70
+
71
+ editor.zoomToBounds(new Box(200, 300, 300, 300))
72
+ expect(editor.getCamera().z).toCloselyMatchObject((1000 - customPadding) / 300)
73
+ })
@@ -13,17 +13,28 @@ beforeEach(() => {
13
13
  editor.createShapes(createDefaultShapes())
14
14
  })
15
15
 
16
- it('zooms to selection bounds', () => {
16
+ it('zooms to 100% when not already at 100%', () => {
17
+ editor.zoomIn() // zoom to something other than 100%
17
18
  editor.setSelectedShapes([ids.box1, ids.box2])
18
19
  editor.zoomToSelection()
19
- editor.expectCameraToBe(354.64, 139.29, 1)
20
+ expect(editor.getZoomLevel()).toBeCloseTo(1)
21
+ })
22
+
23
+ it('zooms to fit selection when already at 100%', () => {
24
+ // Editor starts at 100%, so first call zooms to fit
25
+ editor.setSelectedShapes([ids.box1, ids.box2])
26
+ editor.zoomToSelection()
27
+ // Should now be zoomed to fit the selection bounds (greater than 100% for small selection)
28
+ expect(editor.getZoomLevel()).toBeGreaterThan(1)
20
29
  })
21
30
 
22
31
  it('does not zoom past max', () => {
23
32
  editor.updateShapes([{ id: ids.box1, type: 'geo', props: { w: 1, h: 1 } }])
24
33
  editor.select(ids.box1)
25
34
  editor.zoomToSelection()
26
- expect(editor.getZoomLevel()).toBe(1) // double check again when we're zooming in hard
35
+ // When at 100%, zooms to fit but respects camera max zoom
36
+ const { zoomSteps } = editor.getCameraOptions()
37
+ expect(editor.getZoomLevel()).toBe(zoomSteps[zoomSteps.length - 1])
27
38
  })
28
39
 
29
40
  it('does not zoom past min', () => {
@@ -31,6 +31,9 @@ export type CircleClipShape = TLShape<typeof CIRCLE_CLIP_TYPE>
31
31
 
32
32
  export const isClippingEnabled$ = atom('isClippingEnabled', true)
33
33
 
34
+ // The stroke width used when rendering the circle
35
+ const STROKE_WIDTH = 2
36
+
34
37
  export class CircleClipShapeUtil extends BaseBoxShapeUtil<CircleClipShape> {
35
38
  static override type = CIRCLE_CLIP_TYPE
36
39
  static override props: RecordProps<CircleClipShape> = {
@@ -64,17 +67,20 @@ export class CircleClipShapeUtil extends BaseBoxShapeUtil<CircleClipShape> {
64
67
  }
65
68
 
66
69
  override getClipPath(shape: CircleClipShape): Vec[] | undefined {
67
- // Generate a polygon approximation of the circle
70
+ // Generate a polygon approximation of the circle.
71
+ // We inset the clip path by half the stroke width so that children are
72
+ // clipped to the inner edge of the stroke, not the center line.
68
73
  const centerX = shape.props.w / 2
69
74
  const centerY = shape.props.h / 2
70
- const radius = Math.min(shape.props.w, shape.props.h) / 2
75
+ const outerRadius = Math.min(shape.props.w, shape.props.h) / 2
76
+ const clipRadius = outerRadius - STROKE_WIDTH / 2
71
77
  const segments = 48 // More segments = smoother circle
72
78
 
73
79
  const points: Vec[] = []
74
80
  for (let i = 0; i < segments; i++) {
75
81
  const angle = (i / segments) * Math.PI * 2
76
- const x = centerX + Math.cos(angle) * radius
77
- const y = centerY + Math.sin(angle) * radius
82
+ const x = centerX + Math.cos(angle) * clipRadius
83
+ const y = centerY + Math.sin(angle) * clipRadius
78
84
  points.push(new Vec(x, y))
79
85
  }
80
86
 
@@ -112,7 +118,7 @@ export class CircleClipShapeTool extends StateNode {
112
118
 
113
119
  override onPointerDown(info: Parameters<TLEventHandlers['onPointerDown']>[0]) {
114
120
  if (info.target === 'canvas') {
115
- const { originPagePoint } = this.editor.inputs
121
+ const originPagePoint = this.editor.inputs.getOriginPagePoint()
116
122
 
117
123
  this.editor.createShape({
118
124
  type: CIRCLE_CLIP_TYPE,
@@ -233,13 +239,14 @@ describe('CircleClipShapeUtil', () => {
233
239
 
234
240
  // Should be a polygon approximation of a circle
235
241
  // Check that points are roughly in a circle pattern
236
- const centerX = 100 // shape.x
237
- const centerY = 100 // shape.y
238
- const radius = 100 // min(w, h) / 2
242
+ // The clip path is inset by half the stroke width (STROKE_WIDTH / 2 = 1)
243
+ const centerX = 100 // shape.props.w / 2
244
+ const centerY = 100 // shape.props.h / 2
245
+ const clipRadius = 99 // min(w, h) / 2 - STROKE_WIDTH / 2 = 100 - 1
239
246
 
240
247
  clipPath.forEach((point) => {
241
248
  const distance = Math.sqrt(Math.pow(point.x - centerX, 2) + Math.pow(point.y - centerY, 2))
242
- expect(distance).toBeCloseTo(radius, 0)
249
+ expect(distance).toBeCloseTo(clipRadius, 0)
243
250
  })
244
251
  })
245
252
  })