Bundle
dsh-plugin-pin-session
Pin sessions: adds Pin/Unpin to the sidebar session-row ... menu and a collapsible Pinned Sessions group above the session list.
- Source
- NattoCB
- stars
- 1 stars
- License
- MIT
- Updated
- Updated 13 days ago
Readme
# dsh-plugin-pin-session
<!-- Hero -->
<div align="center">
<b style="font-size: 1.15em;">Pin any DSH session to the top of your sidebar — one click, host-side persistence.</b><br /><br />
<a href="LICENSE"><img alt="License" src="https://img.shields.io/badge/License-MIT-yellow.svg" /></a>
<a href="https://github.com/NattoCB/dsh-plugin-pin-session/releases"><img alt="version" src="https://img.shields.io/badge/version-0.1.10-blue" /></a><br /><br />
<img alt="pin unpin menu" src="https://img.shields.io/badge/-Pin%20%2F%20Unpin%20menu-4d6bfe" />
<img alt="collapsible pinned group" src="https://img.shields.io/badge/-Collapsible%20pinned%20group-4d6bfe" />
<img alt="host side storage" src="https://img.shields.io/badge/-Host%2Dside%20storage-4d6bfe" />
<img alt="zero core changes" src="https://img.shields.io/badge/-Zero%20core%20changes-4d6bfe" /><br /><br />
<b>JSON API</b> — <code>GET /pin-session/pins</code> · <code>POST /pin-session/pin</code> · <code>POST /pin-session/unpin</code>
</div>
> A DeepSeek Harness web plugin that pins sessions: a collapsible **Pinned Sessions**
> group above the sidebar session list, plus **Pin / Unpin** entries in every
> session row's `...` menu. Pure client+host bundle — zero changes to DSH core,
> pin order stored host-side so it follows you across browsers and origins.
## ✨ Features
- 📌 **Pin / Unpin in the row `...` menu** — one entry per row, label reflects current state.
- 🗂️ **Collapsible Pinned Sessions group** directly above the session list:
- click a pinned row to open that session (`sessions.open`);
- hover reveals an `×` quick-unpin;
- header toggles collapse (persisted per browser via `localStorage`);
- sessions no longer present in the list render dimmed and are not clickable.
- 💾 **Host-side storage** at `$DSH_HOME/storages/pin-session.json` — pin order
is oldest-first and consistent across browsers and origins.
- 🧩 **Zero core surgery** — pinned sessions stay in the chronological list;
the shipped WorkspaceBrowser is left untouched.
## 🚀 Install
```bash
dsh plugin --profile web add github:NattoCB/dsh-plugin-pin-session
```
Then add `"dsh-plugin-pin-session"` to `dsh.profile.bundles` in the profile's
`package.json`. **A DSH restart is required** — new client bundles join the boot
graph only at process start.
> Local checkout instead? `dsh plugin --profile web add file:/absolute/path/to/dsh-plugin-pin-session`
## ⚙️ How it works
| Half | Mechanism |
|---|---|
| Host | `webServer.register({ kind: 'prefix', path: '/pin-session' })` JSON API over an atomic JSON file store |
| Row menu | The shipped session-row menu portals a `role="menu"` element to `document.body`; a MutationObserver matches each menu to its row via React fibers (`props.node.id`) and appends one Pin/Unpin item styled with the menu's own classes, closed by an outside `pointerdown` |
| Section | A React root planted before the `[role="tree"]` session list renders each pinned row's own real `primitives.Menu`, anchored at that row's `...` button and wired to the same live callbacks (`onRename(id, title)` / `onFork(id)` / `onArchive(id)`) resolved from the row's fiber |
The shell freezes every vendor module it serves (`Object.isFrozen(primitives) === true`),
so shared components cannot be monkey-patched — but they can be called. The
section menu therefore opens at the pinned row (not wherever the original row
is scrolled) while rename/fork/archive stay single-sourced in the shipped
browser; the `...` affordance hides while the real row is not rendered
(collapsed group / filtered list).
No pin icon ships in the primitives set, so the menu item embeds its own
pushpin SVG.
## ✅ Verify after refresh
1. Sidebar shows **Pinned Sessions** above the list once at least one session is pinned.
2. Hover a session row → `...` → Rename / Fork / Archive + a pushpin-iconed **Pin**.
3. Click **Pin** → row appears under Pinned Sessions; menu flips to **Unpin**.
4. The pinned row's own `...` opens right at that row with the same actions.
5. Click a pinned row → that session opens; **Unpin** removes it.
6. Collapse the group → reload page → collapsed state survives (`localStorage`).
7. `cat ~/.dsh/storages/pin-session.json` shows the id list.
## 🔌 API
```
GET /pin-session/pins -> { pins: [{ id, title?, pinnedAt }] }
POST /pin-session/pin {id, title?} -> { pins }
POST /pin-session/unpin {id} -> { pins }
```
## 🧪 Test
```bash
npm test # node --test — 11 cases across store + HTTP router
```
---
## License
MIT — see [LICENSE](LICENSE).
Install
dsh plugin --profile web add github:NattoCB/dsh-plugin-pin-session
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-plugin-pin-session from the hub
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.