feat: add SharePanel component for social sharing with QR code support
- Implemented SharePanel component in `SharePanel.astro` for sharing content on social media platforms. - Integrated QR code generation for WeChat sharing using the `qrcode` library. - Added localization support for English and Chinese languages. - Created utility functions in `seo.ts` for building article summaries and FAQs. - Introduced API routes for serving IndexNow key and generating full LLM catalog and summaries. - Enhanced SEO capabilities with structured data for articles and pages.
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
---
|
||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
import DiscoveryBrief from '../components/seo/DiscoveryBrief.astro';
|
||||
import PageViewTracker from '../components/seo/PageViewTracker.astro';
|
||||
import SharePanel from '../components/seo/SharePanel.astro';
|
||||
import TerminalWindow from '../components/ui/TerminalWindow.astro';
|
||||
import CommandPrompt from '../components/ui/CommandPrompt.astro';
|
||||
import FilterPill from '../components/ui/FilterPill.astro';
|
||||
@@ -12,6 +15,7 @@ import TechStackList from '../components/TechStackList.astro';
|
||||
import { terminalConfig } from '../lib/config/terminal';
|
||||
import { api, DEFAULT_SITE_SETTINGS } from '../lib/api/client';
|
||||
import { formatReadTime, getI18n } from '../lib/i18n';
|
||||
import { buildDiscoveryHighlights, buildFaqJsonLd, buildPageFaqs, buildPostItemList } from '../lib/seo';
|
||||
import type { AppFriendLink } from '../lib/api/client';
|
||||
import type { ContentOverview, ContentWindowHighlight, PopularPostHighlight, Post } from '../lib/types';
|
||||
import { getAccentVars, getCategoryTheme, getPostTypeTheme, getTagTheme } from '../lib/utils';
|
||||
@@ -63,6 +67,7 @@ let contentOverview: ContentOverview = {
|
||||
};
|
||||
let apiError: string | null = null;
|
||||
const { locale, t } = getI18n(Astro);
|
||||
const isEnglish = locale.startsWith('en');
|
||||
|
||||
const formatDurationMs = (value: number | undefined) => {
|
||||
if (!value || value <= 0) return locale === 'en' ? 'N/A' : '暂无';
|
||||
@@ -221,9 +226,68 @@ const navLinks = [
|
||||
{ icon: 'fa-user-secret', text: t('nav.about'), href: '/about' },
|
||||
...(siteSettings.ai.enabled ? [{ icon: 'fa-robot', text: t('nav.ask'), href: '/ask' }] : []),
|
||||
];
|
||||
const siteBaseUrl = (siteSettings.siteUrl || new URL(Astro.request.url).origin).replace(/\/$/, '');
|
||||
const homeJsonLd = [
|
||||
{
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'CollectionPage',
|
||||
name: siteSettings.siteTitle,
|
||||
description: siteSettings.siteDescription,
|
||||
url: siteBaseUrl,
|
||||
inLanguage: locale,
|
||||
},
|
||||
{
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'ItemList',
|
||||
name: `${siteSettings.siteName} recent posts`,
|
||||
itemListElement: buildPostItemList(recentPosts, siteBaseUrl),
|
||||
},
|
||||
];
|
||||
const homeShareCopy = isEnglish
|
||||
? {
|
||||
badge: 'site entry',
|
||||
title: 'Share the homepage',
|
||||
description:
|
||||
'Use the homepage as the canonical top-level entry for people and AI search to branch into articles, taxonomies, reviews, and profile context.',
|
||||
}
|
||||
: {
|
||||
badge: '站点入口',
|
||||
title: '分享首页总入口',
|
||||
description: '把首页当成站点的规范总入口分发出去,方便用户和 AI 搜索继续进入文章、分类、评测和个人介绍等核心页面。',
|
||||
};
|
||||
const homeBriefHighlights = buildDiscoveryHighlights([
|
||||
siteSettings.siteDescription,
|
||||
siteSettings.heroSubtitle,
|
||||
siteSettings.ownerBio,
|
||||
`${t('common.posts')}: ${allPosts.length}`,
|
||||
`${t('common.categories')}: ${categories.length}`,
|
||||
`${t('common.tags')}: ${tags.length}`,
|
||||
]);
|
||||
const homeFaqs = buildPageFaqs({
|
||||
locale,
|
||||
pageTitle: siteSettings.siteTitle,
|
||||
summary: siteSettings.heroSubtitle || siteSettings.siteDescription,
|
||||
primaryLabel: isEnglish ? 'homepage' : '首页',
|
||||
primaryUrl: siteBaseUrl,
|
||||
relatedLinks: [
|
||||
{ label: t('nav.articles'), url: `${siteBaseUrl}/articles` },
|
||||
{ label: t('nav.categories'), url: `${siteBaseUrl}/categories` },
|
||||
{ label: t('nav.about'), url: `${siteBaseUrl}/about` },
|
||||
],
|
||||
signals: homeBriefHighlights,
|
||||
});
|
||||
const homeFaqJsonLd = buildFaqJsonLd(homeFaqs);
|
||||
---
|
||||
|
||||
<BaseLayout title={siteSettings.siteTitle} description={siteSettings.siteDescription} siteSettings={siteSettings}>
|
||||
<BaseLayout
|
||||
title={siteSettings.siteTitle}
|
||||
description={siteSettings.siteDescription}
|
||||
siteSettings={siteSettings}
|
||||
canonical="/"
|
||||
noindex={hasActiveFilters}
|
||||
jsonLd={[...homeJsonLd, homeFaqJsonLd].filter(Boolean)}
|
||||
>
|
||||
<PageViewTracker pageType="home" entityId="homepage" />
|
||||
<div class="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
|
||||
<TerminalWindow title={terminalConfig.title} class="w-full">
|
||||
<div class="mb-5 px-4 overflow-x-auto">
|
||||
@@ -266,6 +330,31 @@ const navLinks = [
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div class="ml-4 mt-4">
|
||||
<SharePanel
|
||||
shareTitle={siteSettings.siteTitle}
|
||||
summary={siteSettings.heroSubtitle || siteSettings.siteDescription}
|
||||
canonicalUrl={siteBaseUrl}
|
||||
badge={homeShareCopy.badge}
|
||||
kicker="geo / homepage"
|
||||
title={homeShareCopy.title}
|
||||
description={homeShareCopy.description}
|
||||
stats={systemStats.slice(0, 4)}
|
||||
wechatShareQrEnabled={siteSettings.seo.wechatShareQrEnabled}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="ml-4 mt-4">
|
||||
<DiscoveryBrief
|
||||
badge={isEnglish ? 'site brief' : '站点摘要'}
|
||||
kicker="geo / overview"
|
||||
title={isEnglish ? 'AI-readable site brief' : '给 AI 看的站点摘要'}
|
||||
summary={siteSettings.heroSubtitle || siteSettings.siteDescription}
|
||||
highlights={homeBriefHighlights}
|
||||
faqs={homeFaqs}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{apiError && (
|
||||
|
||||
Reference in New Issue
Block a user