From fd3b47fa9154c3b37f397d9472f18a431cb57f15 Mon Sep 17 00:00:00 2001 From: grassblock Date: Sun, 8 Jun 2025 21:14:52 +0800 Subject: [PATCH] feat: add link card shortcode --- src/components/shortcodes/LinkCard.astro | 239 +++++++++++++++++++ src/content/posts/markdown-example/index.mdx | 11 +- 2 files changed, 248 insertions(+), 2 deletions(-) create mode 100644 src/components/shortcodes/LinkCard.astro diff --git a/src/components/shortcodes/LinkCard.astro b/src/components/shortcodes/LinkCard.astro new file mode 100644 index 0000000..8575860 --- /dev/null +++ b/src/components/shortcodes/LinkCard.astro @@ -0,0 +1,239 @@ +--- +interface Props { + url: string; + showArchive?: boolean; + title?: string; + description?: string; + siteName?: string; +} + +const { url, showArchive = true} = Astro.props; + +const siteMetadata = { + title: Astro.props.title || '', + description: Astro.props.description || '', + siteName: Astro.props.siteName || '', + image: '', + domain: new URL(url).hostname || '' +}; + +// Get metadata from the URL +async function fetchMetadata(url: string) { + try { + const response = await fetch(url, { + headers: { + 'User-Agent': 'Mozilla/5.0 (compatible; LinkCard/1.0)' + } + }); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}`); + } + + const html = await response.text(); + + // 提取元数据 + const titleMatch = html.match(/]*>([^<]+)<\/title>/i); + const descriptionMatch = html.match(/]+name=["']description["'][^>]+content=["']([^"']+)["']/i) || + html.match(/]+property=["']og:description["'][^>]+content=["']([^"']+)["']/i); + const imageMatch = html.match(/]+property=["']og:image["'][^>]+content=["']([^"']+)["']/i) || + html.match(/]+name=["']twitter:image["'][^>]+content=["']([^"']+)["']/i); + const siteNameMatch = html.match(/]+property=["']og:site_name["'][^>]+content=["']([^"']+)["']/i); + + return { + title: titleMatch?.[1]?.trim() || new URL(url).hostname, + description: descriptionMatch?.[1]?.trim() || '', + image: imageMatch?.[1]?.trim() || '', + siteName: siteNameMatch?.[1]?.trim() || new URL(url).hostname, + domain: new URL(url).hostname + }; + } catch (error) { + console.warn(`Failed to fetch metadata for ${url}:`, error); + const domain = new URL(url).hostname; + return { + title: domain, + description: '', + image: '', + siteName: domain, + domain + }; + } +} + +// Check if the URL is archived on the Wayback Machine at the build time +async function checkArchive(url: string) { + try { + const archiveUrl = `https://archive.org/wayback/available?url=${encodeURIComponent(url)}`; + const response = await fetch(archiveUrl); + const data = await response.json(); + + if (data.archived_snapshots?.closest?.available) { + return data.archived_snapshots.closest.url; + } + } catch (error) { + console.warn(`Failed to check archive for ${url}:`, error); + } + return null; +} + +// extract metadata and archive URL +const metadata = Astro.props.title ? siteMetadata : await fetchMetadata(url); +const archiveUrl = showArchive ? await checkArchive(url) : null; +--- + + + + \ No newline at end of file diff --git a/src/content/posts/markdown-example/index.mdx b/src/content/posts/markdown-example/index.mdx index c2edeb9..5c59da6 100644 --- a/src/content/posts/markdown-example/index.mdx +++ b/src/content/posts/markdown-example/index.mdx @@ -5,6 +5,7 @@ description: "Sample article showcasing basic Markdown syntax and formatting for tags: ["markdown", "css", "html", "sample"] --- import Callout from '/src/components/shortcodes/Callout.astro'; +import LinkCard from '/src/components/shortcodes/LinkCard.astro'; This article offers a sample of basic and extended Markdown formatting that can be used, also it shows how some basic HTML elements are decorated. @@ -43,8 +44,14 @@ You can use callouts to highlight important information or warnings in your cont This is an error callout. It should be used to indicate critical issues that need immediate attention. ``` - - +### LinkCard +You can use the `LinkCard` component to create cards that link to external resources or pages. This is useful for showcasing projects, documentation, or any other relevant links. + +```mdx + +``` +Or to customize the card further with a title and description: + ## Headings The following HTML `

`—`

` elements represent six levels of section headings. `

` is the highest section level while `

` is the lowest.