dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
DeepSeek Harness (DSH) 二次元壁纸主题插件 · 随机动漫壁纸 / 四档铺满 / 边缘羽化 / 深浅色自适应 / 可拖动控制坞 · theme plugin for DeepSeek Harness
给 DeepSeek Harness(Web 版 / DSH Desktop)换上一张随机二次元壁纸的主题插件。
点一下「换一张」,它就从图源取一张随机图铺成整屏背景,并处理好几件容易做丑的事: 整图完整可见(不裁掉大半张脸)、留白处用同一张图的模糊放大版晕染(不出现白块黑块)、 正片边缘羽化(消除硬缝)、深浅色主题自动适配。右下角常驻一个可拖动的控制坞,随时换图、切分类、调暗幕。
不需要 Pixiv 账号,不需要 Token,不预爬不缓存 —— 每次触发按需直取。
图源是 cnmiw.com(MirlKoi API)的公开随机图接口。本插件只转发它返回的图片地址, 不下载、不留存、不修改图片。
| 功能 | 说明 |
|---|---|
| 🖼️ 随机壁纸 | 每次「换一张」直连 API 取随机图,不预爬不缓存 |
| 🖼️ 四档铺满方式 | 自适应(默认):整图可见 + 留白用同图模糊放大版晕染;cover 铺满居中裁切;cover-top 铺满但裁切焦点偏上(保住人脸);plain 整图 + 纯色留白 |
| 🪶 留白不泛白 | 铺底幕色刻意比正片轻(浅色 0.135 / 深色 0.5)并 saturate(1.45),让两侧是「这张图自己的颜色晕出去」而不是一层白膜 |
| ✂️ 边缘羽化 | 按图的实际渲染矩形算一条 mask-image 渐隐带(羽化宽度 = 图宽 ×0.22,24~160px),把正片与铺底之间的硬缝抹平 |
| 🎯 裁切焦点可调 | 「铺满·偏上」档的裁切位置 0~100% 可调(0 = 顶部,100 = 底部),默认 25% 保住人脸 |
| 🌓 幕色跟随主题 | auto 模式下深色主题用暗幕、浅色主题用亮幕,避免浅色模式被压黑 |
| 🌚 深色模式加深 | 深色 UI 配亮壁纸时浅色字压不住,所以深色主题额外多压一档(dim + 0.2,可关),浅色主题原样不动 |
| 🌗 暗幕 / 亮幕 | 壁纸上加可调遮罩,保证任何图下文字都可读 |
| 🎚️ 实时调节 | 暗幕强度、留白虚化实时可调,拖动即预览 |
| 🎛️ 悬浮控制坞 | 右下角常驻胶囊:换一张 / 开关 / 设置。按住可拖动,位置持久化;空闲时半透明(0.45),鼠标靠近或展开设置恢复实底,不挡状态栏读数 |
| ⚡ 预加载后上屏 | 先等图片真正解码出来(读到自然尺寸)再换背景,不会先闪一下空背景 |
| 🔁 失败自动重试 | 三段都有兜底:上游取地址失败退避重试 3 次;拿到地址却取不到图(403 防盗链 / 404 / 超时)也会自动换下一张;全过程保留上一张壁纸不清空 |
| ⏱️ 自动换图 | 可设每 5 / 15 / 30 / 60 分钟自动换一张,改完立即重新排程 |
| ♻️ 关闭即还原 | 关掉插件写空样式表 + 移除自建层,一条规则都不残留,原背景完整回归 |
| 🧩 自建壁纸层 | 挂在 <html> 之下(不是 body 里),避开「祖先 transform 抢走 position:fixed 包含块」导致图被放大的坑;同时把内置主题插件 dsh-skin 那层 display:none 让位 |
| 🛡️ 零 CORS 依赖 | 宿主半体代理上游 API,浏览器端不再受 api.cnmiw.com 缺 CORS 头影响 |
| 🖼️ 防盗链中继 | 直连链路(sinaimg.cn)自动补 Referer: https://weibo.com/,并做域名白名单 |
图源相关(可选能力,默认关闭)
| 功能 | 说明 |
|---|---|
| 🔞 色图 / 无色图 | 两套独立分类:无色图走 sortSfw、色图走 sortNsfw,下拉框跟随模式切换,不会串台。默认是无色图,色图需手动开启 |
| 🚧 混池拦截 | random / CDNrandom 是上游的「全站随机」,会混进色图;无色图模式下宿主会降级到 CDNcat 并在控制坞如实提示,绝不照单全收 |
| 🔁 近期去重 | 宿主侧记住最近 50 张,命中自动换一张 |
装了 DeepSeek Harness 桌面端的话,把下面这行粘进浏览器地址栏,客户端会被唤起并弹出安装确认:
dsh://plugin/install?id=dsh-anime-theme&name=%E4%BA%8C%E6%AC%A1%E5%85%83%E5%A3%81%E7%BA%B8%E4%B8%BB%E9%A2%98&version=1.5.3&repo=zxr2115-1/dsh-anime-theme&permissions=%E7%BD%91%E7%BB%9C%E8%AE%BF%E9%97%AE%2C%E6%9C%AC%E5%9C%B0%E5%AD%98%E5%82%A8%2C%E5%AE%A2%E6%88%B7%E7%AB%AF%E6%A0%B7%E5%BC%8F%E6%B3%A8%E5%85%A5
⚠ GitHub 会过滤
dsh:这类自定义协议的链接,所以在 README 里做的按钮点不动 —— 上面是给你复制的。 想要能点的按钮,本地打开test-uri-install.html(自带按钮、参数预览和排查清单)。 插件市场 deepseek.stream 的详情页不受此限制,那里的「⚡ 一键安装」可以直接点。
协议格式(dsh:// 由桌面端注册,各参数需 encodeURIComponent):
dsh://plugin/install?id={id}&name={name}&version={version}&repo={repo}&permissions={permissions}&downloadUrl={downloadUrl}
前端拉起用隐藏 iframe 即可,不跳转、也不会被弹窗拦截:
const params = new URLSearchParams({ id, name, version, repo, permissions });
const iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.src = 'dsh://plugin/install?' + params.toString();
document.body.appendChild(iframe);
setTimeout(() => document.body.removeChild(iframe), 2000);
Windows 上如果协议没注册,跑一次 install.ps1 即可(它会兜底写入
HKCU\Software\Classes\dsh)。验证:
reg query "HKCU\Software\Classes\dsh\shell\open\command"
install.ps1 → 使用 PowerShell 运行
(被系统拦截时双击 install.bat,或在 PowerShell 里执行 .\install.ps1)# 1) 复制到插件目录
robocopy . "$env:USERPROFILE\.dsh\plugins\dsh-anime-theme" /E /XD .git
# 2) 在 profile package.json 的 dependencies 里加一行
# "dsh-anime-theme": "file:../../plugins/dsh-anime-theme"
# 注意:第三方插件不要写进 dsh.profile.bundles,否则会重复加载报错
# 3) 在 profile cordis.patch.yml 追加挂载项
# - insert:
# - id: dsh-anime-theme
# name: 'dsh-anime-theme'
# 4) 建软链并安装依赖
cd "$env:USERPROFILE\.dsh\profiles\web"
cmd /c mklink /J "node_modules\dsh-anime-theme" "$env:USERPROFILE\.dsh\plugins\dsh-anime-theme"
pnpm install
# 5) 自检
node "$env:USERPROFILE\.dsh\plugins\dsh-anime-theme\scripts\check-install.js"
| 平台 | profile 目录 | 说明 |
|---|---|---|
| Windows | install.bat / install.ps1 | 用 NTFS 目录联接(mklink /J)建软链,普通用户即可,无需 UAC |
| macOS / Linux | install.sh | 原生 ln -sfn 软链;.sh 与 .ps1 逻辑等价 |
两个脚本都会遍历 web / default / desktop 中所有已初始化的 profile 并逐一接线,
支持 DSH_PROFILE=desktop 指定单个 profile,也支持 DSH_ROOT 覆盖根目录。二者都是幂等的。
profile 目录名:Web 版是 web,DSH Desktop 是 desktop(部分版本为 default)。
重启后界面右下角出现控制坞:
● 二次元壁纸 [换一张] [开] [设置]
路径:~/.dsh/anime-theme/config.json(首次加载自动生成,参考 config.example.json)
{
"enabled": true,
"allowNsfw": false,
"sortSfw": "CDNcat",
"sortNsfw": "CDNsetu",
"autoRefreshMinutes": 0,
"recentLimit": 50,
"recent": [],
"dim": 0.3,
"fillBlur": 40,
"fit": "smart",
"veil": "auto",
"darkBoost": true,
"coverFocus": 25,
"isolatePanels": false,
"dockX": 18,
"dockY": 76,
"schemaVersion": 2
}
| 字段 | 默认 | 说明 |
|---|---|---|
enabled | true | 总开关 |
allowNsfw | false | 是否允许色图分类。关闭时宿主会二次拦截,客户端传参无效 |
sortSfw | CDNcat | 无色图分类。仅限于无色图池,可选 CDNcat cat CDNtop CDNpc CDNmp CDNiw233 iw233 daimao |
sortNsfw | CDNsetu | 色图分类。可选 CDNsetu setu Capoo yuexinmiao Denia CDNrandom random |
autoRefreshMinutes | 0 | 自动换图间隔(分钟),0 = 关闭。控制坞里有 关 / 5 / 15 / 30 / 60 的下拉 |
recentLimit | 50 | 近期去重缓冲长度 |
dim | 0.3 | 暗幕强度 |
fillBlur | 40 | 留白处模糊铺底的虚化半径 px,范围 16~80。下限 16 是硬约束:再低铺底就退化成一张清晰的放大副本,自适应等于没生效 |
schemaVersion | 2 | 配置结构版本。展示层设置改语义时 +1,老版本配置里语义已变的键会被丢弃并回到新默认值 |
fit | smart | smart = 自适应(整图 + 模糊铺底)/ cover = 铺满居中裁切 / cover-top = 铺满、裁切焦点偏上 / plain = 整图 + 纯色留白 |
veil | auto | 暗幕色调:auto 跟随软件明暗主题自动切换 / dark 强制暗幕 / light 强制亮幕 |
coverFocus | 25 | 「铺满·偏上」的裁切焦点,0~100(0 = 顶部,100 = 底部) |
darkBoost | true | 深色主题额外压暗一档(正片 dim + 0.2、铺底跟随),浅色主题不受影响。关闭后深浅两档同值 |
dockX / dockY | 18 / 76 | 控制坞相对右下角的偏移 px。默认 bottom 76 是为了让开底部的状态栏读数;拖动后自动写回,越界会被钳进视口 |
isolatePanels | false | 仅兜底模式生效。是否让本插件自行接管面板透明 token。装了 dsh-skin 时留 false,面板透明度用「设置 → 主题」里的滑杆 |
宿主半体把路由挂在 /dsh-anime-theme 前缀下(同源,无需鉴权):
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /dsh-anime-theme/api/health | 健康检查 |
| GET | /dsh-anime-theme/api/config | 读取配置 |
| POST | /dsh-anime-theme/api/config | 合并写入配置(只接受白名单键) |
| GET | /dsh-anime-theme/api/random?num=10&nsfw=0 | 取随机图地址(服务端去重) |
| GET | /dsh-anime-theme/api/img?u=<urlencoded> | 图片中继(域名白名单 + Referer 补齐) |
按 MirlKoi 上游分类页自己的归组切分(全部实测可用):
| 池 | 上游归类 | sort |
|---|---|---|
| 🟢 无色图 | 无色图 | iw233 / CDNiw233、top / CDNtop、pc / CDNpc、mp / CDNmp |
| 🟢 无色图 | 呆猫八条 | daimao、cat / CDNcat、yin、xing |
| 🔴 色图 | 色图 | setu / CDNsetu、Capoo、yuexinmiao、Denia |
| ⚠️ 混池 | 区块级「全站随机」 | random / CDNrandom —— 跨两个池,只出现在色图模式的下拉框里 |
CDN* 前缀走 setu.iw233.top,免 Referer 更稳;无前缀走 sinaimg.cn,需 Referer: https://weibo.com/(插件自动补)。
GET https://api.cnmiw.com/api.php?sort=<KEY>&type=json&num=<N>{"pic": ["url1", "url2", ...]},num 上限 100,无需鉴权CDNsetu / CDNcat):返回 setu.iw233.top 图片,免 Referersetu / cat):返回 sinaimg.cn 图片,需 Referer: https://weibo.com/(本插件已自动补齐)top(精选)、pc(横屏)、mp(竖屏)、random / iw233(全部),均有 CDN* 变体
api.cnmiw.com不返回Access-Control-Allow-Origin,所以浏览器端不能直连。 本插件的宿主半体代劳这一步,这也是必须装宿主半体而不能只做纯前端注入的原因。
遵循 DSH 官方插件工程规范(与 deepseek.stream/guide 白皮书一致):
index.js(宿主,ESM,声明 name / inject / apply)+ client.js(客户端 bundle)package.json 必须同时有
dsh.client.platform = "web" 与 exports["./client"],
否则 dsh-client-modules 会以 MissingClientBundleError 拒绝加载cordis.patch.yml + dsh.bundle.patchctx.inject(["webServer"], ...),没有 webServer(如 Electron file:// 宿主)时静默降级,不阻塞插件加载require("react")(静态表提供),不依赖任何 DSH 客户端包,避免模块图解析问题UTF8Encoding($false) 写,避免 JSON.parse 报 Unexpected tokenmklink /J(目录联接)普通用户即可创建,改代码即时生效;.ps1 本身以 UTF-8 带 BOM 保存,避免 GBK 代码页下中文乱码scripts/check-install.js 真实模拟模块解析并校验软链、dsh.bundle、exports["./client"]<html> 之下)先说清一个背景:deepseek-ai-cordis(市场里的「deep桌面主题插件」)自己就有一层全屏背景:
<div class="dt-bg dt-fade" data-dsh-theme-bg="true"> ← position:fixed; inset:0; z-index:-1
<div class="dt-bg-gradient"> ← 内置主题渐变
<div class="dt-bg-media"><img src="..."></div> ← 图片 / 视频皮肤
<div class="dt-bg-mask"></div> ← 暗幕
</div>
本插件的做法是:把它整层 display:none 让位,自己另起一层。
position: fixed 的包含块会被祖先抢走 —— 只要任一祖先带 transform / filter /
will-change / contain / zoom,fixed 就退化成 absolute,inset: 0 于是按那个祖先的盒子算。
盒子比视口大时,cover 会再放大一轮、contain 也会溢出被裁,表现就是「图铺得比屏幕大、显示不全」。
历史上 v1.0 / v1.1 两版都走过弯路:先是和 dsh-skin 抢同一个 z-index:-1 被压在后面完全看不见,
改成挂在 body 里又撞上包含块被抢。现在的做法绕开了这两个坑。
document.documentElement
├── #dsh-anime-theme-bg ← 本插件自建,<html> 的第一个子节点
└── <body>
└── <div class="dt-bg" data-dsh-theme-bg="true"> ← 被 display:none 让位
<html> 之下:上面只剩 <html>,没有祖先能抢走包含块;window.innerWidth/innerHeight 写进 CSS 变量 --dsh-anime-theme-w/h,
resize 时重算 —— 不再依赖「包含块 + inset:0」推算出来的盒子;<html>:自建层是 body 的兄弟节点,用不了
body[data-ds-dark-theme] 后代选择器,所以由 JS 把 data-dsh-anime-dark 同步到 <html> 上供 CSS 取用。/* ① 元素自身:兜底色 + 显式视口尺寸 */
#dsh-anime-theme-bg {
position: fixed; top: 0; left: 0;
width: var(--dsh-anime-theme-w, 100vw);
height: var(--dsh-anime-theme-h, 100vh);
z-index: -1; pointer-events: none; overflow: hidden;
background-color: #f4f6fa; /* 深色主题下为 #0b0d12 */
}
/* ② ::before 同一张图 cover + 大半径虚化 —— 留白处的颜色晕染 */
#dsh-anime-theme-bg::before {
content: ""; position: absolute; inset: 0;
background-image: linear-gradient(铺底幕色, 铺底幕色), url(图);
background-size: cover, cover;
filter: blur(40px) saturate(1.45);
transform: scale(1.15);
}
/* ③ ::after 幕色渐变铺满 + 正片;smart/plain 用 contain 保整图,cover 系直接铺满 */
#dsh-anime-theme-bg::after {
content: ""; position: absolute; inset: 0;
background-image: linear-gradient(幕色, 幕色), url(图);
background-size: cover, contain; /* 逐层尺寸:幕色永远铺满,图保整张 */
background-position: center, center;
background-repeat: no-repeat, no-repeat;
mask-image: linear-gradient(to right, transparent …, #000 …, #000 …, transparent …);
}
::before / ::after 都是绝对定位、按 DOM 顺序层叠,
且被本层自己的 overflow: hidden 裁掉,不会溢出到应用界面之上。
--dsw-alias-bg-* token —— 面板透明度归 dsh-skin 的 themeAlpha / dialogAlpha
管(它走 ctx.theme.overrideTokens),再用 !important 去压会直接踩坏它的透明度滑杆;
只有完全没装 dsh-skin 时才由本插件接管(isolatePanels 开关);buildCss() 返回空串 + 自建层移除,写在 dsh-skin 那层上的 display:none 一并消失,
原背景完整回归,一条规则都不残留;::after 挂一条按图的实际渲染矩形算出来的 mask-image 渐隐带,
抹平正片与模糊铺底之间的硬缝(羽化宽度 = 图宽 ×0.22,钳在 24~160px);shell.overlay(kind: "list",可加性席位),
注册失败时回退 conversation.input.dock。副作用:本插件开启期间,宿主主题自己的壁纸设置(内置主题 / 图片 / 视频)会被让位, 点控制坞的「开 / 关」即恢复。面板透明度始终归「设置 → 主题」里的滑杆。
dsh-skin / 0.1.6 liquid-glass)DSH 0.1.6 把内置主题引擎换成了 @deepseek-ai/dsh-client-ui-liquid-glass 2.x,
壁纸层的做法完全变了 —— 旧版认的 [data-dsh-theme-bg="true"] 在新版里根本不存在:
| 0.1.5 及以前 | 0.1.6 起 | |
|---|---|---|
| 根标记 | —— | <html data-dsh-liquid-glass="true"> |
| 壁纸容器 | <div data-dsh-theme-bg="true"> | <div data-dsh-glass-ambient> |
| 壁纸本体 | 容器里的 <img> | <canvas data-dsh-glass-canvas>(WebGL 着色器现画) |
| 纹理来源 | 就是那个 <img> | <div data-dsh-glass-video-holder> 里一个 1×1、opacity:.001 的隐藏 <video> |
最后一行是关键:那个 <video> 肉眼完全看不见,却带着 autoplay loop muted 一直在解码,
画布再把它的帧当纹理渲染成壁纸。所以两套壁纸会叠在一起,而且白烧一路解码器和一份显存
—— 这是「用了动态壁纸后桌面端很卡」的主要来源之一。
2026-10-03 实测补充(0.1.6-max):官方在
0.1.6-re2-community这个构建里 把dsh-client-ui-liquid-glass整个移除了 ——runtime/node_modules/@deepseek-ai下 315 个包、各包的lib/client.js产物里,没有任何一个再提到glass。 顶替它的是@deepseek-ai/dsh-client-ui-theme0.1.6-alpha.2,只负责明暗三态、字号与settings.theme那一行,不再有任何壁纸层。好消息是上面这套接管逻辑不需要改:引擎不在时
themeKind()返回"none"、glassActive()恒为false,所有相关路径都以它为闸门自动早退 ——wireGlassWatch()第 501 行直接return(不建 observer、不排定时器),killGlassShader()/pauseGlassVideo()同理,设置里那行「宿主玻璃层」也不渲染。 实测诊断日志自证:themeKind:"none"、glassActive:false、appliedAfterStyle里壁纸正常铺满、bodyHasDarkAttr:true。
顺带一提:GSH 那个社区版标题写的就是「无壁纸插件版」—— 0.1.6-max 起官方确实不带壁纸了, 本插件从「与宿主主题共存」变成了唯一的背景来源。 检测到
liquid-glass在场时,本插件会:
[data-dsh-glass-ambient](画布 + 隐藏视频容器),让你的壁纸成为唯一背景;<video> 一并 pause(),停掉解码;--dsw-alias-bg-* 这组 token —— liquid-glass 自己用
ctx.theme.overrideTokens 把它们设成 transparent 来造玻璃感,再拿 !important
盖上不透明底色会把它整层压平、两套风格打架。所以 isolatePanels 在它面前自动让路。面板的磨砂、描边与折射 CSS 全部保留,只是背景换成了你的图。
控制坞「设置」里因此多了一行 宿主玻璃层:接管 / 不接管:
liquid-glass 自己画的壁纸。会和本插件叠在一起,
且那个隐藏视频继续解码,GPU 占用明显更高。想进一步降 GPU,可以到「设置 → 主题 → 液态玻璃」把它自己的折射 / 色散 / 涟漪等 特效参数调低或关掉 —— 那部分开销归它管,本插件不越权修改别人的设置。
node scripts/package.mjs # → dist/dsh-anime-theme-<version>.zip
打包前会跑一轮硬校验(必备文件、plugin.json 规范字段、platform 取值、两处版本号一致),
不过就拒绝打包。产物是「一个顶层同名文件夹 + 21 个文件」,服务端会自动穿透单层包裹定位 plugin.json。
scripts/probe-css.mjs 用一个极简 DOM/React 桩把 client.js 跑起来,抓出它生成的样式表并核对,
不需要启动整个 Harness:
node scripts/probe-css.mjs # 用默认配置
node scripts/probe-css.mjs client.js '{"fit":"cover-top"}'
输出形如:
{
"afterBackgroundSize": "cover,contain !important",
"afterBackgroundPosition": "center,center !important",
"afterMask": true,
"beforeDisplay": "block ",
"beforeFillAlpha": "0.135",
"beforeFilter": "blur(40px) saturate(1.45) !important",
"darkFillAlpha": "0.5"
}
四档 fit 各自的 background-size / background-position / 是否有羽化 / 铺底是否关闭,
以及深浅两档的幕色、控制坞下拉框的选项数与配色,都可以在这一步核对完再上机。
改样式时先跑它,能省掉大量重启。
它还会真的把控制坞渲染一次、模拟拖动与点击、点开「设置」,然后逐项核对。 两条断言都是被真实 bug 逼出来的:
① 下拉框选项隐形(<select> 字色写成 color:"inherit"):
<select> 的字色原先写成 color: "inherit",而 Chromium 在 Windows 上的下拉弹层用 UA 默认浅色底,
<option> 继承了深色 UI 的浅色字 → 白底白字,整个列表全隐形。现在 <select> 显式给
color-scheme,<option> 再各自写死底色与字色,双保险。
② 按钮点不动(拖动时在容器上 setPointerCapture,pointerup 被重定向,
浏览器取「按下 / 松开」目标的共同祖先作为 click 目标,于是 click 落到容器、
按钮的 onClick 永远收不到)。修法是:按下时不捕获,位移超过 5px 确认是拖动后才捕获,
且从 button / select / input 上按下的一律不进入拖动。
探针对应输出:
"drag": {
"fromButton": { "captured": false }, // 完全没动
"fromLabel": { "right": "38px", "bottom": "86px", "captured": true }, // 正常拖动
"persisted": { "right": "38px", "bottom": "86px" } // 写回配置
}
$env:PROBE_DARK='1'; node scripts/probe-css.mjs # 跑深色分支
| 现象 | 原因 | 处理 |
|---|---|---|
| 右下角没有控制坞 | 客户端 bundle 没加载 | 检查 exports["./client"] 与 dsh.client.platform;F12 看 [dsh-anime-theme] client runtime error |
| 控制坞出现但壁纸不显示 | 面板仍不透明 / 图片没取到 | 看控制坞底部提示;确认宿主路由 /dsh-anime-theme/api/health 可访问 |
| 控制坞底部提示「宿主路由:不可用」 | 宿主半体没起来(webServer 缺失) | 检查 profile cordis.patch.yml 是否挂载、index.js 是否有报错;此时客户端会退回浏览器直连,但会被 CORS 挡住 |
启动直接崩溃 declares no dsh.bundle | package.json 缺 dsh.bundle.patch | 补 "dsh": { "bundle": { "patch": "./cordis.patch.yml" } } |
SyntaxError: Unexpected token | JSON 带了 UTF-8 BOM | 用 UTF8Encoding($false) 重写该 JSON |
| 图片 403 | 直连链路防盗链 | 换回 CDNsetu / CDNcat,或确认走的是宿主中继 |
pnpm install 报 ERR_PNPM_FETCH_404 | 某个本地插件的依赖被写成 "*",pnpm 会去 npm registry 找它 | 本地插件一律写成 "file:../../plugins/<id>",重跑 pnpm install 即可 |
| 界面打不开 | 插件导致前端故障 | DSH Desktop 用 Harness → 以安全模式重启… 屏蔽第三方插件 |
| 改完插件、重启后行为还是旧的 | pnpm 把 file: 依赖复制进 .pnpm store,改源目录不会传播 | 见下方「改了却不生效」 |
npm run sync # 同步到本机所有副本 + 逐字节校验
npm run sync:dry # 只看会动哪些地方,不写盘
它会自动发现目标(~/.dsh/plugins/<name> 与每个 profile 的 node_modules 入口,
Junction 会先解析到真身),镜像后逐个核对 sha256,不一致就报错退出。
下面这些是它解决的原理,遇到问题时可以对照排查。
file: 依赖是复制品~/.dsh/plugins/dsh-anime-theme 更新之后,profile 里那份不会跟着变:
~/.dsh/profiles/<p>/node_modules/dsh-anime-theme
→ junction → node_modules/.pnpm/dsh-anime-theme@file+..+..+plugins+dsh-anime-theme/node_modules/dsh-anime-theme
.pnpm 里那份是复制品。 文件明明改了、package.json 版本号也对了,宿主照样跑旧代码。
最快确认方式(宿主半会报自己的真实版本):
curl http://127.0.0.1:3080/dsh-anime-theme/api/health
# {"ok":true,"name":"dsh-anime-theme","version":"1.5.3"} ← 版本不对就是没传播
三种解法任选其一:
# A. 正道:让 pnpm 重新复制
cd ~/.dsh/profiles/desktop && pnpm install --force
# B. 删掉缓存的 store 条目再装
rm -rf ~/.dsh/profiles/desktop/node_modules/.pnpm/dsh-anime-theme@* && pnpm install
# C. 零网络依赖:直接把新内容镜像进 store(网络不好时最实用)
# 把 plugins/dsh-anime-theme/ 的内容覆盖到上面那个 .pnpm 目录
node scripts/check-install.js现在会比对内容 sha256,副本是旧的会直接报错并给出上面的命令。
市场(deepseek.stream)只收 .zip,一条命令打出可直接上传的包:
node scripts/package.mjs # → dist/dsh-anime-theme-1.5.4.zip
打包脚本会先自检(清单文件齐不齐、plugin.json 字段全不全、两处版本号一致不一致),
任何一项不过就直接拒绝打包,不会让你把一个会被市场打回的包传上去。
然后把 zip 拖到 https://deepseek.stream/upload 即可。
收录规范、dsh:// 一键安装链接、版本更新清单,以及几条实测踩坑记录
(桌面端宿主会剥离 dsh.bundle、profile bundles 不能放第三方插件、本地依赖必须用 file:、
Windows 下 .ps1 要带 BOM 而 .json 不能带等)都写在 PUBLISH.md。
AppFrame 的 background:var(--dsw-alias-bg-base)(且带 position:relative)建了个层叠上下文,把本插件 z-index:-1 的壁纸层整片盖死。dsh-skin 与 liquid-glass 都不在)就由本插件接管 --dsw-alias-bg-* 这组 token,不再要求先手动打开「分隔面板」。surfaceStrength() 改为给出半透明值(isolatePanels 关 = 壁纸更清楚 / 开 = 正文更清楚),原先默认的 0.92 近乎不透明,即使覆盖了也等于没透。._0TJJea_sidebarCol 与侧栏根的 .AehNHG_root 用的是另一个 token(--dsw-specific-sidebar-fill),不一起调透的话正文透了、侧栏还是一条不透明的板子。已一并纳入覆盖列表。panelAlphaOf() 原先无条件去读 dsh.ui-liquid-glass.settings。宿主把引擎删了,那份设置却还留在 localStorage 里(本机 l1Opacity=0.1),于是它永远报 0.1、永远弹「面板几乎全透」,而提示让你去的「设置 → 主题 → 液态玻璃」这个页面已经不存在了,按提示根本修不了。现在没有宿主层时直接报我们实际生效的值。themeKind() === "none" 这条分支原先在警告里没有对应文案(会落到 dsh-skin 那支,说的还是错的),已补齐三支。dsh-client-ui-layout 的 AppFrame 规则与 dsh-client-ui-theme 的 token 定义抄进测试页做 A/B 对比 —— 无覆盖时整页纯白(复现故障),有覆盖时壁纸正常透出。wallpaper 是
video:data:video/mp4;base64,… 形式的内联视频,实测 291,568 字符 / 285 KB,
而宿主 MAX_BODY_BYTES 只有 64 KB。超限被 request.destroy() 掐断,对端只看到
「连接被重置」,再被那句 catch { /* 静默 */ } 一吞,症状就只剩「诊断日志不再更新」。glassSettingsSummary():只报标量,wallpaper 只留类型 / 长度 / 头部。{"kind":"oversize","bytes":292547},至少让你知道「它曾经太大」。reportFailure():把渲染进程的异常送回宿主落盘 —— 按 where+message 去重、
带预算上限,但绝不静默;并挂上 window.error / unhandledrejection。boot() 改为每一步各自兜住:早先的串行写法里,取图那一环一旦出问题,会把
诊断上报和自动换图定时器一起带走(自动换图静默失效是同一个根因)。readJsonBody() 超限时先回 413 并带上实际字节数与上限,不再无声 destroy()。/api/diag 每条记录自动打上插件版本与宿主侧时间戳。diagCaptured 前置条件,避免对空集合做 every() 而假通过)。document.hidden / visibilitychange / IntersectionObserver
/ offsetParent 全零命中),display:none 只是不参与合成,gl.drawArrays 每帧照跑;drawScene() 里那行 自愈式播放:if (customVideo.paused) customVideo.play()
—— 每帧都会把被暂停的视频重新拉起来。实测 pause 之后 videodecode 不但没降,
反而从 4.8% 升到 6.7%。WEBGL_lose_context.loseContext() → 触发 webglcontextlost
→ 它自己 active.dispose(),而那个 dispose 里正好是 cancelAnimationFrame +
停掉并卸掉视频。着色器开销与视频解码一起归零。display:none 藏掉了。restoreContext() + 主题自行重建,不会把它的上下文永久废掉。shaderContextLost 断言。<video> 是在 hydrateWallpaperOnBoot()
里异步创建的,而 applyCss() 跑在插件 apply 阶段 —— 那时它还不存在,
pauseGlassVideo() 必然扑空。偏偏它的 drawScene() 每帧把 video 当图像源读,
所以 CSS 的 display:none 拦不住解码,实测 videodecode 一直挂在 4~5%。
现在补了两层兜底:视频容器上的 MutationObserver(<video> 一出现就摁停)
applyCss(),
会再排一轮、永不停止(探针跑不完就是它)。改用「已摁停就不再排」的标记收口。scripts/probe-css.mjs 改为显式 process.exit(0),不再被挂起的定时器拖住。0.1.6-re2-community)上实测通过:宿主移除液态玻璃引擎后,
全部液玻相关路径以 glassActive() 为闸门自动早退,无需改动即可正常工作(详见上文共存章节)。@deepseek-ai/dsh-client-ui-liquid-glass 2.x:
旧版只认 [data-dsh-theme-bg="true"],在 0.1.6 上必然落空,会让两套壁纸叠在一起。liquid-glass 的 l1Opacity(新版面板透明度由它管)。isolatePanels 在 liquid-glass 在场时自动让路,避免压平玻璃层与 token 打架。scripts/probe-css.mjs 增加三场景矩阵(旧主题 / liquid-glass / 无主题),
断言接管行为与 token 闸门都按预期生效。author 改为字符串(市场端 String() 会把它渲染成 [object Object])。运行 uninstall.ps1:移除软链、dependencies 项、cordis.patch.yml 挂载项与插件目录。
用户配置保留在 ~/.dsh/anime-theme/,可手动删除。
MIT。
图源版权归原作者所有,本插件仅转发公开随机图接口返回的图片地址,不在本地留存。
设计参考:麦麦 (MaiBot) 生态的「来点二次元图片」插件 把同一个图源用在 QQ 群聊里。本插件与之没有代码或依赖关系,只是选了同一个公开 API; 两者的形态完全不同 —— 那边是发图机器人,这边是界面主题。