[feature] Add val town embed (#1777)
This PR adds val town to tldraw's collection of embeddable things. ### Change Type - [x] `minor` — New feature ### Test Plan 1. Add links to val town, e.g. https://www.val.town/v/steveruizok.mathFact ### Release Notes - (feature) val town
This commit is contained in:
parent
b3186f5881
commit
03514c00c4
7 changed files with 134 additions and 88 deletions
BIN
assets/embed-icons/val_town.png
Normal file
BIN
assets/embed-icons/val_town.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 540 B |
|
@ -19,6 +19,7 @@ import embedIconsReplit from './embed-icons/replit.png'
|
||||||
import embedIconsScratch from './embed-icons/scratch.png'
|
import embedIconsScratch from './embed-icons/scratch.png'
|
||||||
import embedIconsSpotify from './embed-icons/spotify.png'
|
import embedIconsSpotify from './embed-icons/spotify.png'
|
||||||
import embedIconsTldraw from './embed-icons/tldraw.png'
|
import embedIconsTldraw from './embed-icons/tldraw.png'
|
||||||
|
import embedIconsValTown from './embed-icons/val_town.png'
|
||||||
import embedIconsVimeo from './embed-icons/vimeo.png'
|
import embedIconsVimeo from './embed-icons/vimeo.png'
|
||||||
import embedIconsYoutube from './embed-icons/youtube.png'
|
import embedIconsYoutube from './embed-icons/youtube.png'
|
||||||
import fontsMonospace from './fonts/IBMPlexMono-Medium.woff2'
|
import fontsMonospace from './fonts/IBMPlexMono-Medium.woff2'
|
||||||
|
@ -451,6 +452,7 @@ export function getAssetUrlsByImport(opts) {
|
||||||
scratch: formatAssetUrl(embedIconsScratch, opts),
|
scratch: formatAssetUrl(embedIconsScratch, opts),
|
||||||
spotify: formatAssetUrl(embedIconsSpotify, opts),
|
spotify: formatAssetUrl(embedIconsSpotify, opts),
|
||||||
tldraw: formatAssetUrl(embedIconsTldraw, opts),
|
tldraw: formatAssetUrl(embedIconsTldraw, opts),
|
||||||
|
val_town: formatAssetUrl(embedIconsValTown, opts),
|
||||||
vimeo: formatAssetUrl(embedIconsVimeo, opts),
|
vimeo: formatAssetUrl(embedIconsVimeo, opts),
|
||||||
youtube: formatAssetUrl(embedIconsYoutube, opts),
|
youtube: formatAssetUrl(embedIconsYoutube, opts),
|
||||||
},
|
},
|
||||||
|
|
|
@ -236,6 +236,7 @@ export function getAssetUrls(opts) {
|
||||||
scratch: formatAssetUrl('./embed-icons/scratch.png', opts),
|
scratch: formatAssetUrl('./embed-icons/scratch.png', opts),
|
||||||
spotify: formatAssetUrl('./embed-icons/spotify.png', opts),
|
spotify: formatAssetUrl('./embed-icons/spotify.png', opts),
|
||||||
tldraw: formatAssetUrl('./embed-icons/tldraw.png', opts),
|
tldraw: formatAssetUrl('./embed-icons/tldraw.png', opts),
|
||||||
|
val_town: formatAssetUrl('./embed-icons/val_town.png', opts),
|
||||||
vimeo: formatAssetUrl('./embed-icons/vimeo.png', opts),
|
vimeo: formatAssetUrl('./embed-icons/vimeo.png', opts),
|
||||||
youtube: formatAssetUrl('./embed-icons/youtube.png', opts),
|
youtube: formatAssetUrl('./embed-icons/youtube.png', opts),
|
||||||
},
|
},
|
||||||
|
|
1
packages/assets/types.d.ts
vendored
1
packages/assets/types.d.ts
vendored
|
@ -226,6 +226,7 @@ export type AssetUrls = {
|
||||||
scratch: string
|
scratch: string
|
||||||
spotify: string
|
spotify: string
|
||||||
tldraw: string
|
tldraw: string
|
||||||
|
val_town: string
|
||||||
vimeo: string
|
vimeo: string
|
||||||
youtube: string
|
youtube: string
|
||||||
}
|
}
|
||||||
|
|
|
@ -623,6 +623,7 @@ export function getAssetUrlsByMetaUrl(opts) {
|
||||||
scratch: formatAssetUrl(new URL('./embed-icons/scratch.png', import.meta.url).href, opts),
|
scratch: formatAssetUrl(new URL('./embed-icons/scratch.png', import.meta.url).href, opts),
|
||||||
spotify: formatAssetUrl(new URL('./embed-icons/spotify.png', import.meta.url).href, opts),
|
spotify: formatAssetUrl(new URL('./embed-icons/spotify.png', import.meta.url).href, opts),
|
||||||
tldraw: formatAssetUrl(new URL('./embed-icons/tldraw.png', import.meta.url).href, opts),
|
tldraw: formatAssetUrl(new URL('./embed-icons/tldraw.png', import.meta.url).href, opts),
|
||||||
|
val_town: formatAssetUrl(new URL('./embed-icons/val_town.png', import.meta.url).href, opts),
|
||||||
vimeo: formatAssetUrl(new URL('./embed-icons/vimeo.png', import.meta.url).href, opts),
|
vimeo: formatAssetUrl(new URL('./embed-icons/vimeo.png', import.meta.url).href, opts),
|
||||||
youtube: formatAssetUrl(new URL('./embed-icons/youtube.png', import.meta.url).href, opts),
|
youtube: formatAssetUrl(new URL('./embed-icons/youtube.png', import.meta.url).href, opts),
|
||||||
},
|
},
|
||||||
|
|
|
@ -213,6 +213,38 @@ export const EMBED_DEFINITIONS: readonly [{
|
||||||
readonly canUnmount: true;
|
readonly canUnmount: true;
|
||||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||||
|
}, {
|
||||||
|
readonly type: "figma";
|
||||||
|
readonly title: "Figma";
|
||||||
|
readonly hostnames: readonly ["figma.com"];
|
||||||
|
readonly width: 720;
|
||||||
|
readonly height: 500;
|
||||||
|
readonly doesResize: true;
|
||||||
|
readonly canUnmount: true;
|
||||||
|
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||||
|
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||||
|
}, {
|
||||||
|
readonly type: "google_maps";
|
||||||
|
readonly title: "Google Maps";
|
||||||
|
readonly hostnames: readonly ["google.*"];
|
||||||
|
readonly width: 720;
|
||||||
|
readonly height: 500;
|
||||||
|
readonly doesResize: true;
|
||||||
|
readonly canUnmount: false;
|
||||||
|
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||||
|
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||||
|
}, {
|
||||||
|
readonly type: "val_town";
|
||||||
|
readonly title: "Val Town";
|
||||||
|
readonly hostnames: readonly ["val.town"];
|
||||||
|
readonly minWidth: 260;
|
||||||
|
readonly minHeight: 100;
|
||||||
|
readonly width: 720;
|
||||||
|
readonly height: 500;
|
||||||
|
readonly doesResize: true;
|
||||||
|
readonly canUnmount: false;
|
||||||
|
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||||
|
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||||
}, {
|
}, {
|
||||||
readonly type: "codesandbox";
|
readonly type: "codesandbox";
|
||||||
readonly title: "CodeSandbox";
|
readonly title: "CodeSandbox";
|
||||||
|
@ -261,26 +293,6 @@ export const EMBED_DEFINITIONS: readonly [{
|
||||||
readonly isAspectRatioLocked: true;
|
readonly isAspectRatioLocked: true;
|
||||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
readonly toEmbedUrl: (url: string) => string | undefined;
|
||||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
readonly fromEmbedUrl: (url: string) => string | undefined;
|
||||||
}, {
|
|
||||||
readonly type: "figma";
|
|
||||||
readonly title: "Figma";
|
|
||||||
readonly hostnames: readonly ["figma.com"];
|
|
||||||
readonly width: 720;
|
|
||||||
readonly height: 500;
|
|
||||||
readonly doesResize: true;
|
|
||||||
readonly canUnmount: true;
|
|
||||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
|
||||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
|
||||||
}, {
|
|
||||||
readonly type: "google_maps";
|
|
||||||
readonly title: "Google Maps";
|
|
||||||
readonly hostnames: readonly ["google.*"];
|
|
||||||
readonly width: 720;
|
|
||||||
readonly height: 500;
|
|
||||||
readonly doesResize: true;
|
|
||||||
readonly canUnmount: false;
|
|
||||||
readonly toEmbedUrl: (url: string) => string | undefined;
|
|
||||||
readonly fromEmbedUrl: (url: string) => string | undefined;
|
|
||||||
}, {
|
}, {
|
||||||
readonly type: "google_calendar";
|
readonly type: "google_calendar";
|
||||||
readonly title: "Google Calendar";
|
readonly title: "Google Calendar";
|
||||||
|
|
|
@ -40,6 +40,103 @@ export const EMBED_DEFINITIONS = [
|
||||||
return
|
return
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'figma',
|
||||||
|
title: 'Figma',
|
||||||
|
hostnames: ['figma.com'],
|
||||||
|
width: 720,
|
||||||
|
height: 500,
|
||||||
|
doesResize: true,
|
||||||
|
canUnmount: true,
|
||||||
|
toEmbedUrl: (url) => {
|
||||||
|
if (
|
||||||
|
!!url.match(
|
||||||
|
// eslint-disable-next-line no-useless-escape
|
||||||
|
/https:\/\/([\w\.-]+\.)?figma.com\/(file|proto)\/([0-9a-zA-Z]{22,128})(?:\/.*)?$/
|
||||||
|
) &&
|
||||||
|
!url.includes('figma.com/embed')
|
||||||
|
) {
|
||||||
|
return `https://www.figma.com/embed?embed_host=share&url=${url}`
|
||||||
|
}
|
||||||
|
return
|
||||||
|
},
|
||||||
|
fromEmbedUrl: (url) => {
|
||||||
|
const urlObj = safeParseUrl(url)
|
||||||
|
if (urlObj && urlObj.pathname.match(/^\/embed\/?$/)) {
|
||||||
|
const outUrl = urlObj.searchParams.get('url')
|
||||||
|
if (outUrl) {
|
||||||
|
return outUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'google_maps',
|
||||||
|
title: 'Google Maps',
|
||||||
|
hostnames: ['google.*'],
|
||||||
|
width: 720,
|
||||||
|
height: 500,
|
||||||
|
doesResize: true,
|
||||||
|
canUnmount: false,
|
||||||
|
toEmbedUrl: (url) => {
|
||||||
|
if (url.includes('/maps/')) {
|
||||||
|
const match = url.match(/@(.*),(.*),(.*)z/)
|
||||||
|
let result: string
|
||||||
|
if (match) {
|
||||||
|
const [, lat, lng, z] = match
|
||||||
|
const host = new URL(url).host.replace('www.', '')
|
||||||
|
result = `https://${host}/maps/embed/v1/view?key=${process.env.NEXT_PUBLIC_GC_API_KEY}¢er=${lat},${lng}&zoom=${z}`
|
||||||
|
} else {
|
||||||
|
result = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
return
|
||||||
|
},
|
||||||
|
fromEmbedUrl: (url) => {
|
||||||
|
const urlObj = safeParseUrl(url)
|
||||||
|
if (!urlObj) return
|
||||||
|
|
||||||
|
const matches = urlObj.pathname.match(/^\/maps\/embed\/v1\/view\/?$/)
|
||||||
|
if (matches && urlObj.searchParams.has('center') && urlObj.searchParams.get('zoom')) {
|
||||||
|
const zoom = urlObj.searchParams.get('zoom')
|
||||||
|
const [lat, lon] = urlObj.searchParams.get('center')!.split(',')
|
||||||
|
return `https://www.google.com/maps/@${lat},${lon},${zoom}z`
|
||||||
|
}
|
||||||
|
return
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'val_town',
|
||||||
|
title: 'Val Town',
|
||||||
|
hostnames: ['val.town'],
|
||||||
|
minWidth: 260,
|
||||||
|
minHeight: 100,
|
||||||
|
width: 720,
|
||||||
|
height: 500,
|
||||||
|
doesResize: true,
|
||||||
|
canUnmount: false,
|
||||||
|
toEmbedUrl: (url) => {
|
||||||
|
const urlObj = safeParseUrl(url)
|
||||||
|
// e.g. extract "steveruizok.mathFact" from https://www.val.town/v/steveruizok.mathFact
|
||||||
|
const matches = urlObj && urlObj.pathname.match(/\/v\/([^/]+)\/?/)
|
||||||
|
if (matches) {
|
||||||
|
return `https://www.val.town/embed/${matches[1]}`
|
||||||
|
}
|
||||||
|
return
|
||||||
|
},
|
||||||
|
fromEmbedUrl: (url) => {
|
||||||
|
const urlObj = safeParseUrl(url)
|
||||||
|
// e.g. extract "steveruizok.mathFact" from https://www.val.town/v/steveruizok.mathFact
|
||||||
|
const matches = urlObj && urlObj.pathname.match(/\/embed\/([^/]+)\/?/)
|
||||||
|
if (matches) {
|
||||||
|
return `https://www.val.town/v/${matches[1]}`
|
||||||
|
}
|
||||||
|
return
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: 'codesandbox',
|
type: 'codesandbox',
|
||||||
title: 'CodeSandbox',
|
title: 'CodeSandbox',
|
||||||
|
@ -166,74 +263,6 @@ export const EMBED_DEFINITIONS = [
|
||||||
return
|
return
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
type: 'figma',
|
|
||||||
title: 'Figma',
|
|
||||||
hostnames: ['figma.com'],
|
|
||||||
width: 720,
|
|
||||||
height: 500,
|
|
||||||
doesResize: true,
|
|
||||||
canUnmount: true,
|
|
||||||
toEmbedUrl: (url) => {
|
|
||||||
if (
|
|
||||||
!!url.match(
|
|
||||||
// eslint-disable-next-line no-useless-escape
|
|
||||||
/https:\/\/([\w\.-]+\.)?figma.com\/(file|proto)\/([0-9a-zA-Z]{22,128})(?:\/.*)?$/
|
|
||||||
) &&
|
|
||||||
!url.includes('figma.com/embed')
|
|
||||||
) {
|
|
||||||
return `https://www.figma.com/embed?embed_host=share&url=${url}`
|
|
||||||
}
|
|
||||||
return
|
|
||||||
},
|
|
||||||
fromEmbedUrl: (url) => {
|
|
||||||
const urlObj = safeParseUrl(url)
|
|
||||||
if (urlObj && urlObj.pathname.match(/^\/embed\/?$/)) {
|
|
||||||
const outUrl = urlObj.searchParams.get('url')
|
|
||||||
if (outUrl) {
|
|
||||||
return outUrl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'google_maps',
|
|
||||||
title: 'Google Maps',
|
|
||||||
hostnames: ['google.*'],
|
|
||||||
width: 720,
|
|
||||||
height: 500,
|
|
||||||
doesResize: true,
|
|
||||||
canUnmount: false,
|
|
||||||
toEmbedUrl: (url) => {
|
|
||||||
if (url.includes('/maps/')) {
|
|
||||||
const match = url.match(/@(.*),(.*),(.*)z/)
|
|
||||||
let result: string
|
|
||||||
if (match) {
|
|
||||||
const [, lat, lng, z] = match
|
|
||||||
const host = new URL(url).host.replace('www.', '')
|
|
||||||
result = `https://${host}/maps/embed/v1/view?key=${process.env.NEXT_PUBLIC_GC_API_KEY}¢er=${lat},${lng}&zoom=${z}`
|
|
||||||
} else {
|
|
||||||
result = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
return
|
|
||||||
},
|
|
||||||
fromEmbedUrl: (url) => {
|
|
||||||
const urlObj = safeParseUrl(url)
|
|
||||||
if (!urlObj) return
|
|
||||||
|
|
||||||
const matches = urlObj.pathname.match(/^\/maps\/embed\/v1\/view\/?$/)
|
|
||||||
if (matches && urlObj.searchParams.has('center') && urlObj.searchParams.get('zoom')) {
|
|
||||||
const zoom = urlObj.searchParams.get('zoom')
|
|
||||||
const [lat, lon] = urlObj.searchParams.get('center')!.split(',')
|
|
||||||
return `https://www.google.com/maps/@${lat},${lon},${zoom}z`
|
|
||||||
}
|
|
||||||
return
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
type: 'google_calendar',
|
type: 'google_calendar',
|
||||||
title: 'Google Calendar',
|
title: 'Google Calendar',
|
||||||
|
|
Loading…
Reference in a new issue