上一主题 下一主题
ScriptCat,新一代的脚本管理器脚本站,与全世界分享你的用户脚本油猴脚本开发指南教程目录
返回列表 发新帖

油猴菜单库 水果玉米系列

[复制链接]
  • TA的每日心情
    奋斗
    2025-4-23 20:09
  • 签到天数: 7 天

    [LV.3]偶尔看看II

    18

    主题

    17

    回帖

    290

    积分

    荣誉开发者

    积分
    290

    油中2周年新人报道荣誉开发者

    发表于 2025-5-28 18:39:32 | 显示全部楼层 | 阅读模式

    如何使用? 库问题反馈 给库评分 查看代码

    本帖最后由 溯水流光 于 2025-5-28 18:41 编辑

    库地址: https://scriptcat.org/zh-CN/script-show-page/3508
    // @require https://scriptcat.org/lib/3508/1.0.2/MenuManager.js

    本库的功能

    油猴菜单库,支持开关菜单,支持批量添加,为您解决批量添加和开关菜单的烦恼

    • 支持批量添加菜单
    • 支持快速实现开关菜单
    • 支持开关状态自动存储并持久化

    实际应用的案例, 油管自动跳转: https://bbs.tampermonkey.net.cn/thread-8787-1-1.html

    油管播放短视频时, 自动跳转为长视频播放页面

    文末通过一个简单的小案例,带你了解 MenuManager 的使用。

    灵感来源

    https://greasyfork.org/zh-CN/scripts/411512-gm-createmenu

    5年前的库了, 很感谢作者的开源和分享, 但是其代码质量实在无法恭维, 我重构和整理了 2 个多小时, 最后还是选择直接推倒重来

    但本库的 api 设计上是有参考这个库的

    使用方式

    本库是对 GM_registerMenuCommand 和 GM_unregisterMenuCommand 的封装

    所以使用前, 请先参考 GM_registerMenuCommand 的使用教程:

    https://bbs.tampermonkey.net.cn/thread-271-1-1.html

    使用前的准备

    // @grant        GM_registerMenuCommand
    // @grant        GM_unregisterMenuCommand

    我们需要为脚本申请这两个权限, 才能成功通过 menuManager 创建菜单项

    去除 grant none, 这会导致我们无法申请 GM 函数, 直接去除就好了:

    // @grant        none

    或着可以修改为:

    // @grant        unsafeWindow

    创建一个开关菜单

    menuManager.addAndCreate({
        // 开关默认状态, 不写 default 的话, 默认为 true
        default : true,
        on : {
            // 开关 开启状态的提示信息
            name : "自动跳转状态: 开启✅ (点我关闭)", 
        },
        off : {
            // 开关 关闭状态的提示信息
            name : "自动跳转状态: 关闭❎ (点我开启)", 
        },
        accessKey: 'E', // 快捷键
        callback(state, isInit){ 
            // state 为当前开关状态, isInit 为是否为初始化
            console.log(state, isInit)
        }
    });

    大功告成了:

    183450jaztzaojlfgwptfi.png

    可以自己点击菜单, 观察控制台输出

    menuManager 创建开关菜单的内部逻辑:

    menuManager addAndCreate 时
    
    会通过 on 和 off 的 name, 获取唯一 name, 称之为 storeKey
    
    menuManager 会通过这个 storeKey 去 localStorage 获取存储的 开关状态的 boolean 值
    
    如果为 null, 则使用 default 指定的值为 开关状态的 boolean 值
    
    如果不为 null, 则转换 "true" "false" 为 true false, 并作为 开关状态的 boolean 值
    
    开关状态的 boolean, 我们用 currState 存储
    
    然后会调用 callback(currState, true) 这里便是初始化
    
    然后会通过 GM_registerMenuCommand 注册菜单, 来显示对应的 on 或 off 的 name
    
    当菜单点击的时候, 会反转 currState, 并调用 callback(currState, false)
    
    并通过一系列操作, 更新显示正确的 on 或 off name

    可以直接阅读源码, 获取更多细节, 源码不算长, 才 261 行

    创建普通菜单

    menuManager.addAndCreate(
        {
            name : "点击我",
            accessKey: 'C', // 快捷键
            callback(){
                alert("menu is been clicked");
            }
        }
    );
    183633f9mqneoteqmte689.png

    批量创建菜单

    menuManager.addAndCreate 可以传入数组

    menuManager.addAndCreate([
        {
            // 开关状态, 不写 default 的话, 默认为 true
            default : true, 
            on : {
                // 开关 开启状态的提示信息
                name : "自动跳转状态: 开启✅ (点我关闭)", 
            },
            off : {
                // 开关 关闭状态的提示信息
                name : "自动跳转状态: 关闭❎ (点我开启)", 
            },
            accessKey: 'E', // 快捷键
            callback(state, isInit){
                // state 为当前开关状态, isInit 为是否为初始化
                console.log(state, isInit)
            }
        },
        {
            name : "点击我",
            accessKey: 'C', // 快捷键
            callback(){
                alert("menu is been clicked");
            }
        }
    ]);

    API

    下面的 api, 都返回了 menuManager, 方便链式调用

    // 添加单条或多条菜单项
    add(entryOrEntries) 
    // 关闭 menuManager 的日志
    disableLog()
    // add 添加后, 要 create 才能正常使用
    create()
    // add 并自动 create
    addAndCreate(entryOrEntries)

    综合练习

    我们写一个小案例, 来熟悉 menuManager 的使用

    案例目标: Google 自动聚焦

    默认 Google 就是聚焦的, 这里的功能为, 自动展开搜索记录, 本案例只是为了练习

    引入 vite-monkey-plugin

    https://github.com/lisonge/vite-plugin-monkey

    vite-monkey-plugin 支持 HMR: 本地 ctrl + s 保存,浏览器自动更新脚本。

    使用 vite-monkey-plugin ,有诸多好处:

    1.可以使用现代的 IDE 进行脚本开发,如我们这里使用免费的 WebStrom,WebStrom 在去年的 1024 程序员节,支持开源项目免费使用了。

    2.使用了 vite 了,我们可以模块化开发脚本,使用 ES Module 来开发脚本,可以把脚本拆分为多个模块,方便复用和维护。

    初始化项目

    pnpm create monkey

    名字叫: monkey-google-focus

    选择 Vanilla, 也就是纯 JS 开发脚本

    src下, main.js 清空, 其他全部删除

    目录结构如下所示:

    $ tree
    .
    |-- src
    |   `-- main.js
    |-- package.json
    |-- pnpm-lock.yaml
    `-- vite.config.js
    
    19 directories, 52 files
    pnpm run dev

    vite-monkey-plugin 默认创建出来的项目就是匹配 google, 无需配置

    main.js 可以简单打印 console.log("hello world") 来检查是否生效

    配置

    export default defineConfig({
      plugins: [
        monkey({
          entry: 'src/main.js',
          userscript: {
            icon: 'https://vitejs.dev/logo.svg',
            namespace: 'npm/vite-plugin-monkey',
            match: ['https://www.google.com/'],
          },
        }),
      ],
    });
    

    修改为

    // https://vitejs.dev/config/
    export default defineConfig({
      plugins: [
        monkey({
          entry: 'src/main.js', // 脚本入口
          userscript: {
            icon: 'https://vitejs.dev/logo.svg',
            namespace: 'npm/vite-plugin-monkey',
            match: ['https://www.google.com/*'],
            version: "1.0.0",
            author: "hzx", // 你的名字
            license: "MIT", // 开源协议
            description: "google search auto focus", // 介绍
          },
          server: { mountGmApi: true }, // 将 GM 函数注入为全局变量
        }),
      ],
    });

    导入库

    为了更好的代码提示, 我们选择直接把油猴库保存到本地

    我们这里用了:

    MenuManager:

    https://scriptcat.org/zh-CN/script-show-page/3508

    elmGetter 魔改版 (你也可以用原版, 这完全是习惯问题):

    https://scriptcat.org/zh-CN/script-show-page/2847

    把代码保存到本地

    $ tree -I 'node_modules'
    .
    |-- src
    |   |-- lib
    |   |   |-- elmGetter.js
    |   |   `-- menuManager.js
    |   `-- main.js
    |-- package.json
    |-- pnpm-lock.yaml
    `-- vite.config.js
    
    3 directories, 7 files

    修改为 ES module 导出:

    - var menuManager = (() => {
    
    + export const menuManager = (() => {

    两个都要修改

    开始梭哈

    elmGetter.get 使用起来, 其实就是异步的 querySelector:

    import {elmGetter} from "./lib/elmGetter.js";
    
    async function main() {
        const searchEl = await elmGetter.get(".gLFyf")
    
        searchEl.click();
    }
    
    main()

    然后添加开关菜单栏:

    import {elmGetter} from "./lib/elmGetter.js";
    import {menuManager} from "./lib/menuManager.js";
    
    async function main() {
        menuManager.addAndCreate({
            // 开关状态, 不写 default 的话, 默认为 true
            default : true, 
            on : {
                // 开关 开启状态的提示信息
                name : "自动聚焦状态: 开启✅ (点我关闭)", 
            },
            off : {
                 // 开关 关闭状态的提示信息
                name : "自动聚焦状态: 关闭❎ (点我开启)",
            },
            accessKey: 'E', // 快捷键
            // state 为当前开关状态, isInit 为是否为初始化
            callback(state, isInit){ 
                if (state) {
                    setFocus();
                }
            }
        });
    
    }
    
    main()
    
    async function setFocus() {
        const searchEl = await elmGetter.get(".gLFyf")
    
        searchEl.click();
    }

    如果 callback 要调用 async, 同时要保证这些 async 有序执行, 可以自己实现一个异步队列:

    callback 中, 将 async 函数, addToQueue 就 ok 了

    let queue = [];
    let isProcessing = false;
    
    async function processQueue() {
        if (isProcessing || queue.length === 0) return;
    
        isProcessing = true;
        const task = queue.shift();
    
        await task();
    
        isProcessing = false;
        await processQueue();
    }
    
    function addToQueue(task) {
        queue.push(task);
        processQueue();
    }
    
    export { addToQueue };
    

    尾声

    感谢你的阅读, 如果有任何问题, 欢迎评论区讨论

    开源地址: https://github.com/HHsomeHand/monkey-lib-menu-manager

    欢迎提 issue, 发 PR

  • TA的每日心情
    开心
    2024-11-21 13:37
  • 签到天数: 213 天

    [LV.7]常住居民III

    308

    主题

    4630

    回帖

    4382

    积分

    管理员

    积分
    4382

    管理员荣誉开发者油中2周年生态建设者喜迎中秋油中3周年挑战者 lv2

    发表于 2025-5-28 20:00:30 | 显示全部楼层
    ggnb,感谢哥哥的分享
    上不慕古,下不肖俗。为疏为懒,不敢为狂。为拙为愚,不敢为恶。
    回复

    使用道具 举报

    该用户从未签到

    0

    主题

    1

    回帖

    1

    积分

    助理工程师

    积分
    1
    发表于 2025-5-28 23:05:34 | 显示全部楼层
    王一之 发表于 2025-5-28 20:00
    ggnb,感谢哥哥的分享

    111111111111111111111111111
    回复

    使用道具 举报

    发表回复

    本版积分规则

    快速回复 返回顶部 返回列表