Joplin插件开发教程
Joplin 是一个 Markdown 笔记工具,相比于 Obsidian 而言,Joplin 是通过关系型数据库存储所有的笔记、信息等,而 Obsidian 是基于文件的。
技术要求
- 会写代码,会开发
- 能够读懂英语,需要阅读官方 API 文档
- 熟悉 JavaScript 的使用:但不是必须得,使用过其他相似的语言也可以。在开发 Enhancement 插件 之前,我也不太熟悉 JS
- 熟悉 Typescript 的使用:同上,不是必须的
- 略懂 React、css:同上,不是必须的,仅在开发有 UI 类的插件需要。甚至可以不使用 React 开发,只是能让你的页面代码整洁不少
插件功能分类
在插件的最终作用对象上,可以大致分为以下四类。一般情况下,需要多个类型的功能一起实现才能够提供良好的用户使用体验。
- Markdown 编辑器插件:比如编辑器中的自动补全相关的插件。任何和编辑器相关的功能,都通过该类实现。包括但不限于:
- 自动补全:比如 NoteLinkSystem 插件通过
@@
即可触发自动补全,实现对其他笔记链接的快速插入。 - 编辑器的实时渲染:比如 Enhancement 插件。可以将 Mermaid, Math 等 Markdown 格式的代码直接显示出渲染后的结果。当实时渲染足够强大时,甚至可以不再需要额外的 PDF 预览窗口。
- 自定义样式:和 Joplin 提供的自定义样式文件
userchrome.css
类似,但是通过代码级插件可以实现自定义元素的 class 类型,再配合 css 样式,即可实现基本对任意元素自定义样式。比如代码高亮、不同的 Markdown 主题、搜索关键词高亮等等。 - 对内容的修改:一般与快捷键、工具栏等配合实现对内容的自动修改。比如高亮、加背景色等等。
- …
- 自动补全:比如 NoteLinkSystem 插件通过
- Markdown 渲染插件:比如 Admonition 插件。该部分负责修改 Markdown 文本转为 HTML 的过程。包括但不限于:
- 自定义 Markdown 语法渲染:比如 Admonition 插件能够将
!!!note
渲染成更醒目的格式。 - 修改现有 Markdown 渲染结果:比如 Enhancement 插件能够将
![title]()
渲染为带有图标题的结果。
- 自定义 Markdown 语法渲染:比如 Admonition 插件能够将
- 带有独立 Panel 的信息收集、展示、交互等:比如 NoteLink 插件。
- Joplin 数据库相关插件:比如 BackUp 插件。修改笔记的文件夹路径、标题,新建/删除笔记等等。
插件开发初始化
初始化工具
Joplin 提供了插件开发开始时的初始化工具 generaor-joplin
。具体细节请参考官方网站:Getting started with plugin development。
- 安装
Node.js
npm install -g yo generator-joplin
- 在任意文件夹内,执行
yo joplin
完成初始化操作。
文件结构
里面有几个比较关键的文件:
plugin.config.json
:里面的extraScripts
用于存放需要转换成*.js
的*.ts
以及*.tsx
文件路径。**所有的路径都是相对于./src
目录而言!- 插件开发时,Joplin 的插件接口有时候需要直接提供
*.js
文件路径,但是一般情况下都是用*.ts
或者*.tsx
实现的,这种情况下就需要在这个文件中配置一下,不然编译时无法找到对应的*.js
文件。其它*.ts
文件之间相互引用的情况则无需在这个文件中配置。
- 插件开发时,Joplin 的插件接口有时候需要直接提供
./src/index.ts
:插件的入口,初始化后一般包含以下格式,只替换中间的部分即可。./api
:这个是包含在初始文件中的、配合 Joplin 插件接口、函数、类等,最好不要修改。
1
2
3
4
5
joplin.plugins.register({
onStart: async function() {
// 从这里开始写你的插件逻辑
}
})
注册插件
1
2
3
4
5
await joplin.contentScripts.register(
ContentScriptType.MarkdownItPlugin, // 或者是 ContentScriptType.CodeMirrorPlugin, 分别对应 markdown 渲染插件以及 markdown 编辑器插件
'唯一的插件ID',
'./插件的路径/index.js' // 这里的文件如果是用 ts 写的,那么需要再 plugin.config.json 文件中配置
);
更细致的内容请参考:joplin开发文档
Markdown 编辑器插件开发
截止到目前,Joplin 桌面端 的 Markdown 编辑器是基于 Codemirror 5 开发的,目前还没有听到要切换到 Codemirror 6 的消息(Obsidian 等用的就是 v6,v6 相较于 v5 而言新增了一些功能,能够更方便的创建 decoration 等,实现 live preview 功能)。安卓端的 Joplin 使用的则是 v6 版本,但是目前安卓版不支持插件,因此这里仅讨论 v5 版本下的插件开发。
index.ts
文件的主体内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
module.exports = {
default: function(context) {
return {
plugin: function(CodeMirror) {
// 插件逻辑部分
// 这里是通过 defineOption 方式实现 cm 插件
CodeMirror.defineOption('option的唯一ID', false, async function(cm, val, old) {
// xxx
};
// 与 defineOption 不同,这里更像是给 codemirror 对象新增了一个函数,defineExtension 之后,可以用 cm.extensionID(paras) 的方式调用了
// 示例见:https://github.com/CalebJohn/joplin-rich-markdown/blob/434520a5ba3ae4b345fbc88d2679969fcf910adf/src/richMarkdown.ts#L37
// 与此同时,也可以通过 Joplin 的 command 进行调用,见 https://github.com/SeptemberHX/joplin-plugin-enhancement/blob/master/src/driver/codemirror/commands/index.ts 及 https://github.com/SeptemberHX/joplin-plugin-enhancement/blob/dec1ba2055270c791bacc6df70011f75afa55879/src/index.ts#L86
CodeMirror.defineExtension('extension的唯一ID', function(paras) {
// xxx
});
},
codeMirrorResources: [
// cm5 自带的 addons,cm5 本身就自带了不少的插件,比如搜索高亮等
// 具体参考:https://codemirror.net/5/doc/manual.html#addons
// 下面的例子对应 mode/overlay.js 以及 hint/show-hint.js
'addon/mode/overlay',
'addon/hint/show-hint'
],
codeMirrorOptions: {
// 必须在这里进行配置开启,否则无法生效
'对应上面 option的唯一ID': true,
},
assets: {
// 配置 css 样式文件。Joplin 中 cm5 不允许携带 *.js 脚本
},
}
}
}
由于 Joplin 设计上不允许插件进程直接与界面交互,需要通过消息通信实现,codemirror 也是如此。见:示例
剩下的 codemirror 开发相关内容参见官方文档,这里给出几个例子:
- 通过 command 从插件进程获取 codemirror 数据
- 为多行的特定格式内容分配特定 class,配合 css 实现丰富的渲染效果(如 Admonition)
- 单行的特定格式分配特定 class,配合 css 实现丰富的渲染效果(如
==高亮==
) - 将特定格式内容渲染成其他内容,如数学公式
$latex$
- 选中文字后悬浮的工具栏
- 编辑过程中动态校验列表编号并自动修正
Markdown 渲染插件开发
该类插件加载时,插件类型应设定为 ContentScriptType.MarkdownItPlugin
,对应的插件 Index.ts 为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
export default function (context) {
return {
plugin: function (markdownIt, _options) {
const pluginId = context.pluginId;
xxx(markdownIt, _options);
},
assets: function() {
return [
{ name: 'xxx.css' }
];
},
}
}
上一小节是编辑器开发,是 Joplin 左侧的编辑部分,而渲染插件则是针对 markdown 渲染成 PDF 过程的,通过对 markdown 渲染规则/方法的修改、覆写、新增等操作,可以自由实现自己想要达到的效果,例如将 - [ ] xxxx +tag1
中的 +tag1
渲染上背景色等。
Joplin 用的是 markdown-it 进行 markdown 渲染,所以其实就是 markdown-it 相关插件的开发。我并没有详细了解过 markdown-it 的工作原理,猜测应该是先由 md.block.ruler
对文本进行语法解析,将原始文本按照 markdown 的规则划分为多类 token,然后再由 markdownIt.renderer.rules
对标记后的 token 进行渲染,完成从文本到 HTML 的转换。该过程仅仅是猜测,欢迎指正。
目前我用到的就两种:
- 针对
md.block.ruler
,实现自定义的 token 识别,如 Front Matter 的 token 识别 - 针对
markdownIt.renderer.rules
,实现对现有 markdown 元素渲染过程的修改,例如:伪代码块渲染
独立 Panel 插件开发
数据库相关开发
注意事项
第三方库的使用
由于 Joplin 需要跨平台,它自带了一套自己的 fs
以及 sqlite3
库,需要通过以下方式引入包,不能直接 require
:
1
2
const fs = joplin.require('fs-extra')
const sqlite3 = joplin.require('sqlite3')
这进一步导致所有依赖了 fs
以及 sqlite3
的库都无法正常使用!