WorkBuddy 皮肤制作
为 WorkBuddy 客户端制作自定义外观(皮肤)。产出可直接导入的皮肤包,或直接安装到本地。
> 被 / 引用后的第一件事:输出「阶段 0 开场引导语」,主动告诉用户需要准备
> 外观配色与右侧输入框旁的伙伴造型,再按缺项补问。别沉默跑脚本,也别
> 一上来就弹四个提问框。
核心事实(源码实测,务必遵守)
-
皮肤目录名由云端记录决定,不能自造。
客户端computeDirName(theme) = \${resourceKey}-${updatedAt}`, 即~/.workbuddy/appearance-resources/theme-tkbdzr-1788858700000/` 这种形态。
⇒ 手工新建目录永远不会被命中,别做。 -
真正生效的是根级
skin.css(不是嵌套目录里那份,那份只是 zip 解压残留)。 -
客户端读盘逻辑:
<dirName>/skin.css存在 → 直接当缓存用,不下载、不校验签名。
不存在才去下载theme.zipUrl的 CDN zip。
⇒ 改本地文件机制上有效,但必须走「切主题」触发(见第 5 条)。 -
★ 改完文件必须切主题才生效。启动时渲染层用 LocalStorage 的 CSS 缓存
(workbuddy.appearance.lastApplied.css)渲染,随后判定「already applied」直接跳过,
getLocalResource压根不调用。只重启不会生效。
生效操作:客户端「外观」→ 切到另一个主题 → 再切回目标主题。 -
素材 url() 必须写绝对路径。客户端只在「下载 zip」分支做 URL 重写;
走读盘缓存分支不重写。本地 patch 时脚本会自动把相对路径改写成
local-file:///C%3A/...绝对形式。 -
素材白名单(写错会让整个主题加载失败):
.css .png .jpg .jpeg .gif .webp .svg .woff .woff2 .ttf .otf .mp4 .webm .mov .m4v .ogv .mp3 .m4a .wav .ogg -
LRU 上限 8:
appearance-resources里目录数超过 8 个,客户端会按 mtime
从旧到新删掉多余的。别在里面堆垃圾目录。 -
皮肤本质:
skin.css用!important覆盖客户端 CSS 变量,主要是--wb-*(约 559 个)、
--cb-*(62 个)、--cr-*(51 个),加上少量针对硬编码色的靶向补丁层。全套约 1400 行。 -
不要从零手写 CSS。走「模板 + 色值重映射」——模板
assets/template/base-skin.css
已有完整结构,只替换色值,保证 583 个变量一个不漏。详见references/skin-mechanism.md。 -
两条铁律:
- 保留 alpha:
#001f4fe6这类带透明度的色值承载文字层级,只换 RGB、保留 alpha。 - 语义色不动:红/金/微信绿/QQ 蓝/GitHub/Slack/Figma/Google 品牌色原样保留。
- 保留 alpha:
-
★ 客户端没有 zip 导入入口(2026-09-14 源码三重确认)。UI 组件
ThemeCards
只有主题卡片(点击=选中);settings.appearance.*的 30 个 i18n key 里零个
import/upload;appearance:IPC 通道仅 5 个(getLocalResource / getSelections /
listCatalog / listResources / setSelection)。外观面板是云端主题商城,主题带
vipLevel/validity/series,用户只能选不能传。
⇒ 交付只有 patch 一条路。想要多套皮肤 → 多槽位替换法(见阶段 4)。
另:皮肤头注释里的WB Imported Skin字样在客户端代码中搜不到,是皮肤作者
留下的文案,不代表存在导入功能,别被它误导。
流程总览
用户 `/` 引用本 skill
↓
阶段 0 开场引导 ← 主动说「要准备什么」:① 配色 ② 伙伴造型(+底图/视频)
↓ 然后停下等回复
阶段 0b 环境自检 ← 静默做,不打扰用户(目录存在?Python 路径对?)
↓
阶段 1 补缺提问 ← 只问空着的项;信息给全则一步跨过
↓
阶段 2 build_skin.py → 生成皮肤包
↓
阶段 3 preview.py → 预览 HTML → 用户确认(不满意回阶段 2)
↓
阶段 4 install_skin.py --mode patch → 打到槽位
↓
阶段 5 备份 / 回滚支持
↓
阶段 6 交付「用户使用守则」+★提醒切主题(不切不生效)
唯一的用户阻塞点:阶段 0 等用户回复。其余阶段一路做完再汇报。
阶段 0:开场引导(用户 / 引用本 skill 后的第一句话)
用户一引用本 skill,立刻主动说话——不要沉默地去跑脚本,也不要只回一句
「好的,请问你想要什么风格?」。用户此刻并不知道这个 skill 要他准备什么,
引导语的任务就是把「要准备什么」一次讲清。
引导语模板(照此措辞输出,语气可微调,结构不要改)
> 我来给你做一套 WorkBuddy 皮肤。给我这两样就能开工:
>
> ① 外观配色(三选一)
> - 直接给色值,例:#7C5CFF
> - 丢一张参考图给我,我从图里提色
> - 说「你定」,我出 7 套配色对比页给你挑
>
> ② 右侧输入框旁的伙伴造型(就是右下角那个小人/小动物)—— 要不要换?
> - 有图 → 直接上传(透明底 png 最好),我按槽位规范裁好
> - 没图 → 说个风格,例:「赛博朋克猫娘」「国风仙鹤」,我用 AI 生成
> - 不换 → 保持客户端原样,界面其余部分照常换肤
>
> 可选(有就给,没有我按风格补)
> - 首页底图
> - 背景视频(mp4,≤3MB)
>
> 一句话说全也行,比如:「主色 #FF7A45,伙伴换一只橘猫,底图要,视频不要」
懒人填法(跟引导语一起附上,减轻用户负担):
主色: (色值 / 参考图 / 你定)
伙伴造型: (上传图 / 描述风格 / 不换)
首页底图: (有 / 没有)
背景视频: (有 / 没有)
四条纪律
- 必须先说话。用户引用 skill 的这一刻,他的预期是「你来告诉我怎么用」。
- 不要只问一句「你想要什么风格」——用户不知道要准备图、也不知道伙伴能换。
- 不要紧接着弹
AskUserQuestion。引导语已经覆盖全部要素,提问框留给补缺。 - 怎么触发都要先出引导——
/workbuddy-skin-maker显式引用、或自然语言
触发(「帮我换套皮肤」「给客户端改个配色」),一视同仁。
用户只说了「帮我换皮肤」这类极简指令时
先输出上面整段引导语,然后停下等回复。不要紧接着用 AskUserQuestion
连轰四个问题——引导语已经把要素讲清了,提问框留给「补缺」(见阶段 1)。
用户只上传了图片、没写字时
主动识别用途并向用户复述确认,别猜错:
- 像是设计稿 / 截图 / 风景照 → 大概率是 ① 取色参考图
- 像是人物 / 动物立绘(主体居中、背景干净)→ 大概率是 ② 伙伴图
- 复述方式:「这张我按参考图提色用,对吧?」或「这张是伙伴立绘,对吧?」
收到答复后的分流
| 情况 | 动作 |
|---|---|
| ①②(含可选项)都给全了 | 直接进阶段 2,不要再逐项复述确认 |
| 有缺项 | 只对缺的项走阶段 1 的 AskUserQuestion,一次最多 4 问 |
| 用户说「你定」 | 跑 build_gallery.py 出配色画廊,让用户挑编号,同时按缺省处理素材 |
| 用户答「不换伙伴」 | 记下,assets 不带 buddy,模板里伙伴引用会被自动清理 |
阶段 0b:环境自检(静默执行,不打扰用户)
确认以下路径存在(Windows):
C:\Users\<用户名>\.workbuddy\appearance-resources\
不存在则说明未装 WorkBuddy 客户端,向用户说明后终止。
脚本用托管 Python:
C:\Users\<你的用户名>\.workbuddy\binaries\python\versions\3.13.12\python.exe
阶段 1:询问风格偏好(补缺,不可跳过)
先对照阶段 0 的答复,把已明确的字段划掉,只对空着的项提问(一次最多 4 问),
不得自行假设。实践中真正需要补的通常只有一项:配色基调(用户给了主色但没说
浅色还是深色)。交付槽位由阶段 4 自动识别,不占开场提问。
下面四组问题的完整内容,仅对缺项使用:
- 主色:给个色值或参考图。选项:
- 直接给色值(如#7C5CFF)
- 上传参考图(我提色)
- 从预置方案里选(见references/palette-presets.md,7 套)
- 你定 → 我跑build_gallery.py出 7 套方案对比页,用户挑完再继续
用户说「你定」或「给我几套选」时:
bash
python scripts/build_gallery.py --out <输出目录>/gallery.html
用 present_files 打开,让用户按名称/编号选定。
-
配色基调:浅色 / 深色 / 跟随主色自动推导。
-
素材需求(可多选,逐项问):
- 首页底图 —— 要不要?有图就提供,没有我按风格生成(2560×1600 jpg)
- 伙伴立绘(界面右下角那个角色图)—— 要不要换?
这个问题必须单独问清,且要说明代价:> 换伙伴形象需要你提供人物图片(透明底 png 最好)。
> 没有现成图的话我可以用 AI 生成一版,或者保持原样不动。三种答复分别处理:
- 要换 + 给图 → 图存本地后跑prepare_buddy.py规范化(见下)
- 要换 + 没图 → 询问风格后用 AI 生成一张,再规范化
- 不换 →assets里不带buddy,模板的伙伴引用会被自动清理,
界面右下角保持客户端原样
- 背景视频 —— 要不要?mp4,体积需 ≤3MB
伙伴立绘的处理(拿到图后必做):
bash
python scripts/prepare_buddy.py <用户图片> --out <工作目录>/buddy.png --preview <工作目录>/buddy-preview.png
把生成的预览图给用户看一眼,确认落位没问题再进阶段 2。
详细规范见 references/asset-spec.md 的伙伴立绘章节。
多姿势(用户要「不同姿势 / 全身 / 换个动作」时):
- 设定稿通常只有站姿,别的姿势只能图生图重做(裁剪变不出来)。
流程、prompt 模板、抠底坑见references/buddy-poses.md。 - 先算尺寸再动手:方形槽位里站姿全身只有 23~25 CSS px 宽,脸不可辨;
横宽姿势(趴地 98×84、躺卧 120×73)才饱满。如实告知用户代价,别偷偷裁半身。 - 生成前必须告知计费(每次调用约 5-10 credits,6 张约 30-60)。
- 生成时注意同秒撞名覆盖:每张给独立
output_dir,或串行跑。 - 装好多姿势后用
scripts/swap_buddy_pose.py切换:
--list看清单 /--pose <名字>指定 /--random随机。
- 交付方式(已定,不必再问用户):客户端没有 zip 导入入口,唯一可行路径是
patch 到本地已存在的主题目录。需要确认的只是打到哪个槽位(见阶段 4 的槽位表)。
信息不全不推进。四项都拿到答复才进入阶段 2。
脚本清单
| 脚本 | 作用 |
|---|---|
scripts/build_skin.py |
从 spec 生成完整皮肤包(核心) |
scripts/prepare_buddy.py |
伙伴立绘规范化(任意图 → 720×720 合规透明 png) |
scripts/swap_buddy_pose.py |
一键切换伙伴姿势(--list / --pose x / --random) |
scripts/preview.py |
生成单套皮肤的仿界面预览 HTML |
scripts/build_gallery.py |
生成多套配色方案对比画廊 |
scripts/install_skin.py |
交付(patch / pack / restore / list / info) |
scripts/palette.py |
配色工具库(被上述脚本调用) |
参考文档
| 文件 | 内容 |
|---|---|
references/skin-mechanism.md |
皮肤机制与安装原理(源码级实测) |
references/palette-presets.md |
7 套预置配色方案 |
references/asset-spec.md |
素材规范(尺寸 / 格式 / 视频守卫) |
references/buddy-poses.md |
伙伴多姿势:槽位表现数据 / 图生图 prompt 模板 / 抠底坑 / 换姿势脚本 |
references/user-guide.md |
用户使用守则模板(阶段 6 交付用) |
阶段 2:生成皮肤包
- 写
skin-spec.json:
{
"name": "皮肤显示名",
"slug": "英文短名",
"packageName": "workbuddy-skin-<英文短名>",
"style": "风格描述",
"colors": {
"primary": "#主色",
"dark": "#深阶(可省,自动推导)",
"bg": "#主填充底(可省)",
"bgLite": "#页面浅底(可省)",
"fg": "#文字基色(可省)"
},
"assets": {
"homeBg": "底图路径(可选)",
"buddy": "立绘路径(可选)",
"homeVideo": "视频路径(可选)"
}
}
只给 primary 也能跑通,其余自动推导。
- 运行生成器:
python scripts/build_skin.py <spec.json> --out <输出目录>
产出:
<输出目录>/
skin.css ← 根级副本
theme.json ← 元信息
<皮肤包名>/
skin.css
assets/custom/* ← 素材
- 校验:脚本会打印
rules(映射规则数)。正常应 >100 条。
若 <50,说明配色判定异常,检查primary是否为合法 hex。
阶段 3:生成预览并让用户确认
python scripts/preview.py <输出目录>/<皮肤包名>/skin.css \
--meta <输出目录>/theme.json \
--out <输出目录>/preview.html
用 present_files 打开预览,让用户确认效果。不满意就回到阶段 1 调色重来,
不要带着未确认的效果往下走。
阶段 4:交付(唯一路径 = patch)
> 重要前提(源码实测):客户端没有 zip 导入入口。UI 组件里只有主题卡片
> (点击即选中)、i18n key 里零个 import/upload、IPC 通道只有
> getLocalResource / getSelections / listCatalog / listResources / setSelection。
> 不要向用户承诺「打包 zip 上传导入」。
先看一眼现场(当前生效主题、可用槽位、目录数是否超 LRU 上限):
python scripts/install_skin.py --mode list
可用槽位
本机 appearance-resources 里每个主题目录 = 一个可被覆盖的槽位。
--mode list 会列出它们。实测样例:
| 槽位目录 | 原包名 |
|---|---|
theme-tkbdx8-1788767683000 |
workbuddy-skin-purple |
theme-tkbdzr-1788858700000 |
workbuddy-skin-blue-video-fallback |
theme-tkbe90-1788858700000 |
workbuddy-skin-mint |
theme-tkbera-1788858700000 |
workbuddy-skin-green |
theme-tkboqn-1788767683000 |
workbuddy-skin-qqblue |
theme-tkhlwz-1788858700000 |
张韶涵主题皮肤-v11 |
theme-tkmw7j-1788858700000 |
WorkBuddy皮肤_激战金秋-龙狮城_v10 |
默认打到用户当前激活的槽位(脚本自动识别,最常见的诉求是「把我现在这套换个色」)。
用户想「多套并存」时,按槽位表分别 patch 不同皮肤 —— 这就是多槽位替换法。
执行 patch
# 覆盖当前生效主题(自动识别,最常用)
python scripts/install_skin.py <输出目录>/<皮肤包名> --mode patch
# 或指定槽位
python scripts/install_skin.py <输出目录>/<皮肤包名> \
--mode patch --target-theme theme-tkbdzr-1788858700000
脚本会自动:① 完整备份目标主题目录到 ~/.workbuddy/appearance-backups/theme-backup-<时间戳>/;
② 拷贝素材;③ 把素材 url() 改写成 local-file:// 绝对路径;④ 原子覆盖根级 skin.css。
> 状态:2026-09-14 实测通过 ✅(荧光绿验证皮肤,切主题后界面立即变色,
> 文件未被覆盖、无重新下载)。
必须告知用户【关键一步】:
> 打开 WorkBuddy「外观」→ 切到另一个主题 → 再切回目标主题。
> 只重启不生效(启动走缓存并判定 already applied,会跳过应用)。
切换成功后 CSS 缓存被更新,之后重启也会保持。
附带产出 zip(存档 / 跨机搬运,不是导入用)
python scripts/install_skin.py <输出目录>/<皮肤包名> \
--resource-key <皮肤包名> \
--mode pack --zip-out <输出目录>/<皮肤包名>.zip
zip 结构 <皮肤包名>/skin.css + <皮肤包名>/assets/custom/*,非白名单扩展名自动剔除。
用途:源码存档;换台机器后用 --mode patch 打到那个机器的槽位。
不能用来「客户端上传导入」。
风险提示(务必转达)
- 改动会被云端重新下载覆盖(该主题
updatedAt变化时)。 - 槽位总数受 LRU 上限 8 约束。
- 覆盖的是官方主题槽位,原貌靠脚本备份恢复。
阶段 5:回滚支持
- 每次 patch 前自动完整备份目标主题目录:
~/.workbuddy/appearance-backups/theme-backup-<时间戳>/<主题目录名>/ - 回滚:
python scripts/install_skin.py --mode restore --backup <备份目录>
- 回滚后同样需要在「外观」里切走再切回该主题才看得到原貌。
- 绝不删除任何已有皮肤目录。
阶段 6:交付「用户使用守则」
每次交付皮肤时,必须同时给用户一份操作说明,否则用户装完不会切主题,会以为失败。
模板:references/user-guide.md。按其结构输出(可精简),必须包含四件事:
1. 四步流程(定风格 → 看预览 → 装 → 切主题)
2. 切主题那一步要单独强调,并解释「只重启不生效」
3. 三条硬约束(不能新增 / 上限 8 / 只覆盖不删除)
4. 还原方法
用户侧文件可落盘到工作区(如 WorkBuddy皮肤使用守则.md),也可直接在回复里给出。
常用变体
- 只调色不做素材 → 阶段 1 素材全选「不要」,
assets留空对象。 - 微调已有皮肤 → 直接用
--mode patch覆盖目标主题的根级skin.css,不必重跑生成器。 - 深色皮肤 →
primary用亮色(如#3DD6D0),bg用主色同族深色,fg用浅色。
模板默认是浅色结构,深色需额外确认观感,务必让用户在预览页确认。
边界
- 不修改客户端程序文件(
app.asar/ exe),只操作appearance-resources。 - 不删除任何已有皮肤。
- 不新建主题目录(目录名由云端
resourceKey-updatedAt决定,自造无效且挤占 LRU 名额)。 - 不承诺「上传 zip 导入」 —— 客户端无此入口(UI / i18n / IPC 三层均已确认)。
唯一交付路径是 patch 本地已存在主题。
排错:判断是否生效,别看 cssLen
日志 applyTheme-with-css ... cssLen=N 不能用来反推磁盘文件。
实测:5 套未改动主题的 cssLen 恰好等于根级 skin.css 字符数,但改动后的主题
cssLen=55624 ≠ 文件字符数 54228(客户端内部做了 URL 重写等处理)。
判断生效只看两条:
1. 界面颜色是否变化(最直接)。
2. 日志有无 downloadZip / materialize —— 有则是被重新下载覆盖,没有则用的是你的本地文件。