This commit is contained in:
2026-06-18 15:10:33 +08:00 Unverified
parent 39b15b4be9
commit c27c783f9f
3 changed files with 341 additions and 10 deletions
+70 -6
View File
@@ -14,6 +14,15 @@ export function getStaticPaths() {
const { post } = Astro.props;
const { Content } = post;
const allPosts = getAllPosts();
const rawContent = post.rawContent?.() ?? '';
const textContent = rawContent
.replace(/```[\s\S]*?```/g, '')
.replace(/<[^>]+>/g, '')
.replace(/[#>*_`~\-\[\]\(\)!]/g, '')
.replace(/\s+/g, '');
const wordCount = textContent.length;
const wordCountText = wordCount >= 1000 ? `${(wordCount / 1000).toFixed(1)}k` : String(wordCount);
const readingMinutes = Math.max(1, Math.ceil(wordCount / 400));
function slugifyHeading(value: string): string {
return value
@@ -69,14 +78,46 @@ const relatedPosts = allPosts
<BaseLayout title={post.title} description={post.summary}>
<section class="post-layout">
<article class="article post-article">
<header class="article-header">
<time datetime={post.date.toISOString()}>{post.dateText}</time>
<h1>{post.title}</h1>
<div class="meta-list">
{post.categories.map((category) => <a href={`/categories/${encodeURIComponent(category)}/`}>{category}</a>)}
{post.tags.map((tag) => <a href={`/tags/${encodeURIComponent(tag)}/`}>#{tag}</a>)}
<header class={post.cover ? 'article-header article-header-cover' : 'article-header'}>
{post.cover && <img class="article-header-cover-img" src={post.cover} alt="" loading="eager" decoding="async" />}
<div class="article-header-content">
<time datetime={post.date.toISOString()}>{post.dateText}</time>
<h1>{post.title}</h1>
{post.cover ? (
<div class="article-hero-meta" aria-label="文章信息">
<span>发布于 {post.dateText}</span>
{post.categories[0] && <a href={`/categories/${encodeURIComponent(post.categories[0])}/`}>{post.categories[0]}</a>}
<span>总字数:{wordCountText}</span>
<span>阅读时长:{readingMinutes} 分钟</span>
</div>
) : (
<div class="meta-list">
{post.categories.map((category) => <a href={`/categories/${encodeURIComponent(category)}/`}>{category}</a>)}
{post.tags.map((tag) => <a href={`/tags/${encodeURIComponent(tag)}/`}>#{tag}</a>)}
</div>
)}
</div>
</header>
{post.summary && (
<section class="article-summary-card" aria-label="AI 文章摘要">
<div class="article-summary-dots" aria-hidden="true">
<span></span>
<span></span>
<span></span>
</div>
<p class="article-summary-content">
<span class="article-summary-text" data-summary-text={post.summary}>{post.summary}</span>
<span class="article-summary-caret" aria-hidden="true"></span>
</p>
<div class="article-summary-footer">
<span class="article-summary-brand">
<span aria-hidden="true">✣</span>
清羽のAI摘要
</span>
<span class="article-summary-model">KIMI-K2.5</span>
</div>
</section>
)}
<div class="prose" data-toc={JSON.stringify(toc)}>
<Content />
</div>
@@ -102,5 +143,28 @@ const relatedPosts = allPosts
const item = toc[index];
if (item?.id && !heading.id) heading.id = item.id;
});
const summaryText = document.querySelector('.article-summary-text');
if (!summaryText) return;
const fullText = summaryText.dataset.summaryText || summaryText.textContent || '';
const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (reduceMotion) {
summaryText.textContent = fullText;
return;
}
summaryText.textContent = '';
let index = 0;
function typeSummary() {
index += 1;
summaryText.textContent = fullText.slice(0, index);
if (index < fullText.length) {
window.setTimeout(typeSummary, 34);
}
}
window.setTimeout(typeSummary, 280);
})();
</script>