Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions example/cat_bg_input_menu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// ==UserScript==
// @name bg cat input menu
// @namespace https://bbs.tampermonkey.net.cn/
// @version 0.1.0
// @description 在后台脚本中带交互输入的快捷菜单
// @author You
// @background
// @grant CAT_registerMenuInput
// @grant CAT_unregisterMenuInput
// @grant GM_notification
// ==/UserScript==

return new Promise((resolve) => {
const id = CAT_registerMenuInput(
"测试菜单",
() => {
console.log(id);
CAT_unregisterMenuInput(id);
},
"z"
);

CAT_registerMenuInput(
"测试菜单boolean",
(inputValue) => {
GM_notification({
title: "测试菜单boolean",
text: "" + inputValue,
});
},
{
inputType: "boolean",
inputLabel: "是否通知",
inputDefaultValue: true,
autoClose: false,
}
);

CAT_registerMenuInput(
"测试菜单text",
(inputValue) => {
GM_notification({
title: "测试菜单text",
text: "" + inputValue,
});
},
{
inputType: "text",
inputLabel: "通知内容",
inputValue: "text",
autoClose: false,
}
);

CAT_registerMenuInput(
"测试菜单number",
(inputValue) => {
setTimeout(() => {
GM_notification({
title: "测试菜单number",
text: "" + (1000 + inputValue),
});
}, 1000 + inputValue);
},
{
inputType: "number",
inputLabel: "延迟ms",
inputPlaceholder: "最低1000ms",
}
);

resolve();
});
2 changes: 1 addition & 1 deletion example/gm_bg_menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ return new Promise((resolve) => {
GM_unregisterMenuCommand(id);
resolve();
}, "z");
});
});
2 changes: 2 additions & 0 deletions packages/eslint/compat-grant.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ const compat_grant = require("eslint-plugin-userscripts/dist/data/compat-grant.j
const compatMap = {
CAT_userConfig: [{ type: "scriptcat", versionConstraint: ">=0.11.0-beta" }],
CAT_fileStorage: [{ type: "scriptcat", versionConstraint: ">=0.11.0" }],
CAT_registerMenuInput: [{ type: "scriptcat", versionConstraint: ">=0.17.0-beta.2" }],
CAT_unregisterMenuInput: [{ type: "scriptcat", versionConstraint: ">=0.17.0-beta.2" }],
...compat_grant.compatMap,
};

Expand Down
2 changes: 2 additions & 0 deletions packages/eslint/linter-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ const config = {
CATRetryError: "readonly",
CAT_fileStorage: "readonly",
CAT_userConfig: "readonly",
CAT_registerMenuInput: "readonly",
CAT_unregisterMenuInput: "readonly",
},
rules: {
"constructor-super": ["error"],
Expand Down
38 changes: 30 additions & 8 deletions src/app/service/content/gm_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import EventEmitter from "eventemitter3";
import { getStorageName } from "@App/pkg/utils/utils";
import { MessageRequest } from "../service_worker/gm_api";
import { ScriptLoadInfo } from "../service_worker/runtime";
import { ScriptMenuItem } from "../service_worker/popup";

interface ApiParam {
depend?: string[];
Expand Down Expand Up @@ -245,20 +246,26 @@ export default class GMApi {
}

@GMContext.API()
GM_registerMenuCommand(name: string, listener: () => void, accessKey?: string | { id?: number }): number {
GM_registerMenuCommand(
name: string,
listener: (inputValue?: any) => void,
options_or_accessKey?: ScriptMenuItem["options"] | string
): number {
if (!this.menuMap) {
this.menuMap = new Map();
}
if (typeof accessKey === "object") {
if (typeof options_or_accessKey === "object") {
const option: ScriptMenuItem["options"] = options_or_accessKey;
// 如果是对象,并且有id属性,则直接使用id
if (accessKey.id && this.menuMap.has(accessKey.id)) {
if (option.id && this.menuMap.has(option.id)) {
// 如果id存在,则直接使用
this.EE.removeAllListeners("menuClick:" + accessKey.id);
this.EE.addListener("menuClick:" + accessKey.id, listener);
this.sendMessage("GM_registerMenuCommand", [accessKey.id, name, accessKey]);
return accessKey.id;
this.EE.removeAllListeners("menuClick:" + option.id);
this.EE.addListener("menuClick:" + option.id, listener);
this.sendMessage("GM_registerMenuCommand", [option.id, name, option]);
return option.id;
}
} else {
options_or_accessKey = { accessKey: options_or_accessKey };
let flag = 0;
this.menuMap.forEach((val, menuId) => {
if (val === name) {
Expand All @@ -271,12 +278,20 @@ export default class GMApi {
}
this.eventId += 1;
const id = this.eventId;
options_or_accessKey.id = id;
this.menuMap.set(id, name);
this.EE.addListener("menuClick:" + id, listener);
this.sendMessage("GM_registerMenuCommand", [id, name, accessKey]);
this.sendMessage("GM_registerMenuCommand", [id, name, options_or_accessKey]);
return id;
}

@GMContext.API({
depend: ["GM_registerMenuCommand"],
})
CAT_registerMenuInput(...args: Parameters<GMApi["GM_registerMenuCommand"]>): number {
return this.GM_registerMenuCommand(...args);
}

@GMContext.API()
GM_addStyle(css: string) {
// 与content页的消息通讯实际是同步,此方法不需要经过background
Expand Down Expand Up @@ -340,6 +355,13 @@ export default class GMApi {
this.sendMessage("GM_unregisterMenuCommand", [id]);
}

@GMContext.API({
depend: ["GM_unregisterMenuCommand"],
})
CAT_unregisterMenuInput(...args: Parameters<GMApi["GM_unregisterMenuCommand"]>): void {
this.GM_unregisterMenuCommand(...args);
}

@GMContext.API()
CAT_userConfig() {
return this.sendMessage("CAT_userConfig", []);
Expand Down
3 changes: 2 additions & 1 deletion src/app/service/queue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { MessageQueue } from "@Packages/message/message_queue";
import { Script, SCRIPT_RUN_STATUS } from "../repo/scripts";
import { InstallSource } from "./service_worker";
import { Subscribe } from "../repo/subscribe";
import { ScriptMenuItem } from "./service_worker/popup";

export function subscribeScriptInstall(
messageQueue: MessageQueue,
Expand Down Expand Up @@ -48,7 +49,7 @@ export type ScriptMenuRegisterCallbackValue = {
uuid: string;
id: number;
name: string;
options?: { autoClose?: string; title?: string; accessKey?: string };
options?: ScriptMenuItem["options"];
tabId: number;
frameId: number;
documentId: string;
Expand Down
3 changes: 2 additions & 1 deletion src/app/service/service_worker/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,10 +228,11 @@ export class PopupClient extends Client {
return this.do("getPopupData", data);
}

menuClick(uuid: string, data: ScriptMenuItem) {
menuClick(uuid: string, data: ScriptMenuItem, inputValue?: any) {
return this.do("menuClick", {
uuid,
id: data.id,
inputValue,
sender: {
tabId: data.tabId,
frameId: data.frameId,
Expand Down
12 changes: 6 additions & 6 deletions src/app/service/service_worker/gm_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -645,22 +645,22 @@ export default class GMApi {
});
}

@PermissionVerify.API()
@PermissionVerify.API({ alias: ["CAT_registerMenuInput"] })
GM_registerMenuCommand(request: Request, sender: GetSender) {
const [id, name, accessKey] = request.params;
const [id, name, options] = request.params;
// 触发菜单注册, 在popup中处理
this.mq.emit("registerMenuCommand", {
uuid: request.script.uuid,
id: id,
name: name,
options: typeof accessKey === "object" ? accessKey : { accessKey: accessKey },
id,
name,
options,
tabId: sender.getSender().tab?.id || -1,
frameId: sender.getSender().frameId,
documentId: sender.getSender().documentId,
});
}

@PermissionVerify.API()
@PermissionVerify.API({ alias: ["CAT_unregisterMenuInput"] })
GM_unregisterMenuCommand(request: Request, sender: GetSender) {
const [id] = request.params;
// 触发菜单取消注册, 在popup中处理
Expand Down
33 changes: 28 additions & 5 deletions src/app/service/service_worker/popup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,17 @@ import { getStorageName } from "@App/pkg/utils/utils";
export type ScriptMenuItem = {
id: number;
name: string;
options?: { autoClose?: string; title?: string; accessKey?: string };
options?: {
id?: number;
autoClose?: boolean;
title?: string;
accessKey?: string;
// 可选输入框
inputType?: "text" | "number" | "boolean";
inputLabel?: string;
inputDefaultValue?: string | number | boolean;
inputPlaceholder?: string;
};
tabId: number; //-1表示后台脚本
frameId?: number;
documentId?: string;
Expand Down Expand Up @@ -58,16 +68,18 @@ export class PopupService {
genScriptMenuByTabMap(menu: ScriptMenu[]) {
let n = 0;
menu.forEach((script) => {
// 如果是带输入框的菜单则不在页面内注册
const nonInputMenus = script.menus.filter((item) => !item.options?.inputType);
// 创建脚本菜单
if (script.menus.length) {
n += script.menus.length;
if (nonInputMenus.length) {
n += nonInputMenus.length;
chrome.contextMenus.create({
id: `scriptMenu_` + script.uuid,
title: script.name,
contexts: ["all"],
parentId: "scriptMenu",
});
script.menus.forEach((menu) => {
nonInputMenus.forEach((menu) => {
// 创建菜单
chrome.contextMenus.create({
id: `scriptMenu_menu_${script.uuid}_${menu.id}`,
Expand Down Expand Up @@ -320,12 +332,23 @@ export class PopupService {
});
}

menuClick({ uuid, id, sender }: { uuid: string; id: number; sender: ExtMessageSender }) {
menuClick({
uuid,
id,
sender,
inputValue,
}: {
uuid: string;
id: number;
sender: ExtMessageSender;
inputValue?: any;
}) {
// 菜单点击事件
this.runtime.emitEventToTab(sender, {
uuid,
event: "menuClick",
eventId: id.toString(),
data: inputValue,
});
return Promise.resolve(true);
}
Expand Down
Loading