Super Productivity 插件开发指南:从零构建、打包、发布到 i18n 国际化
【免费下载链接】super-productivitySuper Productivity is an advanced todo list app with integrated Timeboxing and time tracking capabilities. It also comes with integrations for Jira, GitLab, GitHub and Open Project.项目地址: https://gitcode.com/GitHub_Trending/su/super-productivity
本指南以packages/plugin-dev/README.md为主体,结合仓库内plugin-api类型定义、vite-plugin构建工具、示例插件与源码实现,系统讲解如何在 Super Productivity(高级待办清单 + 时间追踪应用)中开发插件:从脚手架搭建、PluginAPI能力矩阵、生命周期 Hooks,到plugin.zip打包、大小限制、GitHub Actions 发布,以及基于i18n/目录的完整国际化方案。读完你将能够独立创建一个可安装、可分发、支持多语言的 Super Productivity 插件。
目录
- 插件开发环境与工作区
- 快速上手:三种起步方式
- 插件项目结构与构建产物
- PluginAPI:插件能力全景
- 生命周期 Hooks:订阅应用事件
- 清单文件 manifest.json 详解
- 构建与打包:从源码到 plugin.zip
- 发布插件:GitHub Release 与 npm
- 本地测试与调试
- TypeScript 开发与类型安全
- 插件国际化(i18n)完整指南
- 最佳实践与疑难排查
- 仓库内示例插件一览
插件开发环境与工作区
Super Productivity 的插件开发工作区位于仓库的packages/plugin-dev/目录,其package.json将@super-productivity/plugin-api作为本地依赖,并通过 npm workspaces 把example-plugin、simple-typescript-plugin、procrastination-buster等示例纳入统一管理。
前置条件:
- Node.js 18 或更高版本
- npm 或 yarn
- TypeScript 基础(推荐,非必需)
工作区统一命令(在packages/plugin-dev/下执行):
# 构建所有插件 npm run build # 为所有插件安装依赖 npm run install:all # 清理构建产物(删除所有插件的 dist 目录) npm run clean:dist # 列出当前可用的插件目录 npm run listlist命令在 package.json 中的实现为ls -d */ | grep -v node_modules | grep -v scripts,可以快速查看仓库中已有哪些插件可作为参考。开发期常用的模式是在某个具体插件目录内单独执行npm install、npm run build,而不是每次都全量构建。
快速上手:三种起步方式
QUICK_START.md 为开发者提供了三种渐进式起步方案,按复杂度递增:
方案一:纯 JavaScript(最简单)
cd minimal-plugin # 编辑 plugin.js # 打成 zip 后上传即可- 优点:无构建步骤,改完即生效,反馈最快;
- 缺点:没有 TypeScript 类型检查和打包能力。
方案二:简易 TypeScript(推荐入门)
cd simple-typescript-plugin npm install npm run build # 构建产物 plugin.zip 位于 dist/- 优点:具备 TypeScript 类型支持,构建流程简单;
- 缺点:仅适合单文件插件。
方案三:完整 TypeScript + Webpack/Vite(进阶)
cd example-plugin npm install npm run build npm run package- 优点:支持多源文件、完整构建工具链;
- 缺点:工程配置更复杂。
如何选择
- 只是想快速验证 API → 使用
minimal-plugin; - 需要类型安全 → 使用
simple-typescript-plugin; - 构建复杂插件 → 使用
example-plugin或仓库内更现代的 boilerplate-solid-js(SolidJS + Vite,自带 i18n 支持)。
也可以手动搭建:复制示例插件并重命名,修改manifest.json元数据与package.json名称描述,然后npm install、npm run dev开发、npm run build构建。
插件项目结构与构建产物
一个典型的插件项目结构如下:
my-plugin/ ├── package.json # NPM 包配置 ├── tsconfig.json # TypeScript 配置 ├── webpack.config.js # 构建配置(Webpack 方案) ├── manifest.json # 插件清单(元数据) ├── src/ │ └── index.ts # 插件主代码 ├── assets/ │ ├── index.html # 可选 UI(iframe 插件) │ └── icon.svg # 插件图标 ├── scripts/ │ └── package.js # 生成 plugin.zip 的脚本 └── dist/ # 构建输出 ├── plugin.js # 编译后的插件代码(纯 iframe 插件可省略) ├── manifest.json # 复制过来的清单 └── plugin.zip # 打包产物如果使用仓库提供的 Vite 插件方案(见packages/vite-plugin/),构建输出在dist/,其中manifest.json从src/manifest.json复制,icon.svg从src/assets/icon.svg复制,i18n/*.json从插件根目录的i18n/复制,index.html中的 JS/CSS 会被内联为单文件,并可配置copyTo选项把产物自动拷贝到 Super Productivity 的src/assets/下实现热更新开发(见 vite-plugin 源码)。
PluginAPI:插件能力全景
插件运行时接收一个全局PluginAPI对象(类型定义见 packages/plugin-api/src/types.ts)。按能力维度可划分为以下几类:
配置读取
cfg:当前应用配置,包括theme('light' | 'dark')、appVersion、platform('web' | 'desktop' | 'android' | 'ios')、isDev及lang语言信息。
UI 集成
registerMenuEntry():在应用菜单中添加条目;registerHeaderButton():在顶部标题栏添加按钮;registerSidePanelButton():在侧边面板添加按钮;registerWorkContextHeaderButton():注册仅当活跃工作上下文(项目/标签/Today)匹配时才显示的标题按钮,回调会收到上下文快照;registerShortcut():注册键盘快捷键;unregisterShortcut(id):按 id 注销已注册的快捷键(用户在键盘设置中的绑定会被保留,重新注册同名 id 即可恢复);showIndexHtmlAsView():在应用视图中展示插件 UI;showInWorkContext():将插件index.html嵌入工作视图主体(替代任务列表,仅在项目或 Today 上下文生效);closeWorkContextView():恢复为正常任务列表视图;openDialog():打开自定义对话框。
数据访问
getTasks():获取全部任务;getArchivedTasks():获取已归档任务;getCurrentContextTasks():获取当前项目/标签下的任务;getSelectedTask()/getFocusedTask():读取详情面板中选中的任务 / 当前获得焦点的任务行;getAppState():获取包含任务、项目、标签、笔记、重复任务配置、简单计数器与全局配置的只读应用快照(PluginAppState);updateTask(taskId, updates):更新任务;addTask(taskData):创建任务,返回新任务 id;deleteTask(taskId):删除任务;batchUpdateForProject():在单个项目中批量执行创建/更新/删除/重排操作(支持临时 id 引用,见 BatchUpdateRequest);getAllProjects()/addProject()/updateProject()/deleteProject():项目读写,其中删除项目会级联删除其包含的任务(与 UI 行为一致,且拒绝删除收件箱);getAllTags()/addTag()/updateTag():标签读写;reorderTasks():重排任务顺序;selectTask(taskId):打开指定任务的详情面板。
用户交互
showSnack():显示 snack 条通知,type支持SUCCESS | ERROR | WARNING | INFO;notify():显示系统级通知(NotifyCfg);openDialog():打开自定义对话框,HTML 内容会被宿主白名单清洗(脚本、事件处理器属性与危险 URL 均会被移除,见 DialogCfg 注释)。
数据持久化
persistDataSynced(dataStr, key?):保存插件数据,可选key参数可将同步数据拆分为多个独立解析的条目;loadSyncedData(key?):加载已保存的数据;getConfig():读取插件配置;setSecret(key, value)/getSecret(key)/deleteSecret(key):本地专用的密钥存储(绝不进入同步、导出或备份,适合存放密码、API Token 等敏感信息)。
网络与系统能力
request(url, options):通过宿主受保护的 HTTP 桥发出请求,需要同时在permissions中声明"http"能力、在allowedHosts中声明目标主机(缺一即被拒绝,fail-closed);downloadFile(filename, data):触发文件下载;startOAuthFlow(config)/getOAuthToken()/clearOAuthToken():OAuth 流程与令牌管理;executeNodeScript(request):Electron 桌面端执行 Node 脚本(需主进程用户授权,仅内置插件可授予);dispatchAction(action):派发受限的 NgRx action;isWindowFocused()/onWindowFocusChange(handler):窗口焦点状态;- 简单计数器:
setCounter/getCounter/incrementCounter/decrementCounter/deleteCounter/getAllCounters; log:分级日志对象(critical/err/error/warn/normal/info/verbose/debug)。
生命周期与通信
onReady(fn):应用确认所有声明能力就绪后回调(把启动初始化代码放在这里,而不是plugin.js顶层);onUnload(fn):插件被禁用/重载/卸载时回调,用于清理定时器与事件监听器(iframe 插件中为 no-op,因为 iframe 卸载时会随之销毁);onMessage(handler):跨进程消息通信(插件 iframe 与宿主之间,见 PluginIframeMessageType)。
生命周期 Hooks:订阅应用事件
通过registerHook(hook, handler)订阅应用生命周期事件,完整枚举见 PluginHooks:
| Hook | 触发时机 | 载荷要点 |
|---|---|---|
taskCreated | 任务被创建 | taskId+task |
taskComplete | 任务被标记完成 | taskId+task |
taskUpdate | 任务被修改 | taskId+task+changes |
taskDelete | 任务被删除 | taskId |
currentTaskChange | 活跃任务切换 | current+previous(可为 null) |
finishDay | 一天结束 | date |
languageChange | 应用语言切换 | code/newLanguage |
persistedDataChanged | 本插件持久化数据变化(含远端同步送达与批量导入) | 无载荷,需重新loadSyncedData() |
action | 自定义 action 分发 | action+payload |
anyTaskUpdate | 任意任务任意更新 | action+taskId?+task?+changes? |
projectListUpdate | 项目列表变化 | action+projectId?+project?+changes? |
workContextChange | 活跃工作上下文切换 | 上下文快照(ActiveWorkContext) |
registerHook是泛型方法,处理器会收到与 hook 一一对应的强类型载荷(映射见 HookPayloadMap)。例如:
// 注册任务完成处理器 PluginAPI.registerHook('taskComplete', async (task) => { console.log('Task completed:', task); PluginAPI.showSnack({ msg: `Great job completing: ${task.title}`, type: 'SUCCESS', }); }); // 注册键盘快捷键 PluginAPI.registerShortcut({ id: 'my-action', label: 'My Plugin Action', onExec: async () => { const tasks = await PluginAPI.getTasks(); console.log(`You have ${tasks.length} tasks`); }, });关于workContextChange有一个值得注意的细节:载荷中的taskIds是发出时刻的快照而非实时视图,任务增删移动后会过期;需要当前顺序时应随时调用getActiveWorkContext()或getTasks()重新读取(见 ActiveWorkContext 的注释说明)。
清单文件 manifest.json 详解
manifest.json是插件的元数据核心,完整字段定义见 PluginManifest:
{ "name": "My Awesome Plugin", "id": "my-awesome-plugin", "manifestVersion": 1, "version": "1.0.0", "minSupVersion": "13.0.0", "description": "An awesome plugin for Super Productivity", "hooks": ["taskComplete", "taskUpdate"], "permissions": ["showSnack", "getTasks", "addTask", "showIndexHtmlAsView"], "allowedHosts": ["api.example.com"], "iFrame": true, "uiKit": true, "sidePanel": false, "icon": "icon.svg", "i18n": { "languages": ["en", "de", "fr"] } }关键字段说明:
id:全局唯一插件标识;manifestVersion:清单格式版本号;minSupVersion:兼容的最低 Super Productivity 版本;hooks:插件声明使用的事件列表;permissions:权限声明(最小权限原则)。常用权限见 plugin-api README:showSnack、notify、showIndexHtmlAsView、openDialog、getTasks、getArchivedTasks、getCurrentContextTasks、getSelectedTask、getFocusedTask、addTask、getAllProjects、addProject、deleteProject、getAllTags、addTag、persistDataSynced、getAppState等;allowedHosts:允许PluginAPI.request访问的精确主机名列表(host-only、精确匹配、无通配符、忽略端口)。列表为空或省略时request被禁用(fail-closed);同时还必须在permissions中声明"http"能力(见 types.ts 注释)。安装时该列表会展示给用户以便审查插件的出网范围;iFrame:true表示该插件通过index.html提供 UI;uiKit:是否注入 UI Kit CSS reset,默认true;sidePanel:true时插件加载到右侧面板而非路由;icon:SVG 图标路径(相对插件根目录);jsonSchemaCfg:插件配置的 JSON Schema 文件路径;i18n.languages:插件支持的语言代码数组(详见下文国际化章节)。
构建与打包:从源码到 plugin.zip
打包命令
npm run build npm run package产物为dist/plugin.zip,可直接分发。
文件大小限制
以下限制来自 plugin.const.ts,是宿主端实际执行的校验值(注意:与文档中列出的上限相比,当前源码中的 ZIP 与代码限制更为严格):
| 文件 | 源码实际限制(当前仓库) |
|---|---|
| 插件 ZIP | MAX_PLUGIN_ZIP_SIZE = 10MB |
| 插件代码(plugin.js) | MAX_PLUGIN_CODE_SIZE = 5MB |
| 清单(manifest.json) | MAX_PLUGIN_MANIFEST_SIZE = 100KB |
| 全部翻译文件合计 | MAX_PLUGIN_TRANSLATIONS_TOTAL_SIZE = 5MB |
开发时请以源码常量 plugin.const.ts 为准,并预留余量。
必选与可选文件
plugin.zip必须包含:
manifest.json— 插件元数据;plugin.js— 插件主代码;除非是iFrame: true且带index.html的纯 iframe 插件。
可选文件:
index.html— iframe 插件的 UI;icon.svg— 插件图标;i18n/*.json— 多语言翻译文件。
发布插件:GitHub Release 与 npm
GitHub Release(推荐方式)
- 为插件创建 GitHub 仓库;
- 使用 GitHub Actions 在发布时自动构建,参考 workflow:
name: Build Plugin on: release: types: [created] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: node-version: 18 - run: npm ci - run: npm run build - run: npm run package - uses: softprops/action-gh-release@v1 with: files: dist/plugin.zip- 用户即可从 Releases 下载
.zip文件安装。
NPM 包
也可以将插件源码发布到 npm:
- 在
package.json中配置 npm scope; npm run build构建;npm publish发布。
用户可自行构建,或由你在包中包含构建产物。
本地测试与调试
开发模式测试
# 构建插件 npm run build # 复制到 Super Productivity 的资源目录 npm run install-local # 该命令将构建产物复制到 ../../../src/assets/my-plugin/ # 在开发模式下启动 Super Productivity cd ../../.. && npm start此外,若使用@super-productivity/vite-plugin,可在 vite 配置中设置copyTo指向 Super Productivity 的src/assets/,构建时自动拷贝实现热更新(见 vite-plugin 源码 的copyTo选项)。
生产构建测试
npm run package打包;- 打开 Super Productivity;
- 进入Settings → Plugins;
- 点击Upload Plugin;
- 选择你的
plugin.zip。
调试技巧
- 打开浏览器 DevTools 查看控制台日志;
- 插件在主窗口上下文中运行,
console.log()直接可见; - 插件错误会在 Console 中输出,可利用宿主提供的
PluginAPI.log分级日志(debug/info/warn/error等)。
TypeScript 开发与类型安全
收益
- 类型安全:完整的 IntelliSense 与编译期检查;
- API 发现:所有
PluginAPI方法自动补全; - 重构安全:TypeScript 保证重构不破坏接口;
- 内联文档:IDE 中直接查看类型注释(例如
getAppState()的快照语义、request()的 fail-closed 行为都有详细 JSDoc)。
安装类型包
npm install @super-productivity/plugin-api带类型的示例
import type { TaskData, ProjectData } from '@super-productivity/plugin-api'; // 类型安全的任务处理 async function processTask(task: TaskData): Promise<void> { if (task.projectId) { const projects = await PluginAPI.getAllProjects(); const project = projects.find((p) => p.id === task.projectId); if (project) { console.log(`Task "${task.title}" belongs to project "${project.title}"`); } } } // 类型安全的 hook 注册 PluginAPI.registerHook('taskUpdate', (data: unknown) => { const task = data as TaskData; processTask(task); });类型包还导出了PluginHooks枚举、PluginManifest、PluginBaseCfg、SnackCfg、DialogCfg、Task/Project/Tag等数据与 UI 类型(plugin-api README)。注意TaskData、ProjectData、TagData已被标记为@deprecated,新代码建议直接使用Task、Project、Tag。
插件国际化(i18n)完整指南
完整规范见 PLUGIN_I18N.md,这里汇总核心要点。
目录结构与清单声明
my-plugin/ ├── manifest.json # 声明支持的语言 ├── plugin.js └── i18n/ # 翻译文件 ├── en.json # 必选 — 英文 ├── de.json # 可选 — 德语 └── fr.json # 可选 — 法语{ "id": "my-plugin", "name": "My Plugin", "version": "1.0.0", "i18n": { "languages": ["en", "de", "fr"] } }规则要点:
languages必填,且必须至少包含"en"(英文是兜底语言);- 使用标准小写语言代码(
en、de、fr、es、ja、zh等); - 语言代码必须与 Super Productivity 自身的代码匹配且全小写:
pt-br有效,pt-BR无效会被忽略; - 翻译文件必须是UTF-8编码的 JSON,旧式 8 位编码(Latin-1/CP1252)的文件会被直接拒绝;
- 已声明但缺少对应
i18n/<lang>.json的语言会被跳过并在控制台输出警告;不支持的代码统一输出Unsupported language codes: …警告; - 上传的插件 ZIP 中所有翻译文件合计不得超过 5MB(源码常量
MAX_PLUGIN_TRANSLATIONS_TOTAL_SIZE,见 plugin.const.ts)。
翻译文件格式
使用层级化 JSON 组织:
{ "MESSAGES": { "WELCOME": "Welcome to the plugin!", "ERROR": "An error occurred: {{error}}" }, "BUTTONS": { "SAVE": "Save", "CANCEL": "Cancel" } }最佳实践:键名统一大写;相关翻译分组;层级保持在 2~3 层以内;使用描述性键名(BUTTONS.SAVE_TASK优于BTN1)。
API 方法
translate(key, params?)
按当前语言翻译键,支持{{param}}参数插值。兜底顺序:当前应用语言 → 英文 → 找不到则返回键本身。
api.translate('MESSAGES.WELCOME'); // → "Welcome to the plugin!" (en) api.translate('MESSAGES.ERROR', { error: 'Network timeout' }); // → "An error occurred: Network timeout" api.translate('BUTTONS.SAVE'); // → "Speichern" (de) / "Save" (en 兜底) api.translate('BUTTONS.DELETE'); // → "BUTTONS.DELETE"(都找不到则返回键)formatDate(date, format)
按当前语言环境格式化日期,输入支持Date对象、ISO 8601 字符串、毫秒时间戳。预定义格式:'short'、'medium'、'long'、'time'、'datetime'。
api.formatDate(new Date(), 'short'); // → "1/16/26" (en-US) / "16.1.26" (de) api.formatDate('2026-01-16T14:30:00Z', 'datetime'); // → "1/16/26, 2:30 PM" (en)getCurrentLanguage()
返回当前应用语言代码(如"en"、"de"、"fr"),可据此做条件逻辑(如 CJK 字体处理)。
语言切换 Hook
api.registerHook('languageChange', ({ newLanguage }) => { console.log(`Language changed to: ${newLanguage}`); // 插件翻译会自动重载,此处只需处理额外 UI 更新 updatePluginUI(); });注意:语言切换时插件翻译会自动重新加载,只有需要额外 UI 更新的场景才需注册此 hook。
支持的语言代码
Super Productivity 官方支持的语言(完整表格):en、de、es、fr、it、pt、pt-br、ru、zh、zh-tw、ja、ko、ar、fa、tr、pl、nl、nb、sv、fi、cs、sk、hr、uk、id、ro、ro-md。插件只需声明自己实际翻译了的语言,其余用户自动回退到英文。
复数与参数插值
翻译键中不含复数逻辑,推荐用参数动态选择键:
{ "TASK_COUNT_SINGULAR": "{{count}} task remaining", "TASK_COUNT_PLURAL": "{{count}} tasks remaining" }const key = count === 1 ? 'TASK_COUNT_SINGULAR' : 'TASK_COUNT_PLURAL'; const msg = api.translate(key, { count });参数插值失败的两类常见错误:缺少花括号语法($name不生效,需{{name}});参数名不匹配(传{ user: 'John' }无法替换{{name}})。
性能与缓存语义
- 翻译文件在插件激活时加载一次;
- 翻译结果在内存中缓存,频繁调用
translate()无性能影响; - 语言切换复用已加载的翻译;
- 因此不要在插件里自行缓存翻译结果,每次都调用
api.translate()即可。
最佳实践与疑难排查
最佳实践
- 错误处理:异步操作始终用 try-catch 包裹;
- 性能:不要在主线线程执行重型计算;
- 状态管理:插件状态使用
persistDataSynced()(敏感信息改用setSecret(),避免进入同步/导出/备份); - 用户体验:用 snack 消息提供清晰反馈;
- 权限:只申请实际需要的权限;
- 版本兼容:正确设置
minSupVersion; - 国际化:始终附带
en.json,为插件添加 i18n 支持以触达更多用户; - 启动/卸载:初始化代码放进
onReady(),清理代码放进onUnload(); - 网络访问:使用
request()时必须同时声明"http"权限与allowedHosts白名单。
疑难排查
插件未加载:
- 检查浏览器控制台错误;
- 确认
manifest.json是合法 JSON; - 确认必填字段齐全(
id、manifestVersion、version、minSupVersion等); - 检查文件大小是否超过 plugin.const.ts 中的限制。
TypeScript 报错:
- 运行
npm run typecheck查看全部错误; - 确认安装了
@super-productivity/plugin-api; - 检查
tsconfig.json配置。
构建问题:
- 删除
dist/后重新构建; - 检查 webpack/vite 配置;
- 确认所有依赖已安装。
翻译显示为键名而非译文:
- 检查
i18n/目录存在于插件 ZIP 根目录(与manifest.json同级); - 验证 JSON 文件有效且为 UTF-8;
- 确认键名大小写完全一致(区分大小写);
- 查看浏览器控制台中的警告日志。
仓库内示例插件一览
packages/plugin-dev/下提供了丰富的示例插件(npm run list可查看完整列表):
- minimal-plugin— 最简单的插件(约 10 行代码);
- simple-typescript-plugin— 带 TypeScript 的最小工程;
- example-plugin— 功能完整的 Webpack 示例(覆盖全部 API、iframe UI、状态持久化、Hook 处理、构建配置);
- boilerplate-solid-js— 现代 SolidJS 样板:SolidJS 响应式 UI、Vite 快速构建、i18n 支持(示例翻译)、插件与 iframe 通信,其 manifest.json 与 plugin.ts 是上手参考的绝佳模板;
- procrastination-buster— 真实业务场景的 SolidJS 插件;
- 此外还有
brain-dump、doc-mode、sync-md、todoist-import、voice-reminder、yesterday-tasks-plugin,以及github-issue-provider、gitlab/gitea/azure-devops/clickup/linear/trello-issue-provider、caldav-calendar-provider、google-calendar-provider等 Issue/日历集成示例,可分别作为标准插件与issueProvider类型插件的参考实现。
建议的开发路线:先用minimal-plugin理解 API,需要类型安全时迁移到 TypeScript,构建复杂插件时再引入 Webpack 或 Vite;全程参考 plugin-api 类型定义 中的 JSDoc 注释,它是插件 API 最权威的行为契约。
【免费下载链接】super-productivitySuper Productivity is an advanced todo list app with integrated Timeboxing and time tracking capabilities. It also comes with integrations for Jira, GitLab, GitHub and Open Project.项目地址: https://gitcode.com/GitHub_Trending/su/super-productivity
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考