构建插件
从公开的 TypeScript 模板开始,声明最小的能力集合,并在 ReadAware 桌面应用中运行构建后的软件包。宿主负责生命周期、权限、展示和回滚;插件负责自己的行为和私有数据。
前置条件
- ReadAware 桌面应用,并能访问“设置 → 插件”。
- Bun,用于运行仓库脚本。
- readaware-plugins 仓库的检出版本或 fork。
创建软件包
- 将
template/复制到plugins/<your-plugin-id>/。 - 保持文件夹名、manifest 的
id和运行时命名空间完全一致。 - 编辑
manifest.json和src/main.ts。 - 删除不使用的模板贡献,并移除对应权限。
- 构建 ReadAware 将要加载的自包含
main.js。
bun run build
bun run typecheck
bun test
bun run validate先设计 manifest,再实现
按以下顺序检查 manifest:
- 身份:稳定 ID、名称、软件包版本、作者和最低应用版本。
- 数据:正整数
schemaVersion和迁移路径。 - 兼容性:为每个使用的 API 和 schema 在
requires中写入 semver 范围。 - 授权:语义
permissions和精确的settingsAccess授权。 - 声明:设置、定时任务、主题、字体和入口模块。
安装前使用能力浏览器和权限预览。要求是兼容性声明,不是用户授权;即使能力不需要权限,只要插件依赖其契约,也必须写入 requires。
选择正确的能力
- ReadAware 拥有某种状态或行为时,使用领域。
- 提供选择、动作或提供方时,使用贡献。
- 需要受限的宿主操作时,使用服务。
- 插件存储只用于插件拥有的数据。
- 现有形态都不合适时,请求新的类型化宿主能力。
不要把书籍、进度、标注、设置或记忆复制到插件存储。影子状态会绕过产品不变量、已提交事件、投影重建、同步语义和助手上下文。
让激活保持声明式
在 activate(ctx) 期间检查环境,并注册动作、命令、提供方、订阅和定时任务。不要执行业务写入或外部工作。宿主会一直暂存每项注册,直到激活 RPC 完成且 Worker 回复健康检查。
晋升后,从已注册的处理器启动运行时工作。如果处理器返回 promise,让宿主展示加载和失败状态。只有在可选的 deactivate() 必须关闭外部资源时,才保留这些资源的引用;宿主注册和订阅会自动释放。
明确给私有数据做版本控制
schemaVersion 为插件 KV 和文档集合版本控制;它独立于软件包版本。只有私有数据形态改变时才修改它。在 schema 提交后,为每个支持的升级和降级导出 migrate(storageCtx, change)。
- 迁移只能接收存储:不能使用领域、设置、密钥、网络、UI、LLM 或贡献。
- 让每次转换都具备确定性和幂等性。
- 测试部分写入后发生失败的情况;宿主必须精确恢复 KV、文档、文件和 schema 元数据。
- 不要用软件包版本检查替代数据 schema。
安装工作文件夹
- 运行构建和检查。
- 打开 ReadAware → 设置 → 插件 → 安装插件。
- 选择构建出的插件文件夹并检查同意摘要。
- 在桌面应用中运行真实功能。
- 重新构建并安装,以测试更新。
普通浏览器无法验证插件安装、Worker IPC、SQLite 持久化、原始书籍访问、阅读器集成或回滚。请测试用于发布的 Tauri 应用。
测试生命周期,而不只是成功路径
- 全新安装、启用、停用,以及不重启应用再次启用。
- 使用真实数据完成成功更新和降级。
- 激活超时、处理器拒绝、迁移失败和精确回滚。
- 卸载清理:不残留动作、监听器、定时任务、提供方或 Worker。
- 更新期间移除权限和扩展权限。
- 长标签、空状态、键盘导航和所有宿主主题。
了解当前限制
ReadAware 打开期间,定时任务至少按声明的频率运行,过期时会在启动时补跑。它们不是持久任务:应用关闭时不会执行,没有持久队列、重试/退避契约或崩溃恢复保证。
UI 只在现有的类型化贡献点可用。缺少挂载位置时,需要宿主拥有的贡献点和消费者;不会为了走捷径添加任意 HTML 或通用原生 invoke API。