๐๏ธ A left & right sidebar for DeepSeek Harness
Left ยท workspace/session browsing โ Right ยท file + Git panels
Highlights ยท Install ยท Development ยท Architecture ยท License ยท ไธญๆ
--- > ๐ **็ฎไฝไธญๆ**: ่ง [README.md](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), `Esc` to 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 -z` NUL 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: `createPortal` to body + `position: fixed` anchored, escaping list `overflow` clipping, 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**: ```bash # 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 web` restart**, while **Client changes (`lib/client.js`) only need a page refresh**. > ๐ก **Install recommendation**: > - `dsh โฅ 0.1.2` (incl. `0.1.2-alpha.3`): **use `0.3.2`** โ it adapts the dsh 0.1.2 client service migration and lazy resolution; > - `dsh 0.1.1-rc.x`: use `0.3.2` or `0.3.1`; > - older dsh: fall back to `โค 0.2.x` (but on `dsh โฅ 0.1.2` the 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 ```bash 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/api` prefix route via `webServer`. File ops go through the mounted `fs` service (`resolve` โ `listDir`/`stat`/`readText`/`readBytes`, respecting sandbox & observation policy); git ops go through the mounted `shell` service (`resolve` + `run`, `git -C