ReadAware

构建插件

从公开的 TypeScript 模板开始,声明最小的能力集合,并在 ReadAware 桌面应用中运行构建后的软件包。宿主负责生命周期、权限、展示和回滚;插件负责自己的行为和私有数据。

前置条件

  • ReadAware 桌面应用,并能访问“设置 → 插件”。
  • Bun,用于运行仓库脚本。
  • readaware-plugins 仓库的检出版本或 fork。

创建软件包

  1. template/ 复制到 plugins/<your-plugin-id>/
  2. 保持文件夹名、manifest 的 id 和运行时命名空间完全一致。
  3. 编辑 manifest.jsonsrc/main.ts
  4. 删除不使用的模板贡献,并移除对应权限。
  5. 构建 ReadAware 将要加载的自包含 main.js
bash
bun run build
bun run typecheck
bun test
bun run validate

先设计 manifest,再实现

按以下顺序检查 manifest:

  1. 身份:稳定 ID、名称、软件包版本、作者和最低应用版本。
  2. 数据:正整数 schemaVersion 和迁移路径。
  3. 兼容性:为每个使用的 API 和 schema 在 requires 中写入 semver 范围。
  4. 授权:语义 permissions 和精确的 settingsAccess 授权。
  5. 声明:设置、定时任务、主题、字体和入口模块。

安装前使用能力浏览器和权限预览。要求是兼容性声明,不是用户授权;即使能力不需要权限,只要插件依赖其契约,也必须写入 requires

选择正确的能力

  1. ReadAware 拥有某种状态或行为时,使用领域
  2. 提供选择、动作或提供方时,使用贡献
  3. 需要受限的宿主操作时,使用服务
  4. 插件存储只用于插件拥有的数据。
  5. 现有形态都不合适时,请求新的类型化宿主能力。

不要把书籍、进度、标注、设置或记忆复制到插件存储。影子状态会绕过产品不变量、已提交事件、投影重建、同步语义和助手上下文。

让激活保持声明式

activate(ctx) 期间检查环境,并注册动作、命令、提供方、订阅和定时任务。不要执行业务写入或外部工作。宿主会一直暂存每项注册,直到激活 RPC 完成且 Worker 回复健康检查。

晋升后,从已注册的处理器启动运行时工作。如果处理器返回 promise,让宿主展示加载和失败状态。只有在可选的 deactivate() 必须关闭外部资源时,才保留这些资源的引用;宿主注册和订阅会自动释放。

明确给私有数据做版本控制

schemaVersion 为插件 KV 和文档集合版本控制;它独立于软件包版本。只有私有数据形态改变时才修改它。在 schema 提交后,为每个支持的升级和降级导出 migrate(storageCtx, change)

  • 迁移只能接收存储:不能使用领域、设置、密钥、网络、UI、LLM 或贡献。
  • 让每次转换都具备确定性和幂等性。
  • 测试部分写入后发生失败的情况;宿主必须精确恢复 KV、文档、文件和 schema 元数据。
  • 不要用软件包版本检查替代数据 schema。

安装工作文件夹

  1. 运行构建和检查。
  2. 打开 ReadAware → 设置 → 插件 → 安装插件。
  3. 选择构建出的插件文件夹并检查同意摘要。
  4. 在桌面应用中运行真实功能。
  5. 重新构建并安装,以测试更新。

普通浏览器无法验证插件安装、Worker IPC、SQLite 持久化、原始书籍访问、阅读器集成或回滚。请测试用于发布的 Tauri 应用。

测试生命周期,而不只是成功路径

  • 全新安装、启用、停用,以及不重启应用再次启用。
  • 使用真实数据完成成功更新和降级。
  • 激活超时、处理器拒绝、迁移失败和精确回滚。
  • 卸载清理:不残留动作、监听器、定时任务、提供方或 Worker。
  • 更新期间移除权限和扩展权限。
  • 长标签、空状态、键盘导航和所有宿主主题。

了解当前限制

ReadAware 打开期间,定时任务至少按声明的频率运行,过期时会在启动时补跑。它们不是持久任务:应用关闭时不会执行,没有持久队列、重试/退避契约或崩溃恢复保证。

UI 只在现有的类型化贡献点可用。缺少挂载位置时,需要宿主拥有的贡献点和消费者;不会为了走捷径添加任意 HTML 或通用原生 invoke API。

下一步

API 参考放在编辑器旁边,然后在准备注册表 pull request 前阅读发布插件