dsh-vscode-mode 0.23.3 → 0.23.5
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 +4 -3
- package/lib/client.js +287 -14
- package/lib/client.js.map +2 -2
- package/lib/index.js +60 -16
- package/lib/index.js.map +1 -1
- package/package.json +2 -2
- package/src/client/openReceipt.ts +3 -3
- package/src/client/planBridge.ts +9 -11
- package/src/client/planDirect.ts +266 -0
- package/src/client/planState.ts +20 -2
- package/src/client/state/diffSummary.ts +4 -0
- package/src/pending/decide.ts +9 -2
- package/src/pending/store.ts +13 -9
- package/src/pendingCompat.ts +7 -1
- package/src/pendingRpc.ts +5 -2
- package/src/reviewDb.ts +25 -0
- package/src/reviewRpc.ts +7 -3
package/README.md
CHANGED
|
@@ -533,13 +533,13 @@ profile 请替换为实际目标,不要把示例目录当成当前运行 profi
|
|
|
533
533
|
|
|
534
534
|
```bash
|
|
535
535
|
# ① Git 安装(clone + prepare 构建;推荐打固定 tag)
|
|
536
|
-
dsh plugin --profile web add github:Lenonss/DSH_VsCodeMode#v0.23.
|
|
536
|
+
dsh plugin --profile web add github:Lenonss/DSH_VsCodeMode#v0.23.5
|
|
537
537
|
|
|
538
538
|
# ② npm 注册表(发布到 npm 后)
|
|
539
|
-
dsh plugin --profile web add dsh-vscode-mode@0.23.
|
|
539
|
+
dsh plugin --profile web add dsh-vscode-mode@0.23.5
|
|
540
540
|
|
|
541
541
|
# ③ GitHub Release tgz 直装(Release asset 可用后)
|
|
542
|
-
dsh plugin --profile web add https://github.com/Lenonss/DSH_VsCodeMode/releases/download/v0.23.
|
|
542
|
+
dsh plugin --profile web add https://github.com/Lenonss/DSH_VsCodeMode/releases/download/v0.23.5/dsh-vscode-mode-0.23.5.tgz
|
|
543
543
|
```
|
|
544
544
|
|
|
545
545
|
> **DSH 版本政策(0.17.0 起)**:六项 DSH peer 仍统一声明为 `*`,另以
|
|
@@ -880,6 +880,7 @@ Keep All / Undo All 却是亮的。修复:单文件 Keep / Undo 覆盖冲突
|
|
|
880
880
|
完整变更见 [GitHub Releases](https://github.com/Lenonss/DSH_VsCodeMode/releases)。
|
|
881
881
|
近期关键版本:
|
|
882
882
|
|
|
883
|
+
- **v0.23.5**:修复差异栏把同一文件算成两条(差异文件数偏大)。待审条目存的是写入时的相对路径,官方导航用绝对身份,而 `mergedPage()` 与 `diffSummary` 的去重都只按 path 建覆盖集,相对路径的待审行压不掉绝对身份的官方行,于是同一文件同时留下待审行与官方汇总行;现两侧覆盖集都补入条目身份(与官方 identity 同源)。另修复本会话新建的文件被渲染成一堆修改块:累计基线保留「最早内容」,新建文件首次采集时磁盘已有会话自己的中间产物,基线因此非空,「新增 516 行」被切成若干 `-`/`+` 混合块,与官方整文件新增(`before=false`、`oldLines=0`)口径相反;现按每个文件在该会话内最早的官方记录判定整文件新增,命中时待审侧基线置空,列表与详情只出一块全加差异、增删计数等于整文件行数,`addedFiles()` 查询失败则回退旧口径。验证:typecheck 0 error;全量单测通过;`npm run build` 通过;本版未做 GUI 点按验收(缺口保留)。见 [0.23.5 发布说明](docs/release-0.23.5.md)。- **v0.23.4**:修复桌面端点计划卡「查看全文」会先闪开一页官方计划预览、随即自动关闭。根因:官方卡片走 `ctx.sidebarRight.openResource(planAddress(callId))`,而本插件以 extension 优先级认领了 `dsh-resource://plan/**` 与 `dsh-resource://plan-review/**`,于是侧栏先按本插件类型建出一个页签(正文「正在打开计划预览…」),挂载后才把计划转进「文件编辑」、再把这个页签关掉——用户看到的就是这次建而即关。现由新增 `src/client/planDirect.ts` 在装配期包装 `sidebarRight.openResource`:只接管「2 段计划地址 + 会话为在屏会话」,正文依次取回顾卡片的 `params.planReview`、插件侧地址→正文缓存、可选 `resources.source()` 资源流(上限 1.5s),拿到正文后经 `requestOpen(..., { preview: true })` 直通本插件编辑器的只读计划页签,不再创建侧栏页签;非计划地址、跨会话与 subagent 5 段地址、正文不可得、编辑器 5s 未回执、以及服务对象只读导致包装赋值失败,一律回落原生 `openResource`,原「认领 + 挂载后转移」链路保留为后备。`openReceipt` 新增可选 `timeoutMs`,`planState` 新增有界(64 条)地址→正文缓存并由 live `exit_plan_mode` 写入。验证:typecheck 0 error;`tests/planDirect.test.ts`(新增 9 例)、`tests/planBridge.test.ts`(13)、`tests/planState.test.ts`(10)、`tests/openReceipt.test.ts`(9)四套 41/41;`npm run build` 双 bundle 通过(含客户端产物完整性校验);浏览器确认新 bundle 已加载且 Console 无「计划直通装配失败」;GUI 点按由用户在本机 Web 端手动复验后发布。见 [发布说明](docs/release-0.23.4.md)。
|
|
883
884
|
- **v0.23.3**:修复 issue #13(已删除文件的差异条目把人卡死)与 #12(重启后成串「采集不完整」)。①文件在磁盘上被删掉时,客户端申请整文件删除预览被「已有官方审查记录」拒绝,而拒绝只是一条普通 `Error`,于是落到通用加载失败分支——「文件加载失败:…」+ 进度钉在 0%、重试同错、标签页关不掉;`edrv.review.detail` 还把 Undo 不可用解释成「缺少可验证的原始字节」,而真实原因是文件已不在。现缺失文件预览的可预期拒绝带稳定码(`official-covered` / `official-decided` / `no-records` / `no-evidence` / `too-large`),客户端据此显示「文件已在磁盘删除(· 有官方待审改动)」终态:标题 + 原因 + 重试 / 关闭标签页,不再画 0% 进度条;官方待审记录在磁盘已删除时给出「Keep 按删除结案、Undo 需要读写该文件」的准确原因(Keep 语义不变,仍只写共享审查库),记录都已结案时提示可直接关闭;`!review` 的磁盘状态抖动改为最多重读 2 次后给终态,消除缺失↔重现的无限递归。v0.22.0 起 `hasOfficial()` 已用 `typeof path !== 'string'` 守卫堵掉把不透明 FsTarget 绑进 SQL 导致的 `Unknown named parameter 'targetKey'`,本版不再复现。②官方 `workspace/changes` 的 summary 只存在宿主内存里,插件启动补采原先无条件把历史事件全部入队,重启后每一轮都必然抛「summary 已失效或尚不可用」并写成不可修复的 `gap`(面板成串「轮次 N 采集不完整」);现补采先 `summary()` 探测再入队,失效事件只记一条汇总日志。验证:typecheck 与基线同为 37 个既有错误(`fflate/browser` 依赖缺失)、零新增;定向单测 111/111(含新增 `tests/deletedFileReview.test.ts` 5 例与 `tests/officialBackfill.test.ts` 2 例)、EditorView 相关 82/82;`npm run build` 四个 bundle 通过。见 [发布说明](docs/release-0.23.3.md)。
|
|
884
885
|
- **v0.23.2**:修复 Keep 在非活跃端(桌面端)点了没反应,两处成因。①`edrv.review.decide` 原先一律要求本端活跃会话,而能力位只按审查库推导,会话在另一实例时按钮亮着、点击后抛错被兜底成 HTTP 500。现按写入副作用区分:官方记录 Keep 只写共享审查库(`BEGIN IMMEDIATE` + 幂等键 + 版本 CAS),冷端放行;官方 Undo(校验证据并写回源文件)与待审投影条目(本机 JSON 无跨进程锁)仍限活跃端,返回可读跨端原因而非 500;详情新增 `capability.live`,冷端禁用 Undo 并说明、Keep 提示补「写入共享审查库,仍可执行」。②已结算待审投影的 `capability.keep` 恒为假,却因排在 `items[0]`、同路径官方待审行被历史过滤,把官方记录挤出差异栏(表现为「待审 0」+Keep 禁用+点击无反应);现 `pendingPageOf()` 剔除已结算投影,官方记录重新成为审查目标。另详情未加载时点 Keep 不再静默失败;打开文件时若某条官方记录的改动已全部不在当前文件中(无可定位差异且缓冲区干净),自动按 Keep 结案(只标记不写盘),差异栏不再显示「本记录 N 处改动均已不在当前文件中」;官方元数据首次载入期间差异栏显示 `—` 而非按历史记录报数,重启瞬间不再闪出几十条假待审。验证:typecheck 0 error、目标单测 57/57、build 通过、真实审查库探针确认首条回到官方记录且 `capability={keep:true,undo:true,live:true}`;桌面端 GUI 点按验收因重启后恢复的是另一个会话未完成,缺口保留。发布不自动部署,升级后重启 DSH 并刷新页面。见 [发布说明](docs/release-0.23.2.md)。
|
|
885
886
|
- **v0.23.1**:修复 Keep All / Undo All 在待审超过一页时必定中止——批量按钮原先复用分页接口并把每页的展示用计数当全量总数校验,官方待审 150 条时先报「待审记录在分页期间变化」或最终报「待审记录数量不完整」,实际 0 项被决策。现改为一次性完整快照(`edrv.review.list` 新增 `all: true`,服务端循环取全并与历史改动投影去重,`nextCursor: null`、`pending` 为真实总数),客户端单次取快照、校验唯一且仍待审后逐项决策;超过 2000 条明确拒绝并提示分批,途中变化仍整批中止不部分写入。差异栏浏览仍走原分页接口。本版未在重启后的 GUI 点按验证,缺口保留;发布不自动部署,升级后重启 DSH 并刷新页面。见 [发布说明](docs/release-0.23.1.md)。
|
package/lib/client.js
CHANGED
|
@@ -2613,7 +2613,7 @@ window.__ModuleLoader__.load({
|
|
|
2613
2613
|
* @param line Optional target line.
|
|
2614
2614
|
* @param column Optional target column.
|
|
2615
2615
|
* @param sessionId Owning session, required for routing.
|
|
2616
|
-
* @param options Explicit preview, read-only plan document and
|
|
2616
|
+
* @param options Explicit preview, read-only plan document, cancellation signal and receipt deadline.
|
|
2617
2617
|
* @returns Actual completion; timeout and disposal are false.
|
|
2618
2618
|
*/
|
|
2619
2619
|
function requestOpen(path, line, column, sessionId = "", options = {}) {
|
|
@@ -2647,7 +2647,7 @@ window.__ModuleLoader__.load({
|
|
|
2647
2647
|
request,
|
|
2648
2648
|
finish
|
|
2649
2649
|
});
|
|
2650
|
-
timer = setTimeout(() => finish(false), 2e4);
|
|
2650
|
+
timer = setTimeout(() => finish(false), options.timeoutMs ?? 2e4);
|
|
2651
2651
|
openEditorView(null);
|
|
2652
2652
|
window.dispatchEvent(new CustomEvent("edrv:open-request", { detail: request }));
|
|
2653
2653
|
});
|
|
@@ -4117,6 +4117,7 @@ window.__ModuleLoader__.load({
|
|
|
4117
4117
|
if (!item.id.startsWith("pending:")) continue;
|
|
4118
4118
|
const path = item.currentPath || item.path;
|
|
4119
4119
|
covered.add(diffPathKey(path));
|
|
4120
|
+
covered.add(diffPathKey(item.id.slice(8)));
|
|
4120
4121
|
rows.push({
|
|
4121
4122
|
path,
|
|
4122
4123
|
label: item.display || path,
|
|
@@ -376030,6 +376031,8 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
|
|
|
376030
376031
|
//#endregion
|
|
376031
376032
|
//#region src/client/planState.ts
|
|
376032
376033
|
const associated = /* @__PURE__ */ new Map();
|
|
376034
|
+
/** 地址 → 已解析计划正文(只留内存,供后续点击省掉一次资源解析)。 */
|
|
376035
|
+
const documents = /* @__PURE__ */ new Map();
|
|
376033
376036
|
/** @private @author ddj 2026年10月08号 @param value JSON candidate. @returns Object or empty record. */
|
|
376034
376037
|
function record(value) {
|
|
376035
376038
|
return value !== null && typeof value === "object" ? value : {};
|
|
@@ -376096,9 +376099,25 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
|
|
|
376096
376099
|
associated.set(sessionId, paths);
|
|
376097
376100
|
if (associated.size > 32) associated.delete(associated.keys().next().value);
|
|
376098
376101
|
}
|
|
376102
|
+
/** @public @author ddj 2026年10月31号
|
|
376103
|
+
* 记住一份已解析计划,供「查看全文」时免去资源解析。
|
|
376104
|
+
* @param plan 已解析计划。
|
|
376105
|
+
* @returns 无;只缓存合法地址与正文,超过上限按插入序淘汰。
|
|
376106
|
+
*/
|
|
376107
|
+
function rememberPlan(plan) {
|
|
376108
|
+
const valid = planDocument(plan.address, plan);
|
|
376109
|
+
if (valid === void 0) return;
|
|
376110
|
+
documents.set(valid.address, valid);
|
|
376111
|
+
if (documents.size > 64) documents.delete(documents.keys().next().value);
|
|
376112
|
+
}
|
|
376113
|
+
/** @public @author ddj 2026年10月31号 @param address 官方资源地址。 @returns 已缓存的计划正文。 */
|
|
376114
|
+
function cachedPlan(address) {
|
|
376115
|
+
return documents.get(address);
|
|
376116
|
+
}
|
|
376099
376117
|
/** @public @author ddj 2026年10月08号 Clear associations when the plugin unloads. */
|
|
376100
376118
|
function clearPlanState() {
|
|
376101
376119
|
associated.clear();
|
|
376120
|
+
documents.clear();
|
|
376102
376121
|
}
|
|
376103
376122
|
/** @public @author ddj 2026年10月08号
|
|
376104
376123
|
* Reduce only live append deltas; hydration, pagination, reconnect and duplicate sequences never open plans.
|
|
@@ -376134,6 +376153,7 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
|
|
|
376134
376153
|
if (event.type === "tool/result") return void 0;
|
|
376135
376154
|
const plan = planDocument("dsh-resource://plan/" + encodeURIComponent(sessionId) + "/" + encodeURIComponent(id), { markdown: call.args.plan });
|
|
376136
376155
|
if (plan) {
|
|
376156
|
+
rememberPlan(plan);
|
|
376137
376157
|
remember(id);
|
|
376138
376158
|
return { plan };
|
|
376139
376159
|
}
|
|
@@ -412357,19 +412377,269 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
|
|
|
412357
412377
|
return directTranslate();
|
|
412358
412378
|
}
|
|
412359
412379
|
//#endregion
|
|
412360
|
-
//#region src/client/
|
|
412361
|
-
/**
|
|
412362
|
-
|
|
412363
|
-
|
|
412364
|
-
|
|
412365
|
-
|
|
412366
|
-
|
|
412380
|
+
//#region src/client/planDirect.ts
|
|
412381
|
+
/**
|
|
412382
|
+
* dsh-vscode-mode client — 计划地址直通。
|
|
412383
|
+
*
|
|
412384
|
+
* 背景:官方计划卡片「查看全文」调 `ctx.sidebarRight.openResource(planAddress)`,
|
|
412385
|
+
* 侧栏会先创建计划页签、再由本插件认领并立刻关闭,桌面端表现为「快速打开又迅速关闭」。
|
|
412386
|
+
* 本模块在装配时包装该服务方法:能直接拿到计划正文时改为把只读计划送进「文件编辑」,
|
|
412387
|
+
* 不再创建侧栏页签;地址不属于在屏会话、正文暂不可得、编辑器未回执或包装未生效时
|
|
412388
|
+
* 一律回落原方法,官方页签与原认领转交链路继续作为后备。
|
|
412389
|
+
* 作者 ddj 2026年10月31号
|
|
412390
|
+
*/
|
|
412391
|
+
/** 正文解析上限:超过即回落官方页签,避免点击后长时间无反馈。 */
|
|
412392
|
+
const RESOLVE_MS = 1500;
|
|
412393
|
+
/** 编辑器回执上限:超过即回落官方页签。 */
|
|
412394
|
+
const ROUTE_MS = 5e3;
|
|
412395
|
+
/** @public @author ddj 2026年10月31号
|
|
412396
|
+
* 可选探测客户端服务,缺失或抛错时返回 undefined。
|
|
412397
|
+
* @param ctx 客户端上下文(只需 get)。
|
|
412398
|
+
* @param name 服务名。
|
|
412399
|
+
* @returns 服务实例,或 undefined(不阻断装配)。
|
|
412400
|
+
*/
|
|
412401
|
+
function optionalService(ctx, name) {
|
|
412367
412402
|
try {
|
|
412368
412403
|
return ctx.get(name);
|
|
412369
412404
|
} catch {
|
|
412370
412405
|
return;
|
|
412371
412406
|
}
|
|
412372
412407
|
}
|
|
412408
|
+
/** @public @author ddj 2026年10月31号
|
|
412409
|
+
* 包装官方 openResource,把本会话的计划地址改为直通插件编辑器。
|
|
412410
|
+
* @param ctx 客户端上下文(可选服务一律 ctx.get 探测)。
|
|
412411
|
+
* @param service 官方 sidebarRight 服务;仅在赋值生效时留下包装。
|
|
412412
|
+
* @returns 供路由回退使用的原方法,以及卸载时中止在途并还原的清理函数。
|
|
412413
|
+
*/
|
|
412414
|
+
function installPlanDirect(ctx, service) {
|
|
412415
|
+
const original = service.openResource;
|
|
412416
|
+
const pending = /* @__PURE__ */ new Map();
|
|
412417
|
+
let closed = false;
|
|
412418
|
+
let installed = false;
|
|
412419
|
+
/** @private @author ddj 2026年10月31号
|
|
412420
|
+
* 解码地址段(畸形编码一律视为不可接管)。
|
|
412421
|
+
* @param address 官方资源地址。
|
|
412422
|
+
* @returns 去掉协议前缀后的解码段。
|
|
412423
|
+
*/
|
|
412424
|
+
function decode(address) {
|
|
412425
|
+
try {
|
|
412426
|
+
return address.split("/").slice(3).map(decodeURIComponent);
|
|
412427
|
+
} catch {
|
|
412428
|
+
return;
|
|
412429
|
+
}
|
|
412430
|
+
}
|
|
412431
|
+
/** @private @author ddj 2026年10月31号
|
|
412432
|
+
* 判断该地址是否由本插件接管:只认两段式计划地址,且归属当前在屏会话。
|
|
412433
|
+
* @param address 官方资源地址。
|
|
412434
|
+
* @returns 可接管的会话 id,其他情况 undefined(回落原方法)。
|
|
412435
|
+
*/
|
|
412436
|
+
function take(address) {
|
|
412437
|
+
if (closed || !isPlanAddress(address)) return void 0;
|
|
412438
|
+
const segments = decode(address);
|
|
412439
|
+
if (segments?.length !== 2) return void 0;
|
|
412440
|
+
const current = readSessionScope(ctx).sessionId ?? "";
|
|
412441
|
+
return current !== "" && segments[0] === current ? current : void 0;
|
|
412442
|
+
}
|
|
412443
|
+
/** @private @author ddj 2026年10月31号
|
|
412444
|
+
* 读官方参数里随页签携带的计划正文(临时回顾页签走这里)。
|
|
412445
|
+
* @param address 官方资源地址。
|
|
412446
|
+
* @param options 官方 openResource 参数。
|
|
412447
|
+
* @returns 计划正文,或不带正文时 undefined。
|
|
412448
|
+
*/
|
|
412449
|
+
function inline(address, options) {
|
|
412450
|
+
const params = options?.params;
|
|
412451
|
+
if (params === null || typeof params !== "object") return void 0;
|
|
412452
|
+
return planDocument(address, params.planReview);
|
|
412453
|
+
}
|
|
412454
|
+
/** @private @author ddj 2026年10月31号
|
|
412455
|
+
* 打开计划资源流。
|
|
412456
|
+
* @param services resources 服务。
|
|
412457
|
+
* @param address 官方资源地址。
|
|
412458
|
+
* @returns 资源流;服务缺失或抛错时 undefined。
|
|
412459
|
+
*/
|
|
412460
|
+
function streamOf(services, address) {
|
|
412461
|
+
try {
|
|
412462
|
+
return services?.source?.(address);
|
|
412463
|
+
} catch {
|
|
412464
|
+
return;
|
|
412465
|
+
}
|
|
412466
|
+
}
|
|
412467
|
+
/** @private @author ddj 2026年10月31号
|
|
412468
|
+
* 从资源快照读已就绪的计划正文。
|
|
412469
|
+
* @param address 官方资源地址。
|
|
412470
|
+
* @param stream 计划资源流。
|
|
412471
|
+
* @returns 计划正文;未就绪、失败或内容不完整时 undefined。
|
|
412472
|
+
*/
|
|
412473
|
+
function planOf(address, stream) {
|
|
412474
|
+
let snapshot;
|
|
412475
|
+
try {
|
|
412476
|
+
snapshot = stream.getSnapshot?.();
|
|
412477
|
+
} catch {
|
|
412478
|
+
return;
|
|
412479
|
+
}
|
|
412480
|
+
const state = snapshot !== null && typeof snapshot === "object" ? snapshot : {};
|
|
412481
|
+
return state.status === "live" ? planDocument(address, state.value) : void 0;
|
|
412482
|
+
}
|
|
412483
|
+
/** @private @author ddj 2026年10月31号
|
|
412484
|
+
* 订阅资源快照变化。
|
|
412485
|
+
* @param stream 计划资源流。
|
|
412486
|
+
* @param listener 变更监听。
|
|
412487
|
+
* @returns 退订函数;订阅面缺失或抛错时为 no-op。
|
|
412488
|
+
*/
|
|
412489
|
+
function watchStream(stream, listener) {
|
|
412490
|
+
try {
|
|
412491
|
+
const off = stream.subscribe?.(listener);
|
|
412492
|
+
return typeof off === "function" ? off : () => void 0;
|
|
412493
|
+
} catch {
|
|
412494
|
+
return () => void 0;
|
|
412495
|
+
}
|
|
412496
|
+
}
|
|
412497
|
+
/** @private @author ddj 2026年10月31号
|
|
412498
|
+
* 回落官方方法(包装外的原入口)。
|
|
412499
|
+
* @param address 官方资源地址。
|
|
412500
|
+
* @param options 官方参数。
|
|
412501
|
+
* @returns 无;失败只记日志,不影响点击链路。
|
|
412502
|
+
*/
|
|
412503
|
+
function native(address, options) {
|
|
412504
|
+
if (typeof original !== "function") return;
|
|
412505
|
+
try {
|
|
412506
|
+
original.call(service, address, options);
|
|
412507
|
+
} catch (error) {
|
|
412508
|
+
log$2.warn("官方计划页签回退失败:" + String(error));
|
|
412509
|
+
}
|
|
412510
|
+
}
|
|
412511
|
+
/** @private @author ddj 2026年10月31号
|
|
412512
|
+
* 把已解析的计划送进插件编辑器;地址级在途合并,重复点击不叠开。
|
|
412513
|
+
* @param plan 计划正文。
|
|
412514
|
+
* @param session 归属会话。
|
|
412515
|
+
* @param address 官方资源地址。
|
|
412516
|
+
* @param options 官方参数(回落时原样透传)。
|
|
412517
|
+
* @returns 无;未就绪或失败时回落官方页签,但不劫持已切换的会话。
|
|
412518
|
+
*/
|
|
412519
|
+
function direct(plan, session, address, options) {
|
|
412520
|
+
if (closed || pending.has(address)) return;
|
|
412521
|
+
const controller = new AbortController();
|
|
412522
|
+
pending.set(address, () => {
|
|
412523
|
+
pending.delete(address);
|
|
412524
|
+
controller.abort();
|
|
412525
|
+
});
|
|
412526
|
+
rememberPlan(plan);
|
|
412527
|
+
/** @private @author ddj 2026年10月31号 @param ok 编辑器是否就绪。 @returns 无;失败时回落官方页签。 */
|
|
412528
|
+
function settle(ok) {
|
|
412529
|
+
pending.delete(address);
|
|
412530
|
+
if (ok || closed || readSessionScope(ctx).sessionId !== session) return;
|
|
412531
|
+
native(address, options);
|
|
412532
|
+
}
|
|
412533
|
+
requestOpen(null, void 0, void 0, session, {
|
|
412534
|
+
preview: true,
|
|
412535
|
+
plan,
|
|
412536
|
+
signal: controller.signal,
|
|
412537
|
+
timeoutMs: ROUTE_MS
|
|
412538
|
+
}).then(settle, (error) => {
|
|
412539
|
+
log$2.warn("计划直通失败:" + String(error));
|
|
412540
|
+
settle(false);
|
|
412541
|
+
});
|
|
412542
|
+
}
|
|
412543
|
+
/** @private @author ddj 2026年10月31号
|
|
412544
|
+
* 从计划资源异步解析正文,就绪后直通;超时或无法解析回落官方页签。
|
|
412545
|
+
* @param address 官方资源地址。
|
|
412546
|
+
* @param session 归属会话。
|
|
412547
|
+
* @param options 官方参数。
|
|
412548
|
+
* @returns 无。
|
|
412549
|
+
*/
|
|
412550
|
+
function lookup(address, session, options) {
|
|
412551
|
+
const stream = streamOf(optionalService(ctx, "resources"), address);
|
|
412552
|
+
if (stream === void 0) {
|
|
412553
|
+
native(address, options);
|
|
412554
|
+
return;
|
|
412555
|
+
}
|
|
412556
|
+
const early = planOf(address, stream);
|
|
412557
|
+
if (early !== void 0) {
|
|
412558
|
+
direct(early, session, address, options);
|
|
412559
|
+
return;
|
|
412560
|
+
}
|
|
412561
|
+
if (closed || pending.has(address)) return;
|
|
412562
|
+
let timer;
|
|
412563
|
+
let stop;
|
|
412564
|
+
let done = false;
|
|
412565
|
+
/** @private @author ddj 2026年10月31号 @param ok 是否已拿到正文。 @returns 无;幂等收尾,清定时器、退订、释放地址。 */
|
|
412566
|
+
function finish(ok) {
|
|
412567
|
+
if (done) return;
|
|
412568
|
+
done = true;
|
|
412569
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
412570
|
+
stop?.();
|
|
412571
|
+
pending.delete(address);
|
|
412572
|
+
if (!ok && !closed) native(address, options);
|
|
412573
|
+
}
|
|
412574
|
+
pending.set(address, () => finish(false));
|
|
412575
|
+
timer = setTimeout(() => finish(false), RESOLVE_MS);
|
|
412576
|
+
stop = watchStream(stream, () => {
|
|
412577
|
+
const plan = planOf(address, stream);
|
|
412578
|
+
if (plan === void 0) return;
|
|
412579
|
+
finish(true);
|
|
412580
|
+
direct(plan, session, address, options);
|
|
412581
|
+
});
|
|
412582
|
+
}
|
|
412583
|
+
/** @private @author ddj 2026年10月31号 @param address 官方资源地址。 @param options 官方参数。 @returns 无;包装后的 openResource 入口。 */
|
|
412584
|
+
function open(address, options) {
|
|
412585
|
+
const session = take(address);
|
|
412586
|
+
if (session === void 0) {
|
|
412587
|
+
native(address, options);
|
|
412588
|
+
return;
|
|
412589
|
+
}
|
|
412590
|
+
const known = inline(address, options) ?? cachedPlan(address);
|
|
412591
|
+
if (known !== void 0) {
|
|
412592
|
+
direct(known, session, address, options);
|
|
412593
|
+
return;
|
|
412594
|
+
}
|
|
412595
|
+
lookup(address, session, options);
|
|
412596
|
+
}
|
|
412597
|
+
/** @private @author ddj 2026年10月31号 @returns 无;赋值后校验,未生效即降级为原认领转交链路。 */
|
|
412598
|
+
function mount() {
|
|
412599
|
+
if (typeof original !== "function") return;
|
|
412600
|
+
try {
|
|
412601
|
+
service.openResource = open;
|
|
412602
|
+
} catch (error) {
|
|
412603
|
+
log$2.warn("计划直通装配失败:" + String(error));
|
|
412604
|
+
}
|
|
412605
|
+
installed = service.openResource === open;
|
|
412606
|
+
if (installed) return;
|
|
412607
|
+
try {
|
|
412608
|
+
service.openResource = original;
|
|
412609
|
+
} catch {}
|
|
412610
|
+
}
|
|
412611
|
+
/** @private @author ddj 2026年10月31号 @returns 无;仅在包装仍是自己时还原(HMR 安全)。 */
|
|
412612
|
+
function unmount() {
|
|
412613
|
+
if (!installed || service.openResource !== open) return;
|
|
412614
|
+
try {
|
|
412615
|
+
service.openResource = original;
|
|
412616
|
+
} catch (error) {
|
|
412617
|
+
log$2.warn("计划直通还原失败:" + String(error));
|
|
412618
|
+
}
|
|
412619
|
+
installed = false;
|
|
412620
|
+
}
|
|
412621
|
+
/** @private @author ddj 2026年10月31号 @returns 无;卸载时先标记关闭,再中止在途并还原官方方法。 */
|
|
412622
|
+
function dispose() {
|
|
412623
|
+
if (closed) return;
|
|
412624
|
+
closed = true;
|
|
412625
|
+
for (const cancel of [...pending.values()]) try {
|
|
412626
|
+
cancel();
|
|
412627
|
+
} catch {}
|
|
412628
|
+
pending.clear();
|
|
412629
|
+
unmount();
|
|
412630
|
+
}
|
|
412631
|
+
mount();
|
|
412632
|
+
return {
|
|
412633
|
+
dispose,
|
|
412634
|
+
native
|
|
412635
|
+
};
|
|
412636
|
+
}
|
|
412637
|
+
//#endregion
|
|
412638
|
+
//#region src/client/planBridge.ts
|
|
412639
|
+
/** Optional DSH plan integration; no provider or approval UI replacement. @author ddj 2026年10月08号 */
|
|
412640
|
+
const CLAIM_ID = "dsh-vscode-mode/plan";
|
|
412641
|
+
const CLAIM_KIND = "edrvPlan";
|
|
412642
|
+
const EMPTY_RESOURCE = { status: "none" };
|
|
412373
412643
|
/** @private @author ddj 2026年10月08号 @param tab Current tab. @returns Native plan fallback options, preserving transient review parameters. */
|
|
412374
412644
|
function nativeOptions(tab) {
|
|
412375
412645
|
return {
|
|
@@ -412377,8 +412647,8 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
|
|
|
412377
412647
|
params: tab?.navigation?.params
|
|
412378
412648
|
};
|
|
412379
412649
|
}
|
|
412380
|
-
/** @private @author ddj 2026年10月08号 @param ctx Optional context. @param official Sidebar services. @returns Transfer component with native fallback and cancellable receipts. */
|
|
412381
|
-
function planRouter(ctx, official) {
|
|
412650
|
+
/** @private @author ddj 2026年10月08号 @param ctx Optional context. @param official Sidebar services. @param native Official openResource bypass. @returns Transfer component with native fallback and cancellable receipts. */
|
|
412651
|
+
function planRouter(ctx, official, native) {
|
|
412382
412652
|
const transfers = /* @__PURE__ */ new Map();
|
|
412383
412653
|
/** @private @author ddj 2026年10月08号 @param all Plugin disposal. Cancel scope-stale transfers, not transfers merely hidden by the editor mount. */
|
|
412384
412654
|
function cancel(all = false) {
|
|
@@ -412408,7 +412678,7 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
|
|
|
412408
412678
|
function openNative() {
|
|
412409
412679
|
if (typeof official.service.openResource !== "function") return;
|
|
412410
412680
|
try {
|
|
412411
|
-
|
|
412681
|
+
native(address, nativeOptions(tab));
|
|
412412
412682
|
tab?.actions?.close?.();
|
|
412413
412683
|
} catch (error) {
|
|
412414
412684
|
log$2.warn("计划官方查看器回退失败:" + String(error));
|
|
@@ -412475,18 +412745,21 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
|
|
|
412475
412745
|
canOpen: isPlanAddress,
|
|
412476
412746
|
title: () => "计划预览"
|
|
412477
412747
|
});
|
|
412478
|
-
const
|
|
412748
|
+
const direct = installPlanDirect(ctx, official.service);
|
|
412749
|
+
const router = planRouter(ctx, official, direct.native);
|
|
412479
412750
|
try {
|
|
412480
412751
|
const offBody = ctx.slots.inject(OFFICIAL_SLOT_NAME, () => ctx.slots.register({
|
|
412481
412752
|
name: OFFICIAL_SLOT_NAME,
|
|
412482
412753
|
key: CLAIM_ID
|
|
412483
412754
|
}, router.body));
|
|
412484
412755
|
return () => {
|
|
412756
|
+
direct.dispose();
|
|
412485
412757
|
router.dispose();
|
|
412486
412758
|
if (typeof offBody === "function") offBody();
|
|
412487
412759
|
offType();
|
|
412488
412760
|
};
|
|
412489
412761
|
} catch (error) {
|
|
412762
|
+
direct.dispose();
|
|
412490
412763
|
router.dispose();
|
|
412491
412764
|
offType();
|
|
412492
412765
|
throw error;
|
|
@@ -412502,7 +412775,7 @@ td.UniverTableCell, th.UniverTableCell{mso-padding-alt:0cm 5.4pt 0cm 5.4pt;}
|
|
|
412502
412775
|
/** @private @author ddj 2026年10月08号 @param id Owning session. @returns Valid borrowed feed, or undefined on older/incompatible services. */
|
|
412503
412776
|
function sourceOf(id) {
|
|
412504
412777
|
try {
|
|
412505
|
-
const sessions =
|
|
412778
|
+
const sessions = optionalService(ctx, "sessions");
|
|
412506
412779
|
if (!id || typeof sessions?.binding !== "function") return void 0;
|
|
412507
412780
|
const feed = sessions.binding(id)?.eventSource;
|
|
412508
412781
|
return typeof feed?.getSnapshot === "function" && typeof feed.subscribe === "function" ? feed : void 0;
|
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:
|
|
4
|
-
sha256:
|
|
3
|
+
size: 35694514 bytes
|
|
4
|
+
sha256: 7fa559b003ee01e2e53a8698796d4a4e567f8e63d0b6d00548272a0057026c6d
|
package/lib/index.js
CHANGED
|
@@ -7652,6 +7652,27 @@ function reviewPage(cwd, cursor = 0, history = false, path) {
|
|
|
7652
7652
|
/**
|
|
7653
7653
|
* @public
|
|
7654
7654
|
* @author ddj 2026年10月31号
|
|
7655
|
+
* @description: 该会话里由官方记录判为「整文件新增」的文件身份集合。待审条目命中时基线要置空:
|
|
7656
|
+
* 文件是本会话新建的,累计基线只是会话自己的中间产物,拿它做旧侧会把新增文件渲染成一堆修改块。
|
|
7657
|
+
* @param cwd 迁移后的工作区
|
|
7658
|
+
* @param sessionId 会话
|
|
7659
|
+
* @returns 整文件新增的文件身份集合
|
|
7660
|
+
*/
|
|
7661
|
+
function addedFiles(cwd, sessionId) {
|
|
7662
|
+
try {
|
|
7663
|
+
const db = dbFor(cwd);
|
|
7664
|
+
if (!db.prepare("SELECT 1 FROM sqlite_master WHERE name='review_files' AND type='table'").get()) return /* @__PURE__ */ new Set();
|
|
7665
|
+
const rows = db.prepare(`SELECT identity FROM (SELECT identity,
|
|
7666
|
+
json_extract(diff,'$.before') AS before, ROW_NUMBER() OVER (PARTITION BY identity ORDER BY turn, seq) AS rank
|
|
7667
|
+
FROM review_files WHERE session_id=?) WHERE rank=1 AND before=0`).all(sessionId);
|
|
7668
|
+
return new Set(rows.map((row) => String(row.identity)));
|
|
7669
|
+
} catch {
|
|
7670
|
+
return /* @__PURE__ */ new Set();
|
|
7671
|
+
}
|
|
7672
|
+
}
|
|
7673
|
+
/**
|
|
7674
|
+
* @public
|
|
7675
|
+
* @author ddj 2026年10月31号
|
|
7655
7676
|
* @description: 该文件是否还有未处理的官方待审改动。缺失文件预览据此区分「需要去差异栏 Keep 结案」
|
|
7656
7677
|
* 与「记录都已处理、可直接关闭标签页」,避免已结案的删除文件继续提示待审。
|
|
7657
7678
|
* @param cwd Migrated workspace
|
|
@@ -9297,10 +9318,22 @@ function capabilityOf(entry, flags) {
|
|
|
9297
9318
|
* @author ddj 2026年10月29号
|
|
9298
9319
|
* @description: 供详情接口复用的差异构造。
|
|
9299
9320
|
* @param entry 待审条目
|
|
9321
|
+
* @param baselineEmpty 基线置空:文件由本次会话新建,旧基线只是会话自己的中间产物
|
|
9300
9322
|
* @returns 官方差异
|
|
9301
9323
|
*/
|
|
9302
|
-
function diffOf(entry) {
|
|
9303
|
-
return textDiff(entry.path, entry.path, entry.oldText, entry.newText);
|
|
9324
|
+
function diffOf(entry, baselineEmpty = false) {
|
|
9325
|
+
if (!baselineEmpty) return textDiff(entry.path, entry.path, entry.oldText, entry.newText);
|
|
9326
|
+
if (entry.newText === "") return textDiff(entry.path, entry.path, "", "");
|
|
9327
|
+
const diff = textDiff(entry.path, entry.path, "", entry.newText);
|
|
9328
|
+
if (diff.kind !== "text") return diff;
|
|
9329
|
+
return {
|
|
9330
|
+
...diff,
|
|
9331
|
+
hunks: diff.hunks.map((hunk) => ({
|
|
9332
|
+
...hunk,
|
|
9333
|
+
oldLines: 0,
|
|
9334
|
+
lines: hunk.lines.filter((line) => line !== "-")
|
|
9335
|
+
}))
|
|
9336
|
+
};
|
|
9304
9337
|
}
|
|
9305
9338
|
//#endregion
|
|
9306
9339
|
//#region src/pending/persist.ts
|
|
@@ -9560,10 +9593,11 @@ function flagsOfRow(row) {
|
|
|
9560
9593
|
* @author ddj 2026年10月29号
|
|
9561
9594
|
* @description: 统计一次差异的增删行数;非文本差异统一记 0/0。
|
|
9562
9595
|
* @param entry 待审条目
|
|
9596
|
+
* @param baselineEmpty 基线置空(新建文件按全量新增计)
|
|
9563
9597
|
* @returns 增删行数
|
|
9564
9598
|
*/
|
|
9565
|
-
function countsOf(entry) {
|
|
9566
|
-
const diff = diffOf(entry);
|
|
9599
|
+
function countsOf(entry, baselineEmpty = false) {
|
|
9600
|
+
const diff = diffOf(entry, baselineEmpty);
|
|
9567
9601
|
if (diff.kind !== "text") return {
|
|
9568
9602
|
added: 0,
|
|
9569
9603
|
deleted: 0
|
|
@@ -9633,11 +9667,12 @@ function foldWrite(stored, write) {
|
|
|
9633
9667
|
* @author ddj 2026年10月29号
|
|
9634
9668
|
* @description: 条目转列表投影,已解决条目仍保留但不可再决定。
|
|
9635
9669
|
* @param stored 条目与标记
|
|
9670
|
+
* @param baselineEmpty 基线置空(新建文件按全量新增计)
|
|
9636
9671
|
* @returns 列表项
|
|
9637
9672
|
*/
|
|
9638
|
-
function itemOf(stored) {
|
|
9673
|
+
function itemOf(stored, baselineEmpty = false) {
|
|
9639
9674
|
const { entry, flags } = stored;
|
|
9640
|
-
const counts = countsOf(entry);
|
|
9675
|
+
const counts = countsOf(entry, baselineEmpty);
|
|
9641
9676
|
const item = {
|
|
9642
9677
|
id: entry.id,
|
|
9643
9678
|
path: entry.path,
|
|
@@ -9662,12 +9697,13 @@ function itemOf(stored) {
|
|
|
9662
9697
|
* @description: 读一页待审条目,按更新时间升序;会话过滤走血缘集合。
|
|
9663
9698
|
* @param cwd 工作区
|
|
9664
9699
|
* @param sessionId 当前会话
|
|
9700
|
+
* @param wholeAdd 本次会话新建的文件身份集合,命中时基线置空
|
|
9665
9701
|
* @returns 列表
|
|
9666
9702
|
*/
|
|
9667
|
-
function loadPage(cwd, sessionId) {
|
|
9703
|
+
function loadPage(cwd, sessionId, wholeAdd) {
|
|
9668
9704
|
const all = loadEntries(cwd);
|
|
9669
9705
|
const visible = sessionId ? all.filter((stored) => stored.entry.sessionIds.includes(sessionId)) : all;
|
|
9670
|
-
const items = visible.map(itemOf);
|
|
9706
|
+
const items = visible.map((stored) => itemOf(stored, wholeAdd?.has(stored.entry.id) === true));
|
|
9671
9707
|
const gaps = [];
|
|
9672
9708
|
for (const stored of visible) if (stored.flags.oversized) gaps.push(`${stored.entry.path}: 文本超出上限,仅保留标记`);
|
|
9673
9709
|
return {
|
|
@@ -9682,14 +9718,15 @@ function loadPage(cwd, sessionId) {
|
|
|
9682
9718
|
* @description: 读单条详情,包含可交给现有面板渲染的差异与块决定。
|
|
9683
9719
|
* @param cwd 工作区
|
|
9684
9720
|
* @param id 条目身份
|
|
9721
|
+
* @param baselineEmpty 基线置空(新建文件按全量新增渲染)
|
|
9685
9722
|
* @returns 详情,条目不存在时 null
|
|
9686
9723
|
*/
|
|
9687
|
-
function loadDetail(cwd, id) {
|
|
9724
|
+
function loadDetail(cwd, id, baselineEmpty = false) {
|
|
9688
9725
|
const stored = loadEntries(cwd).find((item) => item.entry.id === id);
|
|
9689
9726
|
if (!stored) return null;
|
|
9690
|
-
const diff = diffOf(stored.entry);
|
|
9727
|
+
const diff = diffOf(stored.entry, baselineEmpty);
|
|
9691
9728
|
return {
|
|
9692
|
-
item: itemOf(stored),
|
|
9729
|
+
item: itemOf(stored, baselineEmpty),
|
|
9693
9730
|
diff,
|
|
9694
9731
|
blocks: blockStatesOf(stored.entry),
|
|
9695
9732
|
capability: capabilityOf(stored.entry, stored.flags)
|
|
@@ -10249,7 +10286,11 @@ function pendingCovers(cwd, path) {
|
|
|
10249
10286
|
* @returns 合并后的页
|
|
10250
10287
|
*/
|
|
10251
10288
|
function mergedPage(official, pending, cwd) {
|
|
10252
|
-
const covered = new Set(
|
|
10289
|
+
const covered = /* @__PURE__ */ new Set();
|
|
10290
|
+
for (const item of pending.items) {
|
|
10291
|
+
covered.add(pathKey$1(item.path));
|
|
10292
|
+
covered.add(pathKey$1(item.id));
|
|
10293
|
+
}
|
|
10253
10294
|
const kept = official.items.filter((item) => !covered.has(pathKey$1(item.currentPath)) && !covered.has(pathKey$1(item.path)));
|
|
10254
10295
|
return {
|
|
10255
10296
|
items: [...pending.items.map((item) => pendingItemOf(item, cwd)), ...kept],
|
|
@@ -16154,7 +16195,7 @@ function reviewHandlers(ctx, registry) {
|
|
|
16154
16195
|
*/
|
|
16155
16196
|
function pendingPageOf(cwd, sessionId) {
|
|
16156
16197
|
try {
|
|
16157
|
-
const page = loadPage(cwd, sessionId);
|
|
16198
|
+
const page = loadPage(cwd, sessionId, addedFiles(cwd, sessionId));
|
|
16158
16199
|
return page.items.some((item) => item.settled === true) ? {
|
|
16159
16200
|
...page,
|
|
16160
16201
|
items: page.items.filter((item) => item.settled !== true)
|
|
@@ -16201,7 +16242,8 @@ function reviewHandlers(ctx, registry) {
|
|
|
16201
16242
|
const scope = await scopeOf(args.sessionId);
|
|
16202
16243
|
const cwd = scope.cwd;
|
|
16203
16244
|
if (args.id.startsWith("pending:")) {
|
|
16204
|
-
const
|
|
16245
|
+
const entryId = args.id.slice(8);
|
|
16246
|
+
const detail = loadDetail(cwd, entryId, addedFiles(cwd, args.sessionId).has(entryId));
|
|
16205
16247
|
if (!detail) return {
|
|
16206
16248
|
ok: false,
|
|
16207
16249
|
error: "待审条目不存在"
|
|
@@ -16350,7 +16392,7 @@ function readHandlers(kit) {
|
|
|
16350
16392
|
return {
|
|
16351
16393
|
"edrv.pending.list": async (args) => {
|
|
16352
16394
|
const cwd = await kit.read(args.sessionId);
|
|
16353
|
-
const page = loadPage(cwd, args.sessionId);
|
|
16395
|
+
const page = loadPage(cwd, args.sessionId, addedFiles(cwd, args.sessionId));
|
|
16354
16396
|
for (const item of page.items) trackPendingCover(cwd, item.path, true);
|
|
16355
16397
|
return {
|
|
16356
16398
|
ok: true,
|
|
@@ -16358,7 +16400,9 @@ function readHandlers(kit) {
|
|
|
16358
16400
|
};
|
|
16359
16401
|
},
|
|
16360
16402
|
"edrv.pending.detail": async (args) => {
|
|
16361
|
-
const
|
|
16403
|
+
const cwd = await kit.read(args.sessionId);
|
|
16404
|
+
const rawId = pendingRawId(args.id);
|
|
16405
|
+
const detail = loadDetail(cwd, rawId, addedFiles(cwd, args.sessionId).has(rawId));
|
|
16362
16406
|
return detail ? {
|
|
16363
16407
|
ok: true,
|
|
16364
16408
|
detail
|