右键菜单与命令
右键菜单是”把插件能力送到用户手边”的最短路径:用户选中几行 → 右键 → 你的菜单项 → 你的插件带着选区上下文被打开。宿主负责渲染菜单与采集选区,插件只管声明和响应。
"contributes": { "contextMenus": [ { "id": "translateSelection", "title": { "zh-CN": "翻译选中 {count} 格", "en-US": "Translate {count} cells" }, "points": ["table.cells"] }, { "id": "exportRows", "title": { "zh-CN": "导出选中行" }, "points": ["table.rows"], "modal": { "view": "exporter", "width": 560, "height": 420 } } ]}title支持多语言对象与{count}占位符(宿主用选区数量插值);points决定出现在哪些右键位置;- 默认点击后打开你的右抽屉视图并把命令消息推进去;声明
modal则改为居中模态框打开指定视图。
五个贡献点与各自的上下文
Section titled “五个贡献点与各自的上下文”点击时宿主采集该位置的选区,作为 context 送达插件:
| point | 出现在 | context 内容 |
|---|---|---|
table.rows | 表格选中行右键 | { kind:'rows', tableNodeId, tableName, recordIds } |
table.cells | 表格选中单元格右键 | { kind:'cells', tableNodeId, tableName, recordIds, fieldIds } |
table.columnHeader | 列头菜单 | { kind:'columnHeader', tableNodeId, tableName, fieldIds, fieldName, fieldType } |
fileTree.table | 文件树里的表节点 | { kind:'table', tableNodeId, tableName } |
fileTree.node | 文件树任意节点 | 节点信息 |
在插件里响应
Section titled “在插件里响应”宿主打开你的视图后 postMessage 一条命令消息(含 commandId 与上面的 context)。UI 侧监听并按 id 分发:
window.addEventListener('message', (e) => { const msg = e.data if (msg?.type !== 'command') return if (msg.commandId === 'translateSelection') { const { tableNodeId, recordIds, fieldIds } = msg.context // 拿着选区调自己的后端:../api/translate }})拿到的是引用(表 id、记录 id、字段 id),不是数据本体 —— 数据由你的后端按需用 cap.dataEngine.getRecords 取(见读写表格数据)。这让菜单点击瞬间响应,也避免大选区把消息撑爆。
modal 形态的闭环
Section titled “modal 形态的闭环”声明了 modal 的菜单项以居中模态框打开视图。context 经上下文桥送达(不是 command 消息),做完后用 ui.resolveModal / ui.closeModal 关闭:
import { installContextBridge, getContext, onContextChange, ui } from '@xinghan/plugin-sdk/ui'
installContextBridge()onContextChange(() => { const ctx = getContext() // 含 modalId 与选区(tableNodeId/recordIds…) if (!ctx.modalId) return // …渲染表单,用户点确定: // void ui.resolveModal({ done: true }) // 关闭模态,结果给宿主 // 用户点取消:void ui.closeModal()})commands:命令与快捷键
Section titled “commands:命令与快捷键”contextMenus 之外还有更朴素的 commands 贡献 —— 全局命令与快捷键:
"contributes": { "commands": [ { "id": "newConversation", "title": "新建对话", "key": "cmd+l" } ]}key 写 mac 形态(cmd+…)。注意:快捷键的全局注册当前尚未接线(字段会被解析保存,但按下不触发)—— 现阶段命令的实际触发入口是右键菜单与宿主界面;声明 key 是给接线后预留的,别指望它现在就响。
如果你做的是自定义字段类型,还有一层只出现在你的字段上的菜单:fieldTypes[].columnMenus(列头)与 cellMenus(单元格,requiresValue: true 可要求”有值才显示”,cell 菜单的 modal context 还会带 cellValue 免回查)。全局菜单用 contextMenus,字段私有菜单用这两个 —— 别混。
- 查重
plugins/marketplace/dup-check/:contextMenus的真实完整闭环(选中列右键 → 查重面板); - Agent 插件的「引用到 Agent」:
table.rows/table.cells/fileTree.table三个点位共用一个id,按 context.kind 分流。