一只住在你桌面上的像素小螃蟹,实时感知 AI 编程助手的每一个动作
一款基于 Electron 的桌面互动桌宠,能实时感知 AI 编程助手的运行状态。发起一个长任务后走开,等桌宠告诉你任务完成再回来。
Clawd 桌宠需要 Node.js 运行环境。如果你没有安装 Node.js,请按以下步骤操作:
node --version # 应显示 v20.x.x 或 v22.x.x npm --version # 应显示 10.x.x 或 11.x.x
✓ 两条命令都能正常显示版本号,说明环境就绪。
在本页底部下载桌宠源码压缩包,解压后进入目录:
cd clawd-on-desk
进入项目目录后,运行以下命令安装依赖包(包括 Electron 框架):
npm install
📌 提示 安装过程中如果遇到网络慢的问题,可以使用国内镜像:
npm install --registry=https://registry.npmmirror.com
安装完成后,目录下会多出一个 node_modules 文件夹,这是所有依赖包,不要动它。
依赖安装完成后,一行命令启动:
npm start
启动后你会看到:
✓ 看到桌宠在桌面上活动,说明安装成功了!
Clawd 默认自动集成了 Claude Code 和 Codex CLI。如需支持其他 AI 工具:
部分工具也可以通过命令行安装:
npm run install:gemini-hooks # 安装 Gemini CLI 集成 npm run install:cursor-hooks # 安装 Cursor IDE 集成 npm run install:copilot-hooks # 安装 Copilot CLI 集成 npm run install:qwen-hooks # 安装 Qwen Code 集成
Clawd 内置三套主题,可以随时切换:
像素小螃蟹 Clawd 的各种动画状态:
待机
思考
打字
建造
耳机律动
三球杂耍| 主题 | 风格 |
|---|---|
| 🦀 Clawd | 像素小螃蟹,活泼好动。眼珠咕噜转,爪子跟着鼠标挥舞,默认主题的可爱担当 |
| 🐱 Calico Cat | 三花猫,优雅慵懒。思考时歪头,打字时爪子轻拍键盘,完成时尾巴翘高高 |
| ☁️ Cloudling | 云宝,轻盈梦幻。漂浮状态上下起伏,做动作时云朵会变形,治愈感满满 |
支持自定义主题,也可导入 Codex Pet 动画包。通过 npm run create-theme my-theme 即可生成主题脚手架。
与主流 AI 编程助手深度集成,自动感知工作状态:
| 功能 | 说明 |
|---|---|
| 多 Agent 支持 | 同时追踪多个 AI 编程助手,独立管理各会话状态 |
| 子代理感知 | 1 个子代理 → 耳机律动,2 个以上 → 三球杂耍 |
| 点击穿透 | 透明区域的点击穿透到下方窗口,只有角色本体可交互 |
| 位置记忆 | 重启后回到上次位置(包括极简模式) |
| 多显示器 | 等比缩放,竖屏额外尺寸补偿,跨屏拖动 |
| 多语言 | 简中、繁中、英文、韩文、日文 — 右键菜单切换 |
| 免打扰 | 静默所有 Hook 事件,直到手动唤醒 |
| 自动更新 | 检查 GitHub release,Windows 退出时自动安装更新包 |
| 音效系统 | 任务完成和权限请求时播放短提示音,右键菜单可开关 |
| 进程守护 | 检测已崩溃/退出的 Agent 进程,10 秒内清理孤儿会话 |
| 技术 | 用途 |
|---|---|
| Electron | 跨平台桌面应用框架(~41.x) |
| Node.js | 后端逻辑、Hook 管理、进程通信 |
| Canvas / SVG | 像素动画渲染、眼球追踪 |
| WebSocket (ws) | 与 AI 工具的状态同步、远程 SSH 转发 |
| PWA | 手机伴侣 Web 应用(局域网实时镜像) |
| electron-updater | 自动更新机制 |
以下展示了 Clawd 桌宠的几个核心文件,每个文件都配有代码说明。
#!/usr/bin/env node
const { spawn } = require("child_process");
const electron = require("electron");
const { buildElectronLaunchConfig } = require("./hooks/shared-process");
const forwardedArgs = process.argv.slice(2);
const launchConfig = buildElectronLaunchConfig(__dirname, { forwardedArgs });
const child = spawn(electron, launchConfig.args, {
stdio: "inherit",
env: launchConfig.env,
cwd: launchConfig.cwd,
});
child.on("close", (code) => process.exit(code ?? 0));
ELECTRON_RUN_AS_NODE=1 环境变量,
会导致 Electron 以 Node.js 模式运行而无法启动窗口。launch.js 会剥离这个环境变量,
然后通过 spawn 重新启动 Electron,确保桌宠窗口正常显示。
const { contextBridge, ipcRenderer } = require("electron");
const themeArg = process.argv.find(a => a.startsWith("--theme-config="));
const themeConfig = themeArg ? JSON.parse(themeArg.slice("--theme-config=".length)) : null;
contextBridge.exposeInMainWorld("themeConfig", themeConfig);
contextBridge.exposeInMainWorld("electronAPI", {
onThemeConfig: (cb) => ipcRenderer.on("theme-config", (_, cfg) => cb(cfg)),
onViewportOffset: (cb) => ipcRenderer.on("viewport-offset", (_, offsetY) => cb(offsetY)),
onStateChange: (callback) => ipcRenderer.on("state-change", (_, state, svg) => callback(state, svg)),
onKimiPermissionPulse: (callback) => ipcRenderer.on("kimi-permission-pulse", () => callback()),
onEyeMove: (callback) => ipcRenderer.on("eye-move", (_, dx, dy) => callback(dx, dy)),
onCloudlingPointer: (callback) => ipcRenderer.on("cloudling-pointer", (_, payload) => callback(payload)),
onRoamHeading: (callback) => ipcRenderer.on("roam-heading", (_, headingLeft) => callback(headingLeft)),
onWakeFromDoze: (callback) => ipcRenderer.on("wake-from-doze", () => callback()),
onDndChange: (callback) => ipcRenderer.on("dnd-change", (_, enabled) => callback(enabled)),
onMiniModeChange: (cb) => ipcRenderer.on("mini-mode-change", (_, enabled, edge, options) => cb(enabled, edge, options)),
onMiniClip: (cb) => ipcRenderer.on("mini-clip", (_, info) => cb(info)),
onLowPowerIdleModeChange: (cb) => ipcRenderer.on("low-power-idle-mode-change", (_, enabled) => cb(enabled)),
onSystemWake: (cb) => ipcRenderer.on("system-wake", (_, payload) => cb(payload)),
onStartDragReaction: (cb) => ipcRenderer.on("start-drag-reaction", (_, direction) => cb(direction)),
onEndDragReaction: (cb) => ipcRenderer.on("end-drag-reaction", () => cb()),
onPlayClickReaction: (cb) => ipcRenderer.on("play-click-reaction", (_, svg, duration) => cb(svg, duration)),
onPreloadSounds: (cb) => ipcRenderer.on("preload-sounds", (_, payload) => cb(payload)),
onPlaySound: (cb) => ipcRenderer.on("play-sound", (_, payload) => cb(payload)),
onInvalidateSoundCache: (cb) => ipcRenderer.on("invalidate-sound-cache", (_, url) => cb(url)),
reportSoundPlaybackError: (payload) => ipcRenderer.send("sound-playback-error", payload),
pauseCursorPolling: () => ipcRenderer.send("pause-cursor-polling"),
resumeFromReaction: () => ipcRenderer.send("resume-from-reaction"),
setLowPowerIdlePaused: (paused) => ipcRenderer.send("low-power-idle-paused", !!paused),
reportSystemWakeStatus: (payload) => ipcRenderer.send("system-wake-status", payload),
});
contextBridge 安全地将主进程的功能暴露给渲染进程:
onStateChange 接收主进程发来的状态变化(如"思考中"、"打字中"),
onEyeMove 接收鼠标坐标用于眼球追踪,
onMiniModeChange 控制极简模式的切换。
所有通信都通过 Electron 的 IPC(进程间通信)机制完成,保证了安全性。
const { contextBridge, ipcRenderer } = require("electron");
contextBridge.exposeInMainWorld("bubbleAPI", {
onPermissionShow: (cb) => ipcRenderer.on("permission-show", (_, data) => cb(data)),
decide: (behavior) => ipcRenderer.send("permission-decide", behavior),
onPermissionHide: (cb) => ipcRenderer.on("permission-hide", () => cb()),
reportHeight: (h) => ipcRenderer.send("bubble-height", h),
});
onPermissionShow 将权限请求信息传给气泡 UI,
用户在气泡上点击"允许"或"拒绝"后,decide 将决定发回主进程,
主进程再转发给 AI 工具。这样用户无需切回终端即可处理权限请求。
"use strict";
function createHitboxRuntime(theme) {
return {
hitBoxes: theme.hitBoxes,
fileHitBoxes: theme.fileHitBoxes || {},
wideSvgs: new Set(theme.wideHitboxFiles || []),
sleepingSvgs: new Set(theme.sleepingHitboxFiles || []),
};
}
function resolveHitBoxForSvg(svg, runtime) {
const hitBoxes = runtime.hitBoxes;
const fileHitBoxes = runtime.fileHitBoxes;
const wideSvgs = runtime.wideSvgs;
const sleepingSvgs = runtime.sleepingSvgs;
if (svg && fileHitBoxes[svg]) return fileHitBoxes[svg];
if (svg && sleepingSvgs.has(svg)) return hitBoxes.sleeping;
if (svg && wideSvgs.has(svg)) return hitBoxes.wide;
return hitBoxes.default;
}
module.exports = {
createHitboxRuntime,
resolveHitBoxForSvg,
};
resolveHitBoxForSvg 根据当前播放的 SVG 帧,从主题配置中查找对应的碰撞盒,
实现"指哪打哪"的精准交互体验。
"use strict";
function shouldKeepOutOfTaskbar(platform = process.platform) {
return platform === "win32" || platform === "linux";
}
function keepOutOfTaskbarForPlatform(w, platform = process.platform) {
if (!w || w.isDestroyed()) return;
if (shouldKeepOutOfTaskbar(platform) && typeof w.setSkipTaskbar === "function") {
w.setSkipTaskbar(true);
}
}
function keepOutOfTaskbar(w) {
keepOutOfTaskbarForPlatform(w);
}
module.exports = {
keepOutOfTaskbar,
__test: {
keepOutOfTaskbarForPlatform,
shouldKeepOutOfTaskbar,
},
};
setSkipTaskbar(true)
确保桌宠窗口不会占用任务栏空间。同时暴露了 __test 对象用于单元测试。
{
"name": "clawd-on-desk",
"version": "0.11.0",
"description": "A desktop pet that reacts to your AI coding sessions in real-time.",
"main": "src/main.js",
"scripts": {
"start": "node scripts/ensure-sidecar-binaries.js && node launch.js",
"build": "electron-builder --win",
"build:win:x64": "electron-builder --win nsis:x64",
"build:win:arm64": "electron-builder --win nsis:arm64",
"build:win:all": "electron-builder --win nsis:x64 nsis:arm64",
"build:mac": "electron-builder --mac",
"build:linux": "electron-builder --linux",
"build:all": "electron-builder --win --mac --linux",
"fetch:sidecars": "node scripts/fetch-sidecar-binaries.js",
"verify:sidecars": "node scripts/verify-sidecar-binaries.js",
"test": "node test/run-tests.js",
"create-theme": "node scripts/create-theme.js",
"install:claude-hooks": "node hooks/install.js",
"uninstall:claude-hooks": "node hooks/uninstall.js",
"install:gemini-hooks": "node hooks/gemini-install.js",
"install:cursor-hooks": "node hooks/cursor-install.js",
"install:kimi-hooks": "node hooks/kimi-install.js",
"install:qwen-hooks": "node hooks/qwen-code-install.js",
"install:codewhale-hooks": "node hooks/codewhale-install.js",
"install:pi-extension": "node hooks/pi-install.js",
"install:openclaw-plugin": "node hooks/openclaw-install.js",
"install:codex-hooks": "node hooks/codex-install.js",
"install:hermes-plugin": "node hooks/hermes-install.js",
"install:qoder-hooks": "node hooks/qoder-install.js",
"install:reasonix-hooks": "node hooks/reasonix-install.js"
},
"license": "AGPL-3.0-only",
"devDependencies": {
"electron": "^41.0.2",
"electron-builder": "^26.8.1"
},
"dependencies": {
"@larksuiteoapi/node-sdk": "^1.66.0",
"electron-updater": "^6.8.3",
"htmlparser2": "^12.0.0",
"koffi": "^2.15.2",
"ws": "^8.21.0"
}
}
package.json 定义了项目的元数据和依赖。关键脚本说明:
npm start 先检测 sidecar 二进制文件,然后启动应用;
各种 install:* 脚本用于安装不同 AI 工具的 Hook 集成。
依赖方面,核心依赖只有 5 个:Electron 框架、自动更新、HTML 解析、FFI 调用和 WebSocket,
保持了最小依赖原则。
检查系统托盘区域是否有螃蟹图标。如果没有任何反应,尝试在终端中手动运行 node launch.js 查看错误输出。常见原因:
检查 Settings → Agents 中对应的工具是否已启用。Claude Code 和 Codex CLI 默认自动注册 Hook,其他工具需要手动安装集成。
运行 npm run uninstall:claude-hooks 移除 Hook,然后删除整个项目文件夹即可。