DeepSeek Harness Plugin Hub

发布与管理完整 Harness Profiles,发现适合你的插件。

探索

插件目录环境预设文档中心动态

社区

发布插件联系我们报告问题

相关链接

Plugin Hub GitHubDeepSeek Harness 官方项目系统状态隐私说明
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

独立、非官方社区项目,与 DeepSeek 官方无隶属、授权或背书关系。

File Link Menu — DeepSeek Harness 插件(DSH Plugin)
← Plugins
F

dsh-file-link-menu

File Link Menu

DSH Web UI 中针对文件和链接的右键菜单,以及会话路径标签上的文件类型图标和简称

插件会安装到这里;不确定时保持 web。

npx -y @deepseek-ai/dsh plugin --profile web add github:shkzhang/dsh-file-link-menu#490e06365666fe9d3d5e784f3828259b436183ed
README兼容性版本

兼容性与来源证明

File Link Menu 以 dsh-file-link-menu 发布,当前版本为 0.1.1。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

DSH 兼容范围
*
运行环境
web
发布来源
github
Registry 更新时间
2026/9/18

版本

0.1.1stable
2026/9/18
0.1.0stable
2026/9/18

相关插件

正在加载相关插件…

最新版
0.1.1
DSH
*
HMR
重启进程
Tree shaking
未声明可安全裁剪
解包体积
未提供
文件数
未提供
Surface
web
许可证
MIT
发布源
github
GitHub
★ 1
周下载
0
最近提交
2026/9/18
查看源码 ↗
README Badge

点击下方 Badge 复制 Markdown,粘贴到 README 即可。

这是你的 Plugin?认领权益 · 优先安全扫描

验证 package.json 声明的 GitHub 仓库,即可管理这个公开页面。认领后,Hub 会优先安排当前版本的安全扫描,并在通过后公开展示结果。

认领这个 Plugin →
报告问题
DeepSeek Harness Plugin Hub
ProfilesPlugins分类动态文档登录管理 Profiles
ProfilesPlugins分类动态文档登录

相关插件

继续浏览 ui-customization 分类下经过校验的插件。

Deepseek Idesigndeepseek-idesign作为原生 DeepSeek Harness 对话视图的 iPolloWork Design Studio 及其精选设计模板。Deepseek Ipptdeepseek-ipptiPolloWork PPT Studio 及其精选幻灯片模板,作为原生 DeepSeek Harness 对话视图。Codex Ui@michengai/dsh-codex-uiDSH Codex UI — 为 DeepSeek Harness Web 提供 Codex 风格侧栏、工作区会话树、全局搜索和轮次导航Dream Skindsh-dream-skinDeepSeek Harness 换肤插件(Dream Skin for DSH):8 套 iOS / Linear 式清透冷调的高质感主题 + 弥散光壁纸 + 每款皮肤智能背景 + 强调色 + 主题包分享,把换肤做成材质与配色克制的高级感工艺,而非贴图。在原生 DSH Web 和第三方桌面客户端(DSH Desktop)中运行:8 款原创高端主题、玻璃材质、壁纸 2.0

README

dsh-file-link-menu

Right-click menus for files and links in the DSH Web UI, and file-type glyphs with short names on every path chip (中文说明见 README.zh.md).

What it does

Right-click a file chip, a delivered file card, an inline path in an answer, or an external link, and the menu offers the actions that surface actually supports:

TargetRows
File (delivered card, produced chip, inline path mention)Open file (default application) · Open in VS Code · Open with ▸ (probed applications) · Save as… · Copy file path · Reveal in file manager
External link (http/https anchor, or a selected bare URL)Open in new tab · Open in default browser · Copy link · Save link as…

The file menu: open, open in VS Code, open with, save as, copy file path, reveal in file manager
The link menu: open in new tab, open in default browser, copy link, save link as

Every row is composed from what the Host reports it can do (GET /api/dsh-file-link-menu/caps), so a Host without a desktop session hides the launch rows instead of offering rows that fail.

Path chips show a type glyph and a file name

A path the shell prints in the conversation — a tool row's argument path, an inline path in an answer, a produced-file chip, an @ reference the user wrote — is re-dressed to lead with the file's type glyph and to show only the file's name. The full path stays on the chip: it is stamped on the element (data-flm-full-path), shown on hover, and it is what every menu action runs against.

Tool rows: each path chip leads with its file's type glyph and shows only the file name
A produced-files list whose chips carry the file-type glyph

The glyph is DSH's own artwork, borrowed from the shared UI primitives, so a .tsx chip carries the React mark, a .md chip the Markdown mark, and a category a later shell release adds appears here without a change. A chip the plugin does not recognize — including the delivered file cards, which already draw both a glyph and a short name — keeps the shell's rendering exactly.

A single read row: the type glyph, the localized title, and the file name

An attachment card — the one the transcript draws for a sent file, and the one the composer shows for a pending file — behaves the same way: a left click previews the file in the right Sidebar, and a right click opens the same file rows. The card carries the file's display name and nothing else, so the Host resolves that name against dsh's attachment store, whose files are content-addressed and live outside every workspace. Name resolution is not exact when one name was attached twice: the most recently written file wins.

An @ reference the user wrote renders as a file chip in a sent message, and that chip takes the same rows on right click. Its own left click stays the shell's — except for a pasted file: dsh-auto-paste references one by its file name (paster-…) rather than by its long stored path, so the name is not a path at all, and this plugin resolves it against the attachment store and previews it in the right Sidebar itself.

Install

# from npm (prebuilt, no build step)
dsh plugin --profile web add dsh-file-link-menu

# or straight from the repository
dsh plugin --profile web add github:shkzhang/dsh-file-link-menu

Restart dsh web (or the application wrapper hosting it) and reload the page.

The npm package and the repository both ship the built lib/, so neither path builds on your machine. A source checkout is the same: the repository keeps its build output committed, so nothing runs at install time and no build approval is asked for. prepublishOnly rebuilds lib/ from the sources only when the package is published, so a release can never ship stale artifacts.

How it works, and what it assumes

The web client slot map has no file or link menu hole, so this plugin cannot contribute rows into the shell's own menus. It therefore watches contextmenu (for the menu) and click (for the attachment preview) in the capture phase, and recognizes a target from anchors the shell publishes:

  • [data-presented-files-row] — delivered file cards, path on the chip's title;
  • [data-produced-files-row] — produced file chips, same title convention;
  • a button[title] carrying a path-shaped title and an accessible label — the inline path mention;
  • a button[data-ref-chip="file"] whose title is the whole @ reference token (@path, or @"path with spaces") — a reference the user wrote, rendered in a sent message. A token carrying a paste name (paster-…, the name dsh-auto-paste gives a paste) is an attachment reference, not a path: the Host resolves it against the attachment store;
  • a[href] whose authored value is an absolute http(s) URL;
  • [data-message-attachments] — the transcript's attachment row, whose card carries the attachment's display name on title;
  • the composer's pending-attachment rail: the labelled role="group" group whose items hold the card that carries the same title.

Anything else — including same-page and relative links — keeps the shell's own menu, and so does any event a previous listener already claimed. The composer rail is styled by CSS modules, so its class names carry a build-hashed prefix; the plugin matches the local name at the end of the class list, which a rebuild keeps.

Anchor failure costs the affected surface only. If a future DSH release renames these anchors, detectTarget stops recognizing that surface and the shell's menu opens as before; no Session data is touched. The anchors and their failure mode live in src/client/surfaces.ts.

The display layer

The same anchor vocabulary drives the chip re-dressing, in three files: chips.ts recognizes a chip and derives the name it should show, icons.ts renders the shared FileTypeIcon once per file type into a detached React root and mints per-instance ids for its gradients, and enhance.ts applies both from the mutation stream.

Two measurements shape that design, and both are exercised by tests/chips.spec.ts:

  • React tolerates inserted nodes while a chip's own props are unchanged, but rewrites its text — rebuilding that button's children — as soon as a prop changes, which a streaming tool call does continuously. The layer therefore re-runs after each commit, and it records the name it wrote (data-flm-shown) so it can tell a chip it shortened from one the shell rewrote afterwards. Without that record a re-rendered chip would keep showing the previous file's name.
  • useId answers the same value on every render into a reused root, so caching per type would put one id on every chip of that type. url(#…) resolves to the first match in the document, so removing that chip would strip the fill from the rest. Ids are therefore scoped per instance — suffixed, never replaced, because one piece of artwork declares several and its shapes reference each of them.

The layer hides, never removes, the glyph the shell draws beside a chip: React still owns that node, and taking it out of the tree would leave the virtual DOM and the document disagreeing. A chip the plugin cannot glyph, cannot name, or cannot recognize is left as the shell rendered it.

Host routes

RouteMethodPurpose
/api/dsh-file-link-menu/capsGETplatform, file-manager flavour, launchable applications
/api/dsh-file-link-menu/attachmentPOSTresolve an attached file's display name to its stored path
/api/dsh-file-link-menu/openPOSTopen a Session file with the default application
/api/dsh-file-link-menu/revealPOSTselect a Session file in the file manager
/api/dsh-file-link-menu/open-withPOSTopen a Session file with one probed application
/api/dsh-file-link-menu/downloadGETstream a Session file as an attachment
/api/dsh-file-link-menu/save-asPOSTcopy a Session file into a chosen directory
/api/dsh-file-link-menu/open-urlPOSThand a URL to the default browser
/api/dsh-file-link-menu/download-linkGETstream a remote URL as an attachment
/api/dsh-file-link-menu/save-link-asPOSTwrite a remote URL into a chosen directory

Guarantees the routes enforce:

  • every route asks the composition's connection service for a Host/Origin and authentication rejection first;
  • a requested path must land inside one of two roots the Host itself names, after symlinks are followed: the Session workspace, which relative paths resolve against, and dsh's attachment store. The store is a root because a pasted attachment lives outside every workspace and its card carries only the file name, which the /attachment route turns back into one of the store's own paths. A path outside both roots, a URL-shaped value, a missing file, a relative path with no resolvable workspace, or an attachment name the store does not hold is refused, so the menu cannot become an arbitrary-file read or open primitive;
  • commands are argv arrays passed to spawn without a shell;
  • 链接另存为 accepts http/https only, re-validates every redirect hop, refuses loopback and private-network destinations, and caps both time and bytes. That refusal belongs to the routes where the Host itself connects. 在外部浏览器中打开 instead hands the URL to the user's own browser, which is reachability the page already has — the adjacent 在新标签页中打开 row opens the same address with no Host round trip at all — so a loopback or intranet address is allowed there, and the user's local dashboard opens like any other link.

Configuration

None. The size, timeout, and redirect ceilings are protocol limits in src/shared.ts, not deployment choices.

Development

pnpm install
pnpm run typecheck
pnpm test
pnpm run build

lib/index.js is the Host half; lib/client.js is the browser bundle the shell's module loader registers as dsh-file-link-menu.

This repository is self-contained: its TypeScript and test configurations do not reference anything outside it, so a fresh clone builds and tests on its own. The official @deepseek-ai/* packages are declared as peerDependencies with an explicit prerelease branch in each range, because a range without one silently excludes the harness's prerelease builds.