genshin-paimon-theme — 原神风格 DSH Harness 插件
把 DeepSeek Harness Web 界面改成原神风格:
1. 随机地理志壁纸背景 — 每次打开/刷新页面随机一张(按国家分目录,蒙德→至冬全覆盖);
2. 原神配色 + 字体 — 暖金/陶土色调 token 覆盖(!important 压过主题 Presenter),全站字体统一用原神中文界面字体 zh-cn.ttf(@font-face 名 GenshinFont);
3. 双人格,默认法涅斯 — 系统提示词写 deployment:persona 槽。默认是法涅斯(最原本的 DSH 体验),点右下角头像或菜单里的「模式切换」可一键换成派蒙(贪吃的小向导)并即时生效;切换状态在宿主进程内是全局的,重启后回到默认值。
文件结构
theme/
├── genshin-paimon/
│ ├── package.json # type: module(ESM)
│ ├── index.js # 插件本体(webServer 路由 + tapIndex 注入 + systemPrompt section)
│ └── README.md
└── genshin-art/
├── zh-cn.ttf # 原神中文界面字体,33,554 字形 / 11.04 MB(唯一的字体,全站使用)
└── nations/ # 地理志壁纸,按国家分子目录
├── mondstadt/ liyue/ inazuma/ sumeru/
├── fontaine/ natlan/ snezhnaya/ teyvat/
> 曾经存在的 font.woff2(得意黑 Smiley Sans Oblique)从未被插件引用,已于 1.1.2 删除,
> 备份在 .claude/work/genshin-art/font.woff2。全部字体统一走 zh-cn.ttf。
启用(需重启 DSH)
- 关闭当前 DSH:在启动它的 PowerShell 窗口按
Ctrl+C(或直接关窗)。 - 重新双击桌面 「DSH 启动」 快捷方式。
- 打开 http://127.0.0.1:3080 —— 背景就是随机的原神地理志风景图;问它「你是谁」会得到派蒙式回答。
验证
http://127.0.0.1:3080/genshin-bg/random应返回一张 JPEG(而非网页 HTML)。- 插件加载日志:DSH 启动窗口会打印
原神主题插件已激活。
卸载
编辑 C:\Users\Δ\.dsh\profiles\web\cordis.patch.yml,删除 - insert: 到 genshin-paimon-theme 那一段,重启即可。
自定义
- 换图:往
genshin-art/nations/<国家>/增删 jpg/png/webp 即可。清单有 60 秒 TTL,最多等 60 秒自动生效;
想立刻看到就用fetch('/genshin-bg/random?fresh=1')强制重扫(不用重启 DSH)。 - 调色:改
index.js里THEME_STYLE的--dsw-alias-*token(亮色值可参考dsh-client-ui-theme/lib/styles/design-platform.css的--dsw-static-*)。 - 字体:替换
genshin-art/zh-cn.ttf(@font-face 名字GenshinFont不用改)。全站只有这一个字体文件。
与其它插件共存(1.1.0 起的逃生开关)
默认观感没有变化,几处"可能与其它插件互相干扰"的行为都留了开关。全部可以在浏览器控制台调用,开关值存在 localStorage,刷新后依然生效。
__genshinTheme.version // '1.1.5'
__genshinTheme.hideFloating(true) // 隐藏右下角派蒙头像 + 左上角菜单按钮(Ctrl+Shift+H 切回来)
__genshinTheme.resetFloat() // 清掉拖动记忆,浮动按钮回到默认贴角位置
__genshinTheme.typography(false) // 关闭侧栏字号放大
__genshinTheme.skip(el) // 把 el 子树排除出主题排版规则(插件作者用)
快捷键(1.1.5 补齐)
| 组合 | 作用 |
|---|---|
Ctrl+Shift+H |
隐藏 / 恢复浮动按钮 |
Ctrl+Shift+B |
换一张壁纸 |
Ctrl+Shift+M |
切换 派蒙 / 法涅斯 |
Ctrl+Shift+/ |
展开 / 收起派蒙菜单 |
匹配以 ev.code 为主、ev.key 兜底,中文输入法或非美式布局下也一样有效;在输入框里照常可用(这几组不会产生字符),只在输入法组合状态(ev.isComposing)时让键。
也可以在「派蒙菜单」里直接点:隐藏浮动按钮 / 派蒙头像归位。
手动写法(等价):
| 目的 | 写法 |
|---|---|
| 隐藏浮动控件 | localStorage['genshin-hide-float']='1' |
| 浮动控件归位 | 删掉 localStorage['genshin-av-pos'] / ['genshin-menu-btn-pos'] 后刷新 |
| 关闭字号放大 | localStorage['genshin-typo-off']='1' |
| 全局关闭排版覆盖 | 或 |
亮色是唯一形态(1.1.3 起):主题不再提供"放开宿主深色模式"的开关,永远清除宿主的深色标记。宿主 0.1.5 施加深色的方式只有两处(dsh-client-ui-theme/lib/index.js):
document.documentElement.style.colorScheme = dark ? 'dark' : 'light'
document.body.toggleAttribute('data-ds-dark-theme', dark)
前者由样式表里的 html{color-scheme:light !important} 加内联兜底压住,后者由一个 MutationObserver 盯着 documentElement 子树实时清掉。另外 ~/.dsh/settings.yaml 的 ui-theme.preference 也建议设成 light(本机已设),从源头就不再解析出深色,省掉启动瞬间的深色闪烁。旧版本留下的 genshin-dark-ok / data-genshin-dark-ok 会在加载时自动清理。
层级:浮动控件原来用 z-index: 2147483000+,而宿主前端实测最大只有 1100(弹窗/遮罩 1000),所以主题控件会压在弹窗之上并抢走点击。1.1.0 收进宿主刻度:金框 890 < 浮动控件 900 < 菜单面板 950 < 弹窗 1000/1100 < 提示条 1200。
浮动控件可拖动:按住右下角派蒙头像或左上角 logo 拖动即可改位置,位置记在 localStorage(genshin-av-pos / genshin-menu-btn-pos),用来避开侧边栏类插件(better-sidebar / task-board / skill-explorer 等)占用的区域。
1.1.1 修「派蒙头像跑到左下角偏中间」:旧版有两个坑——① 只是点一下头像切模式,pointerdown 也会把 CSS 的 right/bottom 贴角改写成当时的绝对像素,之后把窗口拉大/最大化,它还留在旧视口的右下角,在新视口里就落到了"下方偏左";② 记下 {x,y} 却不记视口尺寸,换窗口大小后照旧生效。现在:未发生真实拖动时贴角属性原样还回去;拖动记录带上视口宽高,尺寸变化超过 80px 或坐标越界即判定失效并自动归位;窗口 resize 时复查一次;POS_VER 递增会一次性丢弃所有旧坐标(本次改动即借此把已跑偏的头像拉回右下角)。菜单里的派蒙头像归位等价于手动改回来。
已知未改动的一处:主题会占用系统提示词的 deployment:persona 槽(派蒙/法涅斯人格)。另一个也想写 persona 的插件会和它互相顶掉——这是主题的核心功能,没有替你决定要不要让位。
1.1.2 健壮性加固(布局零改动)
这一版没有动任何 CSS、也没有动 buildUI 的 DOM 结构——浮动控件的位置、尺寸、层级、菜单项全部与 1.1.1 一致。只改了出错时的行为:
| 改动 | 旧行为 | 新行为 |
|---|---|---|
| 壁纸清单缓存 | 一次性缓存死,加新图只能重启 DSH | 60 秒 TTL 自动重扫;?fresh=1 可强制立刻重扫 |
| 扫描失败 | 先把缓存写成 [] 再扫,目录读不到就永久没壁纸 |
扫描失败保留旧清单,不覆盖 |
| 单张图损坏/被占用 | 整条路由 500,页面没背景 | 换一张重试(最多 3 次) |
POST /genshin-bg/mode 入参 |
非法 mode / 坏 JSON 被静默当成 paimon,能悄悄改掉全局人格 |
返回 400,不再改动状态 |
| persona 槽被占用 | 两次 section 都失败就把插件加载整个带崩 |
记一条警告,本次不生效,插件继续运行 |
| 注入判重 | 裸串 genshin-theme,别的插件同名字样会让主题被静默跳过 |
只看自己的 id="genshin-theme" |
buildUI |
无幂等保护 | 已存在按钮就直接返回,重复注入不会多出一套控件 |
| 拖动 | 可以拖到视口外,按钮再也点不到 | 至少保留 24px 可见 |
| 异常 | 两处通篇 catch {} |
宿主与前端各有一个 warn 出口,写控制台不改 DOM |
宿主侧日志前缀 [genshin-paimon],前端控制台前缀 [genshin]。
1.1.3 三项行为调整
| 项 | 旧 | 新 |
|---|---|---|
| 深色模式 | 有「放开宿主深色模式」开关(allowDark + genshin-dark-ok),可放行宿主深色 |
开关与 API 全部删除,只有亮色;遗留标记自动清理 |
| 隐藏浮动按钮 | 隐藏后不再创建 DOM,hideFloating(false) 只能改属性、没有元素可显示 —— 只能刷新页面才回得来 |
DOM 永远构建,隐藏只由 html[data-genshin-hide-float] 的显示规则控制;hideFloating(false) 当场生效,另有 Ctrl+Shift+H 随时切显示/隐藏 |
| 默认人格 | 派蒙 | 法涅斯(最原本的 DSH 使用体验);派蒙仍在菜单「模式切换」里一点即回 |
默认模式由 index.js 里 let currentMode = 'phanes' 与前端 var mode = "phanes" 两处共同决定,
改回派蒙默认就把这两处换成 'paimon' / "paimon"。
1.1.4 一次性复位:1.1.3 之前的「隐藏浮动按钮」是死路,只要点过一次,localStorage['genshin-hide-float']
就被置为 '1' 并一直把按钮挡住,而且当时刷新也救不回来——这正是"按钮不见了"的成因。1.1.4 用一个
只跑一次的迁移(localStorage['genshin-state-ver'] 落地后永不重复)清掉这个卡住的状态,
刷新页面后左上角 logo 与右下角头像必定回来。复位之后你如果再主动隐藏,状态依然会被尊重,
并且随时可以用 Ctrl+Shift+H 或 __genshinTheme.hideFloating(false) 立刻切回来。
1.1.5 观感与状态
壁纸交叉淡入淡出
换图不再是硬切:新图先挂到隐藏层、解码完成后才翻 opacity,两层各 0.55s 交叉过去。
为此背景搬了家——这三层都是 position:fixed; inset:0; z-index:-1,仍然在所有内容之下:
| 层 | 作用 |
|---|---|
#genshin-bg-a / #genshin-bg-b |
交替承载图片,交叉淡入 |
#genshin-bg-veil |
常驻柔光渐变(色值与原 body 规则逐字一致) |
这是 1.1.5 唯一一处结构性改动:原来挂在 body 上的 background(渐变 + 图片)被移除,
改由上面三层承担。之所以必须搬走,是因为负 z-index 层永远画在 body 背景之下,
不搬就无法让新旧两张图同时可见。稳态合成结果与改动前相同(html 底色 → 图片 → 柔光 → 内容);
--genshin-bg 变量仍在维护,供其它插件读取当前壁纸。另外 prefers-reduced-motion: reduce 时过渡自动降到 0.01s。
注入脚本跑在 ,那一刻还没有 `body`:图层会推迟到 `buildUI`(DOMContentLoaded)再建,
但**图片预取在 就开始了**(几张图都是 6~11 MB,不浪费这段空档),所以首屏出现壁纸的时机没有变慢。
右下角元素环 = 运行状态指示
七个元素 SVG 从纯装饰变成状态灯,状态写在 #genshin-avatar-btn[data-genshin-state] 上:
| 状态 | 含义 | 表现 |
|---|---|---|
idle |
空闲 | 维持原样(静态、opacity 0.85) |
busy |
有会话在跑(思考 / 调工具) | 七元素逐个点亮,周期 2.1s |
writing |
正在吐字 | 同款追逐但周期 0.9s,并加一圈金色光晕 |
error |
刚刚失败(2.2s 内) | 整圈转红 |
状态来源是宿主 /genshin-bg/status,浏览器每 1s 轮询一次(document.hidden 时跳过,切回前台补一次)。
宿主侧只订阅两个 payload 全是基本类型的全局事件:api-session/status(sessionId, running) 与
api-session/error(sessionId, message);再加 agent/assistant-stream 作为"正在吐字"的可选心跳
(它是 scope-filtered 派发,实测全局监听收得到;万一收不到也只是退化成 busy 的慢速环)。
动效只碰 opacity / filter / box-shadow,绝不动 transform——七个元素的位置由内联 transform
决定,一旦在动画里写 transform 就会把它们全叠到圆心,这一点在改这块时务必留意。
因此没有任何布局影响,元素环依旧只在右下角。
调试用:fetch('/genshin-bg/status') 直接看 {state, sessions, streamAgo, errorAgo},
其中 streamAgo/errorAgo 是距上次事件的毫秒数(-1 表示从未发生)。
> 已知边界:状态按"任意会话在跑"判定,不区分浏览器当前看的是哪个会话;插件热重载后
> 运行中的会话集合会清空,要等下一次状态变化才重新跟踪。
素材来源
- 壁纸:米游社「地理志/观景点」系列帖(官方游戏实景美术,托管于
upload-bbs.miyoushe.com),
含按国家的 4K 壁纸帖与各版本「新增#地理志-观景点#」帖(4.6→7.0),已按国家归档。 - 字体:
zh-cn.ttf—— 原神中文界面字体,33,554 字形 / 11.04 MB。
(该字体的name表是混淆过的,读不出 family/license 原文,这是字体方自己的处理;不要指望用它反查授权信息。)
曾放在同目录的font.woff2(得意黑 Smiley Sans Oblique)从未被插件引用,1.1.2 已删除,
备份在.claude/work/genshin-art/font.woff2。