首页 > 其他分享 >custom:用户自定义插件,提供开放能力

custom:用户自定义插件,提供开放能力

时间:2024-05-28 23:37:07浏览次数:30  
标签:插件 自定义 plugin custom 右键 hotkey config

custom 插件的功能:支持用户 在右键菜单中自定义插件

简介

custom 插件大量采用声明式代码(声明代替代码开发),比如:

  • 只需使用 style = () => "...",即可注册 css。
  • 只需使用 styleTemplate = () => ({renderArg}),即可引入 css 文件,并且支持渲染模板。
  • 只需使用 html = () => "...",即可注册 HTML 元素。
  • 只需使用 hint = () => "将当前标题的路径复制到剪切板",即可注册 hint。
  • 只需使用 selector = () => "...",即可注册允许运行命令的光标位置。
  • 只需使用 hotkey = () => ["ctrl+shift+y"] ,即可注册快捷键。
  • 只需使用 this.modal 函数即可自动生成自定义的模态框。
  • init、html、process、callback 等等生命周期函数。
class fullPathCopy extends BaseCustomPlugin {
    style = () => "..."
    html = () => "..."
    hint = () => "将当前标题的路径复制到剪切板"
    hotkey = () => ["ctrl+shift+y"]
    callback = anchorNode => {
        const modal = {
            title: "这是模态框标题",
            components: [
                { label: "input的label", type: "input", value: "input的默认value", placeholder: "nput的placeholder" },
                // password、textarea、checkbox、radio、select
                ...
            ]
        };
        const callback = response => console.log(response)
        this.modal(modal, callback)
    }
}

如何使用

仅需两步:

  1. ./plugin/custom/custom_plugin.user.toml 添加配置。
  2. ./plugin/custom/plugins 目录下,创建和插件同名的 js 文件,在此文件中创建一个 class 继承自 BaseCustomPlugin,并导出为 plugin

示例一:快速开始

您可以根据下面的步骤,先把插件跑起来。

步骤一:在 ./plugin/custom/custom_plugin.user.toml 添加如下配置:

[helloWorld]
name = "你好世界"   # 右键菜单中展示的名称
enable = true     # 是否启用此二级插件
hide = false      # 是否在右键菜单中隐藏
order = 1         # 在右键菜单中的出现顺序(越大越排到后面,允许负数)

	[helloWorld.config]
	hotkey_string = "ctrl+alt+u"
	console_message = "i am in process"
	show_message = "this is hello world plugin"

如果您对 TOML 不太了解,可以花三分钟了解 TOML 教程

步骤二:创建文件 ./plugin/custom/plugins/helloWorld.js 文件,将下面代码保存到该文件中:

// ./plugin/custom/plugins/helloWorld.js

class helloWorld extends BaseCustomPlugin {
    hint = () => "this is hello world hint"

    hotkey = () => [this.config.hotkey_string]

    // process方法会在插件初始化时自动运行
    process = () => {
        console.log(this.config.console_message);
        console.log("[helloWorldPlugin]:", this);
    }

    // callback方法会在插件被调用时运行,参数anchorNode:调用此插件时,鼠标光标所在的Element
    callback = anchorNode => {
        alert(this.config.show_message);
    }
}

module.exports = { plugin: helloWorld };

步骤三:验证,完成以上步骤后,重启 Typora。

  1. 打开【检查元素】,发现控制台输出了 i am in process 和 插件对象。你可以仔细看看 this 自带的属性和方法。
  2. 鼠标右键,弹出菜单,鼠标悬停在 常用插件 -> 二级插件 -> 你好世界。发现出现了 hint,显示 this is hello world hint。点击 你好世界,发现弹出了提示框,显示 this is hello world plugin
  3. 键入快捷键 ctrl+alt+u,发现弹出了同样的提示框。

示例二:简易功能

此示例来自 此 issue,简易实现了一个轻量化的需求,旨在让用户了解如何实现一个插件。

需求:md 文档中有几百行的复选框(任务列表)内容,一个个点选中或取消,效率太低了。希望开发全选、反选复选框功能的插件。

步骤一:打开文件 plugin\global\settings\custom_plugin.user.toml,添加下面内容:

[selectCheckboxes]
name = "反选复选框"       # 右键菜单中展示的名称
enable = true           # 是否启用此二级插件
hide = false            # 是否在右键菜单中隐藏
order = 1               # 在右键菜单中的出现顺序(越大越排到后面,允许负数)

    [selectCheckboxes.config]
    select_all_hotkey = "ctrl+alt+h"      # 全选快捷键
    select_none_hotkey = "ctrl+alt+j"     # 取消全部选择快捷键
    select_reverse_hotkey = "ctrl+alt+k"  # 反选快捷键

步骤二:打开目录 plugin\custom\plugins,在此目录下创建文件 selectCheckboxes.js,写入如下内容:

class selectCheckboxes extends BaseCustomPlugin {
    rangeTaskListItem = fn => document.querySelectorAll('.md-task-list-item input[type="checkbox"]').forEach(fn)

    selectAll = input => !input.checked && input.click()
    selectNone = input => input.checked && input.click()
    selectReverse = input => input.click()

    process = () => {
        const { select_all_hotkey, select_none_hotkey, select_reverse_hotkey } = this.config;
        this.utils.registerHotkey([
            { hotkey: select_all_hotkey, callback: () => this.rangeTaskListItem(this.selectAll) },
            { hotkey: select_none_hotkey, callback: () => this.rangeTaskListItem(this.selectNone) },
            { hotkey: select_reverse_hotkey, callback: () => this.rangeTaskListItem(this.selectReverse) },
        ])
    }

    callback = () => this.rangeTaskListItem(this.selectReverse)
}

module.exports = { plugin: selectCheckboxes };

步骤三:重启 Typora,创建一个带有任务列表的 md 文件,尝试以下操作:

示例三:实战

需求如下:

  1. 在右键菜单中添加一个 获取标题路径 (类似于 messing.md\无 一级标题\开放平台 二级标题\window_tab 三级标题),然后将其写入剪切板。
  2. 当光标位于【正文标题】中才可使用。
  3. 快捷键 ctrl+shift+u

实现:

步骤一:修改 ./plugin/global/settings/custom_plugin.user.toml,添加配置:

  • name:(必选)右键菜单中展示的名称
  • enable:(必选)是否启用此插件
  • hide:(可选)是否在右键菜单中隐藏,默认为 false
  • order:(可选)在右键菜单中的出现顺序(越大越排到后面,允许负数),默认为 1
  • config:(可选)插件自己的配置,这里的内容将被封装为 插件类的 this.config 属性
# ./plugin/global/settings/custom_plugin.user.toml

[myFullPathCopy]
name = "我的复制标题路径插件"
enable = true
hide = false
order = 1

    # 插件配置
    [myFullPathCopy.config]
    # 快捷键
    hotkey = "ctrl+shift+u"
    # 如果在空白页调用此插件,使用的文件名(因为尚不存在该文件,需要用一个默认的文件名代替)
    untitled_file_name = "untitled"
    # 跳过空白的标题
    ignore_empty_header = false
    # 标题和提示之前添加空格
    add_space = true
    # 使用绝对路径
    full_file_path = false

步骤二:在 ./plugin/custom/plugins 目录下,创建和插件同名的 js 文件(myFullPathCopy.js),在此文件中创建一个 class 继承自 BaseCustomPlugin,并导出为 plugin

// ./plugin/custom/plugins/myFullPathCopy.js

// 1
class myFullPathCopy extends BaseCustomPlugin {
    // 2
    selector = () => "#write h1, h2, h3, h4, h5, h6"
    // 3
    hint = () => "将当前标题的路径复制到剪切板"
    // 4
    init = () => {}
    // 5
    style = () => {}
    // 6
    styleTemplate = () => {}
    // 7
    html = () => {}
    // 8
    hotkey = () => [this.config.hotkey]
    // 9
    beforeProcess = async () => {}
    // 10
    process = () => {}
    // 11
    callback = anchorNode => {
        const paragraphList = ["H1", "H2", "H3", "H4", "H5", "H6"];
        const nameList = ["一级标题", "二级标题", "三级标题", "四级标题", "五级标题", "六级标题"];
        const pList = [];
        let ele = anchorNode;

        while (ele) {
            const idx = paragraphList.indexOf(ele.tagName);
            if (idx !== -1) {
                if (pList.length === 0 || (pList[pList.length - 1].idx > idx)) {
                    pList.push({ele, idx})
                    if (pList[pList.length - 1].idx === 0) break;
                }
            }
            ele = ele.previousElementSibling;
        }

        pList.reverse();

        let filePath = (this.config.full_file_path) ? this.utils.getFilePath() : File.getFileName();
        filePath = filePath || this.config.untitled_file_name;
        const result = [filePath];
        let headerIdx = 0;
        for (const p of pList) {
            while (headerIdx < 6 && p.ele.tagName !== paragraphList[headerIdx]) {
                if (!this.config.ignore_empty_header) {
                    const name = this.getHeaderName("无", nameList[headerIdx]);
                    result.push(name);
                }
                headerIdx++;
            }

            if (p.ele.tagName === paragraphList[headerIdx]) {
                const name = this.getHeaderName(p.ele.querySelector("span").textContent, nameList[headerIdx]);
                result.push(name);
                headerIdx++;
            }
        }

        const text = this.utils.Package.Path.join(...result);
        navigator.clipboard.writeText(text);
    }

    getHeaderName = (title, name) => {
        const space = this.config.add_space ? " " : "";
        return title + space + name
    }
}

// 12
module.exports = { plugin: myFullPathCopy };

// 1. 创建 class,继承 BaseCustomPlugin 类。之后 myFullPathCopy 将自动拥有 utils、info、config 属性。
//    - utils:插件系统自带的静态工具类,其定义在 `./plugin/global/core/plugin.js/utils`。其中有三个重要的函数:utils.getPlugin(fixedName) 和 utils.getCustomPlugin(fixedName) 用于获取已经实现的全部插件,调用其 API,具体的 API 可看 ./plugin/custom/openPlatformAPI.md 文件。utils.addEventListener(eventType, listener) 用于监听 Typora 的生命周期事件。
//    - info:该插件在 custom_plugin.user.toml 里的所有字段
//    - config:等同于 info.config,即配置文件里的 config 属性
// 2. selector:允许运行命令的光标位置:当光标位于哪些位置时,此命令才可用。返回 null-like value 表示任何位置都可用,在这里的含义就是:只当光标位于【正文标题】时可用
// 3. hint:当鼠标移动到右键菜单时的提示
// 4. init:在这里初始化你要的变量
// 5. style:给 Typora 插入 style 标签。返回值为 string。若你想指定标签的 id,也可以返回 {textID: "", text: ""}。其中 textID 为此 style 标签的 id,text 为 style 内容。
// 6. styleTemplate: 引入 `./plugin/global/user_styles` 目录下和插件同名的 css 文件。详情请参考`user_styles/请读我.md`
// 7. html:为 Typora 插入 HTML 标签,返回 Element 类型或者 Element-string。
// 8. hotkey:为 callabck 注册快捷键,返回 Array<string> 类型。
// 9. beforeProcess:最先执行的函数,在这里初始化插件需要的数据。若返回 this.utils.stopLoadPluginError,则停止加载插件
// 10. process:在这里添加添加插件业务逻辑,比如添加 listener 和修改 Typora 的第一方函数
// 11. callback:右键菜单中点击/键入快捷键后的回调函数。anchorNode 参数: 鼠标光标所在的 Element
// 12. export:导出名为 plugin

验证:

打开 Typora,将光标置于正文标题出,右键弹出菜单,常用插件 -> 二级插件 -> 复制标题路径,点击。当前的标题路径就写入剪切板了。在目标文档区域,粘贴,即可把标题路径复制到相应文档区域。

示例四:参考

如果您有心自己写插件,可以参考 noImageMode 插件,仅有 30+ 行代码。

./plugin/custom/plugins/noImageMode.js

标签:插件,自定义,plugin,custom,右键,hotkey,config
From: https://www.cnblogs.com/bug-designer-cn/p/18219211

相关文章

  • 第20讲:自定义类型:联合和枚举
    目录1.联合体1.1联合体类型的声明1.2联合体的特点1.3相同成员的结构体和联合体对比1.4联合体的大小1.5联合的一个练习2.枚举类型2.1枚举类型的声明2.3枚举类型的使用1.联合体1.1联合体类型的声明像结构体⼀样,联合体也是由⼀个或者多个成员构成,这些成员......
  • 自动上传文章插件
    由于CSDN是直接逆向的接口,上传文章大概是2-3s,两个selenium自动化的平台上传时间长一点,20s左右原因​ 有谁跟我一样,每次写完博客想发布时都得重新登一次wp(因为我隐藏了wp登录的url,防爆破的),wp登录又奇慢无比,手动把标题和文章粘贴过去,设置标签、分类,封面图,慢慢的,5分钟-10分钟就过......
  • 在Spring中自定义事件及发布与监听
    在Spring框架中,自定义事件及其发布与监听是一个涉及Spring事件机制的过程。Spring提供了一个基于观察者模式的事件发布和监听机制,允许在Spring容器中的组件之间进行松耦合的通信。以下是如何自定义事件以及如何发布和监听这些事件的步骤:1.创建自定义事件类首先,需要定义一......
  • 【软件插件】VRay6.2渲染器安装教程(支持SketchUp草图大师2019-2024版本)
    下载链接:https://docs.qq.com/doc/DREh5ak5aQWJTdWdU详细图文教程:https://www.yuque.com/zhefengerhuanzaigua/bld6x5/cul5nfqrf7rw40lv软件介绍VRay是业界最受欢迎的渲染引擎。基于V-Ray内核开发的有VRayfor3dsmax、Maya、Sketchup、Rhino、CINEMA4D等诸多版本,为不同领......
  • 容器组件Tabs如何自定义 tabBar-高亮切换
    1.TabBar如果放在底部的话,一般会显示图形和文字,甚至有特殊的图标,如果要实现此类效果,就需要自定义tabBarTabs(){TabContent(){//内容略}.tabBar(this.tabBarBuilder())}@BuildertabBarBuilder(){//自定义的Tabbar结构}2.自定义TabBa......
  • 解决VScode下载插件十分缓慢的问题
    解决VScode下载插件十分缓慢的问题问题的发现之前由于我对vscode的配置有些问题,导致无法用vscode完成作业,放弃了一段时间的vscode然后最近重拾vscode的时候发现下载插件的时候十分缓慢一直会卡在这样的界面问题的着手解决之后我在浏览器开始搜索的时候,大多数都是没有意义的......
  • Visual Studio 智能代码插件:CodeGeeX
    前言在软件开发领域,高效的编程助手一直是提升开发者效率和质量的关键。随着人工智能技术的不断发展,智能编程助手逐渐成为开发者们不可或缺的工具。其中,CodeGeeX作为一款专为VisualStudio设计的免费智能编程助手,凭借其强大的功能和便捷的使用体验,赢得了广大开发者的青睐。Co......
  • 浅析SpringBoot中的AOP以及自定义注解类
    概念说明SpringBoot中的AOP(面向切面编程)是一种编程范式,它允许开发者定义跨多个对象的横切关注点。横切关注点是与业务逻辑无关的功能,如日志记录、安全检查、事务管理等。AOP的主要目的是将横切关注点与业务逻辑分离,提高代码的模块化和可维护性。AOP的核心概念包括:切面(As......
  • 自定义一个简单的日历
    前言  此博客提供一个个人实现的自定义View,日历的内容全部是通过绘制实现的。 虽然是使用flutter实现自定义日历View的,但是关键核心思想是一致的,这边放到博客中提供给各位参考。后续有时间会继续提供Android版本的自定义日历.效果图代码最关键的是绘制日历内容的4个函数......
  • 自动上传文章插件
    由于CSDN是直接逆向的接口,上传文章大概是2-3s,两个selenium自动化的平台上传时间长一点,20s左右原因​ 有谁跟我一样,每次写完博客想发布时都得重新登一次wp(因为我隐藏了wp登录的url,防爆破的),wp登录又奇慢无比,手动把标题和文章粘贴过去,设置标签、分类,封面图,慢慢的,5分钟-10分钟就过......