From 9973d8d4e6ca0fc00a8009c2fa9750ae1e20d250 Mon Sep 17 00:00:00 2001 From: lucaronin Date: Mon, 4 May 2026 14:41:23 +0200 Subject: [PATCH] fix: bundle tldraw whiteboard assets --- package.json | 1 + pnpm-lock.yaml | 10 +++ src/components/TldrawWhiteboard.test.tsx | 90 ++++++++++++++++++++++++ src/components/TldrawWhiteboard.tsx | 4 ++ 4 files changed, 105 insertions(+) create mode 100644 src/components/TldrawWhiteboard.test.tsx diff --git a/package.json b/package.json index b5b2596f..e8381a6b 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ "@tauri-apps/plugin-opener": "^2.5.3", "@tauri-apps/plugin-process": "^2.3.1", "@tauri-apps/plugin-updater": "^2.10.0", + "@tldraw/assets": "4.5.10", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "date-fns": "^4.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index afa49faf..d519fa29 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -125,6 +125,9 @@ importers: '@tauri-apps/plugin-updater': specifier: ^2.10.0 version: 2.10.0 + '@tldraw/assets': + specifier: 4.5.10 + version: 4.5.10 class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -2499,6 +2502,9 @@ packages: '@tiptap/starter-kit@3.22.5': resolution: {integrity: sha512-LZ/LYbwH6rnDi5DnRyagkuNsYAVyhM+yJvvz+ZuYA0JkPiTXJV86J5PWSKew8M0gVfMHcNVtKjfQCvViFCeIgw==} + '@tldraw/assets@4.5.10': + resolution: {integrity: sha512-nJZ+dZgLcmeEtv4aZv/kEkeY4IU2qrFC8Ayftxql1B+2hXc9ZsjNMKOHyB/W2BsVWdUplZm2qjujKeGNs1VWrw==} + '@tldraw/editor@4.5.10': resolution: {integrity: sha512-kM11sDK1ADXATE/wthBDY3ZOriT5Nzpieq1EoPz/HwSMVFuLq5NVmaOPKF+Pt/n43T1S5eLOfMOc7i3zojNc3g==} peerDependencies: @@ -7757,6 +7763,10 @@ snapshots: '@tiptap/extensions': 3.22.5(@tiptap/core@3.22.5(@tiptap/pm@3.22.5))(@tiptap/pm@3.22.5) '@tiptap/pm': 3.22.5 + '@tldraw/assets@4.5.10': + dependencies: + '@tldraw/utils': 4.5.10 + '@tldraw/editor@4.5.10(@floating-ui/dom@1.7.5)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: '@tiptap/core': 3.19.0(@tiptap/pm@3.19.0) diff --git a/src/components/TldrawWhiteboard.test.tsx b/src/components/TldrawWhiteboard.test.tsx new file mode 100644 index 00000000..fdce6e3a --- /dev/null +++ b/src/components/TldrawWhiteboard.test.tsx @@ -0,0 +1,90 @@ +import { render, screen } from '@testing-library/react' +import { describe, expect, it, vi } from 'vitest' +import { TldrawWhiteboard } from './TldrawWhiteboard' + +interface MockTldrawProps { + assetUrls: MockAssetUrls +} + +interface MockAssetUrls { + embedIcons: Record + fonts: Record + icons: Record + translations: Record +} + +const tldrawMock = vi.hoisted(() => ({ + Tldraw: vi.fn(), +})) + +vi.mock('tldraw', async () => { + const { createElement } = await import('react') + + tldrawMock.Tldraw.mockImplementation(({ assetUrls }: MockTldrawProps) => + createElement('div', { + 'data-testid': 'mock-tldraw', + 'data-draw-font-url': assetUrls.fonts.tldraw_draw, + }) + ) + + return { + Box: class Box { + x: number + y: number + w: number + h: number + + constructor(x: number, y: number, w: number, h: number) { + this.x = x + this.y = y + this.w = w + this.h = h + } + }, + Tldraw: tldrawMock.Tldraw, + createTLStore: vi.fn(() => ({ + listen: vi.fn(() => vi.fn()), + })), + getSnapshot: vi.fn(() => ({ document: {} })), + loadSnapshot: vi.fn(), + } +}) + +function renderedTldrawAssetUrls(): MockAssetUrls { + const props = tldrawMock.Tldraw.mock.calls[0]?.[0] as MockTldrawProps + expect(props.assetUrls).toBeDefined() + return props.assetUrls +} + +function expectNoCdnUrls(urls: Record) { + Object.values(urls).forEach((url) => { + expect(url).not.toContain('cdn.tldraw.com') + }) +} + +function expectBundledTldrawAssetUrls(assetUrls: MockAssetUrls) { + expect(assetUrls.fonts.tldraw_draw).toContain('Shantell_Sans-Informal_Regular.woff2') + expect(assetUrls.icons['tool-pencil']).toContain('0_merged.svg#tool-pencil') + expect(assetUrls.translations.en).toContain('en.json') + expectNoCdnUrls(assetUrls.fonts) + expectNoCdnUrls(assetUrls.icons) + expectNoCdnUrls(assetUrls.translations) +} + +describe('TldrawWhiteboard', () => { + it('uses bundled tldraw assets instead of CDN URLs', () => { + render( + + ) + + expect(screen.getByTestId('mock-tldraw')).toHaveAttribute('data-draw-font-url') + expectBundledTldrawAssetUrls(renderedTldrawAssetUrls()) + }) +}) diff --git a/src/components/TldrawWhiteboard.tsx b/src/components/TldrawWhiteboard.tsx index 23cda4e9..5cd6ccce 100644 --- a/src/components/TldrawWhiteboard.tsx +++ b/src/components/TldrawWhiteboard.tsx @@ -1,4 +1,5 @@ import { useEffect, useMemo, useRef, useState, type CSSProperties, type PointerEvent as ReactPointerEvent } from 'react' +import { getAssetUrlsByImport } from '@tldraw/assets/imports.vite' import { Box, Tldraw, @@ -11,6 +12,8 @@ import { } from 'tldraw' import 'tldraw/tldraw.css' +const tldrawAssetUrls = getAssetUrlsByImport() + interface TldrawWhiteboardProps { boardId: string height: string @@ -274,6 +277,7 @@ export function TldrawWhiteboard({ style={cssSize(visibleSize)} >