DeepSeek Harness Plugin

omdsh-dev/DSH-better-sidebar

Stars ★ 3950 Downloads (30d) 264,167 Category UI Enhancements Added 2026-09-24 npm dsh-better-sidebar

Full sidebar workbench with file rendering and editing, terminal, Git, and subagents; third-party plugins can register new tabs.

Install

# from npm (prebuilt)

dsh plugin --profile web add dsh-better-sidebar

# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)

dsh plugin --profile web add github:omdsh-dev/DSH-better-sidebar

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

[!IMPORTANT] Built on DSH's native sidebar API (since v0.19.0): the right column is DSH's own sidebar — the plugin registers every tab type as a native tab (no right panel of its own anymore) and keeps only its self-drawn bottom workbench and the ctx.betterSidebar service open to every plugin.

Since v0.24.1 the host support floor is DSH 0.2.0-rc.1+ (peer floor ^0.2.0-rc.1). Everything this plugin consumes from 0.2.0 is purely additive (zero removed exports, session format still v4, CLI and client runtime byte-identical), so this version carries no runtime compatibility branch — it only moves the support line. Hosts on the 0.1.7 line should pin dsh-better-sidebar@0.22.1 — a caret never spans a minor bump, and ^0.1.7-rc.1 is silently disabled by the 0.2.0 startup preflight; the DSH-to-plugin version table is in Installation.

📑 Contents

✨ Features

What this plugin adds on top of DSH's stock sidebar:

  • ✏️ Editable editor: the host's document preview is read-only → the plugin keeps an editable CodeMirror editor (save, syntax highlighting, preview toggle); Markdown / HTML also render through the plugin's own pipeline (Mermaid diagrams with safe rendering + click-to-zoom, README-level inline HTML, floating table of contents, sandboxed HTML preview)
  • 🗂️ Enhanced file tree: takes over the built-in Files page — lazy-loading tree, expanded directories watched live and auto-refreshed, symlink awareness, global filename search, drag-and-drop upload, hover @file to drop a reference into the input box; Ctrl/Cmd multi-select and Shift range-select (copy or delete a batch), VS Code-style git status colors + M/A/D/U letters, new folder, multi-select “Zip and download” (the host streams a ZIP built with no third-party dependency), and an “Open with” menu that COMBINES DSH’s own open-in-app (the host’s probed OS handlers + file-manager reveal) with the plugin’s own targets (file manager / VS Code / Cursor / Zed / custom editor URL templates, SSH remotes, pin-to-menu)
  • 🌿 Changes (no Git panel in the stock sidebar): two lenses in one tab — Git (diff / history / stage·commit·revert) and This Session (every file the model touched) — with a unified diff renderer (intra-line character highlights, syntax coloring, secret redaction); the two lenses now share one 36px header (host SegmentedControl), 28px rows, a single empty state and a sticky commit bar, and the git snapshot is shared with the file tree so staging recolors the tree too
  • 🧩 Background Tasks (absent upstream): agent topology preview + background task list (exit codes / live output / force-kill)
  • 💬 Side Chat (absent upstream, beta): Codex-style side threads — inheriting the full parent context, running independently, promotable to a top-level session
  • 🖥️ Bottom workbench (absent upstream): the right column belongs to DSH's native right sidebar; the plugin adds its own bottom workbench (drag-to-split panes, per-session persistence) that coexists with the native bar
  • 📂 Model-driven sidebar opens (opt-in): the sidebar_open tool lets the model actively open files / folders / web pages in the sidebar
  • 🔌 Service API: ctx.betterSidebar is open to every plugin (registerTab / registerFileViewer); the built-in 5 tabs + 3 viewers go through the same API, and 28+ ecosystem plugins already build on it (see "🌐 Plugin Ecosystem")
  • ⚡ On-demand loading: ~325KB core at startup, editor / Mermaid / third-language dictionaries load on demand · 🌏 i18n follows DSH's language · 🔁 Session isolation persists layout per session

🚀 Installation

Prerequisites: DSH installed (dsh web boots), Node.js ≥ 20, pnpm ≥ 10.

Supported DSH versions:

📌 Channel and support line: v0.24.1 targets DSH 0.2.0-rc.1+ (0.2.0's first candidate rides npm's next dist-tag; latest is still 0.1.7-rc.2). Pin the DSH version exactly: npm i -g @deepseek-ai/dsh@0.2.0-rc.1. Hosts on the 0.1.7 line should pin dsh-better-sidebar@0.22.1: 0.2.0 is a host minor bump, and caret ranges such as ^0.1.7-rc.1 fail the host's startup compatibility preflight on 0.2.0, which disables the whole profile row silently.

🧭 Pick the plugin version that matches your DSH:

Your DSH Install command Version / peer declared
0.2.0-rc.1+ (including a later 0.2.0 stable) dsh plugin --profile web add dsh-better-sidebar@latest 0.24.1, ^0.2.0-rc.1
0.1.7-rc.1 – 0.1.7-rc.2 (and a later 0.1.7 stable; npm latest is still 0.1.7-rc.2) dsh plugin --profile web add dsh-better-sidebar@0.22.1 0.22.1, ^0.1.7-rc.1
0.1.7-alpha.1 / 0.1.7-alpha.2 nothing to install — move DSH to rc.1 first, then run the row above:npm i -g @deepseek-ai/dsh@0.1.7-rc.1 —
0.1.6-alpha.2 and earlier, 0.1.5-rc.* (including the 0.1.5-rc.3 that is npm's latest) dsh plugin --profile web add dsh-better-sidebar@0.19.1 0.19.1, ^0.1.5-rc.1
0.1.5-alpha.2 dsh plugin --profile web add dsh-better-sidebar@0.19.0-alpha.1 ^0.1.5-alpha.2
0.1.2-rc.* dsh plugin --profile web add dsh-better-sidebar@0.18.1 ^0.1.2-rc.1
0.1.2-alpha.2 dsh plugin --profile web add dsh-better-sidebar@0.18.0-alpha.0 ^0.1.2-alpha.2
0.1.0-rc.8 / 0.1.1 dsh plugin --profile web add dsh-better-sidebar@0.17.1 ^0.1.0-rc.8

Swap web for your own profile name. Older versions are pinned exactly (@0.19.1, not @latest), because latest moves forward with each new stable cut; conversely, do not install 0.19.1 on a 0.1.7 alpha — it would simply break.

dsh plugin --profile web add dsh-better-sidebar@latest

The plugin depends on no package that needs a build script (the terminal and node-pty went back to DSH wholesale), so installing is one step; once installed you can enable / disable it on DSH's own Plugins page.

Then hard-refresh the browser (Cmd/Ctrl+Shift+R) to see the sidebar (DSH hot-reloads client changes; only host-half updates need a restart).

Or let DSH install it for you — paste this prompt into any DSH session:

Install the dsh-better-sidebar plugin (a sidebar workbench for DSH):
1. Run: dsh plugin --profile web add dsh-better-sidebar@latest (`latest` is the current stable)
2. When done, remind me to hard-refresh the browser (Cmd/Ctrl+Shift+R)
If anything fails, check the troubleshooting table in the README at https://github.com/omdsh-dev/DSH-better-sidebar

Option 3: one-shot script — from a clone of this repo, run bash scripts/install.sh (macOS / Linux / Windows Git Bash; native Windows uses install.ps1; -h for options) — it installs and registers the bundle in one go (including idempotent cleanup of an old manual mount row).

dsh plugin --profile web add dsh-better-sidebar@latest

or bump the version in ~/.dsh/profiles/web/package.json to the matching npm version ("^0.22.1") and run pnpm install. Then hard-refresh the browser (Cmd/Ctrl+Shift+R) — client changes do not need a DSH restart.

Symptom Cause & fix
Ignored build scripts pnpm 11 blocked a transitive dependency's build script. Run pnpm approve-builds (no --all) in the profile directory (~/.dsh/profiles/web) and follow its prompts — the plugin itself has no build-script dependency (node-pty left with the terminal).
minimum release age / version < 24h The release is younger than 24 hours. Wait, or re-run once (pnpm auto-adds minimumReleaseAgeExclude).
"profile directory not found" Run dsh web once so it initializes ~/.dsh/profiles/web.
Two sidebars on the page Double-mount. Old hand-written line: ~/.dsh/profiles/web/cordis.patch.yml still has - insert: ... better-sidebar ... — delete it (a same-id duplicate mount makes the loader fail loudly with duplicate loader entry id). When an aggregate bundle (e.g. @linxin666/dsh-web-ui-all) mounts this package under a different id, the plugin's own bundle patch backs off automatically since 0.13.x (it detects an already-enabled mount of the same package name and does not mount itself) — no manual fix needed; if it still double-mounts, make sure the aggregate bundle precedes dsh-better-sidebar in dsh.profile.bundles.
Where did my settings go after upgrading? DSH 0.1.7 removed the registrable settings namespace: preferences now live on this plugin's mount row in the profile (entry id better-sidebar by default), not in ~/.dsh/settings.yaml. On first boot the plugin imports the dsh-better-sidebar section of the old settings.yaml (renamed settings.yaml.imported by the host) exactly once — only fields the current schema still declares, and only while that row has no user values yet, so it never overwrites values set after the upgrade.
Terminal unusable / shell fails to start The terminal comes from DSH's own ui-sidebar-terminal (this plugin no longer ships a terminal or node-pty, and has no terminal settings). Consult DSH's own docs for problems; if the error mentions build scripts, see the row above.
dsh: command not found Install DSH first, or run npx -y --package @deepseek-ai/dsh dsh plugin --profile web add dsh-better-sidebar@latest.

To debug local changes or track the dev branch, point the dependency at a local clone and build it yourself:

1. git clone https://github.com/omdsh-dev/DSH-better-sidebar.git ~/Code/DSH-better-sidebar
   cd ~/Code/DSH-better-sidebar && pnpm install && pnpm build
2. In ~/.dsh/profiles/web/package.json dependencies write "dsh-better-sidebar": "link:<absolute path of the clone>"
3. Append this mount line to ~/.dsh/profiles/web/cordis.patch.yml (this row's `config` IS this plugin's settings form: the deployment limits `readLimit` / `mediaLimit` / `uploadLimit` / `listLimit` plus the user-preference fields — that is what the settings page writes; omit it and every field falls back to its schema default):
   - insert:
       - id: better-sidebar
         name: 'dsh-better-sidebar'
         config:
           readLimit: 524288
4. Run pnpm install in ~/.dsh/profiles/web
5. Restart DSH and hard-refresh

Update: git pull && pnpm install && pnpm build → just hard-refresh the browser (client changes hot-reload; only host-half changes need a DSH restart). To switch back to the npm channel, restore the matching npm version ("^0.22.1") and re-run pnpm install.

Prerequisite: DSH with plugin-registry integrated (dsh registry available). Enabling both channels double-mounts (the Node half loads twice, the page gets two sidebars).

git clone https://github.com/omdsh-dev/DSH-better-sidebar.git && cd DSH-better-sidebar
pnpm install && pnpm build
node scripts/package-registry.mjs   # assemble the registry/ staging (manifest + artifacts + README, not committed)
dsh registry install ./registry     # install (disabled by default)
dsh registry enable dsh-external/dsh-better-sidebar

Update: git pull && pnpm install && pnpm build → node scripts/package-registry.mjs → dsh registry uninstall/install/enable. Remove the other channel's mount before switching.

🖼️ Feature Tour

Below are real UI screenshots (two per row; click to zoom).

🗂️ File Workbench: ExplorerTwo explorer modes: embedded in the file preview / standalone file tree. Lazy-loading directory tree whose expanded directories the host watches per directory and re-lists on change, symlinks classified by target kind (directory links expand, dangling links flagged), global filename search, file/folder upload buttons plus drag-drop upload, context menu (open in new tab / open to the side / new folder / Open with — the host’s probed OS handlers PLUS the plugin’s own targets (file manager / VS Code / Cursor / Zed / custom editors, SSH remotes, pin-to-menu) / copy paths / rename / delete), Ctrl/Cmd multi-select with Shift range-select (batch copy paths, batch delete, zip and download), git changes colored by status with M/A/D/U letters, and a hover @file button that references a file straight into the composer. 📝 Inline Preview: Markdown · HTMLThe Markdown preview renders Mermaid diagrams (strict-mode safe rendering + a second sanitize pass; click a diagram for a zoom modal with wheel-zoom and drag-pan), README-level inline HTML (badge walls <div align=center>, <details> blocks nesting markdown, inline tags in table cells — DOMPurify-sanitized, <script> stripped, local media rewritten to the session media route) and a floating table of contents (appears with ≥3 headings, smooth-scroll jumping, auto-expanding folded blocks); HTML uses the plugin's own sandboxed preview with the two escape hatches the host does not have (htmlViewerNoSandbox / htmlViewerDefaultUnsafe). Images / PDF / spreadsheets / Office are no longer a plugin capability — the host's own document preview renders those formats.
🖥️ CodeMirror editorAn editable text / code editor (save, syntax highlighting, preview toggle) — the host's own document preview is read-only, which is exactly why the plugin keeps its catch-all viewer. 🖼️ Images / PDF / spreadsheets / Office (provided by DSH)These read-only formats render in DSH's own ui-sidebar-documentpreview: host-side Office→PDF conversion, worker-backed spreadsheet tables, image / PDF zoom viewports, and per-directory auto-refresh. The plugin deleted its own image / pdf / download-fallback viewers and no longer claims those extensions.
💻 Terminal (provided by DSH)The right-Sidebar terminal is provided by DSH's own ui-sidebar-terminal: shell picker, double-click rename, reconnect, restore after reload, theme and contrast following. The plugin no longer ships xterm + node-pty.⚠️ Model-side caveat: the plugin's own 8 terminal_* tools (off by default) were the model's only cross-call persistent terminal; the upstream equivalent @deepseek-ai/dsh-tool-terminal is not mounted by any shipped bundle, so if you need that capability, add a tool-terminal row to your profile's cordis.patch.yml yourself. 🌿 Changes: Git lens + This-Session lensTwo lenses on "what changed?": the Git lens keeps the full source-control surface (stage / unstage / commit (Ctrl+Enter) / revert, history, worktree and child-repo selectors); the This Session lens folds the session event log live, recording every file the model read / wrote / edited (grouped by file, kind-filtered, op-count badge). Both lenses now share one 36px header, 28px rows, a single empty state and a sticky commit bar; the Git lens lists changes as a directory tree (single-child chains collapsed into one row, collapsible folders with descendant counts, status letters and file icons on the leaves); the git snapshot is shared with the file tree, so staging recolors the tree as well. Clicking any change previews it in the draggable bottom pane with the unified diff — del red / add green / mod-blue pairing + intra-line character highlights + syntax coloring + context folding — or expands into a VSCode-style dedicated diff tab (same rendering stack).
🌐 External-link takeover (browser view provided by DSH)Web tabs are DSH's own ui-sidebar-browser (multiple tabs / back-forward-reload / address bar / sandboxed iframe), mounted only in the desktop profile since 0.1.7 — the Web profile has no such kind. The plugin keeps the half the host does not provide: it takes over only links a tab type explicitly claims through urlTarget (Ctrl/Cmd-clicks always pass through) and lets everything else through to the host (whose linkOpening user setting decides where prose links go); the three protocol-routing settings are gone, and a claim whose target type is unavailable at open time falls back to window.open. 🧩 Tasks: Agent Topology + Background JobsLive subagent-tree topology (run states, batched live previews) plus the background-jobs list (exit codes / live output / force-kill); new subagents / jobs can auto-activate the Tasks page, expanding the sidebar on wide viewports without forcing narrow full-screen drawers open (configurable).
💬 Side Chat (beta)Codex-style side threads: one independent tab per conversation; the thread inherits the parent's full context (including the in-progress turn, honestly frozen as "interrupted") and runs independently without polluting the main session; follow-ups survive restarts; one click promotes the thread to a top-level session. 🖥️ DSH's native right sidebar + plugin bottom workbenchThe right column is DSH's own sidebar: the plugin registers every tab type as a native tab (including taking over the built-in Files page), so clicking a file in the chat lands there directly — formats the host's own document preview already covers are rendered by the host, and the plugin claims only Markdown / HTML / editable code; the plugin's own bottom panel can stay open alongside it — drag a tab to a pane edge to split, to the middle to merge, drag the top edge to resize; the toggle lives in the session header.
⚙️ Declarative SettingsThe "Side card" section in DSH settings: one small card per tab / viewer with an independent toggle (highlighted enabled state + brand switch); secondary settings open from the "Feature settings" strip at the card bottom (switch / text / number / select rows); plugin-owned settings persist under pluginSettings, while the whole preference set lives on this plugin's mount row in the profile (since DSH 0.1.7 settings are addressed by Loader entry id). 📱 MobileOn narrow screens (<768px) the panels become a full-width drawer: bottom-panel tabs merge into the sidebar once, with touch-friendly dragging.

💬 Community

WeChat / QQ group QR codes will live here. After uploading the QR images (drag them into any issue/comment to get a user-attachments link), replace src below and uncomment:

🆕 Recent Updates

Supported DSH versions: · full release history on the Releases page

v0.24.1

🐞 Fix release: two defects made the file tree rebuild itself on every interaction — (1) the native surface re-created the files takeover’s slot registration on EVERY session-state write (folder toggles, tab switches, bottom-workbench drags), so the host swapped the slot entry and unmounted/remounted the whole tab body: the explorer lost its level cache, scroll position and directory watcher and re-listed the entire visible tree; (2) the live-refresh re-list deleted a level from the cache before refetching, replacing its rows with a loading placeholder (a build, a formatter or the model’s own bash made whole folders blink). Now a toggle asks only for the toggled level, the tab body survives, and disk changes update rows in place.

v0.23.0

🧭 Development-line version (never published to npm; its content ships with v0.24.1): the Files and Changes pages rebuilt. The Files page gains Ctrl/Cmd and Shift multi-select, a batch bar, Git change coloring, new-folder creation, reworked drag-and-drop upload and "Zip and download" on a multi-selection (a host-side archiving task with progress); "Open with" keeps the host-detected local apps and the plugin's own targets side by side (setting openWithPluginTargets), and the context menu was restructured; the Changes page is a hierarchy tree again (Git view + Agent view, directory staging). Performance: fs.tree measured 22.8ms → 3.8ms (10k entries) with the new batch route fs.trees (mount/refresh: N+1 requests → one), and opening the context menu no longer re-lists directories. ⚠️ Behavior change (security-relevant): the workspace path fence was removed — the plugin's fs routes can read and write any path the host user can reach (OS permissions only). Details in CHANGELOG_EN.

v0.24.0

📦 Support line moved: DSH 0.2.0-rc.1+ only (peer floor ^0.2.0-rc.1, CI pins @deepseek-ai/dsh@0.2.0-rc.1). The 0.1.7 line (including npm latest = 0.1.7-rc.2) should pin v0.22.1 (the last published release on the 0.1.7 line): a caret never spans a minor bump, so ^0.1.7-rc.1 fails the 0.2.0 host's startup compatibility preflight and the whole row is disabled (measured: semver.satisfies('0.2.0-rc.1','^0.1.7-rc.1',{includePrerelease:true}) === false).

  • 📦 Baseline lifted to 0.2.0-rc.1: 14 DSH peers and 27 @deepseek-ai/* devDependencies move together; dsh.plugin.json's engines.dsh follows.
  • 🔍 Measured as purely additive: across the 19 host packages this plugin consumes, zero value exports were removed; the only type-surface changes are ui-primitives (optional props on DisclosureRow / TextShimmer / Tooltip, plus the overlay top inset), dsh-session (new ToolCallRecovery) and dsh-api-remotes (a new product-analytics remote). Session format stays v4, SUBAGENT_DESCRIPTOR_VERSION stays 3, and dsh/lib/bin.js plus the dsh-client-modules runtime are byte-identical — so no compatibility branch for 0.1.7 is kept.
  • 🧪 Mount lane and CI pins follow to 0.2.0-rc.1; the peer-shape rule in tests/market-manifest.spec.ts now pins the current baseline tuple and records the "a caret never spans a minor bump" lesson.
  • ⚠️ Ecosystem side effect: the optional @huanlin/dsh-plugin-better-locale integration pins the ^0.1.x line and cannot load on 0.2.0; its 5 unmet peers are the only residue in pnpm peers check (upstream has not adapted; unrelated to this plugin's own 14 peers). Supported DSH versions: · full release history on the Releases page

v0.22.1

📦 Stable release (npm latest): the support line is unchanged — DSH 0.1.7-rc.1+ only (peer floor ^0.1.7-rc.1, CI pins @deepseek-ai/dsh@0.1.7-rc.1), so 0.21.1 / 0.22.0 users can upgrade straight away. Two defects that were reproducible on a real host while every unit test stayed green are fixed; hosts on DSH 0.1.6-alpha.2 or earlier still pin v0.19.1.

  • 🐛 The files takeover could be orphaned → error spam + an empty tree (community issues #770 / #771, proven from the desktop shell's own log): during an in-page client entry replacement (plugin-market update, Plugins page disable→enable, HMR rebundle), sync()'s drop loop released the files takeover — which is not a descriptor — and re-created it in the same pass. That re-creation ran on an already inactive plugin context: tabs.register lives on the HOST context and took the id anyway, while the ctx.slots.inject right after it threw cannot create effect on inactive context, so the disposer was lost and the id became unregistrable for the rest of the page's life (native register files error: … already registered spam plus the Files window falling back to the host's empty state until a page refresh). The drop loop now skips FILES_KIND (the takeover lives and dies by the editor-type switch and the seat disposer only), and any registration that fails after the host took the id is rolled back (including slots already installed), leaving only a failure the next notification can retry. Taken from community PR #777 (@yanzhaohui1999).
  • 🖥️ macOS desktop: window drag / double-click-title zoom stopped working (#772): the plugin host is a direct body child, so the shell's html[data-platform=darwin] body > :not(#root) { -webkit-app-region: no-drag } applied to it — and app-region ignores pointer-events — leaving the viewport-sized panel layer cancelling every drag strip beneath it (the first drag worked, later ones did not). [data-dsh-better-sidebar], [data-dsh-panel-host] and the zoom modal .mermaidModal now opt out with the neutral initial !important, while panels and their controls stay no-drag so clicks are never swallowed. Merges community PR #773 and finishes the job for the zoom modal, the last viewport-sized body child.
  • ✅ They stay fixed: new unit cases pin "a notification must not tear the takeover down" and "a failed registration must release the type and the slots it installed" to the registry's event log (4/4 red on the unfixed code), plus a deployment-level regression gate tests/e2e/native-reload.e2e.ts (red on 3 consecutive runs against npm 0.22.0, green on 3 consecutive runs of the fixed build (one case, run repeatedly)). The drag contract is guarded by unit cases and by a real cascade probe in the mount lane that reads computed values against the shell's own rules. Verification: pnpm test 122 files / 1293 passed / 9 skipped, pnpm test:mount and test:mount:aggregate green. Incident write-up: docs/plans/2026-09-28-native-files-takeover-reload-leak.md.

v0.22.0

📦 Stable release (npm latest): the support line is unchanged — DSH 0.1.7-rc.1+ only (peer floor ^0.1.7-rc.1, CI pins @deepseek-ai/dsh@0.1.7-rc.1), so v0.21.1 users can upgrade straight away. Hosts on DSH 0.1.6-alpha.2 or earlier keep pinning v0.19.1.

  • 🧩 The Tasks page is now a workflow graph (the primary view): the session tree renders as layered nodes joined by bezier edges — drag to pan, wheel-zoom to the cursor, fit to the content box, a control cluster bottom-right (graph/tree toggle + fold switch + zoom + fit); the classic indented tree is kept (keyboard-navigable) and both modes share one view model, so fold state and team enrichment never drift apart.
  • 🃏 Two-segment node cards: the top segment is the kind badge (main agent / subagent / teammate / workflow / completed aggregate) + phase badge + name + meta; the bottom bar is the state dot + state word + the same merged activity line the main agent shows (concurrent tools grouped and counted, with the running call's detail — wording from the host's chat namespace) + a fold button on finished nodes; a running bar is swept across its whole width (disabled under prefers-reduced-motion). 8px-rounded, hierarchy carried by a faint top-segment tint only, the session you are on wearing a heavier accent border.
  • 🔀 Workflow runs enter the graph: runs folded from tool-workflow/* events (the same events the official panel folds) hang under their origin agent, with member agents re-parented below and boxed per phase with matching badges; members with no catalog row are synthesized from the run's own data, so a finished run still shows who took part.
  • 🗂 Folding split into two groups that say what they hold: ✓ N completed (including failures, called out as N failed) and N idle (teammates that finished a turn and can be called back at any moment) are two separate rows; the manual fold button always works, the automatic rule only sweeps idle members once there are 3 or more, and an aggregate's name line reads "first two names + +N".
  • 🪟 Two persistent floating windows (extracted into a reusable FloatingWindow): background-job output and the shared task's detail/edit surface — draggable, resizable from every edge, a self-scrolling body, dismissed only by the close button or Escape (no outside click / blur / anchor observer); the task window hands its spare height to the description, so enlarging it gives the content room, and the action row is pinned to the bottom.
  • 👥 Agent Teams board (experimental layer): members enrich their nodes and an always-visible strip lists the roster and shared tasks; the state machine follows the host (pending → claim → in progress → complete → reopen) with reassign / edit / two-step delete, and stale CAS revisions get their own notice; member activity is overlaid from subagents.live's running flag.
  • 🔄 Background jobs now read the host's client ctx.jobs (a pushed roster plus a non-consuming output stream and kill): the plugin deleted its own jobs.list / jobs.output / jobs.kill routes and the event-replay mirror, never touching the model's job_output cursor; output streams in a persistent floating window with tail-follow, and the drawer auto-collapses at 8+ agents.
  • 🛠 DSH 0.1.7 data-plane rewrite: upstream deleted the three agentTeams.remoteView-style Remote methods → team reads moved to the Lead Session's agentTeam Session projection (push-based; the teams.view route and its 5-second poll are gone); the two write routes stay, with rejections moved from a result union to a thrown TeamError and a stale revision mapped to a 409 team-conflict. The bug this fixed in the wild: on 0.1.7 the team strip never rendered at all (the route answered remoteView is not a function, and the page degraded silently).
  • 🐛 Four defects caught on a real host, all green in unit tests: the per-node fold button did nothing (blocked by the automatic rule's guards); an idle card never drew a fold button; claiming a task mislabelled it "blocked"; and "complete" on a queued task always failed (a claim comes first).
  • 🎨 Narrow panes and mobile settings: card and row metrics re-tuned for the native right sidebar's narrow width; the settings page gained a Mobile group — on a narrow viewport (≤768px) the Tasks page no longer auto-opens and defaults to the tree view.

📜 Earlier versions: full release history in CHANGELOG_EN.md (v0.21.1 → v0.12.3) and on GitHub Releases.

⌨️ Keyboard Shortcuts

Action Keys
Save edits Ctrl/Cmd + S
Git commit Ctrl + Enter
Close tab Middle mouse button
Tab context menu (right-click) Close / Close Other Tabs / Close Tabs to the Left / Close Tabs to the Right (current pane)
Split / merge panes Drag tab to pane edge / middle
Reference file to input Hover the @file button at end of line
Copy file path Right-click row → copy relative/absolute path

🔌 Service API

Since v0.4.0 the plugin exposes the ctx.betterSidebar service — other plugins can register sidebar pages and file viewers (the 5 built-in tabs + 3 viewers register through the same service). v0.12.1 completed the base capabilities (complete type exports, capability detection, state subscription, tab badges, lifecycle callbacks, targeted open, plugin-owned settings, etc.).

Full integration docs (complete fields, matching algorithm, HMR pitfalls, declarative settings, version detection, the native-sidebar surface and the skinning contract): docs/external-plugin-guide.md; repository rules (hard constraints / CI / release) live in AGENTS.md.

➕ Add Plugins (recommended plugin catalog)

The dashed cards at the end of the "Sidebar content" / "File viewers" grids in the "Side Cards" settings section open the Add tab plugins / Add preview plugins modals: each declares its open extension point, offers a "Browse more plugins on GitHub" button (the GitHub topic dsh-better-sidebar), and lists the recommended catalog (name / repo / description / install script) — "Open" jumps to the repo, "Copy" writes the install command to the clipboard.

Curating a new plugin: append a PluginEntry to src/client/plugins-tabs.ts (tab registrations) or src/client/plugins-viewers.ts (file-previewer registrations) and tag your repo with the dsh-better-sidebar topic; data integrity is guarded by tests/plugin-list.spec.ts.

🛠️ Development & Build

pnpm install      # @deepseek-ai/* devDependencies resolve (baseline 0.1.7-rc.1, alpha dist-tag) — no token needed
pnpm typecheck    # tsc --noEmit
pnpm lint         # eslint . (flat config: js + typescript-eslint + react-hooks recommended)
pnpm build        # → lib/index.js + lib/invariant.js + lib/client.js + lib/client-registry.js + lib/types
pnpm test         # vitest (includes manifest consistency guard; build first)
pnpm watch        # tsdown --watch

Make thin wrappers (make help lists every target; package.json stays the single source of truth):

make check          # aggregate gate: typecheck → build → test → check:consumer-types (mirrors CI)
make mount          # real-mount smoke: build + pack → install Chromium → pnpm test:mount
make clean          # remove lib/, *.tgz, playwright-report/, test-results/

pnpm check:consumer-types: the consumer-facing declaration-surface guard — type-checks the built lib/types from a browser-only consumer's perspective (no @types/node, skipLibCheck: false); run pnpm build first.

Architecture: a single npm package with host/client halves — host (src/index.ts): /sidebar/api/* JSON API, /sidebar/file media route, /sidebar/html preview route, /sidebar/upload upload route, and two WebSockets (/sidebar/ws/agent-opens for model-driven opens, /sidebar/ws/fs-watch for the file tree's directory watch; fs / git / preview are all session-scoped behind a trust fence); client (src/client/index.tsx): portal sidebar + views + link takeover; state persisted per session in localStorage. Organized per DSH official conventions (no default export, dual client bundles); no dependency on npm / checkout at runtime (@deepseek-ai/* provided by the web profile).

🔐 Security

  • Routes protected by a Host-header trust fence (same as /api); fs.write is atomic; git only shells out to the CLI and never sets identity
  • ⚠️ Since v0.23.0 the filesystem routes do NO workspace containment: fs.tree / fs.trees / fs.read / fs.write / fs.rename / fs.remove / fs.mkdir / media / HTML preview / /sidebar/upload / archive.build reach any path the host user can reach (OS permissions are the only bound; the workspaceFence switch and the 403 branch are gone) — do not treat these routes as fenced
  • HTML preview content renders in an opaque-origin sandboxed iframe (no allow-same-origin/allow-top-navigation, no-referrer, all permission policies disabled); the /sidebar/html route carries a CSP sandbox + size/path bounds
  • The settings page can disable the HTML preview's sandbox per feature (htmlViewerNoSandbox / htmlViewerDefaultUnsafe, off by default, with a warning) — when off, content shares the origin with the UI; only recommended for fully trusted content. The web tab's sandbox is no longer this plugin's surface: the browser view comes from the host (desktop profile); see DSH's own docs for its sandbox and navigation policy

⚠️ Known Limitations

  • Git has no push/pull/fetch; Markdown previews provide a manual refresh button with confirmation before discarding unsaved edits; the file tree only watches expanded directories (collapsed folders are unsubscribed, and there is no recursive whole-workspace scan); tool inline file-open buttons cannot be intercepted
  • Which read-only previews exist is the host's call: spreadsheets / PDF / images / Office go to DSH's own ui-sidebar-documentpreview, while the plugin renders only Markdown / HTML and the editable text buffer; the host implementation (rendering details, zoom, refresh timing) follows the DSH version
  • The browser view exists only in the desktop profile: the Web profile has no host browser kind and the plugin no longer ships a browser tab, so web tabs are desktop-only; login state / third-party cookies / X-Frame-Options limits follow the host implementation
  • HTML preview renders the saved file (not unsaved drafts)
  • No bottom panel on mobile (<768px): on narrow screens its tabs merge into the right sidebar once (after migrating back to desktop they stay in the right sidebar); the desktop bottom panel is only available on wide viewports. Without a selected session, tapping the subdued toggle shows the select-session message; with a selected session, it opens the full-width drawer

🖥️ Platform Support

Windows / Linux / macOS (macOS validated daily; the rest covered by unit tests). The plugin carries no native dependencies (the terminal and node-pty went back to DSH wholesale), so building needs only Node + pnpm, with no compiler toolchain.

🌐 Plugin Ecosystem

The ctx.betterSidebar service opens two extension points to every plugin: registerTab (sidebar pages) and registerFileViewer (file previewers). The 5 built-in tabs + 3 viewers register through the exact same API — fully equal capabilities.

import type {} from 'dsh-better-sidebar'  // triggers the ctx.betterSidebar type merge
export const inject = ['betterSidebar']
export function apply(ctx: Context) {
  ctx.effect(() => ctx.betterSidebar.registerTab({
    id: 'my-plugin:db', title: 'Database', component: ({ scope }) => <DbView sessionId={scope.sessionId} />,
  }))
  ctx.effect(() => ctx.betterSidebar.registerFileViewer({
    id: 'my-plugin:csv', exts: ['csv'], fetchStrategy: 'custom',
    load: async (path, scope) => parseCsv(await fetchText(scope, path)),
    component: ({ customData }) => <CsvGrid rows={customData} />,
  }))
}

The GitHub topic dsh-better-sidebar already hosts 28+ ecosystem plugins (and growing):

📑 Tab Plugins (sidebar pages)

Plugin ⭐ Description
ChenRuoT/dsh-sidebar-qa Selection-based side Q&A — Codex-style side questions / Claude Code /btw
fuhefei/dsh-sentinel Condition-driven wakeup: file / command / HTTP / process / webhook watches that wake the agent; dock + sidebar branch + global dashboard
Fisfzy/ego-browser Agent browser: a local browsing tab (@dsh-external/ego-browser, auto-registers the sidebar page when better-sidebar is present, floating-bubble fallback otherwise)
jiuge2467/dsh-studio Full-stack enhancement workbench: multi-source MCP visual debugging hub, visual thinking engine
Iwctwbh/dsh-flowglass Flowglass: live session flowgraph (messages / tool groups / subagent branches)
FeatherHunter/dsh-mattpocock-skills-deck Game-like mission system for mattpocock/skills: fog-of-war map + task bar
GULI-lab/DSH-element-source Click any UI element on your dev page to jump to its Vue / React / Svelte / Angular source, straight into the chat
Lzh3070/dsh-file-review-tab File-change review tab: line-level red/green diffs + undo + chat-line deep links
yq04/dsh-git-remotes Git remotes tab: branches / upstream / ahead-behind, fetch with prune, ff-only pull, confirm-before-push
ztyhehe/dsh-better-sidebar-svn SVN source-control tab: status / diff / log / commit / update / revert / conflict resolution — symmetric to the built-in Git panel
Melody-max114/dsh-excel-panel Excel editing: xlsx preview/edit, live formula evaluation, merged cells, save back to the original file
v587d/dsh-anysearch-refs AnySearch results as sidebar cards: query, source snippets, highlighte

…

Content from the project README on GitHub ↗

Links

More in this category

View the whole category →

Community comments

Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.