dsh-workspace-studio
Coding Toolsyishengjun8/dsh-workspace-studio
Workspace Studio plugin for DSH: three-column layout with file tree, CodeMirror editor, preview tabs, and branch graph view for sessions, aiming for a VSCode-like development experience.
- dsh-plugin
- dsh-plugins
README
🗂️ DeepSeek Harness 工作区 Studio 插件(左中右三栏布局)
English | 中文
此 bundle 将 DeepSeek Harness Web 的根布局替换为左中右三栏:左侧栏(Session/工作区选择器 + 文件树视图切换)· 中部高亮文件预览与受控编辑器 · 右侧聊天。文件预览栏默认显示在对话左侧,可在「工作区设置 → 内容浏览设置」中切换到对话右侧。文件树不再独占一栏,而是融合进左侧栏,与「会话列表」通过顶部按钮互切。插件保留现有侧栏、会话、详情与全局浮层的 Slot 合约,内置的新建会话、会话列表、设置、聊天、工具详情、审批等仍由原插件提供;工具详情以右侧抽屉覆盖在三栏布局上,不额外占用常驻栏位。会话头部提供「导图」按钮,可随时进入导图模式:左侧区域变成会话分支树画布,右侧聊天保持可见、可继续对话。
📸 界面预览
![]() | ![]() |
|---|
✨ 核心亮点
| 能力 | 说明 |
|---|---|
| 📁 工作区文件树 | 融合在左侧栏「文件浏览」视图,目录优先、逐级展开,刷新后展开状态与滚动位置按会话恢复 |
| ⌨️ CodeMirror 6 编辑器 | 20+ 语言语法高亮、行号、代码折叠、编辑器内搜索、自动换行,支持 14 种文本编码 |
| 🗂️ 预览标签页 | 按会话持久化、跨刷新恢复、拖拽重排、固定标签、草稿不丢失、冲突保护 |
| 🎯 编辑器上下文 | 打开文件 / 选区以 <opened_file> / <selection> 前缀注入对话,历史只记录一行摘要 |
| 🧹 文件操作 | 右键新建 / 重命名 / 复制 / 剪切 / 粘贴 / 删除 / 复制路径,支持快捷键 |
| 🧭 导图模式 | 会话分支树:会话头部「导图」进入导图模式,反向解析完整会话记录切成轮次卡片并持久化,在任意卡片处分叉新分支,可重命名 / 删除卡片、归档整图 |
| 📱 手机模式 | 一键切换手机竖屏布局,文件浏览可铺满手机列 |
| 🔒 安全边界 | 工作区受限读写、路径包含校验、修订版本冲突保护、拒绝符号链接 |
🧩 功能
文件树
- 文件树不再独占一栏,而是融合在左侧栏中:侧栏顶部提供「会话列表 / 文件浏览」两个视图按钮,「文件浏览」视图下,会话属于某 Workspace 时自动显示其文件树(会话
cwd与 Workspace 路径一致时同样识别);目录优先于文件排列,支持逐级展开、折叠与手动刷新。 - 文件树展开的文件夹按当前会话持久化,刷新后重新展开并加载内容;标签选中时在树中定位并恢复垂直滚动位置。
- 会话标题右键可重命名当前会话。
- 左侧两栏宽度均可拖拽调宽:侧栏边界与预览栏边界各有一条分隔条(左侧两栏合计最多占视口 80%),布局参数以
localStorage全局持久化。
编辑器
- CodeMirror 6 按文件名或后缀显示行号与语法高亮,未知类型按纯文本显示;内置折叠槽与编辑器内搜索(
Ctrl/Cmd+F、F3)。 Ctrl+K+J展开所有已折叠区域;Ctrl+K+1..9按层级折叠代码(如Ctrl+K+2折叠所有第二层级的折叠区域)。- 可编辑文件打开即进入编辑状态(无需「编辑」按钮);面板头提供「取消」「保存」「自动换行」与「从磁盘重新读取」(刷新)。只读文件(外部拖入、超大、截断、混合换行、符号链接或未启用编辑)显示只读原因横幅。
- 每类文件类型组可在工作区设置页选择编辑器高亮预设(默认、经典、暖色、冷色、单色、XML (VS Code) 等 10+ 款),按类型记忆于
localStorage。
编码
- 文件预览自动检测编码(UTF-8 / UTF-8 BOM / UTF-16 LE / BE / GBK / GB18030 / Big5 / Shift_JIS / EUC-JP / EUC-KR / ISO-8859-1 / Windows-1252 / Windows-1251 / ASCII)。
- 右键预览头可「以编码打开…」重新解码,或「另存为编码…」写回磁盘;面板头显示当前编码徽标。
- 编码列表以服务端
/api/encodings为准,请求失败时回退内置清单,操作不会中断。
预览标签页
- 打开的文件进入按 Session 保存的预览标签页:可
X关闭、拖拽重排,标签页跨重载恢复。 - 固定标签:右键标签可「固定 / 取消固定」,固定标签带图钉图标、自动排前,「关闭其他标签页」只关闭未固定标签。
- 未保存修改时,标签页名称与预览面板标题的文件名末尾显示
·,保存后消失。 - 未保存草稿以暂存盘文件保留(见「编辑与保存」),localStorage 只记脏标记、不存内容;切换文件不会静默丢弃未保存内容。
- 标签条支持滚轮横向滚动,打开新标签自动滚动到可见。
编辑与保存
- 可编辑文件打开即进入编辑模式,提供“保存”“取消”与
Ctrl/Cmd+S。 - 暂存盘(草稿文件):编辑时提取一次快照(源文件内容),所有临时修改防抖写入暂存盘文件(
~/.dsh-plugin/dsh-workspace-studio/drafts/<workspaceId>/,长期留档),源文件不被触碰;刷新页面后从暂存盘文件恢复(草稿 + 快照 + 编码)。自动存盘不视为“保存”,·仍保留直到显式保存。localStorage 只保留脏标记,不存编辑内容/快照。 - 保存(合并回源文件):保存时重新读取源文件,与快照比较——
- 源文件未被其他工具改动(= 快照):把暂存内容静默写回源文件,成功后删除暂存盘文件。
- 源文件被改动、且与你的修改不在同一位置:自动三方合并,双方修改都保留后写回。
- 源文件被改动、且与你的修改在同一位置:弹窗逐处展示冲突区域——上方两栏为行内增删对比(我的修改 / 磁盘版本),下方两栏为修改后的实际代码,可分别选择「保留我的版本 / 保留磁盘版本」,取消则放弃保存。
- 取消:放弃临时修改,删除暂存盘文件,编辑器恢复到源文件内容(源文件本身不改动)。
- 拒绝二进制、非 UTF-8 与工作区外符号链接;截断的大文件、混合换行文件与经符号链接的路径只读。
文件操作
- 可在选中层级新建文件 / 文件夹,
F2重命名。 - 右键菜单:复制名称、复制路径、复制相对路径、「在资源管理器中打开」。
- 右键复制 / 剪切 / 粘贴 / 删除,支持快捷键
Ctrl/Cmd+C、Ctrl/Cmd+X、Ctrl/Cmd+V、Del。 - 剪切 + 粘贴 = 移动;粘贴目标同名自动去重(
a.txt → a-1.txt);删除弹确认对话框,涉及未保存标签时附加警示。 - 剪贴板为内存态、按工作区隔离(跨工作区粘贴置灰),刷新页面即失效;外部文件(拖入的只读预览)不可重命名 / 删除。
搜索
- 搜索内容时结果按文件分组:点击文件头折叠 / 展开该文件的匹配条目,点击匹配条目打开文件并跳到对应行。
- 支持区分大小写切换;大文件仅搜索开头部分时标注「部分」。
- 可在工作区设置页选择搜索结果的默认展开 / 折叠方式。
编辑器上下文
- 编辑器上下文经现有输入 dock 显示为输入框外的不可编辑前缀:启用发送时冻结上下文,文件模式渲染
<opened_file>...</opened_file>、选中文本模式渲染<selection>...</selection>(无选区时不携带文件字节),灰色发送不附加上下文。 - Host 校验并把它拼接到直接用户提示前;对话页折叠成气泡上方显示文件名与行列范围的一行摘要,历史只渲染已记录的用户消息。
聊天体验
- 会话头部标题区是会话切换器:点击弹出下拉面板,列出全部会话(最近更新在前、当前会话高亮、行尾附所属工作区名、子代理会话带「子代理」徽标),点击即切换到该会话;导图家族的分支会话不在其中(根会话保留,便于直达)。
- 界面语言跟随 Harness「设置 → 通用设置 → 语言」(中文 / English)即时切换,无需重启或刷新。
- 聊天中正在输出的思考内容(Think 条)默认自动展开、结束后按可调延迟自动收起(0–10 秒,分度 0.1 秒,默认 3 秒,期间手动操作可取消),用户手动操作优先;可在工作区设置页关闭该行为并调整延迟。
/init命令(类似 Claude Code):在当前会话所属工作区的根目录生成或更新AGENTS.md,已有文件时弹层让你选择「更新」或「取消」,由当前 Agent 分析工作区后生成。- 可将外部文件拖入预览面板直接以只读标签预览(会话内有效,不写入工作区)。仅文本类文件可预览:图片、文件夹等非文本内容会提示「无法作为文本预览」(图片属聊天输入区,此为有意行为)。
导图(会话分支)模式
- 会话头部「导图」按钮进入导图模式:页面左侧弹出导图悬浮窗,覆盖除聊天栏外的整个左侧区域(宽度 = 100% − 聊天栏当前宽度,拖拽分隔条调整聊天栏时实时联动),右侧聊天保持可见可继续对话;再点按钮 / 右上角 × / Esc 关闭。工具栏可切换填充模式:「全部」覆盖侧栏 + 文件浏览,「仅侧栏」只占左侧栏一列(文件浏览区域保持可见)。首次进入时,插件从会话的完整事件日志反向解析全部轮次,切成一张张卡片(主干 1 → 2 → 3 → 4 → 5),并持久化到
~/.dsh-plugin/dsh-workspace-studio/mindmap/—— 该持久化文档是导图的唯一信息源。 - 首次进入前会弹确认框:将普通会话转换为导图会话后,它从侧栏会话列表隐藏,改为对应工作区分组下会话列表末尾的一个自绘条目(点击条目会打开会话并弹出导图悬浮窗);凡由该导图派生出来的 fork 会话都会从列表隐藏,只在导图里管理。
- 点击卡片 = 切换优先、新建兜底:停在某卡片的分支(链尾卡片)点击即切换到该分支(右侧聊天跟随切换,导图内高亮跟随,可自由切换);没有分支停靠的中间轮次卡片(如分支 6-7 里的 6)点击则在该处 fork 新分支并进入对话,新轮次与兄弟轮并列(6 → 8、9 与 7 并列)。所有 fork 都归同一个主导图,绝不新增导图;新分支会话也不出现在侧栏会话列表。分支的新轮次由 Host 在同步时从分支会话的完整日志折叠回文档。
- 右键分支可重命名;工具栏可「归档整个导图」(连同全部分支会话,归档后悬浮窗自动关闭)。右键任意卡片(含主干卡)可删除卡片(真截断):从上一张卡 fork 出截断后的新会话并归档原会话——该卡片及其后的轮次、由此衍生的所有分支一并移除(原会话归档后当前无恢复入口),聊天与导图从此从截断点重新开始、编号一致。导图支持抓手平移、滚轮缩放与「还原视图」。
- 分支正在输出时(输入问题、agent 生成中),导图上实时出现一张「生成中…」卡片(脉冲边框,显示本轮问题文本),并与它的父卡片一起被一个虚线大框圈为一个整体;输出完成后流式卡自动转为正常卡片、大框消失。流式卡可点击 = 切换到正在生成的会话(右侧聊天跟过去实时看输出、高亮跟随;未收尾轮没有 turn/end seq,不能作为分叉点,右键菜单也禁用);生成中会话的最后一张已完成卡此时按中间卡处理,点击即在它处分叉新分支。
外观与设置
- 使用 Harness 主题语义变量,支持亮色、暗色与系统主题。
- 工作区设置页:会话浏览设置(侧栏导图条目流式输出时旋转图标的速度,倍速 0–3×,越大越快,默认 1.5×)、文件树行高、搜索结果显示方式、文件图标徽标配色、每类文件高亮预设、冲突弹窗对比字号、对话文字大小、Think 条自动展开与延迟、文件浏览页面显示在对话左侧或右侧(默认左侧)。
- 侧边栏底部提供「手机模式」开关:开启后整栏布局切换为居中的手机竖屏列,侧栏变为由左上角鲸鱼开合的悬浮抽屉(会话列表与文件树仍在其中);会话头部鲸鱼右侧出现「文件内容浏览」按钮,点击后文件浏览铺满手机列、会话头部保持可操作。手机模式为瞬态状态,刷新后回到桌面布局。
🎨 语法高亮
内置 20+ 语言:JavaScript/JSX、TypeScript/TSX、JSON、HTML、CSS/SCSS/Less、Markdown/MDX、Python、SQL、XML/SVG、YAML、C/C++、Java、Rust、PHP、Go、Shell、PowerShell、Ruby、TOML 与 Dockerfile。
Makefile、.gitignore、.env、LICENSE 与未知扩展名以纯文本显示,仍可浏览与编辑。
🧩 双面实现
一个包内封装三个端面:
- Host 端(
lib/index.js)注册/workspace-studio/api:按 Workspace ID 列目录、读取有上限的 UTF-8 文件,按 membership 或规范化 cwd 授权当前 Session;显式启用编辑时,通过修订版本校验、单段名称校验和原子替换保存已有普通文件、新建文件与文件夹、重命名已有条目,拒绝过期修订版本而不是静默覆盖。另提供/mindmap-doc(读 / 写 / 删)与/mindmap-doc/sync、/mindmap-doc/index、/mindmap-doc/rename接口:按会话持久化导图文档,反向解析完整事件日志生成主干与分支轮次,重命名只更新导图标题而不整份往返。 - Browser 端(
lib/client.js)提供兼容的ctx.layout服务,占用根 Slot,继续声明sidebar、conversation、details与shell.overlay,并加入文件树、CodeMirror 6 浏览器/编辑器、编辑器上下文行、工作区设置页、/init命令与会话分支导图。 - 共享不变量(
lib/invariant.js)为每次 Host 请求提供路径包含与写入资格校验。
激活模型
layout 提供方有意不硬注入 conversation:conversation 插件本身消费 layout。因此 bundle 在激活后通过子注入 patch 现有 sendSession seam,并向 conversation.input.dock 注册编辑器上下文行,避免形成激活依赖环。
已知限制与待办
编辑器上下文发送桥适配 Harness 0.1.x 具体的 sendSession、输入提交与队列 steer 实现,因为跨包公开 face 不承载任意 Composer 上下文。这些 seam 都封装在本包内并在卸载时恢复,未来 Harness 版本可能只需更新本 bundle。
布局状态、展开目录、编辑器选区与 Workspace 草稿缓存均属页面内存状态;预览标签页及其各自的垂直滚动位置在重载后、以及返回原 Session 或 Workspace 时恢复。
模型体验
当前缀启用且 CodeMirror 主选区非空时,每次发送都会捕获该选区的精确文本、规范化工作区路径与范围,并渲染为 <selection>...</selection> 封装。选区为空时,每次发送只捕获打开的文件路径,并渲染固定的 <opened_file>...</opened_file> 封装;绝不提交完整文件。
Browser 发送桥把渲染后的文本拼接到直接用户提示前,因此普通 user/message 记录包含实际模型可见的上下文。对话页会把该封装折叠成气泡上方的一行摘要,只显示文件名与行列范围;鼠标悬浮该行会显示完整的注入 XML。灰色前缀不贡献上下文;后续每个启用回合都会再次记录相同上下文。
Token 与 KV 缓存影响
选区上下文会增加 <selection>...</selection> 封装以及选中文本的输入 Token。资源管理器先按默认 65,536 UTF-8 字节限制预检选中文本;Host 独立将完整渲染默认限制为 69,632 字节,并最多读取 10 MiB 用于 clean 修订版本校验。截断预览以浏览器权威的选区文本为准。仅路径上下文只增加 <opened_file>...</opened_file> 封装、不携带文件正文。每个启用回合都有自己的日志提示文本,因此 compaction 前重复选区可能增加提示 Token。
📦 安装
在 Git Bash、Linux 或 WSL 中执行。先进入本插件所在目录(用你自己的路径替换):
cd <插件目录>
bash ./install.sh # 默认安装到 web profile
bash ./install.sh web # 也可显式指定 profile
示例路径
C:/GreenSoftware/deepseek-harness/deepseek-harness-plugin/dsh-workspace-studio中的deepseek-harness-plugin是作者自定义的插件目录名,不是固定要求。install.sh以插件 目录为基准向上两级解析 Harness 根目录(供 PATH 无dsh时的pnpm --dir回退使用),因此 推荐把插件放在 Harness 根目录下两层的插件目录中(与示例一致);若 PATH 中已有dsh, 插件放在任何位置都可安装。
脚本优先使用 PATH 中的 dsh;当前目录属于 Harness checkout 且 PATH 无 dsh 时自动使用
pnpm --dir <harness-root> dsh,也可用 DSH_BIN 指定可执行文件。安装完成后停止并重启原有
Web 进程(先停止再启动,让插件随 Web 进程重新加载生效),然后刷新 http://127.0.0.1:3080;
脚本不会启动第二个服务器。
从 Git 直接安装
不依赖本地副本,直接从插件仓库安装(首次安装会用 tsdown 把 src/client/index.js 现场构建成 lib/client.js):
bash ./install.sh --git # 默认安装到 web profile
bash ./install.sh --git web # 也可显式指定 profile
脚本把 git 依赖 spec 解析为当前插件的 GitHub 仓库(可用 GIT_SPEC 环境变量覆盖),并锁定到
当前 HEAD 提交(github:<owner>/<repo>#<commit>),因此后续推送不会悄悄改变已安装的代码。
pnpm ≥ 10 默认拒绝执行 git 依赖的 prepare 构建脚本,首次 add 会失败;脚本会解析 pnpm 打印的
allowBuilds 键、写入该 profile 的 pnpm-workspace.yaml,然后重试,无需手动干预。
⚠️ 允许构建意味着允许该包的
prepare脚本在安装时于你的机器上执行(不在 agent 沙箱内)。 从本插件的官方仓库安装时这是预期行为。手动安装的等价命令是dsh plugin --profile web add github:yishengjun8/dsh-workspace-studio:失败后按 pnpm 提示把 allowBuilds 键复制进该 profile 的pnpm-workspace.yaml,再重跑add。
🗑️ 卸载
bash ./uninstall.sh
卸载后同样需要重启 Web 进程;移除 bundle layer 后内置 ui-layout 自动恢复。
⚙️ 配置
cordis.patch.yml 中插件 row 接受:
| 字段 | 默认值 | 说明 |
|---|---|---|
enableEditing | false | 是否启用 Host 写入接口;本 bundle 显式设为 true。 |
maxContextBytes | 65536 | 选中文本 UTF-8 预检上限(1024–1048576);仅路径上下文不提交文件字节。 |
maxPromptContextBytes | 69632 | Host 对完整渲染上下文(含封装与选中文本)的上限(4096–2097152)。 |
maxContextSourceBytes | 10485760 | clean 修订校验最多读取的原始文件字节(1024–104857600)。 |
maxEditableBytes | 1048576 | 单文件可保存的最大 UTF-8 字节(1024–10485760)。 |
maxEntryNameBytes | 255 | 新建/重命名条目名称最大 UTF-8 字节(1–1024)。 |
maxMutationBodyBytes | 4096 | create/rename 请求最大 JSON 字节(128–65536)。 |
maxPreviewBytes | 1048576 | 单文件读取并返回的最大字节(1024–10485760)。 |
💡 改配置直接编辑 bundle 的
cordis.patch.yml;为避免 pnpm 复用已安装的本地file:副本,先运行uninstall.sh,再运行install.sh,最后重启 Web 进程。
🔒 安全边界
路径包含校验:Host 接口只接受已登记的 Workspace ID 与相对路径,每次读写都解析真实路径并确认目标仍位于 Workspace 规范根目录内,..、绝对路径与跳出 Workspace 的符号链接均不可访问;接口同时执行与内置 /api 同目的的 Host、Origin 与 Fetch-Metadata 来源检查。
写入保护:写入接口仅在 enableEditing 开启时接受 PUT,正文必须是有上限的 UTF-8 文本,且必须携带读取时的 If-Match 修订版本,版本不一致返回冲突而不覆盖;写入目标必须是已存在且不经过任何符号链接的普通文件。create/rename 沿用相同的路径包含校验,要求单段名称、拒绝已存在目标。Host 通过同目录临时文件、文件同步与原子重命名提交,并尽量保留原权限模式。
上下文安全:编辑器上下文只接受拥有当前 Session 的 Workspace 内相对路径(拥有关系来自 membership projection 或会话规范化 cwd);仅路径上下文不携带文件字节。Host 拒绝符号链接,按磁盘修订校验 clean 选区,maxPreviewBytes 截断预览时以浏览器提交文本为权威,并把渲染文本拼接在直接提示前,因此普通 Session 日志记录实际模型可见上下文;对话页把它折叠成气泡上方显示文件名与行列范围的一行摘要,历史只渲染已记录的用户消息,不重新读取当前编辑器或磁盘。
⚠️ 这些限制只约束资源管理器自己的文件接口与 Composer 上下文,不改变 agent 的权限策略、沙箱或工具能力;接口为受信任本地 UI 操作提供应用级路径包含校验,不替代 Harness 的内核级沙箱。
📁 项目结构
.
├── package.json # 单包 manifest:bundle patch + client inject + exports
├── cordis.patch.yml # 禁用内置根布局并挂载本插件(自引用单包名)
├── install.sh / uninstall.sh
├── src/client/index.js # 浏览器源码
├── lib/index.js # Host:有界的 Workspace 读、保存、新建、重命名 API
├── lib/invariant.js # Host 共用不变量断言
└── lib/client.js # 预构建三栏布局、文件树与编辑器
CodeMirror 与语言模块已内联到预构建的普通 JavaScript Client bundle;本地 file: 安装无需构建,
从 git 安装时 prepare 会用 tsdown 现场重新构建。维护源码时,在仓库根目录执行
pnpm install --config.auto-install-peers=false,再运行 npm run bundle 重新生成 lib/client.js。
🔄 兼容性说明
针对提供 conversation.input.dock Slot、Session 输入 resolver 与发送服务的 Harness 0.1.x checkout 编写。编辑器上下文功能完全由本 bundle 实现,不要求修改 Harness 源码;发送桥适配 0.1.x 的具体 send/输入提交/队列 steer seam,未来版本可能只需更新 bundle 内桥接代码。其他高优先级 profile/home patch 若重新启用 ui-layout,会与本插件同时占用根 Slot;请保留本 bundle 对 ui-layout 的禁用设置。

