dsh-bg-atelier · 底图工坊 (DSH 标准插件)
为 DSH Desktop 提供可更换底图与输入框特效的标准 DSH 插件。
随 DSH 启动自动加载,重启后保留(设置持久化到 host 侧文件 $DSH_HOME\dsh-bg-atelier\settings.json)。
安装后即随启动加载:本版是打包进 DSH web profile 的标准插件,开机即生效、可在 设置 → 插件 里开启/关闭。
目录结构
dsh-bg-atelier/
├── index.js # Host 半端 (ESM): 分类型供图路由 + /bga/wallpapers.json 清单路由
├── client.js # Client 半端: 类型→图库两级浏览 / 随机换图 / 琉璃卡面 / 特效 / 设置页
├── cordis.patch.yml # bundle 挂载声明 (让 DSH 启动时挂载本插件)
├── package.json # npm 插件清单 (dsh.bundle.patch + dsh.client 客户端声明)
├── wallpapers/ # 内置底图目录 —— 每个子文件夹 = 一个"底图类型"(**原始文件**,PNG/JPG 原样,无损)
│ ├── 线稿风/ # 例如: 亚丝娜.png / 青缭.png / …
│ └── 重返未来1999/ # 例如: Vertin.png / 梁月.png / …
├── INSTALL.md # 安装 / 升级 / 卸载指南
└── README.md
安装(首次)
在 DSH Desktop 的任意会话里让 agent 执行(或直接在宿主 shell 运行):
dsh plugin --profile web add link:D:\DeepSeek\dsh-plugins\dsh-desktop-wallpaper
装完后重启一次 Harness/DSH Desktop,插件即随启动自动加载。
设置页顶栏会多出 底图工坊 入口;侧边栏底部出现「流光宝珠」换图按钮。
发布到仓库后(推荐)改为从仓库安装:
dsh plugin --profile web add https://github.com/Raylen-berry/dsh-desktop-wallpaper#main
放入/更换底图(按类型)
开箱即用:插件内置 4 个类型共 39 张底图(二次元 2 / 线稿风 5 / 重返未来1999 12 / 高清 20)。
clone 完先在插件目录跑一次 node tools/fetch-wallpapers.mjs(或设置页点「下载底图」)把图取回来,再重启 DSH;
设置 → 底图工坊 → 点类型卡进入图库即可选。图片不进 git(v1.5.5 起改走 Release 资产,见下面 A 节),
所以 clone 只有代码、很快;wallpapers/ 里是原始文件(无损,约 820 MB,口径见 A 节)。
加一个新类型/新图:把图片放进「放图目录」下一个子文件夹 = 一个类型,例如:
$wp = "D:\DeepSeek\dsh-plugins\dsh-desktop-wallpaper\wallpapers" # 或下面的放图目录
New-Item -ItemType Directory -Force "$wp\重返未来1999"
Copy-Item C:\some\*.png "$wp\重返未来1999\"
host 每次实时解析、按需刷新,设置页点「刷新」即出现新类型/新图(无需重启)。
放图目录解析顺序(首个有图的目录命中,① 最高):
$env:DSH_BG_ATELIER_WALLPAPERS 指向的绝对目录(显式覆盖)
$DSH_HOME\dsh-bg-atelier\wallpapers(推荐,插件启动时自动建好,随时往里丢图)
- 插件包内置
wallpapers/(随包分发,当作默认图集)
- 工作区相对路径
dsh-plugins/dsh-desktop-wallpaper/wallpapers(旧动态版兼容)
根目录若有散图(旧版遗留)会自动归入「未分类」类型;旧版根目录 URL 仍可访问(host 会自动按文件名在类型里找回),升级不丢当前底图。
高清细分
文件名去掉扩展名后,尾部带这些标记的会自动打上「高清」并可在类型图库内用 全部 / 高清 / 普通 筛选:
高清、_高清、·高清、-高清、(高清)、_4K、·4K、_HD、(HD)、_UHD 等(英文标记需要前置分隔符,避免误伤正常英文名)。
显示名 = 去掉标记后的名字(如 亚丝娜_高清.png 显示为 亚丝娜 + 高清 角标),不会把编号写进显示名。
功能
- 两级底图浏览:设置 → 底图工坊 → 一级页列出底图类型(子文件夹),点类型卡进入二级页图库,
缩略图即点即换;每张带
№编号 角标(按文件名自然序在类型内编号,仅用于指认,不改显示名)。
图库主图走 640px 派生图(v1.4.0 起,见下),类型卡迷你图走 112px 派生图;原图只在点击选中后作为底图加载;
派生图与清单加载中均显示转圈加载动画;网格做了 React.memo 隔离,图片多时拖动滑杆也不卡。
- 高清细分:类型内若有高清/普清混合,提供
全部 / 高清 / 普通 筛选条。
- 随机换图(流光宝珠):跨所有类型的全部图随机,采用轮次式 2/3 不重复洗牌 ——
每轮随机抽取
ceil(2/3 × 总数) 张排成序列逐张播放,同轮内绝不重复(至少播完约 2/3 后才可能出现重复);
一轮放完"放回"、再从总体随机取 2/3 开新一轮;新增/删除图片后自动重洗。
- 图片适应:cover 裁剪 + 九宫格焦点 + 绕焦点缩放(1–2.2×)。
- 配色:10 套预设(樱粉/青碧/琥珀/星紫/薄荷/绛红/雾蓝/薰衣草/蜜桃/墨黑)+ 主色/深色自定义。
- 对话框琉璃卡面:半透明 +
backdrop-filter 背景模糊,英雄页/会话页同时生效。
- 对话框特效(输入框上方那条 dock,v1.5.0 起 8 种 + 关闭):流萤(18 只萤 + 14 颗星 + 2 道流星)/
气泡(16 个 7–22px 大气泡 + 底部水面辉光)/ 光带扫过 / 星轨环绕 / 浮尘光斑 /
落樱 / 墨韵涟漪 / 雨丝(v1.5.0 新增六个,见下)。极光已于 v1.4.2 按用户要求删除
(盘上存过
aurora 的由 normalizeEffect 自动归回流萤,不需要重设)。
全部纯 CSS 动画(无 JS 定时器),粒子位置按序号确定性生成;不碰消息气泡
(气泡样式归 dsh-cache-control,两边同时改会互相盖)。
- 特效画布不得产生横向滚动溢出(v1.4.2 的硬约束):
.bga-dockfx-in 必须是 overflow:clip,
粒子的漂移行程也必须留在画布内。原因见「版本 · v1.4.2」——飘出画布右缘的萤点会把
[data-conversation-scroll] 的 scrollWidth 顶大,会话区底部那条横向滚动条就会一直频闪。
- 滑杆:暗纱 / 透光 / 卡面不透明 / 卡面模糊 / 缩放。
- 设置持久化到 host 侧文件
$DSH_HOME\dsh-bg-atelier\settings.json,重启 DSH 后保留上次选择。
v1.3.0 起不再提供「对话页固定宽度」:那一节(开关 + 640–3840px 滑杆 + 常用宽度快捷键)
已整体移到 dsh-cache-control 的设置页 「会话策略」→ ④ 对话页。钉的是同一组
--dsh-chat-content-width / --dsh-composer-card-max-width / --dsh-chat-user-width
变量,两边同时开只会互相覆盖,所以本插件这边彻底删净。数值不用重设:cache-control 的
host 半在启动时发现自家 settings.json 缺 chatWidth / chatWidthEnabled,就一次性从
$DSH_HOME\dsh-bg-atelier\settings.json 搬过去并写盘。本插件此后不再碰这些字段。
发布前检查(CI 与本地同一条命令)
push / PR 都会跑 .github/workflows/ci.yml,它只做一件事:npm test。本地跑的就是同一条命令,
不装任何依赖、不联网、不做任何真实下载:
npm test # = node tools/run-all.mjs
node tools/run-all.mjs --list # 只看清单:跑哪些、以及哪些被排除、为什么
tools/run-all.mjs 把每套都跑完再汇总,任一套非 0 退出 ⇒ npm test 退出码 1 ⇒ CI 变红。
CI 用 Node 20/22/24 三档矩阵、windows-latest。
本机实测(Node 24.9.0)参与门禁的两套:
| 套件 | 本机结果 |
|---|
tools/test-download-robustness.mjs | 42 项通过(自带假 HTTP 服务,零请求出网) |
tools/verify-dockfx-bounds.mjs | 全部 PASS |
未纳入 CI 的步骤(原因同时写在 tools/run-all.mjs 的 EXCLUDED 里):
tools/fetch-wallpapers.mjs --check(要本机已备好 39 张 820MB 底图,图片不进 git ⇒ CI 上必然失败;
而且这个脚本本身就会真实下载,绝不能进 CI)、
tools/test-served-bytes.mjs(要 wallpapers/ 里的真实图片才能起供图路由断言,离线实测退出码 1)。
换台机器:可迁移性与必须手动的步骤
给后续在任何一台机器上接手的人或 agent:装本插件不需要任何手工点击(不像
dsh-browser-live 要装浏览器扩展),但下面几条必须先看清楚。
(起因:用户 2026-09-12 反馈"工作电脑上传、回家发现可用性很差、必须手动操作"。)
A. 克隆体积(v1.5.5 起:图不进 git,改为 Release 资产 + 按需下载)
39 张底图合计约 820 MB,长期放在 git 里会让每次克隆都变成几百 MB(用户换机时"装个插件要拉几百兆"就是这么来的)。
仓库只留 wallpapers.manifest.json(每张图的路径 + 字节数 + sha256),图片作为 Release 资产发布(tag wallpapers-v1):
git clone https://github.com/Raylen-berry/dsh-desktop-wallpaper.git # 只有代码,很快
cd dsh-desktop-wallpaper && node tools/fetch-wallpapers.mjs # 从 Release 取回 39 张(可重入/断点续传)
node tools/fetch-wallpapers.mjs --check # 只校验本机现有图(不联网)
画质口径:完全无损 —— 不缩放、不重编码。 Release 资产就是原始文件(PNG / JPG 原样),逐张 sha256 与清单一致。
下载量确实大(820 MB),这是有意换来的:底图是长期资产,宁可下载慢,也不要在存档上留一次有损编码。
显示这一侧现在也不打折(v1.6.1 起):host 供图不设尺寸上限,把原图字节直接送给浏览器 ——
5120 / 7680 长边的超宽屏、8K 屏全都吃满,不会再被 3840 拉成放大模糊(此前 SERVED_MAX_DIM = 3840
会先在宿主里缩到 3840 再送)。代价是浏览器要解码整张 30–44 MP 的图:单张解码后约 120–175 MB 内存、
切图首帧多等几百毫秒 —— 换的是大屏上的清晰度;而且送原图这条路上 sharp 完全不参与,
省掉了一次「解码 + 重编码」的 CPU 与首字节等待。派生图(图库 640 / 当前底图 320 / 类型卡 112 px)
另走一套,不受影响。真要一份轻量版:node tools/compress-wallpapers.mjs 可另生成一套 3840 宽 WebP q92
到 wallpapers_light/(仅供自用,不参与发版)。
回归测试(不用重启 DSH):node tools/test-served-bytes.mjs —— 用假 ctx 调 apply() 把路由处理器抓出来
直接请求,断言原图那条路逐字节等于磁盘文件、?sz=thumb|preview|poster 仍在缩放、404 与路径穿越防护仍在。
设置页里同一个入口是「下载底图」按钮(显示进度、逐张校验 sha256;已存在且校验通过的跳过 ⇒ 断网了再点一次即可)。
下载的健壮性参数(fetch-wallpapers.js 的 fetchWallpapers(opts),host 路由与命令行共用这一份)
timeoutMs:单张的时间预算,默认 300000(5 分钟)。超时按"这一张失败"处理并走既有退避重试,
不会让任务永远停在"下载中"。覆盖方式:fetchWallpapers({ timeoutMs: 60000 })。
默认值的依据:清单里最大一张是 63283616 B(60.4MB),5 分钟意味着 ~1.7 Mbps 的慢线也能下完,
而正常家用宽带(20 Mbps 以上)一张只要几秒 —— 既不误杀慢线,也不让真卡死的连接赖着不走。
retries:单张重试次数(不含首次尝试),默认 2,必须是非负整数(非法值回落 2)。
retries: 0 表示只试一次,真的不重试。
signal:外部 AbortSignal。触发后立即停止且不再重试,错误消息会写明"已取消"。
- 下载是流式的:边收边写
.part、边累计 sha256,字节数一超过清单值就立刻中止 ——
不会把整张图先读进内存(最大 60.4MB)再校验。校验通过才 rename 到最终路径。
这些行为的离线断言:node tools/test-download-robustness.mjs(不联网,把 fetch 打桩、
写入临时目录,覆盖超时/取消/字节数不符/哈希不符/正常落盘,共 42 条断言)。
加图/改图必须用追加式清单生成器:tools/make-wallpaper-manifest.mjs 是从零重编号的,
一改名或加图就会让既有 wNN 与 Release 里已上传的资产整体错位(贝利尔.png→贝利尔2.png 就撞过
w08/w16),make-release 会因为"同名资产字节数不符"把已传的删掉重传。发版一律走
node tools/make-manifest-append.mjs(默认拿 HEAD 那份清单作基准,按 sha256 把已发布的图绑回原资产名,
只给新图续编 w20、w21…),再 node tools/make-release.mjs 补传差额。
迁移顺序(重要):先把图作为 Release 资产发布并验证下载可用,再把 wallpapers/ 从 git 移除;
反了的话,新克隆在 Release 就绪前一张图都拿不到。图片从 git 移除之前,git sparse-checkout add wallpapers
仍可按需从 git 取图(离线机器适用)。
B. 装(agent 可全自动)
dsh plugin --profile web add link:<你放插件的绝对路径>
link: 挂载 ⇒ 改完即生效。装完必须重启 DSH Desktop:client 半在服务启动时 compose
(与 dsh-cache-control 同理,刷新页面无效)。重启会掐断正在跑的会话轮次 ⇒ 让用户自己挑时间。
C. 底图在哪 / 设置在哪
- 底图在插件目录自己的
wallpapers/<类型>/ 下(原始文件、不进 git ⇒ 换机后先按 A 节从 Release 取回);
往里加图后派生图(640px 图库图 / 112px 类型卡迷你图)会自动生成。
- 设置(当前底图 / 特效 / 配色 / 卡面不透明度与模糊 / 卡面阴影开关)在
$DSH_HOME/dsh-bg-atelier/settings.json,不在仓库里 ⇒ 换机器后是默认值:
特效回到「流萤」、卡面阴影为开、底图要重新选一张 —— 这是最常见的"装好了但看着没变"。
- 特效与底图已解耦(v1.5.1 起):没有底图也照样画特效。若在老版本上遇到
"点了清除底图 ⇒ 特效全无",那是 v1.5.0 及以前的旧 bug,升级即可。
D. 已知的宿主坑:插件会被 generation 迁移搬走(本机踩过)
部分 DSH Desktop 版本启动时做 installGeneration 迁移,会把 link: 挂载的插件重新 stage,
期间把一个绝对路径当相对路径拼接 ⇒ ENOENT、迁移被 defer,插件也可能不加载。
本机的处置是给应用 bundle 打本地补丁(把本插件加进 KEEP_IN_SHARED_TREE)——该补丁不在本仓库里,
它属于"每台机器各自的 DSH 应用目录"。识别方法:启动日志出现 migration deferred / could not stage,
或 profiles/web/.generations-deferred.json 反复出现。DSH 每次升级都会覆盖该补丁,升级后要重跑。
F. 设置导出/导入(换机器一键搬配置,v1.5.4 新增)
node tools/settings.mjs export --out D:\bga-settings.json # 旧机器
node tools/settings.mjs import D:\bga-settings.json --yes # 新机器(覆盖前自动备份 settings.json.bak-*)
show 看当前值;不带 --yes 是演练模式(只打印将要写入什么)。导入只做表层校验 + 区间钳制
(本插件 host 是原样存取、不 sanitize,所以钳制在这儿做严);底图结构不合法时置空并提示,
底图图片不在导出文件里(脚本会检查本机有没有这张图,缺了就提示去 git sparse-checkout add wallpapers)。
导入后仍需重启。
E. 换机后自查(30 秒)
node tools/extract-real-css.mjs # 期望 PASS(含数量随屏宽 / 阴影独立开关 / DockFx 真渲染三条)
node tools/verify-dockfx-bounds.mjs # 期望 PASS
设置页 →「底图工坊」→「对话框」应看到:卡面不透明、卡面模糊、卡面阴影勾选框、
四个特效(流萤 / 气泡 / 落樱 / 雨丝)+ 关闭。
卸载
dsh plugin --profile web remove dsh-bg-atelier
或在 设置 → 插件 里停用/移除该插件后重启 Harness。
技术要点 (debug 备忘)
- Client 用
window.__ModuleLoader__.load({ id, factory }) 包裹,exports.apply/exports.inject。
- 底图清单走
/bga/wallpapers.json HTTP 路由(返回 { total, categories:[{name,count,hd,items}] }),不再用运行时-only 的 host.call 桥。
- 供图 URL:
/bga/wallpapers/<编码类型名>/<编码文件名>(旧格式 /bga/wallpapers/<文件名> 仍兼容,自动按文件名在类型里找回)。
调试可直接访问:/bga/wallpapers/%E7%BA%BF%E7%A8%BF%E9%A3%8E/%E4%BA%9A%E4%B8%9D%E5%A8%9C.png(含路径穿越防护,../多余斜杠返回 400)。
- 样式自包含注入(
data-bg-atelier-styles),不依赖 styles 全局。
webServer prefix 路由匹配规则 pathname === prefix || startsWith(prefix + '/'),注册路径不带尾斜杠。
- Host 日志
[dsh-bg-atelier](harness 控制台),Client 日志在浏览器 DevTools。
版本