sulfide2085 / dsh-custom-context-menu

Listed

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

mainModel View source

Installation

npx -y @deepseek-ai/dsh plugin --profile web add github:sulfide2085/dsh-custom-context-menu

This installation command is an unverified starting point generated from the GitHub repository address.

README

Maintainer-authored documentation snapshot.

View on GitHub ↗
Commit c774597Synced Aug 18, 2026

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

Project files and signals

Shown items are public repository signals detected in the directory snapshot.

TestsDetected

Repository information

Language
JavaScript
License
MIT
Last updated
Aug 16, 2026, 11:19 AM

Install deliberately

Review source code, permissions, lifecycle hooks, dependencies and network access. Test untrusted plugins in an isolated environment.