Skip to content

🐛 更新页与安装页补齐骨架屏与异步中间态,失败不再被渲染成成功 - #1721

Merged
CodFrm merged 3 commits into
fix/batchupdate-open-detailfrom
fix/batchupdate-loading-states
Sep 7, 2026
Merged

CodFrm merged 3 commits into
fix/batchupdate-open-detailfrom
fix/batchupdate-loading-states

Conversation

@CodFrm

@CodFrm CodFrm commented Sep 3, 2026

Copy link
Copy Markdown
Member

Checklist / 检查清单

  • Fixes mentioned issues / 修复已提及的问题
  • Code reviewed by human / 代码通过人工检查
  • Changes tested / 已完成测试

背景

更新页与安装页这条链路上有多处「异步动作没有中间态」和「失败被渲染成成功」的问题:

  • 取数失败被说成检查成功:loadRecord 没有 catch,失败时记录仍是空数组,页面走到空态渲染「所有脚本均为最新(已检查 0 个脚本)」,唯一出口「重新检查」还会同样失败。
  • 点「检查更新」后的空窗期零反馈:checking 只跟随 Service Worker 广播,往返回来之前按钮不禁用、可连点;服务端回的「正忙」与「结果够新已跳过」被直接丢弃,后者还会让 userCheckPendingRef 残留,在下一次后台检查完成时冒出一条用户没点过的 toast。
  • 忽略是 fire-and-forget:不写行状态也不看返回值(Service Worker 侧 IGNORE 分支根本没有返回值),点完行原地不动,用户会以为没点上而重复点。
  • 批量可并发:批量跑到一半仍可勾选并再次发起,两个循环同时推同一条进度,进度条来回跳、收尾出两条 toast;被「结果失效」中断时还会把已完成的条数一并抹掉。
  • 骨架与真实结构不对齐:桌面骨架缺工具条占位(数据到达时整表下移约 48px),移动骨架只画滚动区内的卡片,而真实态还会在滚动区外插入顶部选择栏与底部操作栏,上下同时挤压。
  • 安装页首屏说错话:状态屏把上下文 chip 写死成「脚本安装」,从更新页点进来必然先闪一次错的上下文;loading_desc 写「正在从来源下载」,但 uuid 入口的代码 Service Worker 早已写进 OPFS/TempStorage,根本不下载。这条入口最常见的失败是暂存条目被 30 分钟定时清理回收,而给出的出口是「重试」——重试多少次都是同一结果。
  • 安装页两个动作不置忙态:toggleWatch / rejectExternalAccess 全程不置忙态,InstallActions 的 busy 判据因此始终为假,连点会发两次安装 / 两次决定。

本次改动

三个提交按面拆分:

Service Worker(4b0afe6c)

  • openUpdatePageByUUID / openUpdatePage 由 boolean 改为 "opened" | "silent" | "failed"。命中静默更新时不开安装页却同样返回 true,页面无从区分,用户点完脚本名只看到转一圈、什么都没发生。
  • IGNORE 分支逐条回报结果。
  • checkScriptUpdate 的结果收敛成 TCheckScriptUpdateResult,并用 reason: "busy" 区分「已有检查在跑」与真正的失败。

安装页(a87c144b)

  • 状态屏按来路分档:确知才渲染 chip,不猜;uuid 入口换掉「正在下载」的文案;补一条与就绪态操作栏等高的底部占位。
  • 暂存代码过期落专属终态,出口换成「重新检查更新」(请服务端重新备料)。
  • Monaco 就绪前渲染代码骨架,替代 340px 空白。
  • toggleWatch / rejectExternalAccess 补忙态,install 加 submittingRef 重入守卫(UI 的 disabled 只作第一道防线——下拉菜单项在 phase 翻转前已展开时仍能被选中)。

批量更新页(dc399245)

  • 取数失败落错误终态(等宽 detail + 重试 / 脚本列表)。
  • 主动检查由本地 pending 立刻接管忙态,并把服务端三条回执分别说出来;跳过时就地清掉待反馈标记。
  • 忽略复用与更新相同的行级阶段(working → success → 退场)。
  • 批量进行中互斥;被中断时保留已完成条数(BatchProgress.interrupted),「结果已失效」提示条加 role="alert" 与内联「重新检查」。
  • 骨架补齐工具条 / 顶部选择栏 / 底部操作栏占位并加 role="status" + aria-busy;空态下重新检查保留空态 + 顶部进度条,不再整页闪回骨架。
  • 脚本名的打开中状态改用 aria-disabled + onClick 早退,spinner 位改为常驻等宽空槽。

实现考虑

为什么忽略不走「结果已失效」。 初稿设计里,缓存随 Service Worker 回收后点忽略应提示「结果已失效」。回源核实后否掉了:scriptDAO.update 写的是脚本自身的 ignoreVersion(src/app/repo/repo.ts:314),与 scriptUpdateCheck.cacheFull 无关——忽略一直是生效的,缺的只是回执和刷新广播。因此改成逐条回执:成功即由页面乐观收起该行,失败才停在失败态并保留重试;只有 UPDATE 才需要 record_expired,因为它真的依赖缓存里的 newCode。

为什么用 aria-disabled 而不是 disabled。 浏览器不向 disabled 表单控件派发指针事件,Radix Tooltip 靠 onPointerMove/onPointerLeave 开关,转圈期间「完整脚本名」tooltip 会失效——而这正是名字被截断、用户最想看全名的时刻;键盘用户按 Enter 后焦点还会掉到 <body>。防连点本来就由同步 ref 守卫承担,不依赖 disabled。

为什么错误捕获放在 loadRecord 外面。 在 async 函数里 catch 到的 setState 无法被证明发生在 await 之后(被调用方同步抛出时它就是同步 setState),react-hooks/set-state-in-effect 会因此报错。改成在调用侧 .catch() 收口,promise 回调必然是异步的。

骨架的判据。 loading || (checking && empty && checktime === 0):首屏必然要骨架;此外只有「从未检查过」才用骨架,已经给出过空态之后再点检查,保留空态 + 顶部进度条即可,不要整页闪回骨架再闪回来。

已知限制

  • 批量汇总条仍会在 5 秒后自动收起,连带「查看已更新脚本」这个唯一出口一起消失。这与 fix/batchupdate-remove-autoclose 的改动主题重叠,留给那条分支处理。
  • 安装页加载屏仍没有超时兜底:Service Worker 唤醒失败且消息通道不 reject 时会永久转圈。需要在消息层加软超时,面比本 PR 大。
  • 未处理的零碎:安装页 keepAlive 定时器从不清理、远程图标无 onError 回退。

建议审查重点

  • runIgnores 与 runUpdates 共用 rowStatesRef / scheduleRowExit,两者交叉时行状态是否仍然自洽。
  • checking: checking || pendingCheck 的合成忙态:pendingCheck 在服务端响应回来时解除,此时「检查完成」广播可能尚未到达。
  • Service Worker 侧 openUpdatePageByUUID 的三态返回是否覆盖了所有调用方(requestCheckUpdate 忽略返回值)。

关联

叠在 #1719 之上(base 选 fix/batchupdate-open-detail 而非 main,否则会把 #1719 的提交一并算进本 PR 的 diff)。#1719 合入后本 PR 的 base 需改回 main。

验证

均在独占状态下执行(该套件对默认 850ms testTimeout 敏感,与其他任务并发跑会大量误超时):

  • npx vitest run → Test Files 364 passed (364) / Tests 4556 passed (4556),退出码 0。
    基线 c73a876f 同样方式跑 → 364 passed / 4516 passed,即本 PR 净增 40 个用例、零回归。
  • npx eslint src/pages/batchupdate src/pages/install src/app/service/service_worker tests/mocks/CodeEditor.tsx → 0 error。
  • npx tsc --noEmit -p tsconfig.json → 0 error。
  • node scripts/check-i18n.mjs → 通过(新增 19 个 key 均已补齐 10 个语言包)。

未做浏览器实机验证;行为改动均由上述单元测试覆盖(新增用例含:取数失败落错误终态、busy/跳过/通道异常三条回执、忽略的行级阶段与逐条回执、批量互斥与中断保留计数、骨架占位与 aria-busy、aria-disabled 不再是 disabled、静默更新回报 silent、安装页加载分档与过期终态、代码骨架、提交忙态与防重入)。

页面此前无从判断服务端到底做了什么:openUpdatePageByUUID 在命中静默更新时
不开安装页却同样返回 true,用户点完脚本名只看到转一圈、什么都没发生;
IGNORE 分支根本没有返回值,页面只能 fire-and-forget。

- openUpdatePageByUUID / openUpdatePage 返回 "opened" | "silent" | "failed"
- IGNORE 逐条回报结果。忽略写的是脚本自身的 ignoreVersion,与检查缓存无关,
  因此缓存随 Service Worker 回收后忽略照样生效,这里如实回报而不是谎报失效
- checkScriptUpdate 的结果收敛成 TCheckScriptUpdateResult 并用 reason 区分
  「已有检查在跑」与真正的失败,页面才能分别提示
从批量更新页点脚本名进来的必然是「更新」,加载屏却把上下文 chip 写死成
「脚本安装」,几百毫秒后再闪成「脚本更新」;描述写着「正在从来源下载」,
但这条入口的代码 Service Worker 早已备好,根本不下载。

- 状态屏按来路分档,未确知场景不渲染 chip(不猜),并补一条与就绪态操作栏
  等高的底部占位,避免就绪瞬间内容区高度再跳一次
- 暂存代码被定时清理回收时落到专属终态,出口换成「重新检查更新」——
  原来的「重试」在这个最常见的失败原因下重试多少次都是同一结果
- Monaco 实例就绪前渲染代码骨架,替代此前 340px 的纯空白
- toggleWatch / rejectExternalAccess 补忙态,install 加重入守卫:
  这两个动作全程不置忙态,连点会发出两次安装/两次决定
取数失败时记录仍是空的,页面直接走到空态,把一次加载失败渲染成
「所有脚本均为最新(已检查 0 个脚本)」这条与事实相反的成功终态;
点「检查更新」到服务端广播回来之间页面完全静止,期间可以连点。

- 取数失败落错误终态:等宽 detail 框 + 重试 / 脚本列表出口
- 主动检查由本地 pending 立刻接管忙态,并把服务端的「正忙」「结果够新已跳过」
  「通道异常」三条回执分别说出来;跳过时就地清掉待反馈标记,
  否则会在下一次后台检查完成时冒出一条用户没点过的 toast
- 忽略复用与更新相同的行级阶段(working → success → 退场),不再 fire-and-forget
- 批量进行中互斥(行内勾选、两个批量按钮、全部恢复),避免两条进度互相覆盖;
  被「结果失效」中断时保留已完成条数,不把汇总抹掉
- 骨架补齐工具条(桌面)与顶部选择栏/底部操作栏(移动)占位,消除数据到达时的
  布局跳动,并加 role="status" / aria-busy;空态下重新检查不再整页闪回骨架
- 脚本名改用 aria-disabled + onClick 早退:disabled 会让浏览器不派发指针事件,
  正好在名字被截断、最需要看全名时把 tooltip 一起关掉,键盘触发后焦点还会掉到 body
@cyfung1031

cyfung1031 commented Sep 4, 2026 •

Copy link
Copy Markdown
Collaborator

沒空去理會這些UI問題
只是提一下

最初的batch update設計是

它不是一個普通的 要求batch update -> 檢查所有腳本有沒有update -> 彈出結果

而是先按時機在背景帶delay慢慢檢查一下所有腳本有沒有update -> 有update就保存一下 ->
等到用戶作出有意圖使用瀏覽器的行為 -> 彈出來跟用戶說:你正瀏覽的網站使用的腳本有更新喔 你要不要更新?不要也沒所謂我先關掉,之後再問你吧

因此如果有近期的檢查,batch update不會重新檢查所有腳本,而是最近的結果

這樣的設計是平衡了更新頻率,同時檢查的網絡請求密度,用戶實際體驗

@CodFrm你用agent改設計時請先考慮原本的設計理念
不然就會像violentmonkey的更新設計被討論好幾年都還在討論中

我看到很多關於 batchupdate的agent pr
有點不安

老實說能動就別搞了
不想自動倒數就改-1秒。這些都是在以前想好的
看到agent提了好幾個pr
不知道是因為ui大改而出現的bug fix
還是agent不了解設計而亂改

@Mishasama

Copy link
Copy Markdown

最初的batch update設計

原来如此,但我仍然坚持任何更新都要先人工审核一遍,防止出现任何预期外的意外状况。

但用户要不要更新,也不应该由程序来定义用户什么时候完成判断,这是对用户极大的不尊重。

  • 如果用户觉得打扰、不需要,动手给它关掉即可。(程序也可以从关闭页面的这个动作中学习到用户的习惯,从而调整显示时机等策略)
  • 但如果用户想更新,且不是很随便的人,需要逐一检查的情况,那这个倒计时就是对用户的一种侵犯,完全没有必要。

@cyfung1031 我这里没有使用繁体及其文法习惯来写(主要我也不知道你是哪里的……),希望对你没有影响。

@cyfung1031

Copy link
Copy Markdown
Collaborator

最初的batch update設計

原来如此,但我仍然坚持任何更新都要先人工审核一遍,防止出现任何预期外的意外状况。

但用户要不要更新,也不应该由程序来定义用户什么时候完成判断,这是对用户极大的不尊重。

  • 如果用户觉得打扰、不需要,动手给它关掉即可。(程序也可以从关闭页面的这个动作中学习到用户的习惯,从而调整显示时机等策略)
  • 但如果用户想更新,且不是很随便的人,需要逐一检查的情况,那这个倒计时就是对用户的一种侵犯,完全没有必要。

@cyfung1031 我这里没有使用繁体及其文法习惯来写(主要我也不知道你是哪里的……),希望对你没有影响。

主要是手机打字没做简体字转换。我看得懂但打不到
batchupdate 的自动关闭不会自动更新。所以不会有 「出现任何预期外的意外状况」
调整显示时机等策略 可以加设定调吧。
还是那句最初只是一个小功能。不想搞太多。
要改的话也可以但要详细测试(包括实机测试)
让 CodFrm 判断和处理吧

@cyfung1031

cyfung1031 commented Sep 5, 2026 •

Copy link
Copy Markdown
Collaborator

主要是我担心
自动检查 -> 保存检查结果 这部份的代码改动
(更新频率 & 同时检查的网络请求密度)

Mishasama 所指出的 检查结果 如何显示 等UI/UX问题都可以改动
(用户实际体验)

但 CodFrm的ScriptCat设计是不想搞太多用户设定
目前是预设方式 (我指 检查结果 如何显示 )
我认为日后可以保留这个预设方式
但用户也可以改变来决定他们需要的方式

@CodFrm

CodFrm commented Sep 5, 2026

Copy link
Copy Markdown
Member Author

@cyfung1031 这个pr只是调整一些用户体验的UI/UX,没有动任何的业务逻辑

autoclose 在另外那个pr中,可以在那里讨论

@CodFrm
CodFrm merged commit fdb7648 into fix/batchupdate-open-detail Sep 7, 2026
6 of 9 checks passed
@CodFrm
CodFrm deleted the fix/batchupdate-loading-states branch September 7, 2026 05:45
CodFrm added a commit that referenced this pull request Sep 7, 2026
* 🐛 批量更新页打开更新详情复用检查缓存并挡住重复点击

点击脚本名查看更新时,openUpdatePageByUUID 会重新 fetch 一次脚本代码,
而这份新版代码在检查更新阶段已经存进 scriptUpdateCheck 的记录缓存里
(行内「更新」按钮装的就是它)。用户因此要为每次点击白等一次网络往返,
期间页面又没有任何反馈,连点几下就会开出多个安装页。

- SW: 拆出 prepareUpdateOrInstallPage,openUpdatePage 命中缓存代码时
  跳过 fetchScriptBody;openUpdatePageByUUID 改为回报 boolean
- 页面: 打开期间行内转圈并同步挡住重复点击,失败弹 toast,
  点击脚本名同样取消自动关闭倒计时

* 🐛 更新页与安装页补齐骨架屏与异步中间态,失败不再被渲染成成功 (#1721)

* 🐛 打开更新详情区分静默更新,忽略动作补齐逐条回执

页面此前无从判断服务端到底做了什么:openUpdatePageByUUID 在命中静默更新时
不开安装页却同样返回 true,用户点完脚本名只看到转一圈、什么都没发生;
IGNORE 分支根本没有返回值,页面只能 fire-and-forget。

- openUpdatePageByUUID / openUpdatePage 返回 "opened" | "silent" | "failed"
- IGNORE 逐条回报结果。忽略写的是脚本自身的 ignoreVersion,与检查缓存无关,
  因此缓存随 Service Worker 回收后忽略照样生效,这里如实回报而不是谎报失效
- checkScriptUpdate 的结果收敛成 TCheckScriptUpdateResult 并用 reason 区分
  「已有检查在跑」与真正的失败,页面才能分别提示

* 🐛 安装页补齐加载分档、代码骨架与提交忙态

从批量更新页点脚本名进来的必然是「更新」,加载屏却把上下文 chip 写死成
「脚本安装」,几百毫秒后再闪成「脚本更新」;描述写着「正在从来源下载」,
但这条入口的代码 Service Worker 早已备好,根本不下载。

- 状态屏按来路分档,未确知场景不渲染 chip(不猜),并补一条与就绪态操作栏
  等高的底部占位,避免就绪瞬间内容区高度再跳一次
- 暂存代码被定时清理回收时落到专属终态,出口换成「重新检查更新」——
  原来的「重试」在这个最常见的失败原因下重试多少次都是同一结果
- Monaco 实例就绪前渲染代码骨架,替代此前 340px 的纯空白
- toggleWatch / rejectExternalAccess 补忙态,install 加重入守卫:
  这两个动作全程不置忙态,连点会发出两次安装/两次决定

* 🐛 批量更新页补齐取数失败、检查空窗期与忽略/批量的中间态

取数失败时记录仍是空的,页面直接走到空态,把一次加载失败渲染成
「所有脚本均为最新(已检查 0 个脚本)」这条与事实相反的成功终态;
点「检查更新」到服务端广播回来之间页面完全静止,期间可以连点。

- 取数失败落错误终态:等宽 detail 框 + 重试 / 脚本列表出口
- 主动检查由本地 pending 立刻接管忙态,并把服务端的「正忙」「结果够新已跳过」
  「通道异常」三条回执分别说出来;跳过时就地清掉待反馈标记,
  否则会在下一次后台检查完成时冒出一条用户没点过的 toast
- 忽略复用与更新相同的行级阶段(working → success → 退场),不再 fire-and-forget
- 批量进行中互斥(行内勾选、两个批量按钮、全部恢复),避免两条进度互相覆盖;
  被「结果失效」中断时保留已完成条数,不把汇总抹掉
- 骨架补齐工具条(桌面)与顶部选择栏/底部操作栏(移动)占位,消除数据到达时的
  布局跳动,并加 role="status" / aria-busy;空态下重新检查不再整页闪回骨架
- 脚本名改用 aria-disabled + onClick 早退:disabled 会让浏览器不派发指针事件,
  正好在名字被截断、最需要看全名时把 tooltip 一起关掉,键盘触发后焦点还会掉到 body
CodFrm added a commit that referenced this pull request Sep 7, 2026
* 🐛 restore ScriptCat registration health checks (#1724)

* 🐛 修复 @run-at context-menu:设置覆写不生效、菜单注册不上、脚本体自己的菜单被屏蔽 (#1718)

* 🐛 修复设置面板覆写运行时机在重新注册后失效

restoreJSCodeFromCompiledResource 用脚本自带 metadata 选择编译分支,
而设置面板改运行时机/early-start 只写 selfMetadata,导致全量重新注册
(扩展更新、切换启用脚本、改黑名单等)后覆写被丢弃:context-menu 脚本
恢复自动执行且不注册菜单项,early-start 退化为普通注入。

pushValueUpdate 判断 early-start 时同样只看自带 metadata,覆写而来的
early-start 脚本在 GM 值变更后不会重新编译,预注入代码里的值会过期。

close #1649

* 🐛 修复 GM API 权限校验忽略用户覆写的运行时机

GMApi.parseRequest 直接把 scriptDAO 里的原始 Script 放进 GMApiRequest,
metadata 没有合并 selfMetadata。PermissionVerify 对 context-menu 脚本的
GM_registerMenuCommand 免 @grant 豁免因此判不出来,浏览器里表现为
verify error {"api":"GM_registerMenuCommand","error":"permission not requested"},
菜单项注册不上 —— 即 #1649 里「上下文菜单中没有出现执行选项」。

真实浏览器验证记录见 e2e/scratch/run-at-override/report.md(未入库)。

* 🐛 context-menu 包装不再屏蔽脚本体自己的 GM_registerMenuCommand

@run-at context-menu 的包装把脚本体塞进菜单回调时,回调开头把
GM_registerMenuCommand 连同 window./GM. 上的引用一起置为 undefined。
于是任何在脚本体里注册菜单的脚本,点菜单执行就会
TypeError: GM_registerMenuCommand is not a function 当场中断,
它自己的菜单项也永远注册不上——用户看到的是「GM_registerMenu 的菜单显示不出来」。
该置空还会污染页面 window 与该脚本的 GM 物件,且是持久的。

去掉这行,脚本体里的菜单注册照常工作。代价是脚本体每次被点执行都会重新注册
一次,内部条目累积(显示层按 groupKey 去重,不会出现重复菜单项,但同名项的
回调会被触发多次)。

真实浏览器验证记录见 e2e/scratch/ctx-menu-{display,fix}/(未入库)。

* ✨ 统一 example/tests 测试结果与人工验证反馈 (#1717)

* ✨ 统一 example/tests 测试结果与人工验证反馈

* 🔒 固定 sctest CDN 引用到框架提交

* 🎨 统一 userscript 诊断面板与测试描述

* 🔒 固定 sctest CDN 引用到最新框架提交

* Update sctest.js

* 🔒 固定 sctest CDN 引用到最新框架提交

* ✨ 增强统一测试诊断与面板反馈

* 🔒 固定 sctest CDN 引用到最终框架提交

* ✨ 优化 sctest 诊断面板与 frame 反馈

* 🔒 固定 sctest CDN 依赖版本

* ♿ 优化 sctest 面板可访问性反馈

* 🔒 重新固定 sctest CDN 引用

* 🎨 优化 sctest 面板指标与布局

* 🔒 固定指标优化后的 sctest CDN 引用

* 🐛 固定 sctest 耗时显示宽度

* ✅ 加固 UI 异步断言与 E2E 保存结果观察 (#1727)

* test: stabilize heavy network rules UI cases

* test: isolate UI files and await observable state

* ✅ 保留 UI 测试原有隔离配置

* ✅ harden async test observations

* 🐛 align E2E save expectations with failure cases

* 🔍 tighten test guard binding and toast observation

* 📄 补充 Agent 自主操作边界、指令冲突裁决与人类可读写作规范 (#1728)

现有 agent 文档规定了改动的质量门槛,但缺三层:agent 在两次人工决策之间可以自行做什么、
指令冲突如何裁决、以及写给人看的东西该怎么写。静态审查的依据:
整套文档没有一处把 agent 写成决策者(`decide` 的主语全是分类表或原则),因而产出「建议生成器」;
`material` 作为门槛术语被引用九次却从未定义,且在 pull-request.md 内有两种含义;
测试失败例外在 AGENTS.md 概括成两条而 owner 定义了六种;路由表是一次性分类;
人工指令能覆盖什么没有成文;以及全套文档没有任何一条关于行文的规范,
而 pull-request.md 提供的九级标题骨架会被当成表格来填。

本次补齐:范围内自行决策、交还决定须指名归属与阻塞点、不写可查证却不查的保留意见、
指令冲突裁决与人工指令覆盖边界、连续路由、`material` 定义、测试失败例外改交 owner 裁决、
自主操作边界、不稳定结果报告口径、面向人类读者的写作原则、文档集自身的指令预算。
PR 模板补一条不渲染注释;pull-request.md 明确其结构是待考虑项而非待填表格。

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>

* 🐛 批量更新页打开更新详情复用检查缓存并挡住重复点击 (#1719)

* 🐛 批量更新页打开更新详情复用检查缓存并挡住重复点击

点击脚本名查看更新时,openUpdatePageByUUID 会重新 fetch 一次脚本代码,
而这份新版代码在检查更新阶段已经存进 scriptUpdateCheck 的记录缓存里
(行内「更新」按钮装的就是它)。用户因此要为每次点击白等一次网络往返,
期间页面又没有任何反馈,连点几下就会开出多个安装页。

- SW: 拆出 prepareUpdateOrInstallPage,openUpdatePage 命中缓存代码时
  跳过 fetchScriptBody;openUpdatePageByUUID 改为回报 boolean
- 页面: 打开期间行内转圈并同步挡住重复点击,失败弹 toast,
  点击脚本名同样取消自动关闭倒计时

* 🐛 更新页与安装页补齐骨架屏与异步中间态,失败不再被渲染成成功 (#1721)

* 🐛 打开更新详情区分静默更新,忽略动作补齐逐条回执

页面此前无从判断服务端到底做了什么:openUpdatePageByUUID 在命中静默更新时
不开安装页却同样返回 true,用户点完脚本名只看到转一圈、什么都没发生;
IGNORE 分支根本没有返回值,页面只能 fire-and-forget。

- openUpdatePageByUUID / openUpdatePage 返回 "opened" | "silent" | "failed"
- IGNORE 逐条回报结果。忽略写的是脚本自身的 ignoreVersion,与检查缓存无关,
  因此缓存随 Service Worker 回收后忽略照样生效,这里如实回报而不是谎报失效
- checkScriptUpdate 的结果收敛成 TCheckScriptUpdateResult 并用 reason 区分
  「已有检查在跑」与真正的失败,页面才能分别提示

* 🐛 安装页补齐加载分档、代码骨架与提交忙态

从批量更新页点脚本名进来的必然是「更新」,加载屏却把上下文 chip 写死成
「脚本安装」,几百毫秒后再闪成「脚本更新」;描述写着「正在从来源下载」,
但这条入口的代码 Service Worker 早已备好,根本不下载。

- 状态屏按来路分档,未确知场景不渲染 chip(不猜),并补一条与就绪态操作栏
  等高的底部占位,避免就绪瞬间内容区高度再跳一次
- 暂存代码被定时清理回收时落到专属终态,出口换成「重新检查更新」——
  原来的「重试」在这个最常见的失败原因下重试多少次都是同一结果
- Monaco 实例就绪前渲染代码骨架,替代此前 340px 的纯空白
- toggleWatch / rejectExternalAccess 补忙态,install 加重入守卫:
  这两个动作全程不置忙态,连点会发出两次安装/两次决定

* 🐛 批量更新页补齐取数失败、检查空窗期与忽略/批量的中间态

取数失败时记录仍是空的,页面直接走到空态,把一次加载失败渲染成
「所有脚本均为最新(已检查 0 个脚本)」这条与事实相反的成功终态;
点「检查更新」到服务端广播回来之间页面完全静止,期间可以连点。

- 取数失败落错误终态:等宽 detail 框 + 重试 / 脚本列表出口
- 主动检查由本地 pending 立刻接管忙态,并把服务端的「正忙」「结果够新已跳过」
  「通道异常」三条回执分别说出来;跳过时就地清掉待反馈标记,
  否则会在下一次后台检查完成时冒出一条用户没点过的 toast
- 忽略复用与更新相同的行级阶段(working → success → 退场),不再 fire-and-forget
- 批量进行中互斥(行内勾选、两个批量按钮、全部恢复),避免两条进度互相覆盖;
  被「结果失效」中断时保留已完成条数,不把汇总抹掉
- 骨架补齐工具条(桌面)与顶部选择栏/底部操作栏(移动)占位,消除数据到达时的
  布局跳动,并加 role="status" / aria-busy;空态下重新检查不再整页闪回骨架
- 脚本名改用 aria-disabled + onClick 早退:disabled 会让浏览器不派发指针事件,
  正好在名字被截断、最需要看全名时把 tooltip 一起关掉,键盘触发后焦点还会掉到 body

---------

Co-authored-by: wangyizhi <yz@ggnb.top>
Co-authored-by: cyfung1031 <44498510+cyfung1031@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants