Adds a Compact context button and a New session button to the context meter panel; compact submits /compact through the same command channel, and new session starts a fresh session in the same workspace.
Install
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:shyuan-hub/dsh-compact-button
Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time — pnpm blocks those until you allow them, so an install can stop with ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED or ERR_PNPM_IGNORED_BUILDS; dsh prints the exact key to add under allowBuilds in your profile’s pnpm-workspace.yaml, and the install works on the next run. Allowing a build is a trust decision: only install sources you trust, and pin a commit (github:owner/repo#sha).
README
简体中文 | English
📑 Contents
- ✨ Features
- 🚀 Install
- 🖱️ Using the buttons
- 🔧 How it works
- 🛠️ Development & build
- ⚠️ Known limitations
✨ Features
- 🔘 One-click compaction: long conversations keep eating the context window, and you used to type
/compactby hand — now the button sits on the context panel you already look at. See it filling up, click once. - 🔁 The exact same command channel: it goes through the same channel as typing
/compact(session.command('/compact')). The result shows up in the conversation stream as a command line, with no special-cased behaviour. - 📊 Live status feedback: the button label follows the submission state (idle → submitting → submitted / not matched / failed), then resets after 4 seconds so the panel stays clean.
- 🌏 Multilingual: follows the DSH language setting, switching between Chinese and English live.
- 🆕 New session: a second button to the right of the compact button — clicking it starts a new session in the same workspace and navigates there. The agent preset and permission settings follow the deployment defaults (see Known limitations for when that matches your current session).
- 🪶 Zero intrusion: a pure client-half plugin (the host half is an empty
apply). When the slot is absent the buttons simply do not render, and nothing else on the platform is affected.
🚀 Install
Prerequisites: DSH installed (dsh web runs normally), Node.js ≥ 20, pnpm ≥ 10.
Option 1: from npm (recommended)
dsh plugin --profile web add dsh-compact-button@latest
# 1. Build and pack
git clone https://github.com/shyuan-hub/dsh-compact-button.git && cd dsh-compact-button
pnpm install && pnpm build
pnpm pack # produces dsh-compact-button-<version>.tgz
# 2. Install it through dsh plugin (file: channel)
dsh plugin --profile web add "file:<your-local-dir>/dsh-compact-button-<version>.tgz"
After installing, restart dsh web and hard-refresh the browser (Cmd/Ctrl+Shift+R).
✅ No need to edit the profile's
package.jsonby hand: this package declaresdsh.bundle.patch, sodsh plugin addappends it todsh.profile.bundlesautomatically.🔧 When debugging local changes you can use the link channel instead:
dsh plugin --profile web add "link:<absolute path to the clone>". After that, eachpnpm build+dsh webrestart is enough.🔄 Updating:
dsh plugin --profile web add dsh-compact-button@latest, then restartdsh weband hard-refresh.
📦 The plugin ships its own bundle patch (
cordis.patch.yml): once installed into a profile it inserts this plugin's mount entry at startup, so you never edit the profile'scordis.patch.ymlyourself. If the aggregate bundle already mounts this plugin, the entry steps aside to avoid a double mount.
| Symptom | Cause and fix |
|---|---|
| No buttons in the panel | The buttons depend on the conversation.context.actions sub-slot, which upstream DSH does not declare — it is injected by a platform patch this plugin applies automatically (self-healing on every dsh web start, host half). Check the dsh startup log: [dsh-compact-button] platform patch applied: ... @<version> means it was injected; platform patch skipped ... "<anchor>" matched N time(s) means the upstream ContextMeter changed too much, so the patch aborted safely without touching platform files. The buttons do not render; the rest of the plugin is unaffected. |
| Clicking shows Command not matched | The current composer has no session that can accept a command. Switch to a page with an active session and try again. |
| Install / changes did not take effect | This plugin needs a dsh web restart (a browser hard-refresh alone is not enough). Restart, then hard-refresh the page. |
🔩 Platform patch (applied automatically): the official
@deepseek-ai/dsh-client-ui-conversationdoes not declare theconversation.context.actionsslot. This plugin ships a patch script (patch-context-meter.cjs) that, on everydsh webstart (host half), locates the installed platform bundle and injects the slot declaration and its render call in place. The patch is idempotent and asserts every anchor; on an abnormal match it aborts safely (platform files untouched), the buttons do not render, and the rest of the plugin keeps working. The package contains no install scripts (nothing depends on postinstall, so no package manager can error out or block it). To patch ahead of a start, runnode node_modules/dsh-compact-button/patch-run.cjsmanually.🏠 Relocated harness home: the platform bundle is located with the platform's own home precedence — an explicitly configured home (
--dsh-home <path>, the analogue of the platform'sconfig.dshHome) >$DSH_HOME> the default~/.dsh, with~/~\expansion. A harness moved elsewhere (e.g.DSH_HOME=D:\AppData\DSH) is followed instead of theprofilestree being missed. The explicit home only **adds** a probe, it never narrows one: a$DSH_HOMEinstall is still self-healed.
🔗 Not tied to DSH releases: this package declares no DSH peer dependencies, and the patch anchors are format-agnostic semantic regexes (indentation, quoting,
var/let/const, the css-module hash and jsx helper renames all still match). So when DSH ships a new version you do not need to bump this plugin's version or dependency ranges — as long as the ContextMeter structure is unchanged it adapts on its own; if the structure really changes you just get "buttons not rendered + one clear log line", never a polluted platform file. The startup log prints the detected platform version, and versions that were not exercised get an extra(not in the tested set)note. Verified:0.1.2-rc.1,0.1.5-rc.1,0.1.5-rc.2,0.1.5-rc.3,0.1.7-alpha.2,0.1.7-rc.1,0.1.7-rc.2.
🖱️ Using the buttons
The panel holds two buttons side by side in one row: Compact context on the left, New session on the right (the context ring next to the composer — click to expand).
Compact context (left)
The label changes with the click state:
| State | English | Meaning |
|---|---|---|
| Idle | Compact context | Clickable; clicking submits /compact |
| Submitting | Compacting… | The command is being sent to the session |
| Submitted | Compaction submitted | The command was accepted; see the conversation stream for the result |
| Not matched | Command not matched | The current composer has no session that can accept a command |
| Failed | Submission failed | An error occurred while submitting the command |
The status resets to Idle after 4 seconds.
New session (right)
Clicking starts a new session in the workspace of the current session and navigates to it (uiWorkspace.startSession()). The button locks for about 1.5 seconds after a click to prevent accidental double-clicks. The new session uses the deployment's default agent preset and permission settings — if your current session uses a non-default preset, the new session will not inherit it (see Known limitations).
🔧 How it works
- Platform extension point: the ContextMeter panel of
@deepseek-ai/dsh-client-ui-conversationdeclares the sub-slotconversation.context.actions(a child ofconversation.composer.bar, kind: list). - Client half: using
slots.inject, once the platform declares the slot it registers a side-by-side container (ContextActionRow) into it —CompactButtonon the left,NewSessionButtonon the right:CompactButtoncallssession.command('/compact')on click — the exact same channel as typing/compact(acceptance semantics belong to the host's command-compact plugin; the compaction result appears in the conversation stream as a command line).NewSessionButtoncallsuiWorkspace.startSession()on click — starts a new session in the current session's workspace and navigates there; it locks for about 1.5 seconds afterwards to prevent accidental double-clicks.
- Host half: applies the platform patch self-healing at startup (injecting the
conversation.context.actionsslot declaration into the installeddsh-client-ui-conversationbundle). The patch is idempotent, aborts on drift, and never blocks dsh from starting. - i18n: dictionaries are registered under the
compactButtonnamespace (zh/en) and follow the DSH language setting live.
Build artifacts
| File | Channel |
|---|---|
lib/index.js |
Host half (self-healing platform patch at startup, injects the conversation.context.actions slot) |
lib/client.js |
Official profile channel (bundle id = package name dsh-compact-button) |
lib/client-registry.js |
Plugin registry channel (bundle id = manifest id dsh-external/dsh-compact-button) |
patch-context-meter.cjs / patch-run.cjs |
Platform patch module and manual CLI entry (after install the patch is applied automatically by the host half on each dsh web start; no install script needed) |
🛠️ Development & build
pnpm install
pnpm typecheck # tsc --noEmit
pnpm build # rm -rf lib && tsdown → lib/index.js + lib/client.js + lib/client-registry.js
pnpm watch # tsdown --watch
Architecture: a single npm package with a host/client split — the host (src/index.ts) is an empty apply; the client (src/client/index.tsx) registers CompactButton into the slot and handles state transitions and i18n. The plugin follows the official DSH conventions (no default export, two client bundles) and depends on neither npm nor a checkout at runtime (@deepseek-ai/* is provided by the web profile).
Version compatibility strategy (why you never touch code when DSH ships)
- No DSH peer dependencies. The plugin imports nothing from the platform at runtime (the client bundle only
require("react"), the host half onlynode:*); every service arrives through cordis injection. Sopackage.jsoncarries no@deepseek-ai/dsh-*peer/dev version lock — and no package manager ever pulls a second copy of the renderer just to satisfy a range (two slot registries would break slot registration outright). - The patch anchors are semantic regexes, not source literals. Every anchor in
patch-context-meter.cjstolerates indentation, quoting,var/let/const, the css-module hash (the prefix is captured live from thebarentry, never hardcoded) and jsx helper renames; the unit suite regresses each of these viaDRIFT_TRANSFORMS. - Assert + degrade instead of gating on a version number. Each anchor must still match exactly once, otherwise nothing is written at all. Compatibility is decided by structure, not by a semver range.
TESTED_PLATFORMSis documentation only. It drives the(not in the tested set)hint in the startup log and never gates the patch. When DSH ships a new version, change nothing: start it once and read the log. Optionally append the new version number toTESTED_PLATFORMSonce you have confirmed it.
Verifying a new DSH version (no code change required):
node -e "const p=require('./patch-context-meter.cjs'),fs=require('fs');console.log(p.patchSource(fs.readFileSync('<path>/lib/client.js','utf8')).status)"
# expect `patched`; `drift` tells you which anchor matched how many times
⚠️ Known limitations
- Depends on the
conversation.context.actionssub-slot: the upstream platform does not declare it, so the plugin's automatic platform patch injects it. The anchors are format-agnostic semantic regexes, so the patch only fails when the upstream ContextMeter structure changes materially (safe abort, platform files untouched, buttons not rendered). - The patch only targets
lib/client.jsof@deepseek-ai/dsh-client-ui-conversation: if upstream splits or renames that bundle,findTargetFileswill not find a target and needs to be updated. - The compact button only submits
/compact; acceptance and execution semantics belong to the host-side command-compact plugin. - The status message resets after 4 seconds; there is no compaction progress display.
- The new-session button starts a session in the same workspace but uses the deployment's default agent preset and permission settings. If the current session uses a non-default preset, the new session will not inherit it (the client-side
uiWorkspace.startSessionexposes no preset / permission selection).
Links
More in this category
zhu1090093659/dsh-web#packages/dsh-task-board★ 8526
Task board for the dsh web GUI: a sidebar multi-column kanban whose cards run in real DSH agent sessions and can also be scheduled with cron expressions, executed host-side even with the browser closed.
zhu1090093659/dsh-web#packages/dsh-web-all★ 8526
Plugin and skin collection for the DSH Web UI: task board, Git graph, right-side panel, remote mobile UI, pet, live token stats, and a skin center.
MeteorNOX/DeepSeek-Balance-Whale-Widget★ 4281
A fixed-corner whale widget for the DSH web GUI — balance, today's usage and per-turn cost with peak/off-peak pricing, editable balance-alert and daily-budget bubbles, a module-based custom bubble queue with A/B weighted choices and random lines or images, 30+ vendor templates (OpenAI, OpenRouter, Kimi, SiliconFlow, Ark, Zhipu, MiniMax and more) with per-model balance and subscription quota, plus task-end sound, imported audio, custom roles and a resource manager. Local-only, no telemetry.
ccch1mneyyy/dsh-TUI★ 4199
Claude Code-style full-screen terminal UI: pixel-whale header, live status line, and streaming thought expansion.
omdsh-dev/DSH-better-sidebar★ 4062
Full sidebar workbench with file rendering and editing, terminal, Git, and subagents; third-party plugins can register new tabs.
Devin-AXIS/deepseek-design#deepseek-idesign★ 1438
Visual design studio for websites, app prototypes, posters, cards, reports, and magazines, with templates, direct element editing, selection-aware AI draft handoff, and export.
Community comments
Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.