同步 Blog 主题变更:Webmention、互动区与样式修复

This commit is contained in:
RiseForever
2026-08-30 20:57:39 +08:00
parent b70ed6051a
commit d6cd9ea4c5
23 changed files with 1534 additions and 38 deletions
+10 -5
View File
@@ -4,7 +4,7 @@ import BaseLayout from '@/layouts/BaseLayout.astro';
import Masthead from '@/components/Masthead.astro';
import { getContentCacheKey, getExcerpt, getPostCardImage, getPostPath, getPostTaxonomy, getPublicPosts, slugify } from '@/lib/content';
import { siteConfig } from '@/site.config';
import TwikooComments from '@/components/TwikooComments.astro';
import EngagementTabs from '@/components/EngagementTabs.astro';
import TableOfContents from '@/components/TableOfContents.astro';
import Mermaid from '@/components/Mermaid.astro';
import ImageLightbox from '@/components/ImageLightbox.astro';
@@ -29,7 +29,7 @@ const date = {
};
const dateText = `${date.year} 年 ${date.month} 月 ${date.day} 日`;
---
<BaseLayout title={post.data.title} description={getExcerpt(post)} image={cover} type="article" math={post.data.math} navbarOverlay={Boolean(cover)}>
<BaseLayout title={post.data.title} description={getExcerpt(post)} image={cover} type="article" math={post.data.math} webmention={post.data.webmention} navbarOverlay={Boolean(cover)}>
<Fragment slot="masthead"><Masthead article={{ title: post.data.title, date, categories: taxonomy.categories, image: cover, textTone: post.data.textTone }} /></Fragment>
<article class:list={['article-shell', 'relative pt-8 pb-14 md:pt-10 md:pb-20', { 'has-toc': toc.enabled, [`toc-${toc.position}`]: toc.enabled }]}>
<div class="sr-only" aria-hidden="true">
@@ -45,10 +45,15 @@ const dateText = `${date.year} 年 ${date.month} 月 ${date.day} 日`;
{tags.map((tag) => <a class="rounded-[3px] bg-[var(--color-post-tag-bg)] px-2.5 py-1 no-underline" href={`/tags/${slugify(tag)}/`}>#{tag}</a>)}
</div>}
<nav class="mt-10 grid grid-cols-1 overflow-hidden rounded-[4px] border border-line bg-raised sm:grid-cols-2" aria-label="相邻文章" data-pagefind-ignore="all">
{newer ? <a class="grid min-h-24 content-center p-4 no-underline sm:border-r sm:border-line" href={getPostPath(newer)}><span class="text-xs text-subtle">一篇</span><strong class="mt-1 font-sans text-base font-normal">{newer.data.title}</strong></a> : <span class="grid min-h-24 content-center p-4 sm:border-r sm:border-line"><span class="text-xs text-subtle">一篇</span><strong class="mt-1 font-sans text-base font-normal">没有更多了</strong></span>}
{older ? <a class="grid min-h-24 content-center border-t border-line p-4 no-underline sm:border-t-0 sm:text-right" href={getPostPath(older)}><span class="text-xs text-subtle">一篇</span><strong class="mt-1 font-sans text-base font-normal">{older.data.title}</strong></a> : <span class="grid min-h-24 content-center border-t border-line p-4 sm:border-t-0 sm:text-right"><span class="text-xs text-subtle">一篇</span><strong class="mt-1 font-sans text-base font-normal">没有更多了</strong></span>}
{older ? <a class="grid min-h-24 content-center p-4 no-underline sm:border-r sm:border-line" href={getPostPath(older)}><span class="text-xs text-subtle">一篇</span><strong class="mt-1 font-sans text-base font-normal">{older.data.title}</strong></a> : <span class="grid min-h-24 content-center p-4 sm:border-r sm:border-line"><span class="text-xs text-subtle">一篇</span><strong class="mt-1 font-sans text-base font-normal">没有更多了</strong></span>}
{newer ? <a class="grid min-h-24 content-center border-t border-line p-4 no-underline sm:border-t-0 sm:text-right" href={getPostPath(newer)}><span class="text-xs text-subtle">一篇</span><strong class="mt-1 font-sans text-base font-normal">{newer.data.title}</strong></a> : <span class="grid min-h-24 content-center border-t border-line p-4 sm:border-t-0 sm:text-right"><span class="text-xs text-subtle">一篇</span><strong class="mt-1 font-sans text-base font-normal">没有更多了</strong></span>}
</nav>
<TwikooComments enabled={post.data.comments} path={Astro.url.pathname} />
<EngagementTabs
commentsEnabled={post.data.comments}
webmentionEnabled={post.data.webmention}
path={Astro.url.pathname}
target={new URL(Astro.url.pathname, siteConfig.site.url).href}
/>
</article>
<Mermaid enabled={post.data.mermaid} />
</BaseLayout>
+31 -13
View File
@@ -26,15 +26,6 @@ const query = Astro.url.searchParams.get('q')?.trim() ?? '';
interface PagefindResult { data: () => Promise<{ url: string; meta?: { title?: string; date?: string; category?: string; categories?: string; tags?: string; cover?: string } }>; }
interface PagefindModule { options: (options: { excerptLength: number }) => Promise<void>; search: (query: string) => Promise<{ results: PagefindResult[] }>; }
const root = document.querySelector<HTMLElement>('[data-search-page]');
const results = root?.querySelector<HTMLElement>('[data-search-page-results]');
const queryLabel = root?.querySelector<HTMLElement>('[data-search-query]');
const input = root?.querySelector<HTMLInputElement>('input[name="q"]');
const query = new URLSearchParams(window.location.search).get('q')?.trim() ?? '';
if (query) {
if (queryLabel) queryLabel.textContent = `查询词:${query}`;
if (input) input.value = query;
}
const cardBackgrounds = ['#547a82', '#806b76', '#657657', '#876d56', '#5f718d', '#796f54'];
const cardBackground = (url: string) => {
let hash = 0;
@@ -42,14 +33,39 @@ const query = Astro.url.searchParams.get('q')?.trim() ?? '';
return cardBackgrounds[hash % cardBackgrounds.length];
};
let pagefindPromise: Promise<PagefindModule> | undefined;
const loadPagefind = () => {
pagefindPromise ??= (async () => {
const pagefindUrl = new URL(`${import.meta.env.BASE_URL}pagefind/pagefind.js`, document.baseURI).href;
const pagefind = await import(/* @vite-ignore */ pagefindUrl) as unknown as PagefindModule;
await pagefind.options({ excerptLength: 0 });
return pagefind;
})();
return pagefindPromise;
};
/* Client-side navigation reuses this module, so every run reads the page and query afresh. */
let runToken = 0;
const run = async () => {
if (!query || !results) return;
const root = document.querySelector<HTMLElement>('[data-search-page]');
const results = root?.querySelector<HTMLElement>('[data-search-page-results]');
if (!root || !results) return;
const queryLabel = root.querySelector<HTMLElement>('[data-search-query]');
const input = root.querySelector<HTMLInputElement>('input[name="q"]');
const query = new URLSearchParams(window.location.search).get('q')?.trim() ?? '';
const token = ++runToken;
if (queryLabel) queryLabel.textContent = query ? `查询词:${query}` : '请输入查询词开始搜索。';
if (input && input.value.trim() !== query) input.value = query;
if (!query) {
results.innerHTML = '<p class="px-3 py-4 text-sm text-subtle">请输入查询词开始搜索。</p>';
return;
}
results.innerHTML = '<p class="px-3 py-4 text-sm text-subtle">正在搜索...</p>';
if (import.meta.env.DEV) { results.innerHTML = '<p class="px-3 py-4 text-sm text-subtle">搜索功能需在构建并预览后使用。</p>'; return; }
try {
const pagefindUrl = new URL(`${import.meta.env.BASE_URL}pagefind/pagefind.js`, document.baseURI).href;
const pagefind = await import(/* @vite-ignore */ pagefindUrl) as unknown as PagefindModule;
await pagefind.options({ excerptLength: 0 });
const pagefind = await loadPagefind();
const search = await pagefind.search(query);
if (token !== runToken) return;
results.replaceChildren();
if (!search.results.length) {
results.innerHTML = '<p class="px-3 py-4 text-sm text-subtle">没有找到相关内容。</p>';
@@ -57,6 +73,7 @@ const query = Astro.url.searchParams.get('q')?.trim() ?? '';
}
for (const result of search.results) {
const data = await result.data();
if (token !== runToken) return;
const article = document.createElement('article');
article.className = 'py-[0.9375rem] md:py-[0.9375rem]';
const link = document.createElement('a');
@@ -107,4 +124,5 @@ const query = Astro.url.searchParams.get('q')?.trim() ?? '';
} catch { results.innerHTML = '<p class="px-3 py-4 text-sm text-subtle">搜索暂时不可用。</p>'; }
};
run();
document.addEventListener('astro:page-load', run);
</script>