From 8e74bead62cc1f97c675e7d6b29594e8dfa2a673 Mon Sep 17 00:00:00 2001 From: wait4xx Date: Mon, 2 Nov 2026 20:20:31 +0800 Subject: [PATCH] =?UTF-8?q?feat(moments):=20=E7=9E=AC=E9=97=B4=E5=AF=8C?= =?UTF-8?q?=E6=96=87=E6=9C=AC=E4=B8=8E=E9=80=90=E6=9D=A1=E8=AF=84=E8=AE=BA?= =?UTF-8?q?=EF=BC=885h=20T2/T3=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 瞬间文字从纯文本直出升级到与文章同级表达力,每条动态获得独立评论流。 T2 富文本: - text 在 frontmatter 预渲染成 momentId → html 的 Map(renderRich 是 async, 模板内不能 await),模板只此一处 set:html 且喂的是净化产物 - 走与全站文案同一条管线:markdown → 内容净化(标签/属性/协议三层白名单) → class 白名单 35 项,故 生效、 + + diff --git a/src/components/pages/MomentsPage.astro b/src/components/pages/MomentsPage.astro index 44fadd8..6247952 100644 --- a/src/components/pages/MomentsPage.astro +++ b/src/components/pages/MomentsPage.astro @@ -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 白名单 → 链接卡片),因此瞬间支持 **加粗**、、列表与换行。 +// 预渲染成 map 再进模板:renderRich 是 async,模板里不能 await。 +// 纯文本老数据零回归:无 markdown 的文本会得到单个

,与旧的 {text} 直出视觉等价。 +const richText = new Map(); +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(); /> )} - {m.data.text &&

{m.data.text}

} + {m.data.text &&
} {m.data.tags.length > 0 && (

{m.data.tags.map((tag) => {`#${tag}`})}

)} +
))} @@ -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 归零,避免卡片被

的默认外边距撑开; + 段间只留半档间距,列表缩进对齐正文基线。基调保持 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;