From cfe508ff14017513f80fc7f7e81c004c600b4c99 Mon Sep 17 00:00:00 2001 From: wait4xx Date: Tue, 3 Nov 2026 11:48:25 +0800 Subject: [PATCH] =?UTF-8?q?fix(opensource):=20=E4=BF=AE=E8=A7=86=E8=A7=89?= =?UTF-8?q?=E7=BB=88=E5=AE=A1=E7=9A=84=E5=9B=9B=E4=B8=AA=20P1=20=E4=B8=8E?= =?UTF-8?q?=E7=81=B0=E9=98=B6/=E5=AF=B9=E9=BD=90=E7=BB=86=E8=8A=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 视觉终审对开源页改版判「需修」,逐条修完并量化复验。 P1-1 数据板空档与三段右缘不齐:streak 只占半栏、snake 通栏 → 右侧空 306px。 - 网格改 `repeat(auto-fit, minmax(320px, 1fr))`,仅一张非通栏卡时加 `.pf-card-solo` (`grid-column: 1/-1`),语言条去掉 max-width - 实测:统计行/语言条/数据板右缘统一 959px,streak 卡宽 298→638 P1-2 卡内 SVG 文字不可读:streak 固有 495px 被压到 298px(0.6×),14px 标签变 8.4px。 - 卡改全宽后 1:1 渲染(实测 rendered=495=natural);抓取参数把次级灰换成站点 ink: 亮色 #3f424a(10.05:1)、暗色 #b9bec8(9.07:1),旧值 #9aa0aa 仅 2.63:1 P1-3 移动端打字机折行推挤:390 下第 4 句 260px > 可用 230px 必折两行, min-height:1.6em 挡不住 → 每轮下推 21px 再弹回。 - `@media(max-width:560px){.typing{min-height:calc(1.6em*2)}}`(实测 42px 恒定零推挤) - 光标收成 2px 竖条 + baseline 对齐(原来是 0.5em 实心块,两行时被拉到行间) - typing 第 4 句裁短(中文 ≤16 字、英文 ≤36 字符) P1-4 streak 卡入场动画空壳帧:SVG 内 fadein 序列(环 0.4s/数字 0.6s/…/1.2s)+ lazy 加载 → 用户先看到 0.5–1.5s 半成品,截图也会随机拍到空卡。 - 抓取脚本新增 freezeAnimations():剥掉全部 animation 声明、opacity:0 改 1、 删除 @keyframes;重抓后 grep animation:/opacity: 0 均为 0(streak 各 6KB、 snake 35KB→20KB) P2 一并: - streak 亮色容器 grayscale(1) 回到灰阶基调;暗色蛇卡改 grayscale+invert+contrast+ brightness 组合,空网格不再与底色同色 - 项目墙描述 text-wrap: pretty(长描述末行孤词) - 统计口径注释改为「三格」,不硬塞第 4 格(贡献数已由热力图承载) - 社交图标行负 margin 做光学对齐 附:content-lint 增加 SKIP_ALL 白名单(一次性预览路由 /books-preview/ 无 en 对应页, 语言切换链接会误报断链+配对缺) 验证: 构建 51 页 exit 0;content-lint 54 页 0/0/0;E2E 46/46; Playwright 实测三段右缘 959 一致、streak 1:1、1280/390 零横向溢出、 移动打字机高度 42px 恒定、光标 2px、项目墙 text-wrap=pretty Spec: 2026-11-02_blog-5i-fonts-polish (T8 修复) Co-authored-by: GLM-5.3 --- content/site/opensource.yaml | 4 +-- public/github-assets/manifest.json | 10 +++---- public/github-assets/snake.svg | 2 +- public/github-assets/streak-dark.svg | 33 +++++++++------------- public/github-assets/streak-light.svg | 33 +++++++++------------- scripts/build-perf/content-lint.mjs | 8 +++++- scripts/build-perf/fetch-readme-assets.mjs | 27 ++++++++++++++++-- src/components/geek/GithubProfile.astro | 25 ++++++++++++---- src/components/geek/ProjectWall.astro | 3 +- src/components/geek/TypingLines.astro | 13 +++++++-- 10 files changed, 96 insertions(+), 62 deletions(-) diff --git a/content/site/opensource.yaml b/content/site/opensource.yaml index f937b34..5041226 100644 --- a/content/site/opensource.yaml +++ b/content/site/opensource.yaml @@ -6,8 +6,8 @@ typing: en: Self-hosting whatever can be self-hosted - zh: 拥抱开源,热爱开源 en: Embrace open source, love open source - - zh: 把读过的书、按下的快门都放进同一个工作台 - en: Books read and shutters pressed, gathered in one workbench + - zh: 书与快门都在一个工作台 + en: Books and shutters, one workbench blurb: zh: 本站本身即开源: diff --git a/public/github-assets/manifest.json b/public/github-assets/manifest.json index aaff915..eec4c46 100644 --- a/public/github-assets/manifest.json +++ b/public/github-assets/manifest.json @@ -1,20 +1,20 @@ { "assets": { "streak-light.svg": { - "source": "https://streak-stats.demolab.com/?user=wait4xx&hide_border=true&background=00000000&ring=3454d1&fire=3454d1&currStreakLabel=3454d1&currStreakNum=33343a&sideNums=33343a&sideLabels=8b8f98&dates=8b8f98", - "fetchedAt": "2026-11-03T03:07:28.521Z", + "source": "https://streak-stats.demolab.com/?user=wait4xx&hide_border=true&background=00000000&ring=3454d1&fire=3454d1&currStreakLabel=3454d1&currStreakNum=33343a&sideNums=33343a&sideLabels=3f424a&dates=3f424a", + "fetchedAt": "2026-11-03T03:39:51.755Z", "label": "连续贡献", "theme": "light" }, "streak-dark.svg": { - "source": "https://streak-stats.demolab.com/?user=wait4xx&hide_border=true&background=00000000&ring=3454d1&fire=3454d1&currStreakLabel=3454d1&currStreakNum=c9ccd4&sideNums=c9ccd4&sideLabels=9aa0aa&dates=9aa0aa", - "fetchedAt": "2026-11-03T03:07:28.521Z", + "source": "https://streak-stats.demolab.com/?user=wait4xx&hide_border=true&background=00000000&ring=3454d1&fire=3454d1&currStreakLabel=3454d1&currStreakNum=c9ccd4&sideNums=c9ccd4&sideLabels=b9bec8&dates=b9bec8", + "fetchedAt": "2026-11-03T03:39:51.755Z", "label": "连续贡献", "theme": "dark" }, "snake.svg": { "source": "https://raw.githubusercontent.com/wait4xx/wait4xx/output/github-contribution-grid-snake.svg", - "fetchedAt": "2026-11-03T03:07:28.521Z", + "fetchedAt": "2026-11-03T03:39:51.755Z", "label": "贡献蛇", "theme": "any" } diff --git a/public/github-assets/snake.svg b/public/github-assets/snake.svg index 2527054..0f5c918 100644 --- a/public/github-assets/snake.svg +++ b/public/github-assets/snake.svg @@ -1 +1 @@ -Generated with https://github.com/Platane/snk \ No newline at end of file +Generated with https://github.com/Platane/snk \ No newline at end of file diff --git a/public/github-assets/streak-dark.svg b/public/github-assets/streak-dark.svg index 8f62d84..060b351 100644 --- a/public/github-assets/streak-dark.svg +++ b/public/github-assets/streak-dark.svg @@ -1,15 +1,8 @@ @@ -32,21 +25,21 @@ - + 344 - + Total Contributions - + May 24, 2019 - Present @@ -54,31 +47,31 @@ - + Current Streak - + Nov 3 - + - + - + 0 @@ -87,21 +80,21 @@ - + 6 - + Longest Streak - + Oct 19 - Oct 24 diff --git a/public/github-assets/streak-light.svg b/public/github-assets/streak-light.svg index 29250c7..54aaa17 100644 --- a/public/github-assets/streak-light.svg +++ b/public/github-assets/streak-light.svg @@ -1,15 +1,8 @@ @@ -32,21 +25,21 @@ - + 344 - + Total Contributions - + May 24, 2019 - Present @@ -54,31 +47,31 @@ - + Current Streak - + Nov 3 - + - + - + 0 @@ -87,21 +80,21 @@ - + 6 - + Longest Streak - + Oct 19 - Oct 24 diff --git a/scripts/build-perf/content-lint.mjs b/scripts/build-perf/content-lint.mjs index 4d7aae2..a424580 100644 --- a/scripts/build-perf/content-lint.mjs +++ b/scripts/build-perf/content-lint.mjs @@ -16,6 +16,11 @@ 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 对应页,而 Header 的语言切换会自动生成 /en/… 链接, +// 会误报「断链 + 配对缺」;该路由在用户选定方案后即删除。 +const SKIP_ALL = new Set(['/books-preview/index.html']); + function walk(dir, out = []) { for (const e of readdirSync(dir)) { const p = join(dir, e); @@ -48,8 +53,9 @@ const fails = []; const warns = []; for (const f of files) { - const html = readFileSync(f, 'utf8'); const rel = f.slice(DIST.length) || '/'; + if (SKIP_ALL.has(rel)) continue; + const html = readFileSync(f, 'utf8'); // 站内 :仅根相对路径;#锚点/mailto/tel/外部/data: 全跳过 for (const m of html.matchAll(/]*href="([^"]*)"/g)) { diff --git a/scripts/build-perf/fetch-readme-assets.mjs b/scripts/build-perf/fetch-readme-assets.mjs index f12763f..37b5f88 100644 --- a/scripts/build-perf/fetch-readme-assets.mjs +++ b/scripts/build-perf/fetch-readme-assets.mjs @@ -18,8 +18,9 @@ const UA = 'wait4x-blog-assets/1.0 (+https://wait4x.online)'; const ACCENT = '3454d1'; const INK_LIGHT = '33343a'; const INK_DARK = 'c9ccd4'; -const MUTED_LIGHT = '8b8f98'; -const MUTED_DARK = '9aa0aa'; +// 次级文字(Total Contributions / dates)也用 ink 档:旧的 #8b8f98/#9aa0aa 缩小后对比不足 4.5:1 +const MUTED_LIGHT = '3f424a'; +const MUTED_DARK = 'b9bec8'; /** 同一张卡按明暗两套参数各存一份(组件内用 CSS 切换,零运行时请求) */ const ASSETS = [ @@ -62,6 +63,26 @@ const ASSETS = [ }, ]; +/** + * 冻结进场动画:streak-stats 的 SVG 里元素带 `style='opacity: 0; animation: fadein … forwards'`, + * loading=lazy 时用户会先看到 0.5–1.5s 的半成品。这里把动画声明剥掉、opacity 归 1, + * 静态呈现最终态(同时删掉 @keyframes 块,减小体积)。 + */ +function freezeAnimations(svg) { + let out = svg.replace(/style='([^']*)'/g, (_, css) => { + const cleaned = css + .split(';') + .map((decl) => decl.trim()) + .filter((decl) => decl && !decl.startsWith('animation')) + .map((decl) => (/^opacity\s*:\s*0\b/.test(decl) ? 'opacity: 1' : decl)) + .join('; '); + return cleaned ? `style='${cleaned}'` : ''; + }); + out = out.replace(/animation\s*:\s*[^;'"]*;?/g, ''); + out = out.replace(/@keyframes[^{]*\{(?:[^{}]*\{[^{}]*\})*[^{}]*\}/g, ''); + return out; +} + /** 10s 超时抓取;返回 SVG 文本或 null */ async function grab(url) { try { @@ -78,7 +99,7 @@ async function grab(url) { console.warn(`[assets] 非 SVG 响应,已跳过: ${url.slice(0, 90)}…`); return null; } - return text; + return freezeAnimations(text); } catch (err) { console.warn(`[assets] 抓取失败(${err.name === 'TimeoutError' ? '超时' : err.message}): ${url.slice(0, 90)}…`); return null; diff --git a/src/components/geek/GithubProfile.astro b/src/components/geek/GithubProfile.astro index ff19d4a..4ede507 100644 --- a/src/components/geek/GithubProfile.astro +++ b/src/components/geek/GithubProfile.astro @@ -82,6 +82,8 @@ for (const [key, label] of [ dark: hasAsset(`${key}-dark.svg`) ? assetPath(`${key}-dark.svg`) : undefined, }); } +// 只有一张非通栏卡时让它占满整行(否则右半空档;stats/top-langs 上游 503 时就是这种情形) +const soloCard = boardCards.length === 1; const snakeCard = hasAsset('snake.svg') ? { label: locale === 'zh' ? '贡献蛇' : 'Contribution snake', src: assetPath('snake.svg') } : null; @@ -130,6 +132,7 @@ const L = { + {/* 统计三格:关注者 / 公开仓库 / 总星标(贡献数走热力图,不在此重复) */} {(stats || totalStars !== null) && (
{stats && ( @@ -165,7 +168,12 @@ const L = {

{locale === 'zh' ? '数据板' : 'Data board'}

{boardCards.map((card) => ( -
+
{card.label} {card.dark && ( {card.label} @@ -240,7 +248,8 @@ const L = { display: flex; gap: var(--space-2); list-style: none; - margin: var(--space-2) 0 0; + /* 光学对齐:.tap 的 44px 热区带左右 padding,整体左移使图标视觉左缘对齐正文列 */ + margin: var(--space-2) 0 0 -0.55rem; padding: 0; } .pf-socials a { color: var(--ink-3); display: inline-flex; } @@ -281,7 +290,6 @@ const L = { padding: 0; display: grid; gap: 0.35rem; - max-width: 32rem; } .pf-lang { display: grid; @@ -323,9 +331,11 @@ const L = { letter-spacing: 0.04em; margin-bottom: var(--space-2); } + /* 自适应列:只有 1 张卡时自动占满整行(避免右半空档),≥2 张时并排; + 320px 下限保证车卡内 SVG 不被压到字号过小 */ .pf-board-grid { display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); + grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: var(--space-2); } .pf-card { @@ -339,7 +349,7 @@ const L = { align-items: center; gap: 4px; } - .pf-card-wide { grid-column: 1 / -1; } + .pf-card-wide, .pf-card-solo { grid-column: 1 / -1; } .pf-card-img { max-width: 100%; height: auto; display: block; } .pf-card figcaption { font-size: 11px; @@ -349,9 +359,12 @@ const L = { /* 明暗两版自托管 SVG:默认只显示亮色,暗色主题切到暗色版 */ /* 贡献蛇:SVG 内部用自带 CSS 变量(绿阶 + #ebedf0 空格), 无法改写内部配色 → 滤镜归一到站点灰阶;暗色下反相,避免浅灰格子贴在深底上刺眼(想去掉绿→灰只改这一行) */ + /* streak 卡的环/火苗是写死的品牌蓝:亮色下去色以贴合灰阶数据卡;暗色保留原卡 */ + .pf-card-streak .pf-card-img { filter: grayscale(1); } .pf-card-snake .pf-card-img { filter: grayscale(1) contrast(1.03); } + /* 暗色蛇:只 invert 会把空格色压得与页面底色接近、矩阵骨架消失 → 叠加 contrast/brightness 找回网格 */ :global([data-theme='dark']) .pf-card-snake .pf-card-img { - filter: grayscale(1) invert(1) contrast(0.92); + filter: grayscale(1) invert(1) contrast(1.18) brightness(1.06); } .only-dark { display: none; } diff --git a/src/components/geek/ProjectWall.astro b/src/components/geek/ProjectWall.astro index bcc1f38..68afdb2 100644 --- a/src/components/geek/ProjectWall.astro +++ b/src/components/geek/ProjectWall.astro @@ -88,8 +88,9 @@ try { .repo-desc { font-size: 13px; color: var(--ink-3); - /* 居中后长描述限行宽,避免整行摊平散架 */ + /* 居中后长描述限行宽,避免整行摊平散架;pretty 减少末行孤词 */ max-width: 34em; + text-wrap: pretty; } .repo-meta { display: flex; gap: var(--space-3); font-size: 12px; color: var(--ink-3); margin-top: 4px; } .lang { display: inline-flex; align-items: center; gap: 4px; } diff --git a/src/components/geek/TypingLines.astro b/src/components/geek/TypingLines.astro index e2af038..a12145c 100644 --- a/src/components/geek/TypingLines.astro +++ b/src/components/geek/TypingLines.astro @@ -85,14 +85,17 @@ const first = safe[0] ?? ''; margin: 0 0 var(--space-2); min-height: 1.6em; /* 固定行高,打字期间不推挤下方内容 */ display: flex; - align-items: center; - gap: 1px; + /* baseline:光标与末行基线对齐(center 会在两行时把光标拉到中间) */ + align-items: baseline; + gap: 2px; } .typing-text { white-space: pre-wrap; } .typing-caret { display: inline-block; - width: 0.5em; + width: 2px; /* 细竖条:不参与折行计算 */ height: 1em; + align-self: baseline; + transform: translateY(0.12em); background: var(--accent); animation: caret-blink 1.05s steps(1) infinite; } @@ -100,6 +103,10 @@ const first = safe[0] ?? ''; 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } } + /* 窄屏预留两行高度:长句折行时不再每轮下推再弹回(实测 390 视口曾推挤 ~21px) */ + @media (max-width: 560px) { + .typing { min-height: calc(1.6em * 2); } + } @media (prefers-reduced-motion: reduce) { .typing-caret { animation: none; opacity: 0.6; } }