dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
DSH Web 客户端的 Git 管理插件 —— 在左侧面板栏打开一个全页三栏 Git 工具(分支 / 历史 / 提交详情),右侧边栏的 Git 胶囊只负责把面板打开。类似 IntelliJ 的 Git 工具窗口,以 DSH 插件 bundle 形式挂载。
| 项 | 值 |
|---|---|
| 包名 / 版本 | GitPanel / 0.2.0(private,仓库根即包根) |
| 挂载方式 | DSH 插件 bundle(dsh.manifestVersion: 1,client 平台 web) |
| 运行要求 | DSH ≥ 0.1.7-rc.1;使用 Web 客户端的 profile;Host 机器的 PATH 里有 git |
| 依赖 | 无运行时依赖、无构建步骤:Host 半一个文件 + 浏览器半若干分片,交付什么运行什么 |
装进 profile —— dsh plugin 把参数原样转给 pnpm:
dsh plugin --profile <name> add github:relaxyabc/dsh-GitPanel # 从 GitHub(公开仓库,默认分支 main)
GitHub 形式装的是拷贝,升级用 dsh plugin --profile <name> update GitPanel 重新解析(要固定版本就写 github:relaxyabc/dsh-GitPanel#v0.2.0 或 #<commit>);本地路径形式装的是指向仓库的链接,改完代码重启即生效,适合边改边用。
在 profile 的 cordis.patch.yml(或以 --patch 叠加层)里插入下面的块 —— 包内 cordis.patch.yml 即此内容:
- insert:
- id: GitPanel
name: 'GitPanel'
重启 profile(启用 dsh-hmr 时自动重载)。
Ctrl/Cmd+Enter 直接提交。打开仓库时会自动读取提交模板(见下文「工作区改动与提交」),已有草稿不会被覆盖。危险操作(hard reset、删除分支、删除文件)有确认对话框;成功提示 4 秒后自动消失。配置在插件页面「已安装」分组里的 GitPanel 上(本包自己的配置位,不占官方分组),分「仓库发现」「差异显示」「文件预览」三组;输入后按「保存」写入 profile,四个字段都即时生效、无需重启。
| 配置项 | 类型 | 默认 | 说明 |
|---|---|---|---|
discoveryDepth | number(1–8) | 3 | 仓库发现向下递归的目录层数;运行时始终夹取在该区间内 |
wholeFileDiff | boolean | false | 开启后 diff 左右两栏显示整个文件而不只是改动片段 |
filePreviewFix | boolean | true | 修复 Chromium 上「文件资源服务不可用」的文件预览,见下 |
mermaidRender | boolean | true | 在 Markdown 文件预览中把 mermaid 代码块渲染成图,见下 |
filePreviewFix 补偿的是 DSH 上游的一个缺陷(discussion #6437):dsh-client-resources 用 new URL(address).hostname 取协议键,而 Chromium 不把非特殊 scheme 的 authority 当作主机(new URL('dsh-resource://file/x').hostname 在 Node 上是 file、在 Chromium 上是 ''),于是每个资源地址都解析不出提供方,右侧边栏的文件预览只剩那句报错。本插件的修复只改 dsh-resource:// 这一类地址(其余地址、相对地址解析、非法地址抛错全部交回浏览器),代价是这类地址不是 instanceof URL、就地改写部件不回写整条地址(面板只读不改)。上游修好后可以关闭本项。
mermaidRender 开启后,.md / .markdown 文件在右侧边栏默认经本插件注册的文档渲染器打开(extension 优先级高于内置 viewer)。正文就是 DSH 自带的 MarkdownText 原语——图片解析、脚注、代码块工具栏与内置完全一致——只有 ```mermaid 围栏被替换成渲染出的 SVG。Mermaid 库(11.17.2,单文件构建,约 3.4 MB)随插件内置分发,渲染完全不需要联网。 图与源码的切换用文件工具栏的 viewer 菜单:选「Markdown(Mermaid 图)」看渲染图,选内置「Markdown」看源码(mermaid 块保持原样)。关闭本项即始终显示源码。
排障:Office 文档与 PDF 预览报 无法显示 PDF:n.toHex is not a function。 这类预览由 DSH 内置的 pdf.js(pdfjs-dist 6.3.289)渲染,而该构建直接调用 Uint8Array.prototype.toHex、Uint8Array.prototype.toBase64、Float16Array、Math.sumPrecise、Map.prototype.getOrInsert、RegExp.escape —— 都是 2026 年前后才进入引擎的 API;另外 DSH 的 PDF 载体明确不会退回主线程解析。所以在旧浏览器(例如 Chrome 128)上,任何 PDF 渲染都会在加载文档时失败,.doc / .docx / .ppt / .pptx 预览因为走「Office → PDF → pdf.js」这条路,看起来像「只有 Office 文档坏」。这与本插件的地址修复无关(地址修复只让 DSH 找得到提供方,能不能渲染是浏览器的事),也补不了:补丁得落进 pdf 渲染 Worker 自己的 realm 并补齐上述六个引擎特性。把浏览器升到当前版本即可恢复(验证方法:旧浏览器上预览一个普通 .pdf 会报同样的错,升级后 PDF 与 Office 文档一起恢复)。
仓库与工作区
node_modules、.git、dist、vendor 等目录。分支
HEAD 标记,显示上游与最新提交说明。提交历史与 diff
git log --graph 风格:首父提交沿本 lane 直线下行,合并提交画曲线,加载更多或筛选后对当前可见的提交即时重算,页面外的父提交画短桩),右侧是首行信息与 ref 装饰;列表按窗口渲染(只建可见行 + 上下占位,滚动条仍然准确),一页最多 200 条,滚到底部自动续读,或点 Load more 读一页、Load all 在后台一路读到命中历史的开头;标题显示「已加载 / 命中总数」(总数由同参数的 rev-list --count 数出);已加载条数有分档上限(默认 2 万条),到档位时明确提示并给 Keep loading 再抬一档 —— 上限是标签页内存,不是历史长度。每提交的文件统计在选中该提交时才单独读取,历史列表始终是一次轻量调用。--since / --until / --author / --grep,哈希形状的搜索改为按 ref 解析),所以「开始日期定到三年前」得到的是整段历史里命中集的最新一页,而不是在最近 200 条里找不到;命中总数同样按这组条件数出,分页在命中集上进行。打字时先对已加载的提交做一次本地收窄作为即时反馈,安静 300 ms 后才成为一次查询(每个条件一次历史遍历,不按按键发请求);筛选生效时出现一键清除按钮,无命中时明确说「没有符合筛选条件的提交」而不是「暂无提交」。%ct),也就是 Git 自己的 --since/--until 读的那个日期;行与详情显示的也是它(rebase / cherry-pick 之后与作者日期不同)。若运行中的 Host 早于这些参数,它会忽略条件并返回未筛选的页:面板发现后回退成「只收窄已加载的提交」并明说这一点。wholeFileDiff 后两栏改为整文件对照。工作区改动与提交
git status --porcelain=v2 -z 的改动列表:状态字母 + 每文件 +/− 行数,二进制标注。.git 内部一律拒绝)。Ctrl/Cmd+Enter 快捷提交;提交信息框的高度随窗口自适应(约 clamp(150px, 26vh, 380px),即 8–16 行),也可拖拽下边缘自行调整。%B),不再因 Git 折叠主题而丢失换行。commit.template 配置(相对路径按仓库根解析),其次仓库根目录的 .gitcommit 文件;填入提交框时剥离 # 注释行(与 Git 的 cleanup 行为一致),已有草稿绝不覆盖,普通提交成功后自动填入下一份模板。git commit --amend 会把已暂存(或全部)内容并入最新提交、并替换其信息;取消勾选会把改写前的草稿还给信息框。改写期间 Commit & push 被禁用 —— 改写后的提交需要强制推送才能发布,面板不提供。clamp(150px, 26vh, 360px)),不再是单行小框。这两处既有菜单的文案保持不变 —— 只有提交框里的 amend 控件在中文界面下也用英文 Amend。--set-upstream)、Fetch(只下载对象与远程跟踪引用)、Pull(仅当前分支有上游时可用)。文件预览(右侧边栏)
mermaidRender 配置;预览本体(文本分页、图片、脚注、代码块工具栏)与 DSH 内置 Markdown 渲染器一致。filePreviewFix 让资源地址在 Chromium 上解析出正确提供方,见上文;两条都即时生效,关闭即回到 DSH 原生行为。子模块
界面与语言
locale/{en,zh}.json。Checkout / Cherry-pick / Reset (soft|mixed|hard) / Revert commit / Merge / Rebase 在中英文界面下都是这几个词(横幅按钮另按实际调用写作 Merge --abort / Rebase --continue),Amend 也一直保持英文。中文只用在它们的方位与解释上,例如中文界面下的分支菜单是「Merge into main」「Rebase main onto this branch」。--dsw-alias-* + 字面回退值),自动适配明暗主题;输入框、下拉、按钮、提交框等控件的边界色由次级文字色派生(color-mix),并带字段底色,明暗两套主题下都有清晰可见的边界,浅色模式下各功能一眼可辨。两半对等,通过一条认证 Fetch 路由通信:
┌─ 浏览器半 src/client.js + src/client.*.js ─┐ ┌─ Host 半 index.js ─────────────────┐
│ client.js:注册入口 + 接线 + apply │ fetch│ ctx.connection.fetch.register │
│ client.*.js:包内分片(词典 / 样式 / │ ────▶│ POST /api/local-git │
│ 面板 / 三栏 / diff / 设置卡 / 修复) │ │ (继承 Host/Origin 围栏 + 会话认证) │
│ React,createElement,无 JSX、无构建 │ ◀────│ resolveInside 路径围栏 → execFile(git)│
│ inject: slots/sidebarRightTabs/layout │ └──────────────────────────────────────┘
└─────────────────────────────────────────────┘ JSON
Host 半在仓库根(index.js),浏览器半在 src/(相当于上游包的 lib/)。两半的定位互不相干:Host 半由 exports["."] 决定,浏览器入口由 exports["./client"] 决定 —— 只有分片必须与浏览器入口同级。
// 一个请求一个操作;错误一律 200 + ok:false,400 只用于非 JSON 请求体,413 只用于超过 1 MiB
POST /api/local-git
{ "op": "state", "args": { "workspaceRoot": "E:/ws", "path": "alpha" } }
→ { "ok": true, "data": { … } }
→ { "ok": false, "error": "the path is outside the workspace root" }
| op | 类型 | 作用 |
|---|---|---|
repos | 读 | 发现工作区下的全部仓库(含子模块),并回报生效的扫描深度 |
state | 读 | 当前分支、本地/远程分支、ahead/behind、子模块、全部改动文件 |
log | 读 | 提交历史(可按分支 / 文件 / since / until / author / search 过滤,哈希形状的搜索走 ref;每次 1–200 条,skip 续读下一页,响应带 hasMore;带 subject / body 与提交日期 committedAt,不含原始 message、不含每提交文件统计) |
logCount | 读 | 同一组条件下的命中总数(git rev-list --count),面板据此显示进度、判断是否读到读完 |
commitTemplate | 读 | 提交模板:commit.template 配置优先,其次仓库根的 .gitcommit;两者皆无时为空串 |
commitFiles | 读 | 某一个提交改动的文件与行数 |
commitMessage | 读 | 某一个提交的原始提交信息(%B):列表页不再携带它,选中提交 / 需要 amend 时才读 |
diff | 读 | 单文件 unified diff(可指定 commit、wholeFile;未跟踪文件也能给出) |
stage / unstage | 写 | 暂存 / 取消暂存指定路径或全部 |
commit | 写 | 创建提交(--all / --amend) |
push / fetch / pull | 写 | 推送 / 只下载对象与远程跟踪引用 / 把上游整合进工作区 |
checkout | 写 | checkout 分支或提交,可顺带新建分支 |
deleteBranch | 写 | 删除本地分支(默认安全删除) |
reset / cherryPick / revert | 写 | reset --soft/--mixed/--hard / cherry-pick 一个提交 / 用新提交 revert |
amend | 写 | 改写最新提交的信息 |
merge | 写 | 把指定分支并入当前分支(--no-ff / --ff-only / --squash / --no-commit;会提交时必须带信息) |
rebase | 写 | 把当前分支重放到指定分支上(可 --interactive / --autostash) |
mergeAbort / rebaseAbort / rebaseContinue | 写 | 解冲突期间的收尾:merge --abort / rebase --abort / rebase --continue |
submodule | 写 | 子模块 init / update / sync |
deleteFile | 写 | 把工作区里的一个文件从磁盘删除(file 须解析在仓库内,目录与 .git 内部一律拒绝;只删工作区,索引与提交不动) |
revealFile | 写 | 在系统文件管理器中显示该文件(Windows explorer /select, / macOS open -R;Linux xdg-open 打开所在目录),同样要求文件存在于工作区 |
参数与语义以 index.js 的 READ_OPERATIONS / WRITE_OPERATIONS 为准(两者合起来就是上表)。
path 的操作都经 resolveInside 解析(Windows 下大小写折叠),越界一律拒绝;deleteFile / revealFile 的 file 参数还要在仓库内二次围栏,并拒绝目录与 .git 内部路径。ctx.connection.fetch.register 注册,继承 API 桥的 Host/Origin 围栏与会话认证,不是裸 webserver 路由。git 一律 execFile + argv 数组,路径前保留 --;revealFile 打开文件管理器同样逐元素 argv、不开 shell(以独立进程拉起、不接管其输入输出 —— 继承 stdio 的拉起方式在 Windows 上会让资源管理器窗口根本不出现)。git 60 s 超时、32 MiB 输出上限;发现扫描 ≤ 8 层 / 64 个仓库 / 20 000 个目录项;diff 弹窗把差异完整渲染(不再有行数上限 —— 一次 diff 的体量由 32 MiB 抓取上限约束,而不是由渲染行数约束);提交历史每页 ≤ 200 条,更早的提交由 skip 续读,一次请求不承担整段历史;面板同时只保留一档已加载条数(默认 2 万,可继续抬),列表只渲染视口内的行 —— 可见边界是标签页内存,不是历史长度。IDEA 的 VCS Log 是同进程、读本地对象库:提交信息不经过任何网络或 JSON 边界(内置 JGit),提交图与元数据还索引在 IDE 的 system 目录里,后台分批加载、界面用虚拟化表格只 materialize 可见行,完整提交信息也只在选中时才取。
前两条这里做不到:浏览器半没有文件系统权限(文件与子进程按设计归 Host 半,页面只走一条认证路由),而把 .git 的字节交给页面也没用 —— pack 是 zlib + delta 链,还原成一条提交等于自己实现一遍 git 对象层。后两条照做:筛选即查询 + 分页、窗口化渲染、完整信息按需取,于是「一次请求多大」换成「一档内存多大」,面板能一路读到仓库的第 1 个提交,而标签页不会被整段历史撑爆。
git submodule status(无子模块的仓库上约 1 s)只在仓库声明了子模块时才执行;每提交的文件统计改为选中时才读。GIT_TERMINAL_PROMPT=0、GIT_OPTIONAL_LOCKS=0,失败以数据形式返回;合并与变基的提交信息一律由参数或 Git 已保存的 MERGE_MSG 提供(rebase / rebaseContinue 另带一次性 GIT_EDITOR=:),面板没有编辑器可回答 Git 的提示。state 顺带报告仓库是否停在某个操作中(inProgress:merge / rebase / cherry-pick / revert / bisect),标记经 git rev-parse --git-path 解析,链接工作树同样正确;合并与变基的冲突路径由 git status --porcelain 读出并写进错误信息。浏览器半没有打包器:DSH 把它当普通脚本加载,client.js 自己调 window.__ModuleLoader__.load({ id, factory }) 注册工厂 —— import 不被允许,DSH 的 require 也只认平台种子与清单里 inject 的包。官方给零构建包留的通道是包内分片:与浏览器入口同级、名字匹配 client.<名字>.js,自己注册成 { id: 'GitPanel', chunk: 'client.panel.js', factory },再由入口 await require.async('./client.panel.js') 取回。
apply 是 async 的,且必须在注册任何席位之前把分片取回来。client.js 的 shared 接线表单向接出去。immutable 缓存。改入口文件本身则会换掉全部版本。dsh-GitPanel/ # 插件包 GitPanel(仓库根即包根)
├─ package.json # 清单:导出映射 / client.inject / files 白名单
├─ cordis.patch.yml # Host 半的插入补丁
├─ index.js # Host 半:发现 / git 子进程 / 解析器 / 路由 / 配置图
├─ icon.svg # 面板图标
├─ src/ # 浏览器半(分片必须与 client.js 同级)
│ ├─ client.js # 入口:词汇表 / shared 接线 / Host 传输 / apply
│ ├─ client.panel.js # 整页面板:状态、Host 调用、工具栏、三栏组装
│ ├─ client.branches.js # 左栏:分支、远程、子模块、工作树与提交框
│ ├─ client.history.js # 中栏(历史)与右栏(提交信息与文件)
│ ├─ client.rows.js # 变更 / 分支 / 提交行,右键菜单,对话框
│ ├─ client.diff.js # unified diff 解析、改动导航、并排视图与弹层
│ ├─ client.settings.js # 插件页面上的配置卡片
│ ├─ client.preview-fix.js # 资源地址修复及其地址读取
│ ├─ client.mermaid.js # Markdown 文档渲染器(Mermaid 图)
│ ├─ client.mermaid-lib.js # Mermaid 11.17.2 内置库(上游单文件构建,离线渲染)
│ ├─ client.sidebar.js # 右侧边栏的门与标题
│ ├─ client.i18n.js # 中英词典
│ └─ client.style.js # 设计令牌样式表与 Git 图标路径
├─ locale/{en,zh}.json # 插件卡片(插件列表与清单)的包名与描述
├─ test/ # 四个无框架 Node 测试 + 分片契约替身
└─ README.md / LICENSE / AGENTS.md
无需安装依赖,Node(≥ 18)与 git 即可;四个测试互相独立,全部通过时退出码 0:
node test/host.e2e.mjs # Host 半端到端:临时工作区 + 真实 git + 裸仓库远程,覆盖全部操作与安全拒绝
node test/client.smoke.mjs # 浏览器半冒烟:真实 ModuleLoader 契约 + React 替身 + 假 fetch,驱动面板全部状态
node test/preview-fix.mjs # 文件预览修复:Node 契约 + 真实无头 Chromium 复现(无浏览器时该半 skip)
node test/preview.mjs # 视觉稿:输出 preview/git-panel.html(明暗双主题共 6 幅)
浏览器半在每次页面加载时重新取用,Host 半只在 DSH 进程启动时导入一次:长跑的 dsh web 上刷新页面只更新面板 UI,Host 侧的新操作要重启进程(或让 dsh-hmr 监听本包目录)才生效;面板会识别出旧 Host 并显示橙色横幅。改动前请读 AGENTS.md(架构不变量、代码风格、测试要求与红线)。
MIT © 2026 relaxyabc