Skip to content
dsh.fish
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

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source