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,266 @@
1
+ import { act } from '@testing-library/react'
2
+ import { createShapeId, Editor } from '@tldraw/editor'
3
+ import { useEffect } from 'react'
4
+ import { Tldraw } from '../../lib/Tldraw'
5
+ import { DefaultKeyboardShortcutsDialogContent } from '../../lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent'
6
+ import { TldrawUiMenuContextProvider } from '../../lib/ui/components/primitives/menus/TldrawUiMenuContext'
7
+ import { useActions } from '../../lib/ui/context/actions'
8
+ import {
9
+ getHotkeysStringFromKbd,
10
+ ParsedKbd,
11
+ parseKbd,
12
+ } from '../../lib/ui/hooks/useKeyboardShortcuts'
13
+ import { useTools } from '../../lib/ui/hooks/useTools'
14
+ import {
15
+ renderTldrawComponent,
16
+ renderTldrawComponentWithEditor,
17
+ } from '../testutils/renderTldrawComponent'
18
+
19
+ // These kbds are intentionally not registered in the shortcut registry, they're handled by
20
+ // useNativeClipboardEvents / the upload asset action instead. See SKIP_KBDS in useKeyboardShortcuts.
21
+ const SKIP_KBDS = ['copy', 'cut', 'paste', 'asset']
22
+
23
+ // Shortcuts that have a kbd but intentionally don't get their own row in the default keyboard
24
+ // shortcuts dialog. If you add a shortcut that should be visible to users, add it to
25
+ // DefaultKeyboardShortcutsDialogContent instead of this list.
26
+ const NOT_IN_SHORTCUTS_DIALOG = new Set([
27
+ // Opening the shortcuts dialog is covered by the a11y "Open keyboard shortcuts" row.
28
+ 'open-kbd-shortcuts',
29
+ // Covered by the a11y "Open context menu" row.
30
+ 'a11y-open-context-menu',
31
+ // Cursor-targeted variants of zoom in/out that share a label with the rows already shown.
32
+ 'zoom-in-on-cursor',
33
+ 'zoom-out-on-cursor',
34
+ // Rotation is covered by the a11y "Rotate shape" rows.
35
+ 'rotate-cw',
36
+ 'rotate-ccw',
37
+ // Cursor-targeted paste variants of the paste row already shown.
38
+ 'paste-at-cursor',
39
+ 'paste-plain-text-at-cursor',
40
+ // Style-picking shortcuts that aren't surfaced in the dialog.
41
+ 'select-white-color',
42
+ 'select-fill-fill',
43
+ 'select-fill-lined-fill',
44
+ // Re-selects the last geo tool; covered by the rectangle/ellipse tool rows.
45
+ 'select-geo-tool',
46
+ // Page navigation shortcuts without dialog labels.
47
+ 'change-page-prev',
48
+ 'change-page-next',
49
+ // Only rendered when collaboration UI is enabled, which is off in this test.
50
+ 'open-cursor-chat',
51
+ ])
52
+
53
+ interface ShortcutEntry {
54
+ id: string
55
+ kbd: string
56
+ }
57
+
58
+ function serializeParsedKbd(parsed: ParsedKbd): string {
59
+ const modifiers: string[] = []
60
+ if (parsed.meta) modifiers.push('meta')
61
+ if (parsed.ctrl) modifiers.push('ctrl')
62
+ if (parsed.alt) modifiers.push('alt')
63
+ if (parsed.shift) modifiers.push('shift')
64
+ return [...modifiers, parsed.key].join('+')
65
+ }
66
+
67
+ export function getKbdKeyCombos(kbd: string): string[] {
68
+ return parseKbd(getHotkeysStringFromKbd(kbd)).map(serializeParsedKbd)
69
+ }
70
+
71
+ function ShortcutCapturer({ onCapture }: { onCapture(entries: ShortcutEntry[]): void }) {
72
+ const actions = useActions()
73
+ const tools = useTools()
74
+
75
+ useEffect(() => {
76
+ const entries: ShortcutEntry[] = []
77
+ for (const action of Object.values(actions)) {
78
+ if (!action.kbd || SKIP_KBDS.includes(action.id)) continue
79
+ entries.push({ id: `action.${action.id}`, kbd: action.kbd })
80
+ }
81
+ for (const tool of Object.values(tools)) {
82
+ if (!tool.kbd || SKIP_KBDS.includes(tool.id)) continue
83
+ entries.push({ id: `tool.${tool.id}`, kbd: tool.kbd })
84
+ }
85
+ onCapture(entries)
86
+ }, [actions, tools, onCapture])
87
+
88
+ return null
89
+ }
90
+
91
+ async function getDefaultShortcutEntries() {
92
+ let captured: ShortcutEntry[] = []
93
+ await renderTldrawComponent(
94
+ <Tldraw>
95
+ <ShortcutCapturer onCapture={(entries) => (captured = entries)} />
96
+ </Tldraw>,
97
+ { waitForPatterns: false }
98
+ )
99
+ return captured
100
+ }
101
+
102
+ describe('default keyboard shortcuts', () => {
103
+ it('does not bind the same key combo to more than one action or tool', async () => {
104
+ const entries = await getDefaultShortcutEntries()
105
+
106
+ // Sanity check: we actually captured the default actions/tools.
107
+ expect(entries.length).toBeGreaterThan(0)
108
+
109
+ const comboToIds = new Map<string, string[]>()
110
+ for (const entry of entries) {
111
+ for (const combo of getKbdKeyCombos(entry.kbd)) {
112
+ const ids = comboToIds.get(combo) ?? []
113
+ ids.push(entry.id)
114
+ comboToIds.set(combo, ids)
115
+ }
116
+ }
117
+
118
+ const collisions = [...comboToIds.entries()]
119
+ .filter(([, ids]) => ids.length > 1)
120
+ .map(([combo, ids]) => `${combo} -> ${ids.join(', ')}`)
121
+
122
+ expect(collisions).toEqual([])
123
+ })
124
+
125
+ it('lists every shortcut in the keyboard shortcuts dialog (or marks it as intentionally omitted)', async () => {
126
+ let entries: ShortcutEntry[] = []
127
+ const rendered = await renderTldrawComponent(
128
+ <Tldraw>
129
+ <ShortcutCapturer onCapture={(captured) => (entries = captured)} />
130
+ <TldrawUiMenuContextProvider type="keyboard-shortcuts" sourceId="kbd">
131
+ <DefaultKeyboardShortcutsDialogContent />
132
+ </TldrawUiMenuContextProvider>
133
+ </Tldraw>,
134
+ { waitForPatterns: false }
135
+ )
136
+
137
+ // The raw ids (without the `action.`/`tool.` prefix) rendered as rows in the dialog.
138
+ const idsInDialog = new Set(
139
+ [...rendered.container.querySelectorAll('[data-testid^="kbd."]')].map((el) =>
140
+ el.getAttribute('data-testid')!.slice('kbd.'.length)
141
+ )
142
+ )
143
+ expect(idsInDialog.size).toBeGreaterThan(0)
144
+
145
+ const missing = entries
146
+ .map((entry) => entry.id.replace(/^(action|tool)\./, ''))
147
+ .filter((id) => !idsInDialog.has(id) && !NOT_IN_SHORTCUTS_DIALOG.has(id))
148
+
149
+ expect(missing).toEqual([])
150
+ })
151
+ })
152
+
153
+ async function setupFocusedEditor() {
154
+ const { editor } = await renderTldrawComponentWithEditor(
155
+ (onMount) => <Tldraw onMount={onMount} />,
156
+ { waitForPatterns: false }
157
+ )
158
+
159
+ // Shortcuts only register while the editor is focused.
160
+ act(() => {
161
+ editor.updateInstanceState({ isFocused: true })
162
+ })
163
+
164
+ return { editor }
165
+ }
166
+
167
+ function keydown(editor: Editor, init: KeyboardEventInit) {
168
+ act(() => {
169
+ editor
170
+ .getContainerDocument()
171
+ .body.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, ...init }))
172
+ })
173
+ }
174
+
175
+ function keyup(editor: Editor, init: KeyboardEventInit) {
176
+ act(() => {
177
+ editor
178
+ .getContainerDocument()
179
+ .body.dispatchEvent(new KeyboardEvent('keyup', { bubbles: true, ...init }))
180
+ })
181
+ }
182
+
183
+ describe('keyboard shortcuts with a held key', () => {
184
+ it('fires the plain shortcut on a fresh key press', async () => {
185
+ const { editor } = await setupFocusedEditor()
186
+ expect(editor.getInstanceState().isToolLocked).toBe(false)
187
+
188
+ // Plain `q` toggles tool lock.
189
+ keydown(editor, { key: 'q', code: 'KeyQ' })
190
+ expect(editor.getInstanceState().isToolLocked).toBe(true)
191
+ })
192
+
193
+ it('does not fall back to the plain shortcut when a modifier is released mid-hold', async () => {
194
+ const { editor } = await setupFocusedEditor()
195
+ expect(editor.getInstanceState().isToolLocked).toBe(false)
196
+
197
+ // Press shift+q (copy-hovered-styles), which does not toggle tool lock.
198
+ keydown(editor, { key: 'q', code: 'KeyQ', shiftKey: true })
199
+ expect(editor.getInstanceState().isToolLocked).toBe(false)
200
+
201
+ // Release shift but keep holding q. The auto-repeat keydown events should not start
202
+ // firing the adjacent plain `q` shortcut (toggle tool lock).
203
+ keydown(editor, { key: 'q', code: 'KeyQ', shiftKey: false, repeat: true })
204
+ keydown(editor, { key: 'q', code: 'KeyQ', shiftKey: false, repeat: true })
205
+ expect(editor.getInstanceState().isToolLocked).toBe(false)
206
+ })
207
+
208
+ it('fires the plain shortcut again after the held key is released and pressed fresh', async () => {
209
+ const { editor } = await setupFocusedEditor()
210
+
211
+ keydown(editor, { key: 'q', code: 'KeyQ', shiftKey: true })
212
+ keydown(editor, { key: 'q', code: 'KeyQ', shiftKey: false, repeat: true })
213
+ expect(editor.getInstanceState().isToolLocked).toBe(false)
214
+
215
+ // Releasing the physical key clears the held-key tracking, so a fresh press works again.
216
+ keyup(editor, { key: 'q', code: 'KeyQ' })
217
+ keydown(editor, { key: 'q', code: 'KeyQ' })
218
+ expect(editor.getInstanceState().isToolLocked).toBe(true)
219
+ })
220
+
221
+ it('releases the held key even when the keyup lands on a text input', async () => {
222
+ const { editor } = await setupFocusedEditor()
223
+
224
+ keydown(editor, { key: 'q', code: 'KeyQ', shiftKey: true })
225
+ expect(editor.getInstanceState().isToolLocked).toBe(false)
226
+
227
+ // The key is released while focus is inside a text input, so the keyup is otherwise
228
+ // skipped. It must still clear the held-key tracking.
229
+ const body = editor.getContainerDocument().body
230
+ const input = editor.getContainerDocument().createElement('input')
231
+ body.appendChild(input)
232
+ act(() => {
233
+ input.dispatchEvent(new KeyboardEvent('keyup', { bubbles: true, key: 'q', code: 'KeyQ' }))
234
+ })
235
+ body.removeChild(input)
236
+
237
+ // A fresh plain `q` press now works rather than being blocked by a stale entry.
238
+ keydown(editor, { key: 'q', code: 'KeyQ' })
239
+ expect(editor.getInstanceState().isToolLocked).toBe(true)
240
+ })
241
+
242
+ // Regression test for #9099: redo (cmd+shift+z) stopped firing after an undo (cmd+z) on
243
+ // macOS, where the browser swallows the `z` keyup while cmd stays held. The held-key
244
+ // tracking from #9099 never got cleared, so the stale undo registration blocked the redo
245
+ // on the same physical `KeyZ`. A fresh keypress must always be free to trigger its match.
246
+ it('fires redo after undo on the same physical key when the keyup is swallowed (cmd held)', async () => {
247
+ const { editor } = await setupFocusedEditor()
248
+
249
+ const id = createShapeId()
250
+ act(() => {
251
+ editor.markHistoryStoppingPoint()
252
+ editor.createShape({ id, type: 'geo', x: 0, y: 0 })
253
+ })
254
+ expect(editor.getCurrentPageShapeIds().has(id)).toBe(true)
255
+
256
+ // cmd+z undoes the shape creation. On macOS the `z` keyup is never delivered while cmd
257
+ // stays held, so we deliberately don't dispatch it.
258
+ keydown(editor, { key: 'z', code: 'KeyZ', metaKey: true })
259
+ expect(editor.getCurrentPageShapeIds().has(id)).toBe(false)
260
+
261
+ // Adding shift and pressing z again is a fresh keypress (not an auto-repeat), so it must
262
+ // trigger redo rather than being blocked by the stale undo registration on `KeyZ`.
263
+ keydown(editor, { key: 'z', code: 'KeyZ', metaKey: true, shiftKey: true })
264
+ expect(editor.getCurrentPageShapeIds().has(id)).toBe(true)
265
+ })
266
+ })
package/tldraw.css CHANGED
@@ -186,6 +186,7 @@
186
186
  --tl-color-text-0: hsl(0, 0%, 11%);
187
187
  --tl-color-text-1: hsl(0, 0%, 18%);
188
188
  --tl-color-text-3: hsl(204, 4%, 45%);
189
+ --tl-color-text-disabled: hsl(204, 4%, 55%);
189
190
  --tl-color-text-shadow: hsl(0, 0%, 100%);
190
191
  --tl-color-text-highlight: hsl(52, 100%, 50%);
191
192
  --tl-color-text-highlight-p3: color(display-p3 0.972 0.8205 0.05);
@@ -240,6 +241,7 @@
240
241
  --tl-color-text-0: hsl(0, 9%, 94%);
241
242
  --tl-color-text-1: hsl(0, 0%, 85%);
242
243
  --tl-color-text-3: hsl(204, 4%, 75%);
244
+ --tl-color-text-disabled: hsl(204, 4%, 50%);
243
245
  --tl-color-text-shadow: hsl(210, 13%, 18%);
244
246
  --tl-color-text-highlight: hsl(52, 100%, 41%);
245
247
  --tl-color-text-highlight-p3: color(display-p3 0.8078 0.6225 0.0312);
@@ -766,17 +768,17 @@ input,
766
768
  .tl-rich-text ol {
767
769
  text-align: left;
768
770
  margin: 0;
769
- padding-left: 3.25ch;
771
+ padding-left: 1.625ch;
770
772
  /* Some resets, like Tailwind, nix the list styling. */
771
773
  list-style: revert;
772
774
  }
773
775
 
774
776
  .tl-rich-text ol:has(> li:nth-child(10)) {
775
- padding-left: 4.25ch;
777
+ padding-left: 2.625ch;
776
778
  }
777
779
 
778
780
  .tl-rich-text ol:has(> li:nth-child(100)) {
779
- padding-left: 5.25ch;
781
+ padding-left: 3.625ch;
780
782
  }
781
783
 
782
784
  .tl-rich-text h1,
@@ -815,6 +817,7 @@ input,
815
817
  .tl-theme__dark .tl-rich-text mark {
816
818
  background-color: var(--tl-color-text-highlight);
817
819
  color: currentColor;
820
+ text-shadow: none;
818
821
  }
819
822
 
820
823
  @supports (color: color(display-p3 1 1 1)) {
@@ -987,6 +990,15 @@ input,
987
990
  color: var(--tl-color-text-1);
988
991
  }
989
992
 
993
+ .tl-content-slot {
994
+ position: absolute;
995
+ inset: 0px;
996
+ height: 100%;
997
+ width: 100%;
998
+ pointer-events: none;
999
+ transform-origin: top left;
1000
+ }
1001
+
990
1002
  /* -------------------- Group shape ------------------ */
991
1003
 
992
1004
  .tl-group {
@@ -1573,9 +1585,8 @@ it from receiving any pointer events or affecting the cursor. */
1573
1585
  * (which creates its own stacking context at z-index 300), so their
1574
1586
  * z-indices are relative to the layout, not the container.
1575
1587
  *
1576
- * cursor and following-indicator are rendered inside canvas-in-front
1577
- * (stacking context at z-index 250), so their z-indices are relative
1578
- * to that context.
1588
+ * cursor is rendered inside canvas-in-front (stacking context at
1589
+ * z-index 250), so their z-indices are relative to that context.
1579
1590
  */
1580
1591
  --tl-layer-above: 1;
1581
1592
  --tl-layer-focused-input: 10;
@@ -1585,7 +1596,6 @@ it from receiving any pointer events or affecting the cursor. */
1585
1596
  --tl-layer-toasts: 650;
1586
1597
  --tl-layer-cursor: 700;
1587
1598
  --tl-layer-header-footer: 999;
1588
- --tl-layer-following-indicator: 1000;
1589
1599
  }
1590
1600
 
1591
1601
  /* Button */
@@ -1615,14 +1625,14 @@ it from receiving any pointer events or affecting the cursor. */
1615
1625
 
1616
1626
  .tlui-button:disabled,
1617
1627
  .tlui-button[data-disabled] {
1618
- color: var(--tl-color-text-3);
1628
+ color: var(--tl-color-text-disabled);
1619
1629
  text-shadow: none;
1620
1630
  cursor: default;
1621
1631
  }
1622
1632
 
1623
1633
  .tlui-button:disabled .tlui-kbd,
1624
1634
  .tlui-button[data-disabled] .tlui-kbd {
1625
- color: var(--tl-color-text-3);
1635
+ color: var(--tl-color-text-disabled);
1626
1636
  }
1627
1637
 
1628
1638
  .tlui-button > * {
@@ -1991,6 +2001,11 @@ it from receiving any pointer events or affecting the cursor. */
1991
2001
  height: 15px;
1992
2002
  }
1993
2003
 
2004
+ .tlui-icon__tiny {
2005
+ width: 12px;
2006
+ height: 12px;
2007
+ }
2008
+
1994
2009
  /* --------------------- Slider --------------------- */
1995
2010
 
1996
2011
  .tlui-slider__container {
@@ -2604,7 +2619,7 @@ it from receiving any pointer events or affecting the cursor. */
2604
2619
  left: 8px;
2605
2620
  }
2606
2621
 
2607
- /*
2622
+ /*
2608
2623
  * focusing skip-to-main-content hide top ui buttons to make sure we avoid
2609
2624
  * z-index clashes on hover (see issue #8328)
2610
2625
  */
@@ -3251,8 +3266,17 @@ tldraw? probably.
3251
3266
  height: 100%;
3252
3267
  z-index: var(--tl-layer-canvas-overlays);
3253
3268
  background-color: var(--tl-color-overlay);
3254
- pointer-events: all;
3255
3269
  animation: tl-fade-in 0.12s ease-out;
3270
+ }
3271
+
3272
+ .tlui-dialog__positioner {
3273
+ position: absolute;
3274
+ top: 0px;
3275
+ left: 0px;
3276
+ width: 100%;
3277
+ height: 100%;
3278
+ z-index: var(--tl-layer-canvas-overlays);
3279
+ pointer-events: all;
3256
3280
  display: grid;
3257
3281
  place-items: center;
3258
3282
  overflow-y: auto;
@@ -3742,7 +3766,6 @@ tldraw? probably.
3742
3766
  inset: 0px;
3743
3767
  border-width: 2px;
3744
3768
  border-style: solid;
3745
- z-index: var(--tl-layer-following-indicator);
3746
3769
  pointer-events: none;
3747
3770
  }
3748
3771
 
@@ -1,100 +0,0 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
- var setStrokePointRadii_exports = {};
20
- __export(setStrokePointRadii_exports, {
21
- setStrokePointRadii: () => setStrokePointRadii
22
- });
23
- module.exports = __toCommonJS(setStrokePointRadii_exports);
24
- var import_editor = require("@tldraw/editor");
25
- const { min } = Math;
26
- const RATE_OF_PRESSURE_CHANGE = 0.275;
27
- function setStrokePointRadii(strokePoints, options) {
28
- const {
29
- size = 16,
30
- thinning = 0.5,
31
- simulatePressure = true,
32
- easing = (t) => t,
33
- start = {},
34
- end = {}
35
- } = options;
36
- const { easing: taperStartEase = import_editor.EASINGS.easeOutQuad } = start;
37
- const { easing: taperEndEase = import_editor.EASINGS.easeOutCubic } = end;
38
- const totalLength = strokePoints[strokePoints.length - 1].runningLength;
39
- let firstRadius;
40
- let prevPressure = strokePoints[0].pressure;
41
- let strokePoint;
42
- if (!simulatePressure && totalLength < size) {
43
- const max = strokePoints.reduce((max2, curr) => Math.max(max2, curr.pressure), 0.5);
44
- strokePoints.forEach((sp) => {
45
- sp.pressure = max;
46
- sp.radius = size * easing(0.5 - thinning * (0.5 - sp.pressure));
47
- });
48
- return strokePoints;
49
- } else {
50
- let p;
51
- for (let i = 0, n = strokePoints.length; i < n; i++) {
52
- strokePoint = strokePoints[i];
53
- if (strokePoint.runningLength > size * 5) break;
54
- const sp = min(1, strokePoint.distance / size);
55
- if (simulatePressure) {
56
- const rp = min(1, 1 - sp);
57
- p = min(1, prevPressure + (rp - prevPressure) * (sp * RATE_OF_PRESSURE_CHANGE));
58
- } else {
59
- p = min(1, prevPressure + (strokePoint.pressure - prevPressure) * 0.5);
60
- }
61
- prevPressure = prevPressure + (p - prevPressure) * 0.5;
62
- }
63
- for (let i = 0; i < strokePoints.length; i++) {
64
- strokePoint = strokePoints[i];
65
- if (thinning) {
66
- let { pressure } = strokePoint;
67
- const sp = min(1, strokePoint.distance / size);
68
- if (simulatePressure) {
69
- const rp = min(1, 1 - sp);
70
- pressure = min(1, prevPressure + (rp - prevPressure) * (sp * RATE_OF_PRESSURE_CHANGE));
71
- } else {
72
- pressure = min(
73
- 1,
74
- prevPressure + (pressure - prevPressure) * (sp * RATE_OF_PRESSURE_CHANGE)
75
- );
76
- }
77
- strokePoint.radius = size * easing(0.5 - thinning * (0.5 - pressure));
78
- prevPressure = pressure;
79
- } else {
80
- strokePoint.radius = size / 2;
81
- }
82
- if (firstRadius === void 0) {
83
- firstRadius = strokePoint.radius;
84
- }
85
- }
86
- }
87
- const taperStart = start.taper === false ? 0 : start.taper === true ? Math.max(size, totalLength) : start.taper;
88
- const taperEnd = end.taper === false ? 0 : end.taper === true ? Math.max(size, totalLength) : end.taper;
89
- if (taperStart || taperEnd) {
90
- for (let i = 0; i < strokePoints.length; i++) {
91
- strokePoint = strokePoints[i];
92
- const { runningLength } = strokePoint;
93
- const ts = runningLength < taperStart ? taperStartEase(runningLength / taperStart) : 1;
94
- const te = totalLength - runningLength < taperEnd ? taperEndEase((totalLength - runningLength) / taperEnd) : 1;
95
- strokePoint.radius = Math.max(0.01, strokePoint.radius * Math.min(ts, te));
96
- }
97
- }
98
- return strokePoints;
99
- }
100
- //# sourceMappingURL=setStrokePointRadii.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../../../src/lib/shapes/shared/freehand/setStrokePointRadii.ts"],
4
- "sourcesContent": ["import { EASINGS } from '@tldraw/editor'\nimport { StrokeOptions, StrokePoint } from './types'\n\nconst { min } = Math\n\n// This is the rate of change for simulated pressure. It could be an option.\nconst RATE_OF_PRESSURE_CHANGE = 0.275\n\n/** @public */\nexport function setStrokePointRadii(strokePoints: StrokePoint[], options: StrokeOptions) {\n\tconst {\n\t\tsize = 16,\n\t\tthinning = 0.5,\n\t\tsimulatePressure = true,\n\t\teasing = (t) => t,\n\t\tstart = {},\n\t\tend = {},\n\t} = options\n\n\tconst { easing: taperStartEase = EASINGS.easeOutQuad } = start\n\tconst { easing: taperEndEase = EASINGS.easeOutCubic } = end\n\n\tconst totalLength = strokePoints[strokePoints.length - 1].runningLength\n\n\tlet firstRadius: number | undefined\n\tlet prevPressure = strokePoints[0].pressure\n\tlet strokePoint: StrokePoint\n\n\tif (!simulatePressure && totalLength < size) {\n\t\tconst max = strokePoints.reduce((max, curr) => Math.max(max, curr.pressure), 0.5)\n\t\tstrokePoints.forEach((sp) => {\n\t\t\tsp.pressure = max\n\t\t\tsp.radius = size * easing(0.5 - thinning * (0.5 - sp.pressure))\n\t\t})\n\t\treturn strokePoints\n\t} else {\n\t\t// Calculate initial pressure based on the average of the first\n\t\t// n number of points. This prevents \"dots\" at the start of the\n\t\t// line. Drawn lines almost always start slow!\n\t\tlet p: number\n\t\tfor (let i = 0, n = strokePoints.length; i < n; i++) {\n\t\t\tstrokePoint = strokePoints[i]\n\t\t\tif (strokePoint.runningLength > size * 5) break\n\t\t\tconst sp = min(1, strokePoint.distance / size)\n\t\t\tif (simulatePressure) {\n\t\t\t\tconst rp = min(1, 1 - sp)\n\t\t\t\tp = min(1, prevPressure + (rp - prevPressure) * (sp * RATE_OF_PRESSURE_CHANGE))\n\t\t\t} else {\n\t\t\t\tp = min(1, prevPressure + (strokePoint.pressure - prevPressure) * 0.5)\n\t\t\t}\n\t\t\tprevPressure = prevPressure + (p - prevPressure) * 0.5\n\t\t}\n\n\t\t// Now calculate pressure and radius for each point\n\t\tfor (let i = 0; i < strokePoints.length; i++) {\n\t\t\tstrokePoint = strokePoints[i]\n\t\t\tif (thinning) {\n\t\t\t\tlet { pressure } = strokePoint\n\t\t\t\tconst sp = min(1, strokePoint.distance / size)\n\t\t\t\tif (simulatePressure) {\n\t\t\t\t\t// If we're simulating pressure, then do so based on the distance\n\t\t\t\t\t// between the current point and the previous point, and the size\n\t\t\t\t\t// of the stroke.\n\t\t\t\t\tconst rp = min(1, 1 - sp)\n\t\t\t\t\tpressure = min(1, prevPressure + (rp - prevPressure) * (sp * RATE_OF_PRESSURE_CHANGE))\n\t\t\t\t} else {\n\t\t\t\t\t// Otherwise, use the input pressure slightly smoothed based on the\n\t\t\t\t\t// distance between the current point and the previous point.\n\t\t\t\t\tpressure = min(\n\t\t\t\t\t\t1,\n\t\t\t\t\t\tprevPressure + (pressure - prevPressure) * (sp * RATE_OF_PRESSURE_CHANGE)\n\t\t\t\t\t)\n\t\t\t\t}\n\n\t\t\t\tstrokePoint.radius = size * easing(0.5 - thinning * (0.5 - pressure))\n\n\t\t\t\tprevPressure = pressure\n\t\t\t} else {\n\t\t\t\tstrokePoint.radius = size / 2\n\t\t\t}\n\n\t\t\tif (firstRadius === undefined) {\n\t\t\t\tfirstRadius = strokePoint.radius\n\t\t\t}\n\t\t}\n\t}\n\n\tconst taperStart =\n\t\tstart.taper === false\n\t\t\t? 0\n\t\t\t: start.taper === true\n\t\t\t\t? Math.max(size, totalLength)\n\t\t\t\t: (start.taper as number)\n\n\tconst taperEnd =\n\t\tend.taper === false\n\t\t\t? 0\n\t\t\t: end.taper === true\n\t\t\t\t? Math.max(size, totalLength)\n\t\t\t\t: (end.taper as number)\n\n\tif (taperStart || taperEnd) {\n\t\tfor (let i = 0; i < strokePoints.length; i++) {\n\t\t\tstrokePoint = strokePoints[i]\n\t\t\t/*\n\t\t\t\tApply tapering\n\n\t\t\t\tIf the current length is within the taper distance at either the\n\t\t\t\tstart or the end, calculate the taper strengths. Apply the smaller \n\t\t\t\tof the two taper strengths to the radius.\n\t\t\t*/\n\n\t\t\tconst { runningLength } = strokePoint\n\n\t\t\tconst ts = runningLength < taperStart ? taperStartEase(runningLength / taperStart) : 1\n\n\t\t\tconst te =\n\t\t\t\ttotalLength - runningLength < taperEnd\n\t\t\t\t\t? taperEndEase((totalLength - runningLength) / taperEnd)\n\t\t\t\t\t: 1\n\n\t\t\tstrokePoint.radius = Math.max(0.01, strokePoint.radius * Math.min(ts, te))\n\t\t}\n\t}\n\n\treturn strokePoints\n}\n"],
5
- "mappings": "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",
6
- "names": ["max"]
7
- }
@@ -1,80 +0,0 @@
1
- import { EASINGS } from "@tldraw/editor";
2
- const { min } = Math;
3
- const RATE_OF_PRESSURE_CHANGE = 0.275;
4
- function setStrokePointRadii(strokePoints, options) {
5
- const {
6
- size = 16,
7
- thinning = 0.5,
8
- simulatePressure = true,
9
- easing = (t) => t,
10
- start = {},
11
- end = {}
12
- } = options;
13
- const { easing: taperStartEase = EASINGS.easeOutQuad } = start;
14
- const { easing: taperEndEase = EASINGS.easeOutCubic } = end;
15
- const totalLength = strokePoints[strokePoints.length - 1].runningLength;
16
- let firstRadius;
17
- let prevPressure = strokePoints[0].pressure;
18
- let strokePoint;
19
- if (!simulatePressure && totalLength < size) {
20
- const max = strokePoints.reduce((max2, curr) => Math.max(max2, curr.pressure), 0.5);
21
- strokePoints.forEach((sp) => {
22
- sp.pressure = max;
23
- sp.radius = size * easing(0.5 - thinning * (0.5 - sp.pressure));
24
- });
25
- return strokePoints;
26
- } else {
27
- let p;
28
- for (let i = 0, n = strokePoints.length; i < n; i++) {
29
- strokePoint = strokePoints[i];
30
- if (strokePoint.runningLength > size * 5) break;
31
- const sp = min(1, strokePoint.distance / size);
32
- if (simulatePressure) {
33
- const rp = min(1, 1 - sp);
34
- p = min(1, prevPressure + (rp - prevPressure) * (sp * RATE_OF_PRESSURE_CHANGE));
35
- } else {
36
- p = min(1, prevPressure + (strokePoint.pressure - prevPressure) * 0.5);
37
- }
38
- prevPressure = prevPressure + (p - prevPressure) * 0.5;
39
- }
40
- for (let i = 0; i < strokePoints.length; i++) {
41
- strokePoint = strokePoints[i];
42
- if (thinning) {
43
- let { pressure } = strokePoint;
44
- const sp = min(1, strokePoint.distance / size);
45
- if (simulatePressure) {
46
- const rp = min(1, 1 - sp);
47
- pressure = min(1, prevPressure + (rp - prevPressure) * (sp * RATE_OF_PRESSURE_CHANGE));
48
- } else {
49
- pressure = min(
50
- 1,
51
- prevPressure + (pressure - prevPressure) * (sp * RATE_OF_PRESSURE_CHANGE)
52
- );
53
- }
54
- strokePoint.radius = size * easing(0.5 - thinning * (0.5 - pressure));
55
- prevPressure = pressure;
56
- } else {
57
- strokePoint.radius = size / 2;
58
- }
59
- if (firstRadius === void 0) {
60
- firstRadius = strokePoint.radius;
61
- }
62
- }
63
- }
64
- const taperStart = start.taper === false ? 0 : start.taper === true ? Math.max(size, totalLength) : start.taper;
65
- const taperEnd = end.taper === false ? 0 : end.taper === true ? Math.max(size, totalLength) : end.taper;
66
- if (taperStart || taperEnd) {
67
- for (let i = 0; i < strokePoints.length; i++) {
68
- strokePoint = strokePoints[i];
69
- const { runningLength } = strokePoint;
70
- const ts = runningLength < taperStart ? taperStartEase(runningLength / taperStart) : 1;
71
- const te = totalLength - runningLength < taperEnd ? taperEndEase((totalLength - runningLength) / taperEnd) : 1;
72
- strokePoint.radius = Math.max(0.01, strokePoint.radius * Math.min(ts, te));
73
- }
74
- }
75
- return strokePoints;
76
- }
77
- export {
78
- setStrokePointRadii
79
- };
80
- //# sourceMappingURL=setStrokePointRadii.mjs.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../../../src/lib/shapes/shared/freehand/setStrokePointRadii.ts"],
4
- "sourcesContent": ["import { EASINGS } from '@tldraw/editor'\nimport { StrokeOptions, StrokePoint } from './types'\n\nconst { min } = Math\n\n// This is the rate of change for simulated pressure. It could be an option.\nconst RATE_OF_PRESSURE_CHANGE = 0.275\n\n/** @public */\nexport function setStrokePointRadii(strokePoints: StrokePoint[], options: StrokeOptions) {\n\tconst {\n\t\tsize = 16,\n\t\tthinning = 0.5,\n\t\tsimulatePressure = true,\n\t\teasing = (t) => t,\n\t\tstart = {},\n\t\tend = {},\n\t} = options\n\n\tconst { easing: taperStartEase = EASINGS.easeOutQuad } = start\n\tconst { easing: taperEndEase = EASINGS.easeOutCubic } = end\n\n\tconst totalLength = strokePoints[strokePoints.length - 1].runningLength\n\n\tlet firstRadius: number | undefined\n\tlet prevPressure = strokePoints[0].pressure\n\tlet strokePoint: StrokePoint\n\n\tif (!simulatePressure && totalLength < size) {\n\t\tconst max = strokePoints.reduce((max, curr) => Math.max(max, curr.pressure), 0.5)\n\t\tstrokePoints.forEach((sp) => {\n\t\t\tsp.pressure = max\n\t\t\tsp.radius = size * easing(0.5 - thinning * (0.5 - sp.pressure))\n\t\t})\n\t\treturn strokePoints\n\t} else {\n\t\t// Calculate initial pressure based on the average of the first\n\t\t// n number of points. This prevents \"dots\" at the start of the\n\t\t// line. Drawn lines almost always start slow!\n\t\tlet p: number\n\t\tfor (let i = 0, n = strokePoints.length; i < n; i++) {\n\t\t\tstrokePoint = strokePoints[i]\n\t\t\tif (strokePoint.runningLength > size * 5) break\n\t\t\tconst sp = min(1, strokePoint.distance / size)\n\t\t\tif (simulatePressure) {\n\t\t\t\tconst rp = min(1, 1 - sp)\n\t\t\t\tp = min(1, prevPressure + (rp - prevPressure) * (sp * RATE_OF_PRESSURE_CHANGE))\n\t\t\t} else {\n\t\t\t\tp = min(1, prevPressure + (strokePoint.pressure - prevPressure) * 0.5)\n\t\t\t}\n\t\t\tprevPressure = prevPressure + (p - prevPressure) * 0.5\n\t\t}\n\n\t\t// Now calculate pressure and radius for each point\n\t\tfor (let i = 0; i < strokePoints.length; i++) {\n\t\t\tstrokePoint = strokePoints[i]\n\t\t\tif (thinning) {\n\t\t\t\tlet { pressure } = strokePoint\n\t\t\t\tconst sp = min(1, strokePoint.distance / size)\n\t\t\t\tif (simulatePressure) {\n\t\t\t\t\t// If we're simulating pressure, then do so based on the distance\n\t\t\t\t\t// between the current point and the previous point, and the size\n\t\t\t\t\t// of the stroke.\n\t\t\t\t\tconst rp = min(1, 1 - sp)\n\t\t\t\t\tpressure = min(1, prevPressure + (rp - prevPressure) * (sp * RATE_OF_PRESSURE_CHANGE))\n\t\t\t\t} else {\n\t\t\t\t\t// Otherwise, use the input pressure slightly smoothed based on the\n\t\t\t\t\t// distance between the current point and the previous point.\n\t\t\t\t\tpressure = min(\n\t\t\t\t\t\t1,\n\t\t\t\t\t\tprevPressure + (pressure - prevPressure) * (sp * RATE_OF_PRESSURE_CHANGE)\n\t\t\t\t\t)\n\t\t\t\t}\n\n\t\t\t\tstrokePoint.radius = size * easing(0.5 - thinning * (0.5 - pressure))\n\n\t\t\t\tprevPressure = pressure\n\t\t\t} else {\n\t\t\t\tstrokePoint.radius = size / 2\n\t\t\t}\n\n\t\t\tif (firstRadius === undefined) {\n\t\t\t\tfirstRadius = strokePoint.radius\n\t\t\t}\n\t\t}\n\t}\n\n\tconst taperStart =\n\t\tstart.taper === false\n\t\t\t? 0\n\t\t\t: start.taper === true\n\t\t\t\t? Math.max(size, totalLength)\n\t\t\t\t: (start.taper as number)\n\n\tconst taperEnd =\n\t\tend.taper === false\n\t\t\t? 0\n\t\t\t: end.taper === true\n\t\t\t\t? Math.max(size, totalLength)\n\t\t\t\t: (end.taper as number)\n\n\tif (taperStart || taperEnd) {\n\t\tfor (let i = 0; i < strokePoints.length; i++) {\n\t\t\tstrokePoint = strokePoints[i]\n\t\t\t/*\n\t\t\t\tApply tapering\n\n\t\t\t\tIf the current length is within the taper distance at either the\n\t\t\t\tstart or the end, calculate the taper strengths. Apply the smaller \n\t\t\t\tof the two taper strengths to the radius.\n\t\t\t*/\n\n\t\t\tconst { runningLength } = strokePoint\n\n\t\t\tconst ts = runningLength < taperStart ? taperStartEase(runningLength / taperStart) : 1\n\n\t\t\tconst te =\n\t\t\t\ttotalLength - runningLength < taperEnd\n\t\t\t\t\t? taperEndEase((totalLength - runningLength) / taperEnd)\n\t\t\t\t\t: 1\n\n\t\t\tstrokePoint.radius = Math.max(0.01, strokePoint.radius * Math.min(ts, te))\n\t\t}\n\t}\n\n\treturn strokePoints\n}\n"],
5
- "mappings": "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",
6
- "names": ["max"]
7
- }