<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>陌殊途左沐の手册</title><description>热爱是拯救无趣人生的唯一途径</description><link>https://azuma.mstzuomu.space/</link><templateTheme>Firefly</templateTheme><templateThemeVersion>6.16.8</templateThemeVersion><templateThemeUrl>https://github.com/CuteLeaf/Firefly</templateThemeUrl><lastBuildDate>2026年10月17日 20:55:33</lastBuildDate><item><title>备案通过后的图床重生记：把 CloudFlare-ImgBed 从 Telegram 渠道搬回境内服务器的完整复刻手册</title><link>https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-22/</link><guid isPermaLink="true">https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-22/</guid><description>ICP 备案下证后，本站完成从 Cloudflare 全家桶到腾讯云生态的整体迁移。本文以图床为主线：为什么 CloudFlare-ImgBed 在国内必死（Telegram 渠道、workers.dev 污染、EO 反代 522、CF 封死子域 zone 四重死因），如何无损导出数据，并在轻量服务器上用 Docker 原版程序 + Nginx + rclone WebDAV 重建出「网页上传 + 后台设置」的完整体验，附全部踩坑与可照抄的复刻步骤</description><pubDate>Sat, 17 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h1&gt;备案通过后的图床重生记：把 CloudFlare-ImgBed 从 Telegram 渠道搬回境内服务器的完整复刻手册&lt;a href=&quot;#备案通过后的图床重生记把-cloudflare-imgbed-从-telegram-渠道搬回境内服务器的完整复刻手册&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;2026 年 10 月，本站的 ICP 备案（&lt;code&gt;鲁ICP备2026062086号-1&lt;/code&gt;）下证，随后完成了一轮伤筋动骨的迁移：&lt;strong&gt;Cloudflare Pages → 腾讯云 EdgeOne Pages&lt;/strong&gt;，DNS 权威从 Cloudflare 切到 EdgeOne（qeodns），博客三入口归一到 &lt;code&gt;azuma.mstzuomu.space&lt;/code&gt;，途中顺手救活了友圈、发布台、Twikoo 评论三个子系统。&lt;/p&gt;&lt;p&gt;但所有变更里最凶险、坑最密的是&lt;strong&gt;图床&lt;/strong&gt;。本文以图床为主线写完整复刻手册——和我处境一样的人（CloudFlare-ImgBed + Telegram 渠道 + 国内备案 + 备案后必须境内访问）照着做就能重建；其他变更在第三节顺带记录。&lt;/p&gt;&lt;hr /&gt;&lt;section&gt;&lt;h2&gt;〇、变更总览（备案下证后都发生了什么）&lt;a href=&quot;#〇变更总览备案下证后都发生了什么&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;项目&lt;/th&gt;&lt;th&gt;变更前&lt;/th&gt;&lt;th&gt;变更后&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;博客托管&lt;/td&gt;&lt;td&gt;Cloudflare Pages&lt;/td&gt;&lt;td&gt;腾讯云 EdgeOne Pages&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;DNS 权威（NS）&lt;/td&gt;&lt;td&gt;Cloudflare&lt;/td&gt;&lt;td&gt;EdgeOne qeodns（&lt;code&gt;ns1/ns2.qeodns.com&lt;/code&gt;）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;主入口&lt;/td&gt;&lt;td&gt;&lt;code&gt;azuma.mstzuomu.space&lt;/code&gt;（CF 301）&lt;/td&gt;&lt;td&gt;&lt;code&gt;azuma.mstzuomu.space&lt;/code&gt;（页面脚本 302）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;页脚备案号&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;td&gt;&lt;code&gt;鲁ICP备2026062086号-1&lt;/code&gt;（&lt;code&gt;FooterConfig.html&lt;/code&gt;）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;图床&lt;/td&gt;&lt;td&gt;CF-ImgBed（Telegram 渠道，CF Worker）&lt;/td&gt;&lt;td&gt;&lt;strong&gt;CF-ImgBed Docker 版自托管在轻量服务器&lt;/strong&gt;（详见下文）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;fc&lt;/code&gt; / &lt;code&gt;pub&lt;/code&gt; / &lt;code&gt;twikoo&lt;/code&gt;&lt;/td&gt;&lt;td&gt;DNS 记录随 NS 迁移丢失&lt;/td&gt;&lt;td&gt;在 EO zone 补 CNAME → &lt;code&gt;cname.vercel-dns.com&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;备案载体&lt;/td&gt;&lt;td&gt;—&lt;/td&gt;&lt;td&gt;轻量应用服务器 &lt;code&gt;Ubuntu-o66q&lt;/code&gt;（&lt;code&gt;140.143.143.178&lt;/code&gt;，北京）&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;一、图床为什么必须搬：四重死因&lt;a href=&quot;#一图床为什么必须搬四重死因&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;原图床是 &lt;a href=&quot;https://github.com/MarSeventh/CloudFlare-ImgBed&quot; target=&quot;_blank&quot;&gt;MarSeventh/CloudFlare-ImgBed&lt;/a&gt;（前端为 Sanyue ImgHub），部署在 Cloudflare Worker 上，域名 &lt;code&gt;tu.mstzuomu.space&lt;/code&gt;。备案后它在国内的每条活路都被堵死了：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;图片本体存在 Telegram 渠道&lt;/strong&gt;。CF-ImgBed 是「元数据存 KV/D1、本体分流到外部渠道」的架构，本站全部图片的二进制在 Telegram 频道里，靠 Bot token 经 Worker 中转。国内服务器直连 Telegram 不可能，Worker 又跑在 CF 上；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;workers.dev&lt;/code&gt; 国内 DNS 污染&lt;/strong&gt;。实测 114DNS 与 DNSPod 对 &lt;code&gt;*.workers.dev&lt;/code&gt; 均返回假 IP（冒充脸书的地址段），国内访客直连必挂；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;EO 反代方案实测 522&lt;/strong&gt;。把 &lt;code&gt;tu&lt;/code&gt; 做成 EdgeOne 加速域名、回源 &lt;code&gt;workers.dev&lt;/code&gt;：EO 海外节点回源正常，&lt;strong&gt;国内节点回源超时（522 Connection Timeout with Origin Server）&lt;/strong&gt;——跨境链路被卡，等于白搭；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CF 封死子域 zone&lt;/strong&gt;。想把 &lt;code&gt;tu.mstzuomu.space&lt;/code&gt; 单独挂回 Cloudflare（NS 委派子域）：新版控制台拒绝子域名，API 报错码 &lt;code&gt;1116&lt;/code&gt;（服务器端硬限制）。而整域 NS 迁回 CF 会让博客瘫痪并与备案接入冲突，不可考虑。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;结论：&lt;strong&gt;数据必须导出，程序必须在境内自己跑&lt;/strong&gt;。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;二、图床重建复刻手册&lt;a href=&quot;#二图床重建复刻手册&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;2.1 架构定稿&lt;a href=&quot;#21-架构定稿&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;访客 → https://tu.mstzuomu.space&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;         &lt;/span&gt;&lt;/span&gt;&lt;span&gt;│&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Nginx（:80/:443，certbot 证书）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;         &lt;/span&gt;&lt;/span&gt;&lt;span&gt;├─ /file/*  → 磁盘静态直出（try_files，老图+新图都在这）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;         &lt;/span&gt;&lt;/span&gt;&lt;span&gt;│              查无此文件时回退 @imgbed&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;         &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└─ 其余路径  → 反代 http://127.0.0.1:7658&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;│&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;imgbed 容器（Docker）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;上传页 / 管理后台 / API&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;│ 存储渠道：WebDAV&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;▼&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rclone serve webdav（172.17.0.1:8081）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;▼&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/var/www/imgbed/file（真身，Nginx 同时静态托管）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;要点：新上传经 WebDAV 落到与静态托管同一目录，于是&lt;strong&gt;新老图片统一走 Nginx 静态&lt;/strong&gt;，应用只负责上传/管理；&lt;code&gt;?width=&lt;/code&gt; 尺寸参数会被 Nginx 无视并返回原图（博客端本就有回退兜底，显示无碍）。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;2.2 数据无损导出（旧站侧）&lt;a href=&quot;#22-数据无损导出旧站侧&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;CloudFlare-ImgBed 的「备份恢复」只导元数据不含图片二进制，别用它当迁移。可用路径：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;管理 API 枚举 + 逐张下载&lt;/strong&gt;（本文采用）：
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;POST /api/auth/adminLogin&lt;/code&gt;，JSON &lt;code&gt;{&quot;username&quot;:&quot;...&quot;,&quot;password&quot;:&quot;...&quot;}&lt;/code&gt;，保存 HttpOnly Cookie；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /api/manage/list?start=0&amp;amp;count=1000&amp;amp;recursive=true&lt;/code&gt;（或 &lt;code&gt;/api/manage/batch/list?limit=1000&amp;amp;includeValue=true&lt;/code&gt;）拿全量 fileId 列表；&lt;/li&gt;
&lt;li&gt;逐个 &lt;code&gt;GET /file/&amp;lt;URL逐段编码的fileId&amp;gt;&lt;/code&gt;（&lt;strong&gt;不带&lt;/strong&gt; &lt;code&gt;width&lt;/code&gt;/&lt;code&gt;fallback&lt;/code&gt; 参数即为原件），按 fileId 相对路径落盘——&lt;strong&gt;fileId 本身含目录&lt;/strong&gt;（如 &lt;code&gt;文章/1791196825739_19文章.jpg&lt;/code&gt;），目录结构天然保留；&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;也可在后台开启 WebDAV 后用 rclone/Cyberduck 递归下载，效果等同。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;本站实测：&lt;strong&gt;56 个文件、45.63 MiB、零失败&lt;/strong&gt;，JPEG/PNG/PDF 魔数头尾全量校验通过。导出目录整包留了一份本地备份（&lt;code&gt;imgbed-export&lt;/code&gt;），确认新站稳定后再删。&lt;/p&gt;&lt;p&gt;注意：Windows 下 PowerShell 5.1 读 UTF-8 JSON 会按 ANSI 解码炸掉，用 pwsh 7 或 &lt;code&gt;[IO.File]::ReadAllText(...,[Text.Encoding]::UTF8)&lt;/code&gt;；curl 提交登录 JSON 建议 &lt;code&gt;-d @文件&lt;/code&gt;（BOM 也会让 JSON 解析失败）。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;2.3 服务器侧：Nginx&lt;a href=&quot;#23-服务器侧nginx&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Ubuntu（备案载体轻量服务器，&lt;strong&gt;不要重装系统&lt;/strong&gt;——备案接入资源绑着它）上：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apt-get&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-y&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nginx&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mkdir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/var/www/imgbed/file&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;# 导出的目录树放进 file/ 下，对齐 /file/ URL 空间&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;站点配置（&lt;code&gt;/etc/nginx/sites-available/imgbed&lt;/code&gt;，核心段）：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;server&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;listen &lt;/span&gt;&lt;span&gt;80&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default_server&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;listen &lt;/span&gt;&lt;span&gt;[::]:80 &lt;/span&gt;&lt;span&gt;default_server&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;server_name &lt;/span&gt;&lt;span&gt;tu.mstzuomu.space _;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;root &lt;/span&gt;&lt;span&gt;/var/www/imgbed;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;charset &lt;/span&gt;&lt;span&gt;utf-8;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;client_max_body_size &lt;/span&gt;&lt;span&gt;100m&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt; /file/ {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;       &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;add_header &lt;/span&gt;&lt;span&gt;Cache-Control &lt;/span&gt;&lt;span&gt;&quot;public, max-age=31536000, immutable&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;       &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;try_files &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;uri&lt;/span&gt;&lt;span&gt; @imgbed;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt; @imgbed {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;       &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;proxy_pass &lt;/span&gt;&lt;span&gt;http://127.0.0.1:7658;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;       &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;proxy_set_header &lt;/span&gt;&lt;span&gt;Host $&lt;/span&gt;&lt;span&gt;host&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;       &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;proxy_set_header &lt;/span&gt;&lt;span&gt;X-Real-IP $&lt;/span&gt;&lt;span&gt;remote_addr&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;       &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;proxy_set_header &lt;/span&gt;&lt;span&gt;X-Forwarded-For $&lt;/span&gt;&lt;span&gt;proxy_add_x_forwarded_for&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;       &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;proxy_set_header &lt;/span&gt;&lt;span&gt;X-Forwarded-Proto $&lt;/span&gt;&lt;span&gt;scheme&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt; / {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;       &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;proxy_pass &lt;/span&gt;&lt;span&gt;http://127.0.0.1:7658;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;       &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;proxy_set_header &lt;/span&gt;&lt;span&gt;Host $&lt;/span&gt;&lt;span&gt;host&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;       &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;proxy_set_header &lt;/span&gt;&lt;span&gt;X-Real-IP $&lt;/span&gt;&lt;span&gt;remote_addr&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;       &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;proxy_set_header &lt;/span&gt;&lt;span&gt;X-Forwarded-For $&lt;/span&gt;&lt;span&gt;proxy_add_x_forwarded_for&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;       &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;proxy_set_header &lt;/span&gt;&lt;span&gt;X-Forwarded-Proto $&lt;/span&gt;&lt;span&gt;scheme&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;2.4 服务器侧：Docker 部署 CF-ImgBed 本体&lt;a href=&quot;#24-服务器侧docker-部署-cf-imgbed-本体&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;官方提供 Docker 部署&lt;/strong&gt;（文档 &lt;a href=&quot;https://cfbed.sanyue.de/&quot; target=&quot;_blank&quot;&gt;https://cfbed.sanyue.de/&lt;/a&gt; 有专门章节），无需改代码。国内服务器拉 Docker Hub 会超时，&lt;strong&gt;必须换镜像源前缀&lt;/strong&gt;，实测腾讯内网源 &lt;code&gt;mirror.ccs.tencentyun.com&lt;/code&gt; 对轻量服务器可用：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;/opt/imgbed/docker-compose.yml&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;services&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;imgbed&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;image&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;mirror.ccs.tencentyun.com/marseventh/cloudflare-imgbed:latest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;container_name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;imgbed&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;restart&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;unless-stopped&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;ports&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;&quot;127.0.0.1:7658:8080&quot;&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;# 注意：应用容器内监听 8080，不是 7658！&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;environment&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;BASIC_USER=&amp;lt;管理员用户名&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;BASIC_PASS=&amp;lt;管理员密码&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;volumes&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;/opt/imgbed/data:/app/data&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/opt/imgbed&lt;/span&gt;&lt;span&gt; &amp;amp;&amp;amp; &lt;/span&gt;&lt;span&gt;docker&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;compose&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;up&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-d&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;两个端口坑：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;容器日志会写 &lt;code&gt;Server running at http://0.0.0.0:8080&lt;/code&gt;——&lt;strong&gt;映射写 &lt;code&gt;宿主机端口:8080&lt;/code&gt;&lt;/strong&gt;，照抄 7658&amp;lt;7658&amp;gt; 会连不上；&lt;/li&gt;
&lt;li&gt;首次启动可能报 &lt;code&gt;duplicate column name: tags&lt;/code&gt; 迁移警告，无害。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;2.5 服务器侧：rclone WebDAV（应用的「本地磁盘」渠道）&lt;a href=&quot;#25-服务器侧rclone-webdav应用的本地磁盘渠道&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;图床程序的存储渠道选 WebDAV，指向宿主机上的 rclone：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apt-get&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-y&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rclone&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;systemd 单元（&lt;code&gt;/etc/systemd/system/rclone-webdav.service&lt;/code&gt;）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;[Service]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;User&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;ubuntu&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;ExecStart&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;/usr/bin/rclone serve webdav /var/www/imgbed/file --addr 172.17.0.1:8081 --user &amp;lt;dav用户名&amp;gt; --pass &amp;lt;dav密码&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;Restart&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;on-failure&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;systemctl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;enable&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--now&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rclone-webdav&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;本方案最大的坑在这里&lt;/strong&gt;：&lt;code&gt;--addr&lt;/code&gt; 绝不能写 &lt;code&gt;127.0.0.1:8081&lt;/code&gt;。Docker 容器里的 &lt;code&gt;127.0.0.1&lt;/code&gt; 是容器自己，访问宿主机要走 &lt;strong&gt;docker0 网桥地址 &lt;code&gt;172.17.0.1&lt;/code&gt;&lt;/strong&gt;。绑定网桥地址后容器可达、公网不可达（配合防火墙），安全与功能兼得。症状是上传接口 500、应用日志 &lt;code&gt;WebDAV upload error: fetch failed&lt;/code&gt;。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;2.6 后台配置&lt;a href=&quot;#26-后台配置&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;浏览器访问 &lt;code&gt;https://tu.mstzuomu.space/dashboard&lt;/code&gt; 登录（&lt;code&gt;BASIC_USER&lt;/code&gt;/&lt;code&gt;BASIC_PASS&lt;/code&gt;）：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;系统设置 → 上传设置 → 上传渠道管理&lt;/strong&gt;：添加渠道，类型 &lt;strong&gt;WebDAV&lt;/strong&gt;，基础地址 &lt;code&gt;http://172.17.0.1:8081&lt;/code&gt;，填 dav 账号密码；启用该渠道；&lt;/li&gt;
&lt;li&gt;顺手关掉默认开启的 &lt;strong&gt;&lt;code&gt;R2_env&lt;/code&gt;&lt;/strong&gt; 渠道开关（Docker 版会把上传塞进容器内 &lt;code&gt;/app/data/r2&lt;/code&gt;，Nginx 静态层看不见，表现为「上传成功但外网 0 字节」）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;安全设置 → 管理端认证&lt;/strong&gt;可改用户名/密码——注意改密码时除了新密码还有**「确认密码」框**，漏填会静默保存失败（接口表现：旧密码仍可登录）。改完记得同步 &lt;code&gt;docker-compose.yml&lt;/code&gt; 的 &lt;code&gt;BASIC_*&lt;/code&gt; 环境变量并 &lt;code&gt;docker compose up -d&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;2.7 HTTPS 与防火墙&lt;a href=&quot;#27-https-与防火墙&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apt-get&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-y&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;certbot&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;python3-certbot-nginx&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;certbot&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--nginx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tu.mstzuomu.space&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-n&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--redirect&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--agree-tos&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--register-unsafely-without-email&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;轻量应用服务器控制台的防火墙默认只开了 22/80/ICMP&lt;/strong&gt;，443 需要手动「添加规则」放行 TCP 443，否则证书和 Nginx 都就绪、外网 443 依然不通。certbot 会重排 Nginx 配置，用 IP 直连 &amp;lt;80&amp;gt; 可能从此 404，属正常现象，管理走域名即可。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;2.8 DNS 与验收&lt;a href=&quot;#28-dns-与验收&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;EdgeOne zone（NS 已在 EO）里给 &lt;code&gt;tu&lt;/code&gt; 加 &lt;strong&gt;A 记录 → 服务器公网 IP&lt;/strong&gt;。验收清单：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt; &lt;code&gt;https://tu.mstzuomu.space/&lt;/code&gt; 出上传页；&lt;/li&gt;
&lt;li&gt; 老图片原 URL（&lt;code&gt;/file/目录/文件名&lt;/code&gt;）200 且字节数与导出一致；&lt;/li&gt;
&lt;li&gt; &lt;code&gt;http://&lt;/code&gt; 自动 301 到 https；&lt;/li&gt;
&lt;li&gt; 后台拖图上传 → 返回 &lt;code&gt;/file/&lt;/code&gt; 链接 → 外网可访问 → 服务器磁盘 &lt;code&gt;/var/www/imgbed/file&lt;/code&gt; 能看到该文件；&lt;/li&gt;
&lt;li&gt; &lt;code&gt;/dashboard&lt;/code&gt; 用新账号登录成功。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;自托管后 Docker、rclone、Nginx 均已 &lt;code&gt;enable&lt;/code&gt; 开机自启，certbot 走 systemd timer 自动续签。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;三、顺带的其他变更记录&lt;a href=&quot;#三顺带的其他变更记录&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;3.1 备案号页脚&lt;a href=&quot;#31-备案号页脚&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;code&gt;src/config/FooterConfig.html&lt;/code&gt;（Firefly 主题专用页脚注入文件）挂 ICP 号。规则：&lt;strong&gt;广东备案挂主体号，非广东挂服务备案号&lt;/strong&gt;（本站为山东，挂 &lt;code&gt;-1&lt;/code&gt; 后缀的服务号），链接到 &lt;code&gt;https://beian.miit.gov.cn/&lt;/code&gt;。公安备案号下证后在同文件解注释补挂（去 &lt;code&gt;beian.mps.gov.cn&lt;/code&gt; 复制官方 HTML）。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;3.2 博客三入口统一到 azuma&lt;a href=&quot;#32-博客三入口统一到-azuma&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;code&gt;Layout.astro&lt;/code&gt; 头部有一段 &lt;code&gt;is:inline&lt;/code&gt; 跳转脚本，&lt;strong&gt;白名单只匹配 &lt;code&gt;www.mstzuomu.space&lt;/code&gt; 与根域 &lt;code&gt;mstzuomu.space&lt;/code&gt;&lt;/strong&gt;，302 到 azuma 并保留路径/查询串。两轮收紧的教训：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;第一版判断「非 azuma 就跳」——&lt;code&gt;localhost&lt;/code&gt; 预览也被跳走，改为线上域名匹配；&lt;/li&gt;
&lt;li&gt;第二版用正则匹配所有 &lt;code&gt;*.mstzuomu.space&lt;/code&gt;——&lt;strong&gt;误伤 &lt;code&gt;tu&lt;/code&gt;&lt;/strong&gt;（若某域名返回了含博客脚本的 HTML）。最终白名单化。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;另：&lt;code&gt;siteConfig.ts&lt;/code&gt; 的 &lt;code&gt;site_url&lt;/code&gt; 已同步为 &lt;code&gt;https://azuma.mstzuomu.space&lt;/code&gt;（canonical/sitemap 基址）。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;平台侧还埋过一颗雷&lt;/strong&gt;：曾按「所有域名统一 301」的思路在 EO zone 建过边缘函数规则（匹配全部 Host）。EO 的规则只对 &lt;strong&gt;zone 加速域名&lt;/strong&gt;生效、对 &lt;strong&gt;EO Pages 域名&lt;/strong&gt;（www/azuma/根域）不生效——所以博客没事，但后加的 zone 加速域名 &lt;code&gt;tu&lt;/code&gt; 被规则误 301 到博客。已删除该规则。复刻时若只要页面级跳转，别在 zone 上留「匹配全部」的边缘函数。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;3.3 fc / pub / twikoo 的复活&lt;a href=&quot;#33-fc--pub--twikoo-的复活&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;三个子域在 NS 切换时 DNS 记录丢失，均为 Vercel 项目，在 EO zone 各补一条 CNAME → &lt;code&gt;cname.vercel-dns.com&lt;/code&gt; 即复活：&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;子域&lt;/th&gt;&lt;th&gt;Vercel 项目&lt;/th&gt;&lt;th&gt;用途&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;fc.mstzuomu.space&lt;/code&gt;&lt;/td&gt;&lt;td&gt;friend-circle-lite&lt;/td&gt;&lt;td&gt;友圈数据站（CORS 头在项目 vercel.json）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;pub.mstzuomu.space&lt;/code&gt;&lt;/td&gt;&lt;td&gt;fireflypriweb-publisher&lt;/td&gt;&lt;td&gt;发布台&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;twikoo.mstzuomu.space&lt;/code&gt;&lt;/td&gt;&lt;td&gt;twikoo-api&lt;/td&gt;&lt;td&gt;Twikoo 评论后端&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;博客端评论开关在 &lt;code&gt;src/config/commentConfig.ts&lt;/code&gt;（&lt;code&gt;type: &quot;twikoo&quot;&lt;/code&gt;，&lt;code&gt;envId&lt;/code&gt; 指向自有域名）。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;3.4 服务器与备案的绑定关系&lt;a href=&quot;#34-服务器与备案的绑定关系&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;轻量服务器是 &lt;strong&gt;ICP 备案的接入资源&lt;/strong&gt;（备案单上的云资源 IP），也是本次图床的宿主机。&lt;strong&gt;不要释放/退订它&lt;/strong&gt;——备案接入悬空有被注销风险。网站本体跑在 EdgeOne Pages 上属于常见实践，但服务器必须保持在网。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;3.5 DNSPod 控制台的「未生效」提示&lt;a href=&quot;#35-dnspod-控制台的未生效提示&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;域名 NS 在 &lt;strong&gt;EdgeOne 的 qeodns&lt;/strong&gt; 上，不在 DNSPod。腾讯云「云解析 DNS（DNSPod）」控制台提示「未设置正确的 DNS 服务器」是跨产品的正常现象，无视即可，解析记录都在 EO 站点的「域名服务 → DNS 记录」里管理。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;四、写给复刻者的最短路径&lt;a href=&quot;#四写给复刻者的最短路径&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;旧站：后台开 WebDAV 或走管理 API，把图片按 fileId 目录树拉到本地，魔数校验，留备份；&lt;/li&gt;
&lt;li&gt;服务器：装 Nginx（&lt;code&gt;/file/&lt;/code&gt; 静态 + 其余反代 &amp;lt;7658&amp;gt;）→ Docker 拉镜像（记得国内源前缀、容器端口 8080）→ rclone WebDAV 绑 &lt;code&gt;172.17.0.1&lt;/code&gt; → 后台配 WebDAV 渠道、关 R2_env；&lt;/li&gt;
&lt;li&gt;certbot 签证书 + 控制台防火墙放行 443；&lt;/li&gt;
&lt;li&gt;DNS 加 A 记录，跑第 2.8 节验收清单；&lt;/li&gt;
&lt;li&gt;全绿之后，旧的 Cloudflare Worker 图床留观两周再退役。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;数据无价，迁移有路。祝各位同行一次跑通。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;</content:encoded></item><item><title>全站动效补完与 anime.js 数字滚动：五项动效计划的落地、四轮实测踩坑与复刻清单</title><link>https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-21/</link><guid isPermaLink="true">https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-21/</guid><description>完整记录 Firefly 博客一次「按门禁筛出来的动效改造」：五项动效计划（浮层开合/模态进出场/公告折叠/翻页桥接/切标签）的病灶定位、精确参数与四个实测才现形的 bug，以及用 anime.js 给侧栏统计做数字滚动的选型决策、v4 API 与零面积 IntersectionObserver 抢跑坑，附可照做的复刻清单</description><pubDate>Tue, 13 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h1&gt;全站动效补完与 anime.js 数字滚动：五项动效计划的落地、四轮实测踩坑与复刻清单&lt;a href=&quot;#全站动效补完与-animejs-数字滚动五项动效计划的落地四轮实测踩坑与复刻清单&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;2026-10-13，本博客完成了一轮「先审计、再动刀」的动效改造：&lt;strong&gt;18 个文件修改（+290/−70 行）+ 1 个新依赖（animejs 4.5.0）+ 6 份可执行计划文档&lt;/strong&gt;，功能零损失。&lt;/p&gt;&lt;p&gt;两条主线：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;五项动效计划&lt;/strong&gt;——按「频率/目的/速度/功能」四问门禁筛出的真缺口：浮层开合瞬移、详情模态无退场、公告关闭瞬隐、翻页内容硬切、代码组切标签硬切；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;anime.js 数字滚动&lt;/strong&gt;——侧栏站点统计从 0 滚到终值，会话内只播一次。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;本文写给两类读者：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;未来的维护者（包括新会话的 AI 助手）&lt;/strong&gt;：读完应知道每个病灶在哪一行、为什么这么修、哪些坑已经踩过（本文记录了 4 个只有实测才会现形的 bug）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;想复现的访客&lt;/strong&gt;：文末「复刻清单」从零列出全部步骤，关键代码在正文中可照抄。&lt;/li&gt;
&lt;/ul&gt;&lt;hr /&gt;&lt;section&gt;&lt;h2&gt;一、方法论：先审计，后动刀&lt;a href=&quot;#一方法论先审计后动刀&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;这轮改造没有「哪里爽加哪里」，流程是三步：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;审计出机会表&lt;/strong&gt;：按四问门禁逐个候选打分——
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;频率&lt;/strong&gt;：每天上百次的动作（键盘快捷键、核心导航）永远不加动画；偶尔动作（模态、折叠）可加标准动画；罕见动作（首次、庆祝）才轮到「花活」；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;目的&lt;/strong&gt;：必须能答出「反馈 / 空间一致性 / 状态指示 / 防止突变 / 解释 / 快乐」之一，「看着酷」不算；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;速度&lt;/strong&gt;：UI 单段动画 &amp;lt; 300ms；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;功能&lt;/strong&gt;：用户正在读的数据不为风格而动。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;写成自包含计划&lt;/strong&gt;：每份计划带改前代码摘录、精确到毫秒和 cubic-bezier 的目标值、边界与验证方法（存于仓库 &lt;code&gt;plans/&lt;/code&gt; 目录）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;执行 + 审查 + 实测&lt;/strong&gt;：执行者照计划改，审查者按「never-ship 清单」过一遍（禁 &lt;code&gt;transition: all&lt;/code&gt;、禁 &lt;code&gt;scale(0)&lt;/code&gt; 入场、禁 UI 用 &lt;code&gt;ease-in&lt;/code&gt;、reduced-motion 必须随动画交付……），最后浏览器实测采样曲线。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;本轮的审查阶段真的抓出了 4 个 bug——全部集中在 003 号计划，见下文第三节。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;统一使用的动效 token&lt;/strong&gt;（定义在 &lt;code&gt;src/styles/variables.styl&lt;/code&gt;，复刻时先建这几个）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;--ease-standard: cubic-bezier(0&lt;/span&gt;&lt;span&gt;.4&lt;/span&gt;&lt;span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;/span&gt;&lt;span&gt;.2&lt;/span&gt;&lt;span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;span&gt;);    &lt;/span&gt;&lt;span&gt;/* 颜色类过渡、默认曲线 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;--ease-decelerate: cubic-bezier(0&lt;/span&gt;&lt;span&gt;.25&lt;/span&gt;&lt;span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;/span&gt;&lt;span&gt;.46&lt;/span&gt;&lt;span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;/span&gt;&lt;span&gt;.45&lt;/span&gt;&lt;span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;/span&gt;&lt;span&gt;.94&lt;/span&gt;&lt;span&gt;); &lt;/span&gt;&lt;span&gt;/* 进入/落位 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;--duration-fast: 100ms;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;--duration-normal: 150ms;&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;/* 浮层、翻页、切标签都用它 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;--duration-medium: 200ms;&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;/* 模态入场、公告折叠 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;二、五项动效计划&lt;a href=&quot;#二五项动效计划&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;001 浮层开合瞬移（HIGH）&lt;a href=&quot;#001-浮层开合瞬移high&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;病灶&lt;/strong&gt;（改前行号）：&lt;code&gt;src/styles/main.css:487&lt;/code&gt; 的 &lt;code&gt;.float-panel&lt;/code&gt; 基类只有&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;@apply&lt;/span&gt;&lt;span&gt; ... transition-colors duration-150 ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;transition-colors&lt;/code&gt; 只覆盖颜色族，而关闭态 &lt;code&gt;.float-panel-closed&lt;/code&gt; 切的是 &lt;code&gt;opacity&lt;/code&gt; 和&lt;strong&gt;位移&lt;/strong&gt;——没有额外工具类的面板（搜索面板 &lt;code&gt;Search.svelte&lt;/code&gt;、导航快捷面板 &lt;code&gt;Navbar.astro&lt;/code&gt;）开合就是瞬移；另有 4 个面板靠 markup 里的 &lt;code&gt;transition-all&lt;/code&gt; 苟活（&lt;code&gt;transition: all&lt;/code&gt; 本身是禁项）。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;本项目最关键的冷知识&lt;/strong&gt;：Tailwind v4 把位移编译成了&lt;strong&gt;独立的 &lt;code&gt;translate&lt;/code&gt; 属性&lt;/strong&gt;，不是 &lt;code&gt;transform&lt;/code&gt;。构建产物实测：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/* .float-panel-closed 的编译结果 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.float-panel-closed&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pointer-events: &lt;/span&gt;&lt;span&gt;none&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--tw-translate-y&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--spacing&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&lt;span&gt;.25&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-1&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;translate: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--tw-translate-x&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--tw-translate-y&lt;/span&gt;&lt;span&gt;); &lt;/span&gt;&lt;span&gt;/* ← 独立属性！ */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;opacity: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;所以 transition 里必须写 &lt;code&gt;translate&lt;/code&gt;——写 &lt;code&gt;transform&lt;/code&gt; 修了个寂寞。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;修复&lt;/strong&gt;（&lt;code&gt;main.css&lt;/code&gt;，改后）：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.float-panel&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@apply top-21 rounded-(&lt;/span&gt;&lt;span&gt;--radius-large&lt;/span&gt;&lt;span&gt;) overflow-hidden bg-(&lt;/span&gt;&lt;span&gt;--card-bg&lt;/span&gt;&lt;span&gt;) shadow-xl border border-black/5 dark:border-white/&lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;transition:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;opacity &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--duration-normal&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--ease-decelerate&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;translate &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--duration-normal&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--ease-decelerate&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;color&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--duration-normal&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--ease-standard&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;background-color &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--duration-normal&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--ease-standard&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;border-color &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--duration-normal&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--ease-standard&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;@media&lt;/span&gt;&lt;span&gt; (prefers-reduced-motion: reduce) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;.float-panel&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;transition:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;opacity &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--duration-normal&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--ease-standard&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;color&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--duration-normal&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--ease-standard&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;background-color &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--duration-normal&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--ease-standard&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;border-color &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--duration-normal&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--ease-standard&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;同时从 4 个 markup 里删掉 &lt;code&gt;transition-all&lt;/code&gt; 这一个词（其余 class 不动），让全站浮层回归同一套机制：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Navbar.astro&lt;/code&gt;（music-nav-panel）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NavMenuPanel.astro&lt;/code&gt;（导航菜单卡片）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DisplaySettingsIntegrated.svelte&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LightDarkSwitch.svelte&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;运行时验证&lt;/strong&gt;：&lt;code&gt;getComputedStyle(#search-panel).transitionProperty&lt;/code&gt; 应为 &lt;code&gt;opacity, translate, color, background-color, border-color&lt;/code&gt;，时长 &lt;code&gt;0.15s&lt;/code&gt;；关闭态 &lt;code&gt;translate: 0px -4px&lt;/code&gt;。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;002 详情模态：补退场、背板补入场&lt;a href=&quot;#002-详情模态补退场背板补入场&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;病灶&lt;/strong&gt;：&lt;code&gt;AnimeDetailModal.svelte&lt;/code&gt; / &lt;code&gt;BilibiliDetailModal.svelte&lt;/code&gt;（同构双文件）——背板 &lt;code&gt;bg-black/60&lt;/code&gt; 随 &lt;code&gt;{#if anime}&lt;/code&gt; 瞬现瞬消；卡片入场是 CSS keyframes（内置 &lt;code&gt;ease-out&lt;/code&gt; 偏弱），关闭时 Svelte 直接摘 DOM，&lt;strong&gt;没有退场&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;修复&lt;/strong&gt;：用 Svelte transition 同时接管进、出（transition 可中断，keyframes 会从零重播），并删掉原 &lt;code&gt;@keyframes animate-in&lt;/code&gt;：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lang&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;ts&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;cubicOut&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;svelte/easing&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;fade&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;fly&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;svelte/transition&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;!-- 背板 --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;use&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;portal&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;fade&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;duration&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;200&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;easing&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cubicOut&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;out&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;fade&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;duration&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;150&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;easing&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cubicOut&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;!-- 卡片：删掉 class 里的 animate-in，加 --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;... scale-90 sm:scale-100&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;fly&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;y&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prefersReducedMotion&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;duration&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;200&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;easing&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cubicOut&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;out&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;fly&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;y&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prefersReducedMotion&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;duration&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;150&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;easing&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cubicOut&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;入 200ms / 出 150ms&lt;/strong&gt;：退场快于入场的非对称节奏，均在模态 200–500ms 档与 UI &amp;lt;300ms 红线内；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cubicOut&lt;/code&gt; 是 &lt;code&gt;svelte/easing&lt;/code&gt; 的具名强 ease-out（JS 消费不了 CSS 的 cubic-bezier 字符串，用框架自带函数而不是手搓近似值）；&lt;/li&gt;
&lt;li&gt;卡片上的 &lt;code&gt;scale-90&lt;/code&gt; 在 Tailwind v4 编译为独立 &lt;code&gt;scale&lt;/code&gt; 属性（实测产物 &lt;code&gt;.scale-90{...scale:var(--tw-scale-x) var(--tw-scale-y)}&lt;/code&gt;），与 fly 写的 &lt;code&gt;transform: translateY()&lt;/code&gt; 互不打架；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;prefersReducedMotion()&lt;/code&gt; 把位移归零、保留淡出。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;验证&lt;/strong&gt;：Esc 关闭后 &lt;code&gt;[role=&quot;dialog&quot;]&lt;/code&gt; 应滞留在 DOM 里跑完 150ms 退场才被移除（旧实现是瞬间蒸发）；打开后 200ms 内立刻 Esc，退场应从中途状态接管而非闪回起点。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;003 公告栏关闭——四个只有实测才现形的 bug&lt;a href=&quot;#003-公告栏关闭四个只有实测才现形的-bug&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;病灶&lt;/strong&gt;：&lt;code&gt;Announcement.astro&lt;/code&gt; 的关闭按钮原本是一行 &lt;code&gt;onclick=&quot;...style.display=&apos;none&apos;&quot;&lt;/code&gt;，一击瞬隐、侧栏下方组件全体跳位。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;布局前提&lt;/strong&gt;（动手前必须确认）：&lt;code&gt;widget-layout&lt;/code&gt; 是 &lt;code&gt;SideBar.astro&lt;/code&gt; 里 &lt;code&gt;flex flex-col gap-4&lt;/code&gt; 容器的直接子项——flex 子项会被 blockify，&lt;code&gt;max-height&lt;/code&gt; 才可用；&lt;code&gt;gap-4 = 1rem&lt;/code&gt;。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;最终方案&lt;/strong&gt;（三层）：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;&lt;code&gt;max-height: 实测高度 → 0&lt;/code&gt; 做折叠（高度动画在「手风琴/折叠」场景是被豁免的）；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;margin-bottom: 0 → -1rem&lt;/code&gt; &lt;strong&gt;吞掉父容器一个 gap&lt;/strong&gt;——否则元素收成 0 高后两侧 gap 还在，动画结束 &lt;code&gt;display:none&lt;/code&gt; 那一帧会再跳 16px；&lt;/li&gt;
&lt;li&gt;淡出不能做在根元素上（原因见坑④），做在&lt;strong&gt;子元素&lt;/strong&gt;上。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;最终 onclick 全文&lt;/strong&gt;（可直接照抄）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;onclick=&quot;var w=this.closest(&apos;widget-layout&apos;);if(!w||w.dataset.closing)return;if(window.matchMedia(&apos;(prefers-reduced-motion: reduce)&apos;).matches){w.style.display=&apos;none&apos;;return}w.dataset.closing=&apos;1&apos;;w.style.maxHeight=w.offsetHeight+&apos;px&apos;;w.style.marginBottom=&apos;0&apos;;void w.offsetHeight;requestAnimationFrame(function(){w.style.transition=&apos;max-height 200ms var(--ease-standard),margin-bottom 200ms var(--ease-standard)&apos;;w.style.maxHeight=&apos;0&apos;;w.style.marginBottom=&apos;-1rem&apos;;for(var i=0;i&amp;lt;w.children.length;i++){var c=w.children[i];c.style.transition=&apos;opacity 200ms var(--ease-standard)&apos;;c.style.opacity=&apos;0&apos;}});setTimeout(function(){w.style.display=&apos;none&apos;},240)&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;踩坑全录&lt;/strong&gt;（按发现顺序，每条都是实测抓的）：&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;#&lt;/th&gt;&lt;th&gt;症状&lt;/th&gt;&lt;th&gt;根因&lt;/th&gt;&lt;th&gt;修复&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;①&lt;/td&gt;&lt;td&gt;点了完全没反应、无报错&lt;/td&gt;&lt;td&gt;初版写成 &lt;code&gt;(function(){...})&lt;/code&gt;——&lt;strong&gt;函数表达式根本没人调用&lt;/strong&gt;，整个 handler 是空操作&lt;/td&gt;&lt;td&gt;去掉包装&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;②&lt;/td&gt;&lt;td&gt;换成 &lt;code&gt;(function(){...})()&lt;/code&gt; 后仍没反应&lt;/td&gt;&lt;td&gt;立即调用的普通函数内部 &lt;code&gt;this&lt;/code&gt; 不是按钮（是 globalThis），&lt;code&gt;this.closest&lt;/code&gt; 抛 TypeError&lt;/td&gt;&lt;td&gt;&lt;strong&gt;不包 IIFE&lt;/strong&gt;，语句直接写在 inline handler 顶层（handler 本身就是函数体，&lt;code&gt;this&lt;/code&gt; = 按钮，&lt;code&gt;return&lt;/code&gt;/&lt;code&gt;var&lt;/code&gt; 合法）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;③&lt;/td&gt;&lt;td&gt;高度瞬移、不折叠&lt;/td&gt;&lt;td&gt;起始值 &lt;code&gt;max-height=Hpx&lt;/code&gt; 与 rAF 里的终值被合并进同一次样式计算，浏览器看到的是 &lt;code&gt;none → 0&lt;/code&gt;，不可插值&lt;/td&gt;&lt;td&gt;起始值写入后 &lt;code&gt;void w.offsetHeight;&lt;/code&gt; &lt;strong&gt;强制回流&lt;/strong&gt;，把起始值钉进上一次计算&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;④&lt;/td&gt;&lt;td&gt;淡出全程卡在 &lt;code&gt;opacity:1&lt;/code&gt;，只在 &lt;code&gt;display:none&lt;/code&gt; 后归零&lt;/td&gt;&lt;td&gt;&lt;code&gt;widget-layout&lt;/code&gt; 带 &lt;code&gt;onload-animation&lt;/code&gt;（&lt;code&gt;fade-in-up ... forwards&lt;/code&gt;）。&lt;strong&gt;已完成 CSS 动画的 fill 值优先级高于 inline style&lt;/strong&gt;；更麻烦的是，&lt;strong&gt;Chrome 中由移除动画引起的值变化不触发 transition&lt;/strong&gt;——在根元素上掐 fill 淡出这条路走不通（先被压死为 1，掐掉后又瞬跳为 0）&lt;/td&gt;&lt;td&gt;根元素只做 max-height + margin 折叠（keyframes 不碰这两个属性），&lt;strong&gt;opacity 淡出施加在 &lt;code&gt;w.children&lt;/code&gt; 子元素&lt;/strong&gt;上（标题与内容块，不受父级 fill 影响）&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&lt;strong&gt;实测曲线&lt;/strong&gt;（每 35ms 采样一次 &lt;code&gt;getComputedStyle&lt;/code&gt;）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;t=39ms   childOp=0.97  maxH=239px  mb=-0.4px&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;t=118ms  childOp=0.25  maxH=62px   mb=-12px&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;t=232ms  childOp=0     maxH=0      mb=-16px&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;t=273ms  display:none&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;三条属性同步走完 200ms，结束瞬间零跳位。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;004 六处翻页桥接&lt;a href=&quot;#004-六处翻页桥接&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;病灶&lt;/strong&gt;：画廊/动态翻页时 &lt;code&gt;currentPage&lt;/code&gt; 一改，&lt;code&gt;{#each}&lt;/code&gt; 立即换一批卡片，整块内容瞬移替换。翻页是偶尔操作，值得一段标准动画；&lt;strong&gt;只动 opacity，不碰布局&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;统一模式&lt;/strong&gt;（淡出 → 换页 → 淡入，transition 而非 keyframes，快速连点可重定向）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;let gridFading = $state(false);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;async function goToPage(page: number) &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;page&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;currentPage&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;// 原有 bounds 检查的文件照旧保留&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;gridFading&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Promise&lt;/span&gt;&lt;span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setTimeout&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;150&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;currentPage&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;page&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Promise&lt;/span&gt;&lt;span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;requestAnimationFrame&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;)));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;gridFading&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;容器上绑（写在容器 div 上，inline style）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;style=&quot;opacity: &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;&lt;span&gt;gridFading&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;; transition: opacity var(--duration-normal) var(--ease-decelerate)&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;六个文件&lt;/strong&gt;：&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;文件&lt;/th&gt;&lt;th&gt;备注&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;pages/anime/AnimeGrid.svelte&lt;/code&gt;&lt;/td&gt;&lt;td&gt;原 goToPage 无 bounds 检查，加了 &lt;code&gt;page === currentPage&lt;/code&gt; 短路&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;pages/bilibili/BilibiliGrid.svelte&lt;/code&gt;&lt;/td&gt;&lt;td&gt;同上&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;pages/vndb/VndbSection.svelte&lt;/code&gt;&lt;/td&gt;&lt;td&gt;保留原 bounds 检查&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;pages/mal/MalSection.svelte&lt;/code&gt;&lt;/td&gt;&lt;td&gt;同上&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;pages/bangumi/BangumiSection.svelte&lt;/code&gt;&lt;/td&gt;&lt;td&gt;同上&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;pages/dynamic/DynamicFeed.svelte&lt;/code&gt;&lt;/td&gt;&lt;td&gt;命令式渲染：换页后 &lt;code&gt;await setTimeout(0)&lt;/code&gt; 让 &lt;code&gt;$effect → renderItems&lt;/code&gt; 跑完一帧再淡入&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&lt;strong&gt;不做&lt;/strong&gt;：搜索打字、筛选 chip、排序下拉——键盘驱动的高频路径，加动画只会钝化（门禁第一问直接否决）。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;实测曲线&lt;/strong&gt;：&lt;code&gt;1 → 0.477（60ms 淡出中）→ 换页 → 0.707（淡入中）→ 1&lt;/code&gt;。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;005 代码组切标签桥接&lt;a href=&quot;#005-代码组切标签桥接&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;病灶&lt;/strong&gt;：&lt;code&gt;TabGroup.svelte&lt;/code&gt; 切标签是 &lt;code&gt;block.style.display&lt;/code&gt; 硬换，代码块瞬移。代码是用户在读的数据——&lt;strong&gt;只做透明度、不做位移&lt;/strong&gt;。&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tabFading&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$state&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;selectTab&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;active&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tabFading&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;// 守卫：连点不叠动画&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tabFading&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Promise&lt;/span&gt;&lt;span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setTimeout&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;150&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;active&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// $effect 随即换 display（此刻容器不可见，硬切被藏住）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Promise&lt;/span&gt;&lt;span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;requestAnimationFrame&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;)));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tabFading&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;按钮 &lt;code&gt;onclick&lt;/code&gt; 改走 &lt;code&gt;selectTab(i)&lt;/code&gt;；容器绑 &lt;code&gt;style=&quot;opacity: {tabFading ? 0 : 1}&quot;&lt;/code&gt;；scoped style 加：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.code-group-blocks&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;transition: opacity &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--duration-normal&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--ease-decelerate&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;标签下划线/颜色的既有过渡保持不动——&lt;strong&gt;指示器即时响应，内容随后桥接&lt;/strong&gt;，两者分工不冲突。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;三、anime.js 数字滚动&lt;a href=&quot;#三animejs-数字滚动&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;选型：为什么整站只用它一处&lt;a href=&quot;#选型为什么整站只用它一处&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;anime.js 的招牌是 &lt;strong&gt;stagger 级联、时间线编排、数字滚动&lt;/strong&gt;——前两样站内已有 CSS 方案（onload 错峰、Swup 过渡），硬上就是「为个淡入淡出装库」。真正值得的缺口是侧栏 &lt;code&gt;SiteStats.astro&lt;/code&gt; 的六个死数字，于是按「最小试水」原则只做这一处。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;安装&lt;a href=&quot;#安装&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;pnpm add animejs &lt;/span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt;registry&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;https:&lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;/span&gt;&lt;span&gt;registry.npmmirror.com&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 装到的是 animejs 4.5.0（npm 主线就是 v4，没有所谓 v6）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;v4 API 速查（从 &lt;code&gt;node_modules/animejs/dist/modules/types/index.d.ts&lt;/code&gt; 核实，别凭记忆写）&lt;a href=&quot;#v4-api-速查从-node_modulesanimejsdistmodulestypesindexdts-核实别凭记忆写&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;animate&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;animejs&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;counter&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;v&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; };&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;animate&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;counter&lt;/span&gt;&lt;span&gt;, {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;v&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;span&gt;,                 &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 普通 JS 对象补间：参数键名 = 对象属性名&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;duration&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1000&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;delay&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;80&lt;/span&gt;&lt;span&gt;,      &lt;/span&gt;&lt;span&gt;// 每行错峰&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;ease&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;outExpo&quot;&lt;/span&gt;&lt;span&gt;,        &lt;/span&gt;&lt;span&gt;// 合法名见 EaseStringParamNames：outQuad/outCubic/outExpo/outBack...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;onUpdate&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;el&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;textContent&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Math&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;round&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;counter&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;v&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;toLocaleString&lt;/span&gt;&lt;span&gt;(); },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;onComplete&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;el&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;textContent&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;toLocaleString&lt;/span&gt;&lt;span&gt;(); },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;要点：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AnimationParams&lt;/code&gt; 是 &lt;code&gt;Record&amp;lt;string, ...&amp;gt;&lt;/code&gt;——属性 tween 与 &lt;code&gt;duration&lt;/code&gt;/&lt;code&gt;delay&lt;/code&gt;/&lt;code&gt;ease&lt;/code&gt;/&lt;code&gt;onUpdate&lt;/code&gt;/&lt;code&gt;onComplete&lt;/code&gt; 回调可以混在同一个对象里；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ease&lt;/code&gt; 取值必须是导出的具名字符串或函数，&lt;code&gt;&quot;outExpo&quot;&lt;/code&gt; 在 &lt;code&gt;EaseStringParamNames&lt;/code&gt; 联合类型里；&lt;/li&gt;
&lt;li&gt;目标是普通对象时走 JS 补间（&lt;code&gt;JSTargetsParam&lt;/code&gt;），不需要 DOM。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;SiteStats 实现（三个关键设计）&lt;a href=&quot;#sitestats-实现三个关键设计&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;锚点&lt;/strong&gt;：给统计值 span 统一加 &lt;code&gt;data-stat-value&lt;/code&gt;（不蒜子两行是异步来的，不加、不碰）。取值时读 &lt;code&gt;textContent&lt;/code&gt; 当终值——静态值 SSR 已是终值，动态值（运行天数/最后更新）由上方 classic script 先写好（classic script 解析期执行，模块脚本 defer 执行，顺序天然正确）。&lt;code&gt;&quot;今天&quot;&lt;/code&gt; 这类非数字用 &lt;code&gt;/^\d+$/&lt;/code&gt; 过滤跳过。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;触发&lt;/strong&gt;：&lt;code&gt;IntersectionObserver&lt;/code&gt;（threshold 0.3）滚入视口才播，播完 &lt;code&gt;disconnect&lt;/code&gt;；非数字、&lt;code&gt;end &amp;lt;= 0&lt;/code&gt; 不播。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;频率守卫&lt;/strong&gt;：&lt;code&gt;sessionStorage&lt;/code&gt; 键 &lt;code&gt;ff-site-stats-counted&lt;/code&gt;——&lt;strong&gt;真正播过才置位&lt;/strong&gt;（这样播之前切页还能换页重试）；swup 容器含 &lt;code&gt;#left-sidebar-dynamic&lt;/code&gt;/&lt;code&gt;#right-sidebar-dynamic&lt;/code&gt;，切页会重渲染本组件，没有守卫的话每次切页数字都滚一遍就是骚扰。切页重挂靠 &lt;code&gt;astro:page-load&lt;/code&gt;（&lt;strong&gt;不要&lt;/strong&gt;用 &lt;code&gt;swup:contentReplaced&lt;/code&gt;——swup 4.x 根本不派发这个旧事件）。&lt;code&gt;prefers-reduced-motion&lt;/code&gt; 直接置守卫跳过。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;两个只有实测才现形的坑&lt;a href=&quot;#两个只有实测才现形的坑&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;坑①：&lt;code&gt;#site-stats&lt;/code&gt; 在 DOM 里有两份&lt;/strong&gt;（桌面侧栏 + 移动侧栏各一），其中一份在当前断点下是 &lt;strong&gt;0×0 的隐藏副本&lt;/strong&gt;。&lt;code&gt;document.getElementById&lt;/code&gt; 永远返回树序第一份——观察了个寂寞。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;坑②（阴险）：零面积元素会被 IntersectionObserver 特判为 &lt;code&gt;isIntersecting&lt;/code&gt;&lt;/strong&gt;。那个 0×0 的隐藏副本躺在 (0,0)，一加载就被判「相交」，抢先把 &lt;code&gt;sessionStorage&lt;/code&gt; 守卫置了位——可见副本的回调一看守卫已设，直接自我了断。表现为：守卫显示已播、数值却全程不动。&lt;/p&gt;&lt;p&gt;双保险修复：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 装观察器前：滤掉零宽根&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;roots&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Array&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;document&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;querySelectorAll&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;#site-stats&quot;&lt;/span&gt;&lt;span&gt;)).&lt;/span&gt;&lt;span&gt;filter&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;root&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;root&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;dataset&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;countUpReady&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;1&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;root&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;getBoundingClientRect&lt;/span&gt;&lt;span&gt;().&lt;/span&gt;&lt;span&gt;width&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 回调里：再验一次目标宽度&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;entries&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;find&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;entry&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;entry&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;isIntersecting&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;entry&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;boundingClientRect&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;width&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;&lt;span&gt;hit&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;给测试者的教训&lt;/strong&gt;：排查过程中用 &lt;code&gt;page.addInitScript&lt;/code&gt; 清守卫是&lt;strong&gt;永久挂在该 Page 上的&lt;/strong&gt;——之后每次导航都会先清 key，把「二载不重播」测了个假阳性。换干净标签页重测才拿到真结论。测试钩子本身也会污染被测行为，切记。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;双载实测结果（干净标签页）&lt;a href=&quot;#双载实测结果干净标签页&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;首载 t=0ms   : [&quot;42&quot;,&quot;4&quot;,&quot;65&quot;,&quot;67,866&quot;,&quot;63&quot;,&quot;1&quot;,  &quot;26&quot;,&quot;1&quot;,&quot;0&quot;,&quot;0&quot;,&quot;0&quot;,&quot;0&quot;]  ← 后半在滚&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;首载收敛      : 两份副本均为 [&quot;42&quot;,&quot;4&quot;,&quot;65&quot;,&quot;67,866&quot;,&quot;63&quot;,&quot;1&quot;]，守卫=&quot;1&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;二载 800ms 全程: 无归零、无变化（stayFinal=true），守卫=&quot;1&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;（前 6 个是隐藏副本的 SSR 终值、后 6 个是可见副本的计数——隐藏副本全程未被触碰，坑①②修复生效。）&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;四、验证方法&lt;a href=&quot;#四验证方法&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;静态三件套&lt;/strong&gt;（本仓库约定）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;npx biome check &amp;lt;改动文件&amp;gt;        &lt;/span&gt;&lt;span&gt;# 只读检查，禁止 --write（pnpm lint 会波及十几个无关文件）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;pnpm type&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;check                   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;# tsc --noEmit，退出码 0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# pnpm check 是 astro check，存量基线 17 errors，验证时以「错误数是否仍为 17」为准&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;动态采样法&lt;/strong&gt;：动画的对错不能靠肉眼定案，用 Playwright &lt;code&gt;page.evaluate&lt;/code&gt; 对着 &lt;code&gt;getComputedStyle&lt;/code&gt; 按 35–150ms 间隔采样，把曲线打出来对照预期。本文第二节的三条曲线全部来自此法。几个采样技巧：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;页面动画可能在你开始采样前就跑完了——用 &lt;code&gt;page.addInitScript&lt;/code&gt; 清状态 + &lt;code&gt;goto&lt;/code&gt; 后&lt;strong&gt;立即&lt;/strong&gt;采样，或先 &lt;code&gt;scrollIntoView&lt;/code&gt; 再采；&lt;/li&gt;
&lt;li&gt;测「二载不重播」这类守卫逻辑，&lt;strong&gt;必须换干净标签页&lt;/strong&gt;（sessionStorage 按标签隔离，&lt;code&gt;addInitScript&lt;/code&gt; 会永久挂在原 Page 上污染后续测试）；&lt;/li&gt;
&lt;li&gt;console 里本机的图床 DNS 失败（&lt;code&gt;ERR_NAME_NOT_RESOLVED&lt;/code&gt;）与不蒜子/天气接口 429 是环境噪音，与改动无关。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;手感复核&lt;/strong&gt;：DevTools → Animations 面板慢放逐帧；Rendering 面板勾 &lt;code&gt;prefers-reduced-motion&lt;/code&gt; 确认「位移归零、淡入保留」。代码只能证明曲线对，手感要活人隔天再看一眼。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;五、复刻清单（从零照做）&lt;a href=&quot;#五复刻清单从零照做&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;建 token&lt;/strong&gt;（已有则跳过）：在全局样式加 &lt;code&gt;--ease-standard&lt;/code&gt;、&lt;code&gt;--ease-decelerate&lt;/code&gt;、&lt;code&gt;--duration-fast/normal/medium/slow&lt;/code&gt;，值见本文第一节；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;001 浮层&lt;/strong&gt;：改 &lt;code&gt;.float-panel&lt;/code&gt; 基类为显式 transition（本文有全文）+ reduce-motion 媒体查询；从 4 个 markup 删 &lt;code&gt;transition-all&lt;/code&gt;；验证 &lt;code&gt;getComputedStyle(#search-panel).transitionProperty&lt;/code&gt; 含 &lt;code&gt;opacity, translate&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;002 模态&lt;/strong&gt;：两个 DetailModal 改 Svelte &lt;code&gt;in:/out:&lt;/code&gt; fade/fly（入 200 / 出 150，&lt;code&gt;cubicOut&lt;/code&gt;），删 keyframes，加 &lt;code&gt;prefersReducedMotion()&lt;/code&gt; 分支；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;003 公告&lt;/strong&gt;：照抄本文的最终 onclick；确认侧栏容器是 &lt;code&gt;flex ... gap-*&lt;/code&gt; 且 widget 为直接子项；&lt;code&gt;-1rem&lt;/code&gt; 要与实际 gap 值一致；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;004 翻页&lt;/strong&gt;：六文件套用 &lt;code&gt;gridFading&lt;/code&gt; 模式（本文有骨架）；键盘/筛选路径不加；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;005 切标签&lt;/strong&gt;：&lt;code&gt;selectTab&lt;/code&gt; 守卫 + 容器 opacity 绑定 + scoped transition；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;anime.js&lt;/strong&gt;：&lt;code&gt;pnpm add animejs --registry=https://registry.npmmirror.com&lt;/code&gt;；SiteStats 加 &lt;code&gt;data-stat-value&lt;/code&gt; 锚点；照抄三个关键设计（锚点/观察器/守卫）与双保险过滤；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;验证&lt;/strong&gt;：跑静态三件套 → Playwright 采样 → reduce-motion 档复核；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;文档化&lt;/strong&gt;：把计划与实测记录存档（本仓库是 &lt;code&gt;plans/&lt;/code&gt; 目录），未来的维护者才不用重踩这 4 个 bug。&lt;/li&gt;
&lt;/ol&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;六、交付物与改动总表&lt;a href=&quot;#六交付物与改动总表&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;交付物&lt;/strong&gt;：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;plans/&lt;/code&gt;——6 份文件：&lt;code&gt;README.md&lt;/code&gt;（执行顺序与状态表）+ &lt;code&gt;001&lt;/code&gt;–&lt;code&gt;005&lt;/code&gt; 五份自包含计划，每份含改前代码摘录、精确目标值、边界与验证方法，Status 均为 DONE，003 号附三轮审查修正记录；&lt;/li&gt;
&lt;li&gt;本文——&lt;code&gt;src/content/posts/txt/21文章.md&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;改动总表&lt;/strong&gt;（&lt;code&gt;18 files changed, 290 insertions(+), 70 deletions(-)&lt;/code&gt;，另加依赖与文章）：&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;类别&lt;/th&gt;&lt;th&gt;文件&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;001 浮层&lt;/td&gt;&lt;td&gt;&lt;code&gt;src/styles/main.css&lt;/code&gt;、&lt;code&gt;Navbar.astro&lt;/code&gt;、&lt;code&gt;NavMenuPanel.astro&lt;/code&gt;、&lt;code&gt;DisplaySettingsIntegrated.svelte&lt;/code&gt;、&lt;code&gt;LightDarkSwitch.svelte&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;002 模态&lt;/td&gt;&lt;td&gt;&lt;code&gt;AnimeDetailModal.svelte&lt;/code&gt;、&lt;code&gt;BilibiliDetailModal.svelte&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;003 公告&lt;/td&gt;&lt;td&gt;&lt;code&gt;Announcement.astro&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;004 翻页&lt;/td&gt;&lt;td&gt;&lt;code&gt;AnimeGrid.svelte&lt;/code&gt;、&lt;code&gt;BilibiliGrid.svelte&lt;/code&gt;、&lt;code&gt;VndbSection.svelte&lt;/code&gt;、&lt;code&gt;MalSection.svelte&lt;/code&gt;、&lt;code&gt;BangumiSection.svelte&lt;/code&gt;、&lt;code&gt;DynamicFeed.svelte&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;005 标签&lt;/td&gt;&lt;td&gt;&lt;code&gt;TabGroup.svelte&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;anime.js&lt;/td&gt;&lt;td&gt;&lt;code&gt;package.json&lt;/code&gt;、&lt;code&gt;pnpm-lock.yaml&lt;/code&gt;、&lt;code&gt;SiteStats.astro&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;blockquote&gt;&lt;p&gt;改前引用的行号（如 &lt;code&gt;main.css:487&lt;/code&gt;）以改造前的 commit 为准，改造后会漂移——按「文件 + 代码特征」定位比行号可靠。&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;&lt;/section&gt;</content:encoded></item><item><title>全站视觉移植复刻：把 Firefly 改造成 animal-island-ui 风格的七期工程（含复刻清单与踩坑全录）</title><link>https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-20/</link><guid isPermaLink="true">https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-20/</guid><description>完整记录如何把一个 Astro + Svelte 博客移植成 animal-island-ui 的治愈系海岛风格：决策依据（为什么不引库、不重写）、规范获取方式、七期改造的逐文件实录与精确色值、Tailwind 层序覆盖等关键机制、Nunito 字体接入、验证三件套，附他人可照做的复刻清单与未尽事项</description><pubDate>Mon, 12 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h1&gt;全站视觉移植复刻：把 Firefly 改造成 animal-island-ui 风格的七期工程（含复刻清单与踩坑全录）&lt;a href=&quot;#全站视觉移植复刻把-firefly-改造成-animal-island-ui-风格的七期工程含复刻清单与踩坑全录&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;2026-10-11 至 10-12，本博客完成了一次全站视觉移植：从原有主题换成 &lt;a href=&quot;https://github.com/guokaigdg/animal-island-ui&quot; target=&quot;_blank&quot;&gt;animal-island-ui&lt;/a&gt;（治愈系海岛风格 React 组件库）的设计语言。&lt;strong&gt;22 个文件修改（+275/−73 行）+ 6 个字体文件 + 33 份参考规范&lt;/strong&gt;，功能零损失。&lt;/p&gt;&lt;p&gt;本文写给两类读者：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;未来的维护者（包括新会话的 AI 助手）&lt;/strong&gt;：读完应知道每一期改了哪些文件、为什么、精确色值在哪、验证基线是多少、哪些坑已经踩过；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;想复现的访客&lt;/strong&gt;：文末「复刻清单」从零列出全部步骤，所有关键代码在正文中可照抄。&lt;/li&gt;
&lt;/ul&gt;&lt;hr /&gt;&lt;section&gt;&lt;h2&gt;〇、决策：为什么不引库、也不从零重写&lt;a href=&quot;#〇决策为什么不引库也不从零重写&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;起因是想把博客「完全变成 animal-island-ui 的风格」。三条路线对比后选了&lt;strong&gt;风格移植&lt;/strong&gt;：&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;路线&lt;/th&gt;&lt;th&gt;结论&lt;/th&gt;&lt;th&gt;原因&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;直接安装它的 React npm 包&lt;/td&gt;&lt;td&gt;❌&lt;/td&gt;&lt;td&gt;本博客是 Astro 7 + Svelte 5 岛屿，混入 React 运行时得不偿失；且库里没有文章卡、目录、归档等博客部件&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;移植它的设计规范到现有站点&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;✅&lt;/td&gt;&lt;td&gt;风格 = CSS + design token，可整体搬运；内容、友圈、发布台、评论、搜索、性能优化成果全部保留&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;按它的风格从零重写博客&lt;/td&gt;&lt;td&gt;❌&lt;/td&gt;&lt;td&gt;功能与内容全部归零，按月计的工程量&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;关键依据：&lt;strong&gt;项目作者自己为「不装库只抄风格」留了官方通道&lt;/strong&gt;——&lt;code&gt;docs/design-system/css-variables.md&lt;/code&gt; 第一句就是 “When re-implementing the style without depending on the component library, declare the following variables on &lt;code&gt;:root&lt;/code&gt;”。README 里那条 &lt;code&gt;import &apos;animal-island-ui/style&apos;&lt;/code&gt; 规则是&lt;strong&gt;库使用者&lt;/strong&gt;的须知，不装库时不仅不适用（没有它的组件需要被样式化），而且不该引（会把整套 reset 和组件类灌进 Tailwind 体系打架）。&lt;/p&gt;&lt;p&gt;暗色模式：用户明确&lt;strong&gt;不做&lt;/strong&gt;，所有改造只管亮色（涉及暗色的规则都用 &lt;code&gt;:root:not(.dark)&lt;/code&gt; 或原样保留）。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;一、准备工作：把规范拉到本地&lt;a href=&quot;#一准备工作把规范拉到本地&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;风格复刻的「唯一真相源」是仓库里的两处：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;技能包&lt;/strong&gt; &lt;code&gt;skills/animal-island-ui-style/&lt;/code&gt;（SKILL.md 中英双语 + 13 份组件 props 参考）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;设计系统&lt;/strong&gt; &lt;code&gt;docs/design-system/&lt;/code&gt;（design-tokens.md 精确值、design-rules.md 七律与 16 条反模式、components/ 逐组件像素级 CSS）&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;本机 &lt;code&gt;raw.githubusercontent.com&lt;/code&gt; 直连不稳，&lt;strong&gt;用 api.github.com 的 contents 接口拿 base64 再解码&lt;/strong&gt;最稳，一个 Node 脚本批量拉：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 拉取单个文件：GET https://api.github.com/repos/&amp;lt;owner&amp;gt;/&amp;lt;repo&amp;gt;/contents/&amp;lt;path&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 返回 JSON 的 content 字段即 base64，解码写盘即可&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;res&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetch&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;, { &lt;/span&gt;&lt;/span&gt;&lt;span&gt;headers&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;Accept&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;application/vnd.github+json&quot;&lt;/span&gt;&lt;span&gt; } });&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;json&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;res&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;json&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;writeFile&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;dest&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Buffer&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;json&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;base64&quot;&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;toString&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;utf8&quot;&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;共下载 33 个文件，落在 &lt;strong&gt;&lt;code&gt;docs/animal-island-ui-style/&lt;/code&gt;&lt;/strong&gt;（含 &lt;code&gt;design-system/&lt;/code&gt; 子目录）。本站后续所有色值都以这份本地规范为准，动样式前先查它。&lt;/p&gt;&lt;p&gt;技能包里 14 条「硬规则」中真正管视觉的核心几条：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;不用纯黑文字（&lt;code&gt;#000&lt;/code&gt;/&lt;code&gt;#111&lt;/code&gt;），不用冷灰背景（&lt;code&gt;#fafafa&lt;/code&gt;/&lt;code&gt;#f5f5f5&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;交互元素圆角 ≥ 12px，按钮和输入框一律 &lt;strong&gt;50px pill&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;3D 堆叠阴影（&lt;code&gt;0 5px 0 #bdaea0&lt;/code&gt;）只属于 primary 按钮&lt;/strong&gt;，default 类只用柔和抬升影&lt;/li&gt;
&lt;li&gt;字体圆体（Nunito + Noto Sans SC），字重 body 500、标题 600–900，永不低于 400&lt;/li&gt;
&lt;li&gt;动效统一 &lt;code&gt;cubic-bezier(0.4, 0, 0.2, 1)&lt;/code&gt;，时长 0.15–0.35s&lt;/li&gt;
&lt;li&gt;焦点环：输入类黄色 &lt;code&gt;#ffcc00&lt;/code&gt;、按钮薄荷 &lt;code&gt;#19c8b9&lt;/code&gt;，绝不用冷蓝&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;核心 token（完整值见本地 &lt;code&gt;design-system/css-variables.md&lt;/code&gt;）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;:root&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--animal-primary&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;19c8b9&lt;/span&gt;&lt;span&gt;;        &lt;/span&gt;&lt;span&gt;/* hover #3dd4c6 / active #11a89b */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--animal-text&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;794f27&lt;/span&gt;&lt;span&gt;;            &lt;/span&gt;&lt;span&gt;/* 标题/主文字（大地棕） */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--animal-text-body&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;725d42&lt;/span&gt;&lt;span&gt;;       &lt;/span&gt;&lt;span&gt;/* 正文 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--animal-text-secondary&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;9f927d&lt;/span&gt;&lt;span&gt;;  &lt;/span&gt;&lt;span&gt;/* 次级 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--animal-bg&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;f8f8f0&lt;/span&gt;&lt;span&gt;;              &lt;/span&gt;&lt;span&gt;/* 页面底（羊皮纸） */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--animal-bg-content&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;rgb&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;247&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;243&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;223&lt;/span&gt;&lt;span&gt;); &lt;/span&gt;&lt;span&gt;/* 卡片/内容区（奶油） */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--animal-border&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;c4b89e&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--animal-radius&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&lt;span&gt;18&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;  &lt;/span&gt;&lt;span&gt;--animal-radius-pill&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&lt;span&gt;50&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--animal-shadow-btn&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;bdaea0&lt;/span&gt;&lt;span&gt;;      &lt;/span&gt;&lt;span&gt;/* 3D 堆叠色 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--animal-ease&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;cubic-bezier&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0.4&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.2&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;二、七期实录&lt;a href=&quot;#二七期实录&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;一期：全局色板地基（4 文件）&lt;a href=&quot;#一期全局色板地基4-文件&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;目标&lt;/strong&gt;：全站从冷灰/白/黑换成羊皮纸 + 大地棕 + 薄荷。&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;文件&lt;/th&gt;&lt;th&gt;改动&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;src/config/siteConfig.ts&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;themeColor.hue: 40 → 185&lt;/code&gt;。站点主色是 oklch 色相机制，185 ≈ &lt;code&gt;#19c8b9&lt;/code&gt; 的薄荷色相，全站 &lt;code&gt;var(--primary)&lt;/code&gt; 派生色自动跟随&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;src/utils/setting-utils.ts&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;getDefaultHue&lt;/code&gt; 兜底 &lt;code&gt;&quot;250&quot; → &quot;185&quot;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;src/styles/variables.styl&lt;/code&gt;&lt;/td&gt;&lt;td&gt;亮色中性色板整块替换（见下表）+ 阴影暖化&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;src/styles/main.css&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;@theme&lt;/code&gt; 里重定义 Tailwind 的 &lt;code&gt;black&lt;/code&gt;（见下）&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&lt;code&gt;variables.styl&lt;/code&gt; 亮色 &lt;code&gt;:root&lt;/code&gt; 关键映射（暗色块未动）：&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;变量&lt;/th&gt;&lt;th&gt;旧值&lt;/th&gt;&lt;th&gt;新值&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;--primary&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;oklch(0.70 0.14 var(--hue))&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;oklch(0.75 0.13 var(--hue))&lt;/code&gt;（hue=185 时精确对准 #19c8b9）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;--page-bg&lt;/code&gt;&lt;/td&gt;&lt;td&gt;oklch 灰&lt;/td&gt;&lt;td&gt;&lt;code&gt;#f8f8f0&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;--card-bg&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;white&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;#f7f3df&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;--card-bg-transparent&lt;/code&gt;&lt;/td&gt;&lt;td&gt;白透明&lt;/td&gt;&lt;td&gt;&lt;code&gt;rgb(247 243 223 / …)&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;--btn-content&lt;/code&gt;&lt;/td&gt;&lt;td&gt;oklch&lt;/td&gt;&lt;td&gt;&lt;code&gt;#794f27&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;--btn-regular-bg&lt;/code&gt;&lt;/td&gt;&lt;td&gt;oklch&lt;/td&gt;&lt;td&gt;&lt;code&gt;#fffbe7&lt;/code&gt;（hover &lt;code&gt;#f4eeda&lt;/code&gt; / active &lt;code&gt;#e9e1c6&lt;/code&gt;）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;--btn-plain-bg-hover / active&lt;/code&gt;&lt;/td&gt;&lt;td&gt;oklch&lt;/td&gt;&lt;td&gt;&lt;code&gt;#efe9d6&lt;/code&gt; / &lt;code&gt;#fdfbf4&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;--deep-text&lt;/code&gt;&lt;/td&gt;&lt;td&gt;近黑 oklch&lt;/td&gt;&lt;td&gt;&lt;code&gt;#794f27&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;--title-active&lt;/code&gt;&lt;/td&gt;&lt;td&gt;oklch&lt;/td&gt;&lt;td&gt;&lt;code&gt;#11a89b&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;--line-divider / --line-color&lt;/code&gt;&lt;/td&gt;&lt;td&gt;冷黑透明&lt;/td&gt;&lt;td&gt;&lt;code&gt;rgba(121,79,39,.12) / .16&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;--card-border-color&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;black&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;#c4b89e&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;--meta-divider / --content-meta&lt;/code&gt;&lt;/td&gt;&lt;td&gt;冷黑透明&lt;/td&gt;&lt;td&gt;&lt;code&gt;rgba(121,79,39,.22) / .72&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;--muted&lt;/code&gt;&lt;/td&gt;&lt;td&gt;oklch&lt;/td&gt;&lt;td&gt;&lt;code&gt;#f0ece2&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;--selection-bg&lt;/code&gt;&lt;/td&gt;&lt;td&gt;oklch&lt;/td&gt;&lt;td&gt;&lt;code&gt;#e6f9f6&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;--license-block-bg&lt;/code&gt;&lt;/td&gt;&lt;td&gt;冷黑透明&lt;/td&gt;&lt;td&gt;&lt;code&gt;rgba(121,79,39,.06)&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;--float-panel-bg&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;white&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;#f8f8f0&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&lt;code&gt;--inline-code-bg/color&lt;/code&gt;、&lt;code&gt;--enter-btn-*&lt;/code&gt; 引用了上面的 btn 变量，&lt;strong&gt;自动跟随，无需单独改&lt;/strong&gt;。链接、TOC、admonitions 等 &lt;code&gt;var(--hue)&lt;/code&gt; 派生项在 hue 185 下自动变薄荷/青绿。&lt;/p&gt;&lt;p&gt;阴影冷黑 → 暖棕（&lt;code&gt;rgba(61, 52, 40, ·)&lt;/code&gt;，色值取自规范的 elevation 阴影）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;--shadow-sm: 0 2px 4px rgba(61&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; 52&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; 40&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;/span&gt;&lt;span&gt;.06&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;--shadow-md: 0 3px 10px rgba(61&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; 52&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; 40&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;/span&gt;&lt;span&gt;.1&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;--shadow-lg: 0 8px 24px rgba(61&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; 52&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; 40&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;/span&gt;&lt;span&gt;.14&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;--shadow-xl: 0 12px 32px rgba(61&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; 52&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; 40&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;/span&gt;&lt;span&gt;.18&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;--shadow-navbar: 0 4px 16px rgba(61&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; 52&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; 40&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;/span&gt;&lt;span&gt;.12&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;--shadow-button: 0 3px 10px rgba(61&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; 52&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; 40&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;/span&gt;&lt;span&gt;.16&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;杠杆点：&lt;code&gt;@theme&lt;/code&gt; 重定义 &lt;code&gt;black&lt;/code&gt;&lt;/strong&gt;（main.css）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;@theme&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--color-black: &lt;/span&gt;&lt;span&gt;#5c3a1a&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/* 暖深棕，替代纯黑语义 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;Tailwind 的 &lt;code&gt;text-black/75&lt;/code&gt;、&lt;code&gt;bg-black/5&lt;/code&gt; 等&lt;strong&gt;全部引用主题变量&lt;/strong&gt;，一行改动让全站 34 处散落的 &lt;code&gt;text-black/*&lt;/code&gt; 和 &lt;code&gt;.text-90/75/50&lt;/code&gt; 工具类自动转暖，不用逐个模板改。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;二期：按钮系统（11 文件）&lt;a href=&quot;#二期按钮系统11-文件&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;关键机制（复刻必读）&lt;/strong&gt;：&lt;code&gt;main.css&lt;/code&gt; 里 &lt;code&gt;.btn-*&lt;/code&gt; 原有规则都在 &lt;code&gt;@layer components&lt;/code&gt;（161–619 行的大块）内，而模板里散落的 &lt;code&gt;rounded-lg&lt;/code&gt; 等是 utilities 层——&lt;strong&gt;层序上 utilities 永远赢&lt;/strong&gt;。所以按钮的 pill 化规则&lt;strong&gt;刻意写在文件末尾、不进任何层&lt;/strong&gt;：未分层的 normal 声明在级联里排最后，无条件压过所有分层规则。&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/* main.css 末尾（未分层）—— animal-island 二期·按钮系统 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.btn-plain&lt;/span&gt;&lt;span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.btn-regular&lt;/span&gt;&lt;span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.btn-card&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;border-radius: &lt;/span&gt;&lt;span&gt;&lt;span&gt;50&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;              &lt;/span&gt;&lt;span&gt;/* 硬规则：按钮一律 pill */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;transition:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;background-color &lt;/span&gt;&lt;span&gt;&lt;span&gt;0.15&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--ease-standard&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;color&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;0.15&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--ease-standard&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;box-shadow &lt;/span&gt;&lt;span&gt;&lt;span&gt;0.2&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--ease-standard&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;transform &lt;/span&gt;&lt;span&gt;&lt;span&gt;0.15&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--ease-standard&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;opacity &lt;/span&gt;&lt;span&gt;&lt;span&gt;0.15&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--ease-standard&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/* default 填充按钮：柔和抬升投影 rest → hover 加深 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.btn-regular&lt;/span&gt;&lt;span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;.btn-card&lt;/span&gt;&lt;span&gt; { box-shadow: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rgba&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;61&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;52&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;40&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;0.06&lt;/span&gt;&lt;span&gt;); }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;.btn-regular&lt;/span&gt;&lt;span&gt;:hover&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;.btn-card&lt;/span&gt;&lt;span&gt;:hover&lt;/span&gt;&lt;/span&gt;&lt;span&gt; { box-shadow: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rgba&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;61&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;52&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;40&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;0.1&lt;/span&gt;&lt;span&gt;); }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/* ghost 按钮透明底不投影，hover 填底后再给，避免浮出脏晕 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;.btn-plain&lt;/span&gt;&lt;span&gt;:hover&lt;/span&gt;&lt;/span&gt;&lt;span&gt; { box-shadow: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rgba&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;61&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;52&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;40&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;0.06&lt;/span&gt;&lt;span&gt;); }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/* primary 主按钮：3D 堆叠阴影，全站唯一（硬规则 5） */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.btn-primary&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;background-color: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--primary&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;color: &lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;fff&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;border-radius: &lt;/span&gt;&lt;span&gt;&lt;span&gt;50&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;font-weight: &lt;/span&gt;&lt;span&gt;600&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;box-shadow: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;bdaea0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;transition:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;background-color &lt;/span&gt;&lt;span&gt;&lt;span&gt;0.15&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--ease-standard&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;color&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;0.15&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--ease-standard&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;box-shadow &lt;/span&gt;&lt;span&gt;&lt;span&gt;0.15&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--ease-standard&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;transform &lt;/span&gt;&lt;span&gt;&lt;span&gt;0.15&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--ease-standard&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;30&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;.btn-primary&lt;/span&gt;&lt;span&gt;:hover&lt;/span&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;31&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;background-color: &lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;3dd4c6&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;32&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;transform: &lt;/span&gt;&lt;span&gt;translateY&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;-1&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;33&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;box-shadow: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;6&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;bdaea0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;34&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;35&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;.btn-primary&lt;/span&gt;&lt;span&gt;:active&lt;/span&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;36&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;background-color: &lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;11a89b&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;37&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;transform: &lt;/span&gt;&lt;span&gt;translateY&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;38&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;box-shadow: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;bdaea0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;39&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;btn-primary&lt;/code&gt; 接入了 &lt;strong&gt;10 处真 CTA&lt;/strong&gt;（只在原类上追加 class，背景/圆角/hover 由未分层规则统一接管）：&lt;/p&gt;&lt;p&gt;&lt;code&gt;404.astro&lt;/code&gt;、&lt;code&gt;sponsor.astro&lt;/code&gt;、&lt;code&gt;atom.astro&lt;/code&gt;、&lt;code&gt;rss.astro&lt;/code&gt;、&lt;code&gt;posts/[...slug].astro&lt;/code&gt;（赞助按钮×2）、&lt;code&gt;EncryptedContent.astro&lt;/code&gt;（解锁提交）、&lt;code&gt;AnimeCard/AnimeDetailModal&lt;/code&gt;、&lt;code&gt;BilibiliCard/BilibiliDetailModal&lt;/code&gt;。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;刻意不给 3D&lt;/strong&gt; 的：分页当前页、筛选器激活态（状态指示非动作按钮）、导航栏 ghost 胶囊（只配柔和影）。&lt;/p&gt;&lt;p&gt;焦点环不用动——全站已有 &lt;code&gt;outline: 2px solid var(--primary)&lt;/code&gt; 基线（main.css &lt;code&gt;@layer components&lt;/code&gt;），默认色相下就是薄荷。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;三期：导航栏 / 浮动 UI / 页脚（5 文件）&lt;a href=&quot;#三期导航栏--浮动-ui--页脚5-文件&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;navbar.css&lt;/code&gt;：吸顶滚动投影 &lt;code&gt;rgba(0,0,0,.1)&lt;/code&gt; → &lt;code&gt;rgba(61,52,40,.12)&lt;/code&gt;；壁纸模式下拉/浮动面板的冷白玻璃 → &lt;strong&gt;奶油玻璃 &lt;code&gt;rgba(248,248,240,.55)&lt;/code&gt;&lt;/strong&gt;，冷黑描边 → &lt;code&gt;rgba(121,79,39,.1)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FloatingControls.astro&lt;/code&gt;（阅读进度徽章）：外框与圆环底轨 → &lt;code&gt;rgba(121,79,39,.15)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FloatingButton.astro&lt;/code&gt;：冷黑 1px 框 → 同上&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FloatingTOC.astro&lt;/code&gt;：空目录占位字 &lt;code&gt;text-gray-500&lt;/code&gt; → &lt;code&gt;text-(--content-meta)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Footer.astro&lt;/code&gt;：&lt;strong&gt;零改动&lt;/strong&gt;——它的 &lt;code&gt;border-black/10&lt;/code&gt; 和 &lt;code&gt;text-50&lt;/code&gt; 被一期的 &lt;code&gt;--color-black&lt;/code&gt; 重定义自动救了&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;四期：卡片系统统一 + 灰阶大手术（2 文件）&lt;a href=&quot;#四期卡片系统统一--灰阶大手术2-文件&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;杠杆点二：整个灰阶重定义&lt;/strong&gt;。全站 &lt;code&gt;neutral&lt;/code&gt; 206 处 + &lt;code&gt;gray&lt;/code&gt; 37 处冷灰，在 &lt;code&gt;@theme&lt;/code&gt; 里整阶换成羊皮纸阶：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;@theme&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--color-neutral-50: &lt;/span&gt;&lt;span&gt;#fbfaf3;&lt;/span&gt;&lt;span&gt;  --color-gray-50: &lt;/span&gt;&lt;span&gt;#fbfaf3;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--color-neutral-100: &lt;/span&gt;&lt;span&gt;#f0ece2;&lt;/span&gt;&lt;span&gt; --color-gray-100: &lt;/span&gt;&lt;span&gt;#f0ece2;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--color-neutral-200: &lt;/span&gt;&lt;span&gt;#e5ddc8;&lt;/span&gt;&lt;span&gt; --color-gray-200: &lt;/span&gt;&lt;span&gt;#e5ddc8;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--color-neutral-300: &lt;/span&gt;&lt;span&gt;#c4b89e;&lt;/span&gt;&lt;span&gt; --color-gray-300: &lt;/span&gt;&lt;span&gt;#c4b89e;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--color-neutral-400: &lt;/span&gt;&lt;span&gt;#b3a68d;&lt;/span&gt;&lt;span&gt; --color-gray-400: &lt;/span&gt;&lt;span&gt;#b3a68d;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--color-neutral-500: &lt;/span&gt;&lt;span&gt;#9f927d&lt;/span&gt;&lt;span&gt;; --color-gray-500: &lt;/span&gt;&lt;span&gt;#9f927d&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--color-neutral-600: &lt;/span&gt;&lt;span&gt;#8a7b66&lt;/span&gt;&lt;span&gt;; --color-gray-600: &lt;/span&gt;&lt;span&gt;#8a7b66&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--color-neutral-700: &lt;/span&gt;&lt;span&gt;#725d42&lt;/span&gt;&lt;span&gt;; --color-gray-700: &lt;/span&gt;&lt;span&gt;#725d42&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--color-neutral-800: &lt;/span&gt;&lt;span&gt;#5a4426&lt;/span&gt;&lt;span&gt;; --color-gray-800: &lt;/span&gt;&lt;span&gt;#5a4426&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--color-neutral-900: &lt;/span&gt;&lt;span&gt;#44331c&lt;/span&gt;&lt;span&gt;; --color-gray-900: &lt;/span&gt;&lt;span&gt;#44331c&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--color-neutral-950: &lt;/span&gt;&lt;span&gt;#2e2110&lt;/span&gt;&lt;span&gt;; --color-gray-950: &lt;/span&gt;&lt;span&gt;#2e2110&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;卡片试点收编&lt;/strong&gt;：早期在 &lt;code&gt;PostCard.astro&lt;/code&gt; scoped 样式里做过一版写死色值的试点，四期把它删掉，升格为 &lt;code&gt;main.css&lt;/code&gt; 末尾的&lt;strong&gt;全局规则&lt;/strong&gt;（发现 &lt;code&gt;ProjectCard&lt;/code&gt; 复用了 &lt;code&gt;post-card-wrapper&lt;/code&gt; 类名，白捡覆盖）：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/* 偏离规范硬规则 8（Card 无 box-shadow）：本站页面底与卡片底同为羊皮纸、&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;色差极小，需要暖色抬升投影做分离；hover 上浮 -2px 与规范一致。 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.post-card-wrapper.card-base&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.album-card.card-base&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;background-color: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--card-bg&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;box-shadow: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rgba&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;61&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;52&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;40&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.1&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;transition:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;transform &lt;/span&gt;&lt;span&gt;&lt;span&gt;0.3&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--ease-standard&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;box-shadow &lt;/span&gt;&lt;span&gt;&lt;span&gt;0.3&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--ease-standard&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;background-color &lt;/span&gt;&lt;span&gt;&lt;span&gt;0.15&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ease&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;.post-card-wrapper.card-base&lt;/span&gt;&lt;span&gt;:hover&lt;/span&gt;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;.album-card.card-base&lt;/span&gt;&lt;span&gt;:hover&lt;/span&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;transform: &lt;/span&gt;&lt;span&gt;translateY&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;-2&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;box-shadow: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;8&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;24&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rgba&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;61&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;52&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;40&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.14&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.post-card-wrapper&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.post-card-title&lt;/span&gt;&lt;span&gt; { color: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--deep-text&lt;/span&gt;&lt;span&gt;); }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.post-card-wrapper&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&lt;span&gt;.post-card-title&lt;/span&gt;&lt;span&gt;:hover&lt;/span&gt;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.post-card-wrapper&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;.post-card-title&lt;/span&gt;&lt;span&gt;:hover&lt;/span&gt;&lt;/span&gt;&lt;span&gt; { color: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--title-active&lt;/span&gt;&lt;span&gt;); }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.post-card-wrapper&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;.post-card-title&lt;/span&gt;&lt;span&gt;::before&lt;/span&gt;&lt;/span&gt;&lt;span&gt; { background-color: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--primary&lt;/span&gt;&lt;span&gt;); }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.post-card-wrapper&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.description&lt;/span&gt;&lt;span&gt; { color: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--content-meta&lt;/span&gt;&lt;span&gt;); }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.album-card&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.album-card-title&lt;/span&gt;&lt;span&gt; { color: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--deep-text&lt;/span&gt;&lt;span&gt;); }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.album-card&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;.album-card-title&lt;/span&gt;&lt;span&gt;:hover&lt;/span&gt;&lt;/span&gt;&lt;span&gt; { color: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--title-active&lt;/span&gt;&lt;span&gt;); }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;banner 横幅没改：它的配色只有「照片上的白字 + 黑投影」，属于图片可读性机制，不在违规用色范围。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;五期：Markdown 正文与代码块（2 文件）&lt;a href=&quot;#五期markdown-正文与代码块2-文件&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;真相&lt;/strong&gt;：文章正文一直吃的是 &lt;code&gt;@tailwindcss/typography&lt;/code&gt; 插件的&lt;strong&gt;默认冷灰&lt;/strong&gt;（body = gray-700 蓝灰、标题 = gray-900 冷近黑）。插件的 &lt;code&gt;--tw-prose-*&lt;/code&gt; 变量挂在 &lt;code&gt;.prose&lt;/code&gt; 类上（注意：这个类藏在 Astro 的 &lt;code&gt;class:list&lt;/code&gt; 表达式里，grep &lt;code&gt;class=&quot;...prose...&quot;&lt;/code&gt; 抓不到）。&lt;/p&gt;&lt;p&gt;&lt;code&gt;markdown.css&lt;/code&gt; 末尾用未分层规则整组覆盖，&lt;strong&gt;仅亮色生效&lt;/strong&gt;：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;:root:not&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;.dark&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;.prose&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;:root:not&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;.dark&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;.custom-md&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--tw-prose-body&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;725d42&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--tw-prose-headings&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;794f27&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--tw-prose-lead&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;794f27&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--tw-prose-bold&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;5c3a1a&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--tw-prose-counters&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;9f927d&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--tw-prose-bullets&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;8a7b66&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--tw-prose-hr&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;d4c9b4&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--tw-prose-quotes&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;725d42&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--tw-prose-quote-borders&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;d4c9b4&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--tw-prose-captions&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;9f927d&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--tw-prose-code&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;794f27&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;代码块（&lt;code&gt;expressive-code.css&lt;/code&gt;）：亮色 one-light 默认冷白 &lt;code&gt;#fafafa&lt;/code&gt; → 奶油，圆角对齐规范 20px：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;data-theme&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&apos;one-light&apos;&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;:not&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;.wallpaper-transparent&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.expressive-code&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.frame&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;background-color: &lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;f7f3df&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!important&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;border: &lt;/span&gt;&lt;span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;solid&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rgba&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;121&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;79&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;39&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.14&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;border-radius: &lt;/span&gt;&lt;span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;壁纸透明模式的既有规则（半透明 &lt;code&gt;--card-bg-transparent&lt;/code&gt;）保留，只补了 &lt;code&gt;border-radius: 20px&lt;/code&gt;；两种模式条件互斥不冲突。行内代码、表格、blockquote 引条在前几期已随变量自动转暖。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;排版期：Nunito 圆体接入（2 文件 + 6 字体）&lt;a href=&quot;#排版期nunito-圆体接入2-文件--6-字体&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;现状原为：正文 Latin = Work Sans，&lt;strong&gt;标题链 = Playfair + 思源宋体（衬线）&lt;/strong&gt;——与规范「圆体无衬线」完全相反。&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;下载 Nunito 静态 woff2&lt;/strong&gt;（fontsource 发行文件，latin 子集，6 字重共约 97KB）。jsdelivr/fastly/unpkg 三源轮询，失败回落 npm 塔包：&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;public/assets/fonts/nunito-latin-{400,500,600,700,800,900}-normal.woff2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;src/config/fontConfig.ts&lt;/code&gt;&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;fontsList&lt;/code&gt; 新增 Nunito 定义（&lt;code&gt;provider: &quot;local&quot;&lt;/code&gt;，六个 variants 指向上列文件）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;selected: [&quot;--font-work-sans&quot;, …] → [&quot;--font-nunito&quot;, &quot;--font-hanyi-wenhei&quot;]&lt;/code&gt;（拉丁 Nunito，CJK 回退汉仪文黑）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;headingFont: [&quot;--font-playfair&quot;, &quot;--font-noto-serif-sc&quot;] → [&quot;--font-nunito&quot;, &quot;--font-hanyi-wenhei&quot;]&lt;/code&gt;（&lt;strong&gt;处刑衬线链&lt;/strong&gt;）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bannerSubtitleFont: &quot;--font-inter&quot; → &quot;--font-nunito&quot;&lt;/code&gt;（横幅主标题 Zen Maru Gothic 本来就是圆体，保留）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;main.css&lt;/code&gt;&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;@theme&lt;/code&gt; 的 &lt;code&gt;--font-sans&lt;/code&gt; → &lt;code&gt;&apos;Nunito&apos;, &apos;Noto Sans SC&apos;, …&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;正文字重 500（规范 body 500）：&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;@layer&lt;/span&gt;&lt;span&gt; base {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt; { font-weight: &lt;/span&gt;&lt;span&gt;500&lt;/span&gt;&lt;span&gt;; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;必须放 &lt;code&gt;base&lt;/code&gt; 层&lt;/strong&gt;——放未分层会因层序压过 utilities 的 &lt;code&gt;font-bold&lt;/code&gt;/&lt;code&gt;font-medium&lt;/code&gt;，全站粗体全废。&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;改 fontConfig 后 &lt;strong&gt;dev server 必须重启&lt;/strong&gt;（文件头注释写明），且首次冷启动约 &lt;strong&gt;57 秒&lt;/strong&gt;（配置变更触发 content store 清空重建）——排查「dev 卡死」时先数到 60 秒再下结论。&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;终验：静态三件套&lt;a href=&quot;#终验静态三件套&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;检查&lt;/th&gt;&lt;th&gt;结果&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;pnpm type-check&lt;/code&gt;&lt;/td&gt;&lt;td&gt;0 错&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;pnpm check&lt;/code&gt;&lt;/td&gt;&lt;td&gt;12 errors，全部为 &lt;code&gt;anime.astro&lt;/code&gt;/&lt;code&gt;vndb.astro&lt;/code&gt;/i18n &lt;strong&gt;存量基线&lt;/strong&gt;（原基线 17），改动文件零新增&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;pnpm build&lt;/code&gt;（带 &lt;code&gt;NODE_OPTIONS=--use-system-ca&lt;/code&gt;）&lt;/td&gt;&lt;td&gt;exit 0，Pagefind 33 页索引正常&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;浏览器探针法（改样式后即时验证，不开截图也能拿到铁证）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 计算色值 / 字体 / 圆角&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;getComputedStyle&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;document&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;.custom-md h2&apos;&lt;/span&gt;&lt;span&gt;)).&lt;/span&gt;&lt;span&gt;color&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;document&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;fonts&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;check&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;700 16px Nunito&apos;&lt;/span&gt;&lt;span&gt;)   &lt;/span&gt;&lt;span&gt;// 字体是否真加载&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 变量挂载点排查：沿祖先链找 --tw-prose-body 在哪一层有值&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;三、关键机制与坑（复刻必读）&lt;a href=&quot;#三关键机制与坑复刻必读&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Tailwind v4 层序是本次的核心手法&lt;/strong&gt;：&lt;code&gt;@layer utilities&lt;/code&gt; &amp;gt; &lt;code&gt;components&lt;/code&gt; &amp;gt; &lt;code&gt;base&lt;/code&gt;，而&lt;strong&gt;未分层 &amp;gt; 所有分层&lt;/strong&gt;。想让全局换肤盖过模板里散落的工具类（&lt;code&gt;rounded-*&lt;/code&gt;、&lt;code&gt;text-black/*&lt;/code&gt;），唯一干净解法是把规则写在层外。反过来，想让 utility 还能覆盖（如 &lt;code&gt;font-bold&lt;/code&gt;），就必须把规则放进 &lt;code&gt;base&lt;/code&gt; 层。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;--color-black&lt;/code&gt; / &lt;code&gt;neutral&lt;/code&gt; / &lt;code&gt;gray&lt;/code&gt; 三个主题变量是最大杠杆&lt;/strong&gt;：改 3 行定义 = 全站 240+ 处冷色自动转暖。新写样式前先想「这属于哪个语义族」，能挂族就不写死。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;data-theme&lt;/code&gt; 属性是双语义&lt;/strong&gt;：它同时是「Expressive Code 高亮主题名」（&lt;code&gt;one-light&lt;/code&gt;/&lt;code&gt;one-dark-pro&lt;/code&gt;）和「友圈 FCL 样式的明暗信号」。想做规范里的&lt;strong&gt;深巧克力代码块&lt;/strong&gt;（&lt;code&gt;#2b2118&lt;/code&gt;）需要让亮色模式也用暗色语法主题——会破坏友圈桥接，必须先把 EC 主题选择解耦到独立属性（如 &lt;code&gt;themeSelector: &apos;html[data-ec-theme]&apos;&lt;/code&gt;）。这是本文搁置的可选工程。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;localStorage.hue&lt;/code&gt; 覆盖默认色相&lt;/strong&gt;：站点机制是存储值优先于 &lt;code&gt;siteConfig&lt;/code&gt;。移植后旧浏览器仍会用旧色相渲染出「奶油底+琥珀主色」的缝合怪，控制台 &lt;code&gt;localStorage.removeItem(&quot;hue&quot;)&lt;/code&gt; 后刷新。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;grep 会被 Astro 语法骗&lt;/strong&gt;：&lt;code&gt;class:list={[&quot;prose …&quot;]}&lt;/code&gt; 里的类名用 &lt;code&gt;class=&quot;...&quot;&lt;/code&gt; 模式搜不到，定位样式来源时优先浏览器探针（getComputedStyle），不要只信文本搜索。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;本机 &lt;code&gt;pnpm&lt;/code&gt; 是 ps1 脚本&lt;/strong&gt;，&lt;code&gt;Start-Process -FilePath &quot;pnpm&quot;&lt;/code&gt; 间歇报「不是有效 Win32 应用」，后台拉起要用 &lt;code&gt;pnpm.cmd&lt;/code&gt; 全路径。&lt;/li&gt;
&lt;li&gt;Playwright 截图会等 &lt;code&gt;document.fonts.ready&lt;/code&gt;，Google Fonts 被墙时永不返回——本机验证截图要么先拦截字体域名，要么走 CDP &lt;code&gt;Page.captureScreenshot&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;四、复刻清单（从零照做）&lt;a href=&quot;#四复刻清单从零照做&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;下载规范：api.github.com 批量拉 &lt;code&gt;skills/animal-island-ui-style/&lt;/code&gt; + &lt;code&gt;docs/design-system/&lt;/code&gt; 到本地参考目录&lt;/li&gt;
&lt;li&gt;定默认色相：换算目标主色的 oklch 色相，改 &lt;code&gt;siteConfig.themeColor.hue&lt;/code&gt; 与 &lt;code&gt;setting-utils.ts&lt;/code&gt; 兜底&lt;/li&gt;
&lt;li&gt;改 &lt;code&gt;variables.styl&lt;/code&gt; 亮色中性块 + 阴影暖化（对照本文一期映射表）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;main.css&lt;/code&gt; 的 &lt;code&gt;@theme&lt;/code&gt; 加入 &lt;code&gt;--color-black&lt;/code&gt; 与 neutral/gray 双阶暖色映射&lt;/li&gt;
&lt;li&gt;&lt;code&gt;main.css&lt;/code&gt; &lt;strong&gt;文件末尾（层外）&lt;/strong&gt; 加按钮 pill / 柔和投影 / &lt;code&gt;btn-primary&lt;/code&gt; 3D 规则，逐处给真 CTA 追加 &lt;code&gt;btn-primary&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;导航栏、浮动组件里手写冷黑 &lt;code&gt;rgba(0,0,0,·)&lt;/code&gt; 的地方换暖棕&lt;/li&gt;
&lt;li&gt;&lt;code&gt;main.css&lt;/code&gt; 层外加卡片全局规则（对照四期代码）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;markdown.css&lt;/code&gt; 末尾加 &lt;code&gt;:root:not(.dark)&lt;/code&gt; 守卫的 11 个 &lt;code&gt;--tw-prose-*&lt;/code&gt;；&lt;code&gt;expressive-code.css&lt;/code&gt; 加奶油代码块规则&lt;/li&gt;
&lt;li&gt;下载 Nunito woff2 → &lt;code&gt;fontConfig.ts&lt;/code&gt; 加定义并切 &lt;code&gt;selected&lt;/code&gt;/&lt;code&gt;headingFont&lt;/code&gt; → &lt;code&gt;@theme --font-sans&lt;/code&gt; → &lt;code&gt;@layer base&lt;/code&gt; 字重 500&lt;/li&gt;
&lt;li&gt;重启 dev（等足 60 秒冷启动）→ &lt;code&gt;pnpm type-check &amp;amp;&amp;amp; pnpm check &amp;amp;&amp;amp; pnpm build&lt;/code&gt; 三件套&lt;/li&gt;
&lt;/ol&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;五、未尽事项&lt;a href=&quot;#五未尽事项&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;深巧克力代码块&lt;/strong&gt;（&lt;code&gt;#2b2118&lt;/code&gt; + &lt;code&gt;#3d3028&lt;/code&gt; 描边）：等 &lt;code&gt;data-theme&lt;/code&gt; 解耦，方案见「坑 3」&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;暗色模式&lt;/strong&gt;：按用户要求未设计；现所有暖色规则以亮色为界，将来做暗色需另出一套映射&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Noto Sans SC 替换汉仪文黑&lt;/strong&gt;：CJK 已是黑体、视觉差距小，且要接 10MB+ 字体进子集管线，暂无必要&lt;/li&gt;
&lt;li&gt;筛选器激活态、分页当前页是「状态」而非「动作」，按规范不配 3D，但如要强化主按钮气质可再议&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font-showcase.astro/png&lt;/code&gt; 两个未跟踪文件非本次工程产物，未纳入提交&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;六、验收状态&lt;a href=&quot;#六验收状态&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;七期全部完成并验收：type-check 0 错、check 12 错（全为存量）、build 全链路 exit 0。相关基建另有前作：&lt;a href=&quot;/posts/azuma.zuomu-blog-13/&quot;&gt;13文章（阅读进度）&lt;/a&gt;、&lt;a href=&quot;/posts/azuma.zuomu-blog-18/&quot;&gt;18文章（性能优化）&lt;/a&gt;、&lt;a href=&quot;/posts/azuma.zuomu-blog-19/&quot;&gt;19文章（导航栏改造）&lt;/a&gt;。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;</content:encoded></item><item><title>导航栏 UI 改造全记录：卡片边框、悬浮胶囊、三段收拢与 PC 收拢态快捷面板（附完整复现指南）</title><link>https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-19/</link><guid isPermaLink="true">https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-19/</guid><description>一次连续迭代的完整复盘——从卡片边框体系、悬浮胶囊导航，到三段结构拆分、下滑收拢动画，再到 PC 收拢态汉堡的控件快捷面板与&quot;行点击原位弹卡&quot;的两处根因修复，追加 PC 搜索控件图标化与收拢动画卡顿的逐帧诊断修复。每个批次改了哪些文件、为什么这么改、踩了哪些坑（CSS 层序、正圆公式、包含块、Vite 缓存、Playwright headless 动画冻结），附 git 重放与手工重做两条复现路径及验收清单</description><pubDate>Mon, 05 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h1&gt;导航栏 UI 改造全记录：卡片边框、悬浮胶囊、三段收拢与 PC 收拢态快捷面板（附完整复现指南）&lt;a href=&quot;#导航栏-ui-改造全记录卡片边框悬浮胶囊三段收拢与-pc-收拢态快捷面板附完整复现指南&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;本文记录 Firefly 博客一次连续的导航栏/卡片 UI 改造：&lt;strong&gt;六个已合并提交 + 一条被回滚的实验分支&lt;/strong&gt;，时间跨度 2026-10-04 至 2026-10-05。写给两类读者：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;未来的维护者（包括新会话的 AI 助手）&lt;/strong&gt;：读完应知道每一批改了什么、为什么、当时的验证结论与未尽事项；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;希望复现的访客&lt;/strong&gt;：文末给出两条复现路径——git 一次重放，或按批次手工重做，全部关键代码在正文中可照抄。&lt;/li&gt;
&lt;/ul&gt;&lt;hr /&gt;&lt;section&gt;&lt;h2&gt;〇、概览：最终形态与提交清单&lt;a href=&quot;#〇概览最终形态与提交清单&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;改造完成后的导航栏最终形态：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;卡片边框体系&lt;/strong&gt;：开启 &lt;code&gt;enable-card-border&lt;/code&gt; 后，全站卡片与导航栏是 2px 纯黑（暗色自动切白）边框，粗细可在显示设置面板用 0–5px 滑块实时调节，持久化到 &lt;code&gt;localStorage&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;悬浮胶囊导航栏&lt;/strong&gt;：高度从 4.5rem 压扁到 4rem，圆角 9999px，整条导航栏通过 wrapper 的 &lt;code&gt;padding-top: 0.75rem&lt;/code&gt; 悬浮于顶部之下，间隙条点击穿透。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;三段独立胶囊 + 下滑收拢动画&lt;/strong&gt;：导航栏拆为左（logo/站名）、中（菜单）、右（控件）三个独立胶囊；向下滚动超过 80px 时左右两段帘式收起为&lt;strong&gt;正圆&lt;/strong&gt;，中段菜单常显不收；上滑或回顶恢复。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PC 收拢态快捷面板&lt;/strong&gt;：收拢后右段只剩一个汉堡按钮（PC 端此时才显示），点击弹出「控件快捷面板」——搜索/音乐/播放/显示设置/亮暗五行；点任意行&lt;strong&gt;导航栏保持收拢&lt;/strong&gt;，原控件的子卡片在原位弹出。移动端汉堡仍开导航链接菜单，两菜单互斥。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;移动端菜单样式可配置&lt;/strong&gt;：&lt;code&gt;siteConfig.nav.mobileMenuStyle&lt;/code&gt; 支持 &lt;code&gt;&quot;card&quot;&lt;/code&gt;（旧版浮层卡片，默认）与 &lt;code&gt;&quot;drawer&quot;&lt;/code&gt;（全屏抽屉）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PC 搜索图标化&lt;/strong&gt;：PC 端导航栏不再内嵌搜索输入框，与移动端统一为一个搜索图标；点击弹出搜索卡片，打开后光标自动落入卡内输入框。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;收拢动画卡顿修复&lt;/strong&gt;：PC 右段收拢成圆不再「弹一下→冻结→猛缩」——汉堡由 &lt;code&gt;display&lt;/code&gt; 切换改为 width 连续过渡，控件组 &lt;code&gt;max-width&lt;/code&gt; 去掉 30rem 死区；亮暗图标切主题时交叉淡化（第十三节）。&lt;/li&gt;
&lt;/ol&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;提交&lt;/th&gt;&lt;th&gt;日期&lt;/th&gt;&lt;th&gt;内容&lt;/th&gt;&lt;th&gt;规模&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;74680341&lt;/code&gt;&lt;/td&gt;&lt;td&gt;10-04&lt;/td&gt;&lt;td&gt;卡片边框默认 2px 纯黑，显示设置新增边框粗细滑块&lt;/td&gt;&lt;td&gt;11 文件 +107/−4&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;2a00a602&lt;/code&gt;&lt;/td&gt;&lt;td&gt;10-04&lt;/td&gt;&lt;td&gt;导航栏改为悬浮胶囊样式并压扁高度&lt;/td&gt;&lt;td&gt;4 文件 +33/−25&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;f6b3d060&lt;/code&gt;&lt;/td&gt;&lt;td&gt;10-04&lt;/td&gt;&lt;td&gt;导航栏接入卡片边框，边框色亮黑暗白跟随主题&lt;/td&gt;&lt;td&gt;2 文件 +16/−12&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;b7cce682&lt;/code&gt;&lt;/td&gt;&lt;td&gt;10-05&lt;/td&gt;&lt;td&gt;三段拆分、收拢动画、移动菜单配置、PC 快捷面板（含”原位弹卡”修复）&lt;/td&gt;&lt;td&gt;17 文件 +500/−157&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;04197b82&lt;/code&gt;&lt;/td&gt;&lt;td&gt;10-05&lt;/td&gt;&lt;td&gt;PC 搜索收成图标按钮，输入统一走搜索面板（第十二节）&lt;/td&gt;&lt;td&gt;4 文件 +37/−72&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;d7d3b62c&lt;/code&gt;&lt;/td&gt;&lt;td&gt;10-05&lt;/td&gt;&lt;td&gt;修复 PC 收拢动画卡顿（第十三节）&lt;/td&gt;&lt;td&gt;2 文件 +29/−20&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;基线提交为 &lt;code&gt;020533ee&lt;/code&gt;（其后是 &lt;code&gt;74680341&lt;/code&gt;），全部改动可用 &lt;code&gt;git diff 020533ee..d7d3b62c&lt;/code&gt; 一次性查看。&lt;/p&gt;&lt;p&gt;另有一次&lt;strong&gt;液态玻璃（liquid glass）导航栏原型&lt;/strong&gt;：做出可运行版本后按要求整体回滚删除（含 &lt;code&gt;index.html&lt;/code&gt; 原型与 &lt;code&gt;banner-home.css&lt;/code&gt; 改动），未进入任何提交，仅在此备忘——若将来重做，需从零开始，git 历史里没有可恢复的痕迹。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;一、环境与验证基线&lt;a href=&quot;#一环境与验证基线&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;技术栈&lt;/strong&gt;：Astro 7 + Svelte 5 + TypeScript，包管理 &lt;strong&gt;pnpm&lt;/strong&gt;（&lt;code&gt;preinstall&lt;/code&gt; 强制），样式为 Tailwind v4 + 分层 CSS（&lt;code&gt;navbar.css&lt;/code&gt;、&lt;code&gt;main.css&lt;/code&gt; 等以 &lt;code&gt;@import&lt;/code&gt; 进主入口，&lt;strong&gt;未分层&lt;/strong&gt;）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;本地开发&lt;/strong&gt;：&lt;code&gt;pnpm dev&lt;/code&gt;（默认 &lt;code&gt;http://localhost:4321&lt;/code&gt;，绑 &lt;code&gt;::1&lt;/code&gt;，测试用 &lt;code&gt;localhost&lt;/code&gt; 不要用 &lt;code&gt;127.0.0.1&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;检查三件套&lt;/strong&gt;（每批改完必跑）：&lt;/li&gt;
&lt;/ul&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;npx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;biome&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;check&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;lt;改动文件&amp;gt;&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;# 格式/静态检查（只读，不用 pnpm lint——会波及无关文件）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pnpm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type-check&lt;/span&gt;&lt;span&gt;               &lt;/span&gt;&lt;span&gt;# tsc --noEmit --isolatedDeclarations&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pnpm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;check&lt;/span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;span&gt;# Astro 诊断；基线 = 12 个存量错误（anime.astro 11 + vndb.astro 1）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;pnpm check&lt;/code&gt; 的验收标准是「&lt;strong&gt;仍为 12，无新增&lt;/strong&gt;」，不是零错误。&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;验证分工&lt;/strong&gt;：AI 负责代码层与自动化断言；视觉验收由人完成。自动化用 Playwright（本文第八节列出它在本项目的三个特有坑）。&lt;/li&gt;
&lt;/ul&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;二、批次一：卡片边框体系（&lt;code&gt;74680341&lt;/code&gt;）&lt;a href=&quot;#二批次一卡片边框体系74680341&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;目标&lt;a href=&quot;#目标&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;把「卡片边框」从主题色细分线（&lt;code&gt;--line-divider&lt;/code&gt;，1px 半透明）改为&lt;strong&gt;可调粗细的纯黑描边&lt;/strong&gt;，默认 2px，并在显示设置面板提供滑块。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;改动明细&lt;a href=&quot;#改动明细&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;1. &lt;code&gt;src/styles/main.css&lt;/code&gt;&lt;/strong&gt; — 三条边框规则统一接变量：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/* 开启边框和阴影效果时的样式（宽度由 --card-border-width 控制，默认 2px，可在显示设置面板调节） */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.enable-card-border&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.card-base&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@apply transition-all duration-300 border border-black shadow-xs;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;border-width: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--card-border-width&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/* .card-base-transparent、.btn-card 同理，共三处 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;2. &lt;code&gt;src/utils/setting-utils.ts&lt;/code&gt;&lt;/strong&gt; — 新增四个函数（+51 行）：&lt;code&gt;getDefaultCardBorderWidth()&lt;/code&gt; 返回 &lt;code&gt;2&lt;/code&gt;；&lt;code&gt;getStoredCardBorderWidth()&lt;/code&gt; 读 &lt;code&gt;localStorage.cardBorderWidth&lt;/code&gt; 并 clamp 到 0–5；&lt;code&gt;applyCardBorderWidthToDocument(width)&lt;/code&gt; 写 &lt;code&gt;document.documentElement.style.setProperty(&quot;--card-border-width&quot;, ...)&lt;/code&gt;；&lt;code&gt;setCardBorderWidth(width)&lt;/code&gt; 持久化 + 应用。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;3. &lt;code&gt;src/layouts/Layout.astro&lt;/code&gt;&lt;/strong&gt; — 首屏内联脚本（FOUC 防护）中追加：读 &lt;code&gt;localStorage.cardBorderWidth&lt;/code&gt;，校验 0–5 后在解析 HTML 期间就把 &lt;code&gt;--card-border-width&lt;/code&gt; 写到 &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; 上，避免滑块值加载后闪一下默认边框。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;4. &lt;code&gt;src/components/controls/DisplaySettingsIntegrated.svelte&lt;/code&gt;&lt;/strong&gt; — 「卡片样式」区块新增滑块（+36 行）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;let cardBorderWidth = $state(getStoredCardBorderWidth());&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;rounded-md bg-(--btn-regular-bg) p-2&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;flex items-center justify-between mb-1&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;text-xs font-medium ...&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;i18n&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;I18nKey&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;cardBorderWidth&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;text-xs ...&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;cardBorderWidth&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;px&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;range&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;min&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;max&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;step&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;cardBorderWidth&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;oninput&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;cardBorderWidth&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Number&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;currentTarget&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;); }&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;slider w-full overlay-slider&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;同时接入三处状态机：&lt;code&gt;$effect(() =&amp;gt; setCardBorderWidth(cardBorderWidth))&lt;/code&gt; 实时生效；&lt;code&gt;cardSettingsIsDefault&lt;/code&gt; 加入宽度默认判断；&lt;code&gt;resetCardSettings()&lt;/code&gt; 恢复默认宽度并 &lt;code&gt;requestAnimationFrame(refreshAllRangeProgress)&lt;/code&gt; 刷新滑块进度条。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;5. i18n&lt;/strong&gt; — &lt;code&gt;I18nKey.cardBorderWidth&lt;/code&gt; 键 + 7 个语言文件（en/ja/ko/ru/zh_CN/zh_TW…）各加一行，中文为「边框粗细」。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;复现要点&lt;a href=&quot;#复现要点&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;按上面 1→5 顺序做即可。注意 &lt;code&gt;clampNumber&lt;/code&gt; 是 &lt;code&gt;setting-utils.ts&lt;/code&gt; 里已有的工具函数，直接复用；滑块 &lt;code&gt;step={0.5}&lt;/code&gt;，但正圆公式依赖的 CSS 变量能接受小数。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;三、批次二：悬浮胶囊化（&lt;code&gt;2a00a602&lt;/code&gt;）&lt;a href=&quot;#三批次二悬浮胶囊化2a00a602&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;目标&lt;a href=&quot;#目标-1&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;导航栏从「贴顶方角条」改为「悬浮圆角胶囊」，并整体压扁一档。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;改动明细&lt;a href=&quot;#改动明细-1&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;1. 高度 4.5rem → 4rem&lt;/strong&gt;（三处 + 联动）：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;HeaderTopRow.astro&lt;/code&gt;：fixed 态类名 &lt;code&gt;h-18&lt;/code&gt; → &lt;code&gt;h-16&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Navbar.astro&lt;/code&gt;：容器 &lt;code&gt;h-18&lt;/code&gt; → &lt;code&gt;h-16&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;layout-base.css&lt;/code&gt;：&lt;code&gt;#top-row&lt;/code&gt; 的 sticky/fixed 固定高度 &lt;code&gt;4.5rem&lt;/code&gt; → &lt;code&gt;4rem&lt;/code&gt;（两处 + 移动端一处），banner 首页占位 &lt;code&gt;calc(var(--banner-height-home) - 4.5rem)&lt;/code&gt; → &lt;code&gt;- 4rem&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;2. 圆角全模式统一 9999px&lt;/strong&gt;（&lt;code&gt;navbar.css&lt;/code&gt;）：bar 本体、&lt;code&gt;#navbar&lt;/code&gt; 阴影载体、以及 semi/semifull/亮暗各壁纸态的 &lt;code&gt;!important&lt;/code&gt; 圆角覆盖，全部由 &lt;code&gt;0 0 0.75rem 0.75rem&lt;/code&gt; 改为 &lt;code&gt;9999px&lt;/code&gt;。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;3. 悬浮间距放 wrapper&lt;/strong&gt;：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/* 间距放在 wrapper 的 padding-top 而非 bar 的 margin：&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;navbar-hidden 的 -translate-y-full 按 wrapper 自身高度平移，&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;padding 计入高度，下移多少就能完整隐藏多少，不会在顶部残留 12px 边缘。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pointer-events：间隙条是 wrapper 的 padding，整体设为 none 让点击穿透到下层，&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;bar 自身设回 auto（未分层选择器优先于 Tailwind 工具类） */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;#navbar-wrapper&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;padding-top: &lt;/span&gt;&lt;span&gt;&lt;span&gt;0.75&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pointer-events: &lt;/span&gt;&lt;span&gt;none&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;配套给 bar（&lt;code&gt;#navbar&amp;gt;div&lt;/code&gt;）加 &lt;code&gt;pointer-events: auto&lt;/code&gt;——否则悬浮间隙条会把点击吃掉，导航栏上的按钮和下拉面板全部点不到。这两行是一对，&lt;strong&gt;必须同时改&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;4. 移动端成形&lt;/strong&gt;（&lt;code&gt;&amp;lt;768px&lt;/code&gt;）：组件里 top-row 是 &lt;code&gt;px-0&lt;/code&gt;，bar 会贴屏幕边缘，胶囊两端没留空就不成形——删掉旧的「移动端去圆角」规则，改为给 &lt;code&gt;#top-row&lt;/code&gt; 补 &lt;code&gt;padding-left/right: 0.75rem&lt;/code&gt;。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;为什么间距不能用 margin&lt;a href=&quot;#为什么间距不能用-margin&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;见上面代码注释：&lt;code&gt;-translate-y-full&lt;/code&gt; 的隐藏动画按 wrapper 自身高度平移，margin 不计入 wrapper 高度，会残留一条边；padding 计入，隐藏才干净。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;四、批次三：亮黑暗白边框（&lt;code&gt;f6b3d060&lt;/code&gt;）&lt;a href=&quot;#四批次三亮黑暗白边框f6b3d060&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;目标&lt;a href=&quot;#目标-2&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;纯黑边框在暗色主题下不可见，需要&lt;strong&gt;亮色纯黑、暗色纯白&lt;/strong&gt;地跟随主题，并让导航栏与卡片共用同一套边框变量。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;改动明细&lt;a href=&quot;#改动明细-2&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;1. &lt;code&gt;src/styles/variables.styl&lt;/code&gt;&lt;/strong&gt; — 亮暗两段各加一个变量：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/* 亮色段 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;--card-border-color: black&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/* 暗色段 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;--card-border-color: white&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;2. &lt;code&gt;src/styles/main.css&lt;/code&gt;&lt;/strong&gt; — 三处卡片规则 &lt;code&gt;border-black&lt;/code&gt; 改 &lt;code&gt;border-(--card-border-color)&lt;/code&gt;；导航栏规则改为：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.enable-card-border&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;#navbar&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;:not&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;.absolute&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@apply transition-all duration-300 border shadow-xs;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;/* 与卡片边框同款：颜色跟随 --card-border-color（亮黑/暗白），宽度跟随 --card-border-width 滑块&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span&gt;（!important 压过 navbar.css 的未分层 1px 透明边框） */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;border-color: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--card-border-color&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;!important&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;border-width: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--card-border-width&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;!important&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;3. 删除两组「防闪现」旧规则&lt;/strong&gt;：原实现里 banner 玻璃态与 fullscreen 模式各有 &lt;code&gt;border: none !important&lt;/code&gt;，目的是防止切页瞬间边框从无到有闪一下。边框改为刻意设计的常驻元素后，「闪现」的定义变了——&lt;strong&gt;常驻即无闪现&lt;/strong&gt;，过渡期由 &lt;code&gt;.is-wallpaper-transitioning&lt;/code&gt; 的透明边框规则兜底。因此删除 banner 的 &lt;code&gt;border: none&lt;/code&gt; 组，fullscreen 只保留 &lt;code&gt;outline: none&lt;/code&gt;。删除行全部有意，复现时不要当作误删恢复。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;本批次踩坑：Vite dev 编译缓存丢更新&lt;a href=&quot;#本批次踩坑vite-dev-编译缓存丢更新&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;现象&lt;/strong&gt;：改了 &lt;code&gt;variables.styl&lt;/code&gt; 暗色白值 + &lt;code&gt;main.css&lt;/code&gt; 导航栏引用，磁盘文件正确，浏览器里暗色边框仍是黑的，刷新无效。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;根因&lt;/strong&gt;：dev server 同一轮里各写入了两次文件，Vite watcher 只吃到了每个文件的&lt;strong&gt;第一次写入&lt;/strong&gt;，第二次的失效事件丢失——served 编译产物残缺。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;处置&lt;/strong&gt;：重存文件踢一脚强制重编译（或重启 &lt;code&gt;pnpm dev&lt;/code&gt;）。诊断手段：&lt;code&gt;curl http://localhost:4321/src/styles/xxx&lt;/code&gt; 直接取模块编译产物，对比磁盘源码。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;结论&lt;/strong&gt;：纯本地 dev 现象。Cloudflare 构建从磁盘全新编译，线上不复现。以后遇到「CSS 改了不生效/只生效一半」，先重启 &lt;code&gt;pnpm dev&lt;/code&gt;。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;五、批次四：三段拆分与下滑收拢动画（&lt;code&gt;b7cce682&lt;/code&gt; 上半）&lt;a href=&quot;#五批次四三段拆分与下滑收拢动画b7cce682-上半&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;5.1 结构：三段独立胶囊&lt;a href=&quot;#51-结构三段独立胶囊&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;src/components/layout/Navbar.astro&lt;/code&gt;&lt;/strong&gt;：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;#navbar&lt;/code&gt; 改为 &lt;code&gt;grid grid-cols-[1fr_auto_1fr]&lt;/code&gt;（类名里保留 &lt;code&gt;z-50&lt;/code&gt; 等），三个直接子 div 各带 &lt;code&gt;.navbar-seg&lt;/code&gt;：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.navbar-seg-left&lt;/code&gt;（&lt;code&gt;justify-self-start&lt;/code&gt;）：logo + 站名，站名包一层 &lt;code&gt;&amp;lt;span class=&quot;navbar-title-text&quot;&amp;gt;&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.navbar-seg-center&lt;/code&gt;（&lt;code&gt;col-start-2&lt;/code&gt;，&lt;code&gt;hidden lg:flex&lt;/code&gt;）：菜单 &lt;code&gt;DropdownMenu&lt;/code&gt; 列表，是否居中由 &lt;code&gt;siteConfig.nav.menuAlign&lt;/code&gt; 决定；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.navbar-seg-right&lt;/code&gt;（&lt;code&gt;col-start-3 justify-self-end&lt;/code&gt;）：内含 &lt;code&gt;.navbar-right-controls&lt;/code&gt; 控件组（搜索、音乐、播放、显示设置、亮暗 + 各浮层面板）与组外的汉堡按钮 &lt;code&gt;#nav-menu-switch&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;原先「面板挂在 body 级 / 控件散放」的结构改为&lt;strong&gt;面板随控件进右段&lt;/strong&gt;（音乐面板等成为控件组子元素）。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;src/styles/navbar.css&lt;/code&gt;&lt;/strong&gt;：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;全部原 &lt;code&gt;#navbar&amp;gt;div&lt;/code&gt; 表面规则（背景、透明边框、毛玻璃 &lt;code&gt;::before&lt;/code&gt;）retarget 为 &lt;code&gt;#navbar&amp;gt;.navbar-seg&lt;/code&gt;，并在其上补一行注释掉的 &lt;code&gt;position: relative&lt;/code&gt;——&lt;strong&gt;它同时是内部浮层面板的定位包含块&lt;/strong&gt;（后文第八节第 3 条会再用到）；&lt;/li&gt;
&lt;li&gt;sticky 滚动投影从元素 &lt;code&gt;box-shadow&lt;/code&gt; 移到 &lt;code&gt;::after&lt;/code&gt; 伪元素（各壁纸态规则会覆盖元素级阴影，伪元素不参与那场竞争）；&lt;/li&gt;
&lt;li&gt;移动端 top-row 补左右留边（承接批次二第 4 条）。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;src/styles/main.css&lt;/code&gt;&lt;/strong&gt;：31 处选择器从 &lt;code&gt;&amp;gt;div&lt;/code&gt; retarget 到 &lt;code&gt;&amp;gt;.navbar-seg&lt;/code&gt;；边框规则同步接 &lt;code&gt;--card-border-color&lt;/code&gt; / &lt;code&gt;--card-border-width&lt;/code&gt;。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;src/styles/layout-base.css&lt;/code&gt;&lt;/strong&gt;：top-row 高度相关 calc 基准同步为 4rem。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;5.2 动画：下滑收拢为正圆&lt;a href=&quot;#52-动画下滑收拢为正圆&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;触发（&lt;code&gt;src/utils/scroll-utils.ts&lt;/code&gt;）&lt;/strong&gt;——把滚动方向 delta 从 dynamic 分支上提为共用计算：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;delta&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;scrollTop&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lastScrollTop&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;lastScrollTop&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;scrollTop&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 三段胶囊收拢：向下滚动且超过 80px 时左右段收成圆形，上滑或回顶恢复&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;navbarElement&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;operations&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;navbarElement&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;classList&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;toggle&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;navbar-collapsed&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;scrollTop&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;80&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;delta&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;语义：&lt;strong&gt;收拢 = 向下滚且 &amp;gt;80px；恢复 = 上滑或回顶&lt;/strong&gt;。中段菜单永不参与。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;切页清态（&lt;code&gt;src/utils/swup-transitions.ts&lt;/code&gt;）&lt;/strong&gt;：astro 换页钩子里 &lt;code&gt;navbar.classList.remove(&quot;navbar-collapsed&quot;)&lt;/code&gt;——收拢态不跨页残留，新页滚动逻辑按新滚动位置重新判断。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;形变规则（&lt;code&gt;src/styles/navbar.css&lt;/code&gt;）&lt;/strong&gt;：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/* 站名帘式收起 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;#navbar&lt;/span&gt;&lt;span&gt;.navbar-collapsed&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.navbar-seg-left&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.navbar-title-text&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;max-width: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;opacity: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/* logo 外边距归零（图标盒固定 1.75rem，保证两种 logo 形态收拢宽度一致） */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;#navbar&lt;/span&gt;&lt;span&gt;.navbar-collapsed&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.navbar-seg-left&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.navbar-logo&lt;/span&gt;&lt;span&gt; { margin-right: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/* 左右段内边距收成正圆所需的值 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;#navbar&lt;/span&gt;&lt;span&gt;.navbar-collapsed&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.navbar-seg-left&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;padding-left: &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;1.125&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--card-border-width&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;padding-right: &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;1.125&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--card-border-width&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.navbar-right-controls&lt;/span&gt;&lt;span&gt; { max-width: &lt;/span&gt;&lt;span&gt;&lt;span&gt;30&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;; overflow: &lt;/span&gt;&lt;span&gt;hidden&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/* ...过渡见批次六 */&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;#navbar&lt;/span&gt;&lt;span&gt;.navbar-collapsed&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.navbar-right-controls&lt;/span&gt;&lt;span&gt; { max-width: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;#navbar&lt;/span&gt;&lt;span&gt;.navbar-collapsed&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.navbar-seg-right&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;padding-left: &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;0.625&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--card-border-width&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;padding-right: &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;0.625&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--card-border-width&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;正圆公式推导&lt;/strong&gt;（&lt;code&gt;box-sizing: border-box&lt;/code&gt;，高度 4rem 含边框）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;宽度 = padding-left + 内容 + padding-right + 2×边框宽  ≡ 高度 4rem&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;左段内容 = logo 图标盒 1.75rem&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;→ padding = (4rem − 2×边框 − 1.75rem) / 2 = 1.125rem − 边框宽&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;右段内容 = 汉堡按钮 2.75rem&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;→ padding = (4rem − 2×边框 − 2.75rem) / 2 = 0.625rem − 边框宽&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;两个注意点：&lt;strong&gt;边框宽取变量&lt;/strong&gt; &lt;code&gt;--card-border-width&lt;/code&gt;，用户用滑块调粗后圆依然精确；&lt;strong&gt;rem 在 &lt;code&gt;&amp;lt;768px&lt;/code&gt; 是 14px&lt;/strong&gt;（根元素 &lt;code&gt;text-[14px] md:text-[16px]&lt;/code&gt;），写死 px 会在移动端错位——所以全部用 rem + calc。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;汉堡按钮显隐（PC 专用）&lt;/strong&gt;：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/* 移动端常显；≥1024px 仅收拢态显示 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;@media&lt;/span&gt;&lt;span&gt; (min-width: &lt;/span&gt;&lt;span&gt;&lt;span&gt;1024&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;#navbar&lt;/span&gt;&lt;span&gt;:not&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;.navbar-collapsed&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;.navbar-seg-right&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;#nav-menu-switch&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;display: &lt;/span&gt;&lt;span&gt;none&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;这里有个关键决策：最初把 &lt;code&gt;lg:hidden!&lt;/code&gt; 写在按钮的类名上，结果 &lt;strong&gt;Tailwind 分层内 important 在层序反转下必胜&lt;/strong&gt;（utilities 层的 &lt;code&gt;!important&lt;/code&gt; 压过未分层的普通规则），收拢态想让汉堡出现的覆盖规则永远赢不了——表现为 PC 收拢后右段无图标、只剩细条、点不到。解法是&lt;strong&gt;放弃 important 角力&lt;/strong&gt;：按钮类名去掉 &lt;code&gt;lg:hidden!&lt;/code&gt;，显隐完全交给上面这条普通规则，由特异性与层序自然接管。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;5.3 本批次的行为语义（验收用）&lt;a href=&quot;#53-本批次的行为语义验收用&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;向下滚（&amp;gt;80px）：左段收成圆（logo 存活、站名消失）、右段收成圆（只剩汉堡）、中段菜单纹丝不动；&lt;/li&gt;
&lt;li&gt;上滑或回顶：全部恢复；&lt;/li&gt;
&lt;li&gt;切页：立即恢复完整态；&lt;/li&gt;
&lt;li&gt;PC 非收拢态：汉堡隐藏；PC 收拢态：汉堡显示；移动端：汉堡始终显示。&lt;/li&gt;
&lt;/ul&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;六、批次五：移动端菜单样式可配置（&lt;code&gt;b7cce682&lt;/code&gt; 一部分）&lt;a href=&quot;#六批次五移动端菜单样式可配置b7cce682-一部分&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;目标&lt;a href=&quot;#目标-3&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;上游主题把移动端汉堡菜单重构成了全屏抽屉（MD3 侧滑 + 遮罩），但旧版「锚定导航栏下方右侧的圆角卡片」更贴合本博客的胶囊风格——做成配置项，两种都保留。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;改动明细&lt;a href=&quot;#改动明细-3&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;1. 配置项&lt;/strong&gt;（&lt;code&gt;src/config/siteConfig.ts&lt;/code&gt; + &lt;code&gt;src/types/siteConfig.ts&lt;/code&gt;）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;nav&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// 移动端汉堡菜单样式：&quot;card&quot; 浮层卡片（旧版）/&quot;drawer&quot; 全屏抽屉（上游重构版）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;mobileMenuStyle&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;card&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// 类型：mobileMenuStyle?: &quot;card&quot; | &quot;drawer&quot;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;2. &lt;code&gt;src/components/layout/NavMenuPanel.astro&lt;/code&gt;&lt;/strong&gt; 双分支渲染：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;card&lt;/code&gt; 分支：复刻历史版本（&lt;code&gt;git show a65c6576^:src/components/layout/NavMenuPanel.astro&lt;/code&gt; 可拿到原貌），根类带 &lt;code&gt;nav-menu--card&lt;/code&gt; 标记，样式为 &lt;code&gt;fixed right-4 top-21&lt;/code&gt; 圆角浮层卡片、&lt;code&gt;max-h-[80vh] overflow-y-auto&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;drawer&lt;/code&gt; 分支：保留全屏 &lt;code&gt;fixed inset-0&lt;/code&gt; 抽屉，所有抽屉专属样式规则加 &lt;code&gt;.nav-menu--drawer&lt;/code&gt; 作用域，避免两个分支互相污染。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;3. 关闭机制&lt;/strong&gt;（&lt;code&gt;src/utils/layout-init.ts&lt;/code&gt;）：卡片模式依赖 click-outside 关闭；抽屉模式面板全屏覆盖、点击恒在面板内、该监听永不触发——&lt;strong&gt;两种模式都注册&lt;/strong&gt;注册即可，无副作用。代码注释里写明了这一点。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;切换方法&lt;a href=&quot;#切换方法&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;改 &lt;code&gt;siteConfig.ts&lt;/code&gt; 的 &lt;code&gt;mobileMenuStyle&lt;/code&gt; 为 &lt;code&gt;&quot;drawer&quot;&lt;/code&gt; 重启 dev 即可切到抽屉版。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;七、批次六：PC 收拢态快捷面板与「原位弹卡」修复（&lt;code&gt;b7cce682&lt;/code&gt; 下半）&lt;a href=&quot;#七批次六pc-收拢态快捷面板与原位弹卡修复b7cce682-下半&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;这是整个系列里迭代轮次最多的一块，经历了「实现 → 验收发现逻辑不对 → 定位两处根因 → 修复」的完整过程，值得完整记录。&lt;/p&gt;&lt;section&gt;&lt;h3&gt;7.1 需求&lt;a href=&quot;#71-需求&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;收拢态下 PC 端右段只剩汉堡（中段菜单已常显导航链接，所以汉堡不该再开链接菜单），期望：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;汉堡点击打开**「右段被隐藏控件」的快捷面板**——搜索/音乐/播放/显示设置/亮暗五行；&lt;/li&gt;
&lt;li&gt;点击任意行，&lt;strong&gt;导航栏保持收拢&lt;/strong&gt;，弹出「原来点击控件时会弹出的那张子卡片」（搜索面板、音乐面板、显示设置、亮暗菜单）；&lt;/li&gt;
&lt;li&gt;移动端行为不变：汉堡开导航链接菜单；两菜单互斥。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;7.2 快捷面板本体&lt;a href=&quot;#72-快捷面板本体&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Navbar.astro&lt;/code&gt;&lt;/strong&gt; 右段新增：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;navbar-quick-panel&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;     &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;float-panel float-panel-closed absolute top-full right-0 mt-2 w-max min-w-52 p-1.5 z-50&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;     &lt;/span&gt;&lt;span&gt;data-floating-panel&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-floating-panel-trigger&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;nav-menu-switch&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;     &lt;/span&gt;&lt;span&gt;inert&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;aria-hidden&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-quick-action&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;search&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;…&amp;lt;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;   &lt;/span&gt;&lt;span&gt;&amp;lt;!-- 音乐/播放/显示/亮暗同构，按配置条件渲染 --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;要点：&lt;code&gt;w-max&lt;/code&gt; 必不可少——面板锚在收拢后只有约 64px 宽的右段里，绝对定位的收缩宽度会被包含块钳制，没有 &lt;code&gt;w-max&lt;/code&gt; 就会被压扁；&lt;code&gt;data-floating-panel&lt;/code&gt; 让它接入统一的 inert/Esc 管理。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;分流逻辑&lt;/strong&gt;（&lt;code&gt;loadButtonScript&lt;/code&gt; 内汉堡 onclick）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;isDesktop&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;window&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;matchMedia&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;(min-width: 1024px)&quot;&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;matches&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;isDesktop&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;quickPanel&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;quickPanel&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;classList&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;toggle&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;float-panel-closed&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;navPanel&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;navPanel&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;classList&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;float-panel-closed&quot;&lt;/span&gt;&lt;span&gt;);   &lt;/span&gt;&lt;span&gt;// 互斥&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;navPanel&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;navPanel&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;classList&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;toggle&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;float-panel-closed&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;quickPanel&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;quickPanel&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;classList&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;float-panel-closed&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;7.3 第一版的 bug 与两处根因&lt;a href=&quot;#73-第一版的-bug-与两处根因&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;现象&lt;/strong&gt;：在快捷面板里逐个点搜索/音乐/显示设置/亮暗——&lt;strong&gt;导航栏重新展开，子卡片却不弹出&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;根因一（展开）&lt;/strong&gt;：行点击处理器里有一句 &lt;code&gt;navbar.classList.remove(&quot;navbar-collapsed&quot;)&lt;/code&gt;。它的本意是「控件组收拢时被 &lt;code&gt;opacity: 0&lt;/code&gt; 藏住了，展开才能看见子卡」——方向就错了，需求是保持收拢。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;根因二（子卡秒关）&lt;/strong&gt;：&lt;code&gt;layout-init.ts&lt;/code&gt; 用 &lt;code&gt;setClickOutsideToClose&lt;/code&gt; 给每个面板注册了 &lt;strong&gt;document 级点击关闭监听&lt;/strong&gt;，它注册得比快捷面板的行点击处理器&lt;strong&gt;更晚&lt;/strong&gt;。DOM 事件按注册顺序派发，于是同一个点击事件里：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;行点击处理器：打开子卡 ✓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;→ 后注册的 click-outside 监听器继续跑：点击目标（快捷面板的行）在子卡之外 → 把刚打开的子卡关掉 ✗&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;两口锅叠加，用户看到的就是「只展开了导航栏，什么卡都没弹」。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;修复（&lt;code&gt;Navbar.astro&lt;/code&gt; 行点击处理器重写）&lt;/strong&gt;：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;document&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;click&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;event&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;event&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;target&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;instanceof&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Element&lt;/span&gt;&lt;span&gt;)) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;row&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;event&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;target&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;closest&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;#navbar-quick-panel [data-quick-action]&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;&lt;span&gt;row&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;event&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;stopImmediatePropagation&lt;/span&gt;&lt;span&gt;();   &lt;/span&gt;&lt;span&gt;// ← 挡掉注册更晚的 document click-outside，防同事件秒关&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;action&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;row&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;getAttribute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;data-quick-action&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;triggerByAction&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;search&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;search-switch&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;music&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;music-player-switch&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                              &lt;/span&gt;&lt;span&gt;video&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;bg-player-toggle&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;display&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;display-settings-switch&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;                              &lt;/span&gt;&lt;span&gt;theme&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;scheme-switch&quot;&lt;/span&gt;&lt;span&gt; };&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// 有子卡的行：关快捷卡、换弹子卡；video 只切换播放没有子卡，快捷卡留着当状态面板&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;action&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;!==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;video&quot;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;document&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getElementById&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;navbar-quick-panel&quot;&lt;/span&gt;&lt;span&gt;)?.&lt;/span&gt;&lt;span&gt;classList&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;float-panel-closed&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;document&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getElementById&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;triggerByAction&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;action&lt;/span&gt;&lt;span&gt;])?.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;click&lt;/span&gt;&lt;span&gt;();   &lt;/span&gt;&lt;span&gt;// 程序化点击原控件按钮&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// 搜索子卡在桌面端复用面板内输入框（见 7.5）；inert 在微任务里解除，焦点延到宏任务再给&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;action&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;search&quot;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;setTimeout&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;document&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;#search-bar-inside input&quot;&lt;/span&gt;&lt;span&gt;)?.&lt;/span&gt;&lt;span&gt;focus&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;stopImmediatePropagation&lt;/code&gt; 的安全性论证（复现时按此检查）：&lt;code&gt;Navbar.astro&lt;/code&gt; 自带的面板关闭监听注册在本处理器&lt;strong&gt;之前&lt;/strong&gt;，此刻子卡尚未打开、必为空操作；&lt;code&gt;layout-init&lt;/code&gt; 的监听注册在&lt;strong&gt;之后&lt;/strong&gt;，被挡掉正是目的；两者的忽略列表本就不依赖这次事件。无论两侧注册顺序如何翻转，结论都成立。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;7.4 收拢态子卡可见性：&lt;code&gt;:has&lt;/code&gt; 守卫&lt;a href=&quot;#74-收拢态子卡可见性has-守卫&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;子卡（搜索/音乐/显示/亮暗面板）是控件组 &lt;code&gt;.navbar-right-controls&lt;/code&gt; 的&lt;strong&gt;后代&lt;/strong&gt;，而收拢规则给控件组加了 &lt;code&gt;opacity: 0&lt;/code&gt;——&lt;strong&gt;opacity 作用于整棵子树&lt;/strong&gt;，绝对定位的子卡也会一起隐身。第一版「展开导航栏」的错误修复就是在绕这个问题。&lt;/p&gt;&lt;p&gt;正确解法是把「形变」与「淡出」拆成两条规则，淡出加守卫（&lt;code&gt;navbar.css&lt;/code&gt;）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/* 形变：始终收拢——flow 子元素（按钮们）被 max-width:0 + overflow:hidden 裁掉 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;#navbar&lt;/span&gt;&lt;span&gt;.navbar-collapsed&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.navbar-right-controls&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;max-width: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/* 淡出：仅在没有任何子浮层面板打开时启用 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;#navbar&lt;/span&gt;&lt;span&gt;&lt;span&gt;.navbar-collapsed&lt;/span&gt;&lt;span&gt;:not&lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;:has&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;.navbar-right-controls&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;.float-panel&lt;/span&gt;&lt;span&gt;:not&lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;.float-panel-closed&lt;/span&gt;&lt;span&gt;))) &lt;/span&gt;&lt;span&gt;.navbar-right-controls&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;opacity: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;两个前提成立才有效，复现时缺一不可：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;max-width: 0&lt;/code&gt; 裁不到子卡&lt;/strong&gt;：子卡是绝对定位，包含块是 &lt;code&gt;position: relative&lt;/code&gt; 的 &lt;code&gt;.navbar-seg&lt;/code&gt;（不是静态的控件组），静态祖先的 &lt;code&gt;overflow: hidden&lt;/code&gt; 不裁切「包含块在其外部」的绝对定位后代；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;:has&lt;/code&gt; 状态即时&lt;/strong&gt;：&lt;code&gt;float-panel-closed&lt;/code&gt; 类名一变，&lt;code&gt;:has&lt;/code&gt; 匹配结果同步翻转（已实测：class 移除当帧 &lt;code&gt;matches()&lt;/code&gt; 即为 false），无需额外 JS 状态同步。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;附带收益：&lt;strong&gt;「子卡开着时再下滚，面板随控件组一起被藏起」这个此前登记在案的边缘情况被一并修掉了&lt;/strong&gt;——现在子卡开着时收拢，守卫命中、控件组不淡出，面板保持可见可交互。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;7.5 搜索子卡在桌面端的输入问题&lt;a href=&quot;#75-搜索子卡在桌面端的输入问题&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;PC 正常态的搜索输入在导航栏内的 &lt;code&gt;#search-bar&lt;/code&gt;（&lt;code&gt;hidden lg:flex&lt;/code&gt;），收拢后被帘式收起；而搜索面板里的 &lt;code&gt;#search-bar-inside&lt;/code&gt; 是给移动端的（&lt;code&gt;lg:hidden&lt;/code&gt;）。若不处理，PC 点搜索行会弹出一张&lt;strong&gt;没有输入框&lt;/strong&gt;的空卡。&lt;/p&gt;&lt;p&gt;修复（&lt;code&gt;navbar.css&lt;/code&gt;）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/* 收拢态从快捷面板唤起搜索时，面板内给移动端用的输入框顶上 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;#navbar&lt;/span&gt;&lt;span&gt;.navbar-collapsed&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;#search-bar-inside&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;display: &lt;/span&gt;&lt;span&gt;flex&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;未分层规则对 Tailwind utilities 层的普通 &lt;code&gt;lg:hidden&lt;/code&gt; 自然占优，不需要 important。加上 7.2 里 &lt;code&gt;setTimeout&lt;/code&gt; 自动聚焦，PC 收拢态点搜索 = 面板弹出、光标已就位、直接打字出结果（dev 环境 Pagefind 走 mock 数据，&lt;code&gt;npm run build &amp;amp;&amp;amp; npm preview&lt;/code&gt; 才能测真搜索）。回顶恢复后该规则因 &lt;code&gt;:not(.navbar-collapsed)&lt;/code&gt; 失效，输入框回到 &lt;code&gt;lg:hidden&lt;/code&gt;，无残留。&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;注意：本节方案已被第十二节取代（2026-10-05 追加批次）&lt;/strong&gt;：PC 内嵌的 &lt;code&gt;#search-bar&lt;/code&gt; 已整体删除，&lt;code&gt;#search-bar-inside&lt;/code&gt; 去掉 &lt;code&gt;lg:hidden&lt;/code&gt; 全端常驻——本节的收拢态补丁规则随之变成死规则、已删。历史过程保留在此供参考，复现以第十二节为准。&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;7.6 video 行的误关与忽略名单&lt;a href=&quot;#76-video-行的误关与忽略名单&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;video 行没有子卡，它程序化 &lt;code&gt;.click()&lt;/code&gt; 的是 &lt;code&gt;#bg-player-toggle&lt;/code&gt;——&lt;strong&gt;这个合成 click 会冒泡到 document&lt;/strong&gt;，被快捷面板自己的 click-outside 监听判定为「点外面」，把快捷面板关了（卡面板行无所谓，反正主动关；video 行就变成了「点一下菜单自己消失」）。&lt;/p&gt;&lt;p&gt;修复（&lt;code&gt;src/utils/layout-init.ts&lt;/code&gt;）——把五个控件触发按钮全部加进快捷面板的忽略名单：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;setClickOutsideToClose&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;navbar-quick-panel&quot;&lt;/span&gt;&lt;span&gt;, [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;navbar-quick-panel&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;nav-menu-switch&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;search-switch&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;music-player-switch&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;bg-player-toggle&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;display-settings-switch&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;scheme-switch&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;]);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;理由写进了注释：这些按钮的程序化点击不算「点外面」。真实用户点不到它们（收拢态下被裁切），所以不影响任何正常交互。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;7.7 配套 i18n&lt;a href=&quot;#77-配套-i18n&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;快捷面板两个新行名需要文案：&lt;code&gt;I18nKey.displaySettings&lt;/code&gt;（显示设置）、&lt;code&gt;I18nKey.lightDarkMode&lt;/code&gt;（亮暗模式），7 个语言文件各加两行。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;7.8 本批次最终行为矩阵（自动化实测通过）&lt;a href=&quot;#78-本批次最终行为矩阵自动化实测通过&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;操作&lt;/th&gt;&lt;th&gt;期望结果&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;PC 下滚收拢 → 点汉堡&lt;/td&gt;&lt;td&gt;快捷面板弹开，导航链接菜单关闭（互斥）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;点搜索行&lt;/td&gt;&lt;td&gt;导航栏&lt;strong&gt;保持收拢&lt;/strong&gt;；快捷卡关；搜索卡开、&lt;code&gt;inert&lt;/code&gt; 解除、焦点在面板输入框；控件组 &lt;code&gt;max-width: 0&lt;/code&gt; 但 &lt;code&gt;opacity: 1&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;点音乐/显示/亮暗行&lt;/td&gt;&lt;td&gt;同上（子卡各自弹出），导航栏保持收拢&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;点 video 行&lt;/td&gt;&lt;td&gt;快捷卡&lt;strong&gt;保持打开&lt;/strong&gt;（无子卡），播放状态切换&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;子卡打开时点外部&lt;/td&gt;&lt;td&gt;子卡关，控件组 &lt;code&gt;opacity&lt;/code&gt; 回 0，导航栏仍收拢&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;快捷卡打开时点外部&lt;/td&gt;&lt;td&gt;快捷卡关&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;回顶&lt;/td&gt;&lt;td&gt;导航栏恢复完整、汉堡隐藏、面板内输入框回 &lt;code&gt;lg:hidden&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;移动端点汉堡&lt;/td&gt;&lt;td&gt;开导航链接菜单，快捷卡不开；再点关闭&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;八、跨批次踩坑合集&lt;a href=&quot;#八跨批次踩坑合集&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;改造过程中沉淀的九条硬结论，单独成节，任何一条都值得后来者抄走：&lt;/p&gt;&lt;p&gt;&lt;strong&gt;1. Tailwind 分层与 important 的层序反转。&lt;/strong&gt; 本项目 &lt;code&gt;navbar.css&lt;/code&gt; 等样式文件未分层，Tailwind utilities 在 &lt;code&gt;@layer utilities&lt;/code&gt; 里。普通规则未分层胜分层；但 utilities 里的 &lt;code&gt;!important&lt;/code&gt; 反转层序、必胜未分层的普通覆盖。想覆盖 &lt;code&gt;lg:hidden!&lt;/code&gt; 这类带 bang 的工具类，靠写更狠的 important 是死路——&lt;strong&gt;把 bang 从类名上删掉，改用一条普通规则&lt;/strong&gt;才是正解（汉堡显隐即是案例）。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;2. border-box 下的正圆公式。&lt;/strong&gt; 高度含边框时，宽度分量必须把边框计入再对半分，且内边距要写成 &lt;code&gt;calc(半径差 - var(--card-border-width))&lt;/code&gt;，滑块调粗边框圆才不破。rem 别写死 px：根字号在移动端是 14px。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;3. 绝对定位子卡的包含块是 &lt;code&gt;.navbar-seg&lt;/code&gt;，不是控件组。&lt;/strong&gt; &lt;code&gt;position: relative&lt;/code&gt; 挂在段上（且段有 &lt;code&gt;overflow-visible!&lt;/code&gt;），静态控件组的 &lt;code&gt;overflow: hidden&lt;/code&gt; / &lt;code&gt;max-width: 0&lt;/code&gt; 裁不到子卡——这是「收拢态弹子卡」方案能成立的几何前提。改结构时若把 &lt;code&gt;position: relative&lt;/code&gt; 挪走或给段加上 overflow，整套都会塌。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;4. &lt;code&gt;opacity&lt;/code&gt; 作用于整棵子树，无法被后代覆盖。&lt;/strong&gt; 「祖先把 opacity 设 0、子卡单独显示」在 CSS 里无解，只能拆规则（&lt;code&gt;:has&lt;/code&gt; 守卫）或挪 DOM。同理 &lt;code&gt;visibility&lt;/code&gt; 可以被后代重写、&lt;code&gt;opacity&lt;/code&gt; 不行——别选错属性。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;5. Vite dev 编译缓存会丢同轮多次写入。&lt;/strong&gt; 症状：磁盘正确、served 残缺。处置：重启 &lt;code&gt;pnpm dev&lt;/code&gt;；诊断：&lt;code&gt;curl&lt;/code&gt; 模块产物对比源码。线上构建从磁盘全新编译，不受影响。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;6. Playwright headless 的 &lt;code&gt;evaluate&lt;/code&gt; 期间不泵帧。&lt;/strong&gt; 长时间驻留在 &lt;code&gt;page.evaluate&lt;/code&gt; 的 async 代码里做 &lt;code&gt;setTimeout&lt;/code&gt; 采样，CSS 过渡的 &lt;code&gt;currentTime&lt;/code&gt; 会冻结在 0、&lt;code&gt;getComputedStyle&lt;/code&gt; 读到旧值——&lt;strong&gt;全部是探针自伤，不是页面 bug&lt;/strong&gt;。本次曾因此误判 &lt;code&gt;:has&lt;/code&gt; 守卫失效，排查了数轮。正确姿势：用 &lt;code&gt;page.waitForFunction&lt;/code&gt;（客户端 rAF 轮询会泵帧）等过渡完成，或读数前 &lt;code&gt;document.getAnimations().forEach(a =&amp;gt; a.finish())&lt;/code&gt; 强制取目标值，或穿插 &lt;code&gt;page.screenshot&lt;/code&gt; 触发渲染。判定标准：&lt;code&gt;getAnimations()&lt;/code&gt; 里 &lt;code&gt;playState: &quot;running&quot;&lt;/code&gt; 但 &lt;code&gt;currentTime&lt;/code&gt; 恒为 0，即冻结而非停滞。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;7. &lt;code&gt;floating-panel-utils&lt;/code&gt; 的分工（&lt;code&gt;src/utils/floating-panel-utils.ts&lt;/code&gt;）。&lt;/strong&gt; &lt;code&gt;data-floating-panel-trigger&lt;/code&gt; 只做 aria/inert/焦点同步，&lt;strong&gt;不绑定点击&lt;/strong&gt;（多面板共享同一 trigger 安全）；面板 class 变化由 MutationObserver 同步 &lt;code&gt;inert&lt;/code&gt; 并在开→关时派发 &lt;code&gt;floating-panel:close&lt;/code&gt;（不冒泡，要在 capture 阶段或直接挂面板上监听）；&lt;code&gt;setClickOutsideToClose(panel, ignores)&lt;/code&gt; 是&lt;strong&gt;无条件&lt;/strong&gt;的 document 点击监听——不检查面板是否打开，直接加 &lt;code&gt;float-panel-closed&lt;/code&gt;；忽略列表按 id 做 DOM 包含判断，与面板开闭状态无关。理解这三条才能推演「同事件内先开后关」类时序 bug。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;8. swup 4 事件。&lt;/strong&gt; 可靠的是 &lt;code&gt;astro:before-swap&lt;/code&gt; / &lt;code&gt;astro:page-load&lt;/code&gt;；&lt;code&gt;swup:contentReplaced&lt;/code&gt; 是死事件（&lt;code&gt;NavMenuPanel.astro&lt;/code&gt; 里有一处历史遗留监听，本次未动，改事件前先处理它）。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;9. 工具纪律。&lt;/strong&gt; &lt;code&gt;pnpm lint&lt;/code&gt;（Biome safe-fix）会波及无关文件，本系列全程只用只读的 &lt;code&gt;npx biome check &amp;lt;files&amp;gt;&lt;/code&gt;；Playwright 的 element screenshot 会被动画卡 stability，交互诊断用 &lt;code&gt;run_code_unsafe&lt;/code&gt; 里 &lt;code&gt;page.setViewportSize&lt;/code&gt; + &lt;code&gt;evaluate&lt;/code&gt;。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;九、如何复现&lt;a href=&quot;#九如何复现&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;路径 A：git 一次重放（推荐）&lt;a href=&quot;#路径-agit-一次重放推荐&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;clone&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://github.com/jmqsOOOtatoba/FireflyPriWeb.git&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FireflyPriWeb&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;checkout&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;020533ee&lt;/span&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;# 系列起点（最后一批之前的基线）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cherry-pick&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;74680341&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2a00a602&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;f6b3d060&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;b7cce682&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;04197b82&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;d7d3b62c&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 若目标仓库没有这几个对象，先从原仓库 fetch，或直接：&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# git diff 020533ee..d7d3b62c | git apply&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pnpm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pnpm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dev&lt;/span&gt;&lt;span&gt;                        &lt;/span&gt;&lt;span&gt;# http://localhost:4321&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;注：&lt;code&gt;b7cce682&lt;/code&gt; 依赖 &lt;code&gt;74680341&lt;/code&gt; 引入的 &lt;code&gt;--card-border-width&lt;/code&gt; 与 &lt;code&gt;f6b3d060&lt;/code&gt; 的 &lt;code&gt;--card-border-color&lt;/code&gt;，顺序不能乱；&lt;code&gt;04197b82&lt;/code&gt;（搜索图标化）依赖 &lt;code&gt;b7cce682&lt;/code&gt; 的三段结构；&lt;code&gt;d7d3b62c&lt;/code&gt;（卡顿修复）建立在 &lt;code&gt;b7cce682&lt;/code&gt; 的收拢动画之上，放最后。&lt;code&gt;9e8a7908&lt;/code&gt;（18 文章补 author 字段）与本系列无关，可跳过。&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;路径 B：按批次手工重做&lt;a href=&quot;#路径-b按批次手工重做&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;按本文第二、三、四、五、六、七节的「改动明细」顺序执行，每节末尾的复现要点即 checklist：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;边框体系（第二节）：&lt;code&gt;main.css&lt;/code&gt; 三规则 + &lt;code&gt;setting-utils.ts&lt;/code&gt; 四函数 + &lt;code&gt;Layout.astro&lt;/code&gt; 内联应用 + 显示设置滑块 + i18n；&lt;/li&gt;
&lt;li&gt;悬浮胶囊（第三节）：高度 4rem 三处联动 + 9999px 圆角 + wrapper padding/pointer-events 对 + 移动端留边；&lt;/li&gt;
&lt;li&gt;亮黑暗白（第四节）：&lt;code&gt;--card-border-color&lt;/code&gt; 变量 + 导航栏 &lt;code&gt;!important&lt;/code&gt; 接管 + 删两组防闪现规则；&lt;/li&gt;
&lt;li&gt;三段与收拢（第五节）：Navbar 结构拆分 + navbar.css/main.css retarget + scroll-utils delta 上提 + 正圆公式 + 汉堡显隐普通规则；&lt;/li&gt;
&lt;li&gt;移动菜单配置（第六节）：&lt;code&gt;mobileMenuStyle&lt;/code&gt; 配置 + NavMenuPanel 双分支 + click-outside 双注册；&lt;/li&gt;
&lt;li&gt;快捷面板（第七节）：quick panel markup + 分流 + 行点击处理器（含 &lt;code&gt;stopImmediatePropagation&lt;/code&gt;）+ &lt;code&gt;:has&lt;/code&gt; 守卫拆规则 + 搜索输入框放开（该补丁后被第十二节取代）+ 忽略名单 + i18n 两键；&lt;/li&gt;
&lt;li&gt;搜索图标化（第十二节）：删 PC 内嵌 &lt;code&gt;#search-bar&lt;/code&gt; + 图标按钮去 &lt;code&gt;lg:hidden!&lt;/code&gt; + 面板输入框去 &lt;code&gt;lg:hidden&lt;/code&gt; + 双关键词合并 + &lt;code&gt;togglePanel&lt;/code&gt; PC 自动聚焦 + 删收拢态补丁与死忽略项；&lt;/li&gt;
&lt;li&gt;收拢卡顿修复（第十三节）：汉堡 &lt;code&gt;display&lt;/code&gt; 切换改 width/visibility 连续过渡 + 控件组 &lt;code&gt;max-width&lt;/code&gt; 30rem→14rem 去死区 + 删淡入 keyframes + 亮暗图标交叉淡化。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;复现后验收清单&lt;a href=&quot;#复现后验收清单&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;npx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;biome&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;check&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;src/&lt;/span&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;# 0 错&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pnpm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type-check&lt;/span&gt;&lt;span&gt;                 &lt;/span&gt;&lt;span&gt;# 0 错&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pnpm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;check&lt;/span&gt;&lt;span&gt;                      &lt;/span&gt;&lt;span&gt;# 12 个存量错误（anime.astro 11 + vndb.astro 1），无新增&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;浏览器人工验收（顺序即第七节 7.8 矩阵）：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;PC 下滚 &amp;gt;80px → 左右段收成正圆、中段不动、汉堡出现；&lt;/li&gt;
&lt;li&gt;点汉堡 → 快捷卡弹开、链接菜单不开；&lt;/li&gt;
&lt;li&gt;逐行点搜索/音乐/显示/亮暗 → &lt;strong&gt;导航栏不展开&lt;/strong&gt;、对应子卡原位弹出、搜索卡光标已就位；&lt;/li&gt;
&lt;li&gt;点外部/Esc → 子卡关、仍收拢；再点外部 → 快捷卡关；&lt;/li&gt;
&lt;li&gt;video 行 → 快捷卡不关、背景播放切换；&lt;/li&gt;
&lt;li&gt;回顶 → 全部恢复、汉堡消失（含第十二节时：面板内输入框全端常驻，不再有 &lt;code&gt;lg:hidden&lt;/code&gt; 切换）；&lt;/li&gt;
&lt;li&gt;移动端（&amp;lt;1024px）→ 汉堡开链接菜单、与快捷卡互斥；&lt;/li&gt;
&lt;li&gt;显示设置调边框粗细滑块 → 卡片与导航栏边框实时变粗细，刷新后保持；切暗色 → 边框变白；&lt;/li&gt;
&lt;li&gt;含第十二节时：PC 正常态右段为纯图标排布，点搜索图标 → 卡片弹出、光标自动就位。&lt;/li&gt;
&lt;/ol&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;十、最终验证记录（2026-10-05）&lt;a href=&quot;#十最终验证记录2026-10-05&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;检查项&lt;/th&gt;&lt;th&gt;结果&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;npx biome check&lt;/code&gt;（改动文件）&lt;/td&gt;&lt;td&gt;通过，无 fix&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;pnpm type-check&lt;/code&gt;&lt;/td&gt;&lt;td&gt;通过&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;pnpm check&lt;/code&gt;&lt;/td&gt;&lt;td&gt;12 errors（= 基线，无新增）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Playwright 行为断言（PC 收拢/五行/外部点击/video/回顶/移动端分流）&lt;/td&gt;&lt;td&gt;全部通过&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;视觉验收&lt;/td&gt;&lt;td&gt;由人工完成，验收通过&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;自动化断言里踩到的探针陷阱见第八节第 6 条——&lt;strong&gt;先证明探针可信，再相信断言&lt;/strong&gt;。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;十一、提交记录&lt;a href=&quot;#十一提交记录&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;9e8a7908  更新18文章作者                      （与本系列无关）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;d7d3b62c  fix: 修复PC收拢动画卡顿，汉堡改width连续过渡，控件组max-width去死区&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;04197b82  feat: PC端搜索收成图标按钮，输入统一走搜索面板&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;b7cce682  feat: PC收拢态汉堡改开控件快捷面板，行点击原位弹出子控件卡&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;f6b3d060  feat: 导航栏接入卡片边框，边框色改为亮黑暗白跟随主题&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;2a00a602  feat: 导航栏改为悬浮胶囊样式并压扁高度&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;74680341  feat: 卡片边框默认 2px 纯黑，显示设置新增边框粗细滑块&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;020533ee  更新公告动态                        （系列起点）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;提交粒度说明：前三个是独立小批次、逐批验收逐批提交；&lt;code&gt;b7cce682&lt;/code&gt; 因三段结构、收拢动画、菜单配置、快捷面板四个特性在同一批文件里交织（&lt;code&gt;Navbar.astro&lt;/code&gt; 一个文件承载三者），拆分提交的手术成本高于收益，故合为一笔、用 commit body 分条说明。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;十二、追加批次：PC 搜索收成图标按钮（&lt;code&gt;04197b82&lt;/code&gt;，2026-10-05）&lt;a href=&quot;#十二追加批次pc-搜索收成图标按钮04197b822026-10-05&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;目标&lt;a href=&quot;#目标-4&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;PC 端右段的内嵌搜索输入框（&lt;code&gt;#search-bar&lt;/code&gt;，常态宽 10rem、聚焦展开到 15rem）太占地方，改为与移动端一致：&lt;strong&gt;只留一个搜索图标，点击弹出搜索卡片，输入统一在卡内完成&lt;/strong&gt;。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;改动明细（4 文件 +37/−72）&lt;a href=&quot;#改动明细4-文件-3772&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;1. &lt;code&gt;src/components/controls/Search.svelte&lt;/code&gt;&lt;/strong&gt;（主体）：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;删除 PC 内嵌输入框 &lt;code&gt;#search-bar&lt;/code&gt;（连带 &lt;code&gt;#search-input-desktop&lt;/code&gt;、&lt;code&gt;handleDesktopFocus&lt;/code&gt;、focus-return 钩子）；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;#search-switch&lt;/code&gt; 图标按钮去掉 &lt;code&gt;lg:hidden!&lt;/code&gt;——注意这是第八节第 1 条的正解：&lt;strong&gt;删掉 bang 让显示自然接管&lt;/strong&gt;，不是写更狠的 important 去压；&lt;/li&gt;
&lt;li&gt;面板内输入框 &lt;code&gt;#search-bar-inside&lt;/code&gt; 去掉 &lt;code&gt;lg:hidden&lt;/code&gt;，桌面/移动共用；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;keywordDesktop&lt;/code&gt;/&lt;code&gt;keywordMobile&lt;/code&gt; 双关键词状态合并为单个 &lt;code&gt;keyword&lt;/code&gt;（桌面输入框没了，双状态就是死代码），&lt;code&gt;setPanelVisibility&lt;/code&gt;/&lt;code&gt;search&lt;/code&gt; 同步去掉 &lt;code&gt;isDesktop&lt;/code&gt; 参数；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;data-floating-panel-trigger&lt;/code&gt; 从 &lt;code&gt;&quot;search-switch search-input-desktop&quot;&lt;/code&gt; 收窄为 &lt;code&gt;&quot;search-switch&quot;&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;togglePanel&lt;/code&gt; 打开面板时若视口 ≥1024px，&lt;code&gt;setTimeout(0)&lt;/code&gt; 把焦点送进卡内输入框（inert 由 MutationObserver 在微任务解除，焦点必须延到宏任务——与 7.2 同理）；移动端刻意&lt;strong&gt;不&lt;/strong&gt;自动聚焦，避免一点开就弹键盘。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;2. &lt;code&gt;src/styles/navbar.css&lt;/code&gt;&lt;/strong&gt;：删除 7.5 的收拢态补丁 &lt;code&gt;#navbar.navbar-collapsed #search-bar-inside { display: flex; }&lt;/code&gt;——输入框全端常驻后它成了死规则。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;3. &lt;code&gt;src/utils/layout-init.ts&lt;/code&gt;&lt;/strong&gt;：search-panel 的 click-outside 忽略名单删掉已不存在的 &lt;code&gt;&quot;search-bar&quot;&lt;/code&gt;。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;4. &lt;code&gt;src/components/layout/Navbar.astro&lt;/code&gt;&lt;/strong&gt;：只更新快捷面板搜索行的过时注释（不再有「收拢态放开 lg」这回事）。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;为什么面板落位不用改&lt;a href=&quot;#为什么面板落位不用改&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;搜索面板的定位规则（&lt;code&gt;navbar.css&lt;/code&gt; 的 &lt;code&gt;--navbar-panel-inset&lt;/code&gt;、&lt;code&gt;&amp;lt;768px&lt;/code&gt; 贴右段右缘）本来就同时服务桌面与移动——PC 旧方案里输入框虽在导航栏内，面板弹出位置也是同一套规则。图标化后直接复用，实测 1440px 视口面板 &lt;code&gt;left≈861 / right≈1341&lt;/code&gt;，正好挂在右胶囊下方不出屏。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;验证（2026-10-05，Playwright MCP 实测）&lt;a href=&quot;#验证2026-10-05playwright-mcp-实测&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;检查项&lt;/th&gt;&lt;th&gt;结果&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Biome / type-check / &lt;code&gt;pnpm check&lt;/code&gt;&lt;/td&gt;&lt;td&gt;通过 / 通过 / 12 errors（= 基线无新增）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;PC：&lt;code&gt;#search-bar&lt;/code&gt; 不复存在、图标可见、面板默认关&lt;/td&gt;&lt;td&gt;✓&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;PC 点图标 → 面板开、inert 解除、&lt;strong&gt;光标就位&lt;/strong&gt;、打字出 mock 结果&lt;/td&gt;&lt;td&gt;✓&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;PC 点外部 → 面板关&lt;/td&gt;&lt;td&gt;✓&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;下滚收拢 → 汉堡 → 快捷面板搜索行 → &lt;strong&gt;保持收拢&lt;/strong&gt;、子卡原位弹出、光标就位&lt;/td&gt;&lt;td&gt;✓&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;移动端回归：图标开卡、不自动聚焦、汉堡开链接菜单&lt;/td&gt;&lt;td&gt;✓&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;视觉截图：右胶囊纯图标排布、面板不出屏&lt;/td&gt;&lt;td&gt;✓&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;复现：检出 &lt;code&gt;b7cce682&lt;/code&gt; 后按上面四条改动明细重做即可，验收按表格逐行过。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;十三、追加批次：PC 收拢动画卡顿修复（&lt;code&gt;d7d3b62c&lt;/code&gt;，2026-10-05）&lt;a href=&quot;#十三追加批次pc-收拢动画卡顿修复d7d3b62c2026-10-05&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;现象与最初假设&lt;a href=&quot;#现象与最初假设&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;PC 端下滚触发右段收拢成圆时，肉眼可见一次「卡顿」；同一套动画在移动端却顺滑。最初的假设是：右端图标从亮暗切换变成汉堡、两图标形状不同，切换瞬间没有过渡动画，想用 anime.js 做 SVG 路径变形。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;假设被逐帧数据否了&lt;/strong&gt;——收拢过程根本不触发太阳/月亮互换（那只发生在切主题时），且 moon 与 hamburger 的 SVG path 不同构，anime.js（v3）做不了任意路径变形。真凶在布局层。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;诊断：逐帧实测抓到两条根因&lt;a href=&quot;#诊断逐帧实测抓到两条根因&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;探针：页面内 rAF 采样右段/控件组/汉堡三元素的 &lt;code&gt;getBoundingClientRect().width&lt;/code&gt;（先 &lt;code&gt;page.bringToFront()&lt;/code&gt;——窗口遮挡时 rAF 掉到 1fps，见第八节第 6 条同源教训）。&lt;/p&gt;&lt;p&gt;修复前曲线（收拢 class 挂上在 t=165ms）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;t=165   汉堡 display:none→flex 单帧出现：seg +44px     ←—— 反向弹出（PC 独有）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;t=165+  控件组 max-width 从 480px 才开始降，内容 220px&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;在被钳到 224px 之前宽度纹丝不动：冻结 ~60ms    ←—— 空转死区&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;t=236+  220px 全挤进剩余时长：单帧 -57、-26、-22px 猛缩 ←—— 卡顿感主体&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;汉堡 &lt;code&gt;display&lt;/code&gt; 单帧切换&lt;/strong&gt;：非收拢态 PC 汉堡 &lt;code&gt;display: none&lt;/code&gt; 不占位，收拢 class 一挂立刻 +2.75rem，右胶囊首帧向外弹。移动端汉堡常显、宽度不变——与「移动端没问题」的观察完全吻合。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;.navbar-right-controls&lt;/code&gt; 的 &lt;code&gt;max-width: 30rem&lt;/code&gt; 死区&lt;/strong&gt;：实际内容只有最多 5 个 2.75rem 控件 = 220px，过渡前段（480→224px）对用宽毫无影响，动画空转后余量猛缩，速度曲线呈「冻结→暴冲→拖尾」。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;改动明细（2 文件 +29/−20）&lt;a href=&quot;#改动明细2-文件-2920&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;1. &lt;code&gt;src/styles/navbar.css&lt;/code&gt;&lt;/strong&gt;（历史注释分「一改 important」「二改卡顿」两段保留）：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/* 汉堡：display:none → width/visibility 连续过渡 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.navbar-seg-right&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;#nav-menu-switch&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;overflow: &lt;/span&gt;&lt;span&gt;hidden&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;transition:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;width &lt;/span&gt;&lt;span&gt;&lt;span&gt;0.36&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cubic-bezier&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0.22&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.36&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;opacity &lt;/span&gt;&lt;span&gt;&lt;span&gt;0.25&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ease&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;visibility &lt;/span&gt;&lt;span&gt;&lt;span&gt;0.36&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;@media&lt;/span&gt;&lt;span&gt; (min-width: &lt;/span&gt;&lt;span&gt;&lt;span&gt;1024&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;#navbar&lt;/span&gt;&lt;span&gt;:not&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;.navbar-collapsed&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;.navbar-seg-right&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;#nav-menu-switch&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;width: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;opacity: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;visibility: &lt;/span&gt;&lt;span&gt;hidden&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pointer-events: &lt;/span&gt;&lt;span&gt;none&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;三个要点：&lt;strong&gt;未分层 &lt;code&gt;width: 0&lt;/code&gt; 自然压过 utilities 层的 &lt;code&gt;w-11&lt;/code&gt;&lt;/strong&gt;（层序反转只对 &lt;code&gt;!important&lt;/code&gt; 生效，这里不需要 bang）；&lt;strong&gt;&lt;code&gt;visibility&lt;/code&gt; 随行过渡&lt;/strong&gt;（宽度播完才真正隐藏），非收拢态不进 Tab 焦点与 a11y 树；&lt;strong&gt;正圆公式不受影响&lt;/strong&gt;——收拢态媒体查询不生效，内容宽仍是 &lt;code&gt;w-11&lt;/code&gt; = 2.75rem。&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.navbar-right-controls&lt;/code&gt; 的 &lt;code&gt;max-width: 30rem → 14rem&lt;/code&gt;（13.75rem 实际内容 + 0.25rem 余量），死区归零；注释里写明了原值的空转机制。&lt;/li&gt;
&lt;li&gt;删除 &lt;code&gt;navbar-collapse-fade&lt;/code&gt; 淡入动画与 keyframes：opacity 已由 transition 接管；该动画还会让移动端常显的汉堡每次收拢从 0 重闪一遍（历史遗留瑕疵，一并消灭）。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;2. &lt;code&gt;src/components/controls/LightDarkSwitch.svelte&lt;/code&gt;&lt;/strong&gt;：太阳/月亮两个图标容器加 &lt;code&gt;transition-opacity duration-300&lt;/code&gt;——这是「图标切换没过渡」假设里唯一真实存在的问题（切主题时 &lt;code&gt;class:opacity-0&lt;/code&gt; 硬切），与收拢无关，顺手修掉。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;修复后曲线（同探针复测）&lt;a href=&quot;#修复后曲线同探针复测&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;控件组 ctr: 220 → 171 → 107 → 63 → 36 → 20 → … → 0    单调平滑减速&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;汉堡   ham:   0 →  10 →  23 → 32 → 37 → 40 → … → 44   从零长出、同步交接&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;终态   seg = 64px 正圆；首帧无反向弹出、无冻结&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;验证（2026-10-05，Playwright MCP 实测）&lt;a href=&quot;#验证2026-10-05playwright-mcp-实测-1&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;检查项&lt;/th&gt;&lt;th&gt;结果&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Biome / type-check / &lt;code&gt;pnpm check&lt;/code&gt;&lt;/td&gt;&lt;td&gt;通过 / 通过 / 12 errors（= 基线无新增）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;收拢首帧无 +44px 跳变、无冻结&lt;/td&gt;&lt;td&gt;✓（逐帧差分只剩单调递减的正常减速帧）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;展开回顶：汉堡 width 0 + visibility hidden、右段回 236px&lt;/td&gt;&lt;td&gt;✓&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;移动端：汉堡常显（38.5px = 14px 根 × 2.75rem）、收拢不闪、点击开菜单&lt;/td&gt;&lt;td&gt;✓&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;视觉验收&lt;/td&gt;&lt;td&gt;由人工完成，验收通过&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;复现：检出 &lt;code&gt;04197b82&lt;/code&gt; 后按改动明细重做。&lt;strong&gt;rAF 逐帧采样 + &lt;code&gt;bringToFront&lt;/code&gt; 这套诊断法可复用于任何「肉眼卡顿但说不清」的动画排查——先量化，再动手。&lt;/strong&gt;&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;结语与未尽事项&lt;a href=&quot;#结语与未尽事项&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;做完的：边框体系、悬浮胶囊、三段收拢、快捷面板与原位弹卡、PC 搜索图标化、收拢动画卡顿修复，全部合入主干并上线（推送即触发 Cloudflare 构建）。&lt;/p&gt;&lt;p&gt;未尽与备忘：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;液态玻璃原型已回滚&lt;/strong&gt;，git 无痕，重做需从零；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;swup:contentReplaced&lt;/code&gt; 死事件&lt;/strong&gt;遗留一处（&lt;code&gt;NavMenuPanel.astro&lt;/code&gt;），本次刻意未动；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;流程教训&lt;/strong&gt;：批次验收通过前不提交推送、一次性授权不跨批次沿用——最后一笔 &lt;code&gt;b7cce682&lt;/code&gt; 是在验收确认前推的，虽未返工，但顺序不对，下不为例。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;对后来者最重要的一句话：&lt;strong&gt;这次所有「看起来是页面 bug」的问题里，只有两处是真 bug（同事件 click-outside 秒关、合成 click 误关快捷卡），其余怪现象全部来自探针与 dev 缓存&lt;/strong&gt;——先怀疑测量工具，再怀疑自己的代码。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;</content:encoded></item><item><title>博客性能优化全记录：Lighthouse 报告拆解到帧率根因排查（A–F 六项实战）</title><link>https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-18/</link><guid isPermaLink="true">https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-18/</guid><description>从两份 Lighthouse 对比报告出发，完整记录六项性能优化的排查方法、根因定位与修复代码：字体子集化、封面图尺寸 API、CSS 内联、强制重排归零、以及&quot;只有 Chrome 掉帧&quot;的浏览器设置级根因，附全部可复现命令与工具脚本</description><pubDate>Sun, 04 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h1&gt;博客性能优化全记录：Lighthouse 报告拆解到帧率根因排查（A–F 六项实战）&lt;a href=&quot;#博客性能优化全记录lighthouse-报告拆解到帧率根因排查af-六项实战&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;2026-10-03，用 Chrome / Edge 无痕模式各跑了一份本博客的 Lighthouse 报告。两份报告揭出一批共性问题，外加一个诡异现象：&lt;strong&gt;同一个站，Chrome 滚动掉帧、Edge 完全正常&lt;/strong&gt;（两者同为 Chromium 154）。随后完成了 A–F 六项优化/排查，最终 Lighthouse 的强制重排审计从稳定 0 分变为多次满分，Chrome 掉帧也以「浏览器设置」定案。&lt;/p&gt;&lt;p&gt;本文是完整复盘：&lt;strong&gt;每项改了什么、为什么这么改、怎么验证的、踩了哪些坑&lt;/strong&gt;。既是给未来维护者的存档，也希望能成为一份可照抄的性能排查教程。&lt;/p&gt;&lt;hr /&gt;&lt;section&gt;&lt;h2&gt;〇、初始状态（两份报告的关键数字）&lt;a href=&quot;#〇初始状态两份报告的关键数字&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;指标&lt;/th&gt;&lt;th&gt;Chrome&lt;/th&gt;&lt;th&gt;Edge&lt;/th&gt;&lt;th&gt;说明&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;性能分&lt;/td&gt;&lt;td&gt;0.71&lt;/td&gt;&lt;td&gt;0.70&lt;/td&gt;&lt;td&gt;两边一致&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;LCP&lt;/td&gt;&lt;td&gt;&lt;strong&gt;4.6s&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;5.1s&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;最大失分项&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;FCP / TBT / CLS&lt;/td&gt;&lt;td&gt;1.5s / 0ms / 0.003&lt;/td&gt;&lt;td&gt;1.6s / 0ms / 0.007&lt;/td&gt;&lt;td&gt;主线程本身很干净&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;总传输量&lt;/td&gt;&lt;td&gt;&lt;strong&gt;5,349 KiB&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;同&lt;/td&gt;&lt;td&gt;严重超标&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;渲染阻塞 CSS&lt;/td&gt;&lt;td&gt;估省 220ms&lt;/td&gt;&lt;td&gt;估省 250ms&lt;/td&gt;&lt;td&gt;3 个 stylesheet link&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;强制重排&lt;/td&gt;&lt;td&gt;~92ms（score 0）&lt;/td&gt;&lt;td&gt;~200ms&lt;/td&gt;&lt;td&gt;有内联脚本热点&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;图片可省&lt;/td&gt;&lt;td&gt;~1,160 KiB&lt;/td&gt;&lt;td&gt;~1,163 KiB&lt;/td&gt;&lt;td&gt;封面原图直出&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;问题清单（即本文的 A–F）：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A&lt;/strong&gt; 字体：一个 3.1MB 的 TTF 在预加载&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;B&lt;/strong&gt; 封面图：330KB 原图 JPEG 直出，尺寸超显示需求 3 倍&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;C&lt;/strong&gt; 三个第三方源没有 preconnect&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;D&lt;/strong&gt; 3 个渲染阻塞的 stylesheet&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;E&lt;/strong&gt; 加载期强制重排（forced reflow）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;F&lt;/strong&gt; 只有 Chrome 掉帧、Edge 不掉帧&lt;/li&gt;
&lt;/ul&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;A、字体子集化：3.1MB TTF → 212KB WOFF2&lt;a href=&quot;#a字体子集化31mb-ttf--212kb-woff2&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;现象&lt;a href=&quot;#现象&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Lighthouse 列出的最大单体资源：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/_astro/fonts/cc84406608407d93.ttf&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;resourceSize: 3,293,520 B（3.1MB）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;transferSize: 约 2,088,846 B（压缩后仍有 2MB）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;且带 rel=&quot;preload&quot; 高优先级，与首屏图片抢带宽&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;根因&lt;a href=&quot;#根因&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;项目里&lt;strong&gt;本来就有一整套字体子集化流水线&lt;/strong&gt;（&lt;code&gt;scripts/subset-fonts.ts&lt;/code&gt;，&lt;code&gt;pnpm build&lt;/code&gt; 会执行），它扫描 &lt;code&gt;dist/&lt;/code&gt; 全部 HTML 收集实际用到的字符，生成 woff2 子集并替换引用、删除原文件。但配置里只登记了一个装饰字体：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;src/config/fontConfig.ts&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;subsetFonts&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;--font-greatvibes&quot;&lt;/span&gt;&lt;span&gt;: { &lt;/span&gt;&lt;span&gt;extraChars&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt; },  &lt;/span&gt;&lt;span&gt;// 只有它&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 而全站正文用的是（fontConfig.selected）：&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;selected&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;--font-hanyi-wenhei&quot;&lt;/span&gt;&lt;span&gt;],          &lt;/span&gt;&lt;span&gt;// ← 没登记，直接漏网&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;子集脚本会跳过「未在 selected/区域字体中引用」的项，于是每次构建都打印 skip，原始 TTF 原样进了部署包。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;修复&lt;a href=&quot;#修复&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;code&gt;src/config/fontConfig.ts&lt;/code&gt; 增加一行：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;subsetFonts&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;--font-greatvibes&quot;&lt;/span&gt;&lt;span&gt;: { &lt;/span&gt;&lt;span&gt;extraChars&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// 全站正文主字体（selected 引用），不配子集化会原样输出 3.1MB TTF&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;--font-hanyi-wenhei&quot;&lt;/span&gt;&lt;span&gt;: { &lt;/span&gt;&lt;span&gt;extraChars&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;无需改脚本——&lt;code&gt;pnpm build&lt;/code&gt; 链路里已有 &lt;code&gt;astro build &amp;amp;&amp;amp; npx tsx scripts/subset-fonts.ts&lt;/code&gt;。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;验证与注意点&lt;a href=&quot;#验证与注意点&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;构建日志出现 &lt;code&gt;Generating subset ... 211.7 KB, original: 3216.3 KB, saved 93.4%&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dist/_astro/fonts/&lt;/code&gt; 下生成 &lt;code&gt;e3c41cafdc6bb33b.woff2&lt;/code&gt;（211.7KB），原始 TTF 被脚本删除&lt;/li&gt;
&lt;li&gt;全站 53 个页面的 &lt;code&gt;@font-face&lt;/code&gt; 与 &lt;code&gt;rel=preload&lt;/code&gt; 均已替换为 &lt;code&gt;type=&quot;font/woff2&quot;&lt;/code&gt;，HTML 中 &lt;code&gt;.ttf&lt;/code&gt; 引用为 0&lt;/li&gt;
&lt;li&gt;浏览器实测：&lt;code&gt;document.fonts&lt;/code&gt; 加载成功、中文渲染无豆腐块&lt;/li&gt;
&lt;/ul&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;已知取舍&lt;/strong&gt;：子集只包含构建时 HTML 里可见的字符。密码/加密文章解密后的字符不在其中，这些页面正文会回退到 &lt;code&gt;fallbacks&lt;/code&gt;（PingFang/雅黑）。介意可在 &lt;code&gt;extraChars&lt;/code&gt; 手动补字。&lt;/p&gt;&lt;/blockquote&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;C、preconnect：三个第三方源提前建连（顺手先做了）&lt;a href=&quot;#cpreconnect三个第三方源提前建连顺手先做了&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;现象&lt;a href=&quot;#现象-1&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;报告写明「未预连接任何源」：图床 &lt;code&gt;tu.mstzuomu.space&lt;/code&gt;、统计 &lt;code&gt;cloud.umami.is&lt;/code&gt;、&lt;code&gt;static.cloudflareinsights.com&lt;/code&gt; 都是 HTML 解析到一半才发现的，连接建立偏晚。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;修复&lt;a href=&quot;#修复-1&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;code&gt;src/layouts/Layout.astro&lt;/code&gt; 的 &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; 最顶部（&lt;code&gt;&amp;lt;meta charset&amp;gt;&lt;/code&gt; 之后）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;!-- 提前建立第三方连接：图床图片、umami 统计、Cloudflare beacon 都在 HTML 解析中途才被发现 --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rel&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;preconnect&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;https://tu.mstzuomu.space&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rel&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;preconnect&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;https://static.cloudflareinsights.com&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;{analyticsConfig?.umamiAnalytics?.websiteId &amp;amp;&amp;amp; (&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rel&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;preconnect&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;https://cloud.umami.is&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;)}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;注意：&lt;strong&gt;这三行会改变 HTML 的行号&lt;/strong&gt;——后文 E 项定位 Lighthouse 坐标时会看到所有行号整体偏移，此为预期内现象。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;B、封面图：接入自建图床的尺寸处理 API&lt;a href=&quot;#b封面图接入自建图床的尺寸处理-api&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;现象与根因&lt;a href=&quot;#现象与根因&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;封面走远程 &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; 直出：&lt;code&gt;src/components/common/CoverImage.astro&lt;/code&gt; 对所有 &lt;code&gt;http(s)&lt;/code&gt; 图片输出裸 &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;，不经过 Astro 的优化管道。单张封面 &lt;strong&gt;2169×569 JPEG 约 330KB&lt;/strong&gt;，实际显示只有 1350×300——格式（该转 WebP）和尺寸（超 3 倍）双重浪费，Lighthouse 估算 5 张可省 1.16MB。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;排查：图床支持什么转换？&lt;a href=&quot;#排查图床支持什么转换&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;图床是自建的 &lt;strong&gt;CloudFlare-ImgBed&lt;/strong&gt;（CF Workers + Telegram 存储）。探测过程：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 1. 直接试尺寸参数 → 403&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;curl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;https://tu.mstzuomu.space/file/文章/xxx.jpg?width=1656&amp;amp;fallback=original&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# → 403 &quot;Image resizing is disabled&quot;   # 功能存在但默认关闭！&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 2. 查官方文档（cfbed.sanyue.de/api/file.html）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;#    支持 width/height/fit/fallback 参数；JPEG/PNG/WebP 源 Worker 部署可处理；&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;#    开启路径：dashboard → 系统设置 → 安全设置 → 访问管理 → 图片尺寸处理&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;#    允许尺寸：留空 = 任意合法值&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 3. 后台开启后复测&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;width&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;1656&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;→&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;141KB&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;（原&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;330KB，-57%）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;width&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;828&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;→&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;48.5KB&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;（-85%）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;format&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;webp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;→&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;被忽略（该版本只改尺寸不转格式）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;博客端实现（4 个文件）&lt;a href=&quot;#博客端实现4-个文件&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;1. 配置开关&lt;/strong&gt;（&lt;code&gt;src/config/siteConfig.ts&lt;/code&gt;）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;imageOptimization&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// ...原有配置&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// 支持尺寸处理的远程图床域名（后台需开启&quot;图片尺寸处理&quot;）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;resizeHosts&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;&quot;tu.mstzuomu.space&quot;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;2. 工具函数&lt;/strong&gt;（&lt;code&gt;src/utils/image-utils.ts&lt;/code&gt;）：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;RESIZE_WIDTHS&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;828&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1656&lt;/span&gt;&lt;span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;canResizeRemote&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;src&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;^&lt;/span&gt;&lt;span&gt;&lt;span&gt;https&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;\/\/&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;test&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;src&lt;/span&gt;&lt;span&gt;)) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hosts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;siteConfig&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;imageOptimization&lt;/span&gt;&lt;span&gt;?.&lt;/span&gt;&lt;span&gt;resizeHosts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; [];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;hosts&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;try&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hosts&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;includes&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;URL&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;src&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;/span&gt;&lt;span&gt;hostname&lt;/span&gt;&lt;span&gt;); }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;buildResizeUrl&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;src&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;width&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;try&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;u&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;URL&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;src&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;u&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;searchParams&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;width&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;width&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;u&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;searchParams&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;fallback&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;original&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;u&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;toString&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;src&lt;/span&gt;&lt;span&gt;; }&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;3. 渲染&lt;/strong&gt;（&lt;code&gt;CoverImage.astro&lt;/code&gt; 远程分支）：白名单域名输出 srcset 双档 + 真实显示宽度的 sizes + 回退数据属性：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const resizeSizes = preview&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;? &quot;(max-width: 768px) 100vw, 770px&quot;    // 列表卡桌面约 680-770px&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;: &quot;(max-width: 768px) 100vw, 100vw&quot;;   // 详情页封面全宽&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;img&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;remoteImgSrc&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;                          /* 1656 &lt;/span&gt;&lt;span&gt;档&lt;/span&gt;&lt;span&gt; */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;srcset&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;resizeSrcset&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;                       /* &quot;…828 828&lt;/span&gt;&lt;span&gt;w&lt;/span&gt;&lt;span&gt;, …1656 1656&lt;/span&gt;&lt;span&gt;w&lt;/span&gt;&lt;span&gt;&quot; */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;sizes&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;&lt;span&gt;useResize&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;resizeSizes&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;undefined&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;data-resize-fallback&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;&lt;span&gt;useResize&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;remoteSrc&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;undefined&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;&lt;code&gt;sizes&lt;/code&gt; 有个坑：本地图沿用的旧值写死了桌面 &lt;code&gt;320px&lt;/code&gt;，单档产物无影响，但双档 srcset 下 &lt;strong&gt;Retina 屏会错选 828w 导致发糊&lt;/strong&gt;——所以尺寸变体必须用真实显示宽度（770px）单独给一套。&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;strong&gt;4. 保险丝&lt;/strong&gt;（&lt;code&gt;CoverImage.astro&lt;/code&gt; 脚本的 &lt;code&gt;onError&lt;/code&gt;）：尺寸版加载失败（如图床后台开关被关）时一次性回退无参数原图：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;resizeFallback&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;img&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;dataset&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;resizeFallback&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;resizeFallback&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;&lt;span&gt;img&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;dataset&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;resizeRetried&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;img&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;dataset&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;resizeRetried&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;true&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;img&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;load&apos;&lt;/span&gt;&lt;span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;hideLoading&lt;/span&gt;&lt;span&gt;, { &lt;/span&gt;&lt;/span&gt;&lt;span&gt;once&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt; });&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;img&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;error&apos;&lt;/span&gt;&lt;span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;onError&lt;/span&gt;&lt;span&gt;, { &lt;/span&gt;&lt;/span&gt;&lt;span&gt;once&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt; });&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;img&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;removeAttribute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;srcset&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;img&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;src&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;resizeFallback&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;验证&lt;a href=&quot;#验证&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;浏览器 Network：封面请求全部带 &lt;code&gt;?width=828&lt;/code&gt;（DPR1 选小档），加载成功&lt;/li&gt;
&lt;li&gt;保险丝实测：手动把 src 改成 404 → 自动回退原图 → 加载成功、无错误态、不循环&lt;/li&gt;
&lt;li&gt;mi-fds 域名的 2 张封面不在白名单，保持原样输出（预期行为）&lt;/li&gt;
&lt;/ul&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;D、CSS 内联：3 个渲染阻塞 link 归零&lt;a href=&quot;#dcss-内联3-个渲染阻塞-link-归零&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;现象&lt;a href=&quot;#现象-2&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;首页 head 里 3 个阻塞请求：&lt;code&gt;Layout.css&lt;/code&gt;（原始 196KB / 传输 28.7KB）、&lt;code&gt;MainGridLayout.css&lt;/code&gt;、全局 CSS，Lighthouse 估算拖慢首屏 220–250ms——浏览器必须等它们下载解析完才画第一笔。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;修复&lt;a href=&quot;#修复-2&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;code&gt;astro.config.mjs&lt;/code&gt; &lt;strong&gt;顶层&lt;/strong&gt;（Astro 配置，不是 &lt;code&gt;vite.build&lt;/code&gt;！）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// Astro 构建行为：全部 CSS 内联进 HTML（非 vite.build）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;build&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;inlineStylesheets&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;always&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;踩坑记录&lt;/strong&gt;：我第一次把这个键写进了 &lt;code&gt;vite: { build: { ... } }&lt;/code&gt; 块里——那是 Vite 的配置，不认识 &lt;code&gt;inlineStylesheets&lt;/code&gt;，&lt;strong&gt;静默无效&lt;/strong&gt;，白构建一轮。判断依据是检查产物里 &lt;code&gt;&amp;lt;link rel=&quot;stylesheet&quot;&amp;gt;&lt;/code&gt; 是否归零。&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;前置安全检查&lt;a href=&quot;#前置安全检查&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;内联前必须确认三个 CSS 没有 &lt;code&gt;url()&lt;/code&gt; 引用（否则内联到嵌套路径页面会相对路径断裂）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 扫描 dist/_astro/*.css 的 url( 引用&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 结果：三个阻塞 CSS 均为 0 处 → 内联无风险&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# （带 url() 的 KaTeX/fancybox 是按需加载的非阻塞样式，不在范围）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;验证与取舍&lt;a href=&quot;#验证与取舍&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;首页 &lt;code&gt;&amp;lt;link rel=&quot;stylesheet&quot;&amp;gt;&lt;/code&gt; 数量：&lt;strong&gt;3 → 0&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;HTML 原始体积 370KB → 608KB（+237KB ≈ 三者之和；gzip 后约 +38KB）&lt;/li&gt;
&lt;li&gt;文章页直连、swup 切页、视觉截图均正常&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;取舍&lt;/strong&gt;：CSS 不再单独缓存，每多看一页 HTML 多带约 38KB——换来首屏少 3 个请求，对博客访问形态是划算的&lt;/li&gt;
&lt;li&gt;残余：文章页 body 尾部还有个 8.9KB 的 &lt;code&gt;twikoo-custom.css&lt;/code&gt;（评论区样式，解析位置在正文后，不挡首屏）和 fancybox 两个运行时注入的 link，都不在阻塞路径上&lt;/li&gt;
&lt;/ul&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;E、强制重排归零（本文最重的一节）&lt;a href=&quot;#e强制重排归零本文最重的一节&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;方法论：怎么抓到强制重排的真凶&lt;a href=&quot;#方法论怎么抓到强制重排的真凶&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;强制重排 = JS 在脚本执行中读取几何/样式，浏览器被迫当场同步全量结算。&lt;strong&gt;坐标解读是最大的坑&lt;/strong&gt;，先立规矩：&lt;/p&gt;&lt;p&gt;&lt;strong&gt;1. 本地跑 Lighthouse 单审计&lt;/strong&gt;（线上报告的行号会随每次构建漂移，必须在当前产物上重测）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 用 Edge 当 Lighthouse 的浏览器（本机没装独立 Chrome 时）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;$env:CHROME_PATH&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;npx &lt;/span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt;yes lighthouse &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;http://127.0.0.1:8931/&quot;&lt;/span&gt;&lt;span&gt; `&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt;only&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;audits&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;forced&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;reflow&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;insight &lt;/span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt;output&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;json &lt;/span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt;output&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;lh.json `&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt;chrome&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;flags&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;--headless=new&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;2. 以 CDP Tracing 全栈为准&lt;/strong&gt;（Lighthouse 只给聚合后的 line/column，且行号 0-based、列号时而脚本内相对——单独解坐标会绕死）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// Playwright run_code：抓带堆栈的 Layout/UpdateLayoutTree 事件&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cats&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;disabled-by-default-devtools.timeline,&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;disabled-by-default-devtools.timeline.stack,&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;disabled-by-default-devtools.timeline.frame&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// Tracing.start(ReportEvents) → goto + 等待 → Tracing.end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 过滤 name 为 Layout/UpdateLayoutTree 且有 stackTrace 的事件，&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 按 dur 排序；stack[0] 是最内层帧 = 真正读取者&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;3. 模拟 Lighthouse 的测量条件&lt;/strong&gt;（否则数字对不上）：Lighthouse 默认&lt;strong&gt;移动视口 + 4 倍 CPU 降速&lt;/strong&gt;——&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;client&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;send&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;Emulation.setDeviceMetricsOverride&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;width&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;412&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;height&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;823&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;deviceScaleFactor&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2.625&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;mobile&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt; });&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;client&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;send&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;Emulation.setCPUThrottlingRate&apos;&lt;/span&gt;&lt;span&gt;, { &lt;/span&gt;&lt;span&gt;rate&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt; });&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;六个根因与对应修复&lt;a href=&quot;#六个根因与对应修复&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;section&gt;&lt;h4&gt;E1. 解析期读 &lt;code&gt;window.innerWidth&lt;/code&gt;（移动端最大单点）&lt;a href=&quot;#e1-解析期读-windowinnerwidth移动端最大单点&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;读视口宽度需要先确定滚动条状态，&lt;strong&gt;移动模拟下会触发首次全量布局&lt;/strong&gt;。三个热点全是这一个病：&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;位置&lt;/th&gt;&lt;th&gt;代价（移动+4x）&lt;/th&gt;&lt;th&gt;修复&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;PostPage 立即执行脚本&lt;/td&gt;&lt;td&gt;~84ms&lt;/td&gt;&lt;td&gt;移动/桌面默认布局本就相同，宽度分支冗余，只按 &lt;code&gt;localStorage&lt;/code&gt; 偏好恢复&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;壁纸 &lt;code&gt;sync()&lt;/code&gt;&lt;/td&gt;&lt;td&gt;~26ms&lt;/td&gt;&lt;td&gt;&lt;code&gt;window.matchMedia(&apos;(min-width: 1024px)&apos;).matches&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ScrollDownIndicator &lt;code&gt;updatePosition&lt;/code&gt;&lt;/td&gt;&lt;td&gt;~31ms&lt;/td&gt;&lt;td&gt;同上改 matchMedia&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;E2. &lt;code&gt;window.pageYOffset || document.documentElement.scrollTop&lt;/code&gt; 老式写法&lt;a href=&quot;#e2-windowpageyoffset--documentdocumentelementscrolltop-老式写法&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;加载时 &lt;code&gt;pageYOffset&lt;/code&gt; 为 0（假值）→ 短路求值走到 &lt;code&gt;documentElement.scrollTop&lt;/code&gt; → 几何读取强制布局。&lt;strong&gt;5 处统一改 &lt;code&gt;window.scrollY&lt;/code&gt;&lt;/strong&gt;（滚动位置干净时读它不强制）：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ScrollDownIndicator.astro&lt;/code&gt; / &lt;code&gt;Navbar.astro&lt;/code&gt; / &lt;code&gt;BackToTop.astro&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fullscreen-wallpaper-utils.ts&lt;/code&gt;（×2）&lt;/li&gt;
&lt;li&gt;另外 &lt;code&gt;scroll-utils.ts&lt;/code&gt; 和 &lt;code&gt;grid-layout-utils.ts&lt;/code&gt; 里还有不带 &lt;code&gt;||&lt;/code&gt; 的直接读法，一并处理&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;E3. &lt;code&gt;document.fonts.ready&lt;/code&gt; getter 本身就强制样式结算&lt;a href=&quot;#e3-documentfontsready-getter-本身就强制样式结算&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;移动端单次可达 300ms+。注意 &lt;strong&gt;&lt;code&gt;typeof document.fonts.ready&lt;/code&gt; 这个 guard 本身就访问了 getter&lt;/strong&gt;——把判断整体挪进首绘后：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;FontSetup.astro&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;document&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;fonts&lt;/span&gt;&lt;span&gt;) {                    &lt;/span&gt;&lt;span&gt;// 只判断对象存在（不触发）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;afterFirstPaint&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;document&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;fonts&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;ready&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;)        &lt;/span&gt;&lt;span&gt;// getter 访问推迟到 FCP 后&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;E4. PostPage 的 transition 开关体操&lt;a href=&quot;#e4-postpage-的-transition-开关体操&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;原代码注释自己写着「&lt;strong&gt;强制重排后恢复过渡动画&lt;/strong&gt;」——&lt;code&gt;transition:none → 换类 → container.offsetHeight（故意强制）→ 恢复&lt;/code&gt;。&lt;/p&gt;&lt;p&gt;关键认知：&lt;strong&gt;脚本在解析期执行，元素还没有计算样式，transition 根本不可能触发&lt;/strong&gt;——体操在初始加载场景纯属多余。改为纯类名写入；即便补上 rAF 恢复过渡的版本，Lighthouse 仍会把异步归因的帧级工作记账 ~84ms（实测两版都 ~84）。首绘后的切页注入场景由 &lt;code&gt;performance.getEntriesByType(&apos;paint&apos;)&lt;/code&gt; 检测后跳过体操。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;E5. 读写交错（改了 DOM 又立刻查询）&lt;a href=&quot;#e5-读写交错改了-dom-又立刻查询&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;壁纸 &lt;code&gt;sync()&lt;/code&gt;&lt;/strong&gt;：拆两阶段——读阶段用 &lt;code&gt;getElementsByClassName&lt;/code&gt;/&lt;code&gt;getElementsByTagName&lt;/code&gt;（纯 DOM 查询，不触发样式结算）收集全部 slot+tpl，写阶段只插入；&lt;code&gt;materialize&lt;/code&gt; 改为插入前从 &lt;code&gt;frag&lt;/code&gt; 取占位层，删掉插入后的 &lt;code&gt;slot.querySelector&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;歌词高亮 &lt;code&gt;updateLrcHighlight&lt;/code&gt;&lt;/strong&gt;：先缓存 &lt;code&gt;offsetTop/clientHeight&lt;/code&gt; 几何值，再改 class，滚动用缓存值&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;封面动画重启的 &lt;code&gt;void ui.cover.offsetWidth&lt;/code&gt;&lt;/strong&gt;、&lt;strong&gt;&lt;code&gt;PRIMARY_COLOR&lt;/code&gt; 顶层 &lt;code&gt;getComputedStyle&lt;/code&gt;&lt;/strong&gt;：都挪到首绘门闩后&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;E6. 新工具 &lt;code&gt;src/utils/after-first-paint.ts&lt;/code&gt;（本轮核心基建）&lt;a href=&quot;#e6-新工具-srcutilsafter-first-paintts本轮核心基建&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 设计要点（缺一不可，都有实测依据）：&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 1. 轮询 performance.getEntriesByType(&apos;paint&apos;) 中的 first-contentful-paint，&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;//    而不是双 rAF——弱性能设备首帧可能被丢弃，rAF 链会在真实绘制前跑完；&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 2. 每帧只放行一个回调（串行队列）——所有回调挤同一帧会互相弄脏：&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;//    前一个的写入让后一个的读取再次强制结算（实测 Waves 读前序回调的脏样式 29-31ms）；&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 3. 超时兜底 120 帧，防 paint 条目异常时回调永不执行。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;afterFirstPaint&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;cb&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;/* 见仓库源码 */&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;加载期读取者全部改走它：滚动初始化、分类栏、波浪、壁纸视差/模糊、侧栏几何缓存、网格列数、fonts.ready、音乐封面动画重启……（共 14 个文件，见 commit &lt;code&gt;d6ceae0a&lt;/code&gt;）&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;排查过程中的三个大坑（后人必读）&lt;a href=&quot;#排查过程中的三个大坑后人必读&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;MCP 浏览器窗口被遮挡 = 测量全部失真&lt;/strong&gt;：遮挡窗口的 Chromium 会&lt;strong&gt;暂停绘制 + rAF 压到 2fps&lt;/strong&gt;，表现为 paint 条目永远为空、所有门闩「不触发」、滚动功能「全坏」。&lt;code&gt;page.bringToFront()&lt;/code&gt; 一秒复活（101fps + paint 条目齐全）。&lt;strong&gt;做帧率/绘制相关测量前必须先置前窗口。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;先做基线 A/B 再怀疑自己&lt;/strong&gt;：修到中途滚动类效果全灭，用 &lt;code&gt;git stash&lt;/code&gt; 丢弃改动构建基线版同环境实测——&lt;strong&gt;基线一模一样&lt;/strong&gt;，证明非本次回归（是环境问题），避免了整段回滚。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PowerShell 引号吞噬&lt;/strong&gt;：复杂内联脚本（python &lt;code&gt;-c&lt;/code&gt;、正则带引号）会被啃得面目全非，一律落盘成临时脚本文件执行。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;结果&lt;a href=&quot;#结果&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;基线&lt;/th&gt;&lt;th&gt;最终&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Lighthouse forced-reflow&lt;/td&gt;&lt;td&gt;每次 score 0，~121ms&lt;/td&gt;&lt;td&gt;&lt;strong&gt;多次 score 1 / 0.0ms&lt;/strong&gt;，最差 18.6ms&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;移动+4x CDP 实测&lt;/td&gt;&lt;td&gt;381–481ms&lt;/td&gt;&lt;td&gt;主要热点归零&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;残余&lt;/td&gt;&lt;td&gt;—&lt;/td&gt;&lt;td&gt;仅 Astro 水合运行时（client.js，8–16ms，库代码）&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;改动：14 文件修改 + 1 新文件，+280/−121，commit &lt;code&gt;d6ceae0a&lt;/code&gt;。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;F、只有 Chrome 掉帧？——浏览器设置级根因&lt;a href=&quot;#f只有-chrome-掉帧浏览器设置级根因&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;症状与排查设计&lt;a href=&quot;#症状与排查设计&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;同机、同站、同引擎（Chromium 154），&lt;strong&gt;只有真 Chrome 掉帧，Edge 正常&lt;/strong&gt;。这个「同引擎不同表现」本身就是最强线索：嫌疑必然在&lt;strong&gt;两者各自的运行环境&lt;/strong&gt;，而非站点代码。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;第一轮：站点侧压力矩阵&lt;/strong&gt;（本机 Chromium，前台窗口）&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;组合&lt;/th&gt;&lt;th&gt;结果&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;1440×900 DPR1 滚动&lt;/td&gt;&lt;td&gt;99.9 FPS，0 掉帧&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;1728×1080 DPR1.5 + 文章页&lt;/td&gt;&lt;td&gt;99.9 FPS，0 掉帧&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;+ CPU 4 倍降速 + 全屏模糊渐变&lt;/td&gt;&lt;td&gt;99.9 FPS，0 掉帧&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;站点在默认设置的 Chrome 系浏览器下&lt;strong&gt;压不出掉帧&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;第二轮：真 Chrome vs 真 Edge 同机对打&lt;/strong&gt;（决定性实验）&lt;/p&gt;&lt;p&gt;用 Playwright 分别以临时 profile 启动两个&lt;strong&gt;真实浏览器 exe&lt;/strong&gt;，跑完全相同的滚动帧率 harness（4 秒三角波滚动，统计帧间隔）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 核心：launchPersistentContext + executablePath，各自临时 user-data-dir&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ctx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;chromium&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;launchPersistentContext&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;tempDir&lt;/span&gt;&lt;span&gt;, {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;executablePath&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;C:/Program Files/Google/Chrome/Application/chrome.exe&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;// 或 msedge.exe&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;headless&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;viewport&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;width&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1728&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;height&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1080&lt;/span&gt;&lt;span&gt; }, &lt;/span&gt;&lt;span&gt;deviceScaleFactor&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1.5&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 页面内 harness：rAF 记录帧间隔，每帧 scrollTo 三角波滚动 4s，&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 统计 fps / jank(&amp;gt;25ms) / jank(&amp;gt;50ms) / worst / p95&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;结果：&lt;strong&gt;Chrome 99.9 FPS / Edge 99.9 FPS，双双零掉帧&lt;/strong&gt;。&lt;br /&gt;
这一步同时排除了「Windows 每应用 GPU 指派」（系统级设置对 exe 生效，测试用的就是你的真 Chrome）。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;第三轮：读真 Chrome 的配置文件&lt;/strong&gt;（只读排查，不用开浏览器）&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# Chrome:  %LOCALAPPDATA%\Google\Chrome\User Data\Local State&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# Edge:    %LOCALAPPDATA%\Microsoft\Edge\User Data\Local State&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;json.&lt;/span&gt;&lt;span&gt;load&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;)[&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;hardware_acceleration_mode&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;hardware_acceleration_mode&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;你的 Chrome&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;&lt;code&gt;{&quot;enabled&quot;: false}&lt;/code&gt; ← 硬件加速是关的！&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;你的 Edge&lt;/td&gt;&lt;td&gt;键不存在（未动过 = 默认开），&lt;code&gt;previous: true&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;扩展数 0、退出状态正常——&lt;strong&gt;唯一差异就是这个开关&lt;/strong&gt;。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;根因与修复&lt;a href=&quot;#根因与修复&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Chrome 关闭了硬件加速&lt;/strong&gt; → 6 层 backdrop-filter 毛玻璃、全屏壁纸滚动模糊渐变（blurRamp）、波浪 canvas 的全部合成工作由 &lt;strong&gt;CPU 软渲染&lt;/strong&gt; → 滚动掉帧；Edge 默认开 → GPU 合成 → 流畅。&lt;/p&gt;&lt;p&gt;修复（用户操作，一分钟）：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;&lt;code&gt;chrome://settings/system&lt;/code&gt; → 开启「使用硬件加速模式」→ 重启 Chrome&lt;/li&gt;
&lt;li&gt;&lt;code&gt;chrome://gpu&lt;/code&gt; 确认 Canvas / Compositing / Rasterization / Video Decode 全部 &lt;strong&gt;Hardware accelerated&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;实测确认恢复正常 ✅&lt;/li&gt;
&lt;/ol&gt;&lt;blockquote&gt;&lt;p&gt;为什么我的自动化测试一直测不出问题：&lt;strong&gt;新开的 profile 用默认值（硬件加速开）&lt;/strong&gt;，测的是健康态的 Chrome。真凶藏在你日常 profile 的 Local State 里。&lt;/p&gt;&lt;/blockquote&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;五、成果总表&lt;a href=&quot;#五成果总表&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;项&lt;/th&gt;&lt;th&gt;根因&lt;/th&gt;&lt;th&gt;修复&lt;/th&gt;&lt;th&gt;效果&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;A 字体&lt;/td&gt;&lt;td&gt;主字体没登记子集化&lt;/td&gt;&lt;td&gt;&lt;code&gt;subsetFonts&lt;/code&gt; +1 行&lt;/td&gt;&lt;td&gt;3.1MB → &lt;strong&gt;212KB（-93%）&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;C 连接&lt;/td&gt;&lt;td&gt;三源无 preconnect&lt;/td&gt;&lt;td&gt;head +3 行&lt;/td&gt;&lt;td&gt;图床/统计早建连&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;B 封面&lt;/td&gt;&lt;td&gt;远程图裸出 + 图床功能未开&lt;/td&gt;&lt;td&gt;图床开关 + srcset 双档 + 回退保险丝&lt;/td&gt;&lt;td&gt;单张 330KB → &lt;strong&gt;48.5–141KB&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;D CSS&lt;/td&gt;&lt;td&gt;3 个阻塞 link&lt;/td&gt;&lt;td&gt;&lt;code&gt;inlineStylesheets: &quot;always&quot;&lt;/code&gt;&lt;/td&gt;&lt;td&gt;阻塞请求 &lt;strong&gt;3 → 0&lt;/strong&gt;，首屏约 -250ms&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;E 重排&lt;/td&gt;&lt;td&gt;innerWidth/scrollTop/fonts.ready/体操/读写交错&lt;/td&gt;&lt;td&gt;FCP 串行门闩 + 14 文件修复&lt;/td&gt;&lt;td&gt;forced-reflow &lt;strong&gt;score 0 → 多次 1 / 0ms&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;F 掉帧&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Chrome 硬件加速被关&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;浏览器设置一键开启&lt;/td&gt;&lt;td&gt;Chrome 恢复流畅 ✅&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;相关提交：&lt;code&gt;3691383a&lt;/code&gt;(A+C) → &lt;code&gt;f875aef9&lt;/code&gt;(D) → &lt;code&gt;d6ceae0a&lt;/code&gt;(E)，另有若干 chore 数据刷新。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;六、可复用的工具箱&lt;a href=&quot;#六可复用的工具箱&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 1. 单审计 Lighthouse（本机没独立 Chrome 时用 Edge）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;$env:CHROME_PATH&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;npx &lt;/span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt;yes lighthouse &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;http://127.0.0.1:8931/&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt;only&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;audits&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;forced&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;reflow&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;insight `&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt;output&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;json &lt;/span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt;output&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;lh.json &lt;/span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt;chrome&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;flags&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;--headless=new&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 2. CDP 带栈追踪骨架（Playwright run_code）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// cats 含 disabled-by-default-devtools.timeline.stack；&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// Tracing.start(ReportEvents) → goto → end；&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 事件过滤 Layout/UpdateLayoutTree + stackTrace，stack[0]=最内层帧&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 3. 滚动帧率 harness（浏览器控制台可直接粘贴）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// rAF 记帧间隔 + 4s 三角波 scrollTo → fps/jank25/jank50/worst/p95&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 4. 浏览器硬件加速状态（只读）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;ls &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; json.&lt;/span&gt;&lt;span&gt;load&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;open&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;&lt;span&gt;&quot;C:&lt;/span&gt;&lt;span&gt;\U&lt;/span&gt;&lt;span&gt;sers&lt;/span&gt;&lt;span&gt;\&amp;lt;&lt;/span&gt;&lt;span&gt;你&amp;gt;\AppData&lt;/span&gt;&lt;span&gt;\L&lt;/span&gt;&lt;span&gt;ocal&lt;/span&gt;&lt;span&gt;\G&lt;/span&gt;&lt;span&gt;oogle&lt;/span&gt;&lt;span&gt;\C&lt;/span&gt;&lt;span&gt;hrome&lt;/span&gt;&lt;span&gt;\U&lt;/span&gt;&lt;span&gt;ser Data&lt;/span&gt;&lt;span&gt;\L&lt;/span&gt;&lt;span&gt;ocal State&quot;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;print&lt;/span&gt;&lt;span&gt;&lt;span&gt;(ls.&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;hardware_acceleration_mode&quot;&lt;/span&gt;&lt;span&gt;))  &lt;/span&gt;&lt;span&gt;# {&quot;enabled&quot;: False} 即被关闭&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;七、测量经验清单&lt;a href=&quot;#七测量经验清单&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;Lighthouse 默认&lt;strong&gt;移动视口 + 4x CPU 降速&lt;/strong&gt;，数字比桌面裸测大数倍——跨环境对比先对齐测量条件&lt;/li&gt;
&lt;li&gt;做绘制/帧率测量前 &lt;strong&gt;&lt;code&gt;page.bringToFront()&lt;/code&gt;&lt;/strong&gt;——被遮挡的 Chromium 窗口暂停绘制、rAF 降到 2fps、paint 条目为空&lt;/li&gt;
&lt;li&gt;Lighthouse 的 line/column 只当粗定位，&lt;strong&gt;真凶以 CDP Tracing 的完整栈为准&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;改性能代码中途若行为异常，先 &lt;code&gt;git stash&lt;/code&gt; 建&lt;strong&gt;基线同环境对照&lt;/strong&gt;，再决定是不是自己的锅&lt;/li&gt;
&lt;li&gt;复杂内联脚本别塞 PowerShell 命令行——引号会被啃，落盘执行&lt;/li&gt;
&lt;li&gt;同引擎双浏览器表现不一致 → 优先怀疑&lt;strong&gt;各自的设置与 profile&lt;/strong&gt;，直接读 &lt;code&gt;Local State&lt;/code&gt; / &lt;code&gt;Preferences&lt;/code&gt; JSON&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;（完）&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;</content:encoded></item><item><title>Firefly 博客实战：从零搭建任意设备可用的发布台（Vercel + GitHub API）</title><link>https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-17/</link><guid isPermaLink="true">https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-17/</guid><description>静态博客只能守在电脑前发文章？本文完整复盘如何用 Vercel 无框架函数 + GitHub API 搭一套带登录的发布后台——发文、改稿、删除、预览全部在浏览器里完成，附我们真实踩过的五个部署坑与安全边界设计，照着做可以从零复刻。</description><pubDate>Sat, 03 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h1&gt;Firefly 博客实战：从零搭建任意设备可用的发布台（Vercel + GitHub API）&lt;a href=&quot;#firefly-博客实战从零搭建任意设备可用的发布台vercel--github-api&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;blockquote&gt;&lt;p&gt;静态博客的一切内容都是 Git 仓库里的 Markdown 文件，代价是：想发篇文章，你得坐在装好开发环境的电脑前。这篇讲我们怎么把”发布”这件事拆出来，做成一个&lt;strong&gt;任何设备打开浏览器就能用&lt;/strong&gt;的独立系统——发文、改稿、删除、预览，全部不用碰 git。&lt;/p&gt;&lt;/blockquote&gt;&lt;section&gt;&lt;h2&gt;一、需求与架构选型&lt;a href=&quot;#一需求与架构选型&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;目标拆开来是三句话：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;博客本体&lt;strong&gt;一行架构都不动&lt;/strong&gt;（继续是纯静态站 + 自动构建）&lt;/li&gt;
&lt;li&gt;发布系统&lt;strong&gt;独立部署&lt;/strong&gt;，有登录，手机能用&lt;/li&gt;
&lt;li&gt;发布动作最终&lt;strong&gt;落回 Git 仓库&lt;/strong&gt;，触发既有的自动构建上线&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;这就是所谓的”前后端分离”在静态博客语境下的含义：&lt;strong&gt;博客 = 展示层（静态产物），发布台 = 独立的前后端（编辑界面 + API）&lt;/strong&gt;，中间靠 GitHub 仓库衔接。&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;口令 + 内容&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;Contents API&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;发布台前端&lt;br /&gt;&lt;br /&gt;(浏览器/手机)&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;Vercel API 函数&lt;br /&gt;&lt;br /&gt;鉴权·命名·拼 frontmatter&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;GitHub 仓库 master&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;自动构建&lt;br /&gt;&lt;br /&gt;(Actions / Cloudflare Pages)&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;博客上线&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;口令 + 内容&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;Contents API&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;发布台前端&lt;br /&gt;&lt;br /&gt;(浏览器/手机)&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;Vercel API 函数&lt;br /&gt;&lt;br /&gt;鉴权·命名·拼 frontmatter&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;GitHub 仓库 master&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;自动构建&lt;br /&gt;&lt;br /&gt;(Actions / Cloudflare Pages)&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;p&gt;博客上线&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;为什么走 Git 而不是数据库？因为博客的内容层（content collections、文件名序号、frontmatter 约定）全部建立在文件上，搬进数据库等于推翻重建；而发布频率很低的个人博客，&lt;strong&gt;每次发布等 3~5 分钟构建完全可以接受&lt;/strong&gt;。这条路只动”发布入口”，不动”内容模型”，风险最低。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;二、准备工作&lt;a href=&quot;#二准备工作&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;开始前你需要：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;一个静态博客仓库&lt;/strong&gt;（本文以 Firefly / Astro 为例，其他静态站同理）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vercel 账号&lt;/strong&gt;（ Hobby 免费版足够）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitHub Fine-grained PAT&lt;/strong&gt;（精细访问令牌）：
&lt;ul&gt;
&lt;li&gt;打开 GitHub → &lt;strong&gt;Settings&lt;/strong&gt;（设置）→ &lt;strong&gt;Developer settings&lt;/strong&gt; → &lt;strong&gt;Personal access tokens&lt;/strong&gt; → &lt;strong&gt;Fine-grained tokens&lt;/strong&gt; → &lt;strong&gt;Generate new token&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Repository access&lt;/strong&gt;（仓库范围）→ Only select repositories → 只勾你的博客仓库&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Permissions&lt;/strong&gt;（权限）→ Repository permissions → &lt;strong&gt;Contents&lt;/strong&gt; 设为 &lt;strong&gt;Read and write&lt;/strong&gt;，其他一律不动&lt;/li&gt;
&lt;li&gt;生成后&lt;strong&gt;立刻复制&lt;/strong&gt;，只显示这一次&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;一个发布口令&lt;/strong&gt;：自己想一串随机字符串（比如 &lt;code&gt;firefly-xxxx-xxxx&lt;/code&gt;），它是发布台的登录密码，后面存进 Vercel 环境变量&lt;/li&gt;
&lt;/ol&gt;&lt;blockquote&gt;&lt;p&gt;PAT 只授单仓库的 Contents 写权限，就算泄露，损失也被锁死在”有人能往你博客仓库提交内容”这一件事上——这是整套设计里最关键的一条安全边界。&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;三、后端：五个函数就是全部&lt;a href=&quot;#三后端五个函数就是全部&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;发布台后端不用任何框架，Vercel 支持&lt;strong&gt;纯函数&lt;/strong&gt;：在项目目录下建 &lt;code&gt;api/&lt;/code&gt; 文件夹，每个 &lt;code&gt;.js&lt;/code&gt; 文件就是一个 HTTP 端点。最终结构：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;publisher/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;├── index.html          # 发布台单页（登录/发布/管理三视图）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;├── app.js              # 前端逻辑&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;├── package.json        # {&quot;type&quot;:&quot;module&quot;}，加一个空 build 脚本&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;├── vercel.json         # 锁定输出目录（防根配置串扰，见第五节）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;├── lib/shared.js       # 端点公共工具&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;└── api/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;├── auth.js         # POST   登录校验&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;├── publish.js      # POST   发布新内容&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;├── articles.js     # GET    内容列表（管理页用）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└── article.js      # GET/PUT/DELETE  单篇读、存、删&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;section&gt;&lt;h3&gt;3.1 鉴权：每个请求都要过口令&lt;a href=&quot;#31-鉴权每个请求都要过口令&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;口令存环境变量 &lt;code&gt;PUBLISH_TOKEN&lt;/code&gt;，前端登录成功后把口令放在请求头 &lt;code&gt;x-publish-token&lt;/code&gt; 里带上，服务端逐次校验。比较要用常数时间函数，避免时序侧信道：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;crypto&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;node:crypto&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;safeEqual&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;b&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ab&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Buffer&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;??&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;), &lt;/span&gt;&lt;span&gt;&quot;utf8&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bb&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Buffer&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;b&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;??&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;), &lt;/span&gt;&lt;span&gt;&quot;utf8&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;ab&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!==&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bb&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;crypto&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;timingSafeEqual&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;ab&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;bb&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 每个端点的第一段逻辑&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;publishToken&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;process&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;PUBLISH_TOKEN&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;safeEqual&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;req&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;headers&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&quot;x-publish-token&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;], &lt;/span&gt;&lt;span&gt;publishToken&lt;/span&gt;&lt;span&gt;)) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;res&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;statusCode&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;401&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;res&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;JSON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;stringify&lt;/span&gt;&lt;span&gt;({ &lt;/span&gt;&lt;span&gt;ok&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;口令错误&quot;&lt;/span&gt;&lt;span&gt; }));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;会话策略用 &lt;strong&gt;sessionStorage&lt;/strong&gt;（存标签页内存，关浏览器即失效）而不是 localStorage，就实现了”每次打开发布台都要登录”。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;3.2 发布端点：命名、frontmatter、提交三件事&lt;a href=&quot;#32-发布端点命名frontmatter提交三件事&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;发布端点收到 &lt;code&gt;{ type, title, content, ...可选字段 }&lt;/code&gt; 后做三件事：&lt;/p&gt;&lt;p&gt;&lt;strong&gt;① 自动命名&lt;/strong&gt;——这是”文件名序号”类博客的关键。发文章前先调 GitHub Contents API 列目录，正则抓所有 &lt;code&gt;数字+文章.md&lt;/code&gt; 取最大值 +1：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;res&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;`https://api.github.com/repos/&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;REPO&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;/contents/src/content/posts/txt?ref=&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;BRANCH&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;headers&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;Authorization&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`Bearer &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;token&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt; } },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;entries&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;res&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;json&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;max&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;of&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;entries&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;m&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt;^&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;\d&lt;/span&gt;&lt;span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;)文章&lt;/span&gt;&lt;span&gt;\.&lt;/span&gt;&lt;span&gt;md&lt;/span&gt;&lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exec&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;m&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;max&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Math&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;max&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;max&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Number&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;m&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;]));&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`src/content/posts/txt/&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;max&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;文章.md`&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;更新公告按日期命名（&lt;code&gt;更新公告20261003.md&lt;/code&gt;）、动态按时间戳命名（&lt;code&gt;20261003-1217.md&lt;/code&gt;），同名冲突就追加 &lt;code&gt;-2&lt;/code&gt; 后缀。&lt;strong&gt;命名规则永远现场查仓库算，不要缓存&lt;/strong&gt;，这样本地手动加了文件它也不会撞车。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;② 拼 frontmatter&lt;/strong&gt;——两条容易翻车的经验：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;字符串一律用 &lt;code&gt;JSON.stringify&lt;/code&gt; 转义&lt;/strong&gt;：标题里的引号、冒号、特殊字符，&lt;code&gt;JSON.stringify&lt;/code&gt; 输出的就是合法 YAML 双引号字符串，手写拼接迟早被一个带 &lt;code&gt;:&lt;/code&gt; 的标题弄炸&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;日期裸写、不要引号&lt;/strong&gt;：&lt;code&gt;published: 2026-10-03&lt;/code&gt; 会被 YAML 解析成 Date 对象（Zod schema 的 &lt;code&gt;z.date()&lt;/code&gt; 才认）；写成 &lt;code&gt;published: &quot;2026-10-03&quot;&lt;/code&gt; 就成了字符串，构建直接报 schema 错误&lt;/li&gt;
&lt;/ul&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lines&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;&quot;---&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;`title: &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;JSON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;stringify&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;`published: &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;now&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;date&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 约定&quot;留空不写&quot;：可选字段没填就完全不出现在 frontmatter 里&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;fields&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;tags&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;lines&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`tags: &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;JSON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;stringify&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;fields&lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;tags&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;fields&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;slug&lt;/span&gt;&lt;span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;lines&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`slug: &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;JSON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;stringify&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;fields&lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;slug&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;lines&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;---&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;③ 提交到仓库&lt;/strong&gt;——用 Contents API 的创建接口，内容 base64 后 PUT 到目标分支：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`https://api.github.com/repos/&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;REPO&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;/contents/&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;, {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;method&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;PUT&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;headers&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;Authorization&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`Bearer &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;token&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;Content-Type&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;application/json&quot;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;JSON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;stringify&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`post: &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;,            &lt;/span&gt;&lt;span&gt;// 提交信息，进 git 历史一眼能看懂&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Buffer&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;markdown&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;utf8&quot;&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;toString&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;base64&quot;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;branch&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;master&quot;&lt;/span&gt;&lt;span&gt;,                     &lt;/span&gt;&lt;span&gt;// 推到构建监听的分支&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;提交成功即返回”已提交，3~5 分钟后生效”——到这一步，剩下的事全交给博客既有的自动构建，发布台&lt;strong&gt;不需要知道构建是否存在、是否成功&lt;/strong&gt;。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;3.3 管理端点：读、存、删&lt;a href=&quot;#33-管理端点读存删&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;列表&lt;/strong&gt;（&lt;code&gt;articles.js&lt;/code&gt;）：调 Git Trees API（&lt;code&gt;/git/trees/master?recursive=1&lt;/code&gt;）一次拿到全仓库文件树，过滤出内容目录的 &lt;code&gt;.md&lt;/code&gt;；再并发拉各文件的 frontmatter 头部解析出标题/日期/草稿标记，拼成列表&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;读取&lt;/strong&gt;（&lt;code&gt;article.js&lt;/code&gt; GET）：Contents API 返回 base64 正文和 &lt;strong&gt;sha&lt;/strong&gt;（文件指纹）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;保存&lt;/strong&gt;（PUT）：必须带上刚才读到的 &lt;code&gt;sha&lt;/code&gt;，GitHub 据此判断文件有没有被别人动过——&lt;strong&gt;sha 对不上返回 409&lt;/strong&gt;，提示”文件已被他人改动，请刷新重试”，两个人同时改稿就不会互相覆盖&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;删除&lt;/strong&gt;（DELETE）：同样要 sha，删除即一次 commit，走同样的构建上线&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;三条铁律写在端点入口：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// ① 路径白名单：只允许内容目录的 markdown，防任意文件被改/删&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;isSafeContentPath&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;includes&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;..&quot;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;startsWith&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;/&quot;&lt;/span&gt;&lt;span&gt;)) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; /&lt;/span&gt;&lt;span&gt;^&lt;/span&gt;&lt;span&gt;&lt;span&gt;src&lt;/span&gt;&lt;span&gt;\/&lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;\/&lt;/span&gt;&lt;span&gt;(posts&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;dynamic)&lt;/span&gt;&lt;span&gt;\/&lt;/span&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;\.&lt;/span&gt;&lt;span&gt;(md&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;mdx)&lt;/span&gt;&lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;test&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// ② 内容不能为空&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// ③ sha 必须提供（先读后改，不允许盲写）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;四、前端：一个单页，三个视图&lt;a href=&quot;#四前端一个单页三个视图&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;前端不需要框架，一个 &lt;code&gt;index.html&lt;/code&gt; + 一个 &lt;code&gt;app.js&lt;/code&gt; 足够，视图切换就是显隐：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;登录视图&lt;/strong&gt;：口令输入框 → 调 &lt;code&gt;POST /api/auth&lt;/code&gt; 校验 → 成功后口令存 sessionStorage，进主界面&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;发布视图&lt;/strong&gt;：内容类型分段器（文章/公告/动态）+ 标题 + 正文 + 可选字段折叠区。可选字段&lt;strong&gt;照抄你博客 schema 的字段表&lt;/strong&gt;，顺序一致、留空不写，这样发布产物和手写文件完全同构&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;管理视图&lt;/strong&gt;：列表（按类型分组、显示草稿/置顶徽章）→ 点条目进编辑器（全文含 frontmatter 在一个 textarea 里改）→ 预览 / 保存 / 删除&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;预览用 &lt;a href=&quot;https://github.com/markedjs/marked&quot; target=&quot;_blank&quot;&gt;marked&lt;/a&gt; 做客户端渲染，把它的 UMD 产物拷进 &lt;code&gt;vendor/&lt;/code&gt; 本地引用（&lt;strong&gt;别用 CDN，国内不稳&lt;/strong&gt;）。渲染前先用正则剥掉 frontmatter 单独展示，正文丢给 &lt;code&gt;marked.parse()&lt;/code&gt; 即可。要认清预览的边界：它是&lt;strong&gt;近似预览&lt;/strong&gt;——自定义指令、代码高亮、排版样式最终以博客构建后的效果为准。&lt;/p&gt;&lt;p&gt;关键交互别忘了：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;删除必须&lt;strong&gt;二次确认&lt;/strong&gt;（弹窗里带标题和路径，“不可撤销”写明白）&lt;/li&gt;
&lt;li&gt;401 统一处理：任何请求收到 401 都踢回登录页&lt;/li&gt;
&lt;li&gt;发布成功后清空”一对一”字段（标题、正文、slug、封面、密码……），保留可复用的（标签、分类、作者），避免下一篇带着上一篇的封面发出去&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;五、部署：五个步骤与五个坑&lt;a href=&quot;#五部署五个步骤与五个坑&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;步骤 1：导入仓库，设 Root Directory&lt;a href=&quot;#步骤-1导入仓库设-root-directory&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Vercel → &lt;strong&gt;Add New Project&lt;/strong&gt; → 导入你的博客仓库 → &lt;strong&gt;Settings → General → Root Directory&lt;/strong&gt; 填 &lt;code&gt;publisher&lt;/code&gt;（根目录）。不填的话 Vercel 会去构建整个博客本体，必错。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;步骤 2：环境变量&lt;a href=&quot;#步骤-2环境变量&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Settings → Environment Variables&lt;/strong&gt; 加两条：&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Key&lt;/th&gt;&lt;th&gt;Value&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;GITHUB_TOKEN&lt;/code&gt;&lt;/td&gt;&lt;td&gt;刚才的 fine-grained PAT&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;PUBLISH_TOKEN&lt;/code&gt;&lt;/td&gt;&lt;td&gt;你自己定的发布口令&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;坑 ①：&lt;code&gt;Command &quot;build&quot; not found&lt;/code&gt;&lt;a href=&quot;#坑-command-build-not-found&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Vercel 对 monorepo 会在仓库根装依赖（正常），但构建命令跑在 Root Directory 里——你的子目录 &lt;code&gt;package.json&lt;/code&gt; 没有 &lt;code&gt;build&lt;/code&gt; 脚本就报这个。&lt;strong&gt;修法&lt;/strong&gt;：补一个空脚本 &lt;code&gt;&quot;build&quot;: &quot;echo ok&quot;&lt;/code&gt;（或对应包管理器的等价物）。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;坑 ②：&lt;code&gt;No Output Directory named &quot;dist&quot; found&lt;/code&gt;&lt;a href=&quot;#坑-no-output-directory-named-dist-found&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;这个最阴险：&lt;strong&gt;仓库根遗留的 &lt;code&gt;vercel.json&lt;/code&gt;&lt;/strong&gt;（写着 &lt;code&gt;buildCommand&lt;/code&gt;、&lt;code&gt;outputDirectory: dist&lt;/code&gt;）会被套用到你的发布台项目上，而且 &lt;strong&gt;vercel.json 优先级高于界面设置&lt;/strong&gt;，在界面上怎么改都没用。&lt;strong&gt;双保险修法&lt;/strong&gt;（两头都堵死）：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;子目录放自己的 &lt;code&gt;vercel.json&lt;/code&gt;：&lt;code&gt;{ &quot;outputDirectory&quot;: &quot;.&quot; }&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;build&lt;/code&gt; 脚本顺便产出一份 &lt;code&gt;dist/&lt;/code&gt;（&lt;code&gt;mkdir -p dist &amp;amp;&amp;amp; cp index.html dist/&lt;/code&gt;），满足根配置的 &lt;code&gt;dist&lt;/code&gt; 预期&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;排查时先看报错信息里的配置来源指向哪，再决定动哪边的 &lt;code&gt;vercel.json&lt;/code&gt;——&lt;strong&gt;别去改博客本体的根配置&lt;/strong&gt;，那是博客自己的部署遗产。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;步骤 3：部署&lt;a href=&quot;#步骤-3部署&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;点 &lt;strong&gt;Deploy&lt;/strong&gt;（部署），等到 &lt;strong&gt;Ready&lt;/strong&gt;（就绪）。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;坑 ③：打开部署地址跳到 &lt;code&gt;vercel.com/login&lt;/code&gt;&lt;a href=&quot;#坑-打开部署地址跳到-vercelcomlogin&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;新项目默认开着 &lt;strong&gt;Deployment Protection&lt;/strong&gt;（部署保护）——Vercel 认证拦住了所有访客。到 &lt;strong&gt;Settings → Deployment Protection → Vercel Authentication&lt;/strong&gt;（Vercel 身份验证）关掉（或只留预览环境保护）。发布台自己有口令把关，页面加 &lt;code&gt;noindex&lt;/code&gt; 即可放心公开。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;步骤 4：绑定自有子域名&lt;a href=&quot;#步骤-4绑定自有子域名&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;坑 ④&lt;/strong&gt;：&lt;code&gt;*.vercel.app&lt;/code&gt; 在国内被 DNS 污染，&lt;strong&gt;不绑域名基本打不开&lt;/strong&gt;。&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Settings → Domains&lt;/strong&gt; → &lt;strong&gt;Add Existing&lt;/strong&gt;（添加已有域名）→ 输入你的子域名（如 &lt;code&gt;pub.example.com&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;到 DNS 服务商（如 Cloudflare）加一条 &lt;code&gt;CNAME&lt;/code&gt;：名称填子域，值照 Vercel 显示的填&lt;/li&gt;
&lt;li&gt;建议开启 CDN 代理（橙云）——解析走代理边缘，避开污染&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;坑 ⑤&lt;/strong&gt;：配完 Vercel 会报 &lt;strong&gt;Proxy Detected&lt;/strong&gt;（检测到代理）警告——&lt;strong&gt;可以无视&lt;/strong&gt;。它只是抱怨自己的防护工具在代理后面不好使，对口令保护的个人发布页毫无影响，而代理恰恰是国内能稳定访问的关键。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;步骤 5：验证&lt;a href=&quot;#步骤-5验证&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;打开你的子域名 → 登录 → 发一条测试内容 → 确认：仓库出现新 commit → 自动构建触发 → 博客页面出现这条内容。三环都绿，系统就通了。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;六、安全边界清单&lt;a href=&quot;#六安全边界清单&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;边界&lt;/th&gt;&lt;th&gt;做法&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;登录&lt;/td&gt;&lt;td&gt;口令存环境变量；每请求校验；sessionStorage 关浏览器失效&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;GitHub 凭据&lt;/td&gt;&lt;td&gt;fine-grained PAT，&lt;strong&gt;单仓库&lt;/strong&gt;、&lt;strong&gt;仅 Contents 读写&lt;/strong&gt;、定期轮换&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;文件操作&lt;/td&gt;&lt;td&gt;路径白名单锁死在内容目录的 &lt;code&gt;.md/.mdx&lt;/code&gt;，拒绝 &lt;code&gt;..&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;并发&lt;/td&gt;&lt;td&gt;读写带 sha，冲突返回 409 而不是覆盖&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;暴露面&lt;/td&gt;&lt;td&gt;页面 &lt;code&gt;noindex&lt;/code&gt;；错误信息只说”口令错误”，不回显任何环境变量&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;仓库纪律&lt;/td&gt;&lt;td&gt;发布台写的文件和本地手写完全同构，&lt;strong&gt;本地改动前先 &lt;code&gt;git pull&lt;/code&gt;&lt;/strong&gt;，正常 push 不会互相覆盖&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;七、成本与额度&lt;a href=&quot;#七成本与额度&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;整套系统跑在免费层就够：Vercel Hobby 免费版（函数请求量对个人博客是天文数字）、GitHub API 每小时 5000 次调用（一次发布约 3~5 次请求）、marked 本地化零外部依赖。唯一的”成本”是每年记得给 PAT 续期。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;八、可以继续加的东西&lt;a href=&quot;#八可以继续加的东西&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;图片上传&lt;/strong&gt;：API 接收文件转存到图床或仓库 &lt;code&gt;images/&lt;/code&gt; 目录，正文插外链&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;构建状态&lt;/strong&gt;：发布后轮询 Actions / Cloudflare 构建 API，把”构建中→上线”做进回执&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;草稿箱&lt;/strong&gt;：发布时勾 &lt;code&gt;draft: true&lt;/code&gt;，列表里单独分组，改完一键转正&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;移动端 PWA&lt;/strong&gt;：加个 manifest，“添加到主屏幕”后体验接近原生 App&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;到这里，你就有了一套&lt;strong&gt;自己写、自己控、任何设备都能用&lt;/strong&gt;的博客发布系统：写在任何地方的 Markdown，贴进发布台，剩下的交给自动化。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;开源项目&lt;a href=&quot;#开源项目&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;这套发布台已经整理成开箱即用的模板仓库，Fork 一键部署或下载拷进你博客的 &lt;code&gt;publisher/&lt;/code&gt; 目录，照着 README 走完环境变量和 Vercel 配置就能用，路径与文件命名全部可用环境变量覆盖：&lt;/p&gt;&lt;a href=&quot;https://github.com/jmqsOOOtatoba/firefly-publisher&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;jmqsOOOtatoba&lt;/div&gt;&lt;/div&gt;&lt;div&gt;/&lt;/div&gt;&lt;div&gt;firefly-publisher&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;给静态博客装一个随时随地发文的发布台：口令登录、发文/改稿/删除/预览，Vercel + GitHub API，路径命名全可配置&lt;/div&gt;&lt;div&gt;&lt;div&gt;—&lt;/div&gt;&lt;div&gt;—&lt;/div&gt;&lt;div&gt;MIT&lt;/div&gt;&lt;span&gt;JavaScript&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/section&gt;&lt;/section&gt;</content:encoded></item><item><title>Win11 资源管理器大小列只显示 KB？开启 KB / MB / GB 自适应显示完整教程</title><link>https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-16/</link><guid isPermaLink="true">https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-16/</guid><description>Win11 详细信息视图的大小列长期以来一律用 KB 显示，4 GB 的文件写成 4,194,304 KB 根本没法看。微软其实已经原生支持自适应单位了，本文给出先更新系统、再用 ViveTool 强制开启（功能 ID 61014711）的完整教程，含回滚方法与常见问题。</description><pubDate>Sat, 03 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h1&gt;Win11 资源管理器大小列只显示 KB？开启 KB / MB / GB 自适应显示完整教程&lt;a href=&quot;#win11-资源管理器大小列只显示-kb开启-kb--mb--gb-自适应显示完整教程&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;blockquote&gt;&lt;p&gt;Windows 11 的大小列动辄一长串 KB 数字，肉眼完全无法换算。好消息是微软已经原生支持自适应单位了；坏消息是灰度推送可能轮不到你。本文给出从”先试免费零风险的更新”到”ViveTool 强制开启功能 ID 61014711”的完整方案，附回滚方法。&lt;/p&gt;&lt;/blockquote&gt;&lt;section&gt;&lt;h2&gt;问题&lt;a href=&quot;#问题&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Windows 11 的文件资源管理器切换到”详细信息”视图后，大小列长期以来&lt;strong&gt;一律用 KB 显示&lt;/strong&gt;：一个 4 GB 的文件写成 &lt;code&gt;4,194,304 KB&lt;/code&gt;，几十 GB 的文件就是一长串数字，肉眼完全无法换算，只能点开属性才能确认实际大小。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;背景：微软已经原生支持了&lt;a href=&quot;#背景微软已经原生支持了&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;2026 年 7 月起，微软开始推送这个改进：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;7 月可选更新 KB5101684&lt;/strong&gt;：24H2 升到 Build 26100.8973，25H2 升到 Build 26200.8973，详细信息视图的文件大小改为按实际大小自适应显示 KB / MB / GB；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;8 月累积更新 KB5121003&lt;/strong&gt;：24H2 到 26100.9168，25H2 到 26200.9168，该功能随安全更新正式向所有用户推送；&lt;/li&gt;
&lt;li&gt;在更早的 25H2 Beta / Experimental 预览版（26220.8370、26300.8376）中，这个功能默认隐藏，功能 ID 为 &lt;strong&gt;61014711&lt;/strong&gt;，需要手动开启。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;也就是说：&lt;strong&gt;先检查 Windows 更新，装完再重启资源管理器，很可能什么都不用改就好了。&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;但我在自己的机器上实测（25H2，Build 26200.9168，已包含 8 月更新）：装了更新、重启了资源管理器，大小列&lt;strong&gt;依然全是 KB&lt;/strong&gt;。原因是这类功能采用 CFR（受控功能放量）分批推送，同一个版本号的机器生效时间可能不同。这时就轮到 ViveTool 出场。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;方法一：先更新系统（免费、零风险）&lt;a href=&quot;#方法一先更新系统免费零风险&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;设置 → Windows 更新 → 检查更新，安装 2026 年 7 月及之后的所有更新；&lt;/li&gt;
&lt;li&gt;打开任务管理器（Ctrl+Shift+Esc）→ 找到”Windows 资源管理器” → 右键”重新启动”；&lt;/li&gt;
&lt;li&gt;打开任意有大文件的文件夹，切到详细信息视图，看大小列是否已显示 MB / GB。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;生效了就不用往下看了。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;方法二：ViveTool 强制开启（更新后仍是 KB 时用）&lt;a href=&quot;#方法二vivetool-强制开启更新后仍是-kb-时用&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;ViveTool 是微软隐藏功能（Feature ID）的命令行开关工具。&lt;/p&gt;&lt;section&gt;&lt;h3&gt;1. 下载 ViveTool&lt;a href=&quot;#1-下载-vivetool&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;任选其一：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;winget（推荐）&lt;/strong&gt;：终端执行&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;winget install --id thebookisclosed.Vive --accept-package-agreements --accept-source-agreements&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;手动下载&lt;/strong&gt;：GitHub 仓库已从 &lt;code&gt;thebookisclosed/ViVeTool&lt;/code&gt; 改名为 &lt;strong&gt;&lt;code&gt;thebookisclosed/ViVe&lt;/code&gt;&lt;/strong&gt;（旧地址会 404），到其 Releases 下载 &lt;code&gt;ViVeTool-v0.x.x-IntelAmd.zip&lt;/code&gt;，解压到任意目录。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;blockquote&gt;&lt;p&gt;注意：如果你的网络直连 GitHub 超时，下载需要走代理；winget 的下载进程不读系统代理，卡住不动时请换手动下载 + 代理，或用 &lt;code&gt;curl.exe -x http://127.0.0.1:&amp;lt;代理端口&amp;gt; -L -o vivetool.zip &amp;lt;下载地址&amp;gt;&lt;/code&gt;。&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;2. 以管理员身份执行&lt;a href=&quot;#2-以管理员身份执行&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;必须管理员&lt;/strong&gt;（写注册表需要提权，否则会报”拒绝访问”）。打开一个&lt;strong&gt;管理员&lt;/strong&gt;终端，执行：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;ViVeTool.exe /enable /id:61014711&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;验证是否写入成功：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;ViVeTool.exe /query /id:61014711&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;看到 &lt;code&gt;State : Enabled&lt;/code&gt; 即成功。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;3. 重启资源管理器&lt;a href=&quot;#3-重启资源管理器&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;任务管理器 → Windows 资源管理器 → 重新启动。再看大文件的大小列，此时应该显示 &lt;code&gt;1.25 MB&lt;/code&gt;、&lt;code&gt;3.72 GB&lt;/code&gt; 这样的自适应单位了。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;如何恢复原状&lt;a href=&quot;#如何恢复原状&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;ViveTool 的修改只是加了一条功能覆盖，随时可逆：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;ViVeTool.exe /disable /id:61014711&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;然后重启资源管理器即可回到 KB 显示。系统后续大版本更新也可能自动重置该覆盖。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;常见问题&lt;a href=&quot;#常见问题&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;执行 /enable 报”拒绝访问”&lt;/strong&gt;：终端不是管理员权限，右键”以管理员身份运行”再执行。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;开启了、重启了还是 KB&lt;/strong&gt;：确认系统已装 2026 年 7 月之后的更新——ViveTool 只是把系统里已有的功能代码”翻到开”，如果系统里根本没有这段代码（更新太旧），开启也无效。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;对文件夹有效吗？&lt;/strong&gt;：大小列对文件夹本身仍不显示大小（这是另一回事，需要第三方工具计算），本功能针对的是文件。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;只影响详细信息视图&lt;/strong&gt;：图标、平铺等其他视图不显示大小数字，自然不受影响。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;总结&lt;a href=&quot;#总结&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;方案&lt;/th&gt;&lt;th&gt;适用场景&lt;/th&gt;&lt;th&gt;风险&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;装 2026 年 7 月后更新&lt;/td&gt;&lt;td&gt;功能已随更新推送&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ViveTool + ID 61014711&lt;/td&gt;&lt;td&gt;已是新版本但被灰度卡住&lt;/td&gt;&lt;td&gt;低，可随时 disable 回滚&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;亲测环境：Windows 11 25H2（Build 26200.9168），更新后仍显示 KB，用 ViveTool 0.3.4 开启 61014711 并重启资源管理器后生效。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;</content:encoded></item><item><title>更新公告20261003</title><link>https://azuma.mstzuomu.space/posts/web-update-8/</link><guid isPermaLink="true">https://azuma.mstzuomu.space/posts/web-update-8/</guid><description>网站更新公告</description><pubDate>Sat, 03 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;hr /&gt;
&lt;section&gt;&lt;h2&gt;更新公告20261003&lt;a href=&quot;#更新公告20261003&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;1.网站版本更新到6.16.8&lt;/p&gt;&lt;p&gt;2.新增&lt;a href=&quot;https://azuma.mstzuomu.space/projects/firefly-publisher/&quot;&gt;&lt;strong&gt;firefly-publisher&lt;/strong&gt;&lt;/a&gt;发布后台：口令鉴权登录，支持文章列表、编辑、预览、删除，填好内容一键自动命名并提交到GitHub发布，手机等任意设备都能发文；已开源并加入项目页，搭建教程可见文章&lt;a href=&quot;https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-17/&quot;&gt;&lt;strong&gt;Firefly 博客实战：从零搭建任意设备可用的发布台&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;3.重构了MD语法下的哔哩哔哩卡片、音乐播放器、PDF阅读器三个组件，B站卡片信息改为API获取，加载更稳定&lt;/p&gt;&lt;p&gt;4.更新了天气API与侧边栏天气样式&lt;/p&gt;&lt;p&gt;5.重构了不蒜子访客统计，并修正了历史统计数据的偏移问题&lt;/p&gt;&lt;p&gt;6.更新了主页背景图&lt;/p&gt;&lt;p&gt;7.导航栏新增Umami、Cloudflare外链入口&lt;/p&gt;&lt;p&gt;8.修复了评论区表情被截断的问题，修复了同天发布文章顺序错乱的问题，修正了文章10的内容错误&lt;/p&gt;&lt;p&gt;9.发布了《Codex 重装后三大故障修复全记录》等多篇新文章，更新了文章封面&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>Codex 重装后三大故障修复全记录：CC Switch 报错、走官方通道、聊天记录丢失</title><link>https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-15/</link><guid isPermaLink="true">https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-15/</guid><description>一次「Codex 删了重装」引发三个环环相扣的故障：CC Switch 报 requireStack、Codex 打到 api.openai.com 401、历史聊天记录不显示。完整根因分析与修复过程。</description><pubDate>Fri, 02 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h1&gt;Codex 重装后三大故障修复全记录：CC Switch 报错、走官方通道、聊天记录丢失&lt;a href=&quot;#codex-重装后三大故障修复全记录cc-switch-报错走官方通道聊天记录丢失&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;blockquote&gt;&lt;p&gt;环境：Windows 11 · Node v24.19.0 · npm 全局 Codex CLI 0.160.0 · Codex Desktop（ChatGPT 桌面版）26.928 · CC Switch（供应商切换工具）&lt;/p&gt;&lt;p&gt;一次「Codex 包名有问题，删了重装」的操作，引发了三个看似独立、实则环环相扣的故障。本文完整记录排查与修复过程，供遇到同类问题的朋友参考。&lt;/p&gt;&lt;/blockquote&gt;&lt;section&gt;&lt;h2&gt;故障现象&lt;a href=&quot;#故障现象&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;重装 Codex 之后，出现了三个问题：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;CC Switch 设置页报错&lt;/strong&gt;：Codex 卡片显示「已安装 · 无法运行」，错误信息为 &lt;code&gt;requireStack: [] } Node.js v24.19.0&lt;/code&gt;，底部提示「请检查运行环境」。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Codex 启动走的还是官方通道&lt;/strong&gt;：打开 Codex 桌面版直接进 ChatGPT 登录页；CLI 调用则返回 &lt;code&gt;401 Unauthorized ... url: https://api.openai.com/v1/responses&lt;/code&gt;，完全没有走 CC Switch 配置的 MiMo 中转。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;历史聊天记录消失&lt;/strong&gt;：旧的会话记录在桌面版里一条都看不到。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;三个问题各有一个根因，下面逐个拆解。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;问题一：CC Switch 报「无法运行」&lt;a href=&quot;#问题一cc-switch-报无法运行&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;根因&lt;a href=&quot;#根因&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;CC Switch 检测 Codex 是否可用的方式，是执行 npm 全局目录下的 &lt;code&gt;codex.cmd&lt;/code&gt;。而重装过程中，&lt;strong&gt;npm 全局目录里 &lt;code&gt;@openai/codex&lt;/code&gt; 的包体被删空了，只留下了启动壳&lt;/strong&gt;：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;%APPDATA%\npm\node_modules\@openai\     ← 目录还在，里面 0 个文件&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;%APPDATA%\npm\codex.cmd                 ← 启动壳还在，指向不存在的包&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;执行 &lt;code&gt;codex.cmd&lt;/code&gt; 时报：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;Error: Cannot find module &apos;...\node_modules\@openai\codex\bin\codex.js&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;code: &apos;MODULE_NOT_FOUND&apos;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;requireStack: []&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;Node.js v24.19.0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;requireStack: []&lt;/code&gt; 就是截图里那行报错的来源——&lt;strong&gt;不是 Node 环境坏了，是包没了&lt;/strong&gt;。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;修复&lt;a href=&quot;#修复&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;npm install &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;g &lt;/span&gt;&lt;/span&gt;&lt;span&gt;@openai&lt;/span&gt;&lt;span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;codex&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;⚠️ &lt;strong&gt;关键坑&lt;/strong&gt;：Codex 的 npm 包采用「主包 + 平台二进制」分离结构，主包 &lt;code&gt;@openai/codex&lt;/code&gt; 很小，真正的可执行文件在 optionalDependencies 里（如 &lt;code&gt;@openai/codex-win32-x64&lt;/code&gt;）。国内直连官方 registry 经常在下载平台包时超时，结果是&lt;strong&gt;主包装上了但运行仍报错&lt;/strong&gt;：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;Error: Missing optional dependency @openai/codex-win32-x64.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;Reinstall Codex: npm install -g @openai/codex@latest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;解决办法是换国内镜像单独补装平台包：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;npm install &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;g &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;@openai/codex-win32-x64@npm:@openai/codex@0.160.0-win32-x64&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt;registry&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;https:&lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;/span&gt;&lt;span&gt;registry.npmmirror.com&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;验证：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;codex &lt;/span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt;version    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;# codex-cli 0.160.0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;codex doctor       &lt;/span&gt;&lt;span&gt;# 应全部通过&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;问题二：Codex 走回官方通道（401）&lt;a href=&quot;#问题二codex-走回官方通道401&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;根因&lt;a href=&quot;#根因-1&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;CC Switch 的工作原理是把供应商配置写进 &lt;code&gt;~/.codex/config.toml&lt;/code&gt;。正常应写入：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;model_provider&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;&quot;custom&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;model_providers&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;custom&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;&quot;xiaomi_mimo&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;base_url&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;&quot;https://api.xiaomimimo.com/v1&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;wire_api&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;&quot;responses&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;requires_openai_auth&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;但实际打开 &lt;code&gt;config.toml&lt;/code&gt; 发现：&lt;strong&gt;&lt;code&gt;model_provider&lt;/code&gt; 和整个 &lt;code&gt;[model_providers.custom]&lt;/code&gt; 表丢失了&lt;/strong&gt;，取而代之的是一堆无效的顶层键（&lt;code&gt;base_url&lt;/code&gt;、&lt;code&gt;wire_api&lt;/code&gt;、&lt;code&gt;experimental_bearer_token&lt;/code&gt; 写在了顶层——TOML 里这些键 Codex 根本不认，启动时会警告 &lt;code&gt;unrecognized configuration settings&lt;/code&gt;）。&lt;/p&gt;&lt;p&gt;没有 &lt;code&gt;model_provider&lt;/code&gt; 指示，Codex 就用默认的 OpenAI 官方 provider，于是打到 &lt;code&gt;api.openai.com&lt;/code&gt; 返回 401；桌面版则因为没有有效凭证直接弹登录页。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;为什么会被写坏？&lt;/strong&gt; 时间线是：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;今早 Codex 桌面版启动时重写了 &lt;code&gt;config.toml&lt;/code&gt;（9&amp;lt;35&amp;gt;）；&lt;/li&gt;
&lt;li&gt;CC Switch 把这份&lt;strong&gt;已经不含 provider 段的 config 回读成了自己的供应商模板&lt;/strong&gt;（双向同步的副作用）；&lt;/li&gt;
&lt;li&gt;之后每次切换供应商，CC Switch 用坏模板写回 &lt;code&gt;config.toml&lt;/code&gt;——&lt;strong&gt;越修越坏，切换一次覆盖一次&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;证据：CC Switch 数据库（&lt;code&gt;~/.cc-switch/cc-switch.db&lt;/code&gt; 的 &lt;code&gt;providers&lt;/code&gt; 表）里存的模板已经是坏的，而当天早些时候的数据库备份（9&amp;lt;29&amp;gt;）里配置还是完整的。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;修复（三处一起改）&lt;a href=&quot;#修复三处一起改&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;改任何一处之前，&lt;strong&gt;先备份 CC Switch 数据库&lt;/strong&gt;：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;Copy-Item&lt;/span&gt;&lt;span&gt;&lt;span&gt; ~\.cc&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;switch\cc&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;switch.db ~\.cc&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;switch\backups\db_backup_manual.db&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;修模板（根因）&lt;/strong&gt;：用 SQLite 打开 &lt;code&gt;~/.cc-switch/cc-switch.db&lt;/code&gt;，找到 &lt;code&gt;providers&lt;/code&gt; 表中当前 Codex 供应商的 &lt;code&gt;settings_config&lt;/code&gt; JSON 字段，删掉无效顶层键，补回 &lt;code&gt;model_provider = &quot;custom&quot;&lt;/code&gt; 和 &lt;code&gt;[model_providers.custom]&lt;/code&gt; 表。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;修 &lt;code&gt;~/.codex/config.toml&lt;/code&gt;&lt;/strong&gt;：同样处理。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;重建 &lt;code&gt;~/.codex/auth.json&lt;/code&gt;&lt;/strong&gt;（如果也丢了）：&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;&quot;OPENAI_API_KEY&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;你的中转 Key&quot;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;教训：&lt;strong&gt;只修 config.toml 不修数据库模板是没用的&lt;/strong&gt;，下次切换就会被覆盖回去。必须修源头。&lt;/p&gt;&lt;/blockquote&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;问题三：恢复的聊天记录不显示&lt;a href=&quot;#问题三恢复的聊天记录不显示&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;背景&lt;a href=&quot;#背景&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Codex 的会话记录是 JSONL 文件，按日期存放在：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;~/.codex/sessions/YYYY/MM/DD/rollout-&amp;lt;时间&amp;gt;-&amp;lt;uuid&amp;gt;.jsonl&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;把旧的 &lt;code&gt;2026/&lt;/code&gt; 文件夹整个复制回 &lt;code&gt;~/.codex/sessions/&lt;/code&gt; 后，文件明明在，桌面版侧边栏却一条都不显示。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;根因&lt;a href=&quot;#根因-2&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;桌面版的会话列表不是实时扫描文件夹的，而是读本地索引库 &lt;code&gt;~/.codex/state_5.sqlite&lt;/code&gt; 的 &lt;code&gt;threads&lt;/code&gt; 表。首次启动时有一个 &lt;strong&gt;backfill 扫描&lt;/strong&gt;流程负责把 rollout 文件导入这张表，扫完后在 &lt;code&gt;backfill_state&lt;/code&gt; 表里记：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;complete&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;NULL&lt;/span&gt;&lt;span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;时间戳&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;时间戳&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;新装的 Codex 在 9&amp;lt;09&amp;gt; 第一次启动时扫过一次——当时 sessions 目录还是空的（聊天记录是之后才复制进去的），扫描结果标记为 &lt;code&gt;complete&lt;/code&gt;，之后永远不会再扫。&lt;/strong&gt;&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;修复&lt;a href=&quot;#修复-1&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;完全退出&lt;/strong&gt; ChatGPT 桌面版（托盘也要退出，避免写库冲突）；&lt;/li&gt;
&lt;li&gt;备份后清空 &lt;code&gt;backfill_state&lt;/code&gt; 表，强制下次启动重扫：&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; sqlite3, shutil&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;shutil.&lt;/span&gt;&lt;span&gt;copy2&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;&lt;span&gt;&quot;C:&lt;/span&gt;&lt;span&gt;\U&lt;/span&gt;&lt;span&gt;sers&lt;/span&gt;&lt;span&gt;\&amp;lt;&lt;/span&gt;&lt;span&gt;你&amp;gt;&lt;/span&gt;&lt;span&gt;\.&lt;/span&gt;&lt;span&gt;codex\state_5.sqlite&quot;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;             &lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;&lt;span&gt;&quot;C:&lt;/span&gt;&lt;span&gt;\U&lt;/span&gt;&lt;span&gt;sers&lt;/span&gt;&lt;span&gt;\&amp;lt;&lt;/span&gt;&lt;span&gt;你&amp;gt;&lt;/span&gt;&lt;span&gt;\.&lt;/span&gt;&lt;span&gt;codex\state_5.sqlite.bak&quot;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;con &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; sqlite3.&lt;/span&gt;&lt;span&gt;connect&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;&lt;span&gt;&quot;C:&lt;/span&gt;&lt;span&gt;\U&lt;/span&gt;&lt;span&gt;sers&lt;/span&gt;&lt;span&gt;\&amp;lt;&lt;/span&gt;&lt;span&gt;你&amp;gt;&lt;/span&gt;&lt;span&gt;\.&lt;/span&gt;&lt;span&gt;codex\state_5.sqlite&quot;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;con.&lt;/span&gt;&lt;span&gt;execute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;DELETE FROM backfill_state&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;con.&lt;/span&gt;&lt;span&gt;commit&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;ol&gt;
&lt;li&gt;重新打开桌面版，启动时自动重扫，历史记录全部回来了。&lt;/li&gt;
&lt;/ol&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;修复顺序建议&lt;a href=&quot;#修复顺序建议&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;如果你同时遇到这三个问题，按这个顺序修，避免互相干扰：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;① npm 包（codex --version 能跑）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;② CC Switch 数据库模板 + config.toml + auth.json（走通中转）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;③ 退出桌面版 → 重置 backfill_state → 重启（恢复列表）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;验证清单&lt;a href=&quot;#验证清单&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt; &lt;code&gt;codex --version&lt;/code&gt; 输出版本号&lt;/li&gt;
&lt;li&gt; &lt;code&gt;codex doctor&lt;/code&gt; 无致命错误&lt;/li&gt;
&lt;li&gt; CC Switch 设置页 Codex 卡片无警告&lt;/li&gt;
&lt;li&gt; &lt;code&gt;codex exec &quot;你好&quot;&lt;/code&gt; 走中转正常回复（不应出现 &lt;code&gt;api.openai.com&lt;/code&gt; 的 401）&lt;/li&gt;
&lt;li&gt; 桌面版侧边栏能看到历史会话&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;经验教训&lt;a href=&quot;#经验教训&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;requireStack: []&lt;/code&gt; ≠ 环境坏了&lt;/strong&gt;：十有八九是包文件缺失，先 &lt;code&gt;ls&lt;/code&gt; 看包目录是不是空的。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;npm 装 Codex 国内务必配镜像&lt;/strong&gt;：平台二进制包体积大，官方 registry 容易半途超时，留下「装了一半」的残局。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;双向同步类工具有「污染源头」风险&lt;/strong&gt;：CC Switch 会把 app 重写过的 config 回读成模板，坏一次就循环放大。修的时候一定要连它的数据库模板一起修。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;文件在 ≠ 能显示&lt;/strong&gt;：现代客户端基本都有本地索引库，恢复数据文件后要找到对应的「重扫/重建索引」开关。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;改数据库前先备份&lt;/strong&gt;：本文所有数据库操作都留了备份，出问题可随时回滚。&lt;/li&gt;
&lt;/ol&gt;&lt;hr /&gt;&lt;p&gt;&lt;em&gt;本文基于一次真实排障记录整理，涉及的具体厂商（CC Switch / MiMo 中转）仅为实例，同类「供应商切换工具 + Codex」组合的排查思路通用。&lt;/em&gt;&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;</content:encoded></item><item><title>评论区回复时表情面板被截断：一次 overflow 裁剪排查实录</title><link>https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-14/</link><guid isPermaLink="true">https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-14/</guid><description>主评论框的表情菜单好好的，一到回复就少了半截？不是 z-index 的锅，是祖先容器的 overflow 在裁。完整复现步骤、根因分析与修复代码。</description><pubDate>Thu, 01 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h1&gt;评论区回复时表情面板被截断：一次 overflow 裁剪排查实录&lt;a href=&quot;#评论区回复时表情面板被截断一次-overflow-裁剪排查实录&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;blockquote&gt;&lt;p&gt;主评论框点开表情菜单一切正常，回复别人的评论时表情按钮本来就靠下，点开后面板下半截连同底部的分类 tab 全被容器”吃”掉了。第一反应是层级（z-index）不够，实际查下来跟层级半点关系没有——是祖先容器的 &lt;code&gt;overflow&lt;/code&gt; 在裁。本文记录完整复现步骤、排查过程和修复方案，无论你用的是不是我这套主题/评论系统，都能照着修。&lt;/p&gt;&lt;/blockquote&gt;&lt;section&gt;&lt;h2&gt;现象与复现&lt;a href=&quot;#现象与复现&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;复现条件很稳定，按下面步骤一定能碰到：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;打开任意一篇文章，滚到评论区；&lt;/li&gt;
&lt;li&gt;在&lt;strong&gt;顶部主评论框&lt;/strong&gt;点表情按钮：表情面板完整弹出，颜文字 / Emoji / Bilibili 分类 tab 都在——一切正常；&lt;/li&gt;
&lt;li&gt;找到一条已有评论，点它的&lt;strong&gt;回复&lt;/strong&gt;，回复框出现在评论列表靠下的位置（贴近评论卡片底部）；&lt;/li&gt;
&lt;li&gt;再点回复框的表情按钮：面板弹是弹出来了，但&lt;strong&gt;下半截被整齐地切掉&lt;/strong&gt;，底部分类 tab 直接消失，切分类无从下手。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;主评论框正常、回复框遭殃，很容易让人怀疑”回复框的 z-index 低了”。但无论你把表情面板的 &lt;code&gt;z-index&lt;/code&gt; 调到多大都没用——&lt;strong&gt;z-index 管的是叠放顺序，管不了裁剪&lt;/strong&gt;。一个绝对定位浮层能不能完整显示，取决于它&lt;strong&gt;全部祖先&lt;/strong&gt;的 &lt;code&gt;overflow&lt;/code&gt; 设置，任何一层 &lt;code&gt;overflow: hidden&lt;/code&gt; 都会在那层的边界上把它齐刷刷切掉。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;排查：顺着 DOM 向上找 overflow&lt;a href=&quot;#排查顺着-dom-向上找-overflow&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;打开 DevTools，用元素选择器选中被截断的表情面板，沿着祖先链逐层看计算样式里的 &lt;code&gt;overflow&lt;/code&gt;：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;表情面板（position: absolute，本身没问题）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└─ 评论卡片 #post-comments          ← overflow: hidden  ❌ 第一层裁剪&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;       &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└─ 主内容列 wrapper             ← overflow: hidden  ❌ 第二层裁剪&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└─ #content-wrapper / main ← 无 overflow        ✅&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;本站（Firefly 衍生主题 + Twikoo）具体是这两处：&lt;/p&gt;&lt;p&gt;&lt;strong&gt;第一层：评论卡片。&lt;/strong&gt; &lt;code&gt;src/components/comment/index.astro&lt;/code&gt; 里的 &lt;code&gt;#post-comments&lt;/code&gt;，类名里带着 &lt;code&gt;overflow-hidden&lt;/code&gt;，而且它继承的 &lt;code&gt;.card-base&lt;/code&gt; 基类本身也带 &lt;code&gt;overflow: hidden&lt;/code&gt;（为了圆角裁剪）。主评论框没事，是因为它在卡片顶部，面板向下展开 200 多 px 依然在卡片内部；回复框贴近卡片底部，面板需要溢出卡片才能显示完整——正好撞在裁剪边界上。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;第二层：主内容列。&lt;/strong&gt; &lt;code&gt;src/utils/responsive-utils.ts&lt;/code&gt; 的 &lt;code&gt;generateMainContentClasses()&lt;/code&gt; 给内容列 push 了 &lt;code&gt;overflow-hidden&lt;/code&gt;。就算修掉卡片那一层，面板接着会被这一层继续裁，所以两层要一起改。&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;同样的问题临渊羡鱼也踩过并写过一篇很详细的分析（&lt;a href=&quot;https://x1anyu.cn/posts/16/&quot; target=&quot;_blank&quot;&gt;评论区表情面板被裁、切换还抖，原因在 overflow&lt;/a&gt;，他修的是 Waline + Firefly，还给上游提了 PR）。评论系统不同，根因完全一致：主题为了圆角在祖先链上套的 &lt;code&gt;overflow: hidden&lt;/code&gt;，把评论系统绝对定位的浮层关在了里面。&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;关键陷阱：为什么不能直接 overflow-y: visible&lt;a href=&quot;#关键陷阱为什么不能直接-overflow-y-visible&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;第一反应可能是把裁剪层改成”横向还裁、纵向放开”。但这里有个规范级的坑：&lt;/p&gt;&lt;p&gt;&lt;strong&gt;只要一个轴是 &lt;code&gt;visible&lt;/code&gt;，另一个轴是 &lt;code&gt;hidden&lt;/code&gt; / &lt;code&gt;auto&lt;/code&gt; / &lt;code&gt;scroll&lt;/code&gt; / &lt;code&gt;clip&lt;/code&gt; 里任何非 visible 值，浏览器就会强制把 &lt;code&gt;visible&lt;/code&gt; 那一轴降级成 &lt;code&gt;auto&lt;/code&gt;。&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/* ❌ 看起来对，实际 overflow-y 被计算成 auto：&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;元素变成滚动容器，浮层照样被关在里面裁掉 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;overflow-x&lt;/span&gt;&lt;span&gt;: hidden;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;overflow-y&lt;/span&gt;&lt;span&gt;: visible;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;正确的组合是 &lt;strong&gt;&lt;code&gt;overflow-x: clip&lt;/code&gt; 配 &lt;code&gt;overflow-y: visible&lt;/code&gt;&lt;/strong&gt;——&lt;code&gt;clip&lt;/code&gt; 不触发上面那条降级规则（规范里 &lt;code&gt;visible&lt;/code&gt; 和 &lt;code&gt;clip&lt;/code&gt; 是允许共存的一对），只裁切、不生成滚动容器，也不影响列内 &lt;code&gt;sticky&lt;/code&gt; 吸顶：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/* ✅ 横向只裁不滚，纵向完全放开 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;overflow-x&lt;/span&gt;&lt;span&gt;: clip;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;overflow-y&lt;/span&gt;&lt;span&gt;: visible;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;修复&lt;a href=&quot;#修复&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;一共改两个文件：&lt;/p&gt;&lt;p&gt;&lt;strong&gt;1. &lt;code&gt;src/components/comment/index.astro&lt;/code&gt; — 评论卡片&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;div id=&quot;post-comments&quot; class=&quot;card-base p-8 mb-6 relative overflow-hidden&quot;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;div id=&quot;post-comments&quot; class=&quot;card-base p-8 mb-6 relative overflow-x-clip overflow-y-visible&quot;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;注意 &lt;code&gt;.card-base&lt;/code&gt; 基类里还有 &lt;code&gt;overflow: hidden&lt;/code&gt;，光删类名上的 &lt;code&gt;overflow-hidden&lt;/code&gt; 没用，必须用 &lt;code&gt;overflow-x-clip&lt;/code&gt; / &lt;code&gt;overflow-y-visible&lt;/code&gt; 两个工具类去覆盖长轴属性（本主题用 Tailwind v4，utilities 层优先级高于 &lt;code&gt;@layer components&lt;/code&gt; 里的 &lt;code&gt;.card-base&lt;/code&gt;，能覆盖住）。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;2. &lt;code&gt;src/utils/responsive-utils.ts&lt;/code&gt; — 主内容列&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;classes.push(&quot;min-w-0&quot;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;classes.push(&quot;overflow-hidden&quot;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;classes.push(&quot;overflow-x-clip&quot;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;classes.push(&quot;overflow-y-visible&quot;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;classes.push(&quot;z-10&quot;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;横向的 &lt;code&gt;clip&lt;/code&gt; 要保留：卡片的圆角和装饰性 SVG 靠它裁剪，横向不能放。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;3. 为什么要加 &lt;code&gt;z-10&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;浮层纵向放开后会溢出内容列、盖到页脚区域。本主题的页脚带着 &lt;code&gt;onload-animation&lt;/code&gt;，动画结束后 &lt;code&gt;transform: translateY(0)&lt;/code&gt; 会&lt;strong&gt;残留&lt;/strong&gt;（&lt;code&gt;animation-fill-mode: forwards&lt;/code&gt;），只要有 transform 就会生成层叠上下文——页脚在 DOM 里靠后，绘制顺序压在内容列上面。结果是：浮层显示出来了，但底部 tab 被页脚盖住，&lt;strong&gt;看得见点不着&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;页脚和内容列都是 &lt;code&gt;#main-grid&lt;/code&gt; 的 grid 子项，grid 子项直接写 &lt;code&gt;z-index&lt;/code&gt; 就能生效（不需要额外加 &lt;code&gt;position&lt;/code&gt;），给内容列加 &lt;code&gt;z-10&lt;/code&gt; 把它整体抬到页脚之上即可。这一幕和羡鱼文章里描述的”旧架构”问题一模一样——他后来赶上上游重构、页脚那层 transform 没了才不用加；本站页脚还有这个 transform 残留，所以这个 &lt;code&gt;z-10&lt;/code&gt; 得带上。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;换成你自己的主题/评论系统怎么修&lt;a href=&quot;#换成你自己的主题评论系统怎么修&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;不管用 Twikoo、Waline 还是 Artalk，凡是”绝对定位浮层被容器切掉”，排查套路通用：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;DevTools 选中浮层，沿祖先链找 &lt;code&gt;overflow&lt;/code&gt; 不是 visible 的那一层（通常不止一层，逐个记下来）；&lt;/li&gt;
&lt;li&gt;每一层改成 &lt;code&gt;overflow-x: clip; overflow-y: visible&lt;/code&gt;——&lt;strong&gt;不要&lt;/strong&gt;用 &lt;code&gt;overflow-x: hidden&lt;/code&gt; 配 &lt;code&gt;overflow-y: visible&lt;/code&gt;，会被降级成 auto 白改；&lt;/li&gt;
&lt;li&gt;横向想继续裁（保圆角、防横向滚动条）就靠 &lt;code&gt;clip&lt;/code&gt;，别用 &lt;code&gt;visible&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;改完如果浮层能出来但&lt;strong&gt;被下方元素盖住或点不动&lt;/strong&gt;，用元素选择器看盖住它的东西：若那个元素（或它的某个祖先）带 &lt;code&gt;transform&lt;/code&gt;、&lt;code&gt;opacity &amp;lt; 1&lt;/code&gt;、&lt;code&gt;position + z-index&lt;/code&gt; 之类生成层叠上下文的属性，给你的内容容器加个 &lt;code&gt;z-index&lt;/code&gt;（grid/flex 子项可直接写，普通元素配合 &lt;code&gt;position: relative&lt;/code&gt;）压回去；&lt;/li&gt;
&lt;li&gt;验证三件事：回复框贴底时面板完整显示、分类 tab 能点、评论卡片的圆角和装饰没有溢出。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;验证结果&lt;a href=&quot;#验证结果&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;主评论框表情面板：正常；&lt;/li&gt;
&lt;li&gt;回复框（贴近卡片底部）表情面板：完整浮出卡片，分类 tab 可见可点；&lt;/li&gt;
&lt;li&gt;卡片圆角、装饰 SVG：横向仍被裁剪，无溢出；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;astro check&lt;/code&gt; / &lt;code&gt;type-check&lt;/code&gt; / Biome lint：无新增问题。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;参考&lt;a href=&quot;#参考&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://x1anyu.cn/posts/16/&quot; target=&quot;_blank&quot;&gt;临渊羡鱼：评论区表情面板被裁、切换还抖，原因在 overflow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.w3.org/TR/css-overflow-3/&quot; target=&quot;_blank&quot;&gt;CSS Overflow Module Level 3&lt;/a&gt;（&lt;code&gt;visible&lt;/code&gt; / &lt;code&gt;clip&lt;/code&gt; 轴组合的降级规则）&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;/section&gt;</content:encoded></item><item><title>Firefly 博客实战：给文章页加阅读进度条与圆环百分比</title><link>https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-13/</link><guid isPermaLink="true">https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-13/</guid><description>从设计思路到逐文件代码，手把手为 Firefly/Astro 博客实现纯客户端阅读进度功能：顶部细条、右下角与悬浮按钮同款的圆环百分比，零网络请求，兼容 Swup 切页</description><pubDate>Mon, 28 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h1&gt;Firefly 博客实战：给文章页加阅读进度条与圆环百分比&lt;a href=&quot;#firefly-博客实战给文章页加阅读进度条与圆环百分比&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;本文记录一次完整的功能实现：为基于 Astro + Swup 的 Firefly 博客添加&lt;strong&gt;阅读进度&lt;/strong&gt;——文章页顶部一条随滚动生长的细条，右下角悬浮按钮栈最上方一个&lt;strong&gt;与现有按钮同规格的方框&lt;/strong&gt;，内部 SVG 圆环实时填充百分比。&lt;/p&gt;&lt;p&gt;功能特点：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;纯客户端计算&lt;/strong&gt;：零网络请求、零第三方依赖，不拖慢加载&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;精准语义&lt;/strong&gt;：只统计正文（横幅、评论区、推荐文章不计入）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;UI 与现有按钮一致&lt;/strong&gt;：3rem 方框、圆角、&lt;code&gt;--card-bg&lt;/code&gt; 背景、毛玻璃、细边框，尺寸断点跟随 &lt;code&gt;FloatingButton&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Swup 兼容&lt;/strong&gt;：切页不残留旧值、软导航后自动重新绑定&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可配置开关&lt;/strong&gt;：&lt;code&gt;siteConfig.post.readingProgress&lt;/code&gt; 一键启停&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;改动规模：&lt;strong&gt;7 个文件修改 + 1 个新文件&lt;/strong&gt;，净增约 100 行。本文给出每一处改动的精确位置和完整代码，可对照逐条复现。&lt;/p&gt;&lt;hr /&gt;&lt;section&gt;&lt;h2&gt;一、设计思路（先读懂再动手）&lt;a href=&quot;#一设计思路先读懂再动手&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;1. 进度怎么算&lt;a href=&quot;#1-进度怎么算&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;进度语义定义为：&lt;strong&gt;正文进入视口底边记 0%，正文底边到达视口底边记 100%&lt;/strong&gt;：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;clamp&lt;/span&gt;&lt;span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;viewportHeight&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rect&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;top&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rect&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;height&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;其中 &lt;code&gt;rect&lt;/code&gt; 是正文容器 &lt;code&gt;#post-container&lt;/code&gt; 的 &lt;code&gt;getBoundingClientRect()&lt;/code&gt;。这个公式的好处：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;横幅高度、评论区长度、推荐文章数量都不影响进度&lt;/li&gt;
&lt;li&gt;短文章（高度小于视口）也能自然收敛到 0~1&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;顶部细条用 &lt;code&gt;transform: scaleX(p)&lt;/code&gt; 驱动；圆环用 SVG &lt;code&gt;stroke-dashoffset = 周长 × (1 - p)&lt;/code&gt; 驱动——两者都不触发布局。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;2. 性能三板斧&lt;a href=&quot;#2-性能三板斧&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;这个博客的滚动管线是 &lt;code&gt;scrollFunction&lt;/code&gt;（&lt;code&gt;scroll&lt;/code&gt; 事件 rAF 节流），阅读进度&lt;strong&gt;不是自己注册监听器&lt;/strong&gt;，而是挂进现有管线，这是全部性能设计的前提：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;调用点卡在布局新鲜期&lt;/strong&gt;：&lt;code&gt;scrollFunction&lt;/code&gt; 先读 &lt;code&gt;scrollTop&lt;/code&gt;（布局脏时浏览器在此强制计算一次），紧接着读 &lt;code&gt;rect&lt;/code&gt; 命中&lt;strong&gt;同一次&lt;/strong&gt;新鲜布局——增量回流成本为 0&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;文字只在整数变化时写&lt;/strong&gt;：百分比从整数维度最多写 100 次/全程；徽章 CSS 带 &lt;code&gt;contain: content&lt;/code&gt;，文字变更的布局失效被隔离在徽章子树内，不污染正文布局。圆环的 &lt;code&gt;dashoffset&lt;/code&gt; 是 SVG 属性，只标记绘制不触发布局，可以逐帧写&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;非文章页近乎零成本&lt;/strong&gt;：靠 &lt;code&gt;isConnected&lt;/code&gt; + &lt;code&gt;null === null&lt;/code&gt; 短路，首页每帧只多一次 &lt;code&gt;getElementById&lt;/code&gt; 哈希查找（与 &lt;code&gt;scrollFunction&lt;/code&gt; 里已有的 navbar 查询同量级）&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;3. Swup 适配（这个博客的特殊点）&lt;a href=&quot;#3-swup-适配这个博客的特殊点&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;本站用 Swup 做软导航，DOM 会局部替换，处理不好会出现”换页后进度条卡在旧值”：&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;元素&lt;/th&gt;&lt;th&gt;位置&lt;/th&gt;&lt;th&gt;跨页行为&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;#reading-progress-bar&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;Layout.astro&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;不在 Swup 容器内&lt;/strong&gt;，元素持久，引用不失效&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;#reading-progress-badge&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;FloatingControls.astro&lt;/code&gt;&lt;/td&gt;&lt;td&gt;同上，持久&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;#post-container&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;#swup-container&lt;/code&gt; 内&lt;/td&gt;&lt;td&gt;&lt;strong&gt;每次换页被替换&lt;/strong&gt;，靠 &lt;code&gt;isConnected&lt;/code&gt; 自愈重查&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;再加两条 CSS：切页期间（&lt;code&gt;html.is-page-transitioning&lt;/code&gt;）同时隐藏细条和徽章，避免旧页百分比残值闪现；&lt;code&gt;scrollFunction&lt;/code&gt; 在过渡期本来就提前 return，&lt;code&gt;visit:end&lt;/code&gt; 后恢复调用并刷新。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;4. 三个补测时机&lt;a href=&quot;#4-三个补测时机&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;有些场景&lt;strong&gt;没有滚动事件&lt;/strong&gt;，需要主动补算：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;图片加载 / 代码块展开 / 加密文章解密&lt;/strong&gt; → 正文高度变了但用户没滚 → &lt;code&gt;ResizeObserver&lt;/code&gt; 盯正文容器&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;移动端地址栏收起&lt;/strong&gt; → 视口高度变了但正文没变 → 单独听 &lt;code&gt;resize&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;沉浸阅读切换&lt;/strong&gt; → 正文位置变了但可能停在同一滚动位置 → 监听 &lt;code&gt;immersiveReadingChange&lt;/code&gt; 事件，立即算一次 + 400ms 过渡结束后再算一次&lt;/li&gt;
&lt;/ul&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;二、改动总览&lt;a href=&quot;#二改动总览&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;#&lt;/th&gt;&lt;th&gt;文件&lt;/th&gt;&lt;th&gt;改动&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;&lt;code&gt;src/types/siteConfig.ts&lt;/code&gt;&lt;/td&gt;&lt;td&gt;类型：加 &lt;code&gt;readingProgress: boolean&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;&lt;code&gt;src/config/siteConfig.ts&lt;/code&gt;&lt;/td&gt;&lt;td&gt;配置：加 &lt;code&gt;readingProgress: true&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;&lt;code&gt;src/utils/reading-progress.ts&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;新文件&lt;/strong&gt;，核心逻辑&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;&lt;code&gt;src/utils/scroll-utils.ts&lt;/code&gt;&lt;/td&gt;&lt;td&gt;滚动管线接入（2 处）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;&lt;code&gt;src/utils/layout-init.ts&lt;/code&gt;&lt;/td&gt;&lt;td&gt;初始化调用（2 处）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;&lt;code&gt;src/layouts/Layout.astro&lt;/code&gt;&lt;/td&gt;&lt;td&gt;顶部条 DOM（1 行）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;7&lt;/td&gt;&lt;td&gt;&lt;code&gt;src/styles/transition.css&lt;/code&gt;&lt;/td&gt;&lt;td&gt;顶部条样式（文件末尾追加）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;8&lt;/td&gt;&lt;td&gt;&lt;code&gt;src/components/controls/FloatingControls.astro&lt;/code&gt;&lt;/td&gt;&lt;td&gt;圆环徽章 DOM + 样式&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;三、分步实现&lt;a href=&quot;#三分步实现&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;Step 1：配置开关（2 个文件）&lt;a href=&quot;#step-1配置开关2-个文件&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;src/types/siteConfig.ts&lt;/code&gt;&lt;/strong&gt; —— 在 &lt;code&gt;post&lt;/code&gt; 类型区块中，&lt;code&gt;generateOgImages: boolean;&lt;/code&gt; 之后添加：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// 是否显示阅读进度（顶部进度条 + 百分比 + 预计剩余时间）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;readingProgress&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;src/config/siteConfig.ts&lt;/code&gt;&lt;/strong&gt; —— 在 &lt;code&gt;post&lt;/code&gt; 配置区块中，&lt;code&gt;generateOgImages: true,&lt;/code&gt; 之后添加：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// 阅读进度：文章页顶部细条 + 悬浮百分比与预计剩余时间（纯客户端计算，无网络请求）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;readingProgress&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;&lt;code&gt;SiteConfig&lt;/code&gt; 是严格类型，只改 config 不改类型会直接 type-check 报错，两处必须成对加。&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;Step 2：核心模块（新文件）&lt;a href=&quot;#step-2核心模块新文件&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;新建 &lt;strong&gt;&lt;code&gt;src/utils/reading-progress.ts&lt;/code&gt;&lt;/strong&gt;，完整内容如下（可整段复制）：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 阅读进度（顶部细条 + 右下角圆环百分比）。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 进度语义：正文进入视口底边为 0%，正文底边到达视口底边为 100%，&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 即 p = clamp((viewportHeight - rect.top) / rect.height, 0, 1)——&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 横幅、评论区、推荐文章不计入。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 每帧成本：一次 getBoundingClientRect + 算术。scrollFunction 先读 scrollTop&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*（布局脏时已在此强制计算），随后本函数的 rect 读取命中同一次新鲜布局，不触发额外回流。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 百分比文字仅在整数变化时写入，且 badge 带 contain:content，文字变更的布局失效&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 被限制在 badge 子树内，不污染正文布局；圆环 dashoffset 走 SVG 属性（不触发布局）。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 正文元素被 Swup 替换后靠 isConnected 自愈重新查询，非文章页每帧仅一次&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* getElementById 哈希查找（与 scrollFunction 中已有的 navbar 查询同量级）。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;initialized&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;enabled&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bar&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;HTMLElement&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;badge&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;HTMLElement&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pctEl&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;HTMLElement&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ringEl&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SVGCircleElement&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;article&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;HTMLElement&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;observer&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ResizeObserver&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;active&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lastP&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lastPct&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;30&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/** 圆环周长 2πr（r=15，与 SVG viewBox 一致） */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;31&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;RING_C&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Math&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;PI&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;15&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;32&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;33&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/** Swup 换页/解密后旧引用断开 → 重查并重绑 ResizeObserver；非文章页 null===null 直接短路 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;34&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;resolveArticle&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;35&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;article&lt;/span&gt;&lt;span&gt;?.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;isConnected&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;36&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;next&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;document&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;getElementById&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;post-container&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;37&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;next&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;article&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;38&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;article&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;next&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;39&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;lastPct&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;40&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;observer&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;41&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;observer&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;disconnect&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;42&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;article&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;observer&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;observe&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;article&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;43&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;44&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;45&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;46&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;setActive&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;next&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;boolean&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;47&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;active&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;next&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;48&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;active&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;next&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;49&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;lastPct&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;50&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;bar&lt;/span&gt;&lt;span&gt;?.&lt;/span&gt;&lt;span&gt;classList&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;toggle&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;visible&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;next&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;51&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;badge&lt;/span&gt;&lt;span&gt;?.&lt;/span&gt;&lt;span&gt;classList&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;toggle&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;show&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;next&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;52&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;53&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;54&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;initReadingProgress&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;55&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;initialized&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;56&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;initialized&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;57&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;58&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// 配置关闭时 DOM 不渲染；查询不到即永久空转&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;59&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;bar&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;document&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;getElementById&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;reading-progress-bar&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;60&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;badge&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;document&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;getElementById&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;reading-progress-badge&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;61&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pctEl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;document&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;getElementById&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;reading-progress-pct&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;62&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ringEl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;document&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;getElementById&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;63&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;reading-progress-ring&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;64&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SVGCircleElement&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;65&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;&lt;span&gt;bar&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;&lt;span&gt;badge&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;&lt;span&gt;pctEl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;&lt;span&gt;ringEl&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;66&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;67&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;enabled&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;68&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;69&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;typeof&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ResizeObserver&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;!==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;undefined&quot;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;70&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// 正文高度变化（图片加载、代码块展开、解密）后无滚动也能重算&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;71&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;observer&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ResizeObserver&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;updateReadingProgress&lt;/span&gt;&lt;span&gt;());&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;72&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;73&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;74&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// 移动端地址栏收起只改视口高度、不改正文尺寸 → 单独听 resize&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;75&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;window&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;resize&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;updateReadingProgress&lt;/span&gt;&lt;span&gt;, { &lt;/span&gt;&lt;/span&gt;&lt;span&gt;passive&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt; });&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;76&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// 沉浸阅读切换改变正文位置且可能落在同一滚动位置（无 scroll 事件）→ 立即 + 过渡结束后各算一次&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;77&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;document&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;immersiveReadingChange&quot;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;78&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;updateReadingProgress&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;79&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;window&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;setTimeout&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;updateReadingProgress&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;400&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;80&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;81&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;82&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;updateReadingProgress&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;83&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;84&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;85&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/** 由 scrollFunction 每帧调用；页面过渡期间 scrollFunction 提前返回，不会执行到这里 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;86&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;updateReadingProgress&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;87&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// enabled 为 true 时下列元素必然存在，显式判空仅为满足 TS 收窄&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;88&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;&lt;span&gt;enabled&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;&lt;span&gt;bar&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;&lt;span&gt;badge&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;&lt;span&gt;pctEl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;&lt;span&gt;ringEl&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;89&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;90&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;resolveArticle&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;91&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;&lt;span&gt;article&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;92&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;setActive&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;93&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;94&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;95&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;96&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rect&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;article&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;getBoundingClientRect&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;97&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;rect&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;height&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;lt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;98&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;99&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Math&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;min&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;100&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;101&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Math&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;max&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;&lt;span&gt;, (&lt;/span&gt;&lt;span&gt;window&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;innerHeight&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rect&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;top&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rect&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;height&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;102&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;103&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;setActive&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;104&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;105&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// transform/dashoffset 只影响合成与绘制，不触发布局，逐帧写入开销可忽略&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;106&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;!==&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lastP&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;107&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;lastP&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;108&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;bar&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;transform&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`scaleX(&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;)`&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;109&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;ringEl&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;strokeDashoffset&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;RING_C&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;));&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;110&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;111&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;112&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// 百分比文字仅整数变化时写，配合 contain:content 把回流限制在徽章内&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;113&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pct&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Math&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;round&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;114&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;pct&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;!==&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lastPct&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;115&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;lastPct&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pct&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;116&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pctEl&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;textContent&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;pct&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;%`&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;117&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;118&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;模块导出两个函数：&lt;code&gt;initReadingProgress&lt;/code&gt;（一次性初始化）和 &lt;code&gt;updateReadingProgress&lt;/code&gt;（每帧调用）。模块级缓存了 DOM 引用、正文引用、&lt;code&gt;ResizeObserver&lt;/code&gt; 实例和两个”上次写入值”（&lt;code&gt;lastP&lt;/code&gt; / &lt;code&gt;lastPct&lt;/code&gt;），全部写入都先比对再落 DOM。&lt;/p&gt;&lt;p&gt;两个细节：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;getElementById&lt;/code&gt; 返回类型是 &lt;code&gt;HTMLElement&lt;/code&gt;，赋给 &lt;code&gt;SVGCircleElement&lt;/code&gt; 需要显式断言（&lt;code&gt;as SVGCircleElement | null&lt;/code&gt;），否则 type-check 报 TS2322&lt;/li&gt;
&lt;li&gt;圆环周长常量 &lt;code&gt;RING_C = 2π×15 ≈ 94.2478&lt;/code&gt; 必须与 Step 6 中 SVG 的 &lt;code&gt;r=&quot;15&quot;&lt;/code&gt; 保持一致，改半径要同步改这里和 CSS 里的 &lt;code&gt;stroke-dasharray&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;Step 3：接入滚动管线（&lt;code&gt;src/utils/scroll-utils.ts&lt;/code&gt;）&lt;a href=&quot;#step-3接入滚动管线srcutilsscroll-utilsts&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;两处改动。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;第一处&lt;/strong&gt;，文件顶部 import 区，加入：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;updateReadingProgress&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;@/utils/reading-progress&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;第二处&lt;/strong&gt;，&lt;code&gt;scrollFunction&lt;/code&gt; 函数内，找到这段：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;scrollTop&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;document&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;documentElement&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;scrollTop&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bannerHeight&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;window&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;innerHeight&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;BANNER_HEIGHT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;navbarElement&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;document&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;getElementById&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;navbar&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// 根据滚动位置动态更新侧边栏 sticky 间距&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;updateSidebarStickySpacing&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;在 &lt;code&gt;navbarElement&lt;/code&gt; 之后、&lt;code&gt;updateSidebarStickySpacing()&lt;/code&gt; &lt;strong&gt;之前&lt;/strong&gt;插入调用：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;scrollTop&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;document&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;documentElement&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;scrollTop&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bannerHeight&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;window&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;innerHeight&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;BANNER_HEIGHT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;navbarElement&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;document&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;getElementById&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;navbar&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// 阅读进度：紧跟 scrollTop 读取（布局已新鲜），后续 DOM 写入前完成 rect 测量&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;updateReadingProgress&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// 根据滚动位置动态更新侧边栏 sticky 间距&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;updateSidebarStickySpacing&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;位置很关键&lt;/strong&gt;：必须在所有 DOM 写入之前完成 rect 测量。&lt;code&gt;scrollTop&lt;/code&gt; 读取已经把布局算干净了，此时 &lt;code&gt;getBoundingClientRect&lt;/code&gt; 不会触发新的回流；如果挪到函数末尾（一堆 class 写入之后），每帧会多付一次强制布局。&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;Step 4：初始化（&lt;code&gt;src/utils/layout-init.ts&lt;/code&gt;）&lt;a href=&quot;#step-4初始化srcutilslayout-initts&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;两处改动。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;import 区&lt;/strong&gt;加：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;initReadingProgress&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;@/utils/reading-progress&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;initLayout()&lt;/code&gt; 函数内&lt;/strong&gt;，&lt;code&gt;initScroll()&lt;/code&gt; 之后紧跟调用：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;initScroll&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;initReadingProgress&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;&lt;code&gt;initLayout&lt;/code&gt; 有 &lt;code&gt;window.__fireflyLayoutInit&lt;/code&gt; 守卫只会执行一次，&lt;code&gt;initReadingProgress&lt;/code&gt; 内部也有 &lt;code&gt;initialized&lt;/code&gt; 双保险。注意顺序：&lt;code&gt;initScroll()&lt;/code&gt; 注册监听后会立即调一次 &lt;code&gt;scrollFunction&lt;/code&gt;，此时阅读进度尚未初始化、&lt;code&gt;enabled&lt;/code&gt; 为 false 会静默跳过——紧接着的 &lt;code&gt;initReadingProgress()&lt;/code&gt; 自己会补一次首帧计算，所以顺序必须是 &lt;code&gt;initScroll&lt;/code&gt; 在前。&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;Step 5：顶部细条 DOM + 样式（2 个文件）&lt;a href=&quot;#step-5顶部细条-dom--样式2-个文件&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;src/layouts/Layout.astro&lt;/code&gt;&lt;/strong&gt; —— 找到 Swup 导航进度条，在其&lt;strong&gt;后面&lt;/strong&gt;加一行：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;progress-bar&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;siteConfig&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;readingProgress&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;reading-progress-bar&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;aria-hidden&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;（&lt;code&gt;siteConfig&lt;/code&gt; 该文件本来就有 import，无需新增。）&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;src/styles/transition.css&lt;/code&gt;&lt;/strong&gt; —— 文件末尾追加：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/* --------------------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;4b. Reading Progress Bar (post pages)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;-------------------------------------------------------------------------- */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;#reading-progress-bar&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;--reading-progress-height&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;position: &lt;/span&gt;&lt;span&gt;fixed&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;top: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;left: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;width: &lt;/span&gt;&lt;span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;height: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--reading-progress-height&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;/* 低于 #progress-bar(9999)：切页时由导航进度条覆盖，同色无缝衔接 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;z-index: &lt;/span&gt;&lt;span&gt;9998&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pointer-events: &lt;/span&gt;&lt;span&gt;none&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;background: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--primary&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;transform-origin: &lt;/span&gt;&lt;span&gt;left&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;center&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;transform: &lt;/span&gt;&lt;span&gt;scaleX&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;opacity: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;transition: opacity &lt;/span&gt;&lt;span&gt;&lt;span&gt;0.3&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ease&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;will-change: transform;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;#reading-progress-bar&lt;/span&gt;&lt;span&gt;.visible&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;opacity: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/* 切页期间隐藏阅读进度条，避免旧页残值与导航进度条混显；visit:end 移除类后淡入 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;.is-page-transitioning&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;#reading-progress-bar&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;30&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;opacity: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;31&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;样式要点：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;z-index 9998 vs Swup 条的 9999&lt;/strong&gt;：切页时导航进度条永远压在阅读条上面，两者同为 &lt;code&gt;var(--primary)&lt;/code&gt; 颜色，视觉无缝&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;transition&lt;/code&gt; 只写了 &lt;code&gt;opacity&lt;/code&gt;&lt;/strong&gt;：transform 不能加过渡，否则进度条会”追不上”滚动&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;html.is-page-transitioning&lt;/code&gt; 规则优先级 (1,1,1) 高于 &lt;code&gt;.visible&lt;/code&gt; 的 (1,1,0)&lt;/strong&gt;：切页时无需 JS 介入即可强制隐藏&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;Step 6：圆环徽章（&lt;code&gt;src/components/controls/FloatingControls.astro&lt;/code&gt;）&lt;a href=&quot;#step-6圆环徽章srccomponentscontrolsfloatingcontrolsastro&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;徽章放在右下角悬浮按钮栈的&lt;strong&gt;第一项&lt;/strong&gt;（最顶部），样式与 &lt;code&gt;FloatingButton&lt;/code&gt;（&lt;code&gt;.floating-btn&lt;/code&gt;）同规格：3rem 方框、1rem 圆角、&lt;code&gt;--card-bg&lt;/code&gt; 背景、12px 毛玻璃、细边框——远看就是第五个按钮，只是内部是圆环而非图标。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;import 区无需改动&lt;/strong&gt;（徽章不依赖 i18n），frontmatter 里 &lt;code&gt;immersiveReadingEnabled&lt;/code&gt; 之后加一行：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;readingProgressEnabled&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;siteConfig&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;readingProgress&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;模板&lt;/strong&gt;里，&lt;code&gt;.floating-controls-container&lt;/code&gt; 内第一项插入：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;floating-controls-container&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;&lt;span&gt;readingProgressEnabled&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;reading-progress-badge&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;reading-progress-badge card-base&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;aria-hidden&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;!-- 类名用 rp- 前缀：.ring 会撞 Tailwind 的 .ring 焦点环工具类（box-shadow 1px currentColor） --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;svg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;rp-ring&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;viewBox&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;0 0 36 36&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;circle&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;rp-ring-track&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cx&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;18&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cy&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;18&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;15&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;circle&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;reading-progress-ring&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;rp-ring-value&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cx&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;18&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;cy&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;18&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;15&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;svg&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;reading-progress-pct&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;0%&amp;lt;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;FloatingTOC&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;headings&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;&lt;span&gt;headings&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;??&lt;/span&gt;&lt;span&gt; []&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;encrypted&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;encrypted&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;!-- ……其余原有内容不动…… --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; 块开头&lt;/strong&gt;（&lt;code&gt;.floating-controls-container&lt;/code&gt; 规则之前）插入徽章样式：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;/*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 阅读进度圆环：与 .floating-btn 同规格的方框。背景/圆角/overflow 交给 card-base——&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 卡片透明度、卡片边框等全局设置都通过 .card-base 类挂钩，必须带上才能跟随这些设置。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* 本组件只补尺寸、毛玻璃、边框与内容布局。不可交互（pointer-events: none），无 hover。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;#reading-progress-badge&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;display: &lt;/span&gt;&lt;span&gt;none&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;place-items: &lt;/span&gt;&lt;span&gt;center&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;position: &lt;/span&gt;&lt;span&gt;relative&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;width: &lt;/span&gt;&lt;span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;height: &lt;/span&gt;&lt;span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;flex-shrink: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;border: &lt;/span&gt;&lt;span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;solid&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rgba&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.1&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;backdrop-filter: &lt;/span&gt;&lt;span&gt;blur&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;12&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;-webkit-backdrop-filter&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;blur&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;12&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;color: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--primary&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pointer-events: &lt;/span&gt;&lt;span&gt;none&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;user-select: &lt;/span&gt;&lt;span&gt;none&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;/* 环与文字的布局失效限制在徽章子树内，不污染正文布局 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;contain: &lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;#reading-progress-badge&lt;/span&gt;&lt;span&gt;.show&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;display: &lt;/span&gt;&lt;span&gt;grid&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;/* 切页期间隐藏，visit:end 移除类并刷新后重新显示，避免旧页百分比残值闪现 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;:global(&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;.is-page-transitioning&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;#reading-progress-badge&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;display: &lt;/span&gt;&lt;span&gt;none&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;30&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;31&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;32&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;/* SVG 与数字叠放（同 FloatingButton 双图标的手法：grid 同格） */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;33&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;#reading-progress-badge&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.rp-ring&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;34&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;#reading-progress-badge&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;#reading-progress-pct&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;35&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;grid-area: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; / &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;36&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;37&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;38&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.rp-ring&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;39&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;width: &lt;/span&gt;&lt;span&gt;&lt;span&gt;83&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;40&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;height: &lt;/span&gt;&lt;span&gt;&lt;span&gt;83&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;41&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;/* 从 12 点方向起笔 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;42&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;transform: &lt;/span&gt;&lt;span&gt;rotate&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;-90&lt;/span&gt;&lt;span&gt;deg&lt;/span&gt;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;43&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;44&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;45&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.rp-ring-track&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;46&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.rp-ring-value&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;47&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fill: &lt;/span&gt;&lt;span&gt;none&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;48&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;stroke-width: &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;49&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;50&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;51&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.rp-ring-track&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;52&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;stroke: &lt;/span&gt;&lt;span&gt;rgba&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.1&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;53&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;54&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;55&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;.rp-ring-value&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;56&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;stroke: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--primary&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;57&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;stroke-linecap: &lt;/span&gt;&lt;span&gt;round&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;58&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;/* 周长 2πr（r=15）≈ 94.2478；初始 dashoffset = 周长 = 0% */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;59&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;stroke-dasharray: &lt;/span&gt;&lt;span&gt;94.2478&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;60&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;stroke-dashoffset: &lt;/span&gt;&lt;span&gt;94.2478&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;61&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;62&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;63&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;#reading-progress-pct&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;64&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;font-size: &lt;/span&gt;&lt;span&gt;&lt;span&gt;0.6875&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;65&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;font-weight: &lt;/span&gt;&lt;span&gt;600&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;66&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;line-height: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;67&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;font-variant-numeric: &lt;/span&gt;&lt;span&gt;tabular-nums&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;68&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;69&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;70&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;:global(&lt;/span&gt;&lt;span&gt;.dark&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;#reading-progress-badge&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;71&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;border-color: &lt;/span&gt;&lt;span&gt;rgba&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;255&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;255&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;255&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.15&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;72&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;73&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;74&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;:global(&lt;/span&gt;&lt;span&gt;.dark&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;.rp-ring-track&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;75&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;stroke: &lt;/span&gt;&lt;span&gt;rgba&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;255&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;255&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;255&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.15&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;76&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;77&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;78&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;/* 尺寸断点跟随 FloatingButton（480 → 2.5rem，360 → 2rem，320 → 1.75rem） */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;79&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;@media&lt;/span&gt;&lt;span&gt; (max-width: &lt;/span&gt;&lt;span&gt;&lt;span&gt;480&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;80&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;#reading-progress-badge&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;81&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;width: &lt;/span&gt;&lt;span&gt;&lt;span&gt;2.5&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;82&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;height: &lt;/span&gt;&lt;span&gt;&lt;span&gt;2.5&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;83&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;84&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;85&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;#reading-progress-pct&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;86&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;font-size: &lt;/span&gt;&lt;span&gt;&lt;span&gt;0.625&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;87&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;88&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;89&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;90&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;@media&lt;/span&gt;&lt;span&gt; (max-width: &lt;/span&gt;&lt;span&gt;&lt;span&gt;360&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;91&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;#reading-progress-badge&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;92&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;width: &lt;/span&gt;&lt;span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;93&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;height: &lt;/span&gt;&lt;span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;94&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;95&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;96&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;#reading-progress-pct&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;97&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;font-size: &lt;/span&gt;&lt;span&gt;&lt;span&gt;0.5625&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;98&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;99&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;100&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;101&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;@media&lt;/span&gt;&lt;span&gt; (max-width: &lt;/span&gt;&lt;span&gt;&lt;span&gt;320&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;102&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;#reading-progress-badge&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;103&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;width: &lt;/span&gt;&lt;span&gt;&lt;span&gt;1.75&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;104&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;height: &lt;/span&gt;&lt;span&gt;&lt;span&gt;1.75&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;105&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;106&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;107&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;#reading-progress-pct&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;108&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;font-size: &lt;/span&gt;&lt;span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;109&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;110&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;设计要点：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;必须带 &lt;code&gt;card-base&lt;/code&gt; 类&lt;/strong&gt;：本站”卡片透明度”滑块的生效链是 &lt;code&gt;--card-transparent-opacity&lt;/code&gt; → &lt;code&gt;--card-bg-transparent&lt;/code&gt; → &lt;code&gt;main.css&lt;/code&gt; 中的 &lt;code&gt;.wallpaper-transparent .card-base { background-color: var(--card-bg-transparent) !important }&lt;/code&gt;——透明背景、卡片边框等全局设置&lt;strong&gt;全部通过 &lt;code&gt;.card-base&lt;/code&gt; 类挂钩&lt;/strong&gt;。徽章的背景/圆角/&lt;code&gt;overflow&lt;/code&gt; 因此直接交给 &lt;code&gt;card-base&lt;/code&gt; 提供（&lt;code&gt;--radius-large&lt;/code&gt; 也等于 1rem），组件自身只写尺寸、毛玻璃、边框和内容布局；漏掉这个类，徽章就会游离在透明度体系之外，和其余按钮表现不一致&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;与 &lt;code&gt;.floating-btn&lt;/code&gt; 同规格&lt;/strong&gt;：3rem / 1rem 圆角 / &lt;code&gt;--card-bg&lt;/code&gt; / blur(12px) / 1px 细边框 / 尺寸断点（480→2.5rem、360→2rem、320→1.75rem）全部对齐，放进按钮栈毫无违和&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SVG 双圆环&lt;/strong&gt;：&lt;code&gt;rp-ring-track&lt;/code&gt; 是底环（淡色轨道），&lt;code&gt;rp-ring-value&lt;/code&gt; 是进度环（&lt;code&gt;--primary&lt;/code&gt; 色、圆头线帽）。整只 SVG &lt;code&gt;rotate(-90deg)&lt;/code&gt; 让进度从 12 点方向起笔&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;类名必须避开 Tailwind 工具类&lt;/strong&gt;：最初用的 &lt;code&gt;.ring&lt;/code&gt; 正好撞上 Tailwind 的 &lt;code&gt;.ring&lt;/code&gt;（&lt;code&gt;box-shadow: 0 0 0 1px currentColor&lt;/code&gt;），导致 SVG 外围出现一圈橙色直角描边——详见坑 6&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;stroke-dasharray&lt;/code&gt; + &lt;code&gt;dashoffset&lt;/code&gt;&lt;/strong&gt;：&lt;code&gt;dasharray = 周长&lt;/code&gt; 把圆环变成一条整长虚线，&lt;code&gt;dashoffset&lt;/code&gt; 从周长（完全看不见 = 0%）收到 0（完整闭合 = 100%）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数字与环同格叠放&lt;/strong&gt;：&lt;code&gt;grid-area: 1 / 1&lt;/code&gt;——与 &lt;code&gt;FloatingButton&lt;/code&gt; 双图标（default/toggled 两个 SVG 同格切换）同一手法&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;font-variant-numeric: tabular-nums&lt;/code&gt;&lt;/strong&gt;：等宽数字防止 9%→10% 时文字宽度抖动&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;aria-hidden=&quot;true&quot;&lt;/code&gt;&lt;/strong&gt;：纯视觉进度，避免读屏被频繁变化的数字打扰&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;默认 &lt;code&gt;display: none&lt;/code&gt;，&lt;code&gt;.show&lt;/code&gt; 才 &lt;code&gt;grid&lt;/code&gt;&lt;/strong&gt;：非文章页徽章完全不占位，悬浮按钮栈不留空隙&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Astro 作用域样式&lt;/strong&gt;：&lt;code&gt;.dark&lt;/code&gt; 和 &lt;code&gt;html.is-page-transitioning&lt;/code&gt; 是全局祖先选择器，必须用 &lt;code&gt;:global()&lt;/code&gt; 包裹，否则匹配不到&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;至此全部改动完成。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;四、验证&lt;a href=&quot;#四验证&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;1. 静态检查&lt;a href=&quot;#1-静态检查&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pnpm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type-check&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;预期&lt;/strong&gt;：与本功能相关的文件 0 错误。注意本仓库存在一批&lt;strong&gt;存量错误&lt;/strong&gt;（与本功能无关，改动前就存在），复现时不要误判：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;en/ja/ko/ru/zh_TW.ts&lt;/code&gt;：缺 &lt;code&gt;siteStatsTotalViews&lt;/code&gt; / &lt;code&gt;siteStatsTotalVisitors&lt;/code&gt; 两个旧键（只有 zh_CN 有）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;anime.astro&lt;/code&gt; / &lt;code&gt;vndb.astro&lt;/code&gt;：引用了不存在的 &lt;code&gt;I18nKey.anime*&lt;/code&gt;、&lt;code&gt;siteConfig.anime&lt;/code&gt;、&lt;code&gt;vndb.nsfw&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;如果在核心模块遇到 &lt;code&gt;TS2322: Type &apos;HTMLElement | null&apos; is not assignable to type &apos;SVGCircleElement&apos;&lt;/code&gt;，说明漏了 Step 2 里的 &lt;code&gt;as SVGCircleElement | null&lt;/code&gt; 断言。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;2. 手动功能验证&lt;a href=&quot;#2-手动功能验证&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pnpm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dev&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;打开任意文章页（如 &lt;code&gt;/posts/markdown-extended/&lt;/code&gt;），逐项核对：&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;#&lt;/th&gt;&lt;th&gt;操作&lt;/th&gt;&lt;th&gt;预期&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;页面加载&lt;/td&gt;&lt;td&gt;顶部无进度条、右下角无圆环徽章&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;向下滚动&lt;/td&gt;&lt;td&gt;顶部 3px 细条从左往右生长；徽章圆环从 12 点方向顺时针填充，中心数字 &lt;code&gt;N%&lt;/code&gt; 实时跳动&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;滚到正文结束（评论区上方）&lt;/td&gt;&lt;td&gt;细条满宽、圆环闭合成整圆、数字停在 &lt;code&gt;100%&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;滚回顶部&lt;/td&gt;&lt;td&gt;细条缩回、圆环清空、数字回 &lt;code&gt;0%&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;导航到首页&lt;/td&gt;&lt;td&gt;徽章和细条消失&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;从首页点回文章（Swup 软导航）&lt;/td&gt;&lt;td&gt;徽章重新出现且数值正确，&lt;strong&gt;无旧值闪现&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;7&lt;/td&gt;&lt;td&gt;两篇文章之间切换&lt;/td&gt;&lt;td&gt;数值立即反映新文章，不残留上一篇的百分比&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;8&lt;/td&gt;&lt;td&gt;切换暗色模式&lt;/td&gt;&lt;td&gt;方框边框与底环切换为暗色配色（&lt;code&gt;rgba(255,255,255,0.15)&lt;/code&gt;）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;9&lt;/td&gt;&lt;td&gt;开启沉浸阅读再退出&lt;/td&gt;&lt;td&gt;进度立即重算（监听了 &lt;code&gt;immersiveReadingChange&lt;/code&gt;）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;10&lt;/td&gt;&lt;td&gt;窗口缩放 / 手机模拟器改视口&lt;/td&gt;&lt;td&gt;进度立即重算；480px 以下徽章缩为 2.5rem、360px 缩为 2rem&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;11&lt;/td&gt;&lt;td&gt;对比右下角其余按钮&lt;/td&gt;&lt;td&gt;同尺寸、同圆角、同背景毛玻璃、同边框，视觉上是”第五个按钮”&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;3. 关闭功能验证&lt;a href=&quot;#3-关闭功能验证&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;把 &lt;code&gt;src/config/siteConfig.ts&lt;/code&gt; 中 &lt;code&gt;readingProgress&lt;/code&gt; 改为 &lt;code&gt;false&lt;/code&gt; 后：细条和徽章的 DOM 都不再渲染，控制台无报错（&lt;code&gt;initReadingProgress&lt;/code&gt; 查不到元素后静默空转）。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;五、避坑记录&lt;a href=&quot;#五避坑记录&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;坑 1：&lt;code&gt;pnpm lint&lt;/code&gt; 会顺手改你的无关文件&lt;a href=&quot;#坑-1pnpm-lint-会顺手改你的无关文件&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;本仓库的 lint 脚本是 &lt;code&gt;biome check --write ./src ./scripts&lt;/code&gt;——&lt;strong&gt;带 &lt;code&gt;--write&lt;/code&gt; 的全局自动修复&lt;/strong&gt;。实际执行时它顺带格式化了 21 个与本功能无关的文件（music-player、各种 config 等），不得不逐一 &lt;code&gt;git checkout&lt;/code&gt; 回滚。&lt;/p&gt;&lt;p&gt;复现时建议只对自己的文件做&lt;strong&gt;只读&lt;/strong&gt;校验：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;npx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;biome&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;check&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;src/utils/reading-progress.ts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;src/utils/scroll-utils.ts&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;或者先 &lt;code&gt;git stash&lt;/code&gt; 记下基线，跑完 lint 后用 &lt;code&gt;git diff --stat&lt;/code&gt; 检查波及范围。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;坑 2：i18n 加键必须六个语言文件同步&lt;a href=&quot;#坑-2i18n-加键必须六个语言文件同步&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;本功能最终没有引入任何 i18n 文案（圆环只显示数字），但仓库里 &lt;code&gt;pnpm type-check&lt;/code&gt; 会报一批 &lt;code&gt;TS2739&lt;/code&gt; 存量错误——成因是 &lt;code&gt;src/i18n/translation.ts&lt;/code&gt; 的 &lt;code&gt;Translation&lt;/code&gt; 类型是全键映射：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Translation&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;K&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;I18nKey&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;每加一个枚举键，六个语言文件必须同时补全&lt;/strong&gt;，漏一个就报错（存量 &lt;code&gt;siteStats*&lt;/code&gt; 错误正是只加了枚举和 zh_CN）。复现本文时不需要动 i18n，但以后加文案要记住这条。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;坑 3：进度条的调用位置不能挪&lt;a href=&quot;#坑-3进度条的调用位置不能挪&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;code&gt;updateReadingProgress()&lt;/code&gt; 必须放在 &lt;code&gt;scrollFunction&lt;/code&gt; 中&lt;strong&gt;所有 class 写入之前&lt;/strong&gt;。本站滚动管线的约定是”先批量读、再批量写”（写操作 deferred 到嵌套 rAF），如果把 rect 测量挪到写操作之后，每帧都会强制重排一次——正是 &lt;code&gt;swup-transitions.ts&lt;/code&gt; 注释里警告过的”大型文章 DOM 同步重排”问题。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;坑 4：&lt;code&gt;transition&lt;/code&gt; 属性别写成 &lt;code&gt;all&lt;/code&gt;&lt;a href=&quot;#坑-4transition-属性别写成-all&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;顶部条 CSS 若写 &lt;code&gt;transition: all .3s&lt;/code&gt;，&lt;code&gt;transform&lt;/code&gt; 也会被过渡——进度条会滞后于滚动约 300ms。必须显式只写 &lt;code&gt;transition: opacity .3s&lt;/code&gt;。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;坑 5：SVG 半径三处联动&lt;a href=&quot;#坑-5svg-半径三处联动&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;圆环半径 &lt;code&gt;r=15&lt;/code&gt; 出现在三个地方，改一个必须同步改全部：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;模板 &lt;code&gt;viewBox=&quot;0 0 36 36&quot;&lt;/code&gt; 与 &lt;code&gt;r=&quot;15&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;核心模块 &lt;code&gt;const RING_C = 2 * Math.PI * 15&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;样式 &lt;code&gt;.rp-ring-value { stroke-dasharray: 94.2478 }&lt;/code&gt;（= 2π×15）&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;任何一处不一致，圆环都会填不满或超过 100%。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;坑 6：类名撞了 Tailwind 工具类（真实踩坑）&lt;a href=&quot;#坑-6类名撞了-tailwind-工具类真实踩坑&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;初版给 SVG 用的类名是 &lt;code&gt;.ring&lt;/code&gt;，结果徽章外围出现一圈&lt;strong&gt;橙色直角描边&lt;/strong&gt;，与预期的圆角按钮完全不像。排查过程值得记录：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;先怀疑自己的 &lt;code&gt;border&lt;/code&gt;/&lt;code&gt;border-radius&lt;/code&gt; 没生效——抓取页面编译后的 scoped CSS，规则完整输出，排除&lt;/li&gt;
&lt;li&gt;再全局搜索 &lt;code&gt;.ring&lt;/code&gt; 的定义——&lt;strong&gt;实锤&lt;/strong&gt;：页面 CSS（Tailwind v4）里有工具类&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.ring&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;--tw-ring-shadow&lt;/span&gt;&lt;span&gt;: ... &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;calc&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--tw-ring-offset-width&lt;/span&gt;&lt;span&gt;)) &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--tw-ring-color&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;currentcolor&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;box-shadow: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--tw-ring-shadow&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;Tailwind 的 &lt;code&gt;.ring&lt;/code&gt; 是焦点环工具类：&lt;code&gt;box-shadow 0 0 0 1px currentColor&lt;/code&gt;。三个要素全部命中截图症状：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;currentColor&lt;/code&gt; = 元素的 &lt;code&gt;color&lt;/code&gt; = &lt;code&gt;var(--primary)&lt;/code&gt; = &lt;strong&gt;橙色&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;box-shadow&lt;/code&gt; 扩散出的轮廓是矩形（SVG 元素没有圆角）→ &lt;strong&gt;直角&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;扩散只 1px 且紧贴 SVG 边界 → 比按钮&lt;strong&gt;小一圈&lt;/strong&gt;的”奇怪方框”&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;而且我 scoped 里的 &lt;code&gt;.ring&lt;/code&gt; 规则只设了 &lt;code&gt;width/height/transform&lt;/code&gt;，没设 &lt;code&gt;box-shadow&lt;/code&gt;——两条规则各管各的属性，scoped 样式”生效了”也压不掉它。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;修复&lt;/strong&gt;：类名换 &lt;code&gt;rp-&lt;/code&gt; 前缀（&lt;code&gt;rp-ring&lt;/code&gt; / &lt;code&gt;rp-ring-track&lt;/code&gt; / &lt;code&gt;rp-ring-value&lt;/code&gt;），彻底避开 Tailwind 命名空间。JS 用的是 &lt;code&gt;id=&quot;reading-progress-ring&quot;&lt;/code&gt;（id 不与类名冲突），核心模块无需改动。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;教训&lt;/strong&gt;：这个项目全局启用 Tailwind，起 class 名前先搜一遍目标名字是否是 Tailwind utility（&lt;code&gt;ring&lt;/code&gt;、&lt;code&gt;grid&lt;/code&gt;、&lt;code&gt;container&lt;/code&gt;、&lt;code&gt;hidden&lt;/code&gt;、&lt;code&gt;group&lt;/code&gt;……都是雷区）；组件内自定义类统一加项目前缀最稳。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;六、文件清单速查&lt;a href=&quot;#六文件清单速查&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;复现顺序建议：&lt;strong&gt;配置类型 → 核心模块 → 滚动管线 → 初始化 → 顶部条 → 圆环徽章 → 验证&lt;/strong&gt;。&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;src/types/siteConfig.ts                        +2    类型开关&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;src/config/siteConfig.ts                       +2    配置开关&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;src/utils/reading-progress.ts                  新增   核心逻辑&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;src/utils/scroll-utils.ts                      +4    每帧调用点&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;src/utils/layout-init.ts                       +2    初始化&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;src/layouts/Layout.astro                       +1    顶部条 DOM&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;src/styles/transition.css                      +31   顶部条样式&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;src/components/controls/FloatingControls.astro +107  圆环徽章 DOM + 样式&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;（本文即为该功能的完整实现记录，欢迎对照复现。）&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;</content:encoded></item><item><title>更新公告20260928</title><link>https://azuma.mstzuomu.space/posts/web-update-7/</link><guid isPermaLink="true">https://azuma.mstzuomu.space/posts/web-update-7/</guid><description>网站更新公告</description><pubDate>Mon, 28 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h2&gt;更新公告20260928&lt;a href=&quot;#更新公告20260928&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;1.网站版本更新到6.16.7&lt;/p&gt;&lt;p&gt;2.新增&lt;a href=&quot;https://azuma.mstzuomu.space/fcircle/&quot;&gt;&lt;strong&gt;友圈&lt;/strong&gt;&lt;/a&gt;功能，聚合各位友链的最新文章，数据来自友链们的RSS订阅，数据站部署在&lt;a href=&quot;https://fc.mstzuomu.space/&quot; target=&quot;_blank&quot;&gt;&lt;strong&gt;fc.mstzuomu.space&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;3.新增文章阅读进度功能：页面顶部显示细进度条，悬浮按钮实时显示已读百分比与预计剩余阅读时间，只统计正文内容，横幅、评论区和推荐文章不计入&lt;/p&gt;&lt;p&gt;4.新增沉浸阅读模式，阅读时可一键隐藏侧边栏等干扰元素，专注正文，配合独立的沉浸目录栏使用&lt;/p&gt;&lt;p&gt;5.新增文章系列功能，同一系列的文章可以通过系列导航连续阅读&lt;/p&gt;&lt;p&gt;6.导航栏支持static、fixed、dynamic三种模式，重构了移动端导航菜单，菜单抽屉加入毛玻璃效果&lt;/p&gt;&lt;p&gt;7.全屏壁纸支持classic、hero两种布局，可自由切换&lt;/p&gt;&lt;p&gt;8.更新了网站logo和默认字体&lt;/p&gt;&lt;p&gt;9.修复了移动端访客数据显示异常、友链评论区Markdown渲染、音乐播放器失效、亮暗色切换面板闪烁、MDX中mermaid图表报错等问题&lt;/p&gt;&lt;p&gt;10.完善了SEO与结构化数据，整理了文章分类，更新了友链&lt;/p&gt;&lt;p&gt;11.重构了三个Markdown嵌入组件（PDF阅读器、文章内音乐播放器、哔哩哔哩卡片）：样式统一收进框架的Markdown扩展样式体系，按页面按需加载；颜色全部改用主题变量，暗色模式与主题色现在能正确跟随；文件去重，清理了散落在public目录的手写样式和脚本引入；修复文章音乐播放器在站内切页时初始化失效、歌曲信息不加载、切页后音乐不停止的问题&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>Firefly 博客接入友链朋友圈：从零开始的完整部署教程</title><link>https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-12/</link><guid isPermaLink="true">https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-12/</guid><description>手把手教你为博客搭建自动更新的友链朋友圈，含全部代码改动与实战踩坑排查</description><pubDate>Sat, 26 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h1&gt;Firefly 博客接入「友链朋友圈」完整部署教程&lt;a href=&quot;#firefly-博客接入友链朋友圈完整部署教程&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;blockquote&gt;&lt;p&gt;适用对象：基于 &lt;strong&gt;Firefly（Astro）&lt;/strong&gt; 主题的静态博客 + &lt;strong&gt;Friend-Circle-Lite&lt;/strong&gt;（下称 FCL）友圈数据服务。
本文是实战复盘：每一步、每一处代码改动都来自真实部署过程，照做即可从零跑通。
预计耗时：1～2 小时。难度：会复制粘贴、会用命令行敲几条命令即可（文中有兜底的网页操作替代方案）。&lt;/p&gt;&lt;/blockquote&gt;&lt;hr /&gt;&lt;section&gt;&lt;h2&gt;0. 先搞懂原理：三个部分怎么协作&lt;a href=&quot;#0-先搞懂原理三个部分怎么协作&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;友链朋友圈不是”装进博客的插件”，而是一条独立的数据流水线：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;┌────────────────────────────┐&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;│ ① 你的博客仓库              │  展示层&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;│ Firefly (Astro)            │  /fcircle/ 页面&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;│ 每次构建生成 friend.json ───┼──┐  （你的友链名单）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;└────────────────────────────┘  │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;▼&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;┌────────────────────────────┐  每 4 小时爬一次&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;│ ② 你的 FCL fork 仓库        │◄─┘ 读取 friend.json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;│ GitHub Actions 定时爬虫      │──► 抓取每个友链的 RSS&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;│ 产出 all.json / link.json   │──► 推送到 page 分支&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;└──────────────┬─────────────┘&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;               &lt;/span&gt;&lt;/span&gt;&lt;span&gt;│ git push (自动)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;               &lt;/span&gt;&lt;/span&gt;&lt;span&gt;▼&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;┌────────────────────────────┐&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;│ ③ Vercel 数据站             │  托管层&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;│ 托管 page 分支的静态文件      │  https://fc.你的域名/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;│ 提供 all.json (带 CORS 头)  │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;└──────────────┬─────────────┘&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;               &lt;/span&gt;&lt;/span&gt;&lt;span&gt;│ fetch 跨域读取&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;               &lt;/span&gt;&lt;/span&gt;&lt;span&gt;▼&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/fcircle/ 页面渲染友圈文章流&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;职责划分：&lt;/strong&gt;&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;部分&lt;/th&gt;&lt;th&gt;负责什么&lt;/th&gt;&lt;th&gt;技术栈&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;① 博客仓库&lt;/td&gt;&lt;td&gt;新增 &lt;code&gt;/fcircle/&lt;/code&gt; 页面；构建时生成友链名单 &lt;code&gt;friend.json&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Astro + Firefly 主题&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;② FCL fork 仓库&lt;/td&gt;&lt;td&gt;每 4 小时定时爬取友链 RSS，生成数据文件&lt;/td&gt;&lt;td&gt;Python + GitHub Actions（免费）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;③ Vercel&lt;/td&gt;&lt;td&gt;把数据文件托管成 HTTPS 静态站（带 CORS 头）&lt;/td&gt;&lt;td&gt;Vercel 免费版&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&lt;strong&gt;最终效果&lt;/strong&gt;：博客上出现「友圈」菜单和 &lt;code&gt;/fcircle/&lt;/code&gt; 页面，自动聚合所有友链的最新文章，每 4 小时更新一次，全程无需人工干预。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;1. 准备工作&lt;a href=&quot;#1-准备工作&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;开始前确认你有：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;GitHub 账号&lt;/strong&gt; —— 托管博客仓库和 FCL fork&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vercel 账号&lt;/strong&gt; —— 免费版即可（vercel.com 注册，可用 GitHub 登录）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cloudflare 账号 + 你自己的域名&lt;/strong&gt; —— 博客已解析在 Cloudflare 上（本文以 &lt;code&gt;mstzuomu.space&lt;/code&gt; 为例，换成你自己的）
&lt;blockquote&gt;&lt;p&gt;国内访问者无法解析 &lt;code&gt;*.vercel.app&lt;/code&gt;（被 DNS 污染），&lt;strong&gt;必须绑自定义域名&lt;/strong&gt;，否则访客拉不到数据。&lt;/p&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;本机环境&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;Node.js ≥ 22、pnpm ≥ 11（你跑得动 &lt;code&gt;pnpm dev&lt;/code&gt; 就说明够了）&lt;/li&gt;
&lt;li&gt;Git 基本操作（add / commit / push）&lt;/li&gt;
&lt;li&gt;浏览器&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;命令行注意（Windows）&lt;/strong&gt;：教程里的 &lt;code&gt;curl&lt;/code&gt; 命令请写成 &lt;strong&gt;&lt;code&gt;curl.exe&lt;/code&gt;&lt;/strong&gt;。PowerShell 里裸写 &lt;code&gt;curl&lt;/code&gt; 会别名到 &lt;code&gt;Invoke-WebRequest&lt;/code&gt;，参数不兼容会报错。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;2. 第一部分：博客侧代码改造&lt;a href=&quot;#2-第一部分博客侧代码改造&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;改动清单（共 10 处，先看全貌再逐个做）：&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;#&lt;/th&gt;&lt;th&gt;文件&lt;/th&gt;&lt;th&gt;操作&lt;/th&gt;&lt;th&gt;作用&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;2.1&lt;/td&gt;&lt;td&gt;&lt;code&gt;scripts/generate-friend-json.ts&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;新增&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;从友链配置生成 FCL 需要的 &lt;code&gt;friend.json&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2.2&lt;/td&gt;&lt;td&gt;&lt;code&gt;package.json&lt;/code&gt;&lt;/td&gt;&lt;td&gt;修改&lt;/td&gt;&lt;td&gt;把生成脚本挂进 &lt;code&gt;pnpm build&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2.3&lt;/td&gt;&lt;td&gt;&lt;code&gt;public/friend.json&lt;/code&gt;&lt;/td&gt;&lt;td&gt;生成&lt;/td&gt;&lt;td&gt;执行脚本产出（会被发布到站点根目录）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2.4&lt;/td&gt;&lt;td&gt;&lt;code&gt;src/config/fcircleConfig.ts&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;新增&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;友圈页面配置（数据站地址等）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2.5&lt;/td&gt;&lt;td&gt;&lt;code&gt;src/config/index.ts&lt;/code&gt;&lt;/td&gt;&lt;td&gt;修改&lt;/td&gt;&lt;td&gt;导出新配置&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2.6&lt;/td&gt;&lt;td&gt;&lt;code&gt;src/pages/fcircle.astro&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;新增&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;友圈页面本体&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2.7&lt;/td&gt;&lt;td&gt;&lt;code&gt;src/types/siteConfig.ts&lt;/code&gt; + &lt;code&gt;src/config/siteConfig.ts&lt;/code&gt;&lt;/td&gt;&lt;td&gt;修改&lt;/td&gt;&lt;td&gt;页面开关 &lt;code&gt;pages.fcircle&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2.8&lt;/td&gt;&lt;td&gt;&lt;code&gt;src/config/navBarConfig.ts&lt;/code&gt;&lt;/td&gt;&lt;td&gt;修改&lt;/td&gt;&lt;td&gt;导航栏「社交 → 友圈」菜单&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2.9&lt;/td&gt;&lt;td&gt;&lt;code&gt;public/fclite/fclite.js&lt;/code&gt;、&lt;code&gt;fclite.css&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;新增&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;FCL 前端渲染脚本（自托管 + 按 2.9 做主题化改造）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2.10&lt;/td&gt;&lt;td&gt;—&lt;/td&gt;&lt;td&gt;验证&lt;/td&gt;&lt;td&gt;本地跑通 → 提交推送 → 线上验证&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;section&gt;&lt;h3&gt;2.1 新增 &lt;code&gt;scripts/generate-friend-json.ts&lt;/code&gt;&lt;a href=&quot;#21-新增-scriptsgenerate-friend-jsonts&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;FCL 的爬虫只认一种友链格式：&lt;code&gt;{&quot;friends&quot;: [[&quot;站点名&quot;, &quot;网址&quot;, &quot;头像&quot;], ...]}&lt;/code&gt;。Firefly 的友链写在 &lt;code&gt;src/config/friendsConfig.ts&lt;/code&gt; 里，所以需要一个脚本做转换。&lt;/p&gt;&lt;p&gt;新建文件，完整内容：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fs&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;node:fs/promises&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;node:path&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;getEnabledFriends&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;../src/config/friendsConfig&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 生成 Friend-Circle-Lite 所需的友链数据文件&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 格式: { &quot;friends&quot;: [[&quot;站点名&quot;, &quot;站点地址&quot;, &quot;头像地址&quot;], ...] }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 产物发布到站点根目录 (public/friend.json)，供 FCL 的 Action 定时拉取&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;OUTPUT_FILE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;join&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;public&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;friend.json&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;main&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;friends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getEnabledFriends&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;friends&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;friends&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;item&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;item&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;item&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;siteurl&lt;/span&gt;&lt;span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;item&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;imgurl&lt;/span&gt;&lt;span&gt;]),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fs&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;mkdir&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;path&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;dirname&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;OUTPUT_FILE&lt;/span&gt;&lt;span&gt;), { &lt;/span&gt;&lt;span&gt;recursive&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt; });&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fs&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;writeFile&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;OUTPUT_FILE&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;JSON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;stringify&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;\n&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;console&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`friend.json 已生成: &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;OUTPUT_FILE&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;friends&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; 个友链)`&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;main&lt;/span&gt;&lt;span&gt;().&lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;console&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;生成 friend.json 失败:&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;process&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exit&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;它只导出 &lt;code&gt;enabled: true&lt;/code&gt; 的友链，与 &lt;code&gt;/friends/&lt;/code&gt; 页面看到的列表保持一致。以后你增删友链，只要正常改 &lt;code&gt;friendsConfig.ts&lt;/code&gt;，其余全自动。&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;2.2 修改 &lt;code&gt;package.json&lt;/code&gt;&lt;a href=&quot;#22-修改-packagejson&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;打开 &lt;code&gt;package.json&lt;/code&gt; 的 &lt;code&gt;scripts&lt;/code&gt;，做两处修改：&lt;/p&gt;&lt;p&gt;&lt;strong&gt;改动 1&lt;/strong&gt;：&lt;code&gt;build&lt;/code&gt; 命令&lt;strong&gt;最前面&lt;/strong&gt;插入生成步骤：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&quot;build&quot;: &quot;npx tsx scripts/generate-github-card-data.ts &amp;amp;&amp;amp; ...&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&quot;build&quot;: &quot;npx tsx scripts/generate-friend-json.ts &amp;amp;&amp;amp; npx tsx scripts/generate-github-card-data.ts &amp;amp;&amp;amp; ...&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;改动 2&lt;/strong&gt;：新增一个手动执行的命令（放在 &lt;code&gt;lqips&lt;/code&gt; 旁边）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&quot;lqips&quot;: &quot;npx tsx scripts/generate-lqips.ts&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&quot;friend-json&quot;: &quot;npx tsx scripts/generate-friend-json.ts&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;2.3 执行脚本生成 &lt;code&gt;public/friend.json&lt;/code&gt;&lt;a href=&quot;#23-执行脚本生成-publicfriendjson&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;pnpm friend&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;json&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;预期输出：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;friend.json 已生成: public\friend.json (N 个友链)   ← N 为你的友链数量&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;打开 &lt;code&gt;public/friend.json&lt;/code&gt; 确认内容长这样：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;friends&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;MIFENG BLOG&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;https://blog.imbee.top/&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;https://blog.imbee.top/images/logo/logo.webp&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;2.4 新增 &lt;code&gt;src/config/fcircleConfig.ts&lt;/code&gt;&lt;a href=&quot;#24-新增-srcconfigfcircleconfigts&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 友链朋友圈 (Friend-Circle-Lite) 页面配置&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fcircleConfig&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// 页面标题&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;友链朋友圈&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// 页面描述&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;聚合朋友们的最新文章，数据来自各位友链的 RSS 订阅&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// FCL 数据站地址（fork 仓库 page 分支部署后的站点根地址，末尾必须带 /）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// 部署完成后把这里改成你自己的地址，例如 https://xxx.vercel.app/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;apiUrl&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;https://YOUR-FCL-DOMAIN.vercel.app/&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// 每次加载文章数量&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;pageSize&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;24&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// 头像加载失败时的默认图片&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;errorImg&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/favicon/sakura2.png&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;⚠️ &lt;strong&gt;先保持占位符不动&lt;/strong&gt;，&lt;code&gt;apiUrl&lt;/code&gt; 在第 6 章拿到数据站地址后再回填（本文示例最终填的是 &lt;code&gt;https://fc.mstzuomu.space/&lt;/code&gt;）。&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;2.5 修改 &lt;code&gt;src/config/index.ts&lt;/code&gt;&lt;a href=&quot;#25-修改-srcconfigindexts&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;加一行导出（保持字母序，放在 &lt;code&gt;dynamicConfig&lt;/code&gt; 之后）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;export { dynamicConfig } from &quot;./dynamicConfig&quot;; // 动态页面配置&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;export { fcircleConfig } from &quot;./fcircleConfig&quot;; // 友链朋友圈页面配置&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;2.6 新增 &lt;code&gt;src/pages/fcircle.astro&lt;/code&gt;（页面本体）&lt;a href=&quot;#26-新增-srcpagesfcircleastro页面本体&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;完整内容：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;Icon&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;astro-icon/components&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;fcircleConfig&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;siteConfig&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;@/config&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MainGridLayout&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;@/layouts/MainGridLayout.astro&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 检查页面是否启用&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;&lt;span&gt;siteConfig&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;pages&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;fcircle&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Astro&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;redirect&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;/404/&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fcircleConfig&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fcircleConfig&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;MainGridLayout&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;flex w-full rounded-(--radius-large) overflow-hidden relative min-h-32&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;card-base z-10 px-9 py-6 relative w-full&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;lt;!-- 页面标题和描述 --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;mb-4&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;flex items-center gap-3 mb-3&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;h-8 w-8 rounded-lg bg-(--primary) flex items-center justify-center text-white dark:text-black/70&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Icon&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;material-symbols:public&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;text-[1.5rem]&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;text-3xl font-bold text-neutral-900 dark:text-neutral-100&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;30&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;31&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;32&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;33&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;34&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;35&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;36&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;text-base text-neutral-600 dark:text-neutral-400 leading-relaxed mb-4&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;37&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;38&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;39&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;40&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;41&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;42&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;43&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;lt;!-- Friend-Circle-Lite 友圈文章流 --&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;44&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rel&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;stylesheet&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;/fclite/fclite.css&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;45&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;friend-circle-lite-root&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;46&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;47&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;is:inline&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;48&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;define:vars&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;49&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;apiUrl&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fcircleConfig&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;apiUrl&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;50&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pageSize&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fcircleConfig&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;pageSize&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;51&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;errorImg&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fcircleConfig&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;errorImg&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;52&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;53&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;54&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;window&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;UserConfig&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;55&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;private_api_url&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apiUrl&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;56&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;page_turning_number&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pageSize&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;57&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;error_img&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;errorImg&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;58&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;59&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;60&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;is:inline&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;/fclite/fclite.js&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;61&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;62&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;63&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;64&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;lt;!--&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;65&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fclite.css 依赖 [data-theme=light]/[data-theme=dark] 切换亮暗色，&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;66&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;而本站的 data-theme 是代码高亮主题名（one-light/one-dark-pro），&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;67&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;亮暗切换实际由 html.dark 控制，这里做一层变量桥接。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;68&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;69&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;is:global&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;70&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;#friend-circle-lite-root&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;71&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;--text-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--text-color-light&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;72&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;--background-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--background-color-light&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;73&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;--tag-bg-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;bfbfbf&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;74&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;--container-bg-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--container-bg-color-light&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;75&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;/* 副文本（日期、统计标签、页脚）统一到主题元信息色 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;76&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;--author-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--content-meta&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;77&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;--shadow-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--shadow-color-light&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;78&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;--border-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--border-color-light&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;79&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;--modal-bg-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;rgba&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;255&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;255&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;255&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;80&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;--modal-content-bg-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;rgba&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;239&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;250&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;255&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;81&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;--load-more-btn-bg-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--container-bg-color&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;82&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;/* 跟随站点主题色 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;83&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;--hover-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--primary&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;84&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;85&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;86&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;:root&lt;/span&gt;&lt;span&gt;.dark&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;#friend-circle-lite-root&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;87&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;--text-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--text-color-dark&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;88&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;--background-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--background-color-dark&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;89&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;--tag-bg-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;474747&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;90&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;--container-bg-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--container-bg-color-dark&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;91&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;--author-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--content-meta&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;92&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;--shadow-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--shadow-color-dark&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;93&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;--border-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--border-color-dark&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;94&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;--modal-bg-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;rgba&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.3&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;95&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;--modal-content-bg-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;rgba&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;96&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;--load-more-btn-bg-color&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--container-bg-color&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;97&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;98&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;99&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;MainGridLayout&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;几个关键点解释（看懂可以更好 DIY）：&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;define:vars&lt;/code&gt; 会把 &lt;code&gt;fcircleConfig&lt;/code&gt; 里的值序列化进内联脚本，所以改配置文件就能改页面行为，不用动这个文件。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;顺序很重要&lt;/strong&gt;：&lt;code&gt;window.UserConfig&lt;/code&gt; 的 &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; 必须写在 &lt;code&gt;fclite.js&lt;/code&gt; 之前——&lt;code&gt;fclite.js&lt;/code&gt; 加载时会立即读取 &lt;code&gt;UserConfig&lt;/code&gt;，读不到会直接报错。Firefly 的 Swup 页面切换会在每次进入页面时重新执行容器内脚本，所以从别的页面切过来也能正常渲染。&lt;/li&gt;
&lt;li&gt;底部 &lt;code&gt;&amp;lt;style is:global&amp;gt;&lt;/code&gt; 是&lt;strong&gt;亮暗色桥接&lt;/strong&gt;：FCL 原版样式用 &lt;code&gt;[data-theme=light/dark]&lt;/code&gt; 切色，而 Firefly 的 &lt;code&gt;data-theme&lt;/code&gt; 装的是代码高亮主题名（&lt;code&gt;one-light&lt;/code&gt;/&lt;code&gt;one-dark-pro&lt;/code&gt;），真正控制暗色的是 &lt;code&gt;html.dark&lt;/code&gt; 类。不加这段，暗色模式下友圈样式会错乱。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;2.7 页面开关：&lt;code&gt;pages.fcircle&lt;/code&gt;&lt;a href=&quot;#27-页面开关pagesfcircle&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;文件 1&lt;/strong&gt; &lt;code&gt;src/types/siteConfig.ts&lt;/code&gt;，在 &lt;code&gt;pages&lt;/code&gt; 类型里加一行：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pages: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;booknav: boolean; // 书签导航页面开关&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;friends: boolean; // 友链页面开关&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fcircle: boolean; // 友链朋友圈页面开关&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;文件 2&lt;/strong&gt; &lt;code&gt;src/config/siteConfig.ts&lt;/code&gt;，在 &lt;code&gt;resolvePageToggles&lt;/code&gt; 里加开关：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 友链页面开关&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;friends: true,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 友链朋友圈页面开关（Friend-Circle-Lite）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;fcircle: true,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;开关为 &lt;code&gt;false&lt;/code&gt; 时：页面自动 404、导航菜单自动隐藏（前提是 2.8 的菜单项配了 &lt;code&gt;pageKey&lt;/code&gt;）。还支持环境变量覆盖 &lt;code&gt;PUBLIC_PAGES_FCIRCLE=false&lt;/code&gt;。&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;2.8 导航栏菜单：&lt;code&gt;src/config/navBarConfig.ts&lt;/code&gt;&lt;a href=&quot;#28-导航栏菜单srcconfignavbarconfigts&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;改动 1&lt;/strong&gt;，在「社交」子菜单里、友链之后插入：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 友链&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;LinkPresets.Friends,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 友链朋友圈&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;LinkPresets.Fcircle,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 留言&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;LinkPresets.Guestbook,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;改动 2&lt;/strong&gt;，在 &lt;code&gt;LinkPresets&lt;/code&gt; 表里、&lt;code&gt;Friends&lt;/code&gt; 之后新增条目：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;Friends: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;name: &quot;友链&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;url: &quot;/friends/&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;icon: &quot;material-symbols:link-2-rounded&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pageKey: &quot;friends&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;Fcircle: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;name: &quot;友圈&quot;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;url: &quot;/fcircle/&quot;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;icon: &quot;material-symbols:public&quot;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pageKey: &quot;fcircle&quot;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;2.9 自托管 FCL 前端文件 &lt;code&gt;public/fclite/&lt;/code&gt;（含主题化改造）&lt;a href=&quot;#29-自托管-fcl-前端文件-publicfclite含主题化改造&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;FCL 官方文档让你用 jsdelivr CDN 引脚本——&lt;strong&gt;国内不稳定，不要用&lt;/strong&gt;。改为自托管：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;打开你 fork 的仓库（第 3 章会讲怎么 fork），进入 &lt;code&gt;main/&lt;/code&gt; 目录&lt;/li&gt;
&lt;li&gt;下载其中的 &lt;strong&gt;&lt;code&gt;fclite.js&lt;/code&gt;&lt;/strong&gt; 和 &lt;strong&gt;&lt;code&gt;fclite.css&lt;/code&gt;&lt;/strong&gt; 两个文件&lt;/li&gt;
&lt;li&gt;在博客仓库新建目录 &lt;code&gt;public/fclite/&lt;/code&gt;，把两个文件放进去&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;（也可以从上游仓库拿：&lt;code&gt;https://github.com/willow-god/Friend-Circle-Lite&lt;/code&gt; 的 &lt;code&gt;main/&lt;/code&gt; 目录。）&lt;/p&gt;&lt;p&gt;拿到的&lt;strong&gt;原版&lt;/strong&gt;文件是 FCL 自带画风（8px 圆角、灰底卡片、方角按钮），和 Firefly 主题不搭，而且&lt;strong&gt;不接入主题的卡片设置&lt;/strong&gt;——拖透明度滑块、开关卡片边框对它完全无效。所以还要做两轮本地化改造（本文实战时改的，改处都留了「本地修改」注释方便日后比对）：&lt;/p&gt;&lt;section&gt;&lt;h4&gt;改造 ①：卡片接入主题卡片系统（&lt;code&gt;card-base&lt;/code&gt;）&lt;a href=&quot;#改造-卡片接入主题卡片系统card-base&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;原理&lt;/strong&gt;：Firefly 的卡片透明度、卡片边框开关、主题色相底色全部通过 &lt;code&gt;.card-base&lt;/code&gt; 类挂钩（规则在 &lt;code&gt;src/styles/main.css&lt;/code&gt;：&lt;code&gt;.wallpaper-transparent .card-base&lt;/code&gt; 用 &lt;code&gt;!important&lt;/code&gt; 吃掉一切自写背景）。FCL 原版自己写死了背景/圆角/边框，把主题系统的值全压掉了。改法 = &lt;strong&gt;给卡片加上 &lt;code&gt;card-base&lt;/code&gt; 类 + 删掉它自写的那三样属性&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;&lt;code&gt;fclite.js&lt;/code&gt; 两处：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const randomArticleContainer = document.createElement(&apos;div&apos;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;randomArticleContainer.id = &apos;random-article&apos;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;randomArticleContainer.classList.add(&apos;card-base&apos;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;card.className = &apos;card&apos;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;card.className = &apos;card card-base&apos;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;fclite.css&lt;/code&gt; 两处（只删下面标 &lt;code&gt;-&lt;/code&gt; 的属性行，其余保留）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;#random-article {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;display: flex;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;background-color: var(--container-bg-color);&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;border-radius: 8px;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;border: 1px solid var(--border-color);&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;padding: 20px;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.card {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;background-color: var(--container-bg-color);&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;border-radius: 8px;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;padding: 12px;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;border: 1px solid var(--border-color);&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;position: relative;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;改完后的效果：底色 = &lt;code&gt;var(--card-bg)&lt;/code&gt;（透明壁纸模式下自动改用滑块控制的 &lt;code&gt;--card-bg-transparent&lt;/code&gt;）、圆角 = &lt;code&gt;--radius-large&lt;/code&gt;（1rem，与全站卡片一致）、边框跟随「卡片边框」开关，开启时 hover 仍会变主题色高亮（原 fclite 的 hover 规则保留）。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;改造 ②：内层元素统一到主题设计语言&lt;a href=&quot;#改造-内层元素统一到主题设计语言&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;以下都在 &lt;code&gt;fclite.css&lt;/code&gt; 里改。&lt;strong&gt;颜色一律用主题变量&lt;/strong&gt;（用之前先在 &lt;code&gt;src/styles/variables.styl&lt;/code&gt; 里核对存在，防止幽灵变量），对照表：&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;选择器&lt;/th&gt;&lt;th&gt;对应页面元素&lt;/th&gt;&lt;th&gt;关键改动&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;.stat-item&lt;/code&gt;&lt;/td&gt;&lt;td&gt;订阅/活跃/文章/失败 四个统计块&lt;/td&gt;&lt;td&gt;底色 → &lt;code&gt;var(--btn-regular-bg)&lt;/code&gt;；边框 → &lt;code&gt;1px solid var(--line-divider)&lt;/code&gt;；圆角 → &lt;code&gt;var(--radius-xl)&lt;/code&gt;；hover 边框 → &lt;code&gt;color-mix(in srgb, var(--primary) 30%, transparent)&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;.card-author&lt;/code&gt;、&lt;code&gt;.random-author&lt;/code&gt;、&lt;code&gt;.random-date&lt;/code&gt;&lt;/td&gt;&lt;td&gt;名字/日期胶囊&lt;/td&gt;&lt;td&gt;改成 &lt;code&gt;category-pill&lt;/code&gt; 同款：底 &lt;code&gt;color-mix(in srgb, var(--btn-content) 6%, transparent)&lt;/code&gt;、边框 &lt;code&gt;color-mix(in srgb, var(--btn-content) 12%, transparent)&lt;/code&gt;、&lt;code&gt;border-radius: var(--radius-full)&lt;/code&gt;、文字 &lt;code&gt;var(--btn-content)&lt;/code&gt;；hover 边框/文字 → &lt;code&gt;var(--primary)&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;.random-button-container a&lt;/code&gt;&lt;/td&gt;&lt;td&gt;「换一篇」按钮&lt;/td&gt;&lt;td&gt;底 &lt;code&gt;var(--btn-regular-bg)&lt;/code&gt;、边框 &lt;code&gt;var(--line-divider)&lt;/code&gt;、圆角 &lt;code&gt;var(--radius-full)&lt;/code&gt;、文字 &lt;code&gt;var(--btn-content)&lt;/code&gt;；hover 底 &lt;code&gt;var(--btn-regular-bg-hover)&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;.random-link-button&lt;/code&gt;&lt;/td&gt;&lt;td&gt;「阅读文章」按钮&lt;/td&gt;&lt;td&gt;&lt;code&gt;border-radius: var(--radius-full)&lt;/code&gt;（底色本来就是 &lt;code&gt;var(--primary)&lt;/code&gt;，不动）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;#load-more-btn&lt;/code&gt;&lt;/td&gt;&lt;td&gt;「再来亿点」加载按钮&lt;/td&gt;&lt;td&gt;套「换一篇」同款 btn-regular 胶囊方案&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;最后回到 2.6 的 &lt;code&gt;fcircle.astro&lt;/code&gt;：桥接块里 &lt;code&gt;--author-color&lt;/code&gt; 两条映射改成 &lt;code&gt;var(--content-meta)&lt;/code&gt;（代码已同步），日期文字、统计标签、页脚这些副文本就统一到主题元信息色，明暗自动适配。&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;⚠️ 这些改造都在&lt;strong&gt;你博客仓库&lt;/strong&gt;的 &lt;code&gt;public/fclite/&lt;/code&gt; 里，不影响数据站。若日后从上游重新下载这两个文件，改造需要重新做一遍。&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;2.10 本地验证 → 推送上线&lt;a href=&quot;#210-本地验证--推送上线&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;本地验证（3 条命令）：&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pnpm check          &lt;/span&gt;&lt;span&gt;# Astro 诊断（既有的 i18n/anime 报错与本改动无关，可忽略）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pnpm lint           &lt;/span&gt;&lt;span&gt;# Biome 检查&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pnpm dev            &lt;/span&gt;&lt;span&gt;# 启动后访问 http://localhost:4321/fcircle/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;浏览器打开 &lt;code&gt;http://localhost:4321/fcircle/&lt;/code&gt;，应看到：标题「友链朋友圈」、友圈数据加载中（此时 &lt;code&gt;apiUrl&lt;/code&gt; 还是占位符，报加载失败属&lt;strong&gt;正常&lt;/strong&gt;，第 6 章回填后就好）。
同时检查导航栏「社交」下多了「友圈」菜单。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;提交推送：&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git add .&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;git commit &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;m &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;feat: 新增友链朋友圈页面&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git push&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;推送后等 CI 构建完成（几分钟），然后用 &lt;code&gt;curl.exe&lt;/code&gt; 线上验证两个关键地址：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 应返回 200，且内容是你的友链名单&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;curl.exe&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;sL https:&lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt;你的域名&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;friend.json&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 应返回 200&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;curl.exe&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;o NUL &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;w &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;%{http_code}&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt; https:&lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt;你的域名&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;fcircle&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;&lt;code&gt;friend.json&lt;/code&gt; 是 FCL 爬虫的&lt;strong&gt;输入&lt;/strong&gt;，必须先上线，第 3 章的爬虫才能读到你的友链。&lt;strong&gt;顺序不能反。&lt;/strong&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;3. 第二部分：Fork Friend-Circle-Lite 并配置（4 处改动）&lt;a href=&quot;#3-第二部分fork-friend-circle-lite-并配置4-处改动&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;3.1 Fork 仓库&lt;a href=&quot;#31-fork-仓库&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;打开上游仓库：&lt;code&gt;https://github.com/willow-god/Friend-Circle-Lite&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;点右上角 &lt;strong&gt;Fork&lt;/strong&gt; → Create fork（只 fork &lt;code&gt;main&lt;/code&gt; 分支即可）&lt;/li&gt;
&lt;li&gt;克隆到本地（换成你的用户名）：&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;git clone https:&lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;/span&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;你的用户名&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;Friend&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;Circle&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;Lite.git&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;cd Friend&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;Circle&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;Lite&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;3.2 改动 1：&lt;code&gt;conf.yaml&lt;/code&gt;（4 处，最重要）&lt;a href=&quot;#32-改动-1confyaml4-处最重要&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;这个文件是爬虫的总配置。&lt;strong&gt;只改值，不动结构&lt;/strong&gt;：&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;配置项&lt;/th&gt;&lt;th&gt;原值（上游）&lt;/th&gt;&lt;th&gt;改成&lt;/th&gt;&lt;th&gt;说明&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;spider_settings.json_url&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;&quot;https://blog.liushen.fun/friend.json&quot;&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;&quot;https://你的域名/friend.json&quot;&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;你的&lt;/strong&gt;友链名单地址&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;link_check.author_url&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;&quot;blog.liushen.fun&quot;&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;&quot;你的域名&quot;&lt;/code&gt;&lt;/td&gt;&lt;td&gt;反链检测用（只填域名不带 https）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;rss_subscribe.enable&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;&lt;td&gt;邮件订阅功能，用不到就关&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;rss_subscribe.github_username&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;willow-god&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;你的GitHub用户名&lt;/code&gt;&lt;/td&gt;&lt;td&gt;顺手改掉&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;rss_subscribe.your_blog_url&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;https://blog.liushen.fun/&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;https://你的域名/&lt;/code&gt;&lt;/td&gt;&lt;td&gt;顺手改掉&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;rss_subscribe.website_info.title&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;&quot;清羽飞扬&quot;&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;&quot;你的博客标题&quot;&lt;/code&gt;&lt;/td&gt;&lt;td&gt;顺手改掉&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;对照 diff：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;spider_settings:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;enable: true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;json_url: &quot;https://blog.liushen.fun/friend.json&quot;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;json_url: &quot;https://你的域名/friend.json&quot;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;article_count: 5&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;link_check:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;enable_backlink_check: true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;author_url: &quot;blog.liushen.fun&quot;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;author_url: &quot;你的域名&quot;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;rss_subscribe:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;enable: true&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;github_username: willow-god&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;enable: false&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;github_username: 你的GitHub用户名&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;github_repo: Friend-Circle-Lite&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;your_blog_url: https://blog.liushen.fun/&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;your_blog_url: https://你的域名/&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;website_info:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;title: &quot;清羽飞扬&quot;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;title: &quot;你的博客标题&quot;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;这是整个部署里最容易漏、漏了后果最严重的一步。&lt;/strong&gt; 如果忘了改 &lt;code&gt;json_url&lt;/code&gt;，爬虫会去爬上游作者的 200 多个友链，你的友圈页面会显示一堆陌生人（本文实战就踩过这个坑，第 9 章有排查方法）。&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;3.3 改动 2：新增 &lt;code&gt;static/vercel.json&lt;/code&gt;（CORS 跨域头）&lt;a href=&quot;#33-改动-2新增-staticverceljsoncors-跨域头&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;浏览器从你的博客域名跨域请求 Vercel 数据站，对方必须返回 &lt;code&gt;Access-Control-Allow-Origin: *&lt;/code&gt;，否则数据拉不下来。&lt;/p&gt;&lt;p&gt;新建文件 &lt;code&gt;static/vercel.json&lt;/code&gt;，完整内容：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;headers&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;source&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;/(.*)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;headers&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;key&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;Access-Control-Allow-Origin&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;value&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;*&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;key&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;Access-Control-Allow-Methods&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;value&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;GET, POST, OPTIONS, PUT, DELETE&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;key&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;Access-Control-Allow-Headers&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&quot;value&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;*&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;为什么放 &lt;code&gt;static/&lt;/code&gt; 而不是仓库根目录？因为 Actions 工作流会把 &lt;code&gt;static/&lt;/code&gt; 里的指定文件&lt;strong&gt;拷贝进 &lt;code&gt;page&lt;/code&gt; 分支&lt;/strong&gt;，而 Vercel 部署 &lt;code&gt;page&lt;/code&gt; 分支时只认分支根目录的 &lt;code&gt;vercel.json&lt;/code&gt;。下一步就是把这个文件加进拷贝清单。&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;3.4 改动 3：&lt;code&gt;.github/workflows/friend_circle_lite.yml&lt;/code&gt;&lt;a href=&quot;#34-改动-3githubworkflowsfriend_circle_liteyml&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;找到 &lt;code&gt;Build static publish directory&lt;/code&gt; 步骤里的 &lt;code&gt;cp&lt;/code&gt; 命令，在清单里加上 &lt;code&gt;./static/vercel.json&lt;/code&gt;：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;- name: Build static publish directory&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;run: |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;mkdir pages&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cp -r main ./static/edgeone.json ./static/_headers ./static/index.html ./static/readme.md ./static/favicon.ico ./static/bg-light.webp ./static/bg-dark.webp all.json link.json errors.json pages/&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cp -r main ./static/edgeone.json ./static/_headers ./static/vercel.json ./static/index.html ./static/readme.md ./static/favicon.ico ./static/bg-light.webp ./static/bg-dark.webp all.json link.json errors.json pages/&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;3.5 改动 4：&lt;code&gt;.gitignore&lt;/code&gt;&lt;a href=&quot;#35-改动-4gitignore&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;仓库的 &lt;code&gt;.gitignore&lt;/code&gt; 忽略了所有 &lt;code&gt;*.json&lt;/code&gt;（防止数据文件进主分支），需要给 &lt;code&gt;vercel.json&lt;/code&gt; 开例外，否则它永远推不上去：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 忽略数据文件&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;*.json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;!edgeone.json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;!vercel.json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;3.6 提交并推送&lt;a href=&quot;#36-提交并推送&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;git add conf.yaml .gitignore .github&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;workflows&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;friend_circle_lite.yml &lt;/span&gt;&lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;vercel.json&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;git commit &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;m &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;fix: 友链数据源指向本站并为 page 分支添加 Vercel CORS 配置&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git push&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;⚠️ &lt;strong&gt;核对清单&lt;/strong&gt;：&lt;code&gt;git status&lt;/code&gt; 确认这 4 个文件都进了提交。实战中曾出现”以为推了实际没推”的情况，导致爬虫用了上游的友链列表。推完可以去 GitHub 网页上点开 &lt;code&gt;conf.yaml&lt;/code&gt; 确认 &lt;code&gt;json_url&lt;/code&gt; 已经是你的域名。&lt;/p&gt;&lt;/blockquote&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;4. 第三部分：启用 GitHub Actions，生成 &lt;code&gt;page&lt;/code&gt; 分支&lt;a href=&quot;#4-第三部分启用-github-actions生成-page-分支&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;FCL 的数据完全由 GitHub Actions 定时生成，部署在&lt;strong&gt;免费额度内&lt;/strong&gt;（公开仓库无限，私有仓库每月 2000 分钟，这个爬虫每次不到 2 分钟，绰绰有余）。&lt;/p&gt;&lt;section&gt;&lt;h3&gt;4.1 启用工作流（fork 有两道开关，都要点！）&lt;a href=&quot;#41-启用工作流fork-有两道开关都要点&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;开关 ①&lt;/strong&gt;：打开 &lt;code&gt;https://github.com/你的用户名/Friend-Circle-Lite/actions&lt;/code&gt;
顶部会有条黄色提示 &lt;em&gt;“Workflows aren’t being run on this forked repository…”&lt;/em&gt; → 点 &lt;strong&gt;“I understand my workflows, go ahead and enable them”&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;开关 ②&lt;/strong&gt;（很多人卡在这）：左侧工作流列表里，&lt;code&gt;Friend Circle Lite&lt;/code&gt; 可能仍显示 &lt;strong&gt;「已禁用」&lt;/strong&gt;（fork 特有的 &lt;code&gt;disabled_fork&lt;/code&gt; 状态）——
→ 点击左侧的 &lt;strong&gt;Friend Circle Lite&lt;/strong&gt; → 工作流页面点绿色 &lt;strong&gt;“Enable workflow”&lt;/strong&gt; 按钮。&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;只启用开关 ① 不启用开关 ②，手动触发会报 &lt;code&gt;422 Cannot trigger a &apos;workflow_dispatch&apos; on a disabled workflow&lt;/code&gt;。&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;4.2 手动运行一次&lt;a href=&quot;#42-手动运行一次&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;左侧点 &lt;strong&gt;Friend Circle Lite&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;右上角 &lt;strong&gt;Run workflow&lt;/strong&gt; → 下拉选 &lt;code&gt;main&lt;/code&gt; → 点绿色 &lt;strong&gt;Run workflow&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;等 1～2 分钟刷新，看到绿色 ✅ &lt;strong&gt;success&lt;/strong&gt; 即可（首次运行要下载依赖，慢一点正常）&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;这次运行会：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;读取你博客上线的 &lt;code&gt;friend.json&lt;/code&gt;（你的友链名单）&lt;/li&gt;
&lt;li&gt;检测每个友链可达性 + 抓取 RSS 文章&lt;/li&gt;
&lt;li&gt;生成 &lt;code&gt;all.json&lt;/code&gt; / &lt;code&gt;link.json&lt;/code&gt; / &lt;code&gt;errors.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;强制推送 &lt;code&gt;page&lt;/code&gt; 分支&lt;/strong&gt;（首次创建）&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;4.3 验证 &lt;code&gt;page&lt;/code&gt; 分支&lt;a href=&quot;#43-验证-page-分支&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;打开 &lt;code&gt;https://github.com/你的用户名/Friend-Circle-Lite/tree/page&lt;/code&gt;，确认存在：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;all.json&lt;/code&gt;（点开能看到 &lt;code&gt;&quot;friends_num&quot;: 你的友链数&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;link.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;vercel.json&lt;/code&gt;（&lt;strong&gt;重点&lt;/strong&gt;：没有它 CORS 会挂）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;main/&lt;/code&gt;、&lt;code&gt;index.html&lt;/code&gt; 等静态文件&lt;/li&gt;
&lt;/ul&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;5. 第四部分：Vercel 部署数据站&lt;a href=&quot;#5-第四部分vercel-部署数据站&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;5.1 导入仓库&lt;a href=&quot;#51-导入仓库&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;登录 &lt;a href=&quot;https://vercel.com&quot; target=&quot;_blank&quot;&gt;vercel.com&lt;/a&gt; → &lt;strong&gt;Add New… → Project&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;在仓库列表里找到 &lt;code&gt;Friend-Circle-Lite&lt;/code&gt; 点 &lt;strong&gt;Import&lt;/strong&gt;（首次会让你安装/授权 Vercel 的 GitHub App，按提示授权）&lt;/li&gt;
&lt;li&gt;构建配置全部保持默认即可（&lt;code&gt;main&lt;/code&gt; 分支没有构建步骤，仓库根目录的 &lt;code&gt;vercel.json&lt;/code&gt; 里 &lt;code&gt;deploymentEnabled.main: false&lt;/code&gt; 会让它跳过 main 分支的部署——这是上游作者设计好的）&lt;/li&gt;
&lt;li&gt;等导入完成&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;5.2 把生产分支改成 &lt;code&gt;page&lt;/code&gt;（关键步骤）&lt;a href=&quot;#52-把生产分支改成-page关键步骤&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;进入项目 → &lt;strong&gt;Settings → Environments → Production&lt;/strong&gt;，找到 &lt;strong&gt;Branch Tracking&lt;/strong&gt;：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;把分支从 &lt;code&gt;main&lt;/code&gt; 改成 &lt;strong&gt;&lt;code&gt;page&lt;/code&gt;&lt;/strong&gt; → 点 &lt;strong&gt;Save&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;两个坑：&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;报错 &lt;code&gt;Branch &quot;page&quot; not found in the connected Git repository&lt;/code&gt;&lt;/strong&gt; —— 说明第 4 章还没跑成功、&lt;code&gt;page&lt;/code&gt; 分支不存在。回去先完成第 4 章再来。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;新版 Vercel 界面里没有单独的 “Production Branch” 选项&lt;/strong&gt;（旧教程会误导你去 Git 页找）——它就在上面说的 Environments → Production → Branch Tracking 里。找不到就用 Settings 页左侧顶部的 &lt;strong&gt;Find&lt;/strong&gt; 搜索框搜 &lt;code&gt;production&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;5.3 首次生产部署&lt;a href=&quot;#53-首次生产部署&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;保存成功后 Vercel 通常会弹出 &lt;strong&gt;Redeploy&lt;/strong&gt; 对话框：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;环境选 &lt;strong&gt;Production&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;源部署选带 &lt;code&gt;page&lt;/code&gt; 分支标记的那条（列表里最新的）&lt;/li&gt;
&lt;li&gt;点 &lt;strong&gt;Redeploy&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;如果没弹窗，去 &lt;strong&gt;Deployments&lt;/strong&gt; 页找到 &lt;code&gt;page&lt;/code&gt; 分支的那条部署 → 右侧 &lt;code&gt;…&lt;/code&gt; → &lt;strong&gt;Promote to Production&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;完成后，项目的生产地址（形如 &lt;code&gt;friend-circle-lite-xxx.vercel.app&lt;/code&gt;）就由 &lt;code&gt;page&lt;/code&gt; 分支驱动了——&lt;strong&gt;以后 Actions 每次推送，Vercel 自动部署，无需任何手动操作。&lt;/strong&gt;&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;5.4 绑定自定义子域名（国内必做）&lt;a href=&quot;#54-绑定自定义子域名国内必做&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;blockquote&gt;&lt;p&gt;为什么必须：&lt;code&gt;*.vercel.app&lt;/code&gt; 域名在国内被 DNS 污染（解析出假 IP），你的访客会全部加载失败。实测必须绑自有域名。&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;strong&gt;第 1 步 — Vercel 侧添加域名：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;项目 → &lt;strong&gt;Settings → Domains&lt;/strong&gt; → 输入你想用的子域名（例：&lt;code&gt;fc.你的域名&lt;/code&gt;）→ &lt;strong&gt;Add&lt;/strong&gt;。
添加后 Vercel 会提示需要一条 CNAME 记录（目标是 &lt;code&gt;cname.vercel-dns.com&lt;/code&gt;）。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;第 2 步 — Cloudflare 侧加 DNS 记录：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Cloudflare 控制台 → 你的域名 → &lt;strong&gt;DNS → Records → Add record&lt;/strong&gt;：&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;字段&lt;/th&gt;&lt;th&gt;填写&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Type&lt;/td&gt;&lt;td&gt;&lt;code&gt;CNAME&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Name&lt;/td&gt;&lt;td&gt;&lt;code&gt;fc&lt;/code&gt;（对应 &lt;code&gt;fc.你的域名&lt;/code&gt; 的前缀）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Target&lt;/td&gt;&lt;td&gt;&lt;code&gt;cname.vercel-dns.com&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Proxy&lt;/td&gt;&lt;td&gt;&lt;strong&gt;DNS only（一定选灰云，不要开橙色云代理）&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;TTL&lt;/td&gt;&lt;td&gt;Auto&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&lt;strong&gt;第 3 步 — 回 Vercel 等证书签发：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;DNS 生效通常 1～5 分钟。如果域名状态显示证书错误，点 &lt;strong&gt;Refresh&lt;/strong&gt; / 重新保存一次域名即可触发重新签发。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;验证：&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 应返回 200，并且能看到 Access-Control-Allow-Origin: *&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;curl.exe&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;sI https:&lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt;fc.你的域名&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;all.json&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;响应头里必须有这一行，缺了就回头检查 &lt;code&gt;page&lt;/code&gt; 分支上有没有 &lt;code&gt;vercel.json&lt;/code&gt;（第 3.3/3.4 步）。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;6. 第五部分：回填数据站地址，全线贯通&lt;a href=&quot;#6-第五部分回填数据站地址全线贯通&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;打开 &lt;code&gt;src/config/fcircleConfig.ts&lt;/code&gt;，把占位符换成你的真实数据站地址（&lt;strong&gt;末尾的 &lt;code&gt;/&lt;/code&gt; 不能少&lt;/strong&gt;）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// FCL 数据站地址（fork 仓库 page 分支部署后的站点根地址，末尾必须带 /）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;apiUrl: &quot;https://YOUR-FCL-DOMAIN.vercel.app/&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;apiUrl: &quot;https://fc.你的域名/&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;提交推送：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;git add src&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;fcircleConfig.ts&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;git commit &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;m &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;feat: 回填友圈数据站地址&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git push&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;等博客 CI 部署完成（几分钟），最终验证：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 1. 页面 HTML 里已包含新地址&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;curl.exe&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;sL https:&lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt;你的域名&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;fcircle&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; | findstr &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;fc.你的域名&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 2. 数据接口 200 + CORS + 你的友链数&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;curl.exe&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;sI https:&lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt;fc.你的域名&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;all.json&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;浏览器打开 &lt;code&gt;https://你的域名/fcircle/&lt;/code&gt; —— 看到自己朋友们的最新文章，&lt;strong&gt;部署完成&lt;/strong&gt; 🎉&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;页面如果还显示旧数据/加载失败：FCL 前端有 &lt;strong&gt;10 分钟 localStorage 缓存&lt;/strong&gt;，等 10 分钟再刷新，或开无痕窗口立即查看。&lt;/p&gt;&lt;/blockquote&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;7. 成品验收清单&lt;a href=&quot;#7-成品验收清单&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;逐项打勾，全过即部署成功：&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;#&lt;/th&gt;&lt;th&gt;检查项&lt;/th&gt;&lt;th&gt;验证方法&lt;/th&gt;&lt;th&gt;预期&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;友链名单已上线&lt;/td&gt;&lt;td&gt;&lt;code&gt;curl.exe -sL https://你的域名/friend.json&lt;/code&gt;&lt;/td&gt;&lt;td&gt;200，&lt;code&gt;friends&lt;/code&gt; 数量 = 你的友链数&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;页面已上线&lt;/td&gt;&lt;td&gt;浏览器打开 &lt;code&gt;/fcircle/&lt;/code&gt;&lt;/td&gt;&lt;td&gt;200，标题「友链朋友圈」&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;导航菜单&lt;/td&gt;&lt;td&gt;导航栏「社交」下拉&lt;/td&gt;&lt;td&gt;有「友链」和「友圈」两项&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;Actions 正常&lt;/td&gt;&lt;td&gt;fork 的 Actions 页&lt;/td&gt;&lt;td&gt;最近一次运行是绿色 success&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;&lt;code&gt;page&lt;/code&gt; 分支&lt;/td&gt;&lt;td&gt;&lt;code&gt;github.com/你/Friend-Circle-Lite/tree/page&lt;/code&gt;&lt;/td&gt;&lt;td&gt;有 all.json / link.json / vercel.json&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;数据站可达&lt;/td&gt;&lt;td&gt;&lt;code&gt;curl.exe -sI https://fc.你的域名/all.json&lt;/code&gt;&lt;/td&gt;&lt;td&gt;200 + &lt;code&gt;Access-Control-Allow-Origin: *&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;7&lt;/td&gt;&lt;td&gt;数据是你的友链&lt;/td&gt;&lt;td&gt;浏览器打开 &lt;code&gt;https://fc.你的域名/all.json&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;friends_num&lt;/code&gt; = 你的友链数，作者都是你朋友&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;8&lt;/td&gt;&lt;td&gt;文章渲染&lt;/td&gt;&lt;td&gt;无痕窗口打开 &lt;code&gt;/fcircle/&lt;/code&gt;&lt;/td&gt;&lt;td&gt;文章卡片正常显示，点「换一篇」「阅读文章」可用&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;9&lt;/td&gt;&lt;td&gt;自动更新&lt;/td&gt;&lt;td&gt;等一个 cron 周期（≤4 小时）后看 &lt;code&gt;last_updated_time&lt;/code&gt;&lt;/td&gt;&lt;td&gt;时间刷新&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;10&lt;/td&gt;&lt;td&gt;亮暗色&lt;/td&gt;&lt;td&gt;切换博客暗色模式&lt;/td&gt;&lt;td&gt;友圈样式跟随切换、无错乱&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;11&lt;/td&gt;&lt;td&gt;卡片跟随主题设置&lt;/td&gt;&lt;td&gt;拖「卡片透明度」滑块、开关「卡片边框」&lt;/td&gt;&lt;td&gt;统计面板与文章卡片底色/边框同步变化（未做 2.9 改造则此项不过）&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;8. 自动化之后：日常运行说明&lt;a href=&quot;#8-自动化之后日常运行说明&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;部署完成后你&lt;strong&gt;什么都不用做&lt;/strong&gt;，全自动链路：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;每 4 小时 (cron: 22 */4 * * * UTC，即北京时间 00:22 / 04:22 / 08:22 / 12:22 / 16:22 / 20:22)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└─ GitHub Actions 爬虫运行&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;├─ 检测友链可达性（结果缓存 24 小时，不会频繁骚扰友链站点）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;├─ 抓取各站 RSS 最新文章&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;├─ 生成 all.json / link.json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└─ 推送 page 分支&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;             &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└─ Vercel 自动生产部署 → fc.你的域名 更新&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└─ 博客 /fcircle/ 下次访问即是新数据&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;常见日常操作：&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;改了友链配置&lt;/strong&gt;：改 &lt;code&gt;friendsConfig.ts&lt;/code&gt; → 推送博客（&lt;code&gt;friend.json&lt;/code&gt; 自动更新）→ 下一个爬虫周期生效。想立即生效就去 fork 的 Actions 手动 Run workflow 一次。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;想手动立刻刷新数据&lt;/strong&gt;：fork → Actions → Friend Circle Lite → Run workflow，约 90 秒完成并自动上线。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;看哪些友链挂了&lt;/strong&gt;：打开 &lt;code&gt;https://fc.你的域名/&lt;/code&gt;（FCL 自带的展示首页有可达性列表），或看博客友圈统计里的「失败」数。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;数据字段含义&lt;/strong&gt;（友圈顶部统计条）：&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;字段&lt;/th&gt;&lt;th&gt;含义&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;订阅&lt;/td&gt;&lt;td&gt;友链总数&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;活跃&lt;/td&gt;&lt;td&gt;可达且能抓到 RSS 的站点数&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;文章&lt;/td&gt;&lt;td&gt;聚合的文章总数&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;失败&lt;/td&gt;&lt;td&gt;不可达 / 没有 RSS / 抓取失败的站点数（&lt;strong&gt;有几台挂了属正常现象&lt;/strong&gt;，不是你的部署问题）&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;9. 踩坑排查表（本文实战遇到的全部问题）&lt;a href=&quot;#9-踩坑排查表本文实战遇到的全部问题&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;现象&lt;/th&gt;&lt;th&gt;原因&lt;/th&gt;&lt;th&gt;解决&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Actions 页显示「无工作流程运行」，列表里工作流带「已禁用」&lt;/td&gt;&lt;td&gt;fork 仓库有&lt;strong&gt;两道开关&lt;/strong&gt;：仓库级启用 + 单个工作流启用&lt;/td&gt;&lt;td&gt;第 4.1 章：先点 “I understand my workflows…”，再进工作流页点 “Enable workflow”&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;手动触发报 &lt;code&gt;422 Cannot trigger a &apos;workflow_dispatch&apos; on a disabled workflow&lt;/code&gt;&lt;/td&gt;&lt;td&gt;同上，第二道开关没开&lt;/td&gt;&lt;td&gt;同上&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Vercel 报 &lt;code&gt;Branch &quot;page&quot; not found&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;page&lt;/code&gt; 分支还不存在&lt;/td&gt;&lt;td&gt;先成功运行一次 Actions（第 4 章），再回来保存&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;找不到 “Production Branch” 设置&lt;/td&gt;&lt;td&gt;Vercel 新版 UI 已移动位置&lt;/td&gt;&lt;td&gt;Settings → &lt;strong&gt;Environments → Production → Branch Tracking&lt;/strong&gt;；或左侧 Find 搜 &lt;code&gt;production&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;友圈显示的全是陌生人&lt;/strong&gt;（200+ 订阅，作者都不认识）&lt;/td&gt;&lt;td&gt;&lt;code&gt;conf.yaml&lt;/code&gt; 的 &lt;code&gt;json_url&lt;/code&gt; 还指向上游作者的友链列表（改动没推送成功）&lt;/td&gt;&lt;td&gt;改 &lt;code&gt;conf.yaml&lt;/code&gt; → &lt;code&gt;git add&lt;/code&gt; 确认 → 推送 → 手动 Run workflow → 核对 &lt;code&gt;page&lt;/code&gt; 分支 &lt;code&gt;all.json&lt;/code&gt; 的 &lt;code&gt;friends_num&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;友圈页面一直加载中/加载失败&lt;/td&gt;&lt;td&gt;&lt;code&gt;apiUrl&lt;/code&gt; 占位符没回填、末尾漏 &lt;code&gt;/&lt;/code&gt;、或数据站 CORS 头缺失&lt;/td&gt;&lt;td&gt;第 6 章回填；&lt;code&gt;curl.exe -sI&lt;/code&gt; 检查 &lt;code&gt;Access-Control-Allow-Origin: *&lt;/code&gt;；确认 &lt;code&gt;page&lt;/code&gt; 分支有 &lt;code&gt;vercel.json&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;数据站地址国内打不开&lt;/td&gt;&lt;td&gt;&lt;code&gt;*.vercel.app&lt;/code&gt; 被 DNS 污染&lt;/td&gt;&lt;td&gt;第 5.4 章绑定自定义子域名（CNAME 到 &lt;code&gt;cname.vercel-dns.com&lt;/code&gt;，&lt;strong&gt;灰云&lt;/strong&gt;）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;页面数据是旧的&lt;/td&gt;&lt;td&gt;fclite.js 有 10 分钟 localStorage 缓存 + 浏览器缓存&lt;/td&gt;&lt;td&gt;等 10 分钟或无痕窗口刷新&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;友圈数据突然变回旧数据（或又出现陌生人文章）&lt;/td&gt;&lt;td&gt;Vercel 的 &lt;strong&gt;Redeploy 弹窗里选中了历史旧部署&lt;/strong&gt;并发布，生产被回滚&lt;/td&gt;&lt;td&gt;Deployments 列表核对最新一条 &lt;code&gt;page&lt;/code&gt; 部署的时间 → 对最新的那条执行 &lt;code&gt;… → Promote to Production&lt;/code&gt;；别选日期是几天前的部署&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;证书签发失败 / 域名一直 Pending&lt;/td&gt;&lt;td&gt;DNS 还没生效&lt;/td&gt;&lt;td&gt;等 1～5 分钟，在 Vercel Domains 页 Refresh 重试；确认 Cloudflare 里是&lt;strong&gt;灰云&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;PowerShell 里 &lt;code&gt;curl -sI&lt;/code&gt; 报”参数无效”&lt;/td&gt;&lt;td&gt;PS5.1 把 &lt;code&gt;curl&lt;/code&gt; 别名成 &lt;code&gt;Invoke-WebRequest&lt;/code&gt;&lt;/td&gt;&lt;td&gt;一律写 &lt;strong&gt;&lt;code&gt;curl.exe&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;pnpm check&lt;/code&gt; / &lt;code&gt;type-check&lt;/code&gt; 一堆报错&lt;/td&gt;&lt;td&gt;项目既有问题（i18n 缺键、anime/vndb 页）&lt;/td&gt;&lt;td&gt;与本教程无关，只看你新增文件有没有报错&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;pnpm lint&lt;/code&gt; 把一堆无关文件改了格式&lt;/td&gt;&lt;td&gt;&lt;code&gt;biome check --write&lt;/code&gt; 会顺手格式化&lt;/td&gt;&lt;td&gt;&lt;code&gt;git checkout -- &amp;lt;无关文件&amp;gt;&lt;/code&gt; 还原，只保留自己的改动&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;本机构建报 &lt;code&gt;unable to verify the first certificate&lt;/code&gt;（OG 图片生成）&lt;/td&gt;&lt;td&gt;本机网络环境的 TLS 证书链问题&lt;/td&gt;&lt;td&gt;&lt;code&gt;set NODE_OPTIONS=--use-system-ca&lt;/code&gt; 后重跑，或忽略（CI 上不会出现）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;本地跑 FCL 爬虫报 &lt;code&gt;ZoneInfoNotFoundError: Asia/Shanghai&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Windows 的 Python 缺 &lt;code&gt;tzdata&lt;/code&gt; 包&lt;/td&gt;&lt;td&gt;&lt;code&gt;pip install tzdata&lt;/code&gt;（见附录 B）&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;10. 安全收尾（推荐）&lt;a href=&quot;#10-安全收尾推荐&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;如果你在部署过程中用过 API Token（比如为了用 CLI 部署临时生成的 Vercel Token）：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;vercel.com → &lt;strong&gt;Settings → Tokens&lt;/strong&gt; → 删除用完的 token&lt;/li&gt;
&lt;li&gt;清理本机临时凭据文件（如果有）&lt;/li&gt;
&lt;li&gt;检查 fork 仓库：&lt;strong&gt;Settings → Actions → General → Workflow permissions&lt;/strong&gt; 确认是 &lt;code&gt;Read repository contents and permissions&lt;/code&gt;（本教程的工作流文件里已显式声明 &lt;code&gt;contents: write&lt;/code&gt;，够用，无需给默认权限开写）&lt;/li&gt;
&lt;/ol&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;附录 A：文件改动总清单&lt;a href=&quot;#附录-a文件改动总清单&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;博客仓库（FireflyPriWeb）：&lt;/strong&gt;&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;文件&lt;/th&gt;&lt;th&gt;操作&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;scripts/generate-friend-json.ts&lt;/code&gt;&lt;/td&gt;&lt;td&gt;新增&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;package.json&lt;/code&gt;&lt;/td&gt;&lt;td&gt;修改（build 链 + friend-json 命令）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;public/friend.json&lt;/code&gt;&lt;/td&gt;&lt;td&gt;脚本生成（勿手改）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;src/config/fcircleConfig.ts&lt;/code&gt;&lt;/td&gt;&lt;td&gt;新增&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;src/config/index.ts&lt;/code&gt;&lt;/td&gt;&lt;td&gt;修改（+1 行导出）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;src/pages/fcircle.astro&lt;/code&gt;&lt;/td&gt;&lt;td&gt;新增&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;src/types/siteConfig.ts&lt;/code&gt;&lt;/td&gt;&lt;td&gt;修改（pages.fcircle 类型）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;src/config/siteConfig.ts&lt;/code&gt;&lt;/td&gt;&lt;td&gt;修改（pages.fcircle 开关）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;src/config/navBarConfig.ts&lt;/code&gt;&lt;/td&gt;&lt;td&gt;修改（Fcircle 菜单项 ×2 处）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;public/fclite/fclite.js&lt;/code&gt;、&lt;code&gt;fclite.css&lt;/code&gt;&lt;/td&gt;&lt;td&gt;新增（从 fork 的 &lt;code&gt;main/&lt;/code&gt; 目录复制，&lt;strong&gt;并按 2.9 完成两轮主题化改造&lt;/strong&gt;）&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&lt;strong&gt;FCL fork 仓库：&lt;/strong&gt;&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;文件&lt;/th&gt;&lt;th&gt;操作&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;conf.yaml&lt;/code&gt;&lt;/td&gt;&lt;td&gt;修改（json_url / author_url / rss_subscribe 共 6 处值）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;static/vercel.json&lt;/code&gt;&lt;/td&gt;&lt;td&gt;新增&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;.github/workflows/friend_circle_lite.yml&lt;/code&gt;&lt;/td&gt;&lt;td&gt;修改（cp 清单 +1 文件）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;.gitignore&lt;/code&gt;&lt;/td&gt;&lt;td&gt;修改（&lt;code&gt;!vercel.json&lt;/code&gt;）&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&lt;strong&gt;平台侧配置（非代码）：&lt;/strong&gt;&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;位置&lt;/th&gt;&lt;th&gt;操作&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;GitHub fork → Actions&lt;/td&gt;&lt;td&gt;启用仓库 workflows + 启用 Friend Circle Lite 工作流&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Vercel 项目 → Environments&lt;/td&gt;&lt;td&gt;Production 分支 = &lt;code&gt;page&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Vercel 项目 → Domains&lt;/td&gt;&lt;td&gt;添加 &lt;code&gt;fc.你的域名&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Cloudflare DNS&lt;/td&gt;&lt;td&gt;CNAME &lt;code&gt;fc&lt;/code&gt; → &lt;code&gt;cname.vercel-dns.com&lt;/code&gt;（灰云）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;博客 &lt;code&gt;fcircleConfig.apiUrl&lt;/code&gt;&lt;/td&gt;&lt;td&gt;回填数据站地址&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;附录 B（可选方案）：不用 GitHub Actions，本地 Python 爬虫手动部署&lt;a href=&quot;#附录-b可选方案不用-github-actions本地-python-爬虫手动部署&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;适用于：想本地先跑通看看效果，或不想用 Actions 的场景。&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;cd Friend&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;Circle&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;Lite&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;python &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;m venv .venv&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.venv\Scripts\&lt;/span&gt;&lt;span&gt;python.exe&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;m pip install &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;i https:&lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt;pypi.tuna.tsinghua.edu.cn&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;simple requests feedparser PyYAML jinja2 python&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;dateutil tzdata&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.venv\Scripts\&lt;/span&gt;&lt;span&gt;python.exe&lt;/span&gt;&lt;span&gt; run.py        &lt;/span&gt;&lt;span&gt;# 生成 all.json / link.json / errors.json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;注意两点：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;国内直连 PyPI 会超时&lt;/strong&gt;，务必加清华镜像 &lt;code&gt;-i https://pypi.tuna.tsinghua.edu.cn/simple&lt;/code&gt;；Windows 必须装 &lt;code&gt;tzdata&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;生成后把 &lt;code&gt;main/&lt;/code&gt;、&lt;code&gt;static/&lt;/code&gt; 下的指定文件、&lt;code&gt;vercel.json&lt;/code&gt; 和三个 json 拷到一个目录，用 Vercel CLI 部署：&lt;/li&gt;
&lt;/ul&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;npm install vercel &lt;/span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt;registry&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;https:&lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;/span&gt;&lt;span&gt;registry.npmmirror.com&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;# npmjs 被墙时用 npmmirror&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;vercel deploy &lt;/span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt;prod &lt;/span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt;token&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;你的token&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;但此方式&lt;strong&gt;数据不会自动更新&lt;/strong&gt;（每次都要手动跑），正式使用推荐本文正文的 GitHub Actions 方案。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;附录 C：把本文发布到你的博客&lt;a href=&quot;#附录-c把本文发布到你的博客&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;想把这篇教程作为博客文章发布：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pnpm &lt;/span&gt;&lt;span&gt;new-post&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;把本文内容粘进生成的 md 文件，补上 frontmatter（标题、日期、分类、标签），放 &lt;code&gt;src/content/posts/&lt;/code&gt; 下推送即可。若你的博客启用了友圈失败站点的&lt;strong&gt;评论系统&lt;/strong&gt;接入，按 Firefly 惯例在 frontmatter 加 &lt;code&gt;comment: true&lt;/code&gt;。&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;&lt;em&gt;教程基于实战部署整理：Firefly 6.16.7 + Friend-Circle-Lite（v2.x）+ Vercel Hobby + Cloudflare DNS。文中示例域名 &lt;code&gt;mstzuomu.space&lt;/code&gt; / &lt;code&gt;fc.mstzuomu.space&lt;/code&gt; 请替换为你自己的。&lt;/em&gt;&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;</content:encoded></item><item><title>MiMo-V2.6：扩展强化学习规模，迈向自我提升</title><link>https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-11/</link><guid isPermaLink="true">https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-11/</guid><description>全新MIMO模型发布</description><pubDate>Tue, 22 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h1&gt;MiMo-V2.6：扩展强化学习规模，迈向自我提升&lt;a href=&quot;#mimo-v26扩展强化学习规模迈向自我提升&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;blockquote&gt;&lt;p&gt;来源：&lt;a href=&quot;https://mimo.mi.com/docs/zh-CN/news/latest/v2-6&quot; target=&quot;_blank&quot;&gt;Xiaomi MiMo 文档中心&lt;/a&gt; ｜ 更新时间：2026 年 09 月 22 日&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;今天，我们正式发布并开源 Xiaomi MiMo-V2.6 系列。这是我们探索 RSI（递归自我改进）路径的关键一步：以可验证的复杂任务为基础，规模化扩展强化学习（RL）算力，让模型在持续的探索与反馈中不断拓展智能边界。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;夫夷以近，则游者众；险以远，则至者少&lt;/strong&gt;。在一个智能容易被复制的时代，我们选择把算力投进真实环境，让模型在反馈中一次次试错、自己学会。这条路更慢，也更少被看见。MiMo-V2.6 这 6 天的 Live RL 训练，是我们在这条路上的一次公开跋涉；而这 6 天的背后，是长达半年的基础研究积累和工程试错。&lt;/p&gt;&lt;p&gt;MiMo-V2.6 系列包含 Pro 和 Flash 两个原生全模态模型。得益于 RL 算力的扩展，&lt;strong&gt;MiMo-V2.6-Pro 在 Artificial Analysis Intelligence Index（AA 综合智能指数）中取得 46 分，超过 Kimi K3 和 Qwen3.8 Max，成为当前最强的开源模型&lt;/strong&gt;；但与最强的闭源模型 Claude Fable 5.1 和 GPT-6 Astra 相比，仍有差距。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://aistudio-cdn.xiaomimimo.com/xiaomimimo-static/mimo-docs-figures/display/KYAEbDMMVoVHsexkfyOcv8HQnch.png&quot; alt=&quot;综合智能指数对比&quot; /&gt;&lt;figcaption&gt;综合智能指数对比&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;MiMo-V2.6 系列沿用 V2.5 系列的 API 定价。智能提升，价格不变，“智能-成本”的帕累托前沿由此再度向外推进。MiMo-V2.6-Pro 刷新了国产模型的性价比纪录：在同等智能水平下，价格仅为海外模型的 &lt;strong&gt;1/20 至 1/60&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://aistudio-cdn.xiaomimimo.com/xiaomimimo-static/mimo-docs-figures/display/OaNDbA6znoG7HOxlWJJcWvsknHe.png&quot; alt=&quot;智能与成本对比&quot; /&gt;&lt;figcaption&gt;智能与成本对比&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;section&gt;&lt;h2&gt;大规模扩展 RL 并全面开源&lt;a href=&quot;#大规模扩展-rl-并全面开源&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;在 RL 训练阶段，MiMo-V2.6 可能是目前国产开源模型中投入算力最多的模型之一&lt;/strong&gt;。经过大规模、多任务强化学习训练，MiMo-V2.6-Pro 在多数 Agent Benchmark 上取得了比肩 Claude Opus5 和 GPT-5.6 Sol 的效果，MiMo-V2.6-Flash 则全面超越 MiMo-V2.5-Pro。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://aistudio-cdn.xiaomimimo.com/xiaomimimo-static/mimo-docs-figures/display/MhdfbCm3AokF6Hxakc6c0d5lnYb.png&quot; alt=&quot;Agent Benchmark 对比&quot; /&gt;&lt;figcaption&gt;Agent Benchmark 对比&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;整个过程中，我们攻克了 RL 训练中的基础研究与工程挑战，并通过 Live 分享正式实验历程。历时不到 6 天，MiMo-V2.6-Flash 与 MiMo-V2.6-Pro 训练成本分别约 85 万与 262 万美元，各完成 30 步，累计约 75 万条轨迹；训练任务平均通过率分别相对提升 25% 和 12%，样本外的长程软件工程评测基准 &lt;strong&gt;DeepSWE v1.1 分别提升约 17 分（48.8 → 65.7）和约 14 分（58.4 → 72.6）&lt;/strong&gt;，体现出 RL 较高的样本效率、持续改进能力和样本外的泛化能力。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://aistudio-cdn.xiaomimimo.com/xiaomimimo-static/mimo-docs-figures/display/PRlRbNId3oGwCwxi0wycqYjUnlc.png&quot; alt=&quot;DeepSWE v1.1 提升&quot; /&gt;&lt;figcaption&gt;DeepSWE v1.1 提升&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;本次训练主要从三个维度扩展 RL 算力：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;更大的 Batch 与更高吞吐&lt;/strong&gt;：结合大 Batch 和全异步架构，单次更新使用 &lt;strong&gt;1,568 个样本&lt;/strong&gt;，支持 &lt;strong&gt;1M 上下文长度&lt;/strong&gt;训练，单步训练 Token 达 &lt;strong&gt;3.5～3.7B&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;更多任务与复杂环境&lt;/strong&gt;：构建覆盖 Code、General、Visual、Cyber 等方向的多任务训练体系，并混合多个 Harness，促进&lt;strong&gt;不同能力维度协同提升&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;更大的 Grader 算力&lt;/strong&gt;：通过 Group 内相对比较，为 Long-Horizon RL 任务提供更精准、多样的奖励信号，形成模型自我改进闭环，并&lt;strong&gt;引导模型以更短路径、更少 Token 完成任务&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;随着训练规模扩大，我们冻结 MoE Router 以抑制专家负载漂移，并建立覆盖奖励设计、对抗性评测、异常检测和验证器交叉校验的 Reward Hacking 防线，提升训练稳定性与奖励可靠性。&lt;/p&gt;&lt;p&gt;为支撑大规模混合任务的智能体强化学习，我们设计统一的轨迹表示与惩罚机制来细化学习信号，支撑多种智能体框架的高并发交互，解耦控制面与数据面以支持海量轨迹的迁移，在混合批次中稳定各任务的样本配比，并优化训练与推理引擎的效率及它们的一致性。&lt;/p&gt;&lt;p&gt;我们已开源上述技术成果及配套资源，包括完整技术报告、训练环境与 RL 代码，帮助更多研究者复现和验证相关结果，共同探索规模化 RL 与模型自我改进的更多可能。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;从 Vibe Coding 到 Vibe World&lt;a href=&quot;#从-vibe-coding-到-vibe-world&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;MiMo-V2.6 融合了 &lt;strong&gt;3D 空间推理、多模态感知与计算机操作（CUA）能力&lt;/strong&gt;，进一步拓展了编程所能触及的边界，它可以将自然语言驱动的编程任务拓展为面向交互世界构建的”Vibe World”。&lt;/p&gt;&lt;section&gt;&lt;h3&gt;3D 开放世界游戏&lt;a href=&quot;#3d-开放世界游戏&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;在游戏开发中，用户输入图片、视频或文字后，MiMo-V2.6 会将需求拆解为多个任务，由多智能体协作完成游戏 3D 场景搭建、交互逻辑编写与视觉验证，并根据渲染结果不断修正，最终生成符合用户意图的可运行交互世界。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;Blender 3D 建模&lt;a href=&quot;#blender-3d-建模&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;MiMo-V2.6 能够根据用户的文字描述或参考图片，在 Blender 中完成物体与场景的三维建模，生成可用于动画制作、3D 打印与游戏开发的 3D 资产。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;具身智能&lt;a href=&quot;#具身智能&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;在具身仿真环境中，MiMo-V2.6 可以直接以多视角相机画面为输入，持续进行推理与决策，并通过视觉反馈闭环控制 Franka Panda 机械臂，完成物体抓取、颜色匹配与精准放置。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;Computer Use Agent&lt;a href=&quot;#computer-use-agent&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;MiMo-V2.6 进一步拓展了 Computer Use 能力，将多模态感知与原生训练的行动能力相结合。它可以理解复杂的图形界面，使用常见办公与生产力工具，完成信息检索、编辑和数据处理等任务，并根据视觉反馈检查结果、排查问题、调整后续行动。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;推动前沿科学研究&lt;a href=&quot;#推动前沿科学研究&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;未经针对科研任务的专项强化学习训练，MiMo-V2.6 已在多个研究领域展现出应用潜力。从材料设计到数学形式化，以下案例展示了模型如何将推理、编程与工具使用能力应用于具体科研任务。&lt;/p&gt;&lt;section&gt;&lt;h3&gt;材料科研的 Co-Scientist&lt;a href=&quot;#材料科研的-co-scientist&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;MiMo-V2.6-Pro 协助研究人员完成材料设计与计算筛选。在小米前沿材料研究团队的多轮提示与交互下，它提出了数种金属有机框架（MOF）材料的设计方案，目标是吸附被称为”永久性污染物”的全氟和多氟烷基物质（PFAS）。在这一过程中，模型检索并梳理相关文献与专利，提出研究假设，并评估设计方案的新颖性。&lt;/p&gt;&lt;p&gt;随后，它进一步开展了”干实验”：即调用开源计算工具，自动搭建模拟环境，计算设计出的 MOF 材料与 PFAS 之间的结合强度，从中筛选出最有潜力的候选材料，以供后续”湿实验”验证。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://aistudio-cdn.xiaomimimo.com/xiaomimimo-static/mimo-docs-figures/display/Xw54brNqwoguv1xZ9e1c6LbVnAh.gif&quot; alt=&quot;MOF 材料设计干实验演示&quot; /&gt;&lt;figcaption&gt;MOF 材料设计干实验演示&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;形式化数学证明&lt;a href=&quot;#形式化数学证明&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;MiMo-V2.6-Pro 协助研究员，在 Lean 4 中完成了 Li–Yorke 经典论文《周期三蕴含混沌》原始主定理的完整形式化。该定理揭示：对于连续的区间自映射，一个三周期轨道的存在，就足以推出所有正整数周期的轨道，以及一个不可数的混沌集。&lt;/p&gt;&lt;p&gt;在研究员设计的探索策略引导下，MiMo-V2.6-Pro 通过 Sub-Agent 协作推进了定理叙述与证明的形式化。经后续修订与整合，项目最终形成了 6000 余行 Lean 源码，完整证明通过 Lean 内核核验，无未完成证明占位。模型未接受过针对 Lean 的专项后训练，这一案例展示了其参与复杂形式化证明任务的能力。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://aistudio-cdn.xiaomimimo.com/xiaomimimo-static/mimo-docs-figures/display/IggpbYLGCoiAHXxFZxIcKkDxnph.gif&quot; alt=&quot;Lean 4 形式化证明过程&quot; /&gt;&lt;figcaption&gt;Lean 4 形式化证明过程&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;代码驱动的内容创作与审美表现&lt;a href=&quot;#代码驱动的内容创作与审美表现&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;MiMo-V2.6 系列大幅提升了模型创建精美数字产品的能力，涵盖包括前端网页、Figma 设计稿、幻灯片、SVG、视频、音乐等在内的方方面面。在设计评测榜单 Design Arena 上，MiMo-V2.6-Pro 取得了与 Claude Opus 5、GPT-5.6 Sol 相近的水平。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://aistudio-cdn.xiaomimimo.com/xiaomimimo-static/mimo-docs-figures/display/NMLvb9QzEoyWtLxkhsccSPBFngf.png&quot; alt=&quot;Design Arena 设计评测&quot; /&gt;&lt;figcaption&gt;Design Arena 设计评测&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;section&gt;&lt;h3&gt;前端 &amp;amp; PPT 的审美表现&lt;a href=&quot;#前端--ppt-的审美表现&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;MiMo-V2.6 可以将简单的指令转化为完整的前端界面和 PPT，生成结构化的布局并精心设计美观的组件、交互元素和丰富的动画效果。还熟练运用 Figma 和图像/视频生成工具，制作符合整体风格的视觉素材，在字体、配色和图文编排上保持协调，兼顾审美表现与阅读、交互体验。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;视频创作&lt;a href=&quot;#视频创作&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;MiMo-V2.6 能够端到端完成高质量视频创作。在创意与产品宣传视频中，MiMo-V2.6 可根据用户需求完成视觉设计、镜头与动效编排、配乐合成及节奏卡点；在科普视频中，它能将傅里叶分解（Fourier Decomposition）、凸包（Convex Hull）等抽象概念转化为通俗解说与连贯动画，并调用 MiMo-V2.5-TTS 合成旁白，与画面精准对齐，从而将复杂知识转化为观众可轻松理解的生动内容，实现从概念拆解到成片输出的全流程自动化。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;音乐创作&lt;a href=&quot;#音乐创作&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;在 MiMo-V2.6 中，我们进一步强化了模型的音乐理解、审美判断与知识运用能力，探索模型在音乐创作中的应用。它已展现出创作 Demo 级音乐作品的能力，以及辅助专业作曲与编曲人员开展创作的潜力。&lt;/p&gt;&lt;p&gt;在这一案例中，MiMo-V2.6-Pro 根据要求创作了一首&lt;strong&gt;包含约十种乐器的管弦乐作品&lt;/strong&gt;，完成乐谱生成后，自主将其转换为 MIDI。作品体现了模型对不同乐器分工与配器关系的理解，也展示了它将音乐知识用于旋律创作和整体编排的能力。&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;开始使用&lt;a href=&quot;#开始使用&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;使用 Xiaomi MiMo Desktop 桌面客户端&lt;a href=&quot;#使用-xiaomi-mimo-desktop-桌面客户端&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;随着新模型发布，MiMo Desktop 桌面客户端及会员订阅方案同步上线，欢迎通过下方链接下载体验。订阅会员即可使用 MiMo-V2.6-Pro 和 Flash 模型，也可配置自己的 API Key 使用客户端。&lt;/p&gt;&lt;p&gt;🔗: &lt;a href=&quot;https://mimo.xiaomimimo.com/desktop/&quot; target=&quot;_blank&quot;&gt;https://mimo.xiaomimimo.com/desktop/&lt;/a&gt;&lt;/p&gt;&lt;p&gt;MiMo Desktop 同步上线 MiMo-V2.6-Pro 的 UltraSpeed 超高速模式，提供最高 20 倍的推理速度，满足强实时交互与对响应速度敏感的场景。&lt;/p&gt;&lt;p&gt;原邀测活动将在 1 周后结束，已获得邀测资格的用户切换模型名称后方可继续使用。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;接入 Xiaomi MiMo API&lt;a href=&quot;#接入-xiaomi-mimo-api&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;同时，MiMo-V2.6 系列已上线 Xiaomi MiMo 开放平台，API 价格维持不变。&lt;/p&gt;&lt;p&gt;MiMo-V2.6-Pro 同样于开放平台提供 UltraSpeed 超高速模式，提供最高 20 倍的推理速度。&lt;/p&gt;&lt;p&gt;模型定价如下：&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://aistudio-cdn.xiaomimimo.com/xiaomimimo-static/mimo-docs-figures/display/LadHbthLYo3UZxx0GMbc67drnYe.png&quot; alt=&quot;模型定价&quot; /&gt;&lt;figcaption&gt;模型定价&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;全面开源&lt;a href=&quot;#全面开源&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;我们全面开源 MiMo-V2.6-Pro、Flash 模型权重与技术报告，同步开放 MiMo-V2.6-Distill-Qwen-9B 及配套 RL 研究资源，分享经过验证的训练实践，以下是详细开源内容：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;7k+ 高质量 RL 任务环境&lt;/strong&gt;：覆盖软件工程、漏洞复现、知识型工作、网页设计开发四类智能体任务。以 MiMo-V2.6-Distill-Qwen-9B 为起点展开 RL 训练，在 11 项评测中均较 SFT 基线取得提升：SWE-bench Verified 从 61.1 提升至 66.2，MiMo Cyber Bench 从 31.3 提升至 47.0，Terminal Bench 2.1 从 37.1 提升至 52.8，MiMo Visual Coding 从 64.0 提升至 72.4；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;端到端 RL 训练框架&lt;/strong&gt;：基于 verl、uni-agent 和 mini-swe-agent，覆盖环境交互、轨迹采集、奖励评估与策略优化的完整训练流程。结合开放的模型与任务环境，支持社区围绕训练算法、奖励机制和 agent harness 开展研究与迭代；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;轻量可组合的 Harness&lt;/strong&gt;：开源极简 mini-harnesses，将系统提示、工具与上下文管理解耦，构建多样且可控的训练配置。通过 Multi-Harness Training，能够将多样性和整洁性纳入 RL 训练，提升模型在不同框架、包括未见框架上的泛化能力，支持社区自由组合框架组件、拓展训练配置，持续探索新的研究方向。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://aistudio-cdn.xiaomimimo.com/xiaomimimo-static/mimo-docs-figures/display/CFceb5NaioiB8Cxx6eEcxUzZntf.png&quot; alt=&quot;开源内容概览&quot; /&gt;&lt;figcaption&gt;开源内容概览&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;希望这次分享能为社区持续探索强化学习算法与智能体机制提供共同基础，推动 Agentic RL 研究不断向前。&lt;/p&gt;&lt;p&gt;开源链接：&lt;a href=&quot;https://huggingface.co/collections/XiaomiMiMo/mimo-v26&quot; target=&quot;_blank&quot;&gt;https://huggingface.co/collections/XiaomiMiMo/mimo-v26&lt;/a&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：调用 API 时请使用全小写模型名 mimo-v2.6-pro、mimo-v2.6-flash、mimo-v2.6-pro-ultraspeed。&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;&lt;/section&gt;</content:encoded></item><item><title>零基础部署Codex接入第三方大模型API</title><link>https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-10/</link><guid isPermaLink="true">https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-10/</guid><description>让你的Codex更换大脑</description><pubDate>Sun, 13 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;&lt;p&gt;OpenAI Codex 作为强大的代码生成、解释、调试智能工具，默认依赖官方接口，存在访问限制、调用成本高、国内适配差等问题。通过接入&lt;strong&gt;第三方大模型API&lt;/strong&gt;（DeepSeek、MiniMax、通义千问、聚合API等），可完美替代官方接口，实现无限制调用、低成本落地、适配国内网络环境，同时完整保留 Codex 的代码补全、项目分析、智能问答、工程优化等核心功能。&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;本文为&lt;strong&gt;零基础保姆级教程&lt;/strong&gt;，主要方便零基础小白进行部署，实际在国内网络能&lt;strong&gt;主动&lt;/strong&gt;访问到本博客的人也不需要本教程 &lt;strong&gt;哈哈&lt;/strong&gt;！&lt;/p&gt;
&lt;section&gt;&lt;h1&gt;前置准备&lt;a href=&quot;#前置准备&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;blockquote&gt;&lt;p&gt;点击相应文本可跳转下载&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://www.python.org/&quot; target=&quot;_blank&quot;&gt;&lt;strong&gt;python&lt;/strong&gt;&lt;/a&gt; : 编写脚本、处理数据、自动化任务、开发后端、运行测试，也能生成 Excel、PDF、图表等文件。&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://git-scm.com/book/zh/v2/&quot; target=&quot;_blank&quot;&gt;&lt;strong&gt;Git&lt;/strong&gt;&lt;/a&gt; : 查看代码改动和历史、对比版本、创建提交、处理分支，帮助安全地管理项目。&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://nodejs.org/zh-cn&quot; target=&quot;_blank&quot;&gt;&lt;strong&gt;node.js&lt;/strong&gt;&lt;/a&gt; : 开发和运行网页、前端及 JavaScript/TypeScript 项目；安装依赖、执行构建、测试和代码检查。&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://code.visualstudio.com/&quot; target=&quot;_blank&quot;&gt;&lt;strong&gt;vscode&lt;/strong&gt;&lt;/a&gt; : 方便查看和继续编辑codex修改的代码、运行程序、调试问题。&lt;/p&gt;&lt;section&gt;&lt;h2&gt;python部署教程&lt;a href=&quot;#python部署教程&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;访问&lt;a href=&quot;https://www.python.org/&quot; target=&quot;_blank&quot;&gt;&lt;strong&gt;python&lt;/strong&gt;&lt;/a&gt;，点击&lt;strong&gt;downloads&lt;/strong&gt;，出现新界面后点击python 3.14.6(默认下载推荐版本即可，注意不要点击Python install manager)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;下载完成后双击安装包&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;img src=&quot;https://tu.mstzuomu.space/file/%E6%96%87%E7%AB%A0%E6%8F%92%E5%9B%BE/1789272337417_codex%E6%95%99%E7%A8%8B1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;&lt;p&gt;推荐通过&lt;em&gt;&lt;strong&gt;Customize installation&lt;/strong&gt;&lt;/em&gt;进行安装，可以自定义安装位置。Install Now是默认安装到C盘。
同时勾选下方的两个选项&lt;/p&gt;&lt;ul&gt;
&lt;li&gt; Use admin privileges when installing py.exe&lt;/li&gt;
&lt;li&gt; Add python.exe to PATH&lt;/li&gt;
&lt;/ul&gt;&lt;ol&gt;
&lt;li&gt;直接点击Next&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;img src=&quot;https://tu.mstzuomu.space/file/%E6%96%87%E7%AB%A0%E6%8F%92%E5%9B%BE/1789283806727_codex%E6%95%99%E7%A8%8B2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;自定义安装路径&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;img src=&quot;https://tu.mstzuomu.space/file/%E6%96%87%E7%AB%A0%E6%8F%92%E5%9B%BE/1789284662883_codex%E6%95%99%E7%A8%8B3.png&quot; alt=&quot;&quot; /&gt;
5. 安装完成后关闭窗口即可&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;Git部署教程&lt;a href=&quot;#git部署教程&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;访问&lt;a href=&quot;https://git-scm.com/book/zh/v2/&quot; target=&quot;_blank&quot;&gt;&lt;strong&gt;Git&lt;/strong&gt;&lt;/a&gt; 点击&lt;strong&gt;install&lt;/strong&gt;，选择对应的系统下载，点击&lt;strong&gt;Click here to download&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;blockquote&gt;&lt;p&gt;如果你在国内的网络下载Git过慢，并且没有加速器，可以选择&lt;a href=&quot;https://mirrors.tuna.tsinghua.edu.cn/&quot; target=&quot;_blank&quot;&gt;&lt;strong&gt;清华大学开源软件镜像站 | Tsinghua Open Source Mirror&lt;/strong&gt;&lt;/a&gt;找到Git for windows下载安装即可。&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;img src=&quot;https://tu.mstzuomu.space/file/%E6%96%87%E7%AB%A0%E6%8F%92%E5%9B%BE/1789286714953_codex%E6%95%99%E7%A8%8B4.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;Git安装相对简单，下载完成安装包后一直点击&lt;strong&gt;Next&lt;/strong&gt;即可，无需修改设置&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;node.js部署教程&lt;a href=&quot;#nodejs部署教程&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;访问&lt;a href=&quot;https://nodejs.org/zh-cn&quot; target=&quot;_blank&quot;&gt;Node.js — 在任何地方运行 JavaScript&lt;/a&gt;,点击&lt;strong&gt;获取Node.js&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;img src=&quot;https://tu.mstzuomu.space/file/%E6%96%87%E7%AB%A0%E6%8F%92%E5%9B%BE/1789287217259_codex%E6%95%99%E7%A8%8B5.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;点击Windows安装程序即可获取下载。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;下载完成安装包后选择安装路径，无需修改设置直接默认安装即可。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;VS Code部署教程&lt;a href=&quot;#vs-code部署教程&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;访问&lt;a href=&quot;https://code.visualstudio.com/&quot; target=&quot;_blank&quot;&gt;Visual Studio Code - 开源的 AI 代码编辑器 |你多智能体开发的家园&lt;/a&gt;，点击&lt;strong&gt;Windows版下载&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;下载完成后打开安装包，选择安装路径后安装即可。&lt;/li&gt;
&lt;li&gt;VScode默认为英文，切换中文可以按如下操作&lt;/li&gt;
&lt;/ol&gt;&lt;ul&gt;
&lt;li&gt;点击左侧工具栏的扩展，在弹出的搜索界面搜索chinese&lt;/li&gt;
&lt;li&gt;点击安装，安装完成后右下角会弹出引导提示，跟随提示重启VScode即可&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;img src=&quot;https://tu.mstzuomu.space/file/%E6%96%87%E7%AB%A0%E6%8F%92%E5%9B%BE/1789287940816_codex%E6%95%99%E7%A8%8B6.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h1&gt;Codex部署&lt;a href=&quot;#codex部署&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;访问&lt;a href=&quot;https://chatgpt.com/zh-Hans-CN/codex/&quot; target=&quot;_blank&quot;&gt;&lt;strong&gt;ChatGPT 中的 Codex | 专为软件工程打造的 AI 编程智能体&lt;/strong&gt;&lt;/a&gt;点击下载Windows版。注意！国内网络不支持访问Codex官网，请自行解决该问题。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果您无法解决网络问题，可以通过微软商店下载。本文提供微软镜像下载方法，您可以通过本站“我的—书签导航—工具”找到&lt;a href=&quot;https://store.rg-adguard.net/&quot; target=&quot;_blank&quot;&gt;Microsoft商店 - 世代项目（v1.2.3）[由@rgadguard提供]&lt;/a&gt;在该网站内键入&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;https://apps.microsoft.com/detail/9plm9xgg6vks?hl=zh-CN&amp;amp;gl=CN&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;选择适合你的安装包&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://tu.mstzuomu.space/file/%E6%96%87%E7%AB%A0%E6%8F%92%E5%9B%BE/1789288756996_codex%E6%95%99%E7%A8%8B7.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;&lt;p&gt;如无特殊设备，选择x64版本的msix文件下载即可&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;警告：如果选择此方式安装，请勿将系统的默认安装位置C盘修改成其他盘，微软商店不支持修改安装位置，否则会无法安装或无法正常使用&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;
&lt;section&gt;&lt;h1&gt;第三方大模型 API Key获取&lt;a href=&quot;#第三方大模型-api-key获取&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;目前市面上有许多主流大模型，如豆包太蠢了哈哈！千问、文心一言、智谱GLM、DeepSeek、Kimi。本文以&lt;strong&gt;小米MiMo&lt;/strong&gt;模型为例演示如何让获取API KEY，推荐读者使用智谱，GPT等模型，不考虑价格因素，目前是市面上相对较强的模型。&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;访问&lt;a href=&quot;https://mimo.mi.com/&quot; target=&quot;_blank&quot;&gt;&lt;strong&gt;Xiaomi MiMo Home&lt;/strong&gt;&lt;/a&gt;，点击API接入&lt;/li&gt;
&lt;li&gt;点击API KEYS—新建API KEY—输入KEY的名称，随后即可生成API KEY&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;警告：API KEY只会出现一次，请及时复制之后保存。&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;ol&gt;
&lt;li&gt;将API KEY填入CC Switch，如果您不想使用CC S作为API中转，可以根据不同Agent的官方文档教程来进行部署，本文不作介绍。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;
&lt;section&gt;&lt;h1&gt;CC Switch部署&lt;a href=&quot;#cc-switch部署&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;访问&lt;a href=&quot;https://ccswitch.io/zh/&quot; target=&quot;_blank&quot;&gt;CC Switch 官方网站 - AI 编程工具统一管理平台&lt;/a&gt;，下载安装即可&lt;/p&gt;&lt;section&gt;&lt;h2&gt;CC Switch快速上手&lt;a href=&quot;#cc-switch快速上手&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;第一步：添加供应商&lt;a href=&quot;#第一步添加供应商&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;点击主界面右上角的 + 按钮&lt;/li&gt;
&lt;li&gt;在「预设」下拉框中选择你的供应商
• 常用预设：智谱 GLM、MiniMax、DeepSeek、Kimi、PackyCode
• 或选择「自定义」手动配置&lt;/li&gt;
&lt;li&gt;填写 API Key&lt;/li&gt;
&lt;li&gt;点击「添加」
&lt;img src=&quot;https://ccswitch.io/docs/assets/image-20260108002807657.png&quot; alt=&quot;&quot; /&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;第二步：切换供应商&lt;a href=&quot;#第二步切换供应商&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;添加完成后，供应商会出现在列表中。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;方式一：主界面切换&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;点击供应商卡片的「启用」按钮&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;方式二：托盘快速切换&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;右键系统托盘图标&lt;/li&gt;
&lt;li&gt;直接点击供应商名称&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;完成以上步骤后，您可以自由的使用Codex接入的第三方API，请注意每次启动Codex之前都需要开启CC Switch&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;&lt;/section&gt;</content:encoded></item><item><title>MIMO CODE客户端发布</title><link>https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-9/</link><guid isPermaLink="true">https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-9/</guid><description>属于MIMO的客户端即将发布</description><pubDate>Wed, 09 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h1&gt;前言&lt;a href=&quot;#前言&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;就在今天下午，小米官方通过邮件发文：面向个人开发者的桌面客户端 &lt;strong&gt;Xiaomi MiMo Desktop&lt;/strong&gt; 正式开放邀测。&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;MiMo Desktop 是一款面向真实工作场景的桌面 AI 应用：接收多格式素材输入，理解目标，拆解任务，调用工具，交付高可用度成果，并支持实时预览与修改。我们希望通过真实任务，验证它在长链路执行、工具调用、交付质量和使用体验上的边界，并据此持续迭代。&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;
&lt;section&gt;&lt;h1&gt;核心能力&lt;a href=&quot;#核心能力&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;从回答问题到交付成果&lt;/strong&gt;：表格、图片、视频、PDF、录音甚至压缩包都可直接作为输入；MiMo Desktop 理解素材、拆解任务、调用工具，交付可继续编辑的文档、表格、幻灯片、网页、图片、音频、视频、3D 模型、App 或软件工程项目。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可直接交互的预览界面&lt;/strong&gt;：成果预览不是静态截图，而是可点击、操作和继续修改的完整页面，支持在会话中实时运行和迭代。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;在成果上继续工作&lt;/strong&gt;：选中任意元素即可用自然语言提出修改要求，系统只处理选中部分并保留版本记录，可随时回退到此前版本。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Smart 调度&lt;/strong&gt;：自动评估任务类型、复杂度与成本，在标准与旗舰模型之间动态路由，并按需匹配 Harness、Agent 与 Skill；复杂任务可拆解给多个 Agent 并行推进。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;浏览器操控&lt;/strong&gt;：自主控制浏览器检索信息、提取素材、填写表单，信息直接进入当前任务；生成网页类成果后可自动检查关键交互。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;让长任务成本可控&lt;/strong&gt;：模型路由、局部编辑与上下文缓存减少不必要消耗，同会话缓存命中率最高可达 99%，跨会话最高可达 95%。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;
&lt;section&gt;&lt;h1&gt;新模型抢先体验&lt;a href=&quot;#新模型抢先体验&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;邀测期间，通过申请的用户可限时、限量、免费体验两款 Xiaomi MiMo 新一代模型（代号：MiMo-X-Pro-Preview、MiMo-X-Flash-Preview）。MiMo-X 系列面向真实专业工作场景优化，重点覆盖复杂任务推理、多 Agent 协作、大型编程项目、Office、网页设计、前端与交互设计、视频剪辑、3D 生成、音乐生成、电脑操控等。目前仍处于预览测试阶段，能力表现可能随版本迭代变化。&lt;/p&gt;&lt;section&gt;&lt;h2&gt;如何加入邀测&lt;a href=&quot;#如何加入邀测&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;访问&lt;a href=&quot;https://mimo.xiaomimimo.com/desktop/invite/&quot; target=&quot;_blank&quot;&gt;&lt;strong&gt;申请链接&lt;/strong&gt;&lt;/a&gt;并提交申请。审核通过后，即可下载和使用 MiMo Desktop。本次邀测将优先面向 Xiaomi MiMo 开放平台已有用户开放。&lt;/p&gt;&lt;p&gt;Xiaomi MiMo Desktop 暂不在欧盟、英国及韩国提供服务，来自相关区域的用户登录时会收到“该地区暂不支持”的提示。&lt;/p&gt;&lt;p&gt;我们相信，AGI 的意义不止于理解世界，更在于帮助每个人创造世界。MiMo Desktop 是我们迈入真实工作流、帮助用户将想法转化为高质量成果的第一步。欢迎申请参与邀测，并用真实任务告诉我们：它已经能够完成什么，还有哪些地方需要改进。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;</content:encoded></item><item><title>龙之剑自由相机模组</title><link>https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-8/</link><guid isPermaLink="true">https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-8/</guid><description>在龙之剑中调整相机</description><pubDate>Fri, 28 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h1&gt;🗡龙之剑自由相机模组&lt;a href=&quot;#龙之剑自由相机模组&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;N网上有个模组我挺喜欢用，可以调整相机镜头，并且反虚化，但是没有汉化并且窗口特别小，在大屏上看眼睛都要花了，于是我简单进行了汉化，并且增添了调整窗口大小的功能。&lt;/p&gt;&lt;p&gt;原版模组&lt;a href=&quot;https://www.nexusmods.com/dragonswordawakening/mods/120&quot; target=&quot;_blank&quot;&gt;&lt;em&gt;&lt;strong&gt;下载地址&lt;/strong&gt;&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;我的修改版&lt;a href=&quot;https://pan.baidu.com/s/16PkOWTgklbnmFSWf3PzAKw?pwd=9bd8&quot; target=&quot;_blank&quot;&gt;&lt;em&gt;&lt;strong&gt;下载地址&lt;/strong&gt;&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;原版模组描述：移除放大角色时的透明效果，这样你可以欣赏素材或其他内容。还允许配置一些摄像机设置（距离、角色偏移等），默认还能解锁摄像机的俯仰角，这样你就能看天空，嗯嗯。&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;
&lt;section&gt;&lt;h1&gt;📕使用教程&lt;a href=&quot;#使用教程&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;ol&gt;
&lt;li&gt;需要安装&lt;a href=&quot;https://github.com/UE4SS-RE/RE-UE4SS/releases&quot; target=&quot;_blank&quot;&gt;发行版 ·UE4SS-RE/RE-UE4SS&lt;/a&gt;，并且一定要安装最新的版本，如果你已经安装可以忽略此步骤。&lt;/li&gt;
&lt;li&gt;下载后解压到&lt;code&gt;DragonSword  Awakening\DS\Binaries\Win64\&lt;/code&gt;路径下。&lt;/li&gt;
&lt;li&gt;下载模组，并解压到&lt;code&gt;DragonSword  Awakening\DS\Binaries\Win64\ue4ss\Mods&lt;/code&gt;目录下&lt;/li&gt;
&lt;li&gt;启动游戏，当完全进入游戏后按&lt;strong&gt;F5&lt;/strong&gt;即可唤出模组界面。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://tu.mstzuomu.space/file/%E6%96%87%E7%AB%A0%E6%8F%92%E5%9B%BE/1787886946708_%E9%BE%99%E4%B9%8B%E5%89%91.png&quot; alt=&quot;龙之剑.png&quot; title=&quot;模组界面&quot; /&gt;&lt;figcaption&gt;龙之剑.png&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h1&gt;📷 CameraTweaker 设置面板功能详解&lt;a href=&quot;#-cameratweaker-设置面板功能详解&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;section&gt;&lt;h2&gt;🔘 开关类设置&lt;a href=&quot;#-开关类设置&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;设置&lt;/th&gt;&lt;th&gt;说明&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;启用相机调整&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;总开关&lt;/strong&gt;。关闭后所有相机参数恢复游戏默认值。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;禁用近距离透明&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;开启后，角色靠近镜头时不会变透明（原版游戏会变透明）。需要切换角色后生效。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;应用到载具&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;开启后，相机参数也会应用到载具上。默认关闭。&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;📐 实时更新选项（拖动滑块立即生效）&lt;a href=&quot;#-实时更新选项拖动滑块立即生效&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;设置&lt;/th&gt;&lt;th&gt;默认值&lt;/th&gt;&lt;th&gt;说明&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;最小俯仰角&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;-90°&lt;/td&gt;&lt;td&gt;视角能往下看的最低角度。原版是 -45°，改成 -90° 可以直接看地面。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;最大俯仰角&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;90°&lt;/td&gt;&lt;td&gt;视角能往上看的最高角度。原版是 45°，改成 90° 可以直接看天空。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;相机跟随速度&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;10&lt;/td&gt;&lt;td&gt;相机跟随角色移动的速度。值越大，相机跟得越紧，不容易”飘”。设为 0 则禁用跟随延迟。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;相机相对位置 X/Y/Z&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;相机相对于角色的直接偏移量。X=前后，Y=左右，Z=上下。&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;⏳ 非实时更新选项（切换角色后生效）&lt;a href=&quot;#-非实时更新选项切换角色后生效&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;设置&lt;/th&gt;&lt;th&gt;默认值&lt;/th&gt;&lt;th&gt;说明&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;相机最近距离&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;拉近镜头的最小距离。值越小，镜头能离角色越近。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;相机最远距离&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;600&lt;/td&gt;&lt;td&gt;拉远镜头的最大距离。值越大，镜头能离角色越远。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;近景插槽偏移 X&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;-100&lt;/td&gt;&lt;td&gt;镜头拉近时，相机的前后位置。负值=在角色前方（防止镜头穿进身体）。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;近景插槽偏移 Y&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;30&lt;/td&gt;&lt;td&gt;镜头拉近时，相机的左右位置。正值=偏右，负值=偏左。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;近景插槽偏移 Z&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;75&lt;/td&gt;&lt;td&gt;镜头拉近时，相机的高度。75 大约是头部高度。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;远景插槽偏移 X&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;镜头拉远时，相机的前后位置。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;远景插槽偏移 Y&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;60&lt;/td&gt;&lt;td&gt;镜头拉远时，相机的左右位置。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;远景插槽偏移 Z&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;125&lt;/td&gt;&lt;td&gt;镜头拉远时，相机的高度。&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;🖥️ 面板设置&lt;a href=&quot;#️-面板设置&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;设置&lt;/th&gt;&lt;th&gt;说明&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;面板宽度&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;调整设置面板的宽度（300-1200）。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;面板高度&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;调整设置面板的高度（400-1000）。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;应用面板大小&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;点击后保存并重建面板，使尺寸更改生效。&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;🔘 底部按钮&lt;a href=&quot;#-底部按钮&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;按钮&lt;/th&gt;&lt;th&gt;说明&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;应用并保存&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;应用所有相机参数并保存到配置文件。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;重置&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;恢复所有设置为默认值。&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;💡 使用建议&lt;a href=&quot;#-使用建议&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;想要自由视角&lt;/strong&gt;：把俯仰角范围调到 -90° ~ 90°&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;想要更近的特写镜头&lt;/strong&gt;：把相机最近距离调小&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;想要更稳定的镜头&lt;/strong&gt;：把相机跟随速度调高&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;想要肩越视角&lt;/strong&gt;：调整近景插槽偏移 Y（左右）和 Z（高度）&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;按 F5 打开面板，试试调整这些参数！&lt;/strong&gt; 🎮&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;</content:encoded></item><item><title>从零开始搭建个人图床</title><link>https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-7/</link><guid isPermaLink="true">https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-7/</guid><description>简单搭建属于你的个人图床</description><pubDate>Mon, 24 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h1&gt;利用CF和TG搭建个人图床&lt;a href=&quot;#利用cf和tg搭建个人图床&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;blockquote&gt;&lt;p&gt;本文介绍方法基于&lt;strong&gt;Cloudflare Workers&lt;/strong&gt;和&lt;strong&gt;Telegram&lt;/strong&gt;，开始部署前请先确保有CF和TG账号，如果您想自行选取方法，请参考官方文档。
&lt;code&gt;项目地址：&lt;/code&gt;&lt;a href=&quot;https://github.com/MarSeventh/CloudFlare-ImgBed&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;CloudFlare-ImgBed&lt;/code&gt;&lt;/a&gt;
&lt;code&gt;官方文档：&lt;/code&gt;&lt;a href=&quot;https://cfbed.sanyue.de/&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;CloudFlare-ImgBed&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;section&gt;&lt;h2&gt;前期准备&lt;a href=&quot;#前期准备&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;在 Telegram 中搜索 &lt;a href=&quot;https://t.me/BotFather&quot; target=&quot;_blank&quot;&gt;@BotFather&lt;/a&gt;并添加到列表&lt;/li&gt;
&lt;li&gt;发送 &lt;code&gt;/newbot&lt;/code&gt; 命令&lt;/li&gt;
&lt;li&gt;按提示输入 Bot 名称和用户名等信息&lt;/li&gt;
&lt;li&gt;成功创建后将会获得 Bot Token（格式：&lt;code&gt;123456789:ABCdefGHIjklMNOpqrsTUVwxyz&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;创建一个全新的频道，选择频道管理，将刚才创建的Bot设为管理员。&lt;/li&gt;
&lt;li&gt;设备Bot权限，除邀请新成员外其他权限全部开启&lt;/li&gt;
&lt;li&gt;在频道中发送一条测试消息&lt;/li&gt;
&lt;li&gt;向 &lt;a href=&quot;https://t.me/VersaToolsBot&quot; target=&quot;_blank&quot;&gt;@VersaToolsBot&lt;/a&gt; 转发这条消息&lt;/li&gt;
&lt;li&gt;获得频道 ID（示例：&lt;code&gt;-1001234567890&lt;/code&gt;）&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;Cloudflare Workers部署&lt;a href=&quot;#cloudflare-workers部署&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;第一步：Fork 项目&lt;a href=&quot;#第一步fork-项目&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;访问 &lt;a href=&quot;https://github.com/MarSeventh/CloudFlare-ImgBed&quot; target=&quot;_blank&quot;&gt;CloudFlare ImgBed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;点击右上角的 “Fork” 按钮，如果您喜欢该项目，欢迎加入Star以支持&lt;/li&gt;
&lt;li&gt;选择您的 GitHub 账户&lt;/li&gt;
&lt;li&gt;确认 Fork 完成&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;第二步：准备 Cloudflare 资源&lt;a href=&quot;#第二步准备-cloudflare-资源&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;section&gt;&lt;h4&gt;2.1 获取 API 令牌 和 Account ID&lt;a href=&quot;#21-获取-api-令牌-和-account-id&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;登录 &lt;a href=&quot;https://dash.cloudflare.com/&quot; target=&quot;_blank&quot;&gt;Cloudflare Dashboard&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;点击右上角头像 → “配置文件” → “API 令牌”&lt;/li&gt;
&lt;li&gt;点击 “创建令牌”&lt;/li&gt;
&lt;li&gt;选择 “编辑 Cloudflare Workers” 模板&lt;/li&gt;
&lt;li&gt;账户资源选择自己的账户，区域资源选择所有区域&lt;/li&gt;
&lt;li&gt;确认权限并创建，&lt;strong&gt;记录生成的API令牌&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;返回账户主页，右侧三个点展开复制账户ID，如果找不到可以直接搜索”复制账户”，&lt;strong&gt;记录复制的账户ID&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;2.2 创建数据库&lt;a href=&quot;#22-创建数据库&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p&gt;数据库用于存储文件元数据，本文选择KV&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;特点&lt;/th&gt;&lt;th&gt;KV 数据库&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;读写性能&lt;/td&gt;&lt;td&gt;高&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;免费额度&lt;/td&gt;&lt;td&gt;少&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/section&gt;&lt;section&gt;&lt;h4&gt;KV 数据库&lt;a href=&quot;#kv-数据库&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;在 Dashboard 中选择 “存储和数据库” → “Workers KV”&lt;/li&gt;
&lt;li&gt;点击 “创建实例”，名称填 &lt;code&gt;img_url&lt;/code&gt;，请勿修改此名称&lt;/li&gt;
&lt;li&gt;创建完成后返回，&lt;strong&gt;记录img_url ID&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;⚙️ 第三步：配置 GitHub Secrets&lt;a href=&quot;#️-第三步配置-github-secrets&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;在 Fork 的仓库中，进入 &lt;strong&gt;Settings → Secrets and variables → Actions → Secrets&lt;/strong&gt;，添加以下 Secrets：&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Secret 名称&lt;/th&gt;&lt;th&gt;说明&lt;/th&gt;&lt;th&gt;是否必填&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;CLOUDFLARE_API_TOKEN&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Cloudflare API Token&lt;/td&gt;&lt;td&gt;✅ 必填&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;CLOUDFLARE_ACCOUNT_ID&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Cloudflare Account ID&lt;/td&gt;&lt;td&gt;✅ 必填&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;KV_NAMESPACE_ID&lt;/code&gt;&lt;/td&gt;&lt;td&gt;KV 命名空间 ID&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;创建完成后进入 &lt;strong&gt;Actions → I understand… →Deploy to Cloudflare Workers →Run workflow →Run workflow&lt;/strong&gt;（注意此处默认的work name不要修改）&lt;/p&gt;&lt;p&gt;运行之后等待一段时间部署deploy&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;存储渠道配置&lt;a href=&quot;#存储渠道配置&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;部署完成后访问您的域名，进入管理后台配置存储渠道。&lt;/p&gt;&lt;section&gt;&lt;h3&gt;访问管理后台&lt;a href=&quot;#访问管理后台&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;访问 &lt;code&gt;https://你的域名/dashboard&lt;/code&gt;&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;管理后台默认无需密码，登录后请及时设置管理员用户名和密码。&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;配置 Telegram 渠道&lt;a href=&quot;#配置-telegram-渠道&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;左上角菜单栏进入 “系统设置” → “上传设置”&lt;/li&gt;
&lt;li&gt;找到 “Telegram 渠道配置”&lt;/li&gt;
&lt;li&gt;点击 “添加渠道”&lt;/li&gt;
&lt;li&gt;填入准备好的 Token 和 Chat ID：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;渠道名称&lt;/strong&gt;：自定义名称（如：主渠道）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bot Token&lt;/strong&gt;：从 @BotFather 获得的 Token&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Chat ID&lt;/strong&gt;：频道 ID（有&lt;code&gt;-&lt;/code&gt;号时需要保留）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;代理 URL&lt;/strong&gt;：（可选）自定义代理地址，用于代理 Telegram API 请求&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;启用状态&lt;/strong&gt;：开启&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;点击 “保存设置”&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;/section&gt;&lt;/section&gt;</content:encoded></item><item><title>更新公告20260823</title><link>https://azuma.mstzuomu.space/posts/web-update-6/</link><guid isPermaLink="true">https://azuma.mstzuomu.space/posts/web-update-6/</guid><description>网站更新公告</description><pubDate>Sun, 23 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;hr /&gt;
&lt;section&gt;&lt;h2&gt;更新公告20260823&lt;a href=&quot;#更新公告20260823&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;1.网站版本更新到6.16.2&lt;/p&gt;&lt;p&gt;2.修复了&lt;a href=&quot;https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-6/&quot;&gt;&lt;strong&gt;文章中添加音乐播放器&lt;/strong&gt;&lt;/a&gt;功能失效的问题，修复了音频丢失的问题&lt;/p&gt;&lt;p&gt;3.增添全新的首页样式&lt;/p&gt;&lt;p&gt;4.网站默认字体更换为汉仪文黑65w,修改路径src\config\fontConfig.ts&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>文章中添加音乐播放器</title><link>https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-6/</link><guid isPermaLink="true">https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-6/</guid><description>简单实现文章嵌入播放器</description><pubDate>Tue, 18 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h2&gt;利用现有组件添加音乐播放器&lt;a href=&quot;#利用现有组件添加音乐播放器&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;blockquote&gt;&lt;p&gt;依旧可以直接利用 MD 语法在文章中嵌入组件，并且实现默认自动播放。&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;效果展示（音频源网易云示例）&lt;a href=&quot;#效果展示音频源网易云示例&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;未知歌曲&lt;/div&gt;&lt;div&gt;未知艺术家&lt;/div&gt;&lt;div&gt;&lt;span&gt;网易云音乐 · 单曲&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;0:00&lt;/span&gt;&lt;span&gt;0:00&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;使用说明&lt;a href=&quot;#使用说明&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;直接URL方式&lt;a href=&quot;#直接url方式&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;::music{url=&quot;https://example.com/song.mp3&quot; title=&quot;歌曲标题&quot; artist=&quot;艺术家&quot; cover=&quot;封面URL&quot;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;网易云音乐方式&lt;a href=&quot;#网易云音乐方式&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;::music{server=&quot;netease&quot; type=&quot;song&quot; id=&quot;1901371647&quot; title=&quot;歌曲标题&quot; artist=&quot;艺术家&quot;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;参数说明（基本和主页播放器组件相同）&lt;a href=&quot;#参数说明基本和主页播放器组件相同&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;参数&lt;/th&gt;&lt;th&gt;说明&lt;/th&gt;&lt;th&gt;示例&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;url&lt;/code&gt;&lt;/td&gt;&lt;td&gt;音乐文件URL&lt;/td&gt;&lt;td&gt;&lt;code&gt;url=&quot;/assets/music/song.mp3&quot;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;server&lt;/code&gt;&lt;/td&gt;&lt;td&gt;音乐平台&lt;/td&gt;&lt;td&gt;&lt;code&gt;server=&quot;netease&quot;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;id&lt;/code&gt;&lt;/td&gt;&lt;td&gt;歌曲ID&lt;/td&gt;&lt;td&gt;&lt;code&gt;id=&quot;1901371647&quot;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;type&lt;/code&gt;&lt;/td&gt;&lt;td&gt;ID类型(默认song)&lt;/td&gt;&lt;td&gt;&lt;code&gt;type=&quot;song&quot;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;title&lt;/code&gt;&lt;/td&gt;&lt;td&gt;歌曲标题&lt;/td&gt;&lt;td&gt;&lt;code&gt;title=&quot;我的歌曲&quot;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;artist&lt;/code&gt;&lt;/td&gt;&lt;td&gt;艺术家&lt;/td&gt;&lt;td&gt;&lt;code&gt;artist=&quot;艺术家&quot;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;cover&lt;/code&gt;&lt;/td&gt;&lt;td&gt;封面图片URL&lt;/td&gt;&lt;td&gt;&lt;code&gt;cover=&quot;https://example.com/cover.jpg&quot;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;autoplay&lt;/code&gt;&lt;/td&gt;&lt;td&gt;自动播放(默认true)&lt;/td&gt;&lt;td&gt;&lt;code&gt;autoplay=&quot;true&quot;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;loop&lt;/code&gt;&lt;/td&gt;&lt;td&gt;循环播放(默认false)&lt;/td&gt;&lt;td&gt;&lt;code&gt;loop=&quot;true&quot;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;volume&lt;/code&gt;&lt;/td&gt;&lt;td&gt;音量0-1(默认0.7)&lt;/td&gt;&lt;td&gt;&lt;code&gt;volume=&quot;0.5&quot;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;section&gt;&lt;h3&gt;代码实现&lt;a href=&quot;#代码实现&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;rehype-component-music-player.mjs（组件，&lt;code&gt;src/plugins/&lt;/code&gt;）
解析 ::music{…} 语法
支持参数：url、server、id、type、title、artist、cover、autoplay、loop、volume&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;music-player-script.js（脚本，&lt;code&gt;src/plugins/&lt;/code&gt;）
音频播放控制（播放/暂停/进度/音量）
Meting API 集成（网易云、QQ音乐等，三级回退）
多播放器互斥暂停、自动播放处理
切页时自动暂停并重新初始化（见下方事件说明）&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;blockquote&gt;&lt;p&gt;切页事件的坑：站点用的是 Swup 4 + &lt;code&gt;@swup/astro&lt;/code&gt;，它有个兼容层会把 Swup 钩子转成
astro 风格事件派发——&lt;code&gt;content:replace&lt;/code&gt; 前触发 &lt;code&gt;astro:before-swap&lt;/code&gt;，&lt;code&gt;page:view&lt;/code&gt;
触发 &lt;code&gt;astro:page-load&lt;/code&gt;。&lt;strong&gt;这两个才是实际会触发的事件&lt;/strong&gt;；Swup v3 时代的
&lt;code&gt;swup:contentReplaced&lt;/code&gt; / &lt;code&gt;swup:willReplaceContent&lt;/code&gt; 在 4.x 里根本不派发，
写了也白写（会表现为：swup 切进文章时播放器不初始化、切页后音乐不停）。
脚本里监听 &lt;code&gt;astro:before-swap&lt;/code&gt; 暂停、&lt;code&gt;astro:page-load&lt;/code&gt; 清理并重新初始化。&lt;/p&gt;&lt;/blockquote&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;music-player.css（样式，&lt;code&gt;src/styles/&lt;/code&gt;）
跟随主题色的扁平卡片风，全部用 Tailwind &lt;code&gt;@apply&lt;/code&gt; + 主题变量
暗色模式自动适配（不再手写 .dark 覆盖）
加载中/错误/播放中三个状态类&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;astro.config.mjs（配置）&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import { MusicPlayerComponent } from &quot;./src/plugins/rehype-component-music-player.mjs&quot;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;components: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;music: MusicPlayerComponent,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;ol&gt;
&lt;li&gt;MusicPlayerManager.astro（脚本接入，&lt;code&gt;src/components/features/&lt;/code&gt;）&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;对齐框架里 GithubCardManager 的模式，一行副作用导入脚本，由 Layout 统一渲染：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 文章内嵌音乐播放器客户端管理器（::music 指令）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;script&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;import &quot;@/plugins/music-player-script.js&quot;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;Layout.astro&lt;/code&gt; 里引入并渲染这个组件即可，不再需要手写 &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; 和 &lt;code&gt;&amp;lt;script src&amp;gt;&lt;/code&gt;：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import MusicPlayerManager from &quot;@components/features/MusicPlayerManager.astro&quot;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;MusicPlayerManager /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;ol&gt;
&lt;li&gt;样式加载（&lt;code&gt;Markdown.astro&lt;/code&gt; / &lt;code&gt;DynamicItemTemplate.astro&lt;/code&gt;）&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import &quot;@/styles/music-player.css&quot;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;和 markdown.css、markdown-extend.styl 放在一起，只在有正文的页面（文章、动态）加载，首页等页面不背这份 CSS。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;</content:encoded></item><item><title>2026新版Kindle解密｜告别旧版PC客户端，一键导出EPUB电子书</title><link>https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-5/</link><guid isPermaLink="true">https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-5/</guid><description>一键去除Kindle的D加密？已经这么方便啦！</description><pubDate>Mon, 17 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h1&gt;解除Kindle电子书DRM加密并转换为EPUB格式教程&lt;a href=&quot;#解除kindle电子书drm加密并转换为epub格式教程&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;blockquote&gt;&lt;p&gt;本教程适用于&lt;strong&gt;微软商店版Kindle应用&lt;/strong&gt;。原exe安装包版本（Kindle for PC）已于2026‑06‑30终止服务，2.8版本已停止维护，2.9版本虽暂时可用，但后续也将不再提供支持。&lt;/p&gt;&lt;/blockquote&gt;&lt;section&gt;&lt;h2&gt;所需工具&lt;a href=&quot;#所需工具&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;微软商店版 &lt;a href=&quot;https://apps.microsoft.com/detail/9p8jq0jjstll?hl=zh-CN&amp;amp;gl=CN&quot; target=&quot;_blank&quot;&gt;Kindle&lt;/a&gt;（如果下载过慢可从&lt;a href=&quot;https://store.rg-adguard.net/&quot; target=&quot;_blank&quot;&gt;离线站&lt;/a&gt;下载）&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://calibre-ebook.com/&quot; target=&quot;_blank&quot;&gt;Calibre&lt;/a&gt;：开源电子书管理软件，支持各类电子书格式转换与插件扩展&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/Satsuoni/DeDRM_tools/releases&quot; target=&quot;_blank&quot;&gt;DeDRM‑Tools&lt;/a&gt; 解密工具包：版本需要与Kindle版本匹配，版本号建议大于等于对应Kindle版本&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;操作步骤&lt;a href=&quot;#操作步骤&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;1. 在Kindle客户端下载电子书&lt;a href=&quot;#1-在kindle客户端下载电子书&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;打开微软商店版Kindle，将需要解密的电子书完整下载到本地。&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;https://tu.mstzuomu.space/file/%E6%96%87%E7%AB%A0%E6%8F%92%E5%9B%BE/1786968985952_%E6%96%87%E7%AB%A0%E6%8F%92%E5%9B%BE.png&quot; alt=&quot;新版Kindle界面&quot; /&gt;&lt;figcaption&gt;新版Kindle界面&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;2. 使用DeDRM‑Tools提取解密后的KFX‑ZIP文件&lt;a href=&quot;#2-使用dedrmtools提取解密后的kfxzip文件&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;在DeDRM工具包目录中，双击运行 &lt;code&gt;MSIXKFXArchiverMobi1_18632.exe&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;程序运行成功后，工具包文件夹内会生成 &lt;code&gt;archived_kfx&lt;/code&gt; 文件夹；&lt;/li&gt;
&lt;li&gt;该文件夹内的 &lt;strong&gt;kfx‑zip&lt;/strong&gt; 文件即为去除DRM加密后的电子书源文件；&lt;/li&gt;
&lt;li&gt;提示：工具会自动在C盘根目录生成 &lt;code&gt;Date&lt;/code&gt; 文件夹，任务完成后可直接删除该文件夹。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;3. Calibre安装KFX Input插件&lt;a href=&quot;#3-calibre安装kfx-input插件&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;blockquote&gt;&lt;p&gt;Calibre需要安装KFX Input插件才能识别kfx‑zip格式文件&lt;/p&gt;&lt;/blockquote&gt;&lt;ol&gt;
&lt;li&gt;打开Calibre，点击顶部菜单栏「首选项」；&lt;/li&gt;
&lt;li&gt;在窗口下方选择「插件」；&lt;/li&gt;
&lt;li&gt;在弹窗底部点击「获取新的插件」；&lt;/li&gt;
&lt;li&gt;搜索 &lt;code&gt;KFX Input&lt;/code&gt;，选中后点击安装，安装过程可能短暂卡顿；&lt;/li&gt;
&lt;li&gt;按照提示&lt;strong&gt;重启Calibre&lt;/strong&gt;使插件生效。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;4. 导入文件并转换为EPUB格式&lt;a href=&quot;#4-导入文件并转换为epub格式&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;将解密得到的 &lt;code&gt;kfx‑zip&lt;/code&gt; 文件直接拖拽到Calibre书库中；&lt;/li&gt;
&lt;li&gt;文件导入成功后，右键选中该书，选择「批量转换」；&lt;/li&gt;
&lt;li&gt;在转换设置右上角，输出格式选择 &lt;code&gt;EPUB&lt;/code&gt;，按需调整其他参数；&lt;/li&gt;
&lt;li&gt;确认执行转换，完成后即可得到无DRM加密的EPUB电子书。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;补充说明&lt;a href=&quot;#补充说明&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;相比旧版Kindle for PC的解密流程，当前DeDRM‑Tools更新后的版本操作流程更加简洁。本教程流程基于该工具作者更新的新版工具实现。&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;⚠️ 法律提示：本教程仅用于个人已购买电子书的备份，请勿将解密后的文件用于传播、分享或商用，遵守著作权相关法律法规。&lt;/p&gt;&lt;/blockquote&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;</content:encoded></item><item><title>更新公告20260817</title><link>https://azuma.mstzuomu.space/posts/web-update-5/</link><guid isPermaLink="true">https://azuma.mstzuomu.space/posts/web-update-5/</guid><description>网站更新公告</description><pubDate>Mon, 17 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;hr /&gt;
&lt;section&gt;&lt;h2&gt;更新公告20260817&lt;a href=&quot;#更新公告20260817&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;1.修复了网站异常跳转后崩溃的问题&lt;/p&gt;&lt;p&gt;2.添加了文章中音乐播放器功能，详情可见&lt;a href=&quot;https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-6/&quot;&gt;&lt;strong&gt;文章中添加音乐播放器&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>基于Firefly主题的列表封面位置改造</title><link>https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-4/</link><guid isPermaLink="true">https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-4/</guid><description>不想让封面挤在侧边？Firefly封面置顶记录</description><pubDate>Sun, 16 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h1&gt;Firefly 主题自定义文章封面位置：实现列表模式封面在上方&lt;a href=&quot;#firefly-主题自定义文章封面位置实现列表模式封面在上方&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;blockquote&gt;&lt;p&gt;Firefly 主题默认的列表模式只支持封面在左侧或右侧，但我想让封面显示在标题上方。于是开始了一番折腾，记录一下完整的修改过程。&lt;/p&gt;&lt;/blockquote&gt;&lt;section&gt;&lt;h2&gt;最终效果&lt;a href=&quot;#最终效果&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;修改前（封面在右侧）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;┌─────────────────────────────────┐&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;│ 标题                    [封面图] │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;│ 标签                            │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;└─────────────────────────────────┘&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;修改后（封面在上方）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;┌─────────────────────────────────┐&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;│ [封面图]                        │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;│ 标题                            │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;│ 标签                            │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;└─────────────────────────────────┘&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;需要修改的文件&lt;a href=&quot;#需要修改的文件&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;一共涉及 4 个文件：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;&lt;code&gt;src/types/siteConfig.ts&lt;/code&gt; — 类型定义&lt;/li&gt;
&lt;li&gt;&lt;code&gt;src/config/siteConfig.ts&lt;/code&gt; — 配置文件&lt;/li&gt;
&lt;li&gt;&lt;code&gt;src/components/layout/PostCard.astro&lt;/code&gt; — 文章卡片组件&lt;/li&gt;
&lt;li&gt;&lt;code&gt;src/components/layout/PostPage.astro&lt;/code&gt; — 文章列表页面&lt;/li&gt;
&lt;/ol&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;第一步：修改类型定义&lt;a href=&quot;#第一步修改类型定义&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;文件：&lt;/strong&gt; &lt;code&gt;src/types/siteConfig.ts&lt;/code&gt;&lt;/p&gt;&lt;p&gt;找到 &lt;code&gt;coverPosition&lt;/code&gt; 的类型定义：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;coverPosition&lt;/span&gt;&lt;span&gt;?:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;left&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;right&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;undefined&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;添加 &lt;code&gt;&quot;top&quot;&lt;/code&gt; 选项，修改为：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;coverPosition&lt;/span&gt;&lt;span&gt;?:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;left&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;right&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;top&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;undefined&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;第二步：修改配置文件&lt;a href=&quot;#第二步修改配置文件&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;文件：&lt;/strong&gt; &lt;code&gt;src/config/siteConfig.ts&lt;/code&gt;&lt;/p&gt;&lt;p&gt;找到 &lt;code&gt;postListLayout&lt;/code&gt; 配置中的 &lt;code&gt;coverPosition&lt;/code&gt;：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;postListLayout&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// 列表模式下封面图显示在哪一侧：&quot;right&quot; 右侧，&quot;left&quot; 左侧&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;coverPosition&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;right&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;修改为：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;postListLayout&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// 列表模式下封面图显示在哪一侧：&quot;right&quot; 右侧，&quot;left&quot; 左侧，&quot;top&quot; 上方&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;coverPosition&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;top&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;第三步：修改文章卡片组件&lt;a href=&quot;#第三步修改文章卡片组件&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;文件：&lt;/strong&gt; &lt;code&gt;src/components/layout/PostCard.astro&lt;/code&gt;&lt;/p&gt;&lt;section&gt;&lt;h3&gt;3.1 添加变量判断&lt;a href=&quot;#31-添加变量判断&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;找到这行代码（约第 53 行）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;coverWidth&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;30%&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;在它下面添加：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;isCoverTop&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;includes&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;cover-top&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;3.2 修改容器 class&lt;a href=&quot;#32-修改容器-class&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;找到容器 div 的 class 定义（约第 107-114 行）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;class:list=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;post-card-wrapper&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;hasCover&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;has-cover&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;no-cover&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pinned&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;pinned&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;gridCoverFullWidth&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;grid-cover-inset&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;card-base flex flex-col-reverse w-full rounded-(--radius-large) overflow-hidden relative&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;添加 &lt;code&gt;cover-top&lt;/code&gt; 类，修改为：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;class:list=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;post-card-wrapper&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;hasCover&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;has-cover&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;no-cover&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pinned&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;pinned&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;gridCoverFullWidth&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;grid-cover-inset&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;isCoverTop&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;cover-top&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;card-base flex flex-col-reverse w-full rounded-(--radius-large) overflow-hidden relative&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;3.3 添加 CSS 样式&lt;a href=&quot;#33-添加-css-样式&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;在文件末尾的 &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; 标签内添加以下 CSS：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/* 列表模式封面在上方 - 移动端 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;:global(&lt;/span&gt;&lt;span&gt;.list-mode.cover-top&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;.post-card-wrapper&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;flex-direction: &lt;/span&gt;&lt;span&gt;column-reverse&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!important&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;:global(&lt;/span&gt;&lt;span&gt;.list-mode.cover-top&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;.has-cover&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.post-card-image&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;position: &lt;/span&gt;&lt;span&gt;relative&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!important&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;top: &lt;/span&gt;&lt;span&gt;auto&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!important&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;bottom: &lt;/span&gt;&lt;span&gt;auto&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!important&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;right: &lt;/span&gt;&lt;span&gt;auto&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!important&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;left: &lt;/span&gt;&lt;span&gt;auto&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!important&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;width: &lt;/span&gt;&lt;span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!important&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;height: &lt;/span&gt;&lt;span&gt;&lt;span&gt;200&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!important&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;margin: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!important&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;border-radius: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--radius-large&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--radius-large&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!important&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;:global(&lt;/span&gt;&lt;span&gt;.list-mode.cover-top&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;.has-cover&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.post-card-content&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;width: &lt;/span&gt;&lt;span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!important&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/* 列表模式封面在上方 - 桌面端 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;@media&lt;/span&gt;&lt;span&gt; (min-width: &lt;/span&gt;&lt;span&gt;&lt;span&gt;768&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;:global(&lt;/span&gt;&lt;span&gt;.list-mode.cover-top&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;.post-card-wrapper&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;flex-direction: &lt;/span&gt;&lt;span&gt;column-reverse&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!important&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;:global(&lt;/span&gt;&lt;span&gt;.list-mode.cover-top&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;.has-cover&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.post-card-image&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;position: &lt;/span&gt;&lt;span&gt;relative&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!important&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;30&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;top: &lt;/span&gt;&lt;span&gt;auto&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!important&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;31&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;bottom: &lt;/span&gt;&lt;span&gt;auto&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!important&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;32&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;right: &lt;/span&gt;&lt;span&gt;auto&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!important&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;33&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;left: &lt;/span&gt;&lt;span&gt;auto&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!important&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;34&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;width: &lt;/span&gt;&lt;span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!important&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;35&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;height: &lt;/span&gt;&lt;span&gt;&lt;span&gt;300&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!important&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;36&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;margin: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!important&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;37&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;border-radius: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--radius-large&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--radius-large&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!important&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;38&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;39&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;40&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;:global(&lt;/span&gt;&lt;span&gt;.list-mode.cover-top&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;.has-cover&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.post-card-content&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;41&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;width: &lt;/span&gt;&lt;span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!important&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;42&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;padding: &lt;/span&gt;&lt;span&gt;&lt;span&gt;1.75&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;2.25&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!important&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;43&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;44&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;45&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;:global(&lt;/span&gt;&lt;span&gt;.list-mode.cover-top&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;.has-cover&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;.post-card-title&lt;/span&gt;&lt;span&gt;::before&lt;/span&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;46&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;display: &lt;/span&gt;&lt;span&gt;none&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!important&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;47&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;48&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;第四步：修改文章列表页面&lt;a href=&quot;#第四步修改文章列表页面&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;文件：&lt;/strong&gt; &lt;code&gt;src/components/layout/PostPage.astro&lt;/code&gt;&lt;/p&gt;&lt;p&gt;找到 &lt;code&gt;coverPositionClass&lt;/code&gt; 的定义（约第 28-29 行）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;coverPositionClass&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;siteConfig&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;postListLayout&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;coverPosition&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;left&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot; cover-left&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;添加对 &lt;code&gt;&quot;top&quot;&lt;/code&gt; 的支持，修改为：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;coverPositionClass&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;siteConfig&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;postListLayout&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;coverPosition&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;left&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot; cover-left&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;siteConfig&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;postListLayout&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;coverPosition&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;top&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot; cover-top&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;完整配置参考&lt;a href=&quot;#完整配置参考&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;修改完成后，&lt;code&gt;siteConfig.ts&lt;/code&gt; 中的 &lt;code&gt;postListLayout&lt;/code&gt; 配置示例：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;postListLayout&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// 默认布局模式：&quot;list&quot; 列表模式，&quot;grid&quot; 网格模式&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;defaultMode&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;list&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// 移动端默认布局模式&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;mobileDefaultMode&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;grid&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// 列表模式下封面图位置：&quot;right&quot; 右侧，&quot;left&quot; 左侧，&quot;top&quot; 上方&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;coverPosition&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;top&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// 文章简介显示行数&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;descriptionLines&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// 其他配置...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;hr /&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;踩坑记录&lt;a href=&quot;#踩坑记录&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;问题 1：TypeScript 类型错误&lt;a href=&quot;#问题-1typescript-类型错误&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;一开始直接在配置里写 &lt;code&gt;coverPosition: &quot;top&quot;&lt;/code&gt;，结果报错：&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;不能将类型”top”分配给类型”left” | “right” | undefined”&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;strong&gt;原因：&lt;/strong&gt; 类型定义文件中没有 &lt;code&gt;&quot;top&quot;&lt;/code&gt; 选项。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;解决：&lt;/strong&gt; 先修改 &lt;code&gt;types/siteConfig.ts&lt;/code&gt;，添加 &lt;code&gt;&quot;top&quot;&lt;/code&gt; 类型。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;问题 2：修改后没有效果&lt;a href=&quot;#问题-2修改后没有效果&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;改了配置和样式，但页面没有任何变化。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;原因：&lt;/strong&gt; &lt;code&gt;PostPage.astro&lt;/code&gt; 中的 &lt;code&gt;coverPositionClass&lt;/code&gt; 只处理了 &lt;code&gt;&quot;left&quot;&lt;/code&gt;，没有处理 &lt;code&gt;&quot;top&quot;&lt;/code&gt;，导致 &lt;code&gt;cover-top&lt;/code&gt; 这个 class 根本没被加到容器上。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;解决：&lt;/strong&gt; 修改 &lt;code&gt;PostPage.astro&lt;/code&gt;，添加对 &lt;code&gt;&quot;top&quot;&lt;/code&gt; 的判断逻辑。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;小结&lt;a href=&quot;#小结&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;整体修改涉及 4 个文件，核心逻辑就是四步：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;在类型定义中添加新选项&lt;/li&gt;
&lt;li&gt;在配置中启用新选项&lt;/li&gt;
&lt;li&gt;在组件中添加对应的 CSS 样式&lt;/li&gt;
&lt;li&gt;在页面中添加对应的 class 处理&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;Firefly 主题的组件化设计很清晰，修改起来逻辑明确，唯一需要注意的就是别漏了 &lt;code&gt;PostPage.astro&lt;/code&gt; 里的 class 映射——我一开始就栽在这了，样式写了半天不生效，最后发现是 class 根本没挂上去。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;</content:encoded></item><item><title>更新公告20260816</title><link>https://azuma.mstzuomu.space/posts/web-update-4/</link><guid isPermaLink="true">https://azuma.mstzuomu.space/posts/web-update-4/</guid><description>网站更新公告</description><pubDate>Sun, 16 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;hr /&gt;
&lt;section&gt;&lt;h2&gt;更新公告20260816&lt;a href=&quot;#更新公告20260816&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;1.为网站添加了MD语法下的PDF控件，现在可以直接在文章里阅读PDF&lt;/p&gt;&lt;p&gt;2.修复了外链PDF无法正常读取的问题&lt;/p&gt;&lt;p&gt;3.更换网站图床，感谢开源项目&lt;a href=&quot;https://cfbed.sanyue.de/&quot; target=&quot;_blank&quot;&gt;&lt;strong&gt;CloudFlare ImgBed&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;4.将文章封面显示位置新增top选项&lt;/p&gt;&lt;p&gt;PDF控件示例：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;我的PDF文档&lt;/div&gt;&lt;a href=&quot;https://tu.mstzuomu.space/file/PDF文件/1786844764371_A51.pdf&quot; target=&quot;_blank&quot;&gt;下载PDF&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;</content:encoded></item><item><title>基于rehype的博客PDF嵌入组件实现</title><link>https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-3/</link><guid isPermaLink="true">https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-3/</guid><description>基于现有组件体系的博客 PDF 预览扩展实践</description><pubDate>Sun, 16 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;&lt;p&gt;博客里已经有了 GitHub 卡片、B站卡片这类 Markdown 扩展组件，一直想着能不能把 PDF 也直接嵌进文章里，不用再丢个链接让读者跳出去看。今天折腾了一下，用 AI 辅助搞定了，由于制作B站卡片时忘记记录了，于是决定记录一下制作PDF组件全过程。&lt;/p&gt;&lt;/blockquote&gt;
&lt;section&gt;&lt;h2&gt;思路&lt;a href=&quot;#思路&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;项目里已经有一套基于 rehype 的自定义指令组件体系，比如 &lt;code&gt;::github{...}&lt;/code&gt; 和 &lt;code&gt;::bilibili{...}&lt;/code&gt;。我要做的就是照着这个模式，新增一个 &lt;code&gt;::pdf{...}&lt;/code&gt; 指令，让它在 Markdown 里渲染成一个带标题栏和下载按钮的 PDF 查看器。&lt;/p&gt;&lt;p&gt;整个过程分三步：&lt;strong&gt;写插件 → 注册插件 → 写样式&lt;/strong&gt;。样式直接写在框架统一存放 Markdown 扩展样式的 &lt;code&gt;markdown-extend.styl&lt;/code&gt; 里，它由 &lt;code&gt;Markdown.astro&lt;/code&gt; 自动加载，不需要额外引入。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;第一步：创建 PDF 查看器插件&lt;a href=&quot;#第一步创建-pdf-查看器插件&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;在 &lt;code&gt;src/plugins/&lt;/code&gt; 路径下新建 &lt;code&gt;rehype-component-pdf-viewer.mjs&lt;/code&gt;，核心逻辑是接收 &lt;code&gt;url&lt;/code&gt;、&lt;code&gt;title&lt;/code&gt;、&lt;code&gt;height&lt;/code&gt; 三个参数，用 &lt;code&gt;hastscript&lt;/code&gt; 拼出一个带 header 和 iframe 的 DOM 结构。&lt;/p&gt;&lt;p&gt;代码里做了两个校验：如果指令不是叶子类型（带了子内容），或者没传 &lt;code&gt;url&lt;/code&gt;，就返回一个隐藏的错误提示 div，避免页面崩掉。&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/// &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;reference&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;types&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;mdast&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;h&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;hastscript&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* Creates a PDF Viewer component.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* &lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{Object}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;properties&lt;/span&gt;&lt;span&gt; - The properties of the component.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* &lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{string}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;properties.url&lt;/span&gt;&lt;span&gt; - The URL of the PDF file.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* &lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{string}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[properties.title]&lt;/span&gt;&lt;span&gt; - Optional title for the PDF viewer.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* &lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{number}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;[properties.height]&lt;/span&gt;&lt;span&gt; - Optional height in pixels (default: 600).&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* &lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{import(&apos;mdast&apos;).RootContent[]}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;children&lt;/span&gt;&lt;span&gt; - The children elements.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* &lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;returns&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{import(&apos;mdast&apos;).Parent}&lt;/span&gt;&lt;span&gt; The created PDF Viewer component.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PdfViewerComponent&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;properties&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;children&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;Array&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;isArray&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;children&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;children&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;h&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;div&quot;&lt;/span&gt;&lt;span&gt;, { &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;hidden&quot;&lt;/span&gt;&lt;span&gt; }, [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&apos;Invalid directive. (&quot;pdf&quot; directive must be leaf type &quot;::pdf{url=&quot;https://example.com/file.pdf&quot;}&quot;)&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;&lt;span&gt;properties&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;h&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;div&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;hidden&quot;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&apos;Invalid URL. (&quot;url&quot; attribute is required)&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;properties&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;properties&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;PDF Viewer&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;height&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;properties&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;height&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;600&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;30&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;31&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// 浏览器原生 PDF 预览（iframe 直接加载），不引入 pdf.js 重依赖&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;32&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;h&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;div&quot;&lt;/span&gt;&lt;span&gt;, { &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;pdf-container&quot;&lt;/span&gt;&lt;span&gt; }, [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;33&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;h&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;div&quot;&lt;/span&gt;&lt;span&gt;, { &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;pdf-header&quot;&lt;/span&gt;&lt;span&gt; }, [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;34&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;h&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;div&quot;&lt;/span&gt;&lt;span&gt;, { &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;pdf-title&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt; }, &lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;35&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;h&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;36&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;a&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;37&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;38&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;pdf-download&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;39&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;40&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;target&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;_blank&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;41&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;download&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;42&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;43&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&quot;下载PDF&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;44&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;45&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;46&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;h&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;div&quot;&lt;/span&gt;&lt;span&gt;, { &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;pdf-viewer-wrapper&quot;&lt;/span&gt;&lt;span&gt; }, [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;47&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;h&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;iframe&quot;&lt;/span&gt;&lt;span&gt;, {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;48&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;pdf-viewer&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;49&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;src&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;50&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`height: &lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;height&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;px;`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;51&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;52&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;53&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;54&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;55&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;hr /&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;第二步：在 astro.config.mjs 中注册插件&lt;a href=&quot;#第二步在-astroconfigmjs-中注册插件&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;插件写好了还得注册才能用。打开 &lt;code&gt;astro.config.mjs&lt;/code&gt;，分两处修改。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;第一处：顶部 import（和其他组件插件放一起，约 41–44 行附近）&lt;/strong&gt;&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;BilibiliCardComponent&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;./src/plugins/rehype-component-bilibili-card.mjs&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;GithubCardComponent&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;./src/plugins/rehype-component-github-card.mjs&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;MusicPlayerComponent&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;./src/plugins/rehype-component-music-player.mjs&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;PdfViewerComponent&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;./src/plugins/rehype-component-pdf-viewer.mjs&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;第二处：components 中注册（约第 329 行附近）&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;找到 components 配置，在里面加一行 &lt;code&gt;pdf&lt;/code&gt; 字段：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;components&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;github&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;GithubCardComponent&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;bilibili&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;BilibiliCardComponent&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pdf&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;PdfViewerComponent&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;hr /&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;第三步：添加 CSS 样式&lt;a href=&quot;#第三步添加-css-样式&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;光有 DOM 还不够，得给它穿件衣服。打开框架统一的 Markdown 扩展样式文件 &lt;code&gt;src/styles/markdown-extend.styl&lt;/code&gt;（GitHub 卡片、图表控件的样式也都在这里），按 Stylus 语法追加一段。&lt;/p&gt;&lt;p&gt;有两点值得注意：颜色全部用主题变量而不是写死的色值——&lt;code&gt;var(--license-block-bg)&lt;/code&gt;、&lt;code&gt;var(--line-divider)&lt;/code&gt; 这类变量在暗色主题下会自动切换，不用再手写一套 &lt;code&gt;.dark&lt;/code&gt; 覆盖；另外要显式覆盖 &lt;code&gt;.custom-md iframe&lt;/code&gt; 的全局 margin 和圆角，否则 iframe 会被文章里通用的 iframe 规则影响。&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// ─── PDF 查看器（::pdf 指令，rehype-component-pdf-viewer） ──────────────────&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.pdf-container&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;margin: &lt;/span&gt;&lt;span&gt;&lt;span&gt;1.5&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;border-radius: &lt;/span&gt;&lt;span&gt;&lt;span&gt;0.75&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;overflow: &lt;/span&gt;&lt;span&gt;hidden&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;background: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--license-block-bg&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;border: &lt;/span&gt;&lt;span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;solid&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--line-divider&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.pdf-header&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;display: &lt;/span&gt;&lt;span&gt;flex&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;align-items: &lt;/span&gt;&lt;span&gt;center&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;justify-content: &lt;/span&gt;&lt;span&gt;space-between&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;padding: &lt;/span&gt;&lt;span&gt;&lt;span&gt;0.75&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;border-bottom: &lt;/span&gt;&lt;span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;solid&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--line-divider&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.pdf-title&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;font-weight: &lt;/span&gt;&lt;span&gt;600&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;font-size: &lt;/span&gt;&lt;span&gt;&lt;span&gt;0.95&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;color: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--tw-prose-headings&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.pdf-download&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;font-size: &lt;/span&gt;&lt;span&gt;&lt;span&gt;0.85&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;color: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--primary&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;text-decoration: &lt;/span&gt;&lt;span&gt;none&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;padding: &lt;/span&gt;&lt;span&gt;&lt;span&gt;0.25&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;0.75&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;border-radius: &lt;/span&gt;&lt;span&gt;&lt;span&gt;0.375&lt;/span&gt;&lt;span&gt;rem&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;background: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--btn-regular-bg&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;transition: background-color &lt;/span&gt;&lt;span&gt;&lt;span&gt;0.2&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ease&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;30&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;31&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;amp;&lt;/span&gt;&lt;span&gt;:hover&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;32&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;background: &lt;/span&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--btn-regular-bg-hover&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;33&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;34&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.pdf-viewer-wrapper&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;35&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;position: &lt;/span&gt;&lt;span&gt;relative&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;36&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;width: &lt;/span&gt;&lt;span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;37&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;38&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.pdf-viewer&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;39&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;display: &lt;/span&gt;&lt;span&gt;block&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;40&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;width: &lt;/span&gt;&lt;span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;41&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;border: &lt;/span&gt;&lt;span&gt;none&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;42&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;43&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 覆盖上方 .custom-md iframe 的全局 margin/border-radius，由容器自己控制留白&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;44&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;.custom-md&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.pdf-viewer&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;45&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;margin: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;46&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;border-radius: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;由于 PDF 阅读器并未进行本地配置而是选择了 iframe 直接嵌入，因此实际阅读控件和读者的浏览器有关。&lt;/p&gt;&lt;p&gt;样式写完就完事了——&lt;code&gt;markdown-extend.styl&lt;/code&gt; 已经由 &lt;code&gt;Markdown.astro&lt;/code&gt; 引入，不需要在 &lt;code&gt;Layout.astro&lt;/code&gt; 里再手动 import 一份。&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;使用方法&lt;a href=&quot;#使用方法&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;全部搞定之后，在任何 Markdown 文章里直接写一行指令就行：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;::pdf{url=&quot;https://example.com/your-file.pdf&quot; title=&quot;我的PDF文档&quot; height=&quot;600&quot;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;三个参数说明：&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;参数&lt;/th&gt;&lt;th&gt;说明&lt;/th&gt;&lt;th&gt;是否必填&lt;/th&gt;&lt;th&gt;默认值&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;url&lt;/code&gt;&lt;/td&gt;&lt;td&gt;PDF 文件的 URL 地址&lt;/td&gt;&lt;td&gt;必填&lt;/td&gt;&lt;td&gt;—&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;title&lt;/code&gt;&lt;/td&gt;&lt;td&gt;查看器顶部显示的标题&lt;/td&gt;&lt;td&gt;可选&lt;/td&gt;&lt;td&gt;&lt;code&gt;&quot;PDF Viewer&quot;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;height&lt;/code&gt;&lt;/td&gt;&lt;td&gt;查看器高度（像素）&lt;/td&gt;&lt;td&gt;可选&lt;/td&gt;&lt;td&gt;&lt;code&gt;600&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;hr /&gt;&lt;p&gt;PDF控件示例：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;我的PDF文档&lt;/div&gt;&lt;a href=&quot;https://tu.mstzuomu.space/file/PDF文件/1786844764371_A51.pdf&quot; target=&quot;_blank&quot;&gt;下载PDF&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;小结&lt;a href=&quot;#小结&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;整体下来没什么坑，核心就是复用项目已有的 rehype 组件模式，新增一个指令处理器而已。iframe 直接加载 PDF URL，浏览器原生支持预览，省得引入 pdf.js 那种重依赖。header 上放个下载按钮，读者想保存也方便。&lt;/p&gt;&lt;p&gt;以后写技术文章放论文、说明书之类的 PDF 就舒服多了，不用再让读者跳出博客去看。&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>更新公告20260815</title><link>https://azuma.mstzuomu.space/posts/web-update-3/</link><guid isPermaLink="true">https://azuma.mstzuomu.space/posts/web-update-3/</guid><description>网站更新公告</description><pubDate>Sat, 15 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;hr /&gt;
&lt;section&gt;&lt;h2&gt;更新公告20260815&lt;a href=&quot;#更新公告20260815&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;1.新增MD语法下的哔哩哔哩控件&lt;/p&gt;&lt;p&gt;2.修复了哔哩哔哩控件无法正常显示的问题&lt;/p&gt;&lt;p&gt;3.修复了移动端哔哩哔哩控件文字溢出的问题&lt;/p&gt;&lt;p&gt;4.为网站添加Live2D控件&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;</content:encoded></item><item><title>Twikoo评论系统部署踩坑记录：Vercel域名无法访问的解决之路</title><link>https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-2/</link><guid isPermaLink="true">https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-2/</guid><description>解决评论系统无法部署的经历</description><pubDate>Fri, 14 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h1&gt;Twikoo 评论系统部署踩坑记录：Vercel 域名无法访问的解决之路&lt;a href=&quot;#twikoo-评论系统部署踩坑记录vercel-域名无法访问的解决之路&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;blockquote&gt;&lt;p&gt;给自己留个备忘，也给遇到同样问题的朋友指条路。&lt;/p&gt;&lt;/blockquote&gt;&lt;section&gt;&lt;h2&gt;起因&lt;a href=&quot;#起因&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;博客的主要内容基本完成更换，于是决定完善评论系统。&lt;/p&gt;&lt;p&gt;在本网站项目里其实已经内置好配置文件了，只需修改个人的 envId 即可，最后定了 &lt;a href=&quot;https://twikoo.js.org/intro.html&quot; target=&quot;_blank&quot;&gt;&lt;strong&gt;Twikoo&lt;/strong&gt;&lt;/a&gt;，（最大的原因是免费🤣）并且支持 Vercel 一键部署。&lt;/p&gt;&lt;p&gt;部署过程遇到一些问题，首先浏览器插件建议关闭，或直接更换浏览器，否则在部署的时候相关控件被隐藏导致部署失败。（建议使用 Chrome ）另外官方文档相关内容略微落后实际网页版本，请以实际网页内容为主，并且在保存自己的 envId 信息的时候，建议下载 .env 文件保存，后续可以一键导入。部署操作完成后 Vercel 控制台显示 &lt;strong&gt;Ready&lt;/strong&gt;，以为已经部署成功了。&lt;/p&gt;&lt;p&gt;然后问题来了。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;问题一：Vercel 默认域名无法访问&lt;a href=&quot;#问题一vercel-默认域名无法访问&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;点了 Visit 按钮，浏览器转了半天，给我甩了一句：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;无法访问此网站&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;xxxxxxxxx-xxxx-xxxx（你部署后的域名） 的响应时间过长。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;ERR_CONNECTION_TIMED_OUT&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;连接超时。&lt;/p&gt;&lt;p&gt;第一反应是我哪里配错了。回去检查：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;部署状态：Ready ✅&lt;/li&gt;
&lt;li&gt;环境变量（MONGODB_URI / USERNAME / PASSWORD）：都配了 ✅&lt;/li&gt;
&lt;li&gt;域名配置：Valid Configuration ✅&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;啥都没问题，但就是打不开。&lt;/p&gt;&lt;p&gt;后来才反应过来——&lt;strong&gt;&lt;code&gt;*.vercel.app&lt;/code&gt; 这个默认子域名在大陆本身就属于超时状态&lt;/strong&gt;，而官方文档和相关教程并没有提到这点，最新的教程也只停留在23年。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;解决方案：绑定自定义域名&lt;a href=&quot;#解决方案绑定自定义域名&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;既然默认域名用不了，那就绑定本网站的子域名。&lt;/p&gt;&lt;section&gt;&lt;h3&gt;第一步：Verc 添加域名&lt;a href=&quot;#第一步verc-添加域名&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;在 Vercel 项目的 &lt;strong&gt;Settings → Domains&lt;/strong&gt; 里点 &lt;strong&gt;Add Existing&lt;/strong&gt;，输入 &lt;code&gt;twikoo.xxxx（你的域名）&lt;/code&gt;。&lt;/p&gt;&lt;p&gt;添加完之后，Vercel 会显示 &lt;strong&gt;Invalid Configuration&lt;/strong&gt;，并告诉你需要配什么 DNS 记录。它自动识别出我的 DNS 是 Cloudflare 管理的，给出了精确的配置：&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;字段&lt;/th&gt;&lt;th&gt;值&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;类型&lt;/td&gt;&lt;td&gt;CNAME&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;名称&lt;/td&gt;&lt;td&gt;twikoo&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;目标&lt;/td&gt;&lt;td&gt;&lt;code&gt;xxxxxx&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;代理状态&lt;/td&gt;&lt;td&gt;Disabled&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;注意类型一定要填写正确&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;第二步：Cloudflare 配置 DNS（重点！）&lt;a href=&quot;#第二步cloudflare-配置-dns重点&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;去 Cloudflare 的 DNS 记录页面，点 &lt;strong&gt;添加记录&lt;/strong&gt;：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;类型选 &lt;code&gt;CNAME&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;名称填 &lt;code&gt;twikoo&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;目标填 Vercel 给的那串 &lt;code&gt;xxxxx.com&lt;/code&gt; 地址&lt;/li&gt;
&lt;li&gt;TTL 保持自动&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;代理状态必须选「仅 DNS」（灰色云朵）&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;这里是最容易踩坑的地方。我博客的主域名和其他子域名都是开着 Cloudflare 代理的（橙色云朵），但 Twikoo 这条&lt;strong&gt;必须关代理&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;原因是：Vercel 需要通过 DNS 验证域名所有权并签发 SSL 证书，如果开着 Cloudflare 代理，请求会先走到 Cloudflare，Vercel 验证不过去，证书也签不下来。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;第三步：等待验证和证书签发&lt;a href=&quot;#第三步等待验证和证书签发&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;保存 DNS 记录后，回到 Vercel 点 &lt;strong&gt;Refresh&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;先是 DNS 验证通过（蓝色对勾），然后状态变成 &lt;strong&gt;Generating SSL Certificate&lt;/strong&gt;。等了大概一两分钟，再访问 &lt;code&gt;https://xxxxx.xxxxx.xxxxx&lt;/code&gt;，返回：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;&quot;code&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;&quot;message&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&quot;Twikoo 云函数运行正常，请参考 https://twikoo.js.org/frontend.html 完成前端的配置&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;&quot;version&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;&quot;1.7.19&quot;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;到这一步就算是成了。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;第四步：博客前端配置&lt;a href=&quot;#第四步博客前端配置&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;最后在博客的 Twikoo 配置里，把 &lt;code&gt;envId&lt;/code&gt; 改成自定义域名：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;https://xxxxxxx.xxxxx.xxxx&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;原来的 &lt;code&gt;*.vercel.app&lt;/code&gt; 地址就可以换掉了。&lt;/p&gt;&lt;p&gt;评论系统首次上线一定要点设置，保存密码，并在配置文件里隐藏设置控件。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;总结&lt;a href=&quot;#总结&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;如果你也在部署 Twikoo 或者类似的 Vercel 项目，并遇到相关问题，希望这篇能帮你少走点弯路。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;</content:encoded></item><item><title>更新公告20260814</title><link>https://azuma.mstzuomu.space/posts/web-update-2/</link><guid isPermaLink="true">https://azuma.mstzuomu.space/posts/web-update-2/</guid><description>网站更新公告</description><pubDate>Fri, 14 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;hr /&gt;
&lt;section&gt;&lt;h2&gt;更新公告20260814&lt;a href=&quot;#更新公告20260814&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;1.留言功能正式上线&lt;/p&gt;&lt;p&gt;2.更换公告图片&lt;/p&gt;&lt;p&gt;3.修复了网站文章丢失的问题&lt;/p&gt;&lt;p&gt;4.为网站添加Umami后台控件，网站数据后台可视化&lt;/p&gt;&lt;p&gt;5.添加不蒜子访客量统计&lt;/p&gt;&lt;p&gt;6.修复访客量不显示的问题&lt;/p&gt;&lt;p&gt;7.删除了Markdown语法中的哔哩哔哩控件，项目手动回档&lt;/p&gt;&lt;p&gt;8.修复了站点统计异常显示的BUG&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;</content:encoded></item><item><title>这是我的第一篇BLOG</title><link>https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-1/</link><guid isPermaLink="true">https://azuma.mstzuomu.space/posts/azuma.zuomu-blog-1/</guid><description>首次个人博客搭建记录</description><pubDate>Thu, 13 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h2&gt;来自我的第一条博客&lt;a href=&quot;#来自我的第一条博客&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;🎉 从零开始，翻阅众多教程，在 &lt;strong&gt;&lt;em&gt;MIMO&lt;/em&gt;&lt;/strong&gt; 的帮助和自己不懈的调试摸索下，我终于搭建完成了属于自己的个人博客！&lt;/p&gt;&lt;hr /&gt;&lt;ul&gt;
&lt;li&gt;📦 感谢开源项目：&lt;strong&gt;&lt;a href=&quot;https://github.com/CuteLeaf/Firefly&quot; target=&quot;_blank&quot;&gt;Firefly&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;📚 感谢 B站UP主：&lt;strong&gt;&lt;a href=&quot;https://space.bilibili.com/2017273493?spm_id_from=333.788.upinfo.head.click&quot; target=&quot;_blank&quot;&gt;番茄煮理人&lt;/a&gt;&lt;/strong&gt;，借助她的视频与博客教程，顺利完成整套部署。&lt;/li&gt;
&lt;li&gt;☁️ 感谢服务平台：&lt;a href=&quot;https://github.com/&quot; target=&quot;_blank&quot;&gt;Github&lt;/a&gt;、&lt;a href=&quot;https://dash.cloudflare.com/&quot; target=&quot;_blank&quot;&gt;Cloudflare&lt;/a&gt;、&lt;a href=&quot;https://www.aliyun.com/?utm_content=se_1023334680&quot; target=&quot;_blank&quot;&gt;阿里云&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;hr /&gt;&lt;p&gt;接下来我会继续摸索，持续更新与完善本站。&lt;/p&gt;&lt;p&gt;END&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>更新公告20260813</title><link>https://azuma.mstzuomu.space/posts/web-update-1/</link><guid isPermaLink="true">https://azuma.mstzuomu.space/posts/web-update-1/</guid><description>网站更新公告</description><pubDate>Thu, 13 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;hr /&gt;
&lt;section&gt;&lt;h2&gt;更新公告20260813&lt;a href=&quot;#更新公告20260813&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;1.移除了站点信息组件&lt;/p&gt;&lt;p&gt;2.新增&lt;strong&gt;访客信息&lt;/strong&gt;组件，并添加&lt;strong&gt;地址&lt;/strong&gt;显示以及&lt;strong&gt;天气&lt;/strong&gt;显示&lt;/p&gt;&lt;p&gt;3.更换天气API地址&lt;/p&gt;&lt;p&gt;4.新增移动端访客信息组件显示&lt;/p&gt;&lt;p&gt;5.修复了移动端访客信息无法成功加载的问题&lt;/p&gt;&lt;p&gt;6.新增&lt;strong&gt;留言&lt;/strong&gt;控件(留言功能暂未开放)&lt;/p&gt;&lt;hr /&gt;&lt;/section&gt;</content:encoded></item></channel></rss>