Enhances the native right-sidebar Files tab rather than replacing it: each row gets an @ reference button that inserts the canonical path mention into the composer, plus a size column, a sortable modification-time column, and a status dot for untracked, unstaged and staged files. Below the tree a git bar shows the branch with ahead/behind counts and expands upward into the 20 most recent commits, with a repository selector when a workspace has independent repositories in subdirectories. A header search box walks the workspace recursively and can open a result or expand the tree at it, preview tabs offer reveal-in-tree and one-click @, the tree is keyboard navigable with ARIA tree semantics, and a right-click entry on the Files tab switches back to the native tree at runtime.
Install
# from npm (prebuilt)
dsh plugin --profile web add dsh-at-sider
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:aiyacharley/dsh-at-sider
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
Six things the native sidebar file tree was missing:
@ references·file size·modification time·sorting·quick search & locate·git commit info at the bottom of the sidebar. Same tab, same entry point, same icons, same way of opening files — the right sidebar's Files tab stays the native one (sameMod+P, same guide capsule, same artwork):
- Each row: a
@filechip right after the name + size / modification time pinned to the far right (width-adaptive, never clipped);- Header: whole-workspace quick search (recursive, results can be
@-ed or jumped to) + sorting (name / time / size / type, remembered);- Bottom git bar: branch, ahead/behind, and the latest commit summary — click to expand the last 20 commits upward; multi-repo workspaces get a repository switcher, and files are coloured by state (untracked / unstaged / staged);
- Right-click a file preview: one-click
@fileand reveal in tree (ancestors expanded, the row highlighted);- Right-click the Files tab to switch back to the native tree whenever you want to compare (no uninstall, switch back any time) — all of it keyboard operable.
Contents
- 🚀 Install (two minutes)
- What it does
- Interaction
- Keyboard and accessibility
- What stays native
- How it works
- Known limitations
- Install and uninstall (complete)
- Development and tests
- Version history
- Requirements
- License
🚀 Install (two minutes)
Prerequisites: Node.js ≥ 20, then install the DSH CLI
globally (recommended — you get the dsh command):
npm install -g @deepseek-ai/dsh
dsh web # boot the DSH web environment (or: npx @deepseek-ai/dsh web)
With the app running, one command installs this plugin:
# one command (official CLI, recommended)
dsh plugin --profile web add dsh-at-sider@latest
# or from GitHub: dsh plugin --profile web add github:aiyacharley/dsh-at-sider
# or from a local checkout: dsh plugin --profile web add /path/to/dsh-at-sider
Then restart DSH (dsh web) and open the right sidebar's Files tab (or
press Mod+P). Three checks:
- hover any row — an
@filechip appears right after the name (@folderon directories); - the row's far right shows its modification time, e.g.
2026-01-02 11:04, with the full local time on hover; - click
@file— the file's@mentionlands in the composer (hold Alt/⌥ to copy the mention instead).
No configuration. Every install route (agent-driven, manual patch, uninstall) is in Install and uninstall (complete).
What it does
| Row element | Behaviour |
|---|---|
@file button (@folder on directories) |
Inserts @path (or @"path with spaces", @dir/ for directories) into the session's composer as an atomic file reference — the same chip the built-in @ completion and the built-in file drop produce. Falls back to copying the mention when no composer is reachable. Shown while the row is hovered or focused. |
@file + Alt/⌥-click |
Always copies the mention to the clipboard. |
| date column | YYYY-MM-DD HH:mm in local time; hover shows the full local date and time. Entries whose stat failed show nothing. |
| size column | Regular files show a humanized size (870 B, 1.5 KB, 1.2 MB); the date's tooltip carries it, and the size's own tooltip gives the exact byte count. |
| sorting | A header button cycles name → modified time → size → type; directories stay first, entries without the field sink to the end, and the mode is remembered (localStorage, best-effort). |
| adaptive columns | The tree measures its own width in three tiers: ≥380px shows every column; 300–379px hides the size column; below 300px the chip collapses to a bare @ and the date to MM-DD HH:mm — nothing gets clipped in narrow sidebars. |
| runtime native fallback (R10) | Right-click the Files tab → "Use the native file tree": instantly switches back to the native tree (no plugin uninstall needed); the same entry becomes "Use the enhanced file tree" to switch back. Note: Harness tabs have no ⋯ button — the entry point is the right-click menu; the enhanced tree's expansion state is not kept across the switch, and a restart starts enhanced. |
| reveal in file tree (R16) | Right-click a file-preview tab → "Reveal in file tree": focuses the tree, expands the ancestor directories, scrolls to the row and briefly highlights it (same outline as the keyboard focus ring). |
one-click @file from a preview (R16) |
Right-click a file-preview tab → "@file": inserts the previewed file's reference straight into the composer — exactly what the row's @ chip does, without going back to the tree to find that row. |
| where the entries appear | Both entries are offered only in the right-click menu of file-preview tabs — not on the Files tab or other tabs. Harness tabs have no ⋯ button, so the entry point is the right-click menu. |
| git state on rows (R40a) | A coloured dot after a file's name marks its work-tree state: untracked (green) / modified, unstaged (amber) / staged (blue), with a naming tooltip. At the bottom of the sidebar a git bar shows ⎇ branch ↑ahead ↓behind · latest commit summary (relative age); clicking it expands the commit list upward (the last 20 commits, newest at the bottom with a ● HEAD marker). Multiple repositories: when the workspace itself is not a repository but its first/second-level subdirectories are, the bar gains a repository selector — the picked repository drives the branch/commit info and the file dots. A broken repository (a .git without HEAD) shows in the selector as a disabled option, and the bar explains 「Not a valid git repository」 instead of vanishing. Outside a repository — or without a git executable — nothing renders; the state is read with the listing and cached for 30 s. |
| icons | The host's own artwork, unchanged: FileTypeIcon + classifyFileType (category-coloured per file kind), the line-art folder icons, and GuideArtworkFiles for the guide capsule — the very components the native tree draws with. Inline glyphs take over only if those exports are unavailable. |
| header | Workspace root path (full path on hover), an auto-refresh toggle, and a reload button. |
| rows | Directories first, then natural case-insensitive name order — the native tree's own ordering. |
Auto-refresh rides the Harness's own workspaceFiles.changes directory watch: a
save in the workspace refreshes the open levels, with no polling.
Interaction
📁 src @folder 2026-01-02 11:04
📄 README.md @file 2026-01-01 09:12
- The chip is hidden until the row is hovered or keyboard-focused; a click inserts
the reference, Alt/⌥-click copies it, and the button then shows
referenced/copied/failedfor 1.4 s before returning to its noun label. - Times use a fixed
YYYY-MM-DD HH:mmshape (no locale drift); the tooltip carries the full local time. - Directories expand level by level; reopening a level rereads it, and expansion plus scroll offset are remembered per tab — a file-preview round trip, or a runtime toggle to the native tree and back, leaves the tree as it was (a page reload starts over).
Keyboard and accessibility
Click any row once to move focus into the tree; after that everything is keyboard-only:
| Key | Behaviour |
|---|---|
| ↑ / ↓ | Move focus row by row across the visible rows (across levels) |
| → | Collapsed directory → expand; expanded → focus the first child row |
| ← | Expanded directory → collapse; file/child → focus the parent row |
| Home / End | First / last visible row |
| Enter / Space | Directory = expand/collapse; file = open in the sidebar |
@ |
Insert that row's reference into the composer |
| Tab | Leaves the whole tree in one step (roving tabindex — no per-row stops) |
Accessibility semantics: the root list is role="tree", rows are
role="treeitem" (with aria-level / aria-expanded), nested levels are
role="group", and the search result count is announced through a polite live
region. The reveal highlight matches the keyboard focus ring.
What stays native
- Tab identity: still the
fileskind — theMod+Pshortcut, the guide page's "Workspace files" capsule and the tab title all come from the same registration. - Icons: the host's category-coloured
FileTypeIcon, its line-art folder icons and itsGuideArtworkFilesartwork are read from the running app — nothing is re-drawn or copied. - Opening: a row click still opens the file in the sidebar through
dsh-resource://file/session/.... - Ordering: directories first, then natural case-insensitive name order.
How it works
The native file tree has no per-row extension point (its row components are
module-local and the package declares no row seat), so richer rows require
supplying the body: a tab type registered at priority extension with the same
kind (files) takes over the builtin, and its body/title are dispatched by the
definition's own id — so there is no key collision.
No Client seam carries a modification time: workspaceFiles entries are
{ name, type, size? }, and the file version token is opaque by contract. The
Host half therefore serves two authenticated routes of its own:
POST /api/dsh-at-sider/list { sessionId, path }
-> { ok: true, value: { path, root, entries: [{ name, type, mtimeMs, size? }], truncated } }
POST /api/dsh-at-sider/search { sessionId, query }
-> { ok: true, value: { query, matches: [{ name, path, dir, type, mtimeMs, size? }], truncated } }
Both are confined to the session's workspace root (the path is resolved against
it and refused when it escapes) and are read-only (file contents are never
read). The listing reads at most 2000 entries per level and caps stat
concurrency at 32; the search walks the whole workspace, skipping
node_modules/.git, never descending into symlinked directories, with a depth
cap of 12 and 200 results, and stats only the matches.
Three further Client mechanisms: the action menu
(sidebar.right.tab.menu.item, a list seat whose entries receive the tab they
belong to plus dismiss) carries 「use the native file tree」, 「reveal in file
tree」 and 「@file」; the runtime fallback dispose/re-registers the tab-type
definition (the builtin resumes the moment it is unregistered); reveal hands
the target path to the tree through openTab('files', { params: { reveal } }),
and the body expands the ancestor chain and flashes the row from
tab.navigation.params. docs/DESIGN.md records every
alternative that was evaluated and rejected.
Known limitations
- The builtin
filesbody is shadowed, not composed: while this plugin is loaded the native tree does not render; uninstalling restores it exactly, and right-clicking the Files tab offers 「use the native file tree」 to switch back at runtime (no native code is modified either way). - Expansion and scroll offset are remembered per tab in a module-level memory, so preview round trips and the runtime toggle keep the tree as it was; what is not kept is the level data (a returning level is refetched) and anything across a page reload — the full slot-store migration is ROADMAP R30.
- Git state comes from
git status/git logsubprocesses in the Host half (30 s cache, in-flight deduplication, timeout and output caps); outside a git repository nothing renders. A workspace that is a subdirectory of its repository maps status paths through--show-prefix; rename records count as their new path. - Modification times and the search both come from
node:fs(thectx.fsseam exposes no time field), so both routes carry their own workspace containment. - The Client half mirrors a few private details of the native tree (row ordering,
the
dsh-resource://address grammar, the composer's reference-insertion call, the tab action-menu seat,openTab's navigation params andTabRecord.contentId). Each has a degradation path — a missing composer falls back to the clipboard, a missing watch falls back to the reload button, a missing menu seat simply means two fewer menu entries — and the version this was built and tested against is pinned indsh.compatibility. - The plugin's inline glyphs only appear when the host artwork is unavailable.
Install and uninstall (complete)
Install
1. One command (official CLI, recommended)
dsh plugin --profile web add dsh-at-sider@latest
# or from GitHub: dsh plugin --profile web add github:aiyacharley/dsh-at-sider
# or from a local checkout: dsh plugin --profile web add /path/to/dsh-at-sider
2. Paste this to an agent
[Please install dsh-at-sider persistently (available in every session after a restart)]
1) Determine the DSH profile name (e.g. web; ask first if unsure).
2) Run: dsh plugin --profile <name> add dsh-at-sider@latest
3) Tell the user to restart DSH web (the Host half's listing and search routes register on boot).
4) After the restart, verify: hovering a row in the right sidebar's Files tab shows
@file, the row's right edge shows a size and a modification time, the header has
a search box, right-clicking the Files tab offers "use the native file tree", and
right-clicking an opened file preview offers "reveal in file tree" and "@file".
3. Manual (optional)
- Profile bundle layer: add
"dsh-at-sider": "link:/path/to/dsh-at-sider"todependenciesin~/.dsh/profiles/<name>/package.json, add"dsh-at-sider"todsh.profile.bundles, runpnpm install, then restart. - Or merge the insert row from cordis.patch.yml into your own patch layer and restart.
Update
dsh plugin --profile web update dsh-at-sider@latest # or @0.2.0 to pin
Restart DSH for the change to take effect.
Changed
client.jsduring development and a restart "did nothing"? A client bundle is addressed by arevderived from the file's metadata (mtime/ctime/size), and an old-rev request is rejected rather than served fresh bytes. Hard-refresh the browser first (Ctrl+Shift+R); if it is still stale, remove and re-add the plugin — a reinstall rescans the artifact and publishes a new revision, even without a restart. Alink:install points at the working tree itself and has not gone stale; this is the revision-stamp layer. See the troubleshooting table in docs/INSTALL.md.
Uninstall
dsh plugin --profile web remove dsh-at-sider, then restart.- For a local
link:install: drop the dependency and thedsh.profile.bundlesentry, runpnpm install, then restart. - The native file tree returns exactly as it was.
Troubleshooting (installed but unchanged, empty date column, …) is in docs/INSTALL.md.
Development and tests
npm test # node --test, 83 cases, fully offline (no network, no browser; git cases use a real git)
- This plugin has no dependencies and no build step:
client.jsis the final browser module-loader artifact,index.jsis the Host entry. - The suite loads
client.jsthe way the module system does — insidenew Function, withwindow,navigatorandfetchinjected — and drives the real components through a minimal React shim: containment, per-level listing andmtimeMs, both route contracts (listing plus the search skip list, result cap and depth cap), the takeover definition and its runtime toggle, menu-entry visibility, the@pathgrammar and address parsing, composer insertion with its clipboard fallback, the button labels, the host-artwork path and its fallback, the row box model, tree semantics (role/aria-level), the keyboard navigation branches, and a rendered-tree smoke test plus the quick-filter and reveal interactions. - Changes: CHANGELOG.md. Design: docs/DESIGN.md. Releasing: PUBLISH.md.
Version history
- v0.2.0 — the bottom git bar (file rows coloured by untracked / unstaged
/ staged; a pinned bottom bar with the branch and the latest commit summary,
expanding upward into the last 20 commits; full hash, author, and time on
hover); multi-repository workspaces (when the workspace itself is not a
repository but its first/second-level subdirectories are, the bar gains a
repository selector — the picked repository re-anchors the state, and a
broken
.gitshows as a disabled option with an explanation instead of vanishing); git state is read with bounded subprocesses and cached. Verified on a real multi-repo workspace; 83 offline tests. - v0.1.1 — fix: the tree's expansion survives the preview round trip now,
like the native tree (remembered per tab in a module-level memory; the runtime
toggle to the native tree and back keeps it too. Level data is still refetched
on return and a page reload starts over — full migration is ROADMAP R30); the
INSTALL troubleshooting table and the update sections document the client
artifact's
revcache layer and the hard-refresh → reinstall ladder; the test shim scopes hook state per component instance; 66 offline tests. - v0.1.0 — first feature release: runtime native fallback (right-click the
Files tab to switch between the enhanced and native trees — no uninstall
needed); quick filter / locate (a header box searching the whole workspace
recursively — skips node_modules/.git, 200 results — with
@chips on every result; click a file to open it, click a directory to jump back into the tree expanded); reveal in file tree (right-click a file preview to expand the ancestors and highlight the row); one-click@filefrom a preview (right-click a file preview to insert its reference straight away); keyboard navigation & a11y (arrows/Home/End/@, roving tabindex,role=treesemantics); 65 offline tests. - v0.0.4 — fixed a Files tab restored after a restart showing "no workspace directory" until a manual reload (the Host route now resolves the root through session persistence for cold sessions; a failed level self-heals with up to two spaced retries); size-column layout polish (right-aligned fixed box, two-space gap before the date, one right-pinned group); 52 offline tests.
- v0.0.3 — new size column (humanized bytes + exact tooltip), sortable rows
(name/modified time/size/type cycle, directories first, remembered preference),
and width-adaptive columns (≥380px full / 300–379px no size / <300px bare
@+ short time); 46 offline tests. - v0.0.2 — fixed the clipped modification-time column (the row is
border-boxagain); file/folder icons are the host's own artwork again (FileTypeIcon+classifyFileType,IconFolder*Regular,GuideArtworkFiles), with the inline glyphs kept as a fallback; the README now follows thedsh-pubmedlayout (Chinese primary +README_EN.md+ npm/listing badges). - v0.0.1 — first release: takes over the
filestab as anextensionwith an@file/@folderreference chip and a modification-time column per row; Host half serves/api/dsh-at-sider/list(workspace-confined, 2000 entries per level, 32 concurrent stats); 41 offline tests.
Per-version detail is in git tags; design notes are in docs/DESIGN.md; the roadmap (done/planned/milestones) is docs/ROADMAP.md.
Requirements
- DSH
0.1.7-rc.2(the design baseline) — re-verified on0.2.0-rc.1(CLI,dsh web) and0.2.0-rc.2(desktop app); all three are declared indsh.compatibility. Either thewebor thedesktopprofile works — put the profile actually in use after--profile. - Node.js ≥ 20 (the Host half uses
node:fs/promises).
License
MIT. See LICENSE.
- File-type classification and icons (
FileTypeIcon,classifyFileType,IconFolder*Regular,GuideArtworkFiles) come from the DSH-shipped@deepseek-ai/dsh-client-ui-primitivesand are read at runtime, not copied; the plugin's inline glyphs are only a fallback. - The tab takeover and its runtime fallback, the
@pathreferences, the modification-time and size columns, sorting and width adaptation, the quick filter and its search route, reveal-in-tree and the tab-menu entries, and the keyboard navigation with tree semantics are all original to this plugin.
Links
More in this category
zhu1090093659/dsh-web#packages/dsh-task-board★ 9262
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★ 9262
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★ 5677
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★ 5125
Claude Code-style full-screen terminal UI: pixel-whale header, live status line, and streaming thought expansion.
omdsh-dev/DSH-better-sidebar★ 4413
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★ 1491
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.