dsh-ui-web/packages/skins/skin-center

34Star2Fork0Issue0Watching

DSH 网页端的皮肤中心:在 GUI 设置页里即时试穿、一键应用皮肤,热载入无需重启。

语言
TypeScript
License
Apache-2.0
分支
main
dsh-plugindsh-plugin-marketdsh-plugins

安装

$ dsh plugin --profile web add github:CAPTAIN1275/dsh-ui-web/packages/skins/skin-center

在终端中运行以上命令,通过 dsh CLI 安装此插件。可在右上角切换 Profile。 第一次用 dsh?看这篇新手教程

一句话定位

DSH 网页端的皮肤中心,把皮肤列表、试穿、一键应用内嵌进 GUI 的"插件配置 → Web UI 插件"卡片组。它通过 host 半区路由写入 ~/.dsh/cordis.patch.yml,DSH 配置 watcher 自动热载入,无需重启。

核心能力

  • 在 GUI 设置页里集中浏览皮肤:"官方默认"+ 已安装的全部皮肤(aurora / ths / xp / blue-fantasy / dragon-heir / minecraft / whale-song / trading / miku),每条带名称、tagline、强调色色卡,当前激活目标带 Active 标记。
  • 真实试穿:点击"试穿"按需加载皮肤的 lib/client.js,走页面自己的模块加载器(<script> 同源注入 + __ModuleLoader__.load),不依赖 eval;亮/暗主题切换走官方 theme 服务,"退出试穿"完整还原激活皮肤的状态。
  • 一键应用:host 路由 /api/skin-center/apply 执行与 dsh-skin use 等价的内嵌实现,写入 ~/.dsh/cordis.patch.ymldsh-skin managed 段,DSH 配置 watcher 秒级热载入后浏览器自动刷新页面,无需重启official 目标会把所有皮肤禁用,回到 DSH 官方原貌。
  • 互斥保险:试穿期间按配方暂时撤掉当前激活皮肤的视觉写面(body 属性、背景内联样式、chrome 子节点),退出后按原顺序、原快照原样恢复;同一时刻页面上永远只有一套皮肤生效。
  • 背景遮挡:卡片内置 0–100 的背景遮罩滑块,把数值写到 --dsw-skin-scrim CSS 变量;只对会画背景插画的皮肤(蓝色幻想 / 鲸吟)即时生效,官方默认无背景图时滑块保持持久但视觉无变化。
  • 跨站围栏:host 路由对 Sec-Fetch-Site=cross-site 与 Origin/Host 不匹配直接 403,仅同源 fetch 可调用 /apply/state,防止恶意网页通过 localhost CSRF 改写用户皮肤配置。

技术实现

  • 语言: TypeScript + React 18(jsx: react-jsx,target es2024)
  • 关键依赖: @deepseek-ai/cordis(host 半区运行时)、@deepseek-ai/dsh-client-runtime / dsh-client-ui-theme / dsh-client-ui-settings / dsh-client-ui-slots / dsh-client-locale(client 半区 SDK)、schemastery(设置命名空间 Schema 定义)
  • 架构模式: cordis 双半区 bundle。src/index.ts 是 host 半区(cordis plugin ui-skin-center,注册 /api/skin-center/{state,apply,bundle/<id>} 路由并声明 skin-background 设置命名空间);src/client/index.ts 是 browser 半区(DSH 客户端插件,挂载到 web-ui.plugin.item 槽位下的"Skin Center"卡片);皮肤切换的 host 实现 src/skin-switch.tsscripts/dsh-skin CLI 的 1:1 ESM 端口——不再依赖 dsh-skin 二进制在 PATH。
  • 入口文件: src/index.ts(host apply)+ src/client/index.ts(browser apply);cordis patch 在 cordis.patch.yml 中插入 id ui-skin-center

适用场景

觉得在终端里敲 dsh-skin use <name> 再等刷新太繁琐、想在 GUI 里直接看到皮肤实际效果再决定是否切换的人:卡片支持在真实 GUI 上"试穿 → 退出还原 → 一键应用"的完整流程,亮/暗主题在试穿时也可即时切换预览。想要在画插画背景的皮肤上加一层遮罩让面板更突出的人:卡片内置的 0–100 背景遮挡滑块只对画背景的皮肤生效。装了多个皮肤、希望把整套皮肤切换入口集中到一个地方的人:本插件同时承担"加载器 + 渲染器"职责,注册表由 packages/skins/<id>/skin.json 自动扫描生成,新皮肤加入即出现在卡片列表里。

前置依赖与兼容性

依赖最低版本说明
@deepseek-ai/dsh-*^0.1.0-rc.6host / client 半区都通过 SDK 注入服务;皮肤中心把对官方 DSH 的耦合吸收在内
React^18.2.0peerDependency,SkinCenter.tsx 使用 React hooks
Node^22.19 || >=24仓库根 AGENTS.md 强制要求;package.json 未声明 engines,但 host 侧使用 node:fs / node:os / node:path
平台跨平台macOS / Windows / Linux 全部支持;Windows 用目录 junction 兜底无权限场景,DSH 主目录优先 $DSH_HOME,仓库根从脚本位置推导
原生模块无 native binding 依赖;样式由皮肤自带 CSS 提供,lightningcss 仅在仓库根 devDependencies

安装方式

dsh plugin --profile web add github:CAPTAIN1275/dsh-ui-web/packages/skins/skin-center

配置项

配置类型说明默认值
skin-background.backgroundOpacity0–100主页背景遮罩强度;写入 body CSS 变量 --dsw-skin-scrim,仅对画背景插画的皮肤(蓝色幻想 / 鲸吟)即时生效,官方默认无背景图时视觉无变化但值仍持久化0

常见问题

Q: 一键应用皮肤后需要重启 DSH 吗?

A: 不需要。点击"应用"或"恢复默认"后,host 端把目标皮肤写入 ~/.dsh/cordis.patch.ymldsh-skin managed 段,DSH 自带的配置 watcher 在数秒内热载入新补丁,浏览器自动刷新页面即生效。

Q: 试穿会改动当前激活的皮肤吗?

A: 不会。试穿仅在浏览器侧临时收回当前皮肤的 body 属性、背景内联样式和 chrome 子节点,"退出试穿"会按原快照、原顺序原样恢复;激活皮肤自身的 cordis 纤维全程不被触碰,状态完全保留。

Q: 皮肤还没构建 lib/client.js 时试穿会怎样?

A: 试穿会报错并自动还原激活皮肤。host 路由 /api/skin-center/bundle/<id> 在目标 lib/client.js 不存在时返回 404,前端 catch 后调用 controller 的还原分支,绝不会留下半套皮肤。

Q: Windows 上能用吗?

A: 可以。皮肤切换在 Windows 下用目录 junction(绝对路径)创建 profile node_modules 链接,不需要开发者模式或管理员权限;DSH 主目录优先读 $DSH_HOME,缺省 ~/.dsh,仓库路径由脚本自身位置推导,不依赖 $HOME 或固定路径。

Q: 跨站请求会被拒绝吗?

A: 会。host 路由对 Sec-Fetch-Site=cross-site 直接返回 403,OriginHost 不匹配同样拒绝,仅同源 fetch 可调用 /api/skin-center/apply/state,防止恶意网页通过 localhost CSRF 改写用户的皮肤配置。

Q: 怎么添加一款新皮肤到列表里?

A: 在 packages/skins/<id>/ 放一份 skin.json(id 匹配 [a-z0-9-]+packagewiring.id 必填),再为该皮肤构建 lib/client.js。注册表由脚本扫描 skin.json 自动生成,无需修改皮肤中心代码;重建 generated/skins.ts 即可在卡片里看到新条目。

Q: 背景遮挡滑块有什么用?

A: 它控制主页背景的遮罩强度(0–100,默认 0),把数值写到 document.body--dsw-skin-scrim CSS 变量上。仅对会画背景插画的皮肤(蓝色幻想 / 鲸吟)即时生效,官方默认无背景图时该滑块不产生视觉变化,但值仍会被持久化到下一次切到画背景的皮肤。

Q: 官方默认也能"试穿"吗?

A: 可以。点击官方默认卡片上的"试穿"会触发同一套收回配方,临时撤掉当前皮肤的视觉写面,但不挂载任何皮肤,页面立即回到 DSH 官方原貌;点击"退出试穿"则恢复激活皮肤。

上手难度

入门 — 装好后打开"插件配置 → Web UI 插件 → 皮肤中心"就能看到所有已安装皮肤,"试穿"无需任何额外配置,"应用"一键完成且自动刷新;仅当要加自定义皮肤或调试 Windows 权限问题时才需要进一步了解。

已知问题与限制

  • host 侧 wiredNames() 暂时只读 skin.jsonwiring.bundleWired 字段判定"bundle 层已注入";若皮肤是通过 active profile 的 dsh.profile.bundles 注入,源码注释里标了 TODO,暂未实现对应探测(src/skin-switch.ts:245-247)。
  • 一键应用对未正确解析的皮肤是硬门槛:/api/skin-center/apply 会先调用 checkResolvable 校验目标 profile 中是否存在带 package.json 与 host 入口的目录,缺一即抛错;这意味着 npm 聚合包里的皮肤目录如果没带可解析包元数据,/apply 会拒绝并报中文提示,让用户先 dsh-skin install 安装(src/skin-switch.ts:527-552)。
  • 试穿过程按"快照当前激活皮肤 → 临时收回 → 加载新皮肤"的顺序进行;若加载中用户连续触发多次切换,前一个 in-flight 请求会被 epoch 机制识别为被超越,只清理自己挂载的资源、不会回退整张页面(src/client/try-on.ts:208-231)。
  • profile node_modules 链接创建需要创建权限;Windows 上没有开发者模式时自动 fallback 到目录 junction,但仍可能因组策略受限失败,错误信息会被 symlinkFriendly 包装成中文提示(src/skin-switch.ts:496-506)。