--- // 书评索引画廊(/books):色脊排版卡 × 年份分组规整网格 // 预研方案 B:色脊天然安全(mood 深浅跨亮暗对比度风险回避),零 JS import { getCollection } from 'astro:content'; import BaseLayout from '../../layouts/BaseLayout.astro'; import { t } from '../../i18n/ui'; import type { Locale } from '../../lib/locale'; interface Props { locale?: Locale } const { locale = 'zh' } = Astro.props; const books = (await getCollection('posts', ({ data }) => !data.draft && data.category === 'reading' && !data.tags.includes('hidden'))) .sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf()); // 回退契约(schema 注释声称而此前未实现——视觉复核抓到的缺口): // description 缺省取正文第一个非空段落(去 markdown 记号)前 60 字 const firstPara = (body: string | undefined): string => { const line = (body ?? '').split(/\n\s*\n/).map((l) => l.trim()).find((l) => l && !l.startsWith('#') && !l.startsWith('---')) ?? ''; return line.replace(/[*_>`\[\]]/g, '').slice(0, 60); }; const years = [...new Set(books.map((b) => b.data.pubDate.getFullYear()))].sort((a, b) => b - a); const L = { shelf: locale === 'zh' ? '书架' : 'Books', count: (n: number) => (locale === 'zh' ? `${n} 本` : `${n} books`), }; ---

{L.shelf}{L.count(books.length)}

{years.map((year) => (

{year}

))}