Bundle
dsh-filemenu
Right-click context menus for the DeepSeek Harness (DSH) web UI: open files, reveal in Explorer/Finder, open in a detected editor, copy paths; workspace/session context menus. 对话文件右键菜单插件。
- Source
- ltsone9
- stars
- 1 stars
- License
- MIT
- Updated
- Updated 13 days ago
Readme
# DshFileMenu
[](./LICENSE)
[](https://www.npmjs.com/package/dsh-filemenu)
[](https://github.com/deepseek-ai/deepseek-harness)



**English** | [中文](./README.zh-CN.md)
Right-click context menus for the [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (DSH) web UI.
Files produced in a conversation, inline file references, sidebar **workspace (project)** rows and **session** rows — open them, reveal them in the OS file manager, open them in a detected editor, or copy their paths. The menu items follow the conventions of the latest Codex desktop apps.
Built as a **dynamic Cordis plugin** — purely additive, never replaces a shipped UI seat, and every side effect is removed when the plugin stops.
---
## ✨ Features
- **File context menu** — right-click any of:
- produced-file chips in the conversation ("Produced" row),
- inline code file references in assistant messages,
- file-path links on tool cards (best effort),
- plain absolute paths in assistant text (for example `D:\\dayu\\XMDJ\\frontend\\voter-web\\dist`),
- → **Open** · **Open containing folder** · **Open in editor** ▸ · **Open with another program…** · **Copy path** · **Copy relative path**
- **Workspace (project) context menu** — right-click a sidebar workspace row:
- **Open containing folder** · **Open** (switch to the workspace's session) · **Copy path**
- **Session context menu** — right-click a sidebar session row:
- **Open containing folder** · **Open** (switch to the session) · **Rename** · **Fork** · **Archive** · **Copy path**
- **Open in editor** — a submenu of editors auto-detected on the host: VS Code, Cursor, Windsurf, VSCodium, Sublime Text, Notepad++, Typora, HBuilderX, Zed, IntelliJ IDEA, WebStorm, PyCharm, Rider, CLion, Neovim, Vim, gedit.
- **Open with another program…** — the Windows "Open With" dialog.
- **Theme aware** — light mode follows the harness alias tokens; dark mode uses a near-black surface with near-white text and lines.
- **Positioned exactly at the cursor** — single column, edge-clamped so it never runs off the viewport; every action has an inline SVG icon.
## 📦 Installation
### Prerequisites
- A running [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) web UI.
- A host OS with a native opener (Windows, macOS, or a desktop Linux). The OS-dependent menu items enable automatically when the Host reports `canOpenPath` (headless Linux hosts hide them).
### Option A — Quick install as a dynamic plugin (no restart)
> DshFileMenu ships as a **dynamic Cordis plugin**: two plain-JavaScript files, one Host half and one Client half. A dynamic plugin is defined and activated **inside a session** and takes effect immediately.
1. Get the two plugin files:
- [`plugin/host.js`](./plugin/host.js) — the Host half
- [`plugin/client.js`](./plugin/client.js) — the Client half
(Also fetchable via the raw URLs: [`host.js`](https://raw.githubusercontent.com/ltsone9/dsh-filemenu/main/plugin/host.js) and [`client.js`](https://raw.githubusercontent.com/ltsone9/dsh-filemenu/main/plugin/client.js).)
2. Open any session in the DSH web UI and ask the agent to install it:
> 请安装 dsh-filemenu:用 `cordis_define`(`kind: new`,`idPrefix: file`)创建插件,`code.host` 填入 `plugin/host.js` 的完整内容,`code.client` 填入 `plugin/client.js` 的完整内容,然后 `cordis_run`(`mode: run`)激活。
(Equivalent English instruction: create with `cordis_define` — `kind: new`, `idPrefix: file`, `code.host` = the full content of `plugin/host.js`, `code.client` = the full content of `plugin/client.js` — then activate with `cordis_run`, `mode: run`.)
3. **Approve the first activation.** The Client half asks for authorization in the Run card — click **Allow** (double-check also trusts future versions). Without approval the plugin stays `awaiting-approval`.
4. Done. Right-click a produced file, a workspace row, or a session row to verify.
> **Note:** dynamic plugins are **process-local**. After the Harness restarts you need to define and run them again (Option B makes the plugin persistent).
### Option B — Persistent install (official CLI route) ✅ v1.1.6
DshFileMenu is shipped in the standard package shape (`dsh.bundle` / `dsh.client` declarations), so it can be installed as a **permanent profile plugin** that survives restarts:
```sh
# from the repository checkout
dsh plugin --profile <profile> add .
```
> **Which `<profile>`?** The DSH **Desktop** app runs the `desktop` profile → `--profile desktop`. A standalone web-server deployment runs `dsh web` and uses the `web` profile → `--profile web`. Check `~/.dsh/profiles/` for your active profiles.
**Install steps (do these in order):**
1. **Install the package** into the profile: `dsh plugin --profile desktop add .` — this runs `pnpm add` in the profile directory and auto-mounts the package because its `package.json` declares `dsh.bundle` (the `cordis.patch.yml` row) and `dsh.client` (the browser half, served at `/plugins/dsh-filemenu/client.js`).
2. **Restart the Harness service.** The host composition reloads and the web shell rebuilds the client bundle. On the Desktop app, restart the app (or the backend).
3. **Verify** — right-click a produced file / workspace row / session row in the web UI.
4. Manage later with the same command: `dsh plugin --profile desktop remove dsh-filemenu`, `update dsh-filemenu`, `why dsh-filemenu` (any pnpm argument is forwarded verbatim).
> **Published to npm** (`dsh-filemenu@1.1.6`) — `dsh plugin --profile desktop add dsh-filemenu` installs directly from the registry. A local checkout (`add .`) also works. Prerequisite: `pnpm` on `PATH` (the CLI forwards to it).
If you run the Harness from source, a patch overlay also works (see the official [Your first plugin](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/user/develop/basic/index.md) tutorial):
```sh
pnpm dsh web --patch ./cordis.patch.yml
```
References: [official plugin docs](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/user/develop/basic/index.md), [deepseek-harness-plugins collection](https://github.com/linxiecoder/deepseek-harness-plugins), [dsh-market](https://github.com/2BingLing/dsh-market).
## 🆕 Recent changes
### v1.1.6
- Added right-click recognition for plain absolute paths in assistant text; the path under the mouse is preferred, and the fallback only accepts a single path in the current text block.
### v1.1.5
- Removed the pin/unpin conversation feature from sidebar session context menus.
### v1.1.4
- Added inline SVG icons to every primary menu action and editor submenu row.
### v1.1.3
- Added **Rename**, **Fork** and **Archive** to sidebar session context menus; archive is confirmed before the existing safe `archiveSession` API is called.
### v1.1.2
- Expanded editor detection with Typora, HBuilderX, Zed, IntelliJ IDEA, WebStorm, PyCharm, Rider, CLion, Neovim and Vim.
- Updated both English and Chinese documentation with direct npm installation and recent-change notes.
### v1.1.1
- Fixed the packaged Host startup race by declaring `inject: ['webServer']`; the `/dsh-filemenu/rpc` route now waits for the web server before registering.
- Improved packaged RPC error reporting for HTTP errors, empty responses and invalid JSON.
### v1.1.0
- Added the persistent package form with `dsh.bundle`, `dsh.client`, `cordis.patch.yml`, a standard `__ModuleLoader__` Client bundle and Host HTTP RPC.
- Published `dsh-filemenu@1.1.0` to npm.
## 🖱️ Usage
Right-click the target; the menu appears exactly at the cursor.
| Surface | Menu items |
| --- | --- |
| Produced-file chip / inline reference / tool-card link | Open · Open containing folder · Open in editor ▸ (detected editors) · Open with another program… · Copy path · Copy relative path |
| Sidebar workspace (project) row | Open containing folder · Open · Copy path |
| Sidebar session row | Open containing folder · Open · Copy path |
Platform behavior:
- **Windows** — "Open containing folder" opens Explorer **with the file selected** (`explorer /select,<path>`); "Open" uses `Invoke-Item`; "Open with another program…" opens the system dialog (`rundll32 shell32.dll,OpenAs_RunDLL`).
- **macOS** — reveal uses `open -R`; open uses `open`.
- **Linux** — reveal/open use `xdg-open` (desktop only).
- "Open with another program…" is **Windows-only** and hidden elsewhere.
- When the Host cannot open paths (`canOpenPath = false`, e.g. remote web access or headless Linux), all OS-dependent items are disabled; copy items keep working.
## ⚙️ How it works
```
┌────────────────────────── Client (browser) ──────────────────────────┐
│ shell.overlay entry hosts the menu; document capture-phase │
│ `contextmenu` listener detects targets via stable DOM markers │
│ (data-produced-files-row / code button[title] / │
│ data-dsh-workspace-drop-target + role=treeitem rows) │
│ Targets are resolved from live sessions/workspaces snapshots. │
└───────────────┬──────────────────────────────────────────────┬────────┘
│ host.call (dynamic) / fetch (packaged RPC) │ client services
▼ ▼
┌────────────────────────── Host (Node) ────────────────────────┐
│ fs service resolves paths; subprocess service launches │
│ native commands; platform derived from processPath shape │
│ (the Host sandbox has no `process` global). │
└────────────────────────────────────────────────────────────────┘
```
Host RPCs:
| RPC / action | Purpose | Typical command |
| --- | --- | --- |
| `open` | Open with the default application | `powershell Invoke-Item` / `open` / `xdg-open` |
| `reveal` | Reveal in the OS file manager | `explorer.exe /select,` / `open -R` / `xdg-open` |
| `editors` | Detect installed editors + report host platform | PATH resolution + install-path probes |
| `openWith` | Launch a detected editor with the file | `<editor-exe> <path>` |
| `openWithDialog` | Windows "Open With" dialog | `rundll32 shell32.dll,OpenAs_RunDLL` |
The transport depends on the install form: the **dynamic** quick-install uses `harness.handle` (package-private RPC); the **packaged** form uses an HTTP RPC endpoint (`POST /dsh-filemenu/rpc`, registered on the `webServer` service, called with `fetch`). Both dispatch to the same actions above.
Editor detection resolves CLI names through `subprocess.resolveExecutable` (PATHEXT-aware on Windows — finds `code.cmd` etc.), traces a `.cmd`/`.bat` bin script back to the real GUI executable in its install directory, and probes known absolute install paths as a fallback.
## 🔧 Configuration
- **Add a custom editor**: edit the `EDITOR_SPECS` table in [`plugin/host.js`](./plugin/host.js) and mirror the entry in [`lib/index.js`](./lib/index.js) — each spec has `commands` (PATH-resolvable names), `exeNames` (GUI executables next to a `bin/` script), and `installs` (absolute install paths).
- **Menu text**: edit the `zh` / `en` dictionaries in [`plugin/client.js`](./plugin/client.js).
- **Theme**: light mode uses harness alias tokens; dark mode colors live in `MENU_CSS` (`data-fm-theme="dark"` block) in [`plugin/client.js`](./plugin/client.js).
## 🚑 Troubleshooting
| Symptom | Check |
| --- | --- |
| Right-click does nothing | Is the plugin `running` (`cordis_inspect_self`)? Was the first Client activation approved? Try refreshing the page. Target a supported surface (produced chip / inline reference / workspace row / session row). |
| OS items are grayed out | `canOpenPath = false` — remote web access or a headless host cannot open paths natively. |
| Menu shows a red error | Read the message shown in the menu (e.g. `sessions service unavailable`); upgrade to the latest version if it references old field names. |
| Editor submenu is empty | The editor is not on `PATH` and not in a probed install location — add it to `EDITOR_SPECS`. |
| Menu does not follow the cursor | Use the latest version; earlier builds were missing the `position: fixed` styling. |
## ❓ FAQ
- **Is any shipped UI replaced?** No. DshFileMenu only adds a `shell.overlay` entry; all official slots (`tool.call.toolview`, `conversation.chat.turnTail`, the sidebar browser, …) are untouched.
- **Do I need to restart the Harness?** No — the dynamic-plugin flow takes effect immediately and cleans up on stop.
- **Why does the dynamic plugin disappear after a restart?** Dynamic plugins are process-local by design. Use Option B (`dsh plugin --profile desktop add dsh-filemenu`) for the persistent npm package, or re-define the dynamic copy.
- **Which platforms are supported?** Windows is primary; macOS and Linux work where a native opener exists. "Open with another program…" is Windows-only.
- **How do the sidebar rows resolve?** Workspace/session targets are matched by displayed title against the live snapshots (most recent session wins on ties), constrained to the containing workspace in grouped view.
## 🛠️ Development
```
dsh-filemenu/
├── README.md English documentation
├── README.zh-CN.md 中文文档
├── LICENSE MIT license
├── package.json package metadata (dsh.bundle / dsh.client declarations)
├── cordis.patch.yml composition row inserted by `dsh plugin add`
├── lib/
│ ├── index.js Host half — persistent profile plugin (webServer RPC)
│ └── client.js Client half — standard __ModuleLoader__ web bundle
└── plugin/
├── host.js Host half — dynamic quick-install copy (code.host)
└── client.js Client half — dynamic quick-install copy (code.client)
```
- `plugin/` holds the **dynamic quick-install** copies: plain JavaScript function bodies that return a Cordis plugin — no TypeScript, JSX, or bundler involved.
- `lib/` holds the **packaged** halves used by Option B. `lib/client.js` is a standard web bundle (`window.__ModuleLoader__.load`, `require('react')`, manual stylesheet injection, `fetch`-based RPC).
- Keep both in sync when changing behavior (the dynamic copy uses `harness.handle` / `host.call`; the packaged copy uses the HTTP RPC endpoint).
Load the `cordis-plugin-development` skill in a DSH session for the full API surface used here (slots, harness, styles, theme).
Contributions are welcome — open an issue or a PR.
## 🗺️ Roadmap
- [x] File context menu (open / reveal / editor submenu / open-with / copy paths)
- [x] Sidebar workspace & session context menus
- [x] Theme-aware dark/light styling
- [x] Persistent npm-package form (Option B install via `dsh plugin add`) — v1.1.0
- [x] Publish to npm (`dsh-filemenu@1.1.6`)
- [ ] Screenshots & animated demo
- [ ] Detect user-selected custom editor paths from Harness settings
## 📜 License
[MIT](./LICENSE) © 2026 [ltsone9](https://github.com/ltsone9)
Install
dsh plugin --profile web add github:ltsone9/dsh-filemenu
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-filemenu from the hub
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.