feat(moments): 瞬间富文本与逐条评论(5h T2/T3)
瞬间文字从纯文本直出升级到与文章同级表达力,每条动态获得独立评论流。
T2 富文本:
- text 在 frontmatter 预渲染成 momentId → html 的 Map(renderRich 是 async,
模板内不能 await),模板只此一处 set:html 且喂的是净化产物
- 走与全站文案同一条管线:markdown → 内容净化(标签/属性/协议三层白名单)
→ class 白名单 35 项,故 <span class="c-blue font-kai"> 生效、
<script> 整棵子树被丢弃(实测)
- 纯文本老数据零回归:无 markdown 的 text 得到单个 <p>,与旧直出视觉等价
- 卡片内富文本首尾 margin 归零、段间半档、列表缩进 1.35em,15px/ink-2 基调不变
T3 逐条评论:
- 新增 MomentComments.astro:<details> 折叠,**展开才拉 Artalk 并 init**
(未展开连 Artalk.js 都不下载,时间线一屏十几条不白付成本)
- page_key = /moments/#<slug>,中英双语共用同一评论流(与文章页同策略)
- pvAdd: false 且复用 ViewMeta readonly 计数:读者开评论不会虚增浏览数
- 5s 兜底失败提示 + 重试;软导航回页对已展开的框补挂
- Playwright 实测同页两个容器各自 init 成功(各渲染 31 个 atk-* 节点,无异常),
故无需降级;若日后内存吃紧可对上一实例调 destroy() 一行切换
验证: 构建 exit 0;E2E 37/37(快照口径);临时瞬间实测 markdown 粗体、
彩色+楷体类保留、行内码、<script> 净化丢弃、details 与 /moments/# 键、
ViewMeta 只读挂载全部命中(验收数据已还原)
Spec: 2026-11-02_blog-5h-content-social (T2/T3)
Co-authored-by: GLM-5.3
This commit is contained in:
1 parent
5bf0c327bf
commit
8e74bead62
3 files changed
+285
-3
No files matched your search
@@ -2,7 +2,9 @@
|
||||
import { Image } from 'astro:assets';
|
||||
import { getCollection } from 'astro:content';
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
import MomentComments from '../MomentComments.astro';
|
||||
import { t } from '../../i18n/ui';
|
||||
import { renderRich } from '../../lib/site-content';
|
||||
import type { Locale } from '../../lib/locale';
|
||||
|
||||
// /en/ 侧由 T4 薄页传 locale='en';zh 路由无人传参,默认 zh 零回归
|
||||
@@ -14,6 +16,16 @@ const { locale = 'zh' } = Astro.props;
|
||||
const moments = (await getCollection('moments')).sort(
|
||||
(a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime(),
|
||||
);
|
||||
|
||||
// 富文本预渲染(Phase 5h T2):text 走与页面文案同一条管线(markdown → 内容净化 →
|
||||
// class 白名单 → 链接卡片),因此瞬间支持 **加粗**、<span class="c-blue font-kai">、列表与换行。
|
||||
// 预渲染成 map 再进模板:renderRich 是 async,模板里不能 await。
|
||||
// 纯文本老数据零回归:无 markdown 的文本会得到单个 <p>,与旧的 {text} 直出视觉等价。
|
||||
const richText = new Map<string, string>();
|
||||
for (const m of moments) {
|
||||
if (!m.data.text) continue;
|
||||
richText.set(m.id, await renderRich(m.data.text));
|
||||
}
|
||||
const iso = (d: Date) => d.toISOString();
|
||||
---
|
||||
|
||||
@@ -46,12 +58,13 @@ const iso = (d: Date) => d.toISOString();
|
||||
/>
|
||||
</figure>
|
||||
)}
|
||||
{m.data.text && <p>{m.data.text}</p>}
|
||||
{m.data.text && <div class="mom-text" set:html={richText.get(m.id) ?? ''} />}
|
||||
{m.data.tags.length > 0 && (
|
||||
<p class="mom-tags">
|
||||
{m.data.tags.map((tag) => <code class="tag">{`#${tag}`}</code>)}
|
||||
</p>
|
||||
)}
|
||||
<MomentComments slug={m.id} locale={locale} />
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
@@ -208,6 +221,41 @@ const iso = (d: Date) => d.toISOString();
|
||||
background: var(--bg-soft);
|
||||
}
|
||||
.mom-card > p { font-size: 15px; color: var(--ink-2); }
|
||||
/* 富文本正文(T2):首尾 margin 归零,避免卡片被 <p> 的默认外边距撑开;
|
||||
段间只留半档间距,列表缩进对齐正文基线。基调保持 15px / ink-2。 */
|
||||
.mom-text {
|
||||
font-size: 15px;
|
||||
color: var(--ink-2);
|
||||
line-height: 1.7;
|
||||
}
|
||||
.mom-text :global(> :first-child) { margin-top: 0; }
|
||||
.mom-text :global(> :last-child) { margin-bottom: 0; }
|
||||
.mom-text :global(p) { margin: 0 0 var(--space-1); }
|
||||
.mom-text :global(ul),
|
||||
.mom-text :global(ol) {
|
||||
margin: 0 0 var(--space-1);
|
||||
padding-left: 1.35em;
|
||||
}
|
||||
.mom-text :global(li) { margin: 0; }
|
||||
.mom-text :global(li + li) { margin-top: 2px; }
|
||||
.mom-text :global(blockquote) {
|
||||
margin: var(--space-1) 0;
|
||||
padding-left: var(--space-2);
|
||||
border-left: 2px solid var(--line);
|
||||
color: var(--ink-3);
|
||||
}
|
||||
.mom-text :global(code) {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.92em;
|
||||
background: var(--bg);
|
||||
border-radius: 4px;
|
||||
padding: 1px 5px;
|
||||
}
|
||||
.mom-text :global(a) {
|
||||
color: var(--accent);
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 0.18em;
|
||||
}
|
||||
.mom-fig {
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
|
||||
Reference in new issue
Block a user