dsh-custom-context-menu
English · 中文
> A custom right-click context menu for DeepSeek Harness (DSH) Web UI: intercept the browser context menu, expose an extensible provider API, and ship a built-in "Open Workspace Folder" action.
Features
- Global contextmenu interception in the capture phase, except for inputs, textareas, and editable elements
- Extensible providers: register menu items with one
register()call; supports separators, group labels, disabled items, danger items, and hints - Keyboard navigation: ArrowUp/ArrowDown cycle, Enter/Space activates
- Smart placement: flips when near the right/bottom viewport edge
- No placeholder noise: when no provider matches, the native browser menu is allowed through
- Open workspace folder: right-click a workspace row in the sidebar and choose "Open Folder"
- Windows: explorer.exe <path> with path normalization and foreground activation - macOS: open <path> - Linux: xdg-open <path>
- No false success: the host validates that the directory exists before opening
Install
Local
cd dsh-custom-context-menu
pnpm install
cd ..
dsh plugin --profile web add ./dsh-custom-context-menuRestart DSH Web.
From GitHub
dsh plugin --profile web add "https://github.com/sulfide2085/dsh-custom-context-menu.git"Usage
In the browser console (F12) or from another plugin:
window.dshContextMenu.register((target, event) => {
const row = target.closest("[data-session-row]");
if (row === null) return null;
return [
{ type: "label", text: row.dataset.title },
{ label: "Copy title", onClick: () => navigator.clipboard.writeText(row.dataset.title) },
{ type: "separator" },
{ label: "Delete session", danger: true, onClick: () => { /* ... */ } }
];
});Item model
| Shape | Meaning |
|---|---|
"string" | shorthand item |
{ label, onClick?, disabled?, danger?, hint? } | regular item; onClick(item, ctx) receives { target, x, y } |
{ type: "separator" } | separator |
{ type: "label", text } | read-only group label |
Full API (window.dshContextMenu)
| Member | Description | | --- | --- | | register(provider) | register (target, event) => items[] \| null; returns an unregister function | | setEnabled(bool) / enabled | global switch | | open(x, y, items) | open a menu imperatively | | close() / isOpen / itemCount | close / state | | debug() | diagnostics |
Development
pnpm install
pnpm testFile layout
| File | Role |
|---|---|
lib/client.js | browser half: interception, menu framework, workspace provider |
lib/index.js | host half: folderOpener Typert remote service |
cordis.patch.yml | bundle patch |
test/ | node:test unit tests |
Limitations
- Single-level menu (no submenus)
- Workspace detection depends on the DSH Web workspace row DOM and
ctx.workspaces - Requires the browser
contextmenuevent
License
MIT