tldraw 4.1.0-canary.e653ec63c99b → 4.1.0-canary.f2f81cd6fe2c

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/dist-cjs/index.d.ts +17 -11
  2. package/dist-cjs/index.js +3 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/defaultEmbedDefinitions.js +2 -25
  5. package/dist-cjs/lib/defaultEmbedDefinitions.js.map +2 -2
  6. package/dist-cjs/lib/defaultExternalContentHandlers.js +9 -32
  7. package/dist-cjs/lib/defaultExternalContentHandlers.js.map +2 -2
  8. package/dist-cjs/lib/shapes/arrow/ArrowShapeUtil.js +3 -0
  9. package/dist-cjs/lib/shapes/arrow/ArrowShapeUtil.js.map +2 -2
  10. package/dist-cjs/lib/shapes/bookmark/BookmarkShapeUtil.js +43 -101
  11. package/dist-cjs/lib/shapes/bookmark/BookmarkShapeUtil.js.map +2 -2
  12. package/dist-cjs/lib/shapes/bookmark/bookmarks.js +138 -0
  13. package/dist-cjs/lib/shapes/bookmark/bookmarks.js.map +7 -0
  14. package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js +25 -3
  15. package/dist-cjs/lib/shapes/embed/EmbedShapeUtil.js.map +2 -2
  16. package/dist-cjs/lib/shapes/line/LineShapeUtil.js +3 -0
  17. package/dist-cjs/lib/shapes/line/LineShapeUtil.js.map +2 -2
  18. package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js +9 -1
  19. package/dist-cjs/lib/tools/SelectTool/childStates/DraggingHandle.js.map +2 -2
  20. package/dist-cjs/lib/ui/components/Minimap/MinimapManager.js +5 -0
  21. package/dist-cjs/lib/ui/components/Minimap/MinimapManager.js.map +2 -2
  22. package/dist-cjs/lib/ui/components/SharePanel/PeopleMenu.js +6 -2
  23. package/dist-cjs/lib/ui/components/SharePanel/PeopleMenu.js.map +2 -2
  24. package/dist-cjs/lib/ui/components/StylePanel/StylePanelButtonPicker.js +1 -1
  25. package/dist-cjs/lib/ui/components/StylePanel/StylePanelButtonPicker.js.map +1 -1
  26. package/dist-cjs/lib/ui/context/actions.js +23 -29
  27. package/dist-cjs/lib/ui/context/actions.js.map +2 -2
  28. package/dist-cjs/lib/ui/version.js +3 -3
  29. package/dist-cjs/lib/ui/version.js.map +1 -1
  30. package/dist-esm/index.d.mts +17 -11
  31. package/dist-esm/index.mjs +3 -1
  32. package/dist-esm/index.mjs.map +2 -2
  33. package/dist-esm/lib/defaultEmbedDefinitions.mjs +2 -25
  34. package/dist-esm/lib/defaultEmbedDefinitions.mjs.map +2 -2
  35. package/dist-esm/lib/defaultExternalContentHandlers.mjs +9 -32
  36. package/dist-esm/lib/defaultExternalContentHandlers.mjs.map +2 -2
  37. package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs +3 -0
  38. package/dist-esm/lib/shapes/arrow/ArrowShapeUtil.mjs.map +2 -2
  39. package/dist-esm/lib/shapes/bookmark/BookmarkShapeUtil.mjs +46 -101
  40. package/dist-esm/lib/shapes/bookmark/BookmarkShapeUtil.mjs.map +2 -2
  41. package/dist-esm/lib/shapes/bookmark/bookmarks.mjs +124 -0
  42. package/dist-esm/lib/shapes/bookmark/bookmarks.mjs.map +7 -0
  43. package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs +26 -3
  44. package/dist-esm/lib/shapes/embed/EmbedShapeUtil.mjs.map +2 -2
  45. package/dist-esm/lib/shapes/line/LineShapeUtil.mjs +3 -0
  46. package/dist-esm/lib/shapes/line/LineShapeUtil.mjs.map +2 -2
  47. package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs +11 -2
  48. package/dist-esm/lib/tools/SelectTool/childStates/DraggingHandle.mjs.map +2 -2
  49. package/dist-esm/lib/ui/components/Minimap/MinimapManager.mjs +5 -0
  50. package/dist-esm/lib/ui/components/Minimap/MinimapManager.mjs.map +2 -2
  51. package/dist-esm/lib/ui/components/SharePanel/PeopleMenu.mjs +6 -2
  52. package/dist-esm/lib/ui/components/SharePanel/PeopleMenu.mjs.map +2 -2
  53. package/dist-esm/lib/ui/components/StylePanel/StylePanelButtonPicker.mjs +1 -1
  54. package/dist-esm/lib/ui/components/StylePanel/StylePanelButtonPicker.mjs.map +1 -1
  55. package/dist-esm/lib/ui/context/actions.mjs +23 -29
  56. package/dist-esm/lib/ui/context/actions.mjs.map +2 -2
  57. package/dist-esm/lib/ui/version.mjs +3 -3
  58. package/dist-esm/lib/ui/version.mjs.map +1 -1
  59. package/package.json +3 -3
  60. package/src/index.ts +1 -0
  61. package/src/lib/defaultEmbedDefinitions.ts +2 -25
  62. package/src/lib/defaultExternalContentHandlers.ts +11 -36
  63. package/src/lib/shapes/arrow/ArrowShapeUtil.tsx +3 -0
  64. package/src/lib/shapes/bookmark/BookmarkShapeUtil.tsx +51 -135
  65. package/src/lib/shapes/bookmark/bookmarks.ts +170 -0
  66. package/src/lib/shapes/embed/EmbedShapeUtil.tsx +28 -2
  67. package/src/lib/shapes/line/LineShapeUtil.tsx +3 -0
  68. package/src/lib/tools/SelectTool/childStates/DraggingHandle.tsx +13 -1
  69. package/src/lib/ui/components/Minimap/MinimapManager.ts +6 -0
  70. package/src/lib/ui/components/SharePanel/PeopleMenu.tsx +6 -2
  71. package/src/lib/ui/components/StylePanel/StylePanelButtonPicker.tsx +1 -1
  72. package/src/lib/ui/context/actions.tsx +27 -31
  73. package/src/lib/ui/version.ts +3 -3
  74. package/src/lib/utils/embeds/embeds.test.ts +16 -34
  75. package/src/test/bookmark-shapes.test.ts +129 -7
  76. package/src/test/customSnapping.test.tsx +55 -11
  77. package/tldraw.css +7 -2
@@ -5,6 +5,7 @@ import {
5
5
  Editor,
6
6
  HALF_PI,
7
7
  PageRecordType,
8
+ Result,
8
9
  TLBookmarkShape,
9
10
  TLEmbedShape,
10
11
  TLFrameShape,
@@ -24,6 +25,7 @@ import {
24
25
  useMaybeEditor,
25
26
  } from '@tldraw/editor'
26
27
  import * as React from 'react'
28
+ import { createBookmarkFromUrl } from '../../shapes/bookmark/bookmarks'
27
29
  import { fitFrameToContent, removeFrame } from '../../utils/frames/frames'
28
30
  import { generateShapeAnnouncementMessage } from '../components/A11y'
29
31
  import { EditLinkDialog } from '../components/EditLinkDialog'
@@ -387,45 +389,39 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
387
389
  {
388
390
  id: 'convert-to-bookmark',
389
391
  label: 'action.convert-to-bookmark',
390
- onSelect(source) {
392
+ async onSelect(source) {
391
393
  if (!canApplySelectionAction()) return
392
394
  if (mustGoBackToSelectToolFirst()) return
393
395
 
394
- editor.run(() => {
395
- trackEvent('convert-to-bookmark', { source })
396
- const shapes = editor.getSelectedShapes()
396
+ trackEvent('convert-to-bookmark', { source })
397
+ const shapes = editor.getSelectedShapes()
397
398
 
398
- const createList: TLShapePartial[] = []
399
- const deleteList: TLShapeId[] = []
400
- for (const shape of shapes) {
401
- if (!shape || !editor.isShapeOfType<TLEmbedShape>(shape, 'embed') || !shape.props.url)
402
- continue
399
+ const markId = editor.markHistoryStoppingPoint('convert shapes to bookmark')
403
400
 
404
- const newPos = new Vec(shape.x, shape.y)
405
- newPos.rot(-shape.rotation)
406
- newPos.add(new Vec(shape.props.w / 2 - 300 / 2, shape.props.h / 2 - 320 / 2)) // see bookmark shape util
407
- newPos.rot(shape.rotation)
408
- const partial: TLShapePartial<TLBookmarkShape> = {
409
- id: createShapeId(),
410
- type: 'bookmark',
411
- rotation: shape.rotation,
412
- x: newPos.x,
413
- y: newPos.y,
414
- opacity: 1,
415
- props: {
416
- url: shape.props.url,
417
- },
418
- }
401
+ const creationPromises: Promise<Result<any, any>>[] = []
419
402
 
420
- createList.push(partial)
421
- deleteList.push(shape.id)
422
- }
403
+ for (const shape of shapes) {
404
+ if (!shape || !editor.isShapeOfType<TLEmbedShape>(shape, 'embed') || !shape.props.url)
405
+ continue
423
406
 
424
- editor.markHistoryStoppingPoint('convert shapes to bookmark')
407
+ const center = editor.getShapePageBounds(shape)?.center
425
408
 
426
- // Should be able to create the shape since we're about to delete the other other
427
- editor.deleteShapes(deleteList)
428
- editor.createShapes(createList)
409
+ if (!center) continue
410
+ editor.deleteShapes([shape.id])
411
+
412
+ creationPromises.push(
413
+ createBookmarkFromUrl(editor, { url: shape.props.url, center }).then((res) => {
414
+ if (!res.ok) {
415
+ throw new Error(res.error)
416
+ }
417
+ return res
418
+ })
419
+ )
420
+ }
421
+
422
+ await Promise.all(creationPromises).catch((error) => {
423
+ editor.bailToMark(markId)
424
+ console.error(error)
429
425
  })
430
426
  },
431
427
  },
@@ -1,9 +1,9 @@
1
1
  // This file is automatically generated by internal/scripts/refresh-assets.ts.
2
2
  // Do not edit manually. Or do, I'm a comment, not a cop.
3
3
 
4
- export const version = '4.1.0-canary.e653ec63c99b'
4
+ export const version = '4.1.0-canary.f2f81cd6fe2c'
5
5
  export const publishDates = {
6
6
  major: '2025-09-18T14:39:22.803Z',
7
- minor: '2025-10-02T09:36:59.846Z',
8
- patch: '2025-10-02T09:36:59.846Z',
7
+ minor: '2025-10-14T14:24:23.105Z',
8
+ patch: '2025-10-14T14:24:23.105Z',
9
9
  }
@@ -275,6 +275,14 @@ const MATCH_URL_TEST_URLS: (MatchUrlTestNoMatchDef | MatchUrlTestMatchDef)[] = [
275
275
  embedUrl: `https://replit.com/@omar/Blob-Generator?embed=true`,
276
276
  },
277
277
  },
278
+ {
279
+ url: 'https://replit.com/@omar/Blob-Generator#index.html',
280
+ match: true,
281
+ output: {
282
+ type: 'replit',
283
+ embedUrl: `https://replit.com/@omar/Blob-Generator?embed=true#index.html`,
284
+ },
285
+ },
278
286
  {
279
287
  url: 'https://replit.com/foobar',
280
288
  match: false,
@@ -347,23 +355,6 @@ const MATCH_URL_TEST_URLS: (MatchUrlTestNoMatchDef | MatchUrlTestMatchDef)[] = [
347
355
  url: 'https://vimeo.com/foobar',
348
356
  match: false,
349
357
  },
350
- // excalidraw
351
- {
352
- url: 'https://excalidraw.com/#room=asdkjashdkjhaskdjh,sadkjhakjshdkjahd',
353
- match: true,
354
- output: {
355
- type: 'excalidraw',
356
- embedUrl: `https://excalidraw.com/#room=asdkjashdkjhaskdjh,sadkjhakjshdkjahd`,
357
- },
358
- },
359
- {
360
- url: 'https://excalidraw.com',
361
- match: false,
362
- },
363
- {
364
- url: 'https://excalidraw.com/help',
365
- match: false,
366
- },
367
358
  //desmos
368
359
  {
369
360
  url: 'https://www.desmos.com/calculator/js9hryvejc',
@@ -599,6 +590,14 @@ const MATCH_EMBED_TEST_URLS: (MatchEmbedTestMatchDef | MatchEmbedTestNoMatchDef)
599
590
  url: `https://replit.com/@omar/Blob-Generator`,
600
591
  },
601
592
  },
593
+ {
594
+ embedUrl: 'https://replit.com/@omar/Blob-Generator?embed=true#index.html',
595
+ match: true,
596
+ output: {
597
+ type: 'replit',
598
+ url: `https://replit.com/@omar/Blob-Generator#index.html`,
599
+ },
600
+ },
602
601
  {
603
602
  embedUrl: 'https://replit.com/@omar/Blob-Generator',
604
603
  match: false,
@@ -671,23 +670,6 @@ const MATCH_EMBED_TEST_URLS: (MatchEmbedTestMatchDef | MatchEmbedTestNoMatchDef)
671
670
  embedUrl: 'https://vimeo.com/foobar',
672
671
  match: false,
673
672
  },
674
- // excalidraw
675
- {
676
- embedUrl: 'https://excalidraw.com/#room=asdkjashdkjhaskdjh,sadkjhakjshdkjahd',
677
- match: true,
678
- output: {
679
- type: 'excalidraw',
680
- url: `https://excalidraw.com/#room=asdkjashdkjhaskdjh,sadkjhakjshdkjahd`,
681
- },
682
- },
683
- {
684
- embedUrl: 'https://excalidraw.com',
685
- match: false,
686
- },
687
- {
688
- embedUrl: 'https://excalidraw.com/help',
689
- match: false,
690
- },
691
673
  // desmos
692
674
  {
693
675
  embedUrl: 'https://www.desmos.com/calculator/js9hryvejc?embed',
@@ -1,5 +1,6 @@
1
1
  import { TLBookmarkShape, createShapeId } from '@tldraw/editor'
2
- import { getHumanReadableAddress } from '../lib/shapes/bookmark/BookmarkShapeUtil'
2
+ import { vi } from 'vitest'
3
+ import { createBookmarkFromUrl, getHumanReadableAddress } from '../lib/shapes/bookmark/bookmarks'
3
4
  import { TestEditor } from './TestEditor'
4
5
 
5
6
  let editor: TestEditor
@@ -74,12 +75,12 @@ describe('The URL formatter', () => {
74
75
  const e = editor.getShape<TLBookmarkShape>(ids.e)!
75
76
  const f = editor.getShape<TLBookmarkShape>(ids.f)!
76
77
 
77
- expect(getHumanReadableAddress(a)).toBe('github.com')
78
- expect(getHumanReadableAddress(b)).toBe('github.com')
79
- expect(getHumanReadableAddress(c)).toBe('github.com')
80
- expect(getHumanReadableAddress(d)).toBe('github.com')
81
- expect(getHumanReadableAddress(e)).toBe('github.com')
82
- expect(getHumanReadableAddress(f)).toBe('github.com')
78
+ expect(getHumanReadableAddress(a.props.url)).toBe('github.com')
79
+ expect(getHumanReadableAddress(b.props.url)).toBe('github.com')
80
+ expect(getHumanReadableAddress(c.props.url)).toBe('github.com')
81
+ expect(getHumanReadableAddress(d.props.url)).toBe('github.com')
82
+ expect(getHumanReadableAddress(e.props.url)).toBe('github.com')
83
+ expect(getHumanReadableAddress(f.props.url)).toBe('github.com')
83
84
  })
84
85
 
85
86
  it("Doesn't resize bookmarks", () => {
@@ -132,3 +133,124 @@ describe('The URL formatter', () => {
132
133
  expect(newBookmark.props.h).toBe(320)
133
134
  })
134
135
  })
136
+
137
+ describe('createBookmarkFromUrl', () => {
138
+ it('creates a bookmark shape with unfurled metadata', async () => {
139
+ const url = 'https://example.com'
140
+ const center = { x: 100, y: 200 }
141
+
142
+ // Mock the asset creation to return a test asset
143
+ const mockAsset = {
144
+ id: 'asset:test-asset-id' as any,
145
+ typeName: 'asset' as const,
146
+ type: 'bookmark' as const,
147
+ props: {
148
+ src: url,
149
+ title: 'Example Site',
150
+ description: 'An example website',
151
+ image: 'https://example.com/image.jpg',
152
+ favicon: 'https://example.com/favicon.ico',
153
+ },
154
+ meta: {},
155
+ }
156
+
157
+ // Mock the getAssetForExternalContent method
158
+ vi.spyOn(editor, 'getAssetForExternalContent').mockResolvedValue(mockAsset)
159
+
160
+ const result = await createBookmarkFromUrl(editor, { url, center })
161
+
162
+ assert(result.ok, 'Failed to create bookmark')
163
+ const shape = result.value
164
+ expect(shape.type).toBe('bookmark')
165
+ expect(shape.props.url).toBe(url)
166
+ expect(shape.props.assetId).toBe('asset:test-asset-id')
167
+ expect(shape.props.w).toBe(300)
168
+ expect(shape.props.h).toBe(320)
169
+ expect(shape.x).toBe(center.x - 150) // BOOKMARK_WIDTH / 2
170
+ expect(shape.y).toBe(center.y - 160) // BOOKMARK_HEIGHT / 2
171
+
172
+ // Verify the shape was created in the editor
173
+ const createdShape = editor.getShape(result.value.id)
174
+ expect(createdShape).toBeDefined()
175
+ expect(createdShape?.type).toBe('bookmark')
176
+
177
+ // Verify the asset was created
178
+ const createdAsset = editor.getAsset('asset:test-asset-id' as any)
179
+ expect(createdAsset).toBeDefined()
180
+ expect(createdAsset?.type).toBe('bookmark')
181
+ })
182
+
183
+ it('creates a bookmark shape with default center when no center provided', async () => {
184
+ const url = 'https://example.com'
185
+ const viewportCenter = { x: 500, y: 300 }
186
+
187
+ // Mock getViewportPageBounds to return a known center
188
+ vi.spyOn(editor, 'getViewportPageBounds').mockReturnValue({
189
+ x: 0,
190
+ y: 0,
191
+ w: 1000,
192
+ h: 600,
193
+ center: viewportCenter,
194
+ } as any)
195
+
196
+ const mockAsset = {
197
+ id: 'asset:test-asset-id' as any,
198
+ typeName: 'asset' as const,
199
+ type: 'bookmark' as const,
200
+ props: {
201
+ src: url,
202
+ title: 'Example Site',
203
+ description: 'An example website',
204
+ image: '',
205
+ favicon: '',
206
+ },
207
+ meta: {},
208
+ }
209
+
210
+ vi.spyOn(editor, 'getAssetForExternalContent').mockResolvedValue(mockAsset)
211
+
212
+ const result = await createBookmarkFromUrl(editor, { url })
213
+
214
+ assert(result.ok, 'Failed to create bookmark')
215
+ const shape = result.value
216
+ expect(shape.x).toBe(viewportCenter.x - 150)
217
+ expect(shape.y).toBe(viewportCenter.y - 160)
218
+ })
219
+
220
+ it('handles asset creation failure gracefully', async () => {
221
+ const url = 'https://invalid-url.com'
222
+ const center = { x: 100, y: 200 }
223
+
224
+ // Mock the asset creation to fail
225
+ vi.spyOn(editor, 'getAssetForExternalContent').mockRejectedValue(new Error('Failed to fetch'))
226
+
227
+ const result = await createBookmarkFromUrl(editor, { url, center })
228
+
229
+ assert(!result.ok, 'Failed to create bookmark')
230
+ expect(result.error).toBe('Failed to fetch')
231
+
232
+ // Verify no shape was created
233
+ const shapes = editor.getCurrentPageShapes()
234
+ expect(shapes).toHaveLength(0)
235
+ })
236
+
237
+ it('creates bookmark shape even when asset creation returns null', async () => {
238
+ const url = 'https://example.com'
239
+ const center = { x: 100, y: 200 }
240
+
241
+ // Mock the asset creation to return null
242
+ vi.spyOn(editor, 'getAssetForExternalContent').mockResolvedValue(null as any)
243
+
244
+ const result = await createBookmarkFromUrl(editor, { url, center })
245
+
246
+ assert(result.ok, 'Failed to create bookmark')
247
+ const shape = result.value
248
+ expect(shape.type).toBe('bookmark')
249
+ expect(shape.props.url).toBe(url)
250
+ expect(shape.props.assetId).toBe(null)
251
+
252
+ // Verify the shape was created
253
+ const createdShape = editor.getShape(result.value.id)
254
+ expect(createdShape).toBeDefined()
255
+ })
256
+ })
@@ -173,6 +173,7 @@ describe('custom handle snapping', () => {
173
173
  handlePoints: VecModel[] | 'default'
174
174
  selfSnapOutline: VecModel[] | 'default'
175
175
  selfSnapPoints: VecModel[] | 'default'
176
+ handleSnapType?: 'point' | 'align'
176
177
  }
177
178
  >
178
179
  class TestShapeUtil extends BaseBoxShapeUtil<TestShape> {
@@ -213,17 +214,23 @@ describe('custom handle snapping', () => {
213
214
  }
214
215
  }
215
216
  override getHandles(shape: TestShape): TLHandle[] {
216
- return [
217
- {
218
- id: 'handle',
219
- label: 'handle',
220
- type: 'vertex',
221
- x: shape.props.ownHandle.x,
222
- y: shape.props.ownHandle.y,
223
- index: ZERO_INDEX_KEY,
224
- canSnap: true,
225
- },
226
- ]
217
+ const handle: TLHandle = {
218
+ id: 'handle',
219
+ label: 'handle',
220
+ type: 'vertex',
221
+ x: shape.props.ownHandle.x,
222
+ y: shape.props.ownHandle.y,
223
+ index: ZERO_INDEX_KEY,
224
+ }
225
+
226
+ if (shape.props.handleSnapType) {
227
+ handle.snapType = shape.props.handleSnapType
228
+ } else {
229
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
230
+ handle.canSnap = true
231
+ }
232
+
233
+ return [handle]
227
234
  }
228
235
  override onHandleDrag(shape: TestShape, { handle }: TLHandleDragInfo<TestShape>) {
229
236
  return { ...shape, props: { ...shape.props, ownHandle: { x: handle.x, y: handle.y } } }
@@ -495,5 +502,42 @@ describe('custom handle snapping', () => {
495
502
  expect(ownHandlePosition()).toMatchObject({ x: 20, y: 50 })
496
503
  })
497
504
  })
505
+
506
+ describe('with snapType set to align', () => {
507
+ beforeEach(() => {
508
+ editor.updateShape<TestShape>({
509
+ id: ids.test,
510
+ type: 'test',
511
+ props: {
512
+ selfSnapPoints: [
513
+ { x: 20, y: 50 },
514
+ { x: 60, y: 10 },
515
+ ],
516
+ handleSnapType: 'align',
517
+ },
518
+ })
519
+ })
520
+
521
+ test('snaps to the y axis', () => {
522
+ startDraggingOwnHandle()
523
+ editor.pointerMove(18, 0, undefined, { ctrlKey: true })
524
+ expect(editor.snaps.getIndicators()).toHaveLength(1)
525
+ expect(ownHandlePosition()).toMatchObject({ x: 20, y: 0 })
526
+ })
527
+
528
+ test('snaps to the x axis', () => {
529
+ startDraggingOwnHandle()
530
+ editor.pointerMove(0, 48, undefined, { ctrlKey: true })
531
+ expect(editor.snaps.getIndicators()).toHaveLength(1)
532
+ expect(ownHandlePosition()).toMatchObject({ x: 0, y: 50 })
533
+ })
534
+
535
+ test('snaps to both axes', () => {
536
+ startDraggingOwnHandle()
537
+ editor.pointerMove(18, 9, undefined, { ctrlKey: true })
538
+ expect(editor.snaps.getIndicators()).toHaveLength(2)
539
+ expect(ownHandlePosition()).toMatchObject({ x: 20, y: 10 })
540
+ })
541
+ })
498
542
  })
499
543
  })
package/tldraw.css CHANGED
@@ -1325,6 +1325,10 @@ input,
1325
1325
  flex: 1;
1326
1326
  }
1327
1327
 
1328
+ .tl-bookmark__copy_container:has(.tl-bookmark__link:only-child) {
1329
+ justify-content: center;
1330
+ }
1331
+
1328
1332
  .tl-bookmark__heading,
1329
1333
  .tl-bookmark__description,
1330
1334
  .tl-bookmark__link {
@@ -1357,7 +1361,7 @@ input,
1357
1361
  line-clamp: 3;
1358
1362
  text-overflow: ellipsis;
1359
1363
  display: -webkit-box;
1360
- color: var(--tl-color-text-2);
1364
+ color: var(--tl-color-text-1);
1361
1365
  margin: var(--tl-space-2) 0px;
1362
1366
  }
1363
1367
 
@@ -1369,11 +1373,12 @@ input,
1369
1373
  font-size: 12px;
1370
1374
  pointer-events: all;
1371
1375
  display: flex;
1372
- color: var(--tl-color-text-2);
1376
+ color: var(--tl-color-text-1);
1373
1377
  align-items: center;
1374
1378
  cursor: var(--tl-cursor-pointer);
1375
1379
  width: fit-content;
1376
1380
  max-width: 100%;
1381
+ text-decoration: none;
1377
1382
  }
1378
1383
 
1379
1384
  .tl-bookmark__link > span {