Back to home

sulfide2085

dsh-custom-context-menu

DSH Web custom context menu: extensible provider API + open workspace folder in Explorer

Stars
0
Language
JavaScript
Created
Aug 16, 2026
Updated
Aug 16, 2026

Introduction

dsh-custom-context-menu

English · 中文

DeepSeek Harness(DSH)Web UI 的自定义右键菜单插件:全局接管浏览器右键,提供零依赖的扩展菜单框架,并内置“右键工作区 → 在资源管理器中打开文件夹”。

特性

  • 全局 contextmenu 接管:捕获阶段拦截浏览器右键,输入框/文本域/可编辑元素与菜单自身除外
  • 可扩展 provider:一行 register() 注册菜单项,支持分隔线、分组标题、禁用项、危险项、hint
  • 键盘导航:↑/↓ 循环选择,Enter/Space 触发
  • 智能定位:菜单超右/下边缘自动翻转,并夹紧在视口内
  • 无命中不打扰:没有 provider 命中时放行浏览器原生菜单,不再显示“暂无菜单项”占位
  • 打开工作区文件夹:右键侧边栏工作区行 → 「打开文件夹」→ host 半直接拉起系统文件管理器
    • Windows:explorer.exe <path>,路径自动规范化为反斜杠,并通过前台激活 helper 把窗口带到最前
    • macOS:open <path>
    • Linux:xdg-open <path>
  • 失败可见:打开前校验目录存在;路径失效时明确报错,不会假报“已打开文件夹”

安装

本地安装

cd dsh-custom-context-menu
pnpm install
cd ..
dsh plugin --profile web add ./dsh-custom-context-menu

重启 DSH Web 生效。

从 GitHub 安装

dsh plugin --profile web add "https://github.com/sulfide2085/dsh-custom-context-menu.git"

dsh plugin add 会把包以 link:/Git 依赖方式装进 profile,并把本包 cordis.patch.yml 声明的 bundle 层追加到 dsh.profile.bundles

快速使用:注册菜单项

在浏览器控制台(F12)或其他插件中:

window.dshContextMenu.register((target, event) => {
  const row = target.closest("[data-session-row]");
  if (row === null) return null; // 不处理,交给下一个 provider

  return [
    { type: "label", text: row.dataset.title },
    { label: "复制标题", onClick: () => navigator.clipboard.writeText(row.dataset.title) },
    { type: "separator" },
    { label: "删除会话", danger: true, onClick: () => { /* ... */ } }
  ];
});

菜单项模型

形式说明
"字符串"简写 item,label 即字符串
{ label, onClick?, disabled?, danger?, hint? }常规项;onClick(item, ctx)ctx{ target, x, y }
{ type: "separator" }分隔线(连续分隔线自动压缩,首尾丢弃)
{ type: "label", text }只读分组标题

完整 API(window.dshContextMenu

成员说明
register(provider)注册 (target, event) => items[] | null,返回注销函数;多个 provider 依次询问,第一个非空结果胜出
setEnabled(bool) / enabled全局开关;关闭后完全放行浏览器原生右键菜单
open(x, y, items)命令式在视口坐标弹菜单(不经过拦截器)
close() / isOpen / itemCount关闭 / 状态查询
debug()诊断各部件状态,便于排障

工作区“打开文件夹”的匹配规则

右键目标向上查找 div[role="treeitem"],三路匹配 ctx.workspaces.list 条目,任一唯一命中即提供菜单项:

  1. 行尾省略号按钮的 aria-label 匹配官方 workspace 字典
  2. 行内首个文本 span 匹配 workspace title
  3. 行内首个文本 span 匹配目录 path 的 basename(title 被重命名时)

重名工作区不提供菜单项,避免打开错误目录。

行为约定

  • 输入框 / 文本域 / 可编辑元素右键 → 放行原生菜单(保留复制/粘贴)
  • 自定义菜单自身右键 → 放行(避免关掉又弹)
  • 其余区域且 provider 命中 → preventDefault() + 弹出自定义菜单
  • 无 provider 命中 → 关闭已打开的自定义菜单并放行原生菜单
  • 关闭方式:点击菜单项 / 点击菜单外 / Esc / Tab / 锚点滚出视口 / 窗口缩放或失焦
  • 菜单尾部固定附“打开浏览器菜单”,点击后下一次右键显示浏览器原生菜单(仅一次)

文件结构

文件作用
lib/client.js浏览器半:全局拦截 + 菜单框架 + 工作区 provider(点击调用 folderOpener/open 远程)
lib/index.jshost 半:folderOpener Typert 远程服务(路径校验、平台命令分派、Windows 前台激活)
cordis.patch.ymlbundle patch,安装时自动挂载
test/node:test 单元测试

开发

pnpm install
pnpm test

依赖

  • zod:host 侧 wire schema
  • @deepseek-ai/dsh-typert-protocol:Typert Remote 服务基类
  • @deepseek-ai/cordis:Service 基类(Typert 协议 peer)

浏览器半(lib/client.js)为手写 bundle,零依赖、全部原生 DOM。

限制

  • 菜单项为单层(无子菜单)
  • 工作区匹配依赖 DSH Web 官方 workspace 行结构与 ctx.workspaces 服务
  • 依赖浏览器 contextmenu 事件;Electron/浏览器外壳同样适用

License

MIT