# Zhaozhao ยท dsh-cook-pet ๐Ÿณ [ไธญๆ–‡](README.md) | **English** > A little chef-girl desktop pet living inside [DeepSeek Harness](https://www.deepseek.com/harness/). > Her name is **Zhaozhao** (็…ง็…ง). When idle she stands around holding a spatula; give her work and she > **tosses a wok**; when a turn finishes she **brings you a plate of food**; when she > **needs your approval or an answer, she props her chin on her hand and waits**; poke her and > she'll wink or spin. ![Zhaozhao, floating on your desktop with a transparent background](docs/images/zhaozhao-240.png) **Four states (animated โ€” these are the actual on-screen motions):** | Idle | Cooking | Serving | Waiting | | :---: | :---: | :---: | :---: | | ![idle](docs/images/idle.gif) | ![cooking](docs/images/cook.gif) | ![serving](docs/images/serve.gif) | ![waiting](docs/images/think.gif) | | stands and sways | tosses the wok | brings a plate | tilts her head, waits | --- ## โœจ What she does | Moment | State | Visual | Text | | --- | --- | --- | --- | | No task running | **idle** | stands, spatula in hand | โ€” | | Turn running (`turn/start`) | **cooking** | tosses the wok | โ€” | | **Waiting for your approval / answer** | **waiting** | chin on hand, head tilted | **"็ญ‰ไฝ ๅ›ž่ฏ"** | | Turn completed successfully | **serving** | brings a plate, auto-idles after ~5 s | โ€” | | Turn aborted / errored | quietly idle | **no celebration** | โ€” | | You click her | **wink or spin** | random pick, then back to current state | โ€” | > **Only the "waiting" state shows text.** Everything else is pure animation โ€” a desktop pet > shouldn't have words covering the screen; only "it's your turn" deserves a glance. > > One more deliberate rule: **"serving" only appears when the task genuinely succeeded.** > Interrupted, errored, timed out โ€” she quietly returns to idle. She never gives you a fake > "done" signal. ### ๐Ÿค” What "waiting" actually means It is not a vague "busy" state. It maps to exactly **two kinds of real waiting**: | Trigger | Host event | | --- | --- | | Permission dialog (a tool needs allow/deny) | `approval/asked` โ†’ `approval/decided` | | Model asks you via `ask_user_question` | `tool/call` โ†’ `tool/result` | | Plan mode hands a plan over for approval (`exit_plan_mode`) | same as above | | Questions raised inside `run_code` scripts | `tool/ptc-dispatch-start` โ†’ `tool/ptc-dispatch` | These are **paired, durable events**, so both "started waiting" and "you answered" are captured precisely โ€” no polling, no guessing, and closing the dialog outright cannot leave her stuck in the waiting state. --- ## ๐Ÿ–ฑ๏ธ Interaction | Action | Effect | | --- | --- | | **Click** | random wink or spin, then back to the current state | | **Drag** | move her; the position is remembered across reloads | | **Right-click** | cycle size: 110 / 138 / 190 / 260 px (also remembered) | | Click ยท wink | Click ยท spin | | :---: | :---: | | ![wink](docs/images/wink.gif) | ![spin](docs/images/spin.gif) | --- ## ๐Ÿ“ฆ Install > Requires **DSH โ‰ฅ 0.2.0** (tested on `0.2.0-rc.1` through `0.2.1-rc.2`). ### โšก One command (recommended, via npm) ```bash dsh plugin --profile desktop add dsh-cook-pet ``` Replace `desktop` with the profile you actually boot (`web` if you run `dsh web`). Then **restart DSH**. - โœ… **No git needed**: pnpm pulls the tarball straight from the npm registry - โœ… Users with a `registry.npmmirror.com` mirror configured automatically get the faster path - โœ… Pin a version: `dsh plugin --profile desktop add dsh-cook-pet@1.0.1` - The project is **plain JavaScript with no build step** and has no install scripts, so no build authorization is required ### Alternative: install from GitHub ```bash dsh plugin --profile desktop add github:ting-good/dsh-cook-pet ``` > Note: this route needs **git installed** and a working connection to `github.com` > (the git resolver runs `git ls-remote`). ### ๐Ÿง‘โ€๐Ÿ’ป From a local directory (developers / offline) ```bash # If dsh isn't on PATH, use the full path, e.g. Windows desktop build: # D:\Deepseek\resources\runtime\cli\bin\dsh.cmd dsh plugin --profile desktop add file:/absolute/path/to/dsh-cook-pet ``` ### ๐Ÿ–ฅ๏ธ The floating desktop window: Electron prepares itself (optional) The always-on-top window is rendered by a separate Electron process and needs an Electron runtime (the DSH host itself is plain Node and ships no Electron binary). **You don't have to do anything**: the first time the runtime is missing, the plugin downloads it **in the background** (~140 MB, once only) and the desktop window appears when it's ready. DSH is fully usable during the download โ€” she just isn't on screen yet. The download is built to survive bad networks: | Capability | Detail | | --- | --- | | Mirror fallback | npmmirror โ†’ registry.npmmirror โ†’ huaweicloud, tried in order | | Limited retries | 3 attempts per mirror before giving up | | **Resumable** | continues from the bytes already downloaded (kept in `~/.dsh/electron/.download/`) | | Stall timeout | aborts an attempt after 60 s without data, instead of hanging forever | **If every attempt fails**, the DSH log prints copy-paste manual instructions (download URL, extract directory, pointing at an existing Electron, or disabling the desktop window) โ€” and the next retry resumes automatically. | You want to | Do this | | --- | --- | | Trigger / retry the download (auto-resumes) | `node scripts/ensure-electron.mjs` | | Reuse an existing Electron | `DSH_COOK_PET_ELECTRON=` | | Different mirror (used exclusively) | `DSH_COOK_PET_ELECTRON_MIRROR=` | > Note: deleting `~/.dsh/electron` makes her disappear (the plugin itself keeps running). > To turn the pet off completely: `DSH_COOK_PET_DESKTOP=0`, or uninstall the plugin. Then Zhaozhao appears in the **bottom-right corner of your screen** โ€” the screen, not the page, because she floats above every application. Drag to move, right-click to resize; both persist. ### ๐Ÿ—‘๏ธ Uninstall ```bash dsh plugin --profile desktop remove dsh-cook-pet ``` All overlay nodes, timers and event listeners are torn down on uninstall (guarded by tests). --- ## ๐ŸŽจ How she was made The character and all six animation clips are **AI-generated** (Q-style anime, green-screen / black-background sources), then converted into **transparent video** by a reproducible asset pipeline. | Step | Technique | Why | | --- | --- | --- | | 1. Derive alpha | treat only *pure black connected to the frame border* as background | the black spatula and dark-brown outlines survive | | 2. Edge colour extension | smear interior colours across the feathered band | otherwise the black background inside the feathered band becomes a dirty dark fringe | | 3. Shared crop box | one box for all six clips (union) | the character must not jump when the action changes | | 4. Encode | premultiplied alpha โ†’ VP9 `yuva420p` | the only transparent video format browsers handle reliably | > The most important lesson: **mp4/H.264 has no alpha channel.** A "cut-out mp4" merely has a > black or green background baked in โ€” the transparency simply isn't in the file. It must be > re-encoded as `yuva420p` WebM. The pipeline's pitfalls (all backed by measurements and regression tests) are documented in [docs/็ด ๆ็ฎก็บฟๅฎžๆต‹.md](docs/็ด ๆ็ฎก็บฟๅฎžๆต‹.md) (Chinese). ### ๐ŸŽจ Want your own character? **Step 0 (most important): generate the video on a green screen.** | Background | Consequence | | --- | --- | | **Pure green `#00FF00`** โญ | Easiest to key: green is far from the character's colours, so the matte is clean and **semi-transparent effects (smoke, glow) survive** | | Pure black | Works, but **flattens translucent content** โ€” white semi-transparent smoke becomes opaque dark grey and has to be reverse-engineered | | White / grey / gradient | Hard to key; tends to eat the character's pale parts (white hat, white apron) | Also put these in the generation prompt: **fixed frontal camera**, **no positional drift**, **identical first and last frame** (so loops don't jump), **no text or watermark**. The full prompts for this project's six clips are in [docs/่ง†้ข‘ๆ็คบ่ฏ-็ญ‰ๅพ…็กฎ่ฎค.md](docs/่ง†้ข‘ๆ็คบ่ฏ-็ญ‰ๅพ…็กฎ่ฎค.md) and [docs/่ง†้ข‘ๆ็คบ่ฏ-็‚นๅ‡ปๅ›žๅบ”.md](docs/่ง†้ข‘ๆ็คบ่ฏ-็‚นๅ‡ปๅ›žๅบ”.md). **Step 1: prepare the files** Name six clips by *what they mean*, and drop them into `assets/cook-ๅทฒๆŠ ๅ›พ/`: | File | Played when | Requirement | | --- | --- | --- | | `ๅพ…ๆœบ.mp4` | no task running | first/last pose identical, loopable | | `้ข ้”….mp4` | a turn is running | same | | `ไธŠ่œ.mp4` | a turn succeeded | plays once | | `ๆ‰˜่…ฎ.mp4` | waiting for your approval/answer | first/last pose identical, long-loopable | | `wink.mp4` | click reply | first/last pose = the idle pose | | `่ฝฌๅœˆ.mp4` | click reply | same | > You can also feed it a **per-frame cut-out PNG sequence** (true alpha), like this project's > final wok and serving clips โ€” best quality, no inference needed. See the read branch in > `scripts/build-assets.py`. **Step 2: run the pipeline and verify** ```bash npm run assets # writes six transparent WebM files into assets/pet/ npm run assets:check # verifies: transparent corners / dark regions kept / identical size / channel order ``` **Step 3: the only two places to touch when the framing changes** | What you changed | Also update | | --- | --- | | The crop box (`CROP` at the top of the script) | the hit-box ratio constants at the top of `runtime/electron-helper/renderer.js` | | The asset content | `ASSET_VERSION` in `renderer.js` (+1, otherwise the desktop window may load a stale file) | **Step 4: restart DSH** The desktop window reads assets into memory at startup, so a restart is required for a full swap. --- ## ๐Ÿ—‚๏ธ Project structure ``` dsh-cook-pet/ โ”œโ”€โ”€ ๐Ÿงฉ lib/ # host side: runs inside the DSH process โ”‚ โ”œโ”€โ”€ index.js # entry: listen to events, serve /state & /asset, launch the desktop window โ”‚ โ”œโ”€โ”€ state.js # 4-state machine (idle / cook / serve / think) โ”‚ โ”œโ”€โ”€ waiting.js # turns approvals/questions into "waiting" โ”‚ โ””โ”€โ”€ desktop.js # desktop-window lifecycle (incl. preparing Electron automatically) โ”‚ โ”œโ”€โ”€ ๐Ÿ–ฅ๏ธ runtime/electron-helper/ # the desktop window: a separate Electron process โ€” what you actually see โ”‚ โ”œโ”€โ”€ main.js # transparent ยท always-on-top ยท frameless + click-through + right-click resize โ”‚ โ”œโ”€โ”€ renderer.js # animation playback, hit-testing, dragging โ”‚ โ””โ”€โ”€ preload.js # IPC bridge between renderer and main โ”‚ โ”œโ”€โ”€ ๐ŸŽฌ assets/pet/*.webm # six transparent clips (514ร—512, VP9 alpha) โ”œโ”€โ”€ ๐Ÿ› ๏ธ scripts/ # asset pipeline, pre-publish checks, Electron setup โ””โ”€โ”€ ๐Ÿ“š docs/ # detailed docs (not published) ``` **How it runs:** ``` DSH host process (lib/index.js) โ”œโ”€ listens to session/event โ†’ lib/state.js (4 states) โ”œโ”€ listens to approvals/questions โ†’ lib/waiting.js ("waiting") โ””โ”€ exposes /dsh-cook-pet/state and /asset/* โ”‚ 1 s polling โ–ผ Electron desktop window (runtime/electron-helper) transparent ยท frameless ยท floats above all apps ``` > No build step: `lib/*.js` is hand-written plain JS that DSH loads directly. > There is no in-page version either โ€” Zhaozhao exists only as this always-on-top window, > so she stays visible even when DSH is minimized. --- ## ๐Ÿงฉ Compatibility | Item | Status | | --- | --- | | DSH | tested on `0.2.0-rc.1` / `0.2.0-rc.2` / `0.2.1-rc.1` / `0.2.1-rc.2` (`engines.dsh: >=0.2.0`) | | Desktop window | tested on Windows (Electron 43, `transparent + alwaysOnTop`); macOS/Linux untested | | Node | โ‰ฅ 22.12 | --- ## โš ๏ธ Known limitations 1. **Part of the alpha is inferred**: the sources have no alpha channel. Four clips derive it from a black/green background; the wok and serving clips use per-frame cut-out PNG sequences (true alpha). 2. **The serving clip is 30 fps but had 5 frames deleted** (4.73 s โ†’ 4.57 s), so a one-frame hiccup is theoretically possible. 3. **Asset changes need a restart**: `?v=` busts the cache, but the desktop window reads assets into memory at startup. 4. **Click replies are a random 1-of-2**, not position/count based. 5. **No settings page**: size cycles on right-click (110 / 138 / 190 / 260), position by dragging (both persisted). 6. **She exists only in the desktop window**: Electron needs a ~100 MB one-time download. **There is no in-page version** โ€” if Electron can't start (download blocked, heavily restricted environment) you won't see her, but DSH itself is unaffected. --- ## ๐Ÿ“œ License - The character "Zhaozhao" and all six animation clips were generated for this project. - Code licensed under **MIT** โ€” see [LICENSE](LICENSE). ## ๐Ÿ“š Further docs | Document | Content | | --- | --- | | [docs/็ด ๆ็ฎก็บฟๅฎžๆต‹.md](docs/็ด ๆ็ฎก็บฟๅฎžๆต‹.md) | Measured data for the six clips: dimensions, frames, alpha coverage, crop-box derivation | | [docs/่ง†้ข‘ๆ็คบ่ฏ-็ญ‰ๅพ…็กฎ่ฎค.md](docs/่ง†้ข‘ๆ็คบ่ฏ-็ญ‰ๅพ…็กฎ่ฎค.md) | Full generation prompt for the chin-on-hand animation | | [docs/่ง†้ข‘ๆ็คบ่ฏ-็‚นๅ‡ปๅ›žๅบ”.md](docs/่ง†้ข‘ๆ็คบ่ฏ-็‚นๅ‡ปๅ›žๅบ”.md) | Full generation prompts for the wink / spin click replies |