Passer au contenu principal
M

dsh-session-tree

minhaoli/dsh-session-tree

Session tree with tag chips, group-by-tag, drag-to-move across workspaces, workspace ordering, favourite sessions, a four-state filter, and a delete action that backs sessions up first.

Installer

dsh plugin --profile web add github:minhaoli/dsh-session-tree

README

dsh-session-tree

An enhanced session tree for the DSH sidebar: the official ui-workspace tree plus tags, tag grouping, drag-to-move across workspaces, a delete action that can back sessions up first, workspace ordering, favourite sessions, and a four-state filter.

Forked from @deepseek-ai/dsh-client-ui-workspace (the 0.2.1 tree) and shipped as a separate bundle so the official package stays untouched.

Requirements

DSH0.2.2-rc.1 (verified against it); 0.2.1-rc.2 also works
@deepseek-ai/cordis~4.0.5
React18.2+
ModeWeb UI (web or desktop profile)
Companiondsh-session-manager — the source of the host half, vendored into this package; no separate install needed

Earlier DSH releases are untested.

Install

dsh plugin --profile <your-profile> add github:minhaoli/dsh-session-tree

Then disable the official ui-workspace row, because this package registers the same uiWorkspace service:

# ~/.dsh/profiles/<profile>/cordis.patch.yml
- id: ui-workspace
  disabled: true

Do not mount dsh-session-manager as well: its host half is vendored into this package, and mounting both registers the same tools and routes twice.

Reload the page after installing — the client half is served as a separate module.

The host half is a vendored session-manager

This plugin's host half is the host half of dsh-session-manager, shipped as a vendored copy in lib/host.js and imported by lib/index.js:

import applySessionManager from "./host.js";

lib/index.js declares the six host services through apply.inject (tools, workspaceRegistry, storageDomain, sessionPersistence, systemPrompt, webServer), exactly as the host half declares them itself.

That makes the package self-contained: no session-manager.js has to be placed in a profile's plugins/ directory, and it does not matter whether dsh-session-manager is installed on the machine.

The head of lib/host.js records the source repository, the upstream commit and the sha256 of the upstream file. To follow upstream, regenerate it with the script instead of editing by hand:

git clone https://github.com/minhaoli/dsh-session-manager ../dsh-session-manager
node scripts/sync-host.mjs ../dsh-session-manager

The cost is that roughly 60 KB of code now exists in two repositories. The sync script refuses a source that does not export apply and writes the digest into the file head, so the drift between the two stays visible.

Because this copy already registers the six agent tools and the /api/dsh-session-manager/ routes, do not mount dsh-session-manager itself at the same time: both would register the same tools and routes.

The session-manager REST dependency

The browser half does not talk to the host over an IPC bridge. It calls a same-origin REST API under /api/dsh-session-manager/:

CallEndpoint
Read all tagsGET /api/dsh-session-manager/tags
Write one session's tagsPUT /api/dsh-session-manager/tags
Delete sessionsPOST /api/dsh-session-manager/sessions/delete
Move sessionsPOST /api/dsh-session-manager/sessions/move

Without the host half, tagRead() catches the failure and returns {} — the tree still renders, but every tag is empty, writes silently do nothing, and drag-to-move / delete fail. If tags look broken, check that the REST routes registered (webServer present).

What it adds to the official tree

FeatureWhere
Tag chips on session rowssidebar.workspaces.session.row.action
Tag / move / delete / favourite menu entriessidebar.workspaces.session.menu.item
Grouping the tree by tagthe tree header
Drag a session onto another workspacethe session row
Archive and unarchivethe row menu (unarchive is the direction the GUI lacks)
Workspace orderingview options
Favourites folderbottom of the tree
Four-state filterview options

Tags and favourites both live in ~/.dsh/session-tags.json, so they survive restarts and ride along when a session moves across workspaces. The client polls for tag refreshes every 20 seconds.

Workspace ordering

View options offer three orders:

OptionBehaviour
Defaultthe order the host reports
Recently editedby the newest session update inside each workspace, descending
Workspace nameby title, Chinese collation

orderWorkspaces() feeds orderedWorkspaces and only reorders workspace entities — session order inside a group is untouched. The choice is stored in the view store as workspaceOrderBy and survives a restart.

Favourite sessions

  • The top of the row menu offers 收藏会话 / 取消收藏.
  • A favourited session appears in a separate 收藏 folder at the bottom of the tree, isomorphic to 已归档: bucketed by the original workspace, each bucket collapsible with an arrow.
  • Unlike the archive folder it does not filter on archive state — archived and unarchived favourites both show.
  • Storage reuses session-tags.json with the reserved key __fav__. That key is filtered out of the tag editor, the inline tag row and the tag picker, so it cannot pollute the user's real tags.
  • The tag editor's commit preserves the favourite marker, so editing tags never drops a favourite.

The four-state filter

筛选会话 is one of four mutually exclusive options:

OptionArchived sessionsFavourites folder
Show everythingshownshown
Hide archivedhiddenshown
Hide favouritesshownhidden
Hide bothhiddenhidden

Internally this is still archivedFilter + favoriteFilter, but the four options cover every meaningful combination, so it behaves as a single choice. The same release fixes the highlight mapping: filter-show-show used to match no menu id, leaving every option unchecked.

Implementation notes

  • lib/index.js is the host-side entry: it mounts the vendored host half (lib/host.js) into this plugin's scope and declares the six host services it needs.
  • lib/client.js (269,814 bytes, 6,225 lines) is the built fork. Its sourceMappingURL still points at the upstream package and the CSS tag keeps dataset.plugin at the upstream name — that is provenance, not a bug.
  • client.js defines class UiWorkspaceService extends Service and calls super(ctx, "uiWorkspace"). That is why the official row must be disabled: two registrations of uiWorkspace cannot coexist.
  • The package mounts into sidebar.workspaces.session.menu.item, sidebar.workspaces.session.row.action and shell.overlay, and injects the official client packages it builds on (dsh-api-remotes, dsh-api-session-controller, dsh-api-workspace-controller, dsh-client-connection, dsh-client-locale, dsh-client-shortcuts, dsh-client-ui-conversation, dsh-client-ui-layout, dsh-client-ui-renderer, dsh-client-ui-session, dsh-client-ui-sidebar).

Changelog

0.3.1

  • the host half changed from "a forward into the profile's session-manager.js" to a vendored copy (lib/host.js): installing no longer depends on a file outside the package
  • added scripts/sync-host.mjs, which regenerates the vendored copy from an upstream checkout and records the source commit and sha256 in the file head
  • docs: install is one command now, and mounting dsh-session-manager alongside is called out as unsupported

0.3.0

Architecture

  • lib/index.js changed from an empty placeholder to a forward into ../../session-manager.js, declaring six host services through apply.inject
  • the profile drops the separate session-manager row and keeps a single plugin entry
  • session-manager.js itself is untouched, so no code is duplicated and the change can be reverted at any time

Added

  • workspace ordering: default / recently edited / workspace name, persisted
  • favourite sessions: row action, bottom folder that does not filter archived state, reserved __fav__ key filtered everywhere tags are read
  • the session filter reworked into a four-state single choice

Fixed while iterating

ProblemFix
favourites had a folder but no filteradded the filter switch
the three-way switch was unclearreplaced by a single "hide favourites" toggle
the toggle sat in its own rowfolded into the filter row
"show everything" did not reset favouritesresets the favourite state too
two dimensions flattened into one group, odd checkbox logicsplit into two sub-groups with their own tri-state
sub-groups still not obviousflattened to four mutually exclusive options
filter-show-show matched no menu id, nothing was checkedfixed the highlight mapping

0.2.0

Tag chips, group-by-tag, drag-to-move, archive/unarchive, delete with backup.

Verification status

The three new features and the host merge in 0.3.0 have passed static checks (node --check) only; they have not been verified at runtime yet. Before relying on them, restart DSH and reload the page, then confirm: the three workspace orders apply and persist, favourites and the four-state filter behave, the tag menu never shows __fav__, and the session-manager agent tools still work.

Companion plugins

License

MIT — see LICENSE. The bundled client code derives from @deepseek-ai/dsh-client-ui-workspace; keep the upstream license terms in mind when redistributing.

Plugins associés