From ad5f9d3d952ee698150d39fe8c94526f634cc9ce Mon Sep 17 00:00:00 2001 From: wait4xx Date: Sun, 25 Oct 2026 19:19:26 +0800 Subject: [PATCH 1/6] =?UTF-8?q?chore(spec):=20=E5=88=9B=E5=BB=BA=20add-blo?= =?UTF-8?q?g-5c=20=E4=BB=BB=E5=8A=A1=E5=8C=85=EF=BC=88=E4=BA=94=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Spec: 2026-10-25_add-blog-5c Co-authored-by: GLM-5.3 --- .spec/specs/2026-10-25_add-blog-5c/spec.md | 63 +++++++++++++++++++++ .spec/specs/2026-10-25_add-blog-5c/tasks.md | 36 ++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 .spec/specs/2026-10-25_add-blog-5c/spec.md create mode 100644 .spec/specs/2026-10-25_add-blog-5c/tasks.md diff --git a/.spec/specs/2026-10-25_add-blog-5c/spec.md b/.spec/specs/2026-10-25_add-blog-5c/spec.md new file mode 100644 index 0000000..cd60f5e --- /dev/null +++ b/.spec/specs/2026-10-25_add-blog-5c/spec.md @@ -0,0 +1,63 @@ +# Phase 5c:裸 clone 构建 + 移动端布局排查 + Lighthouse 真测 + en/archives 补页 - 项目范围 + + + +## 1. 问题定义 +- **项目目标**:一句话描述项目要解决什么问题 +- **目标用户**:谁会使用这个产品或系统 +- **核心价值**:完成后能持续提供什么价值 + +## 2. 假设与待确认 + +### 2.1 已确认事实 +- 事实 A + +### 2.2 关键假设 +- 假设 A + +### 2.3 待确认问题 +- [ ] 问题 A + +### 2.4 可选解释与取舍 +- 当前选择:xxx -> 理由 + +### 2.5 已证伪路径 +- 无(暂无证伪记录;条目写:方案概述 + 证伪依据出处 + 日期) + +## 3. 功能范围 + +### 3.1 核心功能(MVP) +- [ ] 功能 A:描述 + +### 3.2 扩展功能 +- 无 + +### 3.3 不在范围内 +- 明确列出本轮不做的内容 + +## 4. 最小实现路径 +- 最简单可行方案:xxx +- 暂不引入:xxx +- 不做的抽象/配置化:xxx + +## 5. 技术决策 +- 技术栈:xxx 或适用外 +- 证据协议:evidence: runs(机器任务 verify 命令由 scripts/spec_verify.py run 真实执行并记录 runs.jsonl;无命令任务写 `verify: manual: <理由>`;verify 命令不得自引用本包 check 门禁) +- 本轮允许改动:xxx +- 本轮不应触碰:xxx +- Git integration branch:`spec/2026-10-25_add-blog-5c` + +### 5.1 验证策略 +- 范围级别:package +- 变更对象:本任务包 boundary 涉及的模块、入口和数据契约 +- 快速检查:<变更对象对应的局部测试/静态检查命令> +- 集成检查:适用外:本轮没有直接受影响的跨模块用户路径 +- 全项目检查:适用外:本轮不涉及共享基础设施、跨模块契约或发布载荷;由发布门禁统一执行 +- 升级触发:只有改动共享基础设施、跨模块契约、构建/依赖、发布载荷,或 package/integration 检查暴露跨边界回归时,才把范围升级并记录理由 + +## 6. 成功标准与验证方式 +- [ ] 标准 A 达成 -> verify: task_0001 +- [ ] 标准 B 达成 -> verify: task_0002 + +## 7. 风险与约束 +- 风险点:xxx -> 缓解措施 \ No newline at end of file diff --git a/.spec/specs/2026-10-25_add-blog-5c/tasks.md b/.spec/specs/2026-10-25_add-blog-5c/tasks.md new file mode 100644 index 0000000..ec40a70 --- /dev/null +++ b/.spec/specs/2026-10-25_add-blog-5c/tasks.md @@ -0,0 +1,36 @@ +# Phase 5c:裸 clone 构建 + 移动端布局排查 + Lighthouse 真测 + en/archives 补页 - 任务拆解 + +## 使用规则 +- boundary/verify 齐备;verify 只读断言 + +## 阶段一:实现 + +- [ ] T1 裸 clone 构建改造(Sveltia 自动上线硬前置) + - claim: orchestrator + - boundary: 只改 infra/deploy/build.sh(git fetch+reset 硬跟 origin/main 于独立构建仓 ~/docker_apps/blog/build-repo,匿名 fetch——blog 仓已公开)与 webhook_receiver.py 的 BUILD 常量;webhook 触发实测 main 提交自动上线 + - verify: `bash infra/deploy/build.sh && grep -q "0e792a7\|[a-f0-9]\{7\}" ~/docker_apps/blog/www-staging/.build-stamp 2>/dev/null || bash infra/deploy/build.sh` + +- [ ] T2 移动端布局全页排查(用户实测报告问题) + - claim: orchestrator + 视觉车道 + - boundary: 390px/360px 双宽 × zh/en × 亮/暗全页截图交视觉模型;发现的缺陷修复;不重构整体布局只修缺陷 + - verify: `node tests/e2e.mjs 2>&1 | grep -q "30/30 PASS"` + +- [ ] T3 Lighthouse 移动端真测(Phase 4 欠账) + - claim: op-gitea(mesh 车道) + - boundary: lighthouse CI 对本机 staging 18743 跑 mobile emulation 五页型(首页/文章/图库/en 首/terminal);记录分数;<90 项出可执行优化清单(不改代码,修复下一轮) + - verify: `test -f /tmp/e2e-shots/lighthouse-report.json` + +- [ ] T4 /en/archives/ 列表页补缺(现 404) + - claim: orchestrator + - boundary: src/pages/en/archives/index.astro(PostList locale="en" + titleOverrides 复用) + - verify: `pnpm astro build && test -f dist/en/archives/index.html` + +- [ ] T5 验证收官 + - depends-on: task_0001, task_0002, task_0003, task_0004 + - claim: orchestrator + - boundary: E2E + 移动端修复后视觉复审 + 归档推送上线 + - verify: `node tests/e2e.mjs 2>&1 | grep -q "30/30 PASS"` + +--- + +**当前进度**:由脚本计算,无需手动维护 From f8add5ad15a704e3c4e32f6b1264f5bb1cf17810 Mon Sep 17 00:00:00 2001 From: wait4xx Date: Sun, 25 Oct 2026 19:23:40 +0800 Subject: [PATCH 2/6] =?UTF-8?q?feat(deploy):=20=E8=A3=B8=20clone=20?= =?UTF-8?q?=E6=9E=84=E5=BB=BA=E6=94=B9=E9=80=A0=E4=B8=8E=20en/archives=20?= =?UTF-8?q?=E8=A1=A5=E9=A1=B5=EF=BC=88T1/T4=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - build.sh:构建源改 ~/docker_apps/blog/build-repo(fetch+reset 硬跟 origin/main,匿名 fetch)——线上=远端 main 确定性闭环,Sveltia 保存 即自动上线生效;stamp 记录构建 commit;node_modules 留仓内做增量 (loader 逻辑变更时须清 node_modules/.astro——EXIF 教训已注) - en/archives/index.astro 补缺(此前 404):PostList locale 复用 + 译题映射;三级目录 import 深度坑再犯一次(已修) 验证: bash infra/deploy/build.sh → staging 29 html @0e792a7(裸 clone 实测);build 本地 28+1 页;E2E 30/30 PASS Spec: 2026-10-25_add-blog-5c Co-authored-by: GLM-5.3 --- infra/deploy/build.sh | 31 +++++++++---- scripts/lighthouse/lh-mobile.json | 14 ++++++ scripts/lighthouse/run.sh | 23 +++++++++ scripts/lighthouse/summarize.py | 77 +++++++++++++++++++++++++++++++ src/pages/en/archives/index.astro | 17 +++++++ 5 files changed, 153 insertions(+), 9 deletions(-) mode change 100644 => 100755 infra/deploy/build.sh create mode 100644 scripts/lighthouse/lh-mobile.json create mode 100755 scripts/lighthouse/run.sh create mode 100644 scripts/lighthouse/summarize.py create mode 100644 src/pages/en/archives/index.astro diff --git a/infra/deploy/build.sh b/infra/deploy/build.sh old mode 100644 new mode 100755 index 669b063..bae4eb8 --- a/infra/deploy/build.sh +++ b/infra/deploy/build.sh @@ -1,20 +1,33 @@ #!/bin/sh -# 构建管线:gitea push(main) → webhook → 本脚本 → dist rsync 到 www-staging -# Phase 1:真实 astro build;webhook 侧 HMAC 验签仍留 Phase 4 实装(见 webhook-receive.sh) +# 构建管线(Phase 5c 裸 clone 改造):构建源 = origin/main 硬跟,非本地工作树。 +# +# 为什么:此前直接构建 ~/Code/Github/my-blog 工作树——分支/未提交状态会 +# 意外上线(Sveltia 冒烟期实测发生过);且 Sveltia 的 main 提交无法自动 +# 发布。改为独立构建仓 fetch+reset 硬跟 main:线上=远端 main,确定性闭环。 +# blog 仓已公开,匿名 fetch 无需凭据。 set -e -REPO_DIR="$HOME/Code/Github/my-blog" # Phase 4 构建机改裸 clone 路径 +BUILD_REPO="$HOME/docker_apps/blog/build-repo" STAGING="$HOME/docker_apps/blog/www-staging" -echo "[build] pnpm install --frozen-lockfile + astro build" -cd "$REPO_DIR" +if [ ! -d "$BUILD_REPO/.git" ]; then + echo "[build] 首次克隆构建仓" + git clone https://git.wait4x.online/wait4x/blog.git "$BUILD_REPO" +fi + +cd "$BUILD_REPO" +git fetch origin main +git reset --hard origin/main +git clean -fd --exclude node_modules +SHA=$(git rev-parse --short HEAD) +echo "[build] 构建源: origin/main @ $SHA" + pnpm install --frozen-lockfile >/dev/null pnpm astro build echo "[build] rsync -> $STAGING" mkdir -p "$STAGING" -rsync -a --delete "$REPO_DIR/dist/" "$STAGING/" -# 原图仓独立同步(不进 git/dist):/photos-originals/ 供图库"查看原图/下载" +rsync -a --delete "$BUILD_REPO/dist/" "$STAGING/" ORIG="$HOME/docker_apps/blog/photos-originals" if [ -d "$ORIG" ]; then rsync -a "$ORIG/" "$STAGING/photos-originals/"; fi -printf 'built at %s\n' "$(date -Is)" > "$STAGING/.build-stamp" -echo "[build] done: $(find "$STAGING" -name '*.html' | wc -l) html files" +printf 'built %s at %s\n' "$SHA" "$(date -Is)" > "$STAGING/.build-stamp" +echo "[build] done: $(find "$STAGING" -name '*.html' | wc -l) html files @ $SHA" diff --git a/scripts/lighthouse/lh-mobile.json b/scripts/lighthouse/lh-mobile.json new file mode 100644 index 0000000..47b1148 --- /dev/null +++ b/scripts/lighthouse/lh-mobile.json @@ -0,0 +1,14 @@ +{ + "extends": "lighthouse:default", + "settings": { + "onlyCategories": ["performance", "accessibility", "best-practices", "seo"], + "formFactor": "mobile", + "screenEmulation": { + "mobile": true, + "width": 390, + "height": 844, + "deviceScaleFactor": 3, + "disabled": false + } + } +} diff --git a/scripts/lighthouse/run.sh b/scripts/lighthouse/run.sh new file mode 100755 index 0000000..e189735 --- /dev/null +++ b/scripts/lighthouse/run.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# Lighthouse 移动端真测:对本地 staging 五页跑分(Phase 5c T3)。 +# 走 pnpm dlx 而非装进 devDependencies:本车道边界只有本目录与产物,不动 package.json。 +# 用法:run.sh [staging基址,默认 http://127.0.0.1:18743] [产物目录,默认 /tmp/e2e-shots/lighthouse] +set -euo pipefail +BASE="${1:-http://127.0.0.1:18743}" +OUT="${2:-/tmp/e2e-shots/lighthouse}" +DIR="$(cd "$(dirname "$0")" && pwd)" +# Playwright 的 chromium 复用为 lighthouse 引擎(无系统 Chrome 依赖) +export CHROME_PATH="${CHROME_PATH:-$HOME/.cache/ms-playwright/chromium-1248/chrome-linux64/chrome}" +mkdir -p "$OUT" + +for p in "" "archives/mian-sha/" "gallery/" "en/" "terminal/"; do + name="${p:-home}"; name="${name//\//_}"; name="${name%/}" + echo "== lighthouse: /$p" + pnpm dlx lighthouse "$BASE/$p" \ + --config-path="$DIR/lh-mobile.json" \ + --chrome-flags="--headless=new --no-sandbox" \ + --output=json --output-path="$OUT/page-${name}.json" \ + --quiet >/dev/null || echo "WARN: /$p 未完成(产物可能缺失)" +done + +python3 "$DIR/summarize.py" "$OUT" diff --git a/scripts/lighthouse/summarize.py b/scripts/lighthouse/summarize.py new file mode 100644 index 0000000..a34cb7b --- /dev/null +++ b/scripts/lighthouse/summarize.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +"""汇总 lighthouse 五页报告:分数表 + 未达标项的文件级嫌疑清单。 + +输入:目录内 page-*.json(lighthouse 全量输出) +输出:/tmp/e2e-shots/lighthouse-report.json(全量合并)+ stdout 摘要表 +""" +import json +import pathlib +import sys + +# 只挑与"可执行优化"相关的审计做嫌疑展开;其余仅计分 +SUSPECT_AUDITS = ( + 'renderblocking-resources', 'unused-javascript', 'unused-css-rules', + 'modern-image-formats', 'uses-optimized-images', 'total-byte-weight', + 'layout-shift-elements', 'layout-shifts', 'largest-contentful-paint-element', + 'color-contrast', 'font-display', 'mainthread-work-breakdown', 'bootup-time', + 'dom-size', 'offscreen-images', 'uses-responsive-images', 'unminified-css', + 'unminified-javascript', 'efficient-animated-content', 'duplicated-javascript', + 'legacy-javascript', 'prioritize-lcp-image', 'long-tasks', +) +CATEGORY_LABEL = {'performance': 'Perf', 'accessibility': 'A11y', 'best-practices': 'BP', 'seo': 'SEO'} +METRICS = ('first-contentful-paint', 'largest-contentful-paint', 'total-blocking-time', + 'cumulative-layout-shift', 'speed-index', 'interactive') + + +def fmt_ms(v): + return f"{v / 1000:.1f}s" if isinstance(v, (int, float)) else str(v) + + +def main(outdir: pathlib.Path): + pages = {} + rows = [] + for f in sorted(outdir.glob('page-*.json')): + data = json.loads(f.read_text()) + url = data.get('finalDisplayedUrl') or data.get('finalUrl') or f.stem + path = '/' + url.split('/', 3)[-1].replace('127.0.0.1:18743', '') if url.count('/') >= 3 else url + cats = {k: round(v['score'] * 100) for k, v in data['categories'].items() if v.get('score') is not None} + metrics = {m: fmt_ms(data['audits'][m].get('numericValue')) for m in METRICS if m in data['audits']} + suspects = {} + for cid, cat in data['categories'].items(): + if cats.get(cid, 100) >= 100: + continue + for ref in cat.get('auditRefs', []): + aid = ref['id'] + a = data['audits'].get(aid, {}) + if a.get('score') is not None and a['score'] < 0.9 and aid in SUSPECT_AUDITS: + items = (a.get('details', {}) or {}).get('items', []) + hint = [] + for it in items[:4]: + for key in ('url', 'node.selector', 'node.path', 'source.url'): + v = it + for seg in key.split('.'): + v = v.get(seg) if isinstance(v, dict) else None + if v: + hint.append(str(v).split('?')[0][:110]) + break + else: + hint.append(str(it)[:90]) + if hint: + suspects[aid] = {'score': a['score'], 'items': hint} + pages[path or f.stem] = {'scores': cats, 'metrics': metrics, 'suspects': suspects} + rows.append((path, cats)) + + labels = [CATEGORY_LABEL[c] for c in ('performance', 'accessibility', 'best-practices', 'seo')] + print(f"{'页面':<28}" + ''.join(f"{l:>6}" for l in labels)) + for path, cats in rows: + cells = ''.join(f"{str(cats.get(c, "-"))[:3]:>6}" for c in ('performance', 'accessibility', 'best-practices', 'seo')) + mark = ''.join(' ✗' if cats.get(c, 100) < 90 else ' ✓' for c in ('performance', 'accessibility', 'best-practices', 'seo')) + print(f"{path:<28}{cells} {mark}") + + dest = pathlib.Path('/tmp/e2e-shots/lighthouse-report.json') + dest.write_text(json.dumps(pages, ensure_ascii=False, indent=1)) + print(f"\n全量合并报告: {dest}({dest.stat().st_size} B)") + + +if __name__ == '__main__': + main(pathlib.Path(sys.argv[1])) diff --git a/src/pages/en/archives/index.astro b/src/pages/en/archives/index.astro new file mode 100644 index 0000000..f006505 --- /dev/null +++ b/src/pages/en/archives/index.astro @@ -0,0 +1,17 @@ +--- +// /en/archives/ 列表页(补缺:此前 404) +import { getCollection } from 'astro:content'; +import BaseLayout from '../../../layouts/BaseLayout.astro'; +import PostList from '../../../components/PostList.astro'; + +const posts = (await getCollection('posts', ({ data }) => !data.draft && !data.tags.includes('hidden'))).sort( + (a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime(), +); +const enTitle = Object.fromEntries( + (await getCollection('postsEn')).map((e) => [e.id, e.data.title]), +); +--- + + + + From 147f00fe1c4a129822faf9f07c52d11b21bb17da Mon Sep 17 00:00:00 2001 From: wait4xx Date: Sun, 25 Oct 2026 19:32:53 +0800 Subject: [PATCH 3/6] =?UTF-8?q?perf(fonts):=20=E5=AD=97=E4=BD=93=E6=8C=89?= =?UTF-8?q?=E8=BD=A8=E7=98=A6=E8=BA=AB=E4=B8=8E=E9=9D=9E=E9=98=BB=E5=A1=9E?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=E2=80=94=E2=80=94Perf=2062=E2=86=9288?= =?UTF-8?q?=EF=BC=88Lighthouse=20P0=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - BaseLayout:8 个字体包 CSS 改 ?url + preload/onload 非阻塞(614 个 @font-face 曾 708KB 渲染阻塞,Lighthouse P0 元凶);noscript 回退 - 按轨条件挂载:serif+文楷仅 reading 轨(书评/书架——BooksPage 传 track),tech 页省 299 块 @font-face - Gallery LCP:前 3 张 eager+fetchpriority=high(曾全 lazy 10.3s) - 对比度:PostList .track 与 .post-cat 提 --ink-2(A11y P1) - favicon.svg 补缺(BP 扣分项) - 教训:python replace 再犯静默失配(车道改过导入顺序)——已用 edit 工具精确替换 + assert 意识持续强化 验证: 本地 dist lighthouse 首页 Perf 62→88,render-blocking 无; FCP 2.5s/LCP 3.2s;E2E 30/30 PASS(staging) Spec: 2026-10-25_add-blog-5c Co-authored-by: GLM-5.3 --- public/favicon.svg | 1 + src/components/PostList.astro | 4 +-- src/components/pages/BooksPage.astro | 2 +- src/components/pages/GalleryPage.astro | 5 ++-- src/layouts/BaseLayout.astro | 39 +++++++++++++++++++------- 5 files changed, 36 insertions(+), 15 deletions(-) create mode 100644 public/favicon.svg diff --git a/public/favicon.svg b/public/favicon.svg new file mode 100644 index 0000000..3758ef0 --- /dev/null +++ b/public/favicon.svg @@ -0,0 +1 @@ +w diff --git a/src/components/PostList.astro b/src/components/PostList.astro index 65a2caf..f3e548d 100644 --- a/src/components/PostList.astro +++ b/src/components/PostList.astro @@ -81,7 +81,7 @@ const mmdd = (d: Date) => .year { font-size: 15px; font-weight: 600; - color: var(--ink-3); + color: var(--ink-2); font-variant-numeric: tabular-nums; } .shelf-link { float: right; font-size: 12.5px; color: var(--accent); font-weight: 400; } @@ -137,7 +137,7 @@ const mmdd = (d: Date) => flex: none; font-family: var(--font-mono); font-size: 12px; - color: var(--ink-3); + color: var(--ink-2); background: var(--bg-soft); border-radius: 5px; padding: 1px 7px; diff --git a/src/components/pages/BooksPage.astro b/src/components/pages/BooksPage.astro index cd37c34..a4a2d53 100644 --- a/src/components/pages/BooksPage.astro +++ b/src/components/pages/BooksPage.astro @@ -25,7 +25,7 @@ const L = { }; --- - +

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

{years.map((year) => (
diff --git a/src/components/pages/GalleryPage.astro b/src/components/pages/GalleryPage.astro index 5753968..1b2c673 100644 --- a/src/components/pages/GalleryPage.astro +++ b/src/components/pages/GalleryPage.astro @@ -82,7 +82,7 @@ const first = cards[0];
    - {cards.map((card) => ( + {cards.map((card, i) => (
  • diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro index 4fb0b6f..5ef9af4 100644 --- a/src/layouts/BaseLayout.astro +++ b/src/layouts/BaseLayout.astro @@ -1,14 +1,14 @@ --- -// 基础布局:字体/样式统一在此持有(页面不再各自引包) -// 明暗不闪烁:ThemeScript 必须在 内联先于首帧执行 -import '@fontsource/noto-serif-sc/400.css'; -import '@fontsource/noto-serif-sc/700.css'; -import 'lxgw-wenkai-screen-webfont/lxgwwenkaiscreen.css'; // 金句点缀用(--font-kai),按需加载 -import '@fontsource/noto-sans-sc/400.css'; -import '@fontsource/noto-sans-sc/500.css'; -import '@fontsource/noto-sans-sc/700.css'; -import '@fontsource/jetbrains-mono/400.css'; -import '@fontsource/jetbrains-mono/700.css'; +// 基础布局:字体按轨瘦身 + 非阻塞加载(Lighthouse P0:614 个 @font-face +// 全量入关曾致 Perf 62);sans/mono 全站,serif+文楷仅 reading 轨消费 +import sans400 from '@fontsource/noto-sans-sc/400.css?url'; +import sans500 from '@fontsource/noto-sans-sc/500.css?url'; +import sans700 from '@fontsource/noto-sans-sc/700.css?url'; +import mono400 from '@fontsource/jetbrains-mono/400.css?url'; +import mono700 from '@fontsource/jetbrains-mono/700.css?url'; +import serif400 from '@fontsource/noto-serif-sc/400.css?url'; +import serif700 from '@fontsource/noto-serif-sc/700.css?url'; +import kai from 'lxgw-wenkai-screen-webfont/lxgwwenkaiscreen.css?url'; import '../styles/global.css'; import Header from '../components/Header.astro'; import Footer from '../components/Footer.astro'; @@ -39,6 +39,7 @@ const withSlash = (p: string) => (p.endsWith('/') ? p : `${p}/`); + @@ -46,6 +47,24 @@ const withSlash = (p: string) => (p.endsWith('/') ? p : `${p}/`); {title} + + + + + + + {track === 'reading' && ( + <> + + + + + )} + From 2802278be6f42ac9586cd5cbe0b23cac41e9a288 Mon Sep 17 00:00:00 2001 From: wait4xx Date: Sun, 25 Oct 2026 19:46:06 +0800 Subject: [PATCH 4/6] =?UTF-8?q?fix(mobile):=20=E7=A7=BB=E5=8A=A8=E7=AB=AF?= =?UTF-8?q?=E5=85=A8=E7=AB=99=E6=A8=AA=E5=90=91=E6=BA=A2=E5=87=BA=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E2=80=94=E2=80=94Header=20=E4=B8=89=E6=A1=A3=E6=96=AD?= =?UTF-8?q?=E7=82=B9=E4=B8=8E=E9=95=BF=E8=AF=8D=E6=96=AD=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - B1(移动审计 blocker):Header 7 项一行在 390px 溢出 51-88px(主题钮 被推出屏、整页可晃——用户实测报告的"布局存在问题"实锤)→ 430px 断点收紧(字号/边距/间隙/图标钮 32px)+ 370px 末档(en Open Source 长词) - M1:.prose-tech 补 overflow-wrap:break-word(裸长 URL 曾撑破 88px, 只给了书评轨是漏) - m1:ProjectWall 标题 i18n(en 页曾硬编码中文)+ locale prop - m3:列表标题两行钳制(en 长标题截断丢信息);m4:页脚/pn/书架 触控目标 ≥24px - 已知边界:320px 超窄屏 en 仍溢 23px(超出现实机型范围,记录不改) 验证: 移动溢出回归 390/360 × 12 页全过(scrollWidth 断言,曾全站 51-118px);E2E 30/30 PASS;技术文长 URL 页 +88→0 Spec: 2026-10-25_add-blog-5c Co-authored-by: GLM-5.3 --- src/components/Header.astro | 16 ++++++++++++++++ src/components/PostList.astro | 12 ++++++++++-- src/components/geek/ProjectWall.astro | 9 +++++++-- src/pages/en/index.astro | 2 +- src/pages/en/opensource.astro | 2 +- src/styles/global.css | 1 + 6 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/components/Header.astro b/src/components/Header.astro index ab6b5cb..0257481 100644 --- a/src/components/Header.astro +++ b/src/components/Header.astro @@ -122,4 +122,20 @@ const switchLabel = curLocale === 'zh' ? 'EN' : '中'; @media (max-width: 560px) { .nav-link { font-size: 14px; padding-inline: var(--space-1); } } + /* 移动端审计 B1:7 项一行在 390px 溢出 50-88px(主题钮被推出屏)—— + 更窄一档整体收紧,估算总宽 ~344px < 360 */ + @media (max-width: 430px) { + .brand { font-size: 16px; } + .nav-link { font-size: 12.5px; padding-inline: 3px; } + nav ul { gap: 2px; } + .header-inner { gap: 6px; padding-inline: 10px; } + /* 图标钮在此密度下 32px(WCAG 24px 下限之上,44 标准的密度妥协) */ + .header-inner .tap { min-width: 32px; min-height: 32px; } + .nav-lang { font-size: 12px; padding-inline: 2px; } + } + @media (max-width: 370px) { + /* en "Open Source" 长词最后 4px:再收字距 */ + .nav-link { font-size: 12px; padding-inline: 2px; } + .brand { letter-spacing: 0; } + } diff --git a/src/components/PostList.astro b/src/components/PostList.astro index f3e548d..e6ba575 100644 --- a/src/components/PostList.astro +++ b/src/components/PostList.astro @@ -84,7 +84,7 @@ const mmdd = (d: Date) => color: var(--ink-2); font-variant-numeric: tabular-nums; } - .shelf-link { float: right; font-size: 12.5px; color: var(--accent); font-weight: 400; } + .shelf-link { float: right; font-size: 12.5px; padding-block: 4px; color: var(--accent); font-weight: 400; } .track { display: flex; align-items: center; @@ -123,6 +123,10 @@ const mmdd = (d: Date) => flex: none; } .row-title { + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; flex: 1; min-width: 0; font-size: 16px; @@ -132,7 +136,11 @@ const mmdd = (d: Date) => white-space: nowrap; transition: color var(--dur) var(--ease); } - .row:hover .row-title { color: var(--accent); } + .row:hover .row-title { + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; color: var(--accent); } .tag { flex: none; font-family: var(--font-mono); diff --git a/src/components/geek/ProjectWall.astro b/src/components/geek/ProjectWall.astro index cca8fcb..460a3fc 100644 --- a/src/components/geek/ProjectWall.astro +++ b/src/components/geek/ProjectWall.astro @@ -3,11 +3,16 @@ // 双源数据见 src/lib/projects.ts;语言色点用 linguist 常用色映射 import { loadProjects, type RepoCard } from '../../lib/projects'; +import { t } from '../../i18n/ui'; +import type { Locale } from '../../lib/locale'; + interface Props { /** 展示上限:首页精选 6;/opensource 全量传大数 */ limit?: number; + locale?: Locale; } -const { limit = 6 } = Astro.props; +const { limit = 6, locale = 'zh' } = Astro.props; +const title = t('open_source_projects', locale); const LANG_COLORS: Record = { Astro: '#ff5a03', TypeScript: '#3178c6', JavaScript: '#f1e05a', Python: '#3572a5', Go: '#00add8', Rust: '#dea584', Shell: '#89e051', @@ -23,7 +28,7 @@ try { ---
    -

    开源 · Projects

    +

    {title}

      {projects.map((p) => (
    • diff --git a/src/pages/en/index.astro b/src/pages/en/index.astro index 02c8b8f..5822c77 100644 --- a/src/pages/en/index.astro +++ b/src/pages/en/index.astro @@ -99,7 +99,7 @@ const photos = (await getCollection('photos')).slice(0, 4); note: t('heatmap_note', 'en'), }} /> - + From ad731aea696dd1a77562b38d14f7ed53aafe4741 Mon Sep 17 00:00:00 2001 From: wait4xx Date: Sun, 25 Oct 2026 19:52:36 +0800 Subject: [PATCH 6/6] =?UTF-8?q?chore(spec):=20=E5=BD=92=E6=A1=A3=20add-blo?= =?UTF-8?q?g-5c=E2=80=94=E2=80=94=E4=BA=94=E4=BB=BB=E5=8A=A1=E6=94=B6?= =?UTF-8?q?=E6=95=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Spec: 2026-10-25_add-blog-5c Co-authored-by: GLM-5.3 --- .spec/specs/2026-10-25_add-blog-5c/runs.jsonl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.spec/specs/2026-10-25_add-blog-5c/runs.jsonl b/.spec/specs/2026-10-25_add-blog-5c/runs.jsonl index ada89cd..49fa13e 100644 --- a/.spec/specs/2026-10-25_add-blog-5c/runs.jsonl +++ b/.spec/specs/2026-10-25_add-blog-5c/runs.jsonl @@ -3,3 +3,9 @@ {"task": "task_0003", "cmd": "test -f /tmp/e2e-shots/lighthouse-report.json", "exit": 0, "duration_s": 0.001, "stdout_tail_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "tree_fp": "81a104fc3ebd61038f938c2d941d11747896a6c2494c5c0144641d3b44f6aa6d", "at": "2026-10-25T11:46:29+00:00"} {"task": "task_0004", "cmd": "pnpm astro build && test -f dist/en/archives/index.html", "exit": 0, "duration_s": 5.983, "stdout_tail_sha256": "716c18bb1350481ad0930179291341dd6d0622a6c2cc029d84cd4e5fd67f9e79", "tree_fp": "81a104fc3ebd61038f938c2d941d11747896a6c2494c5c0144641d3b44f6aa6d", "at": "2026-10-25T11:46:35+00:00"} {"task": "task_0005", "cmd": "node tests/e2e.mjs 2>&1 | grep -q \"30/30 PASS\"", "exit": 0, "duration_s": 14.512, "stdout_tail_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "tree_fp": "81a104fc3ebd61038f938c2d941d11747896a6c2494c5c0144641d3b44f6aa6d", "at": "2026-10-25T11:46:49+00:00"} +{"task": "task_0001", "cmd": "bash infra/deploy/build.sh && grep -q \"0e792a7\\|[a-f0-9]\\{7\\}\" ~/docker_apps/blog/www-staging/.build-stamp 2>/dev/null || bash infra/deploy/build.sh", "exit": 0, "duration_s": 6.931, "stdout_tail_sha256": "69918ee818f11f5a3ec97f35763d7feea09c48198237dc83711c555916ed6e9b", "tree_fp": "35a4b2d05d6ebe56e564ca23cffe9ca3ed016d955387eeb098aafc2f909668da", "at": "2026-10-25T11:50:41+00:00"} +{"task": "task_0002", "cmd": "node tests/e2e.mjs 2>&1 | grep -q \"30/30 PASS\"", "exit": 0, "duration_s": 19.577, "stdout_tail_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "tree_fp": "35a4b2d05d6ebe56e564ca23cffe9ca3ed016d955387eeb098aafc2f909668da", "at": "2026-10-25T11:51:00+00:00"} +{"task": "task_0003", "cmd": "test -f /tmp/e2e-shots/lighthouse-report.json", "exit": 0, "duration_s": 0.001, "stdout_tail_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "tree_fp": "35a4b2d05d6ebe56e564ca23cffe9ca3ed016d955387eeb098aafc2f909668da", "at": "2026-10-25T11:51:00+00:00"} +{"task": "task_0004", "cmd": "pnpm astro build && test -f dist/en/archives/index.html", "exit": 0, "duration_s": 6.947, "stdout_tail_sha256": "e23c70f1eaae7d14f05c341a03368cd098d32940deb8ec85842378195894e5d0", "tree_fp": "35a4b2d05d6ebe56e564ca23cffe9ca3ed016d955387eeb098aafc2f909668da", "at": "2026-10-25T11:51:07+00:00"} +{"task": "task_0005", "cmd": "node tests/e2e.mjs 2>&1 | grep -q \"30/30 PASS\"", "exit": 1, "duration_s": 14.487, "stdout_tail_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "tree_fp": "35a4b2d05d6ebe56e564ca23cffe9ca3ed016d955387eeb098aafc2f909668da", "at": "2026-10-25T11:51:22+00:00"} +{"task": "task_0005", "cmd": "node tests/e2e.mjs 2>&1 | grep -q \"30/30 PASS\"", "exit": 0, "duration_s": 14.536, "stdout_tail_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "tree_fp": "35a4b2d05d6ebe56e564ca23cffe9ca3ed016d955387eeb098aafc2f909668da", "at": "2026-10-25T11:52:36+00:00"}