Skip to content

dsh-print-screen

Verified

dsh-print-screen · v0.4.8 · MIT · Web UI

Freezes the whole desktop from a scissors button in the DSH composer, crops a dragged region, and inserts it into the prompt as an image attachment. The shot is stored once in the DSH attachment library, indexed per session. Windows only.

Install

dsh plugin add dsh-print-screen

Confirm the layer applied with dsh --profile default --dump-config — see the install guide.

Source

Tags

Readme

dsh-Print Screen

English | 中文

dsh-print-screen 给 DSH 桌面版的输入框加一个剪刀按钮:点一下把整个虚拟桌面(所有显示器)按原位置、原像素冻结,在画面上拖一个矩形,松手后把该区域作为图片附件插入当前提示词框。截图只存一份,放在 DSH 附件库里(按内容哈希命名),不会复制到工作区。仅 Windows。

输入框左下角:  [+]  [✂]  [工作区内修改]  [计划]
                     │
                     └─ 点剪刀 → 屏幕冻结(原位、原大小)→ 拖框 → 松手 → 缩略图进输入框

输入框左侧的剪刀按钮

功能

  • 一个剪刀按钮:和普通的输入框按钮一样——透明背景、只有图标,不占额外宽度。
  • 冻结屏幕再拉框:整块虚拟桌面(所有显示器)冻结,每张画面保持原位、保持原始像素大小;松手即裁剪并插入。
  • 默认隐藏 DSH:截图前先隐藏 DSH 窗口,截完立即还原(最大化状态也还原),因此能截到被它挡住的窗口。
  • 不隐藏的两种方式:按住 Shift 点剪刀,或按 Ctrl+Shift+Alt+1。
  • 插入提示词框:走会话自己的附件 API,缩略图出现在输入框下方,回车随消息发出;失败时退回合成粘贴事件,再不行就复制到剪贴板并提示 Ctrl+V。
  • 文件名带时间戳:附件名是 screenshot-2026-10-26_22-31-05.png(本地时间,精确到秒),多张截图不会重名。
  • 只存一份,不重复占空间:图片以内容哈希命名,存在 DSH 附件库(~/.dsh/attachments),工作区里不产生副本;同一张图传两次也只存一份。
  • 按会话记账:每次截图后往会话目录写 screenshots.json,记录哈希、时间、尺寸和所属会话,用来回答“这张图是哪个对话的”。写不进去不影响截图。注意里面的 object 是插件按哈希推算的路径:图片若没真正插进消息(取消、插入失败),这个路径并不存在,别拿它判断图片还在不在。
  • 可选额外备份:把 client.js 里的 SAVE_TO_SESSION 改成 true,会在会话文件夹里再存一份同名 png(代价是每张图占双份空间)。

交互一览

操作 行为
单击剪刀 截屏,先隐藏 DSH 窗口
Shift + 单击剪刀 截屏,不隐藏 DSH(可以连它一起截)
Ctrl+Alt+1 同单击:截屏并隐藏 DSH
Ctrl+Shift+Alt+1 截屏但不隐藏 DSH
拖框 → 松手 裁剪、插入当前输入框,并在 screenshots.json 记一笔(DSH 窗口在裁剪完立即还原)
Esc / 右键 取消

快捷键用物理键位判断(event.code),Shift+1 打出 ! 的布局也能触发;! 本身不做兼容。

速度

每次截图不再重新起 powershell.exe(那要付约 1.2 秒的进程启动 + 程序集加载,火绒还会再扫一遍)。改成 DSH 启动后 2.5 秒(避开启动高峰)在后台拉起一个常驻截图助手 helper.ps1,WinForms / DPI / P-Invoke / 位图缓冲只初始化一次,之后每次只需要:

阶段 耗时(实测)
助手启动(每次 DSH 启动只一次,后台) ~1.4s
点击 → 冻结画面出现(热态) ~0.15s(关掉该窗口的 DWM 过渡后 SW_HIDE 瞬时生效,只等 30ms;画面用 BitBlt 直贴,首次绘制 ~40ms)
第一次请求(含首次绘制 JIT) ~0.3s
拖框 → 返回裁剪图 保存约 0.02s + 传输/插入

具体做法:截图走 BitBlt 直接拷进常驻的 DIB(比 GDI+ 逐屏 CopyFromScreen 快 30–40ms);隐藏 DSH 用瞬时的 SW_HIDE(SW_MINIMIZE 有 150–250ms 动画);窗体、画笔字体、位图缓冲全部跨请求复用,整套流程只有 helper.ps1 一个实现。

助手起不来或中途挂掉时,宿主会自动重启它再试一次,而不是维护第二套截图脚本。

环境要求

项目 要求
DSH >= 0.2.1-rc.3(engines.dsh)
系统 仅 Windows(10/11,需要系统自带的 Windows PowerShell 5.1 + System.Drawing/WinForms)
其它 无 Node 原生模块、无外部服务、无 API Key

安装

# 从 npm 安装
dsh plugin --profile web add dsh-print-screen

# 或直接从 GitHub 源码安装
dsh plugin --profile web add github:zrrrrrrr77/dsh-print-screen

也可以在 DSH 里打开 插件 → 发现,用插件市场安装。

装完在输入框左侧、「工作区内修改」左边就能看到剪刀;看不到就刷新页面,必要时完全退出 DSH 再打开(宿主半边要重启才加载)。

dsh plugin --profile web 里的 web 换成你自己的 profile 名;DSH 桌面版的 profile 由应用管理,建议直接在应用内的插件页安装。

实现要点

  • DPI 是关键:截图脚本必须声明 Per-Monitor V2 DPI 感知。不声明时 Windows 会把进程按 250% 虚拟化,冻结画面被整体放大 2.5 倍——用户看到的就是「屏幕被放大、不是原样」。声明后所有坐标都是物理像素,显示与鼠标位置 1:1。
  • 抓图用 BitBlt 逐屏直拷进常驻的 DIB(比 GDI+ CopyFromScreen 快 30–40ms),屏幕尺寸变了才重建缓冲。冻结窗口也在 Paint 里用 BitBlt 直接把 DIB 贴到窗口 DC,而不是 BackgroundImage——后者整屏首次绘制要 ~500ms,拖框时会明显卡住。
  • 隐藏 DSH 前先 DwmSetWindowAttribute(hwnd, DWMWA_TRANSITIONS_FORCEDISABLED, TRUE) 关掉这个窗口的 DWM 过渡,SW_HIDE 就变成瞬时的;再等 30ms 让合成器出一帧,然后抓屏(不关过渡时窗口会渐隐 ~200ms,40ms 抓到的还是半透明残影)。窗口匹配取两条线索的并集:进程名是 DeepSeek Harness 的所有可见顶层窗口,以及标题以 DeepSeek Harness 结尾的窗口——实测 DSH 主窗口的宿主进程未必叫这个名字,只按进程名会一个窗口都匹配不到,DSH 就留在冻结画面里。截完按各自原状态 SW_SHOW / SW_SHOWMAXIMIZED 还原。
  • 隐藏后再检查一次:如果仍有窗口可见(隐藏没生效),就把它的矩形从冻结画面里涂掉,保证截图里永远不会出现 DSH。
  • 隐藏/还原的窗口列表用 ArrayList 以参数传入(Get-TargetWindows -Into),不让函数直接 return 集合——PowerShell 会把单元素集合展开成单个对象,只匹配到主窗口时还原逻辑会整段失效(表现为截图后 DSH 停在任务栏、必须手点图标)。还原后还会 SwitchToThisWindow + SetForegroundWindow 把主窗口切回前台。
  • 框选区域不压暗、不画阴影,只描一圈清晰的双色边框(外黑内蓝),任何背景下都能看清边界。
  • DSH 桌面版的 Host 是独立的 node 进程,没有 desktopCapturer,渲染层又被 contextIsolation 隔离且禁用 display media,所以「冻结 + 拉框」交给随包发布的 helper.ps1 完成。
  • 插入图片的三条通道:ctx.get('conversation').createDrafts() + inputActions.addAttachments() → 合成 Lexical paste 事件 → 剪贴板兜底。
  • 落盘:截图先在临时目录收尾,插入后由 DSH 自己的附件通道写入附件库(内容哈希命名、自动去重)。宿主同时算一遍 sha256,把 {时间, 文件名, 哈希, 尺寸, 会话 id/标题, 对象绝对路径} 追加到会话目录的 screenshots.json,用来说明“这张图属于哪个对话”;写不进去不影响截图。

隐私与安全

  • 截图只在本机流转:宿主进程调用 powershell.exe 起一个 WinForms 全屏窗口,画面只在内存里;选完的裁剪结果交给 DSH 附件库保存,临时目录里的中转文件读回浏览器后立即删除。插件没有网络请求。
  • 插件会以你的用户权限执行 powershell.exe -File helper.ps1 -ExecutionPolicy Bypass(脚本随包发布,可自行审查);它只隐藏/还原进程名或窗口标题匹配 DSH 的窗口,不会碰别的程序。
  • 插入图片复用 DSH 自己的附件通道,图片和普通粘贴的图片走同一条上传/落盘路径。
  • 本地留痕只有三处:附件库里的图片本体;会话目录的 screenshots.json(文件名、哈希、尺寸、会话标题——用来回答“这张图属于哪个对话”);%TEMP%\dsh-print-screen-helper.log(只记耗时、隐藏窗口数量和标题长度,不记标题内容)。
  • 诊断开关默认关闭:只有设置环境变量 DSH_SCREENSHOT_FRAME_DUMP=1 后,在取消截图时才会把整块冻结画面存到 %TEMP%\dsh-print-screen-last-frame.png,用于排查“DSH 有没有被截进去”。
  • 截图会离开本机:图片作为消息附件发送时,会随对话内容一起发给模型服务商。那是 DSH 本身的行为,不是这个插件联网。

自测

node test.mjs          # 路由注册、跨站拒绝、真实截一张(-SelfTest 固定裁剪,不弹窗)
node test-client.mjs   # 按钮结构、Shift / Ctrl+Alt+1 快捷键判定、时间戳文件名
node test-route.mjs    # 集成:真实路由 + 常驻助手 + 模拟拖框,量冷/热延迟
node --check client.js
node --check index.js
powershell -ExecutionPolicy Bypass -File test-helper.ps1   # 常驻助手:启动耗时、热态延迟、模拟拖框
powershell -ExecutionPolicy Bypass -File test-hide.ps1     # 用一个替身窗口验证「隐藏 / 还原」

助手协议支持自测模式(第 6 个字段为 1,不弹窗口、固定裁 320×200),node test.mjs 用的就是它。

已知限制

  • 仅 Windows。
  • 冻结窗口是普通 TopMost 窗口,理论上可能被其他 TopMost 窗口盖住一角。
  • 截图期间 DSH 会被隐藏;如果助手卡死(5 分钟超时会被杀掉),DSH 需要手动还原。
  • 不要手动删附件库里的图片:消息里存的是哈希引用,删掉对象会让对应会话报错、历史图片无法显示。要清理就用「删除会话」插件,让图片随会话一起删。
  • 实战案例 ①:删掉附件库里的对象,那个会话就报废了(实测)。 把 ~/.dsh/attachments/v1/objects/ 下一个被消息引用的对象文件改名,历史里那张图立刻变成「图片加载失败」占位;再发任何一句话,模型请求连续重试 5 次后失败,界面只写 DeepSeek Messages transport failed——从这句错误里完全看不出是附件缺失。图片按哈希引用,删对象等于把消息里的一段内容抽走,而且没有回收站;这种会话恢复不回来,只能整条删掉(用「删除会话」插件)。
  • 实战案例 ②:截图途中助手被杀,DSH 停在隐藏状态。 截图时 DSH 是用 SW_HIDE 隐藏的,正常情况下裁剪完立刻还原。但如果这期间助手进程被结束(任务管理器、杀毒软件拦截、或卡住 5 分钟被超时杀掉),隐藏状态就不会还原——任务栏里看不到窗口,看起来像 DSH 崩了。此时到任务管理器结束 DeepSeek Harness 进程,重新启动即可,不用重装、配置也不会丢。

更新记录

版本 变化
0.4.8 仅文档:补实测结论(删附件对象的完整症状与错误文案)、说明 screenshots.json 里 object 是推算路径、新增反馈邮箱。代码与 0.4.7 完全一致。
0.4.7 补更新记录、两条实战案例,澄清安装与落盘说明。代码与 0.4.6 一致。
0.4.6 修正 npm 元数据里的账号邮箱,功能不变。
0.4.5 已标记 deprecated,请用最新版。
0.4.4 首个公开版本,已标记 deprecated。

反馈

[email protected](在102前面加上 rr210)

License

MIT


English: README.en.md