给 dsh web 客户端加四个右侧栏面板:产物浏览、长期记忆、终端、记事本。两两配成一个标签、中间可拖拽分栏,因此两个窗格就能同时看到四个。终端每行执行一条 bash 命令,默认 120 秒期限、可随时终止。宿主侧一并提供 9 个 memory_* 工具、artifacts_list、/memory 命令,以及 /api/artifacts/* 与 /api/terminal-notes/* 路由。需要 dsh 0.1.5 及以上,以及 0.2 系列。
安装
# GitHub 源码(首次需按提示配置 allowBuilds 构建授权后重试)
dsh plugin --profile web add github:windrover/dsh-minimal-UI-panels
装任何插件都等于在你的机器上跑第三方代码,权限和你本人一样大——能读你的文件、用你的凭据、访问网络,工具审批管不到它。GitHub 来源的插件还会在安装时执行构建脚本——pnpm 默认拦截,所以安装可能停在 ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED 或 ERR_PNPM_IGNORED_BUILDS;dsh 会打印出需要添加的确切键名,把它加进该 profile 的 pnpm-workspace.yaml 的 allowBuilds 下,重跑一次即可装上。放行构建本身就是一次信任判断:请只安装可信来源,并尽量锁定 commit(github:owner/repo#sha)。
README
All-in-one DeepSeek Harness UI panels — 一个 bundle、一个 loader 行,把产物 / 记忆 / 终端 / 记事本两两配对成 2 个右侧栏标签页类型,每个标签内部上下可拖分栏。
dsh-minimal-UI-panels 把三个原本独立的 DSH 插件合并为一个包,并以单 loader 行挂载。面板两两配对注册为 @deepseek-ai/dsh-client-ui-sidebar-right 的标签页类型(ctx.sidebarRightTabs):产物 + 终端、记忆 + 记事本,每个标签内部上下叠放、中间可拖动分栏——这样把两个窗格并排就能同时看到 4 个面板。同时提供宿主侧工具/路由,即插即用。
DSH 0.1.5 起架构已变:旧版那个可被第三方占用的
details列已被rightbar取代,由官方右侧栏(docking kit + 标签类型注册表)接管。本包 0.2.0 完成该迁移——不再有自绘的多面板容器。
✨ 功能一览
| 面板 / 能力 | 说明 |
|---|---|
| 成对的右侧栏标签页类型 | 每个类型 = 一对面板,上下叠放、中间是可拖动的分隔条(比例按类型记住,双击回到 50/50)。停靠 / 浮动 / 分屏由官方 docking kit 提供;把两个窗格并排,就能同时看到 4 个面板。入口是标签条的「+」→ guide 页 |
| 产物面板 | 扫描工作区产物文件,按类型/日期/体积/行数分组排序;代码/配置/数据预览语法高亮;图片 base64 预览(4 MiB 上限);mp4/m4v/webm/ogv 视频流式播放(支持 Range 请求) |
| 记忆面板 | 三作用域(user/global/workspace)记忆的查看、新增、搜索、编辑;标签分组;内容高亮 |
| 终端面板 | 深色终端外观的 bash 命令执行器(bash -lc),输出收集后返回,下方常驻常用命令提示 |
| 记事本面板 | Apple 便签风格多条目记事:侧边列表 + 正文编辑、新建/删除、自动保存(600ms 防抖)、按首行自动命名,存储于 ~/.dsh/notes.json |
📸 截图
右侧栏里开着**「产物 + 终端」**这一对:上面是产物面板,下面是终端,中间是可拖动的分隔条。标签条上的胶囊名就是这一对的类型名。
把另一个标签拖到窗格的左/右边缘就分出第二个窗格——两个窗格并排,四个面板同时可见:
📦 安装
本插件以本地 link 依赖挂载(与 DSH 本地插件一致)。
需要 dsh 0.1.5 及以上(含 0.2 系列)。 面板注册为
@deepseek-ai/dsh-client-ui-sidebar-right的标签类型,该包自 0.1.5-alpha.1 才发布;更早的 0.1.x 根本没有rightbar,客户端解析不到这个依赖。peerDependencies里的^0.1.0-rc.8只覆盖宿主服务(dsh-home-paths、dsh-tools),不是真实下限。
1. 克隆仓库
选一个目录并克隆:
git clone https://github.com/windrover/dsh-minimal-UI-panels.git
# → <你的路径>/dsh-minimal-UI-panels
2. 在 DSH profile 中登记
编辑 ~/.dsh/profiles/web/package.json,把 link: 改成你自己的克隆路径(<你的路径> 是你克隆到的目录):
{
"dependencies": {
"dsh-minimal-ui-panels": "link:<你的路径>/dsh-minimal-UI-panels"
},
"dsh": {
"profile": {
"bundles": [
"@deepseek-ai/dsh-base",
"@deepseek-ai/dsh-web-app",
"dsh-minimal-ui-panels"
]
}
}
}
把
<你的路径>换成克隆目录的绝对路径,例如link:/Users/me/dev/dsh-minimal-UI-panels。 目录名可以带大写(如dsh-minimal-UI-panels)——目录名不影响加载,真正起作用的 identity 是package.json的name。
3. 建立 link
cd ~/.dsh/profiles/web && pnpm install
4. 重启 dsh web
Ctrl+C 退出 → 重新 dsh web → 浏览器刷新。loader 启动时扫描 profile bundles,新代码即生效。
若你之前挂载过那几个旧插件,请从
dsh.profile.bundles列表移除它们(本包已取代);同时清理~/.dsh/cordis.patch.yml中残留的旧行(如- id: artifacts-panel),避免patch: entry ... not found告警。
🖥 使用
- 打开任意会话 → 右侧栏(对话区右上角的展开按钮)→ 点标签条的「+」打开 guide 页 → 选择 产物 + 终端 或 长期记忆 + 记事本。
- 同时看 4 个面板:把标签拖到窗格的左/右边缘分出第二个窗格,两格各开一对即可;每对内部的上下比例用中间的分隔条调,双击回到 50/50。空间不够时把右侧栏宽度拖大。
- 终端:输入命令回车执行(
bash -lc),输出展示在下半区,常用命令提示常驻。 - 记事本:点「新建」创建条目,左侧列表可拖动分隔条调宽,点击标题自动隐藏列表聚焦正文,工具栏可手动显示/隐藏列表。
/memory命令(宿主侧):/memory list|search|get|forget|export管理长期记忆。- 宿主侧还提供模型工具:
memory_*(write/recall/list/forget/export/import/correct/batch/diagnose)与artifacts_list。
产物落在哪里
一次对话只占一个目录,凭路径就能定位:
<workspace>/sessions/<YYYY-MM-DD>-<标题slug>-<会话id前8位>/
README.md 索引:这次对话在做什么 + 每个产物是什么
docs/ md、txt、pdf、docx、pptx
data/ csv、tsv、xlsx、json、yaml、sqlite
images/ png、jpg、webp、svg、gif
scripts/ py、sh、js、mjs、ts、ipynb
media/ mp4、mov、mp3、wav
misc/ 其他,以及临时文件
- 日期是这次对话开始当天的本地日期(不是每次写入的日期,也不是 UTC),slug 是会话标题——目录名既可读,又在整个会话期内稳定。
- 会话 id 前 8 位是稳定键:本会话已存在的目录一律复用,包括旧版本写下的
<日期>-<完整会话id>,所以升级后不会分叉、不会散落。 - 子会话嵌套在父会话目录下:
<父目录>/subagents/<slug>-<id8>/。 - 目录不预创建:第一次真正写入才出现,所以只问不做的会话不会留下空目录。
- 面板默认打开本会话的产物目录(由宿主路由
/api/artifacts/session-dir给出);目录尚未出现时留在工作区根,而你自己选过的目录永远优先。 artifacts_list不带dir时扫描本会话的产物目录(尚不存在则退回工作区根)。面板只扫你指定的目录、从不解析目录名,因此任何既有结构都照常列出与预览。
宿主要注入的精确路径由 tool:artifacts-session-dir 提示段给出;规则本体在 lib/host/artifact-paths.js,由 test/artifacts-naming.test.mjs 钉住。
🏗 架构
dsh-minimal-UI-panels (一个 loader 行 / 一个包)
│
├── lib/index.js ── 宿主半身【薄组合入口】
│ └── import + 按序调用:
│ ├── lib/host/ltm.js ← 原 dsh-long-term-memory(一字不改)
│ │ └── store.js / threats.js / llm.js / automation.js
│ ├── lib/host/artifacts.js ← 原 dsh-artifacts-panel(一字不改)
│ │ └── artifact-paths.js ← 每会话产物目录布局(命名、复用、子会话嵌套)
│ └── lib/host/terminal-notes.js ← 原 dsh-terminal-notes(一字不改)
│ inject = tools, systemPrompt, commands, settings, agents,
│ webServer, workspaceRegistry, subprocess
│
├── src/ ── 浏览器半身源码【改面板 UI 就改这里,受版本控制】
│ ├── composite/client.js ← 组合容器:拥有全部右侧栏注册(2 个 tab 类型 + 可拖分隔条)
│ ├── artifacts/client.js ← 产物面板(只提供组件,不注册 tab 类型)
│ ├── long-term-memory/client.js ← 长期记忆面板(同上,另提供设置卡片)
│ └── terminal-notes/client.js ← 终端 + 记事本两个面板(同上)
│
└── lib/client.js ── 浏览器半身【合并单 bundle = scripts/merge-client.mjs 生成,勿手改】
├── function artifacts(react, react_jsx_runtime, panels) ← 提取自 src/artifacts/client.js
├── function ltm(react, react_jsx_runtime, panels) ← 提取自 src/long-term-memory/client.js
├── function terminalNotes(react, react_jsx_runtime, panels) ← 提取自 src/terminal-notes/client.js
├── function composite(react, react_jsx_runtime, panels) ← 提取自 src/composite/client.js
│ 三个面板 fn 只往 panels 注册表里写 { Component, ns, titleKey, t };composite 读它渲染
└── function apply(ctx) ← 主入口:const panels = {} → 依次 apply(composite 必须最后)
exports.inject = ["slots", "locale", "sidebarRightTabs"]
右侧栏标签页类型(**每个类型 = 一对面板**,每个类型再在 sidebar.right.pane.tab 槽下按自己的 id 注册 body):
dsh-minimal-ui-panels/artifacts-terminal kind "artifacts-terminal" 上:产物 下:终端
dsh-minimal-ui-panels/memory-notes kind "memory-notes" 上:长期记忆 下:记事本
宿主工具/路由: memory_*(9) + artifacts_list, /api/artifacts/*, /api/terminal-notes/*
合并流程(详见 scripts/merge-client.mjs):
src/artifacts/client.js ─┐
src/long-term-memory/client.js ├─ 提取 factory 体 → 改写 react/react_jsx_runtime
src/terminal-notes/client.js │ 绑定、删除子模块内 exports. 语句
src/composite/client.js ─┘ (必须最后合并:它读前面填好的 panels 注册表)
│
▼
lib/client.js (单 __ModuleLoader__.load bundle)
📌 源码就在本仓库(
src/<panel>/client.js)。早期版本从三个兄弟目录读取,而那三个仓库后来被 GitHub 归档(只读,push 返回 403),源码没有任何版本控制兜底;当时直接改生成产物lib/client.js的改动被下一次合并覆盖后无法从 git 恢复。现已内联——克隆本仓库即可完整重建浏览器半身。
⚠️ 关键约定:三处名字必须逐字节一致(本包为小写
dsh-minimal-ui-panels)
cordis.patch.yml里insert.namelib/client.js里__ModuleLoader__.load({ id })package.json的name机制:
dsh-client-modules的nearestPackage()从 Loader 行解析出的模块路径逐级向上找package.json,只接受name === 该 specifier的那一个;随后arrive()再断言factories.has(row.id)。任一环大小写不一致,整个浏览器半身会静默地不进window.__DSH_BOOT__——而宿主半(工具/路由)照常工作,极易误判成「插件在跑」。这正是 0.1.x 遗留的隐藏 bug:
package.json早已改成小写,但cordis.patch.yml与 client bundle 仍写着大写UI。目录名带不带大写无关紧要,一切以package.json的name为准。
为什么不再需要「多面板容器」,以及为什么面板要两两配对
旧版四个插件必须合并,是因为它们都要抢同一个 details 单槽。DSH 0.1.5 换成 rightbar 之后,每个面板注册自己的 tab kind 与 body key,彼此不再竞争——容器这个角色由官方右侧栏接管,本包因此删掉了 dsh-details-tabs(约 1100 行)及其自绘的布局持久化 / DockRail。
但「一个面板一个标签」有个现实问题:右侧栏最多分两个窗格,所以一屏最多只能同时看到 2 个面板,切换成本高。因此 0.3.0 起把面板两两配对——一个标签内部上下叠放两个面板,中间是可拖动的分隔条。于是两个窗格并排 = 4 个面板同时可见;而拖动分隔条把某一半压到 15% 就等同于单面板视图。配对关系写在 src/composite/client.js 的 PAIRS 数组里,改组只需改那一处。
🌐 宿主路由
与各原插件一致,未作变更:
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/artifacts/scan |
扫描目录 |
| GET | /api/artifacts/session-dir |
本会话的产物目录(?sessionId=&cwd=),面板据此默认打开它 |
| GET | /api/artifacts/read |
读文件预览 |
| GET | /api/artifacts/media |
视频流(支持 Range) |
| POST | /api/terminal-notes/exec |
执行 bash 命令 |
| GET/POST | /api/terminal-notes/notes |
列出 / 新建记事 |
| GET/POST | /api/terminal-notes/note |
读 / 存单条记事 |
| POST | /api/terminal-notes/note-delete |
删除记事 |
🛠 开发与验证
改代码后先跑预检(DSH 插件工作流):
bash ~/.dsh/dsh-plugin-precheck.sh web --tests预检会做:宿主插件树加载(
--dump-config)+ 各插件node --check+ 客户端 bundle mock 装载契约断言 + 本仓库单元测试。通过后才允许重启;进不去时用~/.dsh/dsh-safe-start.sh安全启动。本包的契约测试
test/client-contract.test.mjs比预检更进一步:它在桩环境里真正执行apply(ctx),断言 2 个组合 tab 类型(id/kind/priority/guide 条目)、2 个sidebar.right.pane.tabbody,并真的把每个 body 渲染一遍——检查两个半边各自的标题、两个面板都以embedded: true且用各自的译者渲染、以及可拖动分隔条存在:node test/client-contract.test.mjstest/artifacts-scroll.test.mjs用一个极小的 hook 运行时真实渲染产物面板并驱动一次「打开预览 → 返回」的往返,断言列表滚动位置被还原、且还原发生在 layout effect(绘制前,不会先闪顶部再跳回);同时验证换目录会丢弃记忆的偏移。它顺带能抓住「effect 依赖数组里引用了 TDZ 变量」这类node --check看不到的错误:node test/artifacts-scroll.test.mjstest/artifacts-session-default.test.mjs真实渲染面板,钉住它打开时落在哪里:本会话已有产物目录时打开它;尚不存在时退回工作区根(扫不存在的目录会 404,而不是空列表);宿主还没提供/api/artifacts/session-dir时同样退回;用户自己钉住的目录永远优先:node test/artifacts-session-default.test.mjstest/artifacts-naming.test.mjs钉住「一次对话一个目录」的布局:本地开始日期(并把旧的 UTC 差一天作为反例钉住)、可读且路径安全的 slug、既有目录(含旧版<日期>-<完整会话id>)必须复用、子会话嵌套、提示原文,以及artifacts_list默认扫描本会话目录且不创建目录:node test/artifacts-naming.test.mjstest/terminal-exec.test.mjs是宿主侧测试:给宿主插件一个 mock ctx,抓住/api/terminal-notes/exec注册的路由并真正调用它,断言 spawn 用的是stdin: 'ignore'(fd 0 指向 /dev/null)、带了 abort signal,且被执行期限终止的命令会返回分类后的timeout结果连同它已经打印的输出:node test/terminal-exec.test.mjs浏览器半身是生成产物,源码在
src/。改面板 UI 请改src/<fragment>/client.js,然后重新生成:node scripts/merge-client.mjs # 生成 lib/client.js node scripts/merge-client.mjs --check # 只校验是否过期(过期退 1,可挂进 CI/预检)脚本从
src/{composite,artifacts,long-term-memory,terminal-notes}/client.js提取 factory 体、改写 react/react_jsx_runtime绑定、删除子模块内exports.语句,并把一个共享的panels注册表作为第 3 个参数传给每个 fragment(三个面板 fragment 往里写组件,composite读它渲染)。composite必须最后合并,否则注册表是空的。直接改lib/client.js的改动会在下次生成时丢失——脚本覆盖前会打印「would change +N/-M」警告,--check则完全不写文件。数据位置:长期记忆
~/.dsh/dsh-memory/{global,user}.jsonl、工作区.dsh/memory.jsonl;记事本~/.dsh/notes.json。
🔒 注意事项
- 记事本存储为单 JSON 文档,用
node:fs写(临时文件 + rename),不走ctx.fs:DSH 0.2.1 起该服务的后端是沙箱(workspace-write只允许写工作区内路径),~/.dsh/notes.json在工作区外,于是新建/保存/删除全部 500,记事本无法新建。该路径是插件自己的固定状态、不是模型给的路径,且长期记忆半身本来就用node:fs持久化。 - 终端不是交互式 shell:它执行
bash -lc <你输入的那一行>,子进程的 stdin 是/dev/null(所以bash、cat、read、python这类读 stdin 的命令会立刻拿到 EOF 退出,而不是把面板挂住)。命令运行中会出现**「终止」按钮**(对应宿主路由POST /api/terminal-notes/exec-cancel,用面板为每次执行生成的runId定位正在跑的子进程)。每条命令另有 120 秒执行期限,超时会被终止、并把已打印的输出一并返回;期限可用宿主配置terminalNotes.timeoutMs调整。手动终止与超时是两种不同结果(cancelled/timeout),两者都会保留已打印的输出。需要交互或长时间的构建,请用会话自带的 bash 工具。 - 面板不再自绘任何栏宽/布局;停靠、浮动、分屏与宽度都由官方
dsh-client-ui-sidebar-right的 docking kit 决定。升级 dsh 不会再冲掉本包的布局改动(因为已经没有这类改动)。 - 本包仅动态注册的服务/路由/工具随 fiber 生命周期;停止或热更新会移除全部副作用。
链接
同类插件
zhu1090093659/dsh-web#packages/dsh-task-board★ 9796
侧边栏多列任务看板:卡片交给真实 DSH 智能体会话执行,支持 cron 定时(Host 侧到点执行,关浏览器也生效)。
zhu1090093659/dsh-web#packages/dsh-web-all★ 9796
DSH Web UI 插件与皮肤合集:任务看板、git 图、右侧面板、远程移动端 UI、桌宠、实时 token 统计与皮肤中心。
MeteorNOX/DeepSeek-Balance-Whale-Widget★ 6532
右下角常驻的小鲸鱼挂件:余额、今日已用与每轮对话消耗(含峰谷价),余额预警与今日预算的泡泡内容都可编辑;泡泡点击序列模块化自定义,支持并列加权 A/B、随机台词与随机图片;内置 30+ 厂商模板(OpenAI / OpenRouter / Kimi / 硅基流动 / 方舟 / 智谱 / MiniMax 等),按模型查余额与订阅额度;另有任务结束音效、导入音频、自定义角色与资源管理。数据全在本机,无遥测。
ccch1mneyyy/dsh-TUI★ 6206
Claude Code 风格全屏终端 UI:像素鲸鱼顶栏、实时工作状态行、思考流式展开。
omdsh-dev/DSH-better-sidebar★ 4738
侧边栏完整工作台:内置文件渲染编辑、终端、Git 与子代理,支持三方插件注册新 Tab。
Devin-AXIS/deepseek-design#deepseek-idesign★ 1632
可视化设计工作室,支持网站、App 原型、海报、信息卡、报告和杂志的模板创建、元素编辑、选区级 AI 草稿衔接与导出。
社区评论
评论公开保存在 GitHub Discussions。加载评论会连接 GitHub 和 Giscus;发表内容需要 GitHub 账号。