@tldraw/editor 3.16.0-canary.dbaaa1b0049c → 3.16.0-canary.dd88abb16ede

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 (146) hide show
  1. package/dist-cjs/index.d.ts +109 -104
  2. package/dist-cjs/index.js +6 -6
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/TldrawEditor.js +7 -7
  5. package/dist-cjs/lib/TldrawEditor.js.map +2 -2
  6. package/dist-cjs/lib/components/default-components/DefaultCanvas.js +12 -2
  7. package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
  8. package/dist-cjs/lib/editor/Editor.js +40 -113
  9. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  10. package/dist-cjs/lib/editor/derivations/notVisibleShapes.js +4 -0
  11. package/dist-cjs/lib/editor/derivations/notVisibleShapes.js.map +2 -2
  12. package/dist-cjs/lib/editor/shapes/ShapeUtil.js +23 -0
  13. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  14. package/dist-cjs/lib/editor/types/misc-types.js.map +1 -1
  15. package/dist-cjs/lib/exports/getSvgJsx.js +34 -14
  16. package/dist-cjs/lib/exports/getSvgJsx.js.map +2 -2
  17. package/dist-cjs/lib/hooks/useCanvasEvents.js +22 -17
  18. package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
  19. package/dist-cjs/lib/hooks/useDocumentEvents.js +5 -5
  20. package/dist-cjs/lib/hooks/useDocumentEvents.js.map +2 -2
  21. package/dist-cjs/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.js +1 -2
  22. package/dist-cjs/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.js.map +2 -2
  23. package/dist-cjs/lib/hooks/useGestureEvents.js +1 -1
  24. package/dist-cjs/lib/hooks/useGestureEvents.js.map +2 -2
  25. package/dist-cjs/lib/hooks/useHandleEvents.js +3 -3
  26. package/dist-cjs/lib/hooks/useHandleEvents.js.map +2 -2
  27. package/dist-cjs/lib/hooks/usePassThroughMouseOverEvents.js +4 -1
  28. package/dist-cjs/lib/hooks/usePassThroughMouseOverEvents.js.map +2 -2
  29. package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js +4 -1
  30. package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js.map +2 -2
  31. package/dist-cjs/lib/hooks/useSelectionEvents.js +4 -4
  32. package/dist-cjs/lib/hooks/useSelectionEvents.js.map +2 -2
  33. package/dist-cjs/lib/license/LicenseManager.js +143 -53
  34. package/dist-cjs/lib/license/LicenseManager.js.map +2 -2
  35. package/dist-cjs/lib/license/LicenseProvider.js +39 -1
  36. package/dist-cjs/lib/license/LicenseProvider.js.map +2 -2
  37. package/dist-cjs/lib/license/Watermark.js +69 -7
  38. package/dist-cjs/lib/license/Watermark.js.map +3 -3
  39. package/dist-cjs/lib/license/useLicenseManagerState.js.map +2 -2
  40. package/dist-cjs/lib/primitives/Box.js +3 -0
  41. package/dist-cjs/lib/primitives/Box.js.map +2 -2
  42. package/dist-cjs/lib/primitives/Vec.js +0 -4
  43. package/dist-cjs/lib/primitives/Vec.js.map +2 -2
  44. package/dist-cjs/lib/primitives/geometry/Geometry2d.js +50 -20
  45. package/dist-cjs/lib/primitives/geometry/Geometry2d.js.map +2 -2
  46. package/dist-cjs/lib/primitives/geometry/Group2d.js +8 -1
  47. package/dist-cjs/lib/primitives/geometry/Group2d.js.map +2 -2
  48. package/dist-cjs/lib/utils/dom.js +12 -1
  49. package/dist-cjs/lib/utils/dom.js.map +2 -2
  50. package/dist-cjs/lib/utils/getPointerInfo.js +2 -2
  51. package/dist-cjs/lib/utils/getPointerInfo.js.map +2 -2
  52. package/dist-cjs/lib/utils/reparenting.js +2 -35
  53. package/dist-cjs/lib/utils/reparenting.js.map +3 -3
  54. package/dist-cjs/version.js +3 -3
  55. package/dist-cjs/version.js.map +1 -1
  56. package/dist-esm/index.d.mts +109 -104
  57. package/dist-esm/index.mjs +9 -7
  58. package/dist-esm/index.mjs.map +2 -2
  59. package/dist-esm/lib/TldrawEditor.mjs +8 -8
  60. package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
  61. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +13 -3
  62. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
  63. package/dist-esm/lib/editor/Editor.mjs +40 -113
  64. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  65. package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs +4 -0
  66. package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs.map +2 -2
  67. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +23 -0
  68. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  69. package/dist-esm/lib/exports/getSvgJsx.mjs +34 -14
  70. package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
  71. package/dist-esm/lib/hooks/useCanvasEvents.mjs +24 -18
  72. package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
  73. package/dist-esm/lib/hooks/useDocumentEvents.mjs +11 -6
  74. package/dist-esm/lib/hooks/useDocumentEvents.mjs.map +2 -2
  75. package/dist-esm/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.mjs +2 -3
  76. package/dist-esm/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.mjs.map +2 -2
  77. package/dist-esm/lib/hooks/useGestureEvents.mjs +2 -2
  78. package/dist-esm/lib/hooks/useGestureEvents.mjs.map +2 -2
  79. package/dist-esm/lib/hooks/useHandleEvents.mjs +9 -4
  80. package/dist-esm/lib/hooks/useHandleEvents.mjs.map +2 -2
  81. package/dist-esm/lib/hooks/usePassThroughMouseOverEvents.mjs +4 -1
  82. package/dist-esm/lib/hooks/usePassThroughMouseOverEvents.mjs.map +2 -2
  83. package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs +4 -1
  84. package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs.map +2 -2
  85. package/dist-esm/lib/hooks/useSelectionEvents.mjs +6 -5
  86. package/dist-esm/lib/hooks/useSelectionEvents.mjs.map +2 -2
  87. package/dist-esm/lib/license/LicenseManager.mjs +144 -54
  88. package/dist-esm/lib/license/LicenseManager.mjs.map +2 -2
  89. package/dist-esm/lib/license/LicenseProvider.mjs +39 -2
  90. package/dist-esm/lib/license/LicenseProvider.mjs.map +2 -2
  91. package/dist-esm/lib/license/Watermark.mjs +70 -8
  92. package/dist-esm/lib/license/Watermark.mjs.map +3 -3
  93. package/dist-esm/lib/license/useLicenseManagerState.mjs.map +2 -2
  94. package/dist-esm/lib/primitives/Box.mjs +4 -1
  95. package/dist-esm/lib/primitives/Box.mjs.map +2 -2
  96. package/dist-esm/lib/primitives/Vec.mjs +0 -4
  97. package/dist-esm/lib/primitives/Vec.mjs.map +2 -2
  98. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs +53 -21
  99. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs.map +2 -2
  100. package/dist-esm/lib/primitives/geometry/Group2d.mjs +8 -1
  101. package/dist-esm/lib/primitives/geometry/Group2d.mjs.map +2 -2
  102. package/dist-esm/lib/utils/dom.mjs +12 -1
  103. package/dist-esm/lib/utils/dom.mjs.map +2 -2
  104. package/dist-esm/lib/utils/getPointerInfo.mjs +2 -2
  105. package/dist-esm/lib/utils/getPointerInfo.mjs.map +2 -2
  106. package/dist-esm/lib/utils/reparenting.mjs +3 -40
  107. package/dist-esm/lib/utils/reparenting.mjs.map +2 -2
  108. package/dist-esm/version.mjs +3 -3
  109. package/dist-esm/version.mjs.map +1 -1
  110. package/editor.css +16 -3
  111. package/package.json +7 -7
  112. package/src/index.ts +4 -9
  113. package/src/lib/TldrawEditor.tsx +9 -16
  114. package/src/lib/components/default-components/DefaultCanvas.tsx +10 -2
  115. package/src/lib/editor/Editor.test.ts +90 -0
  116. package/src/lib/editor/Editor.ts +54 -150
  117. package/src/lib/editor/derivations/notVisibleShapes.ts +6 -0
  118. package/src/lib/editor/shapes/ShapeUtil.ts +46 -0
  119. package/src/lib/editor/types/misc-types.ts +0 -6
  120. package/src/lib/exports/getSvgJsx.test.ts +868 -0
  121. package/src/lib/exports/getSvgJsx.tsx +76 -19
  122. package/src/lib/hooks/useCanvasEvents.ts +23 -17
  123. package/src/lib/hooks/useDocumentEvents.ts +11 -6
  124. package/src/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.ts +2 -2
  125. package/src/lib/hooks/useGestureEvents.ts +2 -2
  126. package/src/lib/hooks/useHandleEvents.ts +9 -4
  127. package/src/lib/hooks/usePassThroughMouseOverEvents.ts +4 -1
  128. package/src/lib/hooks/usePassThroughWheelEvents.ts +6 -1
  129. package/src/lib/hooks/useSelectionEvents.ts +6 -5
  130. package/src/lib/license/LicenseManager.test.ts +721 -382
  131. package/src/lib/license/LicenseManager.ts +204 -58
  132. package/src/lib/license/LicenseProvider.tsx +74 -2
  133. package/src/lib/license/Watermark.tsx +75 -8
  134. package/src/lib/license/useLicenseManagerState.ts +2 -2
  135. package/src/lib/primitives/Box.test.ts +126 -0
  136. package/src/lib/primitives/Box.ts +10 -1
  137. package/src/lib/primitives/Vec.ts +0 -5
  138. package/src/lib/primitives/geometry/Geometry2d.test.ts +420 -0
  139. package/src/lib/primitives/geometry/Geometry2d.ts +78 -21
  140. package/src/lib/primitives/geometry/Group2d.ts +10 -1
  141. package/src/lib/test/InFrontOfTheCanvas.test.tsx +187 -0
  142. package/src/lib/utils/dom.test.ts +94 -0
  143. package/src/lib/utils/dom.ts +38 -1
  144. package/src/lib/utils/getPointerInfo.ts +2 -1
  145. package/src/lib/utils/reparenting.ts +3 -69
  146. package/src/version.ts +3 -3
package/src/index.ts CHANGED
@@ -268,7 +268,6 @@ export {
268
268
  type TLGetShapeAtPointOptions,
269
269
  type TLImageExportOptions,
270
270
  type TLSvgExportOptions,
271
- type TLSvgOptions,
272
271
  type TLUpdatePointerOptions,
273
272
  } from './lib/editor/types/misc-types'
274
273
  export {
@@ -331,9 +330,11 @@ export {
331
330
  type InvalidLicenseReason,
332
331
  type LicenseFromKeyResult,
333
332
  type LicenseInfo,
333
+ type LicenseState,
334
334
  type TestEnvironment,
335
335
  type ValidLicenseKeyResult,
336
336
  } from './lib/license/LicenseManager'
337
+ export { LICENSE_TIMEOUT } from './lib/license/LicenseProvider'
337
338
  export { defaultTldrawOptions, type TldrawOptions } from './lib/options'
338
339
  export {
339
340
  Box,
@@ -446,10 +447,12 @@ export {
446
447
  export {
447
448
  activeElementShouldCaptureKeys,
448
449
  loopToHtmlElement,
450
+ markEventAsHandled,
449
451
  preventDefault,
450
452
  releasePointerCapture,
451
453
  setPointerCapture,
452
454
  stopEventPropagation,
455
+ wasEventAlreadyHandled,
453
456
  } from './lib/utils/dom'
454
457
  export { EditorAtom } from './lib/utils/EditorAtom'
455
458
  export { getIncrementedName } from './lib/utils/getIncrementedName'
@@ -485,14 +488,6 @@ export { type TLStoreWithStatus } from './lib/utils/sync/StoreWithStatus'
485
488
  export { uniq } from './lib/utils/uniq'
486
489
  export { openWindow } from './lib/utils/window-open'
487
490
 
488
- /**
489
- * @deprecated Licensing is now enabled in the tldraw SDK.
490
- * @public */
491
- export function debugEnableLicensing() {
492
- // noop
493
- return
494
- }
495
-
496
491
  registerTldrawLibraryVersion(
497
492
  (globalThis as any).TLDRAW_LIBRARY_NAME,
498
493
  (globalThis as any).TLDRAW_LIBRARY_VERSION,
@@ -1,6 +1,7 @@
1
1
  import { MigrationSequence, Store } from '@tldraw/store'
2
2
  import { TLShape, TLStore, TLStoreSnapshot } from '@tldraw/tlschema'
3
3
  import { annotateError, Required } from '@tldraw/utils'
4
+ import classNames from 'classnames'
4
5
  import React, {
5
6
  memo,
6
7
  ReactNode,
@@ -12,8 +13,6 @@ import React, {
12
13
  useState,
13
14
  useSyncExternalStore,
14
15
  } from 'react'
15
-
16
- import classNames from 'classnames'
17
16
  import { version } from '../version'
18
17
  import { DefaultErrorFallback } from './components/default-components/DefaultErrorFallback'
19
18
  import { OptionalErrorBoundary } from './components/ErrorBoundary'
@@ -45,7 +44,7 @@ import { LicenseProvider } from './license/LicenseProvider'
45
44
  import { Watermark } from './license/Watermark'
46
45
  import { TldrawOptions } from './options'
47
46
  import { TLDeepLinkOptions } from './utils/deepLinks'
48
- import { stopEventPropagation } from './utils/dom'
47
+ import { markEventAsHandled } from './utils/dom'
49
48
  import { TLTextOptions } from './utils/richText'
50
49
  import { TLStoreWithStatus } from './utils/sync/StoreWithStatus'
51
50
 
@@ -189,13 +188,6 @@ export interface TldrawEditorBaseProps {
189
188
  */
190
189
  deepLinks?: true | TLDeepLinkOptions
191
190
 
192
- /**
193
- * Predicate for whether or not a shape should be hidden.
194
- *
195
- * @deprecated Use {@link TldrawEditorBaseProps#getShapeVisibility} instead.
196
- */
197
- isShapeHidden?(shape: TLShape, editor: Editor): boolean
198
-
199
191
  /**
200
192
  * Provides a way to hide shapes.
201
193
  *
@@ -283,7 +275,7 @@ export const TldrawEditor = memo(function TldrawEditor({
283
275
  data-tldraw={version}
284
276
  draggable={false}
285
277
  className={classNames(`${TL_CONTAINER_CLASS} tl-theme__light`, className)}
286
- onPointerDown={stopEventPropagation}
278
+ onPointerDown={markEventAsHandled}
287
279
  tabIndex={-1}
288
280
  role="application"
289
281
  aria-label={_options?.branding ?? 'tldraw'}
@@ -412,8 +404,6 @@ function TldrawEditorWithReadyStore({
412
404
  options,
413
405
  licenseKey,
414
406
  deepLinks: _deepLinks,
415
- // eslint-disable-next-line @typescript-eslint/no-deprecated
416
- isShapeHidden,
417
407
  getShapeVisibility,
418
408
  assetUrls,
419
409
  }: Required<
@@ -473,7 +463,6 @@ function TldrawEditorWithReadyStore({
473
463
  textOptions,
474
464
  options,
475
465
  licenseKey,
476
- isShapeHidden,
477
466
  getShapeVisibility,
478
467
  fontAssetUrls: assetUrls?.fonts,
479
468
  })
@@ -509,7 +498,6 @@ function TldrawEditorWithReadyStore({
509
498
  user,
510
499
  setEditor,
511
500
  licenseKey,
512
- isShapeHidden,
513
501
  getShapeVisibility,
514
502
  textOptions,
515
503
  assetUrls,
@@ -586,8 +574,13 @@ function TldrawEditorWithReadyStore({
586
574
  if (editor !== fontLoadingState?.editor) {
587
575
  fontLoadingState = null
588
576
  }
589
- useEffect(() => {
577
+ useLayoutEffect(() => {
590
578
  if (!editor) return
579
+ if (editor.options.maxFontsToLoadBeforeRender === 0) {
580
+ setFontLoadingState({ editor, isLoaded: true })
581
+ return
582
+ }
583
+
591
584
  let isCancelled = false
592
585
 
593
586
  setFontLoadingState({ editor, isLoaded: false })
@@ -21,7 +21,7 @@ import { Mat } from '../../primitives/Mat'
21
21
  import { Vec } from '../../primitives/Vec'
22
22
  import { toDomPrecision } from '../../primitives/utils'
23
23
  import { debugFlags } from '../../utils/debug-flags'
24
- import { setStyleProperty } from '../../utils/dom'
24
+ import { markEventAsHandled, setStyleProperty } from '../../utils/dom'
25
25
  import { GeometryDebuggingView } from '../GeometryDebuggingView'
26
26
  import { LiveCollaborators } from '../LiveCollaborators'
27
27
  import { MenuClickCapture } from '../MenuClickCapture'
@@ -172,10 +172,18 @@ export function DefaultCanvas({ className }: TLCanvasComponentProps) {
172
172
  <LiveCollaborators />
173
173
  </div>
174
174
  </div>
175
+ <div
176
+ className="tl-canvas__in-front"
177
+ onPointerDown={markEventAsHandled}
178
+ onPointerUp={markEventAsHandled}
179
+ onTouchStart={markEventAsHandled}
180
+ onTouchEnd={markEventAsHandled}
181
+ >
182
+ <InFrontOfTheCanvasWrapper />
183
+ </div>
175
184
  <MovingCameraHitTestBlocker />
176
185
  </div>
177
186
  <MenuClickCapture />
178
- <InFrontOfTheCanvasWrapper />
179
187
  </>
180
188
  )
181
189
  }
@@ -833,3 +833,93 @@ describe('selectAll', () => {
833
833
  setSelectedShapesSpy.mockRestore()
834
834
  })
835
835
  })
836
+
837
+ describe('putExternalContent', () => {
838
+ let mockHandler: any
839
+
840
+ beforeEach(() => {
841
+ mockHandler = vi.fn()
842
+ editor.registerExternalContentHandler('text', mockHandler)
843
+ })
844
+
845
+ it('calls external content handler when not readonly', async () => {
846
+ vi.spyOn(editor, 'getIsReadonly').mockReturnValue(false)
847
+
848
+ const info = { type: 'text' as const, text: 'test-data' }
849
+ await editor.putExternalContent(info)
850
+
851
+ expect(mockHandler).toHaveBeenCalledWith(info)
852
+ })
853
+
854
+ it('does not call external content handler when readonly', async () => {
855
+ vi.spyOn(editor, 'getIsReadonly').mockReturnValue(true)
856
+
857
+ const info = { type: 'text' as const, text: 'test-data' }
858
+ await editor.putExternalContent(info)
859
+
860
+ expect(mockHandler).not.toHaveBeenCalled()
861
+ })
862
+
863
+ it('calls external content handler when readonly but force is true', async () => {
864
+ vi.spyOn(editor, 'getIsReadonly').mockReturnValue(true)
865
+
866
+ const info = { type: 'text' as const, text: 'test-data' }
867
+ await editor.putExternalContent(info, { force: true })
868
+
869
+ expect(mockHandler).toHaveBeenCalledWith(info)
870
+ })
871
+
872
+ it('calls external content handler when force is false and not readonly', async () => {
873
+ vi.spyOn(editor, 'getIsReadonly').mockReturnValue(false)
874
+
875
+ const info = { type: 'text' as const, text: 'test-data' }
876
+ await editor.putExternalContent(info, { force: false })
877
+
878
+ expect(mockHandler).toHaveBeenCalledWith(info)
879
+ })
880
+ })
881
+
882
+ describe('replaceExternalContent', () => {
883
+ let mockHandler: any
884
+
885
+ beforeEach(() => {
886
+ mockHandler = vi.fn()
887
+ editor.registerExternalContentHandler('text', mockHandler)
888
+ })
889
+
890
+ it('calls external content handler when not readonly', async () => {
891
+ vi.spyOn(editor, 'getIsReadonly').mockReturnValue(false)
892
+
893
+ const info = { type: 'text' as const, text: 'test-data' }
894
+ await editor.replaceExternalContent(info)
895
+
896
+ expect(mockHandler).toHaveBeenCalledWith(info)
897
+ })
898
+
899
+ it('does not call external content handler when readonly', async () => {
900
+ vi.spyOn(editor, 'getIsReadonly').mockReturnValue(true)
901
+
902
+ const info = { type: 'text' as const, text: 'test-data' }
903
+ await editor.replaceExternalContent(info)
904
+
905
+ expect(mockHandler).not.toHaveBeenCalled()
906
+ })
907
+
908
+ it('calls external content handler when readonly but force is true', async () => {
909
+ vi.spyOn(editor, 'getIsReadonly').mockReturnValue(true)
910
+
911
+ const info = { type: 'text' as const, text: 'test-data' }
912
+ await editor.replaceExternalContent(info, { force: true })
913
+
914
+ expect(mockHandler).toHaveBeenCalledWith(info)
915
+ })
916
+
917
+ it('calls external content handler when force is false and not readonly', async () => {
918
+ vi.spyOn(editor, 'getIsReadonly').mockReturnValue(false)
919
+
920
+ const info = { type: 'text' as const, text: 'test-data' }
921
+ await editor.replaceExternalContent(info, { force: false })
922
+
923
+ expect(mockHandler).toHaveBeenCalledWith(info)
924
+ })
925
+ })
@@ -116,7 +116,6 @@ import {
116
116
  } from '../constants'
117
117
  import { exportToSvg } from '../exports/exportToSvg'
118
118
  import { getSvgAsImage } from '../exports/getSvgAsImage'
119
- import { tlenv } from '../globals/environment'
120
119
  import { tlmenus } from '../globals/menus'
121
120
  import { tltime } from '../globals/time'
122
121
  import { TldrawOptions, defaultTldrawOptions } from '../options'
@@ -244,16 +243,6 @@ export interface TLEditorOptions {
244
243
  options?: Partial<TldrawOptions>
245
244
  licenseKey?: string
246
245
  fontAssetUrls?: { [key: string]: string | undefined }
247
- /**
248
- * A predicate that should return true if the given shape should be hidden.
249
- *
250
- * @deprecated Use {@link Editor#getShapeVisibility} instead.
251
- *
252
- * @param shape - The shape to check.
253
- * @param editor - The editor instance.
254
- */
255
- isShapeHidden?(shape: TLShape, editor: Editor): boolean
256
-
257
246
  /**
258
247
  * Provides a way to hide shapes.
259
248
  *
@@ -309,21 +298,12 @@ export class Editor extends EventEmitter<TLEventMap> {
309
298
  autoFocus,
310
299
  inferDarkMode,
311
300
  options,
312
- // eslint-disable-next-line @typescript-eslint/no-deprecated
313
- isShapeHidden,
314
301
  getShapeVisibility,
315
302
  fontAssetUrls,
316
303
  }: TLEditorOptions) {
317
304
  super()
318
- assert(
319
- !(isShapeHidden && getShapeVisibility),
320
- 'Cannot use both isShapeHidden and getShapeVisibility'
321
- )
322
305
 
323
- this._getShapeVisibility = isShapeHidden
324
- ? // eslint-disable-next-line @typescript-eslint/no-deprecated
325
- (shape: TLShape, editor: Editor) => (isShapeHidden(shape, editor) ? 'hidden' : 'inherit')
326
- : getShapeVisibility
306
+ this._getShapeVisibility = getShapeVisibility
327
307
 
328
308
  this.options = { ...defaultTldrawOptions, ...options }
329
309
 
@@ -907,14 +887,6 @@ export class Editor extends EventEmitter<TLEventMap> {
907
887
  */
908
888
  readonly fonts: FontManager
909
889
 
910
- /**
911
- * A manager for the editor's environment.
912
- *
913
- * @deprecated This is deprecated and will be removed in a future version. Use the `tlenv` global export instead.
914
- * @public
915
- */
916
- readonly environment = tlenv
917
-
918
890
  /**
919
891
  * A manager for the editor's scribbles.
920
892
  *
@@ -1119,35 +1091,6 @@ export class Editor extends EventEmitter<TLEventMap> {
1119
1091
  return this.history.getNumRedos() > 0
1120
1092
  }
1121
1093
 
1122
- /**
1123
- * Create a new "mark", or stopping point, in the undo redo history. Creating a mark will clear
1124
- * any redos.
1125
- *
1126
- * @example
1127
- * ```ts
1128
- * editor.mark()
1129
- * editor.mark('flip shapes')
1130
- * ```
1131
- *
1132
- * @param markId - The mark's id, usually the reason for adding the mark.
1133
- *
1134
- * @public
1135
- * @deprecated use {@link Editor.markHistoryStoppingPoint} instead
1136
- */
1137
- mark(markId?: string): this {
1138
- if (typeof markId === 'string') {
1139
- console.warn(
1140
- `[tldraw] \`editor.history.mark("${markId}")\` is deprecated. Please use \`const myMarkId = editor.markHistoryStoppingPoint()\` instead.`
1141
- )
1142
- } else {
1143
- console.warn(
1144
- '[tldraw] `editor.mark()` is deprecated. Use `editor.markHistoryStoppingPoint()` instead.'
1145
- )
1146
- }
1147
- this.history._mark(markId ?? uniqueId())
1148
- return this
1149
- }
1150
-
1151
1094
  /**
1152
1095
  * Create a new "mark", or stopping point, in the undo redo history. Creating a mark will clear
1153
1096
  * any redos. You typically want to do this just before a user interaction begins or is handled.
@@ -1272,13 +1215,6 @@ export class Editor extends EventEmitter<TLEventMap> {
1272
1215
  return this
1273
1216
  }
1274
1217
 
1275
- /**
1276
- * @deprecated Use `Editor.run` instead.
1277
- */
1278
- batch(fn: () => void, opts?: TLEditorRunOptions): this {
1279
- return this.run(fn, opts)
1280
- }
1281
-
1282
1218
  /* --------------------- Errors --------------------- */
1283
1219
 
1284
1220
  /** @internal */
@@ -1580,54 +1516,6 @@ export class Editor extends EventEmitter<TLEventMap> {
1580
1516
 
1581
1517
  menus = tlmenus.forContext(this.contextId)
1582
1518
 
1583
- /**
1584
- * @deprecated Use `editor.menus.getOpenMenus` instead.
1585
- *
1586
- * @public
1587
- */
1588
- @computed getOpenMenus(): string[] {
1589
- return this.menus.getOpenMenus()
1590
- }
1591
-
1592
- /**
1593
- * @deprecated Use `editor.menus.addOpenMenu` instead.
1594
- *
1595
- * @public
1596
- */
1597
- addOpenMenu(id: string): this {
1598
- this.menus.addOpenMenu(id)
1599
- return this
1600
- }
1601
-
1602
- /**
1603
- * @deprecated Use `editor.menus.deleteOpenMenu` instead.
1604
- *
1605
- * @public
1606
- */
1607
- deleteOpenMenu(id: string): this {
1608
- this.menus.deleteOpenMenu(id)
1609
- return this
1610
- }
1611
-
1612
- /**
1613
- * @deprecated Use `editor.menus.clearOpenMenus` instead.
1614
- *
1615
- * @public
1616
- */
1617
- clearOpenMenus(): this {
1618
- this.menus.clearOpenMenus()
1619
- return this
1620
- }
1621
-
1622
- /**
1623
- * @deprecated Use `editor.menus.hasAnyOpenMenus` instead.
1624
- *
1625
- * @public
1626
- */
1627
- @computed getIsMenuOpen(): boolean {
1628
- return this.menus.hasAnyOpenMenus()
1629
- }
1630
-
1631
1519
  /* --------------------- Cursor --------------------- */
1632
1520
 
1633
1521
  /**
@@ -4792,8 +4680,10 @@ export class Editor extends EventEmitter<TLEventMap> {
4792
4680
  return this.store.createComputedCache<Box, TLShape>('pageBoundsCache', (shape) => {
4793
4681
  const pageTransform = this.getShapePageTransform(shape)
4794
4682
  if (!pageTransform) return undefined
4795
- const geometry = this.getShapeGeometry(shape)
4796
- return Box.FromPoints(pageTransform.applyToPoints(geometry.vertices))
4683
+
4684
+ return Box.FromPoints(
4685
+ pageTransform.applyToPoints(this.getShapeGeometry(shape).boundsVertices)
4686
+ )
4797
4687
  })
4798
4688
  }
4799
4689
 
@@ -4860,27 +4750,25 @@ export class Editor extends EventEmitter<TLEventMap> {
4860
4750
  return this.store.createComputedCache('pageMaskCache', (shape) => {
4861
4751
  if (isPageId(shape.parentId)) return undefined
4862
4752
 
4863
- const frameAncestors = this.getShapeAncestors(shape.id).filter((shape) =>
4864
- this.isShapeOfType<TLFrameShape>(shape, 'frame')
4865
- )
4866
-
4867
- if (frameAncestors.length === 0) return undefined
4868
-
4869
- const pageMask = frameAncestors
4870
- .map<Vec[] | undefined>((s) => {
4871
- // Apply the frame transform to the frame outline to get the frame outline in the current page space
4872
- const geometry = this.getShapeGeometry(s.id)
4873
- const pageTransform = this.getShapePageTransform(s.id)
4874
- return pageTransform.applyToPoints(geometry.vertices)
4875
- })
4876
- .reduce((acc, b) => {
4877
- if (!(b && acc)) return undefined
4878
- const intersection = intersectPolygonPolygon(acc, b)
4879
- if (intersection) {
4880
- return intersection.map(Vec.Cast)
4881
- }
4882
- return []
4883
- })
4753
+ const clipPaths: Vec[][] = []
4754
+ // Get all ancestors that can potentially clip this shape
4755
+ for (const ancestor of this.getShapeAncestors(shape.id)) {
4756
+ const util = this.getShapeUtil(ancestor)
4757
+ const clipPath = util.getClipPath?.(ancestor)
4758
+ if (!clipPath) continue
4759
+ if (util.shouldClipChild?.(shape) === false) continue
4760
+ const pageTransform = this.getShapePageTransform(ancestor.id)
4761
+ clipPaths.push(pageTransform.applyToPoints(clipPath))
4762
+ }
4763
+ if (clipPaths.length === 0) return undefined
4764
+
4765
+ const pageMask = clipPaths.reduce((acc, b) => {
4766
+ const intersection = intersectPolygonPolygon(acc, b)
4767
+ if (intersection) {
4768
+ return intersection.map(Vec.Cast)
4769
+ }
4770
+ return []
4771
+ })
4884
4772
 
4885
4773
  return pageMask
4886
4774
  })
@@ -5841,11 +5729,6 @@ export class Editor extends EventEmitter<TLEventMap> {
5841
5729
  return shapeIds
5842
5730
  }
5843
5731
 
5844
- /** @deprecated Use {@link Editor.getDraggingOverShape} instead */
5845
- getDroppingOverShape(point: Vec, droppingShapes: TLShape[]): TLShape | undefined {
5846
- return this.getDraggingOverShape(point, droppingShapes)
5847
- }
5848
-
5849
5732
  /**
5850
5733
  * Get the shape that some shapes should be dropped on at a given point.
5851
5734
  *
@@ -8950,8 +8833,13 @@ export class Editor extends EventEmitter<TLEventMap> {
8950
8833
  * Handle external content, such as files, urls, embeds, or plain text which has been put into the app, for example by pasting external text or dropping external images onto canvas.
8951
8834
  *
8952
8835
  * @param info - Info about the external content.
8836
+ * @param opts - Options for handling external content, including force flag to bypass readonly checks.
8953
8837
  */
8954
- async putExternalContent<E>(info: TLExternalContent<E>): Promise<void> {
8838
+ async putExternalContent<E>(
8839
+ info: TLExternalContent<E>,
8840
+ opts = {} as { force?: boolean }
8841
+ ): Promise<void> {
8842
+ if (!opts.force && this.getIsReadonly()) return
8955
8843
  return this.externalContentHandlers[info.type]?.(info as any)
8956
8844
  }
8957
8845
 
@@ -8959,8 +8847,13 @@ export class Editor extends EventEmitter<TLEventMap> {
8959
8847
  * Handle replacing external content.
8960
8848
  *
8961
8849
  * @param info - Info about the external content.
8850
+ * @param opts - Options for handling external content, including force flag to bypass readonly checks.
8962
8851
  */
8963
- async replaceExternalContent<E>(info: TLExternalContent<E>): Promise<void> {
8852
+ async replaceExternalContent<E>(
8853
+ info: TLExternalContent<E>,
8854
+ opts = {} as { force?: boolean }
8855
+ ): Promise<void> {
8856
+ if (!opts.force && this.getIsReadonly()) return
8964
8857
  return this.externalContentHandlers[info.type]?.(info as any)
8965
8858
  }
8966
8859
 
@@ -9461,13 +9354,6 @@ export class Editor extends EventEmitter<TLEventMap> {
9461
9354
  }
9462
9355
  }
9463
9356
 
9464
- /** @deprecated Use {@link Editor.getSvgString} or {@link Editor.getSvgElement} instead. */
9465
- async getSvg(shapes: TLShapeId[] | TLShape[], opts: TLSvgExportOptions = {}) {
9466
- const result = await this.getSvgElement(shapes, opts)
9467
- if (!result) return undefined
9468
- return result.svg
9469
- }
9470
-
9471
9357
  /**
9472
9358
  * Get an exported image of the given shapes.
9473
9359
  *
@@ -9519,6 +9405,24 @@ export class Editor extends EventEmitter<TLEventMap> {
9519
9405
  }
9520
9406
  }
9521
9407
 
9408
+ /**
9409
+ * Get an exported image of the given shapes as a data URL.
9410
+ *
9411
+ * @param shapes - The shapes (or shape ids) to export.
9412
+ * @param opts - Options for the export.
9413
+ *
9414
+ * @returns A data URL of the image.
9415
+ * @public
9416
+ */
9417
+ async toImageDataUrl(shapes: TLShapeId[] | TLShape[], opts: TLImageExportOptions = {}) {
9418
+ const { blob, width, height } = await this.toImage(shapes, opts)
9419
+ return {
9420
+ url: await FileHelpers.blobToDataUrl(blob),
9421
+ width,
9422
+ height,
9423
+ }
9424
+ }
9425
+
9522
9426
  /* --------------------- Events --------------------- */
9523
9427
 
9524
9428
  /**
@@ -7,6 +7,12 @@ function fromScratch(editor: Editor): Set<TLShapeId> {
7
7
  const viewportPageBounds = editor.getViewportPageBounds()
8
8
  const notVisibleShapes = new Set<TLShapeId>()
9
9
  shapesIds.forEach((id) => {
10
+ const shape = editor.getShape(id)
11
+ if (!shape) return
12
+
13
+ const canCull = editor.getShapeUtil(shape.type).canCull(shape)
14
+ if (!canCull) return
15
+
10
16
  // If the shape is fully outside of the viewport page bounds, add it to the set.
11
17
  // We'll ignore masks here, since they're more expensive to compute and the overhead is not worth it.
12
18
  const pageBounds = editor.getShapePageBounds(id)
@@ -283,6 +283,17 @@ export abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {
283
283
  return true
284
284
  }
285
285
 
286
+ /**
287
+ * Whether this shape can be culled. By default, shapes are culled for
288
+ * performance reasons when they are outside of the viewport. Culled shapes are still rendered
289
+ * to the DOM, but have their `display` property set to `none`.
290
+ *
291
+ * @param shape - The shape.
292
+ */
293
+ canCull(_shape: Shape): boolean {
294
+ return true
295
+ }
296
+
286
297
  /**
287
298
  * Does this shape provide a background for its children? If this is true,
288
299
  * then any children with a `renderBackground` method will have their
@@ -296,6 +307,27 @@ export abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {
296
307
  return false
297
308
  }
298
309
 
310
+ /**
311
+ * Get the clip path to apply to this shape's children.
312
+ *
313
+ * @param shape - The shape to get the clip path for
314
+ * @returns Array of points defining the clipping polygon in local coordinates, or undefined if no clipping
315
+ * @public
316
+ */
317
+ getClipPath?(shape: Shape): Vec[] | undefined
318
+
319
+ /**
320
+ * Whether a specific child shape should be clipped by this shape.
321
+ * Only called if getClipPath returns a valid polygon.
322
+ *
323
+ * If not defined, the default behavior is to clip all children.
324
+ *
325
+ * @param child - The child shape to check
326
+ * @returns boolean indicating if this child should be clipped
327
+ * @public
328
+ */
329
+ shouldClipChild?(child: TLShape): boolean
330
+
299
331
  /**
300
332
  * Whether the shape should hide its resize handles when selected.
301
333
  *
@@ -341,6 +373,20 @@ export abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {
341
373
  return false
342
374
  }
343
375
 
376
+ /**
377
+ * By default, the bounds of an image export are the bounds of all the shapes it contains, plus
378
+ * some padding. If an export includes a shape where `isExportBoundsContainer` is true, then the
379
+ * padding is skipped _if the bounds of that shape contains all the other shapes_. This is
380
+ * useful in cases like annotating on top of an image, where you usually want to avoid extra
381
+ * padding around the image if you don't need it.
382
+ *
383
+ * @param _shape - The shape to check
384
+ * @returns True if this shape should be treated as an export bounds container
385
+ */
386
+ isExportBoundsContainer(_shape: Shape): boolean {
387
+ return false
388
+ }
389
+
344
390
  /**
345
391
  * Get a JSX element for the shape (as an HTML element) to be rendered as part of the canvas background - behind any other shape content.
346
392
  *
@@ -72,12 +72,6 @@ export interface TLImageExportOptions extends TLSvgExportOptions {
72
72
  format?: TLExportType
73
73
  }
74
74
 
75
- /**
76
- * @public
77
- * @deprecated use {@link TLImageExportOptions} instead
78
- */
79
- export type TLSvgOptions = TLImageExportOptions
80
-
81
75
  /** @public */
82
76
  export interface TLCameraMoveOptions {
83
77
  /** Whether to move the camera immediately, rather than on the next tick. */