跳转到内容

右键菜单与命令

右键菜单是”把插件能力送到用户手边”的最短路径:用户选中几行 → 右键 → 你的菜单项 → 你的插件带着选区上下文被打开。宿主负责渲染菜单与采集选区,插件只管声明和响应。

"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 则改为居中模态框打开指定视图。

点击时宿主采集该位置的选区,作为 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文件树任意节点节点信息

宿主打开你的视图后 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 的菜单项以居中模态框打开视图。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()
})

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 分流。