From 8debd99e617918c423539c7d7a40ec423ed86682 Mon Sep 17 00:00:00 2001 From: wait4xx Date: Mon, 2 Nov 2026 18:58:11 +0800 Subject: [PATCH] =?UTF-8?q?feat(theme-home):=20=E6=A0=B7=E5=BC=8F=E5=8D=B3?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=20+=20=E9=A6=96=E9=A1=B5=E6=8A=BD=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E5=8E=BB=E9=87=8D=EF=BC=885g=20T3/T4=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 【T3 样式即数据】 - content/site/theme.yaml 九组白名单旋钮(accent 明暗两值 / radius_img / text_h1-h3 / prose_size_reading|tech / leading_reading|tech / space_3-6 / flow_p 与 flow_h2_post / dur);--measure、断点、纸墨、 code 配色按方案 §3.6 红线不开放 - src/lib/theme.ts:getTheme() 钳制 + 单调性校验(h1>h2>h3、 space3<4<5<6 任一不满足整组回落默认);themeVarsCss() 产出 `:root:root{…}` 与 `[data-theme="dark"]:root:root{…}`,靠特异性压过 tokens 的 :root,不依赖注入顺序 - BaseLayout head 内联 ` diff --git a/src/content.config.ts b/src/content.config.ts index 28b222f..86b843f 100644 --- a/src/content.config.ts +++ b/src/content.config.ts @@ -184,6 +184,18 @@ export const collections = { latestPhotos: z.number().int().optional(), /** 首页每轨条数:4-8(越界由访问器夹取,缺省 6) */ perTrackLimit: z.number().int().optional(), + /** 首页项目墙条数:1-99(缺省 6;越界夹取) */ + projectsLimit: z.number().int().optional(), + /** 首页公告(markdown 富文本,走富文本管线);空 = 不渲染 */ + announcement: bilingual.optional(), + /** 首页轨细分开关:分开关阅读/技术/照片三轨(缺省全开) */ + postsTracks: z + .object({ + reading: z.boolean().optional(), + tech: z.boolean().optional(), + photo: z.boolean().optional(), + }) + .optional(), /** 区块开关:缺省全开 */ sections: z .object({ diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro index e4cb051..df17650 100644 --- a/src/layouts/BaseLayout.astro +++ b/src/layouts/BaseLayout.astro @@ -18,6 +18,7 @@ import { siteFavicon } from '../lib/site-content'; import SearchDialog from '../components/SearchDialog.astro'; import BuildInfo from '../components/geek/BuildInfo.astro'; import { ClientRouter } from 'astro:transitions'; +import { themeCss } from '../lib/theme'; interface Props { title: string; @@ -36,6 +37,7 @@ const withSlash = (p: string) => (p.endsWith('/') ? p : `${p}/`); const favicon = await siteFavicon(); // 自指 canonical(T2):SEO 权重收敛到当前页自身,与 hreflang 组合覆盖双轨 const canonicalUrl = new URL(withSlash(rawPath), Astro.site).href; +const themeStyle = await themeCss(); --- @@ -100,7 +102,9 @@ const canonicalUrl = new URL(withSlash(rawPath), Astro.site).href; - + + + diff --git a/src/lib/site-content.ts b/src/lib/site-content.ts index 07c2f11..21f02df 100644 --- a/src/lib/site-content.ts +++ b/src/lib/site-content.ts @@ -223,9 +223,21 @@ export interface HomeSections { projects: boolean; } +export interface HomeTracks { + reading: boolean; + tech: boolean; + photo: boolean; +} + export interface HomeConfig { /** 副标题 markdown 原文;空串 = 不渲染 */ subtitle: string; + /** 公告 markdown 原文;空串 = 不渲染 */ + announcement: string; + /** 首页项目墙条数(1-99,缺省 6) */ + projectsLimit: number; + /** 轨细分开关(缺省全开) */ + postsTracks: HomeTracks; /** 最新影像张数:0 = 隐藏区块 */ latestPhotos: number; /** 每轨文章条数 */ @@ -244,6 +256,13 @@ export async function homeConfig(locale: Locale): Promise { const d = entry?.data; return { subtitle: pickBi(d?.subtitle, { zh: '', en: '' }, locale), + announcement: pickBi(d?.announcement, { zh: '', en: '' }, locale), + projectsLimit: clampInt(d?.projectsLimit, 1, 99, 6), + postsTracks: { + reading: d?.postsTracks?.reading ?? true, + tech: d?.postsTracks?.tech ?? true, + photo: d?.postsTracks?.photo ?? true, + }, latestPhotos: clampInt(d?.latestPhotos, 0, 6, 4), perTrackLimit: clampInt(d?.perTrackLimit, 4, 8, 6), sections: { diff --git a/src/lib/theme.ts b/src/lib/theme.ts new file mode 100644 index 0000000..d587938 --- /dev/null +++ b/src/lib/theme.ts @@ -0,0 +1,143 @@ +/** + * 样式即数据(层 4):把 content/site/theme.yaml 的白名单旋钮编译成 CSS 变量注入串。 + * + * 三条红线(docs/research/10 方案 §3.6): + * ① 只认 9 组白名单旋钮;--measure / 断点 / 纸墨 / code 配色一律不可配(牵动版面结构与分轨身份)。 + * ② 注入用 `:root:root{…}`(0,2,0)与 `[data-theme="dark"]:root:root{…}`(0,3,0)—— + * 必须靠特异性压过 tokens.css 的 `:root`(0,1,0),**不能靠 DOM 顺序**(link 与内联 style 的相对次序不可控)。 + * ③ 值全部钳制 + 单调性校验:越界只回落默认,绝不让构建失败(主题配错不该炸站)。 + * + * 构建期静态:BaseLayout 直接把返回串内联进 + diff --git a/src/pages/index.astro b/src/pages/index.astro index fbc45ad..a26c8d2 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -1,167 +1,5 @@ --- -import { Image } from 'astro:assets'; -import { getCollection } from 'astro:content'; -import BaseLayout from '../layouts/BaseLayout.astro'; -import IdCard from '../components/IdCard.astro'; -import PostList from '../components/PostList.astro'; -import ProjectWall from '../components/geek/ProjectWall.astro'; -import Heatmap from '../components/geek/Heatmap.astro'; -import { pageTypography, renderRich } from '../lib/site-content'; -import { fetchHeartbeat, agoZh } from '../lib/heartbeat'; -import { t } from '../i18n/ui'; -import { homeConfig, homeTagline, renderMarkdown } from '../lib/site-content'; -import SocialLinks from '../components/SocialLinks.astro'; - -// 首页可配项(后台主页设置):副标题/影像张数/区块开关/每轨条数,字段级回落 -const cfg = await homeConfig('zh'); -const subtitleHtml = cfg.subtitle ? await renderMarkdown(cfg.subtitle) : ''; -const posts = (await getCollection('posts', ({ data }) => !data.draft && !data.tags.includes('hidden'))).sort( - (a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime(), -); -// 最新影像取集合前 4 张;照片时间序由照片管线(exif 车道)治理,此处只裁切 -const photos = (await getCollection('photos')).slice(0, Math.max(1, cfg.latestPhotos)); -const heartbeat = await fetchHeartbeat(); -// 身份语改由 content/site/home.yaml 供给(后台可改),缺值回落默认文案 -const tagline = await homeTagline('zh'); -// 层 1:tagline 走富文本(后台可写 ) -const taglineHtml = await renderRich(tagline, { inline: true }); -const typo = await pageTypography('siteHome', 'home'); +// 首页薄壳(逻辑与样式在 src/components/pages/HomePage.astro,zh/en 共用) +import HomePage from '../components/pages/HomePage.astro'; --- - - -
- -

- {subtitleHtml &&

} - - - - {cfg.sections.tracks && } - - {cfg.sections.photos && cfg.latestPhotos > 0 && ( -
-
- 最新影像 - 进入图库 → -
- -
- )} - - {cfg.sections.heatmap && } - {cfg.sections.projects && } - {heartbeat && ( -

- - 最近心跳:{agoZh(heartbeat.when)} · {heartbeat.msg} -

- )} -
- - - +