1015677515/dsh-theme-holo ↗★ 0
dsh-theme-holo
《狼与香辛料》赫萝主题:浅色「麦秋晨光」与深色「星夜麦田」两套暖色皮肤,附带开场动画、动态档案卡、日志标签分类、右栏待机动画与可拖动探头。 适合喜爱《狼与香辛料》、想要个性化暖色调界面的用户。
설치
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:1015677515/dsh-theme-holo
dsh-theme-holo
《狼与香辛料》赫萝主题 —— 给 DeepSeek Harness Web GUI 的一层暖纸皮肤。
羊皮纸底色、麦金强调色、一段 12 秒实时渲染的开场动画、趴在输入框边偷看的她、 一张画像式的账户小卡、一枚可以自己摆位置的「狼与香辛料」字标, 以及只在你开始页亮着的右栏待机画面。
目录
| 章节 | 功能详解的十个小节 |
|---|---|
| 这是什么 | 两套配色 |
| 界面预览 | 开场动画 |
| 功能一览 | 浮动字标 |
| 环境要求 | 偷看的她 |
| 安装 | 动态档案卡 |
| 功能详解 | 日志标签 |
| 插件设置项 | 侧栏脚下的麦田 |
| 目录结构与素材 | 插件详情页横幅 |
| 常见问题 | 右栏待机画面 |
| 实现要点 | 小鲸鱼挂件联动 |
| 已知边界 | |
| 许可证与版权 | |
| 出处声明 | |
| 相关链接 | |
| English |
关于这个目录的方框:GitHub 的 Markdown 渲染器会剥掉所有
style与color, 没法给目录加自定义底色的圆角外框。表格是唯一既自带底色与边框、又保留可点击链接 的结构,所以这里用表格来画这个框——底色与线条是 GitHub 主题给的灰调, 不是插件的米色,这点无法改变。
这是什么

说明:上方这张横幅是图片,配色取自插件浅色「麦秋晨光」——米黄
#FBF7EF底、 深米色#DCC9A8双线圆角外框、金色#A2701F分隔线,文字#6E5D46。 GitHub 会剥掉 README 里所有的style与color属性,页面本身的底色和文字颜色改不了, 能承载配色的只有图片。
| 项 | 说明 |
|---|---|
| 形态 | 正规 DSH 插件:index.js(宿主半)+ client.js(浏览器半)+ assets/ + 素材/ + cordis.patch.yml |
| 插件大小 | 约 16 MB(client.js 与 index.js 合计约 198 KB,其余全是素材;docs/ 的 1.7 MB 配图不参与运行时) |
| 素材 | 图片 9 张、视频 6 组、音频 3 件,由宿主侧的 HTTP 路由送出 |
| 配色 | 浅色「麦秋晨光」与深色「星夜麦田」(开发中,测试功能) |
| 依赖 | Node ≥ 22;无第三方运行时依赖 |
设计上有两条原则:
- 不改宿主布局。 所有装饰要么是挂在 `` 上的独立浮动层,要么是纯 CSS 规则,
不改宿主的 flex 盒、尺寸与
overflow。 - 全部可关。 每个组件在插件设置里都有开关,关掉即彻底移除,不留残余节点。
界面预览
装上之后,整个 Web GUI 长这样(为保护隐私,余额、今日消耗金额与账号名已遮去,其余为原始截图):

一眼能认出的几处:
| 位置 | 看到什么 |
|---|---|
| 顶部中间 | 「狼与香辛料」浮雕字标,可拖动、可关 |
| 右上 | 右栏待机画面(只在右栏自己的开始页亮) |
| 输入框右侧 | 偷看的她,只露兜帽和一双眼 |
| 左下角 | 侧栏脚下的麦田,宽度随侧栏走 |
| 整页 | 羊皮纸底色 + 麦金强调色,所有文字转暖棕 |
更多细节图见下方各功能小节。
功能一览
| 功能 | 一句话 |
|---|---|
| 两套配色 | 浅色羊皮纸 / 深色暖墨(深色为测试功能) |
| 开场动画 | 12 秒 canvas 实时绘制:暗里的红眼 → 狼影描红 → 光炸开 → 标题解码 → 就绪 |
| 浮动字标 | 「狼与香辛料」字标,位置 / 大小 / 不透明度可调,可整个关掉 |
| 偷看的她 | 输入框边只露兜帽和一双眼,指针靠近会升起;可左右拖动 |
| 探头语音 | 点她会「疑惑」一声(可关) |
| 动态档案卡 | 悬停左下账户行,升起一张 Steam 式小卡 |
| 日志标签 | 「思考 / 读取 / 运行命令」等行内标签各一档暖色 |
| 侧栏麦田 | 侧栏脚下的一片麦穗,宽度跟着侧栏走,高度锚在账号行 |
| 插件详情页横幅 | 插件管理器里点进本插件时,内容顶部一张 1920×620 的封面 |
| 右栏待机画面 | 只在右栏自己的开始页亮的一段动画,六片可换,一进去干活就熄 |
| 小鲸鱼联动 | 一键把两个角色 + 一条语音装进 dsh-whale-widget |
环境要求
| 项 | 要求 | 说明 |
|---|---|---|
| DeepSeek Harness | >=0.2.0-rc.1 =22 | DSH 自带运行时即满足 |
| 操作系统 | Windows / macOS / Linux | 已在 Windows 11 + DSH 0.2.1-rc.2 实测 |
| 磁盘空间 | 约 16 MB | 其中素材约 13 MB(素材/动画/ 六段视频占 13.2 MB) |
| 第三方依赖 | 无 | 没有 npm 依赖,安装后不需要 pnpm install |
| 网络 | 安装时需要 | 从 GitHub 取包;运行时不联网 |
这些版本约束同时写进了 package.json:
"engines": { "node": ">=22", "dsh": ">=0.2.0-rc.1 =0.2.0-rc.1 **【未验证】** 这条 `github:` 路径没有在隔离环境里逐步实测过。若解析失败
> (提示找不到包、或装完不出现在列表里),改用下面这条一定可用的本地安装。
本地安装的核心是三行:profile 的 `package.json` 里加 `link:` 依赖、在
`dsh.profile.bundles` 里登记插件名、再到 `node_modules` 下建一个目录链接。
**完整的三种安装方式、自检步骤、卸载与故障排查,都在 [INSTALL.md](https://github.com/1015677515/dsh-theme-holo/blob/dc1878fa2a6173fa6ff4ace8bbe49355af08554c/INSTALL.md)。**
装好之后:开场动画播完、界面变成羊皮纸色就是生效了。如果检测到小鲸鱼挂件,
会弹一次「要不要把赫萝装进挂件」的对话框;不想装就选「暂不安装」,之后不再弹
(见[小鲸鱼挂件联动](#小鲸鱼挂件联动))。
---
## 功能详解
### 两套配色
浅色「麦秋晨光」与深色「星夜麦田」(**开发中,测试功能**),两套值键一一对应。
- 浅色规则挂在 `html body:not([data-ds-dark-theme])` 下;
- 深色规则挂在 `html body[data-ds-dark-theme]` 下。
两套值分别在 `client.js` 顶部的 `PALETTE` 与 `PALETTE_DARK` 两张表里,由同一个循环展开成 CSS 变量。
**两张表的键名与顺序必须完全一致**,改了一个就要改另一个。
**黑夜模式开关**走的是主题服务的 `setTheme('dark' | 'light')`,和「设置 → 外观」用的是同一个
偏好字段,所以两处是同一个状态,不会出现「设置里是深色、插件里写着浅色」的分裂。
宿主没有 theme 服务时回退为直接切 `body[data-ds-dark-theme]`。
**首次安装默认浅色**:DSH 的外观偏好出厂值是「跟随系统」,系统本身是深色的机器,一装好就落在
深色 GUI 上,再叠上「星夜麦田」很容易被误读成「装了插件就被切成黑夜模式」。所以插件在**首次
运行**(`localStorage['dsh-theme-holo.dark-defaulted']` 尚未落闩时)若发现宿主已解析为深色,
会通过主题服务把偏好改回 `light` 一次并落闩;此后只有设置卡片里的开关能再打开深色,插件不会
跟你后来的选择对着干。本来就是浅色的机器不会被碰,只落闩。
**黑夜模式尚未完全适配**:深色下仍有若干细节是按浅色调的,观感不如浅色完整。设置卡片里为此
加了一行提示——「黑夜模式未完全做好适配,请谨慎打开」。
**深色下暖色装饰整体收起**:麦穗纹、飘落的麦粒、偷看的她、连接线这些按浅色羊皮纸调的装饰,
压到深色上会脏,所以用一条规则统一隐藏,只留深色外壳与星夜麦田的 token 值。
浮动字标与联动弹窗不在其列——字标是品牌标识,两套配色下都该在。
### 开场动画(12 秒,实时渲染)

不是视频,是 canvas 2D 逐帧绘制,用 `requestAnimationFrame` 按真实时间推进。
画布固定 1920×1080,帧率 30 fps,总时长 12 秒。
时间轴:
| 秒 | 画面 |
|---|---|
| 0.8 | 全黑,闭着的眼睑先透出红光 |
| 1.15 – 1.95 | 缓缓睁眼(先半睁,再完全睁开) |
| 2.45 / 2.92 | 两次眨眼,每次 0.2 秒 |
| 3.25 – 3.6 | 视线移向左边的狼影 |
| 3.4 – 6.1 | 镜头在黑暗中后拉 |
| 3.75 – 5.15 | 狼影轮廓被红光从背脊描到吻部 |
| 4.85 | 狼影的眼睛亮起 |
| 5.2 – 7.0 | 狼嚎 |
| 6.35 | 她眨一只眼 |
| 6.55 – 7.95 | 光从双眸炸开,照亮整张画 |
| 7.9 / 8.45 | `DEEPSEEK` 逐字解码出现,`HARNESS` 字距收拢 |
| 8.55 – 9.25 | 拉出一根「缰绳」,线头一颗红点 |
| 9.05 – 9.85 | 三行启动日志逐字打出 |
| 9.9 – 10.45 | 进度条走满 |
| 10.5 / 10.75 | 就绪闪光、标语 |
| 11.3 – 12 | 只淡出声音,画面停住 |
**操作**
- **任意点击 / 按键立刻结束**,0.55 秒淡出后交还屏幕。
- 右下角有 `跳过动画` 按钮,效果等同于点画面任意处。
- 系统开了「减少动态效果」(`prefers-reduced-motion: reduce`)时直接停在收尾帧。
- **音轨默认关闭**,右下角开关是 `♪ 音效 开` / `♪ 音效 关`。打开后会立刻播放并**自动对齐当前画面时间**,
选择记在本地,下次启动沿用。点开关**不会**跳过片头。
- 插件设置里可以永久关闭开场动画(见[插件设置项](#插件设置项))。关掉后连 canvas 都不创建,
下一帧就把屏幕交回。
**调试入口**(写在 URL 里)
- `#boot-hold` 把片头停在关键帧;`#boot-hold=11.4` 可指定任意秒数,缺省停在 10.2 秒。
- `#videocap` 停在片头供录屏,`#videocap=tail` 额外多留 5 秒画面。
### 浮动字标
「狼与香辛料」字标(`assets/logo.png`,591×125 透明 PNG)是一块**自己定位的图**:
`position: fixed` + 视口百分比,挂在 `` 上,位置和大小由插件说了算。
**为什么不做成标题栏里的水印**:尺寸和位置会被宿主的 flex 盒、容器单位与 `overflow` 规则牵着走,
插件改不动;而且一开插件设置页(宿主会换掉主面板),整棵会话连同标题栏被卸载,水印得靠常驻观察器
才能回来。挂在 `` 上这两个问题都不存在。
**位置以视口百分比存储**(`xPct` / `yPct` / `wPct`),渲染时用 `vw` / `vh` 单位,
所以窗口缩放、最大化、换分辨率都不需要监听 `resize`,字标自动等比跟随。
**日常不拦点击**:图层常驻 `pointer-events: none`、`aria-hidden="true"`,不挂任何监听。
只有打开「更改 logo 位置」时才变成 `pointer-events: auto`,那时才能拖动。
**调位置、调大小**
1. 插件设置里点「更改 logo 位置」,字标出现一圈细线框、右下角小方块和实时坐标读数;
2. 拖图片移动,拖右下角小方块改大小,读数会实时显示 `x, y · 高 Npx`;
3. 按 `Esc` 也能退出;点「完成调整(固定下来)」把位置写进本地存储。
同一节里还有:
- **关闭 logo**:整个图层收掉(不是把不透明度降到 0——隐藏的东西不该还能接点击)。
位置和大小原样保留,再点「显示 logo」就回到刚才停的地方。
- **恢复默认位置**:位置、大小、不透明度、显隐四项一起回到出厂值。
**不透明度**:10%–100% 的滑块,出厂 50%,拖动即时生效、松手即存。
下限是 10% 而不是 0:一个几乎透明的字标等于一件找不回来的东西。
**图层高度**:`z-index: 2`。这个数字是调出来的——`-1` 会被宿主的 `.BynINW_frame`
(不透明背景)整个吞掉;`0` / `auto` 会和它同趟按文档序绘制,而插件在宿主渲染外框之前就挂载了,
反过来被盖住。`2` 压得过外框,但低于宿主自己浮起来的控件(缩放手柄、overlay 层等)。
编辑态会升到很高的 z-index 并开启 `pointer-events`。
> 代价是它**会压在别的东西上**——它本来就在 GUI 之上。这是有意的取舍:字标是一块可以挪的图,
> 不是垫在纸下面的水印。想让它让开就把图拖走,或者在设置里「关闭 logo」。
### 偷看的她
输入框边上只露兜帽和一双眼,指针靠近会升起。所有动作由一个状态机驱动:
**露出多少、倾角、让位、点头**四个量,行为只写目标值,循环以 0.14 的系数缓动过去,
所以永远不会「跳」。
| 状态 | 触发 | 表现 |
|---|---|---|
| 常态 | 无输入 | 露出 32%,只到眼睛;整体再下沉 10px,底边被输入框卡片裁掉 |
| 正在打字 | 输入框有内容 | 露出 58%,朝文字倾,并从她手边拉出一根金线接到输入框 |
| 生成中 | 出现「停止」按钮 | 露出 62%,朝文字方向倾,盯着看 |
| 长文看累 | 输入 > 420 字 | 沉下去、透明度降到 55%,歪头表示嫌弃 |
| 打瞌睡 | 空闲 170 秒(23:00–05:00 为 75 秒) | 只留帽檐,鼠标一动就醒 |
| 打开菜单 / 弹窗 | 有 `[role=menu/dialog/listbox]` 或 `aria-modal` | 往右让开自身宽度的 55% 并透明到 0 |
| 靠近 | 指针在 96px 内但没落在她身上 | 往下缩 5px、歪头 |
| 指针移动 | 以她自己的中心算方向 | 整颗头朝指针倾斜,位移上限 ±10px / ±3px,倾角上限 ±2.2° |
| 点击她 | 点 | 纵向压扁一下 + 6 粒金麦从卡片后面飘起,并播一句应答语音 |
| 悬停 5 秒 | 一直指着她 | 一枚小苹果从帽檐后滚出来(纯 CSS 画) |
| 发送 | 点发送或回车 | 往下点一下头(320ms) |
麦粒和苹果都是临时节点,动画结束即移除;金线是 SVG 路径,只在「打字且没有弹窗」时出现。
「减少动态效果」时所有缓动系数置 1,麦粒和苹果直接不出现。
**她可以左右拖**:位置是**输入框宽度的分数**(不是像素),默认 `0.72`。
位移不到 4px 不算拖动,照常当成「点她」;拖过了就把这次抬手产生的 click 吃掉,不会误触发语音。
位置夹在她的中心可达范围内,插图不会跑到输入框两端之外。
**点她会说话**:`assets/peek-voice.wav`(「赫萝疑惑」)复用同一个 `Audio` 元素,
每次点击先 `pause()` 再把 `currentTime` 归零,所以连点不会互相打断、也不会叠音轨。
浏览器不给自动播放权限时静默失败——视觉反馈照常,不弹错。
**关掉她**:插件设置里的「探头的赫萝」开关,关掉后彻底不显示。语音可以单独关:
「探头的她说话」关掉后,点她只有动作,没有声音。
### 动态档案卡
悬停左下角账户行时升起,指针离开 **180ms** 后收起。卡片是纯 DOM + inline style,
**不是** `[role=dialog]`,所以不抢焦点、不锁指针;账户菜单本身完全不动。
结构是横幅图(`intro.jpg` 裁切放大)+ 渐变压暗 + 骑在横幅下沿的圆形头像(`avatar.jpg`)
+ 名字与一行小字。名字取自被悬停那一行的文本,所以换账户会跟着变。
只有侧栏内部滚动会让它收起——消息列表在生成时滚动极频繁,跟着收会让卡片闪一下就没了。
### 日志标签
「思考 / 读取 / 运行命令…」是 Harness 自己的行内标签,类名是哈希过的、所有类型共用一套,
没有 CSS 能直接区分它们。插件里加了一个很轻的观察器:
- 监听 body 的 childList(防抖 180ms),只扫 `span[class*="_title_"]`
- 文本以那几个词开头(长度 ≤ 8)时打上 `data-holo-kind="think|read|run|tool|write"`
- 样式全部挂在 `[data-holo-kind]` 上,卸载时把属性一起清掉
对应关系:思考→think;读取 / 图片 / 搜索→read;运行命令→run;工具调用→tool;写入 / 编辑→write。
开销可以忽略:一轮对话里这些 span 不过几十个,打过标记的不会重复处理。
### 侧栏脚下的麦田
`assets/wheat.png` 贴在左边栏最下面。它是一个挂在 `` 上的**独立浮动层**
(`position: fixed`、`pointer-events: none`),只做一件事:测量侧栏列的矩形,
把 `left` / `width` / `bottom` / `height` 写成视口像素。
**不往宿主元素里插任何节点,也不改宿主元素的任何样式。**
| 项目 | 默认 | 说明 |
|---|---|---|
| 整体不透明度 | `.75` | `#holo-wheat` 的 `opacity` |
| 两侧淡出到 | `.4`(叠 75% 后为 30%) | 横向 `mask-image` |
| 上边缘淡出到 | `.4`(同上,30%) | `#holo-wheat i` 的纵向 `mask-image` |
| 高度 | 侧栏底部 → 账号行垂直中线 | `createWheatLayer()` 的 `place()` |
**边缘的 30% 不是一个独立的数**:两层是相乘关系,`0.75 × 0.4 = 0.30`。所以改整体浓度只动
`opacity` 一行,边缘会按比例跟着走。
**宽度跟侧栏、高度不变形**:图是 `background-repeat: repeat-x` + `background-size: auto 100%`,
宽度由 JS 写成侧栏列的像素宽,高度单独写成固定像素,所以侧栏变宽只是多铺几段,麦秆不会被拉胖。
图本身是 `素材/小麦.png`(452×146)左右**镜像拼接**成 904×146 的无缝片——原图左右缘 alpha
不一致,直接平铺会有一道断口。
高度锚在「账号菜单」行的垂直中线;找不到这行时退回列高的 18%,并夹在 `[24px, 列高]` 之间防呆。
窗口缩放、侧栏折叠、会话列表把账号行挤走,都由 `ResizeObserver` 重算,不轮询。
> 它压在宿主的 `.BynINW_frame` 之上,所以跨过名字那条带会有少量穗尖。
> 靠素材本身消化——`wheat.png` 顶部 0–20% 有 93.8% 完全透明,遮罩又降到 40%,几乎看不出;
> `pointer-events: none` 保证账号行仍可点。
### 插件详情页横幅
插件管理器里点进「赫萝主题」的详情页,内容顶部会有一张 1920×620 的横幅(`assets/cover.webp`)。
它是**正常文档流里的一个块**,不是浮动层,用 `::before` 伪元素实现——那页不归本插件所有,
伪元素不占位、不与标题图标打架、也不用往宿主的 DOM 里塞东西。
选择器用的是插件管理器自己打的 `[data-plugin-detail="dsh-theme-holo"]` 属性,
名字里没有哈希,宿主重建样式表也不会失效。
`cover.webp` 是**已经烘好浓度的版本**——75% 的整体场与 50% 的边缘淡出烤进了 WebP 的 alpha 通道。
CSS `opacity` 会把整个元素(含边框)一起淡掉,而背景图自身没有透明度旋钮,所以这一步在导出时做完。
要调浓淡就重新导出这张图;未压缩原件 `素材/横板封面.jpg` 留在作者本地备查(未随仓库发布)。
### 右栏待机画面
一段只在**右栏自己的开始页**亮着的动画。右栏里一开文件、终端或浏览器,开始页就被替换掉,
整层立刻撤掉——连 `` 都从文档里摘掉、`src` 也丢掉,解码器随之释放。
这是刻意的:待机画面是气氛,不该在干活的时候压在栏底下耗显存。
| 项 | 做法 |
|---|---|
| 容器 | `#holo-scene`(`position: fixed; inset: 0; z-index: -1; pointer-events: none`),由 `html[data-holo-scene="on"]` 打开 |
| 切片 | 宽度取 `min(右栏宽, 608px)`,左边缘对齐画面,`overflow: hidden` 负责裁 |
| 视频 | 固定 **608×1080** 的盒子,**1:1 不缩放**。素材本身就是 608×1080 的竖裁,所以画面里的她是真实尺寸 |
| 渐隐 | 视频顶部 58% 不透明、78% 降到 55%、底部到全透明 |
| 左右边缘 | 各 10% 区域做模糊 + 透明度淡到 50%,中间保持清晰 |
| 深色 | 底色渐变 `#14100B → #100D09`,视频 `opacity: .94`,另叠 scrim 与 veil 两层压暗 |
| 浅色 | 底色渐变 `#FBF7EF → #F6EEDF`,视频 `opacity: .82` + 暖色滤镜 + `mix-blend-mode: multiply`——不是加一层暖色,而是把它**印进纸里** |
**为什么必须清掉宿主的底色**:视频垫在 `z-index: -1`,比宿主外框和右栏自己的不透明背景都要靠后,
不清就是「什么都没发生」。所以亮着的时候,插件会给**内联**加 `background-color: transparent`
并打上标记:右栏面板、栏里每个铺满的大盒子、以及列的祖先链(到 `body` 为止)。
收盘面时把那些内联值 `removeProperty` 掉。**只碰这条已知的祖先链和栏里的大盒子,不做全文档扫描**
——扫一遍会把界面扒光。判断「大盒子」用的是面积阈值(宽 ≥ 栏宽 85%、高 ≥ 栏高 50%),
所以开始页那几个入口胶囊不会被动。
**开始页怎么认**:不认哈希类名,认宿主自己写的三个 `data` 属性——
`[data-sidebar-right-panel]`(栏本身)、`[data-sidebar-right-open]`(展开态)、
`[data-sidebar-right-guide]`(栏自己的开始页)。三个条件同时满足才亮。
一个会话一份面板,被搁置的会话节点还留在文档里,所以查面板和开始页都只在**可见的那一个**里找。
**六段可换,名称就是文件名**。`素材/动画/` 下六个 `.webm` 对应设置页里的六个选项:
| 选项名 | 文件 |
|---|---|
| 赫萝抱臂 | `素材/动画/赫萝抱臂.webm` |
| 赫萝开心(默认) | `素材/动画/赫萝开心.webm` |
| 缪莉开心1 | `素材/动画/缪莉开心1.webm` |
| 缪莉开心2 | `素材/动画/缪莉开心2.webm` |
| 赫萝叉腰 | `素材/动画/赫萝叉腰.webm` |
| 缪莉挥手 | `素材/动画/缪莉挥手.webm` |
选项名与文件名严格一致,**换片只要同名覆盖文件,不用改一行代码**。
下拉框末尾还有第七项 `关闭动画`:它不是文件,选中后右侧栏开始页不显示待机画面,
进入开始页时连视频都不加载(连解码器都不占用)。选回任意一段即可恢复,不用重启。
六段各取一帧,从左到右:

**它看起来糊,通常不是它自己的问题**。素材是 608×1080 的竖裁,按 1:1 显示,**没有上采样**。
真正把画面变糊的是**装了 wallpaper 插件(`dsh-plugin-wallpaper-engine`)并开着液态玻璃 /
磨砂效果**:那一层用 `backdrop-filter: blur()` 把背后的一切——包括这段待机动画——一起雾化。
本插件对视频只写 `opacity`、`object-fit`、`mask-image`、色相滤镜与 `mix-blend-mode`,
**没有任何 `blur()`**。
判断方法:把 wallpaper 插件的雾化强度**调低**(或直接关掉液态玻璃)再看这一栏,糊度随之下降
即为其所致。想让画面清楚又保留玻璃质感,把雾化调到最低档通常就够了。
> 注意别和「小鲸鱼挂件」(`dsh-whale-widget`)搞混:那是角色与语音的挂件,不碰背景滤镜。
> 这里说的雾化只来自 wallpaper 插件。
### 小鲸鱼挂件联动
装了小鲸鱼挂件(`dsh-whale-widget`)的人,可以不动手多出两个可替换角色和一条语音。
| 装进挂件的名字 | 来源文件 | 类型 |
|---|---|---|
| 赫萝 | `素材/holo-clean.png` | 角色 |
| 赫萝Q版 | `素材/holo-Q-clean.png` | 角色 |
| 赫萝震惊 | `素材/赫萝语音.WAV` | 音效组(按下音) |
**什么时候问**:片头播完、页面加载完之后,前端先探一次挂件的 `/dsh-whale/roles.json`:
- 挂件没装 → 路由不存在,`fetch` 失败,安静跳过,什么都不弹;
- 挂件装了、且三个名字里缺任何一个 → 弹一次羊皮纸对话框,说明会装什么,
给「立即安装」和「暂不安装」两个选择;
- 已经齐了 → 不弹。
「问过了」这件事记在本地,无论选了哪个都记下来,**所以只弹一次**。
**之后想补装**:插件页(插件 → 已安装 → 赫萝主题)的包详情页里有一块「赫萝 × 小鲸鱼挂件」,
常驻显示当前状态,缺什么就列出什么,按钮「一键导入联动内容」。
| 状态 | 显示 |
|---|---|
| 没装挂件 | 「没有检测到小鲸鱼挂件。」,按钮不可用 |
| 检查中 | 「正在检查小鲸鱼挂件…」 |
| 缺内容 | 「还缺:角色 X;语音片段;音效组。」,按钮「一键导入联动内容」 |
| 齐了 | 「已写入小鲸鱼挂件的数据目录:角色「赫萝」「赫萝Q版」,语音组「赫萝震惊」。」 |
导入成功后还会多出一个「刷新页面」按钮——**小鲸鱼挂件只在页面加载时读一次角色列表**,
导入后要点「刷新页面」(或按 Ctrl+R)才会在它的「角色」里出现。不给这个按钮,
导入看起来就像失败了。
**装的时候做了什么**:对每个缺的角色,`fetch` 一次 `/holo-theme/link/` 拿到字节 →
转成 `data:` URL → `POST /dsh-whale/roles.json`;语音同理,先上传片段拿回 `id`,
再用 `save-group` 建音效组。**按名字比对,已有的不动**,所以重复点是安全的。
> 挂件的路由受宿主鉴权保护,所以联动只在浏览器里跑得通,命令行侧没法代劳。
> 这也意味着**必须重启一次 Harness**,新的 `/holo-theme/link/` 路由才会存在。
---
## 插件设置项
插件页(插件 → 已安装 → `dsh-theme-holo`)的包详情页里,卡片最上面是联动区块,
下面依次是六组开关。组件行页也有同一块。
| 小节 | 控件 | 存哪 |
|---|---|---|
| 开场动画 | `关闭开场动画` / `开启开场动画` | `localStorage['dsh-theme-holo.skip-boot']` |
| 探头的赫萝 | `不启用探头的赫萝` / `启用探头的赫萝` | `localStorage['dsh-theme-holo.peek-off']` |
| 探头的她说话 | `关闭探头语音` / `开启探头语音` | `localStorage['dsh-theme-holo.peek-voice-off']` |
| 黑夜模式 | `开启黑夜模式` / `关闭黑夜模式` | 主题服务的 `preference` 字段(同「设置 → 外观」) |
| 右侧待机动画 | 下拉框,前六个选项就是六个动画文件名,末尾另有一项 `关闭动画` | `localStorage['dsh-theme-holo.scene-name']` |
| 更改 logo 位置 | `更改 logo 位置` / `完成调整`,另有 `关闭 logo` / `显示 logo`、`恢复默认位置`,以及一条 10%–100% 的不透明度滑块 | `localStorage['dsh-theme-holo.logo-place']` |
首次安装时,若宿主外观已解析为深色,插件会自动改回浅色一次并把
`localStorage['dsh-theme-holo.dark-defaulted']` 置为 `1`,之后不再干预。想主动用深色,
点「开启黑夜模式」即可。
> **黑夜模式未完全做好适配,请谨慎打开。** 深色下仍有若干细节按浅色调过,观感不如浅色完整。
各开关的状态行文案:
- **开场动画**——关:「已关闭:启动时直接进入界面。」;开:「已开启:每次启动播放约 12 秒。」
- **探头的赫萝**——关:「已关闭:输入框边上不再出现探头。」;开:「已开启:鼠标移到输入框边上时她会探出头来。」
- **探头的她说话**——关:「已关闭:点她只有动作,没有声音。」;开:「已开启:点她会「疑惑」一声。」
- **黑夜模式**——开:「已开启:界面使用「星夜麦田」深色配色。」;关:「已关闭:界面使用「麦秋晨光」浅色配色。」
- **右侧待机动画**——「当前:赫萝开心(默认)。只在右侧栏的开始页显示;左右边缘渐变到 50% 不透明度。」;选 `关闭动画` 时:「已关闭:右侧栏的开始页不再显示待机动画,进入开始页时也不会加载任何视频。」
- **更改 logo 位置**——编辑中显示实时坐标;关闭时「已关闭:logo 停在原处,不会挡住任何点击。」
---
## 目录结构与素材
dsh-theme-holo/ ├── index.js 宿主半:注册三条只读文件路由 ├── client.js 浏览器半:配色、组件与全部交互 ├── cordis.patch.yml bundle 补丁:向 profile 插入一行宿主行 ├── package.json 清单(dsh.bundle / dsh.client / icon) ├── locale/ zh.json、en.json —— 插件列表里的标题与描述 ├── assets/ 主题自己渲染的 9 件素材 ├── docs/ 本 README 用的配图(不参与运行时) └── 素材/ 源料与联动画材(9 件运行时需要,其余是设计稿,不进仓库)
### `assets/` —— 主题自己渲染的图
| 文件 | 字节 | 尺寸 | 用途 |
|---|---:|---:|---|
| `avatar.jpg` | 11,700 | 160×160 | 档案卡的圆形头像 |
| `intro.jpg` | 85,210 | 1920×1080 | 片头底图,也是档案卡的横幅源 |
| `intro.mp3` | 241,004 | — | 片头音轨(12 秒) |
| `peek.png` | 316,083 | 575×560 | 偷看的她:兜帽抱面包,已抠成透明 |
| `peek-voice.wav` | 130,090 | — | 点她时的应答语音 |
| `logo.png` | 45,916 | 591×125 | 浮动字标,已裁掉透明留白 |
| `icon.jpg` | 4,516 | 64×64 | 插件列表图标。**不经过 HTTP 路由**,由 DSH 从清单 `icon` 字段读取 |
| `wheat.png` | 84,582 | 904×146 | 侧栏脚下的麦田,`素材/小麦.png` 镜像拼接成的无缝片 |
| `cover.webp` | 205,082 | 1920×620 | 插件详情页顶部的横幅 |
合计约 1.07 MB。改素材直接替换同名文件即可,**不需要任何构建步骤**。
### `docs/` —— 本 README 的配图