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
@@ -59,7 +59,7 @@ export function updateBookmarkAssetOnUrlChange(editor: Editor, shape: TLBookmark
59
59
  if (editor.getAsset(assetId)) {
60
60
  // Existing asset for this URL?
61
61
  if (shape.props.assetId !== assetId) {
62
- editor.updateShapes<TLBookmarkShape>([
62
+ editor.updateShapes([
63
63
  {
64
64
  id: shape.id,
65
65
  type: shape.type,
@@ -71,7 +71,7 @@ export function updateBookmarkAssetOnUrlChange(editor: Editor, shape: TLBookmark
71
71
  // No asset for this URL?
72
72
 
73
73
  // First, clear out the existing asset reference
74
- editor.updateShapes<TLBookmarkShape>([
74
+ editor.updateShapes([
75
75
  {
76
76
  id: shape.id,
77
77
  type: shape.type,
@@ -103,7 +103,7 @@ const createBookmarkAssetOnUrlChange = debounce(async (editor: Editor, shape: TL
103
103
  editor.createAssets([asset])
104
104
 
105
105
  // And update the shape
106
- editor.updateShapes<TLBookmarkShape>([
106
+ editor.updateShapes([
107
107
  {
108
108
  id: shape.id,
109
109
  type: shape.type,
@@ -139,7 +139,7 @@ export class DrawShapeUtil extends ShapeUtil<TLDrawShape> {
139
139
  const forceSolid = useValue(
140
140
  'force solid',
141
141
  () => {
142
- const zoomLevel = this.editor.getZoomLevel()
142
+ const zoomLevel = this.editor.getEfficientZoomLevel()
143
143
  return zoomLevel < 0.5 && zoomLevel < 1.5 / sw
144
144
  },
145
145
  [this.editor, sw]
@@ -244,7 +244,7 @@ function DrawShapeSvg({ shape, zoomOverride }: { shape: TLDrawShape; zoomOverrid
244
244
  const forceSolid = useValue(
245
245
  'force solid',
246
246
  () => {
247
- const zoomLevel = zoomOverride ?? editor.getZoomLevel()
247
+ const zoomLevel = zoomOverride ?? editor.getEfficientZoomLevel()
248
248
  return zoomLevel < 0.5 && zoomLevel < 1.5 / sw
249
249
  },
250
250
  [editor, sw, zoomOverride]
@@ -253,7 +253,7 @@ function DrawShapeSvg({ shape, zoomOverride }: { shape: TLDrawShape; zoomOverrid
253
253
  const dotAdjustment = useValue(
254
254
  'dot adjustment',
255
255
  () => {
256
- const zoomLevel = zoomOverride ?? editor.getZoomLevel()
256
+ const zoomLevel = zoomOverride ?? editor.getEfficientZoomLevel()
257
257
  // If we're zoomed way out (10%), then we need to make the dotted line go to 9 instead 0.1
258
258
  // Chrome doesn't render anything otherwise.
259
259
  return zoomLevel < 0.2 ? 0 : 0.1
@@ -238,7 +238,7 @@ export class Drawing extends StateNode {
238
238
  )
239
239
  }
240
240
 
241
- this.editor.updateShapes<TLDrawShape | TLHighlightShape>([shapePartial])
241
+ this.editor.updateShapes([shapePartial])
242
242
 
243
243
  return
244
244
  }
@@ -250,7 +250,7 @@ export class Drawing extends StateNode {
250
250
  const id = createShapeId()
251
251
 
252
252
  // Allow this to trigger the max shapes reached alert
253
- this.editor.createShape<DrawableShape>({
253
+ this.editor.createShape({
254
254
  id,
255
255
  type: this.shapeType,
256
256
  x: originPagePoint.x,
@@ -372,7 +372,7 @@ export class Drawing extends StateNode {
372
372
  )
373
373
  }
374
374
 
375
- this.editor.updateShapes<TLDrawShape | TLHighlightShape>([shapePartial])
375
+ this.editor.updateShapes([shapePartial])
376
376
  }
377
377
  break
378
378
  }
@@ -642,7 +642,7 @@ export class Drawing extends StateNode {
642
642
  const props = this.editor.getShape<DrawableShape>(id)!.props
643
643
 
644
644
  if (!this.editor.canCreateShapes([newShapeId])) return this.cancel()
645
- this.editor.createShape<DrawableShape>({
645
+ this.editor.createShape({
646
646
  id: newShapeId,
647
647
  type: this.shapeType,
648
648
  x: toFixed(inputs.currentPagePoint.x),
@@ -137,7 +137,7 @@ export class EmbedShapeUtil extends BaseBoxShapeUtil<TLEmbedShape> {
137
137
 
138
138
  if (editingShapeId && hoveredShapeId !== editingShapeId) {
139
139
  const editingShape = this.editor.getShape(editingShapeId)
140
- if (editingShape && this.editor.isShapeOfType<TLEmbedShape>(editingShape, 'embed')) {
140
+ if (editingShape && this.editor.isShapeOfType(editingShape, 'embed')) {
141
141
  return true
142
142
  }
143
143
  }
@@ -4,7 +4,7 @@ import { BaseBoxShapeTool, TLShape, TLShapeId } from '@tldraw/editor'
4
4
  export class FrameShapeTool extends BaseBoxShapeTool {
5
5
  static override id = 'frame'
6
6
  static override initial = 'idle'
7
- override shapeType = 'frame'
7
+ override shapeType = 'frame' as const
8
8
 
9
9
  override onCreate(shape: TLShape | null): void {
10
10
  if (!shape) return
@@ -115,7 +115,7 @@ export class FrameShapeUtil extends BaseBoxShapeUtil<TLFrameShape> {
115
115
  override getGeometry(shape: TLFrameShape): Geometry2d {
116
116
  const { editor } = this
117
117
 
118
- const z = editor.getZoomLevel()
118
+ const z = editor.getEfficientZoomLevel()
119
119
 
120
120
  // Which dimension measures the top edge after rotation?
121
121
  const labelSide = getFrameHeadingSide(editor, shape)
@@ -27,8 +27,16 @@ describe('Handle snapping', () => {
27
27
  )
28
28
  })
29
29
 
30
- const geoShape = () => editor.getShape(ids.geo)!
31
- const lineShape = () => editor.getShape(ids.line)!
30
+ const geoShape = () => {
31
+ const shape = editor.getShape(ids.geo)!
32
+ assert(editor.isShapeOfType(shape, 'geo'))
33
+ return shape
34
+ }
35
+ const lineShape = () => {
36
+ const shape = editor.getShape(ids.line)!
37
+ assert(editor.isShapeOfType(shape, 'line'))
38
+ return shape
39
+ }
32
40
  const lineHandles = () => editor.getShapeUtil('line').getHandles!(lineShape())!
33
41
 
34
42
  function startDraggingHandle() {
@@ -43,6 +43,7 @@ import {
43
43
  import { getFillDefForCanvas, getFillDefForExport } from '../shared/defaultStyleDefs'
44
44
  import { useDefaultColorTheme } from '../shared/useDefaultColorTheme'
45
45
  import { useIsReadyForEditing } from '../shared/useEditablePlainText'
46
+ import { useEfficientZoomThreshold } from '../shared/useEfficientZoomThreshold'
46
47
  import { GeoShapeBody } from './components/GeoShapeBody'
47
48
  import { getGeoShapePath } from './getGeoShapePath'
48
49
 
@@ -54,6 +55,10 @@ export class GeoShapeUtil extends BaseBoxShapeUtil<TLGeoShape> {
54
55
  static override props = geoShapeProps
55
56
  static override migrations = geoShapeMigrations
56
57
 
58
+ override options = {
59
+ showTextOutline: true,
60
+ }
61
+
57
62
  override canEdit() {
58
63
  return true
59
64
  }
@@ -195,7 +200,7 @@ export class GeoShapeUtil extends BaseBoxShapeUtil<TLGeoShape> {
195
200
  const isReadyForEditing = useIsReadyForEditing(editor, shape.id)
196
201
  const isEmpty = isEmptyRichText(shape.props.richText)
197
202
  const showHtmlContainer = isReadyForEditing || !isEmpty
198
- const isForceSolid = useValue('force solid', () => editor.getZoomLevel() < 0.2, [editor])
203
+ const isForceSolid = useEfficientZoomThreshold(shape.props.scale * 0.25)
199
204
 
200
205
  return (
201
206
  <>
@@ -224,6 +229,7 @@ export class GeoShapeUtil extends BaseBoxShapeUtil<TLGeoShape> {
224
229
  isSelected={isOnlySelected}
225
230
  labelColor={getColorValue(theme, props.labelColor, 'solid')}
226
231
  wrap
232
+ showTextOutline={this.options.showTextOutline}
227
233
  />
228
234
  </HTMLContainer>
229
235
  )}
@@ -233,9 +239,7 @@ export class GeoShapeUtil extends BaseBoxShapeUtil<TLGeoShape> {
233
239
  }
234
240
 
235
241
  indicator(shape: TLGeoShape) {
236
- const isZoomedOut = useValue('isZoomedOut', () => this.editor.getZoomLevel() < 0.25, [
237
- this.editor,
238
- ])
242
+ const isZoomedOut = useEfficientZoomThreshold(shape.props.scale * 0.25)
239
243
 
240
244
  const { size, dash, scale } = shape.props
241
245
  const strokeWidth = STROKE_SIZES[size]
@@ -283,6 +287,7 @@ export class GeoShapeUtil extends BaseBoxShapeUtil<TLGeoShape> {
283
287
  labelColor={getColorValue(theme, props.labelColor, 'solid')}
284
288
  bounds={bounds}
285
289
  padding={LABEL_PADDING}
290
+ showTextOutline={this.options.showTextOutline}
286
291
  />
287
292
  )
288
293
  }
@@ -24,7 +24,7 @@ export class Pointing extends StateNode {
24
24
  const creatingMarkId = this.editor.markHistoryStoppingPoint(`creating_geo:${id}`)
25
25
  const newPoint = maybeSnapToGrid(originPagePoint, this.editor)
26
26
  this.editor
27
- .createShapes<TLGeoShape>([
27
+ .createShapes([
28
28
  {
29
29
  id,
30
30
  type: 'geo',
@@ -88,7 +88,7 @@ export class Pointing extends StateNode {
88
88
  ? { w: 300, h: 180 }
89
89
  : { w: 200, h: 200 }
90
90
 
91
- this.editor.createShapes<TLGeoShape>([
91
+ this.editor.createShapes([
92
92
  {
93
93
  id,
94
94
  type: 'geo',
@@ -115,7 +115,7 @@ export class Pointing extends StateNode {
115
115
  if (parentTransform) delta.rot(-parentTransform.rotation())
116
116
  const newPoint = maybeSnapToGrid(new Vec(shape.x - delta.x, shape.y - delta.y), this.editor)
117
117
  this.editor.select(id)
118
- this.editor.updateShape<TLGeoShape>({
118
+ this.editor.updateShape({
119
119
  id: shape.id,
120
120
  type: 'geo',
121
121
  x: newPoint.x,
@@ -315,7 +315,7 @@ function useHighlightForceSolid(editor: Editor, shape: TLHighlightShape) {
315
315
  'forceSolid',
316
316
  () => {
317
317
  const sw = getStrokeWidth(shape)
318
- const zoomLevel = editor.getZoomLevel()
318
+ const zoomLevel = editor.getEfficientZoomLevel()
319
319
  if (sw / zoomLevel < 1.5) {
320
320
  return true
321
321
  }
@@ -1,4 +1,4 @@
1
- import { TLLineShape, assert } from '@tldraw/editor'
1
+ import { assert } from '@tldraw/editor'
2
2
  import { TestEditor } from '../../../test/TestEditor'
3
3
 
4
4
  let editor: TestEditor
@@ -127,7 +127,7 @@ describe('When extending the line with the shift-key in tool-lock mode', () => {
127
127
  .pointerUp(20, 10)
128
128
 
129
129
  const line = editor.getCurrentPageShapes()[editor.getCurrentPageShapes().length - 1]
130
- assert(editor.isShapeOfType<TLLineShape>(line, 'line'))
130
+ assert(editor.isShapeOfType(line, 'line'))
131
131
  expect(Object.keys(line.props.points).length).toBe(3)
132
132
  })
133
133
 
@@ -143,7 +143,7 @@ describe('When extending the line with the shift-key in tool-lock mode', () => {
143
143
  .pointerUp(30, 10)
144
144
 
145
145
  const line = editor.getCurrentPageShapes()[editor.getCurrentPageShapes().length - 1]
146
- assert(editor.isShapeOfType<TLLineShape>(line, 'line'))
146
+ assert(editor.isShapeOfType(line, 'line'))
147
147
  expect(Object.keys(line.props.points).length).toBe(2)
148
148
  })
149
149
 
@@ -160,7 +160,7 @@ describe('When extending the line with the shift-key in tool-lock mode', () => {
160
160
  .pointerUp(30, 10)
161
161
 
162
162
  const line = editor.getCurrentPageShapes()[editor.getCurrentPageShapes().length - 1]
163
- assert(editor.isShapeOfType<TLLineShape>(line, 'line'))
163
+ assert(editor.isShapeOfType(line, 'line'))
164
164
  expect(Object.keys(line.props.points).length).toBe(3)
165
165
  })
166
166
 
@@ -179,7 +179,7 @@ describe('When extending the line with the shift-key in tool-lock mode', () => {
179
179
  .pointerUp(30, 10)
180
180
 
181
181
  const line = editor.getCurrentPageShapes()[editor.getCurrentPageShapes().length - 1]
182
- assert(editor.isShapeOfType<TLLineShape>(line, 'line'))
182
+ assert(editor.isShapeOfType(line, 'line'))
183
183
  expect(Object.keys(line.props.points).length).toBe(3)
184
184
  })
185
185
 
@@ -200,7 +200,7 @@ describe('When extending the line with the shift-key in tool-lock mode', () => {
200
200
  .pointerUp(40, 10)
201
201
 
202
202
  const line = editor.getCurrentPageShapes()[editor.getCurrentPageShapes().length - 1]
203
- assert(editor.isShapeOfType<TLLineShape>(line, 'line'))
203
+ assert(editor.isShapeOfType(line, 'line'))
204
204
  expect(Object.keys(line.props.points).length).toBe(3)
205
205
  })
206
206
  })
@@ -24,7 +24,7 @@ beforeEach(() => {
24
24
  editor
25
25
  .selectAll()
26
26
  .deleteShapes(editor.getSelectedShapeIds())
27
- .createShapes<TLLineShape>([
27
+ .createShapes([
28
28
  {
29
29
  id: id,
30
30
  type: 'line',
@@ -168,10 +168,10 @@ describe('Snapping', () => {
168
168
  type: 'line',
169
169
  props: {
170
170
  points: {
171
- a1: { id: 'a1', index: 'a1', x: 0, y: 0 },
172
- a2: { id: 'a2', index: 'a2', x: 100, y: 0 },
173
- a3: { id: 'a3', index: 'a3', x: 100, y: 100 },
174
- a4: { id: 'a4', index: 'a4', x: 0, y: 100 },
171
+ a1: { id: 'a1', index: 'a1' as IndexKey, x: 0, y: 0 },
172
+ a2: { id: 'a2', index: 'a2' as IndexKey, x: 100, y: 0 },
173
+ a3: { id: 'a3', index: 'a3' as IndexKey, x: 100, y: 100 },
174
+ a4: { id: 'a4', index: 'a4' as IndexKey, x: 0, y: 100 },
175
175
  },
176
176
  },
177
177
  })
@@ -89,7 +89,7 @@ export class Pointing extends StateNode {
89
89
 
90
90
  const newPoint = maybeSnapToGrid(currentPagePoint, this.editor)
91
91
 
92
- this.editor.createShapes<TLLineShape>([
92
+ this.editor.createShapes([
93
93
  {
94
94
  id,
95
95
  type: 'line',
@@ -1,3 +1,4 @@
1
+ import { TLNoteShape } from '@tldraw/editor'
1
2
  import { TestEditor } from '../../../test/TestEditor'
2
3
  import { NoteShapeTool } from './NoteShapeTool'
3
4
 
@@ -242,7 +243,7 @@ describe('Adjacent note position helpers (sticky pits)', () => {
242
243
 
243
244
  it('Falls into correct pit below notes with growY', () => {
244
245
  editor.createShape({ type: 'note', x: 0, y: 0 }).updateShape({
245
- ...editor.getLastCreatedShape(),
246
+ ...editor.getLastCreatedShape<TLNoteShape>(),
246
247
  props: { growY: 100 },
247
248
  })
248
249
 
@@ -50,6 +50,7 @@ import {
50
50
  } from '../shared/default-shape-constants'
51
51
  import { useDefaultColorTheme } from '../shared/useDefaultColorTheme'
52
52
  import { useIsReadyForEditing } from '../shared/useEditablePlainText'
53
+ import { useEfficientZoomThreshold } from '../shared/useEfficientZoomThreshold'
53
54
  import {
54
55
  CLONE_HANDLE_MARGIN,
55
56
  NOTE_CENTER_OFFSET,
@@ -158,7 +159,7 @@ export class NoteShapeUtil extends ShapeUtil<TLNoteShape> {
158
159
  const isCoarsePointer = this.editor.getInstanceState().isCoarsePointer
159
160
  if (isCoarsePointer) return []
160
161
 
161
- const zoom = this.editor.getZoomLevel()
162
+ const zoom = this.editor.getEfficientZoomLevel()
162
163
  if (zoom * scale < 0.25) return []
163
164
 
164
165
  const nh = getNoteHeight(shape)
@@ -268,15 +269,12 @@ export class NoteShapeUtil extends ShapeUtil<TLNoteShape> {
268
269
  [this.editor]
269
270
  )
270
271
 
271
- // todo: consider hiding shadows on dark mode if they're invisible anyway
272
-
273
- const hideShadows = useValue('zoom', () => this.editor.getZoomLevel() < 0.35 / scale, [
274
- scale,
275
- this.editor,
276
- ])
277
-
278
272
  const isDarkMode = useValue('dark mode', () => this.editor.user.getIsDarkMode(), [this.editor])
279
273
 
274
+ // Shadows are hidden when zoomed out far enough or in dark mode
275
+ let hideShadows = useEfficientZoomThreshold(scale * 0.25)
276
+ if (isDarkMode) hideShadows = true
277
+
280
278
  const isSelected = shape.id === this.editor.getOnlySelectedShapeId()
281
279
 
282
280
  const isReadyForEditing = useIsReadyForEditing(this.editor, shape.id)
@@ -318,6 +316,7 @@ export class NoteShapeUtil extends ShapeUtil<TLNoteShape> {
318
316
  wrap
319
317
  padding={LABEL_PADDING * scale}
320
318
  hasCustomTabBehavior
319
+ showTextOutline={false}
321
320
  onKeyDown={handleKeyDown}
322
321
  />
323
322
  )}
@@ -104,7 +104,7 @@ export function getAvailableNoteAdjacentPositions(
104
104
  // Get all the positions that are adjacent to the selected note shapes
105
105
  for (const shape of editor.getCurrentPageShapes()) {
106
106
  if (
107
- !editor.isShapeOfType<TLNoteShape>(shape, 'note') ||
107
+ !editor.isShapeOfType(shape, 'note') ||
108
108
  scale !== shape.props.scale ||
109
109
  selectedShapeIds.has(shape.id)
110
110
  ) {
@@ -204,7 +204,7 @@ export function getNoteShapeForAdjacentPosition(
204
204
 
205
205
  // We create it at the center first, so that it becomes
206
206
  // the child of whatever parent was at that center
207
- editor.createShape<TLNoteShape>({
207
+ editor.createShape({
208
208
  id,
209
209
  type: 'note',
210
210
  x: center.x,
@@ -1,13 +1,14 @@
1
- import { useEditor, useValue } from '@tldraw/editor'
1
+ import { useEditor } from '@tldraw/editor'
2
2
  import classNames from 'classnames'
3
3
  import { PointerEventHandler, useCallback } from 'react'
4
+ import { useEfficientZoomThreshold } from './useEfficientZoomThreshold'
4
5
 
5
6
  const LINK_ICON =
6
7
  "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' fill='none'%3E%3Cpath stroke='%23000' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M13 5H7a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6M19 5h6m0 0v6m0-6L13 17'/%3E%3C/svg%3E"
7
8
 
8
9
  export function HyperlinkButton({ url }: { url: string }) {
9
10
  const editor = useEditor()
10
- const hideButton = useValue('zoomLevel', () => editor.getZoomLevel() < 0.32, [editor])
11
+ const hideButton = useEfficientZoomThreshold()
11
12
  const markAsHandledOnShiftKey = useCallback<PointerEventHandler>(
12
13
  (e) => {
13
14
  if (!editor.inputs.shiftKey) editor.markEventAsHandled(e)
@@ -1,11 +1,13 @@
1
1
  import {
2
2
  Box,
3
+ ExtractShapeByProps,
3
4
  TLDefaultFillStyle,
4
5
  TLDefaultFontStyle,
5
6
  TLDefaultHorizontalAlignStyle,
6
7
  TLDefaultVerticalAlignStyle,
7
8
  TLShapeId,
8
9
  } from '@tldraw/editor'
10
+ import classNames from 'classnames'
9
11
  import React from 'react'
10
12
  import { PlainTextArea } from '../text/PlainTextArea'
11
13
  import { TextHelpers } from './TextHelpers'
@@ -15,7 +17,7 @@ import { useEditablePlainText } from './useEditablePlainText'
15
17
  /** @public */
16
18
  export interface PlainTextLabelProps {
17
19
  shapeId: TLShapeId
18
- type: string
20
+ type: ExtractShapeByProps<{ text: string }>['type']
19
21
  font: TLDefaultFontStyle
20
22
  fontSize: number
21
23
  lineHeight: number
@@ -33,6 +35,7 @@ export interface PlainTextLabelProps {
33
35
  textWidth?: number
34
36
  textHeight?: number
35
37
  padding?: number
38
+ showTextOutline?: boolean
36
39
  }
37
40
 
38
41
  /**
@@ -60,6 +63,7 @@ export const PlainTextLabel = React.memo(function PlainTextLabel({
60
63
  style,
61
64
  textWidth,
62
65
  textHeight,
66
+ showTextOutline = true,
63
67
  }: PlainTextLabelProps) {
64
68
  const { rInput, isEmpty, isEditing, isReadyForEditing, ...editableTextRest } =
65
69
  useEditablePlainText(shapeId, type, plaintext)
@@ -108,7 +112,13 @@ export const PlainTextLabel = React.memo(function PlainTextLabel({
108
112
  height: textHeight ? Math.ceil(textHeight) : undefined,
109
113
  }}
110
114
  >
111
- <div className={`${cssPrefix} tl-text tl-text-content`} dir="auto">
115
+ <div
116
+ className={classNames(
117
+ `${cssPrefix} tl-text tl-text-content`,
118
+ showTextOutline ? 'tl-text__outline' : 'tl-text__no-outline'
119
+ )}
120
+ dir="auto"
121
+ >
112
122
  {finalPlainText.split('\n').map((lineOfText, index) => (
113
123
  <div key={index} dir="auto">
114
124
  {lineOfText}
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  Box,
3
3
  DefaultFontFamilies,
4
+ ExtractShapeByProps,
4
5
  TLDefaultFillStyle,
5
6
  TLDefaultFontStyle,
6
7
  TLDefaultHorizontalAlignStyle,
@@ -14,6 +15,7 @@ import {
14
15
  useReactor,
15
16
  useValue,
16
17
  } from '@tldraw/editor'
18
+ import classNames from 'classnames'
17
19
  import React, { useMemo } from 'react'
18
20
  import { renderHtmlFromRichText } from '../../utils/text/richText'
19
21
  import { RichTextArea } from '../text/RichTextArea'
@@ -24,7 +26,7 @@ import { useEditableRichText } from './useEditableRichText'
24
26
  /** @public */
25
27
  export interface RichTextLabelProps {
26
28
  shapeId: TLShapeId
27
- type: string
29
+ type: ExtractShapeByProps<{ richText: TLRichText }>['type']
28
30
  font: TLDefaultFontStyle
29
31
  fontSize: number
30
32
  lineHeight: number
@@ -43,6 +45,7 @@ export interface RichTextLabelProps {
43
45
  textHeight?: number
44
46
  padding?: number
45
47
  hasCustomTabBehavior?: boolean
48
+ showTextOutline?: boolean
46
49
  }
47
50
 
48
51
  /**
@@ -71,6 +74,7 @@ export const RichTextLabel = React.memo(function RichTextLabel({
71
74
  textWidth,
72
75
  textHeight,
73
76
  hasCustomTabBehavior,
77
+ showTextOutline = true,
74
78
  }: RichTextLabelProps) {
75
79
  const editor = useEditor()
76
80
  const isDragging = React.useRef(false)
@@ -128,7 +132,10 @@ export const RichTextLabel = React.memo(function RichTextLabel({
128
132
  const cssPrefix = classNamePrefix || 'tl-text'
129
133
  return (
130
134
  <div
131
- className={`${cssPrefix}-label tl-text-wrapper tl-rich-text-wrapper`}
135
+ className={classNames(
136
+ `${cssPrefix}-label tl-text-wrapper tl-rich-text-wrapper`,
137
+ showTextOutline ? 'tl-text__outline' : 'tl-text__no-outline'
138
+ )}
132
139
  aria-hidden={!isEditing}
133
140
  data-font={font}
134
141
  data-align={align}
@@ -258,7 +265,10 @@ export function RichTextSVG({
258
265
  y={bounds.minY}
259
266
  width={bounds.w}
260
267
  height={bounds.h}
261
- className="tl-export-embed-styles tl-rich-text tl-rich-text-svg"
268
+ className={classNames(
269
+ 'tl-export-embed-styles tl-rich-text tl-rich-text-svg',
270
+ showTextOutline ? 'tl-text__outline' : 'tl-text__no-outline'
271
+ )}
262
272
  >
263
273
  <div style={wrapperStyle}>
264
274
  <div dangerouslySetInnerHTML={{ __html: html }} style={style} />
@@ -50,10 +50,10 @@ export const ShapeFill = React.memo(function ShapeFill({
50
50
  export function PatternFill({ d, color, theme }: ShapeFillProps) {
51
51
  const editor = useEditor()
52
52
  const svgExport = useSvgExportContext()
53
- const zoomLevel = useValue('zoomLevel', () => editor.getZoomLevel(), [editor])
53
+ const zoomLevel = useValue('zoomLevel', () => editor.getEfficientZoomLevel(), [editor])
54
54
  const getHashPatternZoomName = useGetHashPatternZoomName()
55
55
 
56
- const teenyTiny = editor.getZoomLevel() <= 0.18
56
+ const teenyTiny = zoomLevel <= 0.18
57
57
 
58
58
  return (
59
59
  <>
@@ -361,6 +361,7 @@ export function getCropBox<T extends ShapeWithCrop>(
361
361
  x: newPoint.x,
362
362
  y: newPoint.y,
363
363
  props: {
364
+ ...shape.props,
364
365
  w: tempBox.w,
365
366
  h: tempBox.h,
366
367
  crop: newCrop,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  Editor,
3
+ ExtractShapeByProps,
3
4
  TLShapeId,
4
- TLUnknownShape,
5
5
  getPointerInfo,
6
6
  noop,
7
7
  preventDefault,
@@ -13,7 +13,11 @@ import React, { useCallback, useEffect, useRef } from 'react'
13
13
  import { TextHelpers } from './TextHelpers'
14
14
 
15
15
  /** @public */
16
- export function useEditablePlainText(shapeId: TLShapeId, type: string, text?: string) {
16
+ export function useEditablePlainText(
17
+ shapeId: TLShapeId,
18
+ type: ExtractShapeByProps<{ text: string }>['type'],
19
+ text?: string
20
+ ) {
17
21
  const commonUseEditableTextHandlers = useEditableTextCommon(shapeId)
18
22
  const isEditing = commonUseEditableTextHandlers.isEditing
19
23
  const editor = useEditor()
@@ -75,7 +79,7 @@ export function useEditablePlainText(shapeId: TLShapeId, type: string, text?: st
75
79
  if (editor.getEditingShapeId() !== shapeId) return
76
80
 
77
81
  const normalizedPlaintext = TextHelpers.normalizeText(plaintext || '')
78
- editor.updateShape<TLUnknownShape & { props: { text: string } }>({
82
+ editor.updateShape({
79
83
  id: shapeId,
80
84
  type,
81
85
  props: { text: normalizedPlaintext },
@@ -1,10 +1,14 @@
1
- import { TLRichText, TLShapeId, TLUnknownShape, isAccelKey, useEditor } from '@tldraw/editor'
1
+ import { ExtractShapeByProps, TLRichText, TLShapeId, isAccelKey, useEditor } from '@tldraw/editor'
2
2
  import { useCallback, useEffect, useRef } from 'react'
3
3
  import { isEmptyRichText } from '../../utils/text/richText'
4
4
  import { useEditableTextCommon } from './useEditablePlainText'
5
5
 
6
6
  /** @public */
7
- export function useEditableRichText(shapeId: TLShapeId, type: string, richText?: TLRichText) {
7
+ export function useEditableRichText(
8
+ shapeId: TLShapeId,
9
+ type: ExtractShapeByProps<{ richText: TLRichText }>['type'],
10
+ richText?: TLRichText
11
+ ) {
8
12
  const commonUseEditableTextHandlers = useEditableTextCommon(shapeId)
9
13
  const isEditing = commonUseEditableTextHandlers.isEditing
10
14
  const editor = useEditor()
@@ -39,7 +43,7 @@ export function useEditableRichText(shapeId: TLShapeId, type: string, richText?:
39
43
  ({ richText }: { richText: TLRichText }) => {
40
44
  if (editor.getEditingShapeId() !== shapeId) return
41
45
 
42
- editor.updateShape<TLUnknownShape & { props: { richText: TLRichText } }>({
46
+ editor.updateShape({
43
47
  id: shapeId,
44
48
  type,
45
49
  props: { richText },
@@ -0,0 +1,10 @@
1
+ import { useEditor, useValue } from '@tldraw/editor'
2
+
3
+ /** Returns true when zoomed out far enough that shapes should render in a simplified "solid" style. */
4
+ export function useEfficientZoomThreshold(threshold = 0.25) {
5
+ const editor = useEditor()
6
+ return useValue('efficient zoom threshold', () => editor.getEfficientZoomLevel() < threshold, [
7
+ editor,
8
+ threshold,
9
+ ])
10
+ }
@@ -96,7 +96,7 @@ export function useImageOrVideoAsset({ shapeId, assetId, width }: UseImageOrVide
96
96
 
97
97
  const screenScale = exportInfo
98
98
  ? exportInfo.scale * (width / asset.props.w)
99
- : editor.getZoomLevel() * (width / asset.props.w)
99
+ : editor.getEfficientZoomLevel() * (width / asset.props.w)
100
100
 
101
101
  function resolve(asset: TLImageAsset | TLVideoAsset, url: string | null) {
102
102
  if (isCancelled) return // don't update if the hook has remounted
@@ -1,4 +1,4 @@
1
- import { DefaultTextAlignStyle, TLTextShape, toRichText } from '@tldraw/editor'
1
+ import { DefaultTextAlignStyle, toRichText } from '@tldraw/editor'
2
2
  import { vi } from 'vitest'
3
3
  import { TestEditor } from '../../../test/TestEditor'
4
4
  import { TextShapeTool } from './TextShapeTool'
@@ -24,7 +24,7 @@ describe(TextShapeTool, () => {
24
24
  editor.pointerUp()
25
25
  editor.expectToBeIn('select.editing_shape')
26
26
  // This comes from the component, not the state chart
27
- editor.updateShapes<TLTextShape>([
27
+ editor.updateShapes([
28
28
  {
29
29
  ...editor.getCurrentPageShapes()[0]!,
30
30
  type: 'text',
@@ -96,7 +96,7 @@ describe('When in idle state', () => {
96
96
  editor.expectToBeIn('select.editing_shape')
97
97
 
98
98
  // Update the text shape with some content
99
- editor.updateShapes<TLTextShape>([
99
+ editor.updateShapes([
100
100
  {
101
101
  ...editor.getCurrentPageShapes()[0]!,
102
102
  type: 'text',
@@ -133,7 +133,7 @@ describe('When in idle state', () => {
133
133
  editor.expectToBeIn('select.editing_shape')
134
134
 
135
135
  // Update the text shape with some content
136
- editor.updateShapes<TLTextShape>([
136
+ editor.updateShapes([
137
137
  {
138
138
  ...editor.getCurrentPageShapes()[0]!,
139
139
  type: 'text',