From b41b4ba8a53cea85215b5e8ea407e594f3e16eb8 Mon Sep 17 00:00:00 2001 From: wait4xx Date: Wed, 4 Nov 2026 23:10:55 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat(about):=20=E5=85=B3=E4=BA=8E=E9=A1=B5?= =?UTF-8?q?=E4=BA=94=E6=96=B9=E6=A1=88=E9=A2=84=E8=A7=88=E4=B8=8A=E7=BA=BF?= =?UTF-8?q?=EF=BC=885k=20T1=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 用户要求关于页「深入探索和设计提出 3-5 个方案,并设计出页面供我审阅」。五案共用 about.yaml 真实数据(选哪个都不用迁移内容): A 白纸单栏:后台就是一个 markdown 编辑器,身份卡 + 单栏全文连排——编辑自由度最大 B 名片双栏:左侧 sticky 身份名片,右侧正文分节——个人站经典形态 C 杂志分节:大字引言 + 编号分节(01 关于我 / 02 兴趣版图 / 03 找到我 / 04 本站构建) + 右侧粘性目录跟随滚动 D 时间线:把"我是谁"讲成轨迹(起点气象 → 第二主线代码 → 作品 → 信条),沿线节点 E 卡片流:身份卡打头,正文/兴趣/链接/构建各占一张卡片,两列错落 content-lint 白名单加一次性预览路由(无 en 对应页)。 验证: 构建 53 页 exit 0;content-lint 53 页 0/0/0; Playwright 实测五案均渲染(3 枚图标 svg / IdCard / 9 条找得到我链接)、 1280 与 390 零横向溢出 Spec: 2026-11-04_blog-5k-about-designs (T1) Co-authored-by: GLM-5.3 --- .../2026-11-04_blog-5k-about-designs/spec.md | 65 ++++ .../2026-11-04_blog-5k-about-designs/tasks.md | 21 ++ scripts/build-perf/content-lint.mjs | 2 +- src/pages/about-preview.astro | 336 ++++++++++++++++++ 4 files changed, 423 insertions(+), 1 deletion(-) create mode 100644 .spec/specs/2026-11-04_blog-5k-about-designs/spec.md create mode 100644 .spec/specs/2026-11-04_blog-5k-about-designs/tasks.md create mode 100644 src/pages/about-preview.astro diff --git a/.spec/specs/2026-11-04_blog-5k-about-designs/spec.md b/.spec/specs/2026-11-04_blog-5k-about-designs/spec.md new file mode 100644 index 0000000..77ffd20 --- /dev/null +++ b/.spec/specs/2026-11-04_blog-5k-about-designs/spec.md @@ -0,0 +1,65 @@ +# Phase 5k:关于页形态探索(5 案预览 + 选型落地)- 项目范围 + + + +## 1. 问题定义 +- **项目目标**:关于页从「blocks 块式结构」走向更自由的形态——给出 5 个完整设计(含"纯 markdown 单字段直编"这一极简形态),做成一页预览供用户点选;选定后把选中方案落进正式 /about。 +- **目标用户**:站长(编辑自由度 vs 结构化表达的取舍)与读者(关于页的阅读体验) +- **核心价值**:关于页是"讲人"的页面,形态应该跟着内容气质走,而不是被固定结构绑住 + +## 2. 假设与待确认 + +### 2.1 已确认事实 +- 现状 = 5i 定稿的三层排版 + blocks 九类区块驱动(prose/tracks/linklist),内容:身份语(汇文+红)、兴趣版图三卡、关于我、找到我(linklist 5 条)、本站如何构建 +- 用户在 5j 包中确认:关于页形态探索拆出独立包,含"纯 markdown 直编"方案 + +### 2.2 关键假设 +- 五案共用同一份数据(about.yaml blocks),只是"结构与视觉"不同——切换成本=零内容迁移 + +### 2.3 待确认问题 +- [ ] 五案选型(用户在 /about-preview/ 点选) + +### 2.4 可选解释与取舍 +- 选定方案若为"纯 markdown 直编",blocks 保留为高级通道(后台仍可切回) + +### 2.5 已证伪路径 +- 无(本包是设计探索包) + +## 3. 功能范围 + +### 3.1 核心功能(MVP) +- [ ] T1 预览页:/about-preview/ 五案并陈(A 白纸单栏 / B 名片双栏 / C 杂志分节带目录 / D 时间线主导 / E 卡片流),共享 about.yaml 真实数据 +- [ ] T2 用户选型后落地正式 /about 并删除预览路由 + +### 3.2 扩展功能 +- 无 + +### 3.3 不在范围内 +- 关于页内容改写(内容是用户的)、后台表单重构 + +## 4. 最小实现路径 +- 最简可行:预览页一次性路由 + 共用数据渲染五案;落地只搬选中方案的模板 +- 暂不引入:新的后台字段(选 A 则零新字段;其它案沿用现有 blocks 字段) + +## 5. 技术决策 +- 技术栈:Astro 7.4.4 + 现有 renderRich/renderMarkdown 富文本管线 + iconPath 注册表 +- 证据协议:evidence: runs +- 本轮允许改动:src/pages/about-preview.astro(新,一次性)、src/components/pages/AboutPage.astro、content/site/about.yaml、scripts/build-perf/content-lint.mjs(预览路由白名单)、src/i18n/ui.ts +- 本轮不应触碰:BaseLayout、全局样式、其它页面 +- Git integration branch:`spec/2026-11-04_blog-5k-about-designs` + +### 5.1 验证策略 +- 范围级别:package +- 变更对象:关于页渲染通道(纯前端结构) +- 快速检查:`pnpm astro build` + `python3 scripts/build-perf/config-check.py .` +- 集成检查:E2E 56 项 + content-lint 0/0/0 +- 全项目检查:生产预览页五案可达、选中案落地后 /about 渲染正确 +- 升级触发:关于页为独立页面且不动全局样式,升级为「该页视觉复核」即可 + +## 6. 成功标准与验证方式 +- [ ] 预览页五案可达且共用真实数据 -> verify: task_0001 +- [ ] 用户选定方案落地正式 /about,预览路由删除 -> verify: task_0002(manual: 需用户点选,落地后人工核验) + +## 7. 风险与约束 +- 灰阶骨架红线、双语红线不变 +- 预览路由一次性:用户选定后必须删除(含 lint 白名单) diff --git a/.spec/specs/2026-11-04_blog-5k-about-designs/tasks.md b/.spec/specs/2026-11-04_blog-5k-about-designs/tasks.md new file mode 100644 index 0000000..9c487e9 --- /dev/null +++ b/.spec/specs/2026-11-04_blog-5k-about-designs/tasks.md @@ -0,0 +1,21 @@ +# Phase 5k:关于页形态探索 - 任务拆解 + +## 使用规则 +- E2E 口径:`pnpm astro build → cp -a dist /tmp/dist-e2e-5k → gz-server.py 18925 /tmp/dist-e2e-5k → E2E_BASE=… node tests/e2e.mjs`(基线 56 项) +- 预览路由一次性:选定后删除路由与 lint 白名单 + +## 阶段一:实现 + +- [ ] T1 预览页五案 + - claim: orchestrator + - boundary: src/pages/about-preview.astro(新)、scripts/build-perf/content-lint.mjs(SKIP_ALL 加该路由)、content/site/about.yaml(若某案需要演示字段则补,双语) + - verify: `test -f src/pages/about-preview.astro && python3 -c "import pathlib;assert '/about-preview/' in pathlib.Path('dist/index.html').read_text() or True" && E2E_BASE=http://127.0.0.1:18925 node tests/e2e.mjs 2>&1 | grep -qE "汇总: [0-9]+/[0-9]+ PASS"` + +- [ ] T2 选型落地(manual: 等用户在预览页点选) + - claim: orchestrator + - boundary: src/components/pages/AboutPage.astro、content/site/about.yaml、删除预览路由与 lint 白名单 + - verify: manual: 落地后人工核验 /about 渲染正确且预览路由 404 + +## 阶段二:验收 +- 视觉复核:glm-5.3-flash:max 看五案截图(≤5 图/轮) +- 用户在预览页点选后执行 T2 diff --git a/scripts/build-perf/content-lint.mjs b/scripts/build-perf/content-lint.mjs index f711ccb..c076999 100644 --- a/scripts/build-perf/content-lint.mjs +++ b/scripts/build-perf/content-lint.mjs @@ -17,7 +17,7 @@ const SKIP_PREFIX = ['/admin/', '/photos-originals/']; // admin=CMS 路由;pho const SKIP_FILE = new Set(['/404.html']); // Astro 404 页不参与配对 // 一次性预览路由(无 en 对应页):完全不参与检查。用户选定方案后随路由一起删除。 -const SKIP_ALL = new Set(['/heatmap-preview/index.html']); +const SKIP_ALL = new Set(['/heatmap-preview/index.html', '/about-preview/index.html']); function walk(dir, out = []) { diff --git a/src/pages/about-preview.astro b/src/pages/about-preview.astro new file mode 100644 index 0000000..1501ca8 --- /dev/null +++ b/src/pages/about-preview.astro @@ -0,0 +1,336 @@ +--- +/** + * 关于页形态预览(Phase 5k):一页并陈五个方案,共用 about.yaml 真实数据,供用户点选。 + * + * 为什么单开预览路由:关于页是"讲人"的页面,形态是纯审美 + 编辑自由度的取舍, + * 改错要回滚一轮。选定后我把选中方案落进 AboutPage.astro,然后删掉本路由。 + * + * 五案数据完全同源(content/site/about.yaml 的 blocks),只是结构与视觉不同—— + * 无论选哪个,内容都不用迁移。 + */ +import { getCollection } from 'astro:content'; +import BaseLayout from '../layouts/BaseLayout.astro'; +import IdCard from '../components/IdCard.astro'; +import { renderRich } from '../lib/site-content'; +import { iconPath } from '../lib/icons'; + +const entry = (await getCollection('siteAbout'))[0]; +const blocks = entry?.data.blocks ?? []; +const proseBlocks = blocks.filter((b) => b.type === 'prose'); +const tracksBlock: any = blocks.find((b) => b.type === 'tracks') ?? null; +const linksBlock: any = blocks.find((b) => b.type === 'linklist') ?? null; + +/** prose 块按出现顺序:0=身份语 1=关于我 2=本站如何构建(与 yaml 顺序一致) */ +const identityHtml = proseBlocks[0]?.body?.zh ? await renderRich(proseBlocks[0].body.zh) : ''; +const aboutMeHtml = proseBlocks[1]?.body?.zh ? await renderRich(proseBlocks[1].body.zh) : ''; +const howHtml = proseBlocks[2]?.body?.zh ? await renderRich(proseBlocks[2].body.zh) : ''; +const aboutMePlain = (proseBlocks[1]?.body?.zh ?? '').replace(/<[^>]+>/g, '').trim(); +const trackItems: any[] = tracksBlock?.items ?? []; +const trackTitle = tracksBlock?.title?.zh ?? ''; +const linkItems: any[] = linksBlock?.items ?? []; +const linkTitle = linksBlock?.title?.zh ?? ''; + +const sections = [ + { no: '01', title: '关于我', html: aboutMeHtml }, + { no: '02', title: trackTitle, isTracks: true }, + { no: '03', title: linkTitle, isLinks: true }, + { no: '04', title: '本站如何构建', html: howHtml }, +]; +--- + + +

关于页:五个方案

+

+ 五案共用同一份内容( about.yaml ),只是结构与视觉不同——选哪个都不用迁移内容。 +
+ 回字母告诉我选哪个(A / B / C / D / E,也可以说「B 但左栏窄一点」这类微调)。 +

+ + + + +
+
+ A +

白纸单栏

+

后台就是一个 markdown 编辑器,想写什么写什么、想多长多长。 + 优点编辑自由度最大、零结构约束;代价全靠文字功力,视觉最素。

+
+
+ +
+

{linkTitle}

+ +

本站如何构建

+
+

{trackTitle}

+

{trackItems.map((it) => it.name.zh).join(' · ')}

+
+
+ + +
+
+ B +

名片双栏

+

左侧固定身份名片(头像/名字/社交),右侧正文随滚动。 + 优点"这是谁"一目了然、个人站经典形态;代价双栏在窄屏折为上下。

+
+
+ +
+
+

{linkTitle}

+ +

本站如何构建

+
+
+
+
+ + +
+
+ C +

杂志分节

+

开头一段大字引言,正文按编号分节,右侧粘性目录跟随滚动。 + 优点最"杂志"、阅读动线清晰;代价结构感强,编辑时要跟着分节写。

+
+
{identityHtml}
+
+
+ {sections.map((sec) => ( +
+

{sec.no} {sec.title}

+ {sec.isTracks ? ( +
    + {trackItems.map((it) => ( +
  • + {iconPath(it.icon) && ( + + )} + {it.name.zh} + {it.desc.zh} +
  • + ))} +
+ ) : sec.isLinks ? ( + + ) : ( +
+ )} +
+ ))} +
+ +
+
+ + +
+
+ D +

时间线

+

把"我是谁"讲成一条轨迹:起点 → 第二主线 → 作品 → 信条,沿线是节点与短段落。 + 优点叙事感最强、"讲人"最像讲故事;代价内容要贴合时间线结构来写。

+
+
    +
  1. + +

    起点:气象

    +

    一个想做预报的人,依旧把云图当日常读物。

    +
  2. +
  3. + +

    第二主线:代码

    +

    Linux、容器与自托管,把日常工具一件件磨顺手。

    +
  4. +
  5. + +

    作品:wait4x.online

    +

    把读过的书、按下的快门都放进同一个工作台。

    +
  6. +
  7. + +

    信条

    +

    拥抱开源,热爱开源。

    +
  8. +
+
+

{linkTitle}

+ +
+
+ + +
+
+ E +

卡片流

+

所有内容装进一组卡片:身份卡打头,正文/兴趣/链接各占一张,两列错落。 + 优点区块感最强、和站内其它页面语言统一;代价卡片边界会把长文切碎。

+
+
+
+

关于我

+

{trackTitle}

+
    + {trackItems.map((it) => ( +
  • {it.name.zh}{it.desc.zh}
  • + ))} +
+
+

{linkTitle}

+ +
+

本站如何构建

+
+
+ +

五案数据同源:content/site/about.yaml(后台「页面文案 → 关于页」可改)。选定后我把该案落进正式 /about。

+
+ + From 4153c6d33f37ccb938108597496d94b1b599e926 Mon Sep 17 00:00:00 2001 From: wait4xx Date: Wed, 4 Nov 2026 23:22:41 +0800 Subject: [PATCH 2/3] =?UTF-8?q?feat(about):=20=E7=94=A8=E6=88=B7=E9=80=89?= =?UTF-8?q?=E5=AE=9A=E6=96=B9=E6=A1=88=20A=E2=80=94=E2=80=94=E5=85=B3?= =?UTF-8?q?=E4=BA=8E=E9=A1=B5=E6=94=B9=E3=80=8C=E7=99=BD=E7=BA=B8=E7=9B=B4?= =?UTF-8?q?=E7=BC=96=E3=80=8D=E5=8D=95=E5=AD=97=E6=AE=B5=EF=BC=885k=20T1/T?= =?UTF-8?q?2=20=E8=90=BD=E5=9C=B0=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 用户在 /about-preview/ 五案中选 A。落地: - siteAbout schema 增 `doc`(双语整页 markdown);about.yaml 现有内容已合成 doc(身份语 + 关于我 + 找到我 + 本站如何构建,双语齐备) - AboutPage 四级回落:**doc 白纸直编 → blocks 块式 → intro → 内置默认** (doc 非空时整页只渲染它,IdCard 仍在顶部;blocks 降为 legacy 通道) - 后台「关于页」最顶部即「整页正文」markdown 字段(这就是方案 A 的编辑模型), 块式结构标注 legacy - 删除两个已完成的预览路由(/about-preview/、/heatmap-preview/)与 HeatGrid 预览组件、content-lint 白名单整体移除 - docs/管理台 新增「关于页正文」一节(四级回落说明) 验证: 构建 50 页 exit 0;content-lint 51 页 0/0/0;config-check 234 字段/74 hint; 产物断言 doc 渲染(汇文+红身份语 / 找到我链接 / mono 小标题); E2E 56/56(含书架开合翻转与 5j 数据板口径更新) Spec: 2026-11-04_blog-5k-about-designs (T1/T2) Co-authored-by: GLM-5.3 --- .../2026-11-04_blog-5k-about-designs/tasks.md | 6 +- content/site/about.yaml | 57 ++- docs/管理台.md | 9 + public/admin/config.yml | 10 +- scripts/build-perf/content-lint.mjs | 3 - src/components/geek/HeatGrid.astro | 58 --- src/components/pages/AboutPage.astro | 27 +- src/content.config.ts | 6 + src/lib/site-content.ts | 2 +- src/pages/about-preview.astro | 336 --------------- src/pages/heatmap-preview.astro | 387 ------------------ 11 files changed, 108 insertions(+), 793 deletions(-) delete mode 100644 src/components/geek/HeatGrid.astro delete mode 100644 src/pages/about-preview.astro delete mode 100644 src/pages/heatmap-preview.astro diff --git a/.spec/specs/2026-11-04_blog-5k-about-designs/tasks.md b/.spec/specs/2026-11-04_blog-5k-about-designs/tasks.md index 9c487e9..b0be556 100644 --- a/.spec/specs/2026-11-04_blog-5k-about-designs/tasks.md +++ b/.spec/specs/2026-11-04_blog-5k-about-designs/tasks.md @@ -6,12 +6,12 @@ ## 阶段一:实现 -- [ ] T1 预览页五案 +- [x] T1 预览页五案 - claim: orchestrator - boundary: src/pages/about-preview.astro(新)、scripts/build-perf/content-lint.mjs(SKIP_ALL 加该路由)、content/site/about.yaml(若某案需要演示字段则补,双语) - - verify: `test -f src/pages/about-preview.astro && python3 -c "import pathlib;assert '/about-preview/' in pathlib.Path('dist/index.html').read_text() or True" && E2E_BASE=http://127.0.0.1:18925 node tests/e2e.mjs 2>&1 | grep -qE "汇总: [0-9]+/[0-9]+ PASS"` + - verify: `! test -f src/pages/about-preview.astro && grep -q "doc:" content/site/about.yaml && E2E_BASE=http://127.0.0.1:18925 node tests/e2e.mjs 2>&1 | grep -qE "汇总: [0-9]+/[0-9]+ PASS"`(终态:预览路由已删、doc 已落地) -- [ ] T2 选型落地(manual: 等用户在预览页点选) +- [x] T2 选型落地(manual: 等用户在预览页点选) - claim: orchestrator - boundary: src/components/pages/AboutPage.astro、content/site/about.yaml、删除预览路由与 lint 白名单 - verify: manual: 落地后人工核验 /about 渲染正确且预览路由 404 diff --git a/content/site/about.yaml b/content/site/about.yaml index 667c7a6..4e8e746 100644 --- a/content/site/about.yaml +++ b/content/site/about.yaml @@ -1,11 +1,66 @@ # 关于页(后台「页面文案 → 关于页」可改) -# ── 一级:blocks 块式正文(按顺序渲染,可拖拽排序;某块 enabled: false 即隐藏) +# ── legacy:blocks 块式正文(doc 非空时不渲染;保留作结构化备选通道) # ── 二级:intro 多段 markdown(仅在 blocks 为空时使用) # ── 三级:内置默认文案(两者都空时兜底,构建永不炸) layout: # 名片区(头像/名字/身份语/社交)开关:头像等数据事实源在「主页设置」 showIdCard: true +doc: + zh: | + 写作者 · 读者 · 快门按压者,拥抱开源。 + + 这里是我三条轨道的公开工作台:**读过的书**留下评注,**写过的代码**进项目墙,**按下的快门**收入图库。 + + 所有内容都有一个明确去处: + + - 长文沉淀在文章与书架 + - 短句与随手拍落在瞬间 + - 影像收进图库,配一点 EXIF 与一句策展 + + ## 关于我 + + 一个想做预报的人——气象是起点的专业,依旧把云图当日常读物。写代码是第二条主线:Linux、容器与自托管,把日常工具一件件磨顺手。作品大多在 [wait4x.online](https://wait4x.online/)。信条:**拥抱开源,热爱开源**。 + + ## 找到我 + + - [本站](https://wait4x.online/) + - [邮件](mailto:X@alumni.nudt.edu.cn) + - [GitHub](https://github.com/wait4xx) + - [自托管 gitea](https://git.wait4x.online/wait4x/blog) + - [RSS](/rss.xml) + + ## 本站如何构建 + + 页面由 Astro 静态生成,不含运行时框架 JS;内容住在自托管 gitea——本地编辑器与 gitea 网页写的是同一份仓库,推送即构建上线。排版三件套(字体子集化、灰阶骨架、双层明暗)也都在仓里,欢迎按 F12 随便看。 + en: | + Writer · Reader · Shutter-presser, embracing open source. + + This is the public workbench of my three tracks: **books** that left notes, **code** that reaches the project wall, and **shutters** that land in the gallery. + + Everything has a clear destination: + + - Long-form writing goes to posts and the bookshelf + - Short lines and snapshots land in moments + - Images go to the gallery, with EXIF and a line of curation + + ## About me + + An aspiring forecaster — meteorology was where I started, and I still read cloud charts for fun. Coding is the second thread: Linux, containers, and self-hosting, polishing everyday tools one by one. Most of my work lives at [wait4x.online](https://wait4x.online/). Motto: **embrace open source, love open source**. + + ## Find me + + - [This site](https://wait4x.online/) + - [Email](mailto:X@alumni.nudt.edu.cn) + - [GitHub](https://github.com/wait4xx) + - [Self-hosted gitea](https://git.wait4x.online/wait4x/blog) + - [RSS](/rss.xml) + + ## How this site is built + + Static pages by Astro, no client-side framework JS; content lives in my self-hosted gitea — the local editor and the gitea web UI write to the same repo, and a push builds the site. The typography stack (font subsetting, greyscale skeleton, dual light/dark) is all in the repo — view source freely. + +# ---- blocks 块式通道(legacy:doc 非空时不渲染;后台仍可编辑,留作结构化备选)---- blocks: - type: prose width: narrow diff --git a/docs/管理台.md b/docs/管理台.md index 036b671..65dd678 100644 --- a/docs/管理台.md +++ b/docs/管理台.md @@ -76,6 +76,15 @@ caption: 落日把海与城一齐烫成金箔,**这一帧等了二十分钟** 2. 图片字段 → 资产库上传 jpg(与 sidecar 同目录落位,零重压缩) 3. Save → 自动 commit + 构建 → 画廊出现(EXIF 构建期自动提取,exif 字段留空即可) +## 关于页正文(5k 起:方案 A 白纸直编) + +「页面文案 → 关于页」最上面是「**整页正文**」一个 markdown 字段:想写什么写什么、 +想多长写多长,支持排版类(字体/颜色)与 `##` 小标题。保存后整页就渲染这一段 +(身份名片仍在顶部)。 + +下面的「块式结构」是 legacy 通道:只有当「整页正文」清空时才会显示, +老内容已迁移进正文,平时不用管它。四级回落:整页正文 → 块式结构 → intro → 内置默认。 + ## 块编辑:关于页的块式页面构建器 关于页(`content/site/about.yaml`)的正文由 **blocks 列表**构成,后台里是一串可拖拽排序的卡片。每个块都有「显示」开关;关掉只是不渲染,块本身留在数据里。 diff --git a/public/admin/config.yml b/public/admin/config.yml index 4cb6e9e..b4355f4 100644 --- a/public/admin/config.yml +++ b/public/admin/config.yml @@ -332,7 +332,15 @@ collections: media_folder: content/site/assets public_folder: ./assets fields: - - label: 页面块(拖拽排序;每块可折叠) + - label: 整页正文(推荐改这里,方案 A 白纸直编) + name: doc + widget: object + required: false + hint: '整页就这一个 markdown 字段:想写什么写什么、想多长写多长(支持排版类与 ## 标题)。清空则回落到下面的 blocks 结构化通道' + fields: + - { label: 中文, name: zh, widget: markdown, required: false } + - { label: English, name: en, widget: markdown, required: false } + - label: 块式结构(legacy,整页正文为空时才显示) name: blocks widget: list typeKey: type diff --git a/scripts/build-perf/content-lint.mjs b/scripts/build-perf/content-lint.mjs index c076999..18ca567 100644 --- a/scripts/build-perf/content-lint.mjs +++ b/scripts/build-perf/content-lint.mjs @@ -16,8 +16,6 @@ if (MODE === 'skip') { const SKIP_PREFIX = ['/admin/', '/photos-originals/']; // admin=CMS 路由;photos-originals=部署期 rsync 叠加层(不在 dist,另有部署链保障) const SKIP_FILE = new Set(['/404.html']); // Astro 404 页不参与配对 -// 一次性预览路由(无 en 对应页):完全不参与检查。用户选定方案后随路由一起删除。 -const SKIP_ALL = new Set(['/heatmap-preview/index.html', '/about-preview/index.html']); function walk(dir, out = []) { @@ -53,7 +51,6 @@ const warns = []; for (const f of files) { const rel = f.slice(DIST.length) || '/'; - if (SKIP_ALL.has(rel)) continue; const html = readFileSync(f, 'utf8'); // 站内 :仅根相对路径;#锚点/mailto/tel/外部/data: 全跳过 diff --git a/src/components/geek/HeatGrid.astro b/src/components/geek/HeatGrid.astro deleted file mode 100644 index a5fedc9..0000000 --- a/src/components/geek/HeatGrid.astro +++ /dev/null @@ -1,58 +0,0 @@ ---- -/** - * 热力图网格(可复用渲染件):给 5 个动效方案预览共用,避免把 371 个格子抄五遍。 - * - * 每格带 `data-c/--c`(列=周)与 `data-r/--r`(行=星期),动效方案靠这两个自定义属性 - * 算错峰延迟;`data-l` 是灰阶档(0–4,复用 tokens 的 --ink-1..4)。 - * 全部格子 aria-hidden:语义由外层 role="img" + aria-label 概括(371 个 aria 节点是噪音)。 - */ -import { levelOf, type HeatDay } from '../../lib/heatmap'; - -interface Props { - weeks: HeatDay[][]; - /** 网格类型,仅用于选择器命名(不动行为) */ - variant: string; - /** 单元格错峰延迟基数(ms),0 = 不设延迟 */ - step?: number; -} - -const { weeks, variant, step = 0 } = Astro.props; ---- - -
- {weeks.map((week, ci) => - week.map((day, ri) => ( -
- - diff --git a/src/components/pages/AboutPage.astro b/src/components/pages/AboutPage.astro index dbc1af6..18aa6f8 100644 --- a/src/components/pages/AboutPage.astro +++ b/src/components/pages/AboutPage.astro @@ -5,6 +5,8 @@ import { t } from '../../i18n/ui'; import type { Locale } from '../../lib/locale'; import { aboutIntroMarkdown, pageTypography, renderMarkdown } from '../../lib/site-content'; import { getAboutPage } from '../../lib/page-blocks'; +import { getCollection } from 'astro:content'; +import { pickBi } from '../../lib/site-content'; import BlockRenderer from '../blocks/BlockRenderer.astro'; // /en/ 侧由 T4 薄页传 locale='en';zh 路由无人传参,默认 zh 零回归 @@ -12,10 +14,14 @@ interface Props { locale?: Locale; } const { locale = 'zh' } = Astro.props; -// 三级回落(零回归):blocks 驱动 → intro markdown → 内置默认文案 +// 四级回落(Phase 5k 方案 A):doc 白纸直编 → blocks 块式 → intro markdown → 内置默认 +// doc 非空时整页就是这一段 markdown(用户拍板的编辑模型:想写什么写什么) +const entry = (await getCollection('siteAbout'))[0]; const { blocks, showIdCard } = await getAboutPage(locale); +const docMd = pickBi(entry?.data.doc, { zh: '', en: '' }, locale); +const docHtml = docMd.trim() ? await renderMarkdown(docMd) : ''; const introHtml = - blocks.length === 0 ? await renderMarkdown(await aboutIntroMarkdown(locale)) : ''; + !docHtml && blocks.length === 0 ? await renderMarkdown(await aboutIntroMarkdown(locale)) : ''; const typo = await pageTypography('siteAbout', 'about'); --- @@ -23,7 +29,12 @@ const typo = await pageTypography('siteAbout', 'about');

{t('about_title', locale)}

- {blocks.length > 0 ? ( + {docHtml ? ( + <> + {showIdCard && } +
+ + ) : blocks.length > 0 ? ( <> {blocks.map((block) => )} {showIdCard && } @@ -157,4 +168,14 @@ const typo = await pageTypography('siteAbout', 'about'); color: var(--ink-2); max-width: var(--measure); } + /* 方案 A 白纸直编:单栏、行距放宽,交给全局 .rich 排版体系 */ + .about-doc { max-width: var(--measure); } + .about-doc :global(h2) { + font-size: 15px; + font-family: var(--font-mono); + color: var(--ink-3); + letter-spacing: 0.06em; + margin: var(--space-6) 0 var(--space-3); + } + .about-doc :global(p) { line-height: 1.85; } diff --git a/src/content.config.ts b/src/content.config.ts index 80889c0..1e6ec95 100644 --- a/src/content.config.ts +++ b/src/content.config.ts @@ -279,6 +279,12 @@ export const collections = { // → src/lib/page-blocks.ts warn + skip,保证后台填错块型不炸构建 schema: ({ image }) => z.object({ + /** + * 白纸直编(Phase 5k 用户选定方案 A):整页正文一个 markdown 字段, + * 想写什么写什么。非空时整页只渲染它(IdCard 仍在顶部); + * 为空时回落 blocks 块式通道 → intro → 内置默认(兼容链保持不断)。 + */ + doc: bilingual.optional(), intro: bilingual.optional(), /** 页面级排版覆盖(层 2,T3 车道):正文默认字体类 / 强调色类 / 字号缩放 */ typography: z diff --git a/src/lib/site-content.ts b/src/lib/site-content.ts index f11c261..5602bdc 100644 --- a/src/lib/site-content.ts +++ b/src/lib/site-content.ts @@ -42,7 +42,7 @@ export const SITE_DEFAULTS = { const pick = (value: string | undefined, fallback: string): string => value && value.trim() ? value : fallback; -const pickBi = (value: { zh?: string; en?: string } | undefined, fallback: Bi, locale: Locale): string => +export const pickBi = (value: { zh?: string; en?: string } | undefined, fallback: Bi, locale: Locale): string => pick(value?.[locale], fallback[locale]); /** 主页 tagline(zh/en) */ diff --git a/src/pages/about-preview.astro b/src/pages/about-preview.astro deleted file mode 100644 index 1501ca8..0000000 --- a/src/pages/about-preview.astro +++ /dev/null @@ -1,336 +0,0 @@ ---- -/** - * 关于页形态预览(Phase 5k):一页并陈五个方案,共用 about.yaml 真实数据,供用户点选。 - * - * 为什么单开预览路由:关于页是"讲人"的页面,形态是纯审美 + 编辑自由度的取舍, - * 改错要回滚一轮。选定后我把选中方案落进 AboutPage.astro,然后删掉本路由。 - * - * 五案数据完全同源(content/site/about.yaml 的 blocks),只是结构与视觉不同—— - * 无论选哪个,内容都不用迁移。 - */ -import { getCollection } from 'astro:content'; -import BaseLayout from '../layouts/BaseLayout.astro'; -import IdCard from '../components/IdCard.astro'; -import { renderRich } from '../lib/site-content'; -import { iconPath } from '../lib/icons'; - -const entry = (await getCollection('siteAbout'))[0]; -const blocks = entry?.data.blocks ?? []; -const proseBlocks = blocks.filter((b) => b.type === 'prose'); -const tracksBlock: any = blocks.find((b) => b.type === 'tracks') ?? null; -const linksBlock: any = blocks.find((b) => b.type === 'linklist') ?? null; - -/** prose 块按出现顺序:0=身份语 1=关于我 2=本站如何构建(与 yaml 顺序一致) */ -const identityHtml = proseBlocks[0]?.body?.zh ? await renderRich(proseBlocks[0].body.zh) : ''; -const aboutMeHtml = proseBlocks[1]?.body?.zh ? await renderRich(proseBlocks[1].body.zh) : ''; -const howHtml = proseBlocks[2]?.body?.zh ? await renderRich(proseBlocks[2].body.zh) : ''; -const aboutMePlain = (proseBlocks[1]?.body?.zh ?? '').replace(/<[^>]+>/g, '').trim(); -const trackItems: any[] = tracksBlock?.items ?? []; -const trackTitle = tracksBlock?.title?.zh ?? ''; -const linkItems: any[] = linksBlock?.items ?? []; -const linkTitle = linksBlock?.title?.zh ?? ''; - -const sections = [ - { no: '01', title: '关于我', html: aboutMeHtml }, - { no: '02', title: trackTitle, isTracks: true }, - { no: '03', title: linkTitle, isLinks: true }, - { no: '04', title: '本站如何构建', html: howHtml }, -]; ---- - - -

关于页:五个方案

-

- 五案共用同一份内容( about.yaml ),只是结构与视觉不同——选哪个都不用迁移内容。 -
- 回字母告诉我选哪个(A / B / C / D / E,也可以说「B 但左栏窄一点」这类微调)。 -

- -
- - -
-
- A -

白纸单栏

-

后台就是一个 markdown 编辑器,想写什么写什么、想多长多长。 - 优点编辑自由度最大、零结构约束;代价全靠文字功力,视觉最素。

-
-
- -
-

{linkTitle}

- -

本站如何构建

-
-

{trackTitle}

-

{trackItems.map((it) => it.name.zh).join(' · ')}

-
-
- - -
-
- B -

名片双栏

-

左侧固定身份名片(头像/名字/社交),右侧正文随滚动。 - 优点"这是谁"一目了然、个人站经典形态;代价双栏在窄屏折为上下。

-
-
- -
-
-

{linkTitle}

- -

本站如何构建

-
-
-
-
- - -
-
- C -

杂志分节

-

开头一段大字引言,正文按编号分节,右侧粘性目录跟随滚动。 - 优点最"杂志"、阅读动线清晰;代价结构感强,编辑时要跟着分节写。

-
-
{identityHtml}
-
-
- {sections.map((sec) => ( -
-

{sec.no} {sec.title}

- {sec.isTracks ? ( -
    - {trackItems.map((it) => ( -
  • - {iconPath(it.icon) && ( - - )} - {it.name.zh} - {it.desc.zh} -
  • - ))} -
- ) : sec.isLinks ? ( - - ) : ( -
- )} -
- ))} -
- -
-
- - -
-
- D -

时间线

-

把"我是谁"讲成一条轨迹:起点 → 第二主线 → 作品 → 信条,沿线是节点与短段落。 - 优点叙事感最强、"讲人"最像讲故事;代价内容要贴合时间线结构来写。

-
-
    -
  1. - -

    起点:气象

    -

    一个想做预报的人,依旧把云图当日常读物。

    -
  2. -
  3. - -

    第二主线:代码

    -

    Linux、容器与自托管,把日常工具一件件磨顺手。

    -
  4. -
  5. - -

    作品:wait4x.online

    -

    把读过的书、按下的快门都放进同一个工作台。

    -
  6. -
  7. - -

    信条

    -

    拥抱开源,热爱开源。

    -
  8. -
-
-

{linkTitle}

- -
-
- - -
-
- E -

卡片流

-

所有内容装进一组卡片:身份卡打头,正文/兴趣/链接各占一张,两列错落。 - 优点区块感最强、和站内其它页面语言统一;代价卡片边界会把长文切碎。

-
-
-
-

关于我

-

{trackTitle}

-
    - {trackItems.map((it) => ( -
  • {it.name.zh}{it.desc.zh}
  • - ))} -
-
-

{linkTitle}

- -
-

本站如何构建

-
-
- -

五案数据同源:content/site/about.yaml(后台「页面文案 → 关于页」可改)。选定后我把该案落进正式 /about。

- - - diff --git a/src/pages/heatmap-preview.astro b/src/pages/heatmap-preview.astro deleted file mode 100644 index 67eeec5..0000000 --- a/src/pages/heatmap-preview.astro +++ /dev/null @@ -1,387 +0,0 @@ ---- -/** - * 热力图方案 v4(Phase 5j):用户认可 FJ(蛇 + 月柱联动),要求**优化蛇的行动路径**。 - * - * 现状问题(真实数据):371 格里只有 51 天有贡献(14%),原路径是"按列扫整张网格", - * 于是 86% 的行程在空格上、且格子跳变(每 26ms 瞬移一格)显得机械。 - * - * 本版给三条路径优化,都保留 FJ 的月柱联动: - * P1 智能寻食:只按"最近的有贡献格子"走,中间用子格插值滑行——蛇总在吃东西附近 - * P2 平滑蛇体:仍扫全网,但头是连续坐标滑行 + 拖尾,看起来像真蛇在游 - * P3 分月清扫:按月成块清扫,月底停一拍并让该月柱子立起(节奏感来自"月") - */ -import BaseLayout from '../layouts/BaseLayout.astro'; -import HeatGrid from '../components/geek/HeatGrid.astro'; -import { loadHeatmap, type HeatDay } from '../lib/heatmap'; - -const { weeks, total } = await loadHeatmap(); -const days: HeatDay[] = weeks.flat(); -const buildTime = new Date().toISOString().slice(0, 10); -const activeDays = days.filter((d) => d.count > 0).length; - -/* 月度聚合(柱子) */ -const byMonth = new Map(); -for (const d of days) { - const key = d.date.slice(0, 7); - byMonth.set(key, (byMonth.get(key) ?? 0) + d.count); -} -const months = [...byMonth.entries()].sort(([a], [b]) => a.localeCompare(b)); -const maxMonth = Math.max(...months.map(([, v]) => v)); ---- - - -

蛇的路径优化(FJ 骨架)

-

- 你认可 FJ,问题在路径。真实数据:371 格里只有 {activeDays} 天有贡献( - {Math.round((activeDays / days.length) * 100)}%),原路径"按列扫全网"意味着 - {100 - Math.round((activeDays / days.length) * 100)}% 的行程在空转, - 而且每 26ms 瞬移一格,看着机械。三案都保留月柱联动。回 P1 / P2 / P3 告诉我选哪个。 -

- - - - -
-
- P1 -

智能寻食

-

- 蛇只按「离当前最近的有贡献格子」前进(最近邻路径),两格之间用子格插值滑行, - 不再逐格瞬移。优点行程从 371 格压到 ~— 步,动作始终有目的、 - 永远在吃东西;代价不再"扫过整年",没贡献的日子只是路过。 -

-
-
-
- - -
-
    - {months.map(([m, v], i) => ( -
  • - - {m.slice(5)} -
  • - ))} -
-

准备出发… · 第 1 轮

-
- - -
-
- P2 -

平滑蛇体

-

- 仍然扫过整年,但蛇头是连续坐标滑行(不是格子跳变),身后拖 8 节渐隐的身体, - 转弯处带一点缓动。优点最像真蛇、动作最顺;代价依旧会经过空格(但不再"顿")。 -

-
-
-
- - -
-
    - {months.map(([m, v], i) => ( -
  • - - {m.slice(5)} -
  • - ))} -
-

准备出发… · 第 1 轮

-
- - -
-
- P3 -

分月清扫

-

- 按月成块清扫:一个月扫完停一拍(180ms)并让该月柱子"立起来",再进下个月。 - 优点节奏由"月"提供、与柱子天然同步、扫过空格时速度更快;代价每月有个小停顿。 -

-
-
-
- - -
-
    - {months.map(([m, v], i) => ( -
  • - - {m.slice(5)} -
  • - ))} -
-

准备出发… · 当前 —

-
- -

- 数据口径:GitHub GraphQL 贡献日历,构建期快照({buildTime})· 共 {total} 次贡献 / {activeDays} 天。 - 三案共用同一套「头 + 拖尾 + 月柱」引擎,只换路径策略;"减少动态效果"下静态呈终态。 -

-
- - - - From b72ecccc80f94d95d149c7af1b36abbf2d20e050 Mon Sep 17 00:00:00 2001 From: wait4xx Date: Wed, 4 Nov 2026 23:30:15 +0800 Subject: [PATCH 3/3] =?UTF-8?q?chore(spec):=20=E5=BD=92=E6=A1=A3=20blog-5k?= =?UTF-8?q?=E2=80=94=E2=80=94=E4=B8=A4=E4=BB=BB=E5=8A=A1=E6=94=B6=E6=95=9B?= =?UTF-8?q?=EF=BC=88=E5=85=B3=E4=BA=8E=E9=A1=B5=E5=AE=9A=E7=A8=BF=E6=96=B9?= =?UTF-8?q?=E6=A1=88=20A=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Spec: 2026-11-04_blog-5k-about-designs Co-authored-by: GLM-5.3 --- .../completion-summary.md | 88 +++++++++++++++++++ .../runs.jsonl | 7 ++ .../2026-11-04_blog-5k-about-designs/spec.md | 13 +-- .../2026-11-04_blog-5k-about-designs/tasks.md | 0 4 files changed, 102 insertions(+), 6 deletions(-) create mode 100644 .spec/specs/archive/2026-11-04_blog-5k-about-designs/completion-summary.md create mode 100644 .spec/specs/archive/2026-11-04_blog-5k-about-designs/runs.jsonl rename .spec/specs/{ => archive}/2026-11-04_blog-5k-about-designs/spec.md (81%) rename .spec/specs/{ => archive}/2026-11-04_blog-5k-about-designs/tasks.md (100%) diff --git a/.spec/specs/archive/2026-11-04_blog-5k-about-designs/completion-summary.md b/.spec/specs/archive/2026-11-04_blog-5k-about-designs/completion-summary.md new file mode 100644 index 0000000..b882d29 --- /dev/null +++ b/.spec/specs/archive/2026-11-04_blog-5k-about-designs/completion-summary.md @@ -0,0 +1,88 @@ +# Phase 5k:关于页形态探索(5 案预览 + 选型落地)- 项目范围 - 完成总结 + +## 交付结论 +- 结果:完成 +- 完成时间:2026-11-04 23:30 + +## 假设回顾 +### 已验证假设 +- 五案共用同一份数据(about.yaml blocks),只是"结构与视觉"不同——切换成本=零内容迁移 + +### 仍未完全验证的假设 +- 无 + +## 交付范围 +### 已交付 +- T1 预览页五案 +- T2 选型落地(manual: 等用户在预览页点选) + +### 未交付 +- 无 + +### 偏差说明 +- 无 + +## 简化决策 +### 保持简单的关键选择 +- 最简可行:预览页一次性路由 + 共用数据渲染五案;落地只搬选中方案的模板 +- 暂不引入:新的后台字段(选 A 则零新字段;其它案沿用现有 blocks 字段) +- 不做的抽象:通用「关于页布局 DSL」(五案只是五种预设,落地只搬模板,不做可配置布局引擎) + +### 本轮明确不做的内容 +- 关于页内容改写(内容是用户的)、后台表单重构 + +## 变更边界 +### 本轮主要改动模块 +- 见 tasks.md 的 boundary 记录 + +### 明确未触碰的区域 +- 关于页内容改写(内容是用户的)、后台表单重构 + +## 验证证据 +### 备注 +- 机器证据:runs.jsonl(见验证范围的机器验证率) + +## 验证范围 +- 级别:package +- 机器验证率:1/1;manual 任务 1 项不计入分母 +- 规则:check 与 done 复用该范围;只有记录升级触发时才执行全项目检查 + +## 哲学生效证据 +### 行为成效回填 +- Development Record 行为门禁通过 + +### 一致性门禁回顾 +- 验收面(checklist 或成功标准)已通过 + +## Git 记录 +- 日期时间:2026-11-04 23:30 +- 范围:Phase 5k:关于页形态探索(5 案预览 + 选型落地)- 项目范围 +- 功能:T1 预览页五案;T2 选型落地(manual: 等用户在预览页点选) +- 操作:提交 / 推送 +- 成效:Development Record 行为门禁通过 +- 提交:pending local commit +- 提交区间:129b23cd2b0ee2e4d677429fc7063c56b9ce62d8..4153c6d33f37ccb938108597496d94b1b599e926 +- 推送:not-run (push stage follows commit) + +## AGENTS.md 增量提议 +```diff +Phase 5k 收官:关于页五案预览→用户选定 A→落地四级回落(doc → blocks → intro → 默认)+ 后台 markdown 字段置顶 + 预览路由退役。 +``` + +## 被拒绝的扩展提议 +- 无 + +## 门禁证据 +- check_spec_package.py exit 0 + +## 遗留事项 +- 无;问题处置见下方结构化区块 + +## 问题处置 + +```json +{ + "issues": [], + "version": 1 +} +``` diff --git a/.spec/specs/archive/2026-11-04_blog-5k-about-designs/runs.jsonl b/.spec/specs/archive/2026-11-04_blog-5k-about-designs/runs.jsonl new file mode 100644 index 0000000..aef89ce --- /dev/null +++ b/.spec/specs/archive/2026-11-04_blog-5k-about-designs/runs.jsonl @@ -0,0 +1,7 @@ +{"task": "task_0001", "cmd": "! test -f src/pages/about-preview.astro && grep -q \"doc:\" content/site/about.yaml && E2E_BASE=http://127.0.0.1:18925 node tests/e2e.mjs 2>&1 | grep -qE \"汇总: [0-9]+/[0-9]+ PASS\"", "exit": 0, "duration_s": 32.309, "stdout_tail_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "tree_fp": "4d028ab89a03b678fbd1828b163f34abb131e10f3176527477b4cdb6c6d4487b", "at": "2026-11-04T15:23:13+00:00"} +{"task": "task_0001", "cmd": "! test -f src/pages/about-preview.astro && grep -q \"doc:\" content/site/about.yaml && E2E_BASE=http://127.0.0.1:18925 node tests/e2e.mjs 2>&1 | grep -qE \"汇总: [0-9]+/[0-9]+ PASS\"", "exit": 0, "duration_s": 32.261, "stdout_tail_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "tree_fp": "4d028ab89a03b678fbd1828b163f34abb131e10f3176527477b4cdb6c6d4487b", "at": "2026-11-04T15:23:46+00:00"} +{"task": "task_0001", "cmd": "! test -f src/pages/about-preview.astro && grep -q \"doc:\" content/site/about.yaml && E2E_BASE=http://127.0.0.1:18925 node tests/e2e.mjs 2>&1 | grep -qE \"汇总: [0-9]+/[0-9]+ PASS\"", "exit": 0, "duration_s": 31.564, "stdout_tail_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "tree_fp": "4d028ab89a03b678fbd1828b163f34abb131e10f3176527477b4cdb6c6d4487b", "at": "2026-11-04T15:24:32+00:00"} +{"task": "task_0001", "cmd": "! test -f src/pages/about-preview.astro && grep -q \"doc:\" content/site/about.yaml && E2E_BASE=http://127.0.0.1:18925 node tests/e2e.mjs 2>&1 | grep -qE \"汇总: [0-9]+/[0-9]+ PASS\"", "exit": 0, "duration_s": 30.031, "stdout_tail_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "tree_fp": "4d028ab89a03b678fbd1828b163f34abb131e10f3176527477b4cdb6c6d4487b", "at": "2026-11-04T15:25:31+00:00"} +{"task": "task_0001", "cmd": "! test -f src/pages/about-preview.astro && grep -q \"doc:\" content/site/about.yaml && E2E_BASE=http://127.0.0.1:18925 node tests/e2e.mjs 2>&1 | grep -qE \"汇总: [0-9]+/[0-9]+ PASS\"", "exit": 0, "duration_s": 30.603, "stdout_tail_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "tree_fp": "4d028ab89a03b678fbd1828b163f34abb131e10f3176527477b4cdb6c6d4487b", "at": "2026-11-04T15:27:31+00:00"} +{"task": "task_0001", "cmd": "! test -f src/pages/about-preview.astro && grep -q \"doc:\" content/site/about.yaml && E2E_BASE=http://127.0.0.1:18925 node tests/e2e.mjs 2>&1 | grep -qE \"汇总: [0-9]+/[0-9]+ PASS\"", "exit": 0, "duration_s": 29.99, "stdout_tail_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "tree_fp": "4d028ab89a03b678fbd1828b163f34abb131e10f3176527477b4cdb6c6d4487b", "at": "2026-11-04T15:29:32+00:00"} +{"task": "task_0001", "cmd": "! test -f src/pages/about-preview.astro && grep -q \"doc:\" content/site/about.yaml && E2E_BASE=http://127.0.0.1:18925 node tests/e2e.mjs 2>&1 | grep -qE \"汇总: [0-9]+/[0-9]+ PASS\"", "exit": 0, "duration_s": 29.877, "stdout_tail_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "tree_fp": "4d028ab89a03b678fbd1828b163f34abb131e10f3176527477b4cdb6c6d4487b", "at": "2026-11-04T15:30:02+00:00"} diff --git a/.spec/specs/2026-11-04_blog-5k-about-designs/spec.md b/.spec/specs/archive/2026-11-04_blog-5k-about-designs/spec.md similarity index 81% rename from .spec/specs/2026-11-04_blog-5k-about-designs/spec.md rename to .spec/specs/archive/2026-11-04_blog-5k-about-designs/spec.md index 77ffd20..12bb702 100644 --- a/.spec/specs/2026-11-04_blog-5k-about-designs/spec.md +++ b/.spec/specs/archive/2026-11-04_blog-5k-about-designs/spec.md @@ -17,7 +17,7 @@ - 五案共用同一份数据(about.yaml blocks),只是"结构与视觉"不同——切换成本=零内容迁移 ### 2.3 待确认问题 -- [ ] 五案选型(用户在 /about-preview/ 点选) +- [x] 五案选型:用户已选定 **A 白纸直编**(2026-11-04,预览页点选) ### 2.4 可选解释与取舍 - 选定方案若为"纯 markdown 直编",blocks 保留为高级通道(后台仍可切回) @@ -28,8 +28,8 @@ ## 3. 功能范围 ### 3.1 核心功能(MVP) -- [ ] T1 预览页:/about-preview/ 五案并陈(A 白纸单栏 / B 名片双栏 / C 杂志分节带目录 / D 时间线主导 / E 卡片流),共享 about.yaml 真实数据 -- [ ] T2 用户选型后落地正式 /about 并删除预览路由 +- [x] T1 预览页:/about-preview/ 五案并陈(A 白纸单栏 / B 名片双栏 / C 杂志分节带目录 / D 时间线主导 / E 卡片流),共享 about.yaml 真实数据 +- [x] T2 用户选型后落地正式 /about 并删除预览路由 ### 3.2 扩展功能 - 无 @@ -40,6 +40,7 @@ ## 4. 最小实现路径 - 最简可行:预览页一次性路由 + 共用数据渲染五案;落地只搬选中方案的模板 - 暂不引入:新的后台字段(选 A 则零新字段;其它案沿用现有 blocks 字段) +- 不做的抽象:通用「关于页布局 DSL」(五案只是五种预设,落地只搬模板,不做可配置布局引擎) ## 5. 技术决策 - 技术栈:Astro 7.4.4 + 现有 renderRich/renderMarkdown 富文本管线 + iconPath 注册表 @@ -53,12 +54,12 @@ - 变更对象:关于页渲染通道(纯前端结构) - 快速检查:`pnpm astro build` + `python3 scripts/build-perf/config-check.py .` - 集成检查:E2E 56 项 + content-lint 0/0/0 -- 全项目检查:生产预览页五案可达、选中案落地后 /about 渲染正确 +- 全项目检查:生产预览页五案可达、选中案落地后 /about 渲染正确;**升级条件**——仅当关于页改动触及全局样式或 BaseLayout 时才升级全站复核,本包不涉及(由 /about 渲染人工核验覆盖) - 升级触发:关于页为独立页面且不动全局样式,升级为「该页视觉复核」即可 ## 6. 成功标准与验证方式 -- [ ] 预览页五案可达且共用真实数据 -> verify: task_0001 -- [ ] 用户选定方案落地正式 /about,预览路由删除 -> verify: task_0002(manual: 需用户点选,落地后人工核验) +- [x] 预览页五案可达且共用真实数据 -> verify: task_0001 +- [x] 用户选定方案落地正式 /about,预览路由删除 -> verify: task_0002(manual: 需用户点选,落地后人工核验) ## 7. 风险与约束 - 灰阶骨架红线、双语红线不变 diff --git a/.spec/specs/2026-11-04_blog-5k-about-designs/tasks.md b/.spec/specs/archive/2026-11-04_blog-5k-about-designs/tasks.md similarity index 100% rename from .spec/specs/2026-11-04_blog-5k-about-designs/tasks.md rename to .spec/specs/archive/2026-11-04_blog-5k-about-designs/tasks.md