# 今汐·洄天溯海 — DSH 主题包
《鸣潮》今州令尹**今汐**的 DeepSeek Harness 主题。包含一个皮肤中心 v2 皮肤、
一份从 wuther.in 提取的完整今汐语料,以及一套可直接安装的「今汐」Agent 人格预设。
---
## 这个包是什么
| 组成 | 位置 | 说明 |
| --- | --- | --- |
| **皮肤("插件"本体)** | `skin/` | 符合 DSH 皮肤中心 **v2 皮肤契约**的纯资产目录。装进 `$DSH_HOME/skins/` 后在「设置 → 皮肤中心」试穿/应用,**无需重启** |
| **语料** | `corpus/` | 从 wuther.in 提取的**全部**今汐文本,9 份 Markdown + 2 份原始 JSON |
| **人格提示词** | `persona/` | 人读的完整人设文档 + 注入模型的纯文本 + Agent 预设元数据 |
| **安装器** | `install.ps1` / `uninstall.ps1` | 幂等、带安全标记校验、失败回滚 |
| **工具** | `tools/` | 抓取、校验、预览生成、预设合成,全部可复现 |
## 快速开始
```powershell
# 1) 只装皮肤
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\install.ps1
# 2) 皮肤 + 今汐人格预设
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\install.ps1 -WithPersona
# 3) 先看会做什么,不写任何文件
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\install.ps1 -DryRun -WithPersona
```
装完刷新浏览器页面,打开 **设置 → 皮肤中心**,列表里会出现「今汐·洄天溯海」。
先点**试穿**,满意再点**应用**。
卸载:
```powershell
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\uninstall.ps1
```
细节、排错与手工安装见 **[INSTALL.md](INSTALL.md)**。
---
## 皮肤长什么样
配色直接取自角色立绘取样:**银白 / 墨黑 / 金线 / 淡青 / 薄荷青**,不是凭印象调的。
| | 浅色「溯海晴光」 | 深色「洄天溯海」 |
| --- | --- | --- |
| 底 | 瓷白 `#F4F7FA` | 墨夜 `#0C0F13` |
| 主色 | 青金 `#39697A` | 冰蓝 `#8FC3D0` |
| 金线 | `#A8892B` | `#DFC078` |
| 薄荷 | `#4E8C7C` | `#7FBFAD` |
预览图在 `skin/preview/`(`light.jpg` / `dark.jpg`,1280×800)。
### 装饰层:没有一行 JS
皮肤中心提供 6 个固定装饰层,本皮肤全部用 CSS 填充:
| 层 | 内容 |
| --- | --- |
| `background` | 立绘 + 氛围渐变(底色、衍射微光、薄荷青光) |
| `ambient` | 缓慢漂移的衍射光晕 + 令尹印水印 |
| `top` | 金线 + 潮汐回纹带 |
| `bottom` | 金线 + 龙鳞纹带 + 居中衍射分隔纹 |
| `sidebar` | 左缘金缝 |
| `foreground` | 四角回纹 + 暗角 |
自绘纹样(`crest.svg` / `scales.svg` / `tide.svg` / `seal.svg` / `feather.svg`)
全部是单色 SVG,通过 CSS `mask-image` 上色,因此自动跟随亮/暗主题。
### 状态投影:也是纯 CSS
用 `:has()` 读取官方壳层**已有**的属性,不新增任何运行时:
| 条件 | 反应 |
| --- | --- |
| `[data-streaming]` | 顶线金光滑过,环境光晕加速 |
| `[data-chat-flow-kind="tool-call"]` | 左缘金缝转薄荷青并脉动 |
| `[data-chat-flow-kind="turn-error"]` | 上下金线转朱砂 |
| `html[data-dsh-conversation-content]` | 空对话时收敛水印 |
### 与皮肤中心滑杆协同
所有本该半透的底色都乘上 `var(--dsw-skin-scrim, 0)`,因此「背景遮蔽」滑杆真实生效。
底色在滑杆为 0 时**完全透明**,立绘才能透出来;可读性由 manifest 的 `backgroundMedia.scrim`
负责,而不是靠压暗整张底。
### Wallpaper Engine 适配
效果对照图:`we-adaptation.jpg`(用你自己的壁纸「鸣潮-今汐」渲染,遮蔽值取你当前的 50)。
**为什么必须专门写这段。** 读皮肤中心源码后确认的三个事实:
| 事实 | 出处 |
| --- | --- |
| WE 壁纸的**媒体层在 `z-index:-3`**,压暗遮罩在 `-2` | `wallpaper.ts` 顶部注释 |
| 本皮肤的 `background` 装饰层**同样在 `-2`** | `decoration-layers.ts` 的 `LAYER_STYLE` |
| 皮肤中心的中和样式只覆盖 `html` / `body` / `#root` / `[data-dsh-wallpaper-surface]` / `[data-composer-seat]::before`,**不覆盖 `[data-dsh-skin-layer]`**;而控制器抑制皮肤背景媒体时走 `clearLayer()`,**只清层的子节点**,层元素与其身上的 CSS 底色原样留着 | `wallpaper.ts` 的 `rootNeutralizer`、`skin-controller.ts` |
结论:皮肤若不自己让位,那层不透明底色会把用户挑的壁纸**整个盖住**——现象是「壁纸开了但没反应」。
所以 `patches.css` 里有一段专门的适配,做四件事:
1. **背景层由「底」改「纱」**:不再画自己的渐变,改为随遮蔽滑杆变浓的半透明纱幕。
它夹在壁纸(-3)与面板之间,是加纱的正确位置——直接设成 `transparent`
会把中栏正文晾在壁纸上。
2. **环境层收起衍射光晕**:那圈暖雾是专为自有底色打的,压在任意壁纸上会显脏。
3. **前景层加强暗角**:让不可控的壁纸内容与界面四边过渡干净。
4. **消息行与会话头补一层「笺纸」**:官方壳层的中栏根节点会被皮肤中心
强制透明(`[data-dsh-wallpaper-surface]`),纱幕之外还得给正文一张纸。
金线、龙鳞纹、四角回纹、令尹印一概保留——壁纸之下,这些才是皮肤还在场的证据。
**锚点为什么是 `body[...]` 而不是 `html[...]`。** 这是个非常容易踩、而且**不会报错**的坑:
加载器的作用域化只对 `:root` / `html` / `html[data-ds-…]` / `body…` 开头做特判,
`html[data-dsh-wallpaper-active]` 不匹配任何一条(注意是 `data-dsh-`,不是 `data-ds-`),
于是会被拼成 `html[data-dsh-skin="…"] html[data-dsh-wallpaper-active] …`——
要求 `html` 嵌套在 `html` 里,**永远匹配不到**。
`tools/validate-skin.mjs` 现在会复刻这套作用域化逻辑做预演,并带 `--self-test` 证明判死有效:
```sh
node tools/validate-skin.mjs --self-test
```
> 这个检查是被现实打出来的:本皮肤早先有一条
> `html[data-dsh-conversation-content="false"] …` 的死规则,
> 语法合法、lightningcss 照过、离线自检全绿,但线上从未生效。
> 顺带还查清该属性是「**存在即真**」——空对话时是被 `removeAttribute` 掉的,
> 不存在 `="false"`,所以正确的写法是 `body:not([data-dsh-conversation-content])`。
**可读性怎么调。** 「背景遮蔽」滑杆是唯一的旋钮,同时驱动纱幕与面板不透明度:
| 滑杆 | 纱幕 α(浅色) | 效果 |
| --- | --- | --- |
| 0 | 0.26 | 壁纸最清晰,正文靠「笺纸」卡片托住 |
| 50(默认) | 0.54 | 平衡 |
| 100 | 0.82 | 最实,接近不用壁纸 |
两种极端都验证过:正文均可读,不存在「拉到某处突然看不清」的悬崖。
壁纸自带的「压暗」设置仍然独立生效,两者叠加即可。
---
## 身份层:今汐的头像与「飞讯」对话形态
配色和装饰只是皮,还缺一层**身份**——左上角是不是今汐、消息旁边有没有她的脸。
这两件事只靠 CSS 做,锚点全部来自**官方 slot 出口**。
### 关键发现:slot 出口自带 `data-slot`
皮肤中心依赖的 renderer 里,每个 slot 都渲染成:
```jsx
function SlotOutlet({ slotKey, … }) {
return
…
}
```
于是 `[data-slot="sidebar.brand.mark"]`、`[data-slot="sidebar.brand.name"]`、
`[data-slot="conversation.hero.brand.mark"]` 都是**官方稳定锚点**,
不依赖任何 CSS-Modules 哈希类名,官方重建样式也不会失效。
`display:contents` 的元素自身不生成盒子(所以不能在它身上加 `background` / `::before`),
但它的**子元素照常参与 DOM 组合器**——因此一律走 `> svg` / `> span` 这类结构化子选择器。
### 做了什么
| 位置 | 改动 | 选择器 |
| --- | --- | --- |
| 侧栏左上角品牌标记 | 灰鲸鱼 → 今汐圆形头像(金环 + 青玉光晕) | `[data-slot="sidebar.brand.mark"] > svg` |
| 侧栏左上角品牌名 | `DeepSeek Harness` → **今汐** | `span:has(> [data-slot="sidebar.brand.name"])::after` |
| 空对话欢迎区 | 同一个头像,放大到 56px | `[data-slot="conversation.hero.brand.mark"] > svg` |
| 新对话页抬头 | `探索未至之境` → **桃夭灼灼牵丝动**;`预览版` → **漂泊者** | 见下 |
| 每一条助手消息 | 左侧圆形今汐头像 | `[data-chat-flow-kind="assistant-step"]::before` |
### 改文案怎么改(CSS 改不了文本)
CSS 不能改文本内容,所以走标准做法:**把原文案 `font-size` 归零藏起来,再用 `::after` 注入新文案**。
原文仍留在 DOM 里(对读屏与复制友好),行高与内边距都继承原值,布局完全不变。
难点在**定位**。`ui-conversation` 的 `HeroShell`(14619-14649 行)结构是:
```
div.headline
├─ span.fishHitbox > div[data-slot="conversation.hero.brand.mark"]
└─ span.titleGroup > [ 探索未至之境,
预览版 ]
```
这一带**只有 `conversation.hero.brand.mark` 一个 slot 出口**可作稳定锚点,
标题组是它的**下一个兄弟**,所以用:
```css
span:has(> [data-slot="conversation.hero.brand.mark"]) + span > span:not([class])::after { content: "桃夭灼灼牵丝动"; }
span:has(> [data-slot="conversation.hero.brand.mark"]) + span > span[class]::after { content: "漂泊者"; }
```
区分的技巧:标题那个 `` **没有 class**(JSX 里只传了 children),徽标那个**有 class**。
用 `:not([class])` / `[class]` 比 `:first-child` / `:last-child` 稳——
万一将来只渲染一个,前者不会产生歧义。
抬头悬在立绘或壁纸之上、背景明暗不可控,所以标题加了一圈同色系柔光作衬,
徽标底色取近乎实心的 `--jinhsi-porcelain-hi`(半透明 chip 会直接糊掉)。
字号 26px 属「大字」,WCAG AA 门槛为 3:1,自检里已按 3:1 纳入。
> **踩过的坑**:本机 profile 启用了官方品牌插件 `ui-brand-official`,
> 它注册了 `sidebar.brand.mark` / `sidebar.brand.name` 两个 slot,
> 所以**两个 fallback 分支都不渲染**——实际 DOM 里名字是 BrandWordmark 的
> **156×24 `