dsh-bg
Local-only background plugin for DeepSeek Harness: visual framing, interface transparency, background carousel. Fork of beauticode-dsh.
- Stars
- 0
- Language
- JavaScript
- Created
- Oct 2, 2026
- Updated
- Oct 5, 2026
Introduction
dsh-bg
给 DeepSeek Harness 用的本地背景插件:图片 / 视频壁纸、画面裁切、界面透明度、背景轮播。
当前版本 5.6.8,已验证环境为 Windows 官方桌面端 DSH 0.2.0-rc.2 / Electron 44。 DSH 0.2.1-alpha.1 尚未完成兼容性验证。
不含任何出站网络请求 —— 这是它和上游 beauticode-dsh 最主要的区别。
A local-only background plugin for DeepSeek Harness. Image/video wallpapers, visual framing, interface transparency and a background carousel — with no outbound network requests at all. Fork of
beauticode-dsh(MIT); see NOTICE.
派生说明
本项目派生自 beauticode-dsh(MIT)。
原版权声明与 LICENSE 均已保留,具体改动列在 NOTICE。
themes/internal-beyond/ 下的内置壁纸来自上游,随上游一并分发。
特性
去联网
删除了上游的在线皮肤中心(gallery.js / gallery-host.mjs / skin-center.json
以及 /__beauticode/ui/gallery/* 路由),全插件没有任何出站请求。
画面裁切
上游用 object-fit: cover 居中裁切,你无法选择保留哪一部分。本插件改为把媒体元素
放大到 cover 应有的尺寸再做变换,因此可以自由平移与缩放,够得到 cover 本来会丢弃的区域。
- 抽屉式,默认收起 —— 编辑面不占面板第一层,避免误触
- 只用滑块缩放(滚轮的误触代价太大,已移除)
- 重置按钮在行上,一键回默认并持久化
- 画窗预设下也生效 —— 该预设有自己的一层背景,裁切会作用到实际显示的那一层
界面透明度
- 侧边栏跟随内容(默认开)—— 左栏与右栏共用同一层材质
- 工作时浓度(默认 80%,范围 15%–150%)—— 开始对话后界面变实,可调;新设置和重置均使用 80%,保留已有手动保存的数值
- 顶部浓度(默认 130%,范围 50%–150%)—— 独立滑块,100% 与正文一致;拖动实时生效并保存,可一键恢复默认
- 弹窗浓度固定 —— 对话框是前景面,不随浓度滑块缩放,否则背后的正文会透上来叠字
磨砂边缘缝合
- 默认开启,同时适用于普通图片、视频及画窗预设;5.6.1 修复了普通背景开关无效和两侧白边。
- 在媒体上方、界面下方模糊背景的可见区域,保留每张背景的裁切、平移及缩放,不通过额外放大图片隐藏边缘。
- 背景磨砂为 0 时不生成模糊层;关闭缝合时恢复普通模糊,边缘可能重新变淡。轮播中的每个媒体槽独立处理,不改变切换与视频播放逻辑。
窗口控制区透明
Windows 的原生窗口控制区(右上角最小化/最大化/关闭)底色来自网页探针。
本插件把探针色设为 rgba(255,255,255,0.01) —— Electron 44 兑现 alpha,
于是那三个按钮不再有白块,壁纸完整透出。
顶部标题栏与正文原先使用同一颜色,但标题栏叠两层、正文叠三层,因此最终透明度不同。 现在以正文当前材质为基准独立调整顶部:默认 130%,调到 100% 与正文一致。 滑块实时生效并保存,跟随明暗主题和空闲/工作状态;已有用户数值继续保留,重置按钮恢复 130%。
打开设置等模态窗口时,DSH 现有遮罩覆盖顶部,整窗同步变暗;关闭后恢复选定的顶部浓度。 原生窗口拖动和最小化、最大化、关闭按钮继续由桌面端负责。
控件与文件预览兼容
- 在原生会话标题栏内恢复被
dsh-claude全局样式隐藏的「更多操作」按钮。 菜单及 Session 日志下载沿用 DSH 原生功能;背景关闭时仍可使用。 - 淡出层隐藏规则只作用于侧边栏的空装饰层,避免画窗模式下误隐藏聊天/推理区容器及按钮。
- 文件悬浮预览使用明暗主题各自的实体底板,保留 DSH 原生定位和关闭淡出动画。
- 透明度和设置观察器忽略正文流式输出与预览浮层开关,减少重复布局测量。
会话滚动条自动隐藏
- 设置 → 背景 → 会话滚动条自动隐藏:滑块式开关,默认关闭。
- 滚动条隐藏等待:拖动滑块设置 1–30 秒,默认 2 秒,可重置;开关和时间立即生效并保存。
- 鼠标移动、滚轮、点击或按键时恢复显示,操作结束后重新计时;按住拖动期间保持显示。
- 隐藏和恢复均带 180 毫秒的短淡出/淡入,操作打断淡出时平滑转回显示;跟随系统减少动态效果偏好。
- 隐藏的是原生会话右侧滚动条的滑块颜色,保留宽度、滚动位置和原生拖动,正文不会因隐藏重新排版。
- 关闭开关立即恢复 DSH 原来的显示方式。设置面板、文件预览及代码块内部的滚动条保持原样,流式输出与程序自动滚动不重置闲置计时。
输入框磨砂
输入框是半透明的,滚动时聊天记录会和输入框文字叠在一起。本插件模糊输入框背后的内容 并加一层薄底,字不再打架;DSH 提问时的提问卡片同样覆盖。
背景轮播
- 分组:新建 / 改名 / 删除,一个分组就是一个轮播列表
- 背景项:从已保存主题和内置预设里挑选,可排序、可移出;失效项会划掉并标注
- 间隔:5 秒 – 24 小时
- 顺序 / 随机:随机模式不会连续抽到同一张
- 下一张:手动推进
- 启用即应用:打开开关立刻应用第一张,而不是空等一个间隔
- 每张背景各自记住自己的裁切 —— 轮播切换时带着各自的构图上场
其它
- 面板内「刷新页面」按钮 —— DSH 桌面版本身没有任何刷新入口
- 已移除上游的「自动全屏」(保留手动全屏)
安装与更新
需要 DSH 的 desktop profile。插件是源码形式安装的,不是拖进应用里。
先通过桌面端菜单的「管理 dsh 命令」安装 DSH 自带 CLI,再使用官方插件命令。 首次安装可指定解压目录的绝对路径:
dsh plugin --profile desktop add "file:C:/plugins/dsh-bg"
更新时推荐使用带版本号的独立 TGZ,避免相同本地目录依赖被包管理器缓存为旧内容:
dsh plugin --profile desktop add "file:C:/plugins/dsh-bg-5.6.8.tgz"
上述路径是示例,请替换为实际位置。开发者可在仓库中运行 npm pack 生成 TGZ。
通过官方插件管理器安装,不手工覆盖 profile 的 node_modules、包清单或补丁配置。
背景、已保存主题、轮播分组位于 %LOCALAPPDATA%\beautiCode,与插件代码分开保存。
装好后在 设置 → 背景 里使用。
注意
- 服务端代码(
index.mjs/ui-host.mjs/carousel.mjs/browser-injection.mjs) 只在 DSH 进程启动时加载一次 —— 改这些需要完全退出并重启 DSH。 - 客户端脚本(
client.js/console.js/framing.js/atmosphere.js) 每次请求重读,刷新页面即可。
使用版本号不同的 TGZ 更新时,运行中的宿主可能仍指向旧安装目录,需要完全退出并重启 DSH;只刷新页面不会切换这个目录。
一些实现上的取舍
这几处不是随手写的,值得记下来:
一个自定义属性挂在公共祖先上的代价
--dsw-specific-sidebar-fill 在 Windows 上有四个消费者(见
dsh-client-ui-layout/lib/client.js):窗口底衬 .BynINW_frame、40px 顶栏
.BynINW_frame:before、左侧栏 .BynINW_sidebarCol,以及侧栏模块根 ._2H3hWW_root。
自定义属性会向下继承并遵循层叠,所以把它声明在 body 上等于同时改这四处。
「侧边栏跟随内容」原本正是这么写的 —— 关掉时整个窗口底衬一起回落,
观感是"整个界面变实",而不是"侧边栏变了"。
现在别名在开时仍声明在 body(保持原有观感),在关时下移到侧边栏列,
值直接采用 beautiCode 自己的公式。声明在列上就只影响列子树,祖先 _frame 不受影响。
子串选择器只在"另一半唯一"时才安全
修上面那条问题时,有一版用 [class*="_frame"] 去选布局框架 —— 结果在聊天记录上蒙了一层白。
因为 _frame 不唯一:构建产物里有 11 个类名含它,分布在 8 个模块中,
其中 dsh-client-ui-chat 里也有。
同一批探针里 _sidebarCol 只有 1 个,可以这么用;_frame 不行。
判断标准是"另一半是否唯一",不是"我这次是否匹配上了"。
同一类错误在上游代码里也有一个
client.js 里带着一条:
html[data-bc-active="true"] [class*="_fade"]{display:none!important}
作者以为 _fade 是"淡入装饰"。构建产物里含它的类只有 5 个,而且分成两种东西:
_fadeTop / _fadeBottom 滚动边缘遮罩,由标记加在【滚动元素自己】身上
_fade 独立装饰层(会话列表底部那 24px 渐变)
ChatGroupSeat(可折叠的推理分组)把 fadeTop / fadeBottom 加在承载展开体的同一个 div 上 ——
于是 display:none 在滚动边缘变化时把整个推理块隐藏掉 ✓ 而推理流式输出期间这个标志一直在翻 ✓
表现为思考块不停地开合闪屏 ✓
当前修法将范围限定为侧边栏内的空装饰层:
[class*="_sidebarCol"] [class*="_fade"]:empty{display:none!important}
client.js 与画窗的 atmosphere.js 使用相同范围,保留包含内容的滚动容器和其他控件。
关于背景磨砂边缘那圈亮边
filter: blur() 会采样元素边界之外 ✓ 而背景图是满屏铺满的 ✓
于是最外圈像素混合到透明 ✓ 背后的页面底色透出来 ✓ 成为窗口四边细细一圈亮边 ✓
那圈颜色跟着主题变(亮色主题是白 ✓ 深色主题是黑 ✓)这一点本身就排除了"模糊自带颜色"的解释 ✓ 只可能是底色透出 ✓ —— 用户的两次观察正好互相证明了这个机制 ✓
修法是把模糊从壁纸本身移到其上方的背板:普通图片/视频在每个媒体槽内处理,
画窗在独立背景层处理。两种路径均要求背景磨砂大于 0 且启用边缘缝合。
背景磨砂为 0 时没有额外合成层;轮播中的媒体槽随原有切换事务一起淡入淡出。
背板使用 pointer-events:none,保持裁切、缩放、播放和界面交互。
被否掉的方案,记下来免得再走:
| 方案 | 结果 |
|---|---|
transform:scale(1.06) | 有效 ✓ 但会多裁 3% ✗ 用户不接受 ✓ |
| 负 inset / 超尺寸 | 和上一条等价 ✗ 换个写法不会更省 ✓ |
SVG edgeMode="duplicate" | 规范写明的正解 ✓ 但半径要 JS 同步 ✗ 作为备选 ✓ |
一条值得记的判断:我在实测前不敢承诺上层背板方案 ✗ —— 理论上背板会被视口裁剪 ✓ 但 Chromium 把背板当纹理模糊 ✓ 纹理自身也有边界 ✗ 实测结果是成立 ✓ —— 所以现在的默认值是实测出来的 ✓ 不是推理出来的 ✓
裁切为什么能跟着轮播走
轮播调用 actions.useTheme / actions.applyPreset —— 就是面板点击时调的同一批动作。
客户端按身份存构图(主题 theme:<id>、预设 atmo:<id>、裸图 src:<path>),
所以切换落地后,新背景带着自己的那份上来,不需要为轮播写任何裁切代码。
切换时的时序
背景切换时,媒体元素会被替换。缓存命中的图片立刻就能测量, 所以先绘制、后取身份的话,会用上一张的构图先画一帧 —— 大约三分之一秒后才纠正。
现在的顺序是:先取身份、装好 frame、再绘制。
开发
index.mjs 插件入口,注册路由与浏览器注入
ui-host.mjs UI 路由 + 轮播引擎接线 + 身份持久化
carousel.mjs 轮播引擎(分组、游标、跳过失效项、上限保护)
framing.js 注入到页面:画面裁切、界面透明度、轮播面板、若干运行时探测
console.js 注入到页面:背景设置页的整体框架
client.js / atmosphere.js / transport.js 上游的桥接与氛围层
vendor/ DSH 适配器与核心(媒体服务、应用事务、存储)
themes/ 内置壁纸
5.6.8 的会话滚动条行为检查(node tools/scrollbar-idle-test.cjs,需要 Playwright、pngjs 和 Microsoft Edge)共 33 项:默认关闭、闲置隐藏与操作恢复、实时等待时间、重置与刷新后保存、关闭后恢复原生样式、正文宽度与内部滚动条不变、流式输出不重置计时、原生滚动条按住和拖动,以及淡入淡出中间状态、打断后平滑恢复、减少动态效果和原生滚动条截图像素的逐渐变化。还通过包清单与 JavaScript 语法检查。
5.6.5 的验证结果:
| 检查 | 结果 |
|---|---|
| 顶部浓度、主题、工作状态、设置遮罩 | 53 项通过 |
| 图片/视频/画窗边缘、零磨砂、裁切及窗口尺寸 | 26 项通过 |
| 导出按钮兼容、淡出层范围、文件预览、观察器 | 17 项通过 |
| 官方 CLI 安装及包白名单/本地导入完整性 | 通过,48 个运行时文件齐全 |
| DSH 桌面端「更多操作 → 下载 Session 日志」及顶部重置为 130% | 已确认 |
前三项使用独立浏览器检查和 DSH 原生 CSS,合计 96 项。相同的流式输出/预览开关场景中, 界面几何测量由旧版 36 次降为 0 次,设置查询由 51 次降为 1 次;这不代表所有场景的帧率或抖动表现。 文件预览实体背景、定位和关闭淡出通过明暗主题检查,本轮尚未完成桌面端输出文件悬浮卡片的视觉核验。
测试分三层,缺一层都会漏东西:
| 层 | 覆盖什么 |
|---|---|
| 引擎单测 | 轮播引擎自身:持久化、悬空跳过、间隔钳制、随机不重复、省略字段不被重置 |
| 源码级身份链校验 | 裁切兼容所依赖的那串不变量:主题 id 的产生与透传、预设身份、持久化契约、客户端优先级 |
| 路由形状集成测试 | 接口实际返回什么(改动接口把状态嵌在 state 里、GET 是平铺的 —— 这一层就是为了抓这类不一致) |
打包白名单是个陷阱
package.json 的 files 决定 file: 依赖安装后哪些文件真的会到。
pnpm 按它过滤 —— 没列进去的文件不会出现在 node_modules\dsh-bg\ 里。
5.3.0 就栽在这上面:carousel.mjs 没进白名单 ✗,而 ui-host.mjs import 它 ✓,
于是全新安装启动即 ERR_MODULE_NOT_FOUND ✓
本地一直没发现,是因为改完是手工复制到 profile 的 ✓ —— 那条路绕过白名单 ✓ 校验永远通过 ✓ —— 要测的是"按包定义干净安装一次",而不是复制 ✗
改完白名单或增删源文件后跑一次:
node tools/verify-package.mjs
它展开 files 与真实目录比对 ✓ 并逐条检查每个相对导入的目标是否既在磁盘上、又被白名单覆盖 ✓
(后者才是真正会让启动崩掉的那一类 ✓)
卸载
dsh plugin --profile desktop remove dsh-bg
安全边界
与上游一致:控制端点仅接受同源请求;媒体 URL 只允许带令牌的回环地址。
本插件不含任何出站请求,唯一的 fetch 是向 127.0.0.1 自己的回环端口。
一个容易删错的同名物
包里还有个叫 gallery 的东西 —— builtin-gallery 内置主题(画窗),以及
effectsForPreset("gallery") 氛围预设。那是本地的、不联网,被保留了。
被移除的只有在线皮肤中心:gallery.js / gallery-host.mjs / skin-center.json,
以及 /__beauticode/ui/gallery/config|catalog|install 和 /__beauticode/gallery.js。
(上游的 skin-center.json 出厂就指向第三方站点,会拉取远程目录并下载安装皮肤。)