EIGHTfs/dsh-image-preview0

dsh-image-preview

DSH 会话内图片预览服务:把工作区图片通过 HTTP 暴露给会话渲染、文本→图片渲染(双字体防乱码)、SVG→PNG 转换。AI 在回复里贴 markdown 图片 URL,用户直接在对话中看图。

包名
dsh-image-preview
版本
1.0.0
最近更新
2026年8月18日

安装

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:EIGHTfs/dsh-image-preview

🖼️ dsh-image-preview

让 AI 在 DSH 会话里直接给你发图片 —— 静态图片服务 + 文本渲染 + SVG 转换,一条 API 全搞定

功能示意

✨ 为什么需要它?

DSH 的 AI 会话回复是纯文本的,想让用户直接看到一张图片(截图、二维码、配图)时:

  • ❌ 让用户去翻文件目录 —— 麻烦
  • ❌ 手动起 python3 -m http.server —— 每次都要开服务、记端口
  • ❌ 直接在 markdown 里写本地路径 —— GUI 渲染不了

dsh-image-preview 把这一切变成插件内置能力:AI 在回复里贴一个 markdown 图片 URL,用户直接在对话中看到图,无需任何手动操作

📦 核心能力

接口方法说明
/api/image-preview/file?path=xxxGET静态图片服务:暴露工作区文件,AI 回复里贴 URL 即可渲染
/api/image-preview/renderPOST文本 → PNG:AI 把要展示的内容发给接口,生成图片
/api/image-preview/svg2pngPOSTSVG → PNG:GitHub 架构图等 SVG 转成可预览的 PNG
/api/image-preview/statusGET插件状态:版本 / workspace / Pillow 与字体可用性

用法示例

AI 回复里贴图(最常用):

![架构图](http://:/api/image-preview/file?path=docs/architecture.png)

渲染一段文字为图片:

curl -X POST http://127.0.0.1:3081/api/image-preview/render \
  -H 'Content-Type: application/json' \
  -d '{"text":"崩溃检测 ×2 全部正确检出","title":"实测记录","outPath":"report.png"}'
# → {"ok":true,"url":"/api/image-preview/file?path=report.png"}

SVG 架构图转 PNG:

curl -X POST http://127.0.0.1:3081/api/image-preview/svg2png \
  -H 'Content-Type: application/json' \
  -d '{"svgPath":"docs/screenshots/architecture.svg","outPath":"architecture.png","outputWidth":960}'

🛡️ 亮点设计

亮点事实
双字体防乱码中文用 Droid/Noto 等 CJK 字体、符号(→·×①等)用 DejaVu,逐字符渲染——不再出现"✅变方块"
emoji 兜底替换✅→✓、🚀→>> 等,任何字体都没有的 emoji 自动转成可渲染文本
路径穿越防护相对路径 normalize 后必须落在 workspace 内,../ 和绝对路径一律拒绝(实测防护通过)
零运行时依赖插件本身是纯 Node.js;渲染走系统 python3 + Pillow(可选),无 npm 依赖
纯服务端apply(ctx) 写法 + webServer 路由,无 client bundle,不碰 UI

📦 安装

方式一:手动安装(三要素)

# 1. 源码放 node_modules_local
mkdir -p ~/.dsh/profiles/web/node_modules_local/dsh-image-preview
cp -r lib package.json cordis.patch.yml ~/.dsh/profiles/web/node_modules_local/dsh-image-preview/

# 2. package.json 加 file: 依赖
node -e "
const fs=require('fs');
const p=JSON.parse(fs.readFileSync('~/.dsh/profiles/web/package.json'));
p.dependencies['dsh-image-preview']='file:./node_modules_local/dsh-image-preview';
fs.writeFileSync('~/.dsh/profiles/web/package.json',JSON.stringify(p,null,2));
"

# 3. cordis.patch.yml 追加
cat >> ~/.dsh/profiles/web/cordis.patch.yml ` + `PYTHONPATH`)
- **python3 + cairosvg**:SVG 转换(依赖系统 librsvg)
- 都没有时插件仍可用:静态图片服务(/file)不依赖它们

## 🔧 工作原理

AI 会话回复 DSH 插件 用户 ───────── ──────── ──── 回复 markdown ──URL──▶ /api/image-preview/file ──▶ GUI 渲染图片 │ 回复内容 ──▶ /api/image-preview/render ──生成 PNG──▶ 直接看到 │ SVG 路径 ──▶ /api/image-preview/svg2png ──转 PNG──▶ 直接看到


## 🧪 测试

| 测试 | 结果 |
|------|------|
| mock 单元测试(路由注册/路径穿越/渲染核心) | ✅ 9/9 通过 |
| 测试实例真机验证(3083) | ✅ 插件加载 + status/file/render/svg2png 全接口通过 |
| 路径穿越防护 | ✅ `../secret` 与绝对路径均被拒绝(400) |
| 双字体符号渲染 | ✅ ✅→① 等符号无乱码 |

运行测试:`node test-apply.mjs`

## 📚 设计理念

1. **给用户的东西必须能打开**:地址一律用真实主机 IP(见 host-address-convention),不甩 127.0.0.1
2. **插件即服务**:服务随插件生命周期走,不需要用户手动起/停任何东西
3. **能力分层降级**:核心静态服务零依赖;文本渲染/SVG 转换依赖系统工具,缺失时优雅降级
4. **安全默认**:路径穿越防护 + 只读暴露,绝不给任意文件读取

## License

MIT