拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Joplin 插件开发实战:基于插件模板与 joplin.ai.chat API 构建“AI 总结当前笔记”示例

Joplin 插件开发实战基于插件模板与 joplin.ai.chat API 构建“AI 总结当前笔记”示例【免费下载链接】joplinJoplin - the privacy-focused note taking app with sync capabilities for Windows, macOS, Linux, Android and iOS.项目地址: https://gitcode.com/GitHub_Trending/jo/joplin本文围绕当前仓库中 ai_chat 示例插件 所附的插件模板 README 展开系统讲解 Joplin 插件工程的结构、构建与框架升级方法同时以该目录下真实可运行的 AI Chat Demo 源码为落地实例深入剖析joplin.ai.chat()插件 API 的调用方式、前置条件与底层实现。读完本文你可以独立从零搭建一个 Joplin 插件工程并基于用户配置好的 AI 提供商在插件中完成一次“读当前笔记 → 请求对话模型 → 回写结果”的完整闭环。一、这份 README 是谁的 README该文档正文是一份标准的 Joplin 插件脚手架说明也是 Joplin 官方插件生成器仓库内对应 generator-joplin在生成插件时会自动附带的那份 README。它在这份仓库中位于packages/app-cli/tests/support/plugins/ai_chat/从目录归属看app-cli包下的tests/support/plugins这份 README 所描述的项目同时是 Joplin 用于测试插件 API 的支撑样例之一同名目录里放着一个名为AI Chat Demo的真实插件其功能是把当前笔记发送给用户配置的 AI 提供商做总结再把结果追加回笔记。因此本文以 README 的模板讲解为主线、以该ai_chat目录内真实源码为辅证既有“工程怎么搭、怎么构建、怎么升级框架”的实操也有“AI 插件 API 到底怎么调、底层发生了什么”的原理拆解。二、模板项目的目录结构两个必须看的主文件README 指出模板项目里最重要、最需要关注的两个文件是文件作用/src/index.ts插件源码入口插件逻辑生命周期、命令、视图都从这里注册/src/manifest.json插件清单声明插件 id、名称、版本等元信息以仓库中这份模板的实际产物为例完整的工程包含src/index.ts —— 入口整个插件的逻辑都写在这里src/manifest.json —— 插件清单package.json —— npm 工程定义与构建脚本webpack.config.js —— 构建配置负责把 TS 编译并打包成.jpl分发文件tsconfig.json —— TypeScript 编译选项api/—— 一份本地化的 Joplin 插件 API 类型声明Joplin.d.ts、JoplinViews*.d.ts、JoplinData.d.ts等供 IDE 与类型检查使用plugin.config.json—— 插件级用户配置例如extraScripts内容脚本列表GENERATOR_DOC.md—— 脚手架附带的生成器补充说明。2.1 入口文件 src/index.ts插件模板要求入口文件调用joplin.plugins.register()完成注册。下面这份真实示例略去了注释与空行展示了最小的骨架注册时传入包含onStart生命周期回调的对象Joplin 在插件启动时会调用它import joplin from api; import { ToolbarButtonLocation } from api/types; joplin.plugins.register({ onStart: async function() { // 在 onStart 里注册命令、创建视图、订阅事件…… }, });2.2 清单文件 src/manifest.json仓库中 AI Chat Demo 的真实清单如下模板各字段一目了然{ manifest_version: 1, id: org.joplinapp.plugins.AiChatDemo, app_min_version: 2.0, version: 1.0.0, name: AI Chat Demo, description: Sends the current note to the configured AI provider and appends the response., author: , homepage_url: , repository_url: , keywords: [] }各字段含义manifest_version插件清单协议版本当前为1id插件全局唯一标识命名上推荐使用反域名形式这里为org.joplinapp.plugins.AiChatDemoapp_min_version能运行该插件的最低 Joplin 版本version插件自身版本号需要与package.json的version保持一致构建时会有校验与提示name/description插件对外展示的名称与描述author/homepage_url/repository_url作者与主页信息若发布到官方插件仓库应如实填写keywords便于插件市场检索的关键词。构建脚本webpack.config.js在读取清单时还会校验categories分类与screenshots截图字段分类名必须全部小写且不得重复截图类型仅允许jpg/jpeg/png/gif/webp且单张不得超过 1MB见 webpack.config.js。三、让模板“活起来”AI Chat Demo 的完整启动流程模板 README 本身只是空壳说明而仓库中这个目录的真正价值在于示例代码。要理解模板“入口到底写什么”最直接的办法就是读一遍 src/index.ts 中注册的完整业务逻辑。它做的事按顺序为注册命令在onStart内通过joplin.commands.register()注册一个名为aiSummariseCurrentNote、显示文本为Summarise current note with AI、图标为fas fa-magic的命令取当前笔记命令执行时调用joplin.workspace.selectedNote()获取正在编辑的笔记未选中或内容为空时用alert提示并提前返回构造消息序列按ChatMessage类型组织一次对话包括一条约束输出格式的system消息与一条承载笔记正文的user消息interface ChatMessage { role: system | user | assistant; content: string; } const messages: ChatMessage[] [ { role: system, content: Summarise the following note in 2–3 sentences. Output only the summary itself — no preamble, no reasoning, no thinking tags, no headings., }, { role: user, content: body }, ];调用 AIawait (joplin as any).ai.chat(messages)把result.text作为摘要展示与回写先用alert展示摘要确认“请求—响应”链路可用随后用joplin.data.put([notes, note.id], null, { body: newBody })把原文 --- 分隔线 **AI summary:** 摘要写回笔记创建工具栏按钮通过joplin.views.toolbarButtons.create(aiSummariseCurrentNote, aiSummariseCurrentNote, ToolbarButtonLocation.EditorToolbar)在编辑器工具栏生成入口从而把“点击按钮 → 执行命令 → 触发 AI 总结”串起来。其中对每一步的失败未选中笔记、正文为空、AI 调用异常、回写失败都做了独立的try/catch与用户提示这套“先取数据 → 校验 → 调用 → 提示 → 持久化”的写法正是值得在自研插件里复刻的错误处理范式。四、构建插件npm run dist到底做了什么README 明确插件使用 Webpack 构建编译产物放在/dist同时会生成一个可分发安装的JPL 归档Joplin Plugin Archive.jpl文件。构建命令只有一个npm run dist对应 package.json 中的脚本scripts: { dist: webpack --env joplin-plugin-configbuildMain webpack --env joplin-plugin-configbuildExtraScripts webpack --env joplin-plugin-configcreateArchive, prepare: npm run dist, updateVersion: webpack --env joplin-plugin-configupdateVersion, update: npm install -g generator-joplin yo joplin --node-package-manager npm --update --force }一次dist实际是按顺序连续执行三趟 Webpack因为各步之间存在依赖只能串行对应 webpack.config.js 里由--joplin-plugin-config参数选择的三类配置配置阶段工作内容buildMain编译主入口./src/index.ts到dist/index.js并通过copy-webpack-plugin把src/下其余资源CSS、图片、无需编译的 JS 等排除*.ts/*.tsx复制进dist/此阶段会先清理并重建dist/与publish/目录buildExtraScripts按plugin.config.json里的extraScripts逐个编译附加脚本这些内容脚本需要时可通过joplin.require(...)引用若干白名单内的 CodeMirror 系第三方库见 webpack.config.js。没有额外脚本时直接跳过createArchive以dist/index.js为占位入口跑一趟打包器在compiler.done钩子onBuildCompleted里删除临时产物、把dist/全部文件打成.jpl并额外生成一份带发布信息的.json注意一个细节README 中写的归档位置是“root根目录”而仓库里这份脚手架的 webpack 配置实际把产物输出到了publish/目录——归档路径由pluginArchiveFilePath publishDir/${manifest.id}.jpl决定见 webpack.config.js也就是说会生成publish/org.joplinapp.plugins.AiChatDemo.jpl # 可分发安装的插件归档 publish/org.joplinapp.plugins.AiChatDemo.json # 含发布信息的描述文件其中.json会附带两处发布佐证信息_publish_hash.jpl的sha256:...摘要与_publish_commit当前git branch:commit方便官方插件仓库做完整性校验与溯源见 webpack.config.js。.jpl就是最终可交给用户、或在 Joplin 插件管理界面中“从文件安装”的分发物关于插件的安装与使用方式可进一步参考 readme/apps/plugins.md。构建层面还有几条由代码可确认的工程约束Webpack 面向 Node 目标baseConfig里target: node、mode: production同时对 Node 内置模块显式设置fallback: false因为插件运行在 Electron 的 Node 环境里无需 polyfill见 webpack.config.jsTypeScript 优先、兼容纯 JSts-loader编译.ts/.tsxresolve.extensions覆盖js/tsx/ts/json且 tsconfig.json 中allowJs: true——这正是 README 所说的“默认 TypeScript但也可以改成纯 JavaScript”的配置基础发布前自检归档生成后会校验 npm 包名必须以joplin-plugin-开头、keywords必须包含joplin-plugin并提醒用prepare脚本取代postinstall见 webpack.config.js。五、升级框架npm run update的合并策略与注意事项模板 README 强调插件框架会持续演进升级框架统一使用npm run update对应脚本内部执行的是npm install -g generator-joplin yo joplin --node-package-manager npm --update --force即调用全局安装的 generator-joplin 生成器以“重跑脚手架”的方式把框架部分刷到最新。README 给出了该命令的几条行为准则这些准则决定了你平时该如何管理自己的插件源码聪明地合并而非粗暴覆盖package.json、.gitignore这类文件会执行合并把你已有的改动依赖、忽略规则保留下来而不是被模板整份覆盖源码目录不动/src目录与README.md会被原样保留你的业务逻辑不会被更新流程破坏webpack.config.js会被覆盖且最易出问题因为构建配置随框架版本走升级时会整份替换。如果确实需要改它README 的建议是——把自定义逻辑写进一个独立的 JavaScript 文件然后在webpack.config.js里通过require引入。这样升级后你只需恢复那一行引入语句即可找回全部自定义内容。这一建议在仓库源码中有直接印证该脚手架的 webpack.config.js 顶部注释同样声明“不建议编辑本文件因为它会在升级框架时被覆盖如需修改请用外部 JS 文件并在此引入”。此外脚本里还提供了一条配套命令用于发版前的版本号管理npm run updateVersion它会读取package.json与manifest.json把两者的版本号末位1后同步写入见 webpack.config.js并在两处版本号不一致时打印告警避免“npm 版本已升、清单版本没升”这类发布事故。六、joplin.ai.chat()底层原理与调用约束AI Chat Demo 的核心是插件 APIjoplin.ai.chat()。模板 README 虽未展开这一层但仓库内的插件 API 实现把它讲得很透彻。6.1 调用链与返回结构在插件运行时的类型定义上Joplin主对象把 AI 能力封装为一个独立子模块this.ai_ new JoplinAi()见 Joplin.ts。而 JoplinAi.ts 中chat()的实现非常薄——它把插件传来的消息统一映射为system/user/assistant三种角色然后委托给核心层单例AiService.instance().chat(...)最终只向插件返回带text字段的结果对象public async chat(messages: ChatMessage[], options?: ChatOptions): PromiseChatResult { const result await AiService.instance().chat( messages.map(message ({ role: message.role as ChatRole.System | ChatRole.User | ChatRole.Assistant, content: message.content, })), options, ); return { text: result.text }; }返回对象刻意保持最小且稳定{ text: string }这样后续若追加 token 用量、结束原因等字段也不会破坏存量插件。真正的模型路由、密钥管理与网络请求都由核心层packages/lib/services/ai/下的服务AiService、各providers/*完成插件侧不需要关心用的是哪家提供商。6.2 前置条件用户设置说了算从 JoplinAi.ts 的类级注释可以确认joplin.ai的可用性完全由用户的设置决定插件不选模型、不选提供商AI 功能默认关闭用户必须先在Settings → AI里打开总开关若当前激活的提供商是远程提供商云端托管用户还需单独勾选 “Allow remote AI providers”否则插件调用会被拒绝若用户已登录 Joplin CloudAI 可在零配置下工作——只需打开总开关即可该能力当前标注为desktop桌面端专属。6.3 可能的抛错与处理建议chat()在以下场景会直接抛异常API 文档逐条列出AI 功能被关闭 →AI features are disabled激活的是远程提供商但用户未授权 →Remote AI access is not allowed提供商配置不完整缺 API key、缺模型名等→*provider* has no API key configured提供商返回 HTTP 错误 → 消息中会带上状态码与提供商返回的明细。因此官方 API 文档建议插件必须catch这些错误并把用户引导回 Joplin 设置界面。AI Chat Demo 正是这么做的——它对chat()单独包了try/catch失败时弹出AI call failed: ${error.message}见 src/index.ts。6.4 一处关键陷阱必须“单链调用”示例源码中有段极有信息量的注释见 src/index.ts插件的沙箱代理会在每次属性访问时变更内部状态因此必须从joplin出发、在一条链上取到.chat并立刻调用若先把joplin.ai或joplin.ai.chat存进变量、稍后再调用会破坏代理的路径跟踪。也就是说正确的写法只能是const result await (joplin as any).ai.chat(messages);而不能是先const ai joplin.ai;再await ai.chat(...)。这是 Joplin 插件沙箱用 Proxy 做 API 路径隔离引入的特有约束也是编写 AI 类插件最容易被忽略的坑。同目录的同族示例插件ai_search也在其源码注释中引用了这一约定见 ai_search/src/index.ts说明这是一条跨插件的通用经验。另外可以看到示例里调用时使用了(joplin as any)断言说明joplin.ai属于较新的插件 API本地脚手架的api/*.d.ts类型声明尚未覆盖它。6.5joplin.ai上还有哪些能力虽然 AI Chat Demo 只用到了chat()但同一JoplinAi类还向插件暴露了另三个方法见 JoplinAi.tssearch(options)对本地构建的向量索引做语义搜索query可为纯文本或{ noteId }scope支持all/note/folder/tagrelevance提供strict/normal/loose三档预设getEmbeddings(options)分页返回原始 embedding 向量用于自定义聚类、降维等基于不透明游标翻页getIndexStatus()返回本地索引状态便于实现“索引就绪走语义搜索、否则降级”的混合管线。七、小结从模板到可运行插件的完整路径把本文要点串起来就是一条完整的 Joplin 插件生产路径认识工程src/index.ts入口与src/manifest.json清单是插件最核心的两个文件模板目录还附带api/类型声明与 webpack 构建链写业务在onStart里用joplin.commands.register()注册命令、用joplin.views.toolbarButtons.create()挂界面入口业务实现尽量按“校验 → 调用 → 独立异常处理 → 持久化”组织——仓库内 ai_chat 示例的完整源码 是可直接对照的最佳样本构建分发npm run dist串行执行三阶段 webpack编译主入口 → 编译附加脚本 → 打包归档产出dist/与publish/*.jplnpm run updateVersion同步递增两处版本号升级维护npm run update会合并package.json/.gitignore、保留/src与README.md唯一会被覆盖的webpack.config.js应通过“外置 JS 文件 单行 require”的方式保护自定义逻辑调用 AIjoplin.ai.chat()是对核心层AiService的插件侧封装结果收敛为{ text }运行前须满足“AI 总开关已开、远程提供商已获授权”等用户侧前提调用时必须整链一气呵成并务必捕获异常向用户给出可操作提示。若需要基于该模板从零生成自己的工程可以直接使用仓库内置的 generator-joplin对用户侧而非插件开发者侧如何使用 AI 对话功能可进一步阅读 readme/apps/ai_chat.md。结合模板、示例源码与packages/lib中的插件 API 实现三份材料足以支撑你把这类插件快速复用到“摘要、翻译、改写、问答”等各种场景。【免费下载链接】joplinJoplin - the privacy-focused note taking app with sync capabilities for Windows, macOS, Linux, Android and iOS.项目地址: https://gitcode.com/GitHub_Trending/jo/joplin创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门