介绍
这一章回答三个问题:插件在 UDhold 里是什么、能做成什么形态、能调用哪些能力。读完你就能判断自己的点子适不适合做成插件,该做成哪种形态。
插件是什么
UDhold 宿主基于 Tauri 2 构建。每个插件打开时,宿主会为它创建一个独立的 WebView(网页容器),加载插件声明的前端页面;插件与宿主之间通过注入的 window.udhold 桥对象通信。
┌─────────────── UDhold 宿主(Tauri 2 / Rust)───────────────┐
│ 主面板(活动栏 / 左侧面板 / 工作区 / 右侧面板) │
│ ┌────────────┐ ┌──────────────────┐ ┌────────────┐ │
│ │ 插件 A │ │ 插件 B(你的插件) │ │ 插件 C │ │
│ │ WebView │ │ WebView │ │ 服务进程 │ │
│ └────────────┘ └────────┬─────────┘ └────────────┘ │
│ │ window.udhold(桥 + 权限网关) │
│ 文件 / 剪贴板 / 数据库 / AI / 系统命令 ……(26 个能力域) │
└──────────────────────────────────────────────────────────┘
插件工程的唯一契约是根目录的 plugin.json——名字、入口、面板槽位、搜索关键词、权限需求全部声明在这一个文件里,宿主按声明加载与管控。清单体系兼容 uTools 插件协议,熟悉 uTools 开发的开发者可以无缝迁移。
插件的四种形态
| 形态 | 构成 | 典型例子 | 适合场景 |
|---|---|---|---|
| 页面插件 | 前端工程(Vue / React / 纯 HTML),构建出静态产物 | 计算器、JSON Tools、待办清单 | 有界面的工具 |
| 免构建静态插件 | 一个 index.html + plugin.json,无需 Node 工程 | 简单的小工具页 | 轻量工具、快速验证想法 |
| 纯服务插件 | 只有 services[] 声明(Node / Python / exe),没有界面 | 后台服务、绿色软件封装 | 无界面的后台能力 |
| 页面 + 服务 | 前端页面 + 声明式服务进程 | 需要本地后端的复杂插件 | 本地数据库、爬虫、编译类工具 |
页面插件还可以通过 pluginSetting 进一步定制形态:嵌入工作区面板、挂在左右侧栏、打开即分离成独立窗口(Detach)、进活动栏顶部/底部菜单(Menu / MenuBottom)等,详见快速上手的清单字段说明。
能调用哪些能力
插件通过 window.udhold 调用宿主能力,共 26 个命名空间、200 多个方法。常用的能力域:
| 能力域 | 说明 | 示例调用 |
|---|---|---|
db / dbStorage / dbCryptoStorage | 文档数据库 / 键值存储 / 加密键值存储,数据自动按插件隔离 | udhold.dbStorage.setItem('k', 'v') |
plugin / router / route | 插件运行时:生命周期钩子、跨插件调用、tab 操作 | udhold.router.push('files') |
files | 文件资源管理:列举、新建、重命名、删除、批量复制、监听 | udhold.files.list('D:\\') |
clipboard | 剪贴板历史与读写 | udhold.copyText('文本') |
ai / ai.models | 大模型推理(流式)、模型配置 | udhold.ai({ model, messages }) |
system / shell | 系统命令、通知、打开文件/网址 | udhold.shellOpenExternal(url) |
search | 唤起全局搜索框 | udhold.search.openWithKeyword('kw') |
setting | 宿主外观与行为配置 | udhold.setting.setTheme('dark') |
udbrowser | 内嵌浏览器自动化(打开页面、点击、取数) | udhold.udbrowser.create() |
service / node | 拉起并管理本地服务进程 | udhold.service.spawn(cfg) |
user / sync / netdisk | 登录态、WebDAV 同步、简盘 | udhold.user.isLogin() |
除了直接调用,插件还能通过 exports + registerTool 对外暴露接口供其他插件(以及宿主的 MCP 服务)调用,形成插件间的协作生态。
权限模型
UDhold 采用三层声明式权限:用到什么,就在 plugin.json 里声明什么,未声明的调用会被宿主权限网关拒绝(返回 PERMISSION_DENIED)。
| 声明字段 | 管什么 | 示例 |
|---|---|---|
permissions[] | 平台能力路径:敏感域(文件、剪贴板历史、AI、登录态等)必须声明;存储、路由等基础能力免声明 | "permissions": ["files.list", "ai.*"] |
tauri[] | 原生能力:对话框、通知、文件选择器等 | "tauri": ["dialog:default"] |
plugins[] | 跨插件调用白名单:要 push / invoke 其他插件,先在这里登记目标插件名 | "plugins": ["files"] |
通配符只支持后缀:user.* 放行整个 user 域,ai.models.list 精确到单个方法。不要声明 "*"(全量授权,市场审核时视为最高风险)。
生命周期
插件实例有 12 个生命周期钩子,覆盖从打开到销毁的完整过程:
udhold.plugin.onMounted((action) => {}) // 页面就绪,action.code 区分被哪个 feature 唤起
udhold.plugin.onActivated((p) => {}) // tab 切入
udhold.plugin.onDeactivated((p) => {}) // tab 被切走
udhold.plugin.onDetached((p) => {}) // 分离成独立窗口
udhold.plugin.onAttached((p) => {}) // 从独立窗口回归面板
udhold.plugin.onBeforeUnmount((p) => {}) // 实例销毁前(唯一清理时机,宿主等 200ms)
udhold.plugin.onShow((p) => {}) // 获得焦点
udhold.plugin.onBlur((p) => {}) // 失去焦点
udhold.plugin.onMinimized((p) => {}) // 承载窗口最小化
udhold.plugin.onRestored((p) => {}) // 承载窗口还原
udhold.plugin.onAppLaunched((p) => {}) // 宿主启动(粘性)
udhold.plugin.onAppBeforeQuit(() => {}) // 宿主退出前
所有钩子返回取消函数,可多次注册叠加。onMounted 的 action.from 能区分唤起来源(主输入框 main、面板 panel、快捷键 hotkey、其他插件跳转 redirect 等)。
分发方式
- 插件市场:在「插件开发者」里一键打包上传,审核通过后全平台用户可搜可装;已装用户打开插件时宿主自动检查更新。
- 本地安装包:打包为
.zip,可以发给朋友双击安装,或在插件中心手动安装,适合内部分发与灰度测试。
开发前的准备
| 需要 | 说明 |
|---|---|
| UDhold 桌面端 | 调试与打包都在宿主里完成,下载安装 |
| HTML / CSS / JavaScript 基础 | 插件本质是 Web 页面;推荐用 Vue 3(官方插件与组件库同栈,生态最顺) |
| Node.js 20+ 与 pnpm | 工程型插件需要;纯静态插件可跳过 |
| 一个想法 | 从自己每天会重复做的小事开始,最容易做出好插件 |
准备好了?进入快速上手,20 分钟跑起你的第一个插件。