@tldraw/editor 3.16.0-canary.e1b1e53d3c16 → 3.16.0-canary.e1d5c8aeb399

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 (104) hide show
  1. package/dist-cjs/index.d.ts +61 -103
  2. package/dist-cjs/index.js +3 -5
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/TldrawEditor.js +1 -5
  5. package/dist-cjs/lib/TldrawEditor.js.map +2 -2
  6. package/dist-cjs/lib/components/default-components/DefaultCanvas.js +2 -2
  7. package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
  8. package/dist-cjs/lib/editor/Editor.js +23 -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 +7 -5
  18. package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
  19. package/dist-cjs/lib/hooks/usePassThroughMouseOverEvents.js +4 -1
  20. package/dist-cjs/lib/hooks/usePassThroughMouseOverEvents.js.map +2 -2
  21. package/dist-cjs/lib/license/LicenseManager.js +140 -53
  22. package/dist-cjs/lib/license/LicenseManager.js.map +2 -2
  23. package/dist-cjs/lib/license/LicenseProvider.js +39 -1
  24. package/dist-cjs/lib/license/LicenseProvider.js.map +2 -2
  25. package/dist-cjs/lib/license/Watermark.js +68 -6
  26. package/dist-cjs/lib/license/Watermark.js.map +3 -3
  27. package/dist-cjs/lib/license/useLicenseManagerState.js.map +2 -2
  28. package/dist-cjs/lib/primitives/Box.js +3 -0
  29. package/dist-cjs/lib/primitives/Box.js.map +2 -2
  30. package/dist-cjs/lib/primitives/Vec.js +0 -4
  31. package/dist-cjs/lib/primitives/Vec.js.map +2 -2
  32. package/dist-cjs/lib/primitives/geometry/Geometry2d.js +50 -20
  33. package/dist-cjs/lib/primitives/geometry/Geometry2d.js.map +2 -2
  34. package/dist-cjs/lib/primitives/geometry/Group2d.js +8 -1
  35. package/dist-cjs/lib/primitives/geometry/Group2d.js.map +2 -2
  36. package/dist-cjs/lib/utils/reparenting.js +2 -35
  37. package/dist-cjs/lib/utils/reparenting.js.map +3 -3
  38. package/dist-cjs/version.js +3 -3
  39. package/dist-cjs/version.js.map +1 -1
  40. package/dist-esm/index.d.mts +61 -103
  41. package/dist-esm/index.mjs +3 -5
  42. package/dist-esm/index.mjs.map +2 -2
  43. package/dist-esm/lib/TldrawEditor.mjs +1 -5
  44. package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
  45. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +2 -2
  46. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
  47. package/dist-esm/lib/editor/Editor.mjs +23 -113
  48. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  49. package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs +4 -0
  50. package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs.map +2 -2
  51. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +23 -0
  52. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  53. package/dist-esm/lib/exports/getSvgJsx.mjs +34 -14
  54. package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
  55. package/dist-esm/lib/hooks/useCanvasEvents.mjs +7 -5
  56. package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
  57. package/dist-esm/lib/hooks/usePassThroughMouseOverEvents.mjs +4 -1
  58. package/dist-esm/lib/hooks/usePassThroughMouseOverEvents.mjs.map +2 -2
  59. package/dist-esm/lib/license/LicenseManager.mjs +141 -54
  60. package/dist-esm/lib/license/LicenseManager.mjs.map +2 -2
  61. package/dist-esm/lib/license/LicenseProvider.mjs +39 -2
  62. package/dist-esm/lib/license/LicenseProvider.mjs.map +2 -2
  63. package/dist-esm/lib/license/Watermark.mjs +68 -6
  64. package/dist-esm/lib/license/Watermark.mjs.map +3 -3
  65. package/dist-esm/lib/license/useLicenseManagerState.mjs.map +2 -2
  66. package/dist-esm/lib/primitives/Box.mjs +4 -1
  67. package/dist-esm/lib/primitives/Box.mjs.map +2 -2
  68. package/dist-esm/lib/primitives/Vec.mjs +0 -4
  69. package/dist-esm/lib/primitives/Vec.mjs.map +2 -2
  70. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs +53 -21
  71. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs.map +2 -2
  72. package/dist-esm/lib/primitives/geometry/Group2d.mjs +8 -1
  73. package/dist-esm/lib/primitives/geometry/Group2d.mjs.map +2 -2
  74. package/dist-esm/lib/utils/reparenting.mjs +3 -40
  75. package/dist-esm/lib/utils/reparenting.mjs.map +2 -2
  76. package/dist-esm/version.mjs +3 -3
  77. package/dist-esm/version.mjs.map +1 -1
  78. package/editor.css +16 -3
  79. package/package.json +7 -7
  80. package/src/index.ts +2 -9
  81. package/src/lib/TldrawEditor.tsx +1 -13
  82. package/src/lib/components/default-components/DefaultCanvas.tsx +3 -1
  83. package/src/lib/editor/Editor.test.ts +90 -0
  84. package/src/lib/editor/Editor.ts +36 -150
  85. package/src/lib/editor/derivations/notVisibleShapes.ts +6 -0
  86. package/src/lib/editor/shapes/ShapeUtil.ts +46 -0
  87. package/src/lib/editor/types/misc-types.ts +0 -6
  88. package/src/lib/exports/getSvgJsx.test.ts +868 -0
  89. package/src/lib/exports/getSvgJsx.tsx +76 -19
  90. package/src/lib/hooks/useCanvasEvents.ts +6 -6
  91. package/src/lib/hooks/usePassThroughMouseOverEvents.ts +4 -1
  92. package/src/lib/license/LicenseManager.test.ts +721 -382
  93. package/src/lib/license/LicenseManager.ts +201 -58
  94. package/src/lib/license/LicenseProvider.tsx +74 -2
  95. package/src/lib/license/Watermark.tsx +73 -6
  96. package/src/lib/license/useLicenseManagerState.ts +2 -2
  97. package/src/lib/primitives/Box.test.ts +126 -0
  98. package/src/lib/primitives/Box.ts +10 -1
  99. package/src/lib/primitives/Vec.ts +0 -5
  100. package/src/lib/primitives/geometry/Geometry2d.test.ts +420 -0
  101. package/src/lib/primitives/geometry/Geometry2d.ts +78 -21
  102. package/src/lib/primitives/geometry/Group2d.ts +10 -1
  103. package/src/lib/utils/reparenting.ts +3 -69
  104. package/src/version.ts +3 -3
@@ -57,33 +57,21 @@ export function getSvgJsx(editor: Editor, ids: TLShapeId[], opts: TLImageExportO
57
57
  .filter(({ id }) => shapeIdsToInclude.has(id))
58
58
 
59
59
  // --- Common bounding box of all shapes
60
+ const singleFrameShapeId =
61
+ ids.length === 1 && editor.isShapeOfType<TLFrameShape>(editor.getShape(ids[0])!, 'frame')
62
+ ? ids[0]
63
+ : null
64
+
60
65
  let bbox: null | Box = null
61
66
  if (opts.bounds) {
62
- bbox = opts.bounds
67
+ bbox = opts.bounds.clone().expandBy(padding)
63
68
  } else {
64
- for (const { id } of renderingShapes) {
65
- const maskedPageBounds = editor.getShapeMaskedPageBounds(id)
66
- if (!maskedPageBounds) continue
67
- if (bbox) {
68
- bbox.union(maskedPageBounds)
69
- } else {
70
- bbox = maskedPageBounds.clone()
71
- }
72
- }
69
+ bbox = getExportDefaultBounds(editor, renderingShapes, padding, singleFrameShapeId)
73
70
  }
74
71
 
75
72
  // no unmasked shapes to export
76
73
  if (!bbox) return
77
74
 
78
- const singleFrameShapeId =
79
- ids.length === 1 && editor.isShapeOfType<TLFrameShape>(editor.getShape(ids[0])!, 'frame')
80
- ? ids[0]
81
- : null
82
- if (!singleFrameShapeId) {
83
- // Expand by an extra 32 pixels
84
- bbox.expandBy(padding)
85
- }
86
-
87
75
  // We want the svg image to be BIGGER THAN USUAL to account for image quality
88
76
  const w = bbox.width * scale
89
77
  const h = bbox.height * scale
@@ -120,6 +108,75 @@ export function getSvgJsx(editor: Editor, ids: TLShapeId[], opts: TLImageExportO
120
108
  return { jsx: svg, width: w, height: h, exportDelay }
121
109
  }
122
110
 
111
+ /**
112
+ * Calculates the default bounds for an SVG export. This function handles:
113
+ * 1. Computing masked page bounds for each shape
114
+ * 2. Container logic: if a shape is marked as an export bounds container and it
115
+ * contains all other shapes, use its bounds and skip padding
116
+ * 3. Otherwise, create a union of all shape bounds and apply padding
117
+ *
118
+ * The container logic is useful for cases like annotating on an image - if the image
119
+ * contains all annotations, we want to export exactly the image bounds without extra padding.
120
+ *
121
+ * @param editor - The editor instance
122
+ * @param renderingShapes - The shapes to include in the export
123
+ * @param padding - Padding to add around the bounds (only applied if no container bounds)
124
+ * @param singleFrameShapeId - If exporting a single frame, this is its ID (skips padding)
125
+ * @returns The calculated bounds box, or null if no shapes to export
126
+ */
127
+ export function getExportDefaultBounds(
128
+ editor: Editor,
129
+ renderingShapes: TLRenderingShape[],
130
+ padding: number,
131
+ singleFrameShapeId: TLShapeId | null
132
+ ) {
133
+ let isBoundedByContainer = false
134
+ let bbox: null | Box = null
135
+
136
+ for (const { id } of renderingShapes) {
137
+ const maskedPageBounds = editor.getShapeMaskedPageBounds(id)
138
+ if (!maskedPageBounds) continue
139
+
140
+ // Check if this shape is an export bounds container (e.g., an image being annotated)
141
+ const shape = editor.getShape(id)!
142
+ const isContainer = editor.getShapeUtil(shape).isExportBoundsContainer(shape)
143
+
144
+ if (bbox) {
145
+ // Container logic: if this is a container and it contains all shapes processed so far,
146
+ // use the container's bounds instead of the union. This prevents extra padding around
147
+ // things like annotated images.
148
+ if (isContainer && Box.ContainsApproximately(maskedPageBounds, bbox)) {
149
+ isBoundedByContainer = true
150
+ bbox = maskedPageBounds.clone()
151
+ } else {
152
+ // If we were previously bounded by a container but this shape extends outside it,
153
+ // we're no longer bounded by a container
154
+ if (isBoundedByContainer && !Box.ContainsApproximately(bbox, maskedPageBounds)) {
155
+ isBoundedByContainer = false
156
+ }
157
+ // Expand the bounding box to include this shape
158
+ bbox.union(maskedPageBounds)
159
+ }
160
+ } else {
161
+ // First shape sets the initial bounds
162
+ isBoundedByContainer = isContainer
163
+ bbox = maskedPageBounds.clone()
164
+ }
165
+ }
166
+
167
+ // No unmasked shapes to export
168
+ if (!bbox) return null
169
+
170
+ // Only apply padding if:
171
+ // - Not exporting a single frame (frames have their own padding rules)
172
+ // - Not bounded by a container (containers define their own bounds precisely)
173
+ if (!singleFrameShapeId && !isBoundedByContainer) {
174
+ bbox.expandBy(padding)
175
+ }
176
+
177
+ return bbox
178
+ }
179
+
123
180
  function SvgExport({
124
181
  editor,
125
182
  preserveAspectRatio,
@@ -79,15 +79,15 @@ export function useCanvasEvents() {
79
79
  // check that e.target is an HTMLElement
80
80
  if (!(e.target instanceof HTMLElement)) return
81
81
 
82
+ const editingShapeId = editor.getEditingShape()?.id
82
83
  if (
84
+ // if the target is not inside the editing shape
85
+ !(editingShapeId && e.target.closest(`[data-shape-id="${editingShapeId}"]`)) &&
86
+ // and the target is not an clickable element
83
87
  e.target.tagName !== 'A' &&
88
+ // or a TextArea.tsx ?
84
89
  e.target.tagName !== 'TEXTAREA' &&
85
- !e.target.isContentEditable &&
86
- // When in EditingShape state, we are actually clicking on a 'DIV'
87
- // not A/TEXTAREA/contenteditable element yet. So, to preserve cursor position
88
- // for edit mode on mobile we need to not preventDefault.
89
- // TODO: Find out if we still need this preventDefault in general though.
90
- !(editor.getEditingShape() && e.target.className.includes('tl-text-content'))
90
+ !e.target.isContentEditable
91
91
  ) {
92
92
  preventDefault(e)
93
93
  }
@@ -1,14 +1,17 @@
1
1
  import { RefObject, useEffect } from 'react'
2
2
  import { preventDefault } from '../utils/dom'
3
3
  import { useContainer } from './useContainer'
4
+ import { useMaybeEditor } from './useEditor'
4
5
 
5
6
  /** @public */
6
7
  export function usePassThroughMouseOverEvents(ref: RefObject<HTMLElement>) {
7
8
  if (!ref) throw Error('usePassThroughWheelEvents must be passed a ref')
8
9
  const container = useContainer()
10
+ const editor = useMaybeEditor()
9
11
 
10
12
  useEffect(() => {
11
13
  function onMouseOver(e: MouseEvent) {
14
+ if (!editor?.getInstanceState().isFocused) return
12
15
  if ((e as any).isSpecialRedispatchedEvent) return
13
16
  preventDefault(e)
14
17
  const cvs = container.querySelector('.tl-canvas')
@@ -25,5 +28,5 @@ export function usePassThroughMouseOverEvents(ref: RefObject<HTMLElement>) {
25
28
  return () => {
26
29
  elm.removeEventListener('mouseover', onMouseOver)
27
30
  }
28
- }, [container, ref])
31
+ }, [container, editor, ref])
29
32
  }