dsh-web-file-preview

by wingsky-1

1 工具与能力github收录于 08-21

在 web 端预览对话中的文件链接(图片 / 文本 / Markdown / 代码高亮 / git diff):点击链接即可弹窗预览,替代桌面原生打开;loopback 围栏 + 弱 ETag/304 缓存 + DOMPurify 消毒。

Preview conversation file links in the web UI (images / text / Markdown / highlighted code / git diff) — click a link to open a preview modal instead of the desktop opener; loopback-fenced routes with weak ETag/304 caching and DOMPurify sanitization.

安装

dsh plugin --profile web add github:wingsky-1/dsh-plugin-hub#path:/packages/dsh-web-file-preview

GitHub 源码安装:首次需按提示配置 allowBuilds 构建授权后重试

安装与环境配置指引、插件开发教程见 DSH 中文社区文档 ↗

安装即在你的机器上以你的权限运行第三方代码——它可读写文件、使用凭据、访问网络,DSH 的工具审批不会为插件代码加沙箱。「检测到 manifest」仅代表发现 dsh.bundle / dsh.plugin 清单,不构成兼容性或安全审查;安装前请审阅源码,不熟悉的插件先在不含密钥的环境试用。

README

目录

npm GitHub Releases

点击对话中的文件链接,在 web 端直接预览文件内容(图片 / 文本 / Markdown / 代码 / git Diff)。

DSH 自带的“可点击文件引用”在点击产出文件 chip / 行内文件引用时走的是桌面原生打开器——桌面可用,但纯 Web(局域网浏览器 / iPad / iPhone,或 nativeOpen:false 部署)没有 Web 端预览。本插件补上这一环:点击对话中可点击的文件链接,改为在对话框内弹出预览 Modal,图片直接 <img> 显示、文本以等宽 <pre> 渲染,明暗主题自适应。

能力

  • 图片预览:png / jpg / jpeg / gif / webp / svg / avif / bmp;点击进入灯箱放大/平移(滚轮缩放 + 拖拽)。
  • Markdown 预览:.md / .markdown 默认渲染预览(marked + GFM 常用能力),可切「原始」。
  • 代码语法高亮:js/ts/py/java/… 等 25+ 语言(highlight.js 子集)高亮,可切「原始」。
  • 文本预览:txt / log / csv / conf … 等宽展示。
  • Diff 视图(git):.md/代码/文本 若在 git 仓库且有未提交变更,顶栏多出第 3 个 Diff tab,红/绿展示 git diff HEAD -- <file>(未跟踪新文件给提示)。
  • Modal 内动作:预览/原始/Diff、复制路径、在新标签打开、关闭(Esc / 点遮罩)。
  • @ 引用适配(dsh rc8):对话气泡里 @ 引用渲染的 data-ref-chip(文件/文件夹/会话)统一识别——文件引用 @path / @"…" 点 chip 用干净路径(去前导 @ 与引号)直接打开预览 Modal;文件夹引用 @path/ 点 chip 弹轻量提示(无目录浏览能力,不开 Modal);会话引用显式忽略(与文件预览正交)。
  • 加载错误态:错误细分 + 「在新标签打开」兜底。
  • 缓存:不设 JS 内存缓存(文件常被修改,永久缓存会显示陈旧内容);改用浏览器 HTTP 缓存 + 宿主弱 ETag(Cache-Control: no-cache + If-None-Match)自动协商——未变 304 秒回、已变自动拿最新。

实现

  • 宿主端:GET /api/dsh-file-preview/file?cwd=&path=(绝对路径可省 cwd;loopback 围栏,非回环 403 / 方法非 GET 405),按 resolve(cwd, path) 定位读取(~/~/ 前缀用 untildify 展开为用户主目录);后缀分组:图片/文本/Markdown/代码直出,其余 415;文本超过 maxTextBytes 返回 413+truncated(先 stat 判大小、不整读)。GET /api/dsh-file-preview/diff?cwd=&path=(异步 execFile 计算 git diff)与 GET /api/dsh-file-preview/health 健康检查。文件响应统一 X-Content-Type-Options: nosniff,SVG 额外 Content-Security-Policy: sandbox。
  • 客户端:双机制拦截(workspaces.openPath 调用点收口 + document 捕获静态拦截)+ 分组渲染(renderGroupFor)+ 三 tab(预览/原始/Diff,Diff 仅 git 有变更才显示)。md 用 marked、代码用 highlight.js 子集、Diff 用 diff2html。
  • 后缀分组单一事实源:src/grouping.ts 供宿主 mime.ts 与客户端 renderer.ts/client.ts 共用,杜绝双端各写一份后缀表导致漂移;cleanRefChipPath(@-mention chip 标签 → 干净路径的纯函数)同处,与 DSH 的 formatFileMention 互逆。
  • 跨包契约假设(@ 引用):按 data-ref-chip 属性 + title="@…" 形态(由 DSH ui-conversation 产出)识别。若未来 DSH 调整该形态,data-ref-chip 门失效时 file chip 静默退回「点击无反应」(不会 404,属可接受降级);若 DSH 提供干净路径属性(如 data-ref-path)则优先采用。
  • 依赖:marked / highlight.js / diff2html / untildify 为构建期打包依赖(宿主/客户端分别内联进 lib/index.js 与 lib/client.js),运行时零 npm 依赖;Content-Type 用内置小型映射(无需 mime-db 大表,避免宿主第三方依赖内联的 ESM/CJS 兼容问题)。
  • 构建体积:客户端 esbuild --minify,client.js min 后约 226KB(gzip ~68KB)。

安装

前提:已安装 DeepSeek Harness 且 dsh web 可正常启动(未全局安装 dsh 见下方「未全局安装 dsh」)。

安装插件(add)

dsh plugin --profile web add @wingsky-1/dsh-web-file-preview

卸载插件(remove)

dsh plugin --profile web remove @wingsky-1/dsh-web-file-preview

更新插件(update)

dsh plugin --profile web update @wingsky-1/dsh-web-file-preview

安装 / 卸载 / 更新后都需重启一次 dsh web(bundle 层只在启动时组合)生效。

指定版本号(@version)

省略 @版本号 即安装默认 latest(推荐)。仅当 registry 尚未同步到最新、或最新版在你的环境有问题时,在包名后追加 @版本号:

dsh plugin --profile web add @wingsky-1/dsh-web-file-preview@<版本号>

未全局安装 dsh

若本机没有全局 dsh 命令,用 npx 临时拉起(底层调用 pnpm,仍需本机装好 pnpm 与 Node.js):

npx @deepseek-ai/dsh plugin --profile web add @wingsky-1/dsh-web-file-preview
npx @deepseek-ai/dsh plugin --profile web remove @wingsky-1/dsh-web-file-preview
npx @deepseek-ai/dsh plugin --profile web update @wingsky-1/dsh-web-file-preview

配置

Key 默认 说明
enabled true 关闭则不注册任何路由
maxTextBytes 524288 (512KB) 文本类(text/markdown/code)预览最大字节数;超限返回 413 + truncated 标记(Cache-Control: no-store),客户端提示「文件过大」,可在新标签打开原文

验证

pnpm build && pnpm test                 # 仓库内:构建 + smoke
curl http://127.0.0.1:3080/api/dsh-file-preview/health

兼容性(issue #37 起)

静态点击拦截的生效范围与让权约定如下:

  • 作用域圈定:document 捕获拦截仅在宿主对话流子树内生效——判定锚点为祖先链上存在 [data-chat-flow] 或 [data-chat-anchor-key](DSH ChatView 官方自用的滚动锚点属性,见 src/client/link-resolver.ts 的 SCOPE_SELECTORS,追加式数组)。对话流之外的任何元素一律放行(下条豁免属性除外,其跨区域优先):第三方插件 UI(文件树、浮层、面板等)不再被全局路径嗅探劫持。这是行为变更:旧版对全 document 生效的宽松拦截自本版起收敛到对话流内。
  • 解析优先级:元素显式声明的路径凭证(data-ref-chip / title / <a href>)永远优先于"文本像路径"的启发式嗅探;凭证与本轮文本命中 basename 一致时采信凭证完整路径,不一致则跳过该凭证不猜(与 DSH producedFileMentions 的保守原则同源)。第三方文件树常见的 <div title="完整路径"><span>裸文件名</span></div> 行结构因此能解析出完整路径。
  • 逃生门属性:任何元素子树标注 data-dsh-no-preview 即对本插件豁免(跨区域生效,优先级高于作用域圈定)。第三方插件在自有可点击 UI 上加此属性即可确保零干扰。
  • 适配点:若未来 DSH 改版调整了对话流 DOM 标识,更新 src/client/link-resolver.ts 中 SCOPE_SELECTORS 常量即可(追加新锚点,无需改动算法)。

安全模型

  • ⚠️ 局域网部署高危告警(务必阅读):本插件经 dsh-lan-proxy(或任何把外部流量转发到 127.0.0.1 的代理)对外暴露时,loopback 围栏会被代理重写 Host/Origin 而穿透——此时局域网内任意设备(无需任何 dsh 凭据/登录态)可直接访问 /api/dsh-file-preview/file?path=… 预览本机任意扩展名在文本/图片/Markdown/代码白名单内的文件,包括 ~/.dsh/.credentials.yaml、~/.dsh/*.json 等凭据/配置文件(path 支持 ~ 展开与绝对路径,stat 跟随符号链接,不做逃出拦截)。请在可信局域网部署(勿暴露到公共 WiFi/互联网),或自行在网络层配置会话鉴权 / API 前缀白名单后再对外。被穿透后的后果由部署方承担——本插件按下方"不做重复兜底"条款不提供插件层访问控制。
  • 保留 loopback 围栏:所有 /api 路由强制校验回环来源(跨站 / DNS 重绑定防护),与平台既有约定一致;health 之外的 /file、/diff 也仅允许 GET(方法不符 405)。该围栏对本机/回环直连有效;对经代理的局域网访问生效与否取决于代理是否重写 Host/Origin(见上一条)。
  • 不做重复兜底:本插件的语义是“能打开 dsh web 页面即已持有高权限”,因此不做任意文件访问强校验、会话鉴权、敏感名拦截——访问控制由平台/用户负责,本插件不重复实现每一套。
  • 路径定位:/file 按 resolve(cwd, path) 直接定位,不做“逃出 cwd”拦截(任意文件访问由平台/用户负责)。~/~/ 前缀展开为用户主目录。
  • 渲染安全:文件响应一律带 X-Content-Type-Options: nosniff;SVG 额外带 Content-Security-Policy: sandbox(顶层导航时不执行内嵌脚本)。Markdown / 代码渲染输出为 HTML 呈现层,marked / highlight.js 对正文做转义;本插件不承诺对渲染结果做 XSS 消毒——预览内容来自会话已见的文件,安全边界同“能打开 dsh web 即高权限”。
  • @ 引用不新增安全面:data-ref-chip/title 仅经 getAttribute 读取并作为路径字符串拼 URLSearchParams,从不 innerHTML 渲染;清洗后的文件路径仍走 /file 既有 loopback/cwd 围栏(相对路径强制 cwd、绝对路径沿用既有进程可读范围),与「deliverable chip 预览」同一安全模型。

已知限制

  • 文本类超过 maxTextBytes(默认 512KB)返回 413+截断标记,不再整读全文(大文件流式/虚拟滚动未实现,见 W10 专项)。
  • 可点击范围较宽(凡路径 title / 本地 href / 内联路径文本都可能进预览),data-ref-chip 权威分支优先于通用嗅探,避免 @ 引用误触发。
  • 文件夹 @ 引用不开预览(仅提示),目录浏览能力不在本插件范畴。
  • client bundle 含 marked + highlight.js 子集 + diff2html,min 后约 226KB(gzip ~68KB)。
  • 多会话切换以当前活跃会话 cwd 为准。

原始 README: https://github.com/wingsky-1/dsh-plugin-hub/blob/main/packages/dsh-web-file-preview/README.md ↗