开发自定义插件
使用当前 SDK 和 CLI 创建、验证、安装并发布 Vibecape 自定义插件。
Vibecape 扩展可以添加文档操作、Dialog、完整 Main View、设置面板、自定义文档类型,以及需要宿主权限的 Main Action。开发时应以当前安装的 @vibecape/extension-kit 和 @vibecape/cli 为唯一权威来源。
准备环境
- Node.js 18 或更高版本
- pnpm
- 本地安装的 Vibecape,用于安装验证
使用旧示例前,先检查当前 CLI 和类型契约:
pnpm exec vibecape extension --help同时查看 manifest.schema.json 和 @vibecape/extension-kit 导出的类型声明。不要导入 Vibecape App 的私有模块。
初始化项目
pnpm dlx @vibecape/cli extension init ./my-extension \
--id com.example.my-extension \
--name "My Extension"
cd my-extension
pnpm install生成的扩展采用下面的目录结构:
my-extension/
manifest.json
package.json
README.md
tsconfig.json
assets/
icon.svg
main/
index.ts
renderer/
index.tsx不要手写 dist 文件。Main、Renderer、序列化 UI、发布压缩包和 vibecape-extension.json 都由 CLI 生成。
定义 Manifest
从描述扩展所需的最小 Manifest 开始:
{
"$schema": "./node_modules/@vibecape/extension-kit/manifest.schema.json",
"id": "com.example.my-extension",
"name": "My Extension",
"description": "用一句具体的话描述用户最终得到什么。",
"icon": "./assets/icon.svg",
"main": "./dist/main/index.js",
"renderer": "./dist/renderer/index.js",
"activationEvents": ["onStartup"],
"extensionApiVersion": 1
}package.json.version 是唯一的发布版本。所有路径必须位于扩展包内部;只有测试过真实的最低宿主版本后才添加 engines.vibecape;只有确实提供自定义格式时才声明 contributes.documentTypes。
注册 Main Action
需要宿主权限的操作放在 ExtensionMain Action 中。调用会跨越进程边界,因此必须验证所有输入。
import { ExtensionMain } from "@vibecape/extension-kit";
export default class MyExtension extends ExtensionMain {
readonly id = "com.example.my-extension";
activate(): void {
this.extension.action({
id: "example.readDocument",
title: "Read current document",
run: async (input) => {
const record = input && typeof input === "object" ? input : {};
const directId = "docId" in record ? record.docId : undefined;
const doc =
"doc" in record && record.doc && typeof record.doc === "object"
? record.doc
: {};
const contextId = "id" in doc ? doc.id : undefined;
const docId =
typeof directId === "string"
? directId
: typeof contextId === "string"
? contextId
: "";
if (!docId) throw new Error("docId is required");
const file = await this.app.files.read({ id: docId });
this.logger.info("Read document", { docId });
return { id: file.id, name: file.name, content: file.content };
},
});
}
}Action ID 应稳定且带命名空间,输入与返回值必须可以 JSON 序列化。密钥、登录、网络请求、文件系统操作和高开销任务都应留在 Main Runtime。
结构化 App Bridge 提供工作区、文件、资源、外部链接、Finder 和剪贴板等能力。具体签名以当前安装的类型声明为准。
注册 Renderer UI
默认导出 defineRenderer,并导出贡献项引用的每个组件:
import { defineRenderer } from "@vibecape/extension-kit/react";
export function HomePage() {
return <div>Extension main view</div>;
}
export function ExportDialog() {
return <div>Export workflow</div>;
}
export function ConfigPanel() {
return <div>Extension settings</div>;
}
export default defineRenderer(({ ui }) => {
ui.extension.mainview({
id: "home",
title: "My Extension",
component: HomePage,
});
ui.customize.mainview({
id: "home-entry",
title: "My Extension",
page: "home",
});
ui.doc.menu.root({ id: "open", title: "Open", order: 10 }).mainview("home");
ui.doc.menu.export({ id: "export", title: "Export", order: 20 }).dialog(ExportDialog);
ui.doc.menu.root({ id: "read", title: "Read", order: 30 }).run("example.readDocument");
ui.config.panel({ id: "settings", title: "My Extension", component: ConfigPanel });
});每个 .run(actionId) 都必须有对应的 Main Action;每个 .mainview(pageId) 都必须引用已声明的 Extension Main View。文档入口会收到当前文档上下文;Customize 入口可能在没有当前文档时打开,因此必须处理 doc 缺失的情况。
验证与本地安装
运行生成项目提供的脚本:
pnpm run validate
pnpm run typecheck
pnpm run build
pnpm run test完成前检查:
- Manifest 通过当前 CLI 校验;
- 声明的 Main 和 Renderer Bundle 已生成;
- 每个序列化组件都有对应导出;
- 每个序列化 Action 都有 Main 注册;
- 发布描述文件中的版本、大小和 SHA-256 与压缩包一致;
- 可以通过 Extensions → More → Install from folder 安装;
- 相关入口在有、无当前文档时均符合预期;
- 停止和重新启动扩展时,贡献项能够正确移除与恢复。
使用 GitHub Releases 发布
第三方扩展通过公开 GitHub 仓库分发:
- 按语义化版本更新
package.json.version; - 重新执行校验、测试和构建;
- 创建
v1.2.0形式的稳定 Tag; - 上传生成的压缩包和
vibecape-extension.json,不要重命名。
第三方扩展没有 vibecape extension publish 命令。Vibecape 会根据仓库 URL 读取最新的稳定 GitHub Release。