本文实测环境:Windows + DeepSeek Harness 官方桌面客户端(Electron)。文中的用户目录统一写成%USERPROFILE%、%LOCALAPPDATA%,本地监听端口写成<端口>,属于脱敏写法。插件本身是公开包dsh-pet,仓库见文末。
起因
看上了一个 DSH 桌宠插件:一只 Q 版小人,平时在屏幕上自己走动、点一下有反应、能拖起来扔出去,还会显示余额、在你干活的时候跟着切状态。
装的过程本身不复杂,一条命令。但装完遇到了一个很典型的困惑——屏幕上出现了两只一模一样的宠物。我要的是住在桌面上的那只,不想要网页里那只。
这篇按时间顺序记两件事:一是这个插件完整的安装路径(它和普通插件不太一样,会额外拉一个 Electron 运行时);二是那两只到底是怎么来的、怎么一眼分清哪只是哪只。
第一步:先确认你在哪个 profile 里装
DSH 的插件按 profile 隔离。桌面客户端读的是desktop,命令行dsh web读的是web,两边互不相通。装错房间的现象是:命令成功、日志干净、界面上什么都没有。
先问环境变量,不用猜:
Get-ChildItem env: | Where-Object Name -like 'DSH_*'关键的三项:
DSH_HOME %USERPROFILE%\.dsh DSH_PROFILE desktop DSH_PROFILE_DIR %USERPROFILE%\.dsh\profiles\desktopDSH_PROFILE=desktop就是答案。插件的 README 写的是「照你实际在用的那个填」,所以这里必须填desktop,不能照抄 README 示例里的web。
另外,通用的dsh命令按设计不接管desktopprofile——这台机器上实测会直接拒绝。要操作它,得用桌面客户端随包自带的那个 CLI:
$dsh = "$env:LOCALAPPDATA\Programs\DeepSeek Harness\resources\runtime\cli\bin\dsh.cmd"记住这个变量,后面安装和卸载都用它。
第二步:安装,以及那个 347 MB 的附加动作
& $dsh plugin --profile desktop add dsh-pet这就是一次普通的 pnpm 安装,不用手工改任何配置文件。装完 profile 的package.json里依赖和插件清单都自动写好了:
{ "dependencies": { "dsh-pet": "^0.3.6" }, "dsh": { "profile": { "bundles": [ "@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", "dshmarket", "dsh-pet" ] } } }这个插件特殊的地方在于它有「桌面形态」。一般插件分宿主半区和客户端半区两半,这个插件多了一半:它可以把宠物画在一个脱离浏览器的独立透明窗口里。
代价是要有 Electron 运行时。首次拉起桌面形态时,插件会自己探测本机有没有可用的 Electron,找不到就走官方@electron/get下载一份:
%USERPROFILE%\.dsh\electron\ # 运行时落地目录(本次实测 347 MB) %LOCALAPPDATA%\electron\Cache\ # 安装包下载缓存(本次实测 495 MB)两个数字放这儿,是让你对"装个小桌宠"的空间成本有个预期。下载是后台进行的,Electron 不可用时插件只打日志告警,不影响浏览器里的形态。
第三步:display决定宠物住在哪
装完第一件事是配display。这是每只宠物的必填字段,四个值:
| 值 | 含义 |
|---|---|
web | 只住在浏览器页面里(overlay) |
desktop | 只住在桌面独立小窗里 |
both | 两个都显示 |
none | 都不显示 |
包内默认配置给的是both。这就是「两只」的来源。一份宠物定义、两个渲染外壳:
- 浏览器半区把宠物画在页面 DOM 里,定位是页面坐标 + 角落 + 边距;
- 桌面半区为每只宠物各开一个独立透明窗口,窗口跟随宠物移动。
两个外壳共用同一份配置和同一套行为,所以两只长得一模一样、动作也同步——看起来特别像"配置写重了",其实配置里就一只。
改配置有两条路,生效时机完全不同,这是这次真正踩到的坑:
- 在 DSH 设置页「桌宠配置」里改,点保存。保存会写用户配置,并让宿主重启桌面窗口,网页端也会重新拉一次配置——即时生效。
- 直接编辑配置文件
%USERPROFILE%\.dsh\dsh-pet\main-config.jsonc。这个文件是用户覆盖层,优先于包内默认。但网页端只在页面加载时读一次配置,没有变更推送:你把both改成desktop存盘,桌面那只照旧,网页里那只也不会自己消失。
我走的是第 2 条路,于是遇到了标题里那个场景。修法很简单,二选一:
- 刷新一下 DSH 页面(
Ctrl+R)——页面重新拉配置,overlay 那只消失,只剩桌面那只; - 或者打开设置页点一次保存。
刷新只重载界面,不会丢当前会话。以后凡是手改配置文件,都记得补这一步。
第四步:怎么一眼分清哪只是桌面、哪只是网页
这才是这次最值得记下来的部分。
"两只"摆在一起,光看长相完全分不出来——同一份素材、同一套动画。可靠的判据是它们各自属于哪个进程、占据哪块屏幕区域。
第一步,列出有窗口的进程:
Get-Process | Where-Object { $_.MainWindowTitle -ne '' } | Select-Object ProcessName, Id, MainWindowTitle输出里会同时出现桌面客户端和桌宠的 Electron 助手,窗口标题写得很直白:
ProcessName Id MainWindowTitle DeepSeek Harness 22452 ... — DeepSeek Harness electron 13892 dsh-pet 桌宠(桌面模式)那个electron进程就是桌面形态的载体。它的命令行也可以直接看:
(Get-CimInstance Win32_Process -Filter "Name='electron.exe'").CommandLine...\electron.exe ...\dsh-pet\runtime\electron-helper\main.js第二步,拿到桌面窗口的矩形。下面这段枚举可见顶层窗口并输出进程、尺寸、位置:
Add-Type -AssemblyName System.Windows.Forms Add-Type -TypeDefinition @' using System; using System.Text; using System.Runtime.InteropServices; public class Win { [DllImport("user32.dll")] public static extern bool EnumWindows(Cb cb, IntPtr l); [DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr h); [DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr h, out uint pid); [DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr h, out R r); [StructLayout(LayoutKind.Sequential)] public struct R { public int L, T, Rr, B; } public delegate bool Cb(IntPtr h, IntPtr l); public static System.Collections.Generic.List<string> List() { var o = new System.Collections.Generic.List<string>(); EnumWindows((h, l) => { if (!IsWindowVisible(h)) return true; uint pid; GetWindowThreadProcessId(h, out pid); R r; GetWindowRect(h, out r); o.Add(pid + " " + (r.Rr - r.L) + "x" + (r.B - r.T) + " @" + r.L + "," + r.T); return true; }, IntPtr.Zero); return o; } } '@ [Win]::List()我这台机器上,桌宠那个进程的窗口是924x744 @1967,147。
这个数字能反推,而且推得很干净。插件给每只宠物开的窗口尺寸是"宠物包围盒 + 四周外扩半个身位":宠物宽size、高size × 9/16,外扩round(size × 0.5)。我的配置里size = 462:
宽 = 462 + 231×2 = 924 高 = round(259.9 + 21.7) + 462 = 744一字不差。窗口尺寸算得出来,就说明这只确实是桌面形态按配置生成的。
第三步,用位置对齐。两只宠物在屏幕上的纵坐标大致是:
- 桌面窗口内容区:
147 + 231 = 378起,到147 + 744 - 231 = 660止; - 网页 overlay:配置里
marginY: 100,落在视口顶部约100 ~ 360区间。
对照截图,下方那只(y ≈ 519)落在桌面窗口的内容区里,上方那只(y ≈ 250)落在网页 overlay 的位置上。下方 = 桌面宠物,上方 = 网页残留。
把这套流程抽出来,就是一句可复用的话:配置里只写了一只宠物,屏幕上出现两只时,不要怀疑配置写重了,先按进程和窗口矩形把两只归位。
第五步:卸载与清理
& $dsh plugin --profile desktop remove dsh-pet插件的用户数据不随卸载删除,需要的话自己清:
| 路径 | 作用 |
|---|---|
%USERPROFILE%\.dsh\dsh-pet\ | 用户配置main-config.jsonc、对话记忆、自定义动画和表情包 |
%USERPROFILE%\.dsh\electron\ | 桌面形态用的 Electron 运行时(体积最大) |
%APPDATA%\dsh-pet-electron-helper\ | 桌面窗口缓存与主屏缩放缓存,可删,会自动重建 |
三个都能删,也都不会再影响浏览器里的形态。
这次得到的两条经验
一、装带桌面形态的插件,先想清楚"住在哪"。display这个字段值四个,默认both,网页和桌面各画一只。装完第一件事就该核对它,而不是等看到两只再回头找原因。
二、配置文件和设置页是两条不同的生效路径。设置页保存会带动宿主重启窗口 + 前端重载;直接改配置文件只覆盖数据,不会触发任何重载。凡是手改配置,都要补一次刷新或保存。
文中结论的状态说明
- 已验证:
desktopprofile 的安装命令可正常执行并自动写入依赖与dsh.profile.bundles;桌面形态首次运行会自动下载 Electron 运行时到%USERPROFILE%\.dsh\electron\;桌面窗口尺寸与配置中的size按"包围盒 + 半身位外扩"公式严格吻合;display从both改为desktop后刷新页面,网页 overlay 消失且桌面宠物不受影响。 - 已验证(本机实测):通用
dsh命令不接管desktopprofile——这类 profile 由客户端独占管理,需使用随包 CLI。 - 未验证:其他 DSH 版本与插件版本的 profile 行为是否完全一致;插件长期稳定性;以上步骤在 macOS / Linux 下的等价路径(本文只覆盖 Windows;macOS 播放透明视频还需要改用
.mov素材)。 - 待长期观察:桌面形态与浏览器形态共用一份窗口缓存目录,同时开启时可能互相影响(插件自身只告警)。
关联:本文涉及的插件为公开包dsh-pet(npm),仓库见 GitHub - PC2005-cloud/dsh-pet: DSH 桌面宠物:一行命令装好即用的透明动画小桌宠,支持多开、大小位置随心配置;还内置 DIY 素材链,能用 AI 视频自造专属宠物 · GitHub。