dsh-edit-diff 0.2.1 → 0.4.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 +73 -8
- package/cordis.patch.yml +10 -11
- package/lib/client.js +673 -454
- package/lib/client.js.map +1 -0
- package/lib/index.d.ts +34 -0
- package/lib/index.js +27 -11
- package/package.json +35 -5
package/lib/client.js
CHANGED
|
@@ -1,456 +1,675 @@
|
|
|
1
|
-
// dsh-edit-diff 浏览器端,标准 dsh client bundle 形态。
|
|
2
|
-
//
|
|
3
|
-
// 通过 window.__ModuleLoader__.load 注册模块表条目,factory 从注入的 require 解析 react 等外部依赖。
|
|
4
|
-
// 默认导出 Cordis 插件对象,由浏览器内核挂载。
|
|
5
|
-
//
|
|
6
|
-
// 接管 edit/write 工具卡片:内置 DiffBlock 对新旧全文逐行染色且不做行级匹配,相同行在红绿两区各渲染一遍。
|
|
7
|
-
// 本插件接管 tool.call.toolview 的 edit/write key,用近线性行级 diff 跳过相同行,替换行做字符级下划线高亮。
|
|
8
|
-
// 视觉对齐内置 DiffBlock/ToolRow。卡片默认收起,长 diff 二次折叠。
|
|
9
|
-
//
|
|
10
|
-
// PTC 模式下 run_code 子调用中的 edit/write 同样显示去重 diff,按 callId 的 :code: 标记识别,从 argsRaw 动态构建。
|
|
11
|
-
|
|
12
1
|
window.__ModuleLoader__.load({
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
2
|
+
id: "dsh-edit-diff",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
Object.defineProperties(exports, {
|
|
7
|
+
__esModule: { value: true },
|
|
8
|
+
[Symbol.toStringTag]: { value: "Module" }
|
|
9
|
+
});
|
|
10
|
+
//#region \0rolldown/runtime.js
|
|
11
|
+
var __create = Object.create;
|
|
12
|
+
var __defProp = Object.defineProperty;
|
|
13
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
14
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
15
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
16
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
17
|
+
var __copyProps = (to, from, except, desc) => {
|
|
18
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
19
|
+
key = keys[i];
|
|
20
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
21
|
+
get: ((k) => from[k]).bind(null, key),
|
|
22
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
return to;
|
|
26
|
+
};
|
|
27
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
|
|
28
|
+
value: mod,
|
|
29
|
+
enumerable: true
|
|
30
|
+
}) : target, mod));
|
|
31
|
+
//#endregion
|
|
32
|
+
let react = require("react");
|
|
33
|
+
react = __toESM(react, 1);
|
|
34
|
+
//#region src/client.ts
|
|
35
|
+
/** 文档对象,非浏览器环境下为 null。 */
|
|
36
|
+
const DOC = typeof document !== "undefined" ? document : null;
|
|
37
|
+
/** 原生 edit 与 write 的参数名,也是未声明覆盖时的默认值。 */
|
|
38
|
+
const DEFAULT_SPEC = {
|
|
39
|
+
pathKey: "file_path",
|
|
40
|
+
oldKey: "old_string",
|
|
41
|
+
newKey: "new_string",
|
|
42
|
+
contentKey: "content"
|
|
43
|
+
};
|
|
44
|
+
/** 额外接管的工具名到参数名,由 settings 的 extraTools 填充。 */
|
|
45
|
+
const extraSpecs = /* @__PURE__ */ new Map();
|
|
46
|
+
/** 把配置条目补全成完整参数名。 */
|
|
47
|
+
function toKeySpec(tool) {
|
|
48
|
+
return {
|
|
49
|
+
pathKey: tool.pathKey ?? DEFAULT_SPEC.pathKey,
|
|
50
|
+
oldKey: tool.oldKey ?? DEFAULT_SPEC.oldKey,
|
|
51
|
+
newKey: tool.newKey ?? DEFAULT_SPEC.newKey,
|
|
52
|
+
contentKey: tool.contentKey ?? DEFAULT_SPEC.contentKey
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/** 取工具名对应的参数名,非本插件接管的工具返回 null。 */
|
|
56
|
+
function specFor(toolName) {
|
|
57
|
+
if (toolName === "edit" || toolName === "write") return DEFAULT_SPEC;
|
|
58
|
+
if (toolName === void 0) return null;
|
|
59
|
+
return extraSpecs.get(toolName) ?? null;
|
|
60
|
+
}
|
|
61
|
+
/** 行级 Myers:前缀/后缀收缩后仅对中间核心区跑 diff,返回 op 数组。 */
|
|
62
|
+
function myersDiff(aList, bList) {
|
|
63
|
+
const N = aList.length;
|
|
64
|
+
const M = bList.length;
|
|
65
|
+
const max = N + M;
|
|
66
|
+
let prev = { 1: 0 };
|
|
67
|
+
const trace = [];
|
|
68
|
+
let dMax = 0;
|
|
69
|
+
let found = false;
|
|
70
|
+
for (let d = 0; d <= max; d++) {
|
|
71
|
+
trace.push({ ...prev });
|
|
72
|
+
const cur = {};
|
|
73
|
+
for (let k = -d; k <= d; k += 2) {
|
|
74
|
+
let x;
|
|
75
|
+
if (k === -d || k !== d && (prev[k - 1] ?? -Infinity) < (prev[k + 1] ?? -Infinity)) x = prev[k + 1] ?? 0;
|
|
76
|
+
else x = (prev[k - 1] ?? -1) + 1;
|
|
77
|
+
let y = x - k;
|
|
78
|
+
while (x < N && y < M && aList[x] === bList[y]) {
|
|
79
|
+
x++;
|
|
80
|
+
y++;
|
|
81
|
+
}
|
|
82
|
+
cur[k] = x;
|
|
83
|
+
if (x >= N && y >= M) {
|
|
84
|
+
dMax = d;
|
|
85
|
+
found = true;
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
prev = cur;
|
|
90
|
+
if (found) break;
|
|
91
|
+
}
|
|
92
|
+
if (!found) {
|
|
93
|
+
const ops = [];
|
|
94
|
+
for (let i = 0; i < N; i++) ops.push({
|
|
95
|
+
type: "del",
|
|
96
|
+
a: i,
|
|
97
|
+
b: -1
|
|
98
|
+
});
|
|
99
|
+
for (let j = 0; j < M; j++) ops.push({
|
|
100
|
+
type: "add",
|
|
101
|
+
a: -1,
|
|
102
|
+
b: j
|
|
103
|
+
});
|
|
104
|
+
return ops;
|
|
105
|
+
}
|
|
106
|
+
const ops = [];
|
|
107
|
+
let x = N;
|
|
108
|
+
let y = M;
|
|
109
|
+
for (let d = dMax; d > 0; d--) {
|
|
110
|
+
const t = trace[d] ?? {};
|
|
111
|
+
const k = x - y;
|
|
112
|
+
let prevK;
|
|
113
|
+
if (k === -d || k !== d && (t[k - 1] ?? -Infinity) < (t[k + 1] ?? -Infinity)) prevK = k + 1;
|
|
114
|
+
else prevK = k - 1;
|
|
115
|
+
const prevX = t[prevK] ?? 0;
|
|
116
|
+
const prevY = prevX - prevK;
|
|
117
|
+
while (x > prevX && y > prevY) {
|
|
118
|
+
ops.push({
|
|
119
|
+
type: "same",
|
|
120
|
+
a: x - 1,
|
|
121
|
+
b: y - 1
|
|
122
|
+
});
|
|
123
|
+
x--;
|
|
124
|
+
y--;
|
|
125
|
+
}
|
|
126
|
+
if (x === prevX) {
|
|
127
|
+
ops.push({
|
|
128
|
+
type: "add",
|
|
129
|
+
a: -1,
|
|
130
|
+
b: y - 1
|
|
131
|
+
});
|
|
132
|
+
y--;
|
|
133
|
+
} else {
|
|
134
|
+
ops.push({
|
|
135
|
+
type: "del",
|
|
136
|
+
a: x - 1,
|
|
137
|
+
b: -1
|
|
138
|
+
});
|
|
139
|
+
x--;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
while (x > 0 && y > 0) {
|
|
143
|
+
ops.push({
|
|
144
|
+
type: "same",
|
|
145
|
+
a: x - 1,
|
|
146
|
+
b: y - 1
|
|
147
|
+
});
|
|
148
|
+
x--;
|
|
149
|
+
y--;
|
|
150
|
+
}
|
|
151
|
+
ops.reverse();
|
|
152
|
+
return ops;
|
|
153
|
+
}
|
|
154
|
+
/** 行级 diff:线性前缀/后缀收缩,仅对中间核心区跑 Myers。 */
|
|
155
|
+
function lineDiff(aLines, bLines) {
|
|
156
|
+
let p = 0;
|
|
157
|
+
const minLen = Math.min(aLines.length, bLines.length);
|
|
158
|
+
while (p < minLen && aLines[p] === bLines[p]) p++;
|
|
159
|
+
let s = 0;
|
|
160
|
+
const al = aLines.length;
|
|
161
|
+
const bl = bLines.length;
|
|
162
|
+
while (s < minLen - p && aLines[al - 1 - s] === bLines[bl - 1 - s]) s++;
|
|
163
|
+
const ops = [];
|
|
164
|
+
for (let i = 0; i < p; i++) ops.push({
|
|
165
|
+
type: "same",
|
|
166
|
+
a: i,
|
|
167
|
+
b: i
|
|
168
|
+
});
|
|
169
|
+
const aMid = aLines.slice(p, al - s);
|
|
170
|
+
const bMid = bLines.slice(p, bl - s);
|
|
171
|
+
for (const op of myersDiff(aMid, bMid)) ops.push({
|
|
172
|
+
type: op.type,
|
|
173
|
+
a: op.a === -1 ? -1 : op.a + p,
|
|
174
|
+
b: op.b === -1 ? -1 : op.b + p
|
|
175
|
+
});
|
|
176
|
+
for (let i = 0; i < s; i++) ops.push({
|
|
177
|
+
type: "same",
|
|
178
|
+
a: al - s + i,
|
|
179
|
+
b: bl - s + i
|
|
180
|
+
});
|
|
181
|
+
return ops;
|
|
182
|
+
}
|
|
183
|
+
/** 把字符级 op 汇总成高亮区间 [start, end)。 */
|
|
184
|
+
function charRanges(ops) {
|
|
185
|
+
const delIdx = [];
|
|
186
|
+
const addIdx = [];
|
|
187
|
+
for (const op of ops) if (op.type === "del") delIdx.push(op.a);
|
|
188
|
+
else if (op.type === "add") addIdx.push(op.b);
|
|
189
|
+
const mk = (idx) => {
|
|
190
|
+
if (idx.length === 0) return [];
|
|
191
|
+
idx.sort((a, b) => a - b);
|
|
192
|
+
const out = [];
|
|
193
|
+
let start = idx[0];
|
|
194
|
+
let end = start;
|
|
195
|
+
for (let i = 1; i < idx.length; i++) {
|
|
196
|
+
const value = idx[i];
|
|
197
|
+
if (value === end + 1) end = value;
|
|
198
|
+
else {
|
|
199
|
+
out.push([start, end + 1]);
|
|
200
|
+
start = value;
|
|
201
|
+
end = value;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
out.push([start, end + 1]);
|
|
205
|
+
return out;
|
|
206
|
+
};
|
|
207
|
+
return {
|
|
208
|
+
del: mk(delIdx),
|
|
209
|
+
add: mk(addIdx)
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
function lineHighlight(oldStr, newStr) {
|
|
213
|
+
return charRanges(myersDiff([...oldStr], [...newStr]));
|
|
214
|
+
}
|
|
215
|
+
function contentLines(text) {
|
|
216
|
+
if (text === "") return [];
|
|
217
|
+
return (text.endsWith("\n") ? text.slice(0, -1) : text).split("\n");
|
|
218
|
+
}
|
|
219
|
+
/** 从 ops 构建渲染行,仅含真实差异;替换对删/增行数相等做行内字符高亮。 */
|
|
220
|
+
function buildDiffLines(oldLines, newLines) {
|
|
221
|
+
const ops = lineDiff(oldLines, newLines);
|
|
222
|
+
const out = [];
|
|
223
|
+
let i = 0;
|
|
224
|
+
while (i < ops.length) {
|
|
225
|
+
if (ops[i]?.type === "same") {
|
|
226
|
+
i++;
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
const dStart = i;
|
|
230
|
+
while (i < ops.length && ops[i]?.type === "del") i++;
|
|
231
|
+
const addStart = i;
|
|
232
|
+
while (i < ops.length && ops[i]?.type === "add") i++;
|
|
233
|
+
const delOps = ops.slice(dStart, addStart);
|
|
234
|
+
const addOps = ops.slice(addStart, i);
|
|
235
|
+
if (delOps.length === addOps.length && delOps.length > 0) for (let k = 0; k < delOps.length; k++) {
|
|
236
|
+
const del = delOps[k];
|
|
237
|
+
const add = addOps[k];
|
|
238
|
+
if (del === void 0 || add === void 0) continue;
|
|
239
|
+
const oldText = oldLines[del.a] ?? "";
|
|
240
|
+
const newText = newLines[add.b] ?? "";
|
|
241
|
+
const hl = lineHighlight(oldText, newText);
|
|
242
|
+
out.push({
|
|
243
|
+
kind: "del",
|
|
244
|
+
text: oldText,
|
|
245
|
+
hl: hl.del
|
|
246
|
+
});
|
|
247
|
+
out.push({
|
|
248
|
+
kind: "add",
|
|
249
|
+
text: newText,
|
|
250
|
+
hl: hl.add
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
for (const op of delOps) out.push({
|
|
255
|
+
kind: "del",
|
|
256
|
+
text: oldLines[op.a] ?? "",
|
|
257
|
+
hl: []
|
|
258
|
+
});
|
|
259
|
+
for (const op of addOps) out.push({
|
|
260
|
+
kind: "add",
|
|
261
|
+
text: newLines[op.b] ?? "",
|
|
262
|
+
hl: []
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return out;
|
|
267
|
+
}
|
|
268
|
+
/** 收窄外部 diff 数据,字段不合法时返回 null。 */
|
|
269
|
+
function narrowDiffs(diffs) {
|
|
270
|
+
if (!Array.isArray(diffs) || diffs.length === 0) return null;
|
|
271
|
+
const out = [];
|
|
272
|
+
for (const item of diffs) {
|
|
273
|
+
if (item === null || typeof item !== "object") return null;
|
|
274
|
+
const record = item;
|
|
275
|
+
if (typeof record.path !== "string") return null;
|
|
276
|
+
if (record.oldText !== null && typeof record.oldText !== "string") return null;
|
|
277
|
+
if (typeof record.newText !== "string") return null;
|
|
278
|
+
out.push({
|
|
279
|
+
path: record.path,
|
|
280
|
+
oldText: record.oldText,
|
|
281
|
+
newText: record.newText
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
return out;
|
|
285
|
+
}
|
|
286
|
+
/** 从卡片数据块中取出 diff,取不到时回退到参数意图。 */
|
|
287
|
+
function extractDiffs(block, spec) {
|
|
288
|
+
const done = "kind" in block;
|
|
289
|
+
const subCall = block.parentCallId !== void 0 || typeof block.callId === "string" && block.callId.includes(":code:");
|
|
290
|
+
if (done && !subCall && block.isError !== true) {
|
|
291
|
+
const meta = block.meta;
|
|
292
|
+
if (meta !== null && typeof meta === "object" && !Array.isArray(meta)) {
|
|
293
|
+
const diffs = meta.diffs;
|
|
294
|
+
if (Array.isArray(diffs) && diffs.length > 0) {
|
|
295
|
+
const narrowed = narrowDiffs(diffs);
|
|
296
|
+
if (narrowed !== null) return narrowed;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
if (done && !subCall && block.isError === true) return null;
|
|
301
|
+
const rawArgs = (block.call !== void 0 ? block.call.argsRaw : block.argsRaw) ?? "";
|
|
302
|
+
const argsRaw = typeof rawArgs === "string" ? rawArgs : "";
|
|
303
|
+
if (spec !== null) try {
|
|
304
|
+
const args = JSON.parse(argsRaw);
|
|
305
|
+
if (args !== null && typeof args === "object") {
|
|
306
|
+
const record = args;
|
|
307
|
+
const filePath = record[spec.pathKey];
|
|
308
|
+
if (typeof filePath === "string" && filePath !== "") {
|
|
309
|
+
const oldText = record[spec.oldKey];
|
|
310
|
+
const newText = record[spec.newKey];
|
|
311
|
+
if (typeof oldText === "string" && typeof newText === "string") return narrowDiffs([{
|
|
312
|
+
path: filePath,
|
|
313
|
+
oldText: oldText || null,
|
|
314
|
+
newText
|
|
315
|
+
}]);
|
|
316
|
+
if (typeof record[spec.contentKey] === "string") return narrowDiffs([{
|
|
317
|
+
path: filePath,
|
|
318
|
+
oldText: null,
|
|
319
|
+
newText: record[spec.contentKey]
|
|
320
|
+
}]);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
} catch {}
|
|
324
|
+
if (!done) {
|
|
325
|
+
const callView = block.callView;
|
|
326
|
+
if (callView === null || typeof callView !== "object" || callView.card !== "diff") return null;
|
|
327
|
+
return narrowDiffs(callView.diffs);
|
|
328
|
+
}
|
|
329
|
+
const resultView = block.resultView;
|
|
330
|
+
if (resultView === null || typeof resultView !== "object" || resultView.card !== "diff") return null;
|
|
331
|
+
return narrowDiffs(resultView.diffs);
|
|
332
|
+
}
|
|
333
|
+
/** 汇总工具结果文本。 */
|
|
334
|
+
function resultText(block) {
|
|
335
|
+
if (!("kind" in block)) return "";
|
|
336
|
+
const parts = [];
|
|
337
|
+
const content = Array.isArray(block.content) ? block.content : [];
|
|
338
|
+
for (const item of content) {
|
|
339
|
+
const record = item !== null && typeof item === "object" ? item : null;
|
|
340
|
+
const isText = record !== null && record.type === "text" && typeof record.text === "string";
|
|
341
|
+
parts.push(isText ? record.text : JSON.stringify(item));
|
|
342
|
+
}
|
|
343
|
+
if (parts.length === 0 && block.error !== void 0) parts.push(String(block.error.name) + ": " + String(block.error.code));
|
|
344
|
+
return parts.join("\n");
|
|
345
|
+
}
|
|
346
|
+
function firstLine(t) {
|
|
347
|
+
const n = t.indexOf("\n");
|
|
348
|
+
return n === -1 ? t : t.slice(0, n);
|
|
349
|
+
}
|
|
350
|
+
/** 由数据块推导卡片模型。 */
|
|
351
|
+
function buildCardModel(block, toolName, cwd) {
|
|
352
|
+
const done = "kind" in block;
|
|
353
|
+
const spec = specFor(toolName);
|
|
354
|
+
const rawArgs = done ? block.call?.argsRaw ?? "" : block.argsRaw ?? "";
|
|
355
|
+
const argsRaw = typeof rawArgs === "string" ? rawArgs : "";
|
|
356
|
+
const state = !done ? "running" : block.error?.code === "interrupted" ? "stopped" : block.isError === true ? "error" : "ok";
|
|
357
|
+
let kind = toolName === "edit" ? "edit" : toolName === "write" ? "write" : null;
|
|
358
|
+
let filePath;
|
|
359
|
+
let summary = "";
|
|
360
|
+
try {
|
|
361
|
+
const parsed = JSON.parse(argsRaw);
|
|
362
|
+
if (parsed !== null && typeof parsed === "object") {
|
|
363
|
+
const record = parsed;
|
|
364
|
+
const p = (spec === null ? void 0 : record[spec.pathKey]) ?? record.path;
|
|
365
|
+
if (typeof p === "string" && p !== "") filePath = firstLine(p);
|
|
366
|
+
if (kind === null && spec !== null) {
|
|
367
|
+
const oldText = record[spec.oldKey];
|
|
368
|
+
const newText = record[spec.newKey];
|
|
369
|
+
if (typeof oldText === "string" && typeof newText === "string") kind = "edit";
|
|
370
|
+
else if (typeof record[spec.contentKey] === "string") kind = "write";
|
|
371
|
+
}
|
|
372
|
+
const values = Object.values(record).filter((v) => typeof v === "string" && v !== "");
|
|
373
|
+
summary = values.length > 0 ? firstLine(values[0]) : argsRaw;
|
|
374
|
+
} else summary = firstLine(argsRaw);
|
|
375
|
+
} catch {
|
|
376
|
+
summary = firstLine(argsRaw);
|
|
377
|
+
}
|
|
378
|
+
if (filePath !== void 0) summary = filePath;
|
|
379
|
+
if (filePath !== void 0 && cwd !== void 0) {
|
|
380
|
+
const root = cwd.replace(/[/\\]+$/, "");
|
|
381
|
+
if (filePath.startsWith(root + "/") || filePath.startsWith(root + "\\")) summary = filePath.slice(root.length + 1);
|
|
382
|
+
}
|
|
383
|
+
const title = kind === "edit" ? "Edit" : kind === "write" ? "Write" : toolName ?? "";
|
|
384
|
+
const diffs = extractDiffs(block, spec);
|
|
385
|
+
const output = done ? resultText(block) || null : null;
|
|
386
|
+
return {
|
|
387
|
+
title,
|
|
388
|
+
summary,
|
|
389
|
+
filePath,
|
|
390
|
+
state,
|
|
391
|
+
diffs,
|
|
392
|
+
output
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
function DiffCard(props) {
|
|
396
|
+
const { block, cwd, openFile, inspect, toolName } = props;
|
|
397
|
+
const [open, setOpen] = react.useState(false);
|
|
398
|
+
const [revealed, setRevealed] = react.useState(false);
|
|
399
|
+
const [copied, setCopied] = react.useState(false);
|
|
400
|
+
const m = buildCardModel(block, toolName, cwd);
|
|
401
|
+
const fileLines = m.diffs === null ? null : m.diffs.map((d) => ({
|
|
402
|
+
path: d.path,
|
|
403
|
+
lines: buildDiffLines(contentLines(d.oldText ?? ""), contentLines(d.newText))
|
|
404
|
+
}));
|
|
405
|
+
const displayLines = fileLines !== null && fileLines.some((f) => f.lines.length > 0) ? fileLines : null;
|
|
406
|
+
const addCount = displayLines === null ? 0 : displayLines.reduce((sum, f) => sum + f.lines.filter((l) => l.kind === "add").length, 0);
|
|
407
|
+
const delCount = displayLines === null ? 0 : displayLines.reduce((sum, f) => sum + f.lines.filter((l) => l.kind === "del").length, 0);
|
|
408
|
+
const files = m.diffs === null ? 0 : m.diffs.length;
|
|
409
|
+
const expandable = displayLines !== null || m.output !== null;
|
|
410
|
+
const status = m.state === "running" ? "运行中" : m.state === "error" ? "失败" : m.state === "stopped" ? "已中断" : null;
|
|
411
|
+
const failureSummary = m.state === "error" && m.output !== null ? firstLine(m.output) : null;
|
|
412
|
+
const summaryText = failureSummary !== null ? failureSummary : m.summary || m.title;
|
|
413
|
+
const fileLink = m.filePath !== void 0 && openFile !== void 0 && failureSummary === null;
|
|
414
|
+
const MAX = 14;
|
|
415
|
+
let flatLines = null;
|
|
416
|
+
let hidden = 0;
|
|
417
|
+
if (displayLines !== null && open) {
|
|
418
|
+
const rows = [];
|
|
419
|
+
for (const f of displayLines) {
|
|
420
|
+
rows.push({
|
|
421
|
+
kind: "path",
|
|
422
|
+
text: f.path,
|
|
423
|
+
hl: []
|
|
424
|
+
});
|
|
425
|
+
rows.push(...f.lines);
|
|
426
|
+
}
|
|
427
|
+
hidden = rows.length - MAX;
|
|
428
|
+
if (hidden < 0) hidden = 0;
|
|
429
|
+
if (hidden > 0 && !revealed) {
|
|
430
|
+
const headN = Math.ceil(MAX / 2);
|
|
431
|
+
flatLines = [
|
|
432
|
+
...rows.slice(0, headN),
|
|
433
|
+
{
|
|
434
|
+
kind: "mid",
|
|
435
|
+
text: "⋯ 展开其余 " + hidden + " 行差异",
|
|
436
|
+
hl: []
|
|
437
|
+
},
|
|
438
|
+
...rows.slice(rows.length - 7)
|
|
439
|
+
];
|
|
440
|
+
} else flatLines = rows;
|
|
441
|
+
}
|
|
442
|
+
const onToggle = () => {
|
|
443
|
+
setOpen((v) => !v);
|
|
444
|
+
setRevealed(false);
|
|
445
|
+
};
|
|
446
|
+
const onOpenFile = (event) => {
|
|
447
|
+
event.stopPropagation();
|
|
448
|
+
if (m.filePath !== void 0 && openFile !== void 0) openFile(m.filePath);
|
|
449
|
+
};
|
|
450
|
+
const onCopy = () => {
|
|
451
|
+
if (copied || flatLines === null || flatLines.length === 0) return;
|
|
452
|
+
copyTextIntoClipboard(flatLines.map((l) => l.kind === "del" ? "- " + l.text : l.kind === "add" ? "+ " + l.text : l.text).join("\n"));
|
|
453
|
+
setCopied(true);
|
|
454
|
+
const timeout = timerRef.timeout;
|
|
455
|
+
if (timeout !== null) timeout(() => setCopied(false), 1200);
|
|
456
|
+
};
|
|
457
|
+
const rowType = m.title === "Write" ? "write" : m.title === "Edit" ? "edit" : "file";
|
|
458
|
+
return react.createElement("div", {
|
|
459
|
+
className: "edd-root",
|
|
460
|
+
"data-variant": rowType,
|
|
461
|
+
"data-state": m.state
|
|
462
|
+
}, status !== null && react.createElement("span", { className: "edd-vh" }, status), react.createElement("div", {
|
|
463
|
+
className: "edd-row",
|
|
464
|
+
onClick: onToggle,
|
|
465
|
+
role: "button",
|
|
466
|
+
tabIndex: expandable ? 0 : -1,
|
|
467
|
+
"aria-expanded": open
|
|
468
|
+
}, react.createElement("span", { className: "edd-icon" }, "✎"), react.createElement("span", { className: "edd-title" }, m.title), summaryText !== "" && react.createElement(react.Fragment, null, react.createElement("span", {
|
|
469
|
+
className: "edd-sep",
|
|
470
|
+
"aria-hidden": true
|
|
471
|
+
}), fileLink ? react.createElement("button", {
|
|
472
|
+
type: "button",
|
|
473
|
+
className: "edd-filelink",
|
|
474
|
+
onClick: onOpenFile
|
|
475
|
+
}, summaryText) : react.createElement("span", { className: "edd-summary" + (failureSummary !== null ? " edd-err" : "") }, summaryText)), expandable && react.createElement("span", { className: "edd-chev" + (open ? " edd-chev-open" : "") }, "›")), react.createElement("div", { className: "edd-bodyWrap" }, flatLines === null ? null : react.createElement("div", { className: "edd-body" }, react.createElement("button", {
|
|
476
|
+
type: "button",
|
|
477
|
+
className: "edd-copy",
|
|
478
|
+
onClick: onCopy
|
|
479
|
+
}, copied ? "复制成功" : "复制"), flatLines.map((line, idx) => {
|
|
480
|
+
if (line.kind === "mid") return react.createElement("button", {
|
|
481
|
+
key: idx,
|
|
482
|
+
type: "button",
|
|
483
|
+
className: "edd-expand",
|
|
484
|
+
onClick: () => setRevealed(true)
|
|
485
|
+
}, line.text);
|
|
486
|
+
const prefix = line.kind === "del" ? "-" : line.kind === "add" ? "+" : "";
|
|
487
|
+
const kids = [];
|
|
488
|
+
if (prefix !== "") kids.push(react.createElement("span", {
|
|
489
|
+
key: "p",
|
|
490
|
+
className: "edd-marker"
|
|
491
|
+
}, prefix));
|
|
492
|
+
if (line.kind === "del" || line.kind === "add") {
|
|
493
|
+
if (line.text.length === 0) kids.push(react.createElement("span", {
|
|
494
|
+
key: "e",
|
|
495
|
+
className: "edd-empty"
|
|
496
|
+
}, "␣"));
|
|
497
|
+
else if (line.hl.length > 0) {
|
|
498
|
+
let last = 0;
|
|
499
|
+
for (const range of line.hl) {
|
|
500
|
+
if (range[0] > last) kids.push(react.createElement("span", {
|
|
501
|
+
key: "t" + last,
|
|
502
|
+
className: "edd-plain"
|
|
503
|
+
}, line.text.slice(last, range[0])));
|
|
504
|
+
kids.push(react.createElement("span", {
|
|
505
|
+
key: "h" + range[0],
|
|
506
|
+
className: "edd-hl"
|
|
507
|
+
}, line.text.slice(range[0], range[1])));
|
|
508
|
+
last = range[1];
|
|
509
|
+
}
|
|
510
|
+
if (last < line.text.length) kids.push(react.createElement("span", {
|
|
511
|
+
key: "t" + last,
|
|
512
|
+
className: "edd-plain"
|
|
513
|
+
}, line.text.slice(last)));
|
|
514
|
+
} else kids.push(react.createElement("span", {
|
|
515
|
+
key: "t",
|
|
516
|
+
className: "edd-plain"
|
|
517
|
+
}, line.text));
|
|
518
|
+
} else kids.push(react.createElement("span", {
|
|
519
|
+
key: "t",
|
|
520
|
+
className: "edd-plain"
|
|
521
|
+
}, line.text));
|
|
522
|
+
return react.createElement("div", {
|
|
523
|
+
key: idx,
|
|
524
|
+
className: "edd-line " + (line.kind === "del" ? "edd-del" : line.kind === "add" ? "edd-add" : line.kind === "path" ? "edd-path" : "edd-plain-line")
|
|
525
|
+
}, kids);
|
|
526
|
+
})), open && m.output !== null && flatLines === null && react.createElement("div", { className: "edd-io" }, react.createElement("span", { className: "edd-iolabel" }, "OUT"), react.createElement("span", { className: "edd-iotext" }, m.output)), open && (flatLines !== null && flatLines.length > 0 || m.output !== null) && react.createElement("div", { className: "edd-foot" }, "└ +" + addCount + " -" + delCount + " · " + files + " file" + (files === 1 ? "" : "s")), open && react.createElement("div", { className: "edd-actions" }, inspect !== void 0 && (flatLines !== null && flatLines.length > 0 || m.output !== null) && react.createElement("button", {
|
|
527
|
+
type: "button",
|
|
528
|
+
className: "edd-inspect",
|
|
529
|
+
onClick: inspect
|
|
530
|
+
}, "Inspect"))));
|
|
531
|
+
}
|
|
532
|
+
/** 宿主 timer 服务的时间回调,未注入时为 null。 */
|
|
533
|
+
const timerRef = { timeout: null };
|
|
534
|
+
function copyTextIntoClipboard(txt) {
|
|
535
|
+
if (typeof navigator !== "undefined" && navigator.clipboard !== void 0 && navigator.clipboard.writeText !== void 0) {
|
|
536
|
+
navigator.clipboard.writeText(txt).catch(() => {});
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
if (DOC === null) return;
|
|
540
|
+
const ta = DOC.createElement("textarea");
|
|
541
|
+
ta.value = txt;
|
|
542
|
+
ta.style.position = "fixed";
|
|
543
|
+
ta.style.opacity = "0";
|
|
544
|
+
DOC.body.appendChild(ta);
|
|
545
|
+
ta.select();
|
|
546
|
+
try {
|
|
547
|
+
DOC.execCommand("copy");
|
|
548
|
+
} catch {}
|
|
549
|
+
DOC.body.removeChild(ta);
|
|
550
|
+
}
|
|
551
|
+
const CSS = [
|
|
552
|
+
".edd-root{box-sizing:border-box;color:var(--dsw-alias-label-primary);}",
|
|
553
|
+
".edd-vh{position:absolute;width:1px;height:1px;clip:rect(0 0 0 0);overflow:hidden;white-space:nowrap;}",
|
|
554
|
+
".edd-row{display:flex;align-items:center;gap:6px;cursor:pointer;min-height:24px;padding:4px 0;font-size:14px;line-height:24px;}",
|
|
555
|
+
".edd-icon{color:var(--dsw-alias-label-secondary);flex:none;font-style:normal;}",
|
|
556
|
+
".edd-title{font-weight:400;flex:none;color:var(--dsw-alias-label-primary);}",
|
|
557
|
+
".edd-sep{flex:none;width:2px;height:2px;border-radius:1px;margin:0 8px;background:var(--dsw-alias-label-caption);}",
|
|
558
|
+
".edd-summary{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:14px;line-height:24px;color:var(--dsw-alias-label-tertiary);text-align:left;}",
|
|
559
|
+
".edd-err{color:var(--dsw-alias-state-error-primary);}",
|
|
560
|
+
".edd-filelink{flex:1 1 auto;min-width:0;margin:0;padding:0;border:none;background:none;font-family:inherit;font-size:14px;line-height:24px;color:var(--dsw-alias-label-secondary);text-decoration:underline;text-decoration-color:var(--dsw-alias-label-quaternary);text-underline-offset:3px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:left;cursor:pointer;}",
|
|
561
|
+
".edd-filelink:hover{color:var(--dsw-alias-label-primary);text-decoration-color:currentColor;}",
|
|
562
|
+
".edd-chev{margin-left:auto;color:var(--dsw-alias-label-secondary);flex:none;transition:transform .12s;display:inline-block;}",
|
|
563
|
+
".edd-chev-open{transform:rotate(90deg);}",
|
|
564
|
+
".edd-bodyWrap{overflow:hidden;}",
|
|
565
|
+
".edd-body{position:relative;margin:8px 0;background:var(--dsw-alias-markdown-code-block);border-radius:12px;font:var(--dsw-font-markdown-code-block);overflow-x:auto;overflow-y:hidden;padding:12px 14px 12px 14px;box-sizing:border-box;}",
|
|
566
|
+
".edd-copy{position:absolute;top:8px;right:12px;z-index:1;background-color:transparent;border:none;padding:0;margin:0;color:var(--dsw-alias-label-secondary);cursor:pointer;font:var(--dsw-font-xs-13);}",
|
|
567
|
+
".edd-copy:hover{color:var(--dsw-alias-label-primary);}",
|
|
568
|
+
".edd-path{color:var(--dsw-alias-label-primary);font-weight:600;padding-right:56px;}",
|
|
569
|
+
".edd-line{min-height:22px;white-space:pre;}",
|
|
570
|
+
".edd-del{color:var(--dsw-alias-state-error-primary);}",
|
|
571
|
+
".edd-add{color:var(--dsw-alias-state-success-primary);}",
|
|
572
|
+
".edd-marker{color:inherit;opacity:1;user-select:none;}",
|
|
573
|
+
".edd-plain{white-space:pre;}",
|
|
574
|
+
".edd-empty{opacity:.5;font-style:italic;}",
|
|
575
|
+
".edd-hl{text-decoration:underline;text-decoration-thickness:2px;text-decoration-color:currentColor;font-weight:600;}",
|
|
576
|
+
".edd-expand{display:inline-block;margin:2px 0;padding:1px 0;border:none;background:transparent;color:var(--dsw-alias-label-tertiary);cursor:pointer;font:var(--dsw-font-markdown-code-block);text-align:left;}",
|
|
577
|
+
".edd-expand:hover{color:var(--dsw-alias-label-secondary);}",
|
|
578
|
+
".edd-foot{padding:0 14px 12px;font:var(--dsw-font-markdown-code-block);color:var(--dsw-alias-label-tertiary);}",
|
|
579
|
+
".edd-io{padding:8px 14px;}",
|
|
580
|
+
".edd-iolabel{margin-right:8px;color:var(--dsw-alias-label-caption);font-weight:600;}",
|
|
581
|
+
".edd-iotext{color:var(--dsw-alias-label-secondary);white-space:pre-wrap;word-break:break-word;}",
|
|
582
|
+
".edd-actions{padding:4px 0 2px 4px;}",
|
|
583
|
+
".edd-inspect{display:inline-flex;align-items:center;gap:4px;margin:0;padding:2px 8px;border:1px solid var(--dsw-alias-border-l2);border-radius:999px;background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-secondary);font-size:11px;line-height:16px;cursor:pointer;opacity:0;transition:opacity .12s ease;}",
|
|
584
|
+
".edd-root:hover .edd-inspect,.edd-inspect:focus-visible{opacity:1;}",
|
|
585
|
+
".edd-inspect:hover{background:var(--dsw-alias-interactive-bg-hover-solid);color:var(--dsw-alias-label-primary);}"
|
|
586
|
+
].join("");
|
|
587
|
+
let styleTag = null;
|
|
588
|
+
function ensureStyle() {
|
|
589
|
+
if (DOC === null || styleTag !== null) return;
|
|
590
|
+
const tag = DOC.createElement("style");
|
|
591
|
+
tag.dataset.plugin = "dsh-edit-diff";
|
|
592
|
+
tag.textContent = CSS;
|
|
593
|
+
DOC.head.appendChild(tag);
|
|
594
|
+
styleTag = tag;
|
|
595
|
+
}
|
|
596
|
+
const plugin = {
|
|
597
|
+
name: "dsh-edit-diff",
|
|
598
|
+
inject: ["slots", "timer"],
|
|
599
|
+
apply(ctx) {
|
|
600
|
+
const slots = ctx.get?.("slots");
|
|
601
|
+
if (slots === void 0) return;
|
|
602
|
+
const timer = ctx.get?.("timer");
|
|
603
|
+
if (timer !== void 0 && typeof timer.timeout === "function") timerRef.timeout = (callback, ms) => {
|
|
604
|
+
timer.timeout(callback, ms);
|
|
605
|
+
};
|
|
606
|
+
ctx.effect(function() {
|
|
607
|
+
ensureStyle();
|
|
608
|
+
return function() {
|
|
609
|
+
if (styleTag !== null && styleTag.parentNode !== null) styleTag.parentNode.removeChild(styleTag);
|
|
610
|
+
styleTag = null;
|
|
611
|
+
};
|
|
612
|
+
});
|
|
613
|
+
slots.inject("tool.call.toolview", () => {
|
|
614
|
+
const disposeEdit = slots.register({
|
|
615
|
+
name: "tool.call.toolview",
|
|
616
|
+
key: "edit",
|
|
617
|
+
priority: -1
|
|
618
|
+
}, DiffCard);
|
|
619
|
+
const disposeWrite = slots.register({
|
|
620
|
+
name: "tool.call.toolview",
|
|
621
|
+
key: "write",
|
|
622
|
+
priority: -1
|
|
623
|
+
}, DiffCard);
|
|
624
|
+
return () => {
|
|
625
|
+
disposeEdit();
|
|
626
|
+
disposeWrite();
|
|
627
|
+
};
|
|
628
|
+
});
|
|
629
|
+
if (typeof ctx.inject !== "function") return;
|
|
630
|
+
ctx.inject(["settingsScope"], (settingsCtx) => {
|
|
631
|
+
const settingsScope = settingsCtx.get?.("settingsScope");
|
|
632
|
+
if (settingsScope === void 0 || typeof settingsScope.bind !== "function") return;
|
|
633
|
+
const scope = settingsScope.bind({ namespace: "dsh-edit-diff" });
|
|
634
|
+
let disposeExtra;
|
|
635
|
+
const sync = () => {
|
|
636
|
+
if (typeof disposeExtra === "function") disposeExtra();
|
|
637
|
+
const value = scope.getSnapshot().value;
|
|
638
|
+
const tools = Array.isArray(value?.extraTools) ? value.extraTools : [];
|
|
639
|
+
extraSpecs.clear();
|
|
640
|
+
const disposers = [];
|
|
641
|
+
for (const tool of tools) {
|
|
642
|
+
if (tool === null || typeof tool !== "object") continue;
|
|
643
|
+
if (typeof tool.name !== "string" || tool.name === "") continue;
|
|
644
|
+
const toolName = tool.name;
|
|
645
|
+
if (toolName === "edit" || toolName === "write") continue;
|
|
646
|
+
if (extraSpecs.has(toolName)) continue;
|
|
647
|
+
extraSpecs.set(toolName, toKeySpec(tool));
|
|
648
|
+
disposers.push(slots.inject("tool.call.toolview", () => slots.register({
|
|
649
|
+
name: "tool.call.toolview",
|
|
650
|
+
key: toolName,
|
|
651
|
+
priority: -1
|
|
652
|
+
}, DiffCard)));
|
|
653
|
+
}
|
|
654
|
+
disposeExtra = () => {
|
|
655
|
+
for (const dispose of disposers) dispose();
|
|
656
|
+
};
|
|
657
|
+
};
|
|
658
|
+
const unsubscribe = scope.subscribe(sync);
|
|
659
|
+
ctx.effect(() => () => unsubscribe());
|
|
660
|
+
sync();
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
};
|
|
664
|
+
const name = plugin.name;
|
|
665
|
+
const inject = plugin.inject;
|
|
666
|
+
//#endregion
|
|
667
|
+
exports.apply = plugin.apply;
|
|
668
|
+
exports.default = plugin;
|
|
669
|
+
exports.inject = inject;
|
|
670
|
+
exports.name = name;
|
|
671
|
+
return module.exports;
|
|
672
|
+
}
|
|
673
|
+
});
|
|
452
674
|
|
|
453
|
-
|
|
454
|
-
return module.exports
|
|
455
|
-
},
|
|
456
|
-
})
|
|
675
|
+
//# sourceMappingURL=client.js.map
|