{"_id":"@aonochano/remi-pet-dsh","name":"@aonochano/remi-pet-dsh","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@aonochano/remi-pet-dsh","version":"0.1.0","description":"Remielle chibi Spine pet for the DeepSeek Harness web page — a dual-face Cordis plugin (node half serves Spine assets, browser half renders the pet into the shell.overlay slot)","type":"module","main":"lib/index.js","exports":{".":"./lib/index.js","./client":"./dist/client.js","./package.json":"./package.json"},"dsh":{"client":{"platform":"web"}},"scripts":{"build":"node build.mjs","prepare":"node build.mjs","test":"node test/run-tests.mjs"},"keywords":["deepseek-harness","dsh","spine","desktop-pet","mascot","web"],"author":{"name":"Watanabe Satoru","url":"AonoChano"},"license":"MIT","peerDependencies":{"@deepseek-ai/cordis":"^4.0.1","@deepseek-ai/dsh-host-webserver":"^0.1.0-rc.6","react":"^18.2.0"},"devDependencies":{"@esotericsoftware/spine-webgl":"~4.2.0","@rollup/plugin-node-resolve":"^15.3.0","react":"^18.2.0","rollup":"^4.9.0"},"gitHead":"65493ad8ab0259f20d05926f789af5fb0d416126","_id":"@aonochano/remi-pet-dsh@0.1.0","_nodeVersion":"22.15.0","_npmVersion":"11.17.0","dist":{"integrity":"sha512-oYeX23WsaW6Hju5X4JKBwbJVJKV0Tv3lDiH0A5LgtkJ0P38o6WA0wpOtb7EvrQU+qoS2glVteTE0FX1jMw/Pmg==","shasum":"c12a899101f5b65fcc3e6c12a8c71ac8e9f01bb9","tarball":"https://registry.npmjs.org/@aonochano/remi-pet-dsh/-/remi-pet-dsh-0.1.0.tgz","fileCount":7,"unpackedSize":1903685,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIBukc3EPhRLK8Qn3hemfPYuetGQxNpFWKRvL9P/akb57AiB1wa6sGxKNyD1C5T+xoOZi1rh5VDJNavw7h6JfiLrVmw=="}]},"_npmUser":{"name":"aonochano","email":"CYChano@outlook.com"},"directories":{},"maintainers":[{"name":"aonochano","email":"CYChano@outlook.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/remi-pet-dsh_0.1.0_1787148821879_0.701088525817537"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-19T14:13:40.809Z","0.1.0":"2026-08-19T14:13:42.064Z","modified":"2026-08-19T14:13:43.456Z"},"maintainers":[{"name":"aonochano","email":"CYChano@outlook.com"}],"description":"Remielle chibi Spine pet for the DeepSeek Harness web page — a dual-face Cordis plugin (node half serves Spine assets, browser half renders the pet into the shell.overlay slot)","keywords":["deepseek-harness","dsh","spine","desktop-pet","mascot","web"],"author":{"name":"Watanabe Satoru","url":"AonoChano"},"license":"MIT","readme":"# 🐾 @aonochano/remi-pet-dsh — Remielle Chibi Pet for the DeepSeek Harness Web Page\r\n\r\n**Q 版蕾米埃尔 · DeepSeek Harness 网页桌宠插件**\r\n\r\nA drop-in **dual-face Cordis plugin** that renders the Spine chibi Remielle as a\r\nfloating, draggable pet on the [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)\r\nWeb GUI (`dsh web`). The node half serves the Spine assets over an HTTP route;\r\nthe browser half (a pre-built bundle) registers into the `shell.overlay` slot.\r\n\r\n```\r\n┌─────────────────────────── DeepSeek Harness Web page ───────────────────────────┐\r\n│                                                                                  │\r\n│   ┌───────────────────────────────┐            ┌───────────┐                     │\r\n│   │ session list                  │  chat      │   🐾      │ ← drag / click      │\r\n│   │ ...                           │  ...       │  (Spine)  │   bottom-right      │\r\n│   └───────────────────────────────┘            └───────────┘                     │\r\n│   [Settings] [🐾] ← sidebar.footer.action toggle                                  │\r\n└──────────────────────────────────────────────────────────────────────────────────┘\r\n```\r\n\r\n> [!WARNING]\r\n> **Fan-made derivative assets.** Remielle and all related intellectual property\r\n> belong to **HoYoverse / miHoYo**. This project is for **personal and\r\n> educational use only** — see [`ASSET_LICENSE.md`](../../ASSET_LICENSE.md) at\r\n> the repository root.\r\n\r\n---\r\n\r\n## Features\r\n\r\n| Feature | Detail |\r\n| :------ | :------ |\r\n| Spine rendering | `@esotericsoftware/spine-webgl` **bundled inline** — no extra runtime deps |\r\n| Animations | idle (`a`), cute reaction (`e`), user drawing (`d` → `d_win`), appreciation (`c`) after 12 idle loops |\r\n| **State-linked** | Reads `ctx.sessions.list` **and** the current session's `ConversationSnapshot`: **thinks** (`b`) while reasoning/tool calls/jobs run, **writes** (`d` loop) while the assistant streams **formal text**, **pleads** (`e`, teary pleading) while waiting for approval / AskUser questions / plan review; when the whole conversation flow ends, the pet puts the brush down (`d_win`) and **appreciates the work** (`c`) — with a 50% chance it also **overlays the golden light** (`light`) on a separate animation track for a few seconds, never locking the character |\r\n| Drag | Pointer-based, clamped to the viewport; **pixel-accurate click-through** — only pixels the character actually draws intercept clicks (a `gl.readPixels` alpha probe drops pointer-events over transparent areas, so the page underneath stays clickable) |\r\n| Persistence | Position + hidden state in `localStorage` (`remi-pet.pos` / `remi-pet.hidden`) |\r\n| Show/hide | `IconSparkle16` + current-state label 「宠物：显示/隐藏」 in the sidebar footer, aligned beside Settings (theme tokens) |\r\n| **Plugin config card** | Settings → Plugins → 插件配置: a collapsible card like the shipped ones (header + in-place body, ⓘ tooltips on every control) — pet size (120–320px), celebration-light probability, agent-state link, position reset, show/hide; all applied immediately |\r\n| Hide/show reliability | Engine is created/disposed with visibility — hiding disposes the WebGL context, showing rebuilds it (no page refresh needed) |\r\n| No server changes | Assets served by the plugin's own node half via `/remi-pet/assets/*` |\r\n| Graceful degradation | WebGL/asset failure shows a small message instead of breaking the page |\r\n\r\n**Roadmap** (not in v1): eye tracking, swirl eyes, a settings page, i18n.\r\n\r\n---\r\n\r\n## How it works (the integration contract)\r\n\r\nThird-party packages plug into the DSH **web profile** (`$DSH_HOME/profiles/web`)\r\nin two steps: install the package into the profile, and add one loader row to\r\nthe profile's patch layer. No DSH source changes.\r\n\r\n- **Package manifest**: `dsh.client: { platform: \"web\" }` + `exports[\"./client\"]`\r\n  → the built browser bundle. `@deepseek-ai/dsh-client-modules` scans loader\r\n  entries for this declaration, serves the bundle at `/plugins/<id>/client.js`\r\n  and injects it into the page boot graph (`window.__DSH_BOOT__`).\r\n- **Bundle format**: a classic script calling\r\n  `window.__ModuleLoader__.load({ id, factory })`; the factory receives a\r\n  synchronous `require` that may resolve **only** the shell's seed words and\r\n  shell-own modules (`react`, `@deepseek-ai/dsh-client-ui-primitives`,\r\n  `@deepseek-ai/cordis`, …). Everything else — including the Spine runtime — is\r\n  **inlined at build time** (see `build.mjs`, which keeps only `react` and\r\n  `@deepseek-ai/dsh-client-ui-primitives` external).\r\n- **UI seats**: `shell.overlay` — a frame-wide floating layer above every\r\n  column; additive (list) registration, no replacement risk. The show/hide\r\n  toggle sits in `sidebar.footer.action` beside Settings, and the\r\n  plugin-configuration card sits in `settings.plugin.item` (Settings → Plugins\r\n  → 插件配置). The card is self-contained and localStorage-backed — it does\r\n  not use the host settings namespace, whose exposure is an upstream\r\n  allowlist. `settings.plugin.item` is a **keyed** slot since dsh 0.1.0-rc.7;\r\n  the registration probes keyed then list, so a protocol flip degrades to \"no\r\n  card\" instead of failing. Every UI seat and the session feed are isolated\r\n  with try/catch — the plugin never throws through `apply`, so a DSH update\r\n  cannot take the page down.\r\n- **State feed**: `ctx.sessions.list` (`ObservableSnapshot`) — the same\r\n  `useSessions` standard feed the sidebar reads. The plugin derives the pet\r\n  activity (idle / thinking / writing / waiting) from the current session's\r\n  `running`, `pendingInteraction`, `jobsBySession` and the `ConversationSnapshot`\r\n  (`partial.blocks` kinds, `runningCalls`, `pending`).\r\n- **Assets**: the node half registers a `webServer` prefix route (`/remi-pet`)\r\n  serving `assets/` from the package directory (exact > longest-prefix >\r\n  fallback match order, so it never collides with `/plugins` or the SPA\r\n  fallback).\r\n\r\n---\r\n\r\n## Quick start (for end users)\r\n\r\nPrerequisites: Node.js ≥ 18, a running `dsh web` profile.\r\n\r\n```bash\r\n# 1. One-command install from npm (no need to clone the repository)\r\ndsh plugin --profile web add @aonochano/remi-pet-dsh\r\n\r\n# 2. Add one row to the profile patch layer\r\n#    $DSH_HOME/profiles/web/cordis.patch.yml\r\n```\r\n\r\n```yaml\r\n- insert:\r\n    - id: remi-pet\r\n      name: '@aonochano/remi-pet-dsh'\r\n```\r\n\r\n```bash\r\n# 3. Restart dsh web — the pet appears bottom-right\r\n```\r\n\r\n**Notes**\r\n\r\n- The install step runs `pnpm add <pkg>` inside the profile; the profile's\r\n  `pnpm-workspace.yaml` uses a hoisted linker, so the package is linked into\r\n  the profile's `node_modules`.\r\n- Not published yet? Install from a local checkout instead:\r\n  `dsh plugin --profile web add \"<repo>\\mascots\\dsh-web\"` (absolute path, since\r\n  pnpm runs inside the profile directory). For offline distribution,\r\n  `npm pack` in `mascots/dsh-web` produces an installable tarball.\r\n- The bundle is built by the `prepare` script on install — `dist/` is not\r\n  committed (repository `.gitignore` ignores `dist/`).\r\n\r\n**Uninstall**\r\n\r\n```bash\r\n# remove the row from cordis.patch.yml, then:\r\ndsh plugin --profile web remove @aonochano/remi-pet-dsh\r\n```\r\n\r\n---\r\n\r\n## Development\r\n\r\n```bash\r\ncd mascots/dsh-web\r\nnpm install\r\nnpm run build   # rollup: src/client -> dist/client.js (wrapped loader script)\r\nnpm test        # behavior/activity/bundle/route suites\r\n```\r\n\r\nThe profile install is a **symlink to this directory** (`pnpm add <path>`), so\r\n**client-half** changes only need `npm run build` + a page refresh; **node-half**\r\nchanges (`lib/index.js`) need a `dsh web` restart.\r\n\r\n```\r\nmascots/dsh-web/\r\n├── package.json          # dual-face manifest: dsh.client + exports[\"./client\"]\r\n├── build.mjs             # rollup + loader-script wrapper (+ Spine license)\r\n├── assets/               # remi.json / leimi.atlas / leimi.png (served by node half)\r\n├── lib/index.js          # node half: /remi-pet asset route (webServer prefix)\r\n├── src/client/\r\n│   ├── factory.js        # build entry: export default plugin\r\n│   ├── plugin.js         # { inject: ['slots'], apply } — slots + sessions feed\r\n│   ├── PetView.js        # overlay React view: drag, click/dblclick, canvas\r\n│   ├── ToggleView.js     # sidebar.footer.action row (IconSparkle16 + 宠物)\r\n│   ├── ConfigCard.js     # settings.plugin.item card (size/light/link/reset)\r\n│   ├── spine.js          # SpineCanvas app lifecycle (drawSkeleton(…, false)!)\r\n│   ├── behavior.js       # pure animation state machine incl. activity drive\r\n│   ├── activityStore.js  # sessions.list -> activity derivation + notify\r\n│   ├── settings.js       # reactive pet settings (size / light / activity)\r\n│   ├── css.js            # refcounted style-tag injection\r\n│   ├── persist.js        # localStorage position/visibility/settings\r\n│   └── store.js          # hidden-state + position-reset store\r\n└── test/                 # node --test suites\r\n```\r\n\r\n### Rendering gotchas (learned the hard way)\r\n\r\n- The atlas has **no `pma` tag** → always `renderer.drawSkeleton(skeleton, false)`.\r\n  Passing `true` produces bright edge artifacts on overlapping meshes.\r\n- The canvas buffer is sized by `renderer.resize(ResizeMode.Expand)` from the\r\n  CSS size × devicePixelRatio — the host size is configurable (120–320px) and\r\n  the skeleton scale follows it (`petSize / BASE_SIZE`, base 420).\r\n- The single-click / double-click race is resolved with a 260ms timer in\r\n  `PetView`; the behavior machine itself is event-driven and framework-free.\r\n\r\n### Configuration\r\n\r\nNo loader config in v1. User state lives in `localStorage` (edited from the\r\nplugin-configuration card and the sidebar toggle):\r\n\r\n| Key | Meaning |\r\n| :-- | :------ |\r\n| `remi-pet.pos` | `{\"x\":…,\"y\":…}` left/top CSS px after the last drag |\r\n| `remi-pet.hidden` | `\"1\"`/`\"0\"` — pet visibility |\r\n| `remi-pet.size` | Host CSS size in px (120–320) |\r\n| `remi-pet.lightChance` | Golden-light overlay probability, 0–1 |\r\n| `remi-pet.activityEnabled` | `\"1\"`/`\"0\"` — whether the pet animates with the agent state |\r\n\r\n---\r\n\r\n## License\r\n\r\n- **Code**: MIT (see the repository root `LICENSE`).\r\n- **Character & art**: property of miHoYo / HoYoverse — personal/educational\r\n  use only (`ASSET_LICENSE.md`).\r\n- **Spine runtime**: redistributed under the Spine Runtimes License Agreement\r\n  (Esoteric Software); the notice is prepended to `dist/client.js` by\r\n  `build.mjs`.\r\n","readmeFilename":"README.md","_rev":"1-0d4e57eda727b2cdf0882b38f34d9a6b"}