dsh-vscode-mode 0.21.4 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -163,6 +163,50 @@ Host 优先采集官方 `workspaceChanges` 的轮次净差异;旧版逐工具
163
163
  > v0.20.1 已完成重启后真实 GUI 的文件/块级审查与安全拒绝验收。Keep All / Undo All
164
164
  > 已接入全部分页;批量 Undo 不是跨文件原子事务,遇冲突停止,已完成项不会自动恢复。
165
165
 
166
+ ### 6.1 累计基线待审(对齐 dsh-diff-approval)
167
+
168
+ 在保留现有面板的前提下,把待审内核换成**累计基线投影**:行为参考
169
+ [dsh-diff-approval](https://github.com/9087/dsh-diff-approval)(基线
170
+ `a661a7a31583057c8ba8bbeb73721a9b3ee0c57e`,MIT,Copyright (c) 2026 Wu Zhiwei),
171
+ 只借用语义与内核,不引入它的界面资产(Markdown 预览、并排视图、字体子集、悬浮卡片)。
172
+
173
+ - **一条目一文件**:每路径只有一条待审条目,`oldText` 是最早基线、`newText` 是最新内容。
174
+ 同文件多次写入按更新时间折叠,基线取较早来源、内容取较晚来源,`sessionIds` 取并集;
175
+ 于是 A→B→C 显示 A→C,A→B→A 折叠归零(严格字节相等,不做 BOM/换行归一化)。
176
+ - **创建身份**:`earlierVersion: 'none'` 表示基线是"文件不存在"。写入推进基线即结束创建
177
+ 身份(转为 `'file'`),此后再 Revert 是写回而不是删除。
178
+ - **判定粒度**:块、行区间、文件、全部。**Keep 只推进投影、零写盘**;Revert 才写回文件,
179
+ 新建条目整条 Revert 等于删除该文件。块身份由当前差异现场推导,不依赖落库状态。
180
+ - **判定安全**:Revert 走 `replaceIfVersion` 版本守卫并写后读回;内容与投影不一致、
181
+ 条目已解决、行区间歧义时拒绝并给出原因码(`revision-mismatch` / `diverged` /
182
+ `settled` / `unknown-block` / `no-block` / `ambiguous-block`)。
183
+ - **撤销/重做**:判定入 `pending_history`(游标模型,撤销后重做回到同一状态),
184
+ 幂等由 `operationId` 保证;决策栈跨重启保留(上游只在面板打开期间保留)。
185
+ - **差异来源**:`edit` / `write` 结果自带 before/after 直接采集;`str_replace_editor`
186
+ 结果只有成功消息,靠 `fs/edit-intent`、`fs/write-intent` 在写前取预像、`fs/observed`
187
+ 在写后取终像配对。可另外从版本库导入基线改动(git / svn / Perforce)。
188
+ - **外部改动**:已入列路径被外部改写时,新内容被吸收为最新待审内容并标记分歧,
189
+ 基线保持不动;文件缺失/读不到时条目转为 `missing`,不再参与写回。
190
+ - **判定入口**:差异直接画在编辑器里,块级 Keep/Undo 用行内药丸;文件级 Keep/Undo
191
+ 在差异栏上。详情面板与批注(含「让 AI 应答」)已移除——判定与保持/撤销只走这两处。
192
+
193
+ 与上游的有意偏差:
194
+
195
+ | 项 | 上游 | 本插件 | 原因 |
196
+ |---|---|---|---|
197
+ | 条目键 | 全局裸路径 | 工作区 + 路径 | 多工作区同名文件不能互相覆盖 |
198
+ | 文本存储 | 内存/全量 JSON | SQLite,有大小上限 | 大文件不拖垮 Host,超大只按时间戳比较 |
199
+ | Revert 写回 | 无条件写 | 版本守卫 + 写后读回 | provider 不具备跨进程原子 CAS |
200
+ | 撤销栈 | 仅面板打开期间 | 持久化到 SQLite | 重启后仍可撤销上次决定 |
201
+ | 界面 | 自带整套 UI | 复用现有面板 | 保留本插件视觉与交互 |
202
+ | Perforce | 支持 | 支持,但只有假 shell 单测 | 本机无 p4 客户端,无法真机验证 |
203
+ | 新建文件 Revert | 删除文件 | 删除文件(直接 `unlink`) | fs 抽象没有删除动作,已限制在工作区内 |
204
+
205
+ 数据与迁移:新增 `pending_*` 表,`SCHEMA_VERSION` 递增并用 `PRAGMA table_info` 判缺列后
206
+ `ALTER TABLE`,既有 `review_*` 事实表一概不动;首次改动前写 `.before-pending.bak` 备份;
207
+ 路径被待审投影接管后,旧 sidecar 写入口(`edrv.accept` / `edrv.reject` / `edrv.decideBatch`
208
+ / `edrv.missingDecide` 等)直接拒绝,避免双权威。
209
+
166
210
  ### 7. Monaco 离线分发
167
211
 
168
212
  `assets/vendor/monaco` AMD 构建随包发布,经 `/edrv/vendor/*` 前缀路由全离线可用。
@@ -477,7 +521,8 @@ DSH 每次启动监听随机端口,`http://127.0.0.1:<port>` 就是浏览器 o
477
521
 
478
522
  官方 `dsh plugin` 方式,三选一。下面以 `web` profile 为例;Desktop 或其他命名
479
523
  profile 请替换为实际目标,不要把示例目录当成当前运行 profile。升级前可阅读
480
- **v0.21.4** 的任务发送结果分类与诚实草稿降级:只有明确 accepted 才等待分析方案;unknown/submitted/cancelled 不另填草稿或重发,安全降级只填实际当前会话草稿并提示手动发送。旧 boolean 接口保留,legacy true 不等于受理。开发 link 客户端 rev458288ddb012 的真实公开入口与目标受理已验;完整 SVN 分析/方案投递因 `SetNamedSecurityInfoW failed (Win32 5)` 未完成,用户仅本批接受此缺口,不代表 Host 整体升级或当前实例已安装 npm 0.21.4。见 [0.21.4 发布说明](docs/release-0.21.4.md)。以下保留历史版本事实:
524
+ **v0.22.0** 的累计基线待审(非 UI 能力对齐 dsh-diff-approval):同一文件的多次编辑折叠成一条待审、基线保留最早内容,A→B→C 显示 A→C、A→B→A 自然归零;Keep 只推进基线不改磁盘,Revert 走版本守卫写回并读回校验,撤销/重做用游标模型跨重启保留;新增 `str_replace_editor` 预像配对、`fs/observed` 终像、已入列路径的外部改动吸收与缺失/不可读生命周期、git/svn/Perforce 基线导入,以及旧 sidecar 写入口对已接管路径的服务端拒绝。差异仍在编辑器内行内展示、块级 Keep/Undo 用药丸,文件级在差异栏;详情面板与批注(含「让 AI 应答」)本版移除。验收边界:p4 仅有假 shell 单测(本机无 Perforce 客户端),「让 AI 应答」随批注一并下线故不再验证;本版未在宿主上重启验收 GUI 全流程,见 [0.22.0 发布说明](docs/release-0.22.0.md)。以下保留历史版本事实:
525
+ **v0.21.4** 的任务发送结果分类与诚实草稿降级:只有明确 accepted 才等待分析方案;unknown/submitted/cancelled 不另填草稿或重发,安全降级只填实际当前会话草稿并提示手动发送。旧 boolean 接口保留,legacy true 不等于受理。开发 link 客户端 rev458288ddb012 的真实公开入口与目标受理已验;完整 SVN 分析/方案投递因 `SetNamedSecurityInfoW failed (Win32 5)` 未完成,用户仅本批接受此缺口,不代表 Host 整体升级或当前实例已安装 npm 0.21.4。见 [0.21.4 发布说明](docs/release-0.21.4.md)。保留以下历史版本事实:
481
526
  **v0.21.3** 的 MCP 初始化失败结果与安全收尾解耦:已进入 startup 的调用者在失败或截止时间到达时及时收到拒绝,不再等待旧资源收尾;旧 work/close 未确认完成前仍占冷启动名额、阻止同 key 重建,确认收尾后才开始冷却。首次偶发超时根因仍未知,排队和其他屏障仍可能无界;本版当前 Host/GUI 未部署未验证,用户仅本批接受官方实装隔离模块与 JS 套件替代验收,见 [0.21.3 发布说明](docs/release-0.21.3.md)。官方 bridge 隔离 fixture 同步迁移到当前公开 native 执行器及批调用契约,不代表生产执行器故障修复。以下为历史版本事实,不代表本版现场验收:
482
527
  **v0.21.2** 的新版 DSH 会话 Controller 与 MCP 公开工具目录适配:任务发送按目标会话持有生命周期,受理不明时不跨通道重发;文件范围引用的剪贴板与降级文本保留行区间;MCP 初始化失败增加脱敏持久化诊断。当前 Web Host 已完成真实 CodeGraph 调用,但首次连接偶发超时根因尚未证实,不宣称已修复,见 [0.21.2 发布说明](docs/release-0.21.2.md)。保留以下历史版本能力与验收边界:
483
528
  **v0.21.1** 修复缺失历史文件:按完整快照显示整文件删除,Keep 保留删除、Undo 安全恢复快照,预览只读且拒绝覆盖重新出现的文件;真实 GUI 验收缺口已明确保留,见 [0.21.1 发布说明](docs/release-0.21.1.md)。保留 **v0.21.0** 新增 XLSX 基础编辑与浏览器导出副本,原件不自动写回,高级应用通过官方 Host 打开;官方差异定位统一 BOM 与 LF/CRLF/混合行尾口径,并区分内容缺失和多处命中。定位修复不扩展 CRLF patch Undo 能力,目标官方记录 ↑/↓ 的真实点击验收仍保留缺口。保留 **v0.20.5** 跨 DSH 实例只读互通——桌面端与 Web 端各自运行独立宿主时,官方审查列表/详情与工作区只读接口改按持久化会话头的 cwd 解析,另一实例创建的会话也能只读打开官方差异栏,写入类接口仍要求本端活跃会话。保留 **v0.20.4** 统一两套差异栏外观——非官方记录栏角标由内部术语「侧车记录」改为「历史改动」,悬停说明 Keep 只标记已看过、不改写磁盘,Undo 逐项校验后写回,两者都不影响官方审查;官方元数据不可用时给出可读原因与重试按钮,不再静默降级;「会话没在当前端激活」由红色告警改为中性说明。保留 v0.20.3 修复官方差异全部无法定位时差异栏无提示、上下箭头静默禁用(显示「无可跳转」与失效原因,Keep 说明只标记已采纳、不改写磁盘,会话缺失等宿主错误改为可读提示)、v0.20.2 的 MCP 状态告警收敛与原生确认弹窗,以及 v0.20.1 的文件内差异展示、块级/文件级安全审查和跨分页批量操作。升级后重启 DSH 并刷新页面;持久化与回退边界见 [0.20.0 发布说明](docs/release-0.20.0.md);从 0.14.2 或更旧版本升级还须阅读
@@ -485,13 +530,13 @@ profile 请替换为实际目标,不要把示例目录当成当前运行 profi
485
530
 
486
531
  ```bash
487
532
  # ① Git 安装(clone + prepare 构建;推荐打固定 tag)
488
- dsh plugin --profile web add github:Lenonss/DSH_VsCodeMode#v0.21.4
533
+ dsh plugin --profile web add github:Lenonss/DSH_VsCodeMode#v0.22.0
489
534
 
490
535
  # ② npm 注册表(发布到 npm 后)
491
- dsh plugin --profile web add dsh-vscode-mode@0.21.4
536
+ dsh plugin --profile web add dsh-vscode-mode@0.22.0
492
537
 
493
538
  # ③ GitHub Release tgz 直装(Release asset 可用后)
494
- dsh plugin --profile web add https://github.com/Lenonss/DSH_VsCodeMode/releases/download/v0.21.4/dsh-vscode-mode-0.21.4.tgz
539
+ dsh plugin --profile web add https://github.com/Lenonss/DSH_VsCodeMode/releases/download/v0.22.0/dsh-vscode-mode-0.22.0.tgz
495
540
  ```
496
541
 
497
542
  > **DSH 版本政策(0.17.0 起)**:六项 DSH peer 仍统一声明为 `*`,另以
@@ -832,6 +877,7 @@ Keep All / Undo All 却是亮的。修复:单文件 Keep / Undo 覆盖冲突
832
877
  完整变更见 [GitHub Releases](https://github.com/Lenonss/DSH_VsCodeMode/releases)。
833
878
  近期关键版本:
834
879
 
880
+ - **v0.22.0**:新增累计基线待审——同文件多次编辑折叠成一条、基线保留最早内容(A→B→C 显示 A→C、A→B→A 归零);Keep 只推进基线不改磁盘,Revert 带版本守卫写回并读回,撤销/重做游标模型跨重启保留;补 `str_replace_editor` 预像配对、外部改动吸收与缺失生命周期、git/svn/Perforce 基线导入、旧入口服务端拒绝。差异仍在编辑器内行内展示,详情面板与批注(含「让 AI 应答」)移除。p4 仅假 shell 单测(本机无 Perforce 客户端),GUI 全流程未重启验收;发布不自动部署,升级后重启 DSH 并刷新页面。见 [发布说明](docs/release-0.22.0.md)。
835
881
  - **v0.21.4**:区分任务明确受理、拒绝、不明、仅提交与取消;只有 accepted 开始等待,unknown/submitted/cancelled 不另填草稿、重试或轮询。安全降级只填实际当前会话草稿并提示手动发送,缺详细 API 明确本次未自动发送;旧 boolean 兼容但 true 不等于受理。真实开发客户端公开按钮→目标受理已验,完整 SVN 分析/方案投递因 Win32 5 未验,用户仅本批接受;Host 未重启,不自动部署。见 [发布说明](docs/release-0.21.4.md)。
836
882
  - **v0.21.3**:MCP 初始化失败时分离调用者结果与安全收尾,已进入 startup 的调用者及时收到失败;旧 work/close 未完成仍占槽并阻止同 key 重建,清理失败不开放替代连接,收尾完成才开始冷却。不宣称解决首次偶发超时、gamma 排队或整体流程有界。当前 Host/GUI 未部署未验证,用户仅本批接受替代验收;不自动部署或重启。见 [发布说明](docs/release-0.21.3.md)。
837
883
  - **v0.21.2**:适配新版 DSH Session Controller 与 MCP 公开工具目录;固定目标任务发送持有会话直到完成或卸载,受理不明时不跨通道重发。范围文件引用的模型文本、剪贴板与纯文本降级保持一致。MCP 初始化失败记录阶段、耗时和脱敏错误链;迁移官方 native/PTC 执行器集成测试并增加可选真实 CodeGraph stdio 验证。当前 Web Host 真实工具调用已通过,首次连接偶发超时根因仍未证实;不自动部署或重启。见 [发布说明](docs/release-0.21.2.md)。
package/lib/client.js CHANGED
@@ -3749,6 +3749,10 @@ window.__ModuleLoader__.load({
3749
3749
  return reviewBlocks(diff).map(placeRegion);
3750
3750
  }
3751
3751
  //#endregion
3752
+ //#region src/shared/pendingReview.ts
3753
+ /** 待审条目在面板列表里的 id 前缀;客户端与 Host 共用这一份口径。 */
3754
+ const PENDING_PREFIX = "pending:";
3755
+ //#endregion
3752
3756
  //#region src/shared/diff.ts
3753
3757
  /** 统一取对应 hunk;callHunk 只用于旧记录缺失 hunks 时兼容。 */
3754
3758
  function preciseHunk(record, idx) {
@@ -4045,6 +4049,7 @@ window.__ModuleLoader__.load({
4045
4049
  }
4046
4050
  //#endregion
4047
4051
  //#region src/client/state/diffSummary.ts
4052
+ /** Source-aware metadata projection; official patches never become legacy hunks. @author ddj 2026年10月26号 */
4048
4053
  /** @public @author ddj 2026年10月26号 @param path Functional local or remote identity. @returns Comparison key without rewriting remote case. */
4049
4054
  function diffPathKey(path) {
4050
4055
  if (/^[a-z]+:\/\//i.test(path)) return path;
@@ -4056,13 +4061,46 @@ window.__ModuleLoader__.load({
4056
4061
  const key = diffPathKey(path);
4057
4062
  return !!page?.overview?.coveredPaths.some((candidate) => diffPathKey(candidate) === key);
4058
4063
  }
4059
- /** @public @author ddj 2026年10月26号 @param records Tool-call history. @param page Official file navigation, never only its first 50 rows. @returns Official-first file counts; legacy paths remain explicitly labelled. */
4064
+ /**
4065
+ * @author ddj 2026年10月30号
4066
+ * @description: 取出本页待审条目(id 带待审前缀)并折算成文件行。
4067
+ * @param page 审查页
4068
+ * @returns 待审文件行与它们覆盖的路径键
4069
+ */
4070
+ function pendingRowsOf(page) {
4071
+ const rows = [];
4072
+ const covered = /* @__PURE__ */ new Set();
4073
+ for (const item of page?.items ?? []) {
4074
+ if (!item.id.startsWith("pending:")) continue;
4075
+ const path = item.currentPath || item.path;
4076
+ covered.add(diffPathKey(path));
4077
+ rows.push({
4078
+ path,
4079
+ label: item.display || path,
4080
+ recs: [],
4081
+ pending: item.decision === "pending" ? 1 : 0,
4082
+ added: item.added,
4083
+ deleted: item.deleted,
4084
+ sourceKind: "pending"
4085
+ });
4086
+ }
4087
+ return {
4088
+ rows,
4089
+ covered
4090
+ };
4091
+ }
4092
+ /** @public @author ddj 2026年10月26号 @param records Tool-call history. @param page Official file navigation, never only its first 50 rows. @returns Pending first, then official; legacy paths remain explicitly labelled. */
4060
4093
  function diffSummary(records, page) {
4061
- const legacy = summarize(records.filter((record) => !officialPath(record.path, page)));
4062
- const files = (page?.overview?.files ?? []).map((file) => ({
4063
- ...file,
4064
- recs: []
4065
- }));
4094
+ const pending = pendingRowsOf(page);
4095
+ const legacy = summarize(records.filter((record) => !pending.covered.has(diffPathKey(record.path)) && !officialPath(record.path, page)));
4096
+ const files = [...pending.rows];
4097
+ for (const file of page?.overview?.files ?? []) {
4098
+ if (pending.covered.has(diffPathKey(file.path))) continue;
4099
+ files.push({
4100
+ ...file,
4101
+ recs: []
4102
+ });
4103
+ }
4066
4104
  for (const file of legacy.files) files.push({
4067
4105
  ...file,
4068
4106
  sourceKind: "legacy"
@@ -381321,11 +381359,6 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
381321
381359
  }
381322
381360
  //#endregion
381323
381361
  //#region src/client/ui/shared.ts
381324
- /**
381325
- * dsh-vscode-mode client — UI 共用小工具(按钮样式/状态文案/等宽字体)。
381326
- * 迁移自原 src/client/index.ts 的 btn/badgeOf/MONO,语义不改。
381327
- * 作者 ddj 2026-08-20
381328
- */
381329
381362
  /** 次级按钮内联样式(accept=true 绿色 / false 红色)。 */
381330
381363
  function btn(accept) {
381331
381364
  return {
@@ -381338,10 +381371,6 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
381338
381371
  color: accept ? "var(--dsw-alias-state-success-primary,#2e9e44)" : "var(--dsw-alias-state-error-primary,#d9534f)"
381339
381372
  };
381340
381373
  }
381341
- /** 状态中文文案。 */
381342
- function badgeOf(status) {
381343
- return status === ST.ACCEPTED ? "已采纳" : status === ST.REJECTED ? "已拒绝" : "待处理";
381344
- }
381345
381374
  /** 等宽字体常量(差异行渲染用)。 */
381346
381375
  const MONO = {
381347
381376
  fontFamily: "var(--ds-font-family-code, ui-monospace, monospace)",
@@ -381408,6 +381437,7 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
381408
381437
  if (!controller.signal.aborted) setError(String(cause));
381409
381438
  }
381410
381439
  }
381440
+ const gaps = page?.gaps ?? [];
381411
381441
  return h("div", { style: { marginTop: 8 } }, h("label", null, h("input", {
381412
381442
  type: "checkbox",
381413
381443
  checked: history,
@@ -381415,10 +381445,7 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
381415
381445
  setHistory(!history);
381416
381446
  setCursor(0);
381417
381447
  }
381418
- }), "显示已处理及被替换的历史"), page?.gaps.map((gap) => h("div", {
381419
- key: `${gap.sessionId}:${gap.seq}`,
381420
- role: "status"
381421
- }, `轮次 ${gap.turn} 采集不完整:${gap.error}`)), error ? h("div", { role: "alert" }, error) : null, !page ? h("p", { role: "status" }, "正在读取官方审查记录…") : null, page && !page.items.length ? h("p", null, history ? "暂无已落盘历史。" : "没有待处理的官方记录;可切换查看历史。") : null, h("div", { style: {
381448
+ }), "显示已处理及被替换的历史"), error ? h("div", { role: "alert" }, error) : null, !page ? h("p", { role: "status" }, "正在读取官方审查记录…") : null, page && !page.items.length ? h("p", null, history ? "暂无已落盘历史。" : "没有待处理的官方记录;可切换查看历史。") : null, h("div", { style: {
381422
381449
  maxHeight: 210,
381423
381450
  overflow: "auto"
381424
381451
  } }, page?.items.map((item) => h("button", {
@@ -381427,19 +381454,33 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
381427
381454
  onClick: () => {
381428
381455
  select(item);
381429
381456
  },
381457
+ title: item.currentPath || item.path,
381430
381458
  style: {
381431
381459
  display: "block",
381432
381460
  width: "100%",
381433
381461
  textAlign: "left",
381434
- padding: "6px 4px"
381462
+ padding: "6px 4px",
381463
+ whiteSpace: "nowrap",
381464
+ overflow: "hidden",
381465
+ textOverflow: "ellipsis"
381435
381466
  }
381436
- }, `轮次 ${item.turn} · ${item.display} +${item.added} −${item.deleted} · ${item.decision}${item.current ? "" : "(已被同轮新版本替换)"}`))), cursor > 0 ? h("button", {
381467
+ }, item.id.startsWith("pending:") ? `【待审】${item.display} +${item.added} −${item.deleted} · ${item.decision}${item.kind === "oversized" ? " · 过大" : ""}` : `轮次 ${item.turn} · ${item.display} +${item.added} −${item.deleted} · ${item.decision}${item.current ? "" : "(已被同轮新版本替换)"}`))), cursor > 0 ? h("button", {
381437
381468
  type: "button",
381438
381469
  onClick: () => setCursor(Math.max(0, cursor - 50))
381439
381470
  }, "上一页") : null, page?.nextCursor !== null && page?.nextCursor !== void 0 ? h("button", {
381440
381471
  type: "button",
381441
381472
  onClick: () => setCursor(page.nextCursor)
381442
- }, "下一页") : null, detail ? h(PatchDetail, {
381473
+ }, "下一页") : null, gaps.length ? h("details", {
381474
+ key: "gaps",
381475
+ style: { marginTop: 6 }
381476
+ }, h("summary", { style: {
381477
+ cursor: "pointer",
381478
+ fontSize: 11
381479
+ } }, `采集不完整 ${gaps.length} 条(诊断信息)`), gaps.map((gap) => h("div", {
381480
+ key: `${gap.sessionId}:${gap.seq}`,
381481
+ role: "status",
381482
+ style: { fontSize: 11 }
381483
+ }, `轮次 ${gap.turn} 采集不完整:${gap.error}`))) : null, detail ? h(PatchDetail, {
381443
381484
  key: detail.item.id,
381444
381485
  sessionId: props.sessionId,
381445
381486
  detail,
@@ -381654,7 +381695,7 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
381654
381695
  source
381655
381696
  ]);
381656
381697
  let body;
381657
- const legacyFiles = sum.pendingFiles.filter((file) => file.sourceKind !== "official");
381698
+ const legacyFiles = sum.pendingFiles.filter((file) => file.sourceKind === "legacy");
381658
381699
  if (source === "official") body = react$1.default.createElement(react$1.default.Fragment, null, react$1.default.createElement("div", { className: "edrv-search-empty" }, "官方轮次净差异 · 原始历史只读;编辑按钮打开当前文件,保存不代表已审。"), review.error ? react$1.default.createElement("div", { role: "alert" }, review.error) : null, react$1.default.createElement(ReviewLedger, {
381659
381700
  key: sessionId + ":" + tab,
381660
381701
  sessionId,
@@ -382511,11 +382552,9 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
382511
382552
  /** @public @author ddj 2026年10月27号 @param props Existing review callbacks and immutable record metadata. @returns Responsive review toolbar; no disk or decision side effects until an explicit action. */
382512
382553
  function OfficialDiffBar(props) {
382513
382554
  const { official, pendingRegions = [], staleRegions = [], diffIdx = 0 } = props;
382514
- const [expanded, setExpanded] = react$1.default.useState(false);
382515
382555
  const [menu, setMenu] = react$1.default.useState(null);
382516
382556
  const name = String(props.activePath || "").split(/[\\/]/).pop() || "当前文件";
382517
382557
  const detail = official.detail;
382518
- const records = official.records ?? [];
382519
382558
  const count = pendingRegions.length;
382520
382559
  const view = reviewBarView({
382521
382560
  jumps: count,
@@ -382532,11 +382571,6 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
382532
382571
  function closeMenu() {
382533
382572
  setMenu(null);
382534
382573
  }
382535
- /** @private @author ddj 2026年10月27号 @param id Immutable record ID. @returns Select record and dismiss popup. */
382536
- function selectRecord(id) {
382537
- official.onSelect(id);
382538
- closeMenu();
382539
- }
382540
382574
  /** @private @author ddj 2026年10月27号 @param tab Existing launcher tab. @returns Open existing overview, never create another review entry. */
382541
382575
  function openOverview(tab) {
382542
382576
  props.onOpenLauncher(tab);
@@ -382554,16 +382588,6 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
382554
382588
  onClick: action
382555
382589
  }, barIcon(mark));
382556
382590
  }
382557
- const recordTrigger = /* @__PURE__ */ react$1.default.createElement(_deepseek_ai_dsh_client_ui_primitives.Button, {
382558
- size: "sm",
382559
- variant: "ghost",
382560
- className: "edrv-review-record",
382561
- "aria-label": "选择官方差异记录",
382562
- "aria-haspopup": "menu",
382563
- "aria-expanded": menu === "records",
382564
- disabled: !records.length,
382565
- onClick: () => setMenu(menu === "records" ? null : "records")
382566
- }, detail ? `轮次 ${detail.item.turn}` : "选择记录", barIcon("down"));
382567
382591
  const moreTrigger = /* @__PURE__ */ react$1.default.createElement(_deepseek_ai_dsh_client_ui_primitives.Button, {
382568
382592
  size: "sm",
382569
382593
  variant: "ghost",
@@ -382600,22 +382624,7 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
382600
382624
  }, name), /* @__PURE__ */ react$1.default.createElement("span", {
382601
382625
  className: "edrv-review-pending",
382602
382626
  title: view.pendingTitle
382603
- }, "待审 ", /* @__PURE__ */ react$1.default.createElement("strong", null, official.pending)), /* @__PURE__ */ react$1.default.createElement(_deepseek_ai_dsh_client_ui_primitives.Menu, {
382604
- open: menu === "records",
382605
- anchor: recordTrigger,
382606
- items: records.map((item) => ({
382607
- id: item.id,
382608
- label: `轮次 ${item.turn} / 事件 ${item.seq}`
382609
- })),
382610
- selectedId: detail?.item.id,
382611
- onSelect: selectRecord,
382612
- onClose: closeMenu,
382613
- side: "top",
382614
- align: "end",
382615
- portal: true,
382616
- compact: true,
382617
- autoFocus: true
382618
- })), /* @__PURE__ */ react$1.default.createElement("div", { className: "edrv-review-tools" }, /* @__PURE__ */ react$1.default.createElement("div", {
382627
+ }, "待审 ", /* @__PURE__ */ react$1.default.createElement("strong", null, official.pending))), /* @__PURE__ */ react$1.default.createElement("div", { className: "edrv-review-tools" }, /* @__PURE__ */ react$1.default.createElement("div", {
382619
382628
  className: "edrv-review-nav",
382620
382629
  role: "group",
382621
382630
  "aria-label": "文件导航"
@@ -382642,15 +382651,7 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
382642
382651
  onClick: props.onAcceptFile,
382643
382652
  disabled: props.reviewBatchBusy || !detail?.capability.keep,
382644
382653
  title: detail?.capability.keep ? view.keepHint : detail?.capability.reason
382645
- }, "Keep"), /* @__PURE__ */ react$1.default.createElement(_deepseek_ai_dsh_client_ui_primitives.Button, {
382646
- size: "sm",
382647
- variant: "ghost",
382648
- className: "edrv-review-details",
382649
- onClick: () => setExpanded(!expanded),
382650
- "aria-expanded": expanded,
382651
- "aria-label": expanded ? "收起差异" : "展开差异",
382652
- title: expanded ? "收起差异" : "展开差异"
382653
- }, "详情", barIcon(expanded ? "up" : "down")), /* @__PURE__ */ react$1.default.createElement(_deepseek_ai_dsh_client_ui_primitives.Menu, {
382654
+ }, "Keep"), /* @__PURE__ */ react$1.default.createElement(_deepseek_ai_dsh_client_ui_primitives.Menu, {
382654
382655
  open: menu === "more",
382655
382656
  anchor: moreTrigger,
382656
382657
  items: [{
@@ -382673,12 +382674,7 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
382673
382674
  }, view.notice) : null, props.reviewError ? /* @__PURE__ */ react$1.default.createElement("div", {
382674
382675
  className: props.reviewErrorTone === "info" ? "edrv-review-notice" : "edrv-review-notice edrv-review-error",
382675
382676
  role: props.reviewErrorTone === "info" ? "status" : "alert"
382676
- }, props.reviewError) : null, expanded ? /* @__PURE__ */ react$1.default.createElement("div", { className: "edrv-review-body" }, /* @__PURE__ */ react$1.default.createElement(ReviewLedger, {
382677
- key: `${official.sessionId}:${official.path}`,
382678
- sessionId: official.sessionId,
382679
- first: official.page,
382680
- path: official.path
382681
- })) : null);
382677
+ }, props.reviewError) : null);
382682
382678
  }
382683
382679
  //#endregion
382684
382680
  //#region src/client/ui/SidecarDiffBar.tsx
@@ -382693,7 +382689,6 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
382693
382689
  function SidecarDiffBar(props) {
382694
382690
  const pendingRegions = props.pendingRegions ?? [];
382695
382691
  const staleRegions = props.staleRegions ?? [];
382696
- const [expanded, setExpanded] = react$1.default.useState(false);
382697
382692
  const [menu, setMenu] = react$1.default.useState(null);
382698
382693
  const name = String(props.activePath || "").split(/[\\/]/).pop() || "当前文件";
382699
382694
  const count = pendingRegions.length;
@@ -382736,20 +382731,6 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
382736
382731
  "aria-expanded": menu === "more",
382737
382732
  onClick: () => setMenu(menu === "more" ? null : "more")
382738
382733
  }, barIcon("more"));
382739
- const body = expanded ? /* @__PURE__ */ react$1.default.createElement("div", { className: "edrv-review-body" }, pendingRegions.map((region) => {
382740
- const key = callIdAttr(region.callId, region.idx);
382741
- return /* @__PURE__ */ react$1.default.createElement("div", {
382742
- key,
382743
- className: "edrv-diffrow",
382744
- "data-edrv-hunk": key,
382745
- title: "跳转到 L" + (region.start ?? "?"),
382746
- onClick: () => props.onJump?.(region)
382747
- }, /* @__PURE__ */ react$1.default.createElement("span", { className: "edrv-diffrow-l" }, "L" + (region.start ?? "?") + "-" + (region.end ?? "?")), (region.oldLines?.length ?? 0) > 0 ? /* @__PURE__ */ react$1.default.createElement("span", { className: "edrv-diffrow-o" }, "-" + (region.oldLines?.length ?? 0)) : null, (region.newLines?.length ?? 0) > 0 ? /* @__PURE__ */ react$1.default.createElement("span", { className: "edrv-diffrow-n" }, "+" + (region.newLines?.length ?? 0)) : null, region.create ? /* @__PURE__ */ react$1.default.createElement("span", { className: "edrv-diffrow-tag" }, "新建") : null);
382748
- }), staleRegions.map((region) => /* @__PURE__ */ react$1.default.createElement("div", {
382749
- key: "stale" + callIdAttr(region.callId, region.idx),
382750
- className: "edrv-diffrow edrv-diffrow-stale",
382751
- title: "差异无法定位(文件可能已被手动修改)"
382752
- }, /* @__PURE__ */ react$1.default.createElement("span", { className: "edrv-diffrow-l" }, "L" + (region.start ?? "?") + " 无法定位"), /* @__PURE__ */ react$1.default.createElement("span", { className: "edrv-diffrow-tag" }, "冲突/需刷新"), /* @__PURE__ */ react$1.default.createElement("span", { className: "edrv-diffrow-tag" }, badgeOf(region.status ?? ""))))) : null;
382753
382734
  return /* @__PURE__ */ react$1.default.createElement("div", {
382754
382735
  className: "edrv-diffbar edrv-diffbar-dock edrv-reviewbar",
382755
382736
  "data-edrv-diffbox": "1",
@@ -382816,15 +382797,7 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
382816
382797
  onClick: props.onAcceptFile,
382817
382798
  disabled: !canDecide,
382818
382799
  title: props.missingDeletion ? "保持文件删除并结清覆盖的待审历史记录,不写文件" : SIDECAR_KEEP_TITLE
382819
- }, "Keep"), /* @__PURE__ */ react$1.default.createElement(_deepseek_ai_dsh_client_ui_primitives.Button, {
382820
- size: "sm",
382821
- variant: "ghost",
382822
- className: "edrv-review-details",
382823
- onClick: () => setExpanded(!expanded),
382824
- "aria-expanded": expanded,
382825
- "aria-label": expanded ? "收起差异" : "展开差异",
382826
- title: expanded ? "收起差异" : "展开差异"
382827
- }, "详情", barIcon(expanded ? "up" : "down")), /* @__PURE__ */ react$1.default.createElement(_deepseek_ai_dsh_client_ui_primitives.Menu, {
382800
+ }, "Keep"), /* @__PURE__ */ react$1.default.createElement(_deepseek_ai_dsh_client_ui_primitives.Menu, {
382828
382801
  open: menu === "more",
382829
382802
  anchor: moreTrigger,
382830
382803
  items: [
@@ -382870,7 +382843,7 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
382870
382843
  }, view.notice) : null, props.reviewError ? /* @__PURE__ */ react$1.default.createElement("div", {
382871
382844
  className: props.reviewErrorTone === "info" ? "edrv-review-notice" : "edrv-review-notice edrv-review-error",
382872
382845
  role: props.reviewErrorTone === "info" ? "status" : "alert"
382873
- }, props.reviewError) : null, body);
382846
+ }, props.reviewError) : null);
382874
382847
  }
382875
382848
  //#endregion
382876
382849
  //#region src/client/ui/DiffBox.ts
@@ -393800,7 +393773,9 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
393800
393773
  ]);
393801
393774
  react$1.default.useEffect(() => {
393802
393775
  const controller = new AbortController();
393803
- if (!active || !review.page?.overview?.coveredPaths.some((path) => sameFile(path, active))) {
393776
+ const pendingPath = (review.page?.items ?? []).filter((item) => String(item.id).startsWith(PENDING_PREFIX)).map((item) => item.currentPath || item.path).find((path) => sameFile(path, active));
393777
+ const reviewPath = review.page?.overview?.coveredPaths.find((path) => sameFile(path, active)) ?? pendingPath;
393778
+ if (!active || !reviewPath) {
393804
393779
  setOfficialDetail(null);
393805
393780
  setOfficialRecords([]);
393806
393781
  return () => controller.abort();
@@ -393808,7 +393783,6 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
393808
393783
  /** @private @author ddj 2026年10月27号 Load the active file independently of the global history page. */
393809
393784
  async function loadReview() {
393810
393785
  try {
393811
- const reviewPath = review.page?.overview?.coveredPaths.find((path) => sameFile(path, active)) ?? active;
393812
393786
  const page = await rpc("edrv.review.list", {
393813
393787
  sessionId,
393814
393788
  path: reviewPath
package/lib/client.js.map CHANGED
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
2
  name: package/lib/client.js.map
3
- size: 35448554 bytes
4
- sha256: 914a19e1a5249e6bb325b3146b52eb17e2f3687c19a1bea36dd0843e72be1774
3
+ size: 35450967 bytes
4
+ sha256: 96b410b89d31eca1fc14d341d0ae358834242b691a8bae7c053b3b0340107065