Clipbus 插件
插件让 Clipbus 对你复制的内容采取行动 — 识别文本与图片、以富卡片展示、添加快捷 action。它们基于开放的 @clipbus/plugin-sdk 构建,每张卡片都是一个 web 页面(官方模板与示例基于 Vue 3)。
概览
Clipbus 插件是一个挂入剪贴板的小模块,处理文本与图片两种内容。每个插件最多可提供三种能力 — 按需实现即可,不必全部实现。
detector检测检查每次复制的文本或图片,把结构化的 attachment 写到条目上。renderer渲染把 attachment 显示成卡片 — Clipbus 画标题、颜色和按钮,你的 web 页面画内容区。action操作按需把当前的文本或图片转换成新值,结果可以继续交给下一个 Action。工作原理
每次复制文本或图片时(重复复制同一内容也算),Clipbus 会运行已注册的 detector;编辑条目或手动重新检测后也会再次运行。识别到内容的 detector 通过 ctx.item 把一个或多个 attachment — 即类型化的 JSON 数据 — 写到剪贴板条目上。条目上的某个 attachment 只要有插件为它的类型声明了 renderer,这个 renderer 的页面就会在条目下方的附件面板里画出一张卡片。
每张卡片在附件面板里有一个 chip。卡片外框 — 标题、颜色和按钮 — 由 Clipbus 直接按数据绘制,所以卡片首次显示时外观就是对的,你的页面同时加载到内容区。
Runtime 与卡片页
一个插件由两个相互独立的部分组成,它们都只和 Clipbus 通信。Runtime 是每个插件一个的常驻 Node 进程,你的 detector、action 和消息处理函数通过一次 definePlugin({ detectors, actions, messages }) 调用注册。每张卡片都是一个 web 页面,由 Clipbus 按 manifest 中 renderer 的 page 在 WebView 里加载。两侧不共享内存:页面通过类型化的消息找到自己的 Runtime,由 Clipbus 中转。
| Node runtime | 卡片页 | |
|---|---|---|
| 入口 | runtime.nodeEntry | renderers[].page HTML |
| 你写什么 | definePlugin({ … }) | HTML + ctx 调用 |
| 导入自 | @clipbus/plugin-sdk/runtime | @clipbus/plugin-sdk/view |
| 宿主 API | ctx — 处理函数的第二个参数 | ctx — 模块级对象 |
两侧拿到的 ctx 有相同的域 — ctx.item、ctx.system、ctx.env、ctx.settings、ctx.log — 两侧都有的方法签名也相同。ctx.item 永远指向本次调用的主体条目:被检测的条目、Action Flow 的起点条目,或者卡片所在的条目。
并非每种能力都需要两侧 —— 这正是关键区别:
| 能力 | 运行于 | Runtime 处理函数 | 卡片页 |
|---|---|---|---|
detector | Node runtime | (input, ctx) => void | — |
renderer | 卡片页 | — | renderers[].page |
action | Node runtime | (input, ctx) => result.* | — |
message | Node runtime,由卡片页调用 | contract.handle(fn) | contract.call(data) |
只有卡片的插件可以不写 Runtime,Clipbus 就不为它启动 Node 进程。消息是两侧之间的类型化通道:在两侧共同导入的模块里用 defineMessage<Req, Resp>(name) 定义一次,在 Runtime 里处理,在页面里调用。
import { defineMessage } from "@clipbus/plugin-sdk"; export const verify = defineMessage<{ token: string }, { valid: boolean }>("verify"); // Runtime — definePlugin({ messages: [ … ] }) verify.handle(async ({ token }) => ({ valid: await checkSignature(token) })); // 卡片页 const { valid } = await verify.call({ token: attachment.payload.token });
Detector
Detector 把复制的内容转化为 attachment。它以 manifest 中的 detectors[].id 为 key,接收 { item, content, trigger },不返回任何东西 — 写入都走 ctx.item.*。content 是文本全文,或图片的尺寸与格式(像素用 ctx.input.imagePath() 读取);trigger 说明为什么运行:copy、recopy、edit 或 manual。
// payload.ts — 一份 contract,Runtime 与卡片页共用 export const jwtAttachment = defineAttachment<JwtPreview>("plugin.example.decode.jwt"); // detector.ts — 通过 ctx.item 写入,不返回值 export async function detectJwt({ content }, ctx) { if (content.kind !== "text") return; const jwt = tryDecodeJwt(content.text); // { header, claims, token } | undefined if (!jwt) return; // 本次没写 — 这个 detector 上一次的产出会被清掉 await jwtAttachment.set(ctx, [ { key: "main", payload: jwt, card: { title: `JWT · ${jwt.header.alg}` } }, ]); } // plugin.ts — definePlugin({ detectors: { jwt: detectJwt } })
写入先暂存,处理函数成功返回后整批提交;抛错或超时则整批丢弃,上一次的产出保留。每次运行都整组替换这个 detector 上一次的 attachment 与搜索扩展 — 本次没写的就没了 — 与现值相同的写入会被跳过。attachment 类型以插件 id 开头,payload 是 JSON 对象,用 defineAttachment<T>(type) 可以让两侧共用同一份类型。detector 还可以用 ctx.item.setSearchExtension 补充可搜索的文本,或给条目打标签、置顶。
Renderer
Renderer 把一种 attachment 类型显示成卡片。它只在 manifest 里声明,没有 Runtime 处理函数。条目里设置卡片默认的 title、tint 与 buttons,page 是绘制卡片内容区的 HTML — 官方模板使用 Vue 3。
import { ctx } from "@clipbus/plugin-sdk/view"; import { autoFit } from "@clipbus/plugin-sdk/dom"; // 启动快照,payload 按 contract 定型 const { attachment } = ctx.renderer.context(jwtAttachment); render(attachment.payload); autoFit(); // 卡片高度跟随内容 if (isExpired(attachment.payload.claims)) { void ctx.renderer.update({ title: "JWT · 已过期", tint: "#DC2626" }); } ctx.renderer.onButton("copy", () => ctx.system.copyText({ text: JSON.stringify(attachment.payload.claims, null, 2) }));
卡片外观是数据。Clipbus 依次叠加:manifest 默认值、attachment 上的 card(随 attachment 写入,或由页面写回),以及页面运行中调用的 ctx.renderer.update({ title, tint, buttons, height })。省略的字段保持不变,null 回到 manifest 默认值;title、tint 与 buttons 会写回 attachment,height 只影响本次显示。按钮点击(包括对应的数字快捷键)通过 ctx.renderer.onButton 送达。
每个 renderer 在 manifest 条目中用 { min, max } 设置 height,单位 pt(1–800):固定高度把两者写成同一个值,省略则使用默认的 80–400。要让卡片高度跟随内容,调用 @clipbus/plugin-sdk/dom 的 autoFit(),它观察内容尺寸,并在该范围内发送 renderer.update({ height })。
Action
Action 按需把当前值转换成新值。它以 manifest 中的 actions[].id 为 key,只在当前值的种类属于它的 inputKinds 时出现。处理函数接收 { item, content }:item 是 Action Flow 的起点条目,content 是当前值(文本或图片),因此结果可以继续作为下一步输入。它返回 result.text(text)、result.image(path) 或 result.none(),都可以附带一句给用户看的提示。
import { result } from "@clipbus/plugin-sdk/runtime"; export async function decodeBase64({ content }) { if (content.kind !== "text") return result.none("不是文本"); const text = tryDecodeBase64(content.text); return text === undefined ? result.none("不是 Base64") : result.text(text); }
图片结果是插件自己写出的文件,例如放在 cache/ 下;Clipbus 检查后复制一份自己保存。在 Action 面板里,结果先预览,由用户显式推进到下一个 Action;none 不可继续,结果本身也不会自动级联。
插件合集
开源插件合集 scubers/awesome-clipbus-plugins 收录官方插件与插件模板,每个插件独占一个顶层目录、各自独立构建。无需 clone 即可安装:打开 设置 → 插件 → 添加插件 → Git 仓库,填入仓库地址,并把子目录设为插件的目录名,例如 clipbus-decoder-plugin。
目前收录 10 个插件 —— decoder、formatter、converter、extractor、preview、text、image、generator、cron 和 vibe,各自的功能见合集 README。template-plugin/ 是官方脚手架与全能力参考:用来复制出新插件,而不是直接安装。
插件结构与 manifest
插件按功能拆分 — 每种能力位于独立文件夹。plugin.ts 是注册 detector、action 与消息处理函数的 definePlugin 入口。manifest.json 声明插件的身份、Runtime 入口,以及它提供的 detector、renderer 与 action。
{ "schemaVersion": 6, "plugin": { "id": "plugin.example.decode", "title": "Decoder", "version": "1.0.0" }, "install": { "entry": "scripts/install.mjs" }, "runtime": { "nodeEntry": "dist/plugin.cjs" }, "detectors": [ { "id": "jwt", "title": "JWT 检测", "inputKinds": ["text"] } ], "renderers": [{ "id": "jwt-card", "title": "JWT", "attachmentType": "plugin.example.decode.jwt", "page": "dist/ui/jwt-card/index.html", "height": { "min": 120, "max": 480 }, "tint": "#0F766E", "buttons": [{ "id": "copy", "title": "复制 claims" }] }], "actions": [ { "id": "base64", "title": "Base64 解码", "inputKinds": ["text"] } ] }
runtime.nodeEntry、install.entry、renderers[].page 等路径都相对插件根目录。每个 attachmentType 都以插件 id 开头,消息不在 manifest 里声明。可选的 install hook 是安装插件时运行的 Node 脚本,Clipbus 会在运行前请你确认;需要 npm 的 hook 通过 CLIPBUS_PLUGIN_NPM_PATH 获得。依赖包自带的安装脚本不会运行(Clipbus 设置了 npm_config_ignore_scripts);提交 package-lock.json 并用 npm ci 安装。
Runtime 与卡片页不能联网,Runtime 也不能启动其他程序;Runtime 只能读插件包、读写自己的 data 与 cache。要读写用户的文件夹,在 manifest 的 permissions.folders 里为每个用途声明一个槽位(read 或 readWrite),用户启用插件时选择具体文件夹,Runtime 用 ctx.plugin.folder(id) 取得路径,未选择时为 null。
your-plugin/ ├── manifest.json # 身份、Runtime 入口、能力声明 ├── package.json # 依赖 @clipbus/plugin-sdk ├── scripts/ # 构建、校验、install hook ├── src/ │ ├── features/<name>/ │ │ ├── payload.ts # defineAttachment contract │ │ ├── messages.ts # defineMessage contract │ │ ├── detector.ts # 通过 ctx.item 写入 │ │ ├── action.ts # 返回 result.* │ │ ├── app.vue # 卡片页 │ │ └── main.ts # 页面入口(+ index.html) │ ├── shared/ │ ├── preview/ │ └── plugin.ts # definePlugin — detector、action、消息 └── tests/
快速开始
从插件合集开始:clone github.com/scubers/awesome-clipbus-plugins,复制其中的 template-plugin/ —— 官方脚手架,覆盖全部扩展点(detector、从紧凑到展开的 renderer、action 与类型化消息),复制出来就是一个可直接投入开发的生产级骨架。
推荐 —— 交给 AI 来写。 合集在 .agents/skills/ 下自带两个 skill:clipbus-plugin-generator 先勘察现有插件,再把功能并入最匹配的插件或基于模板新建插件,并跑完全量验证;clipbus-template-sync 把模板与 SDK 的更新同步到现有插件。模板本身自带中英双语的 README / GUIDE 以及专为 AI 工具编写的 AGENTS.md,并依赖 @clipbus/plugin-sdk,其 API.md 为真相源。
本地开发需要 Node.js 22 或更新版本。
npm install # 安装 @clipbus/plugin-sdk npm run dev # Vite 预览工作台 # → 用模拟的 Clipbus 预览卡片 npm run build # 类型检查 + lint + 构建 → dist/ npm test # 针对构建产物的 Runtime 测试 npm run verify # manifest 校验 + 构建 + 测试(PR 门禁)
在应用内调试时,打开 设置 → 插件 → 添加插件 → 本地开发插件,选择包含 manifest.json 的目录。插件会出现在开发插件区段,源文件变化时 Runtime 会自动重启。manifest 声明了 install hook 时,Clipbus 会在运行前请你确认(cwd = 该目录);日志写入 <AppData>/development-plugins/<id>/install-logs/,不污染 git 状态。在插件的操作菜单里,用重新加载重新读取 manifest,运行安装脚本…重新执行 install hook,安装失败时查看安装日志。自己的日志用 ctx.log.info(…) 写入;forwardConsole() 还会把卡片页的 console 输出也写进插件日志。
API 参考(SDK)
作者可使用的所有内容均生成到 SDK 包中,始终与你安装的版本一致,不会与本页内容产生偏差。无需阅读 SDK 的内部源码。
- 文档索引 —
node_modules/@clipbus/plugin-sdk/docs/README.md - 真相源 —
node_modules/@clipbus/plugin-sdk/API.md(生成;有冲突以此为准) - 从 SDK 0.x 升级 —
docs/migration-v1-v2.md;manifest v4 的插件在迁移前会显示为需要更新 - SDK 形状规则 —
SPECIFICATION.md - npm — @clipbus/plugin-sdk · 通过
npm update @clipbus/plugin-sdk更新
插件数量限制
免费版每类最多支持 3 个启用中的插件(detector / renderer / action)。Clipbus Pro 将这些上限提升至 无限制插件(同时包含云同步),为一次性买断。详见 定价页。