dsh-video-skin
Verifieddsh-video-skin · v0.1.1 · MIT
DSH 视频皮肤:一段短片循环铺成整个界面的聊天背景(无声、可调透明度),另一段做有声的开机动画(片尾交叉溶解进界面、点击画面随时跳过)。零依赖。
Install
dsh plugin add dsh-video-skin Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Tags
Readme
dsh-video-skin
DSH 的视频皮肤:把一段短片铺成整个 Web 界面的聊天背景,并在启动时用另一段短片做开机动画—— 有声播放、完整放完再渐变淡出进界面、点击画面随时跳过。
- 开机动画:全窗短片(带声音)+ 真实启动进度条 + 常驻的「点击画面跳过」提醒;片子先完整放完
(
ended),再用固定时长fadeMs渐变淡出,音量也跟着一起淡、不会被硬切。早先的版本是"从片尾往前
fadeMs开始交叉溶解、片子放完那一刻正好淡完",而且淡出时长用的是 当时的真实剩余时间 —— 界面就绪得晚时剩余可能只剩 100~200ms,那次退场看起来就是硬切 (用户报的"有时候还没放完就切掉了"正是它)。现在默认exitMode: 'end':先放完,再淡; 想要片尾交叉溶解可以设成'tail',淡出时长同样固定。 - 聊天背景:无声、循环、
object-fit: cover铺满视口,面板半透明化,画面明显看得见、文字照样读得清。 - 界面上就能调透明度:右下角一个小圆钮 → 两个滑块 + 三个预设 + 实时文字对比度读数; 改完就生效、刷新还在,不必碰 JSON、不必重启 DSH(见 §3.1)。
- 一个依赖都没有:宿主半边只用
node:内建,页面侧是一段内联脚本,产物里没有任何 import。
英文说明见 README.md。
安装(一行)
设置 → 插件 → 添加插件,贴进这一行,点「安装」,然后 完全退出并重新打开 DSH:
github:Something11235/dsh-video-skin#main
- 桌面版(Electron)的索引注入表是宿主启动时收集一次的快照 ⇒ 装完必须完全重启 DSH(刷新页面不够)。
- 仓库里已经提交了运行所需的一切(
index.js/lib/runtime/assets的 mp4), 而且没有prepare脚本 —— 这不是省事,是必须:pnpm 会拦 git 依赖的构建脚本, 带prepare的仓库会让dsh plugin add "github:..."直接失败(ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED), 即使产物已经提交。 - 卸载:
dsh plugin --profile <你的 profile> remove dsh-video-skin,或用仓库里的scripts/uninstall.ps1。 - 国内网络直连 github 可能慢:装不上时可以加代理,或先把仓库 fork 到你自己的账号再装 fork 的地址。
0. 它长什么样(本机实测截图)
![]() |
![]() |
| 开机动画:短片铺满视口(有声)+ 真实启动进度条 + 右下角常驻的「点击画面跳过」 | 亮色主题:视频铺满,面板半透明,正文对比度 5% 分位 7.90 |
![]() |
![]() |
| 暗色主题:视频清晰可见,正文对比度 5% 分位 6.86、中位 16.06(正文要求 4.5) | 右下角圆钮展开的调节面板:两个滑块、三个预设、实时对比度 7.9(很充裕) |
三张都是 scripts/verify-browser.mjs 在真 Chromium里跑出来的(见 §6.1)。
1. 它长什么样
启动 → 覆盖层立刻占住视口(同步,先于任何网络请求)
├─ 短片全窗播放(有声——桌面版默认就允许;被浏览器拦下时自动退回静音)
├─ 底部细进度条 = 内核真实的插件激活进度
└─ 右下角常驻胶囊:「▶ 点击画面跳过」
→ 片子放完(ended)且界面已就绪 → 整体渐变淡出 fadeMs(底色/短片/UI/**音量**一起淡)
→ 淡完节点从 DOM 移除,背景层这时才建(避免和开机抢带宽)
exitMode: 'tail'时第 4 步换成"片子剩余 ≤ fadeMs 且界面已就绪就开始淡"(片尾交叉溶解)。 两种模式淡出时长都是固定的fadeMs,不会随"当时还剩多少"缩水成硬切。
参数:fadeMs(默认 1000)· holdMs 18000 / holdGraceMs 4000 · noFrameMs 12000 · endlessMs 20000。
任何时候都可以进入:点一下画面、按 Esc / 空格 / 回车。
关于声音:桌面版(Electron)没有设置 webPreferences.autoplayPolicy,于是用 Electron 的默认值
no-user-gesture-required —— 未静音的 play() 直接成功,开机动画一起就是有声的。普通浏览器
(dsh web 用 Chrome 打开)默认策略会拒掉未静音自动播放;那时运行时自动退回静音继续播
(不扣住用户),并在左下角多出一个独立的声音开关 —— 点它只开声音、不跳过,两个意图不互相踩。
如果只想要安静的开机,把 boot.sound 设成 false。
2. 装进桌面版 DSH(当前正在用的这个)
DSH 桌面版(Electron)的 index.html 来自安装包里的静态 dist,tapIndex 之类的函数式变换永远不生效;
唯一的注入通道是 Host 侧 webserver/index-inject 贡献的结构化行,而且那张表是宿主启动时收集一次、
之后没有任何刷新路径。所以:
装完必须完全退出并重新打开 DSH。 只刷新页面不够——注入表是开机快照。 而且第一次装完之后,"开机动画"本来也只在下次启动时才看得到。
一条命令安装(不用重启就能写进 profile,但注入行要等下次启动):
powershell -ExecutionPolicy Bypass -File "D:\Projects\DSH工作台\插件制作\dsh-video-skin\scripts\install.ps1"
# 可选参数:-Profile <名字>(默认 desktop)、-DshHome <DSH_HOME>(默认 $env:DSH_HOME)
# —— 参数名是 -DshHome,不是 -Home:`$HOME` 是 PowerShell 的只读自动变量,赋值会让脚本直接中止。
它会:备份该 profile 的 package.json / pnpm-lock.yaml / cordis.patch.yml
→ 写 dependencies(link: 指向本包)→ 把包名加进 dsh.profile.bundles
→ 在 profile 的 node_modules 里建目录链接(junction,不需要管理员)→ 打印"请重启 DSH"。
先看一眼当前状态(不改任何东西):
node "D:\Projects\DSH工作台\插件制作\dsh-video-skin\scripts\profile-link.mjs" status --profile desktop
卸载 / 回滚(同样不用重装 DSH,但注入行仍是下次启动才消失):
powershell -ExecutionPolicy Bypass -File "D:\Projects\DSH工作台\插件制作\dsh-video-skin\scripts\uninstall.ps1"
不要动
desktop以外的 profile 来验证:仓库里的plugindev是干净的开发 profile, 拿它试装不会影响你正在用的界面(见 §7)。
3. 调参:改一个 JSON,刷新页面
所有可调项都有默认值,不改也能用。要改,写这两个文件之一(后者覆盖前者):
| 文件 | 用途 |
|---|---|
<插件目录>/video-skin.config.json |
部署期默认值(跟着插件走) |
%USERPROFILE%\.dsh\dsh-video-skin.json |
你自己的覆盖(推荐改这个) |
改完刷新页面即生效(配置由一条 no-store 的路由在页面加载时读取)。
桌面版例外:注入行里的那份是开机快照,所以开机动画的参数要等下次启动。
包根目录的
video-skin.config.example.json是一份逐项带默认值的模板, 改个名字放进上面两个位置之一即可。
{
"boot": {
"enabled": true,
"file": "assets/boot.mp4",
"fadeMs": 1000,
"exitMode": "end",
"skipFadeMs": 320,
"holdMs": 18000,
"holdGraceMs": 4000,
"noFrameMs": 12000,
"endlessMs": 20000,
"hint": "正在启动…",
"skipLabel": "点击画面跳过",
"sound": true,
"volume": 1,
"soundLabel": "开启声音"
},
"background": {
"enabled": true,
"file": "assets/background.mp4",
"veil": 0.14,
"surface": 0.2,
"surfaceLight": 0.6,
"veilLight": 0.1,
"delayMs": 1200,
"playbackRate": 1
}
}
开机动画
| 项 | 默认 | 作用 |
|---|---|---|
enabled |
true |
关掉就是完全不注入那一行覆盖层,回到 DSH 原生启动页 |
file |
assets/boot.mp4 |
片子在包内的相对路径,可以换成你自己的 |
fadeMs |
1000 |
淡出时长(固定值)。退场永远用这个时长,不会随"当时还剩多少"缩水 |
exitMode |
end |
end = 片子放完再淡(默认,动画一定看全);tail = 片尾交叉溶解(从"剩余 ≤ fadeMs"开始淡,片子最后 fadeMs 会盖在溶解里) |
skipFadeMs |
320 |
点击跳过时的淡出时长(要短,用户已经决定不等了) |
holdMs |
18000 |
界面迟迟不就绪时的上界。到点只把状态转成"启动较慢",不截断动画 |
holdGraceMs |
4000 |
holdMs 之后再给片子的宽限;宽限用完才硬让开(这块屏是装饰品) |
noFrameMs |
12000 |
开始播但一帧都出不来,判定素材坏了并跳过。给得宽是因为宿主刚启动时可能应答得慢 |
endlessMs |
20000 |
时长未知(duration = Infinity)时的唯一上界 |
hint |
正在启动… |
覆盖层底部那行状态文字(状态会自己变:正在启动 / 正在进入 / 界面就绪后自动进入 / 启动较慢… / 启动失败) |
skipLabel |
点击画面跳过 |
右下角常驻胶囊的文案 |
sound |
true |
是否尝试带声音播放。桌面版能直接成功;浏览器拦下时自动退回静音并亮出左下角的声音开关 |
volume |
1 |
音量 0..1 |
soundLabel |
开启声音 |
声音被拦下时那个独立开关的文案 |
exitMode: 'tail'且片子比2 × fadeMs还短时,尾部溶解会自动退化成"放完再淡"——否则溶解得 从片子还没出现就开始,那是坏效果。素材报错不会立刻切掉动画:先重新挂一次
src重试,只有第二次也失败才交接 —— 宿主刚启动时那条本地素材路由偶尔应答不上,一次抖动不该让开机动画提前退场。
聊天背景
| 项 | 默认 | 作用 |
|---|---|---|
enabled |
true |
关掉就不建背景层、也不动任何主题变量 |
file |
assets/background.mp4 |
背景片子在包内的相对路径 |
veil |
0.14 |
暗主题视频蒙版不透明度(压黑)。越大越易读、画面越弱 |
surface |
0.2 |
暗主题界面表面不透明度。0 = 表面全透明(画面最强),1 = 原生不透明(看不见视频) |
surfaceLight |
0.6 |
亮主题表面不透明度。默认 = surface + 0.4。亮主题必须更实,原因见下 |
veilLight |
0.1 |
亮主题蒙版不透明度。默认 = veil × 0.7 |
controls |
true |
是否显示右下角的透明度调节控件(圆钮 + 滑块面板)。关掉就只能改 JSON |
delayMs |
1200 |
界面就绪后多久开始加载背景视频(把开机阶段的带宽留给界面) |
playbackRate |
1 |
播放速度 |
为什么亮主题要单独一个值:文字在暗主题是浅色、在亮主题是深色,而这条素材整体偏暗
(实测平均亮度 YAVG ≈ 0.25)。同样薄的白色表面放在亮主题上,有效底色会变成中灰,
深色文字的对比度立刻掉到 3:1 以下;深色表面则不会。实测(同一帧,真 Chromium 采样):
surface(暗) |
surfaceLight(亮) |
暗主题 最小 / 5% / 中位 | 亮主题 最小 / 5% / 中位 |
|---|---|---|---|
| 0.2 | 0.4 | 2.52 / 6.85 / 16.06 | 4.34 / 4.52 / 5.37 |
| 0.2 | 0.5 | 2.50 / 6.81 / 16.05 | 5.87 / 6.06 / 6.91 |
| 0.2 | 0.6 | 2.49 / 6.81 / 16.05 | 7.74 / 7.91 / 8.72 |
| 0.05 | 0.6 | 1.53 / 4.89 / 15.24 | 7.38 / 7.56 / 8.37 |
| 0 | 0.55 | 1.22 / 4.14 / 14.71 | 5.87 / 6.06 / 6.90 |
读法:5% 分位才是有意义的可读性指标(画面 95% 的区域),正文要求 4.5。上表也顺带说明
为什么暗主题的蒙版不能去掉 —— 一旦 veil 归零,暗主题的 5% 分位就掉到 4.14 了。
"最小"那列对应的是画面里极少数的镜面高光像素;在"让视频真的看得见"这个前提下它满足不了 AA,
想满足只能把蒙版加到 0.4 以上 —— 那正是"完全看不清视频"的那一版。这里选择 5% 分位与中位达标。
这五个值都可以改完刷新页面即生效(不必重启),调参时用
node scripts/verify-browser.mjs --url <带令牌地址> --sweep 直接打印上面这张表。
3.1 界面上直接调(不用改 JSON、不用重启)
右下角有一个半透明的小圆钮(鼠标靠近才变实),点开是调节面板:
| 面板里的东西 | 说明 |
|---|---|
| 面板不透明度 滑块 | 调 surface;亮主题跟着按 +0.4 派生,滑块下方把两个主题的实际值都写出来(暗色 20% · 亮色 60%) |
| 视频蒙版 滑块 | 调 veil;亮主题按 ×0.7 派生 |
| 三个预设 | 「接近原生」0.85/0.5、「均衡」0.2/0.14(默认)、「视频最清」0/0 |
| 文字对比度 xx(结论) | 实时量出来的:采样当前视频帧,按「视频→蒙版→表面」合成后算 WCAG 对比度。绿=很充裕(≥7)、蓝=够用(≥4.5)、琥珀=偏低 |
| 恢复默认 | 把写进用户配置的那几项摘掉,值退回部署期默认 |
交互细节(都是特意做的):
- 拖动即时生效(
input事件只改内存),松手才写盘(change事件)—— 拖动过程不会打出几十个请求。 - 写盘走宿主的一条
POST/DELETE /dsh-video-skin/override路由,落到$DSH_HOME/dsh-video-skin.json。只写那四个旋钮,文件里别的内容原样保留。 选文件而不是localStorage:桌面壳的页面来自dsh-app://自定义协议,Web Storage 可用性不确定; 写文件在哪个形态下都一样,而且用户能直接打开看/改。 - 写盘失败(只读目录之类)时面板会如实说"没能写进配置文件",本次会话仍然生效 —— 不假装成功。
Esc收起面板;点面板外面也收起(面板会盖住输入框右下角,用完就走)。Alt+B也能开合面板;不想要这个控件就设background.controls: false。- 控件在模态之下(圆钮
z-index: 900,内核的Modal遮罩是1000)—— 模态打开时圆钮点不到,这是故意的:模态就该盖住一切。
它不写死坐标也不抢焦点:圆钮挂在 document.body 上、和背景层同一层,不注入 DSH 的设置页
(那页是 React 管的,往里塞节点迟早出问题)。
4. 它是怎么接进去的(以及几个不许改的约束)
Host 半边 (index.js)
├─ ctx.on('webserver/index-inject', table => table.push(styleRow, scriptRow)) ← apply() 第一件事,同步
└─ ctx.inject(['webServer'], …) → 三条 exact 路由
/dsh-video-skin/boot.mp4 Range / ETag / 206 / 416 / HEAD
/dsh-video-skin/background.mp4 同上
/dsh-video-skin/config.json no-store,允许改 JSON 刷新即生效
注入行只有两行,都在同一次 push 里:
{ kind: 'style', text: <skin.css> }{ kind: 'script', placement: 'body', text: <bootstrap JSON> + <skin.js> }
三条硬约束(每条都是踩出来的,改之前先读 lib/injections.js 和 .recon/desktop-boot-recon.md):
- 绝不能用
kind:'script-src'。 桌面壳的解释器对script-src是await loadScript(src), 失败即 reject,而那个 reject 会把__DSH_BOOT_READY__打掉 → 整个应用起不来。kind:'script'是createElement + textContent + append,没有 await,不可能"加载失败"。 - 订阅必须同步注册在
apply()里。 对象级inject会把apply推迟到服务就绪之后, 而桌面壳的注入表在宿主启动时收集一次,晚一秒就永远进不了表。 - 覆盖层挂在
document.body上,不放进#root。 内核用hydrateRoot接管#root里 已有的启动页 DOM(判据是:scope > [data-dsh-boot]),往里塞节点会造成 hydration 不匹配。
聊天背景的做法(见 runtime/skin.css):
- 背景层是
position: fixed; z-index: -1的 body 第一个孩子。-1让它落在所有在流内容之下, 所以不需要给应用任何东西加 z-index,也不会挡住任何交互(含挂在 body 上的 portal)。 - 表面半透明化改的是
--dsw-alias-bg-base/--dsw-alias-bg-layer-1..3/--dsw-specific-sidebar-fill这几个别名 token。内核主题把整套色板定义在body/body[data-ds-dark-theme]上,全应用的可视表面 都引用别名 —— 覆盖别名等于一次改到所有面板,不需要知道任何组件类名。自定义属性上的!important会压过主题里的普通声明(与选择器特异性无关)。 - 表面不透明度是 JS 算好再写成独立变量(
--dsvs-*给暗主题、--dsvs-l-*给亮主题), 不在rgba()里写calc(var(...) + .1):自定义属性是 token 流,calc()会被原样替换进颜色函数, 能不能解析取决于实现;算好再写既没有这层不确定性,值本身也可以直接被断言。 - 亮/暗两套值不是化简失误:文字在两边颜色相反,而素材整体偏暗(实测
YAVG ≈ 0.25), 同样薄的白色表面会让深色文字掉到 3:1 以下。实测依据见 §3。 - 刻意不动
--dsw-alias-bg-overlay、--dsw-menu-surface-fill、文档预览与遮罩类 token: 弹层和菜单要保持不透明,否则弹窗里的字会压在视频上。
5. 出问题了怎么查
| 现象 | 原因 / 处理 |
|---|---|
| 完全没有反应 | 注入表是开机快照 —— 完全退出并重开 DSH。第一次装完 / 改过 boot.* 之后都必须重启 |
| 有背景没有开机动画 | 只在页面加载时出现,切会话不会重放;确认 boot.enabled 没关,并且 assets/boot.mp4 在 |
| 开机动画没声音 | 桌面版应当一上来就有声。若是用浏览器打开 dsh web,Chromium 的默认策略会拦未静音的自动播放 —— 左下角会出现「开启声音」开关,点一下即可(点它不会跳过)。想彻底安静就设 boot.sound: false |
| 左下角出现「开启声音」 | 正常:这是浏览器拦下自动播放时的降级路径。点它开声音,点画面跳过,两者互不影响 |
| 开机动画卡住进不去 | 不应该发生:点击/Esc/空格/回车 任何阶段都能进,超 holdMs 也会自动让开。若真卡住,加 ?dsvsdiag=1 |
| 字看不清 | 暗主题调高 background.surface 与 veil;亮主题调高 surfaceLight(0.75 以上基本回到原生观感) |
| 画面太淡 / 看不清视频 | 调低 surface(暗主题可到 0.1,甚至 0)与 veil;亮主题相应调低 surfaceLight |
| 黑屏没画面 | 素材没经过 faststart。node scripts/verify-all.mjs 会检查 moov 是否在 mdat 之前 |
| 想彻底关掉 | uninstall.ps1,或把两个 enabled 都设成 false |
诊断模式:地址后面加 ?dsvsdiag=1,底部提示行会变成实时读数
(phase / ready / done / t / dur / rs / err / why / log)。
出问题时截一张图就能说清楚卡在哪一步。
6. 自检
cd "D:\Projects\DSH工作台\插件制作\dsh-video-skin"
node scripts/check-syntax.mjs # ESM 能求值 + 注入文本能解析
node --test "tests/**/*.test.mjs" # 81 个用例
node scripts/verify-all.mjs # 上面两项 + 素材/打包/patch 契约(14 项)
测试里最有价值的两组:
tests/runtime.test.mjs—— 把runtime/skin.js放进桩 DOM + 虚拟时钟里真跑,断言 "剩余 1600ms 不淡、1200ms 立刻淡,并且淡出时长是 1200ms(真实剩余时间,不是配置的 1500)"、 "点击用skipFadeMs"、"先试未静音、被拒才退回静音、超时窗口到点必须退回静音"、 "点声音开关不跳过而点画面才跳过"、"淡出时音量一起淡到 0"、 "时长未知 / 一帧不出 / 素材报错 / 内核不就绪 四条路径都有上界"。这些性质正则证明不了。tests/host.test.mjs—— 用真的apply(),把注册出来的路由挂到真的 http 服务器上打真请求: 200/206/416/304/HEAD、Cache-Control的两种语义(整片no-cache、切片必须no-store)、 以及"注入行里不可能出现</script"。
6.1 真浏览器端到端(推荐在改样式/素材之后跑)
桩 DOM 证明不了"CSS 到底生效没有""Chromium 到底能不能解码这段片子""音频轨到底有没有被解码"
"点击有没有真的落到覆盖层上"。scripts/verify-browser.mjs 用真的 Chromium(headless)+ CDP
打开一个真的 DSH 页面:桌面等价环境 75 条 + 普通浏览器 65 条断言。
# 1) 造一个独立、可抛弃的 DSH_HOME + profile(只挂本插件)
node scripts\make-verify-profile.mjs
# 2) 起它(不碰正在用的那个 DSH)
$env:DSH_HOME='D:\Projects\DSH工作台\插件制作\dsh-video-skin\.verify-home'
& 'D:\AI\Agent\DSH官方\resources\runtime\cli\bin\dsh.cmd' --profile verify --no-open --port 19388
# 3) 用它打印出来的带令牌 URL 跑验证
node scripts\verify-browser.mjs --url "http://127.0.0.1:19388/?token=..." --shot-dir docs
两种模式都要跑,因为桌面版和浏览器是两条不同的自动播放策略:
| 模式 | 命令 | 断言什么 |
|---|---|---|
--autoplay allow(默认,等价桌面壳) |
--autoplay allow |
素材未静音播放、不出现声音开关、webkitAudioDecodedByteCount > 0(音频轨真的被解码) |
--autoplay block(普通浏览器默认策略) |
--autoplay block |
自动退回静音继续播、左下角亮出声音开关、用真鼠标事件点它不会跳过、点后变成未静音 |
它断言的东西还包括:覆盖层铺满视口且层级压倒一切;点击视口中心真的触发跳过且用的是 skipFadeMs;
淡出后覆盖层整个从 DOM 移除;背景层是 body 第一个孩子且 z-index:-1;
--dsw-alias-bg-* 真的解析成半透明(自定义属性是 token 流,getPropertyValue 拿到的是没解析的声明,
所以它把 token 用在真实属性上再读计算后的值);弹层 token 保持不透明;
背景视频真的解码出画面(readyState/videoWidth/时长);应用真的挂进了 #root。
§3.1 那个调节控件也有专门的端到端用例(E 段 23 条,传了 --dsh-home 才跑):
| 用例 | 断言什么 |
|---|---|
| E1–E4 | 圆钮存在、面板默认收起、两个滑块、圆钮平时半透明 |
| E5–E7 | 被内核模态(z-index:1000)盖住时不抢它的点击;移除模态层后圆钮可点 —— 用 elementFromPoint 实测,不是猜 |
| E8–E10 | 用真鼠标点圆钮真的展开;面板给出实时对比度;读数里把暗/亮两个主题都写出来 |
| E11–E14 | 拖到 0 时暗主题真透明、亮主题跟着走到派生的 0.4(这条正是"只写 surface 会让亮主题纹丝不动"那个 bug 的守卫);蒙版 60% 时亮色派生 42% |
| E15–E17 | 值真的落到 $DSH_HOME/dsh-video-skin.json,只有那四个旋钮,文件里别的内容原样保留 |
| E18–E21 | 刷新之后值还在(写盘 → 运行期配置的闭环),两个主题都维持 |
| E22–E23 | 「恢复默认」把值退回部署期默认,并把文件里那几项摘掉 |
顺手还会把视频帧画进 canvas 采样,按"视频 → 蒙版 → 表面"合成后和正文色算 WCAG 对比度,
给出一张"文字到底还能不能读"的实测表。本机实测(默认值:暗 surface 0.2 / veil 0.14,亮 surfaceLight 0.6 / veilLight 0.1):
| 主题 | 最低 | 5% 分位 | 中位 |
|---|---|---|---|
| 暗色 | 2.84 | 6.86 | 16.06 |
| 亮色 | 7.72 | 7.90 | 8.70 |
正文要求 4.5(WCAG AA):两边的 5% 分位都在 6.8 以上,也就是画面 95% 区域的对比度是要求的 1.5 倍,
而视频同时是明显可见的。暗色的"最低"2.84 对应画面里极少数的镜面高光像素 —— 见 §3 里那张扫描表的说明。
--sweep 会打印完整的(surface, surfaceLight, veil)对比度表,调参不必靠猜。
6.2 装机预演(改完接线、重启之前跑)
桌面版的 profile 由 Electron 应用独占,dsh --profile desktop --dump-config 会被明确拒绝,
所以"装完之后还能不能起来"在重启前没法用 CLI 回答。scripts/rehearse-profile.mjs 用内核自己的
loadProfileDirectory(就是启动路径上第一个会抛的那一步)离线组合一遍:
$env:ELECTRON_RUN_AS_NODE='1'
& 'D:\AI\Agent\DSH官方\DeepSeek Harness.exe' scripts\rehearse-profile.mjs desktop
它断言:本插件的 bundle 层解析得到、没被 skippedBundles 收走、patch 行 id/name 与包名一致、
Host 入口与四个素材都在盘上。退出码 0 才去重启。
7. 在干净 profile 上先试一遍
$dsh = 'D:\AI\Agent\DSH官方\resources\runtime\cli\bin\dsh.cmd'
& $dsh plugin --profile plugindev add "link:D:\Projects\DSH工作台\插件制作\dsh-video-skin"
& $dsh --profile plugindev web --port 0 # 打印带令牌的 URL,用它开一个独立实例看效果
不想动正在用的 DSH 时,这条路最安全。
8. 素材
| 素材 | 来源 | 处理 | 结果 |
|---|---|---|---|
assets/boot.mp4 |
videos/开机动画.mp4 |
1280×720 保持原分辨率,H.264 CRF 20,保留音轨(AAC 160k 立体声),+faststart |
2.56 MiB(原 8.00 MiB) |
assets/background.mp4 |
videos/聊天背景.mp4 |
缩放到 1600×914,H.264 CRF 30,去音轨,+faststart |
10.15 MiB(原 226 MiB) |
- 开机动画保留音轨:用户要求开启声音。桌面版的自动播放策略允许未静音播放,所以一起就是有声的。
- 聊天背景去掉音轨:用户要求"不要声音,有画面即可";没有音轨也就不需要在循环播放入口再考虑一次自动播放策略。
node scripts/verify-all.mjs 会把这两条期望钉死:开机必须有音轨、背景必须没有音轨、两条都必须 faststart。
想换成自己的片子:把 mp4 放进 assets/,改配置里的 file,然后重跑一次
node scripts/verify-all.mjs。
9. 许可
MIT。见 LICENSE。



