跳转到内容

开发插件:贡献点

星汉的插件之间可以互相组合能力,机制叫贡献点:一个插件声明一个”点”, 其它插件向这个点投递纯数据”投稿”,点的所有者决定怎么用这些数据。最常见的 玩法是给编辑器的 AI 能力添砖加瓦——而且最简单的插件只需要一个 plugin.json 文件,一行代码都不用写。

最小可用示例:给选中文字加一个 AI 动作

Section titled “最小可用示例:给选中文字加一个 AI 动作”

在 Markdown / 智能文档里选中一段文字会弹出气泡,✨ 旁边的每个动作按钮都可能 来自不同的插件。加一个”译成英文”动作的完整插件长这样:

// 目录结构:my-translate/plugin.json + my-translate/ui/index.html(占位说明页)
{
"id": "yourname.my-translate",
"name": "我的翻译",
"version": "0.1.0",
"runtime": "static",
"ui": "ui",
"capabilities": [],
"contributes": {
"contributions": [
{
"point": "xinghan.richtext-field/selection-actions",
"data": {
"id": "translate-en",
"icon": "🌐",
"title": "译成英文",
"order": 30,
"prompt": "把这段翻译成英文",
"hints": "把用户选中的文档片段翻译成地道的英文。只输出译文本身,不要解释;保留 markdown 行内格式。"
}
}
]
}
}

装上之后,选中文字的气泡里就多了一个 🌐 按钮——弹窗、流式输出、替换选区这些 交互全由编辑器内置的执行引擎完成,你的投稿只回答”做什么”:

  • hints 是给 AI 的系统提示(这个动作”怎么做”)
  • prompt 可选:填了就是一键动作(点击立即执行),不填则等用户输入指令
  • icon 单个 emoji,order 排序权重(小者靠前)
点投什么出现在哪
xinghan.richtext-field/selection-actions选段 AI 动作(如上)Markdown/智能文档的选中气泡
xinghan.agent-anthropic/skillsAgentSkill(知识+快捷指令+工具)主聊天 / 编辑点 AI 微面板

公式配置、选项配置这些编辑现场都有一个 ✨ 微面板。投一条 surfaces: ["micro-panel"] 的 skill,就能给指定现场加快捷指令和领域知识:

{
"point": "xinghan.agent-anthropic/skills",
"data": {
"id": "formula-tips",
"name": "公式小抄",
"description": "公式微面板的函数速查",
"instructions": "写公式时优先用 IF/AND/OR 组合;日期差用 DATEDIF(开始, 结束, \"d\")。",
"quickChips": ["写一个逾期天数公式(今天减截止日期)"],
"surfaces": ["micro-panel"],
"scope": { "fieldType": "formula" }
}
}
  • scope.fieldType 限定现场(formula 行内公式 / crosslookup 跨表公式 / select 选项字段),scope.nodeType 用于非字段现场(如 automation); 都不填则全局可用
  • quickChips 渲染成一键 chip,instructions 并进该现场的 AI 提示词 (多个投稿合计有长度预算,超出会被截断——长知识请投主聊天 skill)

surfaces: ["main-chat"] 的 skill 进 agent 的技能目录(渐进披露:目录常驻、 全文按需加载)。带后端进程的插件还可以投工具——声明纯数据,执行时宿主 回调你插件的 /_agent/execute-tool。跨插件投稿的工具 v1 只放行只读 (effect.kind: "readonly"),写入面等沙箱治理开放后再扩。

插件还能定义一张表怎么看——日历、画廊、地图、甘特都是”同一张表的另一种 投影”。声明一个 tableViews 贡献,「新建视图」菜单里就会多出你的视图类型:

"contributes": {
"tableViews": [
{ "viewKey": "cards", "name": { "zh-CN": "卡片视图" }, "icon": "🃏", "entry": "ui/index.html" }
]
}

用户建了这种视图后,视图激活时编辑区渲染你的 entry 页面(iframe),宿主会:

  • 推 context/init:context.tableNodeId 是当前表、context.viewId 是视图 id
  • 推 theme/init:tokens 字段带全套主题变量(--background / --foreground / --border 等,值是完整颜色)——写进 :root 就自动跟随亮暗色
  • 应答数据 RPC(postMessage,id 必须是数字): ui/dataEngine.getFields、ui/dataEngine.queryRecords(v1 只读——展示归 插件,编辑归内核)、ui/openRecordList(把行交回宿主弹层,点条目跳转定位)

有构建的插件直接用 @xinghan/plugin-sdk/ui 的 ui.dataEngineGetFields / ui.dataEngineQueryRecords / ui.openRecordList; 零构建的单文件插件抄样板 card-view(内联 postMessage 桥约 40 行)。

你的插件也可以开放自己的扩展面给别人投稿:

"contributes": {
"contributionPoints": [
{
"point": "yourname.my-plugin/my-actions",
"schema": { "required": [{ "key": "id", "type": "string" }] }
}
]
}
  • 点名必须以自己的插件 id 为前缀(所有权即命名空间,防抢注)
  • schema 做顶层必填字段浅校验,不合格的投稿会被拒并计入统计
  • 消费端(你的 UI)用只读端点拉有序快照: GET /plugins/_meta/contributions?point=yourname.my-plugin/my-actions ——响应里每条投稿带 pluginId 和人类可读的 pluginName,方便标注归属
  • 投稿是纯数据:单条 ≤64KB,每插件对每点 ≤64 条
  • 投稿可以先于点声明(挂起,点声明后自动转正)——插件加载顺序无关
  • 插件卸载/停用时它的投稿和点整体退场,重新加载自动恢复
  • 静态插件(runtime: "static")只有 manifest 这一条投稿途径;带后端进程的 插件还可以走运行时 API(cap.contributions.*),两者等价

把插件目录放进 ~/xinghan/plugins/ 后在设置里重载插件(或打包 zip 走扩展 市场的「从本地安装」)。投稿被拒时原因写在插件宿主日志 (~/xinghan/logs/plugin-host.log)里,搜 [contributions]。