zhu1090093659/dsh-web-ui--packages-dsh-pet ↗★ 3.6k
@linxin666/dsh-pet
Multi-pet companion plugin for the dsh web GUI: a registry-driven floating pet that reacts to model activity, with per-pet naming, petting/feeding interactions and an affinity score
AI Analysis
在 DSH 网页界面右下角添加互动桌面宠物。适合希望在等待模型思考时进行趣味互动、提升使用体验的用户。
Install
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:zhu1090093659/dsh-web-ui#aeaaca8692376f442a1761e56273c574027ce96d&path:packages/dsh-petREADME
Read the full README ↗dsh-pet — Multi-pet companion plugin
English | 中文
A registry-driven desktop companion for DeepSeek Harness — the built-in whale girl plus any pet you drop in.
While the model thinks, you wait — your pet swims. It follows official session activity and switches animations while waiting, thinking, using tools, composing a reply, celebrating completion, or reporting failure; you can also pat its head, feed it dried fish, and watch its affinity grow. Pets are registry entries, not code: every pet is one pet.json manifest plus one atlas image, and the host discovers them at startup.
Re-implemented from the pet feature of the Codex desktop app, as an official DSH plugin shape (cordis bundle: host half + client half in one package).
Features
| Feature | Description |
|---|---|
| Multi-pet registry | The host scans built-in assets/, the hatch-pet custom pets directory, and composed config entries; each pet is a manifest plus an atlas |
| Pet selection in settings | The plugin settings card lists every registered pet; switching persists and the sprite swaps immediately |
| Per-pet naming | Rename from the hover panel; each pet keeps its own name (stored per pet id, migrated from the legacy flat name) |
| State animation | Official session activity → 9-state animation: thinking → running, tool → running-right, review → review, waiting → waiting, done → jumping, failed → failed |
| Head-pat interaction | Click the pet → bubble feedback + affinity +1 (10s cooldown) |
| Feeding | Hover panel 喂食 (Feed) → consumes 1 dried fish + affinity +5 (30s cooldown) |
| Treat economy | Dried-fish stock (cap 20): +1 every 30 rounds of work, +1 every 300 minutes (5 hours) — 10x rarer than the original cadence |
| Affinity | +1 per round completed; 9 levels: 幼鲸 → 伙伴 → 挚友 → 深海羁绊 → 心有灵犀 → 传说羁绊 → 神话羁绊 → 永恒之契 → 鲸生共渡 (capped at 999,999,999) |
| Dragging | Hold and drag the pet to reposition; position persisted |
| Hide/Summon | Hover panel 隐藏 (Hide); after hiding, a 召唤{name} (Summon {name}) button appears |
| Witty remarks | Built-in remark library (10 lines per event) plus per-pet custom lines from a manifest remarks block — community PRs give their pet its own voice |
| Status bubbles | Each concurrently active top-level session gets its own bubble, stacked above the pet (up to 12); subagent sessions report through their spawning conversation and never occupy a bubble of their own; click a bubble to jump to its session; transient interaction feedback temporarily takes priority |
| Multi-session activity | The pet is host-global: the most recent meaningful event drives the sprite animation while every active top-level session reports its own state in a separate bubble; completed turns from every session (subagents included) contribute affinity and treats |
Pet contract
A pet is a directory holding one pet.json manifest and one atlas image. Nothing else is required — no host or client code changes.
{
"id": "whale-girl", // unique lowercase kebab id
"displayName": "鲸鱼娘", // shown in the settings selector and panel
"description": "A soft healing whale-girl.", // optional
"spritesheetPath": "spritesheet.webp", // atlas, relative to the manifest
"cell": { "width": 192, "height": 208 }, // optional; defaults to the Codex contract
"columns": 8, // optional; default 8
"frames": [6, 8, 8, 4, 5, 8, 6, 6, 6], // optional per-row frame counts
"tracks": { // optional per-track rhythm overrides
"idle": { "durations": [400, 400, 500, 400, 400, 500] }
},
"remarks": { // optional witty remarks (one line or a pool per slot)
"pet": "摸摸水獭的头~",
"feed": ["小鱼干真香", "再来一条~"]
}
}
- The atlas is an 8-column × 9-row grid (192×208 cells by default); rows are fixed in this order: 0 idle, 1 running-right, 2 running-left, 3 waving, 4 jumping, 5 failed, 6 waiting, 7 running, 8 review. Unused cells stay fully transparent.
- The optional remarks block overrides the reaction bubbles the pet speaks on pet / petCooldown / feed / feedCooldown / noTreats events. Each slot accepts one line or a pool of lines (cycled round-robin); a declared slot replaces the built-in pool for that slot only. This is how community contributions give their pet its own witty voice.
framescounts the used columns per row (defaults to the hatch-pet contract table[6, 8, 8, 4, 5, 8, 6, 6, 6]);tracksoverrides per-frame durations (cycled to the row's frame count),loop, andfallbackper animation (defaults: everything loops;jumpingandfailedhold their last frame, then fall back toidle).
Where pets come from (later sources override earlier ones on id collision):
- Built-in:
assets//pet.jsonin this package. - Custom pets:
${CODEX_HOME:-~/.codex}/pets/ /pet.json— the hatch-pet pipeline stages its output there, so a hatched pet appears in the selector with no further wiring. - Composed:
PetConfig.petsmanifest entries passed to the plugin by the embedding application.
The registry is built once at host startup; add or change a pet, then restart dsh web.
Animation preview
The sprites are an 8-column × 9-row atlas (192×208 cells) generated by the hatch-pet pipeline; below are previews of each state:
| idle | waiting | running | jumping |
|---|---|---|---|
![]() | ![]() | ![]() | ![]() |
| waving | review | failed | move left/right |
|---|---|---|---|
![]() | ![]() | ![]() | ![]() |
Architecture
dsh-pet/
|-- src/
| |-- index.ts # host half: plugin entry (registry build, settings section, routes)
| |-- registry.ts # multi-pet contract: manifest scan + normalization (assets + custom pets)
| |-- service.ts # PetService: pet selection + state machine + affinity + config
| |-- state.ts # pet state machine: projected session activity → 9 state animations
| |-- remarks.ts # witty-remark library: built-in pools + per-pet overrides + picker
| |-- affinity.ts # affinity ledger (pure functions + cooldowns)
| |-- treats.ts # dried-fish stock ledger
| |-- persist.ts # persistence ($DSH_HOME/pet.json: selection + per-pet names, atomic write)
| |-- routes.ts # /api/pet/* JSON API + /pet//* asset routes
| `-- client/ # browser half
| |-- index.ts # global mount (createRoot → body) + registry fetch + polling + wiring
| |-- PetDockEntry.tsx # global floating entry (document.body, always shown)
| |-- PetSprite.tsx # definition-driven floating sprite (portal + rAF + dragging)
| |-- PetSettingsCard.tsx # settings card: pet selector + display layout
| |-- spritesheet.ts # atlas geometry helpers + track trimming
| `-- pet.module.css
|-- assets/whale/ # built-in whale-girl (pet.json + spritesheet.webp + previews)
`-- cordis.patch.yml # bundle patch: inserts the pet plugin row
Data flow
official session events (turn/step/chunk/tool) ----\
> PetService (host) /` — the plugin self-sufficiently provides its own API and assets (the same pattern as dsh-remote-web-ui's `/api/pair`).
## Install
Install the family aggregate package `@linxin666/dsh-web-ui-all` (all plugins and skins in one) or this plugin alone:
```sh
### From npm (recommended)
dsh plugin --profile web add @linxin666/dsh-pet
### From the repository (development)
git clone https://github.com/zhu1090093659/dsh-web-ui.git
cd dsh-web-ui
pnpm install && pnpm -r build
dsh plugin --profile web add link:$(pwd)/packages/dsh-pet
After installing, restart dsh web — your selected pet appears at the bottom-right of the interface. In link mode, pnpm build and refresh the page after a code change; no reinstall needed.
Development
pnpm build # tsc -b (types+declarations) && tsdown (node half + browser bundle)
pnpm test # vitest unit/component tests (registry / event projection / state / UI / ledgers)
pnpm prepare # transpile-only build (no type checking, for consumer installs)
pnpm typecheck # type check only
The browser bundle rides the window.__ModuleLoader__.load contract; React/cordis and so on resolve from the loader's module table (external); CSS Modules are inlined by lightningcss as ``.
Sprites and animation-track calibration
The built-in whale-girl atlas is generated by the hatch-pet pipeline as 9 states × 8 columns: assets/whale/spritesheet.webp (1536×1872, 8 columns × 9 rows of 192×208 cells) + assets/whale/pet.json. The frame count and rhythm of each row live in that manifest's frames and tracks fields — the whale girl carries its own slower healing durations, while pets without overrides follow the hatch-pet contract rhythm. Redoing artwork therefore only edits assets/whale/pet.json (row-order contract: 0 idle / 1 running-right / 2 running-left / 3 waving / 4 jumping / 5 failed / 6 waiting / 7 running / 8 review).







