Skip to content

Commit 79cb27c

Browse files
authored
✨ 实现 CAT_registerMenuInput 支持快捷输入交互 (#411)
* 实现 GM_registerMenuCommand 支持快捷输入交互 - 实现 用户可通过 `GM_registerMenuCommand` 方法在 `popup 页面`与脚本快捷输入交互 - 支持 `"text" | "number" | "boolean"` 三种交互方式 - 新增4个脚本猫独有的options可选字段 `inputType` `inputLabel` `inputDefaultValue` `inputPlaceholder` 用于控制 `popup` 交互界面 - 修复 `autoClose` 字段不生效的bug * Update gm_api.ts * Update scriptcat.d.ts * 修复 autoClose 默认值应为true * 使用 form 重构交互元素 / 实现 inputValue 会话记忆 * 设置 新API CAT_registerMenuInput / CAT_unregisterMenuInput * Revert "实现 inputValue 会话记忆"
1 parent 5274edf commit 79cb27c

11 files changed

Lines changed: 235 additions & 53 deletions

File tree

example/cat_bg_input_menu.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// ==UserScript==
2+
// @name bg cat input menu
3+
// @namespace https://bbs.tampermonkey.net.cn/
4+
// @version 0.1.0
5+
// @description 在后台脚本中带交互输入的快捷菜单
6+
// @author You
7+
// @background
8+
// @grant CAT_registerMenuInput
9+
// @grant CAT_unregisterMenuInput
10+
// @grant GM_notification
11+
// ==/UserScript==
12+
13+
return new Promise((resolve) => {
14+
const id = CAT_registerMenuInput(
15+
"测试菜单",
16+
() => {
17+
console.log(id);
18+
CAT_unregisterMenuInput(id);
19+
},
20+
"z"
21+
);
22+
23+
CAT_registerMenuInput(
24+
"测试菜单boolean",
25+
(inputValue) => {
26+
GM_notification({
27+
title: "测试菜单boolean",
28+
text: "" + inputValue,
29+
});
30+
},
31+
{
32+
inputType: "boolean",
33+
inputLabel: "是否通知",
34+
inputDefaultValue: true,
35+
autoClose: false,
36+
}
37+
);
38+
39+
CAT_registerMenuInput(
40+
"测试菜单text",
41+
(inputValue) => {
42+
GM_notification({
43+
title: "测试菜单text",
44+
text: "" + inputValue,
45+
});
46+
},
47+
{
48+
inputType: "text",
49+
inputLabel: "通知内容",
50+
inputValue: "text",
51+
autoClose: false,
52+
}
53+
);
54+
55+
CAT_registerMenuInput(
56+
"测试菜单number",
57+
(inputValue) => {
58+
setTimeout(() => {
59+
GM_notification({
60+
title: "测试菜单number",
61+
text: "" + (1000 + inputValue),
62+
});
63+
}, 1000 + inputValue);
64+
},
65+
{
66+
inputType: "number",
67+
inputLabel: "延迟ms",
68+
inputPlaceholder: "最低1000ms",
69+
}
70+
);
71+
72+
resolve();
73+
});

example/gm_bg_menu.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@ return new Promise((resolve) => {
1515
GM_unregisterMenuCommand(id);
1616
resolve();
1717
}, "z");
18-
});
18+
});

packages/eslint/compat-grant.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ const compat_grant = require("eslint-plugin-userscripts/dist/data/compat-grant.j
22
const compatMap = {
33
CAT_userConfig: [{ type: "scriptcat", versionConstraint: ">=0.11.0-beta" }],
44
CAT_fileStorage: [{ type: "scriptcat", versionConstraint: ">=0.11.0" }],
5+
CAT_registerMenuInput: [{ type: "scriptcat", versionConstraint: ">=0.17.0-beta.2" }],
6+
CAT_unregisterMenuInput: [{ type: "scriptcat", versionConstraint: ">=0.17.0-beta.2" }],
57
...compat_grant.compatMap,
68
};
79

packages/eslint/linter-config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ const config = {
1313
CATRetryError: "readonly",
1414
CAT_fileStorage: "readonly",
1515
CAT_userConfig: "readonly",
16+
CAT_registerMenuInput: "readonly",
17+
CAT_unregisterMenuInput: "readonly",
1618
},
1719
rules: {
1820
"constructor-super": ["error"],

src/app/service/content/gm_api.ts

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import EventEmitter from "eventemitter3";
1010
import { getStorageName } from "@App/pkg/utils/utils";
1111
import { MessageRequest } from "../service_worker/gm_api";
1212
import { ScriptLoadInfo } from "../service_worker/runtime";
13+
import { ScriptMenuItem } from "../service_worker/popup";
1314

1415
interface ApiParam {
1516
depend?: string[];
@@ -299,20 +300,26 @@ export default class GMApi {
299300
}
300301

301302
@GMContext.API()
302-
GM_registerMenuCommand(name: string, listener: () => void, accessKey?: string | { id?: number }): number {
303+
GM_registerMenuCommand(
304+
name: string,
305+
listener: (inputValue?: any) => void,
306+
options_or_accessKey?: ScriptMenuItem["options"] | string
307+
): number {
303308
if (!this.menuMap) {
304309
this.menuMap = new Map();
305310
}
306-
if (typeof accessKey === "object") {
311+
if (typeof options_or_accessKey === "object") {
312+
const option: ScriptMenuItem["options"] = options_or_accessKey;
307313
// 如果是对象,并且有id属性,则直接使用id
308-
if (accessKey.id && this.menuMap.has(accessKey.id)) {
314+
if (option.id && this.menuMap.has(option.id)) {
309315
// 如果id存在,则直接使用
310-
this.EE.removeAllListeners("menuClick:" + accessKey.id);
311-
this.EE.addListener("menuClick:" + accessKey.id, listener);
312-
this.sendMessage("GM_registerMenuCommand", [accessKey.id, name, accessKey]);
313-
return accessKey.id;
316+
this.EE.removeAllListeners("menuClick:" + option.id);
317+
this.EE.addListener("menuClick:" + option.id, listener);
318+
this.sendMessage("GM_registerMenuCommand", [option.id, name, option]);
319+
return option.id;
314320
}
315321
} else {
322+
options_or_accessKey = { accessKey: options_or_accessKey };
316323
let flag = 0;
317324
this.menuMap.forEach((val, menuId) => {
318325
if (val === name) {
@@ -325,12 +332,20 @@ export default class GMApi {
325332
}
326333
this.eventId += 1;
327334
const id = this.eventId;
335+
options_or_accessKey.id = id;
328336
this.menuMap.set(id, name);
329337
this.EE.addListener("menuClick:" + id, listener);
330-
this.sendMessage("GM_registerMenuCommand", [id, name, accessKey]);
338+
this.sendMessage("GM_registerMenuCommand", [id, name, options_or_accessKey]);
331339
return id;
332340
}
333341

342+
@GMContext.API({
343+
depend: ["GM_registerMenuCommand"],
344+
})
345+
CAT_registerMenuInput(...args: Parameters<GMApi["GM_registerMenuCommand"]>): number {
346+
return this.GM_registerMenuCommand(...args);
347+
}
348+
334349
@GMContext.API()
335350
GM_addStyle(css: string) {
336351
// 与content页的消息通讯实际是同步,此方法不需要经过background
@@ -394,6 +409,13 @@ export default class GMApi {
394409
this.sendMessage("GM_unregisterMenuCommand", [id]);
395410
}
396411

412+
@GMContext.API({
413+
depend: ["GM_unregisterMenuCommand"],
414+
})
415+
CAT_unregisterMenuInput(...args: Parameters<GMApi["GM_unregisterMenuCommand"]>): void {
416+
this.GM_unregisterMenuCommand(...args);
417+
}
418+
397419
@GMContext.API()
398420
CAT_userConfig() {
399421
return this.sendMessage("CAT_userConfig", []);

src/app/service/queue.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { MessageQueue } from "@Packages/message/message_queue";
22
import { Script, SCRIPT_RUN_STATUS } from "../repo/scripts";
33
import { InstallSource } from "./service_worker";
44
import { Subscribe } from "../repo/subscribe";
5+
import { ScriptMenuItem } from "./service_worker/popup";
56

67
export function subscribeScriptInstall(
78
messageQueue: MessageQueue,
@@ -48,7 +49,7 @@ export type ScriptMenuRegisterCallbackValue = {
4849
uuid: string;
4950
id: number;
5051
name: string;
51-
options?: { autoClose?: string; title?: string; accessKey?: string };
52+
options?: ScriptMenuItem["options"];
5253
tabId: number;
5354
frameId: number;
5455
documentId: string;

src/app/service/service_worker/client.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,10 +233,11 @@ export class PopupClient extends Client {
233233
return this.do("getPopupData", data);
234234
}
235235

236-
menuClick(uuid: string, data: ScriptMenuItem) {
236+
menuClick(uuid: string, data: ScriptMenuItem, inputValue?: any) {
237237
return this.do("menuClick", {
238238
uuid,
239239
id: data.id,
240+
inputValue,
240241
sender: {
241242
tabId: data.tabId,
242243
frameId: data.frameId,

src/app/service/service_worker/gm_api.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -730,22 +730,22 @@ export default class GMApi {
730730
});
731731
}
732732

733-
@PermissionVerify.API()
733+
@PermissionVerify.API({ alias: ["CAT_registerMenuInput"] })
734734
GM_registerMenuCommand(request: Request, sender: GetSender) {
735-
const [id, name, accessKey] = request.params;
735+
const [id, name, options] = request.params;
736736
// 触发菜单注册, 在popup中处理
737737
this.mq.emit("registerMenuCommand", {
738738
uuid: request.script.uuid,
739-
id: id,
740-
name: name,
741-
options: typeof accessKey === "object" ? accessKey : { accessKey: accessKey },
739+
id,
740+
name,
741+
options,
742742
tabId: sender.getSender().tab?.id || -1,
743743
frameId: sender.getSender().frameId,
744744
documentId: sender.getSender().documentId,
745745
});
746746
}
747747

748-
@PermissionVerify.API()
748+
@PermissionVerify.API({ alias: ["CAT_unregisterMenuInput"] })
749749
GM_unregisterMenuCommand(request: Request, sender: GetSender) {
750750
const [id] = request.params;
751751
// 触发菜单取消注册, 在popup中处理

src/app/service/service_worker/popup.ts

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,17 @@ import { getStorageName } from "@App/pkg/utils/utils";
2525
export type ScriptMenuItem = {
2626
id: number;
2727
name: string;
28-
options?: { autoClose?: string; title?: string; accessKey?: string };
28+
options?: {
29+
id?: number;
30+
autoClose?: boolean;
31+
title?: string;
32+
accessKey?: string;
33+
// 可选输入框
34+
inputType?: "text" | "number" | "boolean";
35+
inputLabel?: string;
36+
inputDefaultValue?: string | number | boolean;
37+
inputPlaceholder?: string;
38+
};
2939
tabId: number; //-1表示后台脚本
3040
frameId?: number;
3141
documentId?: string;
@@ -58,16 +68,18 @@ export class PopupService {
5868
genScriptMenuByTabMap(menu: ScriptMenu[]) {
5969
let n = 0;
6070
menu.forEach((script) => {
71+
// 如果是带输入框的菜单则不在页面内注册
72+
const nonInputMenus = script.menus.filter((item) => !item.options?.inputType);
6173
// 创建脚本菜单
62-
if (script.menus.length) {
63-
n += script.menus.length;
74+
if (nonInputMenus.length) {
75+
n += nonInputMenus.length;
6476
chrome.contextMenus.create({
6577
id: `scriptMenu_` + script.uuid,
6678
title: script.name,
6779
contexts: ["all"],
6880
parentId: "scriptMenu",
6981
});
70-
script.menus.forEach((menu) => {
82+
nonInputMenus.forEach((menu) => {
7183
// 创建菜单
7284
chrome.contextMenus.create({
7385
id: `scriptMenu_menu_${script.uuid}_${menu.id}`,
@@ -320,12 +332,23 @@ export class PopupService {
320332
});
321333
}
322334

323-
menuClick({ uuid, id, sender }: { uuid: string; id: number; sender: ExtMessageSender }) {
335+
menuClick({
336+
uuid,
337+
id,
338+
sender,
339+
inputValue,
340+
}: {
341+
uuid: string;
342+
id: number;
343+
sender: ExtMessageSender;
344+
inputValue?: any;
345+
}) {
324346
// 菜单点击事件
325347
this.runtime.emitEventToTab(sender, {
326348
uuid,
327349
event: "menuClick",
328350
eventId: id.toString(),
351+
data: inputValue,
329352
});
330353
return Promise.resolve(true);
331354
}

0 commit comments

Comments
 (0)