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
@@ -0,0 +1,547 @@
1
+ import { VecLike } from '@tldraw/editor'
2
+ import type { StrokeOptions, StrokePoint } from './types'
3
+
4
+ // The stroke pipeline's internal representation: one module-level set of reusable
5
+ // struct-of-arrays buffers instead of per-point objects. Like fmt.ts's byte buffer, the
6
+ // buffers are non-reentrant: each public entry point fully consumes them before returning,
7
+ // and the next call overwrites them.
8
+ //
9
+ // A stroke point's vector is not stored; it is derived on the fly from consecutive points
10
+ // (the unit vector pointing back at the predecessor). A point's distance is stored rather
11
+ // than derived from running lengths because `runningLength[i] - runningLength[i - 1]` is
12
+ // not bit-identical to the accumulated distance in floating point.
13
+ //
14
+ // Hot loops capture the array bindings into locals: the arrays never grow mid-fill
15
+ // (capacity is ensured up front), and locals avoid re-reading the module binding on
16
+ // every access.
17
+
18
+ const MIN_PRESSURE = 0.025
19
+
20
+ // This is the rate of change for simulated pressure. It could be an option.
21
+ const RATE_OF_PRESSURE_CHANGE = 0.275
22
+
23
+ // Default taper easings, mirroring `EASINGS.easeOutQuad` and `EASINGS.easeOutCubic`
24
+ // from `@tldraw/editor`.
25
+ const easeOutQuad = (t: number) => t * (2 - t)
26
+ const easeOutCubic = (t: number) => --t * t * t + 1
27
+
28
+ const { min } = Math
29
+
30
+ // ---------------------------------------------------------------------------------
31
+ // Pipeline buffers: one slot per stroke point, filled by `ingest`, radii filled in by
32
+ // `computeRadii`. Callers ensure capacity before filling, so growth never copies.
33
+ // ---------------------------------------------------------------------------------
34
+
35
+ let pointCapacity = 256
36
+ /** Streamlined (smoothed) point coordinates. */
37
+ export let pointX = new Float64Array(pointCapacity)
38
+ export let pointY = new Float64Array(pointCapacity)
39
+ /** The original input coordinates (used for elbows and sharp corners). */
40
+ export let inputX = new Float64Array(pointCapacity)
41
+ export let inputY = new Float64Array(pointCapacity)
42
+ /** The input z (pressure channel) after clamping; kept for materializing StrokePoints. */
43
+ export let inputZ = new Float64Array(pointCapacity)
44
+ export let pressures = new Float64Array(pointCapacity)
45
+ export let distances = new Float64Array(pointCapacity)
46
+ export let runningLengths = new Float64Array(pointCapacity)
47
+ export let radii = new Float64Array(pointCapacity)
48
+ export let pointCount = 0
49
+
50
+ function ensurePointCapacity(n: number) {
51
+ if (n <= pointCapacity) return
52
+ while (pointCapacity < n) pointCapacity *= 2
53
+ pointX = new Float64Array(pointCapacity)
54
+ pointY = new Float64Array(pointCapacity)
55
+ inputX = new Float64Array(pointCapacity)
56
+ inputY = new Float64Array(pointCapacity)
57
+ inputZ = new Float64Array(pointCapacity)
58
+ pressures = new Float64Array(pointCapacity)
59
+ distances = new Float64Array(pointCapacity)
60
+ runningLengths = new Float64Array(pointCapacity)
61
+ radii = new Float64Array(pointCapacity)
62
+ }
63
+
64
+ // Staging buffers for the effective input sequence in `ingest` (after stripping
65
+ // near-start/near-end points, the two-point interpolation, and the duplicated last
66
+ // point) — replaces the cloned `pts` array of the object pipeline.
67
+ let stageCapacity = 256
68
+ let stageX = new Float64Array(stageCapacity)
69
+ let stageY = new Float64Array(stageCapacity)
70
+ let stageZ = new Float64Array(stageCapacity)
71
+
72
+ function ensureStageCapacity(n: number) {
73
+ if (n <= stageCapacity) return
74
+ while (stageCapacity < n) stageCapacity *= 2
75
+ stageX = new Float64Array(stageCapacity)
76
+ stageY = new Float64Array(stageCapacity)
77
+ stageZ = new Float64Array(stageCapacity)
78
+ }
79
+
80
+ /** The z of a raw input point as `Vec.From` plus the pressure clamp would produce it. */
81
+ function zOf(p: VecLike, clampZ: boolean): number {
82
+ const z = p.z === undefined ? 1 : p.z
83
+ // Some pens or OSes report z=0 even while the pen is touching, so we clamp rather
84
+ // than strip to avoid removing real input.
85
+ return clampZ && z < MIN_PRESSURE ? MIN_PRESSURE : z
86
+ }
87
+
88
+ /**
89
+ * Phase 1: ingest and streamline raw input points straight into the pipeline buffers.
90
+ * Mirrors what getStrokePoints used to do with per-point objects, keeping every
91
+ * order-sensitive step: the pressure clamp, near-start/near-end stripping, the two-point
92
+ * simulated-pressure interpolation, the early-noise skip, and the short-stroke pressure
93
+ * fixup.
94
+ *
95
+ * @internal
96
+ */
97
+ export function ingest(rawInputPoints: VecLike[], options: StrokeOptions = {}): void {
98
+ const { streamline = 0.5, size = 16, simulatePressure = false } = options
99
+
100
+ pointCount = 0
101
+ const rawLen = rawInputPoints.length
102
+ if (rawLen === 0) return
103
+
104
+ // Find the interpolation level between points.
105
+ const t = 0.15 + (1 - streamline) * 0.85
106
+
107
+ ensureStageCapacity(rawLen + 8)
108
+ ensurePointCapacity(rawLen + 8)
109
+
110
+ const stX = stageX
111
+ const stY = stageY
112
+ const stZ = stageZ
113
+
114
+ const minDist2 = (size / 3) ** 2
115
+ const clampZ = !simulatePressure
116
+
117
+ // Strip points that are too close to the first point, accumulating the maximum
118
+ // pressure among them into the first point.
119
+ const first = rawInputPoints[0]
120
+ let firstZ = zOf(first, clampZ)
121
+ let startIdx = 1
122
+ while (startIdx < rawLen) {
123
+ const pt = rawInputPoints[startIdx]
124
+ const dx = pt.x - first.x
125
+ const dy = pt.y - first.y
126
+ if (dx * dx + dy * dy > minDist2) break
127
+ firstZ = Math.max(firstZ, zOf(pt, clampZ))
128
+ startIdx++
129
+ }
130
+
131
+ // Stage the surviving points.
132
+ stX[0] = first.x
133
+ stY[0] = first.y
134
+ stZ[0] = firstZ
135
+ let m = 1
136
+ for (let i = startIdx; i < rawLen; i++) {
137
+ const pt = rawInputPoints[i]
138
+ stX[m] = pt.x
139
+ stY[m] = pt.y
140
+ stZ[m] = zOf(pt, clampZ)
141
+ m++
142
+ }
143
+
144
+ // Strip points that are too close to the last point. This can consume the whole
145
+ // sequence, leaving just the last point.
146
+ let pointsRemovedFromNearEnd = 0
147
+ if (m > 1) {
148
+ const lastX = stX[m - 1]
149
+ const lastY = stY[m - 1]
150
+ let j = m - 2
151
+ while (j >= 0) {
152
+ const dx = stX[j] - lastX
153
+ const dy = stY[j] - lastY
154
+ if (dx * dx + dy * dy > minDist2) break
155
+ j--
156
+ pointsRemovedFromNearEnd++
157
+ }
158
+ if (j < m - 2) {
159
+ stX[j + 1] = lastX
160
+ stY[j + 1] = lastY
161
+ stZ[j + 1] = stZ[m - 1]
162
+ m = j + 2
163
+ }
164
+ }
165
+
166
+ const isComplete =
167
+ options.last ||
168
+ !options.simulatePressure ||
169
+ (m > 1 &&
170
+ (stX[m - 1] - stX[m - 2]) * (stX[m - 1] - stX[m - 2]) +
171
+ (stY[m - 1] - stY[m - 2]) * (stY[m - 1] - stY[m - 2]) <
172
+ size ** 2) ||
173
+ pointsRemovedFromNearEnd > 0
174
+
175
+ // Add extra points between the two, to help avoid "dash" lines for strokes with
176
+ // tapered start and ends.
177
+ if (m === 2 && options.simulatePressure) {
178
+ const x0 = stX[0]
179
+ const y0 = stY[0]
180
+ const z0 = stZ[0]
181
+ const x1 = stX[1]
182
+ const y1 = stY[1]
183
+ const z1 = stZ[1]
184
+ for (let i = 1; i < 5; i++) {
185
+ const u = i / 4
186
+ stX[i] = x0 + (x1 - x0) * u
187
+ stY[i] = y0 + (y1 - y0) * u
188
+ stZ[i] = ((z0 + (z1 - z0)) * i) / 4
189
+ }
190
+ m = 5
191
+ }
192
+
193
+ const ptX = pointX
194
+ const ptY = pointY
195
+ const inX = inputX
196
+ const inY = inputY
197
+ const inZ = inputZ
198
+ const press = pressures
199
+ const dists = distances
200
+ const runs = runningLengths
201
+ const rads = radii
202
+
203
+ // The first point needs no adjustment.
204
+ ptX[0] = stX[0]
205
+ ptY[0] = stY[0]
206
+ inX[0] = stX[0]
207
+ inY[0] = stY[0]
208
+ inZ[0] = stZ[0]
209
+ press[0] = simulatePressure ? 0.5 : stZ[0]
210
+ dists[0] = 0
211
+ runs[0] = 0
212
+ rads[0] = 1
213
+ let count = 1
214
+
215
+ if (isComplete && streamline > 0) {
216
+ stX[m] = stX[m - 1]
217
+ stY[m] = stY[m - 1]
218
+ stZ[m] = stZ[m - 1]
219
+ m++
220
+ }
221
+
222
+ // We use the totalLength to keep track of the total distance, and prevX/prevY as the
223
+ // latest streamlined point, to calculate the next point's distance.
224
+ let totalLength = 0
225
+ let prevX = stX[0]
226
+ let prevY = stY[0]
227
+ const u = 1 - t
228
+ const isLast = options.last
229
+
230
+ for (let i = 1; i < m; i++) {
231
+ let x: number, y: number
232
+ if (!t || (isLast && i === m - 1)) {
233
+ x = stX[i]
234
+ y = stY[i]
235
+ } else {
236
+ x = stX[i] + (prevX - stX[i]) * u
237
+ y = stY[i] + (prevY - stY[i]) * u
238
+ }
239
+
240
+ // If the new point is the same as the previous point, skip ahead.
241
+ if (Math.abs(prevX - x) < 0.0001 && Math.abs(prevY - y) < 0.0001) continue
242
+
243
+ // How far is the new point from the previous point?
244
+ const distance = ((y - prevY) ** 2 + (x - prevX) ** 2) ** 0.5
245
+
246
+ // Add this distance to the total "running length" of the line.
247
+ totalLength += distance
248
+
249
+ // At the start of the line, we wait until the new point is a certain distance
250
+ // away from the original point, to avoid noise.
251
+ if (i < 4 && totalLength < size) continue
252
+
253
+ ptX[count] = x
254
+ ptY[count] = y
255
+ inX[count] = stX[i]
256
+ inY[count] = stY[i]
257
+ inZ[count] = stZ[i]
258
+ press[count] = simulatePressure ? 0.5 : stZ[i]
259
+ dists[count] = distance
260
+ runs[count] = totalLength
261
+ rads[count] = 1
262
+ count++
263
+ prevX = x
264
+ prevY = y
265
+ }
266
+
267
+ if (totalLength < 1) {
268
+ let max = 0.5
269
+ for (let i = 0; i < count; i++) max = Math.max(max, press[i])
270
+ for (let i = 0; i < count; i++) press[i] = max
271
+ }
272
+
273
+ pointCount = count
274
+ }
275
+
276
+ /**
277
+ * Resolve a taper option to a distance: `true` tapers over the whole stroke, `false` or
278
+ * `undefined` not at all.
279
+ *
280
+ * @internal
281
+ */
282
+ export function resolveTaper(
283
+ taper: number | boolean | undefined,
284
+ size: number,
285
+ totalLength: number
286
+ ): number {
287
+ if (!taper) return 0
288
+ return taper === true ? Math.max(size, totalLength) : taper
289
+ }
290
+
291
+ /**
292
+ * Phase 2: compute each point's radius from its pressure, distance and running length.
293
+ * Same recurrences as the object pipeline, with the taper pass folded into the main
294
+ * radius loop.
295
+ *
296
+ * @internal
297
+ */
298
+ export function computeRadii(options: StrokeOptions): void {
299
+ const {
300
+ size = 16,
301
+ thinning = 0.5,
302
+ simulatePressure = true,
303
+ easing = (t) => t,
304
+ start = {},
305
+ end = {},
306
+ } = options
307
+
308
+ const { easing: taperStartEase = easeOutQuad } = start
309
+ const { easing: taperEndEase = easeOutCubic } = end
310
+
311
+ const n = pointCount
312
+ const press = pressures
313
+ const dists = distances
314
+ const runs = runningLengths
315
+ const rads = radii
316
+
317
+ const totalLength = runs[n - 1]
318
+
319
+ if (!simulatePressure && totalLength < size) {
320
+ let max = 0.5
321
+ for (let i = 0; i < n; i++) max = Math.max(max, press[i])
322
+ for (let i = 0; i < n; i++) {
323
+ press[i] = max
324
+ rads[i] = size * easing(0.5 - thinning * (0.5 - max))
325
+ }
326
+ return
327
+ }
328
+
329
+ // Calculate initial pressure based on the average of the first n number of points.
330
+ // This prevents "dots" at the start of the line. Drawn lines almost always start slow!
331
+ let prevPressure = press[0]
332
+ for (let i = 0; i < n; i++) {
333
+ if (runs[i] > size * 5) break
334
+ const sp = min(1, dists[i] / size)
335
+ let p: number
336
+ if (simulatePressure) {
337
+ const rp = min(1, 1 - sp)
338
+ p = min(1, prevPressure + (rp - prevPressure) * (sp * RATE_OF_PRESSURE_CHANGE))
339
+ } else {
340
+ p = min(1, prevPressure + (press[i] - prevPressure) * 0.5)
341
+ }
342
+ prevPressure = prevPressure + (p - prevPressure) * 0.5
343
+ }
344
+
345
+ const taperStart = resolveTaper(start.taper, size, totalLength)
346
+ const taperEnd = resolveTaper(end.taper, size, totalLength)
347
+
348
+ const hasTaper = taperStart || taperEnd
349
+
350
+ // Now calculate pressure and radius for each point. If the point falls within a taper
351
+ // distance from either end, scale its radius by the smaller taper strength.
352
+ for (let i = 0; i < n; i++) {
353
+ let radius: number
354
+ if (thinning) {
355
+ let pressure = press[i]
356
+ const sp = min(1, dists[i] / size)
357
+ if (simulatePressure) {
358
+ // If we're simulating pressure, then do so based on the distance between the
359
+ // current point and the previous point, and the size of the stroke.
360
+ const rp = min(1, 1 - sp)
361
+ pressure = min(1, prevPressure + (rp - prevPressure) * (sp * RATE_OF_PRESSURE_CHANGE))
362
+ } else {
363
+ // Otherwise, use the input pressure slightly smoothed based on the distance
364
+ // between the current point and the previous point.
365
+ pressure = min(1, prevPressure + (pressure - prevPressure) * (sp * RATE_OF_PRESSURE_CHANGE))
366
+ }
367
+ radius = size * easing(0.5 - thinning * (0.5 - pressure))
368
+ prevPressure = pressure
369
+ } else {
370
+ radius = size / 2
371
+ }
372
+
373
+ if (hasTaper) {
374
+ const runningLength = runs[i]
375
+ const ts = runningLength < taperStart ? taperStartEase(runningLength / taperStart) : 1
376
+ const te =
377
+ totalLength - runningLength < taperEnd
378
+ ? taperEndEase((totalLength - runningLength) / taperEnd)
379
+ : 1
380
+ radius = Math.max(0.01, radius * Math.min(ts, te))
381
+ }
382
+
383
+ rads[i] = radius
384
+ }
385
+ }
386
+
387
+ // ---------------------------------------------------------------------------------
388
+ // Track-source buffers: the (sub)sequence of stroke points the outline tracks are built
389
+ // from — the whole stroke for getStroke/getStrokeOutlinePoints, one elbow partition at a
390
+ // time for svgInk. `srcIsCap` marks points to treat as the first/last point when placing
391
+ // the outline (the identity check `point === first || point === last` of the object
392
+ // pipeline). Elbow points are loaded with their input coordinates as their point.
393
+ // ---------------------------------------------------------------------------------
394
+
395
+ let srcCapacity = 256
396
+ export let srcX = new Float64Array(srcCapacity)
397
+ export let srcY = new Float64Array(srcCapacity)
398
+ export let srcZ = new Float64Array(srcCapacity)
399
+ export let srcInputX = new Float64Array(srcCapacity)
400
+ export let srcInputY = new Float64Array(srcCapacity)
401
+ export let srcRadius = new Float64Array(srcCapacity)
402
+ export let srcRunningLength = new Float64Array(srcCapacity)
403
+ export let srcIsCap = new Uint8Array(srcCapacity)
404
+ export let srcCount = 0
405
+
406
+ function ensureSrcCapacity(n: number) {
407
+ if (n <= srcCapacity) return
408
+ while (srcCapacity < n) srcCapacity *= 2
409
+ srcX = new Float64Array(srcCapacity)
410
+ srcY = new Float64Array(srcCapacity)
411
+ srcZ = new Float64Array(srcCapacity)
412
+ srcInputX = new Float64Array(srcCapacity)
413
+ srcInputY = new Float64Array(srcCapacity)
414
+ srcRadius = new Float64Array(srcCapacity)
415
+ srcRunningLength = new Float64Array(srcCapacity)
416
+ srcIsCap = new Uint8Array(srcCapacity)
417
+ }
418
+
419
+ /** Load the track source from materialized StrokePoints. @internal */
420
+ export function loadSrcFromStrokePoints(strokePoints: StrokePoint[]): void {
421
+ const n = strokePoints.length
422
+ ensureSrcCapacity(n)
423
+ const sx = srcX
424
+ const sy = srcY
425
+ const sz = srcZ
426
+ const six = srcInputX
427
+ const siy = srcInputY
428
+ const sr = srcRadius
429
+ const srl = srcRunningLength
430
+ const scap = srcIsCap
431
+ const first = strokePoints[0]
432
+ const last = strokePoints[n - 1]
433
+ for (let i = 0; i < n; i++) {
434
+ const sp = strokePoints[i]
435
+ const point = sp.point
436
+ const input = sp.input
437
+ sx[i] = point.x
438
+ sy[i] = point.y
439
+ sz[i] = point.z
440
+ six[i] = input.x
441
+ siy[i] = input.y
442
+ sr[i] = sp.radius
443
+ srl[i] = sp.runningLength
444
+ scap[i] = sp === first || sp === last ? 1 : 0
445
+ }
446
+ srcCount = n
447
+ }
448
+
449
+ /** Load the track source from the whole pipeline. @internal */
450
+ export function loadSrcFromPipeline(): void {
451
+ const n = pointCount
452
+ ensureSrcCapacity(n)
453
+ const sx = srcX
454
+ const sy = srcY
455
+ const sz = srcZ
456
+ const six = srcInputX
457
+ const siy = srcInputY
458
+ const sr = srcRadius
459
+ const srl = srcRunningLength
460
+ const scap = srcIsCap
461
+ const ptX = pointX
462
+ const ptY = pointY
463
+ const inX = inputX
464
+ const inY = inputY
465
+ const inZ = inputZ
466
+ const runs = runningLengths
467
+ const rads = radii
468
+ for (let i = 0; i < n; i++) {
469
+ sx[i] = ptX[i]
470
+ sy[i] = ptY[i]
471
+ sz[i] = inZ[i]
472
+ six[i] = inX[i]
473
+ siy[i] = inY[i]
474
+ sr[i] = rads[i]
475
+ srl[i] = runs[i]
476
+ scap[i] = i === 0 || i === n - 1 ? 1 : 0
477
+ }
478
+ srcCount = n
479
+ }
480
+
481
+ /**
482
+ * Load one elbow partition from the pipeline as the track source: boundary point `a`,
483
+ * the surviving inner points `innerStart..innerEnd`, and boundary point `b`. Elbow
484
+ * boundaries read the input coordinates instead of the streamlined ones. When a hard
485
+ * elbow's duplicated end point survived cleanup (`dupQuirk`), the inner copy of `b` is
486
+ * also marked as a cap point, matching the object pipeline where both array slots held
487
+ * the same point object.
488
+ *
489
+ * @internal
490
+ */
491
+ export function loadSrcPartition(
492
+ a: number,
493
+ aElbow: boolean,
494
+ innerStart: number,
495
+ innerEnd: number,
496
+ b: number,
497
+ bElbow: boolean,
498
+ dupQuirk: boolean
499
+ ): void {
500
+ ensureSrcCapacity(innerEnd - innerStart + 3)
501
+ const sx = srcX
502
+ const sy = srcY
503
+ const sz = srcZ
504
+ const six = srcInputX
505
+ const siy = srcInputY
506
+ const sr = srcRadius
507
+ const srl = srcRunningLength
508
+ const scap = srcIsCap
509
+ const ptX = pointX
510
+ const ptY = pointY
511
+ const inX = inputX
512
+ const inY = inputY
513
+ const inZ = inputZ
514
+ const runs = runningLengths
515
+ const rads = radii
516
+
517
+ sx[0] = aElbow ? inX[a] : ptX[a]
518
+ sy[0] = aElbow ? inY[a] : ptY[a]
519
+ sz[0] = inZ[a]
520
+ six[0] = inX[a]
521
+ siy[0] = inY[a]
522
+ sr[0] = rads[a]
523
+ srl[0] = runs[a]
524
+ scap[0] = 1
525
+ let w = 1
526
+ for (let i = innerStart; i <= innerEnd; i++) {
527
+ sx[w] = ptX[i]
528
+ sy[w] = ptY[i]
529
+ sz[w] = inZ[i]
530
+ six[w] = inX[i]
531
+ siy[w] = inY[i]
532
+ sr[w] = rads[i]
533
+ srl[w] = runs[i]
534
+ scap[w] = 0
535
+ w++
536
+ }
537
+ if (dupQuirk) scap[w - 1] = 1
538
+ sx[w] = bElbow ? inX[b] : ptX[b]
539
+ sy[w] = bElbow ? inY[b] : ptY[b]
540
+ sz[w] = inZ[b]
541
+ six[w] = inX[b]
542
+ siy[w] = inY[b]
543
+ sr[w] = rads[b]
544
+ srl[w] = runs[b]
545
+ scap[w] = 1
546
+ srcCount = w + 1
547
+ }
@@ -0,0 +1,106 @@
1
+ // Fast SVG path data building.
2
+ //
3
+ // Browsers don't render differences below a hundredth of a pixel, so we emit at most 2 decimal
4
+ // places. Stringifying doubles rounded to 2 decimals is slow (they routinely miss V8's fast
5
+ // float-to-string path), and concatenating many small strings costs more than the math itself.
6
+ // Instead we round every coordinate to an integer number of hundredths ("centi-pixels") and
7
+ // write decimal digits straight into a byte buffer, decoding to a string once per path.
8
+ //
9
+ // Paths use relative commands (t/q/a/l) wherever possible: deltas between consecutive points
10
+ // are small numbers, which keeps the path data compact. Because deltas are differences of the
11
+ // rounded integers, they sum back to the rounded absolute positions exactly: no drift.
12
+
13
+ let buf = new Uint8Array(65536)
14
+ let pos = 0
15
+ const decoder = new TextDecoder()
16
+
17
+ function ensure(n: number) {
18
+ if (pos + n > buf.length) {
19
+ const next = new Uint8Array(buf.length * 2)
20
+ next.set(buf)
21
+ buf = next
22
+ }
23
+ }
24
+
25
+ /** Start a new path. Builders must call this before writing. */
26
+ export function resetPath() {
27
+ pos = 0
28
+ }
29
+
30
+ /** Append a string of ASCII characters (command letters, arc flags) to the path. */
31
+ export function writeStr(s: string) {
32
+ ensure(s.length)
33
+ for (let i = 0; i < s.length; i++) {
34
+ buf[pos++] = s.charCodeAt(i)
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Append a value given in integer hundredths, e.g. 255 as `2.55`, -30 as `-.3`, 0 as `0`.
40
+ * Assumes |n| < 2^31, which holds for canvas coordinates (|v| < ~21 million px).
41
+ */
42
+ export function writeC(n: number) {
43
+ ensure(16)
44
+ if (n < 0) {
45
+ buf[pos++] = 45 // '-'
46
+ n = -n
47
+ }
48
+ const i = (n / 100) | 0
49
+ const f = n - i * 100
50
+ if (i === 0) {
51
+ if (f === 0) {
52
+ buf[pos++] = 48 // '0'
53
+ return
54
+ }
55
+ } else if (i < 10) {
56
+ buf[pos++] = 48 + i
57
+ } else if (i < 100) {
58
+ buf[pos++] = 48 + ((i / 10) | 0)
59
+ buf[pos++] = 48 + (i % 10)
60
+ } else {
61
+ const start = pos
62
+ let m = i
63
+ while (m > 0) {
64
+ buf[pos++] = 48 + (m % 10)
65
+ m = (m / 10) | 0
66
+ }
67
+ // digits came out least-significant first; reverse them
68
+ let lo = start
69
+ let hi = pos - 1
70
+ while (lo < hi) {
71
+ const t = buf[lo]
72
+ buf[lo] = buf[hi]
73
+ buf[hi] = t
74
+ lo++
75
+ hi--
76
+ }
77
+ }
78
+ if (f !== 0) {
79
+ buf[pos++] = 46 // '.'
80
+ const d2 = f % 10
81
+ buf[pos++] = 48 + ((f / 10) | 0)
82
+ if (d2 !== 0) {
83
+ buf[pos++] = 48 + d2
84
+ }
85
+ }
86
+ }
87
+
88
+ /** Append a coordinate pair given in integer hundredths as `x,y `. */
89
+ export function writeCPair(nx: number, ny: number) {
90
+ writeC(nx)
91
+ buf[pos++] = 44 // ','
92
+ writeC(ny)
93
+ buf[pos++] = 32 // ' '
94
+ }
95
+
96
+ /** Finish the path: decode everything written since `resetPath` and reset the writer. */
97
+ export function finishPath(): string {
98
+ const s = decoder.decode(buf.subarray(0, pos))
99
+ pos = 0
100
+ return s
101
+ }
102
+
103
+ /** Round a coordinate to integer hundredths. */
104
+ export function toCenti(v: number): number {
105
+ return Math.round(v * 100)
106
+ }
@@ -1,7 +1,6 @@
1
1
  import { Vec, VecLike } from '@tldraw/editor'
2
- import { getStrokeOutlinePoints } from './getStrokeOutlinePoints'
3
- import { getStrokePoints } from './getStrokePoints'
4
- import { setStrokePointRadii } from './setStrokePointRadii'
2
+ import { computeRadii, ingest, loadSrcFromPipeline, pointCount } from './core'
3
+ import { outlineFromSrc } from './getStrokeOutlinePoints'
5
4
  import type { StrokeOptions } from './types'
6
5
 
7
6
  /**
@@ -16,8 +15,9 @@ import type { StrokeOptions } from './types'
16
15
  */
17
16
 
18
17
  export function getStroke(points: VecLike[], options: StrokeOptions = {}): Vec[] {
19
- return getStrokeOutlinePoints(
20
- setStrokePointRadii(getStrokePoints(points, options), options),
21
- options
22
- )
18
+ ingest(points, options)
19
+ if (pointCount === 0) return []
20
+ computeRadii(options)
21
+ loadSrcFromPipeline()
22
+ return outlineFromSrc(options)
23
23
  }