DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

yixin2021 /

yixin2021/dsh-interaction-kit

Verified

Interaction and convenience upgrades for the DSH Web UI: session right-click menu with permanent delete, a Git branch chip under the composer, and opt-in worktrees.

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@53232709

dsh-interaction-kit

English | 中文

Interaction and convenience upgrades for the DSH Web UI. One plugin, three surfaces, all built from the shipped UI primitives so nothing looks bolted on.

  • Right-click a Session — the same menu the ⋯ button opens (fork, rename, archive, pin, permanent delete, …), instead of having to hunt for the three dots.
  • Branch chip under the composer — the workspace's current Git branch, click to switch. The menu is the native searchable menu: pinned search field, sticky section heading, branch list.
  • Worktree checkbox — opt in per Session. On the first file write, a worktree is created and the Session moves into it, so parallel work stops colliding in one checkout.

Features

Session right-click menu

Right-clicking anywhere on a Session row opens exactly the menu the ⋯ trigger opens — it drives the real trigger, not a copy of it, so every item (including ones other plugins add) is present.

The trigger is identified by its accessible name (会话"{title}"的操作 / Session "{title}" actions), never by position. That matters: the hover strip next to it also holds archive and pin, so a positional lookup would archive the Session instead of opening the menu. If the trigger cannot be identified, the plugin installs no listener and leaves the native behaviour alone.

Permanent delete

A row appended to that menu, styled as a danger action. Where the built-in menu only archives, this removes the Session for good:

  1. archive with stopActivity (so a running turn is stopped first),
  2. unarchive, then unpin (clears the flags that pin the record),
  3. detach the Session from every workspace that lists it,
  4. delete the Session log directory under $DSH_HOME/sessions/,
  5. delete the single projection-cache file for that Session,
  6. clear the plugin's own per-Session state,
  7. emit api-session/removed so open views drop it.

Confirmation is a destructive-action dialog, not a plain OK box.

Known residue: storages/session_projcache.json keeps one stale derived entry for the deleted Session. It is a cache index, it is never shown, and it is deliberately left alone rather than hand-editing a shared app file.

Branch chip and switching

The chip sits in conversation.input.left — the composer's tool row, immediately beside the mode selector — and shows the branch of the Session's workspace, with a warning tone when the checkout is detached, plus an uncommitted-file count.

That seat is deliberate. conversation.composer.dock is below the card, but the shipped InputBar renders it only for variant: "composer", and a Session with no messages yet is rendered as the hero variant — so that seat is empty for exactly as long as the repository is still being chosen. conversation.input.left is mounted in both variants, which is why the branch and the worktree option are available before the first message rather than after it.

Clicking it opens a list of local branches. A repository with a thousand branches is normal, so the list renders at most 60 rows and the pinned search field narrows the rest; the heading states the matched/total count while a filter is active. Creating a new branch (and checking it out) is the last row.

Switching never discards work: a checkout that Git refuses is reported in place, and the menu stays open with the error rather than closing on a half-applied change.

Worktree mode

A checkbox on the same row as the branch chip, deliberately not inside the branch menu. When it is on and the Session has no worktree yet, the first write tool call creates one:

<repository>/.worktrees/<branch>      branch: <session>-<slug>

and relocates the Session's working directory into it. Subsequent writes stay there. Turning the checkbox off returns the Session to the repository root.

Creation prefers the host's own worktrees service, so it honours whatever sandbox and policy the profile applies; it falls back to git worktree add only when no live Agent exists to create one.

The label names the checkout this Session is in — worktree(feature/topic-b) — rather than counting worktrees. A count says nothing about where the next write lands; the branch is the name an operator recognises, and a checkout created without one falls back to its directory name.

Once the conversation has started, the option is locked on. Turning it off then would not undo anything: the writes that already ran in the worktree would stay there while later ones reopened the main checkout, splitting one Session across two directories. The Host refuses that transition with worktree-locked and reports worktreeLocked in status; the checkbox disables itself and its tooltip says why. A Session that has not exchanged a message is still free to change its mind.

Install

dsh plugin --profile web add github:yixin2021/dsh-interaction-kit

Reload the page. Removing it:

dsh plugin --profile web rm dsh-interaction-kit

From a local checkout, install the directory instead:

dsh plugin --profile web add /absolute/path/to/dsh-interaction-kit

Updating

package.json in the profile records the spec, but the lockfile pins the exact commit the spec resolved to:

specifier: github:yixin2021/dsh-interaction-kit#main
version:   https://codeload.github.com/yixin2021/dsh-interaction-kit/tar.gz/3eeee4519883...

Which means the spec you typed is not what you are running — the recorded commit is. Two consequences, and they differ:

Following a branch (#main) needs a remove. Re-adding an identical spec is refused as already-installed, because from the manager's view nothing changed:

dsh plugin --profile web rm  dsh-interaction-kit
dsh plugin --profile web add github:yixin2021/dsh-interaction-kit#main

In the desktop app that is the plugin's Remove followed by installing from the source again.

Switching to a tag or commit needs no remove — the spec differs, so it installs over the top and resolves fresh:

dsh plugin --profile web add github:yixin2021/dsh-interaction-kit#v1.0.0
dsh plugin --profile web add github:yixin2021/dsh-interaction-kit#e4aa663

After either one, restart DSH: the Host half is imported once at profile start, so reloading the page alone leaves the old Host code running.

Two install modes, one tradeoff

Spec Editing the source… Use it when
/local/path or link:/local/path takes effect immediately (plus the reload rules below) you are developing the plugin
github:owner/repo has no effect until you push and reinstall you want the installed copy to be exactly what is published

A link: install is a symlink into your checkout, so the running plugin sees your working tree. A github: install is a real directory unpacked from the tarball — read-only in practice, and identical to what everyone else gets. Switching between them is just rm + add.

What needs a reload and what needs a restart

Change Takes effect
client.js Client bytes are snapshotted when the plugin activates — disable/enable the plugin (or restart), then reload the page
index.js Imported once at profile start — restart DSH

How it works

Half File Responsibility
Host index.js One authenticated route, POST /api/interaction-kit, plus a tools/pre-execute hook for auto-worktree. All git and filesystem work happens here.
Client client.js Five slot registrations: composer tool row, branch menu overlay, Session menu row, Session row right-click, delete dialog.

The page never runs git itself. The Client half only sends {action, sessionId, …} and renders the answer, so a repository path or a branch name from the page is validated on the Host (NAME_PATTERN) before it reaches a command line.

State lives in $DSH_HOME/storages/interaction-kit-toolkit.json:

{ "version": 1, "worktreeMode": {}, "worktrees": {} }

Writes are queued and atomic, so two Sessions toggling at once cannot corrupt it.

Slots

Slot Purpose
conversation.input.left Branch chip + worktree checkbox, beside the mode selector
shell.overlay Branch menu, create-branch dialog, delete confirmation
sidebar.workspaces.session.menu.item Permanent delete row
sidebar.workspaces.session.row.action Right-click → menu

Development

npm run check   # node --check on both halves
npm test        # client + layout harnesses
node verify-host.mjs <sessionId>   # drives the Host route against a sandboxed $DSH_HOME

verify-layout.mjs renders the slot components against a stub React and asserts the resulting tree — which primitive was used, which classes, which locale strings, and the safety properties (no archive click, no viewport-wide listener). The harnesses are development-only: files in package.json keeps them out of the published tarball, so a github: install never receives them.

Releasing

# 1. bump "version" in package.json, then
git commit -am "chore: release v1.0.1"
git tag -a v1.0.1 -m "v1.0.1"
git push origin main --follow-tags

Consumers pin #v1.0.1; anyone on #main re-resolves on their next remove + add.

License

MIT

—/ 5

No ratings yet

Verified DSH bundle

Commit 532327098c14

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout