DeepSeek Harness Plugin Hub

发布与管理完整 Harness Profiles,发现适合你的插件。

探索

插件目录环境预设文档中心动态

社区

发布插件联系我们报告问题

相关链接

Plugin Hub GitHubDeepSeek Harness 官方项目系统状态隐私说明
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

独立、非官方社区项目,与 DeepSeek 官方无隶属、授权或背书关系。

DeepSeek Harness Plugin Hub
ProfilesPlugins分类动态文档登录管理 Profiles
ProfilesPlugins分类动态文档登录
← Plugins

dsh-sidebar-entries

Sidebar Entries

DeepSeek Harness 的左侧边栏面板列表:长按拖动以重新排列面板图标,点击一个箭头即可将它们全部折叠,限制整行高度,包含一个保留实时运行中/等待状态的置顶会话区域,可拖动的对话视图标签,已归档的 Sess

插件会安装到这里;不确定时保持 web。

npx -y @deepseek-ai/dsh plugin --profile web add dsh-sidebar-entries@0.7.25
README兼容性版本

兼容性与来源证明

Sidebar Entries 以 dsh-sidebar-entries 发布,当前版本为 0.7.25。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

DSH 兼容范围
*
运行环境
web
发布来源
npm
Registry 更新时间
2026/11/6

版本

0.7.25stable
2026/11/5
0.7.24stable
2026/11/4
0.7.23stable
2026/11/4
查看其余 3 个版本收起版本
0.7.16stable
2026/11/1
0.7.15stable
2026/10/28
0.7.14stable
2026/10/28

相关插件

正在加载相关插件…

最新版
0.7.25
DSH
*
HMR
重启进程
Tree shaking
未声明可安全裁剪
解包体积
606.6 kB
文件数
10
Surface
web
许可证
MIT
发布源
npm
GitHub
★ 0
周下载
947
安全扫描
✓ v0.7.25 扫描通过
最近提交
2026/11/5
查看源码 ↗项目主页 ↗
README Badge

点击下方 Badge 复制 Markdown,粘贴到 README 即可。

这是你的 Plugin?认领权益 · 优先安全扫描

验证 package.json 声明的 GitHub 仓库,即可管理这个公开页面。认领后,Hub 会优先安排当前版本的安全扫描,并在通过后公开展示结果。

认领这个 Plugin →
报告问题

相关插件

继续浏览 ui-customization 分类下经过校验的插件。

Client Ui Skill Explorer@linxin666/dsh-client-ui-skill-explorerDSH 技能中心:按来源(bundled / project / user / custom / runtime)浏览已加载的技能,在 Web GUI 面板中启用或禁用、创建和删除技能。Better Sidebardsh-better-sidebarBetter Sidebar for DSH — 在一列中提供文件树、编辑器、变更、任务和侧边聊天,并为每个对话单独显示。同时提供服务,供其他插件注册侧边栏标签页和文件查看器。Client Ui Task Board@linxin666/dsh-client-ui-task-board面向 DSH Web GUI 的主机权威任务面板,支持实际会话执行、主机 cron 调度以及可选的跨平台空闲睡眠保护;以挂载方式提供,无需修改 DSH 源代码。Client Ui Git Graph@linxin666/dsh-client-ui-git-graph外部 dsh Web GUI 插件:空会话 Git 分支选择器和 Git 图,包含实际的主机端 Git 操作与防护,作为 dsh 配置文件包

README

dsh-sidebar-entries

The sidebar with each enhanced region outlined

A DeepSeek Harness plugin that makes the left sidebar behave the way a long list should: drag to reorder the panel icons, one arrow to fold them all away so the workspace / session list gets its height back, a pinned-session section at the top of the workspace list that keeps its live status, whole-row height caps on both lists so a crowded rail can never eat the session tree, archived Sessions sunk to the bottom instead of interleaving with live work, draggable conversation view tabs, and a generic lift that pulls every plugin's sidebar-footer entry up into the same rail so nothing is split between the top and the bottom of the column.

The sidebar panel list is a stack of icon rows (插件, 插件市场, 自动化任务, …). Once a handful of plugins are installed the stack eats the column and pushes the session list into a narrow strip. This plugin adds three things to that stack:

  • Long-press drag to reorder. Press an icon for ~0.3 s, then drag. The row lifts and follows the cursor; the neighbours slide out of the way with a short FLIP animation; the order is written to localStorage on release. A short click still selects the panel — only a deliberate long press starts a drag, so nothing navigates by accident.
  • A fold arrow at the top of the list. A plain chevron (v when everything is shown, ^ when folded) that hides every panel icon in one click. The state survives a reload. The arrow itself never moves and can never be dragged, so there is always a way back.
  • Footer entries lifted into the rail. Any plugin that registers an action in the sidebar footer gets a row up here automatically — see below.

And one thing to the list below it:

  • A 置顶 section. Pinned Sessions are gathered into one group at the very top of the workspace list and their originals are hidden, so a pinned Session appears exactly once no matter which workspace it belongs to. Unpinning from the copy works; the copy is rebuilt whenever the store changes.

Three things to the tab bar above the conversation:

  • Drag the conversation view tabs to reorder them. 对话 / 轨迹 / 上下文 / … reorder by dragging, with the same lift-and-FLIP feel as the rail. The order is kept in localStorage and reapplied on every render. The 对话 tab cannot be moved — the shipped header pins it first — and how many tabs fit is decided by the bar's own width, not by this plugin.

And two that are about not letting the list get long:

  • Archived Sessions sink to the bottom of their group. The shipped row for an archived Session never even attaches draggable, so those rows used to sit wherever they landed and interleave with live work. This plugin reorders them by updatedAt (newest first) to the end of each group and draws a hairline above them, leaving live rows in exactly the order the Host chose.
  • A whole-row height cap. Both the panel-icon stack and the 置顶 section are capped at 30% of the rail's height, rounded down to whole rows — six 36px rows, not "six and a sliced seventh". The cap is recomputed on resize, so folding the window never leaves a half row hanging.

Layout

lib/index.js     host half  — diagnostics routes (see below), no runtime dependencies
lib/client.js    browser half — wraps in window.__ModuleLoader__.load; requires('react') from the host
cordis.patch.yml bundle patch — inserts the host row under the id `sidebar-entries`

The two halves are independent: the browser half is the actual feature, the host half only provides the /sidebar-entries/* routes used for self-diagnostics and for hot-reloading the client bundle while developing.

How the reordering works (and why it is done in the DOM)

DeepSeek Harness ships a pin/unpin model for sidebar panels (createPanelPins), but it is memory-only — it resets on every reload — and it permanently pins the plugins row. This plugin therefore keeps its own order in localStorage (dse.sidebar.order.v2) and replays it onto the list on every render:

  • rows are identified by aria-label, grouped so that two rows sharing a label move together and keep their relative order;
  • the "More" row is never draggable and never moves — it is the insertion anchor;
  • a MutationObserver on the list re-applies the saved order after React re-renders, with an "already in order → do nothing" short-circuit so the plugin cannot ping-pong with itself.

Nothing in the shipped app is patched; the plugin only manipulates the rendered list.

How the pinned section works

The shipped list already sorts pinned Sessions to the front of their own group, but they stay scattered across workspaces. This plugin lifts them into one group:

  • a MutationObserver on [class*=listArea] re-derives everything after each render;
  • whether a row is pinned is read from the pin indicator the row component itself renders, so no cached list can drift out of date;
  • the original rows are display:none (not removed — React owns them), and a copy of each is appended to a 置顶 section inserted as the first child of the list area;
  • a copy carries the markup but none of React's handlers, so a click on one is replayed on the same element of the original by walking the child index path;
  • the exception is the pin toggle: clicking it in a copy calls uiWorkspace.unpinSession(id) directly, the same service the shipped button calls. Replaying a click into a row that is currently display:none is exactly what made "取消置顶" dead from inside the section;
  • the copy's ⋯ menu button is hidden rather than removed: that menu anchors on its own button, which now sits inside a hidden row, and hiding keeps the child indexes aligned for the replay.

The copy carries the live state, not just the title

A Session row is five parts: a leading slot (the status dot), the title, a trailing time (which reads “待回复” / “待批准” while an interaction is pending, and the relative time otherwise), a pin indicator, and the hover actions. The pinned section builds its own rows, so for a long time it built only the title and the actions — which meant a pinned Session showed no spinner while it was running and no “待回复” when it was waiting for you, exactly when that information matters most.

The status is React state this plugin cannot read, so the two live parts are mirrored from the shipped row instead of recomputed: the shipped row is still in the DOM (only display:none), and its status span is already current. The mirror runs on every sync tick rather than only on rebuild, because running and pending are transient — a row built once would freeze on whatever state it happened to have at that instant. Text and markup are compared before writing, so an idle row is not rewritten 60 times a second.

Child Sessions follow their parent into the section

Forking a Session from the pinned section creates a child Session. That child used to land in whatever workspace the fork chose and drop straight to the bottom of a group — visually disconnected from the parent it was forked from. Now, when a newly created Session's parentId points at a pinned Session, it is pinned too and inserted directly after its parent in the local order.

Two guards keep this from sweeping in history. The first pass only seeds the set of Sessions already known and pins nothing, so an upgrade cannot pin every Session that ever had a parent. And a candidate must have been updated within the last ten minutes: a Session without a usable updatedAt is treated as old, because pinning it on the strength of a guess is exactly the failure this guard exists to prevent. Unpinning the parent does not unpin the child.

Storage keys

keymeaning
dse.sidebar.order.v2JSON array of panel ids, in the user's order
dse.sidebar.collapsed.v1'1' when the panel icons are folded away
dse.pin.order.v1JSON array of Session ids, in the order shown in the 置顶 section
dse.pin.closed.v1'1' when the 置顶 section is folded
dsh.sidebar-entries.viewtab.v1JSON array of conversation-view keys, in the user's tab order

All live in the web app's localStorage. Clearing site data resets the plugin to defaults.

Footer entries are lifted into the rail

Any button rendered in the sidebar footer (sidebar.footer.action, or a plugin that injects its own button into the footer container) is pulled up into the rail automatically. The plugin builds a stand-in row from the footer button itself — same label, a copy of its own icon — and only then hides the original with [data-dse-lifted]{display:none}. If the lift ever fails, the button stays where it was, so a plugin installed tomorrow needs no change in this one.

  • The row forwards its click to the original control, so whatever panel the plugin opens still opens; the plugin does not need to be known by name.
  • The pass is driven by a MutationObserver on document.body and re-checks which footer controls exist, so a plugin that mounts its footer entry late is still picked up.
  • An uninstall or a client rebuild removes the stand-in rows and clears data-dse-lifted, so a footer control can never be left permanently hidden by a stale attribute.

The one built-in row

文献 — opens the literature plugin's panel. The literature plugin moves its own entry into the right sidebar when it detects a betterSidebar service, and does not move it back when the service later disappears, so its footer button is sometimes gone entirely; this row tries the footer button, then the dock tab it migrated into, then the service itself. It degrades quietly when the literature plugin is not installed.

Failure containment

A client module whose apply() throws cannot be mounted, and the only way out of that state is to disable the plugin — which is a miserable thing to discover after a restart. Every subsystem here is therefore started behind its own guard: the stylesheet injection, the slot read, all eight ctx.effect registrations, each subsystem's construction, the slot injection, and the host half's four HTTP routes. A subsystem that fails to start returns null and its teardown becomes a no-op; the rest of the plugin still mounts.

This is verified rather than asserted: the module was mounted against hostile stubs (a ctx that throws on every property read, an effect() that rethrows, an inject() that throws) and against cold-start shapes (readyState: 'loading', documentElement: null, body: null, empty localStorage). It mounts in all of them. The diagnostic snapshot also reports each subsystem, so a silently degraded feature is visible in GET /sidebar-entries/log rather than only on screen.

Install

From npm (recommended — prebuilt, no build-approval prompt):

dsh plugin --profile web add dsh-sidebar-entries

From source, straight off GitHub:

dsh plugin --profile web add https://github.com/FragrantLotus/dsh-sidebar-entries

Either way, restart the web app once so the bundle patch layer is picked up, then reload the page.

The package has no dependencies — it is two plain ES modules (lib/index.js on the Host, lib/client.js in the browser) plus a cordis.patch.yml. Nothing is fetched at install time and nothing is built.

Diagnostics

While developing, the host half exposes four routes. They are intentionally unauthenticated — they only read in-memory state and only mutate the client bundle revision.

routepurpose
GET /sidebar-entries/pingliveness + version
GET /sidebar-entries/graphclient modules visible to the host, and this package's resolved client path
GET/POST /sidebar-entries/loglast 20 client reports (drag start/end, panel state)
POST /sidebar-entries/reloadrebuild this package's client bundle (hot reload, no restart)

Client reports are only sent when window.__dseDebug is set, except for the handful of events that are always sent (drag start/end) — those are what make the drag verifiable without a browser automation harness.

License

MIT