tldraw 5.2.0-next.e2b8d10bf10e → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (420) hide show
  1. package/DOCS.md +20294 -0
  2. package/README.md +13 -1
  3. package/RELEASE_NOTES.md +2908 -0
  4. package/dist-cjs/index.d.ts +110 -23
  5. package/dist-cjs/index.js +3 -3
  6. package/dist-cjs/index.js.map +2 -2
  7. package/dist-cjs/lib/TldrawImage.js +0 -1
  8. package/dist-cjs/lib/TldrawImage.js.map +2 -2
  9. package/dist-cjs/lib/bindings/arrow/ArrowBindingUtil.js +2 -1
  10. package/dist-cjs/lib/bindings/arrow/ArrowBindingUtil.js.map +2 -2
  11. package/dist-cjs/lib/defaultEmbedDefinitions.js +3 -2
  12. package/dist-cjs/lib/defaultEmbedDefinitions.js.map +2 -2
  13. package/dist-cjs/lib/defaultOverlayUtils.js +11 -11
  14. package/dist-cjs/lib/defaultOverlayUtils.js.map +2 -2
  15. package/dist-cjs/lib/overlays/SelectionForegroundOverlayUtil.js +76 -64
  16. package/dist-cjs/lib/overlays/SelectionForegroundOverlayUtil.js.map +2 -2
  17. package/dist-cjs/lib/overlays/ShapeHandleOverlayUtil.js +1 -1
  18. package/dist-cjs/lib/overlays/ShapeHandleOverlayUtil.js.map +2 -2
  19. package/dist-cjs/lib/overlays/ShapeIndicatorOverlayUtil.js +107 -0
  20. package/dist-cjs/lib/overlays/ShapeIndicatorOverlayUtil.js.map +7 -0
  21. package/dist-cjs/lib/shapes/arrow/toolStates/Pointing.js +3 -0
  22. package/dist-cjs/lib/shapes/arrow/toolStates/Pointing.js.map +2 -2
  23. package/dist-cjs/lib/shapes/bookmark/BookmarkShapeUtil.js +12 -3
  24. package/dist-cjs/lib/shapes/bookmark/BookmarkShapeUtil.js.map +2 -2
  25. package/dist-cjs/lib/shapes/bookmark/bookmarks.js +54 -6
  26. package/dist-cjs/lib/shapes/bookmark/bookmarks.js.map +2 -2
  27. package/dist-cjs/lib/shapes/draw/DrawShapeUtil.js +2 -1
  28. package/dist-cjs/lib/shapes/draw/DrawShapeUtil.js.map +2 -2
  29. package/dist-cjs/lib/shapes/draw/getPath.js +1 -1
  30. package/dist-cjs/lib/shapes/draw/getPath.js.map +2 -2
  31. package/dist-cjs/lib/shapes/draw/toolStates/Drawing.js +45 -47
  32. package/dist-cjs/lib/shapes/draw/toolStates/Drawing.js.map +2 -2
  33. package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js +2 -1
  34. package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js.map +2 -2
  35. package/dist-cjs/lib/shapes/frame/FrameShapeTool.js +23 -18
  36. package/dist-cjs/lib/shapes/frame/FrameShapeTool.js.map +2 -2
  37. package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js +1 -1
  38. package/dist-cjs/lib/shapes/geo/GeoShapeUtil.js.map +2 -2
  39. package/dist-cjs/lib/shapes/geo/toolStates/Pointing.js +3 -0
  40. package/dist-cjs/lib/shapes/geo/toolStates/Pointing.js.map +2 -2
  41. package/dist-cjs/lib/shapes/highlight/HighlightShapeUtil.js +11 -7
  42. package/dist-cjs/lib/shapes/highlight/HighlightShapeUtil.js.map +2 -2
  43. package/dist-cjs/lib/shapes/image/ImageShapeUtil.js +3 -11
  44. package/dist-cjs/lib/shapes/image/ImageShapeUtil.js.map +2 -2
  45. package/dist-cjs/lib/shapes/line/toolStates/Pointing.js +5 -1
  46. package/dist-cjs/lib/shapes/line/toolStates/Pointing.js.map +2 -2
  47. package/dist-cjs/lib/shapes/note/NoteShapeUtil.js +31 -41
  48. package/dist-cjs/lib/shapes/note/NoteShapeUtil.js.map +2 -2
  49. package/dist-cjs/lib/shapes/note/noteHelpers.js +1 -1
  50. package/dist-cjs/lib/shapes/note/noteHelpers.js.map +2 -2
  51. package/dist-cjs/lib/shapes/note/toolStates/Pointing.js +28 -6
  52. package/dist-cjs/lib/shapes/note/toolStates/Pointing.js.map +2 -2
  53. package/dist-cjs/lib/shapes/shared/PlainTextLabel.js +3 -2
  54. package/dist-cjs/lib/shapes/shared/PlainTextLabel.js.map +2 -2
  55. package/dist-cjs/lib/shapes/shared/RichTextLabel.js +3 -3
  56. package/dist-cjs/lib/shapes/shared/RichTextLabel.js.map +2 -2
  57. package/dist-cjs/lib/shapes/shared/defaultStyleDefs.js +17 -3
  58. package/dist-cjs/lib/shapes/shared/defaultStyleDefs.js.map +2 -2
  59. package/dist-cjs/lib/shapes/shared/freehand/core.js +424 -0
  60. package/dist-cjs/lib/shapes/shared/freehand/core.js.map +7 -0
  61. package/dist-cjs/lib/shapes/shared/freehand/fmt.js +106 -0
  62. package/dist-cjs/lib/shapes/shared/freehand/fmt.js.map +7 -0
  63. package/dist-cjs/lib/shapes/shared/freehand/getStroke.js +6 -6
  64. package/dist-cjs/lib/shapes/shared/freehand/getStroke.js.map +2 -2
  65. package/dist-cjs/lib/shapes/shared/freehand/getStrokeOutlinePoints.js +320 -86
  66. package/dist-cjs/lib/shapes/shared/freehand/getStrokeOutlinePoints.js.map +3 -3
  67. package/dist-cjs/lib/shapes/shared/freehand/getStrokePoints.js +20 -96
  68. package/dist-cjs/lib/shapes/shared/freehand/getStrokePoints.js.map +3 -3
  69. package/dist-cjs/lib/shapes/shared/freehand/svg.js +55 -19
  70. package/dist-cjs/lib/shapes/shared/freehand/svg.js.map +2 -2
  71. package/dist-cjs/lib/shapes/shared/freehand/svgInk.js +189 -97
  72. package/dist-cjs/lib/shapes/shared/freehand/svgInk.js.map +3 -3
  73. package/dist-cjs/lib/shapes/shared/freehand/types.js.map +1 -1
  74. package/dist-cjs/lib/shapes/shared/interpolate-props.js +6 -2
  75. package/dist-cjs/lib/shapes/shared/interpolate-props.js.map +2 -2
  76. package/dist-cjs/lib/shapes/text/toolStates/Pointing.js +3 -0
  77. package/dist-cjs/lib/shapes/text/toolStates/Pointing.js.map +2 -2
  78. package/dist-cjs/lib/tools/HandTool/HandTool.js +16 -6
  79. package/dist-cjs/lib/tools/HandTool/HandTool.js.map +2 -2
  80. package/dist-cjs/lib/tools/HandTool/childStates/Dragging.js +10 -1
  81. package/dist-cjs/lib/tools/HandTool/childStates/Dragging.js.map +2 -2
  82. package/dist-cjs/lib/tools/HandTool/childStates/OneFingerZooming.js +84 -0
  83. package/dist-cjs/lib/tools/HandTool/childStates/OneFingerZooming.js.map +7 -0
  84. package/dist-cjs/lib/tools/SelectTool/childStates/Idle.js +12 -0
  85. package/dist-cjs/lib/tools/SelectTool/childStates/Idle.js.map +2 -2
  86. package/dist-cjs/lib/tools/SelectTool/childStates/PointingShape.js +4 -0
  87. package/dist-cjs/lib/tools/SelectTool/childStates/PointingShape.js.map +2 -2
  88. package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js +107 -46
  89. package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js.map +2 -2
  90. package/dist-cjs/lib/ui/TldrawUi.js +4 -3
  91. package/dist-cjs/lib/ui/TldrawUi.js.map +2 -2
  92. package/dist-cjs/lib/ui/components/A11y.js +2 -0
  93. package/dist-cjs/lib/ui/components/A11y.js.map +2 -2
  94. package/dist-cjs/lib/ui/components/ContextMenu/DefaultContextMenu.js +14 -1
  95. package/dist-cjs/lib/ui/components/ContextMenu/DefaultContextMenu.js.map +2 -2
  96. package/dist-cjs/lib/ui/components/ContextMenu/DefaultContextMenuContent.js +0 -6
  97. package/dist-cjs/lib/ui/components/ContextMenu/DefaultContextMenuContent.js.map +2 -2
  98. package/dist-cjs/lib/ui/components/DefaultFollowingIndicator.js.map +2 -2
  99. package/dist-cjs/lib/ui/components/Dialogs.js +17 -37
  100. package/dist-cjs/lib/ui/components/Dialogs.js.map +2 -2
  101. package/dist-cjs/lib/ui/components/HelperButtons/BackToContent.js +19 -4
  102. package/dist-cjs/lib/ui/components/HelperButtons/BackToContent.js.map +2 -2
  103. package/dist-cjs/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.js +12 -2
  104. package/dist-cjs/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.js.map +2 -2
  105. package/dist-cjs/lib/ui/components/Minimap/DefaultMinimap.js +47 -70
  106. package/dist-cjs/lib/ui/components/Minimap/DefaultMinimap.js.map +2 -2
  107. package/dist-cjs/lib/ui/components/Minimap/MinimapManager.js +25 -46
  108. package/dist-cjs/lib/ui/components/Minimap/MinimapManager.js.map +2 -2
  109. package/dist-cjs/lib/ui/components/SharePanel/DefaultPeopleMenuAvatar.js.map +2 -2
  110. package/dist-cjs/lib/ui/components/SharePanel/DefaultPeopleMenuContent.js.map +2 -2
  111. package/dist-cjs/lib/ui/components/SharePanel/DefaultPeopleMenuFacePile.js.map +2 -2
  112. package/dist-cjs/lib/ui/components/SharePanel/DefaultPeopleMenuItem.js +1 -1
  113. package/dist-cjs/lib/ui/components/SharePanel/DefaultPeopleMenuItem.js.map +2 -2
  114. package/dist-cjs/lib/ui/components/StylePanel/StylePanelButtonPicker.js +1 -0
  115. package/dist-cjs/lib/ui/components/StylePanel/StylePanelButtonPicker.js.map +2 -2
  116. package/dist-cjs/lib/ui/components/menu-items.js +7 -0
  117. package/dist-cjs/lib/ui/components/menu-items.js.map +2 -2
  118. package/dist-cjs/lib/ui/components/primitives/TldrawUiDropdownMenu.js +2 -1
  119. package/dist-cjs/lib/ui/components/primitives/TldrawUiDropdownMenu.js.map +2 -2
  120. package/dist-cjs/lib/ui/components/primitives/TldrawUiIcon.js +14 -3
  121. package/dist-cjs/lib/ui/components/primitives/TldrawUiIcon.js.map +2 -2
  122. package/dist-cjs/lib/ui/components/primitives/TldrawUiInput.js +2 -0
  123. package/dist-cjs/lib/ui/components/primitives/TldrawUiInput.js.map +2 -2
  124. package/dist-cjs/lib/ui/components/primitives/TldrawUiPopover.js +2 -0
  125. package/dist-cjs/lib/ui/components/primitives/TldrawUiPopover.js.map +2 -2
  126. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.js +0 -2
  127. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.js.map +2 -2
  128. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuSubmenu.js +1 -2
  129. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuSubmenu.js.map +2 -2
  130. package/dist-cjs/lib/ui/context/actions.js +70 -1
  131. package/dist-cjs/lib/ui/context/actions.js.map +2 -2
  132. package/dist-cjs/lib/ui/context/asset-urls.js +5 -8
  133. package/dist-cjs/lib/ui/context/asset-urls.js.map +2 -2
  134. package/dist-cjs/lib/ui/context/events.js.map +2 -2
  135. package/dist-cjs/lib/ui/hooks/useKeyboardShortcuts.js +13 -0
  136. package/dist-cjs/lib/ui/hooks/useKeyboardShortcuts.js.map +2 -2
  137. package/dist-cjs/lib/ui/hooks/useTools.js +1 -1
  138. package/dist-cjs/lib/ui/hooks/useTools.js.map +2 -2
  139. package/dist-cjs/lib/ui/hooks/useTranslation/TLUiTranslationKey.js.map +1 -1
  140. package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js +2 -0
  141. package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js.map +2 -2
  142. package/dist-cjs/lib/ui/version.js +3 -3
  143. package/dist-cjs/lib/ui/version.js.map +1 -1
  144. package/dist-cjs/lib/utils/embeds/embeds.js +4 -4
  145. package/dist-cjs/lib/utils/embeds/embeds.js.map +2 -2
  146. package/dist-cjs/lib/utils/test-helpers.js +2 -2
  147. package/dist-cjs/lib/utils/test-helpers.js.map +2 -2
  148. package/dist-cjs/lib/utils/text/richText.js +3 -1
  149. package/dist-cjs/lib/utils/text/richText.js.map +2 -2
  150. package/dist-esm/index.d.mts +110 -23
  151. package/dist-esm/index.mjs +5 -3
  152. package/dist-esm/index.mjs.map +2 -2
  153. package/dist-esm/lib/TldrawImage.mjs +0 -1
  154. package/dist-esm/lib/TldrawImage.mjs.map +2 -2
  155. package/dist-esm/lib/bindings/arrow/ArrowBindingUtil.mjs +2 -1
  156. package/dist-esm/lib/bindings/arrow/ArrowBindingUtil.mjs.map +2 -2
  157. package/dist-esm/lib/defaultEmbedDefinitions.mjs +3 -2
  158. package/dist-esm/lib/defaultEmbedDefinitions.mjs.map +2 -2
  159. package/dist-esm/lib/defaultOverlayUtils.mjs +11 -11
  160. package/dist-esm/lib/defaultOverlayUtils.mjs.map +2 -2
  161. package/dist-esm/lib/overlays/SelectionForegroundOverlayUtil.mjs +77 -65
  162. package/dist-esm/lib/overlays/SelectionForegroundOverlayUtil.mjs.map +2 -2
  163. package/dist-esm/lib/overlays/ShapeHandleOverlayUtil.mjs +1 -1
  164. package/dist-esm/lib/overlays/ShapeHandleOverlayUtil.mjs.map +2 -2
  165. package/dist-esm/lib/overlays/ShapeIndicatorOverlayUtil.mjs +87 -0
  166. package/dist-esm/lib/overlays/ShapeIndicatorOverlayUtil.mjs.map +7 -0
  167. package/dist-esm/lib/shapes/arrow/toolStates/Pointing.mjs +9 -1
  168. package/dist-esm/lib/shapes/arrow/toolStates/Pointing.mjs.map +2 -2
  169. package/dist-esm/lib/shapes/bookmark/BookmarkShapeUtil.mjs +15 -3
  170. package/dist-esm/lib/shapes/bookmark/BookmarkShapeUtil.mjs.map +2 -2
  171. package/dist-esm/lib/shapes/bookmark/bookmarks.mjs +54 -6
  172. package/dist-esm/lib/shapes/bookmark/bookmarks.mjs.map +2 -2
  173. package/dist-esm/lib/shapes/draw/DrawShapeUtil.mjs +3 -1
  174. package/dist-esm/lib/shapes/draw/DrawShapeUtil.mjs.map +2 -2
  175. package/dist-esm/lib/shapes/draw/getPath.mjs +1 -1
  176. package/dist-esm/lib/shapes/draw/getPath.mjs.map +2 -2
  177. package/dist-esm/lib/shapes/draw/toolStates/Drawing.mjs +46 -47
  178. package/dist-esm/lib/shapes/draw/toolStates/Drawing.mjs.map +2 -2
  179. package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs +2 -1
  180. package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs.map +2 -2
  181. package/dist-esm/lib/shapes/frame/FrameShapeTool.mjs +23 -18
  182. package/dist-esm/lib/shapes/frame/FrameShapeTool.mjs.map +2 -2
  183. package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs +2 -1
  184. package/dist-esm/lib/shapes/geo/GeoShapeUtil.mjs.map +2 -2
  185. package/dist-esm/lib/shapes/geo/toolStates/Pointing.mjs +4 -0
  186. package/dist-esm/lib/shapes/geo/toolStates/Pointing.mjs.map +2 -2
  187. package/dist-esm/lib/shapes/highlight/HighlightShapeUtil.mjs +12 -7
  188. package/dist-esm/lib/shapes/highlight/HighlightShapeUtil.mjs.map +2 -2
  189. package/dist-esm/lib/shapes/image/ImageShapeUtil.mjs +3 -11
  190. package/dist-esm/lib/shapes/image/ImageShapeUtil.mjs.map +2 -2
  191. package/dist-esm/lib/shapes/line/toolStates/Pointing.mjs +6 -1
  192. package/dist-esm/lib/shapes/line/toolStates/Pointing.mjs.map +2 -2
  193. package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs +32 -42
  194. package/dist-esm/lib/shapes/note/NoteShapeUtil.mjs.map +2 -2
  195. package/dist-esm/lib/shapes/note/noteHelpers.mjs +1 -1
  196. package/dist-esm/lib/shapes/note/noteHelpers.mjs.map +2 -2
  197. package/dist-esm/lib/shapes/note/toolStates/Pointing.mjs +29 -6
  198. package/dist-esm/lib/shapes/note/toolStates/Pointing.mjs.map +2 -2
  199. package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs +3 -2
  200. package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs.map +2 -2
  201. package/dist-esm/lib/shapes/shared/RichTextLabel.mjs +4 -3
  202. package/dist-esm/lib/shapes/shared/RichTextLabel.mjs.map +2 -2
  203. package/dist-esm/lib/shapes/shared/defaultStyleDefs.mjs +17 -3
  204. package/dist-esm/lib/shapes/shared/defaultStyleDefs.mjs.map +2 -2
  205. package/dist-esm/lib/shapes/shared/freehand/core.mjs +404 -0
  206. package/dist-esm/lib/shapes/shared/freehand/core.mjs.map +7 -0
  207. package/dist-esm/lib/shapes/shared/freehand/fmt.mjs +86 -0
  208. package/dist-esm/lib/shapes/shared/freehand/fmt.mjs.map +7 -0
  209. package/dist-esm/lib/shapes/shared/freehand/getStroke.mjs +7 -7
  210. package/dist-esm/lib/shapes/shared/freehand/getStroke.mjs.map +2 -2
  211. package/dist-esm/lib/shapes/shared/freehand/getStrokeOutlinePoints.mjs +332 -86
  212. package/dist-esm/lib/shapes/shared/freehand/getStrokeOutlinePoints.mjs.map +3 -3
  213. package/dist-esm/lib/shapes/shared/freehand/getStrokePoints.mjs +31 -96
  214. package/dist-esm/lib/shapes/shared/freehand/getStrokePoints.mjs.map +3 -3
  215. package/dist-esm/lib/shapes/shared/freehand/svg.mjs +55 -19
  216. package/dist-esm/lib/shapes/shared/freehand/svg.mjs.map +2 -2
  217. package/dist-esm/lib/shapes/shared/freehand/svgInk.mjs +214 -99
  218. package/dist-esm/lib/shapes/shared/freehand/svgInk.mjs.map +3 -3
  219. package/dist-esm/lib/shapes/shared/interpolate-props.mjs +6 -2
  220. package/dist-esm/lib/shapes/shared/interpolate-props.mjs.map +2 -2
  221. package/dist-esm/lib/shapes/text/toolStates/Pointing.mjs +4 -0
  222. package/dist-esm/lib/shapes/text/toolStates/Pointing.mjs.map +2 -2
  223. package/dist-esm/lib/tools/HandTool/HandTool.mjs +16 -6
  224. package/dist-esm/lib/tools/HandTool/HandTool.mjs.map +2 -2
  225. package/dist-esm/lib/tools/HandTool/childStates/Dragging.mjs +10 -1
  226. package/dist-esm/lib/tools/HandTool/childStates/Dragging.mjs.map +2 -2
  227. package/dist-esm/lib/tools/HandTool/childStates/OneFingerZooming.mjs +64 -0
  228. package/dist-esm/lib/tools/HandTool/childStates/OneFingerZooming.mjs.map +7 -0
  229. package/dist-esm/lib/tools/SelectTool/childStates/Idle.mjs +12 -0
  230. package/dist-esm/lib/tools/SelectTool/childStates/Idle.mjs.map +2 -2
  231. package/dist-esm/lib/tools/SelectTool/childStates/PointingShape.mjs +4 -0
  232. package/dist-esm/lib/tools/SelectTool/childStates/PointingShape.mjs.map +2 -2
  233. package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs +108 -46
  234. package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs.map +2 -2
  235. package/dist-esm/lib/ui/TldrawUi.mjs +4 -3
  236. package/dist-esm/lib/ui/TldrawUi.mjs.map +2 -2
  237. package/dist-esm/lib/ui/components/A11y.mjs +2 -0
  238. package/dist-esm/lib/ui/components/A11y.mjs.map +2 -2
  239. package/dist-esm/lib/ui/components/ContextMenu/DefaultContextMenu.mjs +21 -2
  240. package/dist-esm/lib/ui/components/ContextMenu/DefaultContextMenu.mjs.map +2 -2
  241. package/dist-esm/lib/ui/components/ContextMenu/DefaultContextMenuContent.mjs +0 -6
  242. package/dist-esm/lib/ui/components/ContextMenu/DefaultContextMenuContent.mjs.map +2 -2
  243. package/dist-esm/lib/ui/components/DefaultFollowingIndicator.mjs.map +2 -2
  244. package/dist-esm/lib/ui/components/Dialogs.mjs +19 -39
  245. package/dist-esm/lib/ui/components/Dialogs.mjs.map +2 -2
  246. package/dist-esm/lib/ui/components/HelperButtons/BackToContent.mjs +20 -5
  247. package/dist-esm/lib/ui/components/HelperButtons/BackToContent.mjs.map +2 -2
  248. package/dist-esm/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.mjs +12 -2
  249. package/dist-esm/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.mjs.map +2 -2
  250. package/dist-esm/lib/ui/components/Minimap/DefaultMinimap.mjs +47 -70
  251. package/dist-esm/lib/ui/components/Minimap/DefaultMinimap.mjs.map +2 -2
  252. package/dist-esm/lib/ui/components/Minimap/MinimapManager.mjs +25 -46
  253. package/dist-esm/lib/ui/components/Minimap/MinimapManager.mjs.map +2 -2
  254. package/dist-esm/lib/ui/components/SharePanel/DefaultPeopleMenuAvatar.mjs.map +2 -2
  255. package/dist-esm/lib/ui/components/SharePanel/DefaultPeopleMenuContent.mjs.map +2 -2
  256. package/dist-esm/lib/ui/components/SharePanel/DefaultPeopleMenuFacePile.mjs.map +2 -2
  257. package/dist-esm/lib/ui/components/SharePanel/DefaultPeopleMenuItem.mjs +1 -1
  258. package/dist-esm/lib/ui/components/SharePanel/DefaultPeopleMenuItem.mjs.map +2 -2
  259. package/dist-esm/lib/ui/components/StylePanel/StylePanelButtonPicker.mjs +1 -0
  260. package/dist-esm/lib/ui/components/StylePanel/StylePanelButtonPicker.mjs.map +2 -2
  261. package/dist-esm/lib/ui/components/menu-items.mjs +7 -0
  262. package/dist-esm/lib/ui/components/menu-items.mjs.map +2 -2
  263. package/dist-esm/lib/ui/components/primitives/TldrawUiDropdownMenu.mjs +2 -1
  264. package/dist-esm/lib/ui/components/primitives/TldrawUiDropdownMenu.mjs.map +2 -2
  265. package/dist-esm/lib/ui/components/primitives/TldrawUiIcon.mjs +14 -3
  266. package/dist-esm/lib/ui/components/primitives/TldrawUiIcon.mjs.map +2 -2
  267. package/dist-esm/lib/ui/components/primitives/TldrawUiInput.mjs +2 -0
  268. package/dist-esm/lib/ui/components/primitives/TldrawUiInput.mjs.map +2 -2
  269. package/dist-esm/lib/ui/components/primitives/TldrawUiPopover.mjs +2 -0
  270. package/dist-esm/lib/ui/components/primitives/TldrawUiPopover.mjs.map +2 -2
  271. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.mjs +0 -2
  272. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.mjs.map +2 -2
  273. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuSubmenu.mjs +1 -2
  274. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuSubmenu.mjs.map +2 -2
  275. package/dist-esm/lib/ui/context/actions.mjs +71 -1
  276. package/dist-esm/lib/ui/context/actions.mjs.map +2 -2
  277. package/dist-esm/lib/ui/context/asset-urls.mjs +5 -8
  278. package/dist-esm/lib/ui/context/asset-urls.mjs.map +2 -2
  279. package/dist-esm/lib/ui/context/events.mjs.map +2 -2
  280. package/dist-esm/lib/ui/hooks/useKeyboardShortcuts.mjs +13 -0
  281. package/dist-esm/lib/ui/hooks/useKeyboardShortcuts.mjs.map +2 -2
  282. package/dist-esm/lib/ui/hooks/useTools.mjs +1 -1
  283. package/dist-esm/lib/ui/hooks/useTools.mjs.map +2 -2
  284. package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs +2 -0
  285. package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs.map +2 -2
  286. package/dist-esm/lib/ui/version.mjs +3 -3
  287. package/dist-esm/lib/ui/version.mjs.map +1 -1
  288. package/dist-esm/lib/utils/embeds/embeds.mjs +4 -4
  289. package/dist-esm/lib/utils/embeds/embeds.mjs.map +2 -2
  290. package/dist-esm/lib/utils/test-helpers.mjs +2 -2
  291. package/dist-esm/lib/utils/test-helpers.mjs.map +2 -2
  292. package/dist-esm/lib/utils/text/richText.mjs +3 -1
  293. package/dist-esm/lib/utils/text/richText.mjs.map +2 -2
  294. package/package.json +11 -6
  295. package/src/index.ts +6 -1
  296. package/src/lib/TldrawImage.tsx +2 -2
  297. package/src/lib/bindings/arrow/ArrowBindingUtil.ts +5 -1
  298. package/src/lib/defaultEmbedDefinitions.ts +28 -6
  299. package/src/lib/defaultOverlayUtils.ts +11 -11
  300. package/src/lib/overlays/SelectionForegroundOverlayUtil.ts +91 -72
  301. package/src/lib/overlays/ShapeHandleOverlayUtil.ts +3 -1
  302. package/src/lib/overlays/ShapeIndicatorOverlayUtil.ts +133 -0
  303. package/src/lib/shapes/arrow/toolStates/Pointing.tsx +11 -1
  304. package/src/lib/shapes/bookmark/BookmarkShapeUtil.tsx +16 -3
  305. package/src/lib/shapes/bookmark/bookmarks.ts +105 -15
  306. package/src/lib/shapes/draw/DrawShapeUtil.tsx +7 -5
  307. package/src/lib/shapes/draw/getPath.ts +1 -1
  308. package/src/lib/shapes/draw/toolStates/Drawing.ts +55 -48
  309. package/src/lib/shapes/embed/EmbedShapeUtil.tsx +14 -4
  310. package/src/lib/shapes/frame/FrameShapeTool.ts +35 -24
  311. package/src/lib/shapes/geo/GeoShapeUtil.test.tsx +46 -0
  312. package/src/lib/shapes/geo/GeoShapeUtil.tsx +2 -1
  313. package/src/lib/shapes/geo/toolStates/Pointing.ts +5 -0
  314. package/src/lib/shapes/highlight/HighlightShapeUtil.tsx +14 -11
  315. package/src/lib/shapes/image/ImageShapeUtil.tsx +7 -11
  316. package/src/lib/shapes/line/LineShapeTool.test.ts +41 -0
  317. package/src/lib/shapes/line/toolStates/Pointing.ts +12 -2
  318. package/src/lib/shapes/note/NoteShapeUtil.tsx +36 -51
  319. package/src/lib/shapes/note/noteCloning.test.ts +5 -5
  320. package/src/lib/shapes/note/noteHelpers.ts +1 -1
  321. package/src/lib/shapes/note/toolStates/Pointing.ts +40 -7
  322. package/src/lib/shapes/shared/PlainTextLabel.tsx +3 -3
  323. package/src/lib/shapes/shared/RichTextLabel.tsx +4 -3
  324. package/src/lib/shapes/shared/defaultStyleDefs.tsx +25 -3
  325. package/src/lib/shapes/shared/freehand/core.ts +547 -0
  326. package/src/lib/shapes/shared/freehand/fmt.ts +106 -0
  327. package/src/lib/shapes/shared/freehand/getStroke.ts +7 -7
  328. package/src/lib/shapes/shared/freehand/getStrokeOutlinePoints.ts +451 -139
  329. package/src/lib/shapes/shared/freehand/getStrokePoints.ts +33 -153
  330. package/src/lib/shapes/shared/freehand/svg.ts +72 -23
  331. package/src/lib/shapes/shared/freehand/svgInk.ts +281 -136
  332. package/src/lib/shapes/shared/freehand/types.ts +0 -1
  333. package/src/lib/shapes/shared/interpolate-props.ts +6 -2
  334. package/src/lib/shapes/text/toolStates/Pointing.ts +5 -0
  335. package/src/lib/tools/HandTool/HandTool.ts +19 -6
  336. package/src/lib/tools/HandTool/childStates/Dragging.ts +17 -1
  337. package/src/lib/tools/HandTool/childStates/OneFingerZooming.ts +79 -0
  338. package/src/lib/tools/SelectTool/childStates/Idle.ts +16 -2
  339. package/src/lib/tools/SelectTool/childStates/PointingShape.ts +8 -0
  340. package/src/lib/tools/SelectTool/childStates/Resizing.ts +137 -49
  341. package/src/lib/ui/TldrawUi.tsx +3 -3
  342. package/src/lib/ui/components/A11y.tsx +2 -0
  343. package/src/lib/ui/components/ContextMenu/DefaultContextMenu.tsx +32 -2
  344. package/src/lib/ui/components/ContextMenu/DefaultContextMenuContent.tsx +5 -6
  345. package/src/lib/ui/components/DefaultFollowingIndicator.tsx +2 -2
  346. package/src/lib/ui/components/Dialogs.tsx +20 -23
  347. package/src/lib/ui/components/HelperButtons/BackToContent.tsx +32 -4
  348. package/src/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.tsx +10 -0
  349. package/src/lib/ui/components/Minimap/DefaultMinimap.tsx +60 -74
  350. package/src/lib/ui/components/Minimap/MinimapManager.ts +35 -57
  351. package/src/lib/ui/components/SharePanel/DefaultPeopleMenuAvatar.tsx +2 -2
  352. package/src/lib/ui/components/SharePanel/DefaultPeopleMenuContent.tsx +2 -1
  353. package/src/lib/ui/components/SharePanel/DefaultPeopleMenuFacePile.tsx +2 -1
  354. package/src/lib/ui/components/SharePanel/DefaultPeopleMenuItem.tsx +3 -3
  355. package/src/lib/ui/components/StylePanel/StylePanelButtonPicker.tsx +5 -0
  356. package/src/lib/ui/components/menu-items.tsx +9 -0
  357. package/src/lib/ui/components/primitives/TldrawUiDropdownMenu.tsx +3 -1
  358. package/src/lib/ui/components/primitives/TldrawUiIcon.tsx +15 -3
  359. package/src/lib/ui/components/primitives/TldrawUiInput.tsx +4 -0
  360. package/src/lib/ui/components/primitives/TldrawUiPopover.tsx +7 -0
  361. package/src/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.tsx +0 -2
  362. package/src/lib/ui/components/primitives/menus/TldrawUiMenuSubmenu.tsx +0 -1
  363. package/src/lib/ui/context/actions.tsx +88 -1
  364. package/src/lib/ui/context/asset-urls.tsx +9 -8
  365. package/src/lib/ui/context/events.tsx +2 -0
  366. package/src/lib/ui/hooks/useKeyboardShortcuts.ts +45 -7
  367. package/src/lib/ui/hooks/useTools.tsx +7 -1
  368. package/src/lib/ui/hooks/useTranslation/TLUiTranslationKey.ts +2 -0
  369. package/src/lib/ui/hooks/useTranslation/defaultTranslation.ts +2 -0
  370. package/src/lib/ui/version.ts +3 -3
  371. package/src/lib/ui.css +20 -9
  372. package/src/lib/utils/embeds/embeds.test.ts +14 -12
  373. package/src/lib/utils/embeds/embeds.ts +10 -4
  374. package/src/lib/utils/test-helpers.ts +3 -3
  375. package/src/lib/utils/text/richText.test.ts +36 -0
  376. package/src/lib/utils/text/richText.ts +9 -1
  377. package/src/test/HandTool.test.ts +93 -0
  378. package/src/test/ImageShapeUtil.test.ts +88 -0
  379. package/src/test/SelectTool.test.ts +102 -1
  380. package/src/test/__snapshots__/drawing.test.ts.snap +2 -1
  381. package/src/test/attribution.test.ts +40 -29
  382. package/src/test/bookmark-shapes.test.ts +256 -28
  383. package/src/test/commands/__snapshots__/getSvgString.test.ts.snap +2 -2
  384. package/src/test/commands/clipboardCallbacks.test.ts +3 -1
  385. package/src/test/commands/getSvgString.test.ts +30 -0
  386. package/src/test/commands/penmode.test.ts +97 -1
  387. package/src/test/commands/setCamera.test.ts +12 -0
  388. package/src/test/dragFromToolbar.test.ts +67 -0
  389. package/src/test/drawing.test.ts +48 -7
  390. package/src/test/frames.test.ts +72 -0
  391. package/src/test/freehand/corpus/presets.ts +71 -0
  392. package/src/test/freehand/corpus/real.ts +370 -0
  393. package/src/test/freehand/corpus/strokes.ts +50 -0
  394. package/src/test/freehand/svg.test.ts +20 -0
  395. package/src/test/freehand/wellformed.test.ts +30 -0
  396. package/src/test/long-press-create.test.ts +102 -0
  397. package/src/test/overlays/CollaboratorBrushOverlayUtil.test.ts +4 -4
  398. package/src/test/overlays/CollaboratorCursorOverlayUtil.test.ts +18 -18
  399. package/src/test/overlays/CollaboratorHintOverlayUtil.test.ts +7 -7
  400. package/src/test/overlays/CollaboratorScribbleOverlayUtil.test.ts +3 -3
  401. package/src/test/overlays/OverlayManager.test.ts +1 -1
  402. package/src/test/overlays/SelectionForegroundOverlayUtil.test.ts +58 -0
  403. package/src/test/overlays/ShapeHandleOverlayUtil.test.ts +2 -2
  404. package/src/test/overlays/ShapeIndicatorOverlayUtil.test.ts +36 -0
  405. package/src/test/resizing.test.ts +250 -0
  406. package/src/test/right-click-context-menu.test.ts +90 -0
  407. package/src/test/rightClickPanning.test.ts +11 -0
  408. package/src/test/selection-omnibus.test.ts +15 -2
  409. package/src/test/spatialIndex.test.ts +132 -0
  410. package/src/test/translating.test.ts +0 -6
  411. package/src/test/ui/ContextMenu.test.tsx +146 -3
  412. package/src/test/ui/Dialogs.test.tsx +256 -0
  413. package/src/test/ui/copyHoveredStyles.test.tsx +154 -0
  414. package/src/test/ui/keyboardShortcuts.test.tsx +266 -0
  415. package/tldraw.css +35 -12
  416. package/dist-cjs/lib/shapes/shared/freehand/setStrokePointRadii.js +0 -100
  417. package/dist-cjs/lib/shapes/shared/freehand/setStrokePointRadii.js.map +0 -7
  418. package/dist-esm/lib/shapes/shared/freehand/setStrokePointRadii.mjs +0 -80
  419. package/dist-esm/lib/shapes/shared/freehand/setStrokePointRadii.mjs.map +0 -7
  420. package/src/lib/shapes/shared/freehand/setStrokePointRadii.ts +0 -127
@@ -12,8 +12,10 @@ export function DefaultKeyboardShortcutsDialogContent() {
12
12
  <TldrawUiMenuGroup label="shortcuts-dialog.tools" id="tools">
13
13
  <TldrawUiMenuActionItem actionId="toggle-tool-lock" />
14
14
  <TldrawUiMenuActionItem actionId="insert-media" />
15
+ <TldrawUiMenuActionItem actionId="insert-embed" />
15
16
  <TldrawUiMenuToolItem toolId="select" />
16
17
  <TldrawUiMenuToolItem toolId="draw" />
18
+ <TldrawUiMenuToolItem toolId="highlight" />
17
19
  <TldrawUiMenuToolItem toolId="eraser" />
18
20
  <TldrawUiMenuToolItem toolId="hand" />
19
21
  <TldrawUiMenuToolItem toolId="rectangle" />
@@ -43,10 +45,13 @@ export function DefaultKeyboardShortcutsDialogContent() {
43
45
  <TldrawUiMenuActionItem actionId="redo" />
44
46
  <TldrawUiMenuActionItem actionId="cut" />
45
47
  <TldrawUiMenuActionItem actionId="copy" />
48
+ <TldrawUiMenuActionItem actionId="copy-as-png" />
49
+ <TldrawUiMenuActionItem actionId="copy-hovered-styles" />
46
50
  <TldrawUiMenuActionItem actionId="paste" />
47
51
  <TldrawUiMenuActionItem actionId="select-all" />
48
52
  <TldrawUiMenuActionItem actionId="delete" />
49
53
  <TldrawUiMenuActionItem actionId="duplicate" />
54
+ <TldrawUiMenuActionItem actionId="print" />
50
55
  </TldrawUiMenuGroup>
51
56
  <TldrawUiMenuGroup label="shortcuts-dialog.view" id="view">
52
57
  <TldrawUiMenuActionItem actionId="select-zoom-tool" />
@@ -71,6 +76,9 @@ export function DefaultKeyboardShortcutsDialogContent() {
71
76
  <TldrawUiMenuActionItem actionId="send-to-back" />
72
77
  <TldrawUiMenuActionItem actionId="group" />
73
78
  <TldrawUiMenuActionItem actionId="ungroup" />
79
+ <TldrawUiMenuActionItem actionId="frame-selection" />
80
+ <TldrawUiMenuActionItem actionId="flatten-to-image" />
81
+ <TldrawUiMenuActionItem actionId="toggle-lock" />
74
82
  <TldrawUiMenuActionItem actionId="flip-horizontal" />
75
83
  <TldrawUiMenuActionItem actionId="flip-vertical" />
76
84
  <TldrawUiMenuActionItem actionId="align-top" />
@@ -79,6 +87,8 @@ export function DefaultKeyboardShortcutsDialogContent() {
79
87
  <TldrawUiMenuActionItem actionId="align-left" />
80
88
  <TldrawUiMenuActionItem actionId="align-center-horizontal" />
81
89
  <TldrawUiMenuActionItem actionId="align-right" />
90
+ <TldrawUiMenuActionItem actionId="distribute-horizontal" />
91
+ <TldrawUiMenuActionItem actionId="distribute-vertical" />
82
92
  </TldrawUiMenuGroup>
83
93
  <TldrawUiMenuGroup label="shortcuts-dialog.text-formatting" id="text">
84
94
  <TldrawUiMenuItem
@@ -15,6 +15,11 @@ import * as React from 'react'
15
15
  import { useTranslation } from '../../hooks/useTranslation/useTranslation'
16
16
  import { MinimapManager } from './MinimapManager'
17
17
 
18
+ // Squared distance (in screen pixels) the pointer can move after pointer down before
19
+ // it counts as a drag. Sub-pixel jitter that accompanies a click stays below this, so
20
+ // it doesn't recenter the camera instantly and cut off the easing animation.
21
+ const CLICK_JITTER_THRESHOLD_SQ = 4
22
+
18
23
  /** @public @react */
19
24
  export function DefaultMinimap() {
20
25
  const editor = useEditor()
@@ -24,6 +29,7 @@ export function DefaultMinimap() {
24
29
  const rCanvas = React.useRef<HTMLCanvasElement>(null!)
25
30
  const rPointing = React.useRef(false)
26
31
  const rActivePointerId = React.useRef<number | null>(null)
32
+ const rOriginScreenPoint = React.useRef(new Vec())
27
33
 
28
34
  const minimapRef = React.useRef<MinimapManager | undefined>(undefined)
29
35
 
@@ -47,25 +53,18 @@ export function DefaultMinimap() {
47
53
  const onDoubleClick = React.useCallback(
48
54
  (e: React.MouseEvent<HTMLCanvasElement>) => {
49
55
  if (!editor.getCurrentPageShapeIds().size) return
50
- if (!minimapRef.current) return
51
-
52
- const point = minimapRef.current.minimapScreenPointToPagePoint(
53
- e.clientX,
54
- e.clientY,
55
- false,
56
- false
57
- )
56
+ const minimap = minimapRef.current
57
+ if (!minimap) return
58
58
 
59
- const clampedPoint = minimapRef.current.minimapScreenPointToPagePoint(
60
- e.clientX,
61
- e.clientY,
62
- false,
63
- true
64
- )
59
+ const { clientX: x, clientY: y } = e
65
60
 
66
- minimapRef.current.originPagePoint.setTo(clampedPoint)
67
- minimapRef.current.originPageCenter.setTo(editor.getViewportPageBounds().center)
61
+ // Stash the clamped point / clamped viewport page center
62
+ const clampedPoint = minimap.minimapScreenPointToPagePoint(x, y, false, true)
63
+ minimap.originPagePoint.setTo(clampedPoint)
64
+ minimap.originPageCenter.setTo(editor.getViewportPageBounds().center)
68
65
 
66
+ // Then center on the unclamped point
67
+ const point = minimap.minimapScreenPointToPagePoint(x, y, false, false)
69
68
  editor.centerOnPoint(point, { animation: { duration: editor.options.animationMediumMs } })
70
69
  },
71
70
  [editor]
@@ -73,54 +72,49 @@ export function DefaultMinimap() {
73
72
 
74
73
  const onPointerDown = React.useCallback(
75
74
  (e: React.PointerEvent<HTMLCanvasElement>) => {
76
- if (!minimapRef.current) return
75
+ const minimap = minimapRef.current
76
+ if (!minimap) return
77
77
  if (e.button !== 0) return
78
78
 
79
79
  const elm = e.currentTarget
80
80
  setPointerCapture(elm, e)
81
81
  if (!editor.getCurrentPageShapeIds().size) return
82
82
 
83
+ const { clientX: x, clientY: y } = e
84
+
83
85
  rPointing.current = true
84
86
  rActivePointerId.current = e.pointerId
87
+ rOriginScreenPoint.current = new Vec(x, y)
85
88
 
86
- minimapRef.current.isInViewport = false
89
+ minimap.isInViewport = false
87
90
 
88
- const point = minimapRef.current.minimapScreenPointToPagePoint(
89
- e.clientX,
90
- e.clientY,
91
- false,
92
- false
93
- )
91
+ const point = minimap.minimapScreenPointToPagePoint(x, y, false, false)
94
92
 
95
- const _vpPageBounds = editor.getViewportPageBounds()
96
- const commonBounds = minimapRef.current.getContentPageBounds()
93
+ const vpPageBounds = editor.getViewportPageBounds()
94
+ const commonBounds = minimap.getContentPageBounds()
97
95
  const allowedBounds = new Box(
98
- commonBounds.x - _vpPageBounds.width / 2,
99
- commonBounds.y - _vpPageBounds.height / 2,
100
- commonBounds.width + _vpPageBounds.width,
101
- commonBounds.height + _vpPageBounds.height
96
+ commonBounds.x - vpPageBounds.width / 2,
97
+ commonBounds.y - vpPageBounds.height / 2,
98
+ commonBounds.width + vpPageBounds.width,
99
+ commonBounds.height + vpPageBounds.height
102
100
  )
103
101
 
104
102
  // If we clicked inside of the allowed area, but outside of the viewport
105
- if (allowedBounds.containsPoint(point) && !_vpPageBounds.containsPoint(point)) {
106
- minimapRef.current.isInViewport = _vpPageBounds.containsPoint(point)
107
- const delta = Vec.Sub(_vpPageBounds.center, _vpPageBounds.point)
103
+ if (allowedBounds.containsPoint(point) && !vpPageBounds.containsPoint(point)) {
104
+ const delta = Vec.Sub(vpPageBounds.center, vpPageBounds.point)
108
105
  const pagePoint = Vec.Add(point, delta)
109
- minimapRef.current.originPagePoint.setTo(pagePoint)
110
- minimapRef.current.originPageCenter.setTo(point)
106
+ minimap.originPagePoint.setTo(pagePoint)
107
+ minimap.originPageCenter.setTo(point)
111
108
  editor.centerOnPoint(point, { animation: { duration: editor.options.animationMediumMs } })
112
109
  } else {
113
- const clampedPoint = minimapRef.current.minimapScreenPointToPagePoint(
114
- e.clientX,
115
- e.clientY,
116
- false,
117
- true
118
- )
119
- minimapRef.current.isInViewport = _vpPageBounds.containsPoint(clampedPoint)
120
- minimapRef.current.originPagePoint.setTo(clampedPoint)
121
- minimapRef.current.originPageCenter.setTo(_vpPageBounds.center)
110
+ const clampedPoint = minimap.minimapScreenPointToPagePoint(x, y, false, true)
111
+ minimap.isInViewport = vpPageBounds.containsPoint(clampedPoint)
112
+ minimap.originPagePoint.setTo(clampedPoint)
113
+ minimap.originPageCenter.setTo(vpPageBounds.center)
122
114
  }
123
115
 
116
+ const body = editor.getContainerDocument().body
117
+
124
118
  function endDrag() {
125
119
  if (rActivePointerId.current !== null && elm.hasPointerCapture(rActivePointerId.current)) {
126
120
  elm.releasePointerCapture(rActivePointerId.current)
@@ -128,44 +122,36 @@ export function DefaultMinimap() {
128
122
 
129
123
  rPointing.current = false
130
124
  rActivePointerId.current = null
131
- body.removeEventListener('pointerup', onPointerUp)
132
- body.removeEventListener('pointercancel', onPointerCancel)
133
- body.removeEventListener('contextmenu', onContextMenu, true)
125
+ body.removeEventListener('pointerup', endDrag)
126
+ body.removeEventListener('pointercancel', endDrag)
127
+ body.removeEventListener('contextmenu', endDrag, true)
134
128
  }
135
129
 
136
- function onPointerUp() {
137
- endDrag()
138
- }
139
- function onPointerCancel() {
140
- endDrag()
141
- }
142
- function onContextMenu() {
143
- endDrag()
144
- }
145
-
146
- const body = editor.getContainerDocument().body
147
- body.addEventListener('pointerup', onPointerUp)
148
- body.addEventListener('pointercancel', onPointerCancel)
149
- body.addEventListener('contextmenu', onContextMenu, true)
130
+ body.addEventListener('pointerup', endDrag)
131
+ body.addEventListener('pointercancel', endDrag)
132
+ body.addEventListener('contextmenu', endDrag, true)
150
133
  },
151
134
  [editor]
152
135
  )
153
136
 
154
137
  const onPointerMove = React.useCallback(
155
138
  (e: React.PointerEvent<HTMLCanvasElement>) => {
156
- if (!minimapRef.current) return
157
- const point = minimapRef.current.minimapScreenPointToPagePoint(
158
- e.clientX,
159
- e.clientY,
160
- e.shiftKey,
161
- true
162
- )
139
+ const minimap = minimapRef.current
140
+ if (!minimap) return
141
+
142
+ const { clientX: x, clientY: y } = e
143
+
144
+ const point = minimap.minimapScreenPointToPagePoint(x, y, e.shiftKey, true)
163
145
 
164
146
  if (rPointing.current) {
165
- if (minimapRef.current.isInViewport) {
166
- const delta = minimapRef.current.originPagePoint
167
- .clone()
168
- .sub(minimapRef.current.originPageCenter)
147
+ // Ignore the sub-pixel pointer jitter that accompanies a click, so it doesn't
148
+ // cut off the easing animation started on pointer down.
149
+ if (Vec.Dist2(rOriginScreenPoint.current, new Vec(x, y)) <= CLICK_JITTER_THRESHOLD_SQ) {
150
+ return
151
+ }
152
+
153
+ if (minimap.isInViewport) {
154
+ const delta = minimap.originPagePoint.clone().sub(minimap.originPageCenter)
169
155
  editor.centerOnPoint(Vec.Sub(point, delta))
170
156
  return
171
157
  }
@@ -173,7 +159,7 @@ export function DefaultMinimap() {
173
159
  editor.centerOnPoint(point)
174
160
  }
175
161
 
176
- const pagePoint = minimapRef.current.getMinimapPagePoint(e.clientX, e.clientY)
162
+ const pagePoint = minimap.getMinimapPagePoint(x, y)
177
163
 
178
164
  const screenPoint = editor.pageToScreen(pagePoint)
179
165
 
@@ -225,10 +211,10 @@ export function DefaultMinimap() {
225
211
  return (
226
212
  <div className="tlui-minimap">
227
213
  <canvas
214
+ ref={rCanvas}
228
215
  role="img"
229
216
  aria-label={msg('navigation-zone.minimap')}
230
217
  data-testid="minimap.canvas"
231
- ref={rCanvas}
232
218
  className="tlui-minimap__canvas"
233
219
  onDoubleClick={onDoubleClick}
234
220
  onPointerMove={onPointerMove}
@@ -17,7 +17,7 @@ export class MinimapManager {
17
17
 
18
18
  @bind
19
19
  close() {
20
- return this.disposables.forEach((d) => d())
20
+ this.disposables.forEach((d) => d())
21
21
  }
22
22
 
23
23
  private readonly ctx: CanvasRenderingContext2D
@@ -78,16 +78,6 @@ export class MinimapManager {
78
78
  : viewportPageBounds
79
79
  }
80
80
 
81
- @computed
82
- getContentScreenBounds() {
83
- const contentPageBounds = this.getContentPageBounds()
84
- const topLeft = this.editor.pageToScreen(contentPageBounds.point)
85
- const bottomRight = this.editor.pageToScreen(
86
- new Vec(contentPageBounds.maxX, contentPageBounds.maxY)
87
- )
88
- return new Box(topLeft.x, topLeft.y, bottomRight.x - topLeft.x, bottomRight.y - topLeft.y)
89
- }
90
-
91
81
  private _getCanvasBoundingRect() {
92
82
  const { x, y, width, height } = this.elem.getBoundingClientRect()
93
83
  return new Box(x, y, width, height)
@@ -116,11 +106,6 @@ export class MinimapManager {
116
106
  return new Vec(rect.width * dpr, rect.height * dpr)
117
107
  }
118
108
 
119
- @computed
120
- getCanvasClientPosition() {
121
- return this.canvasBoundingClientRect.get().point
122
- }
123
-
124
109
  originPagePoint = new Vec()
125
110
  originPageCenter = new Vec()
126
111
 
@@ -209,6 +194,29 @@ export class MinimapManager {
209
194
  return new Vec(px, py)
210
195
  }
211
196
 
197
+ // Shape geometry in page space, split into unselected/selected fills. Built
198
+ // from the per-shape rect cache and selection only, so it survives pan/zoom of
199
+ // the main canvas — those move the `ctx` transform, not the paths themselves.
200
+ @computed
201
+ private getShapePaths() {
202
+ const { editor } = this
203
+ const selectedIds = new Set<string>(editor.getSelectedShapeIds())
204
+ const ids = editor.getCurrentPageShapeIdsSorted()
205
+
206
+ const shapes = new Path2D()
207
+ const selected = new Path2D()
208
+
209
+ for (let i = 0, len = ids.length; i < len; i++) {
210
+ const shapeId = ids[i]
211
+ const bounds = this.shapeRectCache.get(shapeId)
212
+ if (!bounds) continue
213
+ const target = selectedIds.has(shapeId) ? selected : shapes
214
+ target.rect(bounds.x, bounds.y, bounds.w, bounds.h)
215
+ }
216
+
217
+ return { shapes, selected }
218
+ }
219
+
212
220
  @bind
213
221
  render() {
214
222
  const { ctx, editor, elem } = this
@@ -234,21 +242,9 @@ export class MinimapManager {
234
242
  ctx.scale(dpr * zoom, dpr * zoom)
235
243
  ctx.translate(-canvasPageBounds.minX, -canvasPageBounds.minY)
236
244
 
237
- // Shapes — iterate sorted IDs and pull per-shape rects from the
238
- // computed cache so only *changed* shapes re-derive between renders.
239
- const selectedIds = new Set<string>(editor.getSelectedShapeIds())
240
- const ids = editor.getCurrentPageShapeIdsSorted()
241
-
242
- const shapesPath = new Path2D()
243
- const selectedPath = new Path2D()
244
-
245
- for (let i = 0, len = ids.length; i < len; i++) {
246
- const shapeId = ids[i]
247
- const bounds = this.shapeRectCache.get(shapeId)
248
- if (!bounds) continue
249
- const target = selectedIds.has(shapeId) ? selectedPath : shapesPath
250
- target.rect(bounds.x, bounds.y, bounds.w, bounds.h)
251
- }
245
+ // Shapes — page-space paths from a computed, so a pan/zoom that only moves
246
+ // the transform above reuses them instead of rebuilding per shape.
247
+ const { shapes: shapesPath, selected: selectedPath } = this.getShapePaths()
252
248
 
253
249
  ctx.fillStyle = this.colors.shapeFill
254
250
  ctx.fill(shapesPath)
@@ -256,13 +252,18 @@ export class MinimapManager {
256
252
  ctx.fillStyle = this.colors.selectFill
257
253
  ctx.fill(selectedPath)
258
254
 
259
- // Viewport rounded rect
255
+ // Viewport indicator. roundRect is pricier than rect, so when the corner
256
+ // radius would be sub-pixel on screen (and thus invisible) fall back to a
257
+ // plain rect.
260
258
  const viewport = editor.getViewportPageBounds()
261
259
  const { minX: vx, minY: vy, width: vw, height: vh } = viewport
262
260
  const r = Math.min(vw / 4, vh / 4, 4 / zoom)
263
261
  ctx.beginPath()
264
- roundedRect(ctx, vx, vy, vw, vh, r)
265
- ctx.closePath()
262
+ if (r * zoom < 1) {
263
+ ctx.rect(vx, vy, vw, vh)
264
+ } else {
265
+ ctx.roundRect(vx, vy, vw, vh, r)
266
+ }
266
267
  ctx.fillStyle = this.colors.viewportFill
267
268
  ctx.fill()
268
269
 
@@ -276,26 +277,3 @@ export class MinimapManager {
276
277
  }
277
278
  }
278
279
  }
279
-
280
- function roundedRect(
281
- ctx: CanvasRenderingContext2D | Path2D,
282
- x: number,
283
- y: number,
284
- w: number,
285
- h: number,
286
- r: number
287
- ) {
288
- if (r < 1) {
289
- ctx.rect(x, y, w, h)
290
- return
291
- }
292
- ctx.moveTo(x + r, y)
293
- ctx.lineTo(x + w - r, y)
294
- ctx.quadraticCurveTo(x + w, y, x + w, y + r)
295
- ctx.lineTo(x + w, y + h - r)
296
- ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h)
297
- ctx.lineTo(x + r, y + h)
298
- ctx.quadraticCurveTo(x, y + h, x, y + h - r)
299
- ctx.lineTo(x, y + r)
300
- ctx.quadraticCurveTo(x, y, x + r, y)
301
- }
@@ -1,8 +1,8 @@
1
- import { usePresence } from '@tldraw/editor'
1
+ import { TLUserId, usePresence } from '@tldraw/editor'
2
2
 
3
3
  /** @public */
4
4
  export interface TLUiPeopleMenuAvatarProps {
5
- userId: string
5
+ userId: TLUserId
6
6
  }
7
7
 
8
8
  /** @public @react */
@@ -1,8 +1,9 @@
1
+ import { TLUserId } from '@tldraw/editor'
1
2
  import { useTldrawUiComponents } from '../../context/components'
2
3
 
3
4
  /** @public */
4
5
  export interface DefaultPeopleMenuContentProps {
5
- userIds: string[]
6
+ userIds: TLUserId[]
6
7
  }
7
8
 
8
9
  /** @public @react */
@@ -1,10 +1,11 @@
1
+ import { TLUserId } from '@tldraw/editor'
1
2
  import { PORTRAIT_BREAKPOINT } from '../../constants'
2
3
  import { useBreakpoint } from '../../context/breakpoints'
3
4
  import { useTldrawUiComponents } from '../../context/components'
4
5
 
5
6
  /** @public */
6
7
  export interface TLUiPeopleMenuFacePileProps {
7
- userIds: string[]
8
+ userIds: TLUserId[]
8
9
  userName: string
9
10
  userColor: string
10
11
  }
@@ -1,4 +1,4 @@
1
- import { track, useEditor, usePresence } from '@tldraw/editor'
1
+ import { TLUserId, track, useEditor, usePresence } from '@tldraw/editor'
2
2
  import { useCallback } from 'react'
3
3
  import { useUiEvents } from '../../context/events'
4
4
  import { useTranslation } from '../../hooks/useTranslation/useTranslation'
@@ -9,7 +9,7 @@ import { TldrawUiIcon } from '../primitives/TldrawUiIcon'
9
9
 
10
10
  /** @public */
11
11
  export interface TLUiPeopleMenuItemProps {
12
- userId: string
12
+ userId: TLUserId
13
13
  }
14
14
 
15
15
  /** @public @react */
@@ -32,7 +32,7 @@ export const DefaultPeopleMenuItem = track(function DefaultPeopleMenuItem({
32
32
  }
33
33
  }, [editor, userId, trackEvent])
34
34
 
35
- const theyAreFollowingYou = presence?.followingUserId === editor.user.getId()
35
+ const theyAreFollowingYou = presence?.followingUserId === editor.user.getRecordId()
36
36
  const youAreFollowingThem = editor.getInstanceState().followingUserId === userId
37
37
 
38
38
  if (!presence) return null
@@ -123,6 +123,11 @@ function StylePanelButtonPickerInlineInner<T extends string>(
123
123
  }
124
124
 
125
125
  const handleButtonPointerUp = (e: React.PointerEvent<HTMLButtonElement>) => {
126
+ // Only apply on release if the scrub started in this picker. Otherwise
127
+ // releasing over a different style section selects that section's
128
+ // option, even though scrubbing can't cross sections. See #9223.
129
+ if (!rPointing.current) return
130
+
126
131
  const { id } = e.currentTarget.dataset
127
132
  if (value.type === 'shared' && value.value === id) return
128
133
 
@@ -103,6 +103,14 @@ export function UngroupMenuItem() {
103
103
  return <TldrawUiMenuActionItem actionId="ungroup" />
104
104
  }
105
105
 
106
+ /** @public @react */
107
+ export function FrameSelectionMenuItem() {
108
+ const shouldDisplay = useAllowGroup()
109
+ if (!shouldDisplay) return null
110
+
111
+ return <TldrawUiMenuActionItem actionId="frame-selection" />
112
+ }
113
+
106
114
  /** @public @react */
107
115
  export function RemoveFrameMenuItem() {
108
116
  const editor = useEditor()
@@ -343,6 +351,7 @@ export function EditMenuSubmenu() {
343
351
  <GroupMenuItem />
344
352
  <UngroupMenuItem />
345
353
  <FlattenMenuItem />
354
+ <FrameSelectionMenuItem />
346
355
  <EditLinkMenuItem />
347
356
  <FitFrameToContentMenuItem />
348
357
  <RemoveFrameMenuItem />
@@ -68,6 +68,7 @@ export interface TLUiDropdownMenuContentProps {
68
68
  align?: 'start' | 'center' | 'end'
69
69
  sideOffset?: number
70
70
  alignOffset?: number
71
+ collisionPadding?: number
71
72
  children: ReactNode
72
73
  }
73
74
 
@@ -78,6 +79,7 @@ export function TldrawUiDropdownMenuContent({
78
79
  align = 'start',
79
80
  sideOffset = 8,
80
81
  alignOffset = 8,
82
+ collisionPadding = 4,
81
83
  children,
82
84
  }: TLUiDropdownMenuContentProps) {
83
85
  const container = useContainer()
@@ -90,7 +92,7 @@ export function TldrawUiDropdownMenuContent({
90
92
  sideOffset={sideOffset}
91
93
  align={align}
92
94
  alignOffset={alignOffset}
93
- collisionPadding={4}
95
+ collisionPadding={collisionPadding}
94
96
  >
95
97
  {children}
96
98
  </_DropdownMenu.Content>
@@ -11,6 +11,7 @@ export interface TLUiIconProps extends React.HTMLAttributes<HTMLDivElement> {
11
11
  icon: TLUiIconType | Exclude<string, TLUiIconType> | TLUiIconJsx
12
12
  label: string
13
13
  small?: boolean
14
+ tiny?: boolean
14
15
  color?: string
15
16
  children?: undefined
16
17
  invertIcon?: boolean
@@ -23,6 +24,7 @@ export const TldrawUiIcon = memo(function TldrawUiIcon({
23
24
  small,
24
25
  invertIcon,
25
26
  icon,
27
+ tiny,
26
28
  color,
27
29
  className,
28
30
  ...props
@@ -32,6 +34,7 @@ export const TldrawUiIcon = memo(function TldrawUiIcon({
32
34
  <TldrawUIIconInner
33
35
  label={label}
34
36
  small={small}
37
+ tiny={tiny}
35
38
  invertIcon={invertIcon}
36
39
  icon={icon}
37
40
  color={color}
@@ -43,7 +46,11 @@ export const TldrawUiIcon = memo(function TldrawUiIcon({
43
46
 
44
47
  return cloneElement(icon, {
45
48
  ...props,
46
- className: classNames({ 'tlui-icon__small': small }, className, icon.props.className),
49
+ className: classNames(
50
+ { 'tlui-icon__small': small, 'tlui-icon__tiny': tiny },
51
+ className,
52
+ icon.props.className
53
+ ),
47
54
  'aria-label': label,
48
55
  style: {
49
56
  color,
@@ -56,6 +63,7 @@ export const TldrawUiIcon = memo(function TldrawUiIcon({
56
63
  function TldrawUIIconInner({
57
64
  label,
58
65
  small,
66
+ tiny,
59
67
  invertIcon,
60
68
  icon,
61
69
  color,
@@ -85,7 +93,7 @@ function TldrawUIIconInner({
85
93
  <div
86
94
  className={classNames(
87
95
  'tlui-icon tlui-icon__placeholder',
88
- { 'tlui-icon__small': small },
96
+ { 'tlui-icon__small': small, 'tlui-icon__tiny': tiny },
89
97
  className
90
98
  )}
91
99
  {...props}
@@ -99,7 +107,11 @@ function TldrawUIIconInner({
99
107
  ref={ref}
100
108
  aria-label={label}
101
109
  role="img"
102
- className={classNames('tlui-icon', { 'tlui-icon__small': small }, className)}
110
+ className={classNames(
111
+ 'tlui-icon',
112
+ { 'tlui-icon__small': small, 'tlui-icon__tiny': tiny },
113
+ className
114
+ )}
103
115
  style={{
104
116
  color,
105
117
  mask: `url(${asset}) center 100% / 100% no-repeat`,
@@ -17,6 +17,8 @@ export interface TLUiInputProps {
17
17
  autoSelect?: boolean
18
18
  children?: React.ReactNode
19
19
  defaultValue?: string
20
+ /** Maximum number of characters the input will accept. */
21
+ maxLength?: number
20
22
  placeholder?: string
21
23
  onComplete?(value: string): void
22
24
  onValueChange?(value: string): void
@@ -50,6 +52,7 @@ export const TldrawUiInput = React.forwardRef<HTMLInputElement, TLUiInputProps>(
50
52
  autoSelect = false,
51
53
  autoFocus = false,
52
54
  defaultValue,
55
+ maxLength,
53
56
  placeholder,
54
57
  onComplete,
55
58
  onValueChange,
@@ -196,6 +199,7 @@ export const TldrawUiInput = React.forwardRef<HTMLInputElement, TLUiInputProps>(
196
199
  className={classNames('tlui-input', className)}
197
200
  type="text"
198
201
  defaultValue={defaultValue}
202
+ maxLength={maxLength}
199
203
  onKeyDownCapture={handleKeyDownCapture}
200
204
  onChange={handleChange}
201
205
  onFocus={handleFocus}
@@ -47,6 +47,11 @@ export interface TLUiPopoverContentProps {
47
47
  align?: 'start' | 'center' | 'end'
48
48
  alignOffset?: number
49
49
  sideOffset?: number
50
+ /**
51
+ * Minimum distance to keep between the popover and the viewport edge before it
52
+ * shifts to stay in view. Defaults to Radix's `0`.
53
+ */
54
+ collisionPadding?: number
50
55
  disableEscapeKeyDown?: boolean
51
56
  autoFocusFirstButton?: boolean
52
57
  }
@@ -58,6 +63,7 @@ export function TldrawUiPopoverContent({
58
63
  align = 'center',
59
64
  sideOffset = 8,
60
65
  alignOffset = 0,
66
+ collisionPadding,
61
67
  disableEscapeKeyDown = false,
62
68
  autoFocusFirstButton = true,
63
69
  }: TLUiPopoverContentProps) {
@@ -83,6 +89,7 @@ export function TldrawUiPopoverContent({
83
89
  sideOffset={sideOffset}
84
90
  align={align}
85
91
  alignOffset={alignOffset}
92
+ collisionPadding={collisionPadding}
86
93
  dir={dir}
87
94
  ref={ref}
88
95
  onOpenAutoFocus={handleOpenAutoFocus}
@@ -60,7 +60,6 @@ export function TldrawUiMenuCheckboxItem<
60
60
  dir={dir}
61
61
  lang={lang}
62
62
  className="tlui-button tlui-button__menu tlui-button__checkbox"
63
- title={labelStr}
64
63
  onSelect={(e) => {
65
64
  onSelect?.(sourceId)
66
65
  preventDefault(e)
@@ -89,7 +88,6 @@ export function TldrawUiMenuCheckboxItem<
89
88
  className="tlui-button tlui-button__menu tlui-button__checkbox"
90
89
  dir={dir}
91
90
  lang={lang}
92
- title={labelStr}
93
91
  onSelect={(e) => {
94
92
  onSelect(sourceId)
95
93
  preventDefault(e)
@@ -50,7 +50,6 @@ export function TldrawUiMenuSubmenu<Translation extends string = string>({
50
50
  id={`${sourceId}-sub.${id}-button`}
51
51
  disabled={disabled}
52
52
  label={labelStr!}
53
- title={labelStr!}
54
53
  />
55
54
  <TldrawUiDropdownMenuSubContent id={`${sourceId}-sub.${id}-content`} size={size}>
56
55
  {children}