@tldraw/editor 3.16.0-canary.cc5427cdff41 → 3.16.0-canary.cd822ae4ebee
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.
- package/dist-cjs/index.d.ts +57 -4
- package/dist-cjs/index.js +1 -1
- package/dist-cjs/lib/TldrawEditor.js +1 -3
- package/dist-cjs/lib/TldrawEditor.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js +11 -1
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
- package/dist-cjs/lib/editor/Editor.js +38 -4
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/derivations/notVisibleShapes.js +4 -0
- package/dist-cjs/lib/editor/derivations/notVisibleShapes.js.map +2 -2
- package/dist-cjs/lib/editor/managers/FocusManager/FocusManager.js +4 -2
- package/dist-cjs/lib/editor/managers/FocusManager/FocusManager.js.map +2 -2
- package/dist-cjs/lib/editor/shapes/ShapeUtil.js +10 -0
- package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
- package/dist-cjs/lib/hooks/useCanvasEvents.js +19 -16
- package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useDocumentEvents.js +5 -5
- package/dist-cjs/lib/hooks/useDocumentEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.js +1 -2
- package/dist-cjs/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useGestureEvents.js +1 -1
- package/dist-cjs/lib/hooks/useGestureEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useHandleEvents.js +6 -6
- package/dist-cjs/lib/hooks/useHandleEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useSelectionEvents.js +8 -8
- package/dist-cjs/lib/hooks/useSelectionEvents.js.map +2 -2
- package/dist-cjs/lib/license/LicenseManager.js +24 -4
- package/dist-cjs/lib/license/LicenseManager.js.map +2 -2
- package/dist-cjs/lib/license/LicenseProvider.js +17 -1
- package/dist-cjs/lib/license/LicenseProvider.js.map +2 -2
- package/dist-cjs/lib/license/Watermark.js +97 -90
- package/dist-cjs/lib/license/Watermark.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Geometry2d.js +24 -2
- package/dist-cjs/lib/primitives/geometry/Geometry2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Group2d.js +5 -1
- package/dist-cjs/lib/primitives/geometry/Group2d.js.map +2 -2
- package/dist-cjs/lib/utils/dom.js.map +2 -2
- package/dist-cjs/lib/utils/getPointerInfo.js +2 -3
- package/dist-cjs/lib/utils/getPointerInfo.js.map +2 -2
- package/dist-cjs/version.js +3 -3
- package/dist-cjs/version.js.map +1 -1
- package/dist-esm/index.d.mts +57 -4
- package/dist-esm/index.mjs +1 -1
- package/dist-esm/lib/TldrawEditor.mjs +1 -3
- package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +11 -1
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +38 -4
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs +4 -0
- package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/FocusManager/FocusManager.mjs +4 -2
- package/dist-esm/lib/editor/managers/FocusManager/FocusManager.mjs.map +2 -2
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +10 -0
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/hooks/useCanvasEvents.mjs +20 -22
- package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useDocumentEvents.mjs +6 -6
- package/dist-esm/lib/hooks/useDocumentEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.mjs +1 -2
- package/dist-esm/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useGestureEvents.mjs +2 -2
- package/dist-esm/lib/hooks/useGestureEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useHandleEvents.mjs +6 -6
- package/dist-esm/lib/hooks/useHandleEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useSelectionEvents.mjs +9 -14
- package/dist-esm/lib/hooks/useSelectionEvents.mjs.map +2 -2
- package/dist-esm/lib/license/LicenseManager.mjs +24 -4
- package/dist-esm/lib/license/LicenseManager.mjs.map +2 -2
- package/dist-esm/lib/license/LicenseProvider.mjs +16 -1
- package/dist-esm/lib/license/LicenseProvider.mjs.map +2 -2
- package/dist-esm/lib/license/Watermark.mjs +98 -91
- package/dist-esm/lib/license/Watermark.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Geometry2d.mjs +24 -2
- package/dist-esm/lib/primitives/geometry/Geometry2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Group2d.mjs +5 -1
- package/dist-esm/lib/primitives/geometry/Group2d.mjs.map +2 -2
- package/dist-esm/lib/utils/dom.mjs.map +2 -2
- package/dist-esm/lib/utils/getPointerInfo.mjs +2 -3
- package/dist-esm/lib/utils/getPointerInfo.mjs.map +2 -2
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/package.json +7 -7
- package/src/lib/TldrawEditor.tsx +1 -4
- package/src/lib/components/default-components/DefaultCanvas.tsx +7 -1
- package/src/lib/editor/Editor.test.ts +90 -0
- package/src/lib/editor/Editor.ts +49 -4
- package/src/lib/editor/derivations/notVisibleShapes.ts +6 -0
- package/src/lib/editor/managers/FocusManager/FocusManager.ts +6 -2
- package/src/lib/editor/shapes/ShapeUtil.ts +11 -0
- package/src/lib/hooks/useCanvasEvents.ts +20 -20
- package/src/lib/hooks/useDocumentEvents.ts +6 -6
- package/src/lib/hooks/useFixSafariDoubleTapZoomPencilEvents.ts +1 -1
- package/src/lib/hooks/useGestureEvents.ts +2 -2
- package/src/lib/hooks/useHandleEvents.ts +6 -6
- package/src/lib/hooks/useSelectionEvents.ts +9 -14
- package/src/lib/license/LicenseManager.test.ts +78 -2
- package/src/lib/license/LicenseManager.ts +31 -5
- package/src/lib/license/LicenseProvider.tsx +40 -1
- package/src/lib/license/Watermark.tsx +100 -92
- package/src/lib/primitives/geometry/Geometry2d.test.ts +420 -0
- package/src/lib/primitives/geometry/Geometry2d.ts +29 -2
- package/src/lib/primitives/geometry/Group2d.ts +6 -1
- package/src/lib/test/InFrontOfTheCanvas.test.tsx +187 -0
- package/src/lib/utils/dom.test.ts +103 -0
- package/src/lib/utils/dom.ts +8 -1
- package/src/lib/utils/getPointerInfo.ts +3 -2
- package/src/version.ts +3 -3
|
@@ -266,7 +266,7 @@ describe('LicenseManager', () => {
|
|
|
266
266
|
delete window.location
|
|
267
267
|
// @ts-ignore
|
|
268
268
|
window.location = new URL(
|
|
269
|
-
'vscode-webview
|
|
269
|
+
'vscode-webview://1ipd8pun8ud7nd7hv9d112g7evi7m10vak9vviuvia66ou6aibp3/index.html?id=6ec2dc7a-afe9-45d9-bd71-1749f9568d28&origin=955b256f-37e1-4a72-a2f4-ad633e88239c&swVersion=4&extensionId=tldraw-org.tldraw-vscode&platform=electron&vscode-resource-base-authority=vscode-resource.vscode-cdn.net&parentOrigin=vscode-file%3A%2F%2Fvscode-app'
|
|
270
270
|
)
|
|
271
271
|
|
|
272
272
|
const permissiveHostsInfo = JSON.parse(STANDARD_LICENSE_INFO)
|
|
@@ -286,7 +286,7 @@ describe('LicenseManager', () => {
|
|
|
286
286
|
delete window.location
|
|
287
287
|
// @ts-ignore
|
|
288
288
|
window.location = new URL(
|
|
289
|
-
'vscode-webview
|
|
289
|
+
'vscode-webview://1ipd8pun8ud7nd7hv9d112g7evi7m10vak9vviuvia66ou6aibp3/index.html?id=6ec2dc7a-afe9-45d9-bd71-1749f9568d28&origin=955b256f-37e1-4a72-a2f4-ad633e88239c&swVersion=4&extensionId=tldraw-org.tldraw-vscode&platform=electron&vscode-resource-base-authority=vscode-resource.vscode-cdn.net&parentOrigin=vscode-file%3A%2F%2Fvscode-app'
|
|
290
290
|
)
|
|
291
291
|
|
|
292
292
|
const permissiveHostsInfo = JSON.parse(STANDARD_LICENSE_INFO)
|
|
@@ -300,6 +300,70 @@ describe('LicenseManager', () => {
|
|
|
300
300
|
)) as ValidLicenseKeyResult
|
|
301
301
|
expect(result.isDomainValid).toBe(false)
|
|
302
302
|
})
|
|
303
|
+
|
|
304
|
+
it('Succeeds if it is a native app', async () => {
|
|
305
|
+
// @ts-ignore
|
|
306
|
+
delete window.location
|
|
307
|
+
// @ts-ignore
|
|
308
|
+
window.location = new URL('app-bundle://app/index.html')
|
|
309
|
+
|
|
310
|
+
const nativeLicenseInfo = JSON.parse(STANDARD_LICENSE_INFO)
|
|
311
|
+
nativeLicenseInfo[PROPERTIES.FLAGS] = FLAGS.NATIVE_LICENSE
|
|
312
|
+
nativeLicenseInfo[PROPERTIES.HOSTS] = ['app-bundle:']
|
|
313
|
+
const nativeLicenseKey = await generateLicenseKey(JSON.stringify(nativeLicenseInfo), keyPair)
|
|
314
|
+
const result = (await licenseManager.getLicenseFromKey(
|
|
315
|
+
nativeLicenseKey
|
|
316
|
+
)) as ValidLicenseKeyResult
|
|
317
|
+
expect(result.isDomainValid).toBe(true)
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
it('Succeeds if it is a native app with a wildcard', async () => {
|
|
321
|
+
// @ts-ignore
|
|
322
|
+
delete window.location
|
|
323
|
+
// @ts-ignore
|
|
324
|
+
window.location = new URL('app-bundle://unique-id-123/index.html')
|
|
325
|
+
|
|
326
|
+
const nativeLicenseInfo = JSON.parse(STANDARD_LICENSE_INFO)
|
|
327
|
+
nativeLicenseInfo[PROPERTIES.FLAGS] = FLAGS.NATIVE_LICENSE
|
|
328
|
+
nativeLicenseInfo[PROPERTIES.HOSTS] = ['^app-bundle://unique-id-123.*']
|
|
329
|
+
const nativeLicenseKey = await generateLicenseKey(JSON.stringify(nativeLicenseInfo), keyPair)
|
|
330
|
+
const result = (await licenseManager.getLicenseFromKey(
|
|
331
|
+
nativeLicenseKey
|
|
332
|
+
)) as ValidLicenseKeyResult
|
|
333
|
+
expect(result.isDomainValid).toBe(true)
|
|
334
|
+
})
|
|
335
|
+
|
|
336
|
+
it('Succeeds if it is a native app with a wildcard and search param', async () => {
|
|
337
|
+
// @ts-ignore
|
|
338
|
+
delete window.location
|
|
339
|
+
// @ts-ignore
|
|
340
|
+
window.location = new URL('app-bundle://app/index.html?unique-id-123')
|
|
341
|
+
|
|
342
|
+
const nativeLicenseInfo = JSON.parse(STANDARD_LICENSE_INFO)
|
|
343
|
+
nativeLicenseInfo[PROPERTIES.FLAGS] = FLAGS.NATIVE_LICENSE
|
|
344
|
+
nativeLicenseInfo[PROPERTIES.HOSTS] = ['^app-bundle://app.*unique-id-123.*']
|
|
345
|
+
const nativeLicenseKey = await generateLicenseKey(JSON.stringify(nativeLicenseInfo), keyPair)
|
|
346
|
+
const result = (await licenseManager.getLicenseFromKey(
|
|
347
|
+
nativeLicenseKey
|
|
348
|
+
)) as ValidLicenseKeyResult
|
|
349
|
+
expect(result.isDomainValid).toBe(true)
|
|
350
|
+
})
|
|
351
|
+
|
|
352
|
+
it('Fails if it is a native app with the wrong protocol', async () => {
|
|
353
|
+
// @ts-ignore
|
|
354
|
+
delete window.location
|
|
355
|
+
// @ts-ignore
|
|
356
|
+
window.location = new URL('blah-blundle://app/index.html')
|
|
357
|
+
|
|
358
|
+
const nativeLicenseInfo = JSON.parse(STANDARD_LICENSE_INFO)
|
|
359
|
+
nativeLicenseInfo[PROPERTIES.FLAGS] = FLAGS.NATIVE_LICENSE
|
|
360
|
+
nativeLicenseInfo[PROPERTIES.HOSTS] = ['app-bundle:']
|
|
361
|
+
const nativeLicenseKey = await generateLicenseKey(JSON.stringify(nativeLicenseInfo), keyPair)
|
|
362
|
+
const result = (await licenseManager.getLicenseFromKey(
|
|
363
|
+
nativeLicenseKey
|
|
364
|
+
)) as ValidLicenseKeyResult
|
|
365
|
+
expect(result.isDomainValid).toBe(false)
|
|
366
|
+
})
|
|
303
367
|
})
|
|
304
368
|
|
|
305
369
|
describe('License types and flags', () => {
|
|
@@ -316,6 +380,17 @@ describe('LicenseManager', () => {
|
|
|
316
380
|
expect(result.isInternalLicense).toBe(true)
|
|
317
381
|
})
|
|
318
382
|
|
|
383
|
+
it('Checks for native license', async () => {
|
|
384
|
+
const nativeLicenseInfo = JSON.parse(STANDARD_LICENSE_INFO)
|
|
385
|
+
nativeLicenseInfo[PROPERTIES.FLAGS] = FLAGS.NATIVE_LICENSE
|
|
386
|
+
const nativeLicenseKey = await generateLicenseKey(JSON.stringify(nativeLicenseInfo), keyPair)
|
|
387
|
+
|
|
388
|
+
const result = (await licenseManager.getLicenseFromKey(
|
|
389
|
+
nativeLicenseKey
|
|
390
|
+
)) as ValidLicenseKeyResult
|
|
391
|
+
expect(result.isNativeLicense).toBe(true)
|
|
392
|
+
})
|
|
393
|
+
|
|
319
394
|
it('Checks for license with watermark', async () => {
|
|
320
395
|
const withWatermarkLicenseInfo = JSON.parse(STANDARD_LICENSE_INFO)
|
|
321
396
|
withWatermarkLicenseInfo[PROPERTIES.FLAGS] |= FLAGS.WITH_WATERMARK
|
|
@@ -553,6 +628,7 @@ function getDefaultLicenseResult(overrides: Partial<ValidLicenseKeyResult>): Val
|
|
|
553
628
|
isAnnualLicense: true,
|
|
554
629
|
isAnnualLicenseExpired: false,
|
|
555
630
|
isInternalLicense: false,
|
|
631
|
+
isNativeLicense: false,
|
|
556
632
|
isDevelopment: false,
|
|
557
633
|
isDomainValid: true,
|
|
558
634
|
isPerpetualLicense: false,
|
|
@@ -6,11 +6,22 @@ import { importPublicKey, str2ab } from '../utils/licensing'
|
|
|
6
6
|
const GRACE_PERIOD_DAYS = 30
|
|
7
7
|
|
|
8
8
|
export const FLAGS = {
|
|
9
|
+
// -- MUTUALLY EXCLUSIVE FLAGS --
|
|
10
|
+
// Annual means the license expires after a time period, usually 1 year.
|
|
9
11
|
ANNUAL_LICENSE: 1,
|
|
12
|
+
// Perpetual means the license never expires up to the max supported version.
|
|
10
13
|
PERPETUAL_LICENSE: 1 << 1,
|
|
14
|
+
|
|
15
|
+
// -- ADDITIVE FLAGS --
|
|
16
|
+
// Internal means the license is for internal use only.
|
|
11
17
|
INTERNAL_LICENSE: 1 << 2,
|
|
18
|
+
// Watermark means the product is watermarked.
|
|
12
19
|
WITH_WATERMARK: 1 << 3,
|
|
20
|
+
// Evaluation means the license is for evaluation purposes only.
|
|
13
21
|
EVALUATION_LICENSE: 1 << 4,
|
|
22
|
+
// Native means the license is for native apps which switches
|
|
23
|
+
// on special-case logic.
|
|
24
|
+
NATIVE_LICENSE: 1 << 5,
|
|
14
25
|
}
|
|
15
26
|
const HIGHEST_FLAG = Math.max(...Object.values(FLAGS))
|
|
16
27
|
|
|
@@ -69,6 +80,7 @@ export interface ValidLicenseKeyResult {
|
|
|
69
80
|
isPerpetualLicense: boolean
|
|
70
81
|
isPerpetualLicenseExpired: boolean
|
|
71
82
|
isInternalLicense: boolean
|
|
83
|
+
isNativeLicense: boolean
|
|
72
84
|
isLicensedWithWatermark: boolean
|
|
73
85
|
isEvaluationLicense: boolean
|
|
74
86
|
isEvaluationLicenseExpired: boolean
|
|
@@ -166,6 +178,9 @@ export class LicenseManager {
|
|
|
166
178
|
const url = new URL(WATERMARK_TRACK_SRC)
|
|
167
179
|
url.searchParams.set('version', version)
|
|
168
180
|
url.searchParams.set('license_type', trackType)
|
|
181
|
+
if ('license' in result) {
|
|
182
|
+
url.searchParams.set('license_id', result.license.id)
|
|
183
|
+
}
|
|
169
184
|
|
|
170
185
|
// eslint-disable-next-line no-restricted-globals
|
|
171
186
|
fetch(url.toString())
|
|
@@ -271,6 +286,7 @@ export class LicenseManager {
|
|
|
271
286
|
isPerpetualLicense,
|
|
272
287
|
isPerpetualLicenseExpired: isPerpetualLicense && this.isPerpetualLicenseExpired(expiryDate),
|
|
273
288
|
isInternalLicense: this.isFlagEnabled(licenseInfo.flags, FLAGS.INTERNAL_LICENSE),
|
|
289
|
+
isNativeLicense: this.isNativeLicense(licenseInfo),
|
|
274
290
|
isLicensedWithWatermark: this.isFlagEnabled(licenseInfo.flags, FLAGS.WITH_WATERMARK),
|
|
275
291
|
isEvaluationLicense,
|
|
276
292
|
isEvaluationLicenseExpired:
|
|
@@ -291,13 +307,13 @@ export class LicenseManager {
|
|
|
291
307
|
const currentHostname = window.location.hostname.toLowerCase()
|
|
292
308
|
|
|
293
309
|
return licenseInfo.hosts.some((host) => {
|
|
294
|
-
const
|
|
310
|
+
const normalizedHostOrUrlRegex = host.toLowerCase().trim()
|
|
295
311
|
|
|
296
312
|
// Allow the domain if listed and www variations, 'example.com' allows 'example.com' and 'www.example.com'
|
|
297
313
|
if (
|
|
298
|
-
|
|
299
|
-
`www.${
|
|
300
|
-
|
|
314
|
+
normalizedHostOrUrlRegex === currentHostname ||
|
|
315
|
+
`www.${normalizedHostOrUrlRegex}` === currentHostname ||
|
|
316
|
+
normalizedHostOrUrlRegex === `www.${currentHostname}`
|
|
301
317
|
) {
|
|
302
318
|
return true
|
|
303
319
|
}
|
|
@@ -308,6 +324,12 @@ export class LicenseManager {
|
|
|
308
324
|
return true
|
|
309
325
|
}
|
|
310
326
|
|
|
327
|
+
// Native license support
|
|
328
|
+
// In this case, `normalizedHost` is actually a protocol, e.g. `app-bundle:`
|
|
329
|
+
if (this.isNativeLicense(licenseInfo)) {
|
|
330
|
+
return new RegExp(normalizedHostOrUrlRegex).test(window.location.href)
|
|
331
|
+
}
|
|
332
|
+
|
|
311
333
|
// Glob testing, we only support '*.somedomain.com' right now.
|
|
312
334
|
if (host.includes('*')) {
|
|
313
335
|
const globToRegex = new RegExp(host.replace(/\*/g, '.*?'))
|
|
@@ -318,7 +340,7 @@ export class LicenseManager {
|
|
|
318
340
|
if (window.location.protocol === 'vscode-webview:') {
|
|
319
341
|
const currentUrl = new URL(window.location.href)
|
|
320
342
|
const extensionId = currentUrl.searchParams.get('extensionId')
|
|
321
|
-
if (
|
|
343
|
+
if (normalizedHostOrUrlRegex === extensionId) {
|
|
322
344
|
return true
|
|
323
345
|
}
|
|
324
346
|
}
|
|
@@ -327,6 +349,10 @@ export class LicenseManager {
|
|
|
327
349
|
})
|
|
328
350
|
}
|
|
329
351
|
|
|
352
|
+
private isNativeLicense(licenseInfo: LicenseInfo) {
|
|
353
|
+
return this.isFlagEnabled(licenseInfo.flags, FLAGS.NATIVE_LICENSE)
|
|
354
|
+
}
|
|
355
|
+
|
|
330
356
|
private getExpirationDateWithoutGracePeriod(expiryDate: Date) {
|
|
331
357
|
return new Date(expiryDate.getFullYear(), expiryDate.getMonth(), expiryDate.getDate())
|
|
332
358
|
}
|
|
@@ -17,7 +17,7 @@ export const LICENSE_TIMEOUT = 5000
|
|
|
17
17
|
|
|
18
18
|
/** @internal */
|
|
19
19
|
export function LicenseProvider({
|
|
20
|
-
licenseKey,
|
|
20
|
+
licenseKey = getLicenseKeyFromEnv() ?? undefined,
|
|
21
21
|
children,
|
|
22
22
|
}: {
|
|
23
23
|
licenseKey?: string
|
|
@@ -51,3 +51,42 @@ export function LicenseProvider({
|
|
|
51
51
|
function LicenseGate() {
|
|
52
52
|
return <div data-testid="tl-license-expired" style={{ display: 'none' }} />
|
|
53
53
|
}
|
|
54
|
+
|
|
55
|
+
let envLicenseKey: string | undefined | null = undefined
|
|
56
|
+
function getLicenseKeyFromEnv() {
|
|
57
|
+
if (envLicenseKey !== undefined) {
|
|
58
|
+
return envLicenseKey
|
|
59
|
+
}
|
|
60
|
+
// it's important here that we write out the full process.env.WHATEVER expression instead of
|
|
61
|
+
// doing something like process.env[someVariable]. This is because most bundlers do something
|
|
62
|
+
// like a find-replace inject environment variables, and so won't pick up on dynamic ones. It
|
|
63
|
+
// also means we can't do checks like `process.env && process.env.WHATEVER`, which is why we use
|
|
64
|
+
// the `getEnv` try/catch approach.
|
|
65
|
+
|
|
66
|
+
// framework-specific prefixes borrowed from the ones vercel uses, but trimmed down to just the
|
|
67
|
+
// react-y ones: https://vercel.com/docs/environment-variables/framework-environment-variables
|
|
68
|
+
envLicenseKey =
|
|
69
|
+
getEnv(() => process.env.TLDRAW_LICENSE_KEY) ||
|
|
70
|
+
getEnv(() => process.env.NEXT_PUBLIC_TLDRAW_LICENSE_KEY) ||
|
|
71
|
+
getEnv(() => process.env.REACT_APP_TLDRAW_LICENSE_KEY) ||
|
|
72
|
+
getEnv(() => process.env.GATSBY_TLDRAW_LICENSE_KEY) ||
|
|
73
|
+
getEnv(() => process.env.VITE_TLDRAW_LICENSE_KEY) ||
|
|
74
|
+
getEnv(() => process.env.PUBLIC_TLDRAW_LICENSE_KEY) ||
|
|
75
|
+
getEnv(() => (import.meta as any).env.TLDRAW_LICENSE_KEY) ||
|
|
76
|
+
getEnv(() => (import.meta as any).env.NEXT_PUBLIC_TLDRAW_LICENSE_KEY) ||
|
|
77
|
+
getEnv(() => (import.meta as any).env.REACT_APP_TLDRAW_LICENSE_KEY) ||
|
|
78
|
+
getEnv(() => (import.meta as any).env.GATSBY_TLDRAW_LICENSE_KEY) ||
|
|
79
|
+
getEnv(() => (import.meta as any).env.VITE_TLDRAW_LICENSE_KEY) ||
|
|
80
|
+
getEnv(() => (import.meta as any).env.PUBLIC_TLDRAW_LICENSE_KEY) ||
|
|
81
|
+
null
|
|
82
|
+
|
|
83
|
+
return envLicenseKey
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function getEnv(cb: () => string | undefined) {
|
|
87
|
+
try {
|
|
88
|
+
return cb()
|
|
89
|
+
} catch {
|
|
90
|
+
return undefined
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -3,7 +3,7 @@ import { memo, useRef } from 'react'
|
|
|
3
3
|
import { useCanvasEvents } from '../hooks/useCanvasEvents'
|
|
4
4
|
import { useEditor } from '../hooks/useEditor'
|
|
5
5
|
import { usePassThroughWheelEvents } from '../hooks/usePassThroughWheelEvents'
|
|
6
|
-
import { preventDefault
|
|
6
|
+
import { preventDefault } from '../utils/dom'
|
|
7
7
|
import { runtime } from '../utils/runtime'
|
|
8
8
|
import { watermarkDesktopSvg, watermarkMobileSvg } from '../watermarks'
|
|
9
9
|
import { LicenseManager } from './LicenseManager'
|
|
@@ -43,11 +43,13 @@ const UnlicensedWatermark = memo(function UnlicensedWatermark({
|
|
|
43
43
|
isDebugMode: boolean
|
|
44
44
|
isMobile: boolean
|
|
45
45
|
}) {
|
|
46
|
+
const editor = useEditor()
|
|
46
47
|
const events = useCanvasEvents()
|
|
47
48
|
const ref = useRef<HTMLDivElement>(null)
|
|
48
49
|
usePassThroughWheelEvents(ref)
|
|
49
50
|
|
|
50
|
-
const url =
|
|
51
|
+
const url =
|
|
52
|
+
'https://tldraw.dev/pricing?utm_source=dotcom&utm_medium=organic&utm_campaign=watermark'
|
|
51
53
|
|
|
52
54
|
return (
|
|
53
55
|
<div
|
|
@@ -64,26 +66,13 @@ const UnlicensedWatermark = memo(function UnlicensedWatermark({
|
|
|
64
66
|
draggable={false}
|
|
65
67
|
role="button"
|
|
66
68
|
onPointerDown={(e) => {
|
|
67
|
-
|
|
69
|
+
editor.markEventAsHandled(e)
|
|
68
70
|
preventDefault(e)
|
|
69
71
|
}}
|
|
70
|
-
title="
|
|
72
|
+
title="The tldraw SDK requires a license key to work in production. You can get a free 100-day trial license at tldraw.dev/pricing."
|
|
71
73
|
onClick={() => runtime.openWindow(url, '_blank')}
|
|
72
|
-
style={{
|
|
73
|
-
position: 'absolute',
|
|
74
|
-
pointerEvents: 'all',
|
|
75
|
-
cursor: 'pointer',
|
|
76
|
-
color: 'var(--tl-color-text)',
|
|
77
|
-
opacity: 0.8,
|
|
78
|
-
border: 0,
|
|
79
|
-
padding: 0,
|
|
80
|
-
backgroundColor: 'transparent',
|
|
81
|
-
fontSize: '11px',
|
|
82
|
-
fontWeight: '600',
|
|
83
|
-
textAlign: 'center',
|
|
84
|
-
}}
|
|
85
74
|
>
|
|
86
|
-
|
|
75
|
+
Get a license for production
|
|
87
76
|
</button>
|
|
88
77
|
</div>
|
|
89
78
|
)
|
|
@@ -127,10 +116,10 @@ const WatermarkInner = memo(function WatermarkInner({
|
|
|
127
116
|
draggable={false}
|
|
128
117
|
role="button"
|
|
129
118
|
onPointerDown={(e) => {
|
|
130
|
-
|
|
119
|
+
editor.markEventAsHandled(e)
|
|
131
120
|
preventDefault(e)
|
|
132
121
|
}}
|
|
133
|
-
title="
|
|
122
|
+
title="Build infinite canvas applications with the tldraw SDK. Learn more at https://tldraw.dev."
|
|
134
123
|
onClick={() => runtime.openWindow(url, '_blank')}
|
|
135
124
|
style={{ mask: maskCss, WebkitMask: maskCss }}
|
|
136
125
|
/>
|
|
@@ -142,7 +131,8 @@ const LicenseStyles = memo(function LicenseStyles() {
|
|
|
142
131
|
const editor = useEditor()
|
|
143
132
|
const className = LicenseManager.className
|
|
144
133
|
|
|
145
|
-
const CSS =
|
|
134
|
+
const CSS = `
|
|
135
|
+
/* ------------------- SEE LICENSE -------------------
|
|
146
136
|
The tldraw watermark is part of tldraw's license. It is shown for unlicensed
|
|
147
137
|
or "licensed-with-watermark" users. By using this library, you agree to
|
|
148
138
|
preserve the watermark's behavior, keeping it visible, unobscured, and
|
|
@@ -151,87 +141,105 @@ available to user-interaction.
|
|
|
151
141
|
To remove the watermark, please purchase a license at tldraw.dev.
|
|
152
142
|
*/
|
|
153
143
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
144
|
+
.${className} {
|
|
145
|
+
position: absolute;
|
|
146
|
+
bottom: max(var(--tl-space-2), env(safe-area-inset-bottom));
|
|
147
|
+
right: max(var(--tl-space-2), env(safe-area-inset-right));
|
|
148
|
+
width: 96px;
|
|
149
|
+
height: 32px;
|
|
150
|
+
display: flex;
|
|
151
|
+
align-items: center;
|
|
152
|
+
justify-content: center;
|
|
153
|
+
z-index: var(--tl-layer-watermark) !important;
|
|
154
|
+
background-color: color-mix(in srgb, var(--tl-color-background) 62%, transparent);
|
|
155
|
+
opacity: 1;
|
|
156
|
+
border-radius: 5px;
|
|
157
|
+
pointer-events: all;
|
|
158
|
+
padding: 2px;
|
|
159
|
+
box-sizing: content-box;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.${className} > button {
|
|
163
|
+
position: absolute;
|
|
164
|
+
width: 96px;
|
|
165
|
+
height: 32px;
|
|
166
|
+
pointer-events: all;
|
|
167
|
+
cursor: inherit;
|
|
168
|
+
color: var(--tl-color-text);
|
|
169
|
+
opacity: .38;
|
|
170
|
+
border: 0;
|
|
171
|
+
padding: 0;
|
|
172
|
+
background-color: currentColor;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.${className}[data-debug='true'] {
|
|
176
|
+
bottom: max(46px, env(safe-area-inset-bottom));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.${className}[data-mobile='true'] {
|
|
180
|
+
border-radius: 4px 0px 0px 4px;
|
|
181
|
+
right: max(-2px, calc(env(safe-area-inset-right) - 2px));
|
|
182
|
+
width: 8px;
|
|
183
|
+
height: 48px;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.${className}[data-mobile='true'] > button {
|
|
187
|
+
width: 8px;
|
|
188
|
+
height: 32px;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.${className}[data-unlicensed='true'] > button {
|
|
192
|
+
font-size: 100px;
|
|
193
|
+
position: absolute;
|
|
194
|
+
pointer-events: all;
|
|
195
|
+
cursor: pointer;
|
|
196
|
+
color: var(--tl-color-text);
|
|
197
|
+
opacity: 0.8;
|
|
198
|
+
border: 0;
|
|
199
|
+
padding: 0;
|
|
200
|
+
background-color: transparent;
|
|
201
|
+
font-size: 11px;
|
|
202
|
+
font-weight: 600;
|
|
203
|
+
text-align: center;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.${className}[data-mobile='true'][data-unlicensed='true'] > button {
|
|
207
|
+
display: none;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
@media (hover: hover) {
|
|
211
|
+
.${className}[data-licensed='false'] > button {
|
|
212
|
+
pointer-events: none;
|
|
170
213
|
}
|
|
171
214
|
|
|
172
|
-
.${className}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
pointer-events: all;
|
|
177
|
-
cursor: inherit;
|
|
178
|
-
color: var(--tl-color-text);
|
|
179
|
-
opacity: .38;
|
|
180
|
-
border: 0;
|
|
181
|
-
padding: 0;
|
|
182
|
-
background-color: currentColor;
|
|
215
|
+
.${className}[data-licensed='false']:hover {
|
|
216
|
+
background-color: var(--tl-color-background);
|
|
217
|
+
transition: background-color 0.2s ease-in-out;
|
|
218
|
+
transition-delay: 0.32s;
|
|
183
219
|
}
|
|
184
220
|
|
|
185
|
-
.${className}[data-
|
|
186
|
-
|
|
221
|
+
.${className}[data-licensed='false']:hover > button {
|
|
222
|
+
animation: ${className}_delayed_link 0.2s forwards ease-in-out;
|
|
223
|
+
animation-delay: 0.32s;
|
|
187
224
|
}
|
|
188
225
|
|
|
189
|
-
.${className}[data-
|
|
190
|
-
|
|
191
|
-
right: max(-2px, calc(env(safe-area-inset-right) - 2px));
|
|
192
|
-
width: 8px;
|
|
193
|
-
height: 48px;
|
|
226
|
+
.${className}[data-licensed='false'] > button:focus-visible {
|
|
227
|
+
opacity: 1;
|
|
194
228
|
}
|
|
229
|
+
}
|
|
195
230
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
231
|
+
@keyframes ${className}_delayed_link {
|
|
232
|
+
0% {
|
|
233
|
+
cursor: inherit;
|
|
234
|
+
opacity: .38;
|
|
235
|
+
pointer-events: none;
|
|
199
236
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
.${className}:hover {
|
|
207
|
-
background-color: var(--tl-color-background);
|
|
208
|
-
transition: background-color 0.2s ease-in-out;
|
|
209
|
-
transition-delay: 0.32s;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
.${className}:hover > button {
|
|
213
|
-
animation: ${className}_delayed_link 0.2s forwards ease-in-out;
|
|
214
|
-
animation-delay: 0.32s;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
.${className} > button:focus-visible {
|
|
218
|
-
opacity: 1;
|
|
219
|
-
}
|
|
237
|
+
100% {
|
|
238
|
+
cursor: pointer;
|
|
239
|
+
opacity: 1;
|
|
240
|
+
pointer-events: all;
|
|
220
241
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
@keyframes ${className}_delayed_link {
|
|
224
|
-
0% {
|
|
225
|
-
cursor: inherit;
|
|
226
|
-
opacity: .38;
|
|
227
|
-
pointer-events: none;
|
|
228
|
-
}
|
|
229
|
-
100% {
|
|
230
|
-
cursor: pointer;
|
|
231
|
-
opacity: 1;
|
|
232
|
-
pointer-events: all;
|
|
233
|
-
}
|
|
234
|
-
}`
|
|
242
|
+
}`
|
|
235
243
|
|
|
236
244
|
return <style nonce={editor.options.nonce}>{CSS}</style>
|
|
237
245
|
})
|