feat(5f): 代码块窗条+书评纸卡(T5/T6 车道收编,视觉复核 PASS)

- T5:≥4 行块挂 .code-win 窗条(JS 数行标类+伪元素渲染:语言
  标签取 shiki pre[data-language]+灰阶三点 #30363d 投影法一点化
  三点,弃红黄绿;条 #161b22 恒深区亮暗同判);单行/2行裸块;
  csge 文 4/18 判定边界实测准确;scoped 够不着 Content 内 pre
  坑用 :global 解
- T6:书评正文圆角纸卡(亮 #f4f4f2/暗 #201d18 暖棕夜灯,零新
  变量吃 T7 已入 tokens;1px hairline 四边抑制长文竖线感;标题
  区外置结构未动)
- 视觉复核(glm-5.3-flash:max 像素级):T5 四项全 PASS(三点实
  采样无红黄绿、条/点/体 github-dark 同族灰、与块顶圆角对齐);
  T6 双态 PASS;暗态窗条未入截图框为残留观察项(恒定 CSS 推定
  成立,判词 /tmp/beauty-impl/verdict.md)
- tasks.md T5 verify 落点修正(src/pages/archives,车道如实报
  告);E2E ? 浮层偶发竞态 2/2 复跑绿

车道:op-artalk(自验 5/5+2/2);收编验收:窗条 4/18+data-lang+
纸卡双态 computed+视觉双卡 PASS

验证: E2E 34/34 PASS(E2E_BASE=dist 口);窗条/纸卡 computed 与
      像素采样全对

Spec: 2026-10-26_blog-5f

Co-authored-by: GLM-5.3
This commit is contained in:
wait4xandGLM-5.3 committed 2026-11-01 09:31:22 +08:00
1 parent 814b138f89
commit 2bfe2ef3db
4 files changed
+146 -25

No files matched your search

+16 -16
View File
@@ -36,14 +36,14 @@
## 3. 功能范围
### 3.1 核心功能(MVP)
- [ ] T1 SEO 仓库侧三件:robots.txt 新建 + /admin 局部 noindex 预案 + 用户操作单(nginx/ESA)
- [ ] T2 localePath 双 en 修复 + 自指 canonical + 尾斜杠归一
- [ ] T3 构建期内容 lint(断链/图片/frontmatter/双语配对)+ 生产冒烟脚本 + build.sh 守卫挂载
- [ ] T4 reader 纯文本模式(html.reader 单 class + 命令面板 reader + r 键 + localStorage)
- [ ] T5 代码块窗条(P 卡:≥4 行灰阶窗+mono 标题+灰阶三点,单行裸 pill)
- [ ] T6 书评纸面板(Q 卡:圆角米灰纸卡+暗色暖棕+hairline 边框防长文竖线感)
- [ ] T7 版式基底(K/L/O 卡:正文流距 token 化+灰阶补档+暗色 hairline 降档+微交互曲线统一+focus ring 收尾)
- [ ] T8 首屏 tagline + 图库质感(M/N 卡:IdCard 身份语≤20 字+图库圆角统一+暗色 4% 衬底)
- [x] T1 SEO 仓库侧三件:robots.txt 新建 + /admin 局部 noindex 预案 + 用户操作单(nginx/ESA)
- [x] T2 localePath 双 en 修复 + 自指 canonical + 尾斜杠归一
- [x] T3 构建期内容 lint(断链/图片/frontmatter/双语配对)+ 生产冒烟脚本 + build.sh 守卫挂载
- [x] T4 reader 纯文本模式(html.reader 单 class + 命令面板 reader + r 键 + localStorage)
- [x] T5 代码块窗条(P 卡:≥4 行灰阶窗+mono 标题+灰阶三点,单行裸 pill)
- [x] T6 书评纸面板(Q 卡:圆角米灰纸卡+暗色暖棕+hairline 边框防长文竖线感)
- [x] T7 版式基底(K/L/O 卡:正文流距 token 化+灰阶补档+暗色 hairline 降档+微交互曲线统一+focus ring 收尾)
- [x] T8 首屏 tagline + 图库质感(M/N 卡:IdCard 身份语≤20 字+图库圆角统一+暗色 4% 衬底)
### 3.2 扩展功能
- 无(R 热力图暗色补救列下一批)
@@ -72,14 +72,14 @@
- 升级触发:若 T7 灰阶补档波及 Header/导航布局,升级为全站密集扫描并记录
## 6. 成功标准与验证方式
- [ ] dist 每页含自指 canonical 且 en 页无 /en/en/ -> verify: task_0002
- [ ] robots.txt 产物存在且含 Sitemap 行 -> verify: task_0001
- [ ] 人为断链能让 build 输出 FAIL 清单(warn 一周策略首版即 fail 可议) -> verify: task_0003
- [ ] reader 触发后 .bg-glow 隐藏/ICP 可见/刷新保持 -> verify: task_0004
- [ ] 多行代码块有窗条与灰阶三点、单行无 -> verify: task_0005
- [ ] 书评正文为圆角纸卡(computed border-radius+背景) -> verify: task_0006
- [ ] 流距 token 生效(h2 前后距为变量值)+focus ring 统一 -> verify: task_0007
- [ ] 首屏 tagline 渲染≤20 字+图库圆角统一 -> verify: task_0008
- [x] dist 每页含自指 canonical 且 en 页无 /en/en/ -> verify: task_0002
- [x] robots.txt 产物存在且含 Sitemap 行 -> verify: task_0001
- [x] 人为断链能让 build 输出 FAIL 清单(warn 一周策略首版即 fail 可议) -> verify: task_0003
- [x] reader 触发后 .bg-glow 隐藏/ICP 可见/刷新保持 -> verify: task_0004
- [x] 多行代码块有窗条与灰阶三点、单行无 -> verify: task_0005
- [x] 书评正文为圆角纸卡(computed border-radius+背景) -> verify: task_0006
- [x] 流距 token 生效(h2 前后距为变量值)+focus ring 统一 -> verify: task_0007
- [x] 首屏 tagline 渲染≤20 字+图库圆角统一 -> verify: task_0008
## 7. 风险与约束
- 灰阶红线:所有美化以 08 §6 裁定为终态依据
+9 -9
View File
@@ -5,42 +5,42 @@
## 阶段一:实现
- [ ] T1 SEO 仓库侧三件(robots.txt + admin noindex 预案 + 用户操作单)
- [x] T1 SEO 仓库侧三件(robots.txt + admin noindex 预案 + 用户操作单)
- claim: orchestrator
- boundary: 新建 public/robots.txt(Allow 全站;Disallow /admin/ /draft/;Sitemap 行);docs/上线检查单.md 追加「SEO 解封操作单」节(nginx snippet 行号/内部 vhost 补回清单/ESA purge+TTL+绕缓存三动作);nginx/ESA 实操归用户
- verify: `test -f public/robots.txt && grep -q "Sitemap: https://wait4x.online/sitemap-index.xml" public/robots.txt && grep -q "Disallow: /admin" public/robots.txt && grep -q "SEO 解封操作单" docs/上线检查单.md`
- [ ] T2 localePath 双 en 修复 + 自指 canonical + 尾斜杠归一
- [x] T2 localePath 双 en 修复 + 自指 canonical + 尾斜杠归一
- claim: op-gitea(mesh 车道)
- boundary: src/lib/locale.ts(先归一 zh 再拼前缀);BaseLayout.astro 加自指 canonical(Astro.site+withSlash);Header.astro nav-lang href 尾斜杠补齐;E2E 可加断言
- verify: `! grep -r "/en/en/" dist/ && grep -q 'rel="canonical"' dist/en/archives/mian-sha/index.html && node tests/e2e.mjs 2>&1 | grep -q "34/34 PASS"`
- [ ] T3 构建期内容 lint + 生产冒烟 + build.sh 守卫挂载
- [x] T3 构建期内容 lint + 生产冒烟 + build.sh 守卫挂载
- claim: op-gitea(mesh 车道)
- boundary: 新建 scripts/build-perf/content-lint.mjs(dist 断链:站内 a[href] 逐一对产物;图片存在性;zh/en 配对警告)+ scripts/build-perf/prod-smoke.sh(curl robots/sitemap/首页头三断言);build.sh 守卫行(存在性守卫铁律);首版 fail 但提供 SKIP 环境变量逃生门
- verify: `node scripts/build-perf/content-lint.mjs dist && bash -n infra/deploy/build.sh && test -x scripts/build-perf/prod-smoke.sh`
- [ ] T4 reader 纯文本模式(html.reader + 命令面板 + r 键 + 持久化)
- [x] T4 reader 纯文本模式(html.reader + 命令面板 + r 键 + 持久化)
- claim: orchestrator
- boundary: global.css 尾部集中 html.reader 段(按 08 文档 §4.4 清单:隐 bg-glow/进度条/收 Header 尾部钮/Footer 收单行 ICP 必留;正文字号微升行宽收窄);CommandPalette data-cmd="reader" + ui.ts cmd_reader 双语键;Keyboard r 单键;localStorage 持久化;reduced-motion/无 JS 天然兼容(纯 class)
- verify: `node tests/e2e.mjs 2>&1 | grep -q "34/34 PASS" && grep -q 'data-cmd="reader"' src/components/CommandPalette.astro && grep -q "html.reader" src/styles/global.css`
- [ ] T5 代码块窗条(P 卡:≥4 行灰阶窗+mono 标题+灰阶三点)
- [x] T5 代码块窗条(P 卡:≥4 行灰阶窗+mono 标题+灰阶三点)
- claim: op-artalk(mesh 车道)
- boundary: 技术轨代码块渲染层(自查源码定落点:prose-tech 容器/全局 pre 样式);≥4 行块加窗条(灰阶三点非红黄绿+mono 语言/文件标签),单行块裸 pill;亮暗双态;scoped 样式优先不动 global.css(与 T7 划界)
- verify: `node tests/e2e.mjs 2>&1 | grep -q "34/34 PASS" && grep -rqE "code-win|codebar|窗条" src/styles/ src/components/`
- verify: `node tests/e2e.mjs 2>&1 | grep -q "34/34 PASS" && grep -rqE "code-win|codebar|窗条" src/pages/archives/`
- [ ] T6 书评纸面板(Q 卡:圆角米灰纸卡+暗色暖棕+hairline 边框)
- [x] T6 书评纸面板(Q 卡:圆角米灰纸卡+暗色暖棕+hairline 边框)
- claim: op-artalk(mesh 车道)
- boundary: 书评正文容器(prose-reading 所在组件 scoped 样式):圆角纸卡+米灰 #f4f4f2 系亮/暖棕暗态;边框 hairline 级防长文竖线感(08 裁定 §6-2);标题区外置结构不动;--paper 变量需求提给 orchestrator 入 tokens(T7 协同),不直接改 tokens.css
- verify: `node tests/e2e.mjs 2>&1 | grep -q "34/34 PASS" && grep -rq "paper" src/components/ | grep -v node_modules`
- [ ] T7 版式基底(K/L/O 卡:流距 token+灰阶补档+暗色 hairline 降档+微交互曲线+focus ring)
- [x] T7 版式基底(K/L/O 卡:流距 token+灰阶补档+暗色 hairline 降档+微交互曲线+focus ring)
- claim: orchestrator(tokens/global 专属车道)
- boundary: tokens.css(--flow-* 流距五变量+灰阶补档评估+--paper 纸色变量);global.css(h2/h3/p+p 改用流距变量、暗色 hairline 降一档、过渡曲线统一 token :where 一段、focus-visible ring 统一);标题字重 700→600(裁定 #6);行距 +2~4px(裁定 #7);移动端密集扫描复测(涉布局面)
- verify: `node tests/e2e.mjs 2>&1 | grep -q "34/34 PASS" && grep -q "flow-h2-pre" src/styles/tokens.css && grep -q "focus-visible" src/styles/global.css`
- [ ] T8 首屏 tagline + 图库质感(M/N 卡:身份语+圆角统一+暗色衬底)
- [x] T8 首屏 tagline + 图库质感(M/N 卡:身份语+圆角统一+暗色衬底)
- claim: orchestrator
- boundary: 首页身份区(IdCard 或等位组件)加 tagline ≤20 字(措辞备两案入完成总结供用户选,默认「写作者 · 读者 · 快门按压者」);GalleryPage 圆角统一 --radius-img 暗色 4% 衬底(裁定 §6-4 双模式分判;衬底圆角≤照片圆角防框中框);亮色直贴不动
- verify: `node tests/e2e.mjs 2>&1 | grep -q "34/34 PASS" && grep -rq "tagline" src/pages/index.astro src/components/ && grep -q "radius-img" src/styles/tokens.css`
+61
View File
@@ -133,6 +133,24 @@ const toc = headings.filter((h) => h.depth === 2 || h.depth === 3);
addEventListener('scroll', paint, { passive: true });
document.addEventListener('astro:page-load', paint);
}
// 代码块窗条标记(P 卡 §6-3):纯 CSS 无法按内容行数条件挂伪元素,
// 此处数行加类;语言标签取 shiki 的 language-* 类名写入 data-lang 供 CSS attr() 渲染
const markCodeWins = () => {
document.querySelectorAll('.prose-tech pre:not(.code-win)').forEach((pre) => {
const code = pre.querySelector('code');
if (!code) return;
const lines = (code.textContent ?? '').split('\n').length;
if (lines >= 4) {
// astro shiki 语言落 pre[data-language](code 类名为空)
const lang = pre.dataset.language ?? /language-([\w-]+)/.exec(code.className)?.[1] ?? 'text';
pre.dataset.lang = lang;
pre.classList.add('code-win');
}
});
};
document.addEventListener('astro:page-load', markCodeWins);
markCodeWins();
</script>
<style>
@@ -277,4 +295,47 @@ const toc = headings.filter((h) => h.depth === 2 || h.depth === 3);
}
.pn-item:hover strong { color: var(--accent); }
#artalk-container { margin-top: var(--space-5); }
/* ---- T5 代码块窗条(08 裁定 §6-3):≥4 行挂窗条,单行块维持现状裸块 ---- */
.prose-tech :global(pre.code-win) {
position: relative;
padding-top: calc(var(--space-3) + 30px);
}
.prose-tech :global(pre.code-win::before) {
content: attr(data-lang);
position: absolute;
inset: 0 0 auto 0;
height: calc(var(--space-3) + 30px - 1px);
display: flex;
align-items: flex-end;
padding: 0 14px 8px 66px;
background: #161b22; /* code-bg 上一档:恒深区亮暗同判(§6-3 暗色收益最大) */
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
border-radius: 8px 8px 0 0;
font-family: var(--font-mono);
font-size: 11px;
letter-spacing: 0.04em;
color: #6e7681;
text-transform: lowercase;
}
/* 灰阶三点(§6-3 裁定弃红黄绿):terminal 页同款点色,伪元素本体一点+投影两点 */
.prose-tech :global(pre.code-win::after) {
content: '';
position: absolute;
top: 10px;
left: 14px;
width: 10px;
height: 10px;
border-radius: 50%;
background: #30363d;
box-shadow: 18px 0 0 #30363d, 36px 0 0 #30363d;
}
/* ---- T6 书评纸卡(08 裁定 §6-2):通栏 hairline 升圆角纸卡 ----
--paper 三态已入 tokens(亮米白/暗暖棕夜灯),零新变量;7000px 长文
左右边框恒为 1px hairline 防两根竖线感(§6-2 落地注意) */
.prose-reading {
border: 1px solid var(--paper-line);
border-radius: 12px;
padding: var(--space-5) clamp(20px, 4vw, 32px);
}
</style>
+60
View File
@@ -78,4 +78,64 @@ const minutes = Math.max(1, Math.ceil(words / 220));
.pn { display: flex; flex-direction: column; gap: var(--space-2); margin-top: var(--space-5); }
.pn-item { font-size: 14px; color: var(--ink-2); transition: color var(--dur) var(--ease); }
.pn-item:hover { color: var(--accent); }
/* ---- T5 code window bar (§6-3): ≥4-line blocks only ---- */
.prose-tech :global(pre.code-win) {
position: relative;
padding-top: calc(var(--space-3) + 30px);
}
.prose-tech :global(pre.code-win::before) {
content: attr(data-lang);
position: absolute;
inset: 0 0 auto 0;
height: calc(var(--space-3) + 30px - 1px);
display: flex;
align-items: flex-end;
padding: 0 14px 8px 66px;
background: #161b22;
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
border-radius: 8px 8px 0 0;
font-family: var(--font-mono);
font-size: 11px;
letter-spacing: 0.04em;
color: #6e7681;
text-transform: lowercase;
}
.prose-tech :global(pre.code-win::after) {
content: '';
position: absolute;
top: 10px;
left: 14px;
width: 10px;
height: 10px;
border-radius: 50%;
background: #30363d;
box-shadow: 18px 0 0 #30363d, 36px 0 0 #30363d;
}
/* ---- T6 reading paper card (§6-2) ---- */
.prose-reading {
border: 1px solid var(--paper-line);
border-radius: 12px;
padding: var(--space-5) clamp(20px, 4vw, 32px);
}
</style>
<script>
// Code window bar marker (same as zh track): count lines, tag .code-win + data-lang
const markCodeWins = () => {
document.querySelectorAll('.prose-tech pre:not(.code-win)').forEach((pre) => {
const code = pre.querySelector('code');
if (!code) return;
const lines = (code.textContent ?? '').split('\n').length;
if (lines >= 4) {
// astro shiki 语言落 pre[data-language](code 类名为空)
const lang = pre.dataset.language ?? /language-([\w-]+)/.exec(code.className)?.[1] ?? 'text';
pre.dataset.lang = lang;
pre.classList.add('code-win');
}
});
};
document.addEventListener('astro:page-load', markCodeWins);
markCodeWins();
</script>