dsh-plugin-sidebar
September 1, 2026 ยท View on GitHub
๐๏ธ A left & right sidebar for DeepSeek Harness
Left ยท workspace/session browsing โ Right ยท file + Git panels
Highlights ยท Install ยท Development ยท Architecture ยท License ยท ไธญๆ
๐ ็ฎไฝไธญๆ: ่ง README.md ยท English in this file
dsh-plugin-sidebar
Make DeepSeek Harness session management feel like an IDE.
Browse every session by workspace on the left (status dots, groups, search, one-click actions); browse files and review Git changes in-place on the right (commit, switch branches). Install and go โ styling follows the DSH theme and adapts to light/dark.
โจ Highlights
๐งญ Left sidebar ยท workspace/session browsing
- Grouped by workspace: collapsible group headers (folder icon + title + count + chevron), active session highlighted
- Session status at a glance: running (green pulse) / waiting (amber) / done / idle โ status lane + title chip
- Hover quick actions: rename (inline) / fork / archive / move to folder
- Top search: local title/cwd/workspace match merged with remote content search (250 ms debounce),
Escto dismiss - Group-hover actions: new session here / rename workspace / delete workspace
- Rail mode: sidebar collapses to an icon column; search/new auto-expand
๐ Right sidebar ยท file browsing + Git panel
- Session-header toggle (next to the session log download); toggle state is remembered per session โ a session you opened the panel in stays open after switching away and back
- File explorer: lazy directory tree (expand to load children), name search, content search (file/line/text), directory git-status aggregation badges (highest priority D>M>A>R>U of changed files underneath)
- File preview tab (CodeMirror editing): a File preview tab after the main region's Conversation / Trajectory tabs; clicking a file in the right sidebar auto-switches there. Text files render with CodeMirror 6 โ syntax highlighting (language auto-picked by extension) + editable + save back to disk (
โunsaved marker + save button, head pinned so it's always visible); binary/large files show a notice - Git panel: status split into Staged / Changes (
--porcelain=v1 -zNUL parsing), colored diff, commit box, branch switch - Bulk actions (VSCode Source Control style, on section-header hover):
Stage all(+) /Unstage all(โ) /Discard all(undo); per-file row hover: diff(file) + stage(+)/unstage(โ) + discard(undo) - Commit history (VSCode Source Control style): timeline dots + connector line, one-line-truncated message; expand to reveal author ยท date + that commit's changed-file list (M/A/D colored letters), files lazy-loaded on demand



๐ Move session to folder
- Copy semantics (fork): the source session stays; a new session is created in the target workspace inheriting all completed history, auto-opened after creation
- Uses the official
agents.create(seed + meta.cwd)path; the new session is an agent (lifecycle owned by the agent registry, not removed when the plugin stops) - Target-workspace picker:
createPortalto body +position: fixedanchored, escaping listoverflowclipping, flips upward near the viewport bottom
๐ Polished for Chinese users
- Full Chinese UI + English, follows the DSH locale automatically
- Uses only DSH theme tokens (
--dsw-alias-*/--ds-*), adapting to light/dark
๐ฆ Install
Install from GitHub or a local path. First confirm your dsh version, then pick the plugin version that matches it per the Version Compatibility table below:
# From GitHub (recommended; pin the version to match your dsh)
# Note: pnpm's GitHub version-lock syntax uses `#tag`; the old `@0.3.2` form is mis-parsed by pnpm as the nonexistent repo null/0.3.2
dsh plugin --profile web add github:webkong/dsh-plugin-sidebar#v0.3.2
# Or install the latest main branch
dsh plugin --profile web add github:webkong/dsh-plugin-sidebar#main
# Or from a local path (development)
dsh plugin --profile web add /path/to/dsh-plugin-sidebar
After restarting dsh web, the left sidebar takes over sidebar.workspaces, the right sidebar takes over the details column, and a panel toggle appears in the session header.
โ ๏ธ How it takes effect: install / restart; Host changes require a
dsh webrestart, while Client changes (lib/client.js) only need a page refresh.
๐ก Install recommendation:
dsh โฅ 0.1.2(incl.0.1.2-alpha.3): use0.3.2โ it adapts the dsh 0.1.2 client service migration and lazy resolution;dsh 0.1.1-rc.x: use0.3.2or0.3.1;- older dsh: fall back to
โค 0.2.x(but ondsh โฅ 0.1.2the older client never loads).
๐ Version Compatibility
Quick reference: your dsh version โ recommended plugin version
| Your dsh version | Compatible plugin versions | Recommendation |
|---|---|---|
dsh โฅ 0.1.2 (incl. 0.1.2-alpha.3) | 0.3.1 or newer | Recommended 0.3.2: adapts the dsh 0.1.2 uiWorkspace service migration + lazy resolution |
| dsh 0.1.1-rc.x | 0.3.0 โ 0.3.2 | Either; 0.3.0 verified on 0.1.1-rc.2 |
| dsh โค 0.1.1-rc.x | โค 0.2.x | Older; on dsh โฅ 0.1.2 the client never loads (waits for the removed dsh-client-runtime) |
Detailed: plugin version โ compatible dsh versions
| Plugin version | Compatible dsh versions | Notes |
|---|---|---|
| 0.3.2 | dsh โฅ 0.1.1-rc.2 | Left-panel hierarchy styling: the status dot left of a session title is indented rightward to emphasize the folder โ session parent-child level; the session title font size is reduced from 13px to 12px |
| 0.3.1 | dsh โฅ 0.1.1-rc.2 (verified on 0.1.1-rc.2, 0.1.2-alpha.1, 0.1.2-alpha.3) | Adapted to the dsh 0.1.2 client service migration: startSession / pickDirectory now go through the uiWorkspace service (formerly on workspaces), and layout/sessions/workspaces/uiWorkspace/timer are lazy-resolved at call time (services activate asynchronously; no longer cached at apply time). Fixes "right-panel toggle no response" and "left add-folder no response" |
| 0.3.0 | dsh โฅ 0.1.1-rc.2 (verified on 0.1.1-rc.2, 0.1.2-alpha.1) | Removed dsh-client-runtime from the client inject list (removed in dsh 0.1.2); not adapted to the 0.1.2 uiWorkspace migration, so some clicks may be unresponsive on 0.1.2-alpha.3 |
| โค 0.2.x | dsh โค 0.1.1-rc.x | Older; on dsh โฅ 0.1.2 the client never loads while waiting for the removed dsh-client-runtime |
| โค 0.2.x | โค 0.1.1-rc.x | On dsh โฅ 0.1.2 older clients never load while waiting for the removed dsh-client-runtime |
๐ง Development
npm install # esbuild / typescript (dev only)
npm run build # esbuild src/ โ lib/ (Host ESM + Client __ModuleLoader__ bundle)
npm run typecheck # tsc --noEmit strict
npm test # node --test pure-function unit tests (42 cases)
npm run check # typecheck + bundle syntax checks + unit tests
Structure (TypeScript-modular)
Source is organized TypeScript-modular (modeled on the official UI plugins); build output lives in lib/:
dsh-plugin-sidebar/
โโโ package.json # dsh.bundle / dsh.client declarations, scripts
โโโ cordis.patch.yml # bundle patch: mounts the dsp-sidebar row
โโโ build.mjs # esbuild build (Host ESM + Client __ModuleLoader__ bundle)
โโโ tsconfig.json # strict typecheck (node + DOM/React)
โโโ lib/ # build output (gitignored)
โ โโโ index.js # Host single-file ESM bundle
โ โโโ client.js # Client __ModuleLoader__ bundle
โโโ src/
โ โโโ host/ # Host source (Node env)
โ โ โโโ index.ts # entry: name/inject/apply + webServer route registration
โ โ โโโ session.ts # session copy (move to folder): readSession โ cut โ agents.create(seed+meta.cwd)
โ โ โโโ git.ts # git ops: runGit + porcelain/NUL/log parsing
โ โ โโโ fs.ts # fs ops: listDir / readText (512 KB truncation)
โ โ โโโ search.ts # search: name recursion + content line matching
โ โ โโโ http.ts # JSON responses / loopback check / body / escaping
โ โโโ client/ # Client source (DOM + React env)
โ โโโ index.ts # apply entry: inject styles / register dictionaries / register slots
โ โโโ api.ts # /dsp-sidebar/api fetch wrapper
โ โโโ i18n.ts # zh/en dictionary (NS + key types)
โ โโโ types.ts # contracts: session/workspace data plane + Host API + git wire shapes
โ โโโ util.ts # shared helpers: relative time / basename / status derivation
โ โโโ icons.tsx # icons (lucide-style stroke + right-panel filled glyph)
โ โโโ previewStore.ts # file-preview shared store (sessionId-scoped, useSyncExternalStore)
โ โโโ preview/ # main-region "File preview" view
โ โ โโโ PreviewView.tsx # conversation.view occupant: subscribes to previewStore
โ โโโ styles/ # CSS split by component domain + aggregate injection
โ โ โโโ left.css # left sidebar styles
โ โ โโโ right.css # right sidebar styles
โ โ โโโ preview.css # main-region preview tab styles (global dsw tokens)
โ โ โโโ index.ts # injectStyles (idempotent single style tag)
โ โโโ left/ # left sidebar (aligned with official WorkspaceBrowser + rows/)
โ โ โโโ derive.ts # data derivation: grouping / search merge
โ โ โโโ rows.tsx # row components: SessionCard / SearchRow / GroupSection (incl. move portal)
โ โ โโโ WorkspaceBrowser.tsx # main component: header + search + list + rail
โ โโโ right/ # right sidebar (aligned with official RightSidebar + SourceControl + FileExplorer)
โ โโโ derive.ts # git status classification (badge / staged / unstaged / untracked / dirBadge)
โ โโโ FilesPanel.tsx # file browser (lazy tree + search + preview + git badges)
โ โโโ GitPanel.tsx # git panel (status / stage / diff / commit / timeline history / branch)
โ โโโ RightPanel.tsx # panel shell (activity bar + tab switch) + header toggle
โโโ test/ # pure-function unit tests (node --test)
๐ก Communication contract
The Host exposes an HTTP API via a webServer prefix route /dsp-sidebar/api (loopback only, POST, method name as the last path segment); the Client calls it with browser fetch:
| Method | Description |
|---|---|
fs.list | list dir ({path}) |
fs.read | read text (512 KB truncation; binary returns kind:'binary') |
fs.search | search ({mode: 'name'|'content', path, query}) |
fs.gitStatus | dir git-status map (path โ XY, for file badges) |
git.status | git status ({cwd}) |
git.diff | diff ({cwd, path?, staged?}) |
git.stage / git.unstage | stage / unstage ({cwd, path?}) |
git.discard | discard changes ({cwd, path}) |
git.commit | commit ({cwd, message}) |
git.log | commit history ({cwd, count?}) |
git.logFiles | changed files of one commit ({cwd, hash}, name-status parsed) |
git.branches | branch list ({cwd}) |
git.checkout | checkout branch ({cwd, branch}) |
session.copyTo | copy session to target workspace ({srcId, targetPath}) โ returns {sessionId} |
๐ Architecture
- Host (
src/host/): registers the/dsp-sidebar/apiprefix route viawebServer. File ops go through the mountedfsservice (resolveโlistDir/stat/readText/readBytes, respecting sandbox & observation policy); git ops go through the mountedshellservice (resolve+run,git -C <cwd>+ porcelain/NUL parsing โ the same execution path as the official bash tool) - Client (
src/client/): calls the Host viafetch('/dsp-sidebar/api/...'); registers slots (sidebar.workspaces/details/conversation.session.header.utilities/conversation.view); session copy uses Host services likesessionQuery/workspaceRegistry/agents(lazy resolution โ these activate asynchronously, soctx.getat call time rather than cached atapply)