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 的页面就会在条目下方的附件面板里画出一张卡片。

Copydetectorattachmentrenderercard

每张卡片在附件面板里有一个 chip。卡片外框 — 标题、颜色和按钮 — 由 Clipbus 直接按数据绘制,所以卡片首次显示时外观就是对的,你的页面同时加载到内容区。

Runtime 与卡片页

一个插件由两个相互独立的部分组成,它们都只和 Clipbus 通信。Runtime 是每个插件一个的常驻 Node 进程,你的 detector、action 和消息处理函数通过一次 definePlugin({ detectors, actions, messages }) 调用注册。每张卡片都是一个 web 页面,由 Clipbus 按 manifest 中 renderer 的 page 在 WebView 里加载。两侧不共享内存:页面通过类型化的消息找到自己的 Runtime,由 Clipbus 中转。

Node runtime卡片页
入口runtime.nodeEntryrenderers[].page HTML
你写什么definePlugin({ … })HTML + ctx 调用
导入自@clipbus/plugin-sdk/runtime@clipbus/plugin-sdk/view
宿主 APIctx — 处理函数的第二个参数ctx — 模块级对象

两侧拿到的 ctx 有相同的域 — ctx.item、ctx.system、ctx.env、ctx.settings、ctx.log — 两侧都有的方法签名也相同。ctx.item 永远指向本次调用的主体条目:被检测的条目、Action Flow 的起点条目,或者卡片所在的条目。

并非每种能力都需要两侧 —— 这正是关键区别:

能力运行于Runtime 处理函数卡片页
detectorNode runtime(input, ctx) => void—
renderer卡片页—renderers[].page
actionNode runtime(input, ctx) => result.*—
messageNode runtime,由卡片页调用contract.handle(fn)contract.call(data)

只有卡片的插件可以不写 Runtime,Clipbus 就不为它启动 Node 进程。消息是两侧之间的类型化通道:在两侧共同导入的模块里用 defineMessage<Req, Resp>(name) 定义一次,在 Runtime 里处理,在页面里调用。

features/decode/messages.ts · illustrative
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。

features/decode · illustrative
// 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。

features/decode/main.ts · illustrative
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(),都可以附带一句给用户看的提示。

features/decode/action.ts · illustrative
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。

manifest.json
{
  "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。

project structure
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 或更新版本。

terminal
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 将这些上限提升至 无限制插件(同时包含云同步),为一次性买断。详见 定价页。