tldraw
Version:
A tiny little drawing editor.
757 lines (748 loc) • 19.1 kB
text/typescript
import { DEFAULT_EMBED_DEFINITIONS } from '../../defaultEmbedDefinitions'
import { getEmbedInfo, matchEmbedUrl, matchUrl } from './embeds'
interface MatchUrlTestMatchDef {
url: string
match: true
output: {
type: string
embedUrl: string
}
}
interface MatchUrlTestNoMatchDef {
url: string
match: false
}
const MATCH_URL_TEST_URLS: (MatchUrlTestNoMatchDef | MatchUrlTestMatchDef)[] = [
// tldraw
{
url: 'https://beta.tldraw.com/r/choochoo',
match: true,
output: {
type: 'tldraw',
embedUrl: 'https://beta.tldraw.com/r/choochoo',
},
},
{
url: 'https://beta.tldraw.com/',
match: false,
},
{
url: 'https://beta.tldraw.com/r/invalid/room',
match: false,
},
{
url: 'https://beta.tldraw.com/something',
match: false,
},
{
url: 'https://lite.tldraw.com/r/choochoo',
match: true,
output: {
type: 'tldraw',
embedUrl: 'https://lite.tldraw.com/r/choochoo',
},
},
{
url: 'https://lite.tldraw.com/',
match: false,
},
{
url: 'https://lite.tldraw.com/something',
match: false,
},
// codesandbox
{
url: 'https://codesandbox.io/s/breathing-dots-checkpoint-7-nor86',
match: true,
output: {
type: 'codesandbox',
embedUrl: 'https://codesandbox.io/embed/breathing-dots-checkpoint-7-nor86',
},
},
{
url: 'https://codesandbox.io/foobar',
match: false,
},
// codepen
{
url: 'https://codepen.io/Rplus/pen/PWZYRM',
match: true,
output: {
type: 'codepen',
embedUrl: 'https://codepen.io/Rplus/embed/PWZYRM',
},
},
{
url: 'https://codepen.io/foobar',
match: false,
},
// scratch
{
url: 'https://scratch.mit.edu/projects/97246945',
match: true,
output: {
type: 'scratch',
embedUrl: 'https://scratch.mit.edu/projects/embed/97246945',
},
},
{
url: 'https://scratch.mit.edu/projects',
match: false,
},
// youtube
{
url: 'https://www.youtube.com/watch?v=ZMklf0vUl18',
match: true,
output: {
type: 'youtube',
embedUrl: 'https://www.youtube.com/embed/ZMklf0vUl18',
},
},
{
url: 'https://www.youtube.com/watch?v=ZMklf0vUl18&t=62',
match: true,
output: {
type: 'youtube',
embedUrl: 'https://www.youtube.com/embed/ZMklf0vUl18?start=62',
},
},
{
url: 'https://m.youtube.com/watch?v=ZMklf0vUl18',
match: true,
output: {
type: 'youtube',
embedUrl: 'https://www.youtube.com/embed/ZMklf0vUl18',
},
},
{
url: 'https://youtu.be/ZMklf0vUl18',
match: true,
output: {
type: 'youtube',
embedUrl: 'https://www.youtube.com/embed/ZMklf0vUl18',
},
},
{
url: 'https://youtu.be/u1016UnJIgA?feature=shared&t=16',
match: true,
output: {
type: 'youtube',
embedUrl: 'https://www.youtube.com/embed/u1016UnJIgA?feature=shared&start=16',
},
},
{
url: 'https://www.youtube.com/feed/subscriptions',
match: false,
},
// TODO: Mapbox
// figma
{
url: 'https://www.figma.com/file/Xj2Uly2KctVDGdXszHWRcF/Untitled?node-id=0%3A1&t=9O1ocaU18YZ0DoVF-1',
match: true,
output: {
type: 'figma',
embedUrl:
'https://www.figma.com/embed?embed_host=share&url=https://www.figma.com/file/Xj2Uly2KctVDGdXszHWRcF/Untitled?node-id=0%3A1&t=9O1ocaU18YZ0DoVF-1',
},
},
{
url: 'https://www.figma.com/design/c1U7U2I1XfUITXwpr8X0GI/tldraw-dotcom-2025?node-id=556-72935&t=5pTQLNmuvTf3OMXd-4',
match: true,
output: {
type: 'figma',
embedUrl:
'https://www.figma.com/embed?embed_host=share&url=https://www.figma.com/design/c1U7U2I1XfUITXwpr8X0GI/tldraw-dotcom-2025?node-id=556-72935&t=5pTQLNmuvTf3OMXd-4',
},
},
{
url: 'https://www.figma.com/proto/c1U7U2I1XfUITXwpr8X0GI/tldraw-dotcom-2025?page-id=0%3A1&node-id=556-72935&t=5pTQLNmuvTf3OMXd-0&scaling=min-zoom&content-scaling=fixed',
match: true,
output: {
type: 'figma',
embedUrl:
'https://www.figma.com/embed?embed_host=share&url=https://www.figma.com/proto/c1U7U2I1XfUITXwpr8X0GI/tldraw-dotcom-2025?page-id=0%3A1&node-id=556-72935&t=5pTQLNmuvTf3OMXd-0&scaling=min-zoom&content-scaling=fixed',
},
},
{
url: 'https://www.figma.com/foobar',
match: false,
},
// google_maps
{
url: 'https://www.google.com/maps/@52.2449313,0.0813192,14z',
match: true,
output: {
type: 'google_maps',
embedUrl: `https://google.com/maps/embed/v1/view?key=${process.env.NEXT_PUBLIC_GC_API_KEY}¢er=52.2449313,0.0813192&zoom=14&maptype=roadmap`,
},
},
{
url: 'https://www.google.co.uk/maps/@52.2449313,0.0813192,14z',
match: true,
output: {
type: 'google_maps',
embedUrl: `https://google.co.uk/maps/embed/v1/view?key=${process.env.NEXT_PUBLIC_GC_API_KEY}¢er=52.2449313,0.0813192&zoom=14&maptype=roadmap`,
},
},
{
url: `https://www.google.com/maps/place/Shepherd's+Bush,+London/@51.5041626,-0.2468738,14z/data=!4m15!1m8!3m7!1s0x48760e1ce753774f:0x4420ec29705422c7!2sActon,+London!3b1!8m2!3d51.508372!4d-0.27444!16zL20vMG44cm0!3m5!1s0x48760fd28997cb07:0x6c79a6e5e0483766!8m2!3d51.5051913!4d-0.22469!16zL20vMDFqMTJo?entry=ttu&g_ep=EgoyMDI0MTIxMS4wIKXMDSoASAFQAw%3D%3D`,
match: true,
output: {
type: 'google_maps',
embedUrl: `https://google.com/maps/embed/v1/view?key=${process.env.NEXT_PUBLIC_GC_API_KEY}¢er=51.5041626,-0.2468738&zoom=14&maptype=roadmap`,
},
},
{
url: `https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d2480.1159099846072!2d-0.11034668719177695!3d51.566108606294414!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x48761b96b188bb1b%3A0x9d8d2ab7a55d095e!2stldraw!5e0!3m2!1sen!2suk!4v1734706216129!5m2!1sen!2suk`,
match: true,
output: {
type: 'google_maps',
embedUrl: `https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d2480.1159099846072!2d-0.11034668719177695!3d51.566108606294414!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x48761b96b188bb1b%3A0x9d8d2ab7a55d095e!2stldraw!5e0!3m2!1sen!2suk!4v1734706216129!5m2!1sen!2suk`,
},
},
{
url: 'https://www.google.com/maps/@52.2449313,0.0813192,1000m',
match: true,
output: {
type: 'google_maps',
embedUrl: `https://google.com/maps/embed/v1/view?key=${process.env.NEXT_PUBLIC_GC_API_KEY}¢er=52.2449313,0.0813192&zoom=17.313261121327326&maptype=satellite`,
},
},
{
url: 'https://www.google.co.uk/maps/@51.5074,0.1278,1200m',
match: true,
output: {
type: 'google_maps',
embedUrl: `https://google.co.uk/maps/embed/v1/view?key=${process.env.NEXT_PUBLIC_GC_API_KEY}¢er=51.5074,0.1278&zoom=16.984468114035085&maptype=satellite`,
},
},
{
url: 'https://www.google.com/maps/timeline',
match: false,
},
// google_calendar
{
url: 'https://calendar.google.com/calendar/u/0?cid=FOOBAR',
match: true,
output: {
type: 'google_calendar',
embedUrl: `https://calendar.google.com/calendar/embed?src=FOOBAR`,
},
},
{
url: 'https://calendar.google.com/calendar/u?cid=FOOBAR',
match: false,
},
// google_slides
{
url: 'https://docs.google.com/presentation/d/e/2PACX-1vSjHt04pIKxS9JgjPfeIDWLq1GPDZxG-8HZwl_q5m_booZhVrB7FbogTxz6tYnMHgsyx3c8oQ72iRsb/pub?start=false&loop=false&delayms=3000',
match: true,
output: {
type: 'google_slides',
embedUrl: `https://docs.google.com/presentation/d/e/2PACX-1vSjHt04pIKxS9JgjPfeIDWLq1GPDZxG-8HZwl_q5m_booZhVrB7FbogTxz6tYnMHgsyx3c8oQ72iRsb/embed`,
},
},
{
url: 'https://docs.google.com/presentation',
match: false,
},
// gist
{
url: 'https://gist.github.com/steveruizok/3cf65a8545719674dcd48097ada5837e',
match: true,
output: {
type: 'github_gist',
embedUrl: `https://gist.github.com/steveruizok/3cf65a8545719674dcd48097ada5837e`,
},
},
{
url: 'https://gist.github.com/discover',
match: false,
},
{
url: 'https://gist.github.com/x/ixSly/98210ba6e8683bd772e857128cd3cdca.json?callback=prompt&x=ixSly',
match: false,
},
// replit
{
url: 'https://replit.com/@omar/Blob-Generator',
match: true,
output: {
type: 'replit',
embedUrl: `https://replit.com/@omar/Blob-Generator?embed=true`,
},
},
{
url: 'https://replit.com/foobar',
match: false,
},
// observable
{
url: 'https://observablehq.com/d/0d995c9fce64461c',
match: true,
output: {
type: 'observable',
embedUrl: `https://observablehq.com/embed/0d995c9fce64461c?cell=*`,
},
},
{
url: 'https://observablehq.com/@jamie-tldraw-workspace/input-chart',
match: true,
output: {
type: 'observable',
embedUrl: `https://observablehq.com/embed/@jamie-tldraw-workspace/input-chart?cell=*`,
},
},
{
url: 'https://observablehq.com/foobar',
match: false,
},
// felt
{
url: 'https://felt.com/map/Tutorial-Learn-to-Use-Felt-TAyrWamqQTqYCQfFNGw9AnC',
match: true,
output: {
type: 'felt',
embedUrl: `https://felt.com/embed/map/Tutorial-Learn-to-Use-Felt-TAyrWamqQTqYCQfFNGw9AnC`,
},
},
{
url: 'https://felt.com/foobar',
match: false,
},
// spotify
{
url: 'https://open.spotify.com/artist/7r9FLm0Rxp8u9tfSZhFxtL',
match: true,
output: {
type: 'spotify',
embedUrl: `https://open.spotify.com/embed/artist/7r9FLm0Rxp8u9tfSZhFxtL`,
},
},
{
url: 'https://open.spotify.com/album/7AiWUyeSs0ICu0qqe0wowa',
match: true,
output: {
type: 'spotify',
embedUrl: `https://open.spotify.com/embed/album/7AiWUyeSs0ICu0qqe0wowa`,
},
},
{
url: 'https://open.spotify.com/foobar',
match: false,
},
// vimeo
{
url: 'https://vimeo.com/59749737',
match: true,
output: {
type: 'vimeo',
embedUrl: `https://player.vimeo.com/video/59749737?title=0&byline=0`,
},
},
{
url: 'https://vimeo.com/foobar',
match: false,
},
// excalidraw
{
url: 'https://excalidraw.com/#room=asdkjashdkjhaskdjh,sadkjhakjshdkjahd',
match: true,
output: {
type: 'excalidraw',
embedUrl: `https://excalidraw.com/#room=asdkjashdkjhaskdjh,sadkjhakjshdkjahd`,
},
},
{
url: 'https://excalidraw.com',
match: false,
},
{
url: 'https://excalidraw.com/help',
match: false,
},
//desmos
{
url: 'https://www.desmos.com/calculator/js9hryvejc',
match: true,
output: {
type: 'desmos',
embedUrl: 'https://www.desmos.com/calculator/js9hryvejc?embed',
},
},
{
url: 'https://www.desmos.com/calculator',
match: false,
},
{
url: 'https://www.desmos.com/calculator/js9hryvejc?foobar',
match: false,
},
]
interface MatchEmbedTestMatchDef {
embedUrl: string
match: true
output: {
type: string
url: string
}
}
interface MatchEmbedTestNoMatchDef {
embedUrl: string
match: false
}
const MATCH_EMBED_TEST_URLS: (MatchEmbedTestMatchDef | MatchEmbedTestNoMatchDef)[] = [
// tldraw
{
embedUrl: 'https://beta.tldraw.com/r/choochoo',
match: true,
output: {
type: 'tldraw',
url: 'https://beta.tldraw.com/r/choochoo',
},
},
{
embedUrl: 'https://beta.tldraw.com/',
match: false,
},
{
embedUrl: 'https://beta.tldraw.com/r/invalid/room',
match: false,
},
{
embedUrl: 'https://beta.tldraw.com/something',
match: false,
},
{
embedUrl: 'https://lite.tldraw.com/r/choochoo',
match: true,
output: {
type: 'tldraw',
url: 'https://lite.tldraw.com/r/choochoo',
},
},
{
embedUrl: 'https://lite.tldraw.com/',
match: false,
},
{
embedUrl: 'https://lite.tldraw.com/something',
match: false,
},
// codesandbox
{
embedUrl: 'https://codesandbox.io/embed/breathing-dots-checkpoint-7-nor86',
match: true,
output: {
type: 'codesandbox',
url: 'https://codesandbox.io/s/breathing-dots-checkpoint-7-nor86',
},
},
{
embedUrl: 'https://codesandbox.io/breathing-dots-checkpoint-7-nor86',
match: false,
},
// codepen
{
embedUrl: 'https://codepen.io/Rplus/embed/PWZYRM',
match: true,
output: {
type: 'codepen',
url: 'https://codepen.io/Rplus/pen/PWZYRM',
},
},
{
embedUrl: 'https://codepen.io/Rplus/PWZYRM',
match: false,
},
// scratch
{
embedUrl: 'https://scratch.mit.edu/projects/embed/97246945',
match: true,
output: {
type: 'scratch',
url: 'https://scratch.mit.edu/projects/97246945',
},
},
{
embedUrl: 'https://scratch.mit.edu/projects/97246945',
match: false,
},
// youtube
{
embedUrl: 'https://www.youtube.com/embed/ZMklf0vUl18',
match: true,
output: {
type: 'youtube',
url: 'https://www.youtube.com/watch?v=ZMklf0vUl18',
},
},
{
embedUrl: 'https://www.youtube.com/embed/q8KyQovatd0?loop=1&playlist=q8KyQovatd0',
match: true,
output: {
type: 'youtube',
url: 'https://www.youtube.com/watch?loop=1&playlist=q8KyQovatd0&v=q8KyQovatd0',
},
},
{
embedUrl: 'https://www.youtube.com/embed/u1016UnJIgA?start=16',
match: true,
output: {
type: 'youtube',
url: 'https://www.youtube.com/watch?v=u1016UnJIgA&t=16',
},
},
{
embedUrl: 'https://www.youtube.com/embed/',
match: false,
},
// TODO: Mapbox
// figma
{
embedUrl:
'https://www.figma.com/embed?embed_host=share&url=https://www.figma.com/file/Xj2Uly2KctVDGdXszHWRcF/Untitled?node-id=0%3A1&t=9O1ocaU18YZ0DoVF-1',
match: true,
output: {
type: 'figma',
url: 'https://www.figma.com/file/Xj2Uly2KctVDGdXszHWRcF/Untitled?node-id=0:1',
},
},
{
embedUrl: 'https://www.figma.com/embed?foobar=baz',
match: false,
},
// google_maps
{
embedUrl: `https://google.com/maps/embed/v1/view?key=${process.env.NEXT_PUBLIC_GC_API_KEY}¢er=52.2449313,0.0813192&zoom=14`,
match: true,
output: {
type: 'google_maps',
url: 'https://www.google.com/maps/@52.2449313,0.0813192,14z',
},
},
{
embedUrl: `https://google.com/maps/embed/v1/view?key=${process.env.NEXT_PUBLIC_GC_API_KEY}¢er=52.2449313,0.0813192&zoom=14&maptype=satellite`,
match: true,
output: {
type: 'google_maps',
url: 'https://www.google.com/maps/@52.2449313,0.0813192,6279.322445186111m',
},
},
{
embedUrl: `https://google.co.uk/maps/embed/v1/view?key=${process.env.NEXT_PUBLIC_GC_API_KEY}¢er=51.5074,0.1278&zoom=12&maptype=roadmap`,
match: true,
output: {
type: 'google_maps',
url: 'https://www.google.com/maps/@51.5074,0.1278,12z',
},
},
{
embedUrl:
'https://google.com/maps/embed?key=${process.env.NEXT_PUBLIC_GC_API_KEY}¢er=52.2449313,0.0813192&zoom=14',
match: false,
},
// google_calendar
{
embedUrl: `https://calendar.google.com/calendar/embed?src=FOOBAR`,
match: true,
output: {
type: 'google_calendar',
url: 'https://calendar.google.com/calendar/u/0?cid=FOOBAR',
},
},
{
embedUrl: 'https://calendar.google.com/calendar?src=FOOBAR',
match: false,
},
// google_slides
{
embedUrl: `https://docs.google.com/presentation/d/e/2PACX-1vSjHt04pIKxS9JgjPfeIDWLq1GPDZxG-8HZwl_q5m_booZhVrB7FbogTxz6tYnMHgsyx3c8oQ72iRsb/embed`,
match: true,
output: {
type: 'google_slides',
url: 'https://docs.google.com/presentation/d/e/2PACX-1vSjHt04pIKxS9JgjPfeIDWLq1GPDZxG-8HZwl_q5m_booZhVrB7FbogTxz6tYnMHgsyx3c8oQ72iRsb/pub',
},
},
{
embedUrl: 'https://docs.google.com/presentation',
match: false,
},
// gist
{
embedUrl: 'https://gist.github.com/steveruizok/3cf65a8545719674dcd48097ada5837e',
match: true,
output: {
type: 'github_gist',
url: `https://gist.github.com/steveruizok/3cf65a8545719674dcd48097ada5837e`,
},
},
{
embedUrl: 'https://gist.github.com/discover',
match: false,
},
{
embedUrl:
'https://gist.github.com/x/ixSly/98210ba6e8683bd772e857128cd3cdca.json?callback=prompt&x=ixSly',
match: false,
},
// replit
{
embedUrl: 'https://replit.com/@omar/Blob-Generator?embed=true',
match: true,
output: {
type: 'replit',
url: `https://replit.com/@omar/Blob-Generator`,
},
},
{
embedUrl: 'https://replit.com/@omar/Blob-Generator',
match: false,
},
// observable
{
embedUrl: `https://observablehq.com/embed/0d995c9fce64461c?cell=*`,
match: true,
output: {
type: 'observable',
url: 'https://observablehq.com/d/0d995c9fce64461c#cell-*',
},
},
{
embedUrl: `https://observablehq.com/embed/@jamie-tldraw-workspace/input-chart?cell=*`,
match: true,
output: {
type: 'observable',
url: 'https://observablehq.com/@jamie-tldraw-workspace/input-chart#cell-*',
},
},
{
embedUrl: 'https://observablehq.com/embed/f/a',
match: false,
},
// felt
{
embedUrl: 'https://felt.com/embed/map/Tutorial-Learn-to-Use-Felt-TAyrWamqQTqYCQfFNGw9AnC',
match: true,
output: {
type: 'felt',
url: `https://felt.com/map/Tutorial-Learn-to-Use-Felt-TAyrWamqQTqYCQfFNGw9AnC`,
},
},
{
embedUrl: 'https://felt.com/foobar',
match: false,
},
// spotify
{
embedUrl: 'https://open.spotify.com/embed/artist/7r9FLm0Rxp8u9tfSZhFxtL',
match: true,
output: {
type: 'spotify',
url: `https://open.spotify.com/artist/7r9FLm0Rxp8u9tfSZhFxtL`,
},
},
{
embedUrl: 'https://open.spotify.com/embed/album/7AiWUyeSs0ICu0qqe0wowa',
match: true,
output: {
type: 'spotify',
url: `https://open.spotify.com/album/7AiWUyeSs0ICu0qqe0wowa`,
},
},
{
embedUrl: 'https://open.spotify.com/foobar',
match: false,
},
// vimeo
{
embedUrl: 'https://player.vimeo.com/video/59749737?title=0&byline=0',
match: true,
output: {
type: 'vimeo',
url: `https://vimeo.com/59749737`,
},
},
{
embedUrl: 'https://vimeo.com/foobar',
match: false,
},
// excalidraw
{
embedUrl: 'https://excalidraw.com/#room=asdkjashdkjhaskdjh,sadkjhakjshdkjahd',
match: true,
output: {
type: 'excalidraw',
url: `https://excalidraw.com/#room=asdkjashdkjhaskdjh,sadkjhakjshdkjahd`,
},
},
{
embedUrl: 'https://excalidraw.com',
match: false,
},
{
embedUrl: 'https://excalidraw.com/help',
match: false,
},
// desmos
{
embedUrl: 'https://www.desmos.com/calculator/js9hryvejc?embed',
match: true,
output: {
type: 'desmos',
url: 'https://www.desmos.com/calculator/js9hryvejc',
},
},
{
embedUrl: 'https://www.desmos.com/calculator',
match: false,
},
{
embedUrl: 'https://www.desmos.com/calculator/js9hryvejc?foobar',
match: false,
},
]
for (const testDef of MATCH_URL_TEST_URLS) {
test(`matchUrl("${testDef.url}")`, () => {
const result = matchUrl(DEFAULT_EMBED_DEFINITIONS, testDef.url)
if (testDef.match) {
expect(result).toBeDefined()
expect(result?.definition.type).toBe(testDef.output.type)
expect(result?.embedUrl).toBe(testDef.output.embedUrl)
} else {
expect(result).toBeUndefined()
}
})
test(`getEmbedInfo("${testDef.url}")`, () => {
const result = getEmbedInfo(DEFAULT_EMBED_DEFINITIONS, testDef.url)
if (testDef.match) {
expect(result).toBeDefined()
expect(result?.definition.type).toBe(testDef.output.type)
expect(result?.embedUrl).toBe(testDef.output.embedUrl)
} else {
expect(result).toBeUndefined()
}
})
}
for (const testDef of MATCH_EMBED_TEST_URLS) {
test(`matchEmbedUrl("${testDef.embedUrl}")`, () => {
const result = matchEmbedUrl(DEFAULT_EMBED_DEFINITIONS, testDef.embedUrl)
if (testDef.match) {
expect(result).toBeDefined()
expect(result?.definition.type).toBe(testDef.output.type)
expect(result?.url).toBe(testDef.output.url)
} else {
expect(result).toBeUndefined()
}
})
test(`getEmbedInfo("${testDef.embedUrl}")`, () => {
const result = matchEmbedUrl(DEFAULT_EMBED_DEFINITIONS, testDef.embedUrl)
if (testDef.match) {
expect(result).toBeDefined()
expect(result?.definition.type).toBe(testDef.output.type)
expect(result?.url).toBe(testDef.output.url)
} else {
expect(result).toBeUndefined()
}
})
}