# cc-blue **Repository Path**: ricky/cc-blue ## Basic Information - **Project Name**: cc-blue - **Description**: 蓝木偶,一个Claude Code使用的桌宠,有两种形象可选:蓝木偶和金红战神。 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-07-18 - **Last Updated**: 2026-10-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 蓝木偶 · Blue Puppet 一个蓝色科技感的木偶桌面宠物,通过 **Claude Code Hooks(http 类型)+ 本地 HTTP** 实时反映 Claude Code 的当前状态。 **多会话多实例**:程序启动后只在系统托盘出现一个图标,不显示宠物。每个 Claude Code 会话开始后,自动为它弹出一个专属宠物实例(随机位置、下方显示该会话首条提问摘要),各实例互不干扰。无窗口边框、透明背景、始终置顶、可拖拽。所有表情和动作都是纯代码绘制,无需任何图片资源。 > 本项目现有 **Rust 版**(推荐)与 **Python 版** 两个实现,功能完全一致。 > Rust 版单文件 **约 0.8 MB**(UPX 压缩后),Python 版打包后约 37 MB。 ## 功能状态 | 状态 | 表情 | 触发事件(hook_event_name) | 跟随鼠标 | |------|------|------------------|:---:| | `idle` | 无表情(发呆,呼吸浮动) | 无任务时 | ✗ | | `working` | 努力干活(锐利斜眼、手臂快速敲打) | UserPromptSubmit、PreToolUse | ✗ | | `question` | 疑问(歪头、大小眼、浮现"?") | PreToolUse(AskUserQuestion/ExitPlanMode)、PermissionRequest、Notification | ✓ 侧方 | | `error` | 苦恼(橙色警示眼、下撇嘴、警示三角) | PostToolUseFailure | ✗ | | `happy` | 开心(弯月笑眼、跳跃、举手) | PostToolUse | ✗ | | `done` | 开心 + 持续跟随 | Stop(整个任务结束) | ✓ 正下方 | | `sneaky` | 暗中使劲(搓手、东张西望,复用 working 表情) | Stop 时仍有后台任务在跑 | ✗ | - 状态由宠物端根据钩子事件的 `hook_event_name`(和 `tool_name`)推断,钩子本身不传 state。 - `happy` 是临时状态,几秒后自动回到 `working`;`error` 需新事件显式改变(429/过载等会一直显示)。 - `done` 持续到该会话下一个任务开始(`working`)才停止跟随。 - `sneaky`(暗中使劲):`Stop` 触发时若检测到该会话仍有后台任务(`run_in_background` 的 shell/下载等)在跑,则不显示 `done` 而显示 `sneaky`;待后台任务完结(完成/失败/被停止)、 下一次 `Stop` 时才回到 `done`。检测逻辑见 `src/background.rs`。 ## 架构 ``` Claude Code 钩子(http 类型,settings.json) │ 自动把事件完整 JSON(session_id / hook_event_name / prompt / tool_name…) │ 作为 POST body 发出,无需任何脚本 ▼ POST http://127.0.0.1:48291/state ▼ 蓝木偶(托盘常驻,内置单个 HTTP 服务) │ 按 session_id 路由:未见过则懒创建实例,否则更新对应实例 ▼ pets = { session_id: PetWindow } 每只宠物独立状态机/动画/跟随 ``` - **http 钩子**:`type: "http"` 让 Claude Code 直接 POST 事件 JSON,省去读 stdin 的桥接脚本。 - **懒创建**:`SessionStart` 事件不支持 http 类型,故不配它;首个到达的事件(通常是 `UserPromptSubmit`)即为该会话创建实例。 - **形象循环**:新实例按 `RENDERERS` 顺序循环分配形象(bluemesh → wukongmesh → ironmesh …), 多个会话一眼可辨;之后仍可右键"切换形象"单独改。 - **会话摘要**:`UserPromptSubmit` 的 `prompt` 前 12 字显示在宠物下方,便于区分多个会话。 - **端口**默认 `48291`,可用环境变量 `BLUE_PUPPET_PORT` 覆盖(需与 settings.json 中的 url 一致)。 - **同一时刻只运行一个宠物进程**(端口独占),但进程内可有多实例。若状态不更新,先检查是否有残留进程占用 `48291`。 --- ## Rust 版(推荐) ### 技术栈 | 关注点 | 选型 | |--------|------| | 2D 绘制(渐变/贝塞尔/发光/圆角) | [tiny-skia](https://crates.io/crates/tiny-skia)(纯 Rust Skia,API 对应 QPainter) | | 透明/置顶/无边框窗体 | [winit](https://crates.io/crates/winit) + Win32 分层窗体 | | 逐像素透明合成 | `UpdateLayeredWindow`(RGBA → DIB → 屏幕,等同 Qt 的 `WA_TranslucentBackground`) | | HTTP 服务 | [tiny_http](https://crates.io/crates/tiny_http) | | 系统托盘 | [tray-icon](https://crates.io/crates/tray-icon) | | 文本(中文气泡/摘要) | [cosmic-text](https://crates.io/crates/cosmic-text) + swash 光栅化 | | JSON | serde / serde_json | | 图标 | 编译期 `include_bytes!` 内嵌 `app.ico` | ### 构建 需要 Rust 工具链(1.86+,Windows MSVC): ```bash cargo build --release ``` 产物为 `target/release/blue-puppet.exe`(约 2.2 MB,未压缩)。 **一键发布(推荐)**:`release.bat` 一条命令完成 `cargo build --release` + UPX 压缩, 产出约 0.8 MB 的单文件 `target/release/blue-puppet.exe`: ```bat release.bat ``` > 需先自行下载 UPX 放到 `tools\upx-4.2.4-win64\`(见下文「UPX 压缩」)。 > 注意区分:`build.bat` 是旧 **Python 版**的 PyInstaller 打包脚本,与 Rust 版无关。 > 项目内 `.cargo/config.toml` 配置了 rsproxy 镜像加速 crates 下载,仅对本项目生效。 ### 运行 双击 `blue-puppet.exe` 即可(GUI 子系统,**无 cmd 黑框**;图标已内嵌,单文件可独立分发)。 托盘常驻,每个 Claude Code 会话首个事件即弹出对应宠物实例。 ### UPX 压缩(可选,约 0.8 MB) `release.bat` 已自动完成本步(`cargo build --release` 后接 UPX)。手动压缩则先自行下载 [UPX](https://github.com/upx/upx/releases)(4.x,win64),再用 `--best --lzma`: ```bash upx --best --lzma -f target/release/blue-puppet.exe ``` 压缩后约 **0.81 MB**(压缩率 ~37%)。注意:UPX 版首次启动略慢(运行时解压),个别杀软对 UPX 壳较敏感;介意则用未压缩版。 > **许可证说明**:UPX 本身为 GPLv2+,但其官方许可明确"用 UPX 压缩**你自己的软件**,产物不受 GPL 约束、可按任意许可证分发"。压缩仅作用于构建产物、不改变本项目(MIT)的许可证。本仓库**不含** UPX 二进制(`tools/` 已加入 `.gitignore`),请自行下载或经 CI 获取。 ### 测试 渲染与对齐有单元测试(`cargo test`),覆盖:形象水平居中、倾斜不破坏居中、左右倾斜对称: ```bash cargo test ``` ### Rust 源码结构(`src/`) | 文件 | 作用 | |------|------| | `main.rs` | 入口:事件循环、托盘、HTTP 线程、双击检测、右键菜单分发;图标内嵌 | | `anim.rs` | 动画数学(`spring`/`pulse`/`osc`/`osc_d`/`smoothstep`/`clamp01`)与 `pose_for()` 姿态生成 | | `event.rs` | hook 事件 → 状态推断、`QUESTION_EXIT`/`TRANSIENT` 规则、内置钩子配置生成 | | `background.rs` | 后台任务检测:扫描会话 transcript,判断 `Stop` 时是否仍有后台任务在跑(`sneaky` 状态依据) | | `http.rs` | tiny_http 服务,POST/GET `/state`、`/health`,跨线程投递 | | `manager.rs` | PetManager:懒创建、session 路由、形象循环分配、每帧驱动 | | `window.rs` | 单实例窗体:状态机、鼠标跟随、拖拽、双击、逐帧渲染 | | `layered.rs` | Win32 逐像素透明合成(`UpdateLayeredWindow`)、`WS_EX_*` 样式管理、DPI 感知 | | `menu.rs` | 宠物右键菜单(Win32 `TrackPopupMenu`,工具窗体不上任务栏) | | `tray.rs` | 系统托盘与托盘菜单 | | `draw.rs` | 2D 绘制辅助层(渐变/发光/路径/圆角矩形/圆弧/多边形) | | `text.rs` | cosmic-text 中文文本排版与光栅化(气泡/摘要) | | `skins/` | 形象渲染器:`bluemesh.rs`(蓝木偶)、`wukongmesh.rs`(金红战神)、`ironmesh.rs`(钢铁侠)、`common.rs`(撒花/符号/共享脸部) | | `build.rs` | 构建脚本:把 `app.ico` 设为 exe 文件图标 | ### 自定义(Rust 版) - **窗口大小**:`src/window.rs` 的 `SIZE`(默认 170,逻辑像素,高分屏自动按缩放放大)。 - **端口**:环境变量 `BLUE_PUPPET_PORT`,需与 settings.json 中的 url 一致。 - **会话摘要长度**:`src/event.rs` 的 `SUMMARY_LEN`(默认 12 字)。 - **事件→状态映射**:`src/event.rs` 的 `event_to_state()`。 - **临时状态时长**:`src/event.rs` 的 `TRANSIENT_HAPPY`。 - **跟随速度/距离/冷却**:`src/window.rs` 的 `_follow_mouse`(`step`、`gap`)与 `follow_grace`(默认 4 秒)。 - **气泡文字**:`src/event.rs` 的 `label()`。 - **配色**:各 `src/skins/*.rs` 顶部的调色板常量。 --- ## 运行环境 - Windows(已在 Windows 11 验证,含 4K 高分屏 150% 缩放) - Rust 版:无需任何运行时,单文件 exe 即可 - Python 版:Python 3.12+ 与 PySide6 - Claude Code 版本需支持 http 类型钩子 ## 交互方式(对单个宠物实例) | 操作 | 效果 | |------|------| | 左键拖拽 | 移动位置 | | 左键双击 | 普通状态:显示 / 隐藏气泡;跟随状态(`question`/`done`/`error`):暂停 / 恢复跟随 | | 右键 | 打开菜单(见下) | ### 宠物右键菜单 | 菜单项 | 作用 | |--------|------| | 状态:… | 仅显示当前状态(灰色,不可点) | | 会话:… | 仅显示该实例的会话摘要(灰色,不可点) | | 隐藏气泡 / 显示气泡 | 切换气泡文字 | | 复制配置 | 把内置的 http 钩子配置复制到剪贴板,便于其他项目复用 | | 切换形象 | 逐个列出所有形象,当前形象打勾;点击即切换 | | 关闭此宠物 | 关闭该会话对应的实例(不影响其他实例与托盘进程) | ### 托盘菜单 | 菜单项 | 作用 | |--------|------| | 复制配置 | 同宠物菜单的"复制配置" | | 退出 | 退出整个宠物进程(关闭所有实例与托盘) | > **复制配置**用的是程序内置的 http 钩子配置(端口随 `BLUE_PUPPET_PORT` 动态生成),不读取项目里的 > settings.json,因此宠物放在任何目录都能复制出正确配置。其他项目粘贴到自己的 `.claude/settings.json` > 即可使用(前提是那台机器也运行着蓝木偶且端口一致)。 ## 宠物形象 内置多个形象,右键菜单「切换形象」逐个列出、点击即换(当前形象打勾): | key | 名称 | 简介 | |-----|------|------| | `bluemesh` | 蓝木偶 | 蓝色科技机器人,网格体积 + z-buffer 软光栅,朝移动方向转身 | | `wukongmesh` | 金红战神 | 齐天大圣主题:圆台披风/裙甲、弯管雉鸡翎、身前旋转金箍棒、暗红心形面罩 | | `ironmesh` | 钢铁侠 | Q 版钢铁侠:超椭球头身、凹形贴面金面罩、肩后三角飞翼、裆部小裤 | 所有形象共享同一套状态动画与跟随逻辑。立体形象由内置的玩具级软光栅管线 (`src/mesh3d.rs`:球/圆柱/圆台/超椭球/表面贴片,Gouraud + z-buffer + 2x 超采样)渲染, 设 `BLUE_PUPPET_3D_SPIN=1` 可持续 360° 转圈查看各角度。 - Rust 版新增形象:在 `src/skins/` 新建文件实现 `Renderer` trait(`draw`),并加入 `skins.rs` 的 `RENDERERS` / `make_renderer`。 - Python 版新增形象:在 `blue_puppet.py` 继承 `BaseRenderer` 实现 `_draw`,加入 `RENDERERS`。 ## Claude Code Hooks 配置 钩子用 **http 类型**直连,无需脚本。Claude Code 会把事件的完整 JSON(含 `session_id`、 `hook_event_name`、`prompt`、`tool_name` 等)作为 POST body 发出。示例: ```json "UserPromptSubmit": [ { "matcher": "*", "hooks": [ { "type": "http", "url": "http://127.0.0.1:48291/state" } ] } ] ``` - 状态由宠物端根据 `hook_event_name`/`tool_name` 推断,钩子无需传 state。 - 宠物未运行时,http 钩子的连接失败是**非阻塞错误**,不干扰会话。 ## HTTP 接口 | 方法 | 路径 | 说明 | |------|------|------| | `POST` | `/state` | 接收钩子事件 JSON(需含 `session_id`),路由到对应实例 | | `GET` | `/state` | 查询所有实例状态,返回 `{"states": {session_id: state}}` | | `GET` | `/health` | 健康检查,返回 `{"ok": true}` | 手动测试(注意用能正确处理中文/JSON 的工具,Git Bash 的 curl 单引号可能丢字符): ```bash curl -X POST -H "Content-Type: application/json" \ -d "{\"session_id\":\"s1\",\"hook_event_name\":\"UserPromptSubmit\",\"prompt\":\"帮我重构登录\"}" \ http://127.0.0.1:48291/state ``` --- ## Python 版(参考实现) Python 版是最初的实现,功能与 Rust 版一致,但打包后体积较大(约 37 MB)。 ### 安装与运行 ```bash pip install PySide6 python pet_app.py ``` 屏幕**不出现宠物**,只在系统托盘出现一个图标。托盘菜单:复制配置 / 退出。 可选演示多会话多实例: ```bash python demo_states.py ``` ### 打包成 exe(Python 版) ```bash pip install PySide6 pyinstaller Pillow build.bat ``` 产物为 `dist\BluePuppet.exe`。`build.bat` 先运行 `make_icon.py` 生成 `app.ico`,再用 PyInstaller 打包(`--onefile --windowed`,把 `app.ico` 打进 exe 供托盘图标加载,约 37 MB)。 ### Python 文件说明 | 文件 | 作用 | |------|------| | `pet_app.py` | 主程序:托盘、PetManager(session 路由 + HTTP 服务)、PetWindow(窗口/动画/跟随/交互) | | `blue_puppet.py` | 木偶绘制器:三套形象 + 各状态表情,纯代码绘制 | | `demo_states.py` | 演示脚本:模拟两个会话交替发事件(走 HTTP) | | `make_icon.py` | 生成图标:渲染木偶头部 → 多尺寸 `app.ico` | | `build.bat` | 一键打包:生成图标 → PyInstaller 打包带图标 exe | ## 已知限制 - 宠物未运行时,http 钩子的请求失败是非阻塞错误,状态不更新,但不干扰会话。 - 修改 settings.json 后需**重启 Claude Code** 让钩子生效。 - 实例**手动关闭**:会话结束后对应宠物仍留在桌面,需右键"关闭此宠物"(或托盘"退出"全部)。 - `SessionStart` 事件不支持 http 类型,故实例在首个事件(通常 `UserPromptSubmit`)才出现, 新会话第一句提问前桌面上没有对应宠物。