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
@@ -43,6 +43,8 @@ const sizeCache = createComputedCache(
43
43
  export interface TextShapeOptions {
44
44
  /** How much addition padding should be added to the horizontal geometry of the shape when binding to an arrow? */
45
45
  extraArrowHorizontalPadding: number
46
+ /** Whether to show the outline of the text shape (using the same color as the canvas). This helps with overlapping shapes. It does not show up on Safari, where text outline is a performance issues. */
47
+ showTextOutline: boolean
46
48
  }
47
49
 
48
50
  /** @public */
@@ -53,6 +55,7 @@ export class TextShapeUtil extends ShapeUtil<TLTextShape> {
53
55
 
54
56
  override options: TextShapeOptions = {
55
57
  extraArrowHorizontalPadding: 10,
58
+ showTextOutline: true,
56
59
  }
57
60
 
58
61
  getDefaultProps(): TLTextShape['props'] {
@@ -140,6 +143,7 @@ export class TextShapeUtil extends ShapeUtil<TLTextShape> {
140
143
  isSelected={isSelected}
141
144
  textWidth={width}
142
145
  textHeight={height}
146
+ showTextOutline={this.options.showTextOutline}
143
147
  style={{
144
148
  transform: `scale(${scale})`,
145
149
  transformOrigin: 'top left',
@@ -175,6 +179,7 @@ export class TextShapeUtil extends ShapeUtil<TLTextShape> {
175
179
  labelColor={getColorValue(theme, shape.props.color, 'solid')}
176
180
  bounds={exportBounds}
177
181
  padding={0}
182
+ showTextOutline={this.options.showTextOutline}
178
183
  />
179
184
  )
180
185
  }
@@ -124,7 +124,7 @@ export class Pointing extends StateNode {
124
124
  }
125
125
 
126
126
  private createTextShape(id: TLShapeId, point: Vec, autoSize: boolean, width: number) {
127
- this.editor.createShape<TLTextShape>({
127
+ this.editor.createShape({
128
128
  id,
129
129
  type: 'text',
130
130
  x: point.x,
@@ -95,7 +95,8 @@ export class VideoShapeUtil extends BaseBoxShapeUtil<TLVideoShape> {
95
95
 
96
96
  const VideoShape = memo(function VideoShape({ shape }: { shape: TLVideoShape }) {
97
97
  const editor = useEditor()
98
- const showControls = editor.getShapeGeometry(shape).bounds.w * editor.getZoomLevel() >= 110
98
+ const showControls =
99
+ editor.getShapeGeometry(shape).bounds.w * editor.getEfficientZoomLevel() >= 110
99
100
  const isEditing = useIsEditing(shape.id)
100
101
  const prefersReducedMotion = usePrefersReducedMotion()
101
102
  const { Spinner } = useEditorComponents()
@@ -1,7 +1,5 @@
1
1
  import {
2
2
  StateNode,
3
- TLFrameShape,
4
- TLGroupShape,
5
3
  TLPointerEventInfo,
6
4
  TLShapeId,
7
5
  isAccelKey,
@@ -37,8 +35,8 @@ export class Erasing extends StateNode {
37
35
  if (this.editor.isShapeOrAncestorLocked(shape)) return true
38
36
  //If the shape is a group or frame, check we're inside it when we start erasing
39
37
  if (
40
- this.editor.isShapeOfType<TLGroupShape>(shape, 'group') ||
41
- this.editor.isShapeOfType<TLFrameShape>(shape, 'frame')
38
+ this.editor.isShapeOfType(shape, 'group') ||
39
+ this.editor.isShapeOfType(shape, 'frame')
42
40
  ) {
43
41
  const pointInShapeShape = this.editor.getPointInShapeSpace(shape, originPagePoint)
44
42
  const geometry = this.editor.getShapeGeometry(shape)
@@ -111,7 +109,7 @@ export class Erasing extends StateNode {
111
109
  const minDist = this.editor.options.hitTestMargin / zoomLevel
112
110
 
113
111
  for (const shape of currentPageShapes) {
114
- if (editor.isShapeOfType<TLGroupShape>(shape, 'group')) continue
112
+ if (editor.isShapeOfType(shape, 'group')) continue
115
113
 
116
114
  // Avoid testing masked shapes, unless the pointer is inside the mask
117
115
  const pageMask = editor.getShapeMask(shape.id)
@@ -1,11 +1,4 @@
1
- import {
2
- isAccelKey,
3
- StateNode,
4
- TLFrameShape,
5
- TLGroupShape,
6
- TLPointerEventInfo,
7
- TLShapeId,
8
- } from '@tldraw/editor'
1
+ import { isAccelKey, StateNode, TLPointerEventInfo, TLShapeId } from '@tldraw/editor'
9
2
 
10
3
  export class Pointing extends StateNode {
11
4
  static override id = 'pointing'
@@ -27,10 +20,7 @@ export class Pointing extends StateNode {
27
20
 
28
21
  for (let n = currentPageShapesSorted.length, i = n - 1; i >= 0; i--) {
29
22
  const shape = currentPageShapesSorted[i]
30
- if (
31
- this.editor.isShapeOrAncestorLocked(shape) ||
32
- this.editor.isShapeOfType<TLGroupShape>(shape, 'group')
33
- ) {
23
+ if (this.editor.isShapeOrAncestorLocked(shape) || this.editor.isShapeOfType(shape, 'group')) {
34
24
  continue
35
25
  }
36
26
 
@@ -42,10 +32,7 @@ export class Pointing extends StateNode {
42
32
  ) {
43
33
  const hitShape = this.editor.getOutermostSelectableShape(shape)
44
34
  // If we've hit a frame after hitting any other shape, stop here
45
- if (
46
- this.editor.isShapeOfType<TLFrameShape>(hitShape, 'frame') &&
47
- erasing.size > initialSize
48
- ) {
35
+ if (this.editor.isShapeOfType(hitShape, 'frame') && erasing.size > initialSize) {
49
36
  break
50
37
  }
51
38
 
@@ -44,7 +44,7 @@ export class DragAndDropManager {
44
44
 
45
45
  for (const shape of shapesToActuallyMove) {
46
46
  const parent = editor.getShapeParent(shape)
47
- if (parent && editor.isShapeOfType<TLGroupShape>(parent, 'group')) {
47
+ if (parent && editor.isShapeOfType(parent, 'group')) {
48
48
  if (!movingGroups.has(parent)) {
49
49
  movingGroups.add(parent)
50
50
  }
@@ -70,9 +70,7 @@ export class DragAndDropManager {
70
70
  }
71
71
  this.initialIndices.set(shape.id, shape.index)
72
72
 
73
- const group = editor.findShapeAncestor(shape, (s) =>
74
- editor.isShapeOfType<TLGroupShape>(s, 'group')
75
- )
73
+ const group = editor.findShapeAncestor(shape, (s) => editor.isShapeOfType(s, 'group'))
76
74
  if (group) {
77
75
  this.initialGroupIds.set(shape.id, group.id)
78
76
  }
@@ -3,8 +3,6 @@ import {
3
3
  Mat,
4
4
  StateNode,
5
5
  TLCancelEventInfo,
6
- TLFrameShape,
7
- TLGroupShape,
8
6
  TLKeyboardEventInfo,
9
7
  TLPageId,
10
8
  TLPointerEventInfo,
@@ -57,9 +55,7 @@ export class Brushing extends StateNode {
57
55
  editor
58
56
  .getCurrentPageShapes()
59
57
  .filter(
60
- (shape) =>
61
- editor.isShapeOfType<TLGroupShape>(shape, 'group') ||
62
- editor.isShapeOrAncestorLocked(shape)
58
+ (shape) => editor.isShapeOfType(shape, 'group') || editor.isShapeOrAncestorLocked(shape)
63
59
  )
64
60
  .map((shape) => shape.id)
65
61
  )
@@ -177,7 +173,7 @@ export class Brushing extends StateNode {
177
173
 
178
174
  // If we're in wrap mode and the brush did not fully encloses the shape, it's a miss
179
175
  // We also skip frames unless we've completely selected the frame.
180
- if (isWrapping || editor.isShapeOfType<TLFrameShape>(shape, 'frame')) {
176
+ if (isWrapping || editor.isShapeOfType(shape, 'frame')) {
181
177
  continue testAllShapes
182
178
  }
183
179
 
@@ -2,7 +2,6 @@ import {
2
2
  ShapeWithCrop,
3
3
  StateNode,
4
4
  TLClickEventInfo,
5
- TLGroupShape,
6
5
  TLKeyboardEventInfo,
7
6
  TLPointerEventInfo,
8
7
  Vec,
@@ -43,7 +42,7 @@ export class Idle extends StateNode {
43
42
  switch (info.target) {
44
43
  case 'canvas': {
45
44
  const hitShape = getHitShapeOnCanvasPointerDown(this.editor)
46
- if (hitShape && !this.editor.isShapeOfType<TLGroupShape>(hitShape, 'group')) {
45
+ if (hitShape && !this.editor.isShapeOfType(hitShape, 'group')) {
47
46
  this.onPointerDown({
48
47
  ...info,
49
48
  shape: hitShape,
@@ -191,7 +190,7 @@ export class Idle extends StateNode {
191
190
  this.editor.markHistoryStoppingPoint('translate crop')
192
191
  }
193
192
 
194
- this.editor.updateShapes<ShapeWithCrop>([partial])
193
+ this.editor.updateShapes([partial])
195
194
  }
196
195
  }
197
196
  }
@@ -115,7 +115,7 @@ export class DraggingHandle extends StateNode {
115
115
  }
116
116
 
117
117
  // <!-- Only relevant to arrows
118
- if (this.editor.isShapeOfType<TLArrowShape>(shape, 'arrow')) {
118
+ if (this.editor.isShapeOfType(shape, 'arrow')) {
119
119
  const initialBinding = getArrowBindings(this.editor, shape)[info.handle.id as 'start' | 'end']
120
120
 
121
121
  this.isPrecise = false
@@ -228,7 +228,7 @@ export class DraggingHandle extends StateNode {
228
228
  }
229
229
  const endChanges = util.onHandleDragEnd?.(shape, handleDragInfo)
230
230
  if (endChanges) {
231
- this.editor.updateShapes([{ ...endChanges, id: shape.id, type: shape.type }])
231
+ this.editor.updateShapes([{ ...endChanges, id: shape.id }])
232
232
  }
233
233
  }
234
234
 
@@ -295,7 +295,7 @@ export class DraggingHandle extends StateNode {
295
295
  if (!shape) return
296
296
  const util = editor.getShapeUtil(shape)
297
297
 
298
- const initialBinding = editor.isShapeOfType<TLArrowShape>(shape, 'arrow')
298
+ const initialBinding = editor.isShapeOfType(shape, 'arrow')
299
299
  ? getArrowBindings(editor, shape)[initialHandle.id as 'start' | 'end']
300
300
  : undefined
301
301
 
@@ -352,10 +352,7 @@ export class DraggingHandle extends StateNode {
352
352
  const next: TLShapePartial<any> = { id: shape.id, type: shape.type, ...changes }
353
353
 
354
354
  // Arrows
355
- if (
356
- initialHandle.type === 'vertex' &&
357
- this.editor.isShapeOfType<TLArrowShape>(shape, 'arrow')
358
- ) {
355
+ if (initialHandle.type === 'vertex' && this.editor.isShapeOfType(shape, 'arrow')) {
359
356
  const bindingAfter = getArrowBindings(editor, shape)[initialHandle.id as 'start' | 'end']
360
357
 
361
358
  if (bindingAfter) {
@@ -3,10 +3,8 @@ import {
3
3
  TLCancelEventInfo,
4
4
  TLCompleteEventInfo,
5
5
  tlenv,
6
- TLFrameShape,
7
6
  TLPointerEventInfo,
8
7
  TLShape,
9
- TLTextShape,
10
8
  } from '@tldraw/editor'
11
9
  import { getTextLabels } from '../../../utils/shapes/shapes'
12
10
  import { renderPlaintextFromRichText } from '../../../utils/text/richText'
@@ -20,13 +18,25 @@ interface EditingShapeInfo {
20
18
  export class EditingShape extends StateNode {
21
19
  static override id = 'editing_shape'
22
20
 
23
- hitShapeForPointerUp: TLShape | null = null
21
+ hitLabelOnShapeForPointerUp: TLShape | null = null
24
22
  private info = {} as EditingShapeInfo
23
+ private didPointerDownOnEditingShape = false
24
+
25
+ private isTextInputFocused(): boolean {
26
+ const container = this.editor.getContainer()
27
+ return (
28
+ container.contains(document.activeElement) &&
29
+ (document.activeElement?.nodeName === 'INPUT' ||
30
+ document.activeElement?.nodeName === 'TEXTAREA' ||
31
+ (document.activeElement as HTMLElement)?.isContentEditable)
32
+ )
33
+ }
25
34
 
26
35
  override onEnter(info: EditingShapeInfo) {
27
36
  const editingShape = this.editor.getEditingShape()
28
37
  if (!editingShape) throw Error('Entered editing state without an editing shape')
29
- this.hitShapeForPointerUp = null
38
+ this.hitLabelOnShapeForPointerUp = null
39
+ this.didPointerDownOnEditingShape = false
30
40
 
31
41
  this.info = info
32
42
 
@@ -56,15 +66,34 @@ export class EditingShape extends StateNode {
56
66
  override onPointerMove(info: TLPointerEventInfo) {
57
67
  // In the case where on pointer down we hit a shape's label, we need to check if the user is dragging.
58
68
  // and if they are, we need to transition to translating instead.
59
- if (this.hitShapeForPointerUp && this.editor.inputs.isDragging) {
69
+ if (this.hitLabelOnShapeForPointerUp && this.editor.inputs.isDragging) {
60
70
  if (this.editor.getIsReadonly()) return
61
- if (this.hitShapeForPointerUp.isLocked) return
62
- this.editor.select(this.hitShapeForPointerUp)
71
+ if (this.hitLabelOnShapeForPointerUp.isLocked) return
72
+
73
+ this.editor.select(this.hitLabelOnShapeForPointerUp)
63
74
  this.parent.transition('translating', info)
64
- this.hitShapeForPointerUp = null
75
+ this.hitLabelOnShapeForPointerUp = null
65
76
  return
66
77
  }
67
78
 
79
+ // Check if dragging from editing shape with blurred input
80
+ if (this.didPointerDownOnEditingShape && this.editor.inputs.isDragging) {
81
+ if (this.editor.getIsReadonly()) return
82
+
83
+ const editingShape = this.editor.getEditingShape()
84
+ if (!editingShape || editingShape.isLocked) return
85
+
86
+ if (!this.isTextInputFocused()) {
87
+ // Input blurred during drag - exit edit mode and start translating
88
+ this.editor.select(editingShape)
89
+ this.parent.transition('translating', info)
90
+ this.didPointerDownOnEditingShape = false
91
+ return
92
+ }
93
+ // Input still focused - user is selecting text, stay in edit mode
94
+ this.didPointerDownOnEditingShape = false
95
+ }
96
+
68
97
  switch (info.target) {
69
98
  case 'shape':
70
99
  case 'canvas': {
@@ -75,7 +104,8 @@ export class EditingShape extends StateNode {
75
104
  }
76
105
 
77
106
  override onPointerDown(info: TLPointerEventInfo) {
78
- this.hitShapeForPointerUp = null
107
+ this.hitLabelOnShapeForPointerUp = null
108
+ this.didPointerDownOnEditingShape = false
79
109
 
80
110
  switch (info.target) {
81
111
  // N.B. This bit of logic has a bit of history to it.
@@ -109,7 +139,7 @@ export class EditingShape extends StateNode {
109
139
  const textLabel = textLabels.length === 1 ? textLabels[0] : undefined
110
140
  // N.B. One nuance here is that we want empty text fields to be removed from the canvas when the user clicks away from them.
111
141
  const isEmptyTextShape =
112
- this.editor.isShapeOfType<TLTextShape>(editingShape, 'text') &&
142
+ this.editor.isShapeOfType(editingShape, 'text') &&
113
143
  renderPlaintextFromRichText(this.editor, editingShape.props.richText).trim() === ''
114
144
  if (textLabel && !isEmptyTextShape) {
115
145
  const pointInShapeSpace = this.editor.getPointInShapeSpace(
@@ -122,10 +152,11 @@ export class EditingShape extends StateNode {
122
152
  ) {
123
153
  // it's a hit to the label!
124
154
  if (selectingShape.id === editingShape.id) {
125
- // If we clicked on the editing geo / arrow shape's label, do nothing
155
+ // Track click on editing shape for drag detection
156
+ this.didPointerDownOnEditingShape = true
126
157
  return
127
158
  } else {
128
- this.hitShapeForPointerUp = selectingShape
159
+ this.hitLabelOnShapeForPointerUp = selectingShape
129
160
 
130
161
  this.editor.markHistoryStoppingPoint('editing on pointer up')
131
162
  this.editor.select(selectingShape.id)
@@ -135,7 +166,7 @@ export class EditingShape extends StateNode {
135
166
  } else {
136
167
  if (selectingShape.id === editingShape.id) {
137
168
  // If we clicked on a frame, while editing its heading, cancel editing
138
- if (this.editor.isShapeOfType<TLFrameShape>(selectingShape, 'frame')) {
169
+ if (this.editor.isShapeOfType(selectingShape, 'frame')) {
139
170
  this.editor.setEditingShape(null)
140
171
  this.parent.transition('idle', info)
141
172
  }
@@ -159,9 +190,9 @@ export class EditingShape extends StateNode {
159
190
 
160
191
  override onPointerUp(info: TLPointerEventInfo) {
161
192
  // If we're not dragging, and it's a hit to the label, begin editing the shape.
162
- const hitShape = this.hitShapeForPointerUp
193
+ const hitShape = this.hitLabelOnShapeForPointerUp
163
194
  if (!hitShape) return
164
- this.hitShapeForPointerUp = null
195
+ this.hitLabelOnShapeForPointerUp = null
165
196
 
166
197
  // Stay in edit mode to maintain flow of editing.
167
198
  const util = this.editor.getShapeUtil(hitShape)
@@ -3,11 +3,9 @@ import {
3
3
  StateNode,
4
4
  TLAdjacentDirection,
5
5
  TLClickEventInfo,
6
- TLGroupShape,
7
6
  TLKeyboardEventInfo,
8
7
  TLPointerEventInfo,
9
8
  TLShape,
10
- TLTextShape,
11
9
  Vec,
12
10
  VecLike,
13
11
  createShapeId,
@@ -190,7 +188,7 @@ export class Idle extends StateNode {
190
188
  // unexpected results when working "inside of" a hollow shape.
191
189
 
192
190
  const hitShape =
193
- hoveredShape && !this.editor.isShapeOfType<TLGroupShape>(hoveredShape, 'group')
191
+ hoveredShape && !this.editor.isShapeOfType(hoveredShape, 'group')
194
192
  ? hoveredShape
195
193
  : (this.editor.getSelectedShapeAtPoint(this.editor.inputs.currentPagePoint) ??
196
194
  this.editor.getShapeAtPoint(this.editor.inputs.currentPagePoint, {
@@ -201,13 +199,13 @@ export class Idle extends StateNode {
201
199
  const focusedGroupId = this.editor.getFocusedGroupId()
202
200
 
203
201
  if (hitShape) {
204
- if (this.editor.isShapeOfType<TLGroupShape>(hitShape, 'group')) {
202
+ if (this.editor.isShapeOfType(hitShape, 'group')) {
205
203
  // Probably select the shape
206
204
  selectOnCanvasPointerUp(this.editor, info)
207
205
  return
208
206
  } else {
209
207
  const parent = this.editor.getShape(hitShape.parentId)
210
- if (parent && this.editor.isShapeOfType<TLGroupShape>(parent, 'group')) {
208
+ if (parent && this.editor.isShapeOfType(parent, 'group')) {
211
209
  // The shape is the direct child of a group. If the group is
212
210
  // selected, then we can select the shape. If the group is the
213
211
  // focus layer id, then we can double click into it as usual.
@@ -356,7 +354,7 @@ export class Idle extends StateNode {
356
354
  case 'canvas': {
357
355
  const hoveredShape = this.editor.getHoveredShape()
358
356
  const hitShape =
359
- hoveredShape && !this.editor.isShapeOfType<TLGroupShape>(hoveredShape, 'group')
357
+ hoveredShape && !this.editor.isShapeOfType(hoveredShape, 'group')
360
358
  ? hoveredShape
361
359
  : this.editor.getShapeAtPoint(this.editor.inputs.currentPagePoint, {
362
360
  margin: this.editor.options.hitTestMargin / this.editor.getZoomLevel(),
@@ -525,9 +523,7 @@ export class Idle extends StateNode {
525
523
  const selectedShapes = this.editor.getSelectedShapes()
526
524
 
527
525
  // On enter, if every selected shape is a group, then select all of the children of the groups
528
- if (
529
- selectedShapes.every((shape) => this.editor.isShapeOfType<TLGroupShape>(shape, 'group'))
530
- ) {
526
+ if (selectedShapes.every((shape) => this.editor.isShapeOfType(shape, 'group'))) {
531
527
  this.editor.setSelectedShapes(
532
528
  selectedShapes.flatMap((shape) => this.editor.getSortedChildIdsForParent(shape.id))
533
529
  )
@@ -604,7 +600,7 @@ export class Idle extends StateNode {
604
600
  const { x, y } = this.editor.inputs.currentPagePoint
605
601
 
606
602
  // Allow this to trigger the max shapes reached alert
607
- this.editor.createShapes<TLTextShape>([
603
+ this.editor.createShapes([
608
604
  {
609
605
  id,
610
606
  type: 'text',
@@ -124,7 +124,7 @@ export class PointingArrowLabel extends StateNode {
124
124
  }
125
125
 
126
126
  this.didDrag = true
127
- this.editor.updateShape<TLArrowShape>({
127
+ this.editor.updateShape({
128
128
  id: shape.id,
129
129
  type: shape.type,
130
130
  props: { labelPosition: nextLabelPosition },
@@ -1,12 +1,4 @@
1
- import {
2
- Editor,
3
- StateNode,
4
- TLArrowShape,
5
- TLHandle,
6
- TLNoteShape,
7
- TLPointerEventInfo,
8
- Vec,
9
- } from '@tldraw/editor'
1
+ import { Editor, StateNode, TLHandle, TLNoteShape, TLPointerEventInfo, Vec } from '@tldraw/editor'
10
2
  import { updateArrowTargetState } from '../../../shapes/arrow/arrowTargetState'
11
3
  import { getArrowBindings } from '../../../shapes/arrow/shared'
12
4
  import {
@@ -29,7 +21,7 @@ export class PointingHandle extends StateNode {
29
21
  this.didCtrlOnEnter = info.accelKey
30
22
 
31
23
  const { shape } = info
32
- if (this.editor.isShapeOfType<TLArrowShape>(shape, 'arrow')) {
24
+ if (this.editor.isShapeOfType(shape, 'arrow')) {
33
25
  const initialBindings = getArrowBindings(this.editor, shape)
34
26
  const currentBinding = initialBindings[info.handle.id as 'start' | 'end']
35
27
  const oppositeBinding = initialBindings[info.handle.id === 'start' ? 'end' : 'start']
@@ -58,7 +50,7 @@ export class PointingHandle extends StateNode {
58
50
  override onPointerUp() {
59
51
  const { shape, handle } = this.info
60
52
 
61
- if (this.editor.isShapeOfType<TLNoteShape>(shape, 'note')) {
53
+ if (this.editor.isShapeOfType(shape, 'note')) {
62
54
  const { editor } = this
63
55
  const nextNote = getNoteForAdjacentPosition(editor, shape, handle, false)
64
56
  if (nextNote) {
@@ -90,7 +82,7 @@ export class PointingHandle extends StateNode {
90
82
  if (editor.getIsReadonly()) return
91
83
  const { shape, handle } = this.info
92
84
 
93
- if (editor.isShapeOfType<TLNoteShape>(shape, 'note')) {
85
+ if (editor.isShapeOfType(shape, 'note')) {
94
86
  const nextNote = getNoteForAdjacentPosition(editor, shape, handle, true)
95
87
  if (nextNote) {
96
88
  // Center the shape on the current pointer
@@ -1,4 +1,4 @@
1
- import { StateNode, TLClickEventInfo, TLGroupShape, TLPointerEventInfo } from '@tldraw/editor'
1
+ import { StateNode, TLClickEventInfo, TLPointerEventInfo } from '@tldraw/editor'
2
2
  import { selectOnCanvasPointerUp } from '../../selection-logic/selectOnCanvasPointerUp'
3
3
 
4
4
  export class PointingSelection extends StateNode {
@@ -35,7 +35,7 @@ export class PointingSelection extends StateNode {
35
35
  override onDoubleClick?(info: TLClickEventInfo) {
36
36
  const hoveredShape = this.editor.getHoveredShape()
37
37
  const hitShape =
38
- hoveredShape && !this.editor.isShapeOfType<TLGroupShape>(hoveredShape, 'group')
38
+ hoveredShape && !this.editor.isShapeOfType(hoveredShape, 'group')
39
39
  ? hoveredShape
40
40
  : this.editor.getShapeAtPoint(this.editor.inputs.currentPagePoint, {
41
41
  hitInside: true,
@@ -7,12 +7,10 @@ import {
7
7
  SelectionCorner,
8
8
  SelectionEdge,
9
9
  StateNode,
10
- TLFrameShape,
11
10
  TLPointerEventInfo,
12
11
  TLShape,
13
12
  TLShapeId,
14
13
  TLShapePartial,
15
- TLTextShape,
16
14
  TLTickEventInfo,
17
15
  Vec,
18
16
  VecLike,
@@ -228,7 +226,7 @@ export class Resizing extends StateNode {
228
226
 
229
227
  if (shapeSnapshots.size === 1) {
230
228
  const onlySnapshot = [...shapeSnapshots.values()][0]!
231
- if (this.editor.isShapeOfType<TLTextShape>(onlySnapshot.shape, 'text')) {
229
+ if (this.editor.isShapeOfType(onlySnapshot.shape, 'text')) {
232
230
  isAspectRatioLocked = !(this.info.handle === 'left' || this.info.handle === 'right')
233
231
  }
234
232
  }
@@ -528,7 +526,7 @@ export class Resizing extends StateNode {
528
526
  // descendants (easy) but also flagging with behavior like "resize" or "keep absolute position" or "reposition only with accel key",
529
527
  // though I'm not sure where that would be defined; perhaps better handled with onResizeStart / onResize callbacks on the util, and
530
528
  // pass `accelKeyIsPressed` as well as `accelKeyWasPressed`?
531
- if (editor.isShapeOfType<TLFrameShape>(shape, 'frame')) {
529
+ if (editor.isShapeOfType(shape, 'frame')) {
532
530
  frames.push({
533
531
  id: shape.id,
534
532
  children: compact(
@@ -1,8 +1,6 @@
1
1
  import {
2
2
  Geometry2d,
3
3
  StateNode,
4
- TLFrameShape,
5
- TLGroupShape,
6
4
  TLShape,
7
5
  TLShapeId,
8
6
  Vec,
@@ -104,7 +102,7 @@ export class ScribbleBrushing extends StateNode {
104
102
 
105
103
  // If the shape is a group or is already selected or locked, don't select it
106
104
  if (
107
- editor.isShapeOfType<TLGroupShape>(shape, 'group') ||
105
+ editor.isShapeOfType(shape, 'group') ||
108
106
  newlySelectedShapeIds.has(shape.id) ||
109
107
  editor.isShapeOrAncestorLocked(shape)
110
108
  ) {
@@ -115,7 +113,7 @@ export class ScribbleBrushing extends StateNode {
115
113
 
116
114
  // If the scribble started inside of the frame, don't select it
117
115
  if (
118
- editor.isShapeOfType<TLFrameShape>(shape, 'frame') &&
116
+ editor.isShapeOfType(shape, 'frame') &&
119
117
  geometry.bounds.containsPoint(editor.getPointInShapeSpace(shape, originPagePoint))
120
118
  ) {
121
119
  continue
@@ -407,9 +407,7 @@ function getTranslatingSnapshot(editor: Editor) {
407
407
  const { originPagePoint } = editor.inputs
408
408
 
409
409
  const allHoveredNotes = shapeSnapshots.filter(
410
- (s) =>
411
- editor.isShapeOfType<TLNoteShape>(s.shape, 'note') &&
412
- editor.isPointInShape(s.shape, originPagePoint)
410
+ (s) => editor.isShapeOfType(s.shape, 'note') && editor.isPointInShape(s.shape, originPagePoint)
413
411
  ) as (MovingShapeSnapshot & { shape: TLNoteShape })[]
414
412
 
415
413
  if (allHoveredNotes.length === 0) {
@@ -1,4 +1,3 @@
1
- import { useEditor, useValue } from '@tldraw/editor'
2
1
  import { PORTRAIT_BREAKPOINT } from '../../constants'
3
2
  import { useBreakpoint } from '../../context/breakpoints'
4
3
  import {
@@ -9,6 +8,7 @@ import {
9
8
  useThreeStackableItems,
10
9
  useUnlockedSelectedShapesCount,
11
10
  } from '../../hooks/menu-hooks'
11
+ import { ZoomTo100MenuItem } from '../menu-items'
12
12
  import { TldrawUiMenuActionItem } from '../primitives/menus/TldrawUiMenuActionItem'
13
13
 
14
14
  /** @public @react */
@@ -99,14 +99,6 @@ export function ZoomOrRotateMenuItem() {
99
99
  }
100
100
  /** @public @react */
101
101
 
102
- export function ZoomTo100MenuItem() {
103
- const editor = useEditor()
104
- const isZoomedTo100 = useValue('zoom is 1', () => editor.getZoomLevel() === 1, [editor])
105
-
106
- return <TldrawUiMenuActionItem actionId="zoom-to-100" disabled={isZoomedTo100} />
107
- }
108
- /** @public @react */
109
-
110
102
  export function RotateCCWMenuItem() {
111
103
  const oneSelected = useUnlockedSelectedShapesCount(1)
112
104
  const isInSelectState = useIsInSelectState()
@@ -1,4 +1,4 @@
1
- import { T, TLBaseShape, track, useEditor } from '@tldraw/editor'
1
+ import { ExtractShapeByProps, T, TLShape, track, useEditor } from '@tldraw/editor'
2
2
  import { useCallback, useEffect, useRef, useState } from 'react'
3
3
  import { TLUiDialogProps } from '../context/dialogs'
4
4
  import { useTranslation } from '../hooks/useTranslation/useTranslation'
@@ -25,20 +25,28 @@ function validateUrl(url: string) {
25
25
  return { isValid: false, hasProtocol: false }
26
26
  }
27
27
 
28
- type ShapeWithUrl = TLBaseShape<string, { url: string }>
28
+ type ShapeWithUrl = ExtractShapeByProps<{ url: string }>
29
+
30
+ function isShapeWithUrl(shape: TLShape | null | undefined): shape is ShapeWithUrl {
31
+ return !!(shape && 'url' in shape.props && typeof shape.props.url === 'string')
32
+ }
33
+
34
+ function assertShapeWithUrl(shape: TLShape | null | undefined): asserts shape is ShapeWithUrl {
35
+ if (!isShapeWithUrl(shape)) {
36
+ throw new Error('Shape is not a valid ShapeWithUrl')
37
+ }
38
+ }
29
39
 
30
40
  export const EditLinkDialog = track(function EditLinkDialog({ onClose }: TLUiDialogProps) {
31
41
  const editor = useEditor()
32
42
 
33
43
  const selectedShape = editor.getOnlySelectedShape()
34
44
 
35
- if (
36
- !(selectedShape && 'url' in selectedShape.props && typeof selectedShape.props.url === 'string')
37
- ) {
45
+ if (!isShapeWithUrl(selectedShape)) {
38
46
  return null
39
47
  }
40
48
 
41
- return <EditLinkDialogInner onClose={onClose} selectedShape={selectedShape as ShapeWithUrl} />
49
+ return <EditLinkDialogInner onClose={onClose} selectedShape={selectedShape} />
42
50
  })
43
51
 
44
52
  export const EditLinkDialogInner = track(function EditLinkDialogInner({
@@ -98,6 +106,7 @@ export const EditLinkDialogInner = track(function EditLinkDialogInner({
98
106
  const handleClear = useCallback(() => {
99
107
  const onlySelectedShape = editor.getOnlySelectedShape()
100
108
  if (!onlySelectedShape) return
109
+ assertShapeWithUrl(onlySelectedShape)
101
110
  editor.updateShapes([
102
111
  { id: onlySelectedShape.id, type: onlySelectedShape.type, props: { url: '' } },
103
112
  ])
@@ -108,6 +117,7 @@ export const EditLinkDialogInner = track(function EditLinkDialogInner({
108
117
  const onlySelectedShape = editor.getOnlySelectedShape()
109
118
 
110
119
  if (!onlySelectedShape) return
120
+ assertShapeWithUrl(onlySelectedShape)
111
121
 
112
122
  // ? URL is a magic value
113
123
  if (onlySelectedShape && 'url' in onlySelectedShape.props) {
@@ -1,4 +1,4 @@
1
- import { preventDefault, TLShape, TLShapeId, useEditor } from '@tldraw/editor'
1
+ import { ExtractShapeByProps, preventDefault, TLShape, TLShapeId, useEditor } from '@tldraw/editor'
2
2
  import { useCallback, useEffect, useRef, useState } from 'react'
3
3
  import { useUiEvents } from '../../context/events'
4
4
  import { useTranslation } from '../../hooks/useTranslation/useTranslation'
@@ -31,7 +31,7 @@ export function AltTextEditor({ shapeId, onClose, source }: AltTextEditorProps)
31
31
 
32
32
  const handleComplete = () => {
33
33
  trackEvent('set-alt-text', { source })
34
- const shape = editor.getShape<TLShape & { props: { altText: string } }>(shapeId)
34
+ const shape = editor.getShape<ExtractShapeByProps<{ altText: string }>>(shapeId)
35
35
  if (!shape) return
36
36
  editor.updateShapes([
37
37
  {