feat(content): 瞬间/书架标题下的话语改为后台可编辑(5j T3)

用户原话:「瞬间、书架大标题下的话语我从哪里去改?包括其字号颜色还有字体」——
此前这两行是硬编码(瞬间来自 i18n 字典、书架是组件里的字面量),后台完全改不了。

- 新增 sitePages 单例(content/site/pages.yaml)+ pageNote() 访问器:note 走富文本
  行内管线,空则回落内置默认(原硬编码两句逐字搬入 PAGE_NOTE_DEFAULTS)
- 样式只开放三档并全部夹取:size 11–16px、color 仅 ink-1..ink-4、font 仅排版类白名单
  —— 后台可填任意值,不能让它破版或染色
- **关键坑**:字体类不能只挂 class——global.css 的 .font-kai/.font-sans 全挂在富文本
  容器作用域(.prose-*/.rich/.mom-text/.prose-body),裸 .page-note.font-kai 拿不到
  规则(实测第一次无效,仍是 Noto Sans)。改为复用 fontVarFor() 产出 inline
  font-family,class 仅作语义
- 书架与瞬间各自独立配置;默认值取「改造前外观」(均 font-sans)

验证: 构建 exit 0;content-lint 52 页 0/0/0;E2E 56/56;
      实测(只改 books 样式)书架 computed = 16px / rgb(51,52,58) / Ma Shan Zheng,
      瞬间保持 13px / ink-4 / Noto Sans(两页互不影响);
      op-gitea 侧另测:仅改文本、整段删 style(与基线逐字段一致)、非法值
      (size:40/color:red/font 乱写)全部安全回落

Spec: 2026-11-03_blog-5j-content-refine (T3)

Co-authored-by: GLM-5.3
This commit is contained in:
wait4xandGLM-5.3 committed 2026-11-04 20:14:59 +08:00
1 parent 7e0167a8bb
commit 58baad5a7b
3 files changed
+31 -7

No files matched your search

+21
View File
@@ -0,0 +1,21 @@
# 页面标题下的说明文案(瞬间 / 书架):文字、字号、颜色、字体都在这里改。
# 缺省即回落内置默认,删字段不会破版。
# 说明:color 取 tokens 里的灰阶层级(ink-1..ink-4);font 取排版类白名单里的字体类。
moments:
note:
zh: 零碎的记录:随手拍、一句话感想、开源动态。
en: 'Fragmented notes: casual shots, one-liners, and open-source updates.'
style:
size: 13
color: ink-4
font: font-sans
books:
note:
zh: 点一行展开封面与简介 · 置顶的书默认展开
en: 'Tap a row for the cover and blurb · pinned books start open'
style:
size: 13
color: ink-4
font: font-sans # 与改造前一致;想用楷体改成 font-kai
+5 -5
View File
@@ -18,11 +18,14 @@ import BaseLayout from '../../layouts/BaseLayout.astro';
import { t } from '../../i18n/ui'; import { t } from '../../i18n/ui';
import type { Locale } from '../../lib/locale'; import type { Locale } from '../../lib/locale';
import { sortPosts } from '../../lib/post-order'; import { sortPosts } from '../../lib/post-order';
import { pageNote } from '../../lib/site-content';
interface Props { interface Props {
locale?: Locale; locale?: Locale;
} }
const { locale = 'zh' } = Astro.props; const { locale = 'zh' } = Astro.props;
// 标题下说明:文字与样式都可后台改(白名单校验,非法值回落)
const note = await pageNote('books', locale);
const books = sortPosts( const books = sortPosts(
await getCollection( await getCollection(
@@ -58,10 +61,6 @@ const pinnedCount = items.filter((i) => i.pinned).length;
const L = { const L = {
shelf: t('books_title', locale), shelf: t('books_title', locale),
count: locale === 'zh' ? `${items.length} 本` : `${items.length} books`, count: locale === 'zh' ? `${items.length} 本` : `${items.length} books`,
note:
locale === 'zh'
? '点一行展开封面与简介 · 置顶的书默认展开'
: 'Tap a row for the cover and blurb · pinned books start open',
read: locale === 'zh' ? '读这篇书评 →' : 'Read the review →', read: locale === 'zh' ? '读这篇书评 →' : 'Read the review →',
pinned: t('pinned_label', locale), pinned: t('pinned_label', locale),
expand: locale === 'zh' ? '展开封面与简介' : 'Expand cover and blurb', expand: locale === 'zh' ? '展开封面与简介' : 'Expand cover and blurb',
@@ -77,7 +76,8 @@ const L = {
{L.shelf}<span class="count mono">{L.count}</span> {L.shelf}<span class="count mono">{L.count}</span>
{pinnedCount > 0 && <span class="pinned-count mono">{pinnedCount} {L.pinned}</span>} {pinnedCount > 0 && <span class="pinned-count mono">{pinnedCount} {L.pinned}</span>}
</h1> </h1>
<p class="page-note">{L.note}</p> <!-- 标题下说明:由 content/site/pages.yaml 的 books.note/style 驱动 -->
<p class:list={['page-note', note.className]} style={note.style} set:html={note.html} />
<ul class="books"> <ul class="books">
{items.map((b) => ( {items.map((b) => (
+5 -2
View File
@@ -5,7 +5,7 @@ import BaseLayout from '../../layouts/BaseLayout.astro';
import MomentComments from '../MomentComments.astro'; import MomentComments from '../MomentComments.astro';
import ViewMeta from '../geek/ViewMeta.astro'; import ViewMeta from '../geek/ViewMeta.astro';
import { t } from '../../i18n/ui'; import { t } from '../../i18n/ui';
import { renderRich } from '../../lib/site-content'; import { pageNote, renderRich } from '../../lib/site-content';
import type { Locale } from '../../lib/locale'; import type { Locale } from '../../lib/locale';
// /en/ 侧由 T4 薄页传 locale='en';zh 路由无人传参,默认 zh 零回归 // /en/ 侧由 T4 薄页传 locale='en';zh 路由无人传参,默认 zh 零回归
@@ -13,6 +13,8 @@ interface Props {
locale?: Locale; locale?: Locale;
} }
const { locale = 'zh' } = Astro.props; const { locale = 'zh' } = Astro.props;
// 标题下说明:文字与样式都可后台改(白名单校验,非法值回落)
const note = await pageNote('moments', locale);
const moments = (await getCollection('moments')).sort( const moments = (await getCollection('moments')).sort(
(a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime(), (a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime(),
@@ -32,7 +34,8 @@ const iso = (d: Date) => d.toISOString();
<BaseLayout title={`${t('moments_title', locale)} · wait4x`}> <BaseLayout title={`${t('moments_title', locale)} · wait4x`}>
<h1 class="page-title">{t('moments_title', locale)}</h1> <h1 class="page-title">{t('moments_title', locale)}</h1>
<p class="page-note">{t('moments_note', locale)}</p> <!-- 标题下说明:文案/字号/颜色/字体均由后台「页面文案 → 页面说明」控制(site-content.pageNote) -->
<p class:list={['page-note', note.className]} style={note.style} set:html={note.html} />
<!-- 整页计数(Phase 5h T4):这是本页唯一的计数点(page 模式); <!-- 整页计数(Phase 5h T4):这是本页唯一的计数点(page 模式);
每条瞬间卡片底部的徽标是 readonly,读者开评论不会虚增浏览数 --> 每条瞬间卡片底部的徽标是 readonly,读者开评论不会虚增浏览数 -->
<ViewMeta class="mom-page-meta" pageKey={locale === 'en' ? '/en/moments/' : '/moments/'} {locale} variant="chip" /> <ViewMeta class="mom-page-meta" pageKey={locale === 'en' ? '/en/moments/' : '/moments/'} {locale} variant="chip" />