@tldraw/editor 3.16.0-canary.e9c30b532b82 → 3.16.0-canary.ea008b31887f

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 (152) hide show
  1. package/dist-cjs/index.d.ts +71 -101
  2. package/dist-cjs/index.js +3 -5
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/TldrawEditor.js +6 -6
  5. package/dist-cjs/lib/TldrawEditor.js.map +2 -2
  6. package/dist-cjs/lib/components/Shape.js +7 -10
  7. package/dist-cjs/lib/components/Shape.js.map +2 -2
  8. package/dist-cjs/lib/components/default-components/DefaultCanvas.js +4 -23
  9. package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
  10. package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js +1 -1
  11. package/dist-cjs/lib/components/default-components/DefaultErrorFallback.js.map +2 -2
  12. package/dist-cjs/lib/config/TLUserPreferences.js +1 -1
  13. package/dist-cjs/lib/config/TLUserPreferences.js.map +2 -2
  14. package/dist-cjs/lib/editor/Editor.js +44 -112
  15. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  16. package/dist-cjs/lib/editor/derivations/notVisibleShapes.js +4 -0
  17. package/dist-cjs/lib/editor/derivations/notVisibleShapes.js.map +2 -2
  18. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js +1 -1
  19. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +2 -2
  20. package/dist-cjs/lib/editor/shapes/ShapeUtil.js +23 -0
  21. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  22. package/dist-cjs/lib/editor/types/misc-types.js.map +1 -1
  23. package/dist-cjs/lib/exports/getSvgJsx.js +34 -14
  24. package/dist-cjs/lib/exports/getSvgJsx.js.map +2 -2
  25. package/dist-cjs/lib/hooks/useCanvasEvents.js +7 -5
  26. package/dist-cjs/lib/hooks/useCanvasEvents.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/license/LicenseManager.js +138 -50
  32. package/dist-cjs/lib/license/LicenseManager.js.map +2 -2
  33. package/dist-cjs/lib/license/LicenseProvider.js +39 -1
  34. package/dist-cjs/lib/license/LicenseProvider.js.map +2 -2
  35. package/dist-cjs/lib/license/Watermark.js +68 -6
  36. package/dist-cjs/lib/license/Watermark.js.map +3 -3
  37. package/dist-cjs/lib/license/useLicenseManagerState.js.map +2 -2
  38. package/dist-cjs/lib/options.js +6 -0
  39. package/dist-cjs/lib/options.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/reparenting.js +2 -35
  49. package/dist-cjs/lib/utils/reparenting.js.map +3 -3
  50. package/dist-cjs/version.js +3 -3
  51. package/dist-cjs/version.js.map +1 -1
  52. package/dist-esm/index.d.mts +71 -101
  53. package/dist-esm/index.mjs +3 -5
  54. package/dist-esm/index.mjs.map +2 -2
  55. package/dist-esm/lib/TldrawEditor.mjs +6 -6
  56. package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
  57. package/dist-esm/lib/components/Shape.mjs +7 -10
  58. package/dist-esm/lib/components/Shape.mjs.map +2 -2
  59. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +4 -23
  60. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
  61. package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs +1 -1
  62. package/dist-esm/lib/components/default-components/DefaultErrorFallback.mjs.map +2 -2
  63. package/dist-esm/lib/config/TLUserPreferences.mjs +1 -1
  64. package/dist-esm/lib/config/TLUserPreferences.mjs.map +2 -2
  65. package/dist-esm/lib/editor/Editor.mjs +44 -112
  66. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  67. package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs +4 -0
  68. package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs.map +2 -2
  69. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs +1 -1
  70. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +2 -2
  71. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +23 -0
  72. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  73. package/dist-esm/lib/exports/getSvgJsx.mjs +34 -14
  74. package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
  75. package/dist-esm/lib/hooks/useCanvasEvents.mjs +7 -5
  76. package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
  77. package/dist-esm/lib/hooks/usePassThroughMouseOverEvents.mjs +4 -1
  78. package/dist-esm/lib/hooks/usePassThroughMouseOverEvents.mjs.map +2 -2
  79. package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs +4 -1
  80. package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs.map +2 -2
  81. package/dist-esm/lib/license/LicenseManager.mjs +139 -51
  82. package/dist-esm/lib/license/LicenseManager.mjs.map +2 -2
  83. package/dist-esm/lib/license/LicenseProvider.mjs +39 -2
  84. package/dist-esm/lib/license/LicenseProvider.mjs.map +2 -2
  85. package/dist-esm/lib/license/Watermark.mjs +68 -6
  86. package/dist-esm/lib/license/Watermark.mjs.map +3 -3
  87. package/dist-esm/lib/license/useLicenseManagerState.mjs.map +2 -2
  88. package/dist-esm/lib/options.mjs +6 -0
  89. package/dist-esm/lib/options.mjs.map +2 -2
  90. package/dist-esm/lib/primitives/Box.mjs +4 -1
  91. package/dist-esm/lib/primitives/Box.mjs.map +2 -2
  92. package/dist-esm/lib/primitives/Vec.mjs +0 -4
  93. package/dist-esm/lib/primitives/Vec.mjs.map +2 -2
  94. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs +53 -21
  95. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs.map +2 -2
  96. package/dist-esm/lib/primitives/geometry/Group2d.mjs +8 -1
  97. package/dist-esm/lib/primitives/geometry/Group2d.mjs.map +2 -2
  98. package/dist-esm/lib/utils/reparenting.mjs +3 -40
  99. package/dist-esm/lib/utils/reparenting.mjs.map +2 -2
  100. package/dist-esm/version.mjs +3 -3
  101. package/dist-esm/version.mjs.map +1 -1
  102. package/editor.css +16 -3
  103. package/package.json +14 -37
  104. package/src/index.ts +2 -9
  105. package/src/lib/TldrawEditor.tsx +7 -14
  106. package/src/lib/components/Shape.tsx +6 -12
  107. package/src/lib/components/default-components/DefaultCanvas.tsx +5 -22
  108. package/src/lib/components/default-components/DefaultErrorFallback.tsx +1 -1
  109. package/src/lib/config/TLUserPreferences.ts +1 -1
  110. package/src/lib/editor/Editor.test.ts +12 -11
  111. package/src/lib/editor/Editor.ts +53 -149
  112. package/src/lib/editor/derivations/notVisibleShapes.ts +6 -0
  113. package/src/lib/editor/managers/ClickManager/ClickManager.test.ts +15 -14
  114. package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.test.ts +16 -15
  115. package/src/lib/editor/managers/FocusManager/FocusManager.test.ts +49 -48
  116. package/src/lib/editor/managers/FontManager/FontManager.test.ts +24 -23
  117. package/src/lib/editor/managers/HistoryManager/HistoryManager.test.ts +7 -6
  118. package/src/lib/editor/managers/ScribbleManager/ScribbleManager.test.ts +12 -11
  119. package/src/lib/editor/managers/SnapManager/SnapManager.test.ts +57 -50
  120. package/src/lib/editor/managers/TextManager/TextManager.test.ts +51 -26
  121. package/src/lib/editor/managers/TickManager/TickManager.test.ts +14 -13
  122. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +21 -26
  123. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.ts +1 -1
  124. package/src/lib/editor/shapes/ShapeUtil.ts +46 -0
  125. package/src/lib/editor/types/misc-types.ts +0 -6
  126. package/src/lib/exports/getSvgJsx.test.ts +868 -0
  127. package/src/lib/exports/getSvgJsx.tsx +76 -19
  128. package/src/lib/hooks/useCanvasEvents.ts +6 -6
  129. package/src/lib/hooks/usePassThroughMouseOverEvents.ts +4 -1
  130. package/src/lib/hooks/usePassThroughWheelEvents.ts +6 -1
  131. package/src/lib/license/LicenseManager.test.ts +692 -383
  132. package/src/lib/license/LicenseManager.ts +197 -53
  133. package/src/lib/license/LicenseProvider.tsx +74 -2
  134. package/src/lib/license/Watermark.test.tsx +2 -1
  135. package/src/lib/license/Watermark.tsx +73 -6
  136. package/src/lib/license/useLicenseManagerState.ts +2 -2
  137. package/src/lib/options.ts +6 -0
  138. package/src/lib/primitives/Box.test.ts +126 -0
  139. package/src/lib/primitives/Box.ts +10 -1
  140. package/src/lib/primitives/Vec.ts +0 -5
  141. package/src/lib/primitives/geometry/Geometry2d.test.ts +420 -0
  142. package/src/lib/primitives/geometry/Geometry2d.ts +78 -21
  143. package/src/lib/primitives/geometry/Group2d.ts +10 -1
  144. package/src/lib/utils/reparenting.ts +3 -69
  145. package/src/lib/utils/sync/LocalIndexedDb.test.ts +2 -1
  146. package/src/lib/utils/sync/TLLocalSyncClient.test.ts +15 -15
  147. package/src/version.ts +3 -3
  148. package/dist-cjs/lib/utils/nearestMultiple.js +0 -34
  149. package/dist-cjs/lib/utils/nearestMultiple.js.map +0 -7
  150. package/dist-esm/lib/utils/nearestMultiple.mjs +0 -14
  151. package/dist-esm/lib/utils/nearestMultiple.mjs.map +0 -7
  152. package/src/lib/utils/nearestMultiple.ts +0 -13
@@ -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'
@@ -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
  *
@@ -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 })
@@ -28,7 +28,6 @@ export const Shape = memo(function Shape({
28
28
  index,
29
29
  backgroundIndex,
30
30
  opacity,
31
- dprMultiple,
32
31
  }: {
33
32
  id: TLShapeId
34
33
  shape: TLShape
@@ -36,7 +35,6 @@ export const Shape = memo(function Shape({
36
35
  index: number
37
36
  backgroundIndex: number
38
37
  opacity: number
39
- dprMultiple: number
40
38
  }) {
41
39
  const editor = useEditor()
42
40
 
@@ -91,18 +89,14 @@ export const Shape = memo(function Shape({
91
89
  }
92
90
 
93
91
  // Width / Height
94
- // We round the shape width and height up to the nearest multiple of dprMultiple
95
- // to avoid the browser making miscalculations when applying the transform.
96
- const widthRemainder = bounds.w % dprMultiple
97
- const heightRemainder = bounds.h % dprMultiple
98
- const width = widthRemainder === 0 ? bounds.w : bounds.w + (dprMultiple - widthRemainder)
99
- const height = heightRemainder === 0 ? bounds.h : bounds.h + (dprMultiple - heightRemainder)
92
+ const width = Math.max(bounds.width, 1)
93
+ const height = Math.max(bounds.height, 1)
100
94
 
101
95
  if (width !== prev.width || height !== prev.height) {
102
- setStyleProperty(containerRef.current, 'width', Math.max(width, dprMultiple) + 'px')
103
- setStyleProperty(containerRef.current, 'height', Math.max(height, dprMultiple) + 'px')
104
- setStyleProperty(bgContainerRef.current, 'width', Math.max(width, dprMultiple) + 'px')
105
- setStyleProperty(bgContainerRef.current, 'height', Math.max(height, dprMultiple) + 'px')
96
+ setStyleProperty(containerRef.current, 'width', width + 'px')
97
+ setStyleProperty(containerRef.current, 'height', height + 'px')
98
+ setStyleProperty(bgContainerRef.current, 'width', width + 'px')
99
+ setStyleProperty(bgContainerRef.current, 'height', height + 'px')
106
100
  prev.width = width
107
101
  prev.height = height
108
102
  }
@@ -22,7 +22,6 @@ import { Vec } from '../../primitives/Vec'
22
22
  import { toDomPrecision } from '../../primitives/utils'
23
23
  import { debugFlags } from '../../utils/debug-flags'
24
24
  import { setStyleProperty } from '../../utils/dom'
25
- import { nearestMultiple } from '../../utils/nearestMultiple'
26
25
  import { GeometryDebuggingView } from '../GeometryDebuggingView'
27
26
  import { LiveCollaborators } from '../LiveCollaborators'
28
27
  import { MenuClickCapture } from '../MenuClickCapture'
@@ -173,10 +172,12 @@ export function DefaultCanvas({ className }: TLCanvasComponentProps) {
173
172
  <LiveCollaborators />
174
173
  </div>
175
174
  </div>
175
+ <div className="tl-canvas__in-front">
176
+ <InFrontOfTheCanvasWrapper />
177
+ </div>
176
178
  <MovingCameraHitTestBlocker />
177
179
  </div>
178
180
  <MenuClickCapture />
179
- <InFrontOfTheCanvasWrapper />
180
181
  </>
181
182
  )
182
183
  }
@@ -390,18 +391,9 @@ function ShapesWithSVGs() {
390
391
 
391
392
  const renderingShapes = useValue('rendering shapes', () => editor.getRenderingShapes(), [editor])
392
393
 
393
- const dprMultiple = useValue(
394
- 'dpr multiple',
395
- () =>
396
- // dprMultiple is the smallest number we can multiply dpr by to get an integer
397
- // it's usually 1, 2, or 4 (for e.g. dpr of 2, 2.5 and 2.25 respectively)
398
- nearestMultiple(Math.floor(editor.getInstanceState().devicePixelRatio * 100) / 100),
399
- [editor]
400
- )
401
-
402
394
  return renderingShapes.map((result) => (
403
395
  <Fragment key={result.id + '_fragment'}>
404
- <Shape {...result} dprMultiple={dprMultiple} />
396
+ <Shape {...result} />
405
397
  <DebugSvgCopy id={result.id} mode="iframe" />
406
398
  </Fragment>
407
399
  ))
@@ -436,19 +428,10 @@ function ShapesToDisplay() {
436
428
 
437
429
  const renderingShapes = useValue('rendering shapes', () => editor.getRenderingShapes(), [editor])
438
430
 
439
- const dprMultiple = useValue(
440
- 'dpr multiple',
441
- () =>
442
- // dprMultiple is the smallest number we can multiply dpr by to get an integer
443
- // it's usually 1, 2, or 4 (for e.g. dpr of 2, 2.5 and 2.25 respectively)
444
- nearestMultiple(Math.floor(editor.getInstanceState().devicePixelRatio * 100) / 100),
445
- [editor]
446
- )
447
-
448
431
  return (
449
432
  <>
450
433
  {renderingShapes.map((result) => (
451
- <Shape key={result.id + '_shape'} {...result} dprMultiple={dprMultiple} />
434
+ <Shape key={result.id + '_shape'} {...result} />
452
435
  ))}
453
436
  {tlenv.isSafari && <ReflowIfNeeded />}
454
437
  </>
@@ -75,7 +75,7 @@ export const DefaultErrorFallback: TLErrorFallbackComponent = ({ error, editor }
75
75
 
76
76
  // if we can't find a theme class from the app or from a parent, we have
77
77
  // to fall back on using a media query:
78
- if (typeof window !== 'undefined' && 'matchMedia' in window) {
78
+ if (typeof window !== 'undefined' && window.matchMedia) {
79
79
  setIsDarkMode(window.matchMedia('(prefers-color-scheme: dark)').matches)
80
80
  }
81
81
  }, [isDarkModeFromApp])
@@ -135,7 +135,7 @@ function getRandomColor() {
135
135
 
136
136
  /** @internal */
137
137
  export function userPrefersReducedMotion() {
138
- if (typeof window !== 'undefined' && 'matchMedia' in window) {
138
+ if (typeof window !== 'undefined' && window.matchMedia) {
139
139
  return window.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches ?? false
140
140
  }
141
141
 
@@ -1,3 +1,4 @@
1
+ import { vi } from 'vitest'
1
2
  import {
2
3
  Box,
3
4
  Geometry2d,
@@ -59,8 +60,8 @@ beforeEach(() => {
59
60
  getContainer: () => document.body,
60
61
  })
61
62
  editor.setCameraOptions({ isLocked: true })
62
- editor.setCamera = jest.fn()
63
- editor.user.getAnimationSpeed = jest.fn()
63
+ editor.setCamera = vi.fn()
64
+ editor.user.getAnimationSpeed = vi.fn()
64
65
  })
65
66
 
66
67
  describe('centerOnPoint', () => {
@@ -94,13 +95,13 @@ describe('updateShape', () => {
94
95
 
95
96
  describe('zoomToFit', () => {
96
97
  it('no-op when isLocked is set', () => {
97
- editor.getCurrentPageShapeIds = jest.fn(() => new Set([createShapeId('box1')]))
98
+ editor.getCurrentPageShapeIds = vi.fn(() => new Set([createShapeId('box1')]))
98
99
  editor.zoomToFit()
99
100
  expect(editor.setCamera).not.toHaveBeenCalled()
100
101
  })
101
102
 
102
103
  it('sets camera when isLocked is set and force flag is set', () => {
103
- editor.getCurrentPageShapeIds = jest.fn(() => new Set([createShapeId('box1')]))
104
+ editor.getCurrentPageShapeIds = vi.fn(() => new Set([createShapeId('box1')]))
104
105
  editor.zoomToFit({ force: true })
105
106
  expect(editor.setCamera).toHaveBeenCalled()
106
107
  })
@@ -144,13 +145,13 @@ describe('zoomOut', () => {
144
145
 
145
146
  describe('zoomToSelection', () => {
146
147
  it('no-op when isLocked is set', () => {
147
- editor.getSelectionPageBounds = jest.fn(() => Box.From({ x: 0, y: 0, w: 100, h: 100 }))
148
+ editor.getSelectionPageBounds = vi.fn(() => Box.From({ x: 0, y: 0, w: 100, h: 100 }))
148
149
  editor.zoomToSelection()
149
150
  expect(editor.setCamera).not.toHaveBeenCalled()
150
151
  })
151
152
 
152
153
  it('sets camera when isLocked is set and force flag is set', () => {
153
- editor.getSelectionPageBounds = jest.fn(() => Box.From({ x: 0, y: 0, w: 100, h: 100 }))
154
+ editor.getSelectionPageBounds = vi.fn(() => Box.From({ x: 0, y: 0, w: 100, h: 100 }))
154
155
  editor.zoomToSelection({ force: true })
155
156
  expect(editor.setCamera).toHaveBeenCalled()
156
157
  })
@@ -286,7 +287,7 @@ describe('getShapesAtPoint', () => {
286
287
 
287
288
  it('filters out hidden shapes', () => {
288
289
  // Create a spy to mock isShapeHidden
289
- const isShapeHiddenSpy = jest.spyOn(editor, 'isShapeHidden')
290
+ const isShapeHiddenSpy = vi.spyOn(editor, 'isShapeHidden')
290
291
  isShapeHiddenSpy.mockImplementation((shape) => {
291
292
  return typeof shape === 'string' ? shape === ids.shape3 : shape.id === ids.shape3
292
293
  })
@@ -352,7 +353,7 @@ describe('getShapesAtPoint', () => {
352
353
 
353
354
  it('returns empty array when all shapes are hidden', () => {
354
355
  // Mock all shapes as hidden
355
- const isShapeHiddenSpy = jest.spyOn(editor, 'isShapeHidden')
356
+ const isShapeHiddenSpy = vi.spyOn(editor, 'isShapeHidden')
356
357
  isShapeHiddenSpy.mockReturnValue(true)
357
358
 
358
359
  const shapes = editor.getShapesAtPoint({ x: 50, y: 50 })
@@ -692,7 +693,7 @@ describe('selectAll', () => {
692
693
  const initialSelectedIds = editor.getSelectedShapeIds()
693
694
 
694
695
  // Spy on setSelectedShapes to verify it's not called
695
- const setSelectedShapesSpy = jest.spyOn(editor, 'setSelectedShapes')
696
+ const setSelectedShapesSpy = vi.spyOn(editor, 'setSelectedShapes')
696
697
 
697
698
  // Call selectAll
698
699
  editor.selectAll()
@@ -713,7 +714,7 @@ describe('selectAll', () => {
713
714
  const initialSelectedIds = editor.getSelectedShapeIds()
714
715
 
715
716
  // Spy on setSelectedShapes to verify it's not called
716
- const setSelectedShapesSpy = jest.spyOn(editor, 'setSelectedShapes')
717
+ const setSelectedShapesSpy = vi.spyOn(editor, 'setSelectedShapes')
717
718
 
718
719
  // Call selectAll
719
720
  editor.selectAll()
@@ -818,7 +819,7 @@ describe('selectAll', () => {
818
819
  const initialSelectedIds = Array.from(editor.getSelectedShapeIds())
819
820
 
820
821
  // Spy on setSelectedShapes to verify it's not called
821
- const setSelectedShapesSpy = jest.spyOn(editor, 'setSelectedShapes')
822
+ const setSelectedShapesSpy = vi.spyOn(editor, 'setSelectedShapes')
822
823
 
823
824
  // Call selectAll
824
825
  editor.selectAll()
@@ -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
  *
@@ -6333,7 +6216,17 @@ export class Editor extends EventEmitter<TLEventMap> {
6333
6216
 
6334
6217
  this.createShapes(shapesToCreate)
6335
6218
  this.createBindings(bindingsToCreate)
6336
- this.setSelectedShapes(compact(ids.map((id) => shapeIds.get(id))))
6219
+
6220
+ this.setSelectedShapes(
6221
+ compact(
6222
+ ids.map((oldId) => {
6223
+ const newId = shapeIds.get(oldId)
6224
+ if (!newId) return null
6225
+ if (!this.getShape(newId)) return null
6226
+ return newId
6227
+ })
6228
+ )
6229
+ )
6337
6230
 
6338
6231
  if (offset !== undefined) {
6339
6232
  // If we've offset the duplicated shapes, check to see whether their new bounds is entirely
@@ -9451,13 +9344,6 @@ export class Editor extends EventEmitter<TLEventMap> {
9451
9344
  }
9452
9345
  }
9453
9346
 
9454
- /** @deprecated Use {@link Editor.getSvgString} or {@link Editor.getSvgElement} instead. */
9455
- async getSvg(shapes: TLShapeId[] | TLShape[], opts: TLSvgExportOptions = {}) {
9456
- const result = await this.getSvgElement(shapes, opts)
9457
- if (!result) return undefined
9458
- return result.svg
9459
- }
9460
-
9461
9347
  /**
9462
9348
  * Get an exported image of the given shapes.
9463
9349
  *
@@ -9509,6 +9395,24 @@ export class Editor extends EventEmitter<TLEventMap> {
9509
9395
  }
9510
9396
  }
9511
9397
 
9398
+ /**
9399
+ * Get an exported image of the given shapes as a data URL.
9400
+ *
9401
+ * @param shapes - The shapes (or shape ids) to export.
9402
+ * @param opts - Options for the export.
9403
+ *
9404
+ * @returns A data URL of the image.
9405
+ * @public
9406
+ */
9407
+ async toImageDataUrl(shapes: TLShapeId[] | TLShape[], opts: TLImageExportOptions = {}) {
9408
+ const { blob, width, height } = await this.toImage(shapes, opts)
9409
+ return {
9410
+ url: await FileHelpers.blobToDataUrl(blob),
9411
+ width,
9412
+ height,
9413
+ }
9414
+ }
9415
+
9512
9416
  /* --------------------- Events --------------------- */
9513
9417
 
9514
9418
  /**
@@ -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)