@huanlin/dsh-plugin-merge-tool-calls 0.2.0 → 0.2.2

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/lib/client.js CHANGED
@@ -40,6 +40,525 @@ const DEFAULT_MERGE_CONFIG = {
40
40
  maxGroupSize: 8
41
41
  };
42
42
 
43
+ //#endregion
44
+ //#region src/client/dictionaries.ts
45
+ /** All override-language dictionaries for the `NS` namespace. */
46
+ const dicts = {
47
+ ja: {
48
+ running: "実行中",
49
+ failed: "失敗",
50
+ stopped: "中断",
51
+ expand: "展開",
52
+ collapse: "折りたたむ",
53
+ more: "+{n}",
54
+ showLess: "残りを閉じる",
55
+ mergedCount: "{n} 回の呼び出しを統合",
56
+ countFiles: "{n} 個のファイル",
57
+ countQueries: "{n} 回の検索",
58
+ countCommands: "{n} 個のコマンド",
59
+ countPrograms: "{n} 個のプログラム",
60
+ countCalls: "{n} 回の呼び出し",
61
+ "terminal.signal": "シグナル {signal}",
62
+ "terminal.exitCode": "終了コード {code}",
63
+ "terminal.running": "実行中",
64
+ "terminal.failed": "失敗",
65
+ "terminal.done": "完了",
66
+ "terminal.copy": "コピー",
67
+ "terminal.copied": "コピーしました",
68
+ "terminal.noOutput": "出力なし",
69
+ "terminal.collapseAria": "出力を折りたたむ",
70
+ "terminal.collapse": "折りたたむ",
71
+ "terminal.expandAria": "残り {n} 行の出力を展開",
72
+ "terminal.expandRest": "… 残り {n} 行"
73
+ },
74
+ de: {
75
+ running: "Läuft",
76
+ failed: "Fehlgeschlagen",
77
+ stopped: "Unterbrochen",
78
+ expand: "Erweitern",
79
+ collapse: "Einklappen",
80
+ more: "+{n}",
81
+ showLess: "Weniger anzeigen",
82
+ mergedCount: "{n} Aufrufe zusammengeführt",
83
+ countFiles: "{n} Dateien",
84
+ countQueries: "{n} Suchanfragen",
85
+ countCommands: "{n} Befehle",
86
+ countPrograms: "{n} Programme",
87
+ countCalls: "{n} Aufrufe",
88
+ "terminal.signal": "Signal {signal}",
89
+ "terminal.exitCode": "Exit-Code {code}",
90
+ "terminal.running": "Läuft",
91
+ "terminal.failed": "Fehlgeschlagen",
92
+ "terminal.done": "Abgeschlossen",
93
+ "terminal.copy": "Kopieren",
94
+ "terminal.copied": "Kopiert",
95
+ "terminal.noOutput": "Keine Ausgabe",
96
+ "terminal.collapseAria": "Ausgabe einklappen",
97
+ "terminal.collapse": "Einklappen",
98
+ "terminal.expandAria": "Die restlichen {n} Ausgabezeilen ausklappen",
99
+ "terminal.expandRest": "… {n} weitere Zeilen"
100
+ },
101
+ fr: {
102
+ running: "En cours",
103
+ failed: "Échec",
104
+ stopped: "Interrompu",
105
+ expand: "Développer",
106
+ collapse: "Réduire",
107
+ more: "+{n}",
108
+ showLess: "Afficher moins",
109
+ mergedCount: "{n} appels fusionnés",
110
+ countFiles: "{n} fichiers",
111
+ countQueries: "{n} recherches",
112
+ countCommands: "{n} commandes",
113
+ countPrograms: "{n} programmes",
114
+ countCalls: "{n} appels",
115
+ "terminal.signal": "Signal {signal}",
116
+ "terminal.exitCode": "Code de sortie {code}",
117
+ "terminal.running": "En cours",
118
+ "terminal.failed": "Échec",
119
+ "terminal.done": "Terminé",
120
+ "terminal.copy": "Copier",
121
+ "terminal.copied": "Copié",
122
+ "terminal.noOutput": "Aucune sortie",
123
+ "terminal.collapseAria": "Réduire la sortie",
124
+ "terminal.collapse": "Réduire",
125
+ "terminal.expandAria": "Déployer les {n} lignes de sortie restantes",
126
+ "terminal.expandRest": "… {n} lignes restantes"
127
+ },
128
+ pt: {
129
+ running: "Executando",
130
+ failed: "Falhou",
131
+ stopped: "Interrompido",
132
+ expand: "Expandir",
133
+ collapse: "Recolher",
134
+ more: "+{n}",
135
+ showLess: "Mostrar menos",
136
+ mergedCount: "{n} chamadas mescladas",
137
+ countFiles: "{n} arquivos",
138
+ countQueries: "{n} pesquisas",
139
+ countCommands: "{n} comandos",
140
+ countPrograms: "{n} programas",
141
+ countCalls: "{n} chamadas",
142
+ "terminal.signal": "sinal {signal}",
143
+ "terminal.exitCode": "código de saída {code}",
144
+ "terminal.running": "Executando",
145
+ "terminal.failed": "Falhou",
146
+ "terminal.done": "Concluído",
147
+ "terminal.copy": "Copiar",
148
+ "terminal.copied": "Copiado",
149
+ "terminal.noOutput": "Sem saída",
150
+ "terminal.collapseAria": "Recolher saída",
151
+ "terminal.collapse": "Recolher",
152
+ "terminal.expandAria": "Expandir as {n} linhas de saída restantes",
153
+ "terminal.expandRest": "… mais {n} linhas"
154
+ },
155
+ ko: {
156
+ running: "실행 중",
157
+ failed: "실패",
158
+ stopped: "중단됨",
159
+ expand: "펼치기",
160
+ collapse: "접기",
161
+ more: "+{n}",
162
+ showLess: "더 적게 표시",
163
+ mergedCount: "{n}번 호출 병합됨",
164
+ countFiles: "파일 {n}개",
165
+ countQueries: "검색 {n}회",
166
+ countCommands: "명령 {n}개",
167
+ countPrograms: "프로그램 {n}개",
168
+ countCalls: "호출 {n}회",
169
+ "terminal.signal": "신호 {signal}",
170
+ "terminal.exitCode": "종료 코드 {code}",
171
+ "terminal.running": "실행 중",
172
+ "terminal.failed": "실패",
173
+ "terminal.done": "완료",
174
+ "terminal.copy": "복사",
175
+ "terminal.copied": "복사됨",
176
+ "terminal.noOutput": "출력 없음",
177
+ "terminal.collapseAria": "출력 접기",
178
+ "terminal.collapse": "접기",
179
+ "terminal.expandAria": "나머지 {n}줄 출력 펼치기",
180
+ "terminal.expandRest": "… 나머지 {n}줄"
181
+ },
182
+ ar: {
183
+ running: "قيد التشغيل",
184
+ failed: "فشل",
185
+ stopped: "متوقف",
186
+ expand: "توسيع",
187
+ collapse: "طي",
188
+ more: "+{n}",
189
+ showLess: "إظهار أقل",
190
+ mergedCount: "تم دمج {n} استدعاء",
191
+ countFiles: "{n} ملف",
192
+ countQueries: "{n} بحث",
193
+ countCommands: "{n} أمر",
194
+ countPrograms: "{n} برنامج",
195
+ countCalls: "{n} استدعاء",
196
+ "terminal.signal": "إشارة {signal}",
197
+ "terminal.exitCode": "رمز الخروج {code}",
198
+ "terminal.running": "قيد التشغيل",
199
+ "terminal.failed": "فشل",
200
+ "terminal.done": "اكتمل",
201
+ "terminal.copy": "نسخ",
202
+ "terminal.copied": "تم النسخ",
203
+ "terminal.noOutput": "لا يوجد إخراج",
204
+ "terminal.collapseAria": "طي الإخراج",
205
+ "terminal.collapse": "طي",
206
+ "terminal.expandAria": "توسيع {n} أسطر إخراج المتبقية",
207
+ "terminal.expandRest": "… و{n} أسطر أخرى"
208
+ },
209
+ hi: {
210
+ running: "चल रहा",
211
+ failed: "विफल",
212
+ stopped: "रोका गया",
213
+ expand: "विस्तार करें",
214
+ collapse: "संक्षिप्त करें",
215
+ more: "+{n}",
216
+ showLess: "कम दिखाएं",
217
+ mergedCount: "{n} कॉल मर्ज किए गए",
218
+ countFiles: "{n} फ़ाइलें",
219
+ countQueries: "{n} खोज",
220
+ countCommands: "{n} कमांड",
221
+ countPrograms: "{n} प्रोग्राम",
222
+ countCalls: "{n} कॉल",
223
+ "terminal.signal": "सिग्नल {signal}",
224
+ "terminal.exitCode": "एग्ज़िट कोड {code}",
225
+ "terminal.running": "चल रहा",
226
+ "terminal.failed": "विफल",
227
+ "terminal.done": "पूर्ण",
228
+ "terminal.copy": "कॉपी",
229
+ "terminal.copied": "कॉपी हो गया",
230
+ "terminal.noOutput": "कोई आउटपुट नहीं",
231
+ "terminal.collapseAria": "आउटपुट संक्षिप्त करें",
232
+ "terminal.collapse": "संक्षिप्त करें",
233
+ "terminal.expandAria": "शेष {n} आउटपुट पंक्तियाँ विस्तृत करें",
234
+ "terminal.expandRest": "… {n} और पंक्तियाँ"
235
+ },
236
+ id: {
237
+ running: "Berjalan",
238
+ failed: "Gagal",
239
+ stopped: "Dihentikan",
240
+ expand: "Bentangkan",
241
+ collapse: "Lipat",
242
+ more: "+{n}",
243
+ showLess: "Tampilkan lebih sedikit",
244
+ mergedCount: "{n} panggilan digabungkan",
245
+ countFiles: "{n} file",
246
+ countQueries: "{n} pencarian",
247
+ countCommands: "{n} perintah",
248
+ countPrograms: "{n} program",
249
+ countCalls: "{n} panggilan",
250
+ "terminal.signal": "sinyal {signal}",
251
+ "terminal.exitCode": "kode keluar {code}",
252
+ "terminal.running": "Berjalan",
253
+ "terminal.failed": "Gagal",
254
+ "terminal.done": "Selesai",
255
+ "terminal.copy": "Salin",
256
+ "terminal.copied": "Tersalin",
257
+ "terminal.noOutput": "Tidak ada keluaran",
258
+ "terminal.collapseAria": "Lipat keluaran",
259
+ "terminal.collapse": "Lipat",
260
+ "terminal.expandAria": "Bentangkan {n} baris keluaran tersisa",
261
+ "terminal.expandRest": "… {n} baris lagi"
262
+ },
263
+ tr: {
264
+ running: "Çalışıyor",
265
+ failed: "Başarısız",
266
+ stopped: "Durduruldu",
267
+ expand: "Genişlet",
268
+ collapse: "Daralt",
269
+ more: "+{n}",
270
+ showLess: "Daha az göster",
271
+ mergedCount: "{n} çağrı birleştirildi",
272
+ countFiles: "{n} dosya",
273
+ countQueries: "{n} arama",
274
+ countCommands: "{n} komut",
275
+ countPrograms: "{n} program",
276
+ countCalls: "{n} çağrı",
277
+ "terminal.signal": "sinyal {signal}",
278
+ "terminal.exitCode": "çıkış kodu {code}",
279
+ "terminal.running": "Çalışıyor",
280
+ "terminal.failed": "Başarısız",
281
+ "terminal.done": "Tamamlandı",
282
+ "terminal.copy": "Kopyala",
283
+ "terminal.copied": "Kopyalandı",
284
+ "terminal.noOutput": "Çıkış yok",
285
+ "terminal.collapseAria": "Çıkışı daralt",
286
+ "terminal.collapse": "Daralt",
287
+ "terminal.expandAria": "Kalan {n} çıkış satırını genişlet",
288
+ "terminal.expandRest": "… {n} satır daha"
289
+ },
290
+ vi: {
291
+ running: "Đang chạy",
292
+ failed: "Thất bại",
293
+ stopped: "Đã dừng",
294
+ expand: "Mở rộng",
295
+ collapse: "Thu gọn",
296
+ more: "+{n}",
297
+ showLess: "Hiển thị ít hơn",
298
+ mergedCount: "Đã gộp {n} lệnh gọi",
299
+ countFiles: "{n} tệp",
300
+ countQueries: "{n} lượt tìm kiếm",
301
+ countCommands: "{n} lệnh",
302
+ countPrograms: "{n} chương trình",
303
+ countCalls: "{n} lượt gọi",
304
+ "terminal.signal": "tín hiệu {signal}",
305
+ "terminal.exitCode": "mã thoát {code}",
306
+ "terminal.running": "Đang chạy",
307
+ "terminal.failed": "Thất bại",
308
+ "terminal.done": "Đã xong",
309
+ "terminal.copy": "Sao chép",
310
+ "terminal.copied": "Đã sao chép",
311
+ "terminal.noOutput": "Không có đầu ra",
312
+ "terminal.collapseAria": "Thu gọn đầu ra",
313
+ "terminal.collapse": "Thu gọn",
314
+ "terminal.expandAria": "Mở rộng {n} dòng đầu ra còn lại",
315
+ "terminal.expandRest": "… còn {n} dòng"
316
+ },
317
+ th: {
318
+ running: "กำลังทำงาน",
319
+ failed: "ล้มเหลว",
320
+ stopped: "ถูกขัดจังหวะ",
321
+ expand: "ขยาย",
322
+ collapse: "ย่อ",
323
+ more: "+{n}",
324
+ showLess: "แสดงน้อยลง",
325
+ mergedCount: "รวม {n} การเรียก",
326
+ countFiles: "{n} ไฟล์",
327
+ countQueries: "{n} การค้นหา",
328
+ countCommands: "{n} คำสั่ง",
329
+ countPrograms: "{n} โปรแกรม",
330
+ countCalls: "{n} การเรียก",
331
+ "terminal.signal": "สัญญาณ {signal}",
332
+ "terminal.exitCode": "รหัสออก {code}",
333
+ "terminal.running": "กำลังทำงาน",
334
+ "terminal.failed": "ล้มเหลว",
335
+ "terminal.done": "เสร็จสิ้น",
336
+ "terminal.copy": "คัดลอก",
337
+ "terminal.copied": "คัดลอกแล้ว",
338
+ "terminal.noOutput": "ไม่มีเอาต์พุต",
339
+ "terminal.collapseAria": "ย่อเอาต์พุต",
340
+ "terminal.collapse": "ย่อ",
341
+ "terminal.expandAria": "ขยายเอาต์พุตอีก {n} บรรทัด",
342
+ "terminal.expandRest": "… อีก {n} บรรทัด"
343
+ },
344
+ ru: {
345
+ running: "Выполняется",
346
+ failed: "Сбой",
347
+ stopped: "Прервано",
348
+ expand: "Развернуть",
349
+ collapse: "Свернуть",
350
+ more: "+{n}",
351
+ showLess: "Показать меньше",
352
+ mergedCount: "Объединено вызовов: {n}",
353
+ countFiles: "{n} файлов",
354
+ countQueries: "{n} поисков",
355
+ countCommands: "{n} команд",
356
+ countPrograms: "{n} программ",
357
+ countCalls: "{n} вызовов",
358
+ "terminal.signal": "сигнал {signal}",
359
+ "terminal.exitCode": "код выхода {code}",
360
+ "terminal.running": "Выполняется",
361
+ "terminal.failed": "Сбой",
362
+ "terminal.done": "Завершено",
363
+ "terminal.copy": "Копировать",
364
+ "terminal.copied": "Скопировано",
365
+ "terminal.noOutput": "Нет вывода",
366
+ "terminal.collapseAria": "Свернуть вывод",
367
+ "terminal.collapse": "Свернуть",
368
+ "terminal.expandAria": "Показать оставшиеся {n} строк вывода",
369
+ "terminal.expandRest": "… ещё {n} строк"
370
+ },
371
+ it: {
372
+ running: "In esecuzione",
373
+ failed: "Non riuscito",
374
+ stopped: "Interrotto",
375
+ expand: "Espandi",
376
+ collapse: "Comprimi",
377
+ more: "+{n}",
378
+ showLess: "Mostra meno",
379
+ mergedCount: "{n} chiamate unite",
380
+ countFiles: "{n} file",
381
+ countQueries: "{n} ricerche",
382
+ countCommands: "{n} comandi",
383
+ countPrograms: "{n} programmi",
384
+ countCalls: "{n} chiamate",
385
+ "terminal.signal": "segnale {signal}",
386
+ "terminal.exitCode": "codice di uscita {code}",
387
+ "terminal.running": "In esecuzione",
388
+ "terminal.failed": "Non riuscito",
389
+ "terminal.done": "Completato",
390
+ "terminal.copy": "Copia",
391
+ "terminal.copied": "Copiato",
392
+ "terminal.noOutput": "Nessun output",
393
+ "terminal.collapseAria": "Comprimi output",
394
+ "terminal.collapse": "Comprimi",
395
+ "terminal.expandAria": "Espandi le restanti {n} righe di output",
396
+ "terminal.expandRest": "… altre {n} righe"
397
+ },
398
+ nl: {
399
+ running: "Actief",
400
+ failed: "Mislukt",
401
+ stopped: "Onderbroken",
402
+ expand: "Uitklappen",
403
+ collapse: "Inklappen",
404
+ more: "+{n}",
405
+ showLess: "Minder tonen",
406
+ mergedCount: "{n} aanroepen samengevoegd",
407
+ countFiles: "{n} bestanden",
408
+ countQueries: "{n} zoekopdrachten",
409
+ countCommands: "{n} opdrachten",
410
+ countPrograms: "{n} programma's",
411
+ countCalls: "{n} aanroepen",
412
+ "terminal.signal": "signaal {signal}",
413
+ "terminal.exitCode": "exit-code {code}",
414
+ "terminal.running": "Actief",
415
+ "terminal.failed": "Mislukt",
416
+ "terminal.done": "Voltooid",
417
+ "terminal.copy": "Kopiëren",
418
+ "terminal.copied": "Gekopieerd",
419
+ "terminal.noOutput": "Geen uitvoer",
420
+ "terminal.collapseAria": "Uitvoer inklappen",
421
+ "terminal.collapse": "Inklappen",
422
+ "terminal.expandAria": "De overige {n} uitvoerregels uitklappen",
423
+ "terminal.expandRest": "… nog {n} regels"
424
+ },
425
+ sv: {
426
+ running: "Kör",
427
+ failed: "Misslyckades",
428
+ stopped: "Avbruten",
429
+ expand: "Expandera",
430
+ collapse: "Komprimera",
431
+ more: "+{n}",
432
+ showLess: "Visa mindre",
433
+ mergedCount: "{n} anrop sammanslagna",
434
+ countFiles: "{n} filer",
435
+ countQueries: "{n} sökningar",
436
+ countCommands: "{n} kommandon",
437
+ countPrograms: "{n} program",
438
+ countCalls: "{n} anrop",
439
+ "terminal.signal": "signal {signal}",
440
+ "terminal.exitCode": "slutkod {code}",
441
+ "terminal.running": "Kör",
442
+ "terminal.failed": "Misslyckades",
443
+ "terminal.done": "Klar",
444
+ "terminal.copy": "Kopiera",
445
+ "terminal.copied": "Kopierades",
446
+ "terminal.noOutput": "Ingen utdata",
447
+ "terminal.collapseAria": "Komprimera utdata",
448
+ "terminal.collapse": "Komprimera",
449
+ "terminal.expandAria": "Expandera de återstående {n} utdataraderna",
450
+ "terminal.expandRest": "… {n} fler rader"
451
+ },
452
+ pl: {
453
+ running: "Działa",
454
+ failed: "Nie powiodło się",
455
+ stopped: "Przerwano",
456
+ expand: "Rozwiń",
457
+ collapse: "Zwiń",
458
+ more: "+{n}",
459
+ showLess: "Pokaż mniej",
460
+ mergedCount: "Połączono {n} wywołań",
461
+ countFiles: "{n} plików",
462
+ countQueries: "{n} wyszukań",
463
+ countCommands: "{n} poleceń",
464
+ countPrograms: "{n} programów",
465
+ countCalls: "{n} wywołań",
466
+ "terminal.signal": "sygnał {signal}",
467
+ "terminal.exitCode": "kod wyjścia {code}",
468
+ "terminal.running": "Działa",
469
+ "terminal.failed": "Nie powiodło się",
470
+ "terminal.done": "Ukończono",
471
+ "terminal.copy": "Kopiuj",
472
+ "terminal.copied": "Skopiowano",
473
+ "terminal.noOutput": "Brak wyjścia",
474
+ "terminal.collapseAria": "Zwiń wyjście",
475
+ "terminal.collapse": "Zwiń",
476
+ "terminal.expandAria": "Rozwiń pozostałe {n} wierszy wyjścia",
477
+ "terminal.expandRest": "…jeszcze {n} wierszy"
478
+ },
479
+ "zh-HK": {
480
+ running: "執行中",
481
+ failed: "失敗",
482
+ stopped: "已中斷",
483
+ expand: "展開",
484
+ collapse: "收起",
485
+ more: "+{n}",
486
+ showLess: "收起其餘",
487
+ mergedCount: "已合併 {n} 次呼叫",
488
+ countFiles: "{n} 個檔案",
489
+ countQueries: "{n} 次搜尋",
490
+ countCommands: "{n} 條命令",
491
+ countPrograms: "{n} 段程式碼",
492
+ countCalls: "{n} 次呼叫",
493
+ "terminal.signal": "訊號 {signal}",
494
+ "terminal.exitCode": "退出碼 {code}",
495
+ "terminal.running": "執行中",
496
+ "terminal.failed": "失敗",
497
+ "terminal.done": "已完成",
498
+ "terminal.copy": "複製",
499
+ "terminal.copied": "已複製",
500
+ "terminal.noOutput": "無輸出",
501
+ "terminal.collapseAria": "收起輸出",
502
+ "terminal.collapse": "收起",
503
+ "terminal.expandAria": "展開其餘 {n} 行輸出",
504
+ "terminal.expandRest": "… 其餘 {n} 行"
505
+ },
506
+ "zh-TW": {
507
+ running: "執行中",
508
+ failed: "失敗",
509
+ stopped: "已中斷",
510
+ expand: "展開",
511
+ collapse: "收起",
512
+ more: "+{n}",
513
+ showLess: "收起其餘",
514
+ mergedCount: "已合併 {n} 次呼叫",
515
+ countFiles: "{n} 個檔案",
516
+ countQueries: "{n} 次搜尋",
517
+ countCommands: "{n} 條命令",
518
+ countPrograms: "{n} 段程式碼",
519
+ countCalls: "{n} 次呼叫",
520
+ "terminal.signal": "訊號 {signal}",
521
+ "terminal.exitCode": "退出碼 {code}",
522
+ "terminal.running": "執行中",
523
+ "terminal.failed": "失敗",
524
+ "terminal.done": "已完成",
525
+ "terminal.copy": "複製",
526
+ "terminal.copied": "已複製",
527
+ "terminal.noOutput": "無輸出",
528
+ "terminal.collapseAria": "收起輸出",
529
+ "terminal.collapse": "收起",
530
+ "terminal.expandAria": "展開其餘 {n} 行輸出",
531
+ "terminal.expandRest": "… 其餘 {n} 行"
532
+ },
533
+ "zh-MO": {
534
+ running: "執行中",
535
+ failed: "失敗",
536
+ stopped: "已中斷",
537
+ expand: "展開",
538
+ collapse: "收起",
539
+ more: "+{n}",
540
+ showLess: "收起其餘",
541
+ mergedCount: "已合併 {n} 次呼叫",
542
+ countFiles: "{n} 個檔案",
543
+ countQueries: "{n} 次搜尋",
544
+ countCommands: "{n} 條命令",
545
+ countPrograms: "{n} 段程式碼",
546
+ countCalls: "{n} 次呼叫",
547
+ "terminal.signal": "訊號 {signal}",
548
+ "terminal.exitCode": "退出碼 {code}",
549
+ "terminal.running": "執行中",
550
+ "terminal.failed": "失敗",
551
+ "terminal.done": "已完成",
552
+ "terminal.copy": "複製",
553
+ "terminal.copied": "已複製",
554
+ "terminal.noOutput": "無輸出",
555
+ "terminal.collapseAria": "收起輸出",
556
+ "terminal.collapse": "收起",
557
+ "terminal.expandAria": "展開其餘 {n} 行輸出",
558
+ "terminal.expandRest": "… 其餘 {n} 行"
559
+ }
560
+ };
561
+
43
562
  //#endregion
44
563
  //#region src/client/locales.ts
45
564
  /** Product copy for the merged tool-call rows. */
@@ -984,15 +1503,22 @@ const CSS = `
984
1503
  .mtc-inspect:hover { background: var(--dsw-alias-interactive-bg-hover-solid); color: var(--dsw-alias-label-primary); }
985
1504
 
986
1505
  /* Compact child rows: each is the main row's tail structure — [sep dot][path].
987
- The dot/path columns are set at runtime by the component (it measures the
1506
+ The dot/path column is set at runtime by the component (it measures the
988
1507
  main row's sep offset and sets the --mtc-sep-left custom property on the
989
- root), so this is only the pre-measure fallback.
1508
+ root); this is only the pre-measure fallback.
990
1509
 
991
1510
  The .mtc-children-collapse wrapper animates the block's height with a
992
1511
  grid-template-rows 0fr↔1fr transition (children stay in the DOM while
993
1512
  collapsing, so the slide is smooth instead of popping out). The vertical
994
1513
  margin lives on the wrapper (not .mtc-children) so it doesn't leak past the
995
- 0fr row when collapsed. */
1514
+ 0fr row when collapsed.
1515
+
1516
+ The sep-left indent lives on .mtc-child-row (not .mtc-children) so the
1517
+ expanded .mtc-child-body can align back to the root's left edge via a plain
1518
+ 4px margin. When the indent was on .mtc-children + a negative-margin
1519
+ pull-back on .mtc-child-body, the body's left portion was clipped by
1520
+ .mtc-children's overflow:hidden (which is required for the grid collapse
1521
+ animation). */
996
1522
  .mtc-children-collapse {
997
1523
  display: grid;
998
1524
  grid-template-rows: 0fr;
@@ -1005,7 +1531,7 @@ const CSS = `
1005
1531
  }
1006
1532
  .mtc-children {
1007
1533
  display: flex; flex-direction: column; gap: 1px;
1008
- margin: 0 0 0 var(--mtc-sep-left, 61px);
1534
+ margin: 0;
1009
1535
  min-width: 0; min-height: 0; overflow: hidden;
1010
1536
  opacity: 0;
1011
1537
  transition: opacity 150ms ease-out;
@@ -1017,7 +1543,7 @@ const CSS = `
1017
1543
  .mtc-child { display: flex; flex-direction: column; min-width: 0; }
1018
1544
  .mtc-child-row {
1019
1545
  display: flex; align-items: center; gap: 0; min-width: 0; height: 20px;
1020
- margin: 0; padding: 0; border: 0; border-radius: 4px; background: none;
1546
+ margin: 0 0 0 var(--mtc-sep-left, 61px); padding: 0; border: 0; border-radius: 4px; background: none;
1021
1547
  font: inherit; text-align: left; color: var(--dsw-alias-label-secondary); cursor: pointer;
1022
1548
  }
1023
1549
  .mtc-child-row:hover { background: var(--dsw-alias-interactive-bg-hover); color: var(--dsw-alias-label-primary); }
@@ -1040,10 +1566,10 @@ const CSS = `
1040
1566
  .mtc-child-path-link:hover { color: var(--dsw-alias-label-primary); text-decoration-color: currentColor; }
1041
1567
  .mtc-child-state { flex: none; font-size: 11px; line-height: 16px; color: var(--dsw-alias-label-tertiary); }
1042
1568
  .mtc-child-state[data-error] { color: var(--dsw-alias-state-error-primary); }
1043
- /* The expanded child card must not inherit the children block's sep indent:
1044
- pull its left edge back to the main card body's column (root + 4px) so the
1045
- card spans the card width instead of leaving a blank gutter on its left. */
1046
- .mtc-child-body { margin: 2px 0 2px calc(4px - var(--mtc-sep-left, 61px)); min-width: 0; }
1569
+ /* The expanded child card aligns to the main card body's column (root + 4px).
1570
+ No negative-margin pull-back is needed because .mtc-children no longer
1571
+ carries the sep-left indent only .mtc-child-row does. */
1572
+ .mtc-child-body { margin: 2px 0 2px 4px; min-width: 0; }
1047
1573
 
1048
1574
  .mtc-visually-hidden {
1049
1575
  position: absolute; width: 1px; height: 1px; overflow: hidden;
@@ -1080,6 +1606,21 @@ function apply(ctx, config = {}) {
1080
1606
  en
1081
1607
  }), "merge-tool-calls: dictionaries");
1082
1608
  ctx.effect(installStyles, "merge-tool-calls: styles");
1609
+ ctx.effect(() => {
1610
+ let dispose;
1611
+ const sync = () => {
1612
+ dispose?.();
1613
+ dispose = void 0;
1614
+ const store = ctx.get("betterLocale");
1615
+ if (store !== void 0) dispose = store.register(NS, dicts);
1616
+ };
1617
+ sync();
1618
+ const unsubscribe = ctx.locale.subscribe(sync);
1619
+ return () => {
1620
+ unsubscribe();
1621
+ dispose?.();
1622
+ };
1623
+ }, "merge-tool-calls: better-locale override dicts");
1083
1624
  const toolNames = cfg.tools.length === 0 ? ALL_TOOL_NAMES : [...new Set(cfg.tools)];
1084
1625
  for (const tool of toolNames) ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
1085
1626
  name: "tool.call.toolview",
package/lib/client.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["DEFAULT_MERGE_CONFIG: MergeToolCallsConfig","ALL_TOOL_NAMES: readonly string[]","VARIANT_TITLES: Record<ToolVariant, string>","TOOL_VARIANTS: Record<string, ToolVariant>","TOOL_TITLES: Record<string, string>","parts: string[]","parsed: unknown","SUMMARY_KEYS: Record<ToolVariant, readonly string[]>","FILE_PATH_VARIANTS: ReadonlySet<ToolVariant>","state: RowState","out: DiffHunk[]","diffs","blocks: ToolCallBlock[]","VARIANT_ICONS: Record<ToolVariant, ReactNode>","IconSearchOutline16","IconBrowseOutline16","IconApiOutline14","IconEditOutline16","IconCodeOutline16","IconSparkle16","VARIANT_COUNT_KEY: Record<ToolVariant, 'countFiles' | 'countQueries' | 'countCommands' | 'countPrograms' | 'countCalls'>","StateDot","TerminalBlock","DiffBlock","ReadBlock","SearchBlock","WebBlock","RowCard","DisclosureRow","ChildRow","cfg: MergeToolCallsConfig"],"sources":["../src/types.ts","../src/client/locales.ts","../src/client/tool-names.ts","../src/client/card-model.ts","../src/client/merge-run.ts","../src/client/rows.tsx","../src/client/styles.ts","../src/client/index.ts"],"sourcesContent":["/** Shared config surface of the merge-tool-calls plugin (host + client halves). */\n\n/** How consecutive same-tool calls are grouped into one merged card. */\nexport type MergeGroupMode = 'adjacent' | 'step'\n\n/** Plugin configuration; defaults live in the Schemastery schema and cordis.patch.yml. */\nexport interface MergeToolCallsConfig {\n /**\n * Wire tool names whose consecutive calls merge. Empty = every built-in\n * generic-family tool (read/grep/glob/edit/write/bash/pwsh/web_search/\n * web_fetch/run_code/cordis_package_inspect/cordis_runtime_inspect); a\n * non-empty list is an explicit whitelist (any wire name works).\n */\n readonly tools: readonly string[]\n /** `adjacent`: any consecutive run in the chat flow; `step`: only within one agent step. */\n readonly groupBy: MergeGroupMode\n /** Max calls per merged group; the run is truncated and the excess starts a new group. */\n readonly maxGroupSize: number\n}\n\n/** Runtime defaults applied when a half receives no config (defensive only). */\nexport const DEFAULT_MERGE_CONFIG: MergeToolCallsConfig = {\n tools: [],\n groupBy: 'adjacent',\n maxGroupSize: 8,\n}\n","/** Product copy for the merged tool-call rows. */\nexport const zh = {\n running: '进行中',\n failed: '失败',\n stopped: '已中断',\n expand: '展开',\n collapse: '折叠',\n more: '+{n}',\n showLess: '收起其余',\n mergedCount: '已合并 {n} 次调用',\n countFiles: '{n} 个文件',\n countQueries: '{n} 次搜索',\n countCommands: '{n} 条命令',\n countPrograms: '{n} 段代码',\n countCalls: '{n} 次调用',\n 'terminal.signal': '信号 {signal}',\n 'terminal.exitCode': '退出码 {code}',\n 'terminal.running': '运行中',\n 'terminal.failed': '失败',\n 'terminal.done': '已完成',\n 'terminal.copy': '复制',\n 'terminal.copied': '已复制',\n 'terminal.noOutput': '无输出',\n 'terminal.collapseAria': '收起输出',\n 'terminal.collapse': '折叠',\n 'terminal.expandAria': '展开其余 {n} 行输出',\n 'terminal.expandRest': '… 其余 {n} 行',\n} satisfies Record<string, string>\n\nexport type MergeToolCallsKey = keyof typeof zh\n\nexport const en = {\n running: 'Running',\n failed: 'Failed',\n stopped: 'Interrupted',\n expand: 'Expand',\n collapse: 'Collapse',\n more: '+{n}',\n showLess: 'Show fewer',\n mergedCount: '{n} calls merged',\n countFiles: '{n} Files',\n countQueries: '{n} Queries',\n countCommands: '{n} Commands',\n countPrograms: '{n} Programs',\n countCalls: '{n} Calls',\n 'terminal.signal': 'signal {signal}',\n 'terminal.exitCode': 'exit code {code}',\n 'terminal.running': 'Running',\n 'terminal.failed': 'Failed',\n 'terminal.done': 'Done',\n 'terminal.copy': 'Copy',\n 'terminal.copied': 'Copied',\n 'terminal.noOutput': 'No output',\n 'terminal.collapseAria': 'Collapse output',\n 'terminal.collapse': 'Collapse',\n 'terminal.expandAria': 'Expand the remaining {n} output lines',\n 'terminal.expandRest': '… {n} more lines',\n} satisfies Record<MergeToolCallsKey, string>\n\nexport const NS = 'merge-tool-calls'\n","/**\n * The built-in tool universe this plugin shadows by default, plus the\n * variant-classification vocabulary mirroring ui-tool's tool-call model.\n *\n * The keyed `tool.call.toolview` slot dispatches on the exact wire tool name,\n * so a shadowed registration is needed per name (there is no wildcard). This\n * list names every shipped tool whose row belongs to the generic ToolRow\n * family — a variant title/icon plus read/search/diff/terminal/web card or\n * IN/OUT text — which the merged row reproduces faithfully.\n *\n * Deliberately excluded: tools with custom row cards this plugin does not\n * replicate (`skill`, `cordis_define`, and the live `cordis_run` /\n * `cordis_stop` / `cordis_undefine` rows), and `todo_write` /\n * `ask_user_question` whose summary formatting is tool-specific. They keep\n * their built-in rows unless the user names them in the `tools` config\n * explicitly.\n * @module\n */\n\n/** Wire tool names merged by default (empty `tools` config = this list). */\nexport const ALL_TOOL_NAMES: readonly string[] = [\n // File tools (read/search/diff cards).\n 'read', 'grep', 'glob', 'edit', 'write',\n // Shell tools (terminal card).\n 'bash', 'pwsh',\n // Web tools (web card).\n 'web_search', 'web_fetch',\n // Code tool (generic IN/OUT with the program body).\n 'run_code',\n // Cordis inspection verbs (generic read-variant rows).\n 'cordis_package_inspect', 'cordis_runtime_inspect',\n]\n\n/** Tool-row visual variant, mirroring ui-tool's ToolRowVariant. */\nexport type ToolVariant = 'search' | 'read' | 'bash' | 'write' | 'edit' | 'code' | 'others'\n\n/** Figma row titles per variant (design literals, not translatable copy). */\nexport const VARIANT_TITLES: Record<ToolVariant, string> = {\n search: 'Search', read: 'Read', bash: 'Bash',\n write: 'Write', edit: 'Edit', code: 'Code', others: 'Tool call',\n}\n\n/**\n * Known tool name -> row variant (mirrors ui-tool's classification table,\n * minus the run-control verbs: their rows are custom and not shadowed).\n */\nconst TOOL_VARIANTS: Record<string, ToolVariant> = {\n bash: 'bash',\n pwsh: 'bash',\n read: 'read',\n web_fetch: 'read',\n web_search: 'search',\n grep: 'search',\n glob: 'search',\n write: 'write',\n edit: 'edit',\n run_code: 'code',\n cordis_package_inspect: 'read',\n cordis_runtime_inspect: 'read',\n}\n\n/** Tool-owned titles refining a generic row variant (mirrors ui-tool). */\nexport const TOOL_TITLES: Record<string, string> = {\n cordis_package_inspect: 'Inspect',\n cordis_runtime_inspect: 'Inspect',\n pwsh: 'Pwsh',\n}\n\n/** Classify a tool name into its row variant (mirrors ui-tool). */\nexport function classifyTool(toolName: string): ToolVariant {\n return TOOL_VARIANTS[toolName] ?? 'others'\n}\n\n/** The variant title a row shows, honoring tool-owned refinements. */\nexport function variantTitle(toolName: string): string {\n return TOOL_TITLES[toolName] ?? VARIANT_TITLES[classifyTool(toolName)]\n}\n","/**\n * Pure card/row derivation from a frozen call slice, mirroring ui-tool's\n * tool-call/read-card/search-card/diff-card/terminal-card/web-card models at\n * the plugin boundary (those models are ui-tool internals and cannot be\n * imported cross-package). Same wire contract, same defensive treatment of\n * untrusted result views. A merged row renders the same surface the built-in\n * row would: a variant title/icon plus a card primitive or IN/OUT text.\n * @module\n */\nimport {\n resolveWorkspacePath,\n type ToolCallBlock, type ToolResultNode,\n} from '@deepseek-ai/dsh-client-runtime/client'\nimport type {\n DiffBlockProps, DiffHunk, ReadBlockProps, SearchBlockProps, SearchFileGroup,\n TerminalBlockProps, WebBlockProps,\n} from '@deepseek-ai/dsh-client-ui-primitives'\nimport { classifyTool, VARIANT_TITLES, TOOL_TITLES, type ToolVariant } from './tool-names.ts'\n\n/** Row state semantic, mirroring ui-tool's ToolRowState. */\nexport type RowState = 'running' | 'ok' | 'error' | 'stopped'\n\nexport type { ToolVariant } from './tool-names.ts'\n\n/** Read-card props the ReadBlock primitive draws (per-render maxLines owned by the caller). */\nexport type ReadCardModel = Pick<ReadBlockProps, 'label' | 'lines' | 'totalLines' | 'lang'>\n\n/**\n * Distributive `Omit`: a plain `Omit<A | B, K>` keeps only the keys common to\n * both members, dropping the discriminated `files`/`paths` fields. Distributing\n * over the naked type parameter preserves each shape.\n */\ntype DistributiveOmit<T, K extends keyof T> = T extends unknown ? Omit<T, K> : never\n\n/** Search-card props plus the capped-result recovery locator (mirrors ui-tool). */\nexport interface SearchCardModel {\n readonly card: DistributiveOmit<SearchBlockProps, 'maxLines' | 'className'>\n /** The result view's replacement title, which outranks the args summary. */\n readonly title: string | undefined\n readonly recovery: string | undefined\n}\n\n/** Diff-card props (mirrors ui-tool; maxLines/className belong to the render site). */\nexport interface DiffCardModel {\n readonly card: Pick<DiffBlockProps, 'diffs'>\n}\n\n/** Terminal-card props (mirrors ui-tool; maxLines/className/labels belong to the render site). */\nexport interface TerminalCardModel {\n readonly card: Pick<TerminalBlockProps, 'command' | 'cwd' | 'output' | 'exitCode' | 'signal' | 'running'>\n /** The call view's model-authored description shown above the card. */\n readonly description: string | undefined\n}\n\n/** True when a settled terminal card reports a failing exit (mirrors ui-tool). */\nexport function terminalFailed(model: TerminalCardModel): boolean {\n const { exitCode, signal, running } = model.card\n return running !== true && ((exitCode !== undefined && exitCode !== 0) || signal !== undefined)\n}\n\n/** Everything a merged row needs, derived once from the frozen slice. */\nexport interface CallRowModel {\n readonly state: RowState\n readonly variant: ToolVariant\n /** Row title: variant title, or the tool-owned title when it refines one. */\n readonly title: string\n /** Args/result-derived one-line summary (path for file tools, query for searches, …). */\n readonly summary: string\n /** Expanded-body input text (pretty args); null = no input section. */\n readonly body: string | null\n /** Flattened result text; null while running or when the result carries no text. */\n readonly output: string | null\n /** First result line on an error row (the collapsed summary's error text). */\n readonly errorSummary: string | null\n /** Openable workspace path from args; absent for non-file tools and errors. */\n readonly filePath: string | undefined\n /** Whether the row has anything to expand (a card, args body, or output). */\n readonly expandable: boolean\n /** Expanded-body content cards, mutually exclusive, or null when absent. */\n readonly terminal: TerminalCardModel | null\n readonly diff: DiffCardModel | null\n readonly read: ReadCardModel | null\n readonly search: SearchCardModel | null\n readonly web: WebBlockProps | null\n}\n\n/**\n * Flatten a settled result's content blocks to display text.\n * @param node - settled result node.\n * @returns joined text (may be empty).\n */\nexport function resultText(node: ToolResultNode): string {\n const parts: string[] = []\n for (const block of node.content) {\n if (block.type === 'text') parts.push(block.text)\n else parts.push(JSON.stringify(block))\n }\n if (parts.length === 0 && node.error !== undefined) parts.push(`${node.error.name}: ${node.error.code}`)\n return parts.join('\\n')\n}\n\nfunction firstLine(text: string): string {\n const nl = text.indexOf('\\n')\n return nl === -1 ? text : text.slice(0, nl)\n}\n\n/** Strip the workspace root from a workspace-rooted absolute path (display only). */\nexport function relativizeToCwd(text: string, cwd: string | undefined): string {\n if (cwd === undefined || cwd === '') return text\n const root = cwd.replace(/[/\\\\]+$/, '')\n if (text.startsWith(`${root}/`) || text.startsWith(`${root}\\\\`)) return text.slice(root.length + 1)\n return text\n}\n\nfunction parseArgs(argsRaw: string): Record<string, unknown> | undefined {\n try {\n const parsed: unknown = JSON.parse(argsRaw)\n return typeof parsed === 'object' && parsed !== null ? parsed as Record<string, unknown> : undefined\n } catch {\n return undefined\n }\n}\n\nfunction pickString(args: Record<string, unknown>, keys: readonly string[]): string | undefined {\n for (const key of keys) {\n const value = args[key]\n if (typeof value === 'string' && value !== '') return value\n }\n return undefined\n}\n\n/** Summary key preference per row variant (args-derived, mirrors ui-tool). */\nconst SUMMARY_KEYS: Record<ToolVariant, readonly string[]> = {\n bash: ['description', 'command'],\n read: ['path', 'file_path', 'url'],\n search: ['query', 'pattern', 'url'],\n write: ['path', 'file_path'],\n edit: ['path', 'file_path'],\n code: ['description'],\n others: [],\n}\n\nfunction deriveSummary(variant: ToolVariant, argsRaw: string): string {\n const parsed = parseArgs(argsRaw)\n if (parsed === undefined) return firstLine(argsRaw)\n const picked = pickString(parsed, SUMMARY_KEYS[variant])\n if (picked !== undefined) return firstLine(picked)\n for (const value of Object.values(parsed)) {\n if (typeof value === 'string' && value !== '') return firstLine(value)\n }\n return firstLine(argsRaw)\n}\n\n/** Path keys only — never `url` (web_fetch lands on the read variant). */\nconst FILE_PATH_KEYS = ['path', 'file_path'] as const\n\n/** File-tool variants whose summary may be an openable workspace path. */\nconst FILE_PATH_VARIANTS: ReadonlySet<ToolVariant> = new Set(['read', 'write', 'edit'])\n\nfunction deriveFilePath(variant: ToolVariant, argsRaw: string): string | undefined {\n if (!FILE_PATH_VARIANTS.has(variant)) return undefined\n const parsed = parseArgs(argsRaw)\n if (parsed === undefined) return undefined\n return pickString(parsed, FILE_PATH_KEYS)?.split('\\n')[0]\n}\n\nfunction deriveBody(variant: ToolVariant, argsRaw: string): string | null {\n if (argsRaw === '') return null\n const parsed = parseArgs(argsRaw)\n if (parsed === undefined) return argsRaw\n // The code row's expanded body IS the program, not the args JSON envelope.\n if (variant === 'code') {\n const code = parsed.code\n if (typeof code === 'string' && code !== '') return code\n }\n return JSON.stringify(parsed, null, 2)\n}\n\n/** Derive the row model for one call of a grouped tool. */\nexport function callRowModel(\n toolName: string,\n block: ToolCallBlock,\n cwd: string | undefined,\n): CallRowModel {\n const done = 'kind' in block\n const argsRaw = (done ? block.call?.argsRaw : block.argsRaw) ?? ''\n const state: RowState = !done ? 'running'\n : block.error?.code === 'interrupted' ? 'stopped'\n : block.isError ? 'error' : 'ok'\n const variant = classifyTool(toolName)\n const terminal = terminalCardOf(block, cwd)\n const read = readCardOf(block, cwd)\n const diff = diffCardOf(block)\n const search = searchCardOf(block)\n const web = webCardOf(block)\n const base = argsRaw === '' ? block.callId : relativizeToCwd(deriveSummary(variant, argsRaw), cwd)\n const toolTitle = TOOL_TITLES[toolName]\n // Others keeps the static \"Tool call\" title; the real tool name rides the\n // summary slot unless the tool owns a specific title (mirrors ui-tool).\n const argsSummary = variant === 'others' && toolName !== '' && toolTitle === undefined\n ? `${toolName} · ${base}`\n : base\n const filePath = deriveFilePath(variant, argsRaw)\n const output = done ? (resultText(block) || null) : null\n const errorSummary = state === 'error' && output !== null ? firstLine(output) : null\n // A card's own summary outranks the args summary: the terminal presenter's\n // description is the contract's above-card text, and a search result view's\n // replacement title names the query the tool itself chose (mirrors ui-tool).\n const summary = terminal?.description ?? search?.title ?? argsSummary\n // Single-file tools never expose an args body — the path link is the only\n // args interaction (mirrors ui-tool's singleFile rule).\n const body = filePath !== undefined ? null : deriveBody(variant, argsRaw)\n // A failing exit status is the terminal card's own error signal: the call\n // settles isError:false, and the red state dot is its only collapsed signal.\n const finalState = state === 'ok' && terminal !== null && terminalFailed(terminal)\n ? 'error'\n : state\n const expandable = terminal !== null || diff !== null || read !== null\n || search !== null || web !== null || body !== null || output !== null\n return {\n state: finalState,\n variant,\n title: toolTitle ?? VARIANT_TITLES[variant],\n summary,\n body,\n output,\n errorSummary,\n filePath,\n expandable,\n terminal,\n diff,\n read,\n search,\n web,\n }\n}\n\n/** Read-card derivation, or null when this call is not a read card (mirrors ui-tool). */\nfunction readCardOf(block: ToolCallBlock, cwd: string | undefined): ReadCardModel | null {\n if (!('kind' in block)) return null\n const result = block.resultView?.card === 'read' ? block.resultView : null\n if (result === null) return null\n if (!Array.isArray(result.lines)) return null\n const lines = result.lines\n .filter(line => typeof line === 'object' && line !== null\n && typeof (line as { number?: unknown }).number === 'number'\n && typeof (line as { text?: unknown }).text === 'string')\n .map(line => ({ number: (line as { number: number }).number, text: (line as { text: string }).text }))\n return {\n label: result.title ?? relativizeToCwd(result.path, cwd),\n lines,\n totalLines: typeof result.totalLines === 'number' ? result.totalLines : lines.length,\n lang: typeof result.lang === 'string' ? result.lang : undefined,\n }\n}\n\nfunction isValidFiles(files: unknown): files is SearchFileGroup[] {\n return Array.isArray(files) && files.every(file =>\n typeof file === 'object' && file !== null\n && typeof (file as { path?: unknown }).path === 'string'\n && Array.isArray((file as { matches?: unknown }).matches)\n && (file as { matches: unknown[] }).matches.every(match =>\n typeof match === 'object' && match !== null\n && typeof (match as { lineNumber?: unknown }).lineNumber === 'number'\n && typeof (match as { line?: unknown }).line === 'string'))\n}\n\nfunction flattenContent(content: readonly { type: string; text?: string }[]): string | undefined {\n const text = content\n .filter((block): block is { type: 'text'; text: string } => block.type === 'text' && typeof block.text === 'string')\n .map(block => block.text)\n .join('\\n')\n return text === '' ? undefined : text\n}\n\n/** Search-card derivation, or null when this call is not a search card (mirrors ui-tool). */\nfunction searchCardOf(block: ToolCallBlock): SearchCardModel | null {\n if (!('kind' in block)) return null\n const result = block.resultView?.card === 'search' ? block.resultView : null\n if (result === null) return null\n const common = { truncated: result.truncated, total: result.total }\n const recovery = result.truncated ? flattenContent(block.content) : undefined\n if (result.shape === 'matches') {\n if (!isValidFiles(result.files)) return null\n return { title: result.title, recovery, card: { kind: 'matches', files: result.files, ...common } }\n }\n if (result.shape !== 'paths') return null\n if (!Array.isArray(result.paths) || !result.paths.every(path => typeof path === 'string')) return null\n return { title: result.title, recovery, card: { kind: 'paths', paths: result.paths, ...common } }\n}\n\n/** Narrow a wire `card:'diff'` view's `diffs` to well-formed hunks (mirrors ui-tool). */\nfunction narrowDiffs(diffs: unknown): DiffHunk[] | null {\n if (!Array.isArray(diffs) || diffs.length === 0) return null\n const out: DiffHunk[] = []\n for (const hunk of diffs) {\n if (typeof hunk !== 'object' || hunk === null) return null\n const { path, oldText, newText } = hunk as Record<string, unknown>\n if (typeof path !== 'string') return null\n if (oldText !== null && typeof oldText !== 'string') return null\n if (typeof newText !== 'string') return null\n out.push({ path, oldText, newText })\n }\n return out\n}\n\n/** Diff-card derivation, or null when this call is not a diff card (mirrors ui-tool). */\nfunction diffCardOf(block: ToolCallBlock): DiffCardModel | null {\n if (!('kind' in block)) {\n const call = block.callView?.card === 'diff' ? block.callView : null\n const diffs = call === null ? null : narrowDiffs(call.diffs)\n return diffs === null ? null : { card: { diffs } }\n }\n const result = block.resultView?.card === 'diff' ? block.resultView : null\n const diffs = result === null ? null : narrowDiffs(result.diffs)\n return diffs === null ? null : { card: { diffs } }\n}\n\n/**\n * Resolve a terminal view's working directory the way the render-intent\n * contract assigns it: an absolute path is used as-is, a relative one joins\n * under the session workspace, and an omitted one IS the session workspace.\n * @param viewCwd - the cwd the terminal call view carries, if any.\n * @param sessionCwd - the session workspace root, if the caller knows it.\n * @returns the working directory for the prompt label, or undefined.\n */\nfunction resolveTerminalCwd(viewCwd: string | undefined, sessionCwd: string | undefined): string | undefined {\n if (viewCwd === undefined || viewCwd === '') return sessionCwd\n if (sessionCwd === undefined || sessionCwd === '') return viewCwd\n return resolveWorkspacePath(sessionCwd, viewCwd)\n}\n\n/** Terminal-card derivation, or null when this call is not a terminal card (mirrors ui-tool). */\nfunction terminalCardOf(block: ToolCallBlock, sessionCwd: string | undefined): TerminalCardModel | null {\n const call = block.callView?.card === 'terminal' ? block.callView : null\n if (!('kind' in block)) {\n return call === null ? null : {\n description: call.description,\n card: {\n command: call.title,\n cwd: resolveTerminalCwd(call.cwd, sessionCwd),\n output: undefined,\n exitCode: undefined,\n signal: undefined,\n running: true,\n },\n }\n }\n const result = block.resultView?.card === 'terminal' ? block.resultView : null\n if (result === null) return null\n return {\n description: call?.description,\n card: {\n command: result.title ?? call?.title ?? '',\n cwd: call === null ? undefined : resolveTerminalCwd(call.cwd, sessionCwd),\n output: result.output,\n exitCode: result.exitCode,\n signal: result.signal,\n running: false,\n },\n }\n}\n\n/** Web-card derivation, or null when this call is not a web card (mirrors ui-tool). */\nfunction webCardOf(block: ToolCallBlock): WebBlockProps | null {\n if (!('kind' in block)) return null\n const result = block.resultView\n if (result?.card !== 'web') return null\n if (result.kind === 'search') {\n return {\n kind: 'search',\n answer: result.answer,\n sources: result.sources.map(source => ({\n url: source.url,\n title: source.title,\n snippet: source.snippet,\n publishedAt: source.publishedAt,\n })),\n truncated: result.truncated,\n }\n }\n if (result.kind === 'fetch') {\n return {\n kind: 'fetch',\n url: result.url,\n statusCode: result.statusCode,\n truncated: result.truncated,\n }\n }\n return null\n}\n","/**\n * Pure detection of a consecutive run of grouped tool calls in the chat flow,\n * and the per-seat merged-group partition over it.\n *\n * A pure function of the chat snapshot (order + node store), so the merged\n * display is deterministic under replay: the web layer recomputes it per frame.\n * @module\n */\nimport type { ChatConversationViewNode, ChatNodeStore, ToolCallBlock } from '@deepseek-ai/dsh-client-runtime/client'\nimport { conversationContextKey } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { MergeGroupMode } from '../types.ts'\nimport { classifyTool } from './tool-names.ts'\n\n/** Tool-call node kind registered by ui-conversation's built-in tool definition. */\nconst TOOL_CALL_KIND = 'tool-call'\n\n/** Extract a root call block from any chat node, when it is a tool-call node. */\nfunction toolRootOf(node: ChatConversationViewNode): ToolCallBlock | null {\n if (node.kind !== TOOL_CALL_KIND) return null\n const root = (node.data as { root?: unknown }).root\n return typeof root === 'object' && root !== null ? root as ToolCallBlock : null\n}\n\n/** The wire tool name of a call in either lifecycle form (mirrors ui-tool). */\nexport function callNameOf(block: ToolCallBlock): string {\n return 'kind' in block ? (block.call?.name ?? '') : block.name\n}\n\n/** Whether the call's wire name is one of the grouped tools (empty = all). */\nexport function isGroupedTool(name: string, tools: readonly string[]): boolean {\n return tools.length === 0 || tools.includes(name)\n}\n\n/** Step identity of a node's location; undefined when the node is not step-bound. */\nfunction stepIdOf(node: ChatConversationViewNode): string | undefined {\n const location = node.location\n return location.kind === 'step' ? `${location.turn.turn}:${location.step.step}` : undefined\n}\n\n/** One merged group: the group's first call plus its consecutive continuations. */\nexport interface ReadRun {\n /** Whether this seat is the group's rendering head (the only seat that shows the card). */\n readonly isFirst: boolean\n /** The group's root blocks in flow order (first + continuations). */\n readonly blocks: readonly ToolCallBlock[]\n}\n\n/**\n * Compute the merged group this call belongs to.\n *\n * 1. Locates this call's node in the chat order; null when it is not a chat\n * tool-call node (e.g. a read dispatched as a subcall).\n * 2. Walks backward/forward to the maximal consecutive run containing it. A\n * call continues the run when it is a grouped tool AND same-tool-same-run:\n * the identical wire name, or a sibling of the same known variant family\n * (grep+glob, bash+pwsh, read+web_fetch…). Unknown names (variant\n * `others`) only merge with themselves, so unrelated tools never share a\n * card (`adjacent`: any consecutive run; `step`: same agent step as this\n * call).\n * 3. Partitions the run into `maxGroupSize`-sized groups; this call is the\n * group's first only when it heads one of those partitions. Truncation\n * therefore never orphans a call: the excess starts its own group.\n *\n * @param order - chat node key order.\n * @param nodes - chat node store.\n * @param myCallId - the call id of the seat asking about itself.\n * @param tools - grouped wire tool names; empty means every tool.\n * @param groupBy - grouping mode.\n * @param maxGroupSize - per-group cap.\n * @returns the group partition, or null when the call is not a chat tool-call node.\n */\nexport function readRun(\n order: readonly string[],\n nodes: ChatNodeStore,\n myCallId: string,\n tools: readonly string[],\n groupBy: MergeGroupMode,\n maxGroupSize: number,\n): ReadRun | null {\n const myKey = conversationContextKey(TOOL_CALL_KIND, myCallId)\n const myIndex = order.indexOf(myKey)\n if (myIndex < 0) return null\n const myRoot = rootAtNode(order, nodes, myIndex)\n if (myRoot === null) return null\n const myName = callNameOf(myRoot)\n const myVariant = classifyTool(myName)\n const size = Math.max(1, maxGroupSize)\n\n const rootAt = (index: number): ToolCallBlock | null => rootAtNode(order, nodes, index)\n const myStep = groupBy === 'step' ? stepIdOfNodeAt(order, nodes, myIndex) : undefined\n const inGroup = (index: number): boolean => {\n const root = rootAt(index)\n if (root === null || !isGroupedTool(callNameOf(root), tools)) return false\n const name = callNameOf(root)\n // Same-name calls always continue the run; known families merge siblings\n // (grep+glob, bash+pwsh); unknown names merge only with themselves.\n if (name !== myName) {\n if (myVariant === 'others' || classifyTool(name) !== myVariant) return false\n }\n if (groupBy !== 'step') return true\n return myStep !== undefined && stepIdOfNodeAt(order, nodes, index) === myStep\n }\n\n let start = myIndex\n while (start > 0 && inGroup(start - 1)) start--\n let end = myIndex\n while (end < order.length - 1 && inGroup(end + 1)) end++\n\n const groupStart = start + Math.floor((myIndex - start) / size) * size\n const groupEnd = Math.min(groupStart + size, end + 1)\n\n const blocks: ToolCallBlock[] = []\n for (let index = groupStart; index < groupEnd; index++) {\n const root = rootAt(index)\n if (root !== null) blocks.push(root)\n }\n return { isFirst: myIndex === groupStart, blocks }\n}\n\nfunction rootAtNode(order: readonly string[], nodes: ChatNodeStore, index: number): ToolCallBlock | null {\n const key = order[index]\n const node = key === undefined ? undefined : nodes.get(key)\n return node === undefined ? null : toolRootOf(node)\n}\n\nfunction stepIdOfNodeAt(order: readonly string[], nodes: ChatNodeStore, index: number): string | undefined {\n const key = order[index]\n const node = key === undefined ? undefined : nodes.get(key)\n return node === undefined ? undefined : stepIdOf(node)\n}\n","/**\n * Merged tool-call row — the shadowed `tool.call.toolview` registrant.\n *\n * For the first call of a consecutive run of grouped tools, renders the run as\n * one card (the first call's full row) plus one compact child row per\n * continuation call. For a continuation call, renders nothing: the seat stays\n * empty and the injected stylesheet collapses it out of the flow. When the\n * call is not a chat tool-call node at all (e.g. dispatched as a subcall),\n * falls back to a plain single row so the call never disappears.\n *\n * The row surface mirrors the built-in generic ToolRow: a variant title/icon\n * plus the settled card (read/search/diff/terminal/web) or IN/OUT text.\n *\n * Everything here is a pure function of the chat snapshot + the frozen call\n * slices (replay-deterministic); expand state is component-local view state.\n * @module\n */\nimport { memo, useLayoutEffect, useRef, useState, type CSSProperties, type KeyboardEvent, type MouseEvent, type ReactNode } from 'react'\nimport {\n DiffBlock, DisclosureRow, IconApiOutline14, IconBrowseOutline16, IconCodeOutline16,\n IconEditOutline16, IconSearchOutline16, IconSparkle16, ReadBlock, SearchBlock, StateDot,\n TerminalBlock, WebBlock, type TerminalBlockLabels,\n} from '@deepseek-ai/dsh-client-ui-primitives'\nimport type { ToolCallBlock } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { ToolCallViewProps } from '@deepseek-ai/dsh-client-ui-tool/client'\nimport type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { MergeToolCallsConfig } from '../types.ts'\nimport { callRowModel, type CallRowModel, type RowState, type ToolVariant } from './card-model.ts'\nimport { readRun } from './merge-run.ts'\n\n/** Chat rows show a capped card; the details panel stays the full-height surface. */\nconst CHAT_READ_MAX_LINES = 8\nconst CHAT_SEARCH_MAX_LINES = 8\nconst CHAT_DIFF_MAX_LINES = 8\n\n/** Locale seat and inject face of the merged row registration. */\nexport type MergedToolRowProps = ToolCallViewProps\n & PropsLocale<'merge-tool-calls'>\n & { readonly cfg: MergeToolCallsConfig }\n\n/** Variant leading icons (figma table); all glyphs render at 14 inside the 16px leading box. */\nconst VARIANT_ICONS: Record<ToolVariant, ReactNode> = {\n search: <IconSearchOutline16 size={14} />,\n read: <IconBrowseOutline16 size={14} />,\n bash: <IconApiOutline14 size={14} />,\n write: <IconEditOutline16 size={14} />,\n edit: <IconEditOutline16 size={14} />,\n code: <IconCodeOutline16 size={14} />,\n others: <IconSparkle16 size={14} />,\n}\n\n/**\n * Per-variant locale key for the merged-count summary (`{n} Files`, `{n}\n * Commands`, …). Replaces the first call's file path on the main row when the\n * run merges more than one call, so the main row reads `Read · 5 Files` and\n * the file paths move to the collapsed child rows.\n */\nconst VARIANT_COUNT_KEY: Record<ToolVariant, 'countFiles' | 'countQueries' | 'countCommands' | 'countPrograms' | 'countCalls'> = {\n read: 'countFiles',\n write: 'countFiles',\n edit: 'countFiles',\n search: 'countQueries',\n bash: 'countCommands',\n code: 'countPrograms',\n others: 'countCalls',\n}\n\n/** Leading-slot state substitution, mirroring the shipped row. */\nfunction leadingFor(state: RowState, icon: ReactNode): ReactNode {\n switch (state) {\n case 'error': return <StateDot state=\"error\" />\n case 'stopped': return <StateDot state=\"warning\" />\n default: return icon\n }\n}\n\n/** Visually hidden run-state label (the StateDot and sweep are colour-only). */\nfunction stateStatus(state: RowState, t: MergedToolRowProps['t']): string | null {\n switch (state) {\n case 'running': return t('running')\n case 'error': return t('failed')\n case 'stopped': return t('stopped')\n default: return null\n }\n}\n\n/** TerminalBlock display copy from the plugin's own dictionary. */\nfunction terminalLabels(t: MergedToolRowProps['t']): TerminalBlockLabels {\n return {\n signal: signal => t('terminal.signal', { signal }),\n exitCode: code => t('terminal.exitCode', { code }),\n running: t('terminal.running'),\n failed: t('terminal.failed'),\n done: t('terminal.done'),\n copy: t('terminal.copy'),\n copied: t('terminal.copied'),\n noOutput: t('terminal.noOutput'),\n collapseAria: t('terminal.collapseAria'),\n collapse: t('terminal.collapse'),\n expandAria: hidden => t('terminal.expandAria', { n: hidden }),\n expand: hidden => t('terminal.expandRest', { n: hidden }),\n }\n}\n\n/** One call's expanded-body card, mirroring the built-in ToolRow body. */\nfunction CardBody({ model, t }: { model: CallRowModel; t: MergedToolRowProps['t'] }) {\n if (model.terminal !== null) {\n return <TerminalBlock {...model.terminal.card} maxLines={Infinity} labels={terminalLabels(t)} />\n }\n if (model.diff !== null) {\n return <DiffBlock {...model.diff.card} maxLines={CHAT_DIFF_MAX_LINES} />\n }\n if (model.read !== null) {\n return <ReadBlock {...model.read} maxLines={CHAT_READ_MAX_LINES} />\n }\n if (model.search !== null) {\n return (\n <>\n <SearchBlock {...model.search.card} maxLines={CHAT_SEARCH_MAX_LINES} />\n {model.search.recovery !== undefined && (\n <div className=\"mtc-recovery\">{model.search.recovery}</div>\n )}\n </>\n )\n }\n if (model.web !== null) {\n return <WebBlock {...model.web} />\n }\n const hasBody = model.body !== null\n const hasOutput = model.output !== null\n if (!hasBody && !hasOutput) return null\n return (\n <div className=\"mtc-io-card\">\n {hasBody && (\n <div className=\"mtc-io-section\">\n <span className=\"mtc-io-label\">IN</span>\n <span className=\"mtc-io-text\">{model.body}</span>\n </div>\n )}\n {hasBody && hasOutput && <span className=\"mtc-io-divider\" aria-hidden />}\n {hasOutput && (\n <div className=\"mtc-io-section\">\n <span className=\"mtc-io-label\">OUT</span>\n <span className=\"mtc-io-text\" data-error={model.state === 'error' || undefined}>{model.output}</span>\n </div>\n )}\n </div>\n )\n}\n\n/**\n * The run's main card: the first call's chrome (variant title/icon + state)\n * plus, for a single call, its expandable content card. For a merged run the\n * main row's collapsed summary is the count (`Read · 5 Files`) and the\n * expanded body is the child-rows block (passed in as `children`); the first\n * call is rendered as the first child row so every file path lands on a child\n * row, never on the main row.\n */\nexport const RowCard = memo(function RowCard({\n toolName, block, cwd, openFile, inspect, t, mergedCount, children,\n}: {\n toolName: string\n block: ToolCallBlock\n cwd: string | undefined\n openFile: (path: string) => void\n inspect: (() => void) | undefined\n t: MergedToolRowProps['t']\n /** Continuation-call count; 0 means a single-call row (no child rows). */\n mergedCount: number\n /** Child rows for the merged run (all calls, including the first). */\n children?: ReactNode\n}) {\n const model = callRowModel(toolName, block, cwd)\n const [expanded, setExpanded] = useState(false)\n const hasChildren = mergedCount > 0\n const expandable = model.expandable || hasChildren\n const open = expanded && expandable\n const status = stateStatus(model.state, t)\n const failureLine = model.state === 'error' ? model.errorSummary ?? null : null\n // Merged runs trade the first file path + `+n` suffix for a count summary,\n // so the main row reads `Read · 5 Files`; the file paths live on the child\n // rows. An error on the first call still surfaces its error text instead.\n const summaryText = hasChildren && failureLine === null\n ? t(VARIANT_COUNT_KEY[model.variant], { n: String(mergedCount + 1) })\n : failureLine ?? model.summary\n // The open-file link only applies to single-call rows (the merged summary is\n // a count, not a path).\n const fileLink = !hasChildren && model.filePath !== undefined && openFile !== undefined && failureLine === null\n const openFileClick = (event: MouseEvent<HTMLButtonElement>) => {\n event.stopPropagation()\n if (model.filePath !== undefined) openFile(model.filePath)\n }\n return (\n <div className=\"mtc-row\" data-state={model.state} data-variant={model.variant} data-merged={hasChildren || undefined}>\n {status !== null && <span className=\"mtc-visually-hidden\">{status}</span>}\n <DisclosureRow\n rowClassName=\"mtc-title-row\"\n titleClassName=\"mtc-title\"\n leadingClassName=\"mtc-leading\"\n chevronClassName=\"mtc-chevron\"\n icon={leadingFor(model.state, VARIANT_ICONS[model.variant])}\n title={model.title}\n open={open}\n expandable={expandable}\n expandOnRowClick\n keepContentWhenOpen\n onToggle={() => { setExpanded(value => !value) }}\n collapsedContent={summaryText !== '' && (\n <>\n <span className=\"mtc-sep\" aria-hidden />\n {fileLink ? (\n <button type=\"button\" className=\"mtc-summary-link\" onClick={openFileClick}>{summaryText}</button>\n ) : (\n <span className=\"mtc-summary\">{summaryText}</span>\n )}\n </>\n )}\n >\n {/* Single-call mode: the first call's expandable content card lives in\n the disclosure body. Merged mode renders nothing here — the child\n rows block is a sibling below, animated via its own collapse grid. */}\n {!hasChildren && (\n <div className=\"mtc-card-body\">\n <CardBody model={model} t={t} />\n {inspect !== undefined && (\n <button type=\"button\" className=\"mtc-inspect\" onClick={inspect}>Inspect</button>\n )}\n </div>\n )}\n </DisclosureRow>\n {hasChildren && (\n // The animated children block. Lives outside DisclosureRow so the\n // grid-template-rows transition can keep the rows in the DOM while\n // collapsing (DisclosureRow would unmount them instantly). The\n // --mtc-sep-left custom property is set by the parent on .mtc-root.\n <div className=\"mtc-children-collapse\" data-open={open || undefined}>\n <div className=\"mtc-children\">\n {children}\n </div>\n </div>\n )}\n {hasChildren && open && inspect !== undefined && (\n <button type=\"button\" className=\"mtc-inspect\" onClick={inspect}>Inspect</button>\n )}\n </div>\n )\n})\n\n/**\n * One compact continuation row: the main row's tail structure ([sep dot][path]).\n * An expandable call toggles the inline content card on click (mirroring the\n * main row's whole-row disclosure); a read/write/edit-family path additionally\n * renders as an open-file link (the sidebar preview) that stops propagation,\n * exactly like the main row's summary link.\n */\nexport const ChildRow = memo(function ChildRow({\n toolName, block, cwd, openFile, t,\n}: {\n toolName: string\n block: ToolCallBlock\n cwd: string | undefined\n openFile: (path: string) => void\n t: MergedToolRowProps['t']\n}) {\n const model = callRowModel(toolName, block, cwd)\n const [open, setOpen] = useState(false)\n const stateLabel = stateStatus(model.state, t)\n const expandable = model.expandable\n const toggle = (): void => { setOpen(value => !value) }\n const openFileClick = (event: MouseEvent<HTMLButtonElement>) => {\n event.stopPropagation()\n if (model.filePath !== undefined) openFile(model.filePath)\n }\n const onRowKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n toggle()\n }\n }\n return (\n <div className=\"mtc-child\">\n <div\n className=\"mtc-child-row\"\n data-open={expandable && open || undefined}\n data-static={expandable ? undefined : true}\n role={expandable ? 'button' : undefined}\n tabIndex={expandable ? 0 : undefined}\n aria-expanded={expandable ? open : undefined}\n onClick={expandable ? toggle : undefined}\n onKeyDown={expandable ? onRowKeyDown : undefined}\n >\n <span className=\"mtc-sep\" aria-hidden />\n {model.filePath !== undefined ? (\n <button type=\"button\" className=\"mtc-child-path-link\" onClick={openFileClick}>{model.summary}</button>\n ) : (\n <span className=\"mtc-child-path\">{model.summary}</span>\n )}\n {stateLabel !== null && (\n <span className=\"mtc-child-state\" data-error={model.state === 'error' || undefined}>{stateLabel}</span>\n )}\n </div>\n {expandable && open && (\n <div className=\"mtc-child-body\">\n <CardBody model={model} t={t} />\n </div>\n )}\n </div>\n )\n})\n\n/**\n * The shadowed toolview: renders the merged run card for the run's first call,\n * nothing for continuation calls, and a plain single row when this call is not\n * a chat tool-call node.\n *\n * Child-row alignment is measured at runtime, not hardcoded: the main row's\n * separator dot sits after a variable-width title (\"Read\"/\"Search\"/\"Bash\"/…),\n * so its column depends on the rendered font. A layout effect measures the\n * dot's offset from the card root (once, plus on reflow via ResizeObserver)\n * and indents the children so their dots and paths land on the main row's\n * columns — no font/title constants to keep in sync.\n */\nexport function MergedToolRow({ callId, toolName, block, cwd, openFile, inspect, t, cfg, useSession }: MergedToolRowProps) {\n const run = useSession(snapshot => readRun(\n snapshot.chat.order,\n snapshot.chat.nodes,\n callId,\n cfg.tools,\n cfg.groupBy,\n cfg.maxGroupSize,\n ))\n const rootRef = useRef<HTMLDivElement>(null)\n /** Main separator dot's left offset from the card root; null before first measure. */\n const [sepLeft, setSepLeft] = useState<number | null>(null)\n\n useLayoutEffect(() => {\n const root = rootRef.current\n if (root === null) return\n const sep = root.querySelector<HTMLElement>('.mtc-row .mtc-sep')\n if (sep === null) return\n const measure = (): void => {\n setSepLeft(Math.round(sep.getBoundingClientRect().left - root.getBoundingClientRect().left))\n }\n measure()\n // Font swaps, locale switches, and column resizes move the dot; keep the\n // alignment current without re-running the effect.\n const observer = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(measure)\n observer?.observe(root)\n return () => { observer?.disconnect() }\n }, [run === null ? null : run.blocks[0]?.callId ?? null])\n\n if (run === null) {\n // Not a chat tool-call node (subcall dispatch): never blank the call.\n return (\n <RowCard\n toolName={toolName}\n block={block}\n cwd={cwd}\n openFile={openFile}\n inspect={inspect}\n t={t}\n mergedCount={0}\n />\n )\n }\n if (!run.isFirst) return null\n const hasChildren = run.blocks.length > 1\n return (\n <div\n className=\"mtc-root\"\n data-tool={toolName}\n ref={rootRef}\n // The measured dot column lands the children's sep dot on the main row's\n // summary column. Set on the root so it cascades into RowCard's\n // .mtc-children block.\n style={sepLeft === null ? undefined : { ['--mtc-sep-left']: `${sepLeft}px` } as CSSProperties}\n >\n <RowCard\n toolName={toolName}\n block={run.blocks[0] ?? block}\n cwd={cwd}\n openFile={openFile}\n inspect={inspect}\n t={t}\n mergedCount={run.blocks.length - 1}\n >\n {/* Every call (including the first) renders as a child row, so the\n main row's summary stays a count (`Read · 5 Files`) and every file\n path lands on its own child row. */}\n {hasChildren && run.blocks.map(child => (\n <ChildRow key={child.callId} toolName={toolName} block={child} cwd={cwd} openFile={openFile} t={t} />\n ))}\n </RowCard>\n </div>\n )\n}\n","/**\n * One scoped stylesheet injected for the lifetime of the client activation.\n *\n * Two jobs:\n * 1. The empty-seat collapse rule: the shadowed toolview renders `null` for\n * every non-first call of a merged run. The built-in `ToolCallTree` still\n * wraps that null in its `.callRow` div, and the renderer wraps every\n * toolview in an empty `<div data-slot=\"tool.call.toolview\">` (display:\n * contents), so the seat's `.flowItem` is never `:empty` itself and the\n * built-in `.flowItem:empty { display:none }` rule does not fire. This\n * rule extends the same intent (\"a renderer may decline its row\") to a\n * tool seat whose toolview rendered nothing.\n * 2. The plugin's own `.mtc-*` chrome for the merged card and its child rows.\n *\n * All colors come from the shared `--dsw-*` tokens (never literals).\n */\nexport const CSS = `\n/* A tool-call seat whose toolview declined to render (merged-run continuation\n calls) must not consume the flow column's gap. The empty toolview slot\n wrapper is the decline signal; the callRow around it always exists. */\n[data-chat-flow-kind=\"tool-call\"]:has([data-slot=\"tool.call.toolview\"]:empty) { display: none; }\n\n.mtc-root { display: flex; flex-direction: column; min-width: 0; }\n.mtc-row { display: flex; flex-direction: column; min-width: 0; }\n.mtc-row[data-state='running'] .mtc-title-row { position: relative; overflow: hidden; }\n.mtc-row[data-state='running'] .mtc-title-row::after {\n content: '';\n position: absolute;\n top: 0; bottom: 0; left: 0;\n width: 300px;\n background: linear-gradient(90deg, transparent 0%, color-mix(in srgb, var(--dsw-alias-bg-base) 60%, transparent) 55%, transparent 100%);\n animation: dsh-mtc-sweep 2.6s ease-out infinite;\n pointer-events: none;\n}\n@keyframes dsh-mtc-sweep {\n 0% { left: -300px; }\n 90%, 100% { left: 100%; }\n}\n\n.mtc-title { font-weight: 400; }\n.mtc-sep { flex: none; width: 2px; height: 2px; border-radius: 1px; margin: 0 8px; background: var(--dsw-alias-label-caption); }\n.mtc-summary {\n flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n font-size: 14px; line-height: 24px; color: var(--dsw-alias-label-tertiary);\n}\n.mtc-summary-link {\n flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n margin: 0; padding: 0; border: none; background: none; font: inherit; text-align: left;\n font-size: 14px; line-height: 24px; color: var(--dsw-alias-label-secondary);\n text-decoration: underline; text-decoration-color: var(--dsw-alias-label-quaternary);\n text-underline-offset: 3px; cursor: pointer;\n}\n.mtc-summary-link:hover { color: var(--dsw-alias-label-primary); text-decoration-color: currentColor; }\n.mtc-summary-error { color: var(--dsw-alias-state-error-primary); }\n.mtc-summary-suffix { flex: none; margin-left: 4px; white-space: nowrap; font-size: 14px; line-height: 24px; color: var(--dsw-alias-label-tertiary); }\n\n.mtc-card-body { margin: 4px 0 4px 4px; min-width: 0; }\n.mtc-recovery { margin: 4px 0 4px 4px; white-space: pre-wrap; overflow-wrap: anywhere; font: var(--dsw-font-xs-13); color: var(--dsw-alias-label-tertiary); }\n/* IN/OUT text card for calls without a card primitive (mirrors ToolRow). */\n.mtc-io-card {\n display: flex; flex-direction: column; min-width: 0;\n border: 1px solid var(--dsw-alias-border-l2); border-radius: 12px;\n background: var(--dsw-alias-bg-base);\n}\n.mtc-io-section { display: flex; gap: 10px; padding: 8px 12px; min-width: 0; }\n.mtc-io-label {\n flex: none; font-size: 11px; line-height: 16px; font-weight: 600;\n color: var(--dsw-alias-label-tertiary);\n}\n.mtc-io-text {\n flex: 1 1 auto; min-width: 0; white-space: pre-wrap; overflow-wrap: anywhere;\n font-size: 13px; line-height: 20px; color: var(--dsw-alias-label-secondary);\n}\n.mtc-io-text[data-error] { color: var(--dsw-alias-state-error-primary); }\n.mtc-io-divider { height: 1px; background: var(--dsw-alias-border-l2); }\n.mtc-inspect {\n display: inline-flex; align-self: flex-start; align-items: center; gap: 4px;\n margin: 4px 0 2px 4px; padding: 2px 8px;\n border: 1px solid var(--dsw-alias-border-l2); border-radius: 999px;\n background: var(--dsw-alias-bg-base); color: var(--dsw-alias-label-secondary);\n font-size: 11px; line-height: 16px; cursor: pointer;\n opacity: 0; transition: opacity 100ms ease;\n}\n.mtc-root:hover .mtc-inspect, .mtc-inspect:focus-visible { opacity: 1; }\n.mtc-inspect:hover { background: var(--dsw-alias-interactive-bg-hover-solid); color: var(--dsw-alias-label-primary); }\n\n/* Compact child rows: each is the main row's tail structure — [sep dot][path].\n The dot/path columns are set at runtime by the component (it measures the\n main row's sep offset and sets the --mtc-sep-left custom property on the\n root), so this is only the pre-measure fallback.\n\n The .mtc-children-collapse wrapper animates the block's height with a\n grid-template-rows 0fr↔1fr transition (children stay in the DOM while\n collapsing, so the slide is smooth instead of popping out). The vertical\n margin lives on the wrapper (not .mtc-children) so it doesn't leak past the\n 0fr row when collapsed. */\n.mtc-children-collapse {\n display: grid;\n grid-template-rows: 0fr;\n margin: 0;\n transition: grid-template-rows 200ms ease-out, margin 200ms ease-out;\n}\n.mtc-children-collapse[data-open] {\n grid-template-rows: 1fr;\n margin: 2px 0;\n}\n.mtc-children {\n display: flex; flex-direction: column; gap: 1px;\n margin: 0 0 0 var(--mtc-sep-left, 61px);\n min-width: 0; min-height: 0; overflow: hidden;\n opacity: 0;\n transition: opacity 150ms ease-out;\n}\n.mtc-children-collapse[data-open] .mtc-children {\n opacity: 1;\n transition: opacity 150ms ease-out 50ms;\n}\n.mtc-child { display: flex; flex-direction: column; min-width: 0; }\n.mtc-child-row {\n display: flex; align-items: center; gap: 0; min-width: 0; height: 20px;\n margin: 0; padding: 0; border: 0; border-radius: 4px; background: none;\n font: inherit; text-align: left; color: var(--dsw-alias-label-secondary); cursor: pointer;\n}\n.mtc-child-row:hover { background: var(--dsw-alias-interactive-bg-hover); color: var(--dsw-alias-label-primary); }\n.mtc-child-row[data-static] { cursor: default; }\n.mtc-child-row[data-static]:hover { background: none; color: var(--dsw-alias-label-secondary); }\n.mtc-child-row .mtc-sep { margin: 0 8px 0 0; }\n.mtc-child-path {\n flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n font-size: 14px; line-height: 20px;\n}\n/* Read-family child paths are open-file links (sidebar preview), same affordance\n as the main row's summary link. */\n.mtc-child-path-link {\n flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n margin: 0; padding: 0; border: none; background: none; font: inherit; text-align: left;\n font-size: 14px; line-height: 20px; color: var(--dsw-alias-label-secondary);\n text-decoration: underline; text-decoration-color: var(--dsw-alias-label-quaternary);\n text-underline-offset: 3px; cursor: pointer;\n}\n.mtc-child-path-link:hover { color: var(--dsw-alias-label-primary); text-decoration-color: currentColor; }\n.mtc-child-state { flex: none; font-size: 11px; line-height: 16px; color: var(--dsw-alias-label-tertiary); }\n.mtc-child-state[data-error] { color: var(--dsw-alias-state-error-primary); }\n/* The expanded child card must not inherit the children block's sep indent:\n pull its left edge back to the main card body's column (root + 4px) so the\n card spans the card width instead of leaving a blank gutter on its left. */\n.mtc-child-body { margin: 2px 0 2px calc(4px - var(--mtc-sep-left, 61px)); min-width: 0; }\n\n.mtc-visually-hidden {\n position: absolute; width: 1px; height: 1px; overflow: hidden;\n clip: rect(0 0 0 0); white-space: nowrap;\n}\n`\n\n/** Install the stylesheet and return its disposer. */\nexport function installStyles(): () => void {\n const style = document.createElement('style')\n style.setAttribute('data-merge-tool-calls-style', '')\n style.textContent = CSS\n document.head.appendChild(style)\n return () => { style.remove() }\n}\n","/**\n * merge-tool-calls — browser half.\n *\n * Shadows the shipped `tool.call.toolview` entries for every grouped tool at\n * priority -1 (the keyed slot's shadowing rule: lowest priority renders). An\n * empty `tools` config means every built-in generic-family tool\n * (see {@link ALL_TOOL_NAMES}); a non-empty list is an explicit whitelist.\n * The shadowed component merges consecutive calls of one tool in the chat flow\n * into a single card with compact child rows.\n *\n * @module @dsh-external/dsh-plugin-merge-tool-calls/client\n */\n\nimport type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-tool/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-slots'\nimport { DEFAULT_MERGE_CONFIG, type MergeToolCallsConfig } from '../types.ts'\nimport { en, NS, zh, type MergeToolCallsKey } from './locales.ts'\nimport { MergedToolRow } from './rows.tsx'\nimport { installStyles } from './styles.ts'\nimport { ALL_TOOL_NAMES } from './tool-names.ts'\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** Copy for the merged tool-call rows. */\n 'merge-tool-calls': MergeToolCallsKey\n }\n}\n\n/** Required services: the slot registry (toolview shadowing) and locale. */\nexport const inject = ['slots', 'locale']\n\n/**\n * Register one shadowed toolview per grouped tool.\n * @param ctx - client root context.\n * @param config - row config; defaults apply when the loader passes none.\n */\nexport function apply(ctx: ClientContext, config: Partial<MergeToolCallsConfig> = {}): void {\n const cfg: MergeToolCallsConfig = { ...DEFAULT_MERGE_CONFIG, ...config }\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'merge-tool-calls: dictionaries')\n ctx.effect(installStyles, 'merge-tool-calls: styles')\n\n const toolNames = cfg.tools.length === 0 ? ALL_TOOL_NAMES : [...new Set(cfg.tools)]\n for (const tool of toolNames) {\n ctx.slots.inject('tool.call.toolview', () => ctx.slots.register({\n name: 'tool.call.toolview',\n key: tool,\n priority: -1,\n locale: NS,\n inject: () => ({ cfg }),\n }, MergedToolRow))\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"client.js","names":["DEFAULT_MERGE_CONFIG: MergeToolCallsConfig","dicts: Record<string, Record<MergeToolCallsKey, string>>","ALL_TOOL_NAMES: readonly string[]","VARIANT_TITLES: Record<ToolVariant, string>","TOOL_VARIANTS: Record<string, ToolVariant>","TOOL_TITLES: Record<string, string>","parts: string[]","parsed: unknown","SUMMARY_KEYS: Record<ToolVariant, readonly string[]>","FILE_PATH_VARIANTS: ReadonlySet<ToolVariant>","state: RowState","out: DiffHunk[]","diffs","blocks: ToolCallBlock[]","VARIANT_ICONS: Record<ToolVariant, ReactNode>","IconSearchOutline16","IconBrowseOutline16","IconApiOutline14","IconEditOutline16","IconCodeOutline16","IconSparkle16","VARIANT_COUNT_KEY: Record<ToolVariant, 'countFiles' | 'countQueries' | 'countCommands' | 'countPrograms' | 'countCalls'>","StateDot","TerminalBlock","DiffBlock","ReadBlock","SearchBlock","WebBlock","RowCard","DisclosureRow","ChildRow","cfg: MergeToolCallsConfig","dispose: (() => void) | undefined"],"sources":["../src/types.ts","../src/client/dictionaries.ts","../src/client/locales.ts","../src/client/tool-names.ts","../src/client/card-model.ts","../src/client/merge-run.ts","../src/client/rows.tsx","../src/client/styles.ts","../src/client/index.ts"],"sourcesContent":["/** Shared config surface of the merge-tool-calls plugin (host + client halves). */\n\n/** How consecutive same-tool calls are grouped into one merged card. */\nexport type MergeGroupMode = 'adjacent' | 'step'\n\n/** Plugin configuration; defaults live in the Schemastery schema and cordis.patch.yml. */\nexport interface MergeToolCallsConfig {\n /**\n * Wire tool names whose consecutive calls merge. Empty = every built-in\n * generic-family tool (read/grep/glob/edit/write/bash/pwsh/web_search/\n * web_fetch/run_code/cordis_package_inspect/cordis_runtime_inspect); a\n * non-empty list is an explicit whitelist (any wire name works).\n */\n readonly tools: readonly string[]\n /** `adjacent`: any consecutive run in the chat flow; `step`: only within one agent step. */\n readonly groupBy: MergeGroupMode\n /** Max calls per merged group; the run is truncated and the excess starts a new group. */\n readonly maxGroupSize: number\n}\n\n/** Runtime defaults applied when a half receives no config (defensive only). */\nexport const DEFAULT_MERGE_CONFIG: MergeToolCallsConfig = {\n tools: [],\n groupBy: 'adjacent',\n maxGroupSize: 8,\n}\n","/**\n * The 19 better-locale override languages for the merged tool-call row copy,\n * keyed by language id. Each dictionary carries the same key set as `en`/`zh`\n * in `./locales.ts` (enforced by the `Record<MergeToolCallsKey, string>`\n * annotation); values keep `{placeholder}` interpolation (e.g. `{n}` in\n * `mergedCount`), matching the better-locale store's `LocaleDict` contract.\n *\n * The apply function registers these into `ctx.betterLocale` (the override\n * store) under `NS`, so when the user selects an override language through\n * dsh-plugin-better-locale (and DSH is on 'en', whose slot the override\n * borrows), the row copy renders in the override language.\n */\nimport type { MergeToolCallsKey } from './locales.ts'\n\n/** All override-language dictionaries for the `NS` namespace. */\nexport const dicts: Record<string, Record<MergeToolCallsKey, string>> = {\n ja: {\n running: '実行中',\n failed: '失敗',\n stopped: '中断',\n expand: '展開',\n collapse: '折りたたむ',\n more: '+{n}',\n showLess: '残りを閉じる',\n mergedCount: '{n} 回の呼び出しを統合',\n countFiles: '{n} 個のファイル',\n countQueries: '{n} 回の検索',\n countCommands: '{n} 個のコマンド',\n countPrograms: '{n} 個のプログラム',\n countCalls: '{n} 回の呼び出し',\n 'terminal.signal': 'シグナル {signal}',\n 'terminal.exitCode': '終了コード {code}',\n 'terminal.running': '実行中',\n 'terminal.failed': '失敗',\n 'terminal.done': '完了',\n 'terminal.copy': 'コピー',\n 'terminal.copied': 'コピーしました',\n 'terminal.noOutput': '出力なし',\n 'terminal.collapseAria': '出力を折りたたむ',\n 'terminal.collapse': '折りたたむ',\n 'terminal.expandAria': '残り {n} 行の出力を展開',\n 'terminal.expandRest': '… 残り {n} 行',\n },\n de: {\n running: 'Läuft',\n failed: 'Fehlgeschlagen',\n stopped: 'Unterbrochen',\n expand: 'Erweitern',\n collapse: 'Einklappen',\n more: '+{n}',\n showLess: 'Weniger anzeigen',\n mergedCount: '{n} Aufrufe zusammengeführt',\n countFiles: '{n} Dateien',\n countQueries: '{n} Suchanfragen',\n countCommands: '{n} Befehle',\n countPrograms: '{n} Programme',\n countCalls: '{n} Aufrufe',\n 'terminal.signal': 'Signal {signal}',\n 'terminal.exitCode': 'Exit-Code {code}',\n 'terminal.running': 'Läuft',\n 'terminal.failed': 'Fehlgeschlagen',\n 'terminal.done': 'Abgeschlossen',\n 'terminal.copy': 'Kopieren',\n 'terminal.copied': 'Kopiert',\n 'terminal.noOutput': 'Keine Ausgabe',\n 'terminal.collapseAria': 'Ausgabe einklappen',\n 'terminal.collapse': 'Einklappen',\n 'terminal.expandAria': 'Die restlichen {n} Ausgabezeilen ausklappen',\n 'terminal.expandRest': '… {n} weitere Zeilen',\n },\n fr: {\n running: 'En cours',\n failed: 'Échec',\n stopped: 'Interrompu',\n expand: 'Développer',\n collapse: 'Réduire',\n more: '+{n}',\n showLess: 'Afficher moins',\n mergedCount: '{n} appels fusionnés',\n countFiles: '{n} fichiers',\n countQueries: '{n} recherches',\n countCommands: '{n} commandes',\n countPrograms: '{n} programmes',\n countCalls: '{n} appels',\n 'terminal.signal': 'Signal {signal}',\n 'terminal.exitCode': 'Code de sortie {code}',\n 'terminal.running': 'En cours',\n 'terminal.failed': 'Échec',\n 'terminal.done': 'Terminé',\n 'terminal.copy': 'Copier',\n 'terminal.copied': 'Copié',\n 'terminal.noOutput': 'Aucune sortie',\n 'terminal.collapseAria': 'Réduire la sortie',\n 'terminal.collapse': 'Réduire',\n 'terminal.expandAria': 'Déployer les {n} lignes de sortie restantes',\n 'terminal.expandRest': '… {n} lignes restantes',\n },\n pt: {\n running: 'Executando',\n failed: 'Falhou',\n stopped: 'Interrompido',\n expand: 'Expandir',\n collapse: 'Recolher',\n more: '+{n}',\n showLess: 'Mostrar menos',\n mergedCount: '{n} chamadas mescladas',\n countFiles: '{n} arquivos',\n countQueries: '{n} pesquisas',\n countCommands: '{n} comandos',\n countPrograms: '{n} programas',\n countCalls: '{n} chamadas',\n 'terminal.signal': 'sinal {signal}',\n 'terminal.exitCode': 'código de saída {code}',\n 'terminal.running': 'Executando',\n 'terminal.failed': 'Falhou',\n 'terminal.done': 'Concluído',\n 'terminal.copy': 'Copiar',\n 'terminal.copied': 'Copiado',\n 'terminal.noOutput': 'Sem saída',\n 'terminal.collapseAria': 'Recolher saída',\n 'terminal.collapse': 'Recolher',\n 'terminal.expandAria': 'Expandir as {n} linhas de saída restantes',\n 'terminal.expandRest': '… mais {n} linhas',\n },\n ko: {\n running: '실행 중',\n failed: '실패',\n stopped: '중단됨',\n expand: '펼치기',\n collapse: '접기',\n more: '+{n}',\n showLess: '더 적게 표시',\n mergedCount: '{n}번 호출 병합됨',\n countFiles: '파일 {n}개',\n countQueries: '검색 {n}회',\n countCommands: '명령 {n}개',\n countPrograms: '프로그램 {n}개',\n countCalls: '호출 {n}회',\n 'terminal.signal': '신호 {signal}',\n 'terminal.exitCode': '종료 코드 {code}',\n 'terminal.running': '실행 중',\n 'terminal.failed': '실패',\n 'terminal.done': '완료',\n 'terminal.copy': '복사',\n 'terminal.copied': '복사됨',\n 'terminal.noOutput': '출력 없음',\n 'terminal.collapseAria': '출력 접기',\n 'terminal.collapse': '접기',\n 'terminal.expandAria': '나머지 {n}줄 출력 펼치기',\n 'terminal.expandRest': '… 나머지 {n}줄',\n },\n ar: {\n running: 'قيد التشغيل',\n failed: 'فشل',\n stopped: 'متوقف',\n expand: 'توسيع',\n collapse: 'طي',\n more: '+{n}',\n showLess: 'إظهار أقل',\n mergedCount: 'تم دمج {n} استدعاء',\n countFiles: '{n} ملف',\n countQueries: '{n} بحث',\n countCommands: '{n} أمر',\n countPrograms: '{n} برنامج',\n countCalls: '{n} استدعاء',\n 'terminal.signal': 'إشارة {signal}',\n 'terminal.exitCode': 'رمز الخروج {code}',\n 'terminal.running': 'قيد التشغيل',\n 'terminal.failed': 'فشل',\n 'terminal.done': 'اكتمل',\n 'terminal.copy': 'نسخ',\n 'terminal.copied': 'تم النسخ',\n 'terminal.noOutput': 'لا يوجد إخراج',\n 'terminal.collapseAria': 'طي الإخراج',\n 'terminal.collapse': 'طي',\n 'terminal.expandAria': 'توسيع {n} أسطر إخراج المتبقية',\n 'terminal.expandRest': '… و{n} أسطر أخرى',\n },\n hi: {\n running: 'चल रहा',\n failed: 'विफल',\n stopped: 'रोका गया',\n expand: 'विस्तार करें',\n collapse: 'संक्षिप्त करें',\n more: '+{n}',\n showLess: 'कम दिखाएं',\n mergedCount: '{n} कॉल मर्ज किए गए',\n countFiles: '{n} फ़ाइलें',\n countQueries: '{n} खोज',\n countCommands: '{n} कमांड',\n countPrograms: '{n} प्रोग्राम',\n countCalls: '{n} कॉल',\n 'terminal.signal': 'सिग्नल {signal}',\n 'terminal.exitCode': 'एग्ज़िट कोड {code}',\n 'terminal.running': 'चल रहा',\n 'terminal.failed': 'विफल',\n 'terminal.done': 'पूर्ण',\n 'terminal.copy': 'कॉपी',\n 'terminal.copied': 'कॉपी हो गया',\n 'terminal.noOutput': 'कोई आउटपुट नहीं',\n 'terminal.collapseAria': 'आउटपुट संक्षिप्त करें',\n 'terminal.collapse': 'संक्षिप्त करें',\n 'terminal.expandAria': 'शेष {n} आउटपुट पंक्तियाँ विस्तृत करें',\n 'terminal.expandRest': '… {n} और पंक्तियाँ',\n },\n id: {\n running: 'Berjalan',\n failed: 'Gagal',\n stopped: 'Dihentikan',\n expand: 'Bentangkan',\n collapse: 'Lipat',\n more: '+{n}',\n showLess: 'Tampilkan lebih sedikit',\n mergedCount: '{n} panggilan digabungkan',\n countFiles: '{n} file',\n countQueries: '{n} pencarian',\n countCommands: '{n} perintah',\n countPrograms: '{n} program',\n countCalls: '{n} panggilan',\n 'terminal.signal': 'sinyal {signal}',\n 'terminal.exitCode': 'kode keluar {code}',\n 'terminal.running': 'Berjalan',\n 'terminal.failed': 'Gagal',\n 'terminal.done': 'Selesai',\n 'terminal.copy': 'Salin',\n 'terminal.copied': 'Tersalin',\n 'terminal.noOutput': 'Tidak ada keluaran',\n 'terminal.collapseAria': 'Lipat keluaran',\n 'terminal.collapse': 'Lipat',\n 'terminal.expandAria': 'Bentangkan {n} baris keluaran tersisa',\n 'terminal.expandRest': '… {n} baris lagi',\n },\n tr: {\n running: 'Çalışıyor',\n failed: 'Başarısız',\n stopped: 'Durduruldu',\n expand: 'Genişlet',\n collapse: 'Daralt',\n more: '+{n}',\n showLess: 'Daha az göster',\n mergedCount: '{n} çağrı birleştirildi',\n countFiles: '{n} dosya',\n countQueries: '{n} arama',\n countCommands: '{n} komut',\n countPrograms: '{n} program',\n countCalls: '{n} çağrı',\n 'terminal.signal': 'sinyal {signal}',\n 'terminal.exitCode': 'çıkış kodu {code}',\n 'terminal.running': 'Çalışıyor',\n 'terminal.failed': 'Başarısız',\n 'terminal.done': 'Tamamlandı',\n 'terminal.copy': 'Kopyala',\n 'terminal.copied': 'Kopyalandı',\n 'terminal.noOutput': 'Çıkış yok',\n 'terminal.collapseAria': 'Çıkışı daralt',\n 'terminal.collapse': 'Daralt',\n 'terminal.expandAria': 'Kalan {n} çıkış satırını genişlet',\n 'terminal.expandRest': '… {n} satır daha',\n },\n vi: {\n running: 'Đang chạy',\n failed: 'Thất bại',\n stopped: 'Đã dừng',\n expand: 'Mở rộng',\n collapse: 'Thu gọn',\n more: '+{n}',\n showLess: 'Hiển thị ít hơn',\n mergedCount: 'Đã gộp {n} lệnh gọi',\n countFiles: '{n} tệp',\n countQueries: '{n} lượt tìm kiếm',\n countCommands: '{n} lệnh',\n countPrograms: '{n} chương trình',\n countCalls: '{n} lượt gọi',\n 'terminal.signal': 'tín hiệu {signal}',\n 'terminal.exitCode': 'mã thoát {code}',\n 'terminal.running': 'Đang chạy',\n 'terminal.failed': 'Thất bại',\n 'terminal.done': 'Đã xong',\n 'terminal.copy': 'Sao chép',\n 'terminal.copied': 'Đã sao chép',\n 'terminal.noOutput': 'Không có đầu ra',\n 'terminal.collapseAria': 'Thu gọn đầu ra',\n 'terminal.collapse': 'Thu gọn',\n 'terminal.expandAria': 'Mở rộng {n} dòng đầu ra còn lại',\n 'terminal.expandRest': '… còn {n} dòng',\n },\n th: {\n running: 'กำลังทำงาน',\n failed: 'ล้มเหลว',\n stopped: 'ถูกขัดจังหวะ',\n expand: 'ขยาย',\n collapse: 'ย่อ',\n more: '+{n}',\n showLess: 'แสดงน้อยลง',\n mergedCount: 'รวม {n} การเรียก',\n countFiles: '{n} ไฟล์',\n countQueries: '{n} การค้นหา',\n countCommands: '{n} คำสั่ง',\n countPrograms: '{n} โปรแกรม',\n countCalls: '{n} การเรียก',\n 'terminal.signal': 'สัญญาณ {signal}',\n 'terminal.exitCode': 'รหัสออก {code}',\n 'terminal.running': 'กำลังทำงาน',\n 'terminal.failed': 'ล้มเหลว',\n 'terminal.done': 'เสร็จสิ้น',\n 'terminal.copy': 'คัดลอก',\n 'terminal.copied': 'คัดลอกแล้ว',\n 'terminal.noOutput': 'ไม่มีเอาต์พุต',\n 'terminal.collapseAria': 'ย่อเอาต์พุต',\n 'terminal.collapse': 'ย่อ',\n 'terminal.expandAria': 'ขยายเอาต์พุตอีก {n} บรรทัด',\n 'terminal.expandRest': '… อีก {n} บรรทัด',\n },\n ru: {\n running: 'Выполняется',\n failed: 'Сбой',\n stopped: 'Прервано',\n expand: 'Развернуть',\n collapse: 'Свернуть',\n more: '+{n}',\n showLess: 'Показать меньше',\n mergedCount: 'Объединено вызовов: {n}',\n countFiles: '{n} файлов',\n countQueries: '{n} поисков',\n countCommands: '{n} команд',\n countPrograms: '{n} программ',\n countCalls: '{n} вызовов',\n 'terminal.signal': 'сигнал {signal}',\n 'terminal.exitCode': 'код выхода {code}',\n 'terminal.running': 'Выполняется',\n 'terminal.failed': 'Сбой',\n 'terminal.done': 'Завершено',\n 'terminal.copy': 'Копировать',\n 'terminal.copied': 'Скопировано',\n 'terminal.noOutput': 'Нет вывода',\n 'terminal.collapseAria': 'Свернуть вывод',\n 'terminal.collapse': 'Свернуть',\n 'terminal.expandAria': 'Показать оставшиеся {n} строк вывода',\n 'terminal.expandRest': '… ещё {n} строк',\n },\n it: {\n running: 'In esecuzione',\n failed: 'Non riuscito',\n stopped: 'Interrotto',\n expand: 'Espandi',\n collapse: 'Comprimi',\n more: '+{n}',\n showLess: 'Mostra meno',\n mergedCount: '{n} chiamate unite',\n countFiles: '{n} file',\n countQueries: '{n} ricerche',\n countCommands: '{n} comandi',\n countPrograms: '{n} programmi',\n countCalls: '{n} chiamate',\n 'terminal.signal': 'segnale {signal}',\n 'terminal.exitCode': 'codice di uscita {code}',\n 'terminal.running': 'In esecuzione',\n 'terminal.failed': 'Non riuscito',\n 'terminal.done': 'Completato',\n 'terminal.copy': 'Copia',\n 'terminal.copied': 'Copiato',\n 'terminal.noOutput': 'Nessun output',\n 'terminal.collapseAria': 'Comprimi output',\n 'terminal.collapse': 'Comprimi',\n 'terminal.expandAria': 'Espandi le restanti {n} righe di output',\n 'terminal.expandRest': '… altre {n} righe',\n },\n nl: {\n running: 'Actief',\n failed: 'Mislukt',\n stopped: 'Onderbroken',\n expand: 'Uitklappen',\n collapse: 'Inklappen',\n more: '+{n}',\n showLess: 'Minder tonen',\n mergedCount: '{n} aanroepen samengevoegd',\n countFiles: '{n} bestanden',\n countQueries: '{n} zoekopdrachten',\n countCommands: '{n} opdrachten',\n countPrograms: '{n} programma\\'s',\n countCalls: '{n} aanroepen',\n 'terminal.signal': 'signaal {signal}',\n 'terminal.exitCode': 'exit-code {code}',\n 'terminal.running': 'Actief',\n 'terminal.failed': 'Mislukt',\n 'terminal.done': 'Voltooid',\n 'terminal.copy': 'Kopiëren',\n 'terminal.copied': 'Gekopieerd',\n 'terminal.noOutput': 'Geen uitvoer',\n 'terminal.collapseAria': 'Uitvoer inklappen',\n 'terminal.collapse': 'Inklappen',\n 'terminal.expandAria': 'De overige {n} uitvoerregels uitklappen',\n 'terminal.expandRest': '… nog {n} regels',\n },\n sv: {\n running: 'Kör',\n failed: 'Misslyckades',\n stopped: 'Avbruten',\n expand: 'Expandera',\n collapse: 'Komprimera',\n more: '+{n}',\n showLess: 'Visa mindre',\n mergedCount: '{n} anrop sammanslagna',\n countFiles: '{n} filer',\n countQueries: '{n} sökningar',\n countCommands: '{n} kommandon',\n countPrograms: '{n} program',\n countCalls: '{n} anrop',\n 'terminal.signal': 'signal {signal}',\n 'terminal.exitCode': 'slutkod {code}',\n 'terminal.running': 'Kör',\n 'terminal.failed': 'Misslyckades',\n 'terminal.done': 'Klar',\n 'terminal.copy': 'Kopiera',\n 'terminal.copied': 'Kopierades',\n 'terminal.noOutput': 'Ingen utdata',\n 'terminal.collapseAria': 'Komprimera utdata',\n 'terminal.collapse': 'Komprimera',\n 'terminal.expandAria': 'Expandera de återstående {n} utdataraderna',\n 'terminal.expandRest': '… {n} fler rader',\n },\n pl: {\n running: 'Działa',\n failed: 'Nie powiodło się',\n stopped: 'Przerwano',\n expand: 'Rozwiń',\n collapse: 'Zwiń',\n more: '+{n}',\n showLess: 'Pokaż mniej',\n mergedCount: 'Połączono {n} wywołań',\n countFiles: '{n} plików',\n countQueries: '{n} wyszukań',\n countCommands: '{n} poleceń',\n countPrograms: '{n} programów',\n countCalls: '{n} wywołań',\n 'terminal.signal': 'sygnał {signal}',\n 'terminal.exitCode': 'kod wyjścia {code}',\n 'terminal.running': 'Działa',\n 'terminal.failed': 'Nie powiodło się',\n 'terminal.done': 'Ukończono',\n 'terminal.copy': 'Kopiuj',\n 'terminal.copied': 'Skopiowano',\n 'terminal.noOutput': 'Brak wyjścia',\n 'terminal.collapseAria': 'Zwiń wyjście',\n 'terminal.collapse': 'Zwiń',\n 'terminal.expandAria': 'Rozwiń pozostałe {n} wierszy wyjścia',\n 'terminal.expandRest': '…jeszcze {n} wierszy',\n },\n 'zh-HK': {\n running: '執行中',\n failed: '失敗',\n stopped: '已中斷',\n expand: '展開',\n collapse: '收起',\n more: '+{n}',\n showLess: '收起其餘',\n mergedCount: '已合併 {n} 次呼叫',\n countFiles: '{n} 個檔案',\n countQueries: '{n} 次搜尋',\n countCommands: '{n} 條命令',\n countPrograms: '{n} 段程式碼',\n countCalls: '{n} 次呼叫',\n 'terminal.signal': '訊號 {signal}',\n 'terminal.exitCode': '退出碼 {code}',\n 'terminal.running': '執行中',\n 'terminal.failed': '失敗',\n 'terminal.done': '已完成',\n 'terminal.copy': '複製',\n 'terminal.copied': '已複製',\n 'terminal.noOutput': '無輸出',\n 'terminal.collapseAria': '收起輸出',\n 'terminal.collapse': '收起',\n 'terminal.expandAria': '展開其餘 {n} 行輸出',\n 'terminal.expandRest': '… 其餘 {n} 行',\n },\n 'zh-TW': {\n running: '執行中',\n failed: '失敗',\n stopped: '已中斷',\n expand: '展開',\n collapse: '收起',\n more: '+{n}',\n showLess: '收起其餘',\n mergedCount: '已合併 {n} 次呼叫',\n countFiles: '{n} 個檔案',\n countQueries: '{n} 次搜尋',\n countCommands: '{n} 條命令',\n countPrograms: '{n} 段程式碼',\n countCalls: '{n} 次呼叫',\n 'terminal.signal': '訊號 {signal}',\n 'terminal.exitCode': '退出碼 {code}',\n 'terminal.running': '執行中',\n 'terminal.failed': '失敗',\n 'terminal.done': '已完成',\n 'terminal.copy': '複製',\n 'terminal.copied': '已複製',\n 'terminal.noOutput': '無輸出',\n 'terminal.collapseAria': '收起輸出',\n 'terminal.collapse': '收起',\n 'terminal.expandAria': '展開其餘 {n} 行輸出',\n 'terminal.expandRest': '… 其餘 {n} 行',\n },\n 'zh-MO': {\n running: '執行中',\n failed: '失敗',\n stopped: '已中斷',\n expand: '展開',\n collapse: '收起',\n more: '+{n}',\n showLess: '收起其餘',\n mergedCount: '已合併 {n} 次呼叫',\n countFiles: '{n} 個檔案',\n countQueries: '{n} 次搜尋',\n countCommands: '{n} 條命令',\n countPrograms: '{n} 段程式碼',\n countCalls: '{n} 次呼叫',\n 'terminal.signal': '訊號 {signal}',\n 'terminal.exitCode': '退出碼 {code}',\n 'terminal.running': '執行中',\n 'terminal.failed': '失敗',\n 'terminal.done': '已完成',\n 'terminal.copy': '複製',\n 'terminal.copied': '已複製',\n 'terminal.noOutput': '無輸出',\n 'terminal.collapseAria': '收起輸出',\n 'terminal.collapse': '收起',\n 'terminal.expandAria': '展開其餘 {n} 行輸出',\n 'terminal.expandRest': '… 其餘 {n} 行',\n },\n}","/** Product copy for the merged tool-call rows. */\nexport const zh = {\n running: '进行中',\n failed: '失败',\n stopped: '已中断',\n expand: '展开',\n collapse: '折叠',\n more: '+{n}',\n showLess: '收起其余',\n mergedCount: '已合并 {n} 次调用',\n countFiles: '{n} 个文件',\n countQueries: '{n} 次搜索',\n countCommands: '{n} 条命令',\n countPrograms: '{n} 段代码',\n countCalls: '{n} 次调用',\n 'terminal.signal': '信号 {signal}',\n 'terminal.exitCode': '退出码 {code}',\n 'terminal.running': '运行中',\n 'terminal.failed': '失败',\n 'terminal.done': '已完成',\n 'terminal.copy': '复制',\n 'terminal.copied': '已复制',\n 'terminal.noOutput': '无输出',\n 'terminal.collapseAria': '收起输出',\n 'terminal.collapse': '折叠',\n 'terminal.expandAria': '展开其余 {n} 行输出',\n 'terminal.expandRest': '… 其余 {n} 行',\n} satisfies Record<string, string>\n\nexport type MergeToolCallsKey = keyof typeof zh\n\nexport const en = {\n running: 'Running',\n failed: 'Failed',\n stopped: 'Interrupted',\n expand: 'Expand',\n collapse: 'Collapse',\n more: '+{n}',\n showLess: 'Show fewer',\n mergedCount: '{n} calls merged',\n countFiles: '{n} Files',\n countQueries: '{n} Queries',\n countCommands: '{n} Commands',\n countPrograms: '{n} Programs',\n countCalls: '{n} Calls',\n 'terminal.signal': 'signal {signal}',\n 'terminal.exitCode': 'exit code {code}',\n 'terminal.running': 'Running',\n 'terminal.failed': 'Failed',\n 'terminal.done': 'Done',\n 'terminal.copy': 'Copy',\n 'terminal.copied': 'Copied',\n 'terminal.noOutput': 'No output',\n 'terminal.collapseAria': 'Collapse output',\n 'terminal.collapse': 'Collapse',\n 'terminal.expandAria': 'Expand the remaining {n} output lines',\n 'terminal.expandRest': '… {n} more lines',\n} satisfies Record<MergeToolCallsKey, string>\n\nexport const NS = 'merge-tool-calls'\n","/**\n * The built-in tool universe this plugin shadows by default, plus the\n * variant-classification vocabulary mirroring ui-tool's tool-call model.\n *\n * The keyed `tool.call.toolview` slot dispatches on the exact wire tool name,\n * so a shadowed registration is needed per name (there is no wildcard). This\n * list names every shipped tool whose row belongs to the generic ToolRow\n * family — a variant title/icon plus read/search/diff/terminal/web card or\n * IN/OUT text — which the merged row reproduces faithfully.\n *\n * Deliberately excluded: tools with custom row cards this plugin does not\n * replicate (`skill`, `cordis_define`, and the live `cordis_run` /\n * `cordis_stop` / `cordis_undefine` rows), and `todo_write` /\n * `ask_user_question` whose summary formatting is tool-specific. They keep\n * their built-in rows unless the user names them in the `tools` config\n * explicitly.\n * @module\n */\n\n/** Wire tool names merged by default (empty `tools` config = this list). */\nexport const ALL_TOOL_NAMES: readonly string[] = [\n // File tools (read/search/diff cards).\n 'read', 'grep', 'glob', 'edit', 'write',\n // Shell tools (terminal card).\n 'bash', 'pwsh',\n // Web tools (web card).\n 'web_search', 'web_fetch',\n // Code tool (generic IN/OUT with the program body).\n 'run_code',\n // Cordis inspection verbs (generic read-variant rows).\n 'cordis_package_inspect', 'cordis_runtime_inspect',\n]\n\n/** Tool-row visual variant, mirroring ui-tool's ToolRowVariant. */\nexport type ToolVariant = 'search' | 'read' | 'bash' | 'write' | 'edit' | 'code' | 'others'\n\n/** Figma row titles per variant (design literals, not translatable copy). */\nexport const VARIANT_TITLES: Record<ToolVariant, string> = {\n search: 'Search', read: 'Read', bash: 'Bash',\n write: 'Write', edit: 'Edit', code: 'Code', others: 'Tool call',\n}\n\n/**\n * Known tool name -> row variant (mirrors ui-tool's classification table,\n * minus the run-control verbs: their rows are custom and not shadowed).\n */\nconst TOOL_VARIANTS: Record<string, ToolVariant> = {\n bash: 'bash',\n pwsh: 'bash',\n read: 'read',\n web_fetch: 'read',\n web_search: 'search',\n grep: 'search',\n glob: 'search',\n write: 'write',\n edit: 'edit',\n run_code: 'code',\n cordis_package_inspect: 'read',\n cordis_runtime_inspect: 'read',\n}\n\n/** Tool-owned titles refining a generic row variant (mirrors ui-tool). */\nexport const TOOL_TITLES: Record<string, string> = {\n cordis_package_inspect: 'Inspect',\n cordis_runtime_inspect: 'Inspect',\n pwsh: 'Pwsh',\n}\n\n/** Classify a tool name into its row variant (mirrors ui-tool). */\nexport function classifyTool(toolName: string): ToolVariant {\n return TOOL_VARIANTS[toolName] ?? 'others'\n}\n\n/** The variant title a row shows, honoring tool-owned refinements. */\nexport function variantTitle(toolName: string): string {\n return TOOL_TITLES[toolName] ?? VARIANT_TITLES[classifyTool(toolName)]\n}\n","/**\n * Pure card/row derivation from a frozen call slice, mirroring ui-tool's\n * tool-call/read-card/search-card/diff-card/terminal-card/web-card models at\n * the plugin boundary (those models are ui-tool internals and cannot be\n * imported cross-package). Same wire contract, same defensive treatment of\n * untrusted result views. A merged row renders the same surface the built-in\n * row would: a variant title/icon plus a card primitive or IN/OUT text.\n * @module\n */\nimport {\n resolveWorkspacePath,\n type ToolCallBlock, type ToolResultNode,\n} from '@deepseek-ai/dsh-client-runtime/client'\nimport type {\n DiffBlockProps, DiffHunk, ReadBlockProps, SearchBlockProps, SearchFileGroup,\n TerminalBlockProps, WebBlockProps,\n} from '@deepseek-ai/dsh-client-ui-primitives'\nimport { classifyTool, VARIANT_TITLES, TOOL_TITLES, type ToolVariant } from './tool-names.ts'\n\n/** Row state semantic, mirroring ui-tool's ToolRowState. */\nexport type RowState = 'running' | 'ok' | 'error' | 'stopped'\n\nexport type { ToolVariant } from './tool-names.ts'\n\n/** Read-card props the ReadBlock primitive draws (per-render maxLines owned by the caller). */\nexport type ReadCardModel = Pick<ReadBlockProps, 'label' | 'lines' | 'totalLines' | 'lang'>\n\n/**\n * Distributive `Omit`: a plain `Omit<A | B, K>` keeps only the keys common to\n * both members, dropping the discriminated `files`/`paths` fields. Distributing\n * over the naked type parameter preserves each shape.\n */\ntype DistributiveOmit<T, K extends keyof T> = T extends unknown ? Omit<T, K> : never\n\n/** Search-card props plus the capped-result recovery locator (mirrors ui-tool). */\nexport interface SearchCardModel {\n readonly card: DistributiveOmit<SearchBlockProps, 'maxLines' | 'className'>\n /** The result view's replacement title, which outranks the args summary. */\n readonly title: string | undefined\n readonly recovery: string | undefined\n}\n\n/** Diff-card props (mirrors ui-tool; maxLines/className belong to the render site). */\nexport interface DiffCardModel {\n readonly card: Pick<DiffBlockProps, 'diffs'>\n}\n\n/** Terminal-card props (mirrors ui-tool; maxLines/className/labels belong to the render site). */\nexport interface TerminalCardModel {\n readonly card: Pick<TerminalBlockProps, 'command' | 'cwd' | 'output' | 'exitCode' | 'signal' | 'running'>\n /** The call view's model-authored description shown above the card. */\n readonly description: string | undefined\n}\n\n/** True when a settled terminal card reports a failing exit (mirrors ui-tool). */\nexport function terminalFailed(model: TerminalCardModel): boolean {\n const { exitCode, signal, running } = model.card\n return running !== true && ((exitCode !== undefined && exitCode !== 0) || signal !== undefined)\n}\n\n/** Everything a merged row needs, derived once from the frozen slice. */\nexport interface CallRowModel {\n readonly state: RowState\n readonly variant: ToolVariant\n /** Row title: variant title, or the tool-owned title when it refines one. */\n readonly title: string\n /** Args/result-derived one-line summary (path for file tools, query for searches, …). */\n readonly summary: string\n /** Expanded-body input text (pretty args); null = no input section. */\n readonly body: string | null\n /** Flattened result text; null while running or when the result carries no text. */\n readonly output: string | null\n /** First result line on an error row (the collapsed summary's error text). */\n readonly errorSummary: string | null\n /** Openable workspace path from args; absent for non-file tools and errors. */\n readonly filePath: string | undefined\n /** Whether the row has anything to expand (a card, args body, or output). */\n readonly expandable: boolean\n /** Expanded-body content cards, mutually exclusive, or null when absent. */\n readonly terminal: TerminalCardModel | null\n readonly diff: DiffCardModel | null\n readonly read: ReadCardModel | null\n readonly search: SearchCardModel | null\n readonly web: WebBlockProps | null\n}\n\n/**\n * Flatten a settled result's content blocks to display text.\n * @param node - settled result node.\n * @returns joined text (may be empty).\n */\nexport function resultText(node: ToolResultNode): string {\n const parts: string[] = []\n for (const block of node.content) {\n if (block.type === 'text') parts.push(block.text)\n else parts.push(JSON.stringify(block))\n }\n if (parts.length === 0 && node.error !== undefined) parts.push(`${node.error.name}: ${node.error.code}`)\n return parts.join('\\n')\n}\n\nfunction firstLine(text: string): string {\n const nl = text.indexOf('\\n')\n return nl === -1 ? text : text.slice(0, nl)\n}\n\n/** Strip the workspace root from a workspace-rooted absolute path (display only). */\nexport function relativizeToCwd(text: string, cwd: string | undefined): string {\n if (cwd === undefined || cwd === '') return text\n const root = cwd.replace(/[/\\\\]+$/, '')\n if (text.startsWith(`${root}/`) || text.startsWith(`${root}\\\\`)) return text.slice(root.length + 1)\n return text\n}\n\nfunction parseArgs(argsRaw: string): Record<string, unknown> | undefined {\n try {\n const parsed: unknown = JSON.parse(argsRaw)\n return typeof parsed === 'object' && parsed !== null ? parsed as Record<string, unknown> : undefined\n } catch {\n return undefined\n }\n}\n\nfunction pickString(args: Record<string, unknown>, keys: readonly string[]): string | undefined {\n for (const key of keys) {\n const value = args[key]\n if (typeof value === 'string' && value !== '') return value\n }\n return undefined\n}\n\n/** Summary key preference per row variant (args-derived, mirrors ui-tool). */\nconst SUMMARY_KEYS: Record<ToolVariant, readonly string[]> = {\n bash: ['description', 'command'],\n read: ['path', 'file_path', 'url'],\n search: ['query', 'pattern', 'url'],\n write: ['path', 'file_path'],\n edit: ['path', 'file_path'],\n code: ['description'],\n others: [],\n}\n\nfunction deriveSummary(variant: ToolVariant, argsRaw: string): string {\n const parsed = parseArgs(argsRaw)\n if (parsed === undefined) return firstLine(argsRaw)\n const picked = pickString(parsed, SUMMARY_KEYS[variant])\n if (picked !== undefined) return firstLine(picked)\n for (const value of Object.values(parsed)) {\n if (typeof value === 'string' && value !== '') return firstLine(value)\n }\n return firstLine(argsRaw)\n}\n\n/** Path keys only — never `url` (web_fetch lands on the read variant). */\nconst FILE_PATH_KEYS = ['path', 'file_path'] as const\n\n/** File-tool variants whose summary may be an openable workspace path. */\nconst FILE_PATH_VARIANTS: ReadonlySet<ToolVariant> = new Set(['read', 'write', 'edit'])\n\nfunction deriveFilePath(variant: ToolVariant, argsRaw: string): string | undefined {\n if (!FILE_PATH_VARIANTS.has(variant)) return undefined\n const parsed = parseArgs(argsRaw)\n if (parsed === undefined) return undefined\n return pickString(parsed, FILE_PATH_KEYS)?.split('\\n')[0]\n}\n\nfunction deriveBody(variant: ToolVariant, argsRaw: string): string | null {\n if (argsRaw === '') return null\n const parsed = parseArgs(argsRaw)\n if (parsed === undefined) return argsRaw\n // The code row's expanded body IS the program, not the args JSON envelope.\n if (variant === 'code') {\n const code = parsed.code\n if (typeof code === 'string' && code !== '') return code\n }\n return JSON.stringify(parsed, null, 2)\n}\n\n/** Derive the row model for one call of a grouped tool. */\nexport function callRowModel(\n toolName: string,\n block: ToolCallBlock,\n cwd: string | undefined,\n): CallRowModel {\n const done = 'kind' in block\n const argsRaw = (done ? block.call?.argsRaw : block.argsRaw) ?? ''\n const state: RowState = !done ? 'running'\n : block.error?.code === 'interrupted' ? 'stopped'\n : block.isError ? 'error' : 'ok'\n const variant = classifyTool(toolName)\n const terminal = terminalCardOf(block, cwd)\n const read = readCardOf(block, cwd)\n const diff = diffCardOf(block)\n const search = searchCardOf(block)\n const web = webCardOf(block)\n const base = argsRaw === '' ? block.callId : relativizeToCwd(deriveSummary(variant, argsRaw), cwd)\n const toolTitle = TOOL_TITLES[toolName]\n // Others keeps the static \"Tool call\" title; the real tool name rides the\n // summary slot unless the tool owns a specific title (mirrors ui-tool).\n const argsSummary = variant === 'others' && toolName !== '' && toolTitle === undefined\n ? `${toolName} · ${base}`\n : base\n const filePath = deriveFilePath(variant, argsRaw)\n const output = done ? (resultText(block) || null) : null\n const errorSummary = state === 'error' && output !== null ? firstLine(output) : null\n // A card's own summary outranks the args summary: the terminal presenter's\n // description is the contract's above-card text, and a search result view's\n // replacement title names the query the tool itself chose (mirrors ui-tool).\n const summary = terminal?.description ?? search?.title ?? argsSummary\n // Single-file tools never expose an args body — the path link is the only\n // args interaction (mirrors ui-tool's singleFile rule).\n const body = filePath !== undefined ? null : deriveBody(variant, argsRaw)\n // A failing exit status is the terminal card's own error signal: the call\n // settles isError:false, and the red state dot is its only collapsed signal.\n const finalState = state === 'ok' && terminal !== null && terminalFailed(terminal)\n ? 'error'\n : state\n const expandable = terminal !== null || diff !== null || read !== null\n || search !== null || web !== null || body !== null || output !== null\n return {\n state: finalState,\n variant,\n title: toolTitle ?? VARIANT_TITLES[variant],\n summary,\n body,\n output,\n errorSummary,\n filePath,\n expandable,\n terminal,\n diff,\n read,\n search,\n web,\n }\n}\n\n/** Read-card derivation, or null when this call is not a read card (mirrors ui-tool). */\nfunction readCardOf(block: ToolCallBlock, cwd: string | undefined): ReadCardModel | null {\n if (!('kind' in block)) return null\n const result = block.resultView?.card === 'read' ? block.resultView : null\n if (result === null) return null\n if (!Array.isArray(result.lines)) return null\n const lines = result.lines\n .filter(line => typeof line === 'object' && line !== null\n && typeof (line as { number?: unknown }).number === 'number'\n && typeof (line as { text?: unknown }).text === 'string')\n .map(line => ({ number: (line as { number: number }).number, text: (line as { text: string }).text }))\n return {\n label: result.title ?? relativizeToCwd(result.path, cwd),\n lines,\n totalLines: typeof result.totalLines === 'number' ? result.totalLines : lines.length,\n lang: typeof result.lang === 'string' ? result.lang : undefined,\n }\n}\n\nfunction isValidFiles(files: unknown): files is SearchFileGroup[] {\n return Array.isArray(files) && files.every(file =>\n typeof file === 'object' && file !== null\n && typeof (file as { path?: unknown }).path === 'string'\n && Array.isArray((file as { matches?: unknown }).matches)\n && (file as { matches: unknown[] }).matches.every(match =>\n typeof match === 'object' && match !== null\n && typeof (match as { lineNumber?: unknown }).lineNumber === 'number'\n && typeof (match as { line?: unknown }).line === 'string'))\n}\n\nfunction flattenContent(content: readonly { type: string; text?: string }[]): string | undefined {\n const text = content\n .filter((block): block is { type: 'text'; text: string } => block.type === 'text' && typeof block.text === 'string')\n .map(block => block.text)\n .join('\\n')\n return text === '' ? undefined : text\n}\n\n/** Search-card derivation, or null when this call is not a search card (mirrors ui-tool). */\nfunction searchCardOf(block: ToolCallBlock): SearchCardModel | null {\n if (!('kind' in block)) return null\n const result = block.resultView?.card === 'search' ? block.resultView : null\n if (result === null) return null\n const common = { truncated: result.truncated, total: result.total }\n const recovery = result.truncated ? flattenContent(block.content) : undefined\n if (result.shape === 'matches') {\n if (!isValidFiles(result.files)) return null\n return { title: result.title, recovery, card: { kind: 'matches', files: result.files, ...common } }\n }\n if (result.shape !== 'paths') return null\n if (!Array.isArray(result.paths) || !result.paths.every(path => typeof path === 'string')) return null\n return { title: result.title, recovery, card: { kind: 'paths', paths: result.paths, ...common } }\n}\n\n/** Narrow a wire `card:'diff'` view's `diffs` to well-formed hunks (mirrors ui-tool). */\nfunction narrowDiffs(diffs: unknown): DiffHunk[] | null {\n if (!Array.isArray(diffs) || diffs.length === 0) return null\n const out: DiffHunk[] = []\n for (const hunk of diffs) {\n if (typeof hunk !== 'object' || hunk === null) return null\n const { path, oldText, newText } = hunk as Record<string, unknown>\n if (typeof path !== 'string') return null\n if (oldText !== null && typeof oldText !== 'string') return null\n if (typeof newText !== 'string') return null\n out.push({ path, oldText, newText })\n }\n return out\n}\n\n/** Diff-card derivation, or null when this call is not a diff card (mirrors ui-tool). */\nfunction diffCardOf(block: ToolCallBlock): DiffCardModel | null {\n if (!('kind' in block)) {\n const call = block.callView?.card === 'diff' ? block.callView : null\n const diffs = call === null ? null : narrowDiffs(call.diffs)\n return diffs === null ? null : { card: { diffs } }\n }\n const result = block.resultView?.card === 'diff' ? block.resultView : null\n const diffs = result === null ? null : narrowDiffs(result.diffs)\n return diffs === null ? null : { card: { diffs } }\n}\n\n/**\n * Resolve a terminal view's working directory the way the render-intent\n * contract assigns it: an absolute path is used as-is, a relative one joins\n * under the session workspace, and an omitted one IS the session workspace.\n * @param viewCwd - the cwd the terminal call view carries, if any.\n * @param sessionCwd - the session workspace root, if the caller knows it.\n * @returns the working directory for the prompt label, or undefined.\n */\nfunction resolveTerminalCwd(viewCwd: string | undefined, sessionCwd: string | undefined): string | undefined {\n if (viewCwd === undefined || viewCwd === '') return sessionCwd\n if (sessionCwd === undefined || sessionCwd === '') return viewCwd\n return resolveWorkspacePath(sessionCwd, viewCwd)\n}\n\n/** Terminal-card derivation, or null when this call is not a terminal card (mirrors ui-tool). */\nfunction terminalCardOf(block: ToolCallBlock, sessionCwd: string | undefined): TerminalCardModel | null {\n const call = block.callView?.card === 'terminal' ? block.callView : null\n if (!('kind' in block)) {\n return call === null ? null : {\n description: call.description,\n card: {\n command: call.title,\n cwd: resolveTerminalCwd(call.cwd, sessionCwd),\n output: undefined,\n exitCode: undefined,\n signal: undefined,\n running: true,\n },\n }\n }\n const result = block.resultView?.card === 'terminal' ? block.resultView : null\n if (result === null) return null\n return {\n description: call?.description,\n card: {\n command: result.title ?? call?.title ?? '',\n cwd: call === null ? undefined : resolveTerminalCwd(call.cwd, sessionCwd),\n output: result.output,\n exitCode: result.exitCode,\n signal: result.signal,\n running: false,\n },\n }\n}\n\n/** Web-card derivation, or null when this call is not a web card (mirrors ui-tool). */\nfunction webCardOf(block: ToolCallBlock): WebBlockProps | null {\n if (!('kind' in block)) return null\n const result = block.resultView\n if (result?.card !== 'web') return null\n if (result.kind === 'search') {\n return {\n kind: 'search',\n answer: result.answer,\n sources: result.sources.map(source => ({\n url: source.url,\n title: source.title,\n snippet: source.snippet,\n publishedAt: source.publishedAt,\n })),\n truncated: result.truncated,\n }\n }\n if (result.kind === 'fetch') {\n return {\n kind: 'fetch',\n url: result.url,\n statusCode: result.statusCode,\n truncated: result.truncated,\n }\n }\n return null\n}\n","/**\n * Pure detection of a consecutive run of grouped tool calls in the chat flow,\n * and the per-seat merged-group partition over it.\n *\n * A pure function of the chat snapshot (order + node store), so the merged\n * display is deterministic under replay: the web layer recomputes it per frame.\n * @module\n */\nimport type { ChatConversationViewNode, ChatNodeStore, ToolCallBlock } from '@deepseek-ai/dsh-client-runtime/client'\nimport { conversationContextKey } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { MergeGroupMode } from '../types.ts'\nimport { classifyTool } from './tool-names.ts'\n\n/** Tool-call node kind registered by ui-conversation's built-in tool definition. */\nconst TOOL_CALL_KIND = 'tool-call'\n\n/** Extract a root call block from any chat node, when it is a tool-call node. */\nfunction toolRootOf(node: ChatConversationViewNode): ToolCallBlock | null {\n if (node.kind !== TOOL_CALL_KIND) return null\n const root = (node.data as { root?: unknown }).root\n return typeof root === 'object' && root !== null ? root as ToolCallBlock : null\n}\n\n/** The wire tool name of a call in either lifecycle form (mirrors ui-tool). */\nexport function callNameOf(block: ToolCallBlock): string {\n return 'kind' in block ? (block.call?.name ?? '') : block.name\n}\n\n/** Whether the call's wire name is one of the grouped tools (empty = all). */\nexport function isGroupedTool(name: string, tools: readonly string[]): boolean {\n return tools.length === 0 || tools.includes(name)\n}\n\n/** Step identity of a node's location; undefined when the node is not step-bound. */\nfunction stepIdOf(node: ChatConversationViewNode): string | undefined {\n const location = node.location\n return location.kind === 'step' ? `${location.turn.turn}:${location.step.step}` : undefined\n}\n\n/** One merged group: the group's first call plus its consecutive continuations. */\nexport interface ReadRun {\n /** Whether this seat is the group's rendering head (the only seat that shows the card). */\n readonly isFirst: boolean\n /** The group's root blocks in flow order (first + continuations). */\n readonly blocks: readonly ToolCallBlock[]\n}\n\n/**\n * Compute the merged group this call belongs to.\n *\n * 1. Locates this call's node in the chat order; null when it is not a chat\n * tool-call node (e.g. a read dispatched as a subcall).\n * 2. Walks backward/forward to the maximal consecutive run containing it. A\n * call continues the run when it is a grouped tool AND same-tool-same-run:\n * the identical wire name, or a sibling of the same known variant family\n * (grep+glob, bash+pwsh, read+web_fetch…). Unknown names (variant\n * `others`) only merge with themselves, so unrelated tools never share a\n * card (`adjacent`: any consecutive run; `step`: same agent step as this\n * call).\n * 3. Partitions the run into `maxGroupSize`-sized groups; this call is the\n * group's first only when it heads one of those partitions. Truncation\n * therefore never orphans a call: the excess starts its own group.\n *\n * @param order - chat node key order.\n * @param nodes - chat node store.\n * @param myCallId - the call id of the seat asking about itself.\n * @param tools - grouped wire tool names; empty means every tool.\n * @param groupBy - grouping mode.\n * @param maxGroupSize - per-group cap.\n * @returns the group partition, or null when the call is not a chat tool-call node.\n */\nexport function readRun(\n order: readonly string[],\n nodes: ChatNodeStore,\n myCallId: string,\n tools: readonly string[],\n groupBy: MergeGroupMode,\n maxGroupSize: number,\n): ReadRun | null {\n const myKey = conversationContextKey(TOOL_CALL_KIND, myCallId)\n const myIndex = order.indexOf(myKey)\n if (myIndex < 0) return null\n const myRoot = rootAtNode(order, nodes, myIndex)\n if (myRoot === null) return null\n const myName = callNameOf(myRoot)\n const myVariant = classifyTool(myName)\n const size = Math.max(1, maxGroupSize)\n\n const rootAt = (index: number): ToolCallBlock | null => rootAtNode(order, nodes, index)\n const myStep = groupBy === 'step' ? stepIdOfNodeAt(order, nodes, myIndex) : undefined\n const inGroup = (index: number): boolean => {\n const root = rootAt(index)\n if (root === null || !isGroupedTool(callNameOf(root), tools)) return false\n const name = callNameOf(root)\n // Same-name calls always continue the run; known families merge siblings\n // (grep+glob, bash+pwsh); unknown names merge only with themselves.\n if (name !== myName) {\n if (myVariant === 'others' || classifyTool(name) !== myVariant) return false\n }\n if (groupBy !== 'step') return true\n return myStep !== undefined && stepIdOfNodeAt(order, nodes, index) === myStep\n }\n\n let start = myIndex\n while (start > 0 && inGroup(start - 1)) start--\n let end = myIndex\n while (end < order.length - 1 && inGroup(end + 1)) end++\n\n const groupStart = start + Math.floor((myIndex - start) / size) * size\n const groupEnd = Math.min(groupStart + size, end + 1)\n\n const blocks: ToolCallBlock[] = []\n for (let index = groupStart; index < groupEnd; index++) {\n const root = rootAt(index)\n if (root !== null) blocks.push(root)\n }\n return { isFirst: myIndex === groupStart, blocks }\n}\n\nfunction rootAtNode(order: readonly string[], nodes: ChatNodeStore, index: number): ToolCallBlock | null {\n const key = order[index]\n const node = key === undefined ? undefined : nodes.get(key)\n return node === undefined ? null : toolRootOf(node)\n}\n\nfunction stepIdOfNodeAt(order: readonly string[], nodes: ChatNodeStore, index: number): string | undefined {\n const key = order[index]\n const node = key === undefined ? undefined : nodes.get(key)\n return node === undefined ? undefined : stepIdOf(node)\n}\n","/**\n * Merged tool-call row — the shadowed `tool.call.toolview` registrant.\n *\n * For the first call of a consecutive run of grouped tools, renders the run as\n * one card (the first call's full row) plus one compact child row per\n * continuation call. For a continuation call, renders nothing: the seat stays\n * empty and the injected stylesheet collapses it out of the flow. When the\n * call is not a chat tool-call node at all (e.g. dispatched as a subcall),\n * falls back to a plain single row so the call never disappears.\n *\n * The row surface mirrors the built-in generic ToolRow: a variant title/icon\n * plus the settled card (read/search/diff/terminal/web) or IN/OUT text.\n *\n * Everything here is a pure function of the chat snapshot + the frozen call\n * slices (replay-deterministic); expand state is component-local view state.\n * @module\n */\nimport { memo, useLayoutEffect, useRef, useState, type CSSProperties, type KeyboardEvent, type MouseEvent, type ReactNode } from 'react'\nimport {\n DiffBlock, DisclosureRow, IconApiOutline14, IconBrowseOutline16, IconCodeOutline16,\n IconEditOutline16, IconSearchOutline16, IconSparkle16, ReadBlock, SearchBlock, StateDot,\n TerminalBlock, WebBlock, type TerminalBlockLabels,\n} from '@deepseek-ai/dsh-client-ui-primitives'\nimport type { ToolCallBlock } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { ToolCallViewProps } from '@deepseek-ai/dsh-client-ui-tool/client'\nimport type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { MergeToolCallsConfig } from '../types.ts'\nimport { callRowModel, type CallRowModel, type RowState, type ToolVariant } from './card-model.ts'\nimport { readRun } from './merge-run.ts'\n\n/** Chat rows show a capped card; the details panel stays the full-height surface. */\nconst CHAT_READ_MAX_LINES = 8\nconst CHAT_SEARCH_MAX_LINES = 8\nconst CHAT_DIFF_MAX_LINES = 8\n\n/** Locale seat and inject face of the merged row registration. */\nexport type MergedToolRowProps = ToolCallViewProps\n & PropsLocale<'merge-tool-calls'>\n & { readonly cfg: MergeToolCallsConfig }\n\n/** Variant leading icons (figma table); all glyphs render at 14 inside the 16px leading box. */\nconst VARIANT_ICONS: Record<ToolVariant, ReactNode> = {\n search: <IconSearchOutline16 size={14} />,\n read: <IconBrowseOutline16 size={14} />,\n bash: <IconApiOutline14 size={14} />,\n write: <IconEditOutline16 size={14} />,\n edit: <IconEditOutline16 size={14} />,\n code: <IconCodeOutline16 size={14} />,\n others: <IconSparkle16 size={14} />,\n}\n\n/**\n * Per-variant locale key for the merged-count summary (`{n} Files`, `{n}\n * Commands`, …). Replaces the first call's file path on the main row when the\n * run merges more than one call, so the main row reads `Read · 5 Files` and\n * the file paths move to the collapsed child rows.\n */\nconst VARIANT_COUNT_KEY: Record<ToolVariant, 'countFiles' | 'countQueries' | 'countCommands' | 'countPrograms' | 'countCalls'> = {\n read: 'countFiles',\n write: 'countFiles',\n edit: 'countFiles',\n search: 'countQueries',\n bash: 'countCommands',\n code: 'countPrograms',\n others: 'countCalls',\n}\n\n/** Leading-slot state substitution, mirroring the shipped row. */\nfunction leadingFor(state: RowState, icon: ReactNode): ReactNode {\n switch (state) {\n case 'error': return <StateDot state=\"error\" />\n case 'stopped': return <StateDot state=\"warning\" />\n default: return icon\n }\n}\n\n/** Visually hidden run-state label (the StateDot and sweep are colour-only). */\nfunction stateStatus(state: RowState, t: MergedToolRowProps['t']): string | null {\n switch (state) {\n case 'running': return t('running')\n case 'error': return t('failed')\n case 'stopped': return t('stopped')\n default: return null\n }\n}\n\n/** TerminalBlock display copy from the plugin's own dictionary. */\nfunction terminalLabels(t: MergedToolRowProps['t']): TerminalBlockLabels {\n return {\n signal: signal => t('terminal.signal', { signal }),\n exitCode: code => t('terminal.exitCode', { code }),\n running: t('terminal.running'),\n failed: t('terminal.failed'),\n done: t('terminal.done'),\n copy: t('terminal.copy'),\n copied: t('terminal.copied'),\n noOutput: t('terminal.noOutput'),\n collapseAria: t('terminal.collapseAria'),\n collapse: t('terminal.collapse'),\n expandAria: hidden => t('terminal.expandAria', { n: hidden }),\n expand: hidden => t('terminal.expandRest', { n: hidden }),\n }\n}\n\n/** One call's expanded-body card, mirroring the built-in ToolRow body. */\nfunction CardBody({ model, t }: { model: CallRowModel; t: MergedToolRowProps['t'] }) {\n if (model.terminal !== null) {\n return <TerminalBlock {...model.terminal.card} maxLines={Infinity} labels={terminalLabels(t)} />\n }\n if (model.diff !== null) {\n return <DiffBlock {...model.diff.card} maxLines={CHAT_DIFF_MAX_LINES} />\n }\n if (model.read !== null) {\n return <ReadBlock {...model.read} maxLines={CHAT_READ_MAX_LINES} />\n }\n if (model.search !== null) {\n return (\n <>\n <SearchBlock {...model.search.card} maxLines={CHAT_SEARCH_MAX_LINES} />\n {model.search.recovery !== undefined && (\n <div className=\"mtc-recovery\">{model.search.recovery}</div>\n )}\n </>\n )\n }\n if (model.web !== null) {\n return <WebBlock {...model.web} />\n }\n const hasBody = model.body !== null\n const hasOutput = model.output !== null\n if (!hasBody && !hasOutput) return null\n return (\n <div className=\"mtc-io-card\">\n {hasBody && (\n <div className=\"mtc-io-section\">\n <span className=\"mtc-io-label\">IN</span>\n <span className=\"mtc-io-text\">{model.body}</span>\n </div>\n )}\n {hasBody && hasOutput && <span className=\"mtc-io-divider\" aria-hidden />}\n {hasOutput && (\n <div className=\"mtc-io-section\">\n <span className=\"mtc-io-label\">OUT</span>\n <span className=\"mtc-io-text\" data-error={model.state === 'error' || undefined}>{model.output}</span>\n </div>\n )}\n </div>\n )\n}\n\n/**\n * The run's main card: the first call's chrome (variant title/icon + state)\n * plus, for a single call, its expandable content card. For a merged run the\n * main row's collapsed summary is the count (`Read · 5 Files`) and the\n * expanded body is the child-rows block (passed in as `children`); the first\n * call is rendered as the first child row so every file path lands on a child\n * row, never on the main row.\n */\nexport const RowCard = memo(function RowCard({\n toolName, block, cwd, openFile, inspect, t, mergedCount, children,\n}: {\n toolName: string\n block: ToolCallBlock\n cwd: string | undefined\n openFile: (path: string) => void\n inspect: (() => void) | undefined\n t: MergedToolRowProps['t']\n /** Continuation-call count; 0 means a single-call row (no child rows). */\n mergedCount: number\n /** Child rows for the merged run (all calls, including the first). */\n children?: ReactNode\n}) {\n const model = callRowModel(toolName, block, cwd)\n const [expanded, setExpanded] = useState(false)\n const hasChildren = mergedCount > 0\n const expandable = model.expandable || hasChildren\n const open = expanded && expandable\n const status = stateStatus(model.state, t)\n const failureLine = model.state === 'error' ? model.errorSummary ?? null : null\n // Merged runs trade the first file path + `+n` suffix for a count summary,\n // so the main row reads `Read · 5 Files`; the file paths live on the child\n // rows. An error on the first call still surfaces its error text instead.\n const summaryText = hasChildren && failureLine === null\n ? t(VARIANT_COUNT_KEY[model.variant], { n: String(mergedCount + 1) })\n : failureLine ?? model.summary\n // The open-file link only applies to single-call rows (the merged summary is\n // a count, not a path).\n const fileLink = !hasChildren && model.filePath !== undefined && openFile !== undefined && failureLine === null\n const openFileClick = (event: MouseEvent<HTMLButtonElement>) => {\n event.stopPropagation()\n if (model.filePath !== undefined) openFile(model.filePath)\n }\n return (\n <div className=\"mtc-row\" data-state={model.state} data-variant={model.variant} data-merged={hasChildren || undefined}>\n {status !== null && <span className=\"mtc-visually-hidden\">{status}</span>}\n <DisclosureRow\n rowClassName=\"mtc-title-row\"\n titleClassName=\"mtc-title\"\n leadingClassName=\"mtc-leading\"\n chevronClassName=\"mtc-chevron\"\n icon={leadingFor(model.state, VARIANT_ICONS[model.variant])}\n title={model.title}\n open={open}\n expandable={expandable}\n expandOnRowClick\n keepContentWhenOpen\n onToggle={() => { setExpanded(value => !value) }}\n collapsedContent={summaryText !== '' && (\n <>\n <span className=\"mtc-sep\" aria-hidden />\n {fileLink ? (\n <button type=\"button\" className=\"mtc-summary-link\" onClick={openFileClick}>{summaryText}</button>\n ) : (\n <span className=\"mtc-summary\">{summaryText}</span>\n )}\n </>\n )}\n >\n {/* Single-call mode: the first call's expandable content card lives in\n the disclosure body. Merged mode renders nothing here — the child\n rows block is a sibling below, animated via its own collapse grid. */}\n {!hasChildren && (\n <div className=\"mtc-card-body\">\n <CardBody model={model} t={t} />\n {inspect !== undefined && (\n <button type=\"button\" className=\"mtc-inspect\" onClick={inspect}>Inspect</button>\n )}\n </div>\n )}\n </DisclosureRow>\n {hasChildren && (\n // The animated children block. Lives outside DisclosureRow so the\n // grid-template-rows transition can keep the rows in the DOM while\n // collapsing (DisclosureRow would unmount them instantly). The\n // --mtc-sep-left custom property is set by the parent on .mtc-root.\n <div className=\"mtc-children-collapse\" data-open={open || undefined}>\n <div className=\"mtc-children\">\n {children}\n </div>\n </div>\n )}\n {hasChildren && open && inspect !== undefined && (\n <button type=\"button\" className=\"mtc-inspect\" onClick={inspect}>Inspect</button>\n )}\n </div>\n )\n})\n\n/**\n * One compact continuation row: the main row's tail structure ([sep dot][path]).\n * An expandable call toggles the inline content card on click (mirroring the\n * main row's whole-row disclosure); a read/write/edit-family path additionally\n * renders as an open-file link (the sidebar preview) that stops propagation,\n * exactly like the main row's summary link.\n */\nexport const ChildRow = memo(function ChildRow({\n toolName, block, cwd, openFile, t,\n}: {\n toolName: string\n block: ToolCallBlock\n cwd: string | undefined\n openFile: (path: string) => void\n t: MergedToolRowProps['t']\n}) {\n const model = callRowModel(toolName, block, cwd)\n const [open, setOpen] = useState(false)\n const stateLabel = stateStatus(model.state, t)\n const expandable = model.expandable\n const toggle = (): void => { setOpen(value => !value) }\n const openFileClick = (event: MouseEvent<HTMLButtonElement>) => {\n event.stopPropagation()\n if (model.filePath !== undefined) openFile(model.filePath)\n }\n const onRowKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n toggle()\n }\n }\n return (\n <div className=\"mtc-child\">\n <div\n className=\"mtc-child-row\"\n data-open={expandable && open || undefined}\n data-static={expandable ? undefined : true}\n role={expandable ? 'button' : undefined}\n tabIndex={expandable ? 0 : undefined}\n aria-expanded={expandable ? open : undefined}\n onClick={expandable ? toggle : undefined}\n onKeyDown={expandable ? onRowKeyDown : undefined}\n >\n <span className=\"mtc-sep\" aria-hidden />\n {model.filePath !== undefined ? (\n <button type=\"button\" className=\"mtc-child-path-link\" onClick={openFileClick}>{model.summary}</button>\n ) : (\n <span className=\"mtc-child-path\">{model.summary}</span>\n )}\n {stateLabel !== null && (\n <span className=\"mtc-child-state\" data-error={model.state === 'error' || undefined}>{stateLabel}</span>\n )}\n </div>\n {expandable && open && (\n <div className=\"mtc-child-body\">\n <CardBody model={model} t={t} />\n </div>\n )}\n </div>\n )\n})\n\n/**\n * The shadowed toolview: renders the merged run card for the run's first call,\n * nothing for continuation calls, and a plain single row when this call is not\n * a chat tool-call node.\n *\n * Child-row alignment is measured at runtime, not hardcoded: the main row's\n * separator dot sits after a variable-width title (\"Read\"/\"Search\"/\"Bash\"/…),\n * so its column depends on the rendered font. A layout effect measures the\n * dot's offset from the card root (once, plus on reflow via ResizeObserver)\n * and indents the children so their dots and paths land on the main row's\n * columns — no font/title constants to keep in sync.\n */\nexport function MergedToolRow({ callId, toolName, block, cwd, openFile, inspect, t, cfg, useSession }: MergedToolRowProps) {\n const run = useSession(snapshot => readRun(\n snapshot.chat.order,\n snapshot.chat.nodes,\n callId,\n cfg.tools,\n cfg.groupBy,\n cfg.maxGroupSize,\n ))\n const rootRef = useRef<HTMLDivElement>(null)\n /** Main separator dot's left offset from the card root; null before first measure. */\n const [sepLeft, setSepLeft] = useState<number | null>(null)\n\n useLayoutEffect(() => {\n const root = rootRef.current\n if (root === null) return\n const sep = root.querySelector<HTMLElement>('.mtc-row .mtc-sep')\n if (sep === null) return\n const measure = (): void => {\n setSepLeft(Math.round(sep.getBoundingClientRect().left - root.getBoundingClientRect().left))\n }\n measure()\n // Font swaps, locale switches, and column resizes move the dot; keep the\n // alignment current without re-running the effect.\n const observer = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(measure)\n observer?.observe(root)\n return () => { observer?.disconnect() }\n }, [run === null ? null : run.blocks[0]?.callId ?? null])\n\n if (run === null) {\n // Not a chat tool-call node (subcall dispatch): never blank the call.\n return (\n <RowCard\n toolName={toolName}\n block={block}\n cwd={cwd}\n openFile={openFile}\n inspect={inspect}\n t={t}\n mergedCount={0}\n />\n )\n }\n if (!run.isFirst) return null\n const hasChildren = run.blocks.length > 1\n return (\n <div\n className=\"mtc-root\"\n data-tool={toolName}\n ref={rootRef}\n // The measured dot column lands the children's sep dot on the main row's\n // summary column. Set on the root so it cascades into RowCard's\n // .mtc-children block.\n style={sepLeft === null ? undefined : { ['--mtc-sep-left']: `${sepLeft}px` } as CSSProperties}\n >\n <RowCard\n toolName={toolName}\n block={run.blocks[0] ?? block}\n cwd={cwd}\n openFile={openFile}\n inspect={inspect}\n t={t}\n mergedCount={run.blocks.length - 1}\n >\n {/* Every call (including the first) renders as a child row, so the\n main row's summary stays a count (`Read · 5 Files`) and every file\n path lands on its own child row. */}\n {hasChildren && run.blocks.map(child => (\n <ChildRow key={child.callId} toolName={toolName} block={child} cwd={cwd} openFile={openFile} t={t} />\n ))}\n </RowCard>\n </div>\n )\n}\n","/**\n * One scoped stylesheet injected for the lifetime of the client activation.\n *\n * Two jobs:\n * 1. The empty-seat collapse rule: the shadowed toolview renders `null` for\n * every non-first call of a merged run. The built-in `ToolCallTree` still\n * wraps that null in its `.callRow` div, and the renderer wraps every\n * toolview in an empty `<div data-slot=\"tool.call.toolview\">` (display:\n * contents), so the seat's `.flowItem` is never `:empty` itself and the\n * built-in `.flowItem:empty { display:none }` rule does not fire. This\n * rule extends the same intent (\"a renderer may decline its row\") to a\n * tool seat whose toolview rendered nothing.\n * 2. The plugin's own `.mtc-*` chrome for the merged card and its child rows.\n *\n * All colors come from the shared `--dsw-*` tokens (never literals).\n */\nexport const CSS = `\n/* A tool-call seat whose toolview declined to render (merged-run continuation\n calls) must not consume the flow column's gap. The empty toolview slot\n wrapper is the decline signal; the callRow around it always exists. */\n[data-chat-flow-kind=\"tool-call\"]:has([data-slot=\"tool.call.toolview\"]:empty) { display: none; }\n\n.mtc-root { display: flex; flex-direction: column; min-width: 0; }\n.mtc-row { display: flex; flex-direction: column; min-width: 0; }\n.mtc-row[data-state='running'] .mtc-title-row { position: relative; overflow: hidden; }\n.mtc-row[data-state='running'] .mtc-title-row::after {\n content: '';\n position: absolute;\n top: 0; bottom: 0; left: 0;\n width: 300px;\n background: linear-gradient(90deg, transparent 0%, color-mix(in srgb, var(--dsw-alias-bg-base) 60%, transparent) 55%, transparent 100%);\n animation: dsh-mtc-sweep 2.6s ease-out infinite;\n pointer-events: none;\n}\n@keyframes dsh-mtc-sweep {\n 0% { left: -300px; }\n 90%, 100% { left: 100%; }\n}\n\n.mtc-title { font-weight: 400; }\n.mtc-sep { flex: none; width: 2px; height: 2px; border-radius: 1px; margin: 0 8px; background: var(--dsw-alias-label-caption); }\n.mtc-summary {\n flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n font-size: 14px; line-height: 24px; color: var(--dsw-alias-label-tertiary);\n}\n.mtc-summary-link {\n flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n margin: 0; padding: 0; border: none; background: none; font: inherit; text-align: left;\n font-size: 14px; line-height: 24px; color: var(--dsw-alias-label-secondary);\n text-decoration: underline; text-decoration-color: var(--dsw-alias-label-quaternary);\n text-underline-offset: 3px; cursor: pointer;\n}\n.mtc-summary-link:hover { color: var(--dsw-alias-label-primary); text-decoration-color: currentColor; }\n.mtc-summary-error { color: var(--dsw-alias-state-error-primary); }\n.mtc-summary-suffix { flex: none; margin-left: 4px; white-space: nowrap; font-size: 14px; line-height: 24px; color: var(--dsw-alias-label-tertiary); }\n\n.mtc-card-body { margin: 4px 0 4px 4px; min-width: 0; }\n.mtc-recovery { margin: 4px 0 4px 4px; white-space: pre-wrap; overflow-wrap: anywhere; font: var(--dsw-font-xs-13); color: var(--dsw-alias-label-tertiary); }\n/* IN/OUT text card for calls without a card primitive (mirrors ToolRow). */\n.mtc-io-card {\n display: flex; flex-direction: column; min-width: 0;\n border: 1px solid var(--dsw-alias-border-l2); border-radius: 12px;\n background: var(--dsw-alias-bg-base);\n}\n.mtc-io-section { display: flex; gap: 10px; padding: 8px 12px; min-width: 0; }\n.mtc-io-label {\n flex: none; font-size: 11px; line-height: 16px; font-weight: 600;\n color: var(--dsw-alias-label-tertiary);\n}\n.mtc-io-text {\n flex: 1 1 auto; min-width: 0; white-space: pre-wrap; overflow-wrap: anywhere;\n font-size: 13px; line-height: 20px; color: var(--dsw-alias-label-secondary);\n}\n.mtc-io-text[data-error] { color: var(--dsw-alias-state-error-primary); }\n.mtc-io-divider { height: 1px; background: var(--dsw-alias-border-l2); }\n.mtc-inspect {\n display: inline-flex; align-self: flex-start; align-items: center; gap: 4px;\n margin: 4px 0 2px 4px; padding: 2px 8px;\n border: 1px solid var(--dsw-alias-border-l2); border-radius: 999px;\n background: var(--dsw-alias-bg-base); color: var(--dsw-alias-label-secondary);\n font-size: 11px; line-height: 16px; cursor: pointer;\n opacity: 0; transition: opacity 100ms ease;\n}\n.mtc-root:hover .mtc-inspect, .mtc-inspect:focus-visible { opacity: 1; }\n.mtc-inspect:hover { background: var(--dsw-alias-interactive-bg-hover-solid); color: var(--dsw-alias-label-primary); }\n\n/* Compact child rows: each is the main row's tail structure — [sep dot][path].\n The dot/path column is set at runtime by the component (it measures the\n main row's sep offset and sets the --mtc-sep-left custom property on the\n root); this is only the pre-measure fallback.\n\n The .mtc-children-collapse wrapper animates the block's height with a\n grid-template-rows 0fr↔1fr transition (children stay in the DOM while\n collapsing, so the slide is smooth instead of popping out). The vertical\n margin lives on the wrapper (not .mtc-children) so it doesn't leak past the\n 0fr row when collapsed.\n\n The sep-left indent lives on .mtc-child-row (not .mtc-children) so the\n expanded .mtc-child-body can align back to the root's left edge via a plain\n 4px margin. When the indent was on .mtc-children + a negative-margin\n pull-back on .mtc-child-body, the body's left portion was clipped by\n .mtc-children's overflow:hidden (which is required for the grid collapse\n animation). */\n.mtc-children-collapse {\n display: grid;\n grid-template-rows: 0fr;\n margin: 0;\n transition: grid-template-rows 200ms ease-out, margin 200ms ease-out;\n}\n.mtc-children-collapse[data-open] {\n grid-template-rows: 1fr;\n margin: 2px 0;\n}\n.mtc-children {\n display: flex; flex-direction: column; gap: 1px;\n margin: 0;\n min-width: 0; min-height: 0; overflow: hidden;\n opacity: 0;\n transition: opacity 150ms ease-out;\n}\n.mtc-children-collapse[data-open] .mtc-children {\n opacity: 1;\n transition: opacity 150ms ease-out 50ms;\n}\n.mtc-child { display: flex; flex-direction: column; min-width: 0; }\n.mtc-child-row {\n display: flex; align-items: center; gap: 0; min-width: 0; height: 20px;\n margin: 0 0 0 var(--mtc-sep-left, 61px); padding: 0; border: 0; border-radius: 4px; background: none;\n font: inherit; text-align: left; color: var(--dsw-alias-label-secondary); cursor: pointer;\n}\n.mtc-child-row:hover { background: var(--dsw-alias-interactive-bg-hover); color: var(--dsw-alias-label-primary); }\n.mtc-child-row[data-static] { cursor: default; }\n.mtc-child-row[data-static]:hover { background: none; color: var(--dsw-alias-label-secondary); }\n.mtc-child-row .mtc-sep { margin: 0 8px 0 0; }\n.mtc-child-path {\n flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n font-size: 14px; line-height: 20px;\n}\n/* Read-family child paths are open-file links (sidebar preview), same affordance\n as the main row's summary link. */\n.mtc-child-path-link {\n flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n margin: 0; padding: 0; border: none; background: none; font: inherit; text-align: left;\n font-size: 14px; line-height: 20px; color: var(--dsw-alias-label-secondary);\n text-decoration: underline; text-decoration-color: var(--dsw-alias-label-quaternary);\n text-underline-offset: 3px; cursor: pointer;\n}\n.mtc-child-path-link:hover { color: var(--dsw-alias-label-primary); text-decoration-color: currentColor; }\n.mtc-child-state { flex: none; font-size: 11px; line-height: 16px; color: var(--dsw-alias-label-tertiary); }\n.mtc-child-state[data-error] { color: var(--dsw-alias-state-error-primary); }\n/* The expanded child card aligns to the main card body's column (root + 4px).\n No negative-margin pull-back is needed because .mtc-children no longer\n carries the sep-left indent — only .mtc-child-row does. */\n.mtc-child-body { margin: 2px 0 2px 4px; min-width: 0; }\n\n.mtc-visually-hidden {\n position: absolute; width: 1px; height: 1px; overflow: hidden;\n clip: rect(0 0 0 0); white-space: nowrap;\n}\n`\n\n/** Install the stylesheet and return its disposer. */\nexport function installStyles(): () => void {\n const style = document.createElement('style')\n style.setAttribute('data-merge-tool-calls-style', '')\n style.textContent = CSS\n document.head.appendChild(style)\n return () => { style.remove() }\n}\n","/**\n * merge-tool-calls — browser half.\n *\n * Shadows the shipped `tool.call.toolview` entries for every grouped tool at\n * priority -1 (the keyed slot's shadowing rule: lowest priority renders). An\n * empty `tools` config means every built-in generic-family tool\n * (see {@link ALL_TOOL_NAMES}); a non-empty list is an explicit whitelist.\n * The shadowed component merges consecutive calls of one tool in the chat flow\n * into a single card with compact child rows.\n *\n * @module @dsh-external/dsh-plugin-merge-tool-calls/client\n */\n\nimport type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-tool/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-slots'\nimport { DEFAULT_MERGE_CONFIG, type MergeToolCallsConfig } from '../types.ts'\nimport { dicts } from './dictionaries.ts'\nimport { en, NS, zh, type MergeToolCallsKey } from './locales.ts'\nimport { MergedToolRow } from './rows.tsx'\nimport { installStyles } from './styles.ts'\nimport { ALL_TOOL_NAMES } from './tool-names.ts'\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** Copy for the merged tool-call rows. */\n 'merge-tool-calls': MergeToolCallsKey\n }\n}\n\n/** Required services: the slot registry (toolview shadowing) and locale. */\nexport const inject = ['slots', 'locale']\n\n/** Structural view of better-locale's override store (optional; no runtime dep). */\ninterface BetterLocaleOverrideStore {\n register(ns: string, dicts: Record<string, Record<string, string>>): () => void\n}\n\n/**\n * Register one shadowed toolview per grouped tool.\n * @param ctx - client root context.\n * @param config - row config; defaults apply when the loader passes none.\n */\nexport function apply(ctx: ClientContext, config: Partial<MergeToolCallsConfig> = {}): void {\n const cfg: MergeToolCallsConfig = { ...DEFAULT_MERGE_CONFIG, ...config }\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'merge-tool-calls: dictionaries')\n ctx.effect(installStyles, 'merge-tool-calls: styles')\n\n // Optional third-language overrides: register the 19-language dicts into\n // `ctx.betterLocale` (published by dsh-plugin-better-locale) so a selected\n // override language (with DSH on 'en') replaces the row copy. The service\n // is optional — no better-locale, no dicts.\n // Activation-order-safe: re-check ctx.get('betterLocale') on every locale\n // revision bump (better-locale bumps on activation + override switch).\n ctx.effect(() => {\n let dispose: (() => void) | undefined\n const sync = (): void => {\n dispose?.()\n dispose = undefined\n const store = ctx.get('betterLocale') as BetterLocaleOverrideStore | undefined\n if (store !== undefined) {\n dispose = store.register(NS, dicts)\n }\n }\n sync()\n const unsubscribe = ctx.locale.subscribe(sync)\n return () => {\n unsubscribe()\n dispose?.()\n }\n }, 'merge-tool-calls: better-locale override dicts')\n\n const toolNames = cfg.tools.length === 0 ? ALL_TOOL_NAMES : [...new Set(cfg.tools)]\n for (const tool of toolNames) {\n ctx.slots.inject('tool.call.toolview', () => ctx.slots.register({\n name: 'tool.call.toolview',\n key: tool,\n priority: -1,\n locale: NS,\n inject: () => ({ cfg }),\n }, MergedToolRow))\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The 19 better-locale override languages for the merged tool-call row copy,
3
+ * keyed by language id. Each dictionary carries the same key set as `en`/`zh`
4
+ * in `./locales.ts` (enforced by the `Record<MergeToolCallsKey, string>`
5
+ * annotation); values keep `{placeholder}` interpolation (e.g. `{n}` in
6
+ * `mergedCount`), matching the better-locale store's `LocaleDict` contract.
7
+ *
8
+ * The apply function registers these into `ctx.betterLocale` (the override
9
+ * store) under `NS`, so when the user selects an override language through
10
+ * dsh-plugin-better-locale (and DSH is on 'en', whose slot the override
11
+ * borrows), the row copy renders in the override language.
12
+ */
13
+ import type { MergeToolCallsKey } from './locales.ts';
14
+ /** All override-language dictionaries for the `NS` namespace. */
15
+ export declare const dicts: Record<string, Record<MergeToolCallsKey, string>>;
@@ -14,6 +14,6 @@
14
14
  *
15
15
  * All colors come from the shared `--dsw-*` tokens (never literals).
16
16
  */
17
- export declare const CSS = "\n/* A tool-call seat whose toolview declined to render (merged-run continuation\n calls) must not consume the flow column's gap. The empty toolview slot\n wrapper is the decline signal; the callRow around it always exists. */\n[data-chat-flow-kind=\"tool-call\"]:has([data-slot=\"tool.call.toolview\"]:empty) { display: none; }\n\n.mtc-root { display: flex; flex-direction: column; min-width: 0; }\n.mtc-row { display: flex; flex-direction: column; min-width: 0; }\n.mtc-row[data-state='running'] .mtc-title-row { position: relative; overflow: hidden; }\n.mtc-row[data-state='running'] .mtc-title-row::after {\n content: '';\n position: absolute;\n top: 0; bottom: 0; left: 0;\n width: 300px;\n background: linear-gradient(90deg, transparent 0%, color-mix(in srgb, var(--dsw-alias-bg-base) 60%, transparent) 55%, transparent 100%);\n animation: dsh-mtc-sweep 2.6s ease-out infinite;\n pointer-events: none;\n}\n@keyframes dsh-mtc-sweep {\n 0% { left: -300px; }\n 90%, 100% { left: 100%; }\n}\n\n.mtc-title { font-weight: 400; }\n.mtc-sep { flex: none; width: 2px; height: 2px; border-radius: 1px; margin: 0 8px; background: var(--dsw-alias-label-caption); }\n.mtc-summary {\n flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n font-size: 14px; line-height: 24px; color: var(--dsw-alias-label-tertiary);\n}\n.mtc-summary-link {\n flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n margin: 0; padding: 0; border: none; background: none; font: inherit; text-align: left;\n font-size: 14px; line-height: 24px; color: var(--dsw-alias-label-secondary);\n text-decoration: underline; text-decoration-color: var(--dsw-alias-label-quaternary);\n text-underline-offset: 3px; cursor: pointer;\n}\n.mtc-summary-link:hover { color: var(--dsw-alias-label-primary); text-decoration-color: currentColor; }\n.mtc-summary-error { color: var(--dsw-alias-state-error-primary); }\n.mtc-summary-suffix { flex: none; margin-left: 4px; white-space: nowrap; font-size: 14px; line-height: 24px; color: var(--dsw-alias-label-tertiary); }\n\n.mtc-card-body { margin: 4px 0 4px 4px; min-width: 0; }\n.mtc-recovery { margin: 4px 0 4px 4px; white-space: pre-wrap; overflow-wrap: anywhere; font: var(--dsw-font-xs-13); color: var(--dsw-alias-label-tertiary); }\n/* IN/OUT text card for calls without a card primitive (mirrors ToolRow). */\n.mtc-io-card {\n display: flex; flex-direction: column; min-width: 0;\n border: 1px solid var(--dsw-alias-border-l2); border-radius: 12px;\n background: var(--dsw-alias-bg-base);\n}\n.mtc-io-section { display: flex; gap: 10px; padding: 8px 12px; min-width: 0; }\n.mtc-io-label {\n flex: none; font-size: 11px; line-height: 16px; font-weight: 600;\n color: var(--dsw-alias-label-tertiary);\n}\n.mtc-io-text {\n flex: 1 1 auto; min-width: 0; white-space: pre-wrap; overflow-wrap: anywhere;\n font-size: 13px; line-height: 20px; color: var(--dsw-alias-label-secondary);\n}\n.mtc-io-text[data-error] { color: var(--dsw-alias-state-error-primary); }\n.mtc-io-divider { height: 1px; background: var(--dsw-alias-border-l2); }\n.mtc-inspect {\n display: inline-flex; align-self: flex-start; align-items: center; gap: 4px;\n margin: 4px 0 2px 4px; padding: 2px 8px;\n border: 1px solid var(--dsw-alias-border-l2); border-radius: 999px;\n background: var(--dsw-alias-bg-base); color: var(--dsw-alias-label-secondary);\n font-size: 11px; line-height: 16px; cursor: pointer;\n opacity: 0; transition: opacity 100ms ease;\n}\n.mtc-root:hover .mtc-inspect, .mtc-inspect:focus-visible { opacity: 1; }\n.mtc-inspect:hover { background: var(--dsw-alias-interactive-bg-hover-solid); color: var(--dsw-alias-label-primary); }\n\n/* Compact child rows: each is the main row's tail structure \u2014 [sep dot][path].\n The dot/path columns are set at runtime by the component (it measures the\n main row's sep offset and sets the --mtc-sep-left custom property on the\n root), so this is only the pre-measure fallback.\n\n The .mtc-children-collapse wrapper animates the block's height with a\n grid-template-rows 0fr\u21941fr transition (children stay in the DOM while\n collapsing, so the slide is smooth instead of popping out). The vertical\n margin lives on the wrapper (not .mtc-children) so it doesn't leak past the\n 0fr row when collapsed. */\n.mtc-children-collapse {\n display: grid;\n grid-template-rows: 0fr;\n margin: 0;\n transition: grid-template-rows 200ms ease-out, margin 200ms ease-out;\n}\n.mtc-children-collapse[data-open] {\n grid-template-rows: 1fr;\n margin: 2px 0;\n}\n.mtc-children {\n display: flex; flex-direction: column; gap: 1px;\n margin: 0 0 0 var(--mtc-sep-left, 61px);\n min-width: 0; min-height: 0; overflow: hidden;\n opacity: 0;\n transition: opacity 150ms ease-out;\n}\n.mtc-children-collapse[data-open] .mtc-children {\n opacity: 1;\n transition: opacity 150ms ease-out 50ms;\n}\n.mtc-child { display: flex; flex-direction: column; min-width: 0; }\n.mtc-child-row {\n display: flex; align-items: center; gap: 0; min-width: 0; height: 20px;\n margin: 0; padding: 0; border: 0; border-radius: 4px; background: none;\n font: inherit; text-align: left; color: var(--dsw-alias-label-secondary); cursor: pointer;\n}\n.mtc-child-row:hover { background: var(--dsw-alias-interactive-bg-hover); color: var(--dsw-alias-label-primary); }\n.mtc-child-row[data-static] { cursor: default; }\n.mtc-child-row[data-static]:hover { background: none; color: var(--dsw-alias-label-secondary); }\n.mtc-child-row .mtc-sep { margin: 0 8px 0 0; }\n.mtc-child-path {\n flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n font-size: 14px; line-height: 20px;\n}\n/* Read-family child paths are open-file links (sidebar preview), same affordance\n as the main row's summary link. */\n.mtc-child-path-link {\n flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n margin: 0; padding: 0; border: none; background: none; font: inherit; text-align: left;\n font-size: 14px; line-height: 20px; color: var(--dsw-alias-label-secondary);\n text-decoration: underline; text-decoration-color: var(--dsw-alias-label-quaternary);\n text-underline-offset: 3px; cursor: pointer;\n}\n.mtc-child-path-link:hover { color: var(--dsw-alias-label-primary); text-decoration-color: currentColor; }\n.mtc-child-state { flex: none; font-size: 11px; line-height: 16px; color: var(--dsw-alias-label-tertiary); }\n.mtc-child-state[data-error] { color: var(--dsw-alias-state-error-primary); }\n/* The expanded child card must not inherit the children block's sep indent:\n pull its left edge back to the main card body's column (root + 4px) so the\n card spans the card width instead of leaving a blank gutter on its left. */\n.mtc-child-body { margin: 2px 0 2px calc(4px - var(--mtc-sep-left, 61px)); min-width: 0; }\n\n.mtc-visually-hidden {\n position: absolute; width: 1px; height: 1px; overflow: hidden;\n clip: rect(0 0 0 0); white-space: nowrap;\n}\n";
17
+ export declare const CSS = "\n/* A tool-call seat whose toolview declined to render (merged-run continuation\n calls) must not consume the flow column's gap. The empty toolview slot\n wrapper is the decline signal; the callRow around it always exists. */\n[data-chat-flow-kind=\"tool-call\"]:has([data-slot=\"tool.call.toolview\"]:empty) { display: none; }\n\n.mtc-root { display: flex; flex-direction: column; min-width: 0; }\n.mtc-row { display: flex; flex-direction: column; min-width: 0; }\n.mtc-row[data-state='running'] .mtc-title-row { position: relative; overflow: hidden; }\n.mtc-row[data-state='running'] .mtc-title-row::after {\n content: '';\n position: absolute;\n top: 0; bottom: 0; left: 0;\n width: 300px;\n background: linear-gradient(90deg, transparent 0%, color-mix(in srgb, var(--dsw-alias-bg-base) 60%, transparent) 55%, transparent 100%);\n animation: dsh-mtc-sweep 2.6s ease-out infinite;\n pointer-events: none;\n}\n@keyframes dsh-mtc-sweep {\n 0% { left: -300px; }\n 90%, 100% { left: 100%; }\n}\n\n.mtc-title { font-weight: 400; }\n.mtc-sep { flex: none; width: 2px; height: 2px; border-radius: 1px; margin: 0 8px; background: var(--dsw-alias-label-caption); }\n.mtc-summary {\n flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n font-size: 14px; line-height: 24px; color: var(--dsw-alias-label-tertiary);\n}\n.mtc-summary-link {\n flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n margin: 0; padding: 0; border: none; background: none; font: inherit; text-align: left;\n font-size: 14px; line-height: 24px; color: var(--dsw-alias-label-secondary);\n text-decoration: underline; text-decoration-color: var(--dsw-alias-label-quaternary);\n text-underline-offset: 3px; cursor: pointer;\n}\n.mtc-summary-link:hover { color: var(--dsw-alias-label-primary); text-decoration-color: currentColor; }\n.mtc-summary-error { color: var(--dsw-alias-state-error-primary); }\n.mtc-summary-suffix { flex: none; margin-left: 4px; white-space: nowrap; font-size: 14px; line-height: 24px; color: var(--dsw-alias-label-tertiary); }\n\n.mtc-card-body { margin: 4px 0 4px 4px; min-width: 0; }\n.mtc-recovery { margin: 4px 0 4px 4px; white-space: pre-wrap; overflow-wrap: anywhere; font: var(--dsw-font-xs-13); color: var(--dsw-alias-label-tertiary); }\n/* IN/OUT text card for calls without a card primitive (mirrors ToolRow). */\n.mtc-io-card {\n display: flex; flex-direction: column; min-width: 0;\n border: 1px solid var(--dsw-alias-border-l2); border-radius: 12px;\n background: var(--dsw-alias-bg-base);\n}\n.mtc-io-section { display: flex; gap: 10px; padding: 8px 12px; min-width: 0; }\n.mtc-io-label {\n flex: none; font-size: 11px; line-height: 16px; font-weight: 600;\n color: var(--dsw-alias-label-tertiary);\n}\n.mtc-io-text {\n flex: 1 1 auto; min-width: 0; white-space: pre-wrap; overflow-wrap: anywhere;\n font-size: 13px; line-height: 20px; color: var(--dsw-alias-label-secondary);\n}\n.mtc-io-text[data-error] { color: var(--dsw-alias-state-error-primary); }\n.mtc-io-divider { height: 1px; background: var(--dsw-alias-border-l2); }\n.mtc-inspect {\n display: inline-flex; align-self: flex-start; align-items: center; gap: 4px;\n margin: 4px 0 2px 4px; padding: 2px 8px;\n border: 1px solid var(--dsw-alias-border-l2); border-radius: 999px;\n background: var(--dsw-alias-bg-base); color: var(--dsw-alias-label-secondary);\n font-size: 11px; line-height: 16px; cursor: pointer;\n opacity: 0; transition: opacity 100ms ease;\n}\n.mtc-root:hover .mtc-inspect, .mtc-inspect:focus-visible { opacity: 1; }\n.mtc-inspect:hover { background: var(--dsw-alias-interactive-bg-hover-solid); color: var(--dsw-alias-label-primary); }\n\n/* Compact child rows: each is the main row's tail structure \u2014 [sep dot][path].\n The dot/path column is set at runtime by the component (it measures the\n main row's sep offset and sets the --mtc-sep-left custom property on the\n root); this is only the pre-measure fallback.\n\n The .mtc-children-collapse wrapper animates the block's height with a\n grid-template-rows 0fr\u21941fr transition (children stay in the DOM while\n collapsing, so the slide is smooth instead of popping out). The vertical\n margin lives on the wrapper (not .mtc-children) so it doesn't leak past the\n 0fr row when collapsed.\n\n The sep-left indent lives on .mtc-child-row (not .mtc-children) so the\n expanded .mtc-child-body can align back to the root's left edge via a plain\n 4px margin. When the indent was on .mtc-children + a negative-margin\n pull-back on .mtc-child-body, the body's left portion was clipped by\n .mtc-children's overflow:hidden (which is required for the grid collapse\n animation). */\n.mtc-children-collapse {\n display: grid;\n grid-template-rows: 0fr;\n margin: 0;\n transition: grid-template-rows 200ms ease-out, margin 200ms ease-out;\n}\n.mtc-children-collapse[data-open] {\n grid-template-rows: 1fr;\n margin: 2px 0;\n}\n.mtc-children {\n display: flex; flex-direction: column; gap: 1px;\n margin: 0;\n min-width: 0; min-height: 0; overflow: hidden;\n opacity: 0;\n transition: opacity 150ms ease-out;\n}\n.mtc-children-collapse[data-open] .mtc-children {\n opacity: 1;\n transition: opacity 150ms ease-out 50ms;\n}\n.mtc-child { display: flex; flex-direction: column; min-width: 0; }\n.mtc-child-row {\n display: flex; align-items: center; gap: 0; min-width: 0; height: 20px;\n margin: 0 0 0 var(--mtc-sep-left, 61px); padding: 0; border: 0; border-radius: 4px; background: none;\n font: inherit; text-align: left; color: var(--dsw-alias-label-secondary); cursor: pointer;\n}\n.mtc-child-row:hover { background: var(--dsw-alias-interactive-bg-hover); color: var(--dsw-alias-label-primary); }\n.mtc-child-row[data-static] { cursor: default; }\n.mtc-child-row[data-static]:hover { background: none; color: var(--dsw-alias-label-secondary); }\n.mtc-child-row .mtc-sep { margin: 0 8px 0 0; }\n.mtc-child-path {\n flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n font-size: 14px; line-height: 20px;\n}\n/* Read-family child paths are open-file links (sidebar preview), same affordance\n as the main row's summary link. */\n.mtc-child-path-link {\n flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n margin: 0; padding: 0; border: none; background: none; font: inherit; text-align: left;\n font-size: 14px; line-height: 20px; color: var(--dsw-alias-label-secondary);\n text-decoration: underline; text-decoration-color: var(--dsw-alias-label-quaternary);\n text-underline-offset: 3px; cursor: pointer;\n}\n.mtc-child-path-link:hover { color: var(--dsw-alias-label-primary); text-decoration-color: currentColor; }\n.mtc-child-state { flex: none; font-size: 11px; line-height: 16px; color: var(--dsw-alias-label-tertiary); }\n.mtc-child-state[data-error] { color: var(--dsw-alias-state-error-primary); }\n/* The expanded child card aligns to the main card body's column (root + 4px).\n No negative-margin pull-back is needed because .mtc-children no longer\n carries the sep-left indent \u2014 only .mtc-child-row does. */\n.mtc-child-body { margin: 2px 0 2px 4px; min-width: 0; }\n\n.mtc-visually-hidden {\n position: absolute; width: 1px; height: 1px; overflow: hidden;\n clip: rect(0 0 0 0); white-space: nowrap;\n}\n";
18
18
  /** Install the stylesheet and return its disposer. */
19
19
  export declare function installStyles(): () => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@huanlin/dsh-plugin-merge-tool-calls",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },