packages/dsh-task-board
zhu1090093659/dsh-web侧边栏多列任务看板:卡片交给真实 DSH 智能体会话执行,支持 cron 定时(Host 侧到点执行,关浏览器也生效)。
接管对话流的 edit/write 行:外壳与折叠沿用官方原生行,展开后每个文件一张代码卡片——文件类型图标、增删统计、块级折叠、高亮/±差异切换、语法高亮统一 diff(行号为文件真实行号),改动两侧多显示可调的上下文行。另带 patch 工具(测试版,默认关):一次调用把多处零散修改/多文件补丁原子化应用(*** Begin Patch 信封,全有或全无)。已归档并冻结在固定版本:作者不再为它适配新版 DSH。
已归档(2026-10-20),不再维护。 本插件已从
plugins/移入archive/,源码与lib/构建产物仅作历史参考;安装路径为./archive/file-change-viewer,后续不再适配新版 DSH;文中「更新」节仅作历史保留。
DeepSeek Harness(DSH)文件修改查看器:接管对话流里的 edit / write 行——行的外壳与折叠行为完全沿用官方原生行(和「思考 / 读取」行同一套样式,默认同样折叠,也可在设置里改成默认展开),展开后每个文件块是一张代码卡片:卡头是「文件类型图标 + 文件名 + 增删统计(图标 + 绿增红删)」并带该块自己的折叠箭头与「高亮 / ± 差异」切换(各带图标),卡内是带语法高亮的统一 diff(新增行绿底、删除行红底、行号是文件里的真实行号,删除行不占号)。每张卡片还会在改动两侧多显示几行没受影响的上下文(默认 3 行,设置里可调 0 / 3 / 5 / 8)——这几行取自文件当前内容,模型只圈 1 行上下文时也看得清改动落在哪。另外它还带一个 patch 工具(测试版,默认关,设置里可开):一次调用把多处零散修改 / 多个文件的补丁应用下去(*** Begin Patch 信封,全有或全无),批量改文件从此不必退化成 bash + sed/python——那种改法在界面上只留一行 Bash,看不见任何 diff,还得整份重写或逐个替换。
全局默认值在设置 → 左侧导航「File Change View」(本插件自己的设置节)里配置——展示方式是两张并排的单选卡,卡里直接画出两种视图的真实效果,另有上下文行数与默认展开开关;这些值持久化在 profile 条目 dshp-file-change-viewer 的 config:(0.1.7 契约:条目导出 Config schema + volatile 字段,设置页经 settings.update 写入即热更新)。会话页头右侧还多了两个只作用于当前会话的快捷开关(一键展开 / 收起本会话的所有文件改动、切换差异视图),它们不改上面的全局默认,也不写持久化配置。
内置的文件修改行是默认折叠且只有 -/+ 行色的:DSH 用 DisclosureRow 承载折叠语义,折叠态下 DiffBlock 根本不挂载,所以「用 CSS 把行撑开」走不通。本插件按官方扩展点注册 tool.call.toolview 的同名 key(官方文档:a key the shipped composition already covers is replaced, not shared),用同一批官方 primitives 重新组装这一行。
设计原则:只改渲染,不碰数据;能不改的就不改。不注册工具、不改任何文件——内容全部来自官方
dsh-tool-fs已给出的meta.diffs与调用参数,行的几何全部来自官方DisclosureRow默认样式,配色全部来自官方 token。Host 半只多做两件事:把自己的 settings 命名空间注册进设置系统,并提供两条同源/ext路由供设置节读写这两项偏好。
▸ 编辑 · src/client/index.ts +58 -43 ← 原生行:默认折叠(可配成默认展开),与读取/思考行一致
╭──────────────────────────────────────────────────╮
│ ▾ 📄 src/client/index.ts ⊕37 ⊖26 [⌘高亮][⑂± 差异] │ ← 卡头:类型图标 + 文件名 + 图标化统计 + 两块控件
│ 1 const RAW_MAX_CHARS = 4000; │
│ 2 │
│ 3 /** 生成「按行上底色」的 CSS 规则。 */ │
│ - * 官方 CodeBlock 没有逐行样式的 API, │ ← 红底 = 删除
│ 4 + * 官方 CodeBlock 在行号模式下… │ ← 绿底 = 新增
╰──────────────────────────────────────────────────╯
DisclosureRow 默认样式(24px 行高、16px 图标位、13px 标色、hover 图标↔箭头交叉淡出),本插件只把官方 ToolRow 里那几条工具专属样式(分隔点、路径链接、摘要、统计、Inspect 药丸)镜像过来——不自造行外壳、不覆盖背景。+58 用 --dsw-alias-state-success-primary,-43 用 --dsw-alias-state-error-primary(官方行内是单一灰色,这里按要求分色)。FileTypeIcon(按扩展名判类型)+ 文件名 + 图标化增删统计(IconPlusOutline16 / 一图元减号 SVG,绿增红删)+ 该块自己的折叠箭头 + 该块自己的「高亮 / ± 差异」(IconCodeOutline16 / IconBranchOutline16)。官方代码块自带的语言标签栏与复制按钮继续隐藏(不需要)。IconEditOutline16(与内置文件修改行同一个)。ToolRow 同做法),不显示路径与统计。patch 工具(批量 / 零散修改,测试版 · 默认关)它是测试版能力,默认不注册。 到 设置 → File Change View → 工具 打开「启用 patch 工具(测试版)」, 模型才会看到这个工具;关掉即刻消失(正在进行的那次调用不受影响)。开关值是条目
config:里的patchTool字段(volatile),也可以直接用 composition 的config: { patchTool: true }在部署层打开—— 设置页一开一关走settings.update→loader/volatile-update,不需要重启dsh web;手工编辑条目 config 则重启生效。
官方的文件工具是 read / write / edit:write 要重发整份文件,edit 一次只能替换一处。所以「一个文件里改 8 处、顺带再改 3 个文件」这种活儿,agent 往往会退化成 bash + sed/python 一把梭——改动藏在命令里:界面上只留一行 Bash,没有 diff、也没法整体回滚。本插件补上这个缺口:一次调用改多处 / 多文件,而不用重写任何一份文件(省 token、不会误覆盖整份文件、每一段改动都进差异卡片可审查)。
补丁格式是 Codex / opencode apply_patch 的 *** Begin Patch 信封,不是 patch(1) 的 unified diff:
patch(patch: "*** Begin Patch
*** Update File: src/a.ts
@@
-const a = 1;
+const a = 11;
*** Add File: notes/t.md
+| a | b |
*** End Patch")
| 性质 | 说明 |
|---|---|
| 格式 | *** Begin Patch / *** End Patch 信封,段落是 *** Add File: 与 *** Update File:;一次可带多个文件、每个文件多个 @@ 片段 |
| 定位 | 不写行号:片段靠自己的上下文内容定位,五级回退(精确 → 去尾空格 → 两端 trim → Unicode 标点归一 → 忽略一切空白但要求全文件唯一);@@ 锚点 用于消歧——锚点写成本段第一行也认;*** End of File 钉在文件尾 |
| 追加 | 只有 + 行的片段(没有上下文、没有删除)追加到文件末尾——这是上游行为,照抄 |
| 新建文件 | *** Add File: <路径> + 整段 + 行,父目录自动创建 |
| 原子性 | 全有或全无:先把每个文件读进来、在内存里全部应用成功才落盘;任一片段对不上就整体失败,绝不写一半 |
| 后端 | 走 ctx.fs:同样吃沙箱模式、fs/write-intent 观察策略、fs/observed 记账;不绕过 seam |
| 行尾/BOM | CRLF 文件归一后应用、写回时恢复整份 CRLF;带 UTF-8 BOM 的文件改完 BOM 还在 |
| 范围 | 只做「新建 / 修改」:*** Delete File: 与 *** Move to: 解析层认识、执行层明确拒绝(见下) |
| 渲染 | 结果元数据带 diffs,于是这张调用也用它自己的差异卡片显示(按片段各一张卡、统计只算真正变化的行、行号是落盘时算出的真实行号) |
模型第一次调用 patch 失败、第二次才改对,是最常见的浪费。真实会话里抓到的两类根因,以及对应的处理:
| 现象 | 根因 | 现在怎么处理 |
|---|---|---|
写了 @@ Line 1: Hello DSH,下一行又是 -Line 1: Hello DSH,报「找不到这一段的上下文」 | 模型把 @@ 当小节标题用(像 unified diff 的 hunk 头),而它是锚点;旧实现匹配到锚点后从下一行开始找,于是要改的那一行被锚点本身"吃掉" | 锚点命中后从锚点自身开始找:锚点是上方一行 → 继续往下找;锚点就是改动行 → 当场命中。工具描述也把「锚点是文件里真实存在的一行、不是标题」写明白了 |
凭记忆写的那一行与文件只差几个空格(.dashing{ vs .dashing {),报「找不到」 | 前四级回退吃缩进与行尾空白,**吃不了「有没有空格 / 空格在哪」**的差异 | 加第五级:忽略一切空白比较,但配唯一性安全阀——全文件只有一处能对上才采用,两处以上就不猜(宁可失败,也不改错地方) |
| 一个片段里堆了多处删除,但它们不相邻 | 片段语义要求旧内容在文件里连续;模型把它当成「一次批量」 | 报错时点破这一点,并教它「每处单独写一个 @@ 片段 / 把中间那些没改的行也写成上下文行」(判定是精确的:按顺序都能找到、但彼此不连续) |
只发了 *** Begin Patch,里面是空的 | 生成被截断 / 模型先写信封再想内容 | 报错分开说:空补丁(教它段落怎么写)、缺 *** End Patch(提示可能被截断)、整段写成 unified diff(给出可照抄的正确格式,并提醒小改用 edit 更省事) |
| 重试时又发了一遍同样的改动 | 上一次其实已经改好了 | 检测「新内容已经在文件里、旧内容不在」,提示「这段改动看起来已经应用过,确认后把这一段从补丁里去掉」(只是提示,不动磁盘) |
报错不再只说「找不到」:applyChunksToText 失败时会在文件里找与模型写的那一行最像的真实行,把行号、原文与「差在哪」一起回给模型:
patch 无法应用:在 /path/to/s.css 里找不到这一段的上下文:
.dashing{animation:dashFlash .18s ease-out;}
文件第 64 行最像(相似度 94%),实际是:
.dashing { animation: dashFlash .18s ease-out; }
(只有空格 / 制表符的差别(有没有、在哪里))
把这一行**原样**(连同缩进)抄进 - 行再试一次;改动上下各留一行上下文会更稳。
文件没有被修改。
三段信息各有用途:原文回显让它能对照;文件里的真实行让它照抄就能过;差异原因(缩进 / 行内空白 / 标点 / 第几个字符起不同)让它知道下次别再凭记忆写。相似度不够高时改成明确提示「这段文本在文件里不存在,先 read 再写」。诊断只在失败路径上跑,并且有行数上限(2 万行以上不扫)。
为什么不做删除与改名:这两件事本来就有官方出口——bash 的 rm / mv(git 跟踪的用 git rm / git mv),而且与 patch 吃同一份沙箱策略,越界一样会被拒。反过来,把它们塞进 patch 的收益只是省一次调用,代价却很实:
patch 的「全有或全无」只覆盖验证阶段——落盘仍是逐个执行的,第 3 个文件失败时前两个已经改了、删掉的也回不来;rm 与 Windows 的 pwsh 语法不同),等于把一段在 CI 上跑不到的代码交付出去。所以模型真写出这两段时,patch 会在读任何文件、问任何审批之前整体拒绝,并给出替代做法:
patch 只做「新建 / 修改」,不执行删除与改名,这份补丁里有:
*** Delete File: obsolete.txt
删除请用 bash 的 `rm`(git 跟踪的用 `git rm`)、改名请用 `mv`(跟踪的用 `git mv`);两者与 patch 吃同一份沙箱策略,越界一样会被拒。把这几段从补丁里去掉、只留增改,patch 就能继续。
客户端仍然把「它本来打算做的改名」画出来(卡头 旧 → 新)并打 rejected 角标——用户要看到的是模型想干什么。
什么时候用哪个:一处小改动 →
edit;新建 / 整份重写 →write;多处零散或跨文件 →patch;删除 / 改名 / 构建 / 测试 / git →bash。
| 方式 | 组件 | 形态 |
|---|---|---|
| 高亮(默认) | 官方 CodeBlock | 单个代码块里放完整统一 diff(LCS 结果):未变行只出现一次,删除行红底、新增行绿底,删行不占行号;代码原样(不加 - / + 标记) |
| ± 差异 | 官方 DiffBlock + 中性上下文行 | 逐行 -/+ 红绿文字(紧凑),超长中部折叠;自带复制按钮与 └ +A -B 页脚。喂给它的是语义变更(只含真正变化的行),所以它的 +A -B 与高亮视图、行头统计永远一致 |
设置页里这两种方式是两张并排的单选卡,每张卡下面用同一份渲染代码(diffView.ts,与工具行走的就是这一套)画一段真实样张(src/greet.ts 的一次 +2 -1 小改),选中即生效——挑的时候看到什么,会话里就是什么。卡片整块可点(样式里给样张加了 pointer-events:none,官方组件自带的复制按钮在样张里不会抢走点击)。
两个视图都会在改动两侧多显示几行没受影响的上下文(contextLines,默认 3 行)。这几行取自文件当前内容——Host 的 /locate 路由拿文件定位这段 hunk 时,顺手把它两侧各最多 8 行原文回给客户端(响应里只有行号与这十几行,整份文件不回传),客户端按偏好截。
为什么 ± 差异视图的上下文是「另外画的一行行中性文字」:官方
DiffBlock的行 kind 只有path | del | add | gap(别的 kind 直接 throw),画不了中性的上下文行——上下文一旦塞进它的diffs,就会变成绿色的+或红色的-,把没改的行说成改了。所以本插件把上下文单独渲染成中性行贴在它上下:上文 → DiffBlock(红绿改动) → 下文,拼起来正好是一份标准的 unified diff。官方 DiffBlock 进卡后还会自带三样冗余,被结构性清掉(选择器全走它自带的
data-diff根 + 子元素位置):body 首行那条完整路径(官方 buildRows 无条件插入,600 字重 + 56px 右留白,读起来像正文里横插的标题)、右上角复制按钮(卡头视图切换旁再浮一个)、块底└ +A -B · N 个文件footer(与卡头统计同口径重复报数)。卡头已有文件名与统计,这三样只会在上下文与改动之间制造噪声。上下文行虽然不是官方组件画的,但读起来必须像同一块代码,所以做了三件事对齐:① 排版逐像素镜像官方——同一字体 token(
--dsw-font-markdown-code-block,实测 11px/19px + code 字族)、行盒与官方.line同为 22px 最小盒高;② 衔接处零缝隙——有上文时把 DiffBlock 改动区自己的顶部内边距归零(+ .ctx + .diff > div:first-of-type),上下文行与 ± 行逐像素相接;③ 横向滚动收敛到一处——外层diffWrap是整份 diff(上文 + 改动 + 下文)唯一的滚动容器:官方.body的滚动被结构性覆盖关掉(data-diff根下第一个 div,注意不能写:first-child——body 前面还有个复制按钮<button>),上下文行不自己滚。超长行因此只有一根滚动条、一条轴,不会出现「上下文一根条、改动另一根条、各滚各的」。高亮视图的行底色铺满整条滚动宽度:官方
.line是普通块盒,宽度只到code可视宽度;white-space:pre下超长文字以行内溢出延伸到盒外——直接在.line上叠底色的话,往右滚,红绿条会在行中间断开(文字还在、底色没了)。所以行盒被显式撑宽:code > :global(.line) { box-sizing:border-box; width:max-content; min-width:100% }——短行底色照旧铺满整卡,长行底色跟着文字走到行尾。border-box必须显式写:行号列是.line自己的padding-inline-start,默认 content-box 下min-width:100%只约束内容宽,行号内边距叠加在外,背景会凭空多出约一个行号列宽。.line是官方全局类名(官方自己的 CSS Module 里就是:global(.line)),所以这里必须写:global形态,构建产物里它是合法的裸类名(check-client.mjs的哈希护卫已为它豁免)。行号列从卡片左边框让出一档 14px:官方把行号画在行盒的最左缘(绝对定位的
::before落在inset-inline-start:0),而上面为了让底色铺满整卡把pre的横向内边距归零了——官方默认那 16px 一拿掉,行号列就紧贴卡片边框。行号列宽是max(2, 位数)ch(官方按代码行数内联写在块根上),位数一多(3–4 位)数字会把整列填满,等于压在边框上。所以行盒补一条padding-inline-start: calc(var(--dsl-code-block-line-number-width) + 26px)(26px = 新的 14px 档 + 原有的 12px 行号/正文间隙),行号本身用.line::before { inset-inline-start: 14px }同步右移。14px 不是随手取的数:它正是本插件另一套版式那一档(.ctx的padding:0 14px、官方DiffBlock的.body{padding:12px 14px}),于是高亮与 ± 两个视图的行号列、正文列落在同一个 x 上(无头 Chrome 像素实测:两视图的行号 ink 都自卡片左缘 +15px 起、正文 ink 同一 x)。设置页样张的.pvCode复刻同两条规则,样张与真用起来一致。± 差异视图:上下文行带真实行号,改动行的行号列放
+/-。官方DiffBlock的行没有编号机制,上下文行的行号由本插件用 CSS 计数器(dshp-fcv-num)补上——上文从「上文首行的真实行号」起数,下文的起点 = 上文首行 + 上文行数 + 语义新增行数(删除行不占号,与高亮视图同口径);起点由 JSX 内联成.ctx容器上的counter-reset,定位不到(locate没回结果)时不带号、绝不编数。行号列的宽窄(3ch + 12px 间隙)、颜色与淡化与高亮视图的官方行号一致。改动行不编号,而是把
+/-画在同一列里:diffSignRules生成的动态 CSS 给 del / add 行让出与上下文行号同一列(padding-inline-start: calc(3ch + 12px)),并把官方行内的-/+前缀压掉(::before{content:none}),改在自己的::after里画符号(del 红 / add 绿)——于是「上下文行号 / 改动符号 / 正文」三列严格对齐(无头 Chrome 实测:三行正文左缘都是同一个 x)。这里还有一条必需的配套规则:改动行自己得是那个::after的包含块(.diff[data-diff] > div > div { position:relative })——官方行是 static,不定位的话inset-inline-start:0会向上找到官方块根(整张卡)的左缘,+/-就比上下文行号左出一档、还紧贴卡片边框(行号列与符号列错开 14px)。不这样做的话,官方的行内前缀会把改动行的正文多推两个字符,上下文行与改动行的正文就错开了。区间用div:nth-of-type而非nth-child:官方块超长折叠时行间会插一个<button>(展开其余行),:nth-of-type按元素类型计数、button 不占 div 的位,索引与行序严格一致;已隐藏的 path 行仍占 div 索引(display 不影响选择器),所以区间统一 +1 偏移。设置页的样张不传 hunk 类名,所以样张里的改动行保持官方原样(行内前缀、不与上下文对齐)。
四项设置(展示方式、上下文行数、编辑 / 写入是否默认展开、patch 工具开关)里,前三项都有两个入口,第四项只有一个:
| 层 | 入口 | 落盘 | 作用范围 |
|---|---|---|---|
| 单块自己的临时点击 | 卡片头上的折叠箭头 / 「高亮 · ± 差异」两个药丸 | 不落盘 | 那一个文件块 |
| 会话级覆盖 | 会话页头右侧的两个快捷开关 | 不落盘 | 当前会话的所有文件改动 |
| 全局偏好 | 设置 → File Change View(单选卡 / 下拉 / 开关) | 条目 config: | 所有会话的默认值 |
/ext/dshp-file-change-viewer/config 写进 profile 条目 dshp-file-change-viewer 的 config:(Host 调 settings.update),保存成功 / 失败都在设置节里如实反馈。会话标题栏右侧(官方「在应用中打开」、会话日志下载的同一排)多出一组胶囊按钮:
[ ⌄ 收起改动 ] | [ </> 高亮 ] ↺ ← 当前会话已被临时改动(↺ = 恢复跟随偏好)
[ › 展开改动 ] | [ ⑂ ± 差异 ] ← 跟随设置页里的全局默认
src/client/session.ts):一个 /config 请求都不发,持久化配置纹丝不动,换一个会话立刻回到全局默认;rev,行组件发现覆盖换版就作废自己那份临时状态)。改完之后你再单独点某一行 / 某一块,仍然由你说了算。「全局偏好里的默认展开」只影响之后新渲染的编辑 / 写入操作(已经在会话里的行保持原样,不会被开关抽走或撑开);想对当前这个会话立刻生效,用页头那两个开关。行内不再放全局切换按钮——逐行记忆只会让界面自相矛盾。
git pull,旧版 DSH 可 checkout tag# 1. 克隆 monorepo(lib/ 构建产物已提交,clone 下来就能用,无需 build)
git clone git@github.com:Yinxe/deepseek-harness-plugins.git
cd deepseek-harness-plugins
pnpm install
# 2. 本地安装到 profile(路径按你执行命令时的 cwd 解析)
dsh plugin --profile web add ./archive/file-change-viewer
# 3. 重启生效
dsh web
git pull + dsh web(最快——lib 已提交,未改 src 无需 build);兼容旧版 DSH:按 compat.json 的 tag git checkout <tag> 后重跑上面的 add。dsh plugin 会把包写进 profile 的 dsh.profile.bundles —— 无需手动改配置文件。
改源码后:src/client 改完跑 pnpm --filter @dshp/file-change-viewer build 重新打出 lib/host.js + lib/client.js,然后 dsh web 重启(client 半强刷页面即可)。
验证:pnpm --filter @dshp/file-change-viewer test 会跑 node --check 两份产物 + Host 冒烟(scripts/check-host.mjs,152 项:命名空间与 Config 导出契约、configure({ auto: false })、补丁消毒、/ext 三条路由、非法补丁不落库、patch 工具注册契约与动态开关(默认关 / 开了就注册 / 关了立刻反注册,由 loader/volatile-update 驱动)、全有或全无、写前预检、删除 / 改名拒绝、BOM 与沙箱提权、locate 回行号 + 两侧上下文且同一文件只读一次)+ patch 纯函数(scripts/check-patch.mjs,74 项:多文件 / 多片段 / 五级模糊定位(含唯一性安全阀)/ End of File 锚点 / 行尾与 BOM / 现场回归(@@ 当标签用、行内空白差异、多处删除不相邻)/ 信封诊断(空补丁 / 缺收尾 / 写成 unified diff)/ 失败诊断(最近的真实行 + 差异原因 + 已应用提示))+ Client 无头渲染断言(scripts/check-client.mjs,207 项,含一层迷你 React,能验证「偏好实时作用于行 / 换调用复用实例 / 会话页头开关只改当前会话」这类带生命周期的行为);界面上让 AI 改任意一个文件,展开「编辑」行后每个文件块应是带语法高亮的卡片、+A -B 与路径链接正常,且统计与 ± 视图只报真正变化的行数;在设置 → 「File Change View」里改一次偏好,profile 条目 dshp-file-change-viewer 的 config: 应立即出现对应字段,页头那两个开关则不该在配置里留下任何东西。
一键 AI 安装:把下面这段发给你的 DSH AI 即可:
帮我安装文件修改查看器插件(monorepo Yinxe/deepseek-harness-plugins,子目录 archive/file-change-viewer,包名 @dshp/file-change-viewer):
1. git clone monorepo 并 pnpm install
2. dsh plugin --profile web add ./archive/file-change-viewer
3. dsh web 重启,然后改一个文件,确认 edit / write 卡片默认就是展开的、代码有语法高亮
4. 如果这个会话里还有动态注册的同名插件(cordis_define 定义的,@ 引用能列出),用 cordis_undefine 卸掉,避免两份注册互相顶替
latest 滚动版(跟随 main 最新构建;滚动更新 = 重跑同一条 add 命令):
dsh plugin --profile web add \
https://github.com/Yinxe/deepseek-harness-plugins/releases/download/latest/dshp-file-change-viewer-latest.tgz
dsh web # 重启生效
main 永远跟随最新 DSH。老版本 DSH 用户装静态历史版本:按 compat.json 记录的兼容 tag,到 Releases 找对应版本 Release,资产名 = dshp-file-change-viewer-<tag>.tgz:
dsh plugin --profile web add \
https://github.com/Yinxe/deepseek-harness-plugins/releases/download/<tag>/dshp-file-change-viewer-<tag>.tgz
dsh web # 重启生效
| 安装方式 | 更新命令 | 说明 |
|---|---|---|
| 方式一 clone(推荐) | 仓库内 git pull + dsh web | 最快;未改 src 免 build;旧版 DSH 用 git checkout <tag> |
| 方式二 Release | 重跑同一条 add 命令 + dsh web | URL 直装对 update 免疫 |
| 方式三 git 依赖 | dsh plugin --profile web update @dshp/file-change-viewer + dsh web | 一条命令;git 解析约 35s/插件(实测) |
| 部分 | 内容 |
|---|---|
Host(src/host/ → lib/host.js) | name / inject: ['webServer'] / Config 导出 / apply:① 上交条目 Config schema(三项偏好全是 volatile 字段,权威存储在 profile 条目 config:)+ settings.configure({ auto: false }) 关掉自动设置页;② 三条同源路由 /ext/dshp-file-change-viewer/{state,config,locate} 供设置节读写与行号定位;③ patch 工具(测试版,默认关:patchTool 一开一关就注册 / 反注册,热更新走 loader/volatile-update,注册面在 ctx.inject(['tools','fs']) 的注入作用域里)。没有事件;这一行同时是 DSH 从 Host Loader 的条目里扫描 dsh.client 声明来发现并下发客户端 bundle 的入口——没有它,lib/client.js 不会被加载(详见 src/host/index.ts 头注释)。 |
Client(src/client/ → lib/client.js) | 用 priority: -1 注册 tool.call.toolview 的 edit / write / str_replace_editor 三个 key(影子化官方内置行;同 key 同 priority 会抛错,所以必须比内置的 0 更小),带 locale: 'conversation' 取文案;行外壳用官方 DisclosureRow 默认样式(不自造卡片),展开后每个 hunk 一个自己的折叠块,块内是纯高亮代码(官方 CodeBlock 去掉语言栏/复制/外壳);diff.ts 推导模型与 LCS 统一 diff,lang.ts 映射 26 个官方语法 id,行内没内容时给等待 / 截断兜底文案;diff.ts 把每个 hunk 跑一次 LCS(raw / rows / changed 三种口径),统计与 ± 视图都用语义变更;prefs.ts 管全局偏好的读写往返与订阅(设置节与工具行共用同一个 store),FileChangeViewerSection.ts 是设置 → 「File Change View」这一节的内容(展示方式两张样张单选卡走 viewCards.ts),api.ts 封装两条 /ext 路由;diffView.ts 把「高亮 / ± 差异」两种视图的渲染收口(工具行与设置页样张共用同一份代码),SessionControls.ts + session.ts 是会话页头那两个快捷开关与它们的内存级会话覆盖。 |
| 同源路由 | GET /ext/dshp-file-change-viewer/state(当前生效偏好)/ POST …/config(保存补丁 → settings.update)/ POST …/locate(真实行号定位,只回整数、不回文件内容)。业务错误一律 200 + ok:false,跨站 / 方法错误 4xx。 |
| 工具 | 注册一个 patch 工具(*** Begin Patch 信封,一次可改多处 / 多文件,全有或全无):经 ctx.fs 读写,吃沙箱模式与观察策略,结果元数据带 diffs 交给卡片渲染;删除 / 改名明确拒绝并指路 bash。 |
| 场景