鱼皮AI导航
鱼皮AI导航
  • 主页
  • 社区
  • AI工具
  • AI知识库
  • AI提示词
  • MCP
  • Skills
  • AI学习资源
  • AI资讯
  • GitHub开源
登录/注册

dsh-status-rotator

把回合状态那句 "Deep diving..." 替换成更有梗的自定义文案,按阶段轮换,支持打字机与流动渐变。

界面增强
访问 GitHub
文档README

dsh-status-rotator

English | 中文

▼
bash
复制代码
# One-line install dsh plugin --profile web add dsh-status-rotator

v0.9.0 — stable release

⭐ If this made you smile, give it a star — it keeps the memes flowing.

Replaces the Deep diving... status line in the DeepSeek Harness (dsh) Web UI's turn footer with your own text: phase-aware switching, typewriter output, animated rainbow gradient (optional), timed rotation, template placeholders with live values ({elapsed}, {phase}, {model}, {tps}…), optional browser tab title rotation, a live status pill (model, phase, elapsed, tokens/s — fed by the same real-time engine), Danmaku (your phrases float across the page behind the UI like bullet-screen comments on video sites), and presets with time-of-day scheduling. The elapsed-time clock (which appears after 15 seconds) is untouched.

Installation

Two ways to install: the recommended dsh plugin add command, or the manual copy. Either way, you need to restart dsh web once after first install.

Option A: dsh plugin add (recommended)

The plugin's package.json declares a dsh.bundle.patch manifest, so it's recognized automatically after install — no extra flags needed. The command syntax is dsh plugin --profile <name> add <package> (e.g. --profile web):

  • From npm (easiest): dsh plugin --profile web add dsh-status-rotator ← always installs the latest release
  • From a clone: dsh plugin --profile web add ./dsh-status-rotator
  • From a release package: download the packaged tarball from the Release page, then dsh plugin --profile web add /path/to/dsh-status-rotator-<version>.tgz.

Option B: manual install

  1. Put this project directory under your profile's node_modules (default C:\Users\<you>\.dsh\profiles\node_modules\dsh-status-rotator\);

  2. Insert the following into the profile's cordis.patch.yml:

    ▼
    yaml
    复制代码
    - insert: - id: status-rotator name: dsh-status-rotator
  3. Run node gen-config.cjs to initialize the local config.json (copied from config.example.json);

  4. Restart dsh web and hard-refresh the browser with Ctrl+F5.

Features

  • Phase-aware: three sets of phrases — thinking (just started) / running (after 15s) / long (past the threshold). Switches immediately when the clock appears or the timeout hits, no need to wait for the rotation interval;
  • Typewriter effect: phrases are typed out character by character, speed configurable, 0 disables it;
  • Template placeholders: {elapsed} (live, refreshed every liveTickMs), {phase}, {phaseLabel}, {locale}, {date}, {time}, plus live-engine values {model}, {provider}, {tps}, {pending}, {tools}, {running} — e.g. 正在写代码 {elapsed} shows a ticking clock inside the phrase;
  • Real-time status engine: subscribes to the dsh session snapshot (session list, conversation snapshot, model RPC, DOM clock fallback) — one source feeding the phrases, the tab title and the pill;
  • Live status pill: a floating pill in the official shell.overlay seat, template-driven live info ({model} · {phaseLabel} · {elapsed} · ⚡{tps} tok/s), position/opacity configurable;
  • Browser tab title: rotate document.title through your own templates (⏳ {phase} {elapsed}), restore the original title when idle (configurable);
  • Presets & scheduling: multiple named phrase banks with their own config, switchable from the settings page or automatically by time-of-day / weekday rules;
  • Rainbow gradient: text rendered with an animated gradient, colors and speed configurable, can be turned off with one switch;
  • Danmaku: every phrase can also fly across the page as video-site-style bullet-screen comments — random size, per-bullet random rainbow colors, configurable opacity, floating behind the UI by default (zIndex: -1), or in front of it if you prefer;
  • Phrases separated from code: all phrases live in config.json, editing them requires zero code and no restart;
  • Settings page: a new "Status Texts" page in DSH's Settings, with visual editing for the Chinese/English × three-phase phrase banks, saves take effect immediately;
  • Auto-loading: the node half registers an HTTP route to serve config.json, works out of the box with no localStorage or deployment needed;
  • Hot reload: while the page stays open it re-reads config.json periodically, and re-reads immediately when you switch back to the tab — no refresh needed to apply new phrases;
  • Multilingual: phrases switch live between Chinese and English following Settings → Language, unknown languages fall back to Chinese;
  • Zero-intrusion targeting: locates TurnStatus precisely by role="status" + aria-live="polite", so it never touches code snippets in the chat history or other aria-live regions, and never touches the clock.

Phase Awareness

Phrases are split into three groups based on turn progress (determined by whether a clock has appeared in the TurnStatus element and its reading):

PhaseTriggerDefault duration
thinkingTurn just started, no clock0 ~ 15s
runningClock visible, under the limit15s ~ longAfterMs
longClock past longAfterMs≥ 60s

Phase changes swap the phrase immediately without waiting for the rotation interval. If a phase has no phrase group, it falls back automatically (running → thinking → any non-empty group).

Rainbow Gradient

Status text is shown with an animated rainbow gradient by default (applies to the text only, not the clock). Can be disabled or re-colored in the config:

▼
json
复制代码
"gradient": { "enabled": false, // false to disable; true for default colors "colors": ["#ff5f6d", "#00ff88", "#4da6ff"], // gradient color sequence (at least 2, first/last cycle) "speed": 4 // animation speed (seconds per cycle) }

Danmaku

Optional: every phrase can also spawn as video-site-style bullet-screen comments flying from right to left across the page (by default behind the UI — the layer is squeezed between the app background and the chat content, visible in the gaps):

▼
json
复制代码
"danmaku": { "enabled": true, "intervalMs": 2500, // spawn interval (ms); smaller = more of a flood "speedMs": 18000, // time to cross the screen, right → left (ms); larger = slower "fontSizeMin": 14, // min random font size (px) "fontSizeMax": 30, // max random font size (px) "rainbow": true, // rainbow mode: each bullet picks a random color from `colors` "colors": ["#ff5f6d", "#00ff88", "#4da6ff"], // palette (at least 1) "color": "#ffffff", // solid color used when rainbow = false "opacity": 0.3, // global opacity (0.05 ~ 1); each bullet jitters ±25% around it "maxCount": 12, // max concurrent bullets on screen "zIndex": -1, // negative = behind the UI (default), non-negative = above the UI "scope": "all", // "all" = every phrase of the current language; "phase" = current phase only (with fallback) "marginTop": 16, // top padding of the bullet band (px) "marginBottom": 160 // bottom padding (px), keeps the input area clear }
  • With zIndex < 0 (default) the layer is mounted inside the dsh app frame and sits between the app background and the chat content: bullets are visible in the empty area and behind the conversation, never covering the chat bubbles or the sidebar. If your theme paints an opaque background that hides them, set a non-negative zIndex to float them above the UI instead — the layer never intercepts pointers (pointer-events: none).
  • Bullets support the same placeholders as phrases ({elapsed}, {model}, {phase}…), rendered with the live engine values at spawn time.
  • danmaku: false disables it entirely. fontSizeMin / fontSizeMax set the random size range; the range is auto-corrected if reversed.

Template Placeholders

Any phrase (and any title template) may contain placeholders, replaced at render time:

PlaceholderMeaningExample
{elapsed}elapsed time of the current turn, localized like the clock正在写代码 1分02秒…
{phase}phase id: thinking / running / long / idlerunning
{phaseLabel}localized short label of the phase运行中
{model}model of the current session (live engine, — when unknown)deepseek-chat
{provider}provider route of the current session (live engine)deepseek
{tps}streaming tokens/s estimate (live engine)12
{pending}pending/approval interactions count (live engine)1
{tools}running tool names joined with + (live engine)bash+web_search
{running}run / idle (live engine)run
{locale}current UI language (zh / en)zh
{date}local date YYYY-MM-DD2026-08-07
{time}local time HH:MM:SS12:34:56

Placeholders that change over time ({elapsed}, {date}, {time}, {tps}, {pending}, {tools}) are refreshed live every liveTickMs (default 1000 ms; 0 disables live refresh, they then update once per rotation). Unknown placeholders are left as-is, so {...} in a phrase is safe. The live values ({model}/{provider}/{tps}/{pending}/{tools}/{running}) come from a real-time status engine that subscribes to the dsh session snapshot and model RPC, with a DOM clock fallback — if the session API is unavailable, they stay — but the plugin keeps working.

▼
json
复制代码
"phrases": { "zh": { "thinking": ["正在写代码 {elapsed}…", "正在{phaseLabel}中 ({elapsed})…"] } }

Browser Tab Title

Optionally rotate the browser tab title while a turn is running:

▼
json
复制代码
"title": { "enabled": true, "templates": ["⏳ {phase} {elapsed}", "🤔 {phaseLabel}… {elapsed}"], // rotated every intervalMs "idleTemplate": "💤 dsh 空闲", // "" = restore the original title when idle "intervalMs": 8000 }

Templates support the same placeholders as phrases. When no turn is active the title shows idleTemplate, or the original title if it is "". title: false disables it entirely.

Live Status Pill

A floating pill (official shell.overlay seat — the documented place for status pills) shows live information driven by the same real-time engine:

▼
json
复制代码
"pill": { "enabled": true, "template": "{model} · {phaseLabel} · {elapsed} · ⚡{tps} tok/s", "position": "right-bottom", // right-bottom / left-bottom / right-top / left-top "opacity": 0.92 }

The template supports every phrase placeholder (including the live-engine ones: {model}, {provider}, {tps}, {pending}, {tools}). While a turn runs it ticks with: the model name (read from the official model-directory service, following session/model switches), the phase (thinking/running/long), the elapsed time and the streaming tokens/s — phase and elapsed are derived from the session snapshot (the turn's start moment is tracked by the engine itself, so it never depends on DOM structure); when idle it shows — · 空闲 · 0秒 · ⚡0 tok/s. pill: false disables it. If the session API is unavailable (older dsh), the DOM clock drives phase/elapsed as a fallback and the live fields show — — no crash, no errors.

Presets & Scheduling

Named presets can carry their own config and phrases; the editor on the settings page switches between them and a time schedule can switch the active preset automatically:

▼
json
复制代码
{ "activePreset": "work", "presets": [ { "id": "work", "label": { "zh": "工作模式", "en": "Work" }, "config": { "intervalMs": 12000, "gradient": false }, "phrases": { "zh": { "thinking": ["正在认真写代码…"] } } }, { "id": "fun", "label": { "zh": "摸鱼模式", "en": "Fun" }, "phrases": { "zh": { "thinking": ["正在摸鱼…"] } } } ], "schedule": [ { "preset": "work", "days": ["mon", "tue", "wed", "thu", "fri"], "from": "09:00", "to": "18:00" }, { "preset": "fun", "days": ["sat", "sun"], "from": "00:00", "to": "23:59" } ] }
  • presets[]: each has an id (required), optional label (string or {zh, en}), optional config (merged over the top-level config) and optional phrases (used instead of the top-level phrases). A preset may be an id-only "shell" that just switches back to the base library.
  • activePreset: preset id, or null/absent to use the top-level config / phrases.
  • schedule[]: rules with preset, days (mon…sun, omitted = every day), from / to (HH:MM). Overnight windows (e.g. 22:00–06:00) are supported. While a rule matches, that preset is used; otherwise activePreset applies. The schedule is re-evaluated every minute and applies live.
  • Settings-page edits always target the selected preset (or the base library when "Default" is selected); "Set active" writes activePreset; the schedule rules are edited as a list on the same page.

Configuration

Phrases are fully separated from the source code and live in JSON config files. There are two config files at the project root:

  • config.example.json — the complete template committed to the repo: default config + all phrases (bilingual, split into three phases);
  • config.json — your local personalized config, initialized by node gen-config.cjs (only created when missing, never overwrites your changes). It's in .gitignore, so edit freely without polluting git.

Auto-loading (default): the plugin's node half registers an HTTP route (/plugins/dsh-status-rotator/config.json) that serves the config.json next to the plugin (read from disk on every request). The browser fetches it automatically by default, and while the page stays open it re-reads every reloadIntervalMs, plus immediately when you switch back to the tab, so as long as config.json sits in the plugin directory, phrase edits take effect without a refresh or restart. The only restart of dsh web needed is on first install.

Persistent storage since v0.6.1: saved edits are written into the official dsh settings store ($DSH_HOME/settings.yaml, namespace status-rotator) — the same store the rest of dsh uses for its settings, which survives plugin upgrades. Upgrading via npm or a release package will no longer wipe your gradient/phrases/presets (previously config.json lived inside the plugin directory and was deleted on upgrade). The plugin-directory config.json remains as a compatibility mirror and fallback; a one-time import migrates an existing config.json into the settings store on first start.

▼
json
复制代码
{ "config": { "intervalMs": 10000, "typeSpeedMs": 30, "longAfterMs": 60000, "reloadIntervalMs": 15000, "liveTickMs": 1000, "debug": false, "gradient": { "enabled": true, "colors": ["#ff5f6d", "#ffc371", "#ffdd55", "#7dff7d", "#5fd4ff", "#a78bfa", "#ff8adb"], "speed": 4 }, "title": { "enabled": false, "templates": ["⏳ {phaseLabel} {elapsed}"], "idleTemplate": "", "intervalMs": 8000 }, "pill": { "enabled": true, "template": "{model} · {phaseLabel} · {elapsed} · ⚡{tps} tok/s", "position": "right-bottom", "opacity": 0.92 }, "danmaku": { "enabled": true, "intervalMs": 2500, "speedMs": 18000, "fontSizeMin": 14, "fontSizeMax": 30, "rainbow": true, "colors": ["#ff5f6d", "#ffc371", "#ffdd55", "#7dff7d", "#5fd4ff", "#a78bfa", "#ff8adb"], "color": "#ffffff", "opacity": 0.3, "maxCount": 12, "zIndex": -1, "scope": "all", "marginTop": 16, "marginBottom": 160 } }, "phrases": { "zh": { "thinking": ["…"], "running": ["…"], "long": ["…"] }, "en": { "thinking": ["…"], "running": ["…"], "long": ["…"] } }, "presets": [], // optional, see "Presets & Scheduling" "activePreset": null, // optional preset id "schedule": [] // optional time rules }
KeyDefaultDescription
intervalMs10000Rotation interval (ms)
typeSpeedMs30Typewriter delay per character (ms), 0 disables the typewriter
longAfterMs60000Threshold for entering the long phase
reloadIntervalMs15000Interval for auto re-reading config.json while the page is open (ms), 0 disables
liveTickMs1000Refresh interval for live placeholders ({elapsed} / {date} / {time} / {tps}…) in phrases, titles and the pill (ms), 0 disables
debugfalseConsole diagnostic logs
gradientsee aboveRainbow gradient: false / true / {enabled, colors, speed}
titlesee aboveTab title rotation: false / {enabled, templates, idleTemplate, intervalMs}
pillsee aboveLive status pill: false / {enabled, template, position, opacity}
danmakusee aboveBullet-screen comments: false / {enabled, intervalMs, speedMs, fontSizeMin, fontSizeMax, rainbow, colors, color, opacity, maxCount, zIndex, scope, marginTop, marginBottom}
phrasesfrom config fileThe phrases (Chinese/English × three phases; partial entries allowed, missing ones fall back to other sources)
presetsnoneNamed phrase banks, each with optional config / phrases
activePresetnullWhich preset is active (null = use the top-level config/phrases)
schedulenoneTime rules that switch the active preset automatically

Phrase source priority, highest first:

  1. localStorage single-text override dsh-status-rotator.texts[.<locale>] / texts;
  2. localStorage full config dsh-status-rotator.config (paste JSON, applies after refresh);
  3. External JSON: dsh-status-rotator.url > EXTERNAL_URL constant > local auto-load (/plugins/dsh-status-rotator/config.json);
  4. Built-in defaults: only DEFAULT_CONFIG at the top of lib/client.js (no phrases).

If a localStorage override matches, the external config.json is silently suppressed; the new version logs a [status-rotator] ⚠ localStorage override active warning in the browser console — when you see it, clear the corresponding key.

Old phrase-only external JSON ({ "zh": [...], "en": [...] } or { "thinking": [...] }) is still supported and treated as a "phrases-only config".

Phrases switch live between Chinese and English following Settings → Language; unknown languages fall back to Chinese.

Editing the Phrase Bank in the Settings Page

Open Settings in the bottom-left of DSH and a new Status Texts page appears in the navigation:

  • 中文 / English tabs, each with three text boxes for thinking / running / long, one phrase per line, blank lines are ignored;
  • Each phase shows the current phrase count in real time;
  • Basic settings (rotation interval, typewriter speed, long-task threshold, auto-reload interval, placeholder refresh interval) live on the same page;
  • Live pill settings: enable toggle, display template, position — the pill and the live-engine placeholders are configured in the same page;
  • Rainbow gradient settings: enable toggle, color sequence, speed — no more manual config.json editing to turn the gradient off;
  • Danmaku settings: enable toggle, spawn interval, cross duration, random font-size range, rainbow mode + palette, opacity, max concurrent bullets, layer z-index and phrase scope — everything editable without touching config.json;
  • Preset selector: edit each preset's phrases/config independently; "Set active" writes activePreset; the currently effective preset (schedule included) is shown live;
  • Schedule editor: add/remove weekday + time-window rules that switch presets automatically;
  • Clicking "Save Phrase Bank" makes the browser PUT the full JSON to /plugins/dsh-status-rotator/config.json; the node half validates it and writes it back atomically, and already-open pages hot-apply it immediately without a refresh;
  • Submitted content is validated (phrases must be string arrays, presets/schedule must match their shapes); invalid content returns 400 and shows an error on the page, so the config file can't be corrupted.

After upgrading to a version with the settings page, restart dsh web once (so the node half registers the write endpoint); everything after that can be done from the page.

QQ Group Member Phrase Generator

To turn every member of a QQ group into a phrase like 正在路由(群成员)写代码... (meaning "routing (group member) to write code..."), use scripts/fetch-qq-group.cjs to generate a standalone config file in one go — no need to type out the member list by hand.

Prerequisites: the bot is in the target group and you have a OneBot v11 compatible HTTP API (e.g. NapCat / LLOneBot / go-cqhttp / OpenShamrock).

▼
bash
复制代码
# The default group is 684306814; generates config.qq684306814.json directly node scripts/fetch-qq-group.cjs --url http://localhost:3000 --token your-token # Directly replace the config.json the plugin actually uses (the old one is backed up as config.backup-<timestamp>.json) node scripts/fetch-qq-group.cjs --url http://localhost:3000 --token your-token --activate # No bot API? Save the member list as members.txt (one nickname per line) and generate from it node scripts/fetch-qq-group.cjs --input members.txt
OptionDefaultDescription
-g, --group684306814QQ group ID (also reads the QQ_GROUP_ID env var)
-u, --urlhttp://localhost:3000OneBot HTTP URL (also reads ONEBOT_HTTP_URL)
-t, --tokenemptyAccess token (also reads ONEBOT_ACCESS_TOKEN)
-a, --actionget_group_member_listAction path; frameworks with a prefix use /api/...
-i, --inputnoneLocal member list: txt (one per line) / json (array) / csv (first column)
-o, --outputconfig.qq684306814.jsonOutput file
--activateoffWrite back to config.json directly and back up the old file
--dry-runoffPreview only, writes nothing

The display name prefers the group card name, falling back to the nickname. The generated file contains only the zh.thinking group: per this plugin's fallback rules, the thinking phase uses it directly and the other phases fall back to the same group. Template: config.qq684306814.example.json; the generated config.qq684306814.json is gitignored.

Project Structure

▼
text
复制代码
dsh-status-rotator/ ├── lib/ │ ├── index.js # node half: registers the HTTP route for config.json (GET/PUT, validated) │ └── client.js # client half: status text replacement / placeholders / gradient / title / presets ├── config.example.json # complete template (default config + all phrases, committed) ├── config.qq684306814.example.json # QQ group member phrase template (scripts/fetch-qq-group.cjs generates the real file) ├── config.json # local personalized config (gitignored) ├── gen-config.cjs # script that initializes config.json ├── scripts/ │ ├── fetch-qq-group.cjs # fetches QQ group members and generates the phrase config │ └── smoke-test.cjs # pure-function smoke tests (npm test) ├── package.json ├── README.md # English docs ├── README_ZH.md # Chinese docs ├── CONTRIBUTORS.md # English contributors ├── CONTRIBUTORS_ZH.md # Chinese contributors └── LICENSE

Testing

npm test (or node scripts/smoke-test.cjs) loads lib/client.js in a Node sandbox and asserts the pure logic — placeholder interpolation, elapsed formatting, clock parsing, config/preset/schedule normalization, schedule matching, and the node half's validation — no browser needed. The same suite runs automatically in CI on every push/PR (see .github/workflows/test.yml).

Uninstall

Remove the status-rotator line from cordis.patch.yml and restart dsh web.

Contributing

Issues and pull requests are welcome. The easiest way to add phrases: edit the phrases field in config.json or config.example.json directly — no code changes needed.

Credits

This project wouldn't exist without the help of its contributors — see CONTRIBUTORS.md.

License

MIT

界面增强

快捷安装

复制到终端去安装插件

dshplugin--profilewebadddsh-status-rotator

温馨提示

装插件等于在本机跑第三方代码,权限与你相同,能读文件、用凭据、访问网络,工具审批管不到。请只装可信来源,并尽量锁定 commit。

站长:程序员鱼皮 老鱼简历 商务合作 增值电信业务经营许可证
2026 鱼皮AI导航 | 沪ICP备19026706号-6
站长:程序员鱼皮 老鱼简历 商务合作 增值电信业务经营许可证
2026 鱼皮AI导航 | 沪ICP备19026706号-6
🔥热门工具
朱雀AI检测朱雀AI检测
朱雀AI检测助手朱雀AI检测助手
LovartLovart
老鱼简历老鱼简历
WritePass官网WritePass官网
面多多面多多
CursorCursor
星月写作星月写作
零感AI零感AI
墨得问题墨得问题
dsh 插件推荐
DSH-agent-bridge
在 Web 输入框中切换原生 DSH Agent 与 Grok Build,并通过 ACP 代理 Grok Build 会话及斜杠命令。
dsh-gui-hanhua
DeepSeek Harness 界面信息汉化插件:工具调用卡片、插件列表、命令菜单与权限选择器显示中文,保留英文原名。
ai-plugin#dsh-plugin
aipx 工具包:技能之外还有 Hub Console 控制台——用约 4 个元工具代理全部 MCP 服务器,含服务器池健康、工具级启停、工具目录,以及直观展示 mcp_search 返回结果的搜索试验场。
dsh-gpu-pulse
DSH Web 界面内置的悬浮 GPU 监控:紧凑可拖动的条带,每张独立 GPU 一行(精确名称、利用率、显存、温度、功耗),数据来自 nvidia-smi;侧边面板或侧边卡片打开时保持在最上层;驱动支持时还列出显存占用最高的进程;无 NVIDIA 驱动的机器会退化为 n/a 胶囊。
dsh-sticky-notes
泡泡猫的即时便签 — 支持彩色标签、置顶、定时提醒与密码保护。