WorkBuddy 桌面宠物
一个跨平台 Electron 桌面宠物,常驻屏幕右下角,陪伴你使用 WorkBuddy。
核心功能:
- 感知 WorkBuddy 工作状态(思考 → 托腮、工作中 → 跳动、完成 → 庆祝、失败 → 抖动)
- 头顶气泡实时显示任务提示
- 右键菜单:唤起 WorkBuddy、隐藏、回右下角、设置、飞行
- 全局快捷键 Cmd/Ctrl+Shift+P 显隐切换
- 拖拽到任意位置,重启后位置保持不变
- 飞行模式:蜘蛛侠倒挂圆锥摆绕圈转(可切换角色形象通用)
飞行模式:倒挂圆锥摆
右键菜单或 curl -X POST http://127.0.0.1:18920/state -d '{"state":"flying"}' 触发。
效果:宠物倒挂(头朝下、手举蛛丝朝上),绕头顶锚点做圆锥摆圆周运动(屏幕投影为椭圆),丝线从锚点连到宠物背部,近大远小。点屏幕任意位置或按任意键降落,落回起飞位置。
实现要点(renderer.js,已验证的最终方案)
// 圆锥摆参数(常量区可调)
const FLY_RX = 120; // 摆锤圆周半径(水平投影幅度)
const FLY_RY = 40; // 垂直压缩(俯视圆透视到正面的椭圆短轴)
const FLY_SPEED = 0.045; // 转圈角速度
const FLY_ANCHOR_ABOVE = 110; // 锥顶锚点在摆平面中心上方的高度 px
const FLY_DEPTH = 0.12; // 近大远小幅度
const FLY_ATTACH_RATIO = 0.25; // 丝线连接点 = 图像中心上方 1/4(背部/肩),不接手上蛛丝
几何核心:
1. 椭圆中心 = 锚点正下方 FLY_ANCHOR_ABOVE 处(即宠物起飞位置),不是锚点本身
2. 宠物绕该中心转圈:x = cx + FLY_RX*sin(θ),y = cy + FLY_RY*cos(θ)
3. 身体保持竖直不倾斜:transform 只有 scale(近大远小),不加任何 rotate
4. 丝线终点 = 宠物中心正上方 halfH * scale * depth * FLY_ATTACH_RATIO(背部)
踩坑记录(别再犯)
| 错误做法 | 后果 |
|---|---|
pet-flying.png 上再 rotate(180°) |
图片本身已是倒挂姿势,再转 180° 变成正立,用户反复反馈"不对" |
| 椭圆中心放在锚点上 | 宠物像风车叶绕锚点公转,绳子扫 360°,不是圆锥摆 |
身体随绳 rotate(tilt) |
身体跟着绳子偏,用户要求"身体不要偏",只要绳子斜 |
| 丝线连到图像顶边(手上蛛丝端) | 用户要求接到背部(FLY_ATTACH_RATIO 0.25 而非 0.95) |
| 锚点贴屏幕顶部 | 锚点在宠物上方 110px 即可,不贴屏幕顶 |
> 形象图要求:pet-flying.png 必须是"倒挂"姿势(头朝下、手举蛛丝朝上),否则飞行效果需重新调参。
安装流程
原则:全程自动,只在一处让用户确认。
第一步:问角色 → 全自动生成所有图片
询问用户:「要什么角色?直接回车用默认 Q 版机器人。」
用户回答后,不逐张确认,一次性生成全部 5 张状态图:
| 文件 | 状态 | 姿势 |
|---|---|---|
| pet-normal.png | 待机 | 自然站立 |
| pet-happy.png | 开心 | 咧嘴笑庆祝 |
| pet-bored.png | 无聊 | 托腮半眯眼 |
| pet-dance.png | 跳舞 | 跳跃张开双臂 |
| pet-angry.png | 生气 | 皱眉握拳 |
先生成待机基准图,再以它为参考 image-to-image 生成其余 4 张,存入 ~/workbuddy-deskpet-形象图/。
全部生成后,一次性展示 5 张给用户确认。 有问题只改有问题的图,没问题进入下一步。
第二步:自动安装 + 替换 + 配置 hooks(全自动,不问用户)
以下步骤顺序执行、不需要用户操作:
# 1. 创建目录并复制模板
mkdir -p ~/nico-workbuddy-deskpet
cp -R ~/.workbuddy/skills/nico-workbuddy-deskpet/template/. ~/nico-workbuddy-deskpet/
# 2. 安装依赖(ELECTRON_MIRROR + --registry 双保险走国内源)
cd ~/nico-workbuddy-deskpet && ELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/" npm install --no-fund --no-audit --registry=https://registry.npmmirror.com
# 3. 替换形象(自动抠透、缩放、生成托盘图标)
pip3 install Pillow -q 2>/dev/null
python3 ~/.workbuddy/skills/nico-workbuddy-deskpet/scripts/replace-assets.py ~/nico-workbuddy-deskpet ~/workbuddy-deskpet-形象图
# 4. 配置 hooks(两个文件都写,嵌套格式)
# 按 references/hooks-config.md 的脚本合并到 ~/.workbuddy/settings.json 和 ~/.codebuddy/settings.json
如目标目录已有内容,先备份再覆盖。配置 hooks 前先备份 settings.json。
第三步:展示结果
安装完成后告诉用户:
- 在终端执行:
bash ~/nico-workbuddy-deskpet/start-pet.sh - Cmd+Q 完全退出再打开 WorkBuddy,hooks 才会生效
然后告知基本操作(一句说完,不用逐条教):
> 右键桌宠有菜单,Cmd/Ctrl+Shift+P 显隐,拖到哪停哪。
更换形象
素材来源:
- AI 生成(推荐):用 ImageGen 生成 5 张。先生成待机基准图,再以它为参考 image-to-image 生成其余 4 张,保证角色一致
- 用户自备:让用户提供图片路径或文件夹
生成提示词参考(Q 版):Chibi <角色> mascot, cute super-deformed Q version, big round head with large white eyes, tiny stubby body with short limbs, <角色特征>, <状态描述>, adorable kawaii cartoon style, clean thick outlines, soft shading, centered full body composition, isolated on plain white background
5 张必备素材:
| 文件 | 状态 | 姿势 |
|---|---|---|
| pet-normal.png | 待机 | 自然站立 |
| pet-happy.png | 开心 | 咧嘴笑庆祝 |
| pet-bored.png | 无聊 | 托腮半眯眼 |
| pet-dance.png | 跳舞 | 跳跃张开双臂 |
| pet-angry.png | 生气 | 皱眉握拳 |
规格:500×500px PNG,白底即可(脚本自动抠透明),五张角色风格一致。
版权提醒:知名 IP 角色用于教学视频或商用有风险,个人学习演示无问题。长期分发建议用原创角色。
接入状态感知
(安装流程已自动包含 hooks 配置,此处仅作参考说明。)
让桌宠根据 WorkBuddy 工作状态自动切换动画。
- 先完成基础安装
- 告知用户:这会修改配置文件,需要 Cmd+Q 完全重启 WorkBuddy
- 按
references/hooks-config.md合并 hooks 到~/.workbuddy/settings.json和~/.codebuddy/settings.json(两个都要写) - 配置前先备份两个文件
- 用户 Cmd+Q 重启后验证:发消息 → 桌宠切思考状态;工具调用 → 显示工具名;完成 → 庆祝动画
卸载
逐项确认,不自动删除:
- 停止进程:
kill -9 $(lsof -ti:18920) - 移除自启(如有):
launchctl unload ~/Library/LaunchAgents/com.deskpet.workbuddy.plist - 移除 hooks:从 settings.json 中删除 pet 相关条目(先备份)
- 询问用户是否删除
~/nico-workbuddy-deskpet目录
故障排查
| 现象 | 方案 |
|---|---|
| 桌宠不出现 | 用户在终端跑 bash ~/nico-workbuddy-deskpet/start-pet.sh,看 "${TMPDIR:-/tmp}/deskpet-launch.log" 日志 |
| GPU crash | start-pet.sh 须始终带 --no-sandbox --disable-gpu 参数 |
| 端口 18920 被占 | kill -9 $(lsof -ti:18920) 后重启 |
| 状态不响应 | 确认 Cmd+Q 重启了 WorkBuddy;用 curl -X POST http://127.0.0.1:18920/state -d '{"state":"idle"}' 测试 |
| hooks 不触发 | 检查格式是否为 [{hooks:[{type:'command',command:'...'}]}] 嵌套结构,不是扁平数组 |
| 托盘图标不显示 | 用 Cmd/Ctrl+Shift+P 替代,打包 .app 后稳定 |
| hook 报 env: node | 把 command 中的 node 换成绝对路径 |