DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

crossoverthere /

crossoverthere/dsh-whale-desktop

Topic repository only

桌面级鲸鱼娘桌宠:基于dsh-whale-widget(DSH 插件版),做成透明置顶、可点击穿透的 Windows 桌面挂件(Electron)

★ 1 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@92aef015

鲸鱼娘桌宠 · dsh-whale-desktop

把 dsh-whale-musume 的桌宠本体 从 DSH 网页里解放出来,做成 Windows 桌面上的独立挂件:

  • 透明:只有鲸鱼娘本身可见,没有窗口边框、没有底色
  • 置顶:浮在其它窗口之上
  • 点击穿透:指针不在她身上时,鼠标事件全部穿过,不挡你操作桌面
  • 零改动复用上游:立绘、动作、互动、养成、成就、小游戏、齿轮设置面板全部原样可用
  • DSH 工作状态联动:思考中 / 工作中 / 完成 / 出错 会切换对应立绘与状态签, 而且按工具类型换姿势(跑 shell → 摸鱼打电话,写文件 → 开会,搜索 → 灵光一现)
  • 花费播报:每个任务结束时她说一句「这次任务花了 X tokens,约 ¥Y」 (读 DSH 会话里的真实用量,按官方峰谷价估算;低于阈值不打扰)
  • 余额查询:气泡里点一下就念一整句 —— 「当前余额为 CNY 41.11,状态为充裕,今日共计消耗 35.2 万 tokens,消费 0.53 元」
  • 打开 DSH:右键菜单里一条「打开DSH」—— 已经在跑就只开浏览器,没跑才把它拉起来(绝不重启,免得把你正在聊的会话带走)
  • 托盘常驻:显示/隐藏、置顶、跟随 DSH 开关、开机自启、缩放、重置位置、退出

这不是 DSH 插件,而是一个独立的 Electron 应用。它不依赖 DSH 运行, 也不需要浏览器标签页。DSH 那边的插件可以照常装着,两者互不干扰。 工作状态联动是只读 DSH 的会话文件(~/.dsh/sessions/**),不往 DSH 里装任何东西。

要接手维护它?先读 docs/HANDOVER.md:现状一页纸、铁律、 自检矩阵、环境事实、踩过的坑、常见维护动作(加菜单项 / 加设置项 / 发版 / 重启实例) 都在那里。**"为什么是这样设计"**看 docs/DEVELOPMENT.md; **"整体长什么样"**看 docs/ARCHITECTURE-DIAGRAM.md (带排版版本:ARCHITECTURE-DIAGRAM.html)。


快速开始

npm install          # 若 Electron 二进制没下来,见下方"常见问题"
npm start            # 启动桌宠
npm run dev          # 带 DevTools

启动后:

操作 效果
左键点她 脸红 / 爱心
快速连点三次 星星眼庆祝 + 粒子特效
按住拖动 切换「被拎起来」立绘,松手后位置自动记住
右键她 互动 + 唤起 DSH:投喂 / 戳一下 / 夸夸 / 两个小游戏 / 回到原位 / 打开DSH
齿轮 ⚙ 头顶浮层:天气 / 余额 两行状态 + 并排的两个入口「日常养成」「余额查询」;点任一项浮层自动收起(点天气或余额那行去设置)
托盘右键 显示桌宠 / 总是置顶 / 跟随 DSH 工作状态 / 开机自启 / 大小 / 重置到默认位置 / 重新加载页面 / 打开数据目录 / 打开日志 / 设置… / 退出
托盘 →「设置…」 独立设置窗口,二级菜单:左侧 6 个分类(看板娘 · 窗口与状态 · 大小与位置 · 维护 · 天气与余额 · 花费播报),点哪一类右侧就显示哪一屏
齿轮 ⚙ →「日常养成」 养成窗口,五个标签页;称号与成就也在这里(不再占用托盘菜单)
Alt+Shift+W 显示 / 隐藏
托盘图标单击 显示 / 隐藏

分工原则:和她互动的事在桌宠右键菜单;高频全局动作与快速开关在托盘; 需要解释的配置项集中在设置窗口(按分类分屏);养成/图鉴统一走气泡里的 「日常养成」入口 —— 托盘只留"随手就要用"的东西,避免菜单越堆越长。 托盘与设置窗口共用同一份配置,任何一处改动都会同步(所有 setter 末尾都会刷新托盘菜单)。

养成 / 图鉴

入口:气泡齿轮 ⚙ →「日常养成」(托盘菜单里不再单独放称号/成就 —— 它们本来就是 养成的一部分,单独占两行只会让托盘越堆越长)。窗口内五个标签页:

标签页 内容
今日任务 每天 3 个任务(签到 / 摸头 / 跑工具…),达成后一键领取,奖励好感与心情
本周签到 一周七格签到表 + 连续签到天数 + 1/3/7 天里程碑
称号 按等级解锁的称号,可佩戴(会显示在头顶浮层里)
成长日记 她记录下来的成长事件(本地 80 条,不上传)
成就 39 个成就;已解锁高亮显示名称与描述,未解锁灰显并隐藏名称

实现上直接复用上游的纯状态机(vendor/whale/whale-moe-core.js 是 UMD, 浏览器里挂 window.DshWhaleMoeCore、Node 里可 require)—— 成就表、称号表、任务池、等级与签到规则全用它,没有重写任何养成算法。

存档(养成数据、成就、位置、偏好)全部在浏览器 localStorage 里, 键名前缀 whale-moe:,存放于 Electron 的 userData 目录,关掉应用不会丢。


花费播报

每个任务结束后她用一句台词报账:这次任务花了 154.7 万 tokens,约 ¥0.0518。

环节 实现
用量从哪来 DSH 会话文件里每条 assistant/message 的 usage,按 turn/start…turn/end 累加
怎么算钱 src/price-table.js 按当前模型从本地价目表取价 → src/pricing.js 乘用量
什么时候说 一个 turn 正常结束且金额 ≥ 阈值(默认 0.01 元)
怎么开口 上游没有对外的"说话"接口,壳在返回桌宠脚本时注入一段 __dshWhaleMoeSay(vendor/whale/ 磁盘文件不改)
在哪配置 设置窗口 →「花费播报」:开关、阈值、当前模型与价格(只读)、手动配置、测试播报

两个必须记住的计费细节(都踩过):

  • 缓存命中与未命中分开计价,价差约 50 倍;混算会高估几十倍
  • reasoningTokens 是 outputTokens 的子集,不能再加一遍

已知边界(都写进注释了):不处理中国法定节假日(那几天官方按空闲计价,我们会高估一倍); 统计范围是"一个 turn 内的全部记录",子代理(subagent)消耗的 token 也计入。

单价从哪来:本地价目表,只人工刷新

价格写在仓库根目录的 pricing.json 里 —— 桌宠运行时只读这个文件,不联网。 DeepSeek 没有价格 API,官网只有一张给人看的网页表格;爬页面一旦结构变动就会静默失效, 而计费算错是最难发现的一类 bug(她照样说话,只是数字不对)。所以刷新是人工动作:

npm run refresh:prices            # 抓官方页面 → 解析 → 打印 diff → 写文件(不自动提交)
npm run refresh:prices -- --dry-run        # 只看结果不写文件
npm run refresh:prices -- --from-file x.html   # 用已保存的页面(没网时)

脚本解析不出完整表格时会报错退出且不写文件(宁可手动改,也不写半张表)。

价目表按模型分档,并带旧模型名别名:

模型 缓存命中(空闲/高峰) 缓存未命中 输出
deepseek-flash 0.02 / 0.04 1 / 2 4 / 8
deepseek-v4-pro 0.15 / 0.30 4.5 / 9 13.5 / 27

(元 / 百万 tokens;高峰 = 北京时间周一至周五 9:00-12:00、14:00-18:00)

当前用哪个模型是自动识别的:会话记录 request/header 里的 data.header.config.model(没读到就用 ~/.dsh/settings.yaml 的默认模型)。 所以中途换模型不用重启,设置窗口里那行"当前模型 + 价格"每 2 秒自动跟上。

设置窗口 →「花费播报」分两块:

  • 当前模型 / 价格(只读):显示模型名、它在价目表里的空闲与高峰价、当前用哪一档
  • 手动配置(可改):只有当模型不在价目表里时才参与计算(高峰统一 ×2)

余额查询(气泡里那个按钮)

点一下,她把三件事念成一句话:

当前余额为 CNY 41.11,状态为充裕,今日共计消耗 35.2 万 tokens,消费 0.53 元

  • 余额与状态:页面里上游自己按 60 秒刷新的余额(状态词沿用上游那套 已见底 / 告急 / 偏紧 / 正常 / 充裕 / 很充裕)
  • 今日共计消耗:主进程的今日账本(src/usage-today.js)—— 按 turn 记账、落盘、跨天归零,重启不丢,启动时还会从会话文件回放当天已有的 turn 来补账(所以中途才把她叫起来也不会少算)。按 会话#turn 去重,回放不会算两遍; 早于今天的 turn 直接丢弃,不会把账本弄脏。
  • 没启用余额 / 接口没响应时不会念一串"不可用",而是换成一句提示(去设置里开、看看接口)
  • 播报类台词打完字后停留 5 秒;看完不用等,任意点击立刻收起

已知边界:账本只统计当前正在跟的那个会话(换会话前的账在旧会话文件里,不会合计); 不处理中国法定节假日(与花费播报同一限制)。

打开 DSH(右键菜单里那条)

右键她 →「打开DSH」,她会看一眼 DSH 在不在,然后:

情况 她做什么 她说什么
已经在跑 只在默认浏览器里打开它(不重启、不再起一个服务) 「DSH 已经开着啦,这就给你打开~」
没在跑 把它拉起来(见下),等到监听就绪再打开浏览器 「DSH 起来了,浏览器已经给你打开~」
起不来 提前退出就立刻报错,否则等到超时(默认 60 秒),不假装成功 「DSH 没起来…你看看运行日志吧」

没在跑时用什么把它拉起来(按顺序挑第一个能用的):

优先级 用什么 说明
1 你配置的命令/脚本 设置窗口 →「维护」→「DSH WebUI」那一行可以填
2 工作空间里的启动脚本 程序目录或它的上级目录里的 Start-DSH-Web-Background.bat / .cmd;地址被改成非默认端口时跳过(脚本不接受 --port)
3 dsh CLI 直接 node <dsh>/lib/bin.js web --no-open --port <端口>;找不到 bin.js 就退回 cmd /c dsh.cmd,再不行 npx @deepseek-ai/dsh

设置窗口那一行会写清这次走的是哪条路(鼠标悬停有完整命令行与日志路径)。

几个刻意的取舍:

  • 绝不重启正在跑的 DSH。它每次启动都会换一个浏览器信任 token,重启等于把你 正在聊的会话连同页面一起打断;所以"在跑"就只开浏览器,真正的启停由你决定。
  • 401 也算"在跑":DSH 的 browser-trust 会拒绝没有 cookie 的请求, 而那恰恰证明服务在监听(你的浏览器里本来就有那个 cookie)。把它当"没在跑" 就会平白多起一个服务,那更糟。
  • 进程交给 shell 起,不挂在自己身上:桌宠是 GUI 进程,直接 spawn 出来的 子进程会跟它共用一个隐藏控制台 —— 那个控制台随时可能被一个 Ctrl+C / 关闭事件 顺手清掉,服务就死得没头没脑(0.12.0 就是这么栽的,日志里只有一个 ^C)。 现在走 Start-Process -WindowStyle Hidden,服务有自己独立的隐藏窗口,桌宠退出也不牵连它。
  • 拉起前摘掉 DSH_SHELL / DSH_SESSION_ID / DSH_WEB_URL:它们描述的是 "我正处在哪个会话里",对一个新起的服务是错的(谁把桌宠从 DSH 会话里启动的,这些标记 就会一路继承下去)。DSH_HOME 保留。
  • 浏览器由我们开(--no-open),因为这样才能把"起没起来"回报给她; 自己拉起来的这一次还能从输出里拿到带 token 的地址并优先用它打开 (旧 token 一律 401,只有本次启动的 token 有效)。
  • 地址写在配置里(默认 http://127.0.0.1:3080),设置窗口 → 维护 →「DSH WebUI」 里可以直接改,也能从那儿点「打开」。
  • 拉起过程的输出(含 token 地址)落在数据目录的 dsh-web.log;用启动脚本那条路时, 日志在脚本旁边的同名文件(例如 D:\DSWorkspace\dsh-web.log)。

项目结构

dsh-whale-desktop/
├─ src/
│  ├─ main.js              主进程:窗口/托盘/IPC/自检探针
│  ├─ preload.js           contextBridge 通道(页面拿不到 node)
│  ├─ server.js            本地静态服务器:/assets/* → vendor/whale/*(+ 说话钩子补丁)
│  ├─ dsh-state.js         读 DSH 会话文件 → 工作状态 + turn 用量 + 当前模型
│  ├─ pricing.js           怎么乘(纯函数)
│  ├─ price-table.js       单价从哪来(读 pricing.json,按模型 + 峰谷取价)
│  ├─ usage-today.js       今日消耗账本(落盘、跨天归零、按 会话#turn 去重)
│  ├─ open-dsh.js          打开 DSH:探活 / token 地址 / 启动计划 / 交接起进程(纯 node)
│  ├─ balance-proxy.js     内置余额代理(自动读 DSH 里的 DeepSeek Key)
│  └─ pet/
│     ├─ index.html        只负责按顺序引入上游三个文件
│     ├─ shell.css         透明画布 + 选中/滚动等宿主适配
│     ├─ shell.js          点击穿透判定 + 头顶浮层 + 日志回传
│     ├─ settings.*        独立设置窗口
│     └─ growth.*          养成 / 图鉴窗口
├─ pricing.json            本地价目表(运行时只读,人工刷新)
├─ vendor/whale/           上游桌宠素材(MIT,见 THIRD-PARTY.md)
├─ scripts/
│  ├─ sync-upstream.mjs    从上游 tag 同步素材
│  ├─ check-vendor.mjs     素材完整性校验
│  ├─ verify-shell.ps1     点击穿透自动化验证(真鼠标)
│  ├─ open-dsh-e2e.ps1     「打开DSH」端到端(真鼠标点菜单项,隔离 DSH_HOME)
│  ├─ cold-start-check.mjs 真机冷启动 dsh web(独立 DSH_HOME + 空闲端口)
│  ├─ check-desktop-input.ps1  这套环境能不能驱动鼠标(真鼠标类脚本的前置检查)
│  ├─ test-cost.mjs        计价 / 账本 / 价目表单测(npm run test:cost)
│  ├─ refresh-prices.mjs   刷新价目表(npm run refresh:prices)
│  └─ push.ps1             走代理推送
└─ docs/
   ├─ HANDOVER.md          **接手维护先看这份**(现状/铁律/自检/环境/坑)
   ├─ DEVELOPMENT.md       架构细节与调试方法
   ├─ ARCHITECTURE-DIAGRAM.md    一页看完的**项目框架图**(拓扑/数据流/职责/自检)
   └─ ARCHITECTURE-DIAGRAM.html  同一份框架图的**带排版版本**(浏览器打开)

上游素材一个字都没改——这是刻意的,见 docs/DEVELOPMENT.md 里"为什么不需要改上游"。


常见问题

npm install 之后启动报找不到 Electron npm 11+ 默认拦截依赖的生命周期脚本,Electron 的二进制就下不来。手动补一次:

node node_modules/electron/install.js

鲸鱼娘没出现 / 位置跑到屏幕外 托盘右键 →「重置到默认位置」。

天气不显示 设置面板里城市留空时完全不联网;填了城市会请求 api.open-meteo.com。

余额那里报错 默认走内置余额代理(127.0.0.1:3020,随桌宠启停,Key 自动从 ~/.dsh/.credentials.yaml 的 DEEPSEEK_API_KEY 读取),不需要手工配置。 只有在你把余额接口改成别的地址、而那个服务又没起来时才会报错。 设置窗口 →「天气与余额」→「测试」会直接把接口返回摊开给你看。

点了「打开DSH」没反应 / 浏览器里是 401 先看服务自己的日志(其中 token 地址就在里面):

  • 没在跑时需要它自己起来:日志在数据目录的 dsh-web.log(用工作空间启动脚本那条路时, 日志在脚本旁边的同名文件,例如 D:\DSWorkspace\dsh-web.log), 失败原因也会写进 shell.log(托盘 →「打开日志」)。设置窗口 →「维护」→「DSH WebUI」 那一行会告诉你走的是哪条路,想手工试就跑一遍同样的命令。
  • 已经在跑却打不开:多半是浏览器丢了 DSH 的信任 cookie(有效期 30 天)。 让 DSH 重启一次会打印带 token 的地址,用它打开一次就能重新拿到 cookie; 桌宠不会替你重启(那会打断你正在聊的会话)。

真鼠标的验证脚本(verify:shell / e2e:open-dsh)不是"过时不过" 它们会真的移动系统指针;锁屏 / RDP 断开 / 快速用户切换时 SetCursorPos 会被拒, 表现就是"鼠标移不到她身上"。先跑前置检查看退出码:

powershell -NoProfile -ExecutionPolicy Bypass -File scripts/check-desktop-input.ps1

退出码 0 = 能驱动(可以跑那两条),3 = 环境不可驱动(只跑无头探针,并在结论里写明)。 无头探针(test:cost / open-dsh:probe / settings:probe / menu:probe 等)不受影响。

git push 报 Recv failure: Connection was reset 这台机器上 GitHub 的 API 与 codeload 都直连正常,但 git 的 push 端点 (git-receive-pack)会被重置。用仓库自带的推送助手:

powershell -NoProfile -ExecutionPolicy Bypass -File scripts/push.ps1   # 检测到本机 Clash 代理就自动走代理

它只对本次命令生效,不写进 git 全局配置。也可以手动:

git -c http.proxy=http://127.0.0.1:7890 push origin main

路线图

  • 多显示器支持(当前铺满主显示器工作区)
  • electron-builder 打包成免安装 exe
  • 全局快捷键自定义
  • 单实例之外的"每屏一只"
  • 花费播报:节假日按空闲价、按会话区分主/子代理用量

许可

  • 本项目代码:MIT
  • 上游桌宠素材:MIT,版权归 Sutera-Diffusus 及其贡献者,详见 THIRD-PARTY.md 与 vendor/whale/LICENSE
—/ 5

No ratings yet

Manifest verification required

Commit 92aef015d6de

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout