跳到主要内容

dsh-live2d-pet

已验证

dsh-live2d-pet · v0.1.2 · MIT · Web 界面

Live2D companion for DeepSeek Harness: render a Cubism model (e.g. the official Haru sample) in the web UI with state-driven expressions and mouse tracking. · DSH 桌宠的 Live2D 渲染插件

安装

dsh plugin add dsh-live2d-pet

用 dsh --profile default --dump-config 确认 layer 已生效 —— 参见安装指南。

源码

发布到 npm 但没有公开仓库。安装前请检查包内容。

标签

说明文档

dsh-live2d-pet

A Live2D companion pet for DeepSeek Harness — renders a Cubism model as a floating companion in the Web UI, with state-driven expressions, mouse tracking, drag repositioning, and a header toggle button.

DSH 桌宠 Live2D 插件:在 Web UI 里渲染一个 Cubism 模型作为浮动桌宠,支持状态联动表情、鼠标跟随、拖拽摆位、顶栏一键开关。

preview

The bundled official Haru sample model (Live2D Inc.):

附带官方 Haru 示例模型(© Live2D Inc.):

haru sample


Table of Contents / 目录


Features / 功能

English 中文
🐳 Real Live2D Powered by pixi-live2d-display (Cubism 4), rendered on a transparent floating canvas 基于 pixi-live2d-display(Cubism 4)渲染,透明浮动画布
👀 Mouse tracking Head turns and gaze follow your cursor (ParamAngleX/Y, ParamEyeBallX/Y); the iris is written every frame so expression snapshots can't clobber it 头部与视线跟随鼠标;瞳孔每帧写入,表情快照覆盖不了
😊 State-driven expressions The model's .exp3.json expressions map to harness activity states (idle/waiting/thinking/tool/done/failed/sleep/…) plus hover & drag 表情按 AI 活动状态切换(idle/waiting/thinking/tool/done/failed/sleep…)+ hover/拖拽反馈
🎬 Idle motion + fidgets The model's idle motion runs constantly; random tap motions play as idle micro-antics 常驻待机动作;空闲时随机播小动作(发呆/挠头)
🎛️ Config-driven geometry Size / position / offsets come from cordis.patch.yml — tweak without rebuilding 大小/位置/偏移全在 cordis.patch.yml 配置,改配置不用重编译
🖱️ Drag repositioning Drag the pet anywhere; the offset persists in localStorage (with a viewport sanity-clamp against off-screen bugs) 可拖拽到任意位置,偏移持久化(带防怼出屏幕的钳制)
💾 Toggle button & persistence A ✨ header button (next to the 💰 wallet) shows/hides the pet instantly; state survives refreshes & restarts 顶栏 ✨ 按钮(钱包旁边)一键开关,状态跨刷新/重启保留
🔗 Session state linkage Watches the DSH conversation session — deep thinking / sleep / celebrate are time-driven 监听 DSH 会话——深思考 / 睡觉 / 庆祝等按时间驱动

Install / 安装

# from a local checkout
dsh plugin --profile web add link:/path/to/dsh-live2d-pet

# or from npm (once published)
dsh plugin --profile web add dsh-live2d-pet

Use a different profile by changing --profile <name> (e.g. test).

换成自己的 profile 只需改 --profile <name>(例如测试端 test)。


Enable / 启用(必读)

⚠️ The model does NOT appear automatically after install. You must enable it first — either click the ✨ button in the session header, or run one line in the browser console. ⚠️ 装完不会自动显示! 必须先启用——点顶栏 ✨ 按钮,或在浏览器控制台执行下面这行:

localStorage.setItem('dsh-live2d-pet', '1')
  1. Open the DSH web page (the profile where you installed the plugin)
  2. Press F12 → Console tab
  3. Paste localStorage.setItem('dsh-live2d-pet', '1') and press Enter
  4. Reload the page (F5)

The pet then floats at the anchor position (default: bottom-right / above the composer). The setting persists across refreshes and server restarts — you only do this once per browser.

启用后宠物出现在锚定位置(默认:右下/输入框上方)。设置跨刷新和重启保留,每个浏览器只需设置一次。

To disable / 关闭:

localStorage.setItem('dsh-live2d-pet', '0')

Configuration / 配置

cordis.patch.yml (in the plugin package):

- insert:
    - id: live2d-pet
      name: dsh-live2d-pet
      config:
        model: haru/haru_greeter_t03.model3.json  # path under assets/live2d/
        size: 320                                  # canvas size, px
        right: 24                                  # distance from right edge, px
        bottom: 100                                # distance from bottom edge, px
        offsetX: 0                                 # extra horizontal shift, px
        offsetY: 0                                 # extra vertical shift, px
Key Meaning / 含义 Default / 默认
model Path to the .model3.json under assets/live2d/ haru/haru_greeter_t03.model3.json
size Canvas size in px (the model is scaled to fit by height) 320
right Distance from the right edge when not anchored 24
bottom Distance from the bottom edge when not anchored 20
offsetX / offsetY Extra shift applied on top of the anchor position (for fine-tuning) 0

The pet anchors to the chat composer ([data-composer-seat]) every frame and follows internal chat scrolling; right/bottom are only used as fallbacks. offsetX/offsetY are the knobs for final fine-tuning.

宠物每帧锚定到聊天输入框([data-composer-seat])并跟随内部滚动;right/bottom 只是兜底位置。微调用 offsetX/offsetY。


State → Expression map / 状态表情映射

The bundled Haru sample model ships expressions f00..f08. The default mapping (edit PHASE_EXPRESSION in src/client/index.ts to customize):

附带模型 Haru 提供表情 f00..f08。默认映射如下(改 src/client/index.ts 的 PHASE_EXPRESSION 可自定义):

State / 状态 Expression / 表情 Meaning / 含义
idle f00 neutral / 中性
waiting f01 expectant, mouth slightly open / 等待,微张嘴
thinking f02 concentrating, brows knit / 思考,皱眉
deep (thinking > 5s) f02 same concentration face / 深思考同款
tool (a tool is running) f03 working, grin / 干活,咧嘴
done (turn finished) f04 happy squint / 完成,眯眼笑
celebrate (done + ≥3 tools) f04 happy / 庆祝同款
failed f00 sample set has no sad face → neutral / 无委屈脸,用中性
drag (being dragged) f05 big grin / 拖拽,大笑
sleep (idle > 60s) f08 calm / 平静
hover (idle/sleep) f06 surprise, wide eyes / 惊讶瞪眼

Motions / 动作:Haru only ships Idle + Tap groups. The idle motion runs constantly; random Tap motions play as idle fidgets. There are no per-state body animations on the sample — custom models can add a TOOL_MOTION map.

Haru 只带 Idle + Tap 两组动作:待机动作常驻,空闲时随机播 Tap 小动作。 样例模型没有按状态的专属动作——自定义模型可加 TOOL_MOTION 映射。


Debug handles / 调试句柄

Open the browser console (F12) on the page with the pet:

Handle / 句柄 Purpose / 用途
window.__dshLive2dPetModel The loaded Live2DModel instance / 已加载的模型实例
pet('expr','motion') One-shot test: set expression + play motion (either may be omitted) / 一句测试:设表情+播动作(可省略)
window.__dshLive2dPetToggle() Toggle the pet on/off programmatically / 编程式开关宠物
window.__dshLive2dPetFreeze = true Freeze automatic linkage (manual tests stick) — set false to resume. ⚠️ While frozen, real DSH turns will NOT update the pet. / 冻结自动联动(手动测试不被状态覆盖),false 恢复。⚠️ 冻结期间真实流程不会更新宠物
window.__dshLive2dPetRoot The React root handle / React 根句柄

Example / 示例:

pet('f02', 'Tap')   // thinking face + a random tap motion / 思考脸 + 随机小动作
pet('f04')          // done face only / 只切完成表情
window.__dshLive2dPetFreeze = false   // resume auto linkage / 恢复自动联动

Bring your own model / 换自己的模型

  1. Put your model folder under assets/live2d/<model>/ (.model3.json, .moc3, textures, .physics3.json, expressions/, motions/).
  2. Point cordis.patch.yml config.model at it.
  3. Update PHASE_EXPRESSION (and optionally TOOL_MOTION) in src/client/index.ts to your model's expression/motion group names, then pnpm build.
  4. Sync lib/ + assets/ to the target profile's node_modules/dsh-live2d-pet/, restart the server (patch/model changes), hard-refresh the page (Ctrl+Shift+R) — model/expression changes need the hard refresh to load.

把模型放进 assets/live2d/<模型名>/,配置 model 指向它,改好表情/动作映射, pnpm build 后同步到目标 profile,重启服务端(patch/模型改动),硬刷新页面 (Ctrl+Shift+R,模型/表情改动必须硬刷新才加载)。

The architecture is model-agnostic — the pet is the harness, the model is the skin. / 架构与模型无关——宠物是壳,模型是皮。


Architecture & pitfalls / 架构与排雷

How it works / 工作原理

DSH web page
├─ <head> script src="/pet/live2d/live2dcubismcore.min.js"   ← host half, injected via tapIndex
├─ client bundle (lib/client.js, single inlined file)
│   ├─ pixi.js + pixi-live2d-display/cubism4 (fully inlined)
│   ├─ Live2DPet component: anchor / drag / expression / motion / mouse tracking
│   └─ apply(): mount + session-state linkage + ✨ toggle button (slots)
└─ host routes /pet/live2d/* (prefix, serves model assets)

Design constraints (all battle-tested) / 关键约束(全部实战排雷)

  1. No dynamic imports — the DSH client module system can't load split chunks (missed the module table). Everything is statically inlined into a single file. / 禁止动态 import——DSH 客户端模块系统不认分包,必须全内联单文件。
  2. Cubism core must load first — pixi-live2d-display checks window.Live2DCubismCore at module time; the host injects the core script into <head> before the bundle evaluates. / Cubism core 必须先行——host 在 bundle 执行前注入到 head。
  3. Use the cubism4 sub-entry — the main entry checks both Cubism 2 and Cubism 4 runtimes; import 'pixi-live2d-display/cubism4' skips the Cubism 2 check. / 只用 cubism4 子入口——绕过 Cubism 2 运行时检查。
  4. Prefix route without trailing slash — the webserver matches prefixes with ${prefix}/, a trailing slash becomes // and never matches. / 前缀路由不带尾斜杠——带尾斜杠会拼成双斜杠匹配不上。
  5. React must be external — an inlined second React instance breaks hooks in slots-rendered components (React error #321). react/react-dom come from the DSH host module table (same as dsh-emoji-wallet). / React 必须 external——内联第二份 React 会让 slots 组件 hook 崩溃,从 DSH 模块表拿。
  6. ctx.slots needs the inject declaration — both the package.json dsh.client.inject entry (@deepseek-ai/dsh-client-ui-slots) AND export const inject = ['slots'] in the bundle are required, or you get "cannot get property slots without inject". / slots 按钮要双重 inject—— package.json 包名 + bundle 里 export const inject = ['slots']。
  7. Physics output params are locked — on some models, physics-driven parameters (e.g. tail Param_Angle_Rotation_*) are overwritten every frame; motions can't move them. / 物理输出参数被锁——物理每帧覆盖,动作曲线写不动。
  8. Drag offset sanity clamp — a leftover absurd dsh-live2d-pet-drag localStorage value can push the pet off-screen; the loader clamps to the viewport and drops bad values. / 拖拽偏移钳制——异常残留偏移会被自动清除, 防止宠物被怼出屏幕。
  9. Toggle must reset the session identity — tearDownSession() resets currentSessionId/lastKey, otherwise toggling the pet off/on kills the state-linkage refresh loop forever (pet renders but never reacts). / toggle 必须重置会话身份——否则开关一次后状态联动永久死亡(宠物能显示但不再反应)。

Node builtins in dependencies

@pixi/utils imports node's url; scripts/url-shim.js (a copy of the dsh-client-url-shim plugin) rewrites it to a browser-safe module at build time.


Development / 开发

pnpm install
pnpm build        # tsdown → lib/client.js (browser) + lib/index.mjs (node)
pnpm typecheck    # tsc --noEmit

Generator scripts / 生成器脚本

The plugin ships model-agnostic tooling under scripts/:

Script / 脚本 Purpose / 用途
scripts/gen-motions.mjs <assetsDir> [--force] Generate motion3.json actions + exp3.json expressions from code definitions. Existing *.exp3.json are SKIPPED by default (hand-tuned expressions win); --force overwrites them (use with care). / 从代码定义生成动作/表情;默认跳过已存在的 exp3(手动调参优先),--force 才覆盖
scripts/sync-expressions.mjs <assetsDir> Refresh the generator's expression defaults from the live exp3 files (keeps the generator in sync with hand-tuned assets). / 从资产现成 exp3 刷新生成器的表情默认值
scripts/expressions-data.mjs Generated data file (do not edit by hand). / 生成的数据文件,勿手改

Workflow for editing expressions / motions / 改表情/动作流程

  1. Hand-tune in Live2D Cubism Viewer (open the .model3.json, drag parameters / keyframes, export the exp3/motion3) — or edit the generator definitions.
  2. node scripts/sync-expressions.mjs <assetsDir> to refresh expression defaults (hand-tuning wins; the generator never overwrites existing exp3).
  3. pnpm build → sync lib/ + assets/ to the target profile.
  4. Hard-refresh the page (Ctrl+Shift+R) — model/expression changes need it.

人工调参用 Live2D Cubism Viewer 打开 .model3.json 拖参数/摆关键帧,导出 exp3/motion3 即生效。表情手动调参后跑同步脚本刷新生成器默认值——生成器永不覆盖 已有表情文件。


License / 许可

MIT. The bundled Haru model is © Live2D Inc., distributed under its official sample-model license for testing — replace it for production use.

MIT 许可。附带的 Haru 模型 © Live2D Inc.,按官方样例模型许可随附用于测试—— 生产环境请替换为自己的模型。