mirror of
https://github.com/reonokiy/blog.nokiy.net.git
synced 2025-06-16 11:41:17 +02:00
feat: automatically generate meta descriptions for articles
This commit is contained in:
parent
78de8b7911
commit
79f9765688
3 changed files with 51 additions and 10 deletions
|
@ -9,13 +9,18 @@ interface Props {
|
||||||
}
|
}
|
||||||
|
|
||||||
const { postTitle, postDescription, postImage } = Astro.props
|
const { postTitle, postDescription, postImage } = Astro.props
|
||||||
|
|
||||||
const { title, subtitle, description, author, url, favicon } = themeConfig.site
|
const { title, subtitle, description, author, url, favicon } = themeConfig.site
|
||||||
const { mode, light: { background: lightMode }, dark: { background: darkMode } } = themeConfig.color
|
const { mode, light: { background: lightMode }, dark: { background: darkMode } } = themeConfig.color
|
||||||
const initMetaTheme = mode === 'dark' ? darkMode : lightMode
|
|
||||||
const { locale, moreLocale } = themeConfig.global
|
const { locale, moreLocale } = themeConfig.global
|
||||||
const { verification = {}, twitterID = '', googleAnalyticsID = '', umamiAnalyticsID = '' } = themeConfig.seo ?? {}
|
const { verification = {}, twitterID = '', googleAnalyticsID = '', umamiAnalyticsID = '' } = themeConfig.seo ?? {}
|
||||||
const { google = '', bing = '', yandex = '', baidu = '' } = verification
|
const { google = '', bing = '', yandex = '', baidu = '' } = verification
|
||||||
const { commentURL = '', imageHostURL = '', customGoogleAnalyticsJS = '', customUmamiAnalyticsJS = '' } = themeConfig.preload
|
const { commentURL = '', imageHostURL = '', customGoogleAnalyticsJS = '', customUmamiAnalyticsJS = '' } = themeConfig.preload
|
||||||
|
|
||||||
|
const initMetaTheme = mode === 'dark' ? darkMode : lightMode
|
||||||
|
const pageTitle = postTitle ? `${postTitle} | ${title}` : `${title} - ${subtitle}`
|
||||||
|
const pageDescription = postDescription || description
|
||||||
|
const pageImage = postImage || favicon
|
||||||
---
|
---
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
|
@ -25,8 +30,8 @@ const { commentURL = '', imageHostURL = '', customGoogleAnalyticsJS = '', custom
|
||||||
{favicon.toLowerCase().endsWith('.webp') && <link rel="icon" type="image/webp" href={favicon} />}
|
{favicon.toLowerCase().endsWith('.webp') && <link rel="icon" type="image/webp" href={favicon} />}
|
||||||
{favicon.toLowerCase().endsWith('.svg') && <link rel="icon" type="image/svg+xml" href={favicon} />}
|
{favicon.toLowerCase().endsWith('.svg') && <link rel="icon" type="image/svg+xml" href={favicon} />}
|
||||||
{favicon.toLowerCase().endsWith('.png') && <link rel="icon" type="image/png" href={favicon} />}
|
{favicon.toLowerCase().endsWith('.png') && <link rel="icon" type="image/png" href={favicon} />}
|
||||||
<title>{postTitle ? `${postTitle} | ${title}` : `${title} - ${subtitle}`}</title>
|
<title>{pageTitle}</title>
|
||||||
<meta name="description" content={postDescription || description} />
|
<meta name="description" content={pageDescription} />
|
||||||
<meta name="author" content={author} />
|
<meta name="author" content={author} />
|
||||||
<meta name="generator" content={Astro.generator} />
|
<meta name="generator" content={Astro.generator} />
|
||||||
<meta name="theme-color" content={initMetaTheme} />
|
<meta name="theme-color" content={initMetaTheme} />
|
||||||
|
@ -52,18 +57,18 @@ const { commentURL = '', imageHostURL = '', customGoogleAnalyticsJS = '', custom
|
||||||
))}
|
))}
|
||||||
|
|
||||||
<!-- Facebook Open Graph -->
|
<!-- Facebook Open Graph -->
|
||||||
<meta property="og:title" content={postTitle || title} />
|
<meta property="og:title" content={pageTitle} />
|
||||||
<meta property="og:type" content={postTitle ? 'article' : 'website'} />
|
<meta property="og:type" content={postTitle ? 'article' : 'website'} />
|
||||||
<meta property="og:image" content={postImage || favicon} />
|
<meta property="og:image" content={pageImage} />
|
||||||
<meta property="og:url" content={Astro.url} />
|
<meta property="og:url" content={Astro.url} />
|
||||||
<meta property="og:description" content={postDescription || subtitle} />
|
<meta property="og:description" content={pageDescription} />
|
||||||
<meta property="og:site_name" content={title} />
|
<meta property="og:site_name" content={title} />
|
||||||
|
|
||||||
<!-- Twitter Card -->
|
<!-- Twitter Card -->
|
||||||
<meta name="twitter:card" content="summary" />
|
<meta name="twitter:card" content="summary" />
|
||||||
<meta name="twitter:title" content={postTitle || title} />
|
<meta name="twitter:title" content={pageTitle} />
|
||||||
<meta name="twitter:description" content={postDescription || subtitle} />
|
<meta name="twitter:description" content={pageDescription} />
|
||||||
<meta name="twitter:image" content={postImage || favicon} />
|
<meta name="twitter:image" content={pageImage} />
|
||||||
{twitterID && (
|
{twitterID && (
|
||||||
<>
|
<>
|
||||||
<meta name="twitter:site" content={twitterID} />
|
<meta name="twitter:site" content={twitterID} />
|
||||||
|
|
|
@ -3,6 +3,7 @@ import Comments from '@/components/Comments/index.astro'
|
||||||
import PostTime from '@/components/PostTime.astro'
|
import PostTime from '@/components/PostTime.astro'
|
||||||
import Layout from '@/layouts/Layout.astro'
|
import Layout from '@/layouts/Layout.astro'
|
||||||
import { checkSlugDuplication } from '@/utils/content'
|
import { checkSlugDuplication } from '@/utils/content'
|
||||||
|
import { generateDescription } from '@/utils/description'
|
||||||
import { generatePostPaths } from '@/utils/i18n/route'
|
import { generatePostPaths } from '@/utils/i18n/route'
|
||||||
import { getCollection } from 'astro:content'
|
import { getCollection } from 'astro:content'
|
||||||
|
|
||||||
|
@ -18,12 +19,13 @@ export async function getStaticPaths() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const { post } = Astro.props
|
const { post } = Astro.props
|
||||||
|
const description = generateDescription(post)
|
||||||
const { Content, remarkPluginFrontmatter } = await post.render()
|
const { Content, remarkPluginFrontmatter } = await post.render()
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout
|
<Layout
|
||||||
postTitle={post.data.title}
|
postTitle={post.data.title}
|
||||||
postDescription={post.data.description}
|
postDescription={description}
|
||||||
postImage={post.data.image}
|
postImage={post.data.image}
|
||||||
>
|
>
|
||||||
<article class="heti mb-12.6">
|
<article class="heti mb-12.6">
|
||||||
|
|
34
src/utils/description.ts
Normal file
34
src/utils/description.ts
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import type { CollectionEntry } from 'astro:content'
|
||||||
|
import MarkdownIt from 'markdown-it'
|
||||||
|
import sanitizeHtml from 'sanitize-html'
|
||||||
|
|
||||||
|
const parser = new MarkdownIt()
|
||||||
|
|
||||||
|
// Generate an excerpt from Markdown content
|
||||||
|
export function generateExcerpt(content: string, length: number = 100): string {
|
||||||
|
if (!content)
|
||||||
|
return ''
|
||||||
|
|
||||||
|
// Convert Markdown to plain text
|
||||||
|
const plainText = sanitizeHtml(parser.render(content), {
|
||||||
|
allowedTags: [],
|
||||||
|
allowedAttributes: {},
|
||||||
|
})
|
||||||
|
|
||||||
|
// Replace line breaks with spaces
|
||||||
|
const normalizedText = plainText.replace(/\s+/g, ' ')
|
||||||
|
const excerpt = normalizedText.slice(0, length).trim()
|
||||||
|
// Add ellipsis if text was truncated
|
||||||
|
const needsEllipsis = normalizedText.length > length
|
||||||
|
return needsEllipsis ? `${excerpt}...` : excerpt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Automatically generate a description for the article
|
||||||
|
export function generateDescription(post: CollectionEntry<'posts'>): string {
|
||||||
|
// If the article already has a description, return it directly
|
||||||
|
if (post.data.description)
|
||||||
|
return post.data.description
|
||||||
|
|
||||||
|
// Otherwise, generate an excerpt from the article content as the description
|
||||||
|
return generateExcerpt(post.body)
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue