DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

xingzhen199186 /

xingzhen199186/dsh-entry-drawer

Verified

Fold the DSH left sidebar's entry list behind one switch so the session history keeps its room.

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

dsh-entry-drawer(侧栏入口收纳)

简体中文 · English · 安装 · 它是怎么做到的 · 已知限制 · 许可

License: MIT npm release DSH plugin dsh 0.2.x no build step

左侧栏的入口越装越多,会话历史就被一路往下挤。这个插件把入口列表的第一行变成开关:点一下,其余入口整块收起,把空间还给会话历史;再点一下,原样回来。

为什么需要它

DSH 左侧栏上半部分是一列「全局面板入口」:插件、插件市场、自动化任务、远程窗口、任务看板、记忆系统……每装一个插件,这里就多一行。列表一路变长,会话历史一路往下让位——而会话历史才是每天真正在用的那一块。

平台留给第三方插件的位置只有一个:sidebar.panellist(侧栏的面板列表座位)。座位(slot)是界面上一个固定位置,插件只能往里放自己的东西,改不了别人放的东西。这些行的盒子、行高、点击行为都由壳层掌管(壳层是 DSH 自己的界面框架,插件代码跑在它里面),平台也没有「把入口收成一组」的能力。

所以这个插件不新增任何功能,只借一个座位做一件事:在列表最前面放一行,用它把其余行收起来。

你会看到什么

展开时(默认):

  • 位置:左侧栏入口列表的第一行,就在会话历史上方。
  • 内容:一对朝上的双箭头图标,加文字「入口收纳」(英文界面显示 Entry drawer)。
  • 下方是原有的全部入口,一个不少。

点这一行:

  • 其余入口整块隐藏。它们直接不占位置,不是变灰。
  • 会话历史上移,多出几行空间。
  • 箭头翻成朝下,这一行留在原位。

再点一次,全部入口回来,箭头朝上。

收起状态会被记住:重载页面、重启桌面端,都保持上次的选择。

有一条已经实测过的行为,可以提前说清楚:2026-11-05 在桌面端 0.2.1-rc.5 上验证过,先收起再展开都正常;之后新装的第三方插件多出来的入口,也会被一起收进去,不需要为它重新配置。

17938860325941793885963356

安装

前提:DSH 0.2.x,以及可用的 Node.js 与 pnpm(dsh plugin 只是 pnpm 的转发器)。

方式一:从 npm 安装(推荐)

dsh plugin --profile web add dsh-entry-drawer

web 换成你实际在用的 profile 名。包名就是 dsh-entry-drawer,npm 上是 0.1.0。

方式二:从 GitHub 安装

dsh plugin --profile web add github:xingzhen199186/dsh-entry-drawer

适合跟最新代码,或 npm 还没同步到新版时。

方式三:从本地目录安装

dsh plugin --profile web add ./dsh-entry-drawer

在插件管理器里选「从目录安装」、指向这个文件夹,效果一样。这种方式适合一边改代码一边看效果:profile 里记的是这个目录的路径,之后移动或删除它,插件就加载不起来了。

装完要重载。 Web 端刷新页面即可;桌面端建议直接重启应用。跑在浏览器里的那一半(client.js)要等窗口重新加载才会挂上,桌面端默认没有客户端热更新。

桌面端请优先在应用内安装。 桌面端的 profile 由应用自己托管,命令行写入一般会被拒绝(提示该 profile 只由桌面应用管理),这不是权限问题。在那边的「插件」页面填包名、仓库地址或本机目录即可。

卸载

dsh plugin --profile web remove dsh-entry-drawer

然后重载窗口。收起状态不会跟着删掉——它存在浏览器里,想一并清掉见「常见问题」。

兼容性

项目 说明
面向版本 DSH 0.2.x 的 Web 端与桌面端
实测环境 桌面端 0.2.1-rc.5(2026-11-05)
形态 客户端插件(dsh.client 声明 platform: "web"),依赖 @deepseek-ai/dsh-client-ui-sidebar
没有侧栏的形态 不加载,例如 headless 命令行
状态存放 浏览器本地(cookie 与 localStorage),不写 profile,不碰任何配置文件

它是怎么做到的

一共三件事:占一个座位、拦一次点击、写一条样式规则。此外还有一个保险面板,和一处存偏好设置的地方。

1. 占座位

客户端半在侧栏座位上注册一行,order: -1000 让它排在入口列表最前面:

ctx.slots.inject('sidebar.panellist', () =>
  ctx.slots.register(
    { name: 'sidebar.panellist', id: 'entry-drawer', order: -1000, label },
    FoldGlyph,
  ),
)

行里画出来的就是开关本身:一个带 .dsh-entry-drawer-mark 标记的 span,里面是双箭头图标。文字标签「入口收纳」来自插件自带的中英词典。

2. 拦点击

行盒子和行点击都归壳层所有,插件改不了。于是在 document 上挂一个捕获阶段的 click 监听(捕获阶段:事件从最外层往里传的那一段,比壳层自己的处理更早看到这次点击)。

判据很窄:点到的元素在某个 button 里,且那个 button 含本插件的标记。命中就 preventDefault() 加 stopPropagation(),壳层「切换到某个面板」的逻辑不再执行,插件改成翻转收起状态,并把选择存起来。

整个插件只注册一个监听,随插件一起挂上和回收。点侧栏其他位置不受影响。

3. 写样式

收起不改 DOM(DOM:浏览器里那棵界面元素树),由插件自己的一条 <style> 表达。展开时这条样式只含开关图标的基础样式;收起时追加一条:

nav[aria-label="全局面板"] > *:not(:has(.dsh-entry-drawer-mark)) { display: none !important }

读法:在无障碍名为「全局面板」的那个 nav 里(aria-label 是给读屏软件和自动化工具用的名字,DSH 用它标记这块导航),隐藏所有不带本插件标记的直接子项。:has() 是浏览器原生的「包含某元素」判断,DSH 跑在 Chromium 里,能用。

导航名优先取活的语言词典(sidebar 命名空间的 panels.label),取不到时用两个已知名字「全局面板」和「Global panels」,各生成一条规则,所以中英文界面都能命中。名字来自词典,拼进选择器之前做过程序内的转义,不会被拼成奇怪的 CSS。切换界面语言时样式重算。

每个名字都要单独生成一条规则,并把子项那部分写全,不能合成一个逗号列表——否则前半段会把整个导航(连开关一起)隐藏掉。

4. 保险面板

除了列表行,插件还在主内容区(main)注册了一个同 id 的占位面板,渲染结果是空的。正常路径用不到它,因为点击已经被拦下。但如果哪条旁路仍然选中了这个面板,它会在挂载时翻转收起状态,然后跳回你上一个看过的面板(没有可回的就去空面板),而不是因为面板没有内容而报错。

5. 记住选择

每次切换同时写两处:

存哪 键 用途
cookie dsh-entry-drawer 跨端口、跨重启。桌面端每次启动都会在 127.0.0.1 上换一个端口,而浏览器把 localStorage 按「协议 + 主机 + 端口」分区存放;cookie 只认主机、不认端口
localStorage dsh-entry-drawer:collapsed 同一地址内的快速读取

cookie(浏览器按站点存的一小段文本)有效一年。读的时候先看 cookie,读不到才看 localStorage,所以换端口之后旧的 localStorage 值不会盖掉新选择。

宿主半(index.js) 是空实现:apply() 里什么都不做。留着它,是为了让这个包在插件树里有一个可选中、可检查、可卸载的入口。所有逻辑都在客户端。

已知限制

下面写的都是实际情况,不是免责声明。

  • 依赖导航的无障碍名。 样式规则靠 nav[aria-label=...] 定位。上游哪天改掉这个名字,或者不再用 nav 元素,插件会安静地失效:收起不再生效,也不会误藏别的东西。修法是改 client.js 里的 KNOWN_NAV_LABELS。
  • 点击拦截在 React 之外。 它靠「button 里含本插件标记」这个结构判断(React 是 DSH 界面用的界面库,它自己管理界面的更新)。上游若改了行结构,拦截会退化:点击落回壳层,选中这一个面板。保险面板会跳回原面板,但你的体感是「点了没反应」,不是「收起了」。
  • 收起是整组隐藏。 官方的「插件」「自动化任务」「更多」会一起消失,因为它们和第三方入口同在一个列表里。展开即恢复,做不到只藏第三方、留下官方。
  • 窄侧栏下没有可收的东西。 Windows 桌面端把侧栏收成窄条时,整块入口列表本来就是隐藏的,插件不改变任何东西。
  • 本地目录安装有脆弱点。 用 add ./目录 装时,profile 里记的是这个目录的路径。之后移动或删除该文件夹,客户端半会加载失败。长期用请改用 git 安装,或者别动这个目录。
  • 状态按浏览器存。 换浏览器、换主机名(例如从 127.0.0.1 换成 localhost)、清掉站点数据,都会回到展开。多个窗口共用同一份存储,但插件不监听 storage 事件,所以一个窗口切换后,另一个窗口要重载才跟上。

常见问题

点了这一行,入口整块不见了,怎么找回来? 再点同一行。这一行始终留着,它就是开关。

收起之后点不回来了。 通常是上游改了行结构,这一下点击落回了壳层。收起状态记在 cookie 里,刷新不会把它变回来——在控制台执行 document.cookie = 'dsh-entry-drawer=0; path=/' 再刷新,或者清掉这个站点的数据。

为什么桌面端重启后还记得,换了浏览器却忘了? 桌面端重启只换端口,cookie 只认主机、不认端口,所以记得。换浏览器、换主机名或清站点数据,等于换了一份存储,两边都空,于是回到展开。

会影响别的侧栏插件吗? 不会改它们的代码或状态。装在同一列表里的入口,收起时会跟着一起藏起来,展开就回来;侧栏其他区域和主内容区不受影响。

能只收第三方入口、留下官方入口吗? 不能。它们在同一个列表里,从这个座位只能整组隐藏。

开发与测试

没有构建步骤,纯 JS。客户端半是 window.__ModuleLoader__.load({ id, factory }) 形式的模块;宿主半是普通 ESM,导出一个空的 apply()。

测试不需要浏览器,也不需要装依赖:用 Node 内置的 assert、一个极小的假 React 与假 DOM 直接驱动 client.js:

node test/client.test.mjs

通过时输出 client.test.mjs: all checks passed。

覆盖:两个座位的注册契约、展开与收起两种样式表、点击拦截(挡住壳层、翻转状态、写入偏好)、点别处不受影响、活词典优先、标签转义、无词典与无存储时的降级、样式表和监听器的归属与回收。

改完 client.js 要重载窗口才生效;在桌面端请直接重启,它默认不跟随源码热更新。

目录结构

dsh-entry-drawer/
├── package.json         # 包元信息,dsh.bundle / dsh.client 声明
├── cordis.patch.yml     # 把 id 为 entry-drawer 的一行插进插件树
├── index.js             # 宿主半:空实现
├── client.js            # 客户端半:全部逻辑
├── icon.svg             # 插件图标
├── locale/
│   ├── zh.json          # 插件条目的中文标题与描述
│   └── en.json          # 同上,英文
├── test/
│   └── client.test.mjs  # 行为测试
├── LICENSE
├── README.md
└── README.en.md

许可

MIT,见 LICENSE。

—/ 5

No ratings yet

Verified DSH bundle

Commit f7b04ad175e9

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