tldraw 3.15.0-next.f1dfcef63951 → 3.16.0-next.c30b1b5e551a

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 (247) hide show
  1. package/dist-cjs/index.d.ts +161 -95
  2. package/dist-cjs/index.js +42 -31
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/TldrawImage.js +5 -2
  5. package/dist-cjs/lib/TldrawImage.js.map +3 -3
  6. package/dist-cjs/lib/canvas/TldrawCropHandles.js +1 -1
  7. package/dist-cjs/lib/canvas/TldrawCropHandles.js.map +2 -2
  8. package/dist-cjs/lib/canvas/TldrawHandles.js +1 -1
  9. package/dist-cjs/lib/canvas/TldrawHandles.js.map +2 -2
  10. package/dist-cjs/lib/canvas/TldrawOverlays.js +1 -1
  11. package/dist-cjs/lib/canvas/TldrawOverlays.js.map +2 -2
  12. package/dist-cjs/lib/canvas/TldrawSelectionForeground.js +279 -271
  13. package/dist-cjs/lib/canvas/TldrawSelectionForeground.js.map +2 -2
  14. package/dist-cjs/lib/defaultExternalContentHandlers.js +1 -0
  15. package/dist-cjs/lib/defaultExternalContentHandlers.js.map +2 -2
  16. package/dist-cjs/lib/shapes/arrow/ArrowShapeUtil.js +22 -36
  17. package/dist-cjs/lib/shapes/arrow/ArrowShapeUtil.js.map +2 -2
  18. package/dist-cjs/lib/shapes/arrow/arrowLabel.js +16 -4
  19. package/dist-cjs/lib/shapes/arrow/arrowLabel.js.map +2 -2
  20. package/dist-cjs/lib/shapes/arrow/toolStates/Pointing.js +3 -0
  21. package/dist-cjs/lib/shapes/arrow/toolStates/Pointing.js.map +2 -2
  22. package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js +5 -5
  23. package/dist-cjs/lib/shapes/frame/FrameShapeUtil.js.map +2 -2
  24. package/dist-cjs/lib/shapes/line/LineShapeUtil.js +15 -1
  25. package/dist-cjs/lib/shapes/line/LineShapeUtil.js.map +2 -2
  26. package/dist-cjs/lib/shapes/shared/PathBuilder.js +21 -3
  27. package/dist-cjs/lib/shapes/shared/PathBuilder.js.map +2 -2
  28. package/dist-cjs/lib/shapes/shared/PlainTextLabel.js +1 -0
  29. package/dist-cjs/lib/shapes/shared/PlainTextLabel.js.map +2 -2
  30. package/dist-cjs/lib/shapes/shared/RichTextLabel.js +1 -0
  31. package/dist-cjs/lib/shapes/shared/RichTextLabel.js.map +2 -2
  32. package/dist-cjs/lib/shapes/text/TextShapeUtil.js +5 -11
  33. package/dist-cjs/lib/shapes/text/TextShapeUtil.js.map +2 -2
  34. package/dist-cjs/lib/styles.js.map +2 -2
  35. package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js +43 -22
  36. package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js.map +2 -2
  37. package/dist-cjs/lib/tools/SelectTool/childStates/Idle.js +2 -15
  38. package/dist-cjs/lib/tools/SelectTool/childStates/Idle.js.map +2 -2
  39. package/dist-cjs/lib/tools/SelectTool/childStates/PointingShape.js +5 -0
  40. package/dist-cjs/lib/tools/SelectTool/childStates/PointingShape.js.map +2 -2
  41. package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js +8 -0
  42. package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js.map +2 -2
  43. package/dist-cjs/lib/tools/SelectTool/childStates/Rotating.js +8 -0
  44. package/dist-cjs/lib/tools/SelectTool/childStates/Rotating.js.map +2 -2
  45. package/dist-cjs/lib/tools/SelectTool/childStates/Translating.js +8 -0
  46. package/dist-cjs/lib/tools/SelectTool/childStates/Translating.js.map +2 -2
  47. package/dist-cjs/lib/tools/selection-logic/getHitShapeOnCanvasPointerDown.js.map +2 -2
  48. package/dist-cjs/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.js +51 -1
  49. package/dist-cjs/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.js.map +2 -2
  50. package/dist-cjs/lib/ui/components/MainMenu/DefaultMainMenuContent.js +1 -0
  51. package/dist-cjs/lib/ui/components/MainMenu/DefaultMainMenuContent.js.map +2 -2
  52. package/dist-cjs/lib/ui/components/NavigationPanel/DefaultNavigationPanel.js +3 -4
  53. package/dist-cjs/lib/ui/components/NavigationPanel/DefaultNavigationPanel.js.map +2 -2
  54. package/dist-cjs/lib/ui/components/Spinner.js +2 -25
  55. package/dist-cjs/lib/ui/components/Spinner.js.map +2 -2
  56. package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanelContent.js +2 -1
  57. package/dist-cjs/lib/ui/components/StylePanel/DefaultStylePanelContent.js.map +2 -2
  58. package/dist-cjs/lib/ui/components/Toolbar/ToggleToolLockedButton.js.map +2 -2
  59. package/dist-cjs/lib/ui/components/menu-items.js +16 -0
  60. package/dist-cjs/lib/ui/components/menu-items.js.map +2 -2
  61. package/dist-cjs/lib/ui/components/primitives/Button/TldrawUiButtonIcon.js.map +2 -2
  62. package/dist-cjs/lib/ui/components/primitives/TldrawUiDialog.js +1 -1
  63. package/dist-cjs/lib/ui/components/primitives/TldrawUiDialog.js.map +2 -2
  64. package/dist-cjs/lib/ui/components/primitives/TldrawUiIcon.js +35 -1
  65. package/dist-cjs/lib/ui/components/primitives/TldrawUiIcon.js.map +2 -2
  66. package/dist-cjs/lib/ui/components/primitives/TldrawUiSlider.js +6 -2
  67. package/dist-cjs/lib/ui/components/primitives/TldrawUiSlider.js.map +2 -2
  68. package/dist-cjs/lib/ui/components/primitives/TldrawUiToolbar.js +1 -0
  69. package/dist-cjs/lib/ui/components/primitives/TldrawUiToolbar.js.map +2 -2
  70. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.js.map +2 -2
  71. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js +0 -2
  72. package/dist-cjs/lib/ui/components/primitives/menus/TldrawUiMenuItem.js.map +2 -2
  73. package/dist-cjs/lib/ui/context/actions.js +42 -8
  74. package/dist-cjs/lib/ui/context/actions.js.map +2 -2
  75. package/dist-cjs/lib/ui/context/events.js.map +2 -2
  76. package/dist-cjs/lib/ui/hooks/menu-hooks.js.map +2 -2
  77. package/dist-cjs/lib/ui/hooks/useClipboardEvents.js +24 -7
  78. package/dist-cjs/lib/ui/hooks/useClipboardEvents.js.map +2 -2
  79. package/dist-cjs/lib/ui/hooks/useKeyboardShortcuts.js +2 -2
  80. package/dist-cjs/lib/ui/hooks/useKeyboardShortcuts.js.map +2 -2
  81. package/dist-cjs/lib/ui/hooks/useTools.js.map +2 -2
  82. package/dist-cjs/lib/ui/hooks/useTranslation/TLUiTranslationKey.js.map +1 -1
  83. package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js +8 -0
  84. package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js.map +2 -2
  85. package/dist-cjs/lib/ui/kbd-utils.js +2 -1
  86. package/dist-cjs/lib/ui/kbd-utils.js.map +2 -2
  87. package/dist-cjs/lib/ui/version.js +3 -3
  88. package/dist-cjs/lib/ui/version.js.map +1 -1
  89. package/dist-cjs/lib/utils/excalidraw/putExcalidrawContent.js +1 -1
  90. package/dist-cjs/lib/utils/excalidraw/putExcalidrawContent.js.map +2 -2
  91. package/dist-cjs/lib/utils/tldr/buildFromV1Document.js +3 -2
  92. package/dist-cjs/lib/utils/tldr/buildFromV1Document.js.map +2 -2
  93. package/dist-esm/index.d.mts +161 -95
  94. package/dist-esm/index.mjs +154 -135
  95. package/dist-esm/index.mjs.map +2 -2
  96. package/dist-esm/lib/TldrawImage.mjs +5 -2
  97. package/dist-esm/lib/TldrawImage.mjs.map +2 -2
  98. package/dist-esm/lib/canvas/TldrawCropHandles.mjs +1 -1
  99. package/dist-esm/lib/canvas/TldrawCropHandles.mjs.map +2 -2
  100. package/dist-esm/lib/canvas/TldrawHandles.mjs +1 -1
  101. package/dist-esm/lib/canvas/TldrawHandles.mjs.map +2 -2
  102. package/dist-esm/lib/canvas/TldrawOverlays.mjs +1 -1
  103. package/dist-esm/lib/canvas/TldrawOverlays.mjs.map +2 -2
  104. package/dist-esm/lib/canvas/TldrawSelectionForeground.mjs +279 -271
  105. package/dist-esm/lib/canvas/TldrawSelectionForeground.mjs.map +2 -2
  106. package/dist-esm/lib/defaultExternalContentHandlers.mjs +1 -0
  107. package/dist-esm/lib/defaultExternalContentHandlers.mjs.map +2 -2
  108. package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs +24 -36
  109. package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs.map +2 -2
  110. package/dist-esm/lib/shapes/arrow/arrowLabel.mjs +19 -5
  111. package/dist-esm/lib/shapes/arrow/arrowLabel.mjs.map +2 -2
  112. package/dist-esm/lib/shapes/arrow/toolStates/Pointing.mjs +3 -0
  113. package/dist-esm/lib/shapes/arrow/toolStates/Pointing.mjs.map +2 -2
  114. package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs +5 -5
  115. package/dist-esm/lib/shapes/frame/FrameShapeUtil.mjs.map +2 -2
  116. package/dist-esm/lib/shapes/line/LineShapeUtil.mjs +15 -1
  117. package/dist-esm/lib/shapes/line/LineShapeUtil.mjs.map +2 -2
  118. package/dist-esm/lib/shapes/shared/PathBuilder.mjs +22 -3
  119. package/dist-esm/lib/shapes/shared/PathBuilder.mjs.map +2 -2
  120. package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs +1 -0
  121. package/dist-esm/lib/shapes/shared/PlainTextLabel.mjs.map +2 -2
  122. package/dist-esm/lib/shapes/shared/RichTextLabel.mjs +1 -0
  123. package/dist-esm/lib/shapes/shared/RichTextLabel.mjs.map +2 -2
  124. package/dist-esm/lib/shapes/text/TextShapeUtil.mjs +5 -11
  125. package/dist-esm/lib/shapes/text/TextShapeUtil.mjs.map +2 -2
  126. package/dist-esm/lib/styles.mjs.map +2 -2
  127. package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs +43 -22
  128. package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs.map +2 -2
  129. package/dist-esm/lib/tools/SelectTool/childStates/Idle.mjs +2 -15
  130. package/dist-esm/lib/tools/SelectTool/childStates/Idle.mjs.map +2 -2
  131. package/dist-esm/lib/tools/SelectTool/childStates/PointingShape.mjs +5 -0
  132. package/dist-esm/lib/tools/SelectTool/childStates/PointingShape.mjs.map +2 -2
  133. package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs +8 -0
  134. package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs.map +2 -2
  135. package/dist-esm/lib/tools/SelectTool/childStates/Rotating.mjs +8 -0
  136. package/dist-esm/lib/tools/SelectTool/childStates/Rotating.mjs.map +2 -2
  137. package/dist-esm/lib/tools/SelectTool/childStates/Translating.mjs +8 -0
  138. package/dist-esm/lib/tools/SelectTool/childStates/Translating.mjs.map +2 -2
  139. package/dist-esm/lib/tools/selection-logic/getHitShapeOnCanvasPointerDown.mjs.map +2 -2
  140. package/dist-esm/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.mjs +51 -1
  141. package/dist-esm/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.mjs.map +2 -2
  142. package/dist-esm/lib/ui/components/MainMenu/DefaultMainMenuContent.mjs +2 -0
  143. package/dist-esm/lib/ui/components/MainMenu/DefaultMainMenuContent.mjs.map +2 -2
  144. package/dist-esm/lib/ui/components/NavigationPanel/DefaultNavigationPanel.mjs +3 -4
  145. package/dist-esm/lib/ui/components/NavigationPanel/DefaultNavigationPanel.mjs.map +2 -2
  146. package/dist-esm/lib/ui/components/Spinner.mjs +3 -26
  147. package/dist-esm/lib/ui/components/Spinner.mjs.map +2 -2
  148. package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanelContent.mjs +2 -1
  149. package/dist-esm/lib/ui/components/StylePanel/DefaultStylePanelContent.mjs.map +2 -2
  150. package/dist-esm/lib/ui/components/Toolbar/ToggleToolLockedButton.mjs.map +2 -2
  151. package/dist-esm/lib/ui/components/menu-items.mjs +16 -0
  152. package/dist-esm/lib/ui/components/menu-items.mjs.map +2 -2
  153. package/dist-esm/lib/ui/components/primitives/Button/TldrawUiButtonIcon.mjs.map +2 -2
  154. package/dist-esm/lib/ui/components/primitives/TldrawUiDialog.mjs +1 -1
  155. package/dist-esm/lib/ui/components/primitives/TldrawUiDialog.mjs.map +2 -2
  156. package/dist-esm/lib/ui/components/primitives/TldrawUiIcon.mjs +36 -2
  157. package/dist-esm/lib/ui/components/primitives/TldrawUiIcon.mjs.map +2 -2
  158. package/dist-esm/lib/ui/components/primitives/TldrawUiSlider.mjs +6 -2
  159. package/dist-esm/lib/ui/components/primitives/TldrawUiSlider.mjs.map +2 -2
  160. package/dist-esm/lib/ui/components/primitives/TldrawUiToolbar.mjs +1 -0
  161. package/dist-esm/lib/ui/components/primitives/TldrawUiToolbar.mjs.map +2 -2
  162. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.mjs.map +2 -2
  163. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs +0 -2
  164. package/dist-esm/lib/ui/components/primitives/menus/TldrawUiMenuItem.mjs.map +2 -2
  165. package/dist-esm/lib/ui/context/actions.mjs +42 -8
  166. package/dist-esm/lib/ui/context/actions.mjs.map +2 -2
  167. package/dist-esm/lib/ui/context/events.mjs.map +2 -2
  168. package/dist-esm/lib/ui/hooks/menu-hooks.mjs.map +2 -2
  169. package/dist-esm/lib/ui/hooks/useClipboardEvents.mjs +24 -7
  170. package/dist-esm/lib/ui/hooks/useClipboardEvents.mjs.map +2 -2
  171. package/dist-esm/lib/ui/hooks/useKeyboardShortcuts.mjs +2 -2
  172. package/dist-esm/lib/ui/hooks/useKeyboardShortcuts.mjs.map +2 -2
  173. package/dist-esm/lib/ui/hooks/useTools.mjs.map +2 -2
  174. package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs +8 -0
  175. package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs.map +2 -2
  176. package/dist-esm/lib/ui/kbd-utils.mjs +2 -1
  177. package/dist-esm/lib/ui/kbd-utils.mjs.map +2 -2
  178. package/dist-esm/lib/ui/version.mjs +3 -3
  179. package/dist-esm/lib/ui/version.mjs.map +1 -1
  180. package/dist-esm/lib/utils/excalidraw/putExcalidrawContent.mjs +1 -1
  181. package/dist-esm/lib/utils/excalidraw/putExcalidrawContent.mjs.map +2 -2
  182. package/dist-esm/lib/utils/tldr/buildFromV1Document.mjs +3 -2
  183. package/dist-esm/lib/utils/tldr/buildFromV1Document.mjs.map +2 -2
  184. package/package.json +4 -3
  185. package/src/index.ts +174 -160
  186. package/src/lib/TldrawImage.tsx +6 -2
  187. package/src/lib/canvas/TldrawCropHandles.tsx +3 -1
  188. package/src/lib/canvas/TldrawHandles.tsx +5 -1
  189. package/src/lib/canvas/TldrawOverlays.tsx +1 -1
  190. package/src/lib/canvas/TldrawSelectionForeground.tsx +5 -1
  191. package/src/lib/defaultExternalContentHandlers.ts +2 -1
  192. package/src/lib/shapes/arrow/ArrowShapeUtil.test.ts +5 -5
  193. package/src/lib/shapes/arrow/ArrowShapeUtil.tsx +25 -39
  194. package/src/lib/shapes/arrow/arrowLabel.ts +23 -3
  195. package/src/lib/shapes/arrow/toolStates/Pointing.tsx +3 -0
  196. package/src/lib/shapes/frame/FrameShapeUtil.tsx +5 -7
  197. package/src/lib/shapes/line/LineShapeUtil.tsx +19 -2
  198. package/src/lib/shapes/shared/PathBuilder.test.tsx +1 -1
  199. package/src/lib/shapes/shared/PathBuilder.tsx +35 -1
  200. package/src/lib/shapes/shared/PlainTextLabel.tsx +1 -0
  201. package/src/lib/shapes/shared/RichTextLabel.tsx +1 -0
  202. package/src/lib/shapes/text/TextShapeUtil.tsx +5 -12
  203. package/src/lib/styles.tsx +3 -1
  204. package/src/lib/tools/SelectTool/childStates/DraggingHandle.tsx +54 -30
  205. package/src/lib/tools/SelectTool/childStates/Idle.ts +2 -24
  206. package/src/lib/tools/SelectTool/childStates/PointingShape.ts +7 -0
  207. package/src/lib/tools/SelectTool/childStates/Resizing.ts +12 -1
  208. package/src/lib/tools/SelectTool/childStates/Rotating.ts +11 -0
  209. package/src/lib/tools/SelectTool/childStates/Translating.ts +11 -0
  210. package/src/lib/tools/selection-logic/getHitShapeOnCanvasPointerDown.ts +1 -0
  211. package/src/lib/ui/components/KeyboardShortcutsDialog/DefaultKeyboardShortcutsDialogContent.tsx +40 -0
  212. package/src/lib/ui/components/MainMenu/DefaultMainMenuContent.tsx +2 -0
  213. package/src/lib/ui/components/NavigationPanel/DefaultNavigationPanel.tsx +3 -4
  214. package/src/lib/ui/components/Spinner.tsx +2 -24
  215. package/src/lib/ui/components/StylePanel/DefaultStylePanelContent.tsx +1 -0
  216. package/src/lib/ui/components/Toolbar/ToggleToolLockedButton.tsx +3 -1
  217. package/src/lib/ui/components/menu-items.tsx +17 -0
  218. package/src/lib/ui/components/primitives/Button/TldrawUiButtonIcon.tsx +2 -2
  219. package/src/lib/ui/components/primitives/TldrawUiDialog.tsx +1 -1
  220. package/src/lib/ui/components/primitives/TldrawUiIcon.tsx +41 -3
  221. package/src/lib/ui/components/primitives/TldrawUiSlider.tsx +6 -1
  222. package/src/lib/ui/components/primitives/TldrawUiToolbar.tsx +4 -0
  223. package/src/lib/ui/components/primitives/menus/TldrawUiMenuCheckboxItem.tsx +2 -2
  224. package/src/lib/ui/components/primitives/menus/TldrawUiMenuItem.tsx +3 -4
  225. package/src/lib/ui/context/actions.tsx +44 -9
  226. package/src/lib/ui/context/events.tsx +4 -2
  227. package/src/lib/ui/hooks/menu-hooks.ts +1 -0
  228. package/src/lib/ui/hooks/useClipboardEvents.ts +31 -10
  229. package/src/lib/ui/hooks/useKeyboardShortcuts.ts +3 -2
  230. package/src/lib/ui/hooks/useTools.tsx +2 -1
  231. package/src/lib/ui/hooks/useTranslation/TLUiTranslationKey.ts +8 -0
  232. package/src/lib/ui/hooks/useTranslation/defaultTranslation.ts +8 -0
  233. package/src/lib/ui/kbd-utils.ts +2 -1
  234. package/src/lib/ui/version.ts +3 -3
  235. package/src/lib/ui.css +8 -22
  236. package/src/lib/utils/excalidraw/__snapshots__/putExcalidrawContent.test.tsx.snap +16 -2
  237. package/src/lib/utils/excalidraw/putExcalidrawContent.ts +1 -1
  238. package/src/lib/utils/tldr/__snapshots__/buildFromV1Document.test.ts.snap +24 -3
  239. package/src/lib/utils/tldr/buildFromV1Document.ts +2 -1
  240. package/src/test/Editor.test.tsx +68 -1
  241. package/src/test/SelectTool.test.ts +37 -11
  242. package/src/test/commands/clipboard.test.ts +1 -1
  243. package/src/test/commands/deletePage.test.ts +84 -1
  244. package/src/test/navigation.test.ts +254 -0
  245. package/src/test/shapeutils.test.ts +394 -45
  246. package/tldraw.css +29 -49
  247. package/src/test/editor.test.ts +0 -77
@@ -123,6 +123,7 @@ export function useAnySelectedShapesCount(min?: number, max?: number) {
123
123
 
124
124
  /**
125
125
  * Returns true if the number of UNLOCKED selected shapes is at least min or at most max.
126
+ * @public
126
127
  */
127
128
  export function useUnlockedSelectedShapesCount(min?: number, max?: number) {
128
129
  const editor = useEditor()
@@ -352,7 +352,7 @@ async function handleClipboardThings(editor: Editor, things: ClipboardThing[], p
352
352
 
353
353
  if (tldrawHtmlComment) {
354
354
  try {
355
- // First try parsing as plain JSON (version 2 format)
355
+ // First try parsing as plain JSON (version 2/3 formats)
356
356
  let json
357
357
  try {
358
358
  json = JSON.parse(tldrawHtmlComment)
@@ -380,19 +380,32 @@ async function handleClipboardThings(editor: Editor, things: ClipboardThing[], p
380
380
  }
381
381
 
382
382
  // Handle versioned clipboard format
383
- if (json.version === 2) {
384
- // Version 2: Assets are plain, decompress only other data
383
+ if (json.version === 3) {
384
+ // Version 3: Assets are plain, decompress only other data
385
385
  try {
386
- r({ type: 'tldraw', data: json.data })
386
+ const otherData = JSON.parse(
387
+ lz.decompressFromBase64(json.data.otherCompressed) || '{}'
388
+ )
389
+ const reconstructedData = {
390
+ assets: json.data.assets || [],
391
+ ...otherData,
392
+ }
393
+
394
+ r({ type: 'tldraw', data: reconstructedData })
387
395
  return
388
396
  } catch (error) {
389
397
  r({
390
398
  type: 'error',
391
399
  data: json,
392
- reason: `failed to parse version 2 clipboard data: ${error}`,
400
+ reason: `failed to decompress version 2 clipboard data: ${error}`,
393
401
  })
394
402
  return
395
403
  }
404
+ }
405
+ if (json.version === 2) {
406
+ // Version 2: Everything is plain, this had issues with encoding... :-/
407
+ // TODO: nix this support after some time.
408
+ r({ type: 'tldraw', data: json.data })
396
409
  } else {
397
410
  // Version 1 or no version: Legacy format
398
411
  if (typeof json.data === 'string') {
@@ -584,13 +597,21 @@ const handleNativeOrMenuCopy = async (editor: Editor) => {
584
597
  return
585
598
  }
586
599
 
587
- // Version 2: Don't compress anything.
588
- const stringifiedClipboard = JSON.stringify({
600
+ // Use versioned clipboard format for better compression
601
+ // Version 3: Don't compress assets, only compress other data
602
+ const { assets, ...otherData } = content
603
+ const clipboardData = {
589
604
  type: 'application/tldraw',
590
605
  kind: 'content',
591
- version: 2,
592
- data: content,
593
- })
606
+ version: 3,
607
+ data: {
608
+ assets: assets || [], // Plain JSON, no compression
609
+ otherCompressed: lz.compressToBase64(JSON.stringify(otherData)), // Only compress non-asset data
610
+ },
611
+ }
612
+
613
+ // Don't compress the final structure - just use plain JSON
614
+ const stringifiedClipboard = JSON.stringify(clipboardData)
594
615
 
595
616
  if (typeof navigator === 'undefined') {
596
617
  return
@@ -61,7 +61,7 @@ export function useKeyboardShortcuts() {
61
61
  if (SKIP_KBDS.includes(action.id)) continue
62
62
 
63
63
  hot(getHotkeysStringFromKbd(action.kbd), (event) => {
64
- if (areShortcutsDisabled(editor)) return
64
+ if (areShortcutsDisabled(editor) && !action.isRequiredA11yAction) return
65
65
  preventDefault(event)
66
66
  action.onSelect('kbd')
67
67
  })
@@ -149,7 +149,8 @@ export function areShortcutsDisabled(editor: Editor) {
149
149
  return (
150
150
  editor.menus.hasAnyOpenMenus() ||
151
151
  editor.getEditingShapeId() !== null ||
152
- editor.getCrashingError()
152
+ editor.getCrashingError() ||
153
+ !editor.user.getAreKeyboardShortcutsEnabled()
153
154
  )
154
155
  }
155
156
 
@@ -1,6 +1,7 @@
1
1
  import { Editor, GeoShapeGeoStyle, useMaybeEditor } from '@tldraw/editor'
2
2
  import * as React from 'react'
3
3
  import { EmbedDialog } from '../components/EmbedDialog'
4
+ import { TLUiIconJsx } from '../components/primitives/TldrawUiIcon'
4
5
  import { useA11y } from '../context/a11y'
5
6
  import { TLUiEventSource, useUiEvents } from '../context/events'
6
7
  import { TLUiIconType } from '../icon-types'
@@ -16,7 +17,7 @@ export interface TLUiToolItem<
16
17
  id: string
17
18
  label: TranslationKey
18
19
  shortcutsLabel?: TranslationKey
19
- icon: IconType
20
+ icon: IconType | TLUiIconJsx
20
21
  onSelect(source: TLUiEventSource): void
21
22
  /**
22
23
  * The keyboard shortcut for this tool. This is a string that can be a single key,
@@ -55,6 +55,7 @@ export type TLUiTranslationKey =
55
55
  | 'action.new-project'
56
56
  | 'action.new-shared-project'
57
57
  | 'action.open-cursor-chat'
58
+ | 'action.open-kbd-shortcuts'
58
59
  | 'action.open-file'
59
60
  | 'action.pack'
60
61
  | 'action.paste'
@@ -90,6 +91,8 @@ export type TLUiTranslationKey =
90
91
  | 'action.toggle-wrap-mode'
91
92
  | 'action.toggle-reduce-motion.menu'
92
93
  | 'action.toggle-reduce-motion'
94
+ | 'action.toggle-keyboard-shortcuts.menu'
95
+ | 'action.toggle-keyboard-shortcuts'
93
96
  | 'action.toggle-edge-scrolling.menu'
94
97
  | 'action.toggle-edge-scrolling'
95
98
  | 'action.toggle-debug-mode.menu'
@@ -283,11 +286,16 @@ export type TLUiTranslationKey =
283
286
  | 'a11y.repeat-shape'
284
287
  | 'a11y.move-shape'
285
288
  | 'a11y.move-shape-faster'
289
+ | 'a11y.rotate-shape-cw'
290
+ | 'a11y.rotate-shape-ccw'
291
+ | 'a11y.rotate-shape-cw-fine'
292
+ | 'a11y.rotate-shape-ccw-fine'
286
293
  | 'a11y.enlarge-shape'
287
294
  | 'a11y.shrink-shape'
288
295
  | 'a11y.pan-camera'
289
296
  | 'a11y.adjust-shape-styles'
290
297
  | 'a11y.open-context-menu'
298
+ | 'a11y.open-keyboard-shortcuts'
291
299
  | 'menu.title'
292
300
  | 'menu.theme'
293
301
  | 'menu.copy-as'
@@ -55,6 +55,7 @@ export const DEFAULT_TRANSLATION = {
55
55
  'action.new-project': 'New project',
56
56
  'action.new-shared-project': 'New shared project',
57
57
  'action.open-cursor-chat': 'Cursor chat',
58
+ 'action.open-kbd-shortcuts': 'Open keyboard shortcuts',
58
59
  'action.open-file': 'Open file',
59
60
  'action.pack': 'Pack',
60
61
  'action.paste': 'Paste',
@@ -91,6 +92,8 @@ export const DEFAULT_TRANSLATION = {
91
92
  'action.toggle-wrap-mode': 'Toggle Select on wrap',
92
93
  'action.toggle-reduce-motion.menu': 'Reduce motion',
93
94
  'action.toggle-reduce-motion': 'Toggle reduce motion',
95
+ 'action.toggle-keyboard-shortcuts.menu': 'Keyboard shortcuts',
96
+ 'action.toggle-keyboard-shortcuts': 'Toggle keyboard shortcuts',
94
97
  'action.toggle-edge-scrolling.menu': 'Edge scrolling',
95
98
  'action.toggle-edge-scrolling': 'Toggle edge scrolling',
96
99
  'action.toggle-debug-mode.menu': 'Debug mode',
@@ -284,11 +287,16 @@ export const DEFAULT_TRANSLATION = {
284
287
  'a11y.repeat-shape': 'Repeat shape',
285
288
  'a11y.move-shape': 'Move shape',
286
289
  'a11y.move-shape-faster': 'Move shape faster',
290
+ 'a11y.rotate-shape-cw': 'Rotate shape clockwise',
291
+ 'a11y.rotate-shape-ccw': 'Rotate shape counterclockwise',
292
+ 'a11y.rotate-shape-cw-fine': 'Rotate shape clockwise (fine)',
293
+ 'a11y.rotate-shape-ccw-fine': 'Rotate shape counterclockwise (fine)',
287
294
  'a11y.enlarge-shape': 'Enlarge shape',
288
295
  'a11y.shrink-shape': 'Shrink shape',
289
296
  'a11y.pan-camera': 'Pan camera',
290
297
  'a11y.adjust-shape-styles': 'Adjust shape styles',
291
298
  'a11y.open-context-menu': 'Open context menu',
299
+ 'a11y.open-keyboard-shortcuts': 'Open keyboard shortcuts',
292
300
  'menu.title': 'Menu',
293
301
  'menu.theme': 'Theme',
294
302
  'menu.copy-as': 'Copy as',
@@ -2,6 +2,7 @@ import { tlenv } from '@tldraw/editor'
2
2
 
3
3
  // N.B. We rework these Windows placeholders down below.
4
4
  const cmdKey = tlenv.isDarwin ? '⌘' : '__CTRL__'
5
+ const ctrlKey = tlenv.isDarwin ? '⌃' : '__CTRL__'
5
6
  const altKey = tlenv.isDarwin ? '⌥' : '__ALT__'
6
7
 
7
8
  /** @public */
@@ -19,7 +20,7 @@ export function kbd(str: string) {
19
20
  ? s.replace(/[[\]]/g, '')
20
21
  : s
21
22
  .replace(/cmd\+/g, cmdKey)
22
- .replace(/ctrl\+/g, cmdKey)
23
+ .replace(/ctrl\+/g, ctrlKey)
23
24
  .replace(/alt\+/g, altKey)
24
25
  .replace(/shift\+/g, '⇧')
25
26
  // Backwards compatibility with the old system.
@@ -1,9 +1,9 @@
1
1
  // This file is automatically generated by internal/scripts/refresh-assets.ts.
2
2
  // Do not edit manually. Or do, I'm a comment, not a cop.
3
3
 
4
- export const version = '3.15.0-next.f1dfcef63951'
4
+ export const version = '3.16.0-next.c30b1b5e551a'
5
5
  export const publishDates = {
6
6
  major: '2024-09-13T14:36:29.063Z',
7
- minor: '2025-07-03T10:22:18.515Z',
8
- patch: '2025-07-03T10:22:18.515Z',
7
+ minor: '2025-07-30T09:52:11.036Z',
8
+ patch: '2025-07-30T09:52:11.036Z',
9
9
  }
package/src/lib/ui.css CHANGED
@@ -964,7 +964,7 @@
964
964
  justify-content: center;
965
965
  border-radius: 99px;
966
966
  opacity: 0;
967
- animation: fade-in;
967
+ animation: tl-fade-in;
968
968
  animation-duration: 0.12s;
969
969
  animation-delay: 2s;
970
970
  animation-fill-mode: forwards;
@@ -1108,20 +1108,6 @@
1108
1108
  display: none;
1109
1109
  }
1110
1110
 
1111
- .tlui-navigation-panel__toggle .tlui-icon {
1112
- opacity: 0.24;
1113
- }
1114
-
1115
- .tlui-navigation-panel__toggle:active .tlui-icon {
1116
- opacity: 1;
1117
- }
1118
-
1119
- @media (hover: hover) {
1120
- .tlui-navigation-panel__toggle:hover .tlui-icon {
1121
- opacity: 1;
1122
- }
1123
- }
1124
-
1125
1111
  /* Minimap */
1126
1112
 
1127
1113
  .tlui-minimap {
@@ -1379,11 +1365,11 @@
1379
1365
 
1380
1366
  @media (prefers-reduced-motion: no-preference) {
1381
1367
  .tlui-toast__container[data-state='open'] {
1382
- animation: slide-in 200ms cubic-bezier(0.785, 0.135, 0.15, 0.86);
1368
+ animation: tlui-slide-in 200ms cubic-bezier(0.785, 0.135, 0.15, 0.86);
1383
1369
  }
1384
1370
 
1385
1371
  .tlui-toast__container[data-state='closed'] {
1386
- animation: hide 100ms ease-in;
1372
+ animation: tlui-fade-out 100ms ease-in;
1387
1373
  }
1388
1374
 
1389
1375
  .tlui-toast__container[data-swipe='move'] {
@@ -1396,7 +1382,7 @@
1396
1382
  }
1397
1383
 
1398
1384
  .tlui-toast__container[data-swipe='end'] {
1399
- animation: swipe-out 100ms ease-out;
1385
+ animation: tlui-slide-out 100ms ease-out;
1400
1386
  }
1401
1387
  }
1402
1388
 
@@ -1411,7 +1397,7 @@
1411
1397
  z-index: var(--layer-canvas-overlays);
1412
1398
  background-color: var(--color-overlay);
1413
1399
  pointer-events: all;
1414
- animation: fadeIn 0.12s ease-out;
1400
+ animation: tl-fade-in 0.12s ease-out;
1415
1401
  display: grid;
1416
1402
  place-items: center;
1417
1403
  overflow-y: auto;
@@ -1978,7 +1964,7 @@
1978
1964
  }
1979
1965
 
1980
1966
  /* ------------------- Animations ------------------- */
1981
- @keyframes hide {
1967
+ @keyframes tlui-fade-out {
1982
1968
  0% {
1983
1969
  opacity: 1;
1984
1970
  }
@@ -1987,7 +1973,7 @@
1987
1973
  }
1988
1974
  }
1989
1975
 
1990
- @keyframes slide-in {
1976
+ @keyframes tlui-slide-in {
1991
1977
  from {
1992
1978
  transform: translateX(calc(100% + var(--space-3)));
1993
1979
  }
@@ -1996,7 +1982,7 @@
1996
1982
  }
1997
1983
  }
1998
1984
 
1999
- @keyframes swipe-out {
1985
+ @keyframes tlui-slide-out {
2000
1986
  from {
2001
1987
  transform: translateX(var(--radix-toast-swipe-end-x));
2002
1988
  }
@@ -149,13 +149,20 @@ exports[`putExcalidrawContent test fixtures bound-arrows.json 1`] = `
149
149
  "kind": "arc",
150
150
  "labelColor": "black",
151
151
  "labelPosition": 0.5,
152
+ "richText": {
153
+ "content": [
154
+ {
155
+ "type": "paragraph",
156
+ },
157
+ ],
158
+ "type": "doc",
159
+ },
152
160
  "scale": 1,
153
161
  "size": "m",
154
162
  "start": {
155
163
  "x": 0,
156
164
  "y": 0,
157
165
  },
158
- "text": "",
159
166
  },
160
167
  "rotation": 0,
161
168
  "type": "arrow",
@@ -315,13 +322,20 @@ exports[`putExcalidrawContent test fixtures bound-elbow-arrows.json 1`] = `
315
322
  "kind": "elbow",
316
323
  "labelColor": "black",
317
324
  "labelPosition": 0.5,
325
+ "richText": {
326
+ "content": [
327
+ {
328
+ "type": "paragraph",
329
+ },
330
+ ],
331
+ "type": "doc",
332
+ },
318
333
  "scale": 1,
319
334
  "size": "m",
320
335
  "start": {
321
336
  "x": 0,
322
337
  "y": 0,
323
338
  },
324
- "text": "",
325
339
  },
326
340
  "rotation": 0,
327
341
  "type": "arrow",
@@ -221,7 +221,7 @@ export async function putExcalidrawContent(
221
221
  ...base,
222
222
  type: 'arrow',
223
223
  props: {
224
- text,
224
+ richText: toRichText(text),
225
225
  kind: element.elbowed ? 'elbow' : 'arc',
226
226
  bend: getBend(element, start, end),
227
227
  dash: getDash(element),
@@ -112,13 +112,20 @@ exports[`buildFromV1Document test fixtures arrow-binding.tldr 1`] = `
112
112
  "kind": "arc",
113
113
  "labelColor": "red",
114
114
  "labelPosition": 0.5,
115
+ "richText": {
116
+ "content": [
117
+ {
118
+ "type": "paragraph",
119
+ },
120
+ ],
121
+ "type": "doc",
122
+ },
115
123
  "scale": 1,
116
124
  "size": "m",
117
125
  "start": {
118
126
  "x": 146.32,
119
127
  "y": 0,
120
128
  },
121
- "text": "",
122
129
  },
123
130
  "rotation": 0,
124
131
  "type": "arrow",
@@ -241,13 +248,20 @@ exports[`buildFromV1Document test fixtures exact-arrow-binding.tldr 1`] = `
241
248
  "kind": "arc",
242
249
  "labelColor": "red",
243
250
  "labelPosition": 0.5,
251
+ "richText": {
252
+ "content": [
253
+ {
254
+ "type": "paragraph",
255
+ },
256
+ ],
257
+ "type": "doc",
258
+ },
244
259
  "scale": 1,
245
260
  "size": "m",
246
261
  "start": {
247
262
  "x": 293.36,
248
263
  "y": 0,
249
264
  },
250
- "text": "",
251
265
  },
252
266
  "rotation": 0,
253
267
  "type": "arrow",
@@ -389,13 +403,20 @@ exports[`buildFromV1Document test fixtures incorrect-arrow-binding.tldr 1`] = `
389
403
  "kind": "arc",
390
404
  "labelColor": "red",
391
405
  "labelPosition": 0.5,
406
+ "richText": {
407
+ "content": [
408
+ {
409
+ "type": "paragraph",
410
+ },
411
+ ],
412
+ "type": "doc",
413
+ },
392
414
  "scale": 1,
393
415
  "size": "m",
394
416
  "start": {
395
417
  "x": 252.64,
396
418
  "y": 0,
397
419
  },
398
- "text": "",
399
420
  },
400
421
  "rotation": 0,
401
422
  "type": "arrow",
@@ -405,7 +405,7 @@ export function buildFromV1Document(editor: Editor, _document: unknown) {
405
405
  ...inCommon,
406
406
  type: 'arrow',
407
407
  props: {
408
- text: v1Shape.label ?? '',
408
+ richText: toRichText(v1Shape.label ?? ''),
409
409
  color: getV2Color(v1Shape.style.color),
410
410
  labelColor: getV2Color(v1Shape.style.color),
411
411
  size: getV2Size(v1Shape.style.size),
@@ -562,6 +562,7 @@ export function buildFromV1Document(editor: Editor, _document: unknown) {
562
562
  y: point.y,
563
563
  },
564
564
  isPrecise: point.x !== 0.5 || point.y !== 0.5,
565
+ isCreatingShape: true,
565
566
  })
566
567
 
567
568
  if (change) {
@@ -49,7 +49,7 @@ beforeEach(() => {
49
49
  })
50
50
 
51
51
  const moveShapesToPage2 = () => {
52
- // directly maniuplate parentId like would happen in multiplayer situations
52
+ // directly manipulate parentId like would happen in multiplayer situations
53
53
 
54
54
  editor.updateShapes([
55
55
  { id: ids.box1, type: 'geo', parentId: ids.page2 },
@@ -899,3 +899,70 @@ describe('the geometry cache', () => {
899
899
  expect(editor.getShapePageBounds(A)!.width).toBe(200)
900
900
  })
901
901
  })
902
+ describe('editor.getShapePageBounds', () => {
903
+ it('calculates axis aligned bounds correctly', () => {
904
+ editor.createShape({
905
+ type: 'geo',
906
+ x: 99,
907
+ y: 88,
908
+ props: {
909
+ w: 199,
910
+ h: 188,
911
+ },
912
+ })
913
+ const shape = editor.getLastCreatedShape()
914
+ expect(editor.getShapePageBounds(shape)!).toMatchInlineSnapshot(`
915
+ Box {
916
+ "h": 188,
917
+ "w": 199,
918
+ "x": 99,
919
+ "y": 88,
920
+ }
921
+ `)
922
+ })
923
+
924
+ it('calculates rotated bounds correctly', () => {
925
+ editor.createShape({
926
+ type: 'geo',
927
+ x: 99,
928
+ y: 88,
929
+ rotation: Math.PI / 4,
930
+ props: {
931
+ w: 199,
932
+ h: 188,
933
+ },
934
+ })
935
+ const shape = editor.getLastCreatedShape()
936
+ expect(editor.getShapePageBounds(shape)!).toMatchInlineSnapshot(`
937
+ Box {
938
+ "h": 273.65032431919394,
939
+ "w": 273.6503243191939,
940
+ "x": -33.93607486307093,
941
+ "y": 88,
942
+ }
943
+ `)
944
+ })
945
+
946
+ it('calculates bounds based on vertices, not corners', () => {
947
+ editor.createShape({
948
+ type: 'geo',
949
+ x: 99,
950
+ y: 88,
951
+ rotation: Math.PI / 4,
952
+ props: {
953
+ geo: 'ellipse',
954
+ w: 199,
955
+ h: 188,
956
+ },
957
+ })
958
+ const shape = editor.getLastCreatedShape()
959
+ expect(editor.getShapePageBounds(shape)!).toMatchInlineSnapshot(`
960
+ Box {
961
+ "h": 193.49999999999997,
962
+ "w": 193.50000000000003,
963
+ "x": 6.139087296526014,
964
+ "y": 128.07516215959694,
965
+ }
966
+ `)
967
+ })
968
+ })
@@ -275,17 +275,27 @@ describe('PointingLabel', () => {
275
275
  type: 'arrow',
276
276
  x: 100,
277
277
  y: 100,
278
- props: { text: 'Test Label', end: { x: 100, y: 100 } },
278
+ props: {
279
+ richText: toRichText('Test Label'),
280
+ start: { x: 0, y: 0 },
281
+ end: { x: 100, y: 0 },
282
+ },
279
283
  },
280
284
  ])
281
- const shape = editor.getShape(ids.arrow1)
282
- editor.pointerDown(150, 150, {
285
+ const shape = editor.getShape(ids.arrow1)!
286
+ // First select the shape so it's already selected
287
+ editor.select(shape.id)
288
+
289
+ // Click at the middle of the arrow where the label would be and drag to move the label
290
+ editor.pointerDown(150, 100, {
283
291
  target: 'shape',
284
292
  shape,
285
293
  })
286
- editor.pointerMove(100, 100)
294
+ editor.pointerMove(160, 100)
287
295
  editor.expectToBeIn('select.pointing_arrow_label')
288
296
 
297
+ // Continue dragging to actually move the label, then it should go to idle
298
+ editor.pointerMove(170, 100)
289
299
  editor.pointerUp()
290
300
  editor.expectToBeIn('select.idle')
291
301
  })
@@ -297,16 +307,21 @@ describe('PointingLabel', () => {
297
307
  type: 'arrow',
298
308
  x: 100,
299
309
  y: 100,
300
- props: { text: 'Test Label', end: { x: 100, y: 100 } },
310
+ props: {
311
+ richText: toRichText('Test Label'),
312
+ start: { x: 0, y: 0 },
313
+ end: { x: 100, y: 0 },
314
+ },
301
315
  },
302
316
  ])
303
317
  const shape = editor.getShape(ids.arrow1)
304
318
 
305
- editor.pointerDown(150, 150, {
319
+ // Click at the middle of the arrow where the label would be
320
+ editor.pointerDown(150, 100, {
306
321
  target: 'shape',
307
322
  shape,
308
323
  })
309
- editor.pointerMove(100, 100)
324
+ editor.pointerMove(160, 100)
310
325
  editor.expectToBeIn('select.pointing_arrow_label')
311
326
  editor.cancel()
312
327
  editor.expectToBeIn('select.idle')
@@ -314,14 +329,25 @@ describe('PointingLabel', () => {
314
329
 
315
330
  it('Doesnt go into pointing_arrow_label mode if not selecting the arrow shape', () => {
316
331
  editor.createShapes<TLArrowShape>([
317
- { id: ids.arrow1, type: 'arrow', x: 100, y: 100, props: { text: 'Test Label' } },
332
+ {
333
+ id: ids.arrow1,
334
+ type: 'arrow',
335
+ x: 100,
336
+ y: 100,
337
+ props: {
338
+ richText: toRichText(''), // Empty label
339
+ start: { x: 0, y: 0 },
340
+ end: { x: 100, y: 0 },
341
+ },
342
+ },
318
343
  ])
319
- const shape = editor.getShape(ids.arrow1)
320
- editor.pointerDown(0, 150, {
344
+ const shape = editor.getShape(ids.arrow1)!
345
+ // Click anywhere on the arrow - since there's no label, it should go to translating
346
+ editor.pointerDown(150, 100, {
321
347
  target: 'shape',
322
348
  shape,
323
349
  })
324
- editor.pointerMove(100, 100)
350
+ editor.pointerMove(155, 105)
325
351
  editor.expectToBeIn('select.translating')
326
352
 
327
353
  editor.pointerUp()
@@ -33,7 +33,7 @@ const doMockClipboard = () => {
33
33
  },
34
34
  })
35
35
 
36
- globalThis.ClipboardItem = jest.fn((payload: any) => payload)
36
+ globalThis.ClipboardItem = jest.fn((payload: any) => payload) as any
37
37
 
38
38
  return context
39
39
  }