From c79a60034db5401192749058d6e9db69ecd8b3aa Mon Sep 17 00:00:00 2001 From: the3ash <24548223+the3ash@users.noreply.github.com> Date: Fri, 18 Jul 2025 15:01:04 +0800 Subject: [PATCH] feat: add link card for embedded content --- src/components/ui/GitHubCard.astro | 6 +- src/components/ui/LinkCard.astro | 311 ++++++++++++++++++++++++++ src/content/posts/embedded-content.md | 6 + src/layouts/PostLayout.astro | 2 + src/pages/api/proxy.ts | 33 +++ src/plugins/remark-embedded-media.mjs | 27 +++ src/types/component.types.ts | 8 + 7 files changed, 390 insertions(+), 3 deletions(-) create mode 100644 src/components/ui/LinkCard.astro create mode 100644 src/pages/api/proxy.ts diff --git a/src/components/ui/GitHubCard.astro b/src/components/ui/GitHubCard.astro index 0934f9d..1fb025a 100644 --- a/src/components/ui/GitHubCard.astro +++ b/src/components/ui/GitHubCard.astro @@ -161,7 +161,7 @@ } .prose .gc-container:hover { - background: color-mix(in srgb, var(--selection) 60%, transparent); + background: color-mix(in srgb, var(--selection) 75%, transparent); text-decoration: none; } @@ -206,7 +206,7 @@ .prose .gc-repo-description { font-size: var(--font-size-m); color: var(--text-primary); - opacity: 0.75; + opacity: 0.6; margin: 0 0 0.75rem 0; line-height: 1.4; } @@ -215,7 +215,7 @@ display: flex; align-items: center; color: var(--text-primary); - opacity: 0.75; + opacity: 0.6; gap: 0.35rem; } diff --git a/src/components/ui/LinkCard.astro b/src/components/ui/LinkCard.astro new file mode 100644 index 0000000..eb6284d --- /dev/null +++ b/src/components/ui/LinkCard.astro @@ -0,0 +1,311 @@ + + + diff --git a/src/content/posts/embedded-content.md b/src/content/posts/embedded-content.md index df6be29..f3d56fd 100644 --- a/src/content/posts/embedded-content.md +++ b/src/content/posts/embedded-content.md @@ -6,6 +6,8 @@ pubDate: '2025-06-06' Use these directives to embed media: ``` +::link{url="https://xxxxx"} + ::spotify{url="https://open.spotify.com/type/xxxxxx"} ::youtube{url="https://www.youtube.com/watch?v=xxxxxx"} @@ -21,6 +23,10 @@ Use these directives to embed media: 🟡 When embedded content is still loading, the table of contents positioning may be inaccurate. ``` +## Link Card + +::link{url="https://pitchfork.com/reviews/albums/ichiko-aoba-luminescent-creatures/"} + ## Spotify ::spotify{url="https://open.spotify.com/track/41Y0ch6R3jzpJOZv6nhf9Z?si=6c82dbed65ab4853"} diff --git a/src/layouts/PostLayout.astro b/src/layouts/PostLayout.astro index 301ba97..8e0cabd 100644 --- a/src/layouts/PostLayout.astro +++ b/src/layouts/PostLayout.astro @@ -10,6 +10,7 @@ import TableOfContents from '@/components/ui/TableOfContents.astro' import GradientMask from '@/components/ui/GradientMask.astro' import ImageViewer from '@/components/ui/ImageViewer.astro' import GitHubCard from '@/components/ui/GitHubCard.astro' +import LinkCard from '@/components/ui/LinkCard.astro' import ImageOptimizer from '@/components/ui/ImageOptimizer.astro' import XPOST from '@/components/ui/XPOST.astro' import CopyCode from '@/components/ui/CopyCode.astro' @@ -60,6 +61,7 @@ const ogImage = `/open-graph/${postSlug}.png` + {themeConfig.post.imageViewer && } diff --git a/src/pages/api/proxy.ts b/src/pages/api/proxy.ts new file mode 100644 index 0000000..82a5a1a --- /dev/null +++ b/src/pages/api/proxy.ts @@ -0,0 +1,33 @@ +export const prerender = false + +import type { APIContext } from 'astro' + +export async function GET(context: APIContext) { + const host = context.request.headers.get('host') || 'localhost:4321' + const url = new URL(context.request.url, `http://${host}`) + const target = url.searchParams.get('url') + + if (!target) { + return new Response('Missing url param', { status: 400 }) + } + + try { + const res = await fetch(target, { + headers: { + 'User-Agent': 'Mozilla/5.0' + } + }) + const contentType = res.headers.get('content-type') || 'text/html' + const data = await res.text() + return new Response(data, { + status: 200, + headers: { + 'Content-Type': contentType, + 'Cache-Control': 'no-store', + 'Access-Control-Allow-Origin': '*' + } + }) + } catch { + return new Response('Proxy error', { status: 500 }) + } +} diff --git a/src/plugins/remark-embedded-media.mjs b/src/plugins/remark-embedded-media.mjs index 24c7350..b9ceceb 100644 --- a/src/plugins/remark-embedded-media.mjs +++ b/src/plugins/remark-embedded-media.mjs @@ -2,8 +2,35 @@ import { visit } from 'unist-util-visit' /** * A remark plugin that converts custom directives to embedded media HTML elements + * Supports: link cards, Spotify, YouTube, Bilibili, X posts, and GitHub repository cards */ const embedHandlers = { + // Link Card + link: (node) => { + const url = node.attributes?.url + if (!url) { + return false + } + + // Create the LinkCard HTML structure - all metadata will be fetched by JavaScript + return ` + + ` + }, + // Spotify spotify: (node) => { const url = node.attributes?.url ?? '' diff --git a/src/types/component.types.ts b/src/types/component.types.ts index 1d41812..045bc20 100644 --- a/src/types/component.types.ts +++ b/src/types/component.types.ts @@ -82,3 +82,11 @@ export interface CardElements { forks: HTMLElement | null license: HTMLElement | null } + +// LinkCard metadata interface (fetched from URL) +export interface LinkCardMetadata { + title: string + description: string + image: string + imageAlt: string +} -- 2.51.2