dsh-plugins/dsh-thought-buddy
A DeepSeek Harness Web plugin that puts a dynamic little buddy — a GrokBot-style animated avatar with a synchronized typewriter status line — right in front of the "Deep diving..." indicator.
已收录
2
Theme
Bundle 已验证
预览
功能介绍
在「Deep diving...」状态提示前,放一只动态小伙伴——GrokBot 风格动画头像,状态文字还会同步打字机变换。
适合
- 希望模型工作状态提示更生动的 DSH Web 用户。
- 偏好头像与打字机文字同步动画,且不想引入运行时依赖的用户。
不适合
- 需要改变模型执行、路由或结果的用户;该插件只调整工作状态的视觉呈现。
- 希望“减少动态效果”设置能停用全部动画的用户;表情切换和眨眼仍会保留。
README

dsh-thought-buddy

A DeepSeek Harness Web plugin that puts a dynamic little buddy — a GrokBot-style animated avatar with a synchronized typewriter status line — right in front of the “Deep diving…” indicator.
| English | 简体中文 |
dsh-thought-buddy is a pure client-side plugin for the DeepSeek Harness Web GUI. While the model is working, the status pill that reads Deep diving... grows a tiny Grok-style robot avatar: it blinks, swaps expressions with a springy morph, wanders its gaze, and gently bobs — all drawn live as SVG via requestAnimationFrame, with zero runtime dependencies. Every time the avatar switches expression, the status text rewrites itself with a typewriter effect (deleting character by character, then typing out the next word).
The avatar animation is ported from nasawz/GrokBot (a pure Flutter CustomPaint widget) to the web: all 25 expressions × 2 eyes × 48-point eye rings, 18 body shapes, and 39 states with their expression/blink cadences are preserved.

Features
| Feature | Description |
|---|---|
| GrokBot avatar | thinking-state expression pool [8,16,14,17,5] cycles automatically; springy morph between expressions; 320 ms blinks (random 3.5–7 s interval); spherical head-turn projection + wandering gaze; gentle 1.7 s breathing bob |
| Expression-synced typewriter | on every expression switch, the status text leaves Deep diving... via a typewriter effect (delete character by character, pause, then type out the next word) cycling through 55 candidates (Accomplishing…Working), e.g. Reticulating...; React re-renders never overwrite it (the text fiber’s children string never changes, so React bails out) |
| Emoji mode | fallback mode that cycles an emoji list (default 🤿 🫧 🌊 🐙 🔍 🧠 💭) with a pop-in on every switch |
| Theme aware | follows prefers-color-scheme: light #5b7fe5/#fffdf7, dark #6689ea/#181a15 (matching the DSH theme) |
| Reduced motion | under prefers-reduced-motion: reduce, the bob and head-turn sway are disabled; expression changes and blinks remain |
| Self-cleaning | the animation stops as soon as the status pill leaves the DOM; inserted nodes survive React re-renders and are re-asserted on the next mutation if ever removed |
Install
Copy the block below and paste it to your DSH agent (the assistant in this web GUI). The agent performs the install and verification for you — no manual npm or profile editing needed:
Install the @dsh-plugin/dsh-thought-buddy plugin into the profile I specify (or ask me if I didn't name one). The npm package name is `@dsh-plugin/dsh-thought-buddy`; use the GitHub source `github:dsh-plugins/dsh-thought-buddy`, or `file:<path>` / `link:<path>` for local development.
Steps:
1. Add the plugin dependency: `dsh plugin --profile <PROFILE> add @dsh-plugin/dsh-thought-buddy` (or the equivalent plugin-manager command for my profile).
2. Verify `node_modules/@dsh-plugin/dsh-thought-buddy` resolves and holds a built `lib/` directory (at least `lib/index.js` and `lib/client.js`). The runtime has no dependencies; building the TypeScript sources needs the `typescript` devDependency (`npm ci`/`npm install` once). If the artifacts are missing, run `npm run build` in the plugin directory and re-add.
3. Make sure the profile manifest's `dsh.profile.bundles` includes `@dsh-plugin/dsh-thought-buddy` — the bundle patch (`cordis.patch.yml`) inserts the plugin row automatically at boot.
4. Do not start the profile — install and verify only, then report what you changed.
Then restart dsh web, refresh the page, and send the model a message — the buddy appears in front of Deep diving.... All runtime options are set via localStorage (see below); there is no settings page.
Install from a local checkout (link:)
For local development, wire the plugin into a web profile via a link: dependency (example: C:\Users\Administrator\.dsh\profiles\web):
// package.json (profile)
"dependencies": { "@dsh-plugin/dsh-thought-buddy": "link:C:/path/to/dsh-thought-buddy" },
"dsh": { "profile": { "bundles": [ /* ... */, "@dsh-plugin/dsh-thought-buddy" ] } }
- Build the artifacts:
npm run build→ compiles TypeScript and produceslib/client.js+lib/index.js - In the profile, run
pnpm install(works offline) -
Restart
dsh web— the client-module manifest is composed at boot, so a new bundle needs a restart - Refresh the page and send the model a message
Configuration (localStorage, applied on reload)
| Key | Default | Description |
|---|---|---|
dsh-thought-buddy.enabled |
1 |
0 disables the plugin |
dsh-thought-buddy.mode |
avatar |
emoji switches to emoji cycling |
dsh-thought-buddy.size |
18 |
avatar size in px (8–64) |
dsh-thought-buddy.emojis |
🤿 🫧 🌊 🐙 🔍 🧠 💭 |
space/comma-separated emoji list (emoji mode) |
// console example
localStorage.setItem('dsh-thought-buddy.mode', 'emoji')
localStorage.setItem('dsh-thought-buddy.size', '22')
location.reload()
Development
dsh-thought-buddy/
├── ref/GrokBot/ # reference project (git-ignored, read-only)
├── src/
│ ├── index.ts # host half (no-op mount row)
│ └── client/
│ ├── data.ts # generated typed data: 25 expressions × 2 eyes × 48 points, 18 shapes, 39 states
│ └── index.ts # client engine: SVG avatar + typewriter + observer + apply()
├── scripts/
│ ├── gen-data-lib.mjs # Dart parsing + data.ts rendering helpers
│ ├── gen-data.mjs # regenerates data.ts from ref/GrokBot's Dart sources
│ └── build.mjs # assembles lib/client.js (__ModuleLoader__ contract) from tsc output
├── test/verify.mjs # browserless tests against the built bundle (SVG, pacing, typewriter)
├── tsconfig.json # host-half compile: src/index.ts → lib/ (node ESM)
├── tsconfig.client.json # client-half compile: src/client/*.ts → .build/client/ (plain scripts)
├── demo/ # local preview (node demo/server.mjs → 4173)
└── cordis.patch.yml # bundle patch: inserts the thought-buddy row
npm install # once — installs the typescript devDependency
npm run gen # refresh data (after upstream GrokBot data changes)
npm run typecheck # type-check both halves (no emit)
npm run build # compile TS → lib/ + .build/client/, then assemble lib/client.js
npm run verify # full browserless verification
node demo/server.mjs 4173 # preview http://127.0.0.1:4173/demo/demo.html (run npm run build first)
The client half is written as TypeScript “plain scripts” (no
import/export, so the types and data stay in one global scope).tsccompiles them to plain JS in.build/client/, andscripts/build.mjsconcatenates the two files into thewindow.__ModuleLoader__factory — the same loader contract as before.data.tsmust be evaluated beforeindex.ts(build order is fixed).
Architecture
-
Host half: provides only the cordis bundle mount row (
cordis.patch.ymlinsertsthought-buddy), so client-modules discovers thedsh.clientdeclaration andexports["./client"], serving the browser half at/plugins/@dsh-plugin/dsh-thought-buddy/client.js. -
Client half:
apply(ctx)registers aMutationObserverwatching[data-conversation-scroll] [role="status"]pills whose text contains “diving”; the avatar/typewriter mount in front of the text and live with the pill’s DOM lifecycle. -
Animation: a per-frame port of Flutter’s
_GrokBotState._onTick— critically-damped spring expression morphing (ω=7, 1/120 substeps), thinking-pool expression switching, 320 ms blink curve, spherical head-turn projection (asin/cosdepth culling), wandering gaze; polygon coordinates are written to the SVGpointsattribute every frame. -
Typewriter: a timer-driven state machine over the pill’s text node. React never touches the node because the text fiber’s
childrenstring is always"Deep diving..."(bail-out), exactly like the injected avatar node.
⚠️ Two different
injects: the bundle’s exportedexports.injectis the cordis service dependency (this plugin only usesctx.effect, so it must be an empty array — putting package names there makes the fiber wait forever for a service that does not exist and boot fails withpending (waiting for service: ...));package.json’sdsh.client.injectis the client module dependency declaration (this plugin needs none, so it is omitted).
Links
- Repository: https://github.com/dsh-plugins/dsh-thought-buddy
- npm: https://www.npmjs.com/package/@dsh-plugin/dsh-thought-buddy
License
BSD-3-Clause. The eye-ring geometry, body shapes, and state cadences derive from nasawz/GrokBot (BSD-3-Clause, Copyright (c) 2026 nasawz), credited in the LICENSE file.
常见问题常见问题
在启用了 DSH 的终端中执行已验证命令 dsh plugin --profile default add github:dsh-plugins/dsh-thought-buddy。命令会解析公开 package 元数据,并保持插件与本页展示的目录身份一致。
兼容性以页面上展示的 bundle 与 profile 状态为准。如果某个 profile 尚未检测到,请先保持禁用,并在生产启用前阅读仓库文档。
GitHub 链接和 activity 元数据是 release 与维护状态的来源。新版本发布后重新查看本页,确认目录已经观察到最新版本。