dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
DSH 输入框旁的整屏截图面板:框选后可标注矩形、椭圆、箭头、画笔、马赛克与文字,再插入对话、复制或另存为;框选区域可以用 PP-OCRv6(onnxruntime-node,模型未就绪时回落 Windows 自带 OCR 引擎)离线识别成文字,再用 DSH 里已配置好的模型翻译成九种语言。Windows 下框选覆盖含任务栏的整个屏幕,截图前会先隐藏 DSH 窗口,截到的是真实桌面;面板由 Edge 或 Chrome 的 kiosk 窗口承载,找不到可用浏览器时回退到 DSH 内截图。注意 DSH Desktop 默认不放行普通浏览器的访问,所以整屏面板还需要在 profile 设置里打开「允许在浏览器中打开」(兼容模式);没打开时每次截图都走 DSH 内回退流程。仅支持 Windows(DSH Desktop);PP-OCRv6 模型在首次识别时从 ModelScope 下载。
简体中文 | English
给 DSH Desktop 加一个截图插件:点输入框旁的按钮(或按 Alt+A)→ 屏幕整体变暗、出现独立的全屏截图面板(微信式:覆盖整屏含任务栏,DSH 窗口先被临时隐藏,所以你看到的就是真实桌面)→ 框选并标注(矩形/椭圆/箭头/画笔/马赛克/文字,可撤销重做)→ 点「插入对话/复制/另存为」,面板关闭,结果落到 DSH 里。
同一块选区还能识别文字和翻译:工具栏最左边的「识别文字」把框选区域读成可复制、可编辑的文本(默认引擎 PP-OCRv6,在本机跑,离线、无密钥;模型没就绪时回落 Windows 自带 OCR),「翻译」再把它翻成 9 种语言的任意一种(用你在 DSH 里已经配好的模型,默认零配置);原文与译文都落在同一张结果卡片上,各自带「复制 / Copy」。
下面三张都是真机实拍(本机 3440×1440 主屏),没有摆拍、没有合成:
① 框选 + 标注 + 区域识别(面板) —— 拖出选区后工具栏出现(第一行最左边是「识别文字」与「翻译」两个按钮),红框/椭圆、黄色箭头与手绘曲线、两个文字标注都画在选区里;下方是「识别文字」的结果卡片(引擎 ppocr-v6-small、耗时、行数,行尾「复制 / Copy」)。识别读的是屏幕上原图:画在文字上的红框不会进识别结果。
② 抓屏模式(右键截图按钮) —— 「穿透截图(隐藏 DSH)」与「普通截图(含 DSH 窗口)」,当前选中的那条前面是蓝点;选择会被记住,左键 / Alt+A 都用它。
③ DSH 插件页里的同一项配置 —— 插件 → DSH 截图(xn),下拉框写的是同一份 captureMode,改完立即生效、重启保留。
![]() | ![]() |
| ② 右键菜单切换模式 | ③ 插件页切换模式(同一份配置) |
插件名:
dsh-screenshot-xn(2026-09-30 由dsh-screenshot改名,用于和市面上同名/近名的截屏插件区分)。 目录(改名当天)仍是E:\dshplugins\dsh-screenshot;改的只是包名与 cordis 行 id/name(插件列表与插件页显示的就是它)。 2026-10-11:仓库目录迁到E:\dsh-screenshot-xn\dsh-screenshot(与仓库名对齐,老路径E:\dshplugins\dsh-screenshot作废)。目录一搬,profile 里那条link:就指向了不存在的路径 —— 在 DSH Desktop 2.0.17 上这是启动级故障(整个应用起不来),不是「插件不生效」:见「故障排查」第一行。 保持不变(都是冻结契约,改名不碰):路由/api/dsh-screenshot/*、度量头X-DSH-Screenshot、 槽位条目 iddsh-screenshot.button/dsh-screenshot.overlay、DOM 标记data-dsh-screenshot-ui。 同名迁移已做:活动 profile 的注册(依赖键 / bundles / 行 id+name)、captureMode持久化覆盖、node_modules 链接。
E:\dsh-screenshot-xn\dsh-screenshot(2026-10-11 起;此前为 E:\dshplugins\dsh-screenshot)System.Drawing(系统自带,无第三方原生依赖);独立面板用系统浏览器(Edge / Chrome)的 kiosk 窗口承载;客户端半是纯浏览器代码。comfyui、node v24.14.1、Windows PowerShell 5.1.26100.9444(R-06:DSH 升级后请重新目视核对槽位与路由)。改完代码要重启 DSH Desktop:宿主半(
index.js的路由、kiosk 拉起、退出清理)只在 DSH 进程启动时加载。若宿主还是旧版,点截图会看到「独立截图面板不可用…(overlay.start http 404)」并自动回退到下面「局限」里说的 DSH 内覆盖层流程——功能不缺失,但那不是本版本的主路径。
安装由官方 plugin_manager 工具负责,不要手写 profile 的 package.json / cordis.patch.yml,也不要在 profile 目录执行 pnpm。
用 plugin_manager 的 install_bundle 指向本目录的绝对路径:
plugin_manager
action: install_bundle
target: E:\dsh-screenshot-xn\dsh-screenshot
该动作会调用 pnpm 把本地目录装进活动 profile,并按 package.json 的 dsh.bundle.patch 应用 cordis.patch.yml。
target 就是本仓库当前所在的绝对路径(上面是这台开发机上的路径;换机器 / 搬目录请换成你自己的)。装完后 profile 的 package.json 里会留下一条 "dsh-screenshot-xn": "link:<该路径>",<profile>/node_modules/dsh-screenshot-xn 也指向同一目录。
这两处任意一处指向不存在的路径,DSH Desktop 2.0.17 就拒绝启动(症状与修法见「故障排查」第一行)。
需要批准:安装动作(以及可能的构建脚本)会请求用户批准;未批准时安装不会生效。若返回体里出现 pendingBuilds,本插件不需要任何构建脚本,不要为它授权。
记录 install_bundle 返回的 application 字段(DoD A-7):
application | 含义与后续动作 |
|---|---|
applied | 当场生效(客户端半热加载);宿主半的路由仍以进程启动时加载的那份为准,改过 index.js 请重启 DSH |
restart-required | 需要重启 DSH Desktop 后再核对 |
overridden | 有更高优先级的层覆盖了本行,检查 profile 的 patch 层 |
failed | 看返回体的 warnings/错误原因;常见原因是 target 不是本目录的绝对路径 |
前置开关(每一台新机器都要看):整屏面板需要 DSH Desktop 放行"普通浏览器访问"。 主路径的「独立整屏截图面板」是由系统浏览器(Edge / Chrome 的 kiosk 窗口)去加载
http://127.0.0.1:<DSH 端口>/api/dsh-screenshot/overlay/page实现的;而 DSH Desktop 的 webServer 默认只放行带渲染器令牌的请求 —— 实测 DSH Desktop 2.0.15 里lib/webserver.js的permits()每次请求现查desktopBrowserAccess,lib/desktop-browser-access-*.js的decideDesktopBrowserAccess在ordinaryBrowserEnabled为false(=openBrowser的默认值)时对一切非 Electron 渲染器请求返回denied,于是响应是403+ 正文就 9 个字节forbidden。 在默认设置下,kiosk 窗口拿到的就是这个 403:插件在抓屏之前就会预检出来(desktop-browser-access-denied), 给出可见提示,并自动回退到「DSH 内覆盖层」流程(DSH 窗口里先出一张冻结帧,再在这张图上框选)。 这不是故障,也不是装的版本不对 —— 功能不缺失,但那不是主路径。打开方式(任选其一,改完重启 DSH Desktop;host 半只在进程启动时加载):
界面:DSH Desktop 设置 → 「浏览器与局域网」→ 勾选「允许在浏览器中打开」。该开关只在窗口模式为「兼容模式」时可用,否则是灰的并写着「浏览器访问仅在兼容模式下可用;如需开启,请先选择兼容模式。」;
配置:往当前 profile 的
cordis.patch.yml里加下面这一行(本机就是靠它验证的,case与缩进照抄):- id: desktop-shell name: dsh-plugin-desktop config: mode: compatibility openBrowser: true networkExposure: loopback不打开也能用:整屏面板那一套每次都走回退路径,标注、识别、翻译、插入、复制、另存为的行为完全一样。
前置条件 2(不要提权):DSH Desktop 必须以普通权限运行。 被提权的父进程拉起 Chromium(Edge / Chrome)时,浏览器会走 AutoDeElevate:它检测到自己由提权进程启动就立刻退出,让 shell 以普通权限重新启动自己;命令行偏长时这次中继会把参数丢掉,于是 kiosk 根本没打开面板 URL。现象:点截图后窗口闪一下就不见了、DSH 日志里
panel files fetched: 0 (none),而浏览器自己的日志(插件已开--enable-logging,落在临时 profile 的chrome_debug.log)只有一行Edge is running elevated: 1;上游是 Chromium issue 436869753。 插件自 t81 起已在 kiosk 旗标里带上游的规避开关--do-not-de-elevate,提权时一般也能正常打开面板 —— 代价是浏览器会一并留在提权状态(这是它唯一的副作用),所以推荐的做法始终是不要提权。 特别地,如果 DSH 的 exe 被打了兼容性层~ RUNASADMIN(在HKCU\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers下),提权就是必然的:启动日志会直接点名(DSH is running with the RUNASADMIN compatibility layer (__COMPAT_LAYER=…))。去掉那一层、以普通权限重启 DSH Desktop 即可。
自检清单(安装 + 重启后 30 秒内可做完):
panel files fetched: 0 (none) 且浏览器日志只有 Edge is running elevated: 1。配置写在 cordis.patch.yml 那一行的 config: 里(config: {} 即全部使用默认值)。所有键都是可选的,未知键会被忽略。
其中 captureMode 比较特别:它由 Config schema 声明为 volatile 字段,并由 client 半边贡献到 DSH 插件页的 plugins.bundle.config(key = 包名 dsh-screenshot)—— 所以它会以「截图模式 / Capture mode」下拉框出现在 插件 → dsh-screenshot 那一页,和语音输入插件的「识别服务 / 识别语言」同一套机制。你在那里(或右键菜单里)改的值由 DSH 写进当前 profile 的 cordis.patch.yml(不是本插件的 bundle patch),改完立即生效、重启保留。手写 config: { captureMode: normal } 同样有效。
注:DSH 插件页只在有客户端贡献时才渲染配置区(
renderSlot("plugins.bundle.config", …, { entryKey: pkg.name })外面那层configured ? … : null),所以这一项必须由本插件的client.js贡献 —— 这是 t74 补上的最后一环。 长这样(真机实拍):见「效果预览」的 ③ 插件页切换模式。
- insert:
- id: dsh-screenshot-xn
name: dsh-screenshot-xn
config:
timeoutMs: 20000
cacheTtlMs: 1500
keepTempFile: false
抓屏(宿主)
| 键 | 默认值 | 含义 |
|---|---|---|
captureMode | through | 可持久化的抓屏模式(through / normal),也是设置页 / 插件管理里唯一渲染出来的那一项:声明为 Config 的 volatile 字段,改完立即生效并写进当前 profile 的 patch,重启后保留。右键菜单选的模式写的就是它。 |
enabled | true | false 时插件不注册任何路由(等同临时停用) |
timeoutMs | 20000 | 单次抓屏的上限;超时后子进程被终止并返回 502 capture.failed(capture.timeout) |
cacheTtlMs | 1500 | 抓屏结果的短 TTL 缓存:并发 fetch 由"单飞"合并成一次抓屏;缓存引用在 TTL 后释放(D-9) |
scriptPath | lib/capture.ps1 | 抓屏脚本路径;相对路径按包目录解析,也可给绝对路径(D-6 的故障注入点) |
outDir | %TEMP%\dsh-screenshot-xn | 抓屏 PNG 的临时目录(读完即删,见 keepTempFile) |
tag | dsh-screenshot-xn | 临时 PNG 的文件名前缀 |
keepTempFile | false | true 时保留临时 PNG,便于排查(注意会累积磁盘占用)。t75 起它同样是识别用的区域 PNG 与剪贴板用的文本文件的开关 |
powershellPath | %SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe | PowerShell 引擎路径;不存在时回退到 PATH 里的 powershell.exe |
hideWaitMs | 250 | 隐藏 DSH 窗口后等待多久再抓屏。取值被夹到 150–400 ms |
hideMethod | hide | hide(SW_HIDE,窗口完全消失)或 minimize(SW_MINIMIZE,走最小化动画) |
dshImage | 由 process.execPath 推导(如 DSH Desktop) | 用来确认"这是我自己的窗口"的映像名。只有 pid/映像名/标题之一能对上,才允许隐藏 |
dshTitleHint | DSH | 标题包含该片段的窗口才算 DSH 的(最后的兜底判据) |
dshPid | 插件宿主进程的父进程 pid | 首选的窗口归属进程(Electron 主进程)。脚本还会用映像名/标题交叉确认 |
独立截图面板(覆盖层,B1)
| 键 | 默认值 | 含义 |
|---|---|---|
overlayBrowserPaths | Edge ×2 → Chrome ×2(C:\Program Files…) | 可用的浏览器可执行文件候选,按顺序取第一个存在的;给了自定义列表就不再去 PATH 里找 |
overlayDir | 包内 overlay/ | 面板页面目录(index.html + overlay.js + overlay.css),由宿主同源提供 |
overlayHostScript | lib/overlay-host.ps1 | 浏览器查找 / kiosk 探活 / 关窗与强杀的辅助脚本 |
overlayHeartbeatMs | 5000 | 心跳静默上限:面板页面每 1 s 发一次心跳,超过这个窗口没消息就判会话 aborted(夹到 1000–5000) |
overlayTimeoutMs | 120000 | 单次面板会话的上限;到点判 timeout 并关掉 kiosk 窗口(夹到 5 s–15 min) |
overlayRetainMs | 30000 | 会话结束后帧/结果还保留多久(客户端要在 ready 后取走结果 PNG) |
overlayBodyLimitBytes | 33554432 | 面板回传结果的上限(32 MB) |
overlayLaunch | 无(用真实浏览器) | 仅供离线测试:用自定义 argv 替身替换浏览器拉起命令,${url} / ${token} 会被替换;也可用环境变量 DSH_SCREENSHOT_OVERLAY_LAUNCH(JSON 数组或空格分隔命令) |
区域识别与翻译(t75)
| 键 | 默认值 | 含义 |
|---|---|---|
ocrEnabled | true | false 时识别接口回 503 ocr.disabled(面板上仍看得见按钮,点了会说明原因) |
ocrScriptPath | lib/ocr.ps1 | 识别脚本路径;相对路径按包目录解析。它跑在 Windows PowerShell 5.1 上(powershellPath,也是 capture.ps1 用的那个引擎)—— 只有 5.1 还投影 Windows.Media.Ocr 的 WinRT 类型 |
ocrLanguage | 空(自动) | BCP-47 标签,例如 zh-Hans-CN / en-US;空 = 用 Windows 用户配置的语言顺序。脏值(含引号、分号、超长变体)一律回落"自动",不会带进脚本参数 |
ocrTimeoutMs | 20000 | 单次识别的上限;超时回 504 ocr.timeout |
识别引擎(t77:默认换成 PP-OCRv6,本地跑)
Windows 自带引擎对小字中文截图很差(同一张 1978×1059 的实机截图,「截取屏幕、框选并标注,然后复制、另存为或插入当前对话。」
被读成「截取驛幂梃选并标注 然 制 、 另 存 为 或 入 当 前 对 话」)。所以默认引擎换成了
PP-OCRv6(检测 + 识别)跑在 onnxruntime-node 上:同样这张图,它能读成
DSH 截图 v1.0.0 / dsh-screenshot / 截取屏幕、框选并标注,然后复制、另存为或插入当前对话。 /
穿透截图(隐藏 DSH) / Through (hide DSH),耗时约 1.6s(CPU、960 长边、30 行)。
| 键 | 默认值 | 含义 |
|---|---|---|
ocrEngine | auto | auto = 模型就绪就用 PP-OCR,否则回落 Windows 引擎;onnx = 强制 PP-OCR(不可用即 503 ocr.unavailable,不静默降级);windows = 只用自带引擎 |
ocrModelTier | small | tiny / small / medium(检测 + 识别两个模型)。体积:tiny ≈ ?/ small ≈ 9.9MB + 21.2MB / medium 更大,质量递增 |
ocrModelDir | ~/.dsh/dsh-screenshot-ocr | 模型存放目录(用户数据目录,不是临时目录);删掉即回到 Windows 引擎 |
ocrModelSource | ModelScope 官方直链 | 模型下载源前缀;需要自建镜像/代理时改它(默认源国内可直连) |
ocrDownloadModels | true | 首次识别时按需下载模型(SHA256 校验,写入 .part 再原子改名)。false = 永不联网,只用已有模型或 Windows 引擎 |
ocrDetLimit | 960 | 检测输入长边上限(夹在 320–4096);调大更准更慢 |
依赖与体积:运行时是 npm 包 onnxruntime-node(预编译,免 Python/GPU/管理员),声明为
可选依赖:不允许构建脚本 / 装不上 / 离线时插件照样能装能用,OCR 自动回落 Windows 引擎(auto 档)。
它的 postinstall 会下载原生库,所以安装时允许构建脚本能让 OCR 更强(管理器会问)。这个包把各平台二进制都打进 tarball(实测 287MB),
本插件带一个裁剪助手,只留当前平台:
node lib/prune-onnx-runtime.mjs # 先看能省多少(只报告)
node lib/prune-onnx-runtime.mjs --apply # 真删:288MB → 65MB(win-x64),重装依赖即可恢复
模型与 ONNX Runtime 都不随包分发,也不进 git。许可:PP-OCR 模型与 RapidOCR 清单为 Apache-2.0,
onnxruntime-node 为 MIT;本插件只下载官方直链并按官方 SHA256 校验(清单见 lib/ocr-models.mjs)。
| translateEnabled | true | false 时翻译接口回 503 translate.disabled。翻译会真的调用你的模型(花 token),所以给了独立的开关 |
| translateProvider / translateModel | 空 / 空 | 翻译用哪条模型路由;两个都留空 = 用 DSH 当前默认模型(agentDefaultModel.currentSelection()),所以默认零配置 |
| translateTarget | zh-Hans | 默认目标语言,取值是 lib/ocr.mjs 里那张闭集的 id(zh-Hans/zh-Hant/en/ja/ko/fr/de/es/ru)。它只决定面板下拉框的初值:用户在卡片里改只影响本次会话 |
| translateTimeoutMs | 45000 | 单次翻译的上限;到点用 AbortController 收回请求并回 504 translate.timeout |
| translateMaxTokens | 4000 | 译文输出上限(一屏文字用不到更多) |
| translatePrompt | 空 | 追加到 system 提示词后面的额外要求(例如"术语保持英文"),会与内置规则逐条并列 |
宿主路由(客户端半依赖它,也可用于自检)
| 项 | 值 |
|---|---|
| 抓屏 | GET /api/dsh-screenshot/capture(其他方法 405 {"ok":false,"error":"method.not_allowed"}) |
| 抓屏成功 | 200,body 是 PNG,Content-Type: image/png、Cache-Control: no-store, max-age=0 |
| 抓屏度量头 | X-DSH-Screenshot: <base64url(JSON)>,形如 {"widthPx":3440,"heightPx":1440,"url":"/api/dsh-screenshot/capture","scale":1,"bounds":{"x":0,"y":0,"width":3440,"height":1440},"viewportCss":{...},"bytes":2202907,"elapsedMs":148,"mode":"through","hiddenMs":325,"restoreOk":true} |
| 抓屏失败 | 502,{"ok":false,"error":"capture.failed","message":"<短文本,例如 capture.timeout: no result within 20000 ms>"} |
| 抓屏查询参数 | vw/vh=抓屏时客户端 CSS 视口宽高;fresh=1 跳过缓存重新抓屏;mode=through 启用穿透抓屏(缺省/其它值=普通模式) |
| 面板会话 | POST /api/dsh-screenshot/overlay/start → {ok:true,token,startMs,mode,hiddenMs,frameBytes},无可用浏览器时 {ok:false,reason:"no-browser"}(不隐藏 DSH、不抓屏) |
| 面板页面与静态资源 | GET …/overlay/page?token=、…/overlay/asset/<name>、…/overlay/lib/<name>.mjs(纯逻辑模块直接给页面 import,不复制一份) |
| 面板帧 / 结果 | GET …/overlay/frame?token=(会话缓存里的冻结帧,刷新页面不会二次抓屏)、GET …/overlay/result.png?token=(ready 后才可用) |
| 面板状态 / 心跳 / 提交 | GET …/overlay/status?token= → `{state:"running" |
| 区域识别 | POST …/overlay/ocr?token=,body {png:"data:image/png;base64,…", language?:"zh-Hans-CN"} → 200 {ok:true,text,lines:[{text,words:[{text,x,y,width,height}]}],language,engine,elapsedMs,empty};失败 400 ocr.bad-body / 503 ocr.unavailable,ocr.disabled / 502 ocr.failed / 504 ocr.timeout |
| 翻译 | POST …/overlay/translate?token=,body {text:"…", target?:"ja"} → 200 {ok:true,text,source,target,provider,model,unchanged,truncated,elapsedMs};失败 400 translate.bad-body / 503 translate.unavailable,translate.disabled / 502 translate.failed / 504 translate.timeout |
| 复制文本 | POST …/overlay/clipboard?token=,body {text:"…"} → 200 {ok:true,chars,elapsedMs}。页面自己不碰剪贴板:它把文本交给宿主,宿主用 lib/clipboard.ps1 写系统剪贴板(tests/overlay-page.test.mjs 的 t57-4 钉着这条) |
| 未知 token | 404 {"ok":false,"error":"overlay.unknown-token"}(所有数据路由都先校验 token,含上面三条) |
注意:度量头用的是 base64url 字母表(-、_,无 = 填充),客户端解码时要先还原成标准 base64。
抓屏链路:index.js → lib/capture.ps1(DPI-aware,SetProcessDpiAwarenessContext(PER_MONITOR_AWARE_V2))→ 输出 ---JSON-BEGIN--- {...} ---JSON-END---;脚本失败时以非零退出码 + ok:false 的 JSON 报错。优先使用宿主已提供的 subprocess 进程 Service,不可用时回退 node:child_process;两条路径都受 timeoutMs 约束、隐藏子进程控制台窗口、超时强制终止(D-7:无残留进程)。
抓屏时隐藏 DSH(贯穿两条路径)
语义:抓屏前把 DSH 自己的窗口临时隐藏(SW_HIDE,或 hideMethod: minimize),等待 hideWaitMs 让桌面合成器重绘,抓一帧,然后立即恢复(SW_RESTORE + SetForegroundWindow)。因此被 DSH 遮住的桌面内容可以截到,而且面板里看到的画面不含 DSH 界面。
只动自己的窗口:脚本按 ①父进程 pid ②映像名 ③标题片段 三条判据确认"这是 DSH 的窗口",且必须是可见的顶层窗口、且不是脚本自己。任何一条都对不上就不隐藏,直接回退普通抓屏(响应仍是 200,只是 mode 回到 normal,图里含 DSH)——宁可截到 DSH,也不会去最小化用户的其它窗口。
恢复不依赖运气:隐藏+等待+抓屏+恢复全程在脚本的 try/finally 里;恢复判据是"窗口重新可见且未最小化",SW_RESTORE 会重试并依次降级到 SW_SHOW/SW_SHOWNORMAL;如果脚本报 restoreOk:false,宿主还会再跑一次 -RestoreOnly 救援入口(幂等,5 s 上限)。恢复未确认时整次请求返回 502,不会把"可能停在被隐藏状态"的图交给客户端。
耗时(本机 3440×1440 实测,脚本内计时取 3 次中位数):
| 路径 | 脚本总耗时 | 其中隐藏段 hiddenMs | 端到端(含进程启动) |
|---|---|---|---|
| 普通(不隐藏) | 89 ms | 0 | ≈ 490 ms |
隐藏再抓(hideWaitMs: 250) | 539 ms | 325 ms | ≈ 980 ms |
隐藏再抓(hideWaitMs: 150) | 442 ms | 230 ms | ≈ 890 ms |
即隐藏比不隐藏多约 350–450 ms。要把面板弹得更快,把 hideWaitMs 调到 150(少等待可能让个别窗口停留在半透明淡出帧上,可自行权衡)。
两种方式,任选其一:
plugin_manager → action: remove_bundle,target: dsh-screenshot-xn。该动作会从活动 profile 移除包与 bundle 行;按提示重启后按钮消失,DSH 正常启动(F-4)。工作区源码目录不受影响。plugin_manager → action: set_bundle,target: dsh-screenshot-xn,enabled: false。重启后插件不激活,输入框不受影响(D-5);enabled: true 即可恢复。残留检查:卸载后 %TEMP%\dsh-screenshot-xn 里最多有本插件留下的临时 PNG(正常路径下读完即删);%TEMP%\dsh-screenshot-xn-overlay-<token> 是面板浏览器的一次性 profile,会话结束即删(强杀等异常路径可能留下空目录,可直接删除)。
| 现象 | 原因与处理 |
|---|---|
DSH Desktop 升级到 2.0.17 后整个应用起不来(窗口一闪就没、或只有一个空白页/错误框),日志里是 dsh-plugin-desktop: cannot resolve package "dsh-screenshot-xn" from the Desktop installation or active Profile(错误名 PackageOverlayNotFoundError) | profile 里本插件那条 link: 指向了不存在的路径(典型场景:仓库目录被搬走或改名,见开头的「2026-10-11 迁目录」)。2.0.17 在启动时会拿 profile 的每一行 bundle 走 resolveOverlayPackage(lib/profile-*.js 的 loadRecoveryFilteredProfile → lib/package-overlay-*.js),解析不到就抛错并拒绝启动;2.0.15 及更早只是把这一行当「缺失」跳过,所以这是升级到 2.0.17 之后才显形的。处理(任选其一):① 官方路径:plugin_manager → action: install_bundle、target: E:\dsh-screenshot-xn\dsh-screenshot,它会把这条链接重写到当前路径;② 手工路径:改 profile 的 package.json,让 "dsh-screenshot-xn": "link:<本仓库绝对路径>" 指向真实目录,并让 <profile>/node_modules/dsh-screenshot-xn 指向同一目录(junction 或 symlink 都行,必须是目录链接而不是拷贝)。起不来时的应急通道:把 dsh-screenshot-xn 从 profile 的 dsh.profile.bundles 里去掉(=桌面端把插件「停用」),DSH 立刻能起,之后再按上面修好加回来。自检(不必重启):node -e "const{createRequire}=require('module');console.log(createRequire('<profile>/package.json').resolve('dsh-screenshot-xn/package.json'))" 能打印出路径即已修好;2.0.17 只认落在 <profile>/node_modules 里的那份解析结果(lib/package-overlay-*.js 里那条偏移检查),所以别把 link: 指到 profile 之外的祖先 node_modules、指望它兜底 |
| 输入框旁没有截图按钮 | ① 插件未激活:确认 install_bundle 返回 application: applied,否则重启一次;② 客户端 bundle 未加载:打开开发者工具看是否有 bundle ... loaded without registering "dsh-screenshot";③ 槽位:本插件当前只注册 conversation.input.right(若该槽在 composer 变体下不渲染,保底方案是改注册 conversation.composer.dock,按钮会移到输入卡片下方,见"局限") |
| 点截图后全屏被一层白/灰底挡住,只看得见一行"拖动鼠标框选"和一个输入框 | 面板页的 CSS/JS 没加载成功(没样式化的骨架)。原因(t64 已修):宿主把 token 校验放在静态资源分支之前,而页面的 <link>、<script> 与静态 import '…/overlay/lib/x.mjs' 都不带 token → 三族全 404、脚本一行不跑、没有心跳、5 s 后会话 aborted。修好后必须重启 DSH Desktop(宿主半只在进程启动时加载);页面自带启动看门狗,再遇到这种情况会在屏幕顶部显示红色横幅(含自身 URL),宿主日志里也会有 overlay static file missing: 行 |
| 点截图后提示「独立截图面板不可用…(overlay.start http 404)」 | 宿主半还是旧版(没有 overlay 路由):重启 DSH Desktop。本次截图已经自动回退到 DSH 内覆盖层流程,功能不缺失 |
| 点截图后提示「未找到可用的浏览器(Edge / Chrome)…」 | 机器上没有 Edge/Chrome,或 overlayBrowserPaths 指错了:面板无法全屏承载,本次截图自动回退到 DSH 内覆盖层流程 |
| 点截图后提示「DSH Desktop 挡住了独立截图面板(…403 forbidden)…」 | 这是默认状态,不是故障:DSH Desktop 的 webServer 默认只放行带渲染器令牌的请求,普通浏览器(kiosk 窗口)访问面板页一律得到 403 + 正文 forbidden(lib/webserver.js 的 permits() → decideDesktopBrowserAccess,openBrowser 默认 false)。插件在抓屏前预检到这一条,本次截图已自动回退到 DSH 内覆盖层流程。想要完整的整屏面板:设置 → 「浏览器与局域网」→ 勾选「允许在浏览器中打开」(该开关只在兼容模式下可用),或在当前 profile 的 cordis.patch.yml 里给 desktop-shell 那行加 openBrowser: true,然后重启 DSH Desktop。细节见「安装」里的前置开关 |
开关已经开了,点截图却什么都不出来,DSH 日志里是 overlay aborted: no heartbeat for … ms: the kiosk page never pinged (…) | 闸门已经放行(预检没报 403),是浏览器那一侧没把面板页渲染出来:kiosk 进程起来了(日志里 stopped=true killed=false,不是崩溃),但一次心跳都没发。三个已知原因(t80 都已处理):① 机器上有系统代理(ProxyEnable=1,ProxyServer 指向本机端口;代理本身是死的更明显)——代理替 127.0.0.1 作答,面板就成了白屏/错误页,而宿主看不出区别(本机实测:配了死代理时 Edge 打开面板 URL 只拿到一张错误页,加上 --no-proxy-server 立刻恢复成真页面)→ kiosk 现在带 --no-proxy-server;② 每次会话都用全新的 --user-data-dir,慢机器冷启动可能超过心跳窗口 → 首次心跳给 20 s(OVERLAY_FIRST_PING_MS);③ 开关散在 URL 两侧时,哪些开关真正生效取决于浏览器的解析顺序 → 现在所有开关都排在 URL 之前,URL 永远是最后一个参数。若仍是这一条,日志里紧接着有三行自诊断,按顺序读:overlay assets: … MISSING …(插件目录本身不完整,重装后重启 DSH Desktop)、panel files fetched: 0 (none)(浏览器一次都没连到插件服务器,问题在浏览器到回环地址这一段:代理/策略/根本没打开 URL)、panel files fetched: 3 (page, asset:overlay.js, lib:geometry.mjs)(文件都取走了但脚本没跑起来 → 看 overlay kiosk browser log: 里浏览器的原话) |
点截图后 kiosk 窗口闪一下就不见了,日志里 panel files fetched: 0 (none),而 overlay kiosk browser log: 只有一行 Edge is running elevated: 1 | DSH Desktop 正在以管理员权限运行(或它的 exe 被打了 ~ RUNASADMIN 兼容性层)→ Chromium 的 AutoDeElevate 检测到自己由提权进程启动就直接退出、让 shell 以普通权限重启自己,命令行偏长时这次中继会把参数丢掉,于是 kiosk 根本没打开面板 URL(上游 Chromium issue 436869753)。注意此时 stopped=true killed=false 也不能证明浏览器真的活着。处理:去掉那个兼容性层并以普通权限重启 DSH Desktop;插件自 t81 起已在 kiosk 旗标里带上游的规避开关 --do-not-de-elevate,所以提权时一般也能打开面板(代价是浏览器会一并留在提权状态),但推荐的做法始终是不要提权。启动日志里的 DSH is running with the RUNASADMIN compatibility layer (__COMPAT_LAYER=…) 就是这一条的预告 |
| 点截图后一直转圈 / 提示"抓屏失败" | 宿主抓屏路由没通。先在浏览器 fetch /api/dsh-screenshot/capture 看返回:404 表示路由未注册(插件未激活或 webServer 服务缺席,看 DSH 日志里 [dsh-screenshot] 行);502 表示脚本失败,message 里带 capture.* 细分码(capture.script-missing=scriptPath 指错、capture.timeout=超时、capture.spawn-failed=powershell 找不到) |
点截图后 502 且 message 含 capture.timeout | 单次抓屏超过 timeoutMs。先把 timeoutMs 调大;若仍超时,手动执行 npm run capture:probe 观察耗时(本机实测:脚本自身抓屏 73 ms、含保存 155 ms;Node 拉起进程后端到端约 560 ms) |
502 且 message 里是 GDI+ 中发生一般性错误(或任何 … @ line <n>) | 抓屏本身成功了,失败在写盘(t81):默认输出目录 %TEMP%\dsh-screenshot-xn 可能是上一次提权运行创建的,属主是 BUILTIN\Administrators 且没有任何给当前用户的 ACE,GDI+ 于是建不了文件;这种目录常常连删都删不掉(父目录不给 DELETE_CHILD),所以用户无法靠删目录自愈。现在 capture.ps1 落盘前先探测可写性(写一个 .dsh-write-probe-*.tmp 再删掉),不可写就自动改写到 %LOCALAPPDATA%\dsh-screenshot-xn(再不行用 %TEMP%\dsh-screenshot-xn-<随机>),本次截图照常成功;用了哪个目录、被拒的原因(含 owner)都在结果的 out_dir_requested / out_dir_writable / out_dir_reason / out_dir_fallback 字段里,宿主还会在日志里写一行 output directory is not writable, the PNG went to <目录>: <原因> |
| 面板出现了,但 DSH 窗口仍然出现在画面里 | 宿主没能确认"这是 DSH 的窗口"(pid/映像名/标题三条都对不上)→ 脚本按设计回退为"不隐藏"抓屏,并在界面给出可见提示。检查 dshPid / dshImage / dshTitleHint |
| 面板卡住不动,或提交后一直没回 DSH | 面板页面每 1 s 发一次心跳。第一次心跳之前宿主给 20 s 冷启动窗口(OVERLAY_FIRST_PING_MS,t80:每次会话都是全新 --user-data-dir,慢机器首绘可能超过常规窗口),之后超过 overlayHeartbeatMs 没消息宿主就判 aborted 并关掉窗口,DSH 侧给出可见提示(可重试);会话总上限是 overlayTimeoutMs。判 aborted 前宿主还会把浏览器自己写的日志尾部(overlay kiosk browser log:)与面板文件被取走的记录(panel files fetched:)一并写进 DSH 日志 |
| 面板提交后 DSH 里没有插入 | 插入走官方 paste 桥接(useInput 的附件数必须增加才算成功);桥接不可用时会真实降级为"复制到剪贴板 + 提示手动 Ctrl+V",不会静默。看控制台 [dsh-screenshot] paste bridge … 行 |
路由 404 | webServer 未激活(极简 profile)时插件只记一条日志、不注册路由,这是设计如此(插件不会拖垮启动)。换回带 Web 载体的 profile 即可 |
| 坐标对不上(框选区域与粘贴图不一致) | 面板是整屏 1:1:页面按"位图尺寸 = 冻结帧像素、CSS 尺寸 = 视口尺寸"绘制,框选坐标即屏幕坐标。若真的出现偏移,先确认抓屏是 DPI-aware(npm run capture:probe 输出的 dpi_awareness 应含 =True,scale 应为 1),并附面板控制台的 [dsh-screenshot] overlay mapping 日志 |
| 主题切换后 DSH 内面板看不清 | DSH 内覆盖层只用 --dsw-alias-* 令牌;独立面板是独立窗口,自绘深色 UI,不依赖 DSH 主题(这是刻意的:独立窗口拿不到 DSH 的 CSS 变量) |
| 权限/安全软件拦截 PowerShell | 抓屏依赖 powershell.exe 启动。企业策略禁用 -ExecutionPolicy Bypass 或拦截脚本时,把 scriptPath 指到允许的副本,或改用允许的策略重新调用;失败都会以 502 + capture.failed 显式报错,不会静默 |
用 git/pnpm 更新插件时报 Unable to create '…index.lock': Permission denied,而 cmd / PowerShell / node 写同一路径正常 | 火绒 6.0 一类安全软件会拒绝 MSYS2 家族可执行文件(各处自带的 git.exe、touch.exe、mkdir.exe、bash.exe 等)对非系统盘(D:/E:/F:/G:)的一切写操作,与 ACL、属主无关(提权时正常、C: 盘正常)。处理:把插件目录放在系统盘,或用 zip / 资源管理器覆盖安装(不经过 MSYS2 工具链),或在安全软件里给这些可执行文件放行 |
| 想验证纯逻辑单测 | 在包目录执行 npm test(即 node --test)。用例完全脱机:不读真实屏幕、不联网、不加载 DSH 运行时 |
| 识别说"这台机器没有可用的 OCR 语言包" | Windows 的 OCR 引擎要装语言包:设置 → 时间和语言 → 语言和区域,给中文/英文加上"可选语言功能 → 光学字符识别"。想看这台机器到底装了哪些,直接跑 powershell -NoProfile -ExecutionPolicy Bypass -File lib/ocr.ps1 -ListLanguages;只装了一种也没关系,ocrLanguage 留空即用它 |
| 识别出来是问号/方块 | 那是 PowerShell 的 stdout 编码问题,不是识别问题:重定向的管道默认按 OEM 代码页(中文机器上是 936/GBK)写出,宿主按 UTF-8 读。lib/ocr.ps1 与 lib/clipboard.ps1 顶上已经把 [Console]::OutputEncoding 钉成 UTF-8;自己写脚本调用同一批接口时记得照做 |
| 点翻译没反应/报"没有可用于翻译的模型" | 翻译走 ctx.get('llm') + DSH 的默认模型。先确认这个 profile 里有可用的模型(模型选择器里有值);也可以在配置里显式指定 translateProvider / translateModel |
| 不想让截图功能花 token | 把 translateEnabled 设为 false:翻译接口会回 503 translate.disabled,界面上会说明原因。识别是纯本地的,不受影响 |
conversation.input.right)的截图按钮,文案"截图 / Screenshot";Alt+A 是同一入口(应用内快捷键,见"局限")。captureMode,也就是设置页 / 插件管理里那一项,落在当前 profile 的 patch 里),因此 DSH 重启后不用重选;左键 / Alt+A 都用记住的模式;菜单本身不触发截图(截图永远由左键 / Alt+A 显式触发)。菜单支持键盘:↑↓ 移动、Enter/Space 选定、Esc 或点外面关闭,按钮上有 aria-haspopup="menu" / aria-expanded。