dsh-splash-fish
A startup film for DeepSeek Harness — your own illustration develops out of the dark, the frame drifts with real depth parallax, then dissolves into the workspace.
纯客户端 UI 插件,挂在 dsh 的 shell.overlay 槽位上。每次页面加载只播一次,播完自行卸载,不残留 DOM。
| 预览 | 内容 |
|---|---|
preview/splash-25d-preview.mp4 |
完整启动动画,5.07 秒,含 2.5D 视差 |
preview/hair_wave_preview.mp4 |
上一代的头发位移层(3.6 秒,对照用) |
preview.html |
浏览器直接打开即可预览,?t=2600 冻结到某一帧 |
安装
方式一:让 Agent 装(推荐)
在 DeepSeek Harness 里新建一个会话,把下面这段整个发给 Agent:
帮我安装这个 DeepSeek Harness 插件:https://github.com/Lby-2007/dsh-splash-fish
步骤:
1. 把这个仓库克隆到 %USERPROFILE%\.dsh\bundles\dsh-splash-fish
(目录要长期存在,插件是以 link 方式引用的,移走会失效)
2. 读写 profile 配置,把它注册进 desktop profile:
- 文件:%USERPROFILE%\.dsh\profiles\desktop\package.json
- dependencies 里加一行 "dsh-splash-fish": "link:<上一步的绝对路径>"
- dsh.profile.bundles 数组里追加 "dsh-splash-fish"
3. 在 %USERPROFILE%\.dsh\profiles\desktop\node_modules\ 下为它建一个目录链接
4. 告诉我需要重启应用才生效
Agent 会自己读 AGENTS.md 里的细节。装完彻底退出应用再打开。
方式二:应用内安装(不用命令行)
- 把仓库下载或 clone 到一个长期目录,例如
D:\dsh-plugins\dsh-splash-fish - 打开 DeepSeek Harness → 设置 → 插件 → 添加插件
- 填入那个目录的绝对路径(必须是含
package.json的那一层) - 安装 → 立即启用 → 重启应用
方式三:命令行(Web 版 / 有 dsh CLI 时)
dsh plugin --profile web add /你的绝对路径/dsh-splash-fish
桌面版的
desktopprofile 由 Electron 应用独占管理,命令行会被拒绝(profile "desktop" is managed exclusively by the Electron application)。桌面版请用方式一或方式二。
它是怎么工作的
一个视频,而不是一堆 CSS 动画
单张插画里,头发和脸在同一条像素上,没有分层。CSS 只能做整块平移——那看起来像有人在拽纸片。
所以画面上每一帧的位移都是离线渲染好的:深度图驱动视差、发根发梢权重控制幅度、空间相位让波横扫、高频湍流让区域内部也发生形变。渲染结果编码成 WebM,运行时作为视频层播放。
amp = A_BASE + A_DEPTH·depth + A_HAIR·hairWeight
φ = 2π(PHASE_U·u + PHASE_V·v)
dx = amp·(0.62·sin(2πf₁t+φ) + 0.38·sin(2πf₂t−0.7φ)) + A_TURB·turb·sin(2πf₃t+2.1φ)
其中 A_BASE 保证最远处也在动(没有死区),A_DEPTH 给视差,A_TURB 给区域内部的形变。所有波走整数个周期并减掉 t=0 的相位偏置,首帧与末帧位移严格为 0——视频播完摘掉时画面不会跳。
三个必须解决的接入问题
| 问题 | 做法 |
|---|---|
| 视频要和底图共用同一个缩放/模糊变换 | 把 <video> 放进 .sf-bg 内部,子元素继承父节点的 transform 与 filter |
| 插件没有打包器,引用不到外部文件 | base64 内联 → 运行时 atob → Blob → createObjectURL |
| 视频 3.6 秒、时间轴 4.4 秒,播完会跳 | 位移公式保证首尾归零,播完直接 display:none |
不闪宿主启动壳
宿主自带的启动画面([data-dsh-boot])在任何客户端插件被实例化之前就画好了。所以 lib/index.js 通过 dsh 的结构化注入行,在 index.html 解析阶段就塞进一行同色封面:
const inject = ["webServer"];
function apply(ctx) {
ctx.on("webserver/index-inject", (table) => {
table.push({ kind: "style", text: COVER_CSS });
table.push({ kind: "script", placement: "head", text: COVER_JS });
});
}
几条纪律(都是踩坑换来的):只推 style / script 两种行(未知 kind 会让 boot gate 直接 reject)、style 必须在 script 之前、订阅必须同步注册、不能用 tapIndex(桌面端走 dsh-app:// 直读,那条路是死代码)。封面在 client 挂载时被摘除,另有 3.5 秒硬超时兜底。
绝不能挡住界面
shell.overlay 的槽位层级只有 z-index: 20,祖先的层叠上下文会把后代的大 z-index 锁死在里面,所以动画层挂到 document.body 下、z-index 取 2147483600。
但仅仅是"挂到 body"还不够——全屏层一旦残留就会吃掉所有点击。所以三层防御:
.sf-root与 React 空壳都设pointer-events: none(即使残留,鼠标也穿透)- 挂载前清掉上一次可能残留的
.sf-stage-host - 播完再设一道 1.2 秒后的强制清理,不依赖卸载回调
播放期间的"点击跳过"改到 window 的捕获相位监听,并 stopPropagation 吞掉这次点击,不误触下面界面。
换成你自己的图
pip install opencv-python numpy scipy # 渲染必需
pip install transformers # 深度图(可选,装不上会退伪深度)
# 1) 深度图
python3 hair/tools/05_depth.py --image assets/splash.webp --out hair/masks/depth.png
# 2) 头发 mask(SAM2,需要 torch + 权重 sam2.1_hiera_large.pt)
python3 hair/tools/01_sam2_hair_mask.py \
--image assets/splash.webp --out hair/masks/hair_mask.png
# 3) 发根→发梢权重图
python3 hair/tools/02_make_weight.py \
--mask hair/masks/hair_mask.png --out hair/masks/hair_weight.png
# 4) 渲染 108 帧 + 编码 + 内联
python3 hair/tools/06_render_25d.py
bash hair/tools/04_encode_webm.sh hair/frames25d assets/wall_25d.webm
python3 build.py
所有可调常量在 06_render_25d.py 顶部:A_BASE / A_DEPTH / A_HAIR / A_TURB / A_BREATH / PHASE_U / PHASE_V / CYCLES。当前值峰值位移约 9 px。
仓库里的 mask 是几何近似(生成时没有 GPU 环境跑 SAM2,用的是"椭圆约束 + 蓝度阈值 + 挖掉脸/肩")。发丝之间的镂空是糊的。跑一遍
01_sam2_hair_mask.py换成真 mask 再重渲染,边缘会干净得多。
版本历史
| 版本 | 变化 |
|---|---|
| v0.1.0 | 第一版:纯 CSS 启动动画(黑场显影、光晕、斜光、粒子、字标、淡出) |
| v0.2.0 | 换成深度驱动的 2.5D 位移,并消除宿主启动壳的闪烁 |
| v0.2.1 | 动画层改挂 <body> 下,压住第三方浮层(余额鲸鱼挂件那类) |
| v0.2.2 | 加顶部压暗带,让 Windows 原生窗口按钮融入画面 |
| v0.2.3 | 修掉启动层拦截点击的问题(pointer-events: none + 兜底清理),撤掉压暗带 |
完整改动见 CHANGELOG.md。
已知限制
- 无纹理区域看不出在动。 皮肤、纯色背景这类平滑区域,位移 9 px 在像素值上只差不到 2 个灰度级。这是整图位移方案的物理极限,不是参数问题。
- 盖不住原生窗口按钮。 Windows 的最小化/最大化/关闭由操作系统绘制在网页之上,CSS 无解。要么切 dsh 的
advanced窗口模式(标题栏变透明),要么接受它。 - 只播一次,刷新页面重播。
- VP9 + alpha 只在 Chromium 系生效(dsh 桌面版是 Electron,没问题)。Safari 不支持。
- dsh 仍是 developer preview,
shell.overlay/slots.register契约可能随小版本变动。
项目结构
dsh-splash-fish/
├── package.json dsh.bundle.patch + dsh.client.platform=web
├── cordis.patch.yml roster 里插一行
├── lib/index.js host 半边:注入启动封面
├── client.js 构建产物(图 + 视频已内联,勿手改)
├── client.template.js 源码:样式、时间线、video 挂载、清理逻辑
├── build.py 内联静图 + webm → client.js
├── assets/ splash.webp / wall_25d.webm / hair_wave.webm
├── hair/tools/ 01 SAM2 分割 / 02 权重 / 05 深度 / 06 2.5D 渲染
│ 04 编码 / 03 旧渲染 / 07 拆层 / 08 分层渲染
├── hair/masks/ hair_mask.png / hair_weight.png / depth.png
├── preview/ 预览 mp4
├── preview.html 免安装预览页
├── dev/harness.html 伪宿主自测页(?t= 冻结帧 / ?nohair=1 差分)
└── AGENTS.md 给 AI Agent 的安装与改动指引
License
MIT. 插图版权归原作者,仓库内仅供演示,请自行替换 assets/splash.*。
No comments yet. Be the first to write one.