sdk: wires up tldraw to have licensing mechanisms (#4021)

For non-commercial usage of tldraw, this adds a watermark in the corner,
both for branding purposes and as an incentive for our enterprise
customers to purchase a license.

For commercial usage of tldraw, you add a license to the `<Tldraw
licenseKey={YOUR_LICENSE_KEY} />` component so that the watermark
doesn't show.

The license is a signed key that has various bits of information in it,
such as:
- license type
- hosts that the license is valid for
- whether it's an internal-only license
- expiry date

We check the license on load and show a watermark (or throw an error if
internal-only) if the license is not valid in a production environment.

This is a @MitjaBezensek, @Taha-Hassan-Git, @mimecuvalo joint
production! 🤜 🤛

### Change Type

<!--  Please select a 'Scope' label ️ -->

- [x] `sdk` — Changes the tldraw SDK
- [ ] `dotcom` — Changes the tldraw.com web app
- [ ] `docs` — Changes to the documentation, examples, or templates.
- [ ] `vs code` — Changes to the vscode plugin
- [ ] `internal` — Does not affect user-facing stuff

<!--  Please select a 'Type' label ️ -->

- [ ] `bugfix` — Bug fix
- [x] `feature` — New feature
- [ ] `improvement` — Improving existing features
- [ ] `chore` — Updating dependencies, other boring stuff
- [ ] `galaxy brain` — Architectural changes
- [ ] `tests` — Changes to any test code
- [ ] `tools` — Changes to infrastructure, CI, internal scripts,
debugging tools, etc.
- [ ] `dunno` — I don't know


### Test Plan

1. We will be dogfooding on staging.tldraw.com and tldraw.com itself
before releasing this.

### Release Notes

- SDK: wires up tldraw to have licensing mechanisms.

---------

Co-authored-by: Mitja Bezenšek <mitja.bezensek@gmail.com>
Co-authored-by: Taha <98838967+Taha-Hassan-Git@users.noreply.github.com>
Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
This commit is contained in:
Mime Čuvalo 2024-07-11 12:49:18 +01:00 committed by GitHub
parent a7fac3bcc4
commit 69a1c17b46
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
50 changed files with 1297 additions and 118 deletions

View file

@ -69,5 +69,6 @@ jobs:
SENTRY_CSP_REPORT_URI: ${{ secrets.SENTRY_CSP_REPORT_URI }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
SUPABASE_LITE_ANON_KEY: ${{ secrets.SUPABASE_LITE_ANON_KEY }}
TLDRAW_LICENSE: ${{ secrets.TLDRAW_LICENSE }}
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
WORKER_SENTRY_DSN: ${{ secrets.WORKER_SENTRY_DSN }}

View file

@ -1,9 +1,8 @@
import { getLicenseKey } from '@tldraw/dotcom-shared'
import { useCallback } from 'react'
import {
DefaultDebugMenu,
DefaultDebugMenuContent,
DefaultHelpMenu,
DefaultHelpMenuContent,
DefaultKeyboardShortcutsDialog,
DefaultKeyboardShortcutsDialogContent,
DefaultMainMenu,
@ -11,6 +10,7 @@ import {
Editor,
ExportFileContentSubMenu,
ExtrasGroup,
HelpGroup,
PreferencesGroup,
TLComponents,
Tldraw,
@ -37,14 +37,6 @@ const components: TLComponents = {
ErrorFallback: ({ error }) => {
throw error
},
HelpMenu: () => (
<DefaultHelpMenu>
<TldrawUiMenuGroup id="help">
<DefaultHelpMenuContent />
</TldrawUiMenuGroup>
<Links />
</DefaultHelpMenu>
),
MainMenu: () => (
<DefaultMainMenu>
<LocalFileMenu />
@ -53,6 +45,7 @@ const components: TLComponents = {
<ExportFileContentSubMenu />
<ExtrasGroup />
<PreferencesGroup />
<HelpGroup />
<Links />
</DefaultMainMenu>
),
@ -99,6 +92,7 @@ export function LocalEditor() {
return (
<div className="tldraw__editor">
<Tldraw
licenseKey={getLicenseKey()}
assetUrls={assetUrls}
persistenceKey={SCRATCH_PERSISTENCE_KEY}
onMount={handleMount}

View file

@ -1,16 +1,21 @@
import { ROOM_OPEN_MODE, RoomOpenModeToPath, type RoomOpenMode } from '@tldraw/dotcom-shared'
import {
getLicenseKey,
ROOM_OPEN_MODE,
RoomOpenModeToPath,
type RoomOpenMode,
} from '@tldraw/dotcom-shared'
import { useMultiplayerSync } from '@tldraw/sync'
import { useCallback } from 'react'
import {
DefaultHelpMenu,
DefaultHelpMenuContent,
assertExists,
DefaultKeyboardShortcutsDialog,
DefaultKeyboardShortcutsDialogContent,
DefaultMainMenu,
EditSubmenu,
Editor,
EditSubmenu,
ExportFileContentSubMenu,
ExtrasGroup,
HelpGroup,
PeopleMenu,
PreferencesGroup,
TLComponents,
@ -20,12 +25,11 @@ import {
TldrawUiButtonLabel,
TldrawUiMenuGroup,
TldrawUiMenuItem,
ViewSubmenu,
assertExists,
useActions,
useEditor,
useTranslation,
useValue,
ViewSubmenu,
} from 'tldraw'
import { UrlStateParams, useUrlState } from '../hooks/useUrlState'
import { assetUrls } from '../utils/assetUrls'
@ -47,14 +51,6 @@ const components: TLComponents = {
ErrorFallback: ({ error }) => {
throw error
},
HelpMenu: () => (
<DefaultHelpMenu>
<TldrawUiMenuGroup id="help">
<DefaultHelpMenuContent />
</TldrawUiMenuGroup>
<Links />
</DefaultHelpMenu>
),
MainMenu: () => (
<DefaultMainMenu>
<MultiplayerFileMenu />
@ -63,6 +59,7 @@ const components: TLComponents = {
<ExportFileContentSubMenu />
<ExtrasGroup />
<PreferencesGroup />
<HelpGroup />
<Links />
</DefaultMainMenu>
),
@ -157,6 +154,7 @@ export function MultiplayerEditor({
return (
<div className="tldraw__editor">
<Tldraw
licenseKey={getLicenseKey()}
store={storeWithStatus}
assetUrls={assetUrls}
onMount={handleMount}

View file

@ -1,7 +1,6 @@
import { getLicenseKey } from '@tldraw/dotcom-shared'
import { useMemo } from 'react'
import {
DefaultHelpMenu,
DefaultHelpMenuContent,
DefaultKeyboardShortcutsDialog,
DefaultKeyboardShortcutsDialogContent,
DefaultMainMenu,
@ -22,20 +21,11 @@ import { SAVE_FILE_COPY_ACTION, useFileSystem } from '../utils/useFileSystem'
import { useHandleUiEvents } from '../utils/useHandleUiEvent'
import { ExportMenu } from './ExportMenu'
import { MultiplayerFileMenu } from './FileMenu'
import { Links } from './Links'
const components: TLComponents = {
ErrorFallback: ({ error }) => {
throw error
},
HelpMenu: () => (
<DefaultHelpMenu>
<TldrawUiMenuGroup id="help">
<DefaultHelpMenuContent />
</TldrawUiMenuGroup>
<Links />
</DefaultHelpMenu>
),
MainMenu: () => (
<DefaultMainMenu>
<MultiplayerFileMenu />
@ -83,6 +73,7 @@ export function SnapshotsEditor({ schema, records }: SnapshotEditorProps) {
return (
<div className="tldraw__editor">
<Tldraw
licenseKey={getLicenseKey()}
assetUrls={assetUrls}
snapshot={snaphot}
overrides={[sharingUiOverrides, fileSystemUiOverrides]}

View file

@ -2,3 +2,8 @@
// Do not edit manually. Or do, I'm a comment, not a cop.
export const version = '2.3.0'
export const publishDates = {
major: '2024-06-28T10:56:07.893Z',
minor: '2024-07-02T16:49:50.397Z',
patch: '2024-07-02T16:49:50.397Z',
}

View file

@ -48,6 +48,7 @@ export default defineConfig((env) => ({
'process.env.ASSET_UPLOAD': urlOrLocalFallback(env.mode, process.env.ASSET_UPLOAD, 8788),
'process.env.IMAGE_WORKER': urlOrLocalFallback(env.mode, process.env.IMAGE_WORKER, 8786),
'process.env.TLDRAW_ENV': JSON.stringify(process.env.TLDRAW_ENV ?? 'development'),
'process.env.TLDRAW_LICENSE': JSON.stringify(process.env.TLDRAW_LICENSE ?? ''),
// Fall back to staging DSN for local develeopment, although you still need to
// modify the env check in 'sentry.client.config.ts' to get it reporting errors
'process.env.SENTRY_DSN': JSON.stringify(

View file

@ -1,41 +0,0 @@
import { expect } from '@playwright/test'
import { setup } from '../shared-e2e'
import test from './fixtures/fixtures'
test.describe('help menu', () => {
test.beforeEach(setup)
test('you can open and close the menus', async ({ helpMenu, isMobile }) => {
// No help menu on mobile
const { helpMenuButton, languagesButton, keyboardShortcutsMenu, languagesContent } = helpMenu
test.skip(isMobile, 'only run on desktop')
await test.step('open help menu', async () => {
await expect(languagesButton).toBeHidden()
await expect(keyboardShortcutsMenu.button).toBeHidden()
await helpMenuButton.click()
await expect(languagesButton).toBeVisible()
await expect(keyboardShortcutsMenu.button).toBeVisible()
})
await test.step('hover languages submenu', async () => {
await expect(languagesContent).toBeHidden()
await languagesButton.hover()
await expect(languagesContent).toBeVisible()
})
await test.step('open the keyboard shortcuts menu', async () => {
await expect(keyboardShortcutsMenu.heading).toBeHidden()
await keyboardShortcutsMenu.button.click()
await expect(keyboardShortcutsMenu.heading).toBeVisible()
})
await test.step('close the keyboard shortcuts menu', async () => {
await keyboardShortcutsMenu.closeButton.click()
await expect(keyboardShortcutsMenu.heading).toBeHidden()
await expect(languagesButton).toBeHidden()
})
})
// ...
// More tests here
// ...
})

View file

@ -36,6 +36,7 @@
"@playwright/test": "^1.38.1",
"@radix-ui/react-alert-dialog": "^1.0.5",
"@tldraw/assets": "workspace:*",
"@tldraw/dotcom-shared": "workspace:*",
"@vercel/analytics": "^1.1.1",
"classnames": "^2.3.2",
"lazyrepo": "0.0.0-alpha.27",

View file

@ -1,3 +1,4 @@
import { getLicenseKey } from '@tldraw/dotcom-shared'
import { Tldraw } from 'tldraw'
import 'tldraw/tldraw.css'
import { usePerformance } from '../hooks/usePerformance'
@ -7,6 +8,7 @@ export default function Develop() {
return (
<div className="tldraw__editor">
<Tldraw
licenseKey={getLicenseKey()}
overrides={[performanceOverrides]}
persistenceKey="example"
onMount={(editor) => {

View file

@ -1,3 +1,4 @@
import { getLicenseKey } from '@tldraw/dotcom-shared'
import { useEffect } from 'react'
import { Tldraw, exportAs, useActions, useEditor } from 'tldraw'
import 'tldraw/tldraw.css'
@ -9,6 +10,7 @@ export default function EndToEnd() {
return (
<div className="tldraw__editor">
<Tldraw
licenseKey={getLicenseKey()}
onMount={(editor) => {
;(window as any).app = editor
;(window as any).editor = editor

View file

@ -12,6 +12,9 @@
},
{
"path": "../../packages/tldraw"
},
{
"path": "../../packages/dotcom-shared"
}
]
}

View file

@ -245,6 +245,7 @@
"menu.file": "File",
"menu.language": "Language",
"menu.preferences": "Preferences",
"menu.help": "Help",
"menu.view": "View",
"context-menu.edit": "Edit",
"context-menu.arrange": "Arrange",
@ -295,6 +296,7 @@
"people-menu.invite": "Invite others",
"help-menu.title": "Help and resources",
"help-menu.about": "About",
"help-menu.docs": "Documentation & API",
"help-menu.discord": "Discord",
"help-menu.github": "GitHub",
"help-menu.keyboard-shortcuts": "Keyboard shortcuts",

View file

@ -0,0 +1,18 @@
<svg width="107" height="32" viewBox="0 0 107 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M92.0365 18.249C91.3697 18.249 90.7885 17.7953 90.6266 17.1484L88.3831 8.18532C88.1535 7.26789 88.8474 6.37897 89.7931 6.37897H90.9965C91.6955 6.37897 92.2955 6.87657 92.4248 7.56352L93.1706 11.5252C93.2584 11.9911 93.9218 12.002 94.0248 11.5392L94.9197 7.51677C95.0676 6.85193 95.6574 6.37897 96.3385 6.37897H98.1662C98.8418 6.37897 99.4284 6.84455 99.5817 7.50256L100.513 11.499C100.62 11.96 101.282 11.9436 101.367 11.4778L102.074 7.57321C102.2 6.88174 102.802 6.37897 103.505 6.37897H104.7C105.645 6.37897 106.339 7.26789 106.11 8.18532L103.866 17.1484C103.704 17.7953 103.123 18.249 102.456 18.249H100.086C99.432 18.249 98.8588 17.8128 98.6846 17.1829L97.6448 13.4227C97.5264 12.9945 96.9182 12.997 96.8033 13.4263L95.8016 17.1711C95.6315 17.8068 95.0556 18.249 94.3975 18.249H92.0365Z" fill="black"/>
<path d="M79.3673 18.4346C78.609 18.4346 77.9385 18.3109 77.3556 18.0636C76.778 17.8112 76.324 17.4299 75.9939 16.9199C75.6638 16.4099 75.4988 15.7607 75.4988 14.9725C75.4988 14.3233 75.6097 13.7695 75.8315 13.311C76.0533 12.8473 76.3627 12.4687 76.7599 12.175C77.1571 11.8813 77.6187 11.6572 78.1448 11.5027C78.6761 11.3481 79.2486 11.2477 79.8624 11.2013C80.5278 11.1498 81.0617 11.0879 81.464 11.0158C81.8715 10.9385 82.1655 10.8329 82.346 10.699C82.5265 10.5599 82.6168 10.377 82.6168 10.1503V10.1194C82.6168 9.81027 82.4982 9.57328 82.2609 9.40842C82.0236 9.24356 81.7193 9.16113 81.3479 9.16113C80.9405 9.16113 80.6078 9.25129 80.3499 9.43161C79.3704 10.1104 79.4422 10.243 77.9282 10.243C76.1274 10.243 76.6747 7.6058 78.4543 6.78095C79.2435 6.41001 80.2286 6.22455 81.4098 6.22455C82.2609 6.22455 83.0243 6.32501 83.7 6.52593C84.3757 6.7217 84.9508 6.99733 85.4253 7.35281C85.8999 7.70314 86.2609 8.11529 86.5085 8.58927C86.7612 9.05809 86.8876 9.56813 86.8876 10.1194V16.7956C86.8876 17.5984 86.2369 18.2491 85.4342 18.2491H83.6917C83.2519 18.2491 82.8953 17.8925 82.8953 17.4527C82.8953 17.2249 82.5367 17.116 82.3783 17.2796C81.9383 17.7341 81.3647 18.0831 80.7831 18.2491C80.355 18.3727 79.8831 18.4346 79.3673 18.4346ZM80.7599 15.7453C81.0849 15.7453 81.3892 15.6783 81.6729 15.5443C81.9617 15.4104 82.1964 15.2172 82.377 14.9648C82.6213 14.6231 82.6648 14.2001 82.6639 13.7771C82.6631 13.4328 82.2964 13.1946 81.9638 13.2835C81.6292 13.373 81.2873 13.437 80.9456 13.4887C80.6155 13.5403 80.3447 13.6253 80.1332 13.7438C79.9269 13.8571 79.7722 13.9988 79.669 14.1688C79.571 14.3336 79.522 14.5191 79.522 14.7252C79.522 15.0549 79.6381 15.3074 79.8702 15.4825C80.1023 15.6577 80.3989 15.7453 80.7599 15.7453Z" fill="black"/>
<path d="M68.6156 18.2491C67.8129 18.2491 67.1622 17.5984 67.1622 16.7956V7.83255C67.1622 7.02983 67.8129 6.3791 68.6156 6.3791H70.5938C70.9889 6.3791 71.3092 6.69942 71.3092 7.09455C71.3092 7.32551 71.8521 7.44928 71.9983 7.27052C72.1463 7.08967 72.304 6.9342 72.462 6.80414C72.9366 6.41774 73.491 6.22455 74.1255 6.22455C75.4297 6.22455 75.6045 7.52553 75.6045 8.17838C75.6045 9.66905 75.1303 9.81027 73.6303 9.81027C73.2125 9.81027 72.836 9.90558 72.5007 10.0962C72.1706 10.2817 71.9101 10.5444 71.7193 10.8844C71.5284 11.2193 71.433 11.6134 71.433 12.0668V16.7956C71.433 17.5984 70.7823 18.2491 69.9796 18.2491H68.6156Z" fill="black"/>
<path d="M57.4213 18.4036C56.5754 18.4036 55.7966 18.1821 55.0848 17.739C54.3781 17.296 53.8107 16.6236 53.3826 15.7221C52.9597 14.8205 52.7482 13.6845 52.7482 12.3141C52.7482 10.8818 52.97 9.71751 53.4136 8.82107C53.8623 7.92464 54.44 7.26777 55.1467 6.85047C55.8585 6.43317 56.6064 6.22451 57.3904 6.22451C58.23 6.22451 59.0588 6.50437 59.7119 7.00975C60.0092 7.23979 60.7947 7.00154 60.7947 6.62566V3.87585C60.7947 3.07314 61.4454 2.42241 62.2481 2.42241H63.612C64.4148 2.42241 65.0655 3.07314 65.0655 3.87585V16.7956C65.0655 17.5983 64.4148 18.2491 63.612 18.2491H61.6393C61.1899 18.2491 60.8256 17.8848 60.8256 17.4354C60.8256 17.1901 60.3421 17.0433 60.1747 17.2224C60.1234 17.2772 60.0719 17.331 60.021 17.3835C59.7167 17.6978 59.3453 17.9477 58.9068 18.1331C58.4736 18.3135 57.9784 18.4036 57.4213 18.4036ZM58.9997 15.127C59.3917 15.127 59.727 15.0137 60.0055 14.787C60.2892 14.5551 60.5058 14.2306 60.6554 13.8133C60.8101 13.3908 60.8875 12.8911 60.8875 12.3141C60.8875 11.7267 60.8101 11.2244 60.6554 10.8071C60.5058 10.3847 60.2892 10.0627 60.0055 9.84115C59.727 9.61447 59.3917 9.50113 58.9997 9.50113C58.6077 9.50113 58.2724 9.61447 57.9939 9.84115C57.7205 10.0627 57.509 10.3847 57.3594 10.8071C57.215 11.2244 57.1428 11.7267 57.1428 12.3141C57.1428 12.9014 57.215 13.4063 57.3594 13.8287C57.509 14.246 57.7205 14.568 57.9939 14.7947C58.2724 15.0162 58.6077 15.127 58.9997 15.127Z" fill="black"/>
<path d="M49.4901 2.42241C50.2928 2.42241 50.9435 3.07314 50.9435 3.87585V16.7956C50.9435 17.5983 50.2928 18.2491 49.4901 18.2491H48.1261C47.3234 18.2491 46.6727 17.5983 46.6727 16.7956V3.87585C46.6727 3.07314 47.3234 2.42241 48.1261 2.42241H49.4901Z" fill="black"/>
<path d="M38.3612 4.98874C38.3612 4.18602 39.012 3.53529 39.8147 3.53529H41.1786C41.9813 3.53529 42.6321 4.18602 42.6321 4.98874V5.84621C42.6321 6.14054 42.8707 6.37914 43.165 6.37914H43.5643C44.0995 6.37914 44.5333 6.81296 44.5333 7.34811V8.50132C44.5333 9.03647 44.0995 9.47029 43.5643 9.47029H43.165C42.8707 9.47029 42.6321 9.70889 42.6321 10.0032V14.4316C42.6321 14.5964 42.6604 14.7355 42.7172 14.8489C42.7739 14.9571 42.8616 15.0395 42.9802 15.0962C43.0989 15.1477 43.251 15.1734 43.4367 15.1734C43.8923 15.1734 44.3197 15.3384 44.4137 15.7842L44.7166 17.2201C44.8152 17.6879 44.5369 18.149 44.0711 18.2569C43.7204 18.3393 43.3026 18.3934 42.8177 18.4191C41.8377 18.4707 41.015 18.3702 40.3496 18.1178C39.6843 17.8602 39.1839 17.4557 38.8487 16.9045C38.5134 16.3532 38.3509 15.6629 38.3612 14.8334V10.0032C38.3612 9.70889 38.1175 9.47029 37.8231 9.47029C37.288 9.47029 36.8448 9.03647 36.8448 8.50132V7.34811C36.8448 6.81296 37.288 6.37914 37.8231 6.37914C38.1175 6.37914 38.3612 6.14054 38.3612 5.84621V4.98874Z" fill="black"/>
<path d="M52.2803 29.4602C51.8071 29.4602 51.4235 29.0778 51.4235 28.6062V23.3865C51.4235 22.9148 51.8071 22.5325 52.2803 22.5325H52.4535C52.9267 22.5325 53.3104 22.9148 53.3104 23.3865V25.2703C53.3104 25.3051 53.3387 25.3333 53.3736 25.3333C53.3936 25.3333 53.4124 25.3239 53.4244 25.3079L55.2389 22.8767C55.4006 22.6601 55.6555 22.5325 55.9264 22.5325H55.9693C56.6775 22.5325 57.08 23.3404 56.6518 23.9028L55.7033 25.1488C55.4885 25.431 55.4708 25.8161 55.6588 26.1168L56.9328 28.1544C57.2885 28.7233 56.8781 29.4602 56.2056 29.4602H55.9814C55.6802 29.4602 55.4011 29.3026 55.2464 29.045L54.3634 27.5755C54.1578 27.2333 53.6708 27.2055 53.4273 27.5221C53.3515 27.6207 53.3104 27.7415 53.3104 27.8658V28.6062C53.3104 29.0778 52.9267 29.4602 52.4535 29.4602H52.2803Z" fill="black"/>
<path d="M47.089 29.4602H45.2717C44.7985 29.4602 44.4148 29.0778 44.4148 28.6062V23.3865C44.4148 22.9148 44.7985 22.5325 45.2717 22.5325H47.0619C47.7768 22.5325 48.3945 22.6712 48.9148 22.9485C49.4374 23.2237 49.8402 23.6206 50.123 24.1392C50.408 24.6557 50.5506 25.2747 50.5506 25.9963C50.5506 26.718 50.4092 27.3381 50.1264 27.8568C49.8436 28.3732 49.4431 28.7701 48.925 29.0475C48.4069 29.3227 47.7949 29.4602 47.089 29.4602ZM46.3017 27.1465C46.3017 27.5425 46.6238 27.8636 47.0212 27.8636C47.3651 27.8636 47.658 27.8083 47.9001 27.6978C48.1445 27.5873 48.33 27.3968 48.4567 27.1262C48.5856 26.8555 48.6501 26.4789 48.6501 25.9963C48.6501 25.5137 48.5845 25.1371 48.4533 24.8665C48.3243 24.5959 48.1343 24.4053 47.8831 24.2948C47.6343 24.1843 47.3289 24.1291 46.9669 24.1291C46.5995 24.1291 46.3017 24.4259 46.3017 24.7921V27.1465Z" fill="black"/>
<path d="M42.4006 24.6972C42.0594 24.6972 41.8037 24.3876 41.542 24.1695C41.3927 24.0433 41.1653 23.9801 40.8599 23.9801C40.6653 23.9801 40.5058 24.0038 40.3814 24.0512C40.2592 24.0963 40.1687 24.1583 40.1099 24.2372C40.051 24.3161 40.0205 24.4063 40.0182 24.5078C40.0137 24.5913 40.0284 24.6668 40.0623 24.7345C40.0985 24.7999 40.1551 24.8596 40.232 24.9137C40.309 24.9656 40.4074 25.013 40.5273 25.0558C40.6472 25.0987 40.7897 25.137 40.9549 25.1708L41.525 25.2926C41.9096 25.3738 42.2388 25.4809 42.5126 25.614C42.7863 25.747 43.0103 25.9037 43.1845 26.0842C43.3587 26.2623 43.4865 26.463 43.568 26.6863C43.6517 26.9095 43.6947 27.1531 43.697 27.4169C43.6947 27.8725 43.5804 28.2581 43.3542 28.5738C43.1279 28.8895 42.8044 29.1297 42.3836 29.2943C41.9651 29.459 41.4617 29.5413 40.8734 29.5413C40.2694 29.5413 39.7422 29.4522 39.292 29.274C38.844 29.0959 38.4956 28.8219 38.2467 28.452C38.1332 28.2807 38.0456 28.0875 37.9838 27.8722C37.8534 27.4174 38.2591 27.0245 38.7336 27.0245H39.0993C39.4166 27.0245 39.6507 27.2945 39.8248 27.559C39.9198 27.7033 40.0533 27.8127 40.2252 27.8871C40.3995 27.9615 40.6065 27.9988 40.8463 27.9988C41.0476 27.9988 41.2162 27.9739 41.3519 27.9243C41.4877 27.8747 41.5906 27.8059 41.6608 27.718C41.7309 27.63 41.7671 27.5297 41.7694 27.4169C41.7671 27.3109 41.732 27.2185 41.6642 27.1396C41.5985 27.0584 41.4899 26.9862 41.3384 26.9231C41.1868 26.8577 40.982 26.7968 40.7241 26.7404L40.0318 26.5916C39.4164 26.4585 38.9311 26.2364 38.5759 25.9252C38.223 25.6117 38.0476 25.1844 38.0499 24.6431C38.0476 24.2034 38.1653 23.8189 38.4029 23.4896C38.6427 23.1581 38.9741 22.8999 39.3972 22.715C39.8225 22.5301 40.3101 22.4376 40.8599 22.4376C41.4209 22.4376 41.9062 22.5312 42.3157 22.7184C42.7252 22.9056 43.0408 23.1694 43.2626 23.5099C43.3354 23.62 43.3966 23.7361 43.446 23.8583C43.6234 24.2974 43.2157 24.6972 42.7408 24.6972H42.4006Z" fill="black"/>
<path d="M72.57 25.9833C72.57 26.7531 72.4198 27.4025 72.1195 27.9314C71.8191 28.4581 71.4138 28.8576 70.9035 29.13C70.3931 29.4001 69.8241 29.5351 69.1963 29.5351C68.564 29.5351 67.9927 29.399 67.4824 29.1266C66.9743 28.852 66.5701 28.4514 66.2698 27.9247C65.9717 27.3957 65.8227 26.7486 65.8227 25.9833C65.8227 25.2135 65.9717 24.5653 66.2698 24.0386C66.5701 23.5096 66.9743 23.1101 67.4824 22.84C67.9927 22.5677 68.564 22.4315 69.1963 22.4315C69.8241 22.4315 70.3931 22.5677 70.9035 22.84C71.4138 23.1101 71.8191 23.5096 72.1195 24.0386C72.4198 24.5653 72.57 25.2135 72.57 25.9833ZM70.6325 25.9833C70.6325 25.5692 70.5772 25.2203 70.4665 24.9367C70.3581 24.6508 70.1967 24.4347 69.9822 24.2884C69.7699 24.1399 69.5079 24.0656 69.1963 24.0656C68.8847 24.0656 68.6216 24.1399 68.4071 24.2884C68.1948 24.4347 68.0334 24.6508 67.9227 24.9367C67.8143 25.2203 67.7601 25.5692 67.7601 25.9833C67.7601 26.3975 67.8143 26.7475 67.9227 27.0333C68.0334 27.3169 68.1948 27.533 68.4071 27.6816C68.6216 27.8279 68.8847 27.901 69.1963 27.901C69.5079 27.901 69.7699 27.8279 69.9822 27.6816C70.1967 27.533 70.3581 27.3169 70.4665 27.0333C70.5772 26.7475 70.6325 26.3975 70.6325 25.9833Z" fill="black"/>
<path d="M61.1348 29.4405C60.6625 29.4405 60.2796 29.0588 60.2796 28.588V23.3783C60.2796 22.9075 60.6625 22.5259 61.1348 22.5259H64.3984C64.8174 22.5259 65.1571 22.8645 65.1571 23.2822C65.1571 23.6998 64.8174 24.0384 64.3984 24.0384H62.759C62.4298 24.0384 62.1629 24.3045 62.1629 24.6327C62.1629 24.9608 62.4298 25.2269 62.759 25.2269H64.1003C64.5194 25.2269 64.8591 25.5655 64.8591 25.9832C64.8591 26.4008 64.5194 26.7394 64.1003 26.7394H63.0181C62.5457 26.7394 62.1629 27.1211 62.1629 27.5919V28.588C62.1629 29.0588 61.78 29.4405 61.3077 29.4405H61.1348Z" fill="black"/>
<path d="M74.0859 29.535C73.7299 29.535 73.4413 29.2474 73.4413 28.8925V23.1684C73.4413 22.8135 73.7299 22.5259 74.0859 22.5259H76.4766C76.9985 22.5259 77.4551 22.6206 77.8465 22.8099C78.238 22.9993 78.5424 23.272 78.7599 23.6279C78.9773 23.9838 79.086 24.4105 79.086 24.9079C79.086 25.4098 78.9739 25.8331 78.7496 26.1776C78.5423 26.4992 78.2563 26.7472 77.8915 26.9217C77.8655 26.9341 77.839 26.9462 77.8122 26.9579C77.4116 27.1336 76.9435 27.2214 76.4079 27.2214H75.2396C74.8836 27.2214 74.595 26.9338 74.595 26.5789V24.9348C74.595 24.7269 74.7641 24.5583 74.9727 24.5583C75.1813 24.5583 75.3504 24.7269 75.3504 24.9348V25.1507C75.3504 25.4778 75.6164 25.743 75.9446 25.743H76.0233C76.2477 25.743 76.4388 25.7156 76.5967 25.6608C76.757 25.6038 76.8794 25.5137 76.9641 25.3904C77.0511 25.2672 77.0946 25.1064 77.0946 24.9079C77.0946 24.7071 77.0511 24.544 76.9641 24.4185C76.8794 24.2907 76.757 24.1972 76.5967 24.1378C76.4388 24.0762 76.2477 24.0454 76.0233 24.0454H75.995C75.639 24.0454 75.3504 24.3331 75.3504 24.6879V25.9759C75.3504 26.1416 75.4415 26.2941 75.5878 26.3729L75.8903 26.5361C76.2242 26.7162 76.0959 27.2214 75.7162 27.2214C75.5142 27.2214 75.3504 27.3847 75.3504 27.5861V28.8925C75.3504 29.2474 75.0618 29.535 74.7058 29.535H74.0859ZM77.1786 26.3179C77.4147 26.3179 77.6318 26.4465 77.7447 26.6531L77.8915 26.9217L78.8005 28.5852C79.0344 29.0133 78.7235 29.535 78.2344 29.535H77.6303C77.3933 29.535 77.1755 29.4055 77.063 29.1977L76.1123 27.4419C76.0388 27.3061 75.8964 27.2214 75.7416 27.2214C75.4204 27.2214 75.2174 26.8777 75.3732 26.5978L75.3995 26.5505C75.4794 26.407 75.6311 26.3179 75.7958 26.3179H77.1786Z" fill="black"/>
<path d="M99.879 29.4819C99.4003 29.4819 99.0123 29.0951 99.0123 28.6179V23.3379C99.0123 22.8608 99.4003 22.4739 99.879 22.4739H102.061C102.596 22.4739 103.046 22.5458 103.41 22.6895C103.776 22.8332 104.052 23.0363 104.237 23.2986C104.425 23.561 104.519 23.8701 104.519 24.2259C104.519 24.4837 104.461 24.7187 104.347 24.9308C104.235 25.143 104.077 25.3209 103.873 25.4646C103.759 25.5439 103.634 25.6324 103.498 25.7046C103.452 25.7286 103.469 25.8663 103.519 25.8775C103.683 25.9145 103.838 25.9736 103.983 26.0566C104.23 26.1958 104.427 26.3885 104.574 26.6349C104.72 26.879 104.793 27.1664 104.793 27.4972C104.793 27.8805 104.693 28.2215 104.491 28.5203C104.292 28.8192 104.008 29.0541 103.64 29.2252C103.271 29.3963 102.832 29.4819 102.322 29.4819H99.879ZM100.921 27.2235C100.921 27.6317 101.253 27.9626 101.662 27.9626H101.814C102.134 27.9626 102.374 27.9033 102.534 27.7846C102.695 27.6637 102.775 27.4858 102.775 27.2508C102.775 27.0866 102.737 26.9474 102.661 26.8334C102.586 26.7193 102.478 26.6326 102.339 26.5733C102.201 26.514 102.036 26.4843 101.841 26.4843H101.662C101.253 26.4843 100.921 26.8153 100.921 27.2235ZM100.921 24.6366C100.921 25.007 101.222 25.3072 101.594 25.3072H101.704C101.871 25.3072 102.018 25.281 102.147 25.2285C102.275 25.1761 102.374 25.1008 102.445 25.0027C102.518 24.9023 102.555 24.7803 102.555 24.6366C102.555 24.4198 102.477 24.2544 102.322 24.1404C102.166 24.024 101.969 23.9659 101.731 23.9659H101.594C101.222 23.9659 100.921 24.2661 100.921 24.6366Z" fill="black"/>
<path d="M93.8655 29.4819C93.3868 29.4819 92.9987 29.0951 92.9987 28.6179V23.3379C92.9987 22.8608 93.3868 22.4739 93.8655 22.4739H97.2967C97.7214 22.4739 98.0657 22.8171 98.0657 23.2404C98.0657 23.6638 97.7214 24.0069 97.2967 24.0069H95.5116C95.1779 24.0069 94.9074 24.2766 94.9074 24.6092C94.9074 24.9418 95.1779 25.2114 95.5116 25.2114H97.0358C97.4605 25.2114 97.8048 25.5546 97.8048 25.9779C97.8048 26.4012 97.4605 26.7444 97.0358 26.7444H95.5116C95.1779 26.7444 94.9074 27.014 94.9074 27.3466C94.9074 27.6793 95.1779 27.9489 95.5116 27.9489H97.283C97.7077 27.9489 98.052 28.2921 98.052 28.7154C98.052 29.1387 97.7077 29.4819 97.283 29.4819H93.8655Z" fill="black"/>
<path d="M84.64 29.5776C84.3543 29.5776 84.1027 29.3901 84.0218 29.1169L82.3285 23.3938C82.2067 22.9822 82.5162 22.5697 82.9466 22.5697H83.6988C83.9996 22.5697 84.2604 22.7771 84.3272 23.0695L84.991 25.9768C85.047 26.2222 85.3955 26.2284 85.4602 25.9851L86.2418 23.0474C86.3168 22.7657 86.5725 22.5697 86.8648 22.5697H87.5472C87.84 22.5697 88.0961 22.7665 88.1705 23.0489L88.9368 25.9568C89.0008 26.1997 89.3484 26.1951 89.4058 25.9505L90.0836 23.0656C90.1518 22.7751 90.4118 22.5697 90.7111 22.5697H91.4665C91.897 22.5697 92.2064 22.9822 92.0847 23.3938L90.3913 29.1169C90.3105 29.3901 90.0589 29.5776 89.7732 29.5776H88.9024C88.6206 29.5776 88.3714 29.3951 88.2873 29.1269L87.4176 26.3552C87.3465 26.1286 87.0241 26.1304 86.9555 26.3578L86.1224 29.1201C86.0405 29.3917 85.7897 29.5776 85.5052 29.5776H84.64Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 4.16C0 1.8625 1.8625 0 4.16 0H27.84C30.1375 0 32 1.8625 32 4.16V27.84C32 30.1375 30.1375 32 27.84 32H4.16C1.8625 32 0 30.1375 0 27.84V4.16ZM18.88 9.6C18.88 11.1906 17.5906 12.48 16 12.48C14.4094 12.48 13.12 11.1906 13.12 9.6C13.12 8.00942 14.4094 6.72 16 6.72C17.5906 6.72 18.88 8.00942 18.88 9.6ZM15.054 25.92C16.4086 25.9248 17.7129 24.0442 18.2008 22.994C18.8018 21.7002 19.1769 19.3745 18.574 17.9967C18.1517 17.0316 17.1842 16.32 15.9062 16.32C14.3674 16.32 13.12 17.5711 13.12 19.1145C13.12 20.4895 14.1346 21.5803 15.4222 21.7904C15.4882 21.8012 15.5358 21.8608 15.5263 21.927C15.4119 22.7208 15.0621 23.7613 14.4967 24.2926C13.8174 24.9311 14.0299 25.9163 15.054 25.92Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -0,0 +1,9 @@
<svg width="141" height="32" viewBox="0 0 141 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 4.16C0 1.8625 1.8625 0 4.16 0H27.84C30.1375 0 32 1.8625 32 4.16V27.84C32 30.1375 30.1375 32 27.84 32H4.16C1.8625 32 0 30.1375 0 27.84V4.16ZM18.88 9.6C18.88 11.1906 17.5906 12.48 16 12.48C14.4094 12.48 13.12 11.1906 13.12 9.6C13.12 8.00942 14.4094 6.72 16 6.72C17.5906 6.72 18.88 8.00942 18.88 9.6ZM15.054 25.92C16.4086 25.9248 17.7129 24.0442 18.2008 22.994C18.8018 21.7002 19.1769 19.3745 18.574 17.9967C18.1517 17.0316 17.1842 16.32 15.9062 16.32C14.3674 16.32 13.12 17.5711 13.12 19.1145C13.12 20.4758 14.1145 21.5586 15.3839 21.7839C15.4708 21.7993 15.5339 21.8783 15.5206 21.9656C15.4006 22.7542 15.0529 23.7698 14.4967 24.2926C13.8174 24.9311 14.0299 25.9163 15.054 25.92Z" fill="black"/>
<path d="M119.715 27.5561C118.834 27.5561 118.066 26.9567 117.853 26.1024L114.426 12.3939C114.123 11.1826 115.039 10.0092 116.288 10.0092H118.543C119.467 10.0092 120.259 10.6665 120.43 11.5737L121.627 17.9426C121.743 18.558 122.619 18.5724 122.755 17.9611L124.188 11.5121C124.383 10.634 125.162 10.0092 126.062 10.0092H129.122C130.014 10.0092 130.788 10.6242 130.991 11.4933L132.482 17.8983C132.623 18.5072 133.498 18.4855 133.609 17.8703L134.746 11.5865C134.912 10.6733 135.707 10.0092 136.635 10.0092H138.88C140.128 10.0092 141.045 11.1826 140.742 12.3939L137.315 26.1024C137.101 26.9567 136.333 27.5561 135.453 27.5561H131.604C130.74 27.5561 129.983 26.9798 129.754 26.1478L128.105 20.1805C127.949 19.6147 127.146 19.6181 126.994 20.1851L125.405 26.1323C125.181 26.972 124.42 27.5561 123.551 27.5561H119.715Z" fill="black"/>
<path d="M101.185 27.8302C100.065 27.8302 99.0753 27.6474 98.2147 27.2818C97.3618 26.9086 96.6916 26.3451 96.2042 25.5911C95.7167 24.8371 95.473 23.8775 95.473 22.7123C95.473 21.7527 95.6368 20.934 95.9643 20.2562C96.2917 19.5708 96.7487 19.011 97.3351 18.5769C97.9215 18.1428 98.6032 17.8115 99.38 17.583C100.164 17.3546 101.01 17.2061 101.916 17.1375C102.898 17.0614 103.687 16.97 104.281 16.8633C104.882 16.7491 105.317 16.593 105.583 16.395C105.85 16.1893 105.983 15.919 105.983 15.5839V15.5382C105.983 15.0812 105.808 14.7309 105.457 14.4872C105.107 14.2435 104.658 14.1216 104.109 14.1216C103.508 14.1216 103.017 14.2549 102.636 14.5215C101.19 15.525 101.296 15.721 99.0601 15.721C96.4012 15.721 97.2093 11.8225 99.8369 10.6031C101.002 10.0548 102.457 9.78062 104.201 9.78062C105.457 9.78062 106.585 9.92913 107.582 10.2261C108.58 10.5155 109.429 10.923 110.13 11.4485C110.83 11.9664 111.363 12.5756 111.729 13.2763C112.102 13.9693 112.289 14.7233 112.289 15.5382V25.6368C112.289 26.6967 111.43 27.556 110.37 27.556H107.571C106.921 27.556 106.394 27.0292 106.394 26.3794C106.394 26.0428 105.864 25.8818 105.63 26.1237C104.98 26.7952 104.134 27.3107 103.275 27.556C102.643 27.7388 101.947 27.8302 101.185 27.8302ZM103.241 23.8547C103.721 23.8547 104.17 23.7557 104.589 23.5577C105.016 23.3597 105.362 23.0741 105.629 22.7009C106.011 22.1653 106.061 21.4946 106.051 20.8318C106.045 20.3771 105.565 20.0634 105.127 20.1859C104.599 20.3339 104.057 20.4369 103.515 20.519C103.028 20.5951 102.628 20.7208 102.316 20.8959C102.011 21.0635 101.783 21.2729 101.63 21.5242C101.486 21.7679 101.413 22.0421 101.413 22.3468C101.413 22.8342 101.585 23.2073 101.927 23.4663C102.27 23.7252 102.708 23.8547 103.241 23.8547Z" fill="black"/>
<path d="M85.0832 27.556C84.0232 27.556 83.164 26.6967 83.164 25.6368V11.9283C83.164 10.8683 84.0232 10.0091 85.0832 10.0091H88.2303C88.814 10.0091 89.2871 10.4822 89.2871 11.0659C89.2871 11.4071 90.0895 11.5899 90.3055 11.3258C90.5237 11.0589 90.7563 10.8294 90.9892 10.6374C91.6899 10.0662 92.5086 9.78062 93.4454 9.78062C95.3711 9.78062 95.6292 11.7038 95.6292 12.6689C95.6292 14.8725 94.929 15.0812 92.7142 15.0812C92.0974 15.0812 91.5414 15.2221 91.0464 15.5039C90.5589 15.7781 90.1743 16.1665 89.8926 16.6691C89.6108 17.1642 89.4699 17.7468 89.4699 18.417V25.6368C89.4699 26.6967 88.6106 27.556 87.5507 27.556H85.0832Z" fill="black"/>
<path d="M68.7814 27.7843C67.5324 27.7843 66.3824 27.4568 65.3314 26.8019C64.2881 26.1469 63.4503 25.1531 62.8182 23.8203C62.1937 22.4875 61.8815 20.8082 61.8815 18.7824C61.8815 16.6652 62.2089 14.944 62.8639 13.6189C63.5265 12.2937 64.3794 11.3227 65.4228 10.7058C66.4738 10.0889 67.5781 9.78049 68.7357 9.78049C70.0716 9.78049 71.3891 10.2609 72.3832 11.1212C72.7586 11.4461 73.7622 11.1552 73.7622 10.6587V6.0792C73.7622 5.01926 74.6214 4.16 75.6814 4.16H78.1489C79.2088 4.16 80.0681 5.01926 80.0681 6.0792V25.6367C80.0681 26.6966 79.2088 27.5559 78.1489 27.5559H75.0099C74.346 27.5559 73.8078 27.0177 73.8078 26.3538C73.8078 25.9914 73.0931 25.7744 72.8458 26.0393C72.7704 26.1199 72.6947 26.199 72.6198 26.2764C72.1704 26.741 71.6221 27.1103 70.9748 27.3845C70.335 27.6511 69.6039 27.7843 68.7814 27.7843ZM71.1118 22.9407C71.6907 22.9407 72.1857 22.7731 72.5969 22.438C73.0158 22.0953 73.3357 21.6155 73.5565 20.9986C73.785 20.3741 73.8992 19.6354 73.8992 18.7824C73.8992 17.9142 73.785 17.1717 73.5565 16.5548C73.3357 15.9303 73.0158 15.4543 72.5969 15.1268C72.1857 14.7917 71.6907 14.6242 71.1118 14.6242C70.533 14.6242 70.038 14.7917 69.6268 15.1268C69.2231 15.4543 68.9109 15.9303 68.69 16.5548C68.4768 17.1717 68.3701 17.9142 68.3701 18.7824C68.3701 19.6506 68.4768 20.397 68.69 21.0215C68.9109 21.6383 69.2231 22.1143 69.6268 22.4494C70.038 22.7769 70.533 22.9407 71.1118 22.9407Z" fill="black"/>
<path d="M57.2977 4.16C58.3576 4.16 59.2169 5.01926 59.2169 6.0792V25.6366C59.2169 26.6966 58.3576 27.5559 57.2977 27.5559H54.8302C53.7702 27.5559 52.911 26.6966 52.911 25.6367V6.0792C52.911 5.01926 53.7702 4.16 54.8302 4.16H57.2977Z" fill="black"/>
<path d="M40.639 7.7243C40.639 6.66435 41.4983 5.8051 42.5582 5.8051H45.0257C46.0857 5.8051 46.9449 6.66435 46.9449 7.7243V9.30533C46.9449 9.69398 47.26 10.009 47.6486 10.009H48.4726C49.1793 10.009 49.7521 10.5819 49.7521 11.2885V13.2991C49.7521 14.0057 49.1793 14.5785 48.4726 14.5785H47.6486C47.26 14.5785 46.9449 14.8936 46.9449 15.2822V21.9126C46.9449 22.1563 46.9868 22.3619 47.0706 22.5295C47.1544 22.6894 47.2838 22.8113 47.459 22.895C47.6342 22.9712 47.8588 23.0093 48.133 23.0093C48.8056 23.0093 49.4367 23.2526 49.5753 23.9108L50.023 26.036C50.1686 26.7269 49.7576 27.4079 49.0697 27.5673C48.5519 27.6892 47.935 27.7692 47.2191 27.8072C45.7721 27.8834 44.5574 27.7349 43.5749 27.3617C42.5925 26.9809 41.8537 26.3831 41.3587 25.5682C40.8637 24.7533 40.6238 23.7328 40.639 22.5066V15.2822C40.639 14.8936 40.324 14.5785 39.9353 14.5785H39.6795C38.9728 14.5785 38.4 14.0057 38.4 13.2991V11.2885C38.4 10.5819 38.9728 10.009 39.6795 10.009H39.9353C40.324 10.009 40.639 9.69398 40.639 9.30533V7.7243Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 6.6 KiB

View file

@ -0,0 +1,6 @@
<svg width="21" height="32" viewBox="0 0 21 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 2.64799C0 1.18555 1.18555 0 2.648 0H17.7212C19.1836 0 20.3692 1.18555 20.3692 2.64799V17.7212C20.3692 19.1836 19.1836 20.3692 17.7212 20.3692H2.648C1.18555 20.3692 0 19.1836 0 17.7212V2.64799ZM12.0178 6.11076C12.0178 7.12322 11.1971 7.94398 10.1846 7.94398C9.17213 7.94398 8.35137 7.12322 8.35137 6.11076C8.35137 5.09829 9.17213 4.27753 10.1846 4.27753C11.1971 4.27753 12.0178 5.09829 12.0178 6.11076ZM9.58242 16.499C10.4447 16.5021 11.2749 15.3051 11.5855 14.6365C11.968 13.813 12.2068 12.3326 11.8231 11.4556C11.5542 10.8412 10.9384 10.3883 10.1249 10.3883C9.14541 10.3883 8.35137 11.1847 8.35137 12.1671C8.35137 13.0336 8.98443 13.7228 9.7924 13.8662C9.84773 13.8761 9.88788 13.9264 9.87943 13.9819C9.80306 14.4839 9.58175 15.1304 9.22771 15.4631C8.7953 15.8696 8.93053 16.4967 9.58242 16.499Z" fill="black"/>
<path d="M15.247 31.9137C14.7452 31.9137 14.3384 31.5069 14.3384 31.0051V25.4524C14.3384 24.9506 14.7452 24.5438 15.247 24.5438H15.4307C15.9325 24.5438 16.3392 24.9506 16.3392 25.4524V27.4564C16.3392 27.4934 16.3693 27.5234 16.4063 27.5234C16.4275 27.5234 16.4475 27.5134 16.4601 27.4964L18.3842 24.9101C18.5557 24.6796 18.826 24.5438 19.1132 24.5438H19.1587C19.9097 24.5438 20.3364 25.4033 19.8825 26.0016L18.8767 27.3271C18.6489 27.6274 18.6301 28.0371 18.8294 28.3569L20.1804 30.5246C20.5576 31.1297 20.1224 31.9137 19.4093 31.9137H19.1716C18.8522 31.9137 18.5563 31.746 18.3921 31.472L17.4558 29.9087C17.2378 29.5447 16.7214 29.5151 16.4633 29.8519C16.3828 29.9568 16.3392 30.0853 16.3392 30.2175V31.0051C16.3392 31.5069 15.9325 31.9137 15.4307 31.9137H15.247Z" fill="black"/>
<path d="M9.74217 31.9137H7.81507C7.31329 31.9137 6.90651 31.5069 6.90651 31.0051V25.4524C6.90651 24.9506 7.31329 24.5438 7.81508 24.5438H9.71338C10.4715 24.5438 11.1264 24.6914 11.6782 24.9865C12.2324 25.2791 12.6594 25.7014 12.9593 26.2532C13.2616 26.8025 13.4127 27.4611 13.4127 28.2288C13.4127 28.9965 13.2628 29.6562 12.9629 30.208C12.663 30.7573 12.2384 31.1796 11.689 31.4747C11.1396 31.7673 10.4907 31.9137 9.74217 31.9137ZM8.90731 29.4523C8.90731 29.8736 9.24887 30.2152 9.6702 30.2152C10.0349 30.2152 10.3455 30.1564 10.6022 30.0388C10.8613 29.9213 11.058 29.7186 11.1924 29.4307C11.3291 29.1428 11.3975 28.7422 11.3975 28.2288C11.3975 27.7154 11.3279 27.3147 11.1888 27.0268C11.052 26.739 10.8505 26.5362 10.5842 26.4187C10.3203 26.3011 9.99647 26.2424 9.61262 26.2424C9.22309 26.2424 8.90731 26.5581 8.90731 26.9477V29.4523Z" fill="black"/>
<path d="M4.77075 26.8469C4.409 26.8469 4.13786 26.5175 3.8603 26.2855C3.70196 26.1511 3.46086 26.084 3.13699 26.084C2.93067 26.084 2.76154 26.1092 2.62959 26.1595C2.50004 26.2075 2.40408 26.2735 2.3417 26.3575C2.27933 26.4414 2.24694 26.5374 2.24454 26.6453C2.23975 26.7341 2.25534 26.8145 2.29132 26.8864C2.32971 26.956 2.38969 27.0196 2.47125 27.0772C2.55282 27.1323 2.65718 27.1827 2.78433 27.2283C2.91148 27.2739 3.06262 27.3147 3.23774 27.3507L3.8423 27.4802C4.25014 27.5666 4.5992 27.6805 4.88948 27.8221C5.17977 27.9636 5.41727 28.1304 5.602 28.3223C5.78672 28.5118 5.92227 28.7253 6.00863 28.9628C6.0974 29.2003 6.14298 29.4594 6.14538 29.7401C6.14298 30.2247 6.02183 30.6349 5.78192 30.9708C5.54202 31.3067 5.19896 31.5622 4.75274 31.7373C4.30892 31.9124 3.77513 32 3.15138 32C2.51084 32 1.95186 31.9052 1.47445 31.7157C0.999443 31.5262 0.629991 31.2347 0.366097 30.8413C0.245715 30.659 0.152788 30.4534 0.0873166 30.2244C-0.0509949 29.7406 0.379195 29.3227 0.882363 29.3227H1.27008C1.6066 29.3227 1.85479 29.6099 2.03943 29.8912C2.14019 30.0448 2.28173 30.1611 2.46406 30.2403C2.64878 30.3195 2.86829 30.3591 3.12259 30.3591C3.33611 30.3591 3.51483 30.3327 3.65878 30.2799C3.80272 30.2271 3.91188 30.1539 3.98625 30.0604C4.06062 29.9668 4.099 29.8601 4.1014 29.7401C4.099 29.6274 4.06181 29.529 3.98984 29.445C3.92027 29.3587 3.80512 29.2819 3.64438 29.2147C3.48365 29.1451 3.26653 29.0804 2.99304 29.0204L2.25894 28.8621C1.6064 28.7205 1.09181 28.4842 0.715157 28.1531C0.340907 27.8197 0.154981 27.3651 0.15738 26.7893C0.154981 26.3215 0.279731 25.9124 0.53163 25.5622C0.785928 25.2095 1.13739 24.9348 1.58601 24.7381C2.03703 24.5414 2.55402 24.443 3.13699 24.443C3.73195 24.443 4.24654 24.5426 4.68077 24.7417C5.11499 24.9408 5.44966 25.2215 5.68476 25.5838C5.76203 25.7008 5.82686 25.8244 5.87924 25.9544C6.06742 26.4215 5.63508 26.8469 5.13152 26.8469H4.77075Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

View file

@ -1,3 +1,5 @@
const svgTransformPath = require.resolve('config/svgTransform.js')
module.exports = {
roots: ['<rootDir>/src'],
transform: {
@ -20,6 +22,7 @@ module.exports = {
},
},
],
'^.+\\.svg$': svgTransformPath,
},
testRegex: '.+\\.(test|spec)\\.(jsx?|tsx?)$',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],

View file

@ -1,4 +1,5 @@
import { equals, getObjectSubset, iterableEquality, subsetEquality } from '@jest/expect-utils'
import crypto from 'crypto'
import {
matcherHint,
printDiffOrStringify,
@ -10,6 +11,8 @@ import { TextDecoder, TextEncoder } from 'util'
global.TextEncoder = TextEncoder
global.TextDecoder = TextDecoder
delete global.crypto
global.crypto = crypto
Image.prototype.decode = async function () {
return true

10
config/svgTransform.js Normal file
View file

@ -0,0 +1,10 @@
module.exports = {
process() {
return {
code: `module.exports = {};`,
}
},
getCacheKey() {
return 'svgTransform'
},
}

View file

@ -1,7 +1,5 @@
{
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
"packages": [
"packages/*"
],
"packages": ["packages/*"],
"version": "2.3.0"
}

View file

@ -1,3 +1,4 @@
export { default as getLicenseKey } from './license'
export {
READ_ONLY_LEGACY_PREFIX,
READ_ONLY_PREFIX,

View file

@ -0,0 +1,4 @@
const getLicenseKey = () =>
process.env.TLDRAW_LICENSE ??
'tldraw-tldraw-2025-07-10/WyJiTEZhTGFLRSIsWyIqLnRsZHJhdy5jb20iXSwxLCIyMDI1LTA3LTEwIl0.frNy824rh1/JzMGLzLuW9JFKrah66N23/+xY0Z+5XoyKQWO7UpIDdKgDN5+N//yHE5Fh1DThM7ykoGzul/RjrA'
export default getLicenseKey

View file

@ -782,7 +782,7 @@ export class EdgeScrollManager {
// @public (undocumented)
export class Editor extends EventEmitter<TLEventMap> {
constructor({ store, user, shapeUtils, bindingUtils, tools, getContainer, cameraOptions, initialState, autoFocus, inferDarkMode, options, }: TLEditorOptions);
constructor({ store, user, shapeUtils, bindingUtils, tools, getContainer, cameraOptions, initialState, autoFocus, inferDarkMode, options, licenseKey, }: TLEditorOptions);
addOpenMenu(id: string): this;
alignShapes(shapes: TLShape[] | TLShapeId[], operation: 'bottom' | 'center-horizontal' | 'center-vertical' | 'left' | 'right' | 'top'): this;
animateShape(partial: null | TLShapePartial | undefined, opts?: Partial<{
@ -1390,6 +1390,9 @@ export function getArcMeasure(A: number, B: number, sweepFlag: number, largeArcF
// @public (undocumented)
export function getCursor(cursor: TLCursorType, rotation?: number, color?: string): string;
// @public (undocumented)
export function getDefaultCdnBaseUrl(): string;
// @public (undocumented)
export function getFreshUserPreferences(): TLUserPreferences;
@ -2506,6 +2509,7 @@ export interface TldrawEditorBaseProps {
components?: TLEditorComponents;
inferDarkMode?: boolean;
initialState?: string;
licenseKey?: string;
onMount?: TLOnMountHandler;
options?: Partial<TldrawOptions>;
shapeUtils?: readonly TLAnyShapeUtilConstructor[];
@ -2668,6 +2672,8 @@ export interface TLEditorOptions {
inferDarkMode?: boolean;
initialState?: string;
// (undocumented)
licenseKey?: string;
// (undocumented)
options?: Partial<TldrawOptions>;
shapeUtils: readonly TLShapeUtilConstructor<TLUnknownShape>[];
store: TLStore;

View file

@ -0,0 +1,18 @@
<svg width="107" height="32" viewBox="0 0 107 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M92.0365 18.249C91.3697 18.249 90.7885 17.7953 90.6266 17.1484L88.3831 8.18532C88.1535 7.26789 88.8474 6.37897 89.7931 6.37897H90.9965C91.6955 6.37897 92.2955 6.87657 92.4248 7.56352L93.1706 11.5252C93.2584 11.9911 93.9218 12.002 94.0248 11.5392L94.9197 7.51677C95.0676 6.85193 95.6574 6.37897 96.3385 6.37897H98.1662C98.8418 6.37897 99.4284 6.84455 99.5817 7.50256L100.513 11.499C100.62 11.96 101.282 11.9436 101.367 11.4778L102.074 7.57321C102.2 6.88174 102.802 6.37897 103.505 6.37897H104.7C105.645 6.37897 106.339 7.26789 106.11 8.18532L103.866 17.1484C103.704 17.7953 103.123 18.249 102.456 18.249H100.086C99.432 18.249 98.8588 17.8128 98.6846 17.1829L97.6448 13.4227C97.5264 12.9945 96.9182 12.997 96.8033 13.4263L95.8016 17.1711C95.6315 17.8068 95.0556 18.249 94.3975 18.249H92.0365Z" fill="black"/>
<path d="M79.3673 18.4346C78.609 18.4346 77.9385 18.3109 77.3556 18.0636C76.778 17.8112 76.324 17.4299 75.9939 16.9199C75.6638 16.4099 75.4988 15.7607 75.4988 14.9725C75.4988 14.3233 75.6097 13.7695 75.8315 13.311C76.0533 12.8473 76.3627 12.4687 76.7599 12.175C77.1571 11.8813 77.6187 11.6572 78.1448 11.5027C78.6761 11.3481 79.2486 11.2477 79.8624 11.2013C80.5278 11.1498 81.0617 11.0879 81.464 11.0158C81.8715 10.9385 82.1655 10.8329 82.346 10.699C82.5265 10.5599 82.6168 10.377 82.6168 10.1503V10.1194C82.6168 9.81027 82.4982 9.57328 82.2609 9.40842C82.0236 9.24356 81.7193 9.16113 81.3479 9.16113C80.9405 9.16113 80.6078 9.25129 80.3499 9.43161C79.3704 10.1104 79.4422 10.243 77.9282 10.243C76.1274 10.243 76.6747 7.6058 78.4543 6.78095C79.2435 6.41001 80.2286 6.22455 81.4098 6.22455C82.2609 6.22455 83.0243 6.32501 83.7 6.52593C84.3757 6.7217 84.9508 6.99733 85.4253 7.35281C85.8999 7.70314 86.2609 8.11529 86.5085 8.58927C86.7612 9.05809 86.8876 9.56813 86.8876 10.1194V16.7956C86.8876 17.5984 86.2369 18.2491 85.4342 18.2491H83.6917C83.2519 18.2491 82.8953 17.8925 82.8953 17.4527C82.8953 17.2249 82.5367 17.116 82.3783 17.2796C81.9383 17.7341 81.3647 18.0831 80.7831 18.2491C80.355 18.3727 79.8831 18.4346 79.3673 18.4346ZM80.7599 15.7453C81.0849 15.7453 81.3892 15.6783 81.6729 15.5443C81.9617 15.4104 82.1964 15.2172 82.377 14.9648C82.6213 14.6231 82.6648 14.2001 82.6639 13.7771C82.6631 13.4328 82.2964 13.1946 81.9638 13.2835C81.6292 13.373 81.2873 13.437 80.9456 13.4887C80.6155 13.5403 80.3447 13.6253 80.1332 13.7438C79.9269 13.8571 79.7722 13.9988 79.669 14.1688C79.571 14.3336 79.522 14.5191 79.522 14.7252C79.522 15.0549 79.6381 15.3074 79.8702 15.4825C80.1023 15.6577 80.3989 15.7453 80.7599 15.7453Z" fill="black"/>
<path d="M68.6156 18.2491C67.8129 18.2491 67.1622 17.5984 67.1622 16.7956V7.83255C67.1622 7.02983 67.8129 6.3791 68.6156 6.3791H70.5938C70.9889 6.3791 71.3092 6.69942 71.3092 7.09455C71.3092 7.32551 71.8521 7.44928 71.9983 7.27052C72.1463 7.08967 72.304 6.9342 72.462 6.80414C72.9366 6.41774 73.491 6.22455 74.1255 6.22455C75.4297 6.22455 75.6045 7.52553 75.6045 8.17838C75.6045 9.66905 75.1303 9.81027 73.6303 9.81027C73.2125 9.81027 72.836 9.90558 72.5007 10.0962C72.1706 10.2817 71.9101 10.5444 71.7193 10.8844C71.5284 11.2193 71.433 11.6134 71.433 12.0668V16.7956C71.433 17.5984 70.7823 18.2491 69.9796 18.2491H68.6156Z" fill="black"/>
<path d="M57.4213 18.4036C56.5754 18.4036 55.7966 18.1821 55.0848 17.739C54.3781 17.296 53.8107 16.6236 53.3826 15.7221C52.9597 14.8205 52.7482 13.6845 52.7482 12.3141C52.7482 10.8818 52.97 9.71751 53.4136 8.82107C53.8623 7.92464 54.44 7.26777 55.1467 6.85047C55.8585 6.43317 56.6064 6.22451 57.3904 6.22451C58.23 6.22451 59.0588 6.50437 59.7119 7.00975C60.0092 7.23979 60.7947 7.00154 60.7947 6.62566V3.87585C60.7947 3.07314 61.4454 2.42241 62.2481 2.42241H63.612C64.4148 2.42241 65.0655 3.07314 65.0655 3.87585V16.7956C65.0655 17.5983 64.4148 18.2491 63.612 18.2491H61.6393C61.1899 18.2491 60.8256 17.8848 60.8256 17.4354C60.8256 17.1901 60.3421 17.0433 60.1747 17.2224C60.1234 17.2772 60.0719 17.331 60.021 17.3835C59.7167 17.6978 59.3453 17.9477 58.9068 18.1331C58.4736 18.3135 57.9784 18.4036 57.4213 18.4036ZM58.9997 15.127C59.3917 15.127 59.727 15.0137 60.0055 14.787C60.2892 14.5551 60.5058 14.2306 60.6554 13.8133C60.8101 13.3908 60.8875 12.8911 60.8875 12.3141C60.8875 11.7267 60.8101 11.2244 60.6554 10.8071C60.5058 10.3847 60.2892 10.0627 60.0055 9.84115C59.727 9.61447 59.3917 9.50113 58.9997 9.50113C58.6077 9.50113 58.2724 9.61447 57.9939 9.84115C57.7205 10.0627 57.509 10.3847 57.3594 10.8071C57.215 11.2244 57.1428 11.7267 57.1428 12.3141C57.1428 12.9014 57.215 13.4063 57.3594 13.8287C57.509 14.246 57.7205 14.568 57.9939 14.7947C58.2724 15.0162 58.6077 15.127 58.9997 15.127Z" fill="black"/>
<path d="M49.4901 2.42241C50.2928 2.42241 50.9435 3.07314 50.9435 3.87585V16.7956C50.9435 17.5983 50.2928 18.2491 49.4901 18.2491H48.1261C47.3234 18.2491 46.6727 17.5983 46.6727 16.7956V3.87585C46.6727 3.07314 47.3234 2.42241 48.1261 2.42241H49.4901Z" fill="black"/>
<path d="M38.3612 4.98874C38.3612 4.18602 39.012 3.53529 39.8147 3.53529H41.1786C41.9813 3.53529 42.6321 4.18602 42.6321 4.98874V5.84621C42.6321 6.14054 42.8707 6.37914 43.165 6.37914H43.5643C44.0995 6.37914 44.5333 6.81296 44.5333 7.34811V8.50132C44.5333 9.03647 44.0995 9.47029 43.5643 9.47029H43.165C42.8707 9.47029 42.6321 9.70889 42.6321 10.0032V14.4316C42.6321 14.5964 42.6604 14.7355 42.7172 14.8489C42.7739 14.9571 42.8616 15.0395 42.9802 15.0962C43.0989 15.1477 43.251 15.1734 43.4367 15.1734C43.8923 15.1734 44.3197 15.3384 44.4137 15.7842L44.7166 17.2201C44.8152 17.6879 44.5369 18.149 44.0711 18.2569C43.7204 18.3393 43.3026 18.3934 42.8177 18.4191C41.8377 18.4707 41.015 18.3702 40.3496 18.1178C39.6843 17.8602 39.1839 17.4557 38.8487 16.9045C38.5134 16.3532 38.3509 15.6629 38.3612 14.8334V10.0032C38.3612 9.70889 38.1175 9.47029 37.8231 9.47029C37.288 9.47029 36.8448 9.03647 36.8448 8.50132V7.34811C36.8448 6.81296 37.288 6.37914 37.8231 6.37914C38.1175 6.37914 38.3612 6.14054 38.3612 5.84621V4.98874Z" fill="black"/>
<path d="M52.2803 29.4602C51.8071 29.4602 51.4235 29.0778 51.4235 28.6062V23.3865C51.4235 22.9148 51.8071 22.5325 52.2803 22.5325H52.4535C52.9267 22.5325 53.3104 22.9148 53.3104 23.3865V25.2703C53.3104 25.3051 53.3387 25.3333 53.3736 25.3333C53.3936 25.3333 53.4124 25.3239 53.4244 25.3079L55.2389 22.8767C55.4006 22.6601 55.6555 22.5325 55.9264 22.5325H55.9693C56.6775 22.5325 57.08 23.3404 56.6518 23.9028L55.7033 25.1488C55.4885 25.431 55.4708 25.8161 55.6588 26.1168L56.9328 28.1544C57.2885 28.7233 56.8781 29.4602 56.2056 29.4602H55.9814C55.6802 29.4602 55.4011 29.3026 55.2464 29.045L54.3634 27.5755C54.1578 27.2333 53.6708 27.2055 53.4273 27.5221C53.3515 27.6207 53.3104 27.7415 53.3104 27.8658V28.6062C53.3104 29.0778 52.9267 29.4602 52.4535 29.4602H52.2803Z" fill="black"/>
<path d="M47.089 29.4602H45.2717C44.7985 29.4602 44.4148 29.0778 44.4148 28.6062V23.3865C44.4148 22.9148 44.7985 22.5325 45.2717 22.5325H47.0619C47.7768 22.5325 48.3945 22.6712 48.9148 22.9485C49.4374 23.2237 49.8402 23.6206 50.123 24.1392C50.408 24.6557 50.5506 25.2747 50.5506 25.9963C50.5506 26.718 50.4092 27.3381 50.1264 27.8568C49.8436 28.3732 49.4431 28.7701 48.925 29.0475C48.4069 29.3227 47.7949 29.4602 47.089 29.4602ZM46.3017 27.1465C46.3017 27.5425 46.6238 27.8636 47.0212 27.8636C47.3651 27.8636 47.658 27.8083 47.9001 27.6978C48.1445 27.5873 48.33 27.3968 48.4567 27.1262C48.5856 26.8555 48.6501 26.4789 48.6501 25.9963C48.6501 25.5137 48.5845 25.1371 48.4533 24.8665C48.3243 24.5959 48.1343 24.4053 47.8831 24.2948C47.6343 24.1843 47.3289 24.1291 46.9669 24.1291C46.5995 24.1291 46.3017 24.4259 46.3017 24.7921V27.1465Z" fill="black"/>
<path d="M42.4006 24.6972C42.0594 24.6972 41.8037 24.3876 41.542 24.1695C41.3927 24.0433 41.1653 23.9801 40.8599 23.9801C40.6653 23.9801 40.5058 24.0038 40.3814 24.0512C40.2592 24.0963 40.1687 24.1583 40.1099 24.2372C40.051 24.3161 40.0205 24.4063 40.0182 24.5078C40.0137 24.5913 40.0284 24.6668 40.0623 24.7345C40.0985 24.7999 40.1551 24.8596 40.232 24.9137C40.309 24.9656 40.4074 25.013 40.5273 25.0558C40.6472 25.0987 40.7897 25.137 40.9549 25.1708L41.525 25.2926C41.9096 25.3738 42.2388 25.4809 42.5126 25.614C42.7863 25.747 43.0103 25.9037 43.1845 26.0842C43.3587 26.2623 43.4865 26.463 43.568 26.6863C43.6517 26.9095 43.6947 27.1531 43.697 27.4169C43.6947 27.8725 43.5804 28.2581 43.3542 28.5738C43.1279 28.8895 42.8044 29.1297 42.3836 29.2943C41.9651 29.459 41.4617 29.5413 40.8734 29.5413C40.2694 29.5413 39.7422 29.4522 39.292 29.274C38.844 29.0959 38.4956 28.8219 38.2467 28.452C38.1332 28.2807 38.0456 28.0875 37.9838 27.8722C37.8534 27.4174 38.2591 27.0245 38.7336 27.0245H39.0993C39.4166 27.0245 39.6507 27.2945 39.8248 27.559C39.9198 27.7033 40.0533 27.8127 40.2252 27.8871C40.3995 27.9615 40.6065 27.9988 40.8463 27.9988C41.0476 27.9988 41.2162 27.9739 41.3519 27.9243C41.4877 27.8747 41.5906 27.8059 41.6608 27.718C41.7309 27.63 41.7671 27.5297 41.7694 27.4169C41.7671 27.3109 41.732 27.2185 41.6642 27.1396C41.5985 27.0584 41.4899 26.9862 41.3384 26.9231C41.1868 26.8577 40.982 26.7968 40.7241 26.7404L40.0318 26.5916C39.4164 26.4585 38.9311 26.2364 38.5759 25.9252C38.223 25.6117 38.0476 25.1844 38.0499 24.6431C38.0476 24.2034 38.1653 23.8189 38.4029 23.4896C38.6427 23.1581 38.9741 22.8999 39.3972 22.715C39.8225 22.5301 40.3101 22.4376 40.8599 22.4376C41.4209 22.4376 41.9062 22.5312 42.3157 22.7184C42.7252 22.9056 43.0408 23.1694 43.2626 23.5099C43.3354 23.62 43.3966 23.7361 43.446 23.8583C43.6234 24.2974 43.2157 24.6972 42.7408 24.6972H42.4006Z" fill="black"/>
<path d="M72.57 25.9833C72.57 26.7531 72.4198 27.4025 72.1195 27.9314C71.8191 28.4581 71.4138 28.8576 70.9035 29.13C70.3931 29.4001 69.8241 29.5351 69.1963 29.5351C68.564 29.5351 67.9927 29.399 67.4824 29.1266C66.9743 28.852 66.5701 28.4514 66.2698 27.9247C65.9717 27.3957 65.8227 26.7486 65.8227 25.9833C65.8227 25.2135 65.9717 24.5653 66.2698 24.0386C66.5701 23.5096 66.9743 23.1101 67.4824 22.84C67.9927 22.5677 68.564 22.4315 69.1963 22.4315C69.8241 22.4315 70.3931 22.5677 70.9035 22.84C71.4138 23.1101 71.8191 23.5096 72.1195 24.0386C72.4198 24.5653 72.57 25.2135 72.57 25.9833ZM70.6325 25.9833C70.6325 25.5692 70.5772 25.2203 70.4665 24.9367C70.3581 24.6508 70.1967 24.4347 69.9822 24.2884C69.7699 24.1399 69.5079 24.0656 69.1963 24.0656C68.8847 24.0656 68.6216 24.1399 68.4071 24.2884C68.1948 24.4347 68.0334 24.6508 67.9227 24.9367C67.8143 25.2203 67.7601 25.5692 67.7601 25.9833C67.7601 26.3975 67.8143 26.7475 67.9227 27.0333C68.0334 27.3169 68.1948 27.533 68.4071 27.6816C68.6216 27.8279 68.8847 27.901 69.1963 27.901C69.5079 27.901 69.7699 27.8279 69.9822 27.6816C70.1967 27.533 70.3581 27.3169 70.4665 27.0333C70.5772 26.7475 70.6325 26.3975 70.6325 25.9833Z" fill="black"/>
<path d="M61.1348 29.4405C60.6625 29.4405 60.2796 29.0588 60.2796 28.588V23.3783C60.2796 22.9075 60.6625 22.5259 61.1348 22.5259H64.3984C64.8174 22.5259 65.1571 22.8645 65.1571 23.2822C65.1571 23.6998 64.8174 24.0384 64.3984 24.0384H62.759C62.4298 24.0384 62.1629 24.3045 62.1629 24.6327C62.1629 24.9608 62.4298 25.2269 62.759 25.2269H64.1003C64.5194 25.2269 64.8591 25.5655 64.8591 25.9832C64.8591 26.4008 64.5194 26.7394 64.1003 26.7394H63.0181C62.5457 26.7394 62.1629 27.1211 62.1629 27.5919V28.588C62.1629 29.0588 61.78 29.4405 61.3077 29.4405H61.1348Z" fill="black"/>
<path d="M74.0859 29.535C73.7299 29.535 73.4413 29.2474 73.4413 28.8925V23.1684C73.4413 22.8135 73.7299 22.5259 74.0859 22.5259H76.4766C76.9985 22.5259 77.4551 22.6206 77.8465 22.8099C78.238 22.9993 78.5424 23.272 78.7599 23.6279C78.9773 23.9838 79.086 24.4105 79.086 24.9079C79.086 25.4098 78.9739 25.8331 78.7496 26.1776C78.5423 26.4992 78.2563 26.7472 77.8915 26.9217C77.8655 26.9341 77.839 26.9462 77.8122 26.9579C77.4116 27.1336 76.9435 27.2214 76.4079 27.2214H75.2396C74.8836 27.2214 74.595 26.9338 74.595 26.5789V24.9348C74.595 24.7269 74.7641 24.5583 74.9727 24.5583C75.1813 24.5583 75.3504 24.7269 75.3504 24.9348V25.1507C75.3504 25.4778 75.6164 25.743 75.9446 25.743H76.0233C76.2477 25.743 76.4388 25.7156 76.5967 25.6608C76.757 25.6038 76.8794 25.5137 76.9641 25.3904C77.0511 25.2672 77.0946 25.1064 77.0946 24.9079C77.0946 24.7071 77.0511 24.544 76.9641 24.4185C76.8794 24.2907 76.757 24.1972 76.5967 24.1378C76.4388 24.0762 76.2477 24.0454 76.0233 24.0454H75.995C75.639 24.0454 75.3504 24.3331 75.3504 24.6879V25.9759C75.3504 26.1416 75.4415 26.2941 75.5878 26.3729L75.8903 26.5361C76.2242 26.7162 76.0959 27.2214 75.7162 27.2214C75.5142 27.2214 75.3504 27.3847 75.3504 27.5861V28.8925C75.3504 29.2474 75.0618 29.535 74.7058 29.535H74.0859ZM77.1786 26.3179C77.4147 26.3179 77.6318 26.4465 77.7447 26.6531L77.8915 26.9217L78.8005 28.5852C79.0344 29.0133 78.7235 29.535 78.2344 29.535H77.6303C77.3933 29.535 77.1755 29.4055 77.063 29.1977L76.1123 27.4419C76.0388 27.3061 75.8964 27.2214 75.7416 27.2214C75.4204 27.2214 75.2174 26.8777 75.3732 26.5978L75.3995 26.5505C75.4794 26.407 75.6311 26.3179 75.7958 26.3179H77.1786Z" fill="black"/>
<path d="M99.879 29.4819C99.4003 29.4819 99.0123 29.0951 99.0123 28.6179V23.3379C99.0123 22.8608 99.4003 22.4739 99.879 22.4739H102.061C102.596 22.4739 103.046 22.5458 103.41 22.6895C103.776 22.8332 104.052 23.0363 104.237 23.2986C104.425 23.561 104.519 23.8701 104.519 24.2259C104.519 24.4837 104.461 24.7187 104.347 24.9308C104.235 25.143 104.077 25.3209 103.873 25.4646C103.759 25.5439 103.634 25.6324 103.498 25.7046C103.452 25.7286 103.469 25.8663 103.519 25.8775C103.683 25.9145 103.838 25.9736 103.983 26.0566C104.23 26.1958 104.427 26.3885 104.574 26.6349C104.72 26.879 104.793 27.1664 104.793 27.4972C104.793 27.8805 104.693 28.2215 104.491 28.5203C104.292 28.8192 104.008 29.0541 103.64 29.2252C103.271 29.3963 102.832 29.4819 102.322 29.4819H99.879ZM100.921 27.2235C100.921 27.6317 101.253 27.9626 101.662 27.9626H101.814C102.134 27.9626 102.374 27.9033 102.534 27.7846C102.695 27.6637 102.775 27.4858 102.775 27.2508C102.775 27.0866 102.737 26.9474 102.661 26.8334C102.586 26.7193 102.478 26.6326 102.339 26.5733C102.201 26.514 102.036 26.4843 101.841 26.4843H101.662C101.253 26.4843 100.921 26.8153 100.921 27.2235ZM100.921 24.6366C100.921 25.007 101.222 25.3072 101.594 25.3072H101.704C101.871 25.3072 102.018 25.281 102.147 25.2285C102.275 25.1761 102.374 25.1008 102.445 25.0027C102.518 24.9023 102.555 24.7803 102.555 24.6366C102.555 24.4198 102.477 24.2544 102.322 24.1404C102.166 24.024 101.969 23.9659 101.731 23.9659H101.594C101.222 23.9659 100.921 24.2661 100.921 24.6366Z" fill="black"/>
<path d="M93.8655 29.4819C93.3868 29.4819 92.9987 29.0951 92.9987 28.6179V23.3379C92.9987 22.8608 93.3868 22.4739 93.8655 22.4739H97.2967C97.7214 22.4739 98.0657 22.8171 98.0657 23.2404C98.0657 23.6638 97.7214 24.0069 97.2967 24.0069H95.5116C95.1779 24.0069 94.9074 24.2766 94.9074 24.6092C94.9074 24.9418 95.1779 25.2114 95.5116 25.2114H97.0358C97.4605 25.2114 97.8048 25.5546 97.8048 25.9779C97.8048 26.4012 97.4605 26.7444 97.0358 26.7444H95.5116C95.1779 26.7444 94.9074 27.014 94.9074 27.3466C94.9074 27.6793 95.1779 27.9489 95.5116 27.9489H97.283C97.7077 27.9489 98.052 28.2921 98.052 28.7154C98.052 29.1387 97.7077 29.4819 97.283 29.4819H93.8655Z" fill="black"/>
<path d="M84.64 29.5776C84.3543 29.5776 84.1027 29.3901 84.0218 29.1169L82.3285 23.3938C82.2067 22.9822 82.5162 22.5697 82.9466 22.5697H83.6988C83.9996 22.5697 84.2604 22.7771 84.3272 23.0695L84.991 25.9768C85.047 26.2222 85.3955 26.2284 85.4602 25.9851L86.2418 23.0474C86.3168 22.7657 86.5725 22.5697 86.8648 22.5697H87.5472C87.84 22.5697 88.0961 22.7665 88.1705 23.0489L88.9368 25.9568C89.0008 26.1997 89.3484 26.1951 89.4058 25.9505L90.0836 23.0656C90.1518 22.7751 90.4118 22.5697 90.7111 22.5697H91.4665C91.897 22.5697 92.2064 22.9822 92.0847 23.3938L90.3913 29.1169C90.3105 29.3901 90.0589 29.5776 89.7732 29.5776H88.9024C88.6206 29.5776 88.3714 29.3951 88.2873 29.1269L87.4176 26.3552C87.3465 26.1286 87.0241 26.1304 86.9555 26.3578L86.1224 29.1201C86.0405 29.3917 85.7897 29.5776 85.5052 29.5776H84.64Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 4.16C0 1.8625 1.8625 0 4.16 0H27.84C30.1375 0 32 1.8625 32 4.16V27.84C32 30.1375 30.1375 32 27.84 32H4.16C1.8625 32 0 30.1375 0 27.84V4.16ZM18.88 9.6C18.88 11.1906 17.5906 12.48 16 12.48C14.4094 12.48 13.12 11.1906 13.12 9.6C13.12 8.00942 14.4094 6.72 16 6.72C17.5906 6.72 18.88 8.00942 18.88 9.6ZM15.054 25.92C16.4086 25.9248 17.7129 24.0442 18.2008 22.994C18.8018 21.7002 19.1769 19.3745 18.574 17.9967C18.1517 17.0316 17.1842 16.32 15.9062 16.32C14.3674 16.32 13.12 17.5711 13.12 19.1145C13.12 20.4895 14.1346 21.5803 15.4222 21.7904C15.4882 21.8012 15.5358 21.8608 15.5263 21.927C15.4119 22.7208 15.0621 23.7613 14.4967 24.2926C13.8174 24.9311 14.0299 25.9163 15.054 25.92Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -0,0 +1,9 @@
<svg width="141" height="32" viewBox="0 0 141 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 4.16C0 1.8625 1.8625 0 4.16 0H27.84C30.1375 0 32 1.8625 32 4.16V27.84C32 30.1375 30.1375 32 27.84 32H4.16C1.8625 32 0 30.1375 0 27.84V4.16ZM18.88 9.6C18.88 11.1906 17.5906 12.48 16 12.48C14.4094 12.48 13.12 11.1906 13.12 9.6C13.12 8.00942 14.4094 6.72 16 6.72C17.5906 6.72 18.88 8.00942 18.88 9.6ZM15.054 25.92C16.4086 25.9248 17.7129 24.0442 18.2008 22.994C18.8018 21.7002 19.1769 19.3745 18.574 17.9967C18.1517 17.0316 17.1842 16.32 15.9062 16.32C14.3674 16.32 13.12 17.5711 13.12 19.1145C13.12 20.4758 14.1145 21.5586 15.3839 21.7839C15.4708 21.7993 15.5339 21.8783 15.5206 21.9656C15.4006 22.7542 15.0529 23.7698 14.4967 24.2926C13.8174 24.9311 14.0299 25.9163 15.054 25.92Z" fill="black"/>
<path d="M119.715 27.5561C118.834 27.5561 118.066 26.9567 117.853 26.1024L114.426 12.3939C114.123 11.1826 115.039 10.0092 116.288 10.0092H118.543C119.467 10.0092 120.259 10.6665 120.43 11.5737L121.627 17.9426C121.743 18.558 122.619 18.5724 122.755 17.9611L124.188 11.5121C124.383 10.634 125.162 10.0092 126.062 10.0092H129.122C130.014 10.0092 130.788 10.6242 130.991 11.4933L132.482 17.8983C132.623 18.5072 133.498 18.4855 133.609 17.8703L134.746 11.5865C134.912 10.6733 135.707 10.0092 136.635 10.0092H138.88C140.128 10.0092 141.045 11.1826 140.742 12.3939L137.315 26.1024C137.101 26.9567 136.333 27.5561 135.453 27.5561H131.604C130.74 27.5561 129.983 26.9798 129.754 26.1478L128.105 20.1805C127.949 19.6147 127.146 19.6181 126.994 20.1851L125.405 26.1323C125.181 26.972 124.42 27.5561 123.551 27.5561H119.715Z" fill="black"/>
<path d="M101.185 27.8302C100.065 27.8302 99.0753 27.6474 98.2147 27.2818C97.3618 26.9086 96.6916 26.3451 96.2042 25.5911C95.7167 24.8371 95.473 23.8775 95.473 22.7123C95.473 21.7527 95.6368 20.934 95.9643 20.2562C96.2917 19.5708 96.7487 19.011 97.3351 18.5769C97.9215 18.1428 98.6032 17.8115 99.38 17.583C100.164 17.3546 101.01 17.2061 101.916 17.1375C102.898 17.0614 103.687 16.97 104.281 16.8633C104.882 16.7491 105.317 16.593 105.583 16.395C105.85 16.1893 105.983 15.919 105.983 15.5839V15.5382C105.983 15.0812 105.808 14.7309 105.457 14.4872C105.107 14.2435 104.658 14.1216 104.109 14.1216C103.508 14.1216 103.017 14.2549 102.636 14.5215C101.19 15.525 101.296 15.721 99.0601 15.721C96.4012 15.721 97.2093 11.8225 99.8369 10.6031C101.002 10.0548 102.457 9.78062 104.201 9.78062C105.457 9.78062 106.585 9.92913 107.582 10.2261C108.58 10.5155 109.429 10.923 110.13 11.4485C110.83 11.9664 111.363 12.5756 111.729 13.2763C112.102 13.9693 112.289 14.7233 112.289 15.5382V25.6368C112.289 26.6967 111.43 27.556 110.37 27.556H107.571C106.921 27.556 106.394 27.0292 106.394 26.3794C106.394 26.0428 105.864 25.8818 105.63 26.1237C104.98 26.7952 104.134 27.3107 103.275 27.556C102.643 27.7388 101.947 27.8302 101.185 27.8302ZM103.241 23.8547C103.721 23.8547 104.17 23.7557 104.589 23.5577C105.016 23.3597 105.362 23.0741 105.629 22.7009C106.011 22.1653 106.061 21.4946 106.051 20.8318C106.045 20.3771 105.565 20.0634 105.127 20.1859C104.599 20.3339 104.057 20.4369 103.515 20.519C103.028 20.5951 102.628 20.7208 102.316 20.8959C102.011 21.0635 101.783 21.2729 101.63 21.5242C101.486 21.7679 101.413 22.0421 101.413 22.3468C101.413 22.8342 101.585 23.2073 101.927 23.4663C102.27 23.7252 102.708 23.8547 103.241 23.8547Z" fill="black"/>
<path d="M85.0832 27.556C84.0232 27.556 83.164 26.6967 83.164 25.6368V11.9283C83.164 10.8683 84.0232 10.0091 85.0832 10.0091H88.2303C88.814 10.0091 89.2871 10.4822 89.2871 11.0659C89.2871 11.4071 90.0895 11.5899 90.3055 11.3258C90.5237 11.0589 90.7563 10.8294 90.9892 10.6374C91.6899 10.0662 92.5086 9.78062 93.4454 9.78062C95.3711 9.78062 95.6292 11.7038 95.6292 12.6689C95.6292 14.8725 94.929 15.0812 92.7142 15.0812C92.0974 15.0812 91.5414 15.2221 91.0464 15.5039C90.5589 15.7781 90.1743 16.1665 89.8926 16.6691C89.6108 17.1642 89.4699 17.7468 89.4699 18.417V25.6368C89.4699 26.6967 88.6106 27.556 87.5507 27.556H85.0832Z" fill="black"/>
<path d="M68.7814 27.7843C67.5324 27.7843 66.3824 27.4568 65.3314 26.8019C64.2881 26.1469 63.4503 25.1531 62.8182 23.8203C62.1937 22.4875 61.8815 20.8082 61.8815 18.7824C61.8815 16.6652 62.2089 14.944 62.8639 13.6189C63.5265 12.2937 64.3794 11.3227 65.4228 10.7058C66.4738 10.0889 67.5781 9.78049 68.7357 9.78049C70.0716 9.78049 71.3891 10.2609 72.3832 11.1212C72.7586 11.4461 73.7622 11.1552 73.7622 10.6587V6.0792C73.7622 5.01926 74.6214 4.16 75.6814 4.16H78.1489C79.2088 4.16 80.0681 5.01926 80.0681 6.0792V25.6367C80.0681 26.6966 79.2088 27.5559 78.1489 27.5559H75.0099C74.346 27.5559 73.8078 27.0177 73.8078 26.3538C73.8078 25.9914 73.0931 25.7744 72.8458 26.0393C72.7704 26.1199 72.6947 26.199 72.6198 26.2764C72.1704 26.741 71.6221 27.1103 70.9748 27.3845C70.335 27.6511 69.6039 27.7843 68.7814 27.7843ZM71.1118 22.9407C71.6907 22.9407 72.1857 22.7731 72.5969 22.438C73.0158 22.0953 73.3357 21.6155 73.5565 20.9986C73.785 20.3741 73.8992 19.6354 73.8992 18.7824C73.8992 17.9142 73.785 17.1717 73.5565 16.5548C73.3357 15.9303 73.0158 15.4543 72.5969 15.1268C72.1857 14.7917 71.6907 14.6242 71.1118 14.6242C70.533 14.6242 70.038 14.7917 69.6268 15.1268C69.2231 15.4543 68.9109 15.9303 68.69 16.5548C68.4768 17.1717 68.3701 17.9142 68.3701 18.7824C68.3701 19.6506 68.4768 20.397 68.69 21.0215C68.9109 21.6383 69.2231 22.1143 69.6268 22.4494C70.038 22.7769 70.533 22.9407 71.1118 22.9407Z" fill="black"/>
<path d="M57.2977 4.16C58.3576 4.16 59.2169 5.01926 59.2169 6.0792V25.6366C59.2169 26.6966 58.3576 27.5559 57.2977 27.5559H54.8302C53.7702 27.5559 52.911 26.6966 52.911 25.6367V6.0792C52.911 5.01926 53.7702 4.16 54.8302 4.16H57.2977Z" fill="black"/>
<path d="M40.639 7.7243C40.639 6.66435 41.4983 5.8051 42.5582 5.8051H45.0257C46.0857 5.8051 46.9449 6.66435 46.9449 7.7243V9.30533C46.9449 9.69398 47.26 10.009 47.6486 10.009H48.4726C49.1793 10.009 49.7521 10.5819 49.7521 11.2885V13.2991C49.7521 14.0057 49.1793 14.5785 48.4726 14.5785H47.6486C47.26 14.5785 46.9449 14.8936 46.9449 15.2822V21.9126C46.9449 22.1563 46.9868 22.3619 47.0706 22.5295C47.1544 22.6894 47.2838 22.8113 47.459 22.895C47.6342 22.9712 47.8588 23.0093 48.133 23.0093C48.8056 23.0093 49.4367 23.2526 49.5753 23.9108L50.023 26.036C50.1686 26.7269 49.7576 27.4079 49.0697 27.5673C48.5519 27.6892 47.935 27.7692 47.2191 27.8072C45.7721 27.8834 44.5574 27.7349 43.5749 27.3617C42.5925 26.9809 41.8537 26.3831 41.3587 25.5682C40.8637 24.7533 40.6238 23.7328 40.639 22.5066V15.2822C40.639 14.8936 40.324 14.5785 39.9353 14.5785H39.6795C38.9728 14.5785 38.4 14.0057 38.4 13.2991V11.2885C38.4 10.5819 38.9728 10.009 39.6795 10.009H39.9353C40.324 10.009 40.639 9.69398 40.639 9.30533V7.7243Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 6.6 KiB

View file

@ -0,0 +1,6 @@
<svg width="21" height="32" viewBox="0 0 21 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 2.64799C0 1.18555 1.18555 0 2.648 0H17.7212C19.1836 0 20.3692 1.18555 20.3692 2.64799V17.7212C20.3692 19.1836 19.1836 20.3692 17.7212 20.3692H2.648C1.18555 20.3692 0 19.1836 0 17.7212V2.64799ZM12.0178 6.11076C12.0178 7.12322 11.1971 7.94398 10.1846 7.94398C9.17213 7.94398 8.35137 7.12322 8.35137 6.11076C8.35137 5.09829 9.17213 4.27753 10.1846 4.27753C11.1971 4.27753 12.0178 5.09829 12.0178 6.11076ZM9.58242 16.499C10.4447 16.5021 11.2749 15.3051 11.5855 14.6365C11.968 13.813 12.2068 12.3326 11.8231 11.4556C11.5542 10.8412 10.9384 10.3883 10.1249 10.3883C9.14541 10.3883 8.35137 11.1847 8.35137 12.1671C8.35137 13.0336 8.98443 13.7228 9.7924 13.8662C9.84773 13.8761 9.88788 13.9264 9.87943 13.9819C9.80306 14.4839 9.58175 15.1304 9.22771 15.4631C8.7953 15.8696 8.93053 16.4967 9.58242 16.499Z" fill="black"/>
<path d="M15.247 31.9137C14.7452 31.9137 14.3384 31.5069 14.3384 31.0051V25.4524C14.3384 24.9506 14.7452 24.5438 15.247 24.5438H15.4307C15.9325 24.5438 16.3392 24.9506 16.3392 25.4524V27.4564C16.3392 27.4934 16.3693 27.5234 16.4063 27.5234C16.4275 27.5234 16.4475 27.5134 16.4601 27.4964L18.3842 24.9101C18.5557 24.6796 18.826 24.5438 19.1132 24.5438H19.1587C19.9097 24.5438 20.3364 25.4033 19.8825 26.0016L18.8767 27.3271C18.6489 27.6274 18.6301 28.0371 18.8294 28.3569L20.1804 30.5246C20.5576 31.1297 20.1224 31.9137 19.4093 31.9137H19.1716C18.8522 31.9137 18.5563 31.746 18.3921 31.472L17.4558 29.9087C17.2378 29.5447 16.7214 29.5151 16.4633 29.8519C16.3828 29.9568 16.3392 30.0853 16.3392 30.2175V31.0051C16.3392 31.5069 15.9325 31.9137 15.4307 31.9137H15.247Z" fill="black"/>
<path d="M9.74217 31.9137H7.81507C7.31329 31.9137 6.90651 31.5069 6.90651 31.0051V25.4524C6.90651 24.9506 7.31329 24.5438 7.81508 24.5438H9.71338C10.4715 24.5438 11.1264 24.6914 11.6782 24.9865C12.2324 25.2791 12.6594 25.7014 12.9593 26.2532C13.2616 26.8025 13.4127 27.4611 13.4127 28.2288C13.4127 28.9965 13.2628 29.6562 12.9629 30.208C12.663 30.7573 12.2384 31.1796 11.689 31.4747C11.1396 31.7673 10.4907 31.9137 9.74217 31.9137ZM8.90731 29.4523C8.90731 29.8736 9.24887 30.2152 9.6702 30.2152C10.0349 30.2152 10.3455 30.1564 10.6022 30.0388C10.8613 29.9213 11.058 29.7186 11.1924 29.4307C11.3291 29.1428 11.3975 28.7422 11.3975 28.2288C11.3975 27.7154 11.3279 27.3147 11.1888 27.0268C11.052 26.739 10.8505 26.5362 10.5842 26.4187C10.3203 26.3011 9.99647 26.2424 9.61262 26.2424C9.22309 26.2424 8.90731 26.5581 8.90731 26.9477V29.4523Z" fill="black"/>
<path d="M4.77075 26.8469C4.409 26.8469 4.13786 26.5175 3.8603 26.2855C3.70196 26.1511 3.46086 26.084 3.13699 26.084C2.93067 26.084 2.76154 26.1092 2.62959 26.1595C2.50004 26.2075 2.40408 26.2735 2.3417 26.3575C2.27933 26.4414 2.24694 26.5374 2.24454 26.6453C2.23975 26.7341 2.25534 26.8145 2.29132 26.8864C2.32971 26.956 2.38969 27.0196 2.47125 27.0772C2.55282 27.1323 2.65718 27.1827 2.78433 27.2283C2.91148 27.2739 3.06262 27.3147 3.23774 27.3507L3.8423 27.4802C4.25014 27.5666 4.5992 27.6805 4.88948 27.8221C5.17977 27.9636 5.41727 28.1304 5.602 28.3223C5.78672 28.5118 5.92227 28.7253 6.00863 28.9628C6.0974 29.2003 6.14298 29.4594 6.14538 29.7401C6.14298 30.2247 6.02183 30.6349 5.78192 30.9708C5.54202 31.3067 5.19896 31.5622 4.75274 31.7373C4.30892 31.9124 3.77513 32 3.15138 32C2.51084 32 1.95186 31.9052 1.47445 31.7157C0.999443 31.5262 0.629991 31.2347 0.366097 30.8413C0.245715 30.659 0.152788 30.4534 0.0873166 30.2244C-0.0509949 29.7406 0.379195 29.3227 0.882363 29.3227H1.27008C1.6066 29.3227 1.85479 29.6099 2.03943 29.8912C2.14019 30.0448 2.28173 30.1611 2.46406 30.2403C2.64878 30.3195 2.86829 30.3591 3.12259 30.3591C3.33611 30.3591 3.51483 30.3327 3.65878 30.2799C3.80272 30.2271 3.91188 30.1539 3.98625 30.0604C4.06062 29.9668 4.099 29.8601 4.1014 29.7401C4.099 29.6274 4.06181 29.529 3.98984 29.445C3.92027 29.3587 3.80512 29.2819 3.64438 29.2147C3.48365 29.1451 3.26653 29.0804 2.99304 29.0204L2.25894 28.8621C1.6064 28.7205 1.09181 28.4842 0.715157 28.1531C0.340907 27.8197 0.154981 27.3651 0.15738 26.7893C0.154981 26.3215 0.279731 25.9124 0.53163 25.5622C0.785928 25.2095 1.13739 24.9348 1.58601 24.7381C2.03703 24.5414 2.55402 24.443 3.13699 24.443C3.73195 24.443 4.24654 24.5426 4.68077 24.7417C5.11499 24.9408 5.44966 25.2215 5.68476 25.5838C5.76203 25.7008 5.82686 25.8244 5.87924 25.9544C6.06742 26.4215 5.63508 26.8469 5.13152 26.8469H4.77075Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

4
packages/editor/modules.d.ts vendored Normal file
View file

@ -0,0 +1,4 @@
declare module '*.svg' {
const content: any
export default content
}

View file

@ -352,7 +352,7 @@ export {
SharedStyleMap,
type SharedStyle,
} from './lib/utils/SharedStylesMap'
export { dataUrlToFile } from './lib/utils/assets'
export { dataUrlToFile, getDefaultCdnBaseUrl } from './lib/utils/assets'
export {
debugFlags,
featureFlags,

View file

@ -164,6 +164,11 @@ export interface TldrawEditorBaseProps {
* Options for the editor.
*/
options?: Partial<TldrawOptions>
/**
* The license key.
*/
licenseKey?: string
}
/**
@ -187,6 +192,8 @@ declare global {
const EMPTY_SHAPE_UTILS_ARRAY = [] as const
const EMPTY_BINDING_UTILS_ARRAY = [] as const
const EMPTY_TOOLS_ARRAY = [] as const
/** @internal */
export const TL_CONTAINER_CLASS = 'tl-container'
/** @public @react */
export const TldrawEditor = memo(function TldrawEditor({
@ -217,7 +224,7 @@ export const TldrawEditor = memo(function TldrawEditor({
<div
ref={setContainer}
draggable={false}
className={classNames('tl-container tl-theme__light', className)}
className={classNames(`${TL_CONTAINER_CLASS} tl-theme__light`, className)}
onPointerDown={stopEventPropagation}
tabIndex={-1}
>
@ -334,6 +341,7 @@ function TldrawEditorWithReadyStore({
inferDarkMode,
cameraOptions,
options,
licenseKey,
}: Required<
TldrawEditorProps & {
store: TLStore
@ -380,6 +388,7 @@ function TldrawEditorWithReadyStore({
inferDarkMode,
cameraOptions,
options,
licenseKey,
})
setEditor(editor)
@ -389,7 +398,7 @@ function TldrawEditorWithReadyStore({
}
},
// if any of these change, we need to recreate the editor.
[bindingUtils, container, options, shapeUtils, store, tools, user, setEditor]
[bindingUtils, container, options, shapeUtils, store, tools, user, setEditor, licenseKey]
)
// keep the editor up to date with the latest camera options

View file

@ -108,7 +108,7 @@ import { intersectPolygonPolygon } from '../primitives/intersect'
import { PI2, approximately, areAnglesCompatible, clamp, pointInPolygon } from '../primitives/utils'
import { ReadonlySharedStyleMap, SharedStyle, SharedStyleMap } from '../utils/SharedStylesMap'
import { dataUrlToFile } from '../utils/assets'
import { debugFlags } from '../utils/debug-flags'
import { debugFlags, featureFlags } from '../utils/debug-flags'
import { getIncrementedName } from '../utils/getIncrementedName'
import { getReorderingShapesChanges } from '../utils/reorderShapes'
import { applyRotationToSnapshotShapes, getRotationSnapshot } from '../utils/rotation'
@ -128,11 +128,13 @@ import { EdgeScrollManager } from './managers/EdgeScrollManager'
import { EnvironmentManager } from './managers/EnvironmentManager'
import { FocusManager } from './managers/FocusManager'
import { HistoryManager } from './managers/HistoryManager'
import { LicenseManager } from './managers/LicenseManager'
import { ScribbleManager } from './managers/ScribbleManager'
import { SnapManager } from './managers/SnapManager/SnapManager'
import { TextManager } from './managers/TextManager'
import { TickManager } from './managers/TickManager'
import { UserPreferencesManager } from './managers/UserPreferencesManager'
import { WatermarkManager } from './managers/WatermarkManager'
import { ShapeUtil, TLResizeMode, TLShapeUtilConstructor } from './shapes/ShapeUtil'
import { RootState } from './tools/RootState'
import { StateNode, TLStateNodeConstructor } from './tools/StateNode'
@ -213,6 +215,7 @@ export interface TLEditorOptions {
*/
cameraOptions?: Partial<TLCameraOptions>
options?: Partial<TldrawOptions>
licenseKey?: string
}
/** @public */
@ -229,6 +232,7 @@ export class Editor extends EventEmitter<TLEventMap> {
autoFocus,
inferDarkMode,
options,
licenseKey,
}: TLEditorOptions) {
super()
@ -698,6 +702,16 @@ export class Editor extends EventEmitter<TLEventMap> {
this._tickManager.start()
})
const checkLicenseKey = async (licenseKey: string | undefined) => {
if (!featureFlags.enableLicensing.get()) return
const licenseManager = new LicenseManager()
const watermarkManager = new WatermarkManager(this)
const license = await licenseManager.getLicenseFromKey(licenseKey)
watermarkManager.checkWatermark(license)
}
checkLicenseKey(licenseKey)
this.performanceTracker = new PerformanceTracker()
}

View file

@ -0,0 +1,383 @@
import crypto from 'crypto'
import { publishDates } from '../../../version'
import { str2ab } from '../../utils/licensing'
import { FLAGS, LicenseManager, PROPERTIES, ValidLicenseKeyResult } from './LicenseManager'
jest.mock('../../../version', () => {
return {
publishDates: {
major: '2024-06-28T10:56:07.893Z',
minor: '2024-07-02T16:49:50.397Z',
patch: '2030-07-02T16:49:50.397Z',
},
}
})
const now = new Date()
const expiryDate = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 5).toISOString()
const STANDARD_LICENSE_INFO = JSON.stringify([
'id',
['www.example.com'],
FLAGS.ANNUAL_LICENSE,
expiryDate,
])
describe('LicenseManager', () => {
let keyPair: { publicKey: string; privateKey: string }
let licenseManager: LicenseManager
beforeAll(() => {
return new Promise((resolve) => {
generateKeyPair().then((kp) => {
keyPair = kp
licenseManager = new LicenseManager(keyPair.publicKey, 'production')
resolve(void 0)
})
})
})
beforeEach(() => {
// @ts-ignore
delete window.location
// @ts-ignore
window.location = new URL('https://www.example.com')
})
it('Fails if no key provided', async () => {
const result = await licenseManager.getLicenseFromKey('')
expect(result).toMatchObject({ isLicenseParseable: false, reason: 'no-key-provided' })
})
it('Signals that it is development mode when appropriate', async () => {
// @ts-ignore
delete window.location
// @ts-ignore
window.location = new URL('http://localhost:3000')
const testEnvLicenseManager = new LicenseManager(keyPair.publicKey, 'development')
const licenseKey = await generateLicenseKey(STANDARD_LICENSE_INFO, keyPair)
const result = await testEnvLicenseManager.getLicenseFromKey(licenseKey)
expect(result).toMatchObject({
isLicenseParseable: true,
isDomainValid: false,
isDevelopment: true,
})
})
it('Cleanses out valid keys that accidentally have zero-width characters or newlines', async () => {
const cleanLicenseKey = await generateLicenseKey(STANDARD_LICENSE_INFO, keyPair)
const dirtyLicenseKey = cleanLicenseKey + '\u200B\u200D\uFEFF\n\r'
const result = await licenseManager.getLicenseFromKey(dirtyLicenseKey)
expect(result.isLicenseParseable).toBe(true)
})
it('Fails if garbage key provided', async () => {
const badPublicKeyLicenseManager = new LicenseManager('badpublickey', 'production')
const invalidLicenseKey = await generateLicenseKey(STANDARD_LICENSE_INFO, keyPair)
const result = await badPublicKeyLicenseManager.getLicenseFromKey(invalidLicenseKey)
expect(result).toMatchObject({ isLicenseParseable: false, reason: 'invalid-license-key' })
})
it('Fails if non-JSON parseable message is provided', async () => {
const invalidMessage = await generateLicenseKey('asdfsad', keyPair)
const result = await licenseManager.getLicenseFromKey(invalidMessage)
expect(result).toMatchObject({ isLicenseParseable: false, reason: 'invalid-license-key' })
})
it('Succeeds if valid key provided', async () => {
const licenseKey = await generateLicenseKey(STANDARD_LICENSE_INFO, keyPair)
const result = await licenseManager.getLicenseFromKey(licenseKey)
expect(result).toMatchObject({
isLicenseParseable: true,
license: {
id: 'id',
hosts: ['www.example.com'],
flags: FLAGS.ANNUAL_LICENSE,
expiryDate,
},
isDomainValid: true,
isAnnualLicense: true,
isAnnualLicenseExpired: false,
isPerpetualLicense: false,
isPerpetualLicenseExpired: false,
isInternalLicense: false,
} as ValidLicenseKeyResult)
})
it('Fails if the license key has expired', async () => {
const expiredLicenseInfo = JSON.parse(STANDARD_LICENSE_INFO)
const expiryDate = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 6) // 6 days ago
expiredLicenseInfo[PROPERTIES.EXPIRY_DATE] = expiryDate
const expiredLicenseKey = await generateLicenseKey(JSON.stringify(expiredLicenseInfo), keyPair)
const result = (await licenseManager.getLicenseFromKey(
expiredLicenseKey
)) as ValidLicenseKeyResult
expect(result.isAnnualLicenseExpired).toBe(true)
})
it('Allows a grace period for expired licenses', async () => {
const almostExpiredLicenseInfo = JSON.parse(STANDARD_LICENSE_INFO)
const expiryDate = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 5) // 5 days ago
almostExpiredLicenseInfo[PROPERTIES.EXPIRY_DATE] = expiryDate
const almostExpiredLicenseKey = await generateLicenseKey(
JSON.stringify(almostExpiredLicenseInfo),
keyPair
)
const result = (await licenseManager.getLicenseFromKey(
almostExpiredLicenseKey
)) as ValidLicenseKeyResult
expect(result.isAnnualLicenseExpired).toBe(false)
})
// We mock the patch version to be in 2030 above.
it('Succeeds for perpetual license with correct version (and patch does not matter)', async () => {
const majorDate = new Date(publishDates.major)
const expiryDate = new Date(
majorDate.getFullYear(),
majorDate.getMonth(),
majorDate.getDate() + 100
)
const perpetualLicenseInfo = ['id', ['www.example.com'], FLAGS.PERPETUAL_LICENSE, expiryDate]
const almostExpiredLicenseKey = await generateLicenseKey(
JSON.stringify(perpetualLicenseInfo),
keyPair
)
const result = (await licenseManager.getLicenseFromKey(
almostExpiredLicenseKey
)) as ValidLicenseKeyResult
expect(result.isPerpetualLicense).toBe(true)
expect(result.isPerpetualLicenseExpired).toBe(false)
})
it('Fails for perpetual license past the release version', async () => {
const majorDate = new Date(publishDates.major)
const expiryDate = new Date(
majorDate.getFullYear(),
majorDate.getMonth(),
majorDate.getDate() - 100
)
const perpetualLicenseInfo = ['id', ['www.example.com'], FLAGS.PERPETUAL_LICENSE, expiryDate]
const almostExpiredLicenseKey = await generateLicenseKey(
JSON.stringify(perpetualLicenseInfo),
keyPair
)
const result = (await licenseManager.getLicenseFromKey(
almostExpiredLicenseKey
)) as ValidLicenseKeyResult
expect(result.isPerpetualLicense).toBe(true)
expect(result.isPerpetualLicenseExpired).toBe(true)
})
it('Fails with invalid host', async () => {
// @ts-ignore
delete window.location
// @ts-ignore
window.location = new URL('https://www.foo.com')
const expiredLicenseKey = await generateLicenseKey(STANDARD_LICENSE_INFO, keyPair)
const result = (await licenseManager.getLicenseFromKey(
expiredLicenseKey
)) as ValidLicenseKeyResult
expect(result.isDomainValid).toBe(false)
})
it('Succeeds if hosts is equal to only "*"', async () => {
// @ts-ignore
delete window.location
// @ts-ignore
window.location = new URL('https://www.foo.com')
const permissiveHostsInfo = JSON.parse(STANDARD_LICENSE_INFO)
permissiveHostsInfo[PROPERTIES.HOSTS] = ['*']
const permissiveLicenseKey = await generateLicenseKey(
JSON.stringify(permissiveHostsInfo),
keyPair
)
const result = (await licenseManager.getLicenseFromKey(
permissiveLicenseKey
)) as ValidLicenseKeyResult
expect(result.isDomainValid).toBe(true)
})
it('Succeeds if has an apex domain specified', async () => {
// @ts-ignore
delete window.location
// @ts-ignore
window.location = new URL('https://www.example.com')
const permissiveHostsInfo = JSON.parse(STANDARD_LICENSE_INFO)
permissiveHostsInfo[PROPERTIES.HOSTS] = ['example.com']
const permissiveLicenseKey = await generateLicenseKey(
JSON.stringify(permissiveHostsInfo),
keyPair
)
const result = (await licenseManager.getLicenseFromKey(
permissiveLicenseKey
)) as ValidLicenseKeyResult
expect(result.isDomainValid).toBe(true)
})
it('Succeeds if has an www domain specified, but at the apex domain', async () => {
// @ts-ignore
delete window.location
// @ts-ignore
window.location = new URL('https://example.com')
const permissiveHostsInfo = JSON.parse(STANDARD_LICENSE_INFO)
permissiveHostsInfo[PROPERTIES.HOSTS] = ['www.example.com']
const permissiveLicenseKey = await generateLicenseKey(
JSON.stringify(permissiveHostsInfo),
keyPair
)
const result = (await licenseManager.getLicenseFromKey(
permissiveLicenseKey
)) as ValidLicenseKeyResult
expect(result.isDomainValid).toBe(true)
})
it('Succeeds if has a subdomain wildcard', async () => {
// @ts-ignore
delete window.location
// @ts-ignore
window.location = new URL('https://sub.example.com')
const permissiveHostsInfo = JSON.parse(STANDARD_LICENSE_INFO)
permissiveHostsInfo[PROPERTIES.HOSTS] = ['*.example.com']
const permissiveLicenseKey = await generateLicenseKey(
JSON.stringify(permissiveHostsInfo),
keyPair
)
const result = (await licenseManager.getLicenseFromKey(
permissiveLicenseKey
)) as ValidLicenseKeyResult
expect(result.isDomainValid).toBe(true)
})
it('Succeeds if has a sub-subdomain wildcard', async () => {
// @ts-ignore
delete window.location
// @ts-ignore
window.location = new URL('https://pr-2408.sub.example.com')
const permissiveHostsInfo = JSON.parse(STANDARD_LICENSE_INFO)
permissiveHostsInfo[PROPERTIES.HOSTS] = ['*.example.com']
const permissiveLicenseKey = await generateLicenseKey(
JSON.stringify(permissiveHostsInfo),
keyPair
)
const result = (await licenseManager.getLicenseFromKey(
permissiveLicenseKey
)) as ValidLicenseKeyResult
expect(result.isDomainValid).toBe(true)
})
it('Fails if has a subdomain wildcard isnt for the same base domain', async () => {
// @ts-ignore
delete window.location
// @ts-ignore
window.location = new URL('https://sub.example.com')
const permissiveHostsInfo = JSON.parse(STANDARD_LICENSE_INFO)
permissiveHostsInfo[PROPERTIES.HOSTS] = ['*.foo.com']
const permissiveLicenseKey = await generateLicenseKey(
JSON.stringify(permissiveHostsInfo),
keyPair
)
const result = (await licenseManager.getLicenseFromKey(
permissiveLicenseKey
)) as ValidLicenseKeyResult
expect(result.isDomainValid).toBe(false)
})
it('Checks for internal license', async () => {
const internalLicenseInfo = JSON.parse(STANDARD_LICENSE_INFO)
internalLicenseInfo[PROPERTIES.FLAGS] = FLAGS.INTERNAL_LICENSE
const internalLicenseKey = await generateLicenseKey(
JSON.stringify(internalLicenseInfo),
keyPair
)
const result = (await licenseManager.getLicenseFromKey(
internalLicenseKey
)) as ValidLicenseKeyResult
expect(result.isInternalLicense).toBe(true)
})
})
async function generateLicenseKey(
message: string,
keyPair: { publicKey: string; privateKey: string }
) {
const enc = new TextEncoder()
const encodedMsg = enc.encode(message)
const privateKey = await importPrivateKey(keyPair.privateKey)
const signedLicenseKeyBuffer = await crypto.subtle.sign(
{
name: 'ECDSA',
hash: { name: 'SHA-256' },
},
privateKey,
encodedMsg
)
const signature = btoa(ab2str(signedLicenseKeyBuffer))
const prefix = 'tldraw-'
const licenseKey = `${prefix}/${btoa(message)}.${signature}`
return licenseKey
}
/*
Import a PEM encoded RSA private key, to use for RSA-PSS signing.
Takes a string containing the PEM encoded key, and returns a Promise
that will resolve to a CryptoKey representing the private key.
*/
function importPrivateKey(pemContents: string) {
// base64 decode the string to get the binary data
const binaryDerString = atob(pemContents)
// convert from a binary string to an ArrayBuffer
const binaryDer = str2ab(binaryDerString) as Uint8Array
return crypto.subtle.importKey(
'pkcs8',
new Uint8Array(binaryDer),
{
name: 'ECDSA',
namedCurve: 'P-256',
},
true,
['sign']
)
}
/*
Generate a sign/verify key pair.
*/
async function generateKeyPair() {
const keyPair = await crypto.subtle.generateKey(
{
name: 'ECDSA',
namedCurve: 'P-256',
},
true,
['sign', 'verify']
)
const publicKey = await exportCryptoKey(keyPair.publicKey, true /* isPublic */)
const privateKey = await exportCryptoKey(keyPair.privateKey)
return { publicKey, privateKey }
}
async function exportCryptoKey(key: CryptoKey, isPublic = false) {
const exported = await crypto.subtle.exportKey(isPublic ? 'spki' : 'pkcs8', key)
return btoa(ab2str(exported))
}
/*
Convert an ArrayBuffer into a string
from https://developer.chrome.com/blog/how-to-convert-arraybuffer-to-and-from-string/
*/
export function ab2str(buf: ArrayBuffer) {
return String.fromCharCode.apply(null, new Uint8Array(buf) as unknown as number[])
}

View file

@ -0,0 +1,303 @@
import { publishDates } from '../../../version'
import { importPublicKey, str2ab } from '../../utils/licensing'
const GRACE_PERIOD_DAYS = 5
export const FLAGS = {
ANNUAL_LICENSE: 0x1,
PERPETUAL_LICENSE: 0x2,
INTERNAL_LICENSE: 0x4,
}
const HIGHEST_FLAG = Math.max(...Object.values(FLAGS))
export const PROPERTIES = {
ID: 0,
HOSTS: 1,
FLAGS: 2,
EXPIRY_DATE: 3,
}
const NUMBER_OF_KNOWN_PROPERTIES = Object.keys(PROPERTIES).length
const LICENSE_EMAIL = 'sales@tldraw.com'
interface LicenseInfo {
id: string
hosts: string[]
flags: number
expiryDate: string
}
type InvalidLicenseReason = 'invalid-license-key' | 'no-key-provided' | 'has-key-development-mode'
export type LicenseFromKeyResult = InvalidLicenseKeyResult | ValidLicenseKeyResult
interface InvalidLicenseKeyResult {
isLicenseParseable: false
reason: InvalidLicenseReason
}
export interface ValidLicenseKeyResult {
isLicenseParseable: true
license: LicenseInfo
isDevelopment: boolean
isDomainValid: boolean
expiryDate: Date
isAnnualLicense: boolean
isAnnualLicenseExpired: boolean
isPerpetualLicense: boolean
isPerpetualLicenseExpired: boolean
isInternalLicense: boolean
}
type TestEnvironment = 'development' | 'production'
export class LicenseManager {
private publicKey =
'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEHJh0uUfxHtCGyerXmmatE368Hd9rI6LH9oPDQihnaCryRFWEVeOvf9U/SPbyxX74LFyJs5tYeAHq5Nc0Ax25LQ=='
public isDevelopment: boolean
public isCryptoAvailable: boolean
constructor(testPublicKey?: string, testEnvironment?: TestEnvironment) {
this.isDevelopment = this.getIsDevelopment(testEnvironment)
this.publicKey = testPublicKey || this.publicKey
this.isCryptoAvailable = !!crypto.subtle
}
private getIsDevelopment(testEnvironment?: TestEnvironment) {
if (testEnvironment === 'development') return true
if (testEnvironment === 'production') return false
// If we are using https we assume it's a production env and a development one otherwise
return window.location.protocol !== 'https:'
}
private async extractLicenseKey(licenseKey: string): Promise<LicenseInfo> {
const [data, signature] = licenseKey.split('.')
const [prefix, encodedData] = data.split('/')
if (!prefix.startsWith('tldraw-')) {
throw new Error(`Unsupported prefix '${prefix}'`)
}
const publicCryptoKey = await importPublicKey(this.publicKey)
let isVerified
try {
isVerified = await crypto.subtle.verify(
{
name: 'ECDSA',
hash: { name: 'SHA-256' },
},
publicCryptoKey,
new Uint8Array(str2ab(atob(signature))),
new Uint8Array(str2ab(atob(encodedData)))
)
} catch (e) {
console.error(e)
throw new Error('Could not perform signature validation')
}
if (!isVerified) {
throw new Error('Invalid signature')
}
let decodedData: any
try {
decodedData = JSON.parse(atob(encodedData))
} catch (e) {
throw new Error('Could not parse object')
}
if (decodedData.length > NUMBER_OF_KNOWN_PROPERTIES) {
this.outputMessages([
'License key contains some unknown properties.',
'You may want to update tldraw packages to a newer version to get access to new functionality.',
])
}
return {
id: decodedData[PROPERTIES.ID],
hosts: decodedData[PROPERTIES.HOSTS],
flags: decodedData[PROPERTIES.FLAGS],
expiryDate: decodedData[PROPERTIES.EXPIRY_DATE],
}
}
async getLicenseFromKey(licenseKey?: string): Promise<LicenseFromKeyResult> {
if (!licenseKey) {
if (!this.isDevelopment) {
this.outputNoLicenseKeyProvided()
}
return { isLicenseParseable: false, reason: 'no-key-provided' }
}
if (this.isDevelopment && !this.isCryptoAvailable) {
// eslint-disable-next-line no-console
console.log(
'tldraw: you seem to be in a development environment that does not support crypto. License not verified.'
)
// eslint-disable-next-line no-console
console.log('You should check that this works in production separately.')
// We can't parse the license if we are in development mode since crypto
// is not available on http
return { isLicenseParseable: false, reason: 'has-key-development-mode' }
}
// Borrowed idea from AG Grid:
// Copying from various sources (like PDFs) can include zero-width characters.
// This helps makes sure the key validation doesn't fail.
let cleanedLicenseKey = licenseKey.replace(/[\u200B-\u200D\uFEFF]/g, '')
cleanedLicenseKey = cleanedLicenseKey.replace(/\r?\n|\r/g, '')
try {
const licenseInfo = await this.extractLicenseKey(cleanedLicenseKey)
const expiryDate = new Date(licenseInfo.expiryDate)
const isAnnualLicense = this.isFlagEnabled(licenseInfo.flags, FLAGS.ANNUAL_LICENSE)
const isPerpetualLicense = this.isFlagEnabled(licenseInfo.flags, FLAGS.PERPETUAL_LICENSE)
const result: ValidLicenseKeyResult = {
license: licenseInfo,
isLicenseParseable: true,
isDevelopment: this.isDevelopment,
isDomainValid: this.isDomainValid(licenseInfo),
expiryDate,
isAnnualLicense,
isAnnualLicenseExpired: isAnnualLicense && this.isAnnualLicenseExpired(expiryDate),
isPerpetualLicense,
isPerpetualLicenseExpired: isPerpetualLicense && this.isPerpetualLicenseExpired(expiryDate),
isInternalLicense: this.isFlagEnabled(licenseInfo.flags, FLAGS.INTERNAL_LICENSE),
}
this.outputLicenseInfoIfNeeded(result)
return result
} catch (e: any) {
this.outputInvalidLicenseKey(e.message)
// If the license can't be parsed, it's invalid
return { isLicenseParseable: false, reason: 'invalid-license-key' }
}
}
private isDomainValid(licenseInfo: LicenseInfo) {
const currentHostname = window.location.hostname.toLowerCase()
return licenseInfo.hosts.some((host) => {
const normalizedHost = host.toLowerCase().trim()
// Allow the domain if listed and www variations, 'example.com' allows 'example.com' and 'www.example.com'
if (
normalizedHost === currentHostname ||
`www.${normalizedHost}` === currentHostname ||
normalizedHost === `www.${currentHostname}`
) {
return true
}
// If host is '*', we allow all domains.
if (host === '*') {
// All domains allowed.
return true
}
// Glob testing, we only support '*.somedomain.com' right now.
if (host.includes('*')) {
const globToRegex = new RegExp(host.replace(/\*/g, '.*?'))
return globToRegex.test(currentHostname)
}
return false
})
}
private getExpirationDateWithoutGracePeriod(expiryDate: Date) {
return new Date(expiryDate.getFullYear(), expiryDate.getMonth(), expiryDate.getDate())
}
private getExpirationDateWithGracePeriod(expiryDate: Date) {
return new Date(
expiryDate.getFullYear(),
expiryDate.getMonth(),
expiryDate.getDate() + GRACE_PERIOD_DAYS + 1 // Add 1 day to include the expiration day
)
}
private isAnnualLicenseExpired(expiryDate: Date) {
const expiration = this.getExpirationDateWithGracePeriod(expiryDate)
const isExpired = new Date() >= expiration
// If it is not expired yet (including the grace period), but after the expiry date we warn the users
if (!isExpired && new Date() >= this.getExpirationDateWithoutGracePeriod(expiryDate)) {
this.outputMessages([
'tldraw license is about to expire, you are in a grace period.',
`Please reach out to ${LICENSE_EMAIL} if you would like to renew your license.`,
])
}
return isExpired
}
private isPerpetualLicenseExpired(expiryDate: Date) {
const expiration = this.getExpirationDateWithGracePeriod(expiryDate)
const dates = {
major: new Date(publishDates.major),
minor: new Date(publishDates.minor),
}
// We allow patch releases, but the major and minor releases should be within the expiration date
return dates.major >= expiration || dates.minor >= expiration
}
private isFlagEnabled(flags: number, flag: number) {
return (flags & flag) === flag
}
private outputNoLicenseKeyProvided() {
this.outputMessages([
'No tldraw license key provided!',
`Please reach out to ${LICENSE_EMAIL} if you would like to license tldraw or if you'd like a trial.`,
])
}
private outputInvalidLicenseKey(msg: string) {
this.outputMessages(['Invalid tldraw license key', `Reason: ${msg}`])
}
private outputLicenseInfoIfNeeded(result: ValidLicenseKeyResult) {
if (result.isAnnualLicenseExpired) {
this.outputMessages([
'Your tldraw license has expired!',
`Please reach out to ${LICENSE_EMAIL} to renew.`,
])
}
if (!result.isDomainValid && !result.isDevelopment) {
this.outputMessages([
'This tldraw license key is not valid for this domain!',
`Please reach out to ${LICENSE_EMAIL} if you would like to use tldraw on other domains.`,
])
}
// If we added a new flag it will be twice the value of the currently highest flag.
// And if all the current flags are on we would get the `HIGHEST_FLAG * 2 - 1`, so anything higher than that means there are new flags.
if (result.license.flags >= HIGHEST_FLAG * 2) {
this.outputMessages([
'This tldraw license contains some unknown flags.',
'You may want to update tldraw packages to a newer version to get access to new functionality.',
])
}
}
private outputMessages(messages: string[]) {
this.outputDelimiter()
for (const message of messages) {
// eslint-disable-next-line no-console
console.log(
`%c${message}`,
`color: white; background: crimson; padding: 2px; border-radius: 3px;`
)
}
this.outputDelimiter()
}
private outputDelimiter() {
// eslint-disable-next-line no-console
console.log(
'%c-------------------------------------------------------------------',
`color: white; background: crimson; padding: 2px; border-radius: 3px;`
)
}
}

View file

@ -0,0 +1,149 @@
import { createTLStore } from '../../config/createTLStore'
import { Editor } from '../Editor'
import { FLAGS, ValidLicenseKeyResult } from './LicenseManager'
import { WatermarkManager } from './WatermarkManager'
function getDefaultLicenseResult(overrides: Partial<ValidLicenseKeyResult>): ValidLicenseKeyResult {
return {
isAnnualLicense: true,
isAnnualLicenseExpired: false,
isInternalLicense: false,
isDevelopment: false,
isDomainValid: true,
isPerpetualLicense: false,
isPerpetualLicenseExpired: false,
isLicenseParseable: true as const,
// WatermarkManager does not check these fields, it relies on the calculated values like isAnnualLicenseExpired
license: {
id: 'id',
hosts: ['localhost'],
flags: FLAGS.PERPETUAL_LICENSE,
expiryDate: new Date().toISOString(),
},
expiryDate: new Date(),
...overrides,
}
}
describe('WatermarkManager', () => {
let watermarkManager: WatermarkManager
let editor: Editor
beforeAll(() => {
editor = new Editor({
store: createTLStore({}),
bindingUtils: [],
shapeUtils: [],
getContainer: () => document.createElement('div'),
tools: [],
})
watermarkManager = new WatermarkManager(editor)
})
it('shows watermark when license is not parseable', () => {
const licenseResult = getDefaultLicenseResult({
// @ts-ignore
isLicenseParseable: false,
})
expect(watermarkManager.checkWatermark(licenseResult)).toBe(true)
})
it('shows watermark when domain is not valid', () => {
const licenseResult = getDefaultLicenseResult({
isDomainValid: false,
})
expect(watermarkManager.checkWatermark(licenseResult)).toBe(true)
})
it('shows watermark when annual license has expired', () => {
const licenseResult = getDefaultLicenseResult({
isAnnualLicense: true,
isAnnualLicenseExpired: true,
})
expect(watermarkManager.checkWatermark(licenseResult)).toBe(true)
})
it('shows watermark when annual license has expired, even if dev mode', () => {
const licenseResult = getDefaultLicenseResult({
isAnnualLicense: true,
isAnnualLicenseExpired: true,
isDevelopment: true,
})
expect(watermarkManager.checkWatermark(licenseResult)).toBe(true)
})
it('shows watermark when perpetual license has expired', () => {
const licenseResult = getDefaultLicenseResult({
isPerpetualLicense: true,
isPerpetualLicenseExpired: true,
})
expect(watermarkManager.checkWatermark(licenseResult)).toBe(true)
})
it('does not show watermark when license is valid and not expired', () => {
const licenseResult = getDefaultLicenseResult({
isAnnualLicense: true,
isAnnualLicenseExpired: false,
isInternalLicense: false,
})
expect(watermarkManager.checkWatermark(licenseResult)).toBe(false)
})
it('does not show watermark when perpetual license is valid and not expired', () => {
const licenseResult = getDefaultLicenseResult({
isPerpetualLicense: true,
isPerpetualLicenseExpired: false,
isInternalLicense: false,
})
expect(watermarkManager.checkWatermark(licenseResult)).toBe(false)
})
it('does not show watermark when in development mode', () => {
const licenseResult = getDefaultLicenseResult({
isDevelopment: true,
})
expect(watermarkManager.checkWatermark(licenseResult)).toBe(false)
})
it('does not show watermark when license is parseable and domain is valid', () => {
const licenseResult = getDefaultLicenseResult({
isLicenseParseable: true,
isDomainValid: true,
})
expect(watermarkManager.checkWatermark(licenseResult)).toBe(false)
})
it('does not show watermark when license is parseable and domain is not valid and dev mode', () => {
const licenseResult = getDefaultLicenseResult({
isLicenseParseable: true,
isDomainValid: false,
isDevelopment: true,
})
expect(watermarkManager.checkWatermark(licenseResult)).toBe(false)
})
it('throws when an internal annual license has expired', () => {
const expiryDate = new Date(2023, 1, 1)
const licenseResult = getDefaultLicenseResult({
isAnnualLicense: true,
isAnnualLicenseExpired: true,
isInternalLicense: true,
expiryDate,
})
expect(() => watermarkManager.checkWatermark(licenseResult)).toThrow(
/License: Internal license expired/
)
})
it('throws when an internal perpetual license has expired', () => {
const expiryDate = new Date(2023, 1, 1)
const licenseResult = getDefaultLicenseResult({
isPerpetualLicense: true,
isPerpetualLicenseExpired: true,
isInternalLicense: true,
expiryDate,
})
expect(() => watermarkManager.checkWatermark(licenseResult)).toThrow(
/License: Internal license expired/
)
})
})

View file

@ -0,0 +1,126 @@
import watermarkDesktop from '../../../../assets/watermarks/watermark-desktop.svg'
import watermarkMobile from '../../../../assets/watermarks/watermark-mobile.svg'
import { TL_CONTAINER_CLASS } from '../../TldrawEditor'
import { getDefaultCdnBaseUrl } from '../../utils/assets'
import { Editor } from '../Editor'
import { LicenseFromKeyResult } from './LicenseManager'
export const WATERMARK_DESKTOP_FILENAME = 'watermark-desktop.svg'
export const WATERMARK_MOBILE_FILENAME = 'watermark-mobile.svg'
export const WATERMARKS_FOLDER = 'watermarks'
export class WatermarkManager {
constructor(private editor: Editor) {}
private forceLocal = false
private setWatermarkSrc(watermark: HTMLImageElement) {
const isMobile = window.innerWidth < 840 /* PORTRAIT_BREAKPOINTS[TABLET] */
const width = isMobile ? '32px' : '120px'
let src = ''
if (navigator.onLine && !this.forceLocal) {
src = `${getDefaultCdnBaseUrl()}/${WATERMARKS_FOLDER}/${isMobile ? WATERMARK_MOBILE_FILENAME : WATERMARK_DESKTOP_FILENAME}`
} else {
src = isMobile ? watermarkMobile : watermarkDesktop
}
if (src !== watermark.src) {
watermark.style.width = width
watermark.src = src
}
}
private createWatermark(doReplace = false) {
let watermark = this.findWatermark()
if (watermark && !doReplace) return watermark
if (!watermark) {
watermark = document.createElement('img')
}
this.applyStyles(watermark)
const canvas = this.getWatermarkParent()
if (canvas) canvas.appendChild(watermark)
return watermark
}
private getWatermarkParent() {
return document.getElementsByClassName(TL_CONTAINER_CLASS)[0] as HTMLElement
}
private shouldShowWatermark(license: LicenseFromKeyResult) {
if (!license.isLicenseParseable) return true
if (!license.isDomainValid && !license.isDevelopment) return true
if (license.isPerpetualLicenseExpired || license.isAnnualLicenseExpired) {
if (license.isInternalLicense) {
throw new Error('License: Internal license expired.')
}
return true
}
return false
}
private findWatermark() {
const canvas = this.getWatermarkParent()
if (!canvas) return
const children = [...canvas.children]
return children.find(
(element) =>
element instanceof HTMLImageElement &&
(element.src.includes(WATERMARK_DESKTOP_FILENAME) ||
element.src.includes(WATERMARK_MOBILE_FILENAME))
) as HTMLImageElement
}
checkWatermark(license: LicenseFromKeyResult) {
if (!this.shouldShowWatermark(license)) return false
this.createWatermark()
const resizeListener = () => {
// We need to replace the watermark to ensure the correct size is shown.
const watermark = this.createWatermark(true /* doReplace */)
watermark && this.setWatermarkSrc(watermark)
}
window.addEventListener('resize', resizeListener)
this.editor.timers.setTimeout(() => {
// Ensure the watermark is still there.
// We check this once for any naughtiness.
// Don't be naughty.
const watermark = this.createWatermark()
this.applyStyles(watermark)
}, 5000)
return true
}
applyStyles(watermark: HTMLImageElement) {
watermark.style.setProperty('position', 'absolute', 'important')
watermark.style.setProperty('bottom', '8px', 'important')
watermark.style.setProperty('right', '8px', 'important')
watermark.style.setProperty('opacity', '1', 'important')
watermark.style.setProperty('z-index', '2147483647' /* max */, 'important')
watermark.style.setProperty('pointer-events', 'all', 'important')
watermark.style.setProperty('cursor', 'pointer', 'important')
watermark.setAttribute('target', '_blank')
watermark.onerror = () => {
// In case we're online but it's blocking this specific request,
// we still fallback to the local watermark.
this.forceLocal = true
this.setWatermarkSrc(watermark)
}
watermark.onclick = () => {
window.open('https://tldraw.dev', '_blank', 'noopener noreferrer')
}
this.setWatermarkSrc(watermark)
}
}

View file

@ -1,4 +1,5 @@
import { fetch } from '@tldraw/utils'
import { version } from '../../version'
/** @public */
export function dataUrlToFile(url: string, filename: string, mimeType: string) {
@ -10,3 +11,11 @@ export function dataUrlToFile(url: string, filename: string, mimeType: string) {
return new File([buf], filename, { type: mimeType })
})
}
/** @internal */
const CDN_BASE_URL = 'https://cdn.tldraw.com'
/** @public */
export function getDefaultCdnBaseUrl() {
return `${CDN_BASE_URL}/${version}`
}

View file

@ -9,7 +9,9 @@ import { deleteFromSessionStorage, getFromSessionStorage, setInSessionStorage }
// `true` by default in development and staging, and `false` in production.
/** @internal */
export const featureFlags: Record<string, DebugFlag<boolean>> = {
// canMoveArrowLabel: createFeatureFlag('canMoveArrowLabel'),
enableLicensing: createFeatureFlag('enableLicensing', {
defaults: { all: true, production: false },
}),
}
/** @internal */
@ -106,16 +108,19 @@ function createDebugValue<T>(
})
}
// function createFeatureFlag(
// name: string,
// defaults: Defaults<boolean> = { all: true, production: false }
// ) {
// return createDebugValueBase({
// name,
// defaults,
// shouldStoreForSession: true,
// })
// }
function createFeatureFlag<T>(
name: string,
{
defaults,
shouldStoreForSession = true,
}: { defaults: DebugFlagDefaults<T>; shouldStoreForSession?: boolean }
) {
return createDebugValueBase({
name,
defaults,
shouldStoreForSession,
})
}
function createDebugValueBase<T>(def: DebugFlagDef<T>): DebugFlag<T> {
const defaultValue = getDefaultValue(def)

View file

@ -0,0 +1,30 @@
/*
Convert a string into an ArrayBuffer
from https://developers.google.com/web/updates/2012/06/How-to-convert-ArrayBuffer-to-and-from-String
*/
export function str2ab(str: string) {
const buf = new ArrayBuffer(str.length)
const bufView = new Uint8Array(buf)
for (let i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i)
}
return buf
}
export function importPublicKey(pemContents: string) {
// base64 decode the string to get the binary data
const binaryDerString = atob(pemContents)
// convert from a binary string to an ArrayBuffer
const binaryDer = str2ab(binaryDerString)
return crypto.subtle.importKey(
'spki',
new Uint8Array(binaryDer),
{
name: 'ECDSA',
namedCurve: 'P-256',
},
true,
['verify']
)
}

View file

@ -2,3 +2,8 @@
// Do not edit manually. Or do, I'm a comment, not a cop.
export const version = '2.3.0'
export const publishDates = {
major: '2024-06-28T10:56:07.893Z',
minor: '2024-07-02T16:49:50.397Z',
patch: '2024-07-02T16:49:50.397Z',
}

File diff suppressed because one or more lines are too long

View file

@ -122,6 +122,7 @@ export {
EditSubmenu,
ExportFileContentSubMenu,
ExtrasGroup,
HelpGroup,
MiscMenuGroup,
PreferencesGroup,
UndoRedoGroup,
@ -410,7 +411,6 @@ export { exportAs, type TLExportType } from './lib/utils/export/exportAs'
export { fitFrameToContent, removeFrame } from './lib/utils/frames/frames'
export {
defaultEditorAssetUrls,
getDefaultCdnBaseUrl,
setDefaultEditorAssetUrls,
type TLEditorAssetUrls,
} from './lib/utils/static-assets/assetUrls'

View file

@ -44,6 +44,10 @@ export interface TldrawImageProps extends TLSvgOptions {
* Additional binding utils to use.
*/
bindingUtils?: readonly TLAnyBindingUtilConstructor[]
/**
* The license key.
*/
licenseKey?: string
}
/**
@ -89,6 +93,7 @@ export const TldrawImage = memo(function TldrawImage(props: TldrawImageProps) {
darkMode,
preserveAspectRatio,
format = 'svg',
licenseKey,
} = props
useLayoutEffect(() => {
@ -108,6 +113,7 @@ export const TldrawImage = memo(function TldrawImage(props: TldrawImageProps) {
bindingUtils: bindingUtilsWithDefaults,
tools: [],
getContainer: () => tempElm,
licenseKey,
})
if (pageId) editor.setCurrentPage(pageId)
@ -169,6 +175,7 @@ export const TldrawImage = memo(function TldrawImage(props: TldrawImageProps) {
preserveAspectRatio,
preloadingComplete,
preloadingError,
licenseKey,
])
if (preloadingError) {

View file

@ -1,9 +1,10 @@
import { EMBED_DEFINITIONS, LANGUAGES, RecursivePartial } from '@tldraw/editor'
import {
TLEditorAssetUrls,
defaultEditorAssetUrls,
EMBED_DEFINITIONS,
LANGUAGES,
RecursivePartial,
getDefaultCdnBaseUrl,
} from '../utils/static-assets/assetUrls'
} from '@tldraw/editor'
import { TLEditorAssetUrls, defaultEditorAssetUrls } from '../utils/static-assets/assetUrls'
import { TLUiIconType, iconTypes } from './icon-types'
/** @public */

View file

@ -2,6 +2,7 @@ import { useEditor, useValue } from '@tldraw/editor'
import { useActions } from '../../context/actions'
import { useCanRedo, useCanUndo } from '../../hooks/menu-hooks'
import { ColorSchemeMenu } from '../ColorSchemeMenu'
import { KeyboardShortcutsMenuItem } from '../HelpMenu/DefaultHelpMenuContent'
import { LanguageMenu } from '../LanguageMenu'
import {
ClipboardMenuGroup,
@ -46,6 +47,7 @@ export function DefaultMainMenuContent() {
<ExportFileContentSubMenu />
<ExtrasGroup />
<PreferencesGroup />
<HelpGroup />
</>
)
}
@ -159,6 +161,32 @@ export function ExtrasGroup() {
)
}
/** @public @react */
export function HelpGroup() {
function openUrl(url: string) {
window.open(url, '_blank')
}
return (
<TldrawUiMenuGroup id="help">
<TldrawUiMenuSubmenu id="help" label="menu.help">
<TldrawUiMenuGroup id="help-actions">
<KeyboardShortcutsMenuItem />
<TldrawUiMenuItem
id="about"
label="help-menu.docs"
icon="external-link"
readonlyOk
onSelect={() => {
openUrl('https://tldraw.dev')
}}
/>
</TldrawUiMenuGroup>
</TldrawUiMenuSubmenu>
</TldrawUiMenuGroup>
)
}
/* ------------------- Preferences ------------------ */
/** @public @react */

View file

@ -11,7 +11,7 @@ import {
import { CursorChatBubble } from '../components/CursorChatBubble'
import { DefaultDebugMenu } from '../components/DebugMenu/DefaultDebugMenu'
import { DefaultDebugPanel } from '../components/DefaultDebugPanel'
import { DefaultHelpMenu, TLUiHelpMenuProps } from '../components/HelpMenu/DefaultHelpMenu'
import { TLUiHelpMenuProps } from '../components/HelpMenu/DefaultHelpMenu'
import {
DefaultHelperButtons,
TLUiHelperButtonsProps,
@ -81,7 +81,7 @@ export function TldrawUiComponentsProvider({
() => ({
ContextMenu: DefaultContextMenu,
ActionsMenu: DefaultActionsMenu,
HelpMenu: DefaultHelpMenu,
HelpMenu: null,
ZoomMenu: DefaultZoomMenu,
MainMenu: DefaultMainMenu,
Minimap: DefaultMinimap,

View file

@ -249,6 +249,7 @@ export type TLUiTranslationKey =
| 'menu.file'
| 'menu.language'
| 'menu.preferences'
| 'menu.help'
| 'menu.view'
| 'context-menu.edit'
| 'context-menu.arrange'
@ -299,6 +300,7 @@ export type TLUiTranslationKey =
| 'people-menu.invite'
| 'help-menu.title'
| 'help-menu.about'
| 'help-menu.docs'
| 'help-menu.discord'
| 'help-menu.github'
| 'help-menu.keyboard-shortcuts'

View file

@ -250,6 +250,7 @@ export const DEFAULT_TRANSLATION = {
'menu.file': 'File',
'menu.language': 'Language',
'menu.preferences': 'Preferences',
'menu.help': 'Help',
'menu.view': 'View',
'context-menu.edit': 'Edit',
'context-menu.arrange': 'Arrange',
@ -302,6 +303,7 @@ export const DEFAULT_TRANSLATION = {
'people-menu.invite': 'Invite others',
'help-menu.title': 'Help and resources',
'help-menu.about': 'About',
'help-menu.docs': 'Documentation & API',
'help-menu.discord': 'Discord',
'help-menu.github': 'GitHub',
'help-menu.keyboard-shortcuts': 'Keyboard shortcuts',

View file

@ -2,3 +2,8 @@
// Do not edit manually. Or do, I'm a comment, not a cop.
export const version = '2.3.0'
export const publishDates = {
major: '2024-06-28T10:56:07.893Z',
minor: '2024-07-02T16:49:50.397Z',
patch: '2024-07-02T16:49:50.397Z',
}

View file

@ -1,14 +1,5 @@
import { RecursivePartial } from '@tldraw/editor'
import { RecursivePartial, getDefaultCdnBaseUrl } from '@tldraw/editor'
import { useMemo } from 'react'
import { version } from '../../ui/version'
/** @internal */
const CDN_BASE_URL = 'https://cdn.tldraw.com'
/** @public */
export function getDefaultCdnBaseUrl() {
return `${CDN_BASE_URL}/${version}`
}
/** @public */
export interface TLEditorAssetUrls {

View file

@ -49,6 +49,7 @@ const env = makeEnv([
'SUPABASE_LITE_ANON_KEY',
'SUPABASE_LITE_URL',
'TLDRAW_ENV',
'TLDRAW_LICENSE',
'VERCEL_ORG_ID',
'VERCEL_PROJECT_ID',
'VERCEL_TOKEN',

View file

@ -1,6 +1,8 @@
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync } from 'fs'
import { join } from 'path'
import { SemVer } from 'semver'
import { optimize } from 'svgo'
import { publishDates, version } from './../packages/editor/src/version'
import {
readJsonIfExists,
REPO_ROOT,
@ -271,7 +273,24 @@ async function copyTranslations() {
}
}
// 4. ASSET DECLARATION FILES
// 4. WATERMARKS
async function copyWatermarks() {
const folderName = 'watermarks'
const extension = '.svg'
const sourceFolderPath = join(ASSETS_FOLDER_PATH, folderName)
const itemsToCopy = readdirSync(sourceFolderPath).filter((watermark) =>
watermark.endsWith(extension)
)
const destinationFolderPath = join(REPO_ROOT, 'packages', 'editor', 'assets', 'watermarks')
// Copy all items into the new folder
for (const item of itemsToCopy) {
await writeFile(join(destinationFolderPath, item), readFileSync(join(sourceFolderPath, item)))
}
}
// 5. ASSET DECLARATION FILES
async function writeUrlBasedAssetDeclarationFile() {
const codeFilePath = join(REPO_ROOT, 'packages', 'assets', 'urls.js')
const codeFile = `
@ -404,11 +423,43 @@ async function writeAssetDeclarationDTSFile() {
await writeCodeFile('scripts/refresh-assets.ts', 'typescript', assetDeclarationFilePath, dts)
}
function getNewPublishDates(packageVersion: string) {
const currentVersion = new SemVer(version)
const currentPackageVersion = new SemVer(packageVersion)
const now = new Date().toISOString()
if (currentPackageVersion.major > currentVersion.major) {
return {
major: now,
minor: now,
patch: now,
}
} else if (currentPackageVersion.minor > currentVersion.minor) {
return {
major: publishDates.major,
minor: now,
patch: now,
}
} else if (currentPackageVersion.patch > currentVersion.patch) {
return {
major: publishDates.major,
minor: publishDates.minor,
patch: now,
}
}
return publishDates
}
async function copyVersionToDotCom() {
const packageJson = await readJsonIfExists(join(REPO_ROOT, 'packages', 'tldraw', 'package.json'))
const packageVersion = packageJson.version
const publishDates = getNewPublishDates(packageVersion)
const file = `export const version = '${packageVersion}'
export const publishDates = {
major: '${publishDates.major}',
minor: '${publishDates.minor}',
patch: '${publishDates.patch}',
}`
const file = `export const version = '${packageVersion}'`
await writeCodeFile(
'scripts/refresh-assets.ts',
'typescript',
@ -439,6 +490,8 @@ async function main() {
await copyFonts()
nicelog('Copying translations...')
await copyTranslations()
nicelog('Copying watermarks...')
await copyWatermarks()
nicelog('Writing asset declaration file...')
await writeAssetDeclarationDTSFile()
await writeUrlBasedAssetDeclarationFile()

View file

@ -11849,6 +11849,7 @@ __metadata:
"@playwright/test": "npm:^1.38.1"
"@radix-ui/react-alert-dialog": "npm:^1.0.5"
"@tldraw/assets": "workspace:*"
"@tldraw/dotcom-shared": "workspace:*"
"@types/lodash": "npm:^4.14.188"
"@vercel/analytics": "npm:^1.1.1"
"@vitejs/plugin-react-swc": "npm:^3.6.0"