@tldraw/editor 3.12.0-canary.c4570c603c42 → 3.12.0-canary.c4d18f877b2f

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 (105) hide show
  1. package/dist-cjs/index.d.ts +155 -17
  2. package/dist-cjs/index.js +3 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/TldrawEditor.js +5 -0
  5. package/dist-cjs/lib/TldrawEditor.js.map +2 -2
  6. package/dist-cjs/lib/components/GeometryDebuggingView.js +2 -2
  7. package/dist-cjs/lib/components/GeometryDebuggingView.js.map +2 -2
  8. package/dist-cjs/lib/components/Shape.js +8 -12
  9. package/dist-cjs/lib/components/Shape.js.map +2 -2
  10. package/dist-cjs/lib/components/default-components/DefaultCanvas.js +10 -1
  11. package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
  12. package/dist-cjs/lib/editor/Editor.js +215 -30
  13. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  14. package/dist-cjs/lib/editor/derivations/notVisibleShapes.js +1 -1
  15. package/dist-cjs/lib/editor/derivations/notVisibleShapes.js.map +2 -2
  16. package/dist-cjs/lib/editor/shapes/ShapeUtil.js +12 -0
  17. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  18. package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js +4 -13
  19. package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js.map +2 -2
  20. package/dist-cjs/lib/editor/types/selection-types.js.map +1 -1
  21. package/dist-cjs/lib/exports/StyleEmbedder.js +19 -5
  22. package/dist-cjs/lib/exports/StyleEmbedder.js.map +2 -2
  23. package/dist-cjs/lib/exports/cssRules.js +127 -0
  24. package/dist-cjs/lib/exports/cssRules.js.map +7 -0
  25. package/dist-cjs/lib/exports/parseCss.js +0 -69
  26. package/dist-cjs/lib/exports/parseCss.js.map +2 -2
  27. package/dist-cjs/lib/hooks/useDocumentEvents.js +16 -0
  28. package/dist-cjs/lib/hooks/useDocumentEvents.js.map +2 -2
  29. package/dist-cjs/lib/hooks/useGestureEvents.js +12 -6
  30. package/dist-cjs/lib/hooks/useGestureEvents.js.map +2 -2
  31. package/dist-cjs/lib/license/Watermark.js +10 -20
  32. package/dist-cjs/lib/license/Watermark.js.map +2 -2
  33. package/dist-cjs/lib/primitives/geometry/Geometry2d.js +133 -16
  34. package/dist-cjs/lib/primitives/geometry/Geometry2d.js.map +3 -3
  35. package/dist-cjs/lib/primitives/geometry/Group2d.js +54 -11
  36. package/dist-cjs/lib/primitives/geometry/Group2d.js.map +2 -2
  37. package/dist-cjs/lib/primitives/intersect.js +20 -0
  38. package/dist-cjs/lib/primitives/intersect.js.map +2 -2
  39. package/dist-cjs/lib/utils/reorderShapes.js +2 -8
  40. package/dist-cjs/lib/utils/reorderShapes.js.map +2 -2
  41. package/dist-cjs/version.js +3 -3
  42. package/dist-cjs/version.js.map +1 -1
  43. package/dist-esm/index.d.mts +155 -17
  44. package/dist-esm/index.mjs +8 -2
  45. package/dist-esm/index.mjs.map +2 -2
  46. package/dist-esm/lib/TldrawEditor.mjs +5 -0
  47. package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
  48. package/dist-esm/lib/components/GeometryDebuggingView.mjs +3 -3
  49. package/dist-esm/lib/components/GeometryDebuggingView.mjs.map +2 -2
  50. package/dist-esm/lib/components/Shape.mjs +9 -13
  51. package/dist-esm/lib/components/Shape.mjs.map +2 -2
  52. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +10 -1
  53. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
  54. package/dist-esm/lib/editor/Editor.mjs +216 -30
  55. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  56. package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs +1 -1
  57. package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs.map +2 -2
  58. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +12 -0
  59. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  60. package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs +4 -13
  61. package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs.map +2 -2
  62. package/dist-esm/lib/exports/StyleEmbedder.mjs +21 -12
  63. package/dist-esm/lib/exports/StyleEmbedder.mjs.map +2 -2
  64. package/dist-esm/lib/exports/cssRules.mjs +107 -0
  65. package/dist-esm/lib/exports/cssRules.mjs.map +7 -0
  66. package/dist-esm/lib/exports/parseCss.mjs +0 -69
  67. package/dist-esm/lib/exports/parseCss.mjs.map +2 -2
  68. package/dist-esm/lib/hooks/useDocumentEvents.mjs +16 -0
  69. package/dist-esm/lib/hooks/useDocumentEvents.mjs.map +2 -2
  70. package/dist-esm/lib/hooks/useGestureEvents.mjs +12 -6
  71. package/dist-esm/lib/hooks/useGestureEvents.mjs.map +2 -2
  72. package/dist-esm/lib/license/Watermark.mjs +10 -20
  73. package/dist-esm/lib/license/Watermark.mjs.map +2 -2
  74. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs +137 -14
  75. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs.map +2 -2
  76. package/dist-esm/lib/primitives/geometry/Group2d.mjs +55 -12
  77. package/dist-esm/lib/primitives/geometry/Group2d.mjs.map +2 -2
  78. package/dist-esm/lib/primitives/intersect.mjs +20 -0
  79. package/dist-esm/lib/primitives/intersect.mjs.map +2 -2
  80. package/dist-esm/lib/utils/reorderShapes.mjs +2 -8
  81. package/dist-esm/lib/utils/reorderShapes.mjs.map +2 -2
  82. package/dist-esm/version.mjs +3 -3
  83. package/dist-esm/version.mjs.map +1 -1
  84. package/package.json +7 -7
  85. package/src/index.ts +11 -2
  86. package/src/lib/TldrawEditor.tsx +30 -3
  87. package/src/lib/components/GeometryDebuggingView.tsx +3 -3
  88. package/src/lib/components/Shape.tsx +13 -17
  89. package/src/lib/components/default-components/DefaultCanvas.tsx +6 -1
  90. package/src/lib/editor/Editor.ts +322 -37
  91. package/src/lib/editor/derivations/notVisibleShapes.ts +1 -1
  92. package/src/lib/editor/shapes/ShapeUtil.ts +14 -0
  93. package/src/lib/editor/shapes/group/GroupShapeUtil.tsx +7 -15
  94. package/src/lib/editor/types/selection-types.ts +3 -0
  95. package/src/lib/exports/StyleEmbedder.ts +25 -15
  96. package/src/lib/exports/cssRules.ts +126 -0
  97. package/src/lib/exports/parseCss.ts +0 -79
  98. package/src/lib/hooks/useDocumentEvents.ts +18 -0
  99. package/src/lib/hooks/useGestureEvents.ts +12 -6
  100. package/src/lib/license/Watermark.tsx +18 -29
  101. package/src/lib/primitives/geometry/Geometry2d.ts +196 -16
  102. package/src/lib/primitives/geometry/Group2d.ts +76 -13
  103. package/src/lib/primitives/intersect.ts +41 -0
  104. package/src/lib/utils/reorderShapes.ts +2 -9
  105. package/src/version.ts +3 -3
@@ -1,5 +1,6 @@
1
- import { assertExists, objectMapValues, uniqueId } from '@tldraw/utils'
1
+ import { assertExists, getOwnProperty, objectMapValues, uniqueId } from '@tldraw/utils'
2
2
  import { FontEmbedder } from './FontEmbedder'
3
+ import { ReadonlyStyles, Styles, cssRules } from './cssRules'
3
4
  import {
4
5
  elementStyle,
5
6
  getComputedStyle,
@@ -7,15 +8,8 @@ import {
7
8
  getRenderedChildren,
8
9
  } from './domUtils'
9
10
  import { resourceToDataUrl } from './fetchCache'
10
- import {
11
- isPropertyCoveredByCurrentColor,
12
- isPropertyInherited,
13
- parseCssValueUrls,
14
- shouldIncludeCssProperty,
15
- } from './parseCss'
16
-
17
- type Styles = { [K in string]?: string }
18
- type ReadonlyStyles = { readonly [K in string]?: string }
11
+ import { parseCssValueUrls, shouldIncludeCssProperty } from './parseCss'
12
+
19
13
  const NO_STYLES = {} as const
20
14
 
21
15
  interface ElementStyleInfo {
@@ -239,15 +233,22 @@ function styleFromComputedStyleMap(
239
233
  { defaultStyles, parentStyles }: ReadStyleOpts
240
234
  ) {
241
235
  const styles: Record<string, string> = {}
236
+ const currentColor = style.get('color')?.toString() || ''
237
+ const ruleOptions = {
238
+ currentColor,
239
+ parentStyles,
240
+ defaultStyles,
241
+ getStyle: (property: string) => style.get(property)?.toString() ?? '',
242
+ }
242
243
  for (const property of style.keys()) {
243
244
  if (!shouldIncludeCssProperty(property)) continue
244
245
 
245
246
  const value = style.get(property)!.toString()
246
247
 
247
248
  if (defaultStyles[property] === value) continue
248
- if (parentStyles[property] === value && isPropertyInherited(property)) continue
249
- if (isPropertyCoveredByCurrentColor(style.get('color')?.toString() || '', property, value))
250
- continue
249
+
250
+ const rule = getOwnProperty(cssRules, property)
251
+ if (rule && rule(value, property, ruleOptions)) continue
251
252
 
252
253
  styles[property] = value
253
254
  }
@@ -260,14 +261,23 @@ function styleFromComputedStyle(
260
261
  { defaultStyles, parentStyles }: ReadStyleOpts
261
262
  ) {
262
263
  const styles: Record<string, string> = {}
264
+ const currentColor = style.color
265
+ const ruleOptions = {
266
+ currentColor,
267
+ parentStyles,
268
+ defaultStyles,
269
+ getStyle: (property: string) => style.getPropertyValue(property),
270
+ }
271
+
263
272
  for (const property in style) {
264
273
  if (!shouldIncludeCssProperty(property)) continue
265
274
 
266
275
  const value = style.getPropertyValue(property)
267
276
 
268
277
  if (defaultStyles[property] === value) continue
269
- if (parentStyles[property] === value && isPropertyInherited(property)) continue
270
- if (isPropertyCoveredByCurrentColor(style.color, property, value)) continue
278
+
279
+ const rule = getOwnProperty(cssRules, property)
280
+ if (rule && rule(value, property, ruleOptions)) continue
271
281
 
272
282
  styles[property] = value
273
283
  }
@@ -0,0 +1,126 @@
1
+ export type Styles = { [K in string]?: string }
2
+ export type ReadonlyStyles = { readonly [K in string]?: string }
3
+
4
+ type CanSkipRule = (
5
+ value: string,
6
+ property: string,
7
+ options: {
8
+ getStyle(property: string): string
9
+ parentStyles: ReadonlyStyles
10
+ defaultStyles: ReadonlyStyles
11
+ currentColor: string
12
+ }
13
+ ) => boolean
14
+
15
+ const isCoveredByCurrentColor: CanSkipRule = (value, property, { currentColor }) => {
16
+ return value === 'currentColor' || value === currentColor
17
+ }
18
+
19
+ const isInherited: CanSkipRule = (value, property, { parentStyles }) => {
20
+ return parentStyles[property] === value
21
+ }
22
+
23
+ // see comment below about why we exclude border styles
24
+ const isExcludedBorder =
25
+ (borderDirection: string): CanSkipRule =>
26
+ (value, property, { getStyle }) => {
27
+ const borderWidth = getStyle(`border-${borderDirection}-width`)
28
+ const borderStyle = getStyle(`border-${borderDirection}-style`)
29
+
30
+ if (borderWidth === '0px') return true
31
+ if (borderStyle === 'none') return true
32
+ return false
33
+ }
34
+
35
+ export const cssRules = {
36
+ // currentColor properties:
37
+ 'border-block-end-color': isCoveredByCurrentColor,
38
+ 'border-block-start-color': isCoveredByCurrentColor,
39
+ 'border-bottom-color': isCoveredByCurrentColor,
40
+ 'border-inline-end-color': isCoveredByCurrentColor,
41
+ 'border-inline-start-color': isCoveredByCurrentColor,
42
+ 'border-left-color': isCoveredByCurrentColor,
43
+ 'border-right-color': isCoveredByCurrentColor,
44
+ 'border-top-color': isCoveredByCurrentColor,
45
+ 'caret-color': isCoveredByCurrentColor,
46
+ 'column-rule-color': isCoveredByCurrentColor,
47
+ 'outline-color': isCoveredByCurrentColor,
48
+ 'text-decoration': (value, property, { currentColor }) => {
49
+ return value === 'none solid currentColor' || value === 'none solid ' + currentColor
50
+ },
51
+ 'text-decoration-color': isCoveredByCurrentColor,
52
+ 'text-emphasis-color': isCoveredByCurrentColor,
53
+
54
+ // inherited properties:
55
+ 'border-collapse': isInherited,
56
+ 'border-spacing': isInherited,
57
+ 'caption-side': isInherited,
58
+ // N.B. We shouldn't inherit 'color' because there's some UA styling, e.g. `mark` elements
59
+ // 'color': isInherited,
60
+ cursor: isInherited,
61
+ direction: isInherited,
62
+ 'empty-cells': isInherited,
63
+ 'font-family': isInherited,
64
+ 'font-size': isInherited,
65
+ 'font-style': isInherited,
66
+ 'font-variant': isInherited,
67
+ 'font-weight': isInherited,
68
+ 'font-size-adjust': isInherited,
69
+ 'font-stretch': isInherited,
70
+ font: isInherited,
71
+ 'letter-spacing': isInherited,
72
+ 'line-height': isInherited,
73
+ 'list-style-image': isInherited,
74
+ 'list-style-position': isInherited,
75
+ 'list-style-type': isInherited,
76
+ 'list-style': isInherited,
77
+ orphans: isInherited,
78
+ 'overflow-wrap': isInherited,
79
+ quotes: isInherited,
80
+ 'stroke-linecap': isInherited,
81
+ 'stroke-linejoin': isInherited,
82
+ 'tab-size': isInherited,
83
+ 'text-align': isInherited,
84
+ 'text-align-last': isInherited,
85
+ 'text-indent': isInherited,
86
+ 'text-justify': isInherited,
87
+ 'text-shadow': isInherited,
88
+ 'text-transform': isInherited,
89
+ visibility: isInherited,
90
+ 'white-space': isInherited,
91
+ 'white-space-collapse': isInherited,
92
+ widows: isInherited,
93
+ 'word-break': isInherited,
94
+ 'word-spacing': isInherited,
95
+ 'word-wrap': isInherited,
96
+
97
+ // special border cases - we have a weird case (tailwind seems to trigger this) where all
98
+ // border-styles sometimes get set to 'solid', but the border-width is 0 so they don't render.
99
+ // but in SVGs, **sometimes**, the border-width defaults (i think from a UA style-sheet? but
100
+ // honestly can't tell) to 1.5px so the border displays. we work around this by only including
101
+ // border styles at all if both the border-width and border-style are set to something that
102
+ // would show a border.
103
+ 'border-top': isExcludedBorder('top'),
104
+ 'border-right': isExcludedBorder('right'),
105
+ 'border-bottom': isExcludedBorder('bottom'),
106
+ 'border-left': isExcludedBorder('left'),
107
+ 'border-block-end': isExcludedBorder('block-end'),
108
+ 'border-block-start': isExcludedBorder('block-start'),
109
+ 'border-inline-end': isExcludedBorder('inline-end'),
110
+ 'border-inline-start': isExcludedBorder('inline-start'),
111
+ 'border-top-style': isExcludedBorder('top'),
112
+ 'border-right-style': isExcludedBorder('right'),
113
+ 'border-bottom-style': isExcludedBorder('bottom'),
114
+ 'border-left-style': isExcludedBorder('left'),
115
+ 'border-block-end-style': isExcludedBorder('block-end'),
116
+ 'border-block-start-style': isExcludedBorder('block-start'),
117
+ 'border-inline-end-style': isExcludedBorder('inline-end'),
118
+ 'border-inline-start-style': isExcludedBorder('inline-start'),
119
+ 'border-top-width': isExcludedBorder('top'),
120
+ 'border-right-width': isExcludedBorder('right'),
121
+ 'border-bottom-width': isExcludedBorder('bottom'),
122
+ 'border-left-width': isExcludedBorder('left'),
123
+ 'border-block-end-width': isExcludedBorder('block-end'),
124
+ 'border-block-start-width': isExcludedBorder('block-start'),
125
+ 'border-inline-end-width': isExcludedBorder('inline-end'),
126
+ } satisfies Record<string, CanSkipRule>
@@ -110,82 +110,3 @@ export function parseCssValueUrls(value: string) {
110
110
  url: m[1] || m[2] || m[3],
111
111
  }))
112
112
  }
113
-
114
- const currentColorProperties = new Set([
115
- 'border-block-end-color',
116
- 'border-block-start-color',
117
- 'border-bottom-color',
118
- 'border-inline-end-color',
119
- 'border-inline-start-color',
120
- 'border-left-color',
121
- 'border-right-color',
122
- 'border-top-color',
123
- 'caret-color',
124
- 'column-rule-color',
125
- 'outline-color',
126
- 'text-decoration',
127
- 'text-decoration-color',
128
- 'text-emphasis-color',
129
- ])
130
-
131
- export function isPropertyCoveredByCurrentColor(
132
- currentColor: string,
133
- property: string,
134
- value: string
135
- ) {
136
- if (currentColorProperties.has(property)) {
137
- return (
138
- value === 'currentColor' ||
139
- value === currentColor ||
140
- (property === 'text-decoration' && value === `none solid ${currentColor}`)
141
- )
142
- }
143
- }
144
-
145
- const inheritedProperties = new Set([
146
- 'border-collapse',
147
- 'border-spacing',
148
- 'caption-side',
149
- // N.B. We shouldn't inherit 'color' because there's some UA styling, e.g. `mark` elements
150
- // 'color',
151
- 'cursor',
152
- 'direction',
153
- 'empty-cells',
154
- 'font-family',
155
- 'font-size',
156
- 'font-style',
157
- 'font-variant',
158
- 'font-weight',
159
- 'font-size-adjust',
160
- 'font-stretch',
161
- 'font',
162
- 'letter-spacing',
163
- 'line-height',
164
- 'list-style-image',
165
- 'list-style-position',
166
- 'list-style-type',
167
- 'list-style',
168
- 'orphans',
169
- 'overflow-wrap',
170
- 'quotes',
171
- 'stroke-linecap',
172
- 'stroke-linejoin',
173
- 'tab-size',
174
- 'text-align',
175
- 'text-align-last',
176
- 'text-indent',
177
- 'text-justify',
178
- 'text-shadow',
179
- 'text-transform',
180
- 'visibility',
181
- 'white-space',
182
- 'white-space-collapse',
183
- 'widows',
184
- 'word-break',
185
- 'word-spacing',
186
- 'word-wrap',
187
- ])
188
-
189
- export function isPropertyInherited(property: string) {
190
- return inheritedProperties.has(property)
191
- }
@@ -104,6 +104,7 @@ export function useDocumentEvents() {
104
104
 
105
105
  if ((e as any).isKilled) return
106
106
  ;(e as any).isKilled = true
107
+ const hasSelectedShapes = !!editor.getSelectedShapeIds().length
107
108
 
108
109
  switch (e.key) {
109
110
  case '=':
@@ -124,6 +125,23 @@ export function useDocumentEvents() {
124
125
  if (areShortcutsDisabled(editor)) {
125
126
  return
126
127
  }
128
+ if (hasSelectedShapes) {
129
+ // This is used in tandem with shape navigation.
130
+ preventDefault(e)
131
+ }
132
+ break
133
+ }
134
+ case 'ArrowLeft':
135
+ case 'ArrowRight':
136
+ case 'ArrowUp':
137
+ case 'ArrowDown': {
138
+ if (areShortcutsDisabled(editor)) {
139
+ return
140
+ }
141
+ if (hasSelectedShapes && (e.metaKey || e.ctrlKey)) {
142
+ // This is used in tandem with shape navigation.
143
+ preventDefault(e)
144
+ }
127
145
  break
128
146
  }
129
147
  case ',': {
@@ -135,7 +135,6 @@ export function useGestureEvents(ref: React.RefObject<HTMLDivElement>) {
135
135
 
136
136
  let initDistanceBetweenFingers = 1 // the distance between the two fingers when the pinch starts
137
137
  let initZoom = 1 // the browser's zoom level when the pinch starts
138
- let currZoom = 1 // the current zoom level according to the pinch gesture recognizer
139
138
  let currDistanceBetweenFingers = 0
140
139
  const initPointBetweenFingers = new Vec()
141
140
  const prevPointBetweenFingers = new Vec()
@@ -239,7 +238,7 @@ export function useGestureEvents(ref: React.RefObject<HTMLDivElement>) {
239
238
 
240
239
  switch (pinchState) {
241
240
  case 'zooming': {
242
- currZoom = offset[0]
241
+ const currZoom = offset[0] ** editor.getCameraOptions().zoomSpeed
243
242
 
244
243
  editor.dispatch({
245
244
  type: 'pinch',
@@ -278,7 +277,7 @@ export function useGestureEvents(ref: React.RefObject<HTMLDivElement>) {
278
277
  if (event instanceof WheelEvent) return
279
278
  if (!(event.target === elm || elm?.contains(event.target as Node))) return
280
279
 
281
- const scale = offset[0]
280
+ const scale = offset[0] ** editor.getCameraOptions().zoomSpeed
282
281
 
283
282
  pinchState = 'not sure'
284
283
 
@@ -309,14 +308,21 @@ export function useGestureEvents(ref: React.RefObject<HTMLDivElement>) {
309
308
  target: ref,
310
309
  eventOptions: { passive: false },
311
310
  pinch: {
312
- from: () => [editor.getZoomLevel(), 0], // Return the camera z to use when pinch starts
311
+ from: () => {
312
+ const { zoomSpeed } = editor.getCameraOptions()
313
+ const level = editor.getZoomLevel() ** (1 / zoomSpeed)
314
+ return [level, 0]
315
+ }, // Return the camera z to use when pinch starts
313
316
  scaleBounds: () => {
314
317
  const baseZoom = editor.getBaseZoom()
315
- const zoomSteps = editor.getCameraOptions().zoomSteps
318
+ const { zoomSteps, zoomSpeed } = editor.getCameraOptions()
316
319
  const zoomMin = zoomSteps[0] * baseZoom
317
320
  const zoomMax = zoomSteps[zoomSteps.length - 1] * baseZoom
318
321
 
319
- return { from: editor.getZoomLevel(), max: zoomMax, min: zoomMin }
322
+ return {
323
+ max: zoomMax ** (1 / zoomSpeed),
324
+ min: zoomMin ** (1 / zoomSpeed),
325
+ }
320
326
  },
321
327
  },
322
328
  })
@@ -1,6 +1,5 @@
1
1
  import { useValue } from '@tldraw/state-react'
2
2
  import { memo, useRef } from 'react'
3
- import { tlenv } from '../globals/environment'
4
3
  import { useCanvasEvents } from '../hooks/useCanvasEvents'
5
4
  import { useEditor } from '../hooks/useEditor'
6
5
  import { usePassThroughWheelEvents } from '../hooks/usePassThroughWheelEvents'
@@ -57,29 +56,17 @@ const WatermarkInner = memo(function WatermarkInner({ src }: { src: string }) {
57
56
  draggable={false}
58
57
  {...events}
59
58
  >
60
- {tlenv.isWebview ? (
61
- <a
62
- draggable={false}
63
- role="button"
64
- onPointerDown={(e) => {
65
- stopEventPropagation(e)
66
- preventDefault(e)
67
- }}
68
- onClick={() => runtime.openWindow(url, '_blank')}
69
- style={{ mask: maskCss, WebkitMask: maskCss }}
70
- />
71
- ) : (
72
- <a
73
- href={url}
74
- target="_blank"
75
- rel="noreferrer"
76
- draggable={false}
77
- onPointerDown={(e) => {
78
- stopEventPropagation(e)
79
- }}
80
- style={{ mask: maskCss, WebkitMask: maskCss }}
81
- />
82
- )}
59
+ <button
60
+ draggable={false}
61
+ role="button"
62
+ onPointerDown={(e) => {
63
+ stopEventPropagation(e)
64
+ preventDefault(e)
65
+ }}
66
+ title="made with tldraw"
67
+ onClick={() => runtime.openWindow(url, '_blank')}
68
+ style={{ mask: maskCss, WebkitMask: maskCss }}
69
+ />
83
70
  </div>
84
71
  )
85
72
  })
@@ -115,7 +102,7 @@ To remove the watermark, please purchase a license at tldraw.dev.
115
102
  box-sizing: content-box;
116
103
  }
117
104
 
118
- .${className} > a {
105
+ .${className} > button {
119
106
  position: absolute;
120
107
  width: 96px;
121
108
  height: 32px;
@@ -123,6 +110,8 @@ To remove the watermark, please purchase a license at tldraw.dev.
123
110
  cursor: inherit;
124
111
  color: var(--color-text);
125
112
  opacity: .38;
113
+ border: 0;
114
+ padding: 0;
126
115
  background-color: currentColor;
127
116
  }
128
117
 
@@ -137,13 +126,13 @@ To remove the watermark, please purchase a license at tldraw.dev.
137
126
  height: 48px;
138
127
  }
139
128
 
140
- .${className}[data-mobile='true'] > a {
129
+ .${className}[data-mobile='true'] > button {
141
130
  width: 8px;
142
131
  height: 32px;
143
132
  }
144
133
 
145
134
  @media (hover: hover) {
146
- .${className} > a {
135
+ .${className} > button {
147
136
  pointer-events: none;
148
137
  }
149
138
 
@@ -153,12 +142,12 @@ To remove the watermark, please purchase a license at tldraw.dev.
153
142
  transition-delay: 0.32s;
154
143
  }
155
144
 
156
- .${className}:hover > a {
145
+ .${className}:hover > button {
157
146
  animation: delayed_link 0.2s forwards ease-in-out;
158
147
  animation-delay: 0.32s;
159
148
  }
160
149
 
161
- .${className} > a:focus-visible {
150
+ .${className} > button:focus-visible {
162
151
  opacity: 1;
163
152
  }
164
153
  }