
HY
折腾进行时
Hello, I'm HY.
这里记录这个博客从上线、写作到页面迭代的每一个重要节点,每一次都按真实改动整理,展开即可查看完整变更说明。
版本规则:Major 代表结构级升级 · Minor 代表新功能 · Patch 代表修复、内容与维护
小助手上线后一直能用,我也一直没细看。直到有人在友链页留言问事,它回得像在回文章评论,翻代码才发现问题出在「抽正文」这一步:当时用正则取页面上第一个 <article>,而我这站每页的第一个 <article> 都是首页那张壁纸卡片(<article id="home-wallpaper-card">),里面只有头像、名字和一句 Hello, I'm HY. —— 所以不管评论发在哪一页,它拿到的「文章内容」永远只有那 23 个字,只能盯着留言硬编。这次改的核心不是把正文抽得更全,而是先回答「这条评论发在站内什么界面」:按 pathname 分成文章详情页 / 友情链接页 / 留言板 / 书签导航 / 动态 / 相册页 / 首页 等,把「这一页是干什么的」写进提示词;再按页面类型选内容容器 —— 文章页与关于页取 data-pagefind-body(注意文章页有两个:一个带 data-pagefind-meta="title" 的标题、一个正文),友链页 / 书签页 / 列表页取 #content-wrapper(友链卡片其实在 pagefind-body 外面,只认标记会漏掉整张列表)。动态页的评论路径 /dynamic/<memo id>/ 是前端合成的伪路径,线上请求必 404,改从 /dynamic/rss.xml 按 memo id 反查那条随手记的正文;留言板整页由 Svelte 渲染、静态 HTML 只有空壳,改用 /llms.txt 里的站点名、一句话简介和各页面官方说明补场景,这两份辅助数据各做 5 分钟进程内缓存。为了让抽取真的可靠,另外用 jsdom 建真 DOM 树写了版独立实现做逐字符对账,结果反过来抓出三个会污染正文的 bug,全出在「正则拿 [^>]*> 匹配标签」这件事上:一、代码块复制按钮是 <button data-code="http://<你的NAS-IP>:8080">,属性值里那个 > 让标签提前收尾,:8080"> 这种残渣漏进正文,30 多篇文章中招;二、属性值里冒出的 <div 被当成真开标签,容器深度计数失衡,正文多抓 2–3 KB(实测 53205 字符 vs 真实 51240);三、去掉隐藏节点时按「先算区间、再倒序删」,内外两层都要删时外层结束下标错位,多删一段还留下半个标签(/archive/ 的正文被啃出 onclass="archive-year-toggle…")。修法是把标签处理整个换掉:先 shieldTagGt() 把属性值里的 > 顶成占位符,再用引号感知的分词器 scanTags() 扫标签(扫到就跳到结束位置),容器切片、属性查找、整块删除全部走它。顺手还补了:认 <div hidden> 布尔属性(只查 class 会漏,但得按 class 分词精确匹配、别误伤 overflow-hidden);<button>/select/option 整块剔除(展开、收起、复制、筛选标签都是 UI 外壳);把 <figure> 里的 img alt 补成〔图:…〕(我文章的图说只写在 alt 上,全站 325 个 alt 里 272 个是有意义的,原先全丢);正文里的伪标签按浏览器语义处理,<4> 留成正文、</4> 当注释丢掉,写在正文里的占位符(设备名<端口>)才不会被吃。验证走了三条互相独立的路:离线自测 41 项全过(含 11 项本轮坑固化的回归用例)、与 jsdom 独立实现逐字对账本地 109 页 / 线上 17 页 / astro dev 渲染 11 页全部一致、线上真发评论让真模型真回,4 类页面 4/4。
AI 自动回复新增页面分类:按 pathname 判定文章详情页 / 友情链接页 / 留言板 / 书签导航页 / 动态 / 相册页 / 首页等,把「这一页是干什么的」写进提示词,回复不再把友链页的留言当成文章评论
动态页的 /dynamic/<memo id>/ 是合成伪路径、线上请求必 404,改为从 /dynamic/rss.xml 按 memo id 反查那条随手记的正文;留言板这类前端渲染页改用 /llms.txt 的站点简介与页面说明补场景;两份辅助数据各做 5 分钟进程内缓存
<figure> 里的 img alt 补成〔图:…〕—— 文章图说只写在 alt 上,剥标签时会连图说一起丢(全站文章页 325 个 alt 里 272 个是有意义的);同时剔除 <div hidden> 布尔属性、<button>/select/option 等 UI 外壳,并让 <4>、<端口> 这类伪标签按浏览器语义留在正文里
离线自测 30 项扩到 41 项(11 项本轮坑的回归用例);抽取逻辑与 jsdom 真 DOM 树的独立实现逐字对账,本地 109 页 / 线上 17 页 / dev 渲染 11 页全部一致;线上真发评论实测 4 类页面 4/4
正文抽取改成按页面类型选容器:文章页与关于页取 data-pagefind-body(标题与正文两块都要),友链页 / 书签页 / 列表页取 #content-wrapper(友链卡片列表不在 pagefind-body 里),再退化 <main> → <body>
剥标签改用引号感知分词器(shieldTagGt + scanTags)替换原来的正则:代码块复制按钮 data-code="http://<你的NAS-IP>:8080" 的属性值里带 >,会让 <button[^>]*> 提前收尾、残渣漏进正文;属性值里的 <div 还会被当成真开标签,让容器深度失衡、正文多抓 2–3 KB
AI_CONTEXT_CHARS 默认 4000 → 12000(文章正文中位数 3000 余字,基本等于整篇喂全),超长改为掐中间留头尾而不是直接砍尾;新增 AI_SITE_SUMMARY 开关控制是否读 /llms.txt
又是从参考站(blog.amamo.top,同主题的另一个 fork)看上的东西:右栏一张天气预报卡 —— 地点、大号温度、高低温,两列指标(风力、能见度、体感温度、湿度、气压、空气质量、云量、紫外线、降水量、降水概率、日出日落),再加一行污染物浓度、一个更新时间、一个收起按钮,一张卡把当天该知道的都说了。先把他那页扒开看数据从哪来:uapis.cn 的免费天气接口,一次请求返回全部字段,中文描述现成,不需要注册 key;最妙的是不传参数时按请求方 IP 定位 —— 也就是说每个访客看到的是自己所在地的天气,不是我这儿的天气,等于白捡一个「个性化」。落法照旧走页脚注入那一套:样式追加在 FooterConfig.html,卡片的 DOM 与全部逻辑放在新增的 public/assets/js/weather-card.js,由页脚的 PAGE_SCRIPTS 全站加载,主题源文件一个没动。桌面插在右栏 top 容器末尾(站点信息之后),小于 768px 换挂到移动端的底部组件栏,跨断点自动换挂载点;这两类容器都在 swup 替换范围之外,搬一次永久有效,切页不丢。两个自己踩的坑:一是图标,参考站那套是 Astro 构建期按需渲染出来的 symbol,我这边动态插入的节点取不到,照抄 <use> 指向 symbol 只会渲染成空白 —— 最后把 34 枚用到的图标 path 直接内联进了脚本;二是指标网格本来拿 auto-fit + minmax 做「宽度够就两列」,结果右栏最窄的 280px 算下来只放得下一列,12 项竖着排把卡片顶到 925px 高,改成固定两列后落到 613px。另外那接口是有配额的(响应头里能看到 1500 credits / 31 天),所以本地缓存给到 30 分钟,同一个访客半小时内怎么翻页都只请求一次。
右栏新增天气预报卡:标题左侧主题色竖条(几何照上游 widget-title 复刻)、地点、大号温度 + 天气描述、红色高温 / 蓝色低温、一条虚线分隔,下面两列指标网格 + 全宽的污染物浓度行,底部是更新时间与收起按钮
桌面挂右栏 top 容器末尾、移动端挂底部组件栏,跨 768px 自动换挂载点;两处容器都在 swup 替换范围外,切页不重建节点、不重新请求,折叠状态也保持
加载失败显示错误态与重试按钮;数据本地缓存 30 分钟(该接口有 1500 credits / 31 天的配额,同一访客半小时内翻多少页都只请求一次)
数据来自 uapis.cn 的免费天气接口(一次拿全 extended + forecast,无需 key)。默认按访客 IP 定位,想固定成站长常驻地就把脚本顶部的 FIXED_CITY 填成城市名或 adcode
默认收起(只露地点、大号温度与高低温,右栏不被一张卡占满),点「展开」放开全部指标;加载态与错误态不折叠、也不显示折叠按钮;状态记在会话存储里,同一次会话内切页不弹回默认
新增 public/assets/js/weather-card.js,样式与 PAGE_SCRIPTS 条目追加在 src/config/FooterConfig.html,主题源文件零改动;以后改这个 js 记得把 ?v= 往上加(/assets/* 有 30 天强缓存)
起因是想把动态页换个样子,参照的是另一个同主题的站(也是 Firefly 的 fork);但数据源不变,还是继续接 Memos。动手前先把两边扒了一遍,发现一件省事的事:上游动态卡片的 DOM 本来就齐 —— 头像、作者、时间、地点、置顶徽章、图片网格、评论按钮全都有,差的只是样式。所以没走「自己写一个新页面」那条路,而是纯样式覆盖加一点脚本注入,零改动主题源文件:新增一份只在 /dynamic/ 加载的样式表,再把页脚注入的那套机制扩出一个 PAGE_STYLES(原来只认 script),让这份样式只在动态页请求。封面区是上游压根没有的东西(封面大图、浮出的头像、昵称签名、写说说按钮、置顶条),外壳连同全部文案与链接写在页脚,由一个新增脚本搬进正文顶部 —— 搬运用 clone,原壳留在页脚,因为正文整个在 swup 的替换范围里,直接搬节点切一次页就没了。评论区顺带收拾了三件事:按钮上标评论数(不用点开就知道有没有人聊过)、只自动展开真有评论的条目(空面板摊开只是占地方)、把 Waline 那个「昵称/邮箱/网址 + 大输入框 + 表情栏」压成一行(点进去自动还原)。后面这一版之前其实做过一次「全部自动展开」,自己回看一遍又推翻了 —— 那时没配套处理空面板和那个大表单,一进页面每条动态下面都摊着它,观感确实差;这次把方案补齐再上。
卡片改成:左侧 4px 主题色竖条、内边距收到 1rem、头像 40px 圆 + 2px 主题色描边、昵称走主题色、时间与地点之间补一个 · 分隔、正文取消跟头像对齐的 3.75rem 缩进、右上角那枚纯装饰的引号撤掉
新增封面区:封面图 280px、右下角浮出的头像、压在封面上的昵称、右对齐的签名,配一个「写说说」按钮和一条置顶预览(左侧「置顶」标签 + 右侧缩略图,没有置顶动态时整条不显示)
拆掉页面头部那张卡片的外壳与左侧大图标,标题行与搜索 / 年份筛选直接收上去当封面卡片的下半截(两张卡叠着等于两层壳)
只自动展开 count > 0 的评论面板,没评论的保持收起;懒展开照旧(离视口 400px 才展开),一页十几条全展开会把首屏拖垮。自动展开只做一次,手动收起后不会再被弹开
动态页藏掉评论区那行「评论区 / 分享你的想法,与大家交流讨论」—— 这里一条动态配一个评论区,标题只是白占地方(全站共用的组件没动,文章页照旧)
踩到的坑:图片网格那几条必须写成 `dynamic-entry .page-gallery > .page-gallery-grid[data-count=…]`,上游同批规则的特异性是 0-2-0,只加一个元素名前缀(0-1-1)压不过,容器宽度会残留成 448px;防重复插入不能用 `host.querySelector(":scope > .ff-wx-cover")`,实测不生效,连触发三次事件会插出三份封面,改用锚点上的 dataset 标记;Waline v3 的输入框是 `<textarea class="wl-editor">`,不是 `.wl-textarea`,照后者写选择器会一声不响地不生效
图片网格改成固定 100px 方块:1 张 100px、2 张与 4 张两列、其余三列;单图保留上游的「完整显示不裁剪」(一张风景照被裁成小方块反而难看)
封面图从桌面壁纸池里随机挑一张,每次进页面换一张、页面内不轮播;池子写在页脚注入那段 HTML 的 data-covers 上,加减都改那里
评论按钮标上评论数(「评论 2」):进页面时批量问一次 Waline 的计数接口拿全部,0 条就不带数字 —— 有数字本身就是在说有人聊过
Waline 的编辑器默认压成一行(整块 77px、输入框 35px),点进去还原成完整表单(227px);输入框有内容时不收起来,写一半被收走太烦
新增 public/assets/css/dynamic-card.css(只在 /dynamic/ 按需加载)与 public/assets/js/dynamic-hero.js;页脚注入那套机制扩出 PAGE_STYLES;选择器一律以 dynamic-entry 打头,避免误伤同样在用 .page- 前缀的友链页、书签导航
把鼠标指针整套换掉 —— 亮色主题配高坂桐乃、深色主题配五更琉璃(黑猫),一共 56 枚素材。素材是两张 6144×4096 的总览图,每张 7 列 4 行。切图这步先翻车一次:这批图标是「深色描边 + 白色填充」,人物衣服和箭头内部都是纯白、跟页面背景同色,按处理贴纸那套「全局去白底」会把图形整个掏空,只剩一圈描边。改用孔洞填充(binary_fill_holes)—— 被描边围住的白色算图形内部,只有跟裁切框边缘连通的那片白才是透明。另外两张图的行距并不相同(黑猫那张表头底边会落进第 1 行裁切框、第 3 行标签又会顶到框底),裁切框按主题各测一套才行。CSS 那边把「用哪张图 + 热点坐标」全塞进自定义属性::root 给桐乃、html.dark 给黑猫,于是 28 条规则只写一遍、切主题只换变量,连热点也能跟着换(两套的指尖差 1~2px);每条三档声明(纯 url 兜底 → -webkit-image-set → image-set)覆盖高分屏,两种倍率共用同一组 CSS px 热点。代码块保留系统原生 I 型光标,这里踩到 expressive-code 自带的一条 `.expressive-code :not(:is(svg, svg *)) { cursor: revert }`,特异性 0,1,1 压过 pre,会把代码块的 cursor 回滚成「继承」、继承到 body 的箭头,加 !important 才压得住。最别扭的是验证 —— 光标是浏览器在合成层上画的东西、不属于页面内容,截图根本拍不到,只能逐个元素翻 computed style、再把里面那个图片加载一遍确认能解码。
全站鼠标指针换成《我的妹妹哪有这么可爱!》主题:亮色用高坂桐乃、深色用五更琉璃(黑猫),各 28 枚
样式追加在 src/config/FooterConfig.html,主题源文件零改动;素材落在 public/images/cursors/{kirino,kuroneko}/,每枚 32px(1x) + 64px(2x),共 112 个文件约 700 KB
pre / code 保留系统原生 I 型光标(代码块是选中复制命令行的地方)。expressive-code 自带 cursor: revert 且特异性更高,必须 !important 才能压住,pre 定住后内部同样 revert 的后代一路继承;复制按钮在 <figcaption> 内、不被误伤。实测:body / a / p / pre / abbr[title] / :disabled / [draggable] 与 28 个 [data-cursor] 全部命中对应图标,加 html.dark 后整套换成黑猫且热点跟着变,112 个 PNG 全部 200
新增相册「俺妹指针主题」,两张总览图原图字节一致零压缩归档
正常选择、链接选择、文本选择、垂直文本、帮助、精确选择、移动、抓取 / 抓取手、繁忙、加载中、不可用都有天然触发点(含 abbr[title]、:disabled、[draggable]);滚动、四向调整大小、候选、已完成、添加、删除、背景这些本站没场景的用 [data-cursor="…"] 点名,28 枚全部可点名
「用哪张图 + 热点坐标」全部放进 CSS 自定义属性,:root 放桐乃、html.dark 放黑猫,28 条规则只写一遍;实测 Chromium 允许 var() 承载整个 <image> 与热点数字,变量缺失时整条声明失效并回退到 auto
同步这次的实现记录(/posts/blog/cursortheme/):切图为什么不能用全局去白底、cursor 里能不能塞 CSS 变量、以及截图拍不到光标时该怎么验证,都写了
「回复默认展示 2 条」和「回复引用」两项全站上线后,动态页成了唯一的例外 —— 文章页、留言板、友链页的回复都规规矩矩显示 2 条,唯独动态里那条 13 条回复的长串一条不落地全摊着。根子在结构上:动态页每条动态的评论区是个同源 iframe(src 指向 /dynamic/comments/),而那个页面走的是 Layout —— 它没有页脚。全站的评论增强都是靠页脚注入 + PAGE_SCRIPTS 分发的,注入压根到不了 iframe 那个文档,样式和脚本一起落空,所以在动态里看起来就是「什么都没装」的原生 Waline。修法顺着同源这一点做:只在 /dynamic/ 多跑一个 dynamic-comment-enhance.js,由父页面按 id 把页脚里那两个 <style> 块 clone 进 iframe,再把 comment-reply-collapse.js / comment-reply-quote.js 以 script 标签代投进去 —— 单一样式来源,以后改 FooterConfig.html 两边一起变;两个脚本本来就自包含,塞进任何文档都能独立跑。iframe 是点「评论」那一刻才由上游创建、脚本却早就加载好了,所以靠 MutationObserver 盯新增节点、再轮询 contentDocument.readyState 等它真正导航到评论页(刚插进 DOM 时 document 还是 about:blank,而它的 readyState 也是 complete,不辨一下就会把样式注进空气里)。全程零改动主题源文件。
动态页内嵌评论区的回复同样默认展示 2 条,多出来的收进「展开更多(还剩 M 条)」,展开/收起行为与全站完全一致
新增 public/assets/js/dynamic-comment-enhance.js,只在 /dynamic/ 按需加载;样式按 id 从页脚 clone 进 iframe,两个脚本以标签代投,单一来源不另存一份
iframe 刚插入时 document 还是 about:blank(readyState 同样是 complete),所以按 location.pathname 必须是 /dynamic/comments/ 才注入;iframe 在轮询期间被 swup 换页拿掉时立即停止追踪。实测:13 条回复的长串默认 2 条 + 11 条收着,连点「展开更多」按 7 / 12 / 13 递增,摊完变「收起回复」,点一下回到 2 条;回复引用的引用块在动态里的真实评论上也已生效
回复引用在动态里也生效 —— 被回复那条的内容摘成引用块,`@昵称:` 那行同样不再重复显示
FooterConfig.html 里两个评论样式块补上 id(ff-comment-style-collapse / ff-comment-style-quote)供 iframe 克隆用,PAGE_SCRIPTS 增加一条 /dynamic/ 专属条目;主题源文件零改动
当初在页脚补这枚徽标,是为了照顾移动端和不看顶栏的访客,但顶栏那个入口已经在,两处就成了重复。现在把页脚这份撤掉 —— 开往只要求外链放在「打开网站就能看到的地方」,顶栏满足,不影响参与该项目。徽标撤掉的同时把自托管的两张黑白图一并删了(不再被任何地方引用),顺带省一点页脚体积。
页脚萌 ICP 备案号下方的「开往 Travellings」徽标移除,连同它那段 <style> 一起删
顶栏的「开往」入口保留,不影响参与友链接力
删掉自托管的 travellings-b.png / travellings-w.png —— 不再被引用
V1.43 的引用块一上来是「@昵称:」一行 + 带底色的引用块 + 正文,三样东西排在一起,重复又臃肿 —— 「@谁」这件事 Waline 原生那行说过一遍、引用块里又说了一遍。现在把那行原生 @ 直接不显示,引用块前面加一枚 ↩ 符号顶上它,去掉底色只留左边那根主题色竖线(悬停时才浮一层浅底,提示能点),浅底色挪到回复正文上 —— 引用只是一句提示,正文才是这条回复的主体。底色用 :has 精确框住「带引用的回复」,顶层评论和普通回复的正文一律不受影响。
回复里 Waline 原生的「@某某:」那行不再显示 —— 引用块里已经写明回的是谁,两行并排纯属重复
引用块前面加一枚 ↩ 符号。用真实 span 而不是 ::before —— 实测在 button 上(且 display:-webkit-box 做两行截断)伪元素压根不生成,computed content 一直是 normal
引用块去掉底色,只留左边那根主题色竖线;浅底色挪到回复正文上(:has 精确匹配,顶层评论与普通回复的正文不受影响),引用块改成悬停才浮底
Waline 的二级回复只渲染一行「@昵称:」,接下来就是自己的话。谁在回谁,一眼看得出;回的是他那句话里的哪一句,全靠猜 —— 一条评论下面十几条你来我往,读起来像拼拼图。Waline 其实在 DOM 里留了线索:回复正文的第一行是 `p.wl-reply-to`,里面那个 a 标签的 href 就是**被回复评论的 objectId**,而每条评论节点的 id 正好也是它。顺着锚点把目标评论找回来,摘出「谁 + 说了什么」做成引用块,插在「@昵称:」下面;点它会滚到原评论并闪一下,目标若正被「回复折叠」收着,还会先自动展开到那一条。找不到目标的(分页翻走了、评论被删)就静默跳过,保持 Waline 原样。做法照旧:只加文件,没动主题源文件。
回复正文上方多一个引用块:被回复者昵称 + 正文摘要(两行截断)。原本的「@昵称:」那行保持不动,引用块排在它下面
目标不在当前页(分页翻走、评论已删)时静默跳过,不插引用块,Waline 原样显示;摘内容时只取纯文本,不把用户内容再插回页面
点引用块滚到原评论并高亮闪一下;目标正被「回复折叠」收着时,先自动展开到它那一条再滚(两个脚本之间用一个全局钩子对接)
新增 public/assets/js/comment-reply-quote.js,样式与 PAGE_SCRIPTS 条目加在 src/config/FooterConfig.html
评论区里长回复串能把一整屏吃光 —— 友链页有一条评论下面挂着 13 条回复,想看下一条评论得一路滚过去。但一刀切全收起来又太狠:访客第一眼什么都看不到,每条还得点一下。所以改成默认展示 2 条,多出来的才收着 —— 既不空,长度也可控。还有剩就给个「展开更多(还剩 M 条)」,每次再放 5 条;全部摊开后按钮变「收起回复」,箭头翻上去,点一下回到默认的 2 条。回复本来不超过 2 条的评论完全不碰,连按钮都不插。实现上照旧只加文件、不动上游:样式写在 FooterConfig.html 的 style 块里(页脚在 swup 容器之外,切页不会被换掉),逻辑放在新增的 comment-reply-collapse.js,用页脚现成的 PAGE_SCRIPTS 全站加载。Waline 的 DOM 是 Vue 渲染的,回复并没有嵌成嵌套条目,而是统一挂在每张卡片的 .wl-quote 里(一条顶层评论 = .wl-card-item > .wl-card > .wl-quote > .wl-card-item × N),没有回复时 .wl-quote 仍然存在、只是空的,所以判定必须按「有直接子 .wl-card-item」;而收起也只认直接子,Waline 点「回复」时插进来的编辑框不是这个类,不会被误伤。观察器挂在 document.body 而不是 #waline —— 后者会随 swup 换页被整个替换,挂它身上的观察器会失效;挂 body 则换页后 Waline 重新渲染出的新节点也能接住。
每条评论下的回复默认展示 2 条,多出来的先收着;点「展开更多(还剩 M 条)」每次再放 5 条,全部摊开后变「收起回复」,点一下回到默认的 2 条,chevron 箭头同步翻转
顶层评论本身不动,照常全部显示
MutationObserver 挂 document.body,接住 swup 无刷新切页与 Waline 自身的重绘。实测切换排序(整个列表重渲染)后按钮仍是 4 个、每条评论最多 1 个、无重复插入,并回到默认折叠态
回复本来就不超过 2 条的评论完全不碰 —— 不折叠、不插按钮,跟改动前一样原样平铺
新增 public/assets/js/comment-reply-collapse.js,样式与 PAGE_SCRIPTS 条目加在 src/config/FooterConfig.html,没有改任何主题源文件
站内搜索一直有个说不清的「慢」:敲完字按回车,要等好几秒才出结果。查下来是两件事叠在一起。一是资源加载链太深 —— pagefind.js、worker、wasm、entry、meta、索引分片是严格串行依赖的,而 pagefind.js 只在用户碰到搜索框时才开始加载,于是这几跳的等待全部落在输入之后;二是取数请求数量和结果条数同阶,主题的取数写法是对「全部」结果调用 data(),而每个结果的 data() 就是一次 fragment 请求,搜一个常见词命中 45 条就是 45 个 HTTP 请求。现在新增一个全站注入的预热脚本:首屏空闲或鼠标碰到搜索框时就把 worker 起来、wasm 编译好、索引读进内存;再用一层代理只让前 6 条真正拉正文(面板本来就只显示 5 条),结果总数保持不变,所以「查看更多 N 条」的计数仍然准确。全程通过页脚注入实现,没有改动任何主题源文件。
预热 Pagefind 加载链:新增 public/assets/js/search-warmup.js,在 requestIdleCallback(或鼠标悬停/焦点/触摸搜索框)时调用 __loadPagefind() 并用几个中文高频字 preload()。实测一次预热会提前拉好 js + worker + wasm + entry + meta + 3 个索引分片共 8 个文件
回车补上行为:主题的搜索框没有绑定 Enter,敲回车什么都不发生。现在回车进入第一条结果,并且 isComposing 为真时放行,不干扰中文输入法用回车确认候选项
收敛取数请求:运行时用 Proxy 包一层 pagefind.search,只让前 6 条结果真正拉取正文摘要,其余条目返回空摘要。结果数组长度保持真实,「查看更多 N 条」计数不受影响;/search/ 页面要展示完整列表,不做限制。实测顶栏搜索的 fragment 请求 45 → 6(本地索引对照)
移动网络与省流模式(navigator.connection 的 saveData 或 2G)下跳过预热,不抢带宽
当前页面没有目录
当前页面没有目录
当前页面没有目录