When work and gaming eat your attention, it is easy to forget to reply. Toy is a small desktop pet that helps you stay “present”: a tap or poke sends a preset message so you can nudge someone without breaking flow.
This monorepo hosts every Toy client: shared pet assets live next to each platform’s code.
- Transparent always-on-top window — the pet floats above other windows, frameless and out of the way of clicks.
- 9 built-in pets — each with unique animated GIFs for idle, running, waiting, done, error, and drag directions.
- Multi-device relay — WebSocket relay so pets on different devices can interact: poke pets for others in the same group or sync state changes.
- System tray menu — switch pets, toggle bubble / low-power / always-on-top / relay, import custom pets, open settings.
- State machine — unified
idle→running→waiting→done/errorflow with auto-return timers. - Custom pet import — ZIP packs with a simple JSON manifest + GIF assets.
- Position persistence — remembers the window position and clamps it to the current monitor layout.
- Low-power mode — lowers GIF frame rate while idle to save CPU.
Assets come from the Codex Pets community.
| ID | Name | Link |
|---|---|---|
bubu |
Bubu | https://codex-pets.net/#/pets/bubu-yier |
guga |
Guga | https://codex-pets.net/#/pets/guga |
ikun |
iKun | https://codex-pets.net/#/pets/ikun |
miku |
Hatsune Miku | https://codex-pets.net/#/pets/miku |
miku-kimono |
Miku Kimono | https://codex-pets.net/#/pets/miku-kimono |
tiga |
Tiga (Ultraman) | https://codex-pets.net/#/pets/tiga |
xiaoba |
Xiaoba | https://codex-pets.net/#/pets/xiaoba |
xiaoyan |
Xiaoyan | https://codex-pets.net/#/pets/xiaoyan |
yier |
Yier | https://codex-pets.net/#/pets/yier-gif |
┌─────────────────────────────────────────────────┐
│ Frontend │
│ React + TypeScript (Vite) │
│ ┌──────────┐ ┌──────────────┐ ┌───────────┐ │
│ │ Pet.tsx │ │ petState- │ │ petRelay │ │
│ │ (render) │ │ Machine.ts │ │ (WebSocket)│ │
│ └──────────┘ └──────────────┘ └───────────┘ │
│ ┌──────────────────────────────────────────┐ │
│ │ App.tsx (orchestrator) │ │
│ └──────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────┐ │
│ │ tauriApi.ts (IPC bridge) │ │
│ └──────────────────────────────────────────┘ │
├─────────────────────────────────────────────────┤
│ Backend │
│ Rust (Tauri v2) │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ lib.rs │ │ pet_zip_ │ │
│ │ (tray, IPC, │ │ import.rs │ │
│ │ settings) │ │ (user pets) │ │
│ └──────────────┘ └──────────────┘ │
│ ┌──────────────────────────────────────┐ │
│ │ Settings (JSON file) │ │
│ │ ~/AppData/.../settings.json │ │
│ └──────────────────────────────────────┘ │
│ ┌──────────────────────────────────────┐ │
│ │ User Pets Dir (ZIP import) │ │
│ │ ~/AppData/.../pets/ │ │
│ └──────────────────────────────────────┘ │
└─────────────────────────────────────────────────┘
┌─────────┐
┌───→│ idle │←──────────────┐
│ └────┬────┘ │
│ │ click / remote │ auto-return
│ ▼ │ (timer)
│ ┌─────────┐ │
│ │ running │──────────┐ │
│ └─────────┘ │ │
│ │ │ │
│ ┌────┴────┐ ┌───┴───┴──┐
│ ▼ ▼ ▼ ▼
│ waiting done error (any state
│ via remote)
└───────────────────────────────┘
idle— default standbyrunning— working / poked, shows bubblewaiting— waiting, no auto-returndone— finished, returns to idle after a delay (default 3s)error— error, returns after max(5s, configured delay)
The relay uses a WebSocket service to sync actions for pets in the same group:
- Join: each client uses
groupId+groupToken. - Actions:
show_bubble(poke),jump,wave,state_change. - Delivery: clicks are sent to the server and broadcast to other group members; your own client ignores self-originated messages.
- Legacy: supports both the new
eventpayload and older flat JSON messages.
Default relay server: wss://api.example.cn/pet-relay.
| Path | Description |
|---|---|
tool-pet-tauri/ |
Desktop: Tauri v2 + React + TypeScript — docs/tauri.md |
tool-pet-android/ |
Android: Kotlin + Compose — docs/android.md |
tool-pet-net48/ |
Windows .NET Framework 4.8: WinForms — docs/net48.md |
sources/ |
Pet assets: shared GIFs and pet.json (synced into Tauri public/, Android assets, etc.) |
docs/ |
Documentation: per-platform guides (CN / EN) |
| Setting | Default | Description |
|---|---|---|
alwaysOnTop |
true |
Keep pet above other windows |
lowPowerMode |
true |
Reduce GIF frame rate when idle |
currentPetId |
bubu |
Active pet |
showBubble |
true |
Show speech bubble |
autoReturnToIdle |
true |
Auto-return from done / error to idle |
autoReturnDelayMs |
3000 |
Auto-return delay (ms) |
windowWidth / windowHeight |
100 / 120 |
Window size (px); 0 uses manifest size |
relayEnabled |
true |
Enable WebSocket relay |
relayServerUrl |
wss://api.example.cn/pet-relay |
Relay URL |
relayGroupId |
peng1028-pet |
Group id |
relayGroupToken |
(empty) | Group token |
relayDeviceId |
(auto-generated) | Device id |
relayUserName |
(system user) | Display name in relay messages |
relayMessageText |
拍了你一下 |
Default poke text |
Each pet folder contains a pet.json manifest:
{
"id": "bubu",
"name": "bubu",
"version": "1.0.0",
"width": 192,
"height": 208,
"defaultState": "idle",
"states": {
"idle": { "src": "bubu-yier-idle.gif", "bubble": "bubu准备好了" },
"running": { "src": "bubu-yier-running.gif", "bubble": "bubu正在努力" },
"waiting": { "src": "bubu-yier-waiting.gif", "bubble": "bubu在等待" },
"done": { "src": "bubu-yier-review.gif", "bubble": "bubu完成了" },
"error": { "src": "bubu-yier-failed.gif", "bubble": "bubu遇到错误" },
"dragLeft": { "src": "bubu-yier-running-left.gif", "bubble": "bubu向左移动" },
"dragRight":{ "src": "bubu-yier-running-right.gif", "bubble": "bubu向右移动" }
}
}Fields:
id— unique idname— display name (tray menu)version— semverwidth/height— art size (px)defaultState— initial state (usuallyidle)states— maps state →{ "src", "bubble" }for GIF path and bubble text- Optional:
dragLeft/dragRightfor drag animation
- Tauri desktop:
cd tool-pet-tauri, runnpm installandnpm run tauri:dev(see that folder’s docs). - Android: open
tool-pet-androidin Android Studio. - .NET: open
tool-pet-net48/ToolPet.Net48.sln.
Install npm dependencies and run frontend builds only under tool-pet-tauri/. If you ever ran npm install at the repo root, remove stray node_modules/ / dist/ there and reinstall inside tool-pet-tauri/.
| Layer | Technology |
|---|---|
| Desktop | Tauri v2 |
| Frontend | React 18 + TypeScript + Vite |
| Backend | Rust (edition 2021, MSRV 1.77.2) |
| Assets | GIF (animated) |
| Relay | WebSocket (JSON) |
| Build scripts | Node.js (sync-pet-sources.mjs, generate-default-pet-assets.mjs) |
See Codex Pets for community packs and authoring guidance.
See LICENSE at the repository root.