tldraw 4.1.1 → 4.2.0-next.d76c345101d5

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 (111) hide show
  1. package/dist-cjs/index.d.ts +15 -11
  2. package/dist-cjs/index.js +3 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/defaultEmbedDefinitions.js +2 -25
  5. package/dist-cjs/lib/defaultEmbedDefinitions.js.map +2 -2
  6. package/dist-cjs/lib/defaultExternalContentHandlers.js +8 -31
  7. package/dist-cjs/lib/defaultExternalContentHandlers.js.map +2 -2
  8. package/dist-cjs/lib/shapes/bookmark/BookmarkShapeUtil.js +31 -101
  9. package/dist-cjs/lib/shapes/bookmark/BookmarkShapeUtil.js.map +2 -2
  10. package/dist-cjs/lib/shapes/bookmark/bookmarks.js +138 -0
  11. package/dist-cjs/lib/shapes/bookmark/bookmarks.js.map +7 -0
  12. package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js +25 -3
  13. package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js.map +2 -2
  14. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/Cropping.js +20 -4
  15. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/Cropping.js.map +2 -2
  16. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.js +23 -11
  17. package/dist-cjs/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.js.map +2 -2
  18. package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js +18 -5
  19. package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js.map +2 -2
  20. package/dist-cjs/lib/tools/SelectTool/childStates/PointingArrowLabel.js +21 -9
  21. package/dist-cjs/lib/tools/SelectTool/childStates/PointingArrowLabel.js.map +2 -2
  22. package/dist-cjs/lib/tools/SelectTool/childStates/PointingResizeHandle.js +24 -8
  23. package/dist-cjs/lib/tools/SelectTool/childStates/PointingResizeHandle.js.map +2 -2
  24. package/dist-cjs/lib/tools/SelectTool/childStates/PointingRotateHandle.js +21 -9
  25. package/dist-cjs/lib/tools/SelectTool/childStates/PointingRotateHandle.js.map +2 -2
  26. package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js +23 -8
  27. package/dist-cjs/lib/tools/SelectTool/childStates/Resizing.js.map +2 -2
  28. package/dist-cjs/lib/tools/SelectTool/childStates/Rotating.js +21 -9
  29. package/dist-cjs/lib/tools/SelectTool/childStates/Rotating.js.map +2 -2
  30. package/dist-cjs/lib/tools/SelectTool/childStates/Translating.js +26 -11
  31. package/dist-cjs/lib/tools/SelectTool/childStates/Translating.js.map +2 -2
  32. package/dist-cjs/lib/ui/components/DebugMenu/DefaultDebugMenuContent.js +2 -2
  33. package/dist-cjs/lib/ui/components/DebugMenu/DefaultDebugMenuContent.js.map +1 -1
  34. package/dist-cjs/lib/ui/components/menu-items.js +2 -2
  35. package/dist-cjs/lib/ui/components/menu-items.js.map +1 -1
  36. package/dist-cjs/lib/ui/context/actions.js +23 -29
  37. package/dist-cjs/lib/ui/context/actions.js.map +2 -2
  38. package/dist-cjs/lib/ui/hooks/useEditorEvents.js +1 -1
  39. package/dist-cjs/lib/ui/hooks/useEditorEvents.js.map +1 -1
  40. package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js +4 -4
  41. package/dist-cjs/lib/ui/hooks/useTranslation/defaultTranslation.js.map +1 -1
  42. package/dist-cjs/lib/ui/version.js +3 -3
  43. package/dist-cjs/lib/ui/version.js.map +1 -1
  44. package/dist-esm/index.d.mts +15 -11
  45. package/dist-esm/index.mjs +3 -1
  46. package/dist-esm/index.mjs.map +2 -2
  47. package/dist-esm/lib/defaultEmbedDefinitions.mjs +2 -25
  48. package/dist-esm/lib/defaultEmbedDefinitions.mjs.map +2 -2
  49. package/dist-esm/lib/defaultExternalContentHandlers.mjs +8 -31
  50. package/dist-esm/lib/defaultExternalContentHandlers.mjs.map +2 -2
  51. package/dist-esm/lib/shapes/bookmark/BookmarkShapeUtil.mjs +34 -101
  52. package/dist-esm/lib/shapes/bookmark/BookmarkShapeUtil.mjs.map +2 -2
  53. package/dist-esm/lib/shapes/bookmark/bookmarks.mjs +124 -0
  54. package/dist-esm/lib/shapes/bookmark/bookmarks.mjs.map +7 -0
  55. package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs +26 -3
  56. package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs.map +2 -2
  57. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/Cropping.mjs +20 -4
  58. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/Cropping.mjs.map +2 -2
  59. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.mjs +23 -11
  60. package/dist-esm/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.mjs.map +2 -2
  61. package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs +18 -5
  62. package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs.map +2 -2
  63. package/dist-esm/lib/tools/SelectTool/childStates/PointingArrowLabel.mjs +21 -9
  64. package/dist-esm/lib/tools/SelectTool/childStates/PointingArrowLabel.mjs.map +2 -2
  65. package/dist-esm/lib/tools/SelectTool/childStates/PointingResizeHandle.mjs +24 -8
  66. package/dist-esm/lib/tools/SelectTool/childStates/PointingResizeHandle.mjs.map +2 -2
  67. package/dist-esm/lib/tools/SelectTool/childStates/PointingRotateHandle.mjs +21 -9
  68. package/dist-esm/lib/tools/SelectTool/childStates/PointingRotateHandle.mjs.map +2 -2
  69. package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs +23 -8
  70. package/dist-esm/lib/tools/SelectTool/childStates/Resizing.mjs.map +2 -2
  71. package/dist-esm/lib/tools/SelectTool/childStates/Rotating.mjs +21 -9
  72. package/dist-esm/lib/tools/SelectTool/childStates/Rotating.mjs.map +2 -2
  73. package/dist-esm/lib/tools/SelectTool/childStates/Translating.mjs +26 -11
  74. package/dist-esm/lib/tools/SelectTool/childStates/Translating.mjs.map +2 -2
  75. package/dist-esm/lib/ui/components/DebugMenu/DefaultDebugMenuContent.mjs +2 -2
  76. package/dist-esm/lib/ui/components/DebugMenu/DefaultDebugMenuContent.mjs.map +1 -1
  77. package/dist-esm/lib/ui/components/menu-items.mjs +2 -2
  78. package/dist-esm/lib/ui/components/menu-items.mjs.map +1 -1
  79. package/dist-esm/lib/ui/context/actions.mjs +23 -29
  80. package/dist-esm/lib/ui/context/actions.mjs.map +2 -2
  81. package/dist-esm/lib/ui/hooks/useEditorEvents.mjs +1 -1
  82. package/dist-esm/lib/ui/hooks/useEditorEvents.mjs.map +1 -1
  83. package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs +4 -4
  84. package/dist-esm/lib/ui/hooks/useTranslation/defaultTranslation.mjs.map +1 -1
  85. package/dist-esm/lib/ui/version.mjs +3 -3
  86. package/dist-esm/lib/ui/version.mjs.map +1 -1
  87. package/package.json +3 -3
  88. package/src/index.ts +1 -0
  89. package/src/lib/defaultEmbedDefinitions.ts +2 -25
  90. package/src/lib/defaultExternalContentHandlers.ts +10 -35
  91. package/src/lib/shapes/bookmark/BookmarkShapeUtil.tsx +39 -133
  92. package/src/lib/shapes/bookmark/bookmarks.ts +170 -0
  93. package/src/lib/shapes/embed/EmbedShapeUtil.tsx +28 -2
  94. package/src/lib/tools/SelectTool/childStates/Crop/children/Cropping.ts +23 -6
  95. package/src/lib/tools/SelectTool/childStates/Crop/children/PointingCropHandle.ts +24 -12
  96. package/src/lib/tools/SelectTool/childStates/DraggingHandle.tsx +21 -10
  97. package/src/lib/tools/SelectTool/childStates/PointingArrowLabel.ts +23 -11
  98. package/src/lib/tools/SelectTool/childStates/PointingResizeHandle.ts +26 -9
  99. package/src/lib/tools/SelectTool/childStates/PointingRotateHandle.ts +23 -10
  100. package/src/lib/tools/SelectTool/childStates/Resizing.ts +24 -9
  101. package/src/lib/tools/SelectTool/childStates/Rotating.ts +27 -11
  102. package/src/lib/tools/SelectTool/childStates/Translating.ts +28 -12
  103. package/src/lib/ui/components/DebugMenu/DefaultDebugMenuContent.tsx +2 -2
  104. package/src/lib/ui/components/menu-items.tsx +2 -2
  105. package/src/lib/ui/context/actions.tsx +27 -31
  106. package/src/lib/ui/hooks/useEditorEvents.ts +1 -1
  107. package/src/lib/ui/hooks/useTranslation/defaultTranslation.ts +4 -4
  108. package/src/lib/ui/version.ts +3 -3
  109. package/src/lib/utils/embeds/embeds.test.ts +16 -34
  110. package/src/test/SelectTool.test.ts +251 -0
  111. package/src/test/bookmark-shapes.test.ts +129 -7
@@ -19,14 +19,20 @@ export class Rotating extends StateNode {
19
19
 
20
20
  snapshot = {} as TLRotationSnapshot
21
21
 
22
- info = {} as Extract<TLPointerEventInfo, { target: 'selection' }> & { onInteractionEnd?: string }
22
+ info = {} as Extract<TLPointerEventInfo, { target: 'selection' }> & {
23
+ onInteractionEnd?: string | (() => void)
24
+ }
23
25
 
24
26
  markId = ''
25
27
 
26
- override onEnter(info: TLPointerEventInfo & { target: 'selection'; onInteractionEnd?: string }) {
28
+ override onEnter(
29
+ info: TLPointerEventInfo & { target: 'selection'; onInteractionEnd?: string | (() => void) }
30
+ ) {
27
31
  // Store the event information
28
32
  this.info = info
29
- this.parent.setCurrentToolIdMask(info.onInteractionEnd)
33
+ if (typeof info.onInteractionEnd === 'string') {
34
+ this.parent.setCurrentToolIdMask(info.onInteractionEnd)
35
+ }
30
36
 
31
37
  this.markId = this.editor.markHistoryStoppingPoint('rotate start')
32
38
 
@@ -121,11 +127,16 @@ export class Rotating extends StateNode {
121
127
  })
122
128
 
123
129
  this.editor.bailToMark(this.markId)
124
- if (this.info.onInteractionEnd) {
125
- this.editor.setCurrentTool(this.info.onInteractionEnd, this.info)
126
- } else {
127
- this.parent.transition('idle', this.info)
130
+ const { onInteractionEnd } = this.info
131
+ if (onInteractionEnd) {
132
+ if (typeof onInteractionEnd === 'string') {
133
+ this.editor.setCurrentTool(onInteractionEnd, this.info)
134
+ } else {
135
+ onInteractionEnd()
136
+ }
137
+ return
128
138
  }
139
+ this.parent.transition('idle', this.info)
129
140
  }
130
141
 
131
142
  private complete() {
@@ -139,11 +150,16 @@ export class Rotating extends StateNode {
139
150
  this.editor,
140
151
  this.snapshot.shapeSnapshots.map((s) => s.shape.id)
141
152
  )
142
- if (this.info.onInteractionEnd) {
143
- this.editor.setCurrentTool(this.info.onInteractionEnd, this.info)
144
- } else {
145
- this.parent.transition('idle', this.info)
153
+ const { onInteractionEnd } = this.info
154
+ if (onInteractionEnd) {
155
+ if (typeof onInteractionEnd === 'string') {
156
+ this.editor.setCurrentTool(onInteractionEnd, this.info)
157
+ } else {
158
+ onInteractionEnd()
159
+ }
160
+ return
146
161
  }
162
+ this.parent.transition('idle', this.info)
147
163
  }
148
164
 
149
165
  _getRotationFromPointerPosition({ snapToNearestDegree }: { snapToNearestDegree: boolean }) {
@@ -28,7 +28,7 @@ export type TranslatingInfo = TLPointerEventInfo & {
28
28
  isCreating?: boolean
29
29
  creatingMarkId?: string
30
30
  onCreate?(): void
31
- onInteractionEnd?: string
31
+ onInteractionEnd?: string | (() => void)
32
32
  }
33
33
 
34
34
  export class Translating extends StateNode {
@@ -59,7 +59,9 @@ export class Translating extends StateNode {
59
59
  }
60
60
 
61
61
  this.info = info
62
- this.parent.setCurrentToolIdMask(info.onInteractionEnd)
62
+ if (typeof info.onInteractionEnd === 'string') {
63
+ this.parent.setCurrentToolIdMask(info.onInteractionEnd)
64
+ }
63
65
  this.isCreating = isCreating
64
66
 
65
67
  this.markId = ''
@@ -190,15 +192,24 @@ export class Translating extends StateNode {
190
192
  this.snapshot.movingShapes.map((s) => s.id)
191
193
  )
192
194
 
193
- if (this.editor.getInstanceState().isToolLocked && this.info.onInteractionEnd) {
194
- this.editor.setCurrentTool(this.info.onInteractionEnd)
195
- } else {
196
- if (this.isCreating) {
197
- this.onCreate?.(this.editor.getOnlySelectedShape())
195
+ const { onInteractionEnd } = this.info
196
+ if (onInteractionEnd) {
197
+ if (typeof onInteractionEnd === 'string') {
198
+ if (this.editor.getInstanceState().isToolLocked) {
199
+ this.editor.setCurrentTool(onInteractionEnd)
200
+ return
201
+ }
198
202
  } else {
199
- this.parent.transition('idle')
203
+ onInteractionEnd()
204
+ return
200
205
  }
201
206
  }
207
+
208
+ if (this.isCreating) {
209
+ this.onCreate?.(this.editor.getOnlySelectedShape())
210
+ } else {
211
+ this.parent.transition('idle')
212
+ }
202
213
  }
203
214
 
204
215
  private cancel() {
@@ -214,11 +225,16 @@ export class Translating extends StateNode {
214
225
  })
215
226
 
216
227
  this.reset()
217
- if (this.info.onInteractionEnd) {
218
- this.editor.setCurrentTool(this.info.onInteractionEnd)
219
- } else {
220
- this.parent.transition('idle', this.info)
228
+ const { onInteractionEnd } = this.info
229
+ if (onInteractionEnd) {
230
+ if (typeof onInteractionEnd === 'string') {
231
+ this.editor.setCurrentTool(onInteractionEnd)
232
+ } else {
233
+ onInteractionEnd()
234
+ }
235
+ return
221
236
  }
237
+ this.parent.transition('idle', this.info)
222
238
  }
223
239
 
224
240
  protected handleStart() {
@@ -172,7 +172,7 @@ export function DebugFlags() {
172
172
  const items = Object.values(debugFlags)
173
173
  if (!items.length) return null
174
174
  return (
175
- <TldrawUiMenuSubmenu id="debug flags" label="Debug Flags">
175
+ <TldrawUiMenuSubmenu id="debug flags" label="Debug flags">
176
176
  <TldrawUiMenuGroup id="debug flags">
177
177
  {items.map((flag) => (
178
178
  <DebugFlagToggle key={flag.name} flag={flag} />
@@ -186,7 +186,7 @@ export function FeatureFlags() {
186
186
  const items = Object.values(featureFlags)
187
187
  if (!items.length) return null
188
188
  return (
189
- <TldrawUiMenuSubmenu id="feature flags" label="Feature Flags">
189
+ <TldrawUiMenuSubmenu id="feature flags" label="Feature flags">
190
190
  <TldrawUiMenuGroup id="feature flags">
191
191
  {items.map((flag) => (
192
192
  <DebugFlagToggle key={flag.name} flag={flag} />
@@ -482,11 +482,11 @@ export function MoveToPageMenu() {
482
482
 
483
483
  if (toPage) {
484
484
  addToast({
485
- title: 'Changed Page',
485
+ title: 'Changed page',
486
486
  description: `Moved to ${toPage.name}.`,
487
487
  actions: [
488
488
  {
489
- label: 'Go Back',
489
+ label: 'Go back',
490
490
  type: 'primary',
491
491
  onClick: () => {
492
492
  editor.markHistoryStoppingPoint('change-page')
@@ -5,6 +5,7 @@ import {
5
5
  Editor,
6
6
  HALF_PI,
7
7
  PageRecordType,
8
+ Result,
8
9
  TLBookmarkShape,
9
10
  TLEmbedShape,
10
11
  TLFrameShape,
@@ -24,6 +25,7 @@ import {
24
25
  useMaybeEditor,
25
26
  } from '@tldraw/editor'
26
27
  import * as React from 'react'
28
+ import { createBookmarkFromUrl } from '../../shapes/bookmark/bookmarks'
27
29
  import { fitFrameToContent, removeFrame } from '../../utils/frames/frames'
28
30
  import { generateShapeAnnouncementMessage } from '../components/A11y'
29
31
  import { EditLinkDialog } from '../components/EditLinkDialog'
@@ -387,45 +389,39 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
387
389
  {
388
390
  id: 'convert-to-bookmark',
389
391
  label: 'action.convert-to-bookmark',
390
- onSelect(source) {
392
+ async onSelect(source) {
391
393
  if (!canApplySelectionAction()) return
392
394
  if (mustGoBackToSelectToolFirst()) return
393
395
 
394
- editor.run(() => {
395
- trackEvent('convert-to-bookmark', { source })
396
- const shapes = editor.getSelectedShapes()
396
+ trackEvent('convert-to-bookmark', { source })
397
+ const shapes = editor.getSelectedShapes()
397
398
 
398
- const createList: TLShapePartial[] = []
399
- const deleteList: TLShapeId[] = []
400
- for (const shape of shapes) {
401
- if (!shape || !editor.isShapeOfType<TLEmbedShape>(shape, 'embed') || !shape.props.url)
402
- continue
399
+ const markId = editor.markHistoryStoppingPoint('convert shapes to bookmark')
403
400
 
404
- const newPos = new Vec(shape.x, shape.y)
405
- newPos.rot(-shape.rotation)
406
- newPos.add(new Vec(shape.props.w / 2 - 300 / 2, shape.props.h / 2 - 320 / 2)) // see bookmark shape util
407
- newPos.rot(shape.rotation)
408
- const partial: TLShapePartial<TLBookmarkShape> = {
409
- id: createShapeId(),
410
- type: 'bookmark',
411
- rotation: shape.rotation,
412
- x: newPos.x,
413
- y: newPos.y,
414
- opacity: 1,
415
- props: {
416
- url: shape.props.url,
417
- },
418
- }
401
+ const creationPromises: Promise<Result<any, any>>[] = []
419
402
 
420
- createList.push(partial)
421
- deleteList.push(shape.id)
422
- }
403
+ for (const shape of shapes) {
404
+ if (!shape || !editor.isShapeOfType<TLEmbedShape>(shape, 'embed') || !shape.props.url)
405
+ continue
423
406
 
424
- editor.markHistoryStoppingPoint('convert shapes to bookmark')
407
+ const center = editor.getShapePageBounds(shape)?.center
425
408
 
426
- // Should be able to create the shape since we're about to delete the other other
427
- editor.deleteShapes(deleteList)
428
- editor.createShapes(createList)
409
+ if (!center) continue
410
+ editor.deleteShapes([shape.id])
411
+
412
+ creationPromises.push(
413
+ createBookmarkFromUrl(editor, { url: shape.props.url, center }).then((res) => {
414
+ if (!res.ok) {
415
+ throw new Error(res.error)
416
+ }
417
+ return res
418
+ })
419
+ )
420
+ }
421
+
422
+ await Promise.all(creationPromises).catch((error) => {
423
+ editor.bailToMark(markId)
424
+ console.error(error)
429
425
  })
430
426
  },
431
427
  },
@@ -10,7 +10,7 @@ export function useEditorEvents() {
10
10
  useEffect(() => {
11
11
  function handleMaxShapes({ name, count }: { name: string; pageId: string; count: number }) {
12
12
  addToast({
13
- title: 'Maximum Shapes Reached',
13
+ title: 'Maximum shapes reached',
14
14
  description: `You've reached the maximum number of shapes allowed on ${name} (${count}). Please delete some shapes or move to a different page to continue.`,
15
15
  severity: 'warning',
16
16
  })
@@ -8,8 +8,8 @@ export const DEFAULT_TRANSLATION = {
8
8
  'action.toggle-auto-none': 'Auto',
9
9
  'action.toggle-mouse': 'Mouse',
10
10
  'action.toggle-trackpad': 'Trackpad',
11
- 'action.convert-to-bookmark': 'Convert to Bookmark',
12
- 'action.convert-to-embed': 'Convert to Embed',
11
+ 'action.convert-to-bookmark': 'Convert to bookmark',
12
+ 'action.convert-to-embed': 'Convert to embed',
13
13
  'action.open-embed-link': 'Open link',
14
14
  'action.align-bottom': 'Align bottom',
15
15
  'action.align-center-horizontal': 'Align horizontally',
@@ -94,7 +94,7 @@ export const DEFAULT_TRANSLATION = {
94
94
  'action.toggle-paste-at-cursor.menu': 'Paste at cursor',
95
95
  'action.toggle-paste-at-cursor': 'Toggle paste at cursor',
96
96
  'action.toggle-wrap-mode.menu': 'Select on wrap',
97
- 'action.toggle-wrap-mode': 'Toggle Select on wrap',
97
+ 'action.toggle-wrap-mode': 'Toggle select on wrap',
98
98
  'action.toggle-reduce-motion.menu': 'Reduce motion',
99
99
  'action.toggle-reduce-motion': 'Toggle reduce motion',
100
100
  'action.toggle-keyboard-shortcuts.menu': 'Enable keyboard shortcuts',
@@ -364,7 +364,7 @@ export const DEFAULT_TRANSLATION = {
364
364
  'people-menu.change-color': 'Change color',
365
365
  'people-menu.follow': 'Following',
366
366
  'people-menu.following': 'Following',
367
- 'people-menu.leading': 'Following You',
367
+ 'people-menu.leading': 'Following you',
368
368
  'people-menu.user': '(You)',
369
369
  'people-menu.invite': 'Invite others',
370
370
  'people-menu.anonymous-user': 'New user',
@@ -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 = '4.1.1'
4
+ export const version = '4.2.0-next.d76c345101d5'
5
5
  export const publishDates = {
6
6
  major: '2025-09-18T14:39:22.803Z',
7
- minor: '2025-10-15T12:23:29.168Z',
8
- patch: '2025-10-15T13:38:40.898Z',
7
+ minor: '2025-10-15T13:27:49.690Z',
8
+ patch: '2025-10-15T13:27:49.690Z',
9
9
  }
@@ -275,6 +275,14 @@ const MATCH_URL_TEST_URLS: (MatchUrlTestNoMatchDef | MatchUrlTestMatchDef)[] = [
275
275
  embedUrl: `https://replit.com/@omar/Blob-Generator?embed=true`,
276
276
  },
277
277
  },
278
+ {
279
+ url: 'https://replit.com/@omar/Blob-Generator#index.html',
280
+ match: true,
281
+ output: {
282
+ type: 'replit',
283
+ embedUrl: `https://replit.com/@omar/Blob-Generator?embed=true#index.html`,
284
+ },
285
+ },
278
286
  {
279
287
  url: 'https://replit.com/foobar',
280
288
  match: false,
@@ -347,23 +355,6 @@ const MATCH_URL_TEST_URLS: (MatchUrlTestNoMatchDef | MatchUrlTestMatchDef)[] = [
347
355
  url: 'https://vimeo.com/foobar',
348
356
  match: false,
349
357
  },
350
- // excalidraw
351
- {
352
- url: 'https://excalidraw.com/#room=asdkjashdkjhaskdjh,sadkjhakjshdkjahd',
353
- match: true,
354
- output: {
355
- type: 'excalidraw',
356
- embedUrl: `https://excalidraw.com/#room=asdkjashdkjhaskdjh,sadkjhakjshdkjahd`,
357
- },
358
- },
359
- {
360
- url: 'https://excalidraw.com',
361
- match: false,
362
- },
363
- {
364
- url: 'https://excalidraw.com/help',
365
- match: false,
366
- },
367
358
  //desmos
368
359
  {
369
360
  url: 'https://www.desmos.com/calculator/js9hryvejc',
@@ -599,6 +590,14 @@ const MATCH_EMBED_TEST_URLS: (MatchEmbedTestMatchDef | MatchEmbedTestNoMatchDef)
599
590
  url: `https://replit.com/@omar/Blob-Generator`,
600
591
  },
601
592
  },
593
+ {
594
+ embedUrl: 'https://replit.com/@omar/Blob-Generator?embed=true#index.html',
595
+ match: true,
596
+ output: {
597
+ type: 'replit',
598
+ url: `https://replit.com/@omar/Blob-Generator#index.html`,
599
+ },
600
+ },
602
601
  {
603
602
  embedUrl: 'https://replit.com/@omar/Blob-Generator',
604
603
  match: false,
@@ -671,23 +670,6 @@ const MATCH_EMBED_TEST_URLS: (MatchEmbedTestMatchDef | MatchEmbedTestNoMatchDef)
671
670
  embedUrl: 'https://vimeo.com/foobar',
672
671
  match: false,
673
672
  },
674
- // excalidraw
675
- {
676
- embedUrl: 'https://excalidraw.com/#room=asdkjashdkjhaskdjh,sadkjhakjshdkjahd',
677
- match: true,
678
- output: {
679
- type: 'excalidraw',
680
- url: `https://excalidraw.com/#room=asdkjashdkjhaskdjh,sadkjhakjshdkjahd`,
681
- },
682
- },
683
- {
684
- embedUrl: 'https://excalidraw.com',
685
- match: false,
686
- },
687
- {
688
- embedUrl: 'https://excalidraw.com/help',
689
- match: false,
690
- },
691
673
  // desmos
692
674
  {
693
675
  embedUrl: 'https://www.desmos.com/calculator/js9hryvejc?embed',
@@ -648,3 +648,254 @@ test('right clicking a shape inside of a group does not focus the group if the g
648
648
  editor.pointerUp(100, 100, { target: 'shape', button: 0, shape: editor.getShape(boxAId)! })
649
649
  expect(editor.getFocusedGroupId()).toBe(groupId)
650
650
  })
651
+
652
+ describe('when passing a function to onInteractionEnd', () => {
653
+ it('calls the function for cropping', () => {
654
+ const id = createShapeId('image')
655
+ editor.createShapes([
656
+ {
657
+ id,
658
+ type: 'image',
659
+ x: 100,
660
+ y: 100,
661
+ props: {
662
+ w: 1200,
663
+ h: 800,
664
+ },
665
+ },
666
+ ])
667
+
668
+ editor.select(id)
669
+
670
+ const fn = vi.fn()
671
+ editor.setCurrentTool('select.cropping', {
672
+ handle: 'bottom_right',
673
+ onInteractionEnd: fn,
674
+ })
675
+ editor.pointerUp(50, 50)
676
+
677
+ expect(fn).toHaveBeenCalled()
678
+ })
679
+
680
+ it('calls the function for pointing crop handle', () => {
681
+ const fn = vi.fn()
682
+ editor.setCurrentTool('select.crop.pointing_crop_handle', {
683
+ onInteractionEnd: fn,
684
+ })
685
+ editor.pointerUp(50, 50)
686
+ expect(fn).toHaveBeenCalled()
687
+ })
688
+
689
+ it('calls the function for pointing arrow label', () => {
690
+ const fn = vi.fn()
691
+ const id = createShapeId('arrow')
692
+
693
+ const arrow = {
694
+ id,
695
+ type: 'arrow' as const,
696
+ x: 100,
697
+ y: 100,
698
+ props: {
699
+ richText: toRichText('Test Label'),
700
+ start: { x: 0, y: 0 },
701
+ end: { x: 100, y: 0 },
702
+ },
703
+ }
704
+
705
+ editor.createShapes<TLArrowShape>([arrow])
706
+
707
+ editor.setCurrentTool('select.pointing_arrow_label', {
708
+ shape: arrow,
709
+ onInteractionEnd: fn,
710
+ })
711
+ editor.pointerUp(50, 50)
712
+ expect(fn).toHaveBeenCalled()
713
+ })
714
+
715
+ it('calls the function for pointing a resize handle', () => {
716
+ const fn = vi.fn()
717
+ editor.setCurrentTool('select.pointing_resize_handle', {
718
+ target: 'selection',
719
+ handle: 'bottom_right',
720
+ onInteractionEnd: fn,
721
+ })
722
+ editor.pointerUp(50, 50)
723
+ expect(fn).toHaveBeenCalled()
724
+ })
725
+
726
+ it('calls the function for pointing a rotate handle', () => {
727
+ const fn = vi.fn()
728
+ editor.setCurrentTool('select.pointing_rotate_handle', {
729
+ target: 'selection',
730
+ handle: 'bottom_right_rotate',
731
+ onInteractionEnd: fn,
732
+ })
733
+ editor.pointerUp(50, 50)
734
+ expect(fn).toHaveBeenCalled()
735
+ })
736
+
737
+ it('calls the function for resizing', () => {
738
+ const id = createShapeId('box')
739
+ editor.createShapes([
740
+ {
741
+ id,
742
+ type: 'geo',
743
+ x: 100,
744
+ y: 100,
745
+ },
746
+ ])
747
+
748
+ editor.select(id)
749
+
750
+ const fn = vi.fn()
751
+ editor.setCurrentTool('select.resizing', {
752
+ target: 'selection',
753
+ handle: 'bottom_right',
754
+ onInteractionEnd: fn,
755
+ })
756
+ editor.pointerUp(50, 50)
757
+ expect(fn).toHaveBeenCalled()
758
+ })
759
+
760
+ it('calls the function for translating', () => {
761
+ const id = createShapeId('box')
762
+ editor.createShapes([
763
+ {
764
+ id,
765
+ type: 'geo',
766
+ x: 100,
767
+ y: 100,
768
+ },
769
+ ])
770
+ editor.select(id)
771
+
772
+ const fn = vi.fn()
773
+ editor.setCurrentTool('select.translating', {
774
+ onInteractionEnd: fn,
775
+ })
776
+ editor.pointerUp(50, 50)
777
+ expect(fn).toHaveBeenCalled()
778
+ })
779
+ })
780
+
781
+ describe('when passing a string to onInteractionEnd', () => {
782
+ it('transitions to the tool for cropping', () => {
783
+ const id = createShapeId('image')
784
+ editor.createShapes([
785
+ {
786
+ id,
787
+ type: 'image',
788
+ x: 100,
789
+ y: 100,
790
+ props: {
791
+ w: 1200,
792
+ h: 800,
793
+ },
794
+ },
795
+ ])
796
+
797
+ editor.select(id)
798
+
799
+ editor.setCurrentTool('select.cropping', {
800
+ handle: 'bottom_right',
801
+ onInteractionEnd: 'select.idle',
802
+ })
803
+ editor.pointerUp(50, 50)
804
+
805
+ editor.expectToBeIn('select.idle')
806
+ })
807
+
808
+ it('transitions to the tool for pointing crop handle', () => {
809
+ editor.setCurrentTool('select.crop.pointing_crop_handle', {
810
+ onInteractionEnd: 'select.idle',
811
+ })
812
+ editor.pointerUp(50, 50)
813
+ editor.expectToBeIn('select.idle')
814
+ })
815
+
816
+ it('transitions to the tool for pointing arrow label', () => {
817
+ const id = createShapeId('arrow')
818
+
819
+ const arrow = {
820
+ id,
821
+ type: 'arrow' as const,
822
+ x: 100,
823
+ y: 100,
824
+ props: {
825
+ richText: toRichText('Test Label'),
826
+ start: { x: 0, y: 0 },
827
+ end: { x: 100, y: 0 },
828
+ },
829
+ }
830
+
831
+ editor.createShapes<TLArrowShape>([arrow])
832
+
833
+ editor.setCurrentTool('select.pointing_arrow_label', {
834
+ shape: arrow,
835
+ onInteractionEnd: 'select.idle',
836
+ })
837
+ editor.pointerUp(50, 50)
838
+ editor.expectToBeIn('select.idle')
839
+ })
840
+
841
+ it('transitions to the tool for pointing a resize handle', () => {
842
+ editor.setCurrentTool('select.pointing_resize_handle', {
843
+ target: 'selection',
844
+ handle: 'bottom_right',
845
+ onInteractionEnd: 'select.idle',
846
+ })
847
+ editor.pointerUp(50, 50)
848
+ editor.expectToBeIn('select.idle')
849
+ })
850
+
851
+ it('transitions to the tool for pointing a rotate handle', () => {
852
+ editor.setCurrentTool('select.pointing_rotate_handle', {
853
+ target: 'selection',
854
+ handle: 'bottom_right_rotate',
855
+ onInteractionEnd: 'select.idle',
856
+ })
857
+ editor.pointerUp(50, 50)
858
+ editor.expectToBeIn('select.idle')
859
+ })
860
+
861
+ it('transitions to the tool for resizing', () => {
862
+ const id = createShapeId('box')
863
+ editor.createShapes([
864
+ {
865
+ id,
866
+ type: 'geo',
867
+ x: 100,
868
+ y: 100,
869
+ },
870
+ ])
871
+
872
+ editor.select(id)
873
+
874
+ editor.setCurrentTool('select.resizing', {
875
+ target: 'selection',
876
+ handle: 'bottom_right',
877
+ onInteractionEnd: 'select.idle',
878
+ })
879
+ editor.pointerUp(50, 50)
880
+ editor.expectToBeIn('select.idle')
881
+ })
882
+
883
+ it('transitions to the tool for translating', () => {
884
+ const id = createShapeId('box')
885
+ editor.createShapes([
886
+ {
887
+ id,
888
+ type: 'geo',
889
+ x: 100,
890
+ y: 100,
891
+ },
892
+ ])
893
+ editor.select(id)
894
+
895
+ editor.setCurrentTool('select.translating', {
896
+ onInteractionEnd: 'select.idle',
897
+ })
898
+ editor.pointerUp(50, 50)
899
+ editor.expectToBeIn('select.idle')
900
+ })
901
+ })