# dsh-copy-file-path [English README](./README.md) 给 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(`dsh`)Web 前端的**交付文件卡片菜单**加上「复制相对路径」和「复制绝对路径」两项。 每轮对话结束时,dsh 会把该轮交付的文件显示成卡片。卡片上有一个「打开」按钮,以及一个「⋯」菜单,里面只有「用默认应用打开」和「在文件资源管理器中显示」(Linux 上是「打开所在文件夹」)。本插件在这个菜单末尾追加两项: | 菜单项 | 复制的内容 | | --- | --- | | 复制相对路径 | 相对当前会话工作区的路径 | | 复制绝对路径 | 主机上的绝对路径 | 点击即复制到剪贴板,并弹出一个短提示显示复制了什么。 ``` ⋯ ┌──────────────────────────┐ │ ↗ 用默认应用打开 │ ← dsh-client-ui-deliverables 自带 │ ▤ 在文件资源管理器中显示 │ │ ⧉ 复制相对路径 │ ← 本插件 │ ⧉ 复制绝对路径 │ ← 本插件 └──────────────────────────┘ ``` ## 安装 ```sh dsh plugin --profile web add github:Meowrium/dsh-copy-file-path ``` 装完重启 `dsh web`,并硬刷新页面(`Ctrl+Shift+R`)——宿主在启动时装配 bundle,浏览器端需要重建客户端模块。 ## 为什么非得写插件 那两行菜单是 `@deepseek-ai/dsh-client-ui-deliverables` 里**硬编码的 `items` 数组**,由 `@deepseek-ai/dsh-client-ui-primitives` 的 `Menu` 渲染(一个 `role="menu"` 的列表,`portal` 到 `document.body`)。既没有 slot、注册表或钩子可以插行,想 shadow 就得把整张卡片重写一遍。 所以本插件走**DOM 后处理**这条路(和 `dsh-mermaid` 同一套办法):不改内核、不猴补 React 内部。它盯着那个 portal,当某张交付卡片的箭头处于展开态(`aria-expanded="true"`)时,克隆最后一行、改标签、换成复制图标、接管点击。注入的行是纯 DOM:点击处理器会 `stopPropagation`,React 根节点根本看不到这个事件;菜单则通过与「点击别处」完全相同的 outside-`pointerdown` 路径关闭。 ## 两个路径从哪来 不猜、不改写,两个值都取自 dsh 自己手里已有的东西: | 取值 | 来源 | | --- | --- | | 绝对路径 | 卡片预览按钮的 `title`,也就是 dsh 自己解析并打开的那个路径(`resolveWorkspacePath(cwd, file.path)`)。**逐字复制**,分隔符不动。 | | 相对路径 | 该轮向 `present` 工具声明的路径,从卡片自身的 `aria-label` 里取回(`{路径} 的更多文件操作` / `More file actions for {路径}`)。若当初声明的就是绝对路径,则按工作区根重新计算。 | | 工作区根 | 由上面这一对(绝对、声明)反推,构造上精确;客户端会话状态(`sessions.list`)只是兜底,用于标签里没有声明路径的卡片。 | 某个值拿不到时,对应的那一行**直接不出现**——绝不给一个错的路径。 ## 兼容性 - 适用于带交付文件卡片的 `dsh` Web 界面。实测环境:**0.1.6-alpha.1** / Windows;更早版本线未验(bundle 形态与 0.1.5 线相同,但没实测过)。 - 零运行时依赖。浏览器半只用 DOM,外加**可选**的 `locale`、`sessions` 两个客户端服务:`inject` 为空,即使这两个服务缺失插件照样加载——标签回退英文、工作区根从卡片自身推导。 ## 测试 两个用例都不需要 dsh 实例: ```sh node test/paths.test.mjs # 路径纯函数的 35 条断言 ``` `test/fixture.html` 复刻了交付卡片与 primitives 菜单 portal(结构逐条读自 0.1.6-alpha.1 的发布代码),用桩模块加载器载入 `lib/client.js`,在真实浏览器里跑 23 条检查:注入与标签语言、两个复制值、菜单关闭、事件不外泄进 React 容器、负控(无关菜单不注入)、幂等、以及完整卸载。用浏览器打开该文件即可,结果渲染在页面上,同时写进 `document.title`。 ## 已知边界 - 只覆盖交付文件卡片的菜单。「本轮文件改动」胶囊与侧边栏文件树本身没有菜单,因此不受影响也没有增强。 - 那个菜单属于内置组件:将来 dsh 若改动卡片结构,本插件可能需要同步更新。检测不到时它选择**不注入**,而不是把两项塞进一个无关的菜单。 - 只复制文本:不读文件、不打开文件、不外发,完全没有宿主侧访问(host 半是刻意留空的)。 ## 卸载 ```sh dsh plugin --profile web remove dsh-copy-file-path ``` ## 许可 MIT