dsh-endfield-theme
《明日方舟:终末地》主题:炭黑与警戒黄的工业 HUD、在外壳渲染前注入的开屏动画、对话区背景,以及重新配色的 Windows 标题栏。侧栏品牌会改为「终末地工业 / ENDFIELD INDUSTRIES」,可用 URL 参数还原。
Install
dsh plugin --profile web add dsh-endfield-theme
Sandbox test results
| Stage | Result |
|---|---|
| 1 install | ✓ |
| 2 list | ✓ |
| 3 mount | ✓ |
| 4 boot | — |
Four stages run in an isolated sandbox: install (network on) → list → mount → boot (last three with no network).
README
终末地主题 for DeepSeek Harness


《明日方舟:终末地》风格的 DSH 客户端主题:炭黑 + 警戒黄工业 HUD、开机开屏动画、可选的对话区底图,
以及把账户余额画成"电力储备"的侧栏读数。设置项统一收在自己的一个分页里。
npm 包名 dsh-endfield-theme 仓库 github.com/Longxiangjunlin/dsh-endfield-theme
它做了什么
- 开屏动画 —— 每次加载播一次,约 2.9 秒。网格与磁带先亮,徽记以横向擦除展开,BOOT 进度条
与百分比读数同步推进,右下角打出 FACTORY OS 与站名,然后整屏收起。开屏音效有两种可选:
游戏里的系统播报,或一声 1.5 秒的「Arknights」。
- 对话区底图 —— 内置四档(炭灰原画 / 雪原 / 等高线 / 纯色),也可以放自己的图片。
- 侧栏电力储备 —— 把账户余额画成电量计,带状态词(
NOMINAL/OUTPUT LOW/DEPLETED)、
今日消耗、以及"还能撑几天"的折算。点标题可以折成一行(¥6.06),再点展开,折叠状态会记住;
它停靠在侧栏左下,并会为工作区列表让出底部空间——列表变长时是自己滚动,而不是被面板压住。
- 余额语音播报 —— 低于阈值或耗尽时播一次,四种播报版本可选。
- 对话轮次索引 —— 对话左侧的轮次号,从
data-chat-turn读,没有脚本遍历正文。 - 屏幕细纹 —— 静态扫描线,不占每帧预算。
- 设置页 —— 设置对话框里多一个「终末地主题」分页,17 项,含配置导出/导入与一键清除。
- 品牌位 —— 侧栏左上换成终末地 lockup(可关)。
这是一个 host-only 插件:cordis.patch.yml 里没有 dsh.client facet,因此注册不了官方设置页,
主题自己扩展设置对话框的 DOM;也正因为如此,它不需要数据库、不写入 profile,
除"读取自带底图/清除数据"外不碰磁盘。
安装
从 npm(推荐) —— 走 registry,不需要 Git,国内可直连(npmmirror 已同步):
dsh plugin --profile desktop add dsh-endfield-theme
从 GitHub —— 等价,但需要能连上 github.com:
dsh plugin --profile desktop add github:Longxiangjunlin/dsh-endfield-theme
本地改代码用 link 装,之后改 client/ 下的文件刷新页面即生效:
dsh plugin --profile desktop add link:<本仓库绝对路径>
若提示找不到 dsh:它装在 DSH 安装目录下的 resources\runtime\cli\bin\dsh.cmd,不在 PATH 里;
或者直接用客户端界面「设置 → 插件 → 添加插件」按包名安装。
升级
客户端插件不支持自动更新。两条路:
1. 市场更新:设置 → 插件 → 已安装 → 更新。它会自己从 npm 的 latest 取版本、按具名版本安装,
并带上 --config.minimum-release-age=0,所以能绕过 pnpm 的 24 小时新包冷静期。
2. 先卸再装:按包名重装会受冷静期影响——发布不足 24 小时的版本 pnpm 不会选,它会退回上一个
通过冷静期的版本。
卸载
- 界面:设置 → 终末地主题 →「清除数据」→ 确认。它会把自己从 profile 的
package.json
(依赖与 bundles)和 cordis.patch.yml 里删掉,清空本地数据,并撤掉页面上的元素;重启客户端后
彻底消失。写配置前会把两个文件备份成 *.ef-uninstall-backup。
- 命令行:
dsh plugin --profile desktop remove dsh-endfield-theme。
屏幕
开屏 · 浅色
 · 
品牌位(深色) · 品牌位(浅色)
 · 
设置页
18 项设置,存在 localStorage,改完即时生效。开屏 / 品牌位 / 开屏音效三项例外,
要重启客户端才生效(桌面端没有刷新按钮,所以说明里直接写"重启")。
分组 · 项 · 默认 · 说明
开屏 · 开屏动画 · 开 · 每次加载播一次,约 2.9 s
· 开屏音效 · 开 · 与开屏同步,缺失或不允许播放时静默
· 开屏音效版本 · 经典 · 经典:系统播报;Arknights:短语音
外观 · 替换侧栏品牌位 · 开 · 换成终末地 lockup
· 屏幕细纹 · 开 · 静态扫描线
· 细纹强度 · 22% · 0 = 关闭
· 轮次索引 · 开 · 对话左侧的轮次号
· 对话区背景 · 开 · 有对话内容时显示底图
· 对话区底图 · 原画 · 四档内置 + 自备图片
电力储备 · 侧栏电力储备 · 开 · 关掉则不轮询,播报随之停
· 余额刷新间隔 · 60 s · 最小 15 s
· 余额语音播报 · 开 ·
· 播报版本 · 完整 · 完整 / 简短 / 提示音 / 自定义
· 播报时长 · 2.5 s · 仅「自定义」生效,0.5–10 s
· 播报音量 · 90% · 拖动即试听
· 预警阈值 · ¥5 · 低于它播报一次
· 余额可用天数 · 0(关) · 按最近一周消耗速度折算
· 电量计满量程 · 4× · 阈值 × 这个倍数 = 满格
强关联的项收成一组,起决定作用的当主项,从属项跟着缩进:
开屏动画 ─┬─ 开屏音效 ─── 开屏音效版本
替换侧栏品牌位
屏幕细纹 ─── 细纹强度
轮次索引
对话区背景 ─── 对话区底图
侧栏电力储备 ─┬─ 余额刷新间隔
├─ 余额语音播报 ─┬─ 播报版本 ─── 播报时长
│ └─ 播报音量
├─ 预警阈值
├─ 余额可用天数
└─ 电量计满量程
这些是真依赖,不是视觉分组:开屏关掉就没有音效可听;电力储备关掉就不轮询,播报一起停;
阈值为 0 时"满量程 = 阈值 × N"没有意义。判据是主项的值是否为真,唯一的例外是「播报时长」——
它只对"自定义"这一种播报版本有意义,属于等值判断,单独一张表处理。
主项关掉时从属项变灰并且真的 disabled(不是 pointer-events 障眼法,键盘也不该够到一个
没有作用的控件)。变灰而不隐藏,来回切主项时页面不会在手底下跳。
底部还有:试听警告音(两个按钮,走和真实预警同一条播放路径)、配置(重置 / 导出 / 导入;
导入只接受本版本认识的键且类型一致)、清除数据(自卸载)。
面板为什么需要"让位"。 侧栏里唯一能滚的是工作区列表,而电力储备是 position: fixed 的浮层,
停靠左下时会压在列表下沿——列表再长也只是继续往下长,最后几行永远被盖住。所以停靠时把面板高度
再加上一点间距,作为那个列表的 margin-bottom:列表的盒子因此在面板上沿结束,内容超了就是它
自己滚动(实测塞入溢出内容后 scrollHeight 834 > clientHeight 330,滚到底最低一行停在面板上方
30px)。用 margin 而不是 padding 是量出来的——padding 不会移动这个元素的底边(它的 scrollHeight
始终等于 clientHeight),margin 才会真的把弹性盒子的下沿提起来。
折叠是同一件事的另一半:展开时面板 116px,折起来 48px,让出的空间随之从 134px 回到 66px。
折叠不移动面板,只是把"仪表 + 状态词 + 今日消耗"换成标题行里的一个数字——浮层放到窗口中间会显得
没归位,收起来则不会。标题条既是点击目标也是键盘目标(role="button" + Enter/Space),因为折起来
之后它是唯一还看得见的东西。
余额读数
数据来自 DSH 自己的 deepseekAccount.getBalance,经宿主的一条路由转发;插件不碰凭据,
也不假定投影的形状。实际载荷是:
{ "ok": true, "balance": { "status": "ready",
"value": [ { "currency": "CNY", "balance": "17.22" } ],
"bonusWallets": [ { "currency": "CNY", "balance": "0" } ] } }
可消费的是 value(一个裸数组),bonusWallets 从不计入——赠金与充值余额是两回事,
算进去会高估可用余额。
电量计:满量程 = 预警阈值 × 倍数(默认 4),预警点因此固定落在四分之一处。
今日变化:账号服务只报一个余额,"今天花了多少"只能自己记基线再相减。跨自然日、
或余额上升(充值)时重置基线,所以刷新不会把测量从错误的数字重新开始。花钱显示 −,
充值显示 +。
余额可用天数:用最近一周的轮询采样算消耗速度,剩余不足设定天数时也触发预警。它与金额阈值
并列,不替代——¥5 对不同消耗速度的人意义不同。速率取窗口两端的差值,而不是逐对平均:
期间充值会让逐对平均失真,而两端相减在充值时直接得到负消耗,于是报"无法估算"而不是"余额很多"。
告警唯一不能答错的方向就是这一边。
两条触发共用同一个边沿:低余额只响一次,回升后重置,下次再跌再响;填 0 关掉天数触发。
播报版本四种:
版本 · 长度 · 做法
完整语音 · 约 9 s · 原样播放
简短 · 约 2.4 s · 同一份素材在播放时截断,末尾 0.3 s 淡出
提示音 · 0.5 s · WebAudio 合成的两声,不占体积
自定义 · 0.5–10 s · 自己填秒数
浅色模式
浅色不是"反色",而是把外壳调色板换成雪原一套(炭黑文字、#6B6A00 一类的强调色),并
把主题自己画的每个部分都重新量过对比度——那些颜色原本是照着炭黑侧栏写的。实测
(rgb(244,246,240) 为底,WCAG 正文要求 4.5):
元素 · 曾经 · 现在
面板标题「电力储备」 · 1.04 · 16.64
余额数字(警戒黄) · 1.12 · 5.23
POWER RESERVE / NOMINAL / 今日 · 1.96 · 5.94
设置页副标题 · 1.22 · 5.49
数值读数 22% / 90% / 4x · 1.22 · 5.69
试听 / 重置 / 导出 等按钮 · 1.22 · 均高于 5.49
输入框占位符 · 2.75 · 4.79
设置页 51 个带文字的节点现在全部达标,最差 5.49。
两处不是"加一条覆盖"能解决的:占位符用的是外壳的 --dsw-alias-label-caption,主题把它设成
了偏浅的灰;开关的填充在 ::after 上,只改外框会留下一块纯黄方块。输入光标同样是警戒黄——
一条 1px 竖线对对比度的要求本来就比正文高,浅色下改为墨黑,深色下提到更亮的黄。
几处非显然的做法
开屏音效有两份,而不是把旧的换掉。 两份音效都随包发:游戏里的系统播报约 9 秒(开屏只有 2.8 秒,
所以它总是被淡出截断),另一份是 1.5 秒的短语音,长度正好落在开屏之内。选择写在 开屏音效版本 里,
受 开屏音效 管;两份都是鹰角的素材,授权同样记在 NOTICE.md。
开屏走 index 注入行,而不是脚本事后搭。 原版没有独立开屏环节,要成为首帧就必须在外壳渲染
之前进 DOM,所以 webserver/index-inject 里加的是一条内联脚本(内联行不可能加载失败;桌
…(truncated)
Rendered from the README published on npm. Formatted only, not edited.
Versions
Published on npm::14,最后更新 2026-11-01