✨ 安装页标出不生效的指令与 GM 能力 - #1742
✨ 安装页标出不生效的指令与 GM 能力#1742
Conversation
安装页要标出「写了但不会生效」的指令与 GM 能力,但仓库里没有一份「脚本猫支持 什么」的数据:编辑器的未定义标签判定取自悬浮提示表(langs/*.ts 的 prompt 键, 一张翻译表),ESLint 的合法 header 集合取自 packages/eslint 对 eslint-plugin- userscripts 的覆盖,两者互不相识且已经漂移;GM 能力的真相只在 GMContext 注册表 里,而注册表由装饰器在 content 上下文载入时填充,安装页不可能为查一次支持性把 整套 GM 实现拉进包。 新增 src/pkg/utils/script_compat.ts 作为唯一判定来源。判定是二元的:指令要么被 脚本猫消费,要么写了也不生效。收录标准写在文件头——「会被消费」或「不消费但也 不改变脚本运行行为」,只有会改变别家管理器下脚本行为、而脚本猫没实现的指令才刻 意留在表外。GM 能力静态镜像注册表,由 GMContextApiNames() 双向守卫,新增 API 漏 进表会让测试转红;不经注册表、由上下文恒定提供的 unsafeWindow / GM_info / window.onurlchange / none 单独登记,便于审阅。 编辑器的已知标签集合改用这张表,不再随语言包重建——一条指令是否被支持与界面语言 无关。随之修正两处既有误报:@antifeature:<locale> 此前不在本地化后缀规则内被当成 未定义标签;@Definition 是脚本猫自有指令却不在 ESLint 的合法 header 集合里,编辑 器会报「不是合法 userscript header」。 另加 parseMetadataLines:与 parseMetadata 共用 HEADER_BLOCK / META_LINE,逐条保留 行号。诊断必须与运行时解析看到同一批指令,否则会对着一条运行时根本没读到的行报警。
用户安装前看不出脚本里哪些声明脚本猫不会执行。以 @exclude-match 为例:脚本猫 只识别 @match/@include/@exclude,这条声明被原样保留却从不参与匹配,本该被排除 的页面照样会运行脚本,而安装页对此只字不提,装完也不会有任何提示(#1713)。 判定沿用支持表,是二元的:脚本猫不消费就是不生效,不再分兼容程度。标记就近长在 它所属的权限行上,不另起一张卡——不受支持的 @grant 直接替换权限行里原有的 chip, 匹配类声明(@exclude-match)追加到「运行网站」行,归不到任何权限类别的(@sandbox、 @top-level-await 等)落在新增的「其他声明」行。说明走浮层,鼠标移入与键盘聚焦都能 打开;点击 chip 本身恒为跳转,展开代码卡并滚动到该声明所在行并选中。含义不只靠颜色 传达:每枚 chip 带禁止图标与「不生效」的无障碍名。 两处折叠形态会藏掉标记,一并处理:权限一项没变时整卡本会塌成一行,有不生效项时 不塌(折叠的是上次已确认过的权限,而标记是这次才出现的新信息);移动端有标记的 类别默认展开。全都没有时安装页与今天一字不改。 代码定位要求 diff 预览也能拿到实例,为此 CodeEditor 新增 onReady(普通与 diff 两条 分支都触发),revealLine 在 diff 预览里定位到修改侧。这连带修掉一个既有缺陷:此前 onEditorMount 只在普通分支触发,更新安装页(有 diff)的代码骨架永远收不起来——真实 浏览器确认过它被 monaco 盖住不影响观感,但 role=status + aria-busy 会一直留在无障碍 树里说「正在加载代码」。tests/mocks/CodeEditor.tsx 此前无视 diff 一律回调,正是它让 这个缺陷在单元测试里看不出来,现已与真实实现对齐。
真实会话验证时发现两处:浮层里有「兼容性文档」链接,但只有 chip 自己在跟踪悬停, 鼠标一离开 chip 去点链接浮层就关了,链接实际上点不到;标记 chip 常常并排,浮层宽 288px,切换悬停时旧浮层要等关闭延迟才收,两枚会同时开着互相盖住。 改用仓库已有的 useHoverMenu,把浮层本体一并纳入悬停范围;再以模块级「当前开着的 浮层」做互斥,悬停切换时立刻收掉上一枚。
|
这个做法有点古怪吧 但看截图来说,你是用记录「不支持」的方式来实现。不然怎么知道「 「 |
| const TAG_GROUP: Readonly<Record<string, IneffectiveTagGroup>> = { | ||
| "exclude-match": "match", | ||
| matchaboutblank: "match", | ||
| }; |
There was a problem hiding this comment.
记录不支持的syntax这个做法超白痴
不支持的有无数的多
说不定明天 TM FM 又会加一堆新syntax
这种列表的更新一定不会追得上实际
There was a problem hiding this comment.
说得对,这张 TAG_GROUP 实际上就是一份不支持清单,已经在 94c5992 删掉了。
判定本身一直是白名单:script_compat.ts 里只登记「脚本猫会消费」和「不影响运行的信息类」指令,表外一律算不生效,不需要事先登记。当初只有「把它摆到哪一行」这一步是按名字猜的。现在表外指令统一放在「其他声明」行,@match-website-only 这种没见过的指令也会被标出来,compat.test.ts 里加了对应用例。
There was a problem hiding this comment.
好像不太对,这里的记录不支持,是因为知道这是属于 运行网站 那类的,如果是未知的,会显示到其它声明去
There was a problem hiding this comment.
是因为知道这是属于 运行网站 那类的
不需要 (no need)
對scriptcat來說不明意義的就標不明
需要支持才做實作
不要在 支持 & 不支持 中間加一個 "我們知道有這樣的聲明但我們不支援"
這個在已有的editor有提示
安裝頁只需要說明支持跟不支持
There was a problem hiding this comment.
我最初的論點沒有改變。仍舊認為這個 「不支持名單」是無謂不需要
倒過來那堆「不支持聲明」中少了一個 exclude-match會讓人誤以為支持
There was a problem hiding this comment.
已按这个方向处理(7058a825):去掉归行名单(TAG_GROUP),不再维护「知道但不支持」的中间档,支持表之外的指令一律直接落「其他声明」行。@match 解析不出规则的取值仍在运行网站行就地打标——那是取值级的 chip 替换,不属于归行。
|
另外,如果要标记「不支持」,应该也要标记「仅限ScriptCat」(那堆CAT_XXXXXX) |
此前 @exclude-match 等靠一张名单归到「运行网站」行,这等于维护一份不支持指令清单, 别家新增的指令永远追不上。判定本来就只认支持表,呈现也不再按名字猜归属。
CAT_ / CAT. 是脚本猫自有命名空间,装在别的脚本管理器里不可用,在 GM 能力 chip 上就地注明。
|
@cyfung1031 两条都已处理:
在新构建上跑过真实扩展会话,亮暗两色都确认了。PR 描述已同步更新。
|
指令名早已按支持表判定,但受支持指令的取值没有:运行时对不认得的 @run-at / @run-in / @inject-into 取值静默回退,@run-in container-id-2 这类写法会让脚本在任何标签页都不运行, 解析不出规则的 @match 被直接丢弃,安装页却一字不提。现在取值同样只认白名单, @match 交给运行时同一个解析器判定,只读第一个取值的指令标出被忽略的后续取值, @early-start 缺 @run-at document-start 时也标为不生效。 另把 @early-start、@background 等脚本猫独有的指令在「其他声明」行标出「仅限脚本猫」; 名单由测试对照 eslint-plugin-userscripts 收录的别家指令守卫。
- @exclude-match 等已知影响运行网站的指令挂回运行网站行;归组只决定摆放, 支持与否仍由支持表判定,不认识的指令照样落在其他声明,不会漏标。 解析不出规则的 @match 在运行网站行就地换成不生效标记,不再重复成条。 - 浮层链接改为「描述文档」并带小节锚点,只给文档里确有说明的条目; 不支持的指令与 API 文档里本来没有,不再给一个点过去找不到的链接。 「仅限脚本猫」标签同样链到对应小节。 - 标记按作者的原始写法呈现(@storageName 而非 @storagename)。 - 移动端卡头的不生效计数不再折行。
@matchAboutBlank 是 FireMonkey 特有的,移出运行网站行的名单;它仍按支持表标为不生效,落在其他声明行。
GM 能力超过 8 项时排在后面的不生效能力会收进「+N」;更新时属于未变动的会收进折叠桶, 整行未变动时整行塌成单行——三种情况下标记都看不到。带标记的取值现在不参与截断与折叠, 带标记的类别不塌行(移动端本来就默认展开)。
|
@cyfung1031 更正我之前的两条回复:「归到运行网站行」的名单已经恢复(75bb2feb),不是删掉。复核后原设计没有问题,原因如下。 判定和摆放是两回事。 支不支持只看 回应评审之后,另外补了这些:
具体改动和验证记录见 PR 描述里的提交 5~9。 |
CodeEditor 冲突取舍:main 为全屏预览让 diff 分支也回调 onEditorMount(取 getModifiedEditor),本 PR 已把就绪信号拆成 onReady(普通与 diff 都触发), 且 mock 与注释都约定 onEditorMount 仅普通编辑器触发,故 diff 分支只保留 onReady,全屏/骨架就绪由 onReady 承担;main 为此加的 onEditorMount-diff 测试随之移除,diff 就绪已由本 PR 的 onReady 用例与 CodePreview 全屏 diff 用例覆盖。CodePreview 合并两边:保留 main 的全屏对话框,跳行排队与 revealLine 走 PR 的 handleReady/onReady;section 不带 overflow-hidden (沿用 main #1746 的修正)。
回应评审:安装页只说支持/不支持,不再维护「知道但不支持」的中间档 归行表(TAG_GROUP),那张表永远追不上别家新增的指令。@exclude-match 等不生效指令不再挂到运行网站行,与其他指令一起在「其他声明」行呈现; @match 解析不出规则的取值仍在运行网站行就地打标(取值级,不属归行)。

Checklist / 检查清单
背景
安装前看不出脚本里哪些声明脚本猫不会执行。以 issue 里的
@exclude-match为例:脚本猫的匹配链路只认@match/@include/@exclude(src/pkg/utils/url_matcher.ts),这条声明被 metadata 原样保留却从不参与匹配,本该被排除的页面照样会运行脚本;安装页对此只字不提,装完也不会有任何提示。#931 的反馈者正是「翻文档才发现原来不兼容」。
动手时发现真正缺的不是 UI,而是没有一份「脚本猫支持什么」的数据,现有三份东西互不相识:
monaco-editor/langs/*.ts的prompt键,一张翻译表);packages/eslint/对eslint-plugin-userscripts的覆盖,描述的是 TM/VM/GM 的支持情况;GMContext注册表里,而它由装饰器在 content 上下文载入时填充。两份列表已经漂移(
allFrames/require-css在 ESLint 覆盖里、不在悬浮提示表里)。另外userscripts/compat-grant与
compat-headers两条规则在 plugin 的configs.recommended里是off,仓库也从没设settings.userscriptVersions,所以那两个覆盖文件今天只影响「名字合不合法」,并不做兼容性判定。
本次改动
九个提交,可分开审(4~9 为回应评审的后续提交;4 的归组已在 7 中恢复):
1.
♻️ 兼容性判定收敛为一张支持表— 新增src/pkg/utils/script_compat.ts作为唯一判定来源。判定是二元的:指令要么被脚本猫消费,要么写了也不生效,不分兼容程度。收录标准写在文件头——「会被消费」或「不消费但也不改变脚本
运行行为」(著作信息、脚本站元数据),只有会改变别家管理器下脚本行为、而脚本猫没实现的指令才刻意留在表外。
GM 能力静态镜像注册表,由新增的
GMContextApiNames()双向守卫。编辑器的已知标签集合改用这张表,不再随语言包重建。随之修正两处既有误报:
@antifeature:<locale>此前不在本地化后缀规则内被当成未定义标签;@definition是脚本猫自有指令却不在 ESLint 的合法 header 集合里。另加
parseMetadataLines,与parseMetadata共用HEADER_BLOCK/META_LINE但逐条保留行号——诊断必须与运行时解析看到同一批指令。
2.
✨ 安装页标出不生效的指令与 GM 能力— 标记就近长在它所属的权限行上,不另起一张卡:不受支持的@grant直接替换权限行里原有的 chip,已知影响运行网站的指令(
@exclude-match)追加到「运行网站」行,其余不生效的元数据指令(@sandbox等)落在新增的「其他声明」行。说明走浮层(鼠标移入与键盘聚焦都能开),点击 chip恒为跳转:展开代码卡、滚动到该行并选中。含义不只靠颜色传达,每枚 chip 带禁止图标与「不生效」的无障碍名。
两处折叠形态会藏掉标记,一并处理:权限一项没变时整卡本会塌成一行,有不生效项时不塌;移动端有标记的类别默认展开。
文案补齐 10 个语言包。
3.
🐛 不生效标记的浮层不再吞掉文档链接与相邻浮层— 真实会话验证时发现的两处交互缺陷,见下。4.
💄 不生效的指令统一落在「其他声明」行— 最初@exclude-match靠一张名单归到「运行网站」行,那张名单本质上是不支持指令清单,别家新增的指令追不上。判定一直只认支持表(表外即不生效,@match-website-only这类没见过的指令同样会被标出,已加用例),现在呈现也不再按名字猜归属。(维护者复核后认为原设计没有问题,已在提交 7 恢复,见下。)5.
✨ 安装页标出仅限脚本猫的 CAT 能力—CAT_*/CAT.*能力在 GM 能力 chip 上就地加「仅限脚本猫」标签。按命名空间前缀判定(且须是脚本猫已实现的能力),不维护名单;不计入「N 项不生效」,也不影响折叠。6.
✨ 安装页按白名单标出不生效的取值与脚本猫独有指令— 指令名早已按支持表判定,但受支持指令的取值没有:运行时对不认得的@run-at/@run-in/@inject-into取值静默回退(getRunAt、runtime.ts的 run-in 判定),@run-in container-id-2会让脚本在任何标签页都不运行;解析不出规则的@match被extractUrlPatterns直接丢弃。现在script_compat.ts给取值有限的指令列了取值白名单(@unwrap/@early-start另接受false,效果与不写一致),@match交给运行时同一个解析器判定,运行时只读第一个取值的指令标出被忽略的后续取值,@early-start缺@run-at document-start时标为不生效。这些取值标记落在「其他声明」行,形如@run-at document-weird(@match的除外,见提交 7),行摘要改为「兼容性与其他脚本管理器不同」。同一行还给@early-start、@background、@storageName等脚本猫独有指令加「仅限脚本猫」标签;名单由测试对照 eslint-plugin-userscripts 收录的别家指令守卫(只对照会被消费的指令,@definition不算)。7.
✨ 安装页不生效标记恢复按类归行,文档链接只指向有说明的小节— 恢复提交 4 之前的归组:TAG_GROUP只决定摆放位置,不决定支不支持——判定仍然只认支持表,名单里没有的不生效指令一律落在「其他声明」,不会漏标;名单里的(@exclude-match)挂在它影响的「运行网站」行,读者能就地判断后果。解析不出规则的@match在运行网站行就地换成不生效标记,与@grant同一做法。浮层链接从笼统的「兼容性文档」改为「描述文档」并带小节锚点,只给文档里确有说明的条目(取值类标记,以及@storageName、CAT_fileStorage等「仅限脚本猫」标签,锚点表在src/pages/install/compat_docs.ts);不支持的指令与 API 文档里本来就没有,不再给一个点过去找不到的链接。另外标记按作者原始写法呈现(@storageName),移动端卡头计数不再折行。8.
💄 归行名单只收 Tampermonkey / Violentmonkey 的指令— 归行名单按 TM / VM 现行文档收录,别家特有的指令与旧写法不收;FireMonkey 的@matchAboutBlank因此移出,仍标为不生效,落在其他声明行。目前名单只有@exclude-match。9.
🐛 不生效标记不再被折叠藏起来— 不支持的@grant始终只标在 GM 能力行,但此前有三种情况看不到标记:能力超过 8 项时排在后面的被收进「+N」、更新时属于未变动的被收进折叠桶、整行未变动时整行塌成单行。带标记的取值现在不参与截断与折叠,带标记的类别不塌行(移动端本来就默认展开有标记的类别)。实现考虑
不在安装页跑 ESLint worker。 issue 的 implementation guidance 提到复用编辑器的 lint 链路,没有采用:
eslint_config是用户可改的(
src/pkg/config/config.ts),用户关掉规则提示就没了——把安全相关的提示挂在用户的 lint 偏好上是错的;安装页是信任决策页,要确定、要快,不该为此拉进 linter worker bundle。标记全部由 metadata 静态派生。
不扫描正文里的 GM 调用。
@grant本来就写在 metadata 里,不需要扫正文;正则扫GM_xxx误报率高,属于编辑器那侧的事。@definition按信息类处理(不标记)。 它是脚本猫自己文档化的编辑器指令,但全仓库没有任何消费方。在它被实现或从文档撤下之前,对着脚本猫自己的文档报警只会让人以为是 bug。这条在
script_compat.ts里写了注释,属于已知的待决项。代码定位要求 diff 预览也能拿到实例,为此
CodeEditor新增onReady(普通与 diff 两条分支都触发),revealLine在 diff 预览里定位到修改侧。这连带修掉一个既有缺陷:此前
onEditorMount只在普通分支触发,更新安装页(有 diff)的代码骨架永远收不起来。真实浏览器确认它被 monaco 盖住、不影响观感,问题在
role=status+aria-busy一直留在无障碍树里说「正在加载代码」。
tests/mocks/CodeEditor.tsx此前无视 diff 一律回调,正是它让这个缺陷在单元测试里看不出来,现已与真实实现对齐。
已知限制
GM_setClipboard只支持字符串这类行为差异不会在安装页出现。安装后会被忽略」,对信息类不算错,但是噪音)。新增时往
INFORMATIONAL_TAGS补一行即可。@exclude-match([Compatibility] Add support for@exclude-match#1714 单独跟踪)。标记不阻断安装。@inject-into auto(VM:页面注入失败时退回 content)按二元口径标为不生效:脚本猫只认content,其余一律按页面注入。@run-at document-weird仍无提示。@grant不区分「别家有但脚本猫没实现」和「拼写错误」,统一一句文案。要区分需把eslint-plugin-userscripts的grant 数据带进安装页 bundle,收益不抵成本。
建议审查重点
src/pkg/utils/script_compat.ts的CONSUMED_TAGS/INFORMATIONAL_TAGS两份名单——这是全部判定的根,逐条都值得看一眼。deriveCompatMarks里 metadata 键与代码行的对齐方式(src/pages/install/compat.ts)。CodeEditor新增onReady与onEditorMount的职责划分。关联
close #1713
相关:#931(兼容性跟踪)、#1714(
@exclude-match支持本身)验证
pnpm run lint:prettier / tsc / check:i18n / check:issue-templates / eslint 全通过。pnpm run test:ci:4781 passed(空载)。并行满载时src/pages/options/routes/Tools/NetworkRules/的850ms UI budget 用例会超时(观察到 3~19 例不等),同一批用例空载连续三次全绿;这是 ⚡️ 降低网络规则跨页测试成本并固化测试约束 #1734 自己记录的既有限制
(「本地并行运行整个 NetworkRules 目录时…会出现 worker contention 超时」),与本次改动无关。另外
useInstallData.test.ts的「技能安装成功后同样走按钮状态机与延时关闭」在满载下出现过一次超时,未再复现。script_compat.test.ts(支持判定 + 与注册表/ESLint header 集合的双向一致性守卫)、script.test.ts的parseMetadataLines、install/compat.test.ts(派生与定位)、CompatChip.test.tsx(浮层、跳转、互斥、无行号兜底)、
PermissionRow/PermissionCard/CodePreview/CodeEditor/App的接线与折叠形态。pnpm run lint全通过;install / pkg/utils / locales 1009 例通过;在 75bb2fe 的构建上重跑真实扩展会话,确认@exclude-match与写错的@match在运行网站行、其余在其他声明行,取值浮层链到docs/dev/meta#run-at,不支持的指令浮层无链接,@storageName标签链到#storagename-,亮暗两色与 390px 移动端卡头正常。pnpm run lint全通过;install / pkg/utils / locales 997 例通过;在 0823786 的构建上重跑真实扩展会话,确认@run-at document-weird、@inject-into auto与@exclude-match、@sandbox同在「其他声明」行、卡头计 5 项、@storagename与CAT_fileStorage带「仅限脚本猫」、亮暗两色正常、全兼容脚本仍零标记。pnpm run lint全通过;install / script_compat / locales 相关 290 例通过(useInstallData.test.ts的上述用例在并行时又超时一次,单独重跑通过);在 a3c3897 的构建上重跑真实扩展会话,确认@exclude-match与@sandbox同在「其他声明」行、「运行网站」行无标记、CAT_fileStorage带「仅限脚本猫」、亮暗两色正常,下面其余场景也都还过。未重跑全量test:ci。pnpm run build后加载dist/ext,Playwright 驱动安装页)覆盖:三枚标记落在正确的行、卡头徽章计数、受支持能力不带标记、悬停浮层、鼠标移入浮层后文档链接可达、悬停切换只保留一枚浮层、点击跳转在普通
预览与更新态内联 diff 下都滚动并选中目标行(diff 下 monaco 会自动展开折叠的 unchanged region)、更新态代码骨架
已收起、暗色主题、全部受支持的脚本零标记。
code-skeletonisVisible()恒为true。Screenshots / 截图
以下两张摄于提交 3,缺少提交 5~7 新增的取值标记、「仅限脚本猫」标签与文档链接调整。