diff --git a/content/fennifith/posts/example/index.md b/content/fennifith/posts/example/index.md index 7ca76361c..b12bd2121 100644 --- a/content/fennifith/posts/example/index.md +++ b/content/fennifith/posts/example/index.md @@ -156,3 +156,7 @@ This is regular text. # Heading `with a code snippet` inside of it # Heading [with a link](https://example.com) inside of it + +# COOL EMBED FEATURE FOR THE YOUTUBEZZZ + + diff --git a/src/styles/markdown/embed.scss b/src/styles/markdown/embed.scss index 9b404e9ff..5edfb5c35 100644 --- a/src/styles/markdown/embed.scss +++ b/src/styles/markdown/embed.scss @@ -98,6 +98,9 @@ &__placeholder { background-color: var(--embed_iframe_background-color); + background-size: cover; + background-position: center; + background-repeat: no-repeat; border: var(--embed-iframe_border-width) solid var(--embed_iframe_border_color); border-radius: var(--embed-iframe_corner-radius); diff --git a/src/utils/markdown/createHtmlPlugins.ts b/src/utils/markdown/createHtmlPlugins.ts index af76e0ac2..6f5be6e6f 100644 --- a/src/utils/markdown/createHtmlPlugins.ts +++ b/src/utils/markdown/createHtmlPlugins.ts @@ -81,10 +81,11 @@ export function createHtmlPlugins(unified: Processor) { .use(rehypeTooltips) .use(rehypeAstroImageMd) .use(rehypeUnicornIFrameClickToRun, { - srcReplacements: [ - (val: string, file: VFile) => { + replacements: [ + // PFP code + async (val: string, file: VFile) => { const iFrameUrl = new URL(val); - if (!iFrameUrl.protocol.startsWith("pfp-code:")) return val; + if (!iFrameUrl.protocol.startsWith("pfp-code:")) return {}; const contentDir = dirname(file.path); const fullPath = resolve(contentDir, iFrameUrl.pathname); @@ -101,9 +102,54 @@ export function createHtmlPlugins(unified: Processor) { const q = iFrameUrl.search; const repoPath = siteMetadata.repoPath; const provider = `stackblitz.com/github`; - return ` + return { + src: ` https://${provider}/${repoPath}/tree/${currentBranch}/${urlRelativePath}${q} - `.trim(); + `.trim(), + }; + }, + + // Youtube embed + async (val) => { + const iFrameUrl = new URL(val); + if ( + iFrameUrl.hostname !== "youtube.com" && + iFrameUrl.hostname !== "www.youtube.com" && + iFrameUrl.hostname !== "youtu.be" + ) { + return {}; + } + + interface YouTubeLookup { + url: string; + version: string; + thumbnail_height: number; + provider_name: "YouTube"; + thumbnail_width: number; + width: number; + title: string; + author_name: string; + thumbnail_url: string; + author_url: string; + html: string; + type: "video"; + height: number; + provider_url: string; + } + + const json = await fetch( + `https://noembed.com/embed?dataType=json&url=${encodeURIComponent(val)}`, + ) + .then((r) => { + if (r.status !== 200) return null; + return r.json() as Promise; + }) + .catch(() => null); + if (!json) { + return {}; + } + + return { src: val, bgImg: json.thumbnail_url, aspectRatio: 16 / 9 }; }, ], }) diff --git a/src/utils/markdown/iframes/iframe-placeholder.tsx b/src/utils/markdown/iframes/iframe-placeholder.tsx index 5672f884a..5632b1393 100644 --- a/src/utils/markdown/iframes/iframe-placeholder.tsx +++ b/src/utils/markdown/iframes/iframe-placeholder.tsx @@ -9,6 +9,8 @@ const play = await fs.readFile("src/icons/play.svg", "utf8"); export interface IFramePlaceholderProps { width: string; height: string; + background: string | null; + aspectRatio : number | null; src: string; propsToPreserve: string; pageTitle: string; @@ -20,6 +22,8 @@ export interface IFramePlaceholderProps { export function IFramePlaceholder({ height, width, + background, + aspectRatio, propsToPreserve, ...props }: IFramePlaceholderProps): Element { @@ -70,7 +74,11 @@ export function IFramePlaceholder({ class="embed__placeholder" data-iframeurl={props.src} data-iframeprops={propsToPreserve} - style={`height: ${Number(height) ? `${height}px` : height};`} + style={` + ${!aspectRatio && height ? "height: " + (Number(height) ? height + 'px' : height) + ";" : ""} + ${background ? 'background-image: url("' + background + '");' : ""} + ${aspectRatio ? 'aspect-ratio:' + aspectRatio + ';' : ""} + `} > diff --git a/src/utils/markdown/iframes/iframe-script.ts b/src/utils/markdown/iframes/iframe-script.ts index 2f706e012..cad9b0367 100644 --- a/src/utils/markdown/iframes/iframe-script.ts +++ b/src/utils/markdown/iframes/iframe-script.ts @@ -25,6 +25,7 @@ export const iFrameClickToRun = () => { } iframe.style.width = parent.style.width; iframe.style.height = parent.style.height; + iframe.style.aspectRatio = parent.style.aspectRatio; parent.replaceWith(iframe); }); }); diff --git a/src/utils/markdown/iframes/rehype-transform.ts b/src/utils/markdown/iframes/rehype-transform.ts index fb5673f0c..e4b34b7ec 100644 --- a/src/utils/markdown/iframes/rehype-transform.ts +++ b/src/utils/markdown/iframes/rehype-transform.ts @@ -17,7 +17,18 @@ import { fetchPageHtml, getPageTitle } from "utils/fetch-page-html"; import { LRUCache } from "lru-cache"; interface RehypeUnicornIFrameClickToRunProps { - srcReplacements?: Array<(val: string, root: VFile) => string>; + replacements?: Array< + ( + val: string, + root: VFile, + ) => Promise<{ + src?: string; + bgImg?: string; + btnColor?: string; + btnIconColor?: string; + aspectRatio?: number; + }> + >; } // default icon, used if a frame's favicon cannot be resolved @@ -175,7 +186,7 @@ export async function fetchPageInfo(src: string): Promise { export const rehypeUnicornIFrameClickToRun: Plugin< [RehypeUnicornIFrameClickToRunProps | never], Root -> = ({ srcReplacements = [], ...props }) => { +> = ({ replacements = [], ...props }) => { return async (tree, file) => { const iframeNodes: Element[] = []; visit(tree, "element", (node: Element) => { @@ -196,8 +207,21 @@ export const rehypeUnicornIFrameClickToRun: Plugin< ...propsToPreserve } = iframeNode.properties; - for (const replacement of srcReplacements) { - src = replacement(src!.toString(), file); + let background: string | null = null; + let aspectRatio: number | null = null; + + for (const replacement of replacements) { + const replacementData = await replacement(src!.toString(), file); + + if (replacementData.src) { + src = replacementData.src; + } + if (replacementData.bgImg) { + background = replacementData.bgImg; + } + if (replacementData.aspectRatio) { + aspectRatio = replacementData.aspectRatio; + } } width = width ?? EMBED_SIZE.w; @@ -212,6 +236,8 @@ export const rehypeUnicornIFrameClickToRun: Plugin< const iframeReplacement = IFramePlaceholder({ width: width.toString(), height: height.toString(), + background: background, + aspectRatio: aspectRatio, src: String(src), pageTitle: String(dataFrameTitle ?? "") || info.title || "", pageIcon: info.iconFile,