@qzsy/dsh-workspace-context-menu
v0.2.1
Published
Context menu for sidebar workspace (project) rows: right-click on desktop, long-press for a bottom action sheet on touch devices — new session, rename, copy path, open directory, open file list, and remove from sidebar.
Readme
@qzsy/dsh-workspace-context-menu
Packaged from the local DSH plugin
@local/dsh-workspace-context-menu; published under the@qzsyscope. It is a pure Client bundle and registers no Host route.
A DeepSeek Harness Client plugin that adds a context menu to the sidebar workspace (project) rows: right-click on desktop, long-press on touch devices, where it opens as a bottom action sheet.
Install
dsh plugin add @qzsy/dsh-workspace-context-menuWhat it does
Right-click any workspace row in the left sidebar tree — the same row that
already offers the hover … button — and a host-styled menu opens at the
pointer:
| Row | Behavior |
| --- | --- |
| 新建会话 / New session | Starts a fresh Session in that workspace (uiWorkspace.startSession). |
| 重命名… / Rename… | Opens a small dialog with the current name. Enter saves; an empty name or an existing name is refused before the request. |
| 复制路径 / Copy path | Writes the workspace directory to the clipboard and confirms with a short toast. |
| 打开目录 / Open directory | Hands the workspace directory to the operating system's file manager (Finder / Explorer / Files) through the Host's existing /open-in-app/open hand-off — the same route used by the shipped Open In App controls. The file-manager app is discovered from GET /open-in-app/apps; the item is hidden when the Host lists none, and a refused call surfaces as an error toast. |
| 用右侧面板打开文件列表 / Open file list in right panel | Selects a Session belonging to that workspace (or connects a blank one when the workspace has none) and opens the right sidebar's Files tab, which is rooted at the Session's workspace directory. |
| 从侧栏移除… / Remove from sidebar… | Confirms first (the folder and the Session history stay; its Sessions move to Ungrouped), then calls workspaces.delete. |
Mobile / touch
Right-click does not exist on phones and tablets. Long-press a workspace row for about half a second and the same actions open in a bottom action sheet:
- The sheet respects
env(safe-area-inset-bottom), dims the frame, and closes on the backdrop, 取消 / Cancel,Esc, rotation, or window resize. - The overlay is portaled to
document.bodywithz-index: 2147483000, so the sheet covers the mobile sidebar drawer instead of sliding in behind it. Theshell.overlayslot stays the mount point. - Moving the finger more than 10 px, scrolling the sidebar, or a second touch cancels the press, so normal scrolling never opens the sheet.
- The click synthesized at the end of a long press, plus the native
contextmenuevent some Android browsers emit, is swallowed so releasing does not also select or open the row. - Workspace rows opt out of the native long-press text callout on coarse pointers; the desktop pointer-anchored menu is unchanged.
- A short
navigator.vibratetick acknowledges the press where the platform supports it (Android).
Details that follow the host UI:
- Rendered in
shell.overlay(frame-wide, click-through layer), then portaled todocument.bodyatz-index: 2147483000; the sidebar component is not wrapped or replaced. - Workspaces come from the
useWorkspacesslot hook, so the menu always reflects the live list; a workspace removed elsewhere retires the open menu. - Theme tokens only (
--dsw-alias-*), light and dark, Chinese and English from the active locale. - Keyboard:
↑↓HomeEndwalk the rows,Enter/Spaceactivate,Esccloses,Tableaves; dialogs trapTaband focus their field/primary action. The anchored menu flips above the pointer near the viewport edge; the sheet is bottom-aligned by CSS and needs no pointer maths. - Dismisses on outside press and
Esc; while the anchored menu is open it also closes on sidebar scroll, window resize, or blur. The sheet keeps its own scroll area, and dialogs survive the software keyboard's resize. Right-clicking outside a workspace row keeps the browser/OS menu untouched.
Where it plugs in
| Surface | Mechanism |
| --- | --- |
| Workspace row context menu | A shell.overlay occupant listens for contextmenu on [data-row-key^="workspace:"], plus a 480 ms pointer long-press that opens the bottom sheet on touch devices. Its overlay is portaled to document.body so the mobile sidebar drawer cannot cover it. |
| Workspace facts and mutations | The installed Client services uiWorkspace, workspaces, and the useWorkspaces slot hook. |
| Open directory | The Host's existing /open-in-app/apps (discover the file-manager app) and /open-in-app/open (hand off { app, path }) routes. The plugin registers neither. |
| File list in right panel | The sidebarRight tab registry (openTab("files")), plus uiWorkspace.openSession()/openWorkspace() to select or connect the Session whose cwd roots the Files tree. |
| Host capability | None of its own. The bundle patch inserts one row, workspace-context-menu; the plugin adds no route, command, or tool. |
Files
src/index.ts— empty Host half (the bundle row needs one).runtime/client.js— prebuilt DSH Client module: overlay layer, menu, dialogs, the open-directory hand-off, the right-panel Files tab hand-off, styles, and locale dictionaries.cordis.patch.yml— inserts theworkspace-context-menubundle row.locale/en.json,locale/zh.json— Plugin Manager title and description.icon.svg— original artwork for the Plugin Manager row.dist/— Bun build output (bun run build), not committed.
