diff --git a/webview-ui/src/components/chat/BatchDiffApproval.tsx b/webview-ui/src/components/chat/BatchDiffApproval.tsx index a88914cd88..f128e4310d 100644 --- a/webview-ui/src/components/chat/BatchDiffApproval.tsx +++ b/webview-ui/src/components/chat/BatchDiffApproval.tsx @@ -35,12 +35,12 @@ export const BatchDiffApproval = memo(({ files = [], ts }: BatchDiffApprovalProp return (
{{regex}}",
"didSearch": "Roo searched this directory for {{regex}}",
"wantsToSearchOutsideWorkspace": "Roo wants to search this directory (outside workspace) for {{regex}}",
diff --git a/webview-ui/src/i18n/locales/es/chat.json b/webview-ui/src/i18n/locales/es/chat.json
index d4e4272455..b7329cdab1 100644
--- a/webview-ui/src/i18n/locales/es/chat.json
+++ b/webview-ui/src/i18n/locales/es/chat.json
@@ -219,8 +219,7 @@
"didViewTopLevelOutsideWorkspace": "Roo vio los archivos de nivel superior en este directorio (fuera del espacio de trabajo)",
"wantsToViewRecursiveOutsideWorkspace": "Roo quiere ver recursivamente todos los archivos en este directorio (fuera del espacio de trabajo)",
"didViewRecursiveOutsideWorkspace": "Roo vio recursivamente todos los archivos en este directorio (fuera del espacio de trabajo)",
- "wantsToViewMultipleDirectories": "Roo quiere ver varios directorios",
- "didViewMultipleDirectories": "Roo vio varios directorios"
+ "wantsToViewMultipleDirectories": "Roo quiere ver varios directorios"
},
"commandOutput": "Salida del comando",
"commandExecution": {
diff --git a/webview-ui/src/i18n/locales/fr/chat.json b/webview-ui/src/i18n/locales/fr/chat.json
index 95dc15a67b..74283e7d4a 100644
--- a/webview-ui/src/i18n/locales/fr/chat.json
+++ b/webview-ui/src/i18n/locales/fr/chat.json
@@ -219,8 +219,7 @@
"didViewTopLevelOutsideWorkspace": "Roo a vu les fichiers de premier niveau dans ce répertoire (hors espace de travail)",
"wantsToViewRecursiveOutsideWorkspace": "Roo veut voir récursivement tous les fichiers dans ce répertoire (hors espace de travail)",
"didViewRecursiveOutsideWorkspace": "Roo a vu récursivement tous les fichiers dans ce répertoire (hors espace de travail)",
- "wantsToViewMultipleDirectories": "Roo veut voir plusieurs répertoires",
- "didViewMultipleDirectories": "Roo a vu plusieurs répertoires"
+ "wantsToViewMultipleDirectories": "Roo veut voir plusieurs répertoires"
},
"commandOutput": "Sortie de la Commande",
"commandExecution": {
diff --git a/webview-ui/src/i18n/locales/hi/chat.json b/webview-ui/src/i18n/locales/hi/chat.json
index cf385e7daf..a6c7683c84 100644
--- a/webview-ui/src/i18n/locales/hi/chat.json
+++ b/webview-ui/src/i18n/locales/hi/chat.json
@@ -219,8 +219,7 @@
"didViewTopLevelOutsideWorkspace": "Roo ने इस निर्देशिका (कार्यक्षेत्र के बाहर) में शीर्ष स्तर की फ़ाइलें देखीं",
"wantsToViewRecursiveOutsideWorkspace": "Roo इस निर्देशिका (कार्यक्षेत्र के बाहर) में सभी फ़ाइलों को पुनरावर्ती रूप से देखना चाहता है",
"didViewRecursiveOutsideWorkspace": "Roo ने इस निर्देशिका (कार्यक्षेत्र के बाहर) में सभी फ़ाइलों को पुनरावर्ती रूप से देखा",
- "wantsToViewMultipleDirectories": "Roo कई डायरेक्ट्रीज़ देखना चाहता है",
- "didViewMultipleDirectories": "Roo ने कई डायरेक्ट्रीज़ देखीं"
+ "wantsToViewMultipleDirectories": "Roo कई डायरेक्ट्रीज़ देखना चाहता है"
},
"commandOutput": "कमांड आउटपुट",
"commandExecution": {
diff --git a/webview-ui/src/i18n/locales/id/chat.json b/webview-ui/src/i18n/locales/id/chat.json
index 66ef4b30fe..4dfc357bf2 100644
--- a/webview-ui/src/i18n/locales/id/chat.json
+++ b/webview-ui/src/i18n/locales/id/chat.json
@@ -247,8 +247,7 @@
"didViewTopLevelOutsideWorkspace": "Roo melihat file tingkat atas di direktori ini (di luar workspace)",
"wantsToViewRecursiveOutsideWorkspace": "Roo ingin melihat semua file secara rekursif di direktori ini (di luar workspace)",
"didViewRecursiveOutsideWorkspace": "Roo melihat semua file secara rekursif di direktori ini (di luar workspace)",
- "wantsToViewMultipleDirectories": "Roo ingin melihat beberapa direktori",
- "didViewMultipleDirectories": "Roo melihat beberapa direktori"
+ "wantsToViewMultipleDirectories": "Roo ingin melihat beberapa direktori"
},
"codebaseSearch": {
"wantsToSearch": "Roo ingin mencari codebase untuk {{query}}",
diff --git a/webview-ui/src/i18n/locales/it/chat.json b/webview-ui/src/i18n/locales/it/chat.json
index b2078f37e9..8aaa2ebac3 100644
--- a/webview-ui/src/i18n/locales/it/chat.json
+++ b/webview-ui/src/i18n/locales/it/chat.json
@@ -219,8 +219,7 @@
"didViewTopLevelOutsideWorkspace": "Roo ha visualizzato i file di primo livello in questa directory (fuori dall'area di lavoro)",
"wantsToViewRecursiveOutsideWorkspace": "Roo vuole visualizzare ricorsivamente tutti i file in questa directory (fuori dall'area di lavoro)",
"didViewRecursiveOutsideWorkspace": "Roo ha visualizzato ricorsivamente tutti i file in questa directory (fuori dall'area di lavoro)",
- "wantsToViewMultipleDirectories": "Roo vuole visualizzare più directory",
- "didViewMultipleDirectories": "Roo ha visualizzato più directory"
+ "wantsToViewMultipleDirectories": "Roo vuole visualizzare più directory"
},
"commandOutput": "Output del Comando",
"commandExecution": {
diff --git a/webview-ui/src/i18n/locales/ja/chat.json b/webview-ui/src/i18n/locales/ja/chat.json
index eee2b888fc..48b13cfd66 100644
--- a/webview-ui/src/i18n/locales/ja/chat.json
+++ b/webview-ui/src/i18n/locales/ja/chat.json
@@ -219,8 +219,7 @@
"didViewTopLevelOutsideWorkspace": "Rooはこのディレクトリ(ワークスペース外)のトップレベルファイルを表示しました",
"wantsToViewRecursiveOutsideWorkspace": "Rooはこのディレクトリ(ワークスペース外)のすべてのファイルを再帰的に表示したい",
"didViewRecursiveOutsideWorkspace": "Rooはこのディレクトリ(ワークスペース外)のすべてのファイルを再帰的に表示しました",
- "wantsToViewMultipleDirectories": "Roo は複数のディレクトリを表示したい",
- "didViewMultipleDirectories": "Roo は複数のディレクトリを表示しました"
+ "wantsToViewMultipleDirectories": "Roo は複数のディレクトリを表示したい"
},
"commandOutput": "コマンド出力",
"commandExecution": {
diff --git a/webview-ui/src/i18n/locales/ko/chat.json b/webview-ui/src/i18n/locales/ko/chat.json
index 1227dd6c15..eb037eefe1 100644
--- a/webview-ui/src/i18n/locales/ko/chat.json
+++ b/webview-ui/src/i18n/locales/ko/chat.json
@@ -219,8 +219,7 @@
"didViewTopLevelOutsideWorkspace": "Roo가 이 디렉토리(워크스페이스 외부)의 최상위 파일을 보았습니다",
"wantsToViewRecursiveOutsideWorkspace": "Roo가 이 디렉토리(워크스페이스 외부)의 모든 파일을 재귀적으로 보고 싶어합니다",
"didViewRecursiveOutsideWorkspace": "Roo가 이 디렉토리(워크스페이스 외부)의 모든 파일을 재귀적으로 보았습니다",
- "wantsToViewMultipleDirectories": "Roo가 여러 디렉토리를 보려고 합니다",
- "didViewMultipleDirectories": "Roo가 여러 디렉토리를 확인했습니다"
+ "wantsToViewMultipleDirectories": "Roo가 여러 디렉토리를 보려고 합니다"
},
"commandOutput": "명령 출력",
"commandExecution": {
diff --git a/webview-ui/src/i18n/locales/nl/chat.json b/webview-ui/src/i18n/locales/nl/chat.json
index c639180525..77d2531000 100644
--- a/webview-ui/src/i18n/locales/nl/chat.json
+++ b/webview-ui/src/i18n/locales/nl/chat.json
@@ -214,8 +214,7 @@
"didViewTopLevelOutsideWorkspace": "Roo heeft de bovenliggende bestanden in deze map (buiten werkruimte) bekeken",
"wantsToViewRecursiveOutsideWorkspace": "Roo wil alle bestanden in deze map (buiten werkruimte) recursief bekijken",
"didViewRecursiveOutsideWorkspace": "Roo heeft alle bestanden in deze map (buiten werkruimte) recursief bekeken",
- "wantsToViewMultipleDirectories": "Roo wil meerdere mappen bekijken",
- "didViewMultipleDirectories": "Roo heeft meerdere mappen bekeken"
+ "wantsToViewMultipleDirectories": "Roo wil meerdere mappen bekijken"
},
"commandOutput": "Commando-uitvoer",
"commandExecution": {
diff --git a/webview-ui/src/i18n/locales/pl/chat.json b/webview-ui/src/i18n/locales/pl/chat.json
index 7471169553..66801899e9 100644
--- a/webview-ui/src/i18n/locales/pl/chat.json
+++ b/webview-ui/src/i18n/locales/pl/chat.json
@@ -219,8 +219,7 @@
"didViewTopLevelOutsideWorkspace": "Roo zobaczył pliki najwyższego poziomu w tym katalogu (poza obszarem roboczym)",
"wantsToViewRecursiveOutsideWorkspace": "Roo chce rekurencyjnie zobaczyć wszystkie pliki w tym katalogu (poza obszarem roboczym)",
"didViewRecursiveOutsideWorkspace": "Roo rekurencyjnie zobaczył wszystkie pliki w tym katalogu (poza obszarem roboczym)",
- "wantsToViewMultipleDirectories": "Roo chce wyświetlić wiele katalogów",
- "didViewMultipleDirectories": "Roo wyświetlił wiele katalogów"
+ "wantsToViewMultipleDirectories": "Roo chce wyświetlić wiele katalogów"
},
"commandOutput": "Wyjście polecenia",
"commandExecution": {
diff --git a/webview-ui/src/i18n/locales/pt-BR/chat.json b/webview-ui/src/i18n/locales/pt-BR/chat.json
index 649d196c34..d563aa6b36 100644
--- a/webview-ui/src/i18n/locales/pt-BR/chat.json
+++ b/webview-ui/src/i18n/locales/pt-BR/chat.json
@@ -219,8 +219,7 @@
"didViewTopLevelOutsideWorkspace": "Roo visualizou os arquivos de nível superior neste diretório (fora do espaço de trabalho)",
"wantsToViewRecursiveOutsideWorkspace": "Roo quer visualizar recursivamente todos os arquivos neste diretório (fora do espaço de trabalho)",
"didViewRecursiveOutsideWorkspace": "Roo visualizou recursivamente todos os arquivos neste diretório (fora do espaço de trabalho)",
- "wantsToViewMultipleDirectories": "Roo quer visualizar vários diretórios",
- "didViewMultipleDirectories": "Roo visualizou vários diretórios"
+ "wantsToViewMultipleDirectories": "Roo quer visualizar vários diretórios"
},
"commandOutput": "Saída do comando",
"commandExecution": {
diff --git a/webview-ui/src/i18n/locales/ru/chat.json b/webview-ui/src/i18n/locales/ru/chat.json
index b1187f49f4..f599bacaa1 100644
--- a/webview-ui/src/i18n/locales/ru/chat.json
+++ b/webview-ui/src/i18n/locales/ru/chat.json
@@ -214,8 +214,7 @@
"didViewTopLevelOutsideWorkspace": "Roo просмотрел файлы верхнего уровня в этой директории (вне рабочего пространства)",
"wantsToViewRecursiveOutsideWorkspace": "Roo хочет рекурсивно просмотреть все файлы в этой директории (вне рабочего пространства)",
"didViewRecursiveOutsideWorkspace": "Roo рекурсивно просмотрел все файлы в этой директории (вне рабочего пространства)",
- "wantsToViewMultipleDirectories": "Roo хочет просмотреть несколько директорий",
- "didViewMultipleDirectories": "Roo просмотрел несколько директорий"
+ "wantsToViewMultipleDirectories": "Roo хочет просмотреть несколько директорий"
},
"commandOutput": "Вывод команды",
"commandExecution": {
diff --git a/webview-ui/src/i18n/locales/tr/chat.json b/webview-ui/src/i18n/locales/tr/chat.json
index 53540ae988..e4a9bf71f0 100644
--- a/webview-ui/src/i18n/locales/tr/chat.json
+++ b/webview-ui/src/i18n/locales/tr/chat.json
@@ -219,8 +219,7 @@
"didViewTopLevelOutsideWorkspace": "Roo bu dizindeki (çalışma alanı dışında) üst düzey dosyaları görüntüledi",
"wantsToViewRecursiveOutsideWorkspace": "Roo bu dizindeki (çalışma alanı dışında) tüm dosyaları özyinelemeli olarak görüntülemek istiyor",
"didViewRecursiveOutsideWorkspace": "Roo bu dizindeki (çalışma alanı dışında) tüm dosyaları özyinelemeli olarak görüntüledi",
- "wantsToViewMultipleDirectories": "Roo birden fazla dizini görüntülemek istiyor",
- "didViewMultipleDirectories": "Roo birden fazla dizini görüntüledi"
+ "wantsToViewMultipleDirectories": "Roo birden fazla dizini görüntülemek istiyor"
},
"commandOutput": "Komut Çıktısı",
"commandExecution": {
diff --git a/webview-ui/src/i18n/locales/vi/chat.json b/webview-ui/src/i18n/locales/vi/chat.json
index 5b8f514af0..7823801766 100644
--- a/webview-ui/src/i18n/locales/vi/chat.json
+++ b/webview-ui/src/i18n/locales/vi/chat.json
@@ -219,8 +219,7 @@
"didViewTopLevelOutsideWorkspace": "Roo đã xem các tệp cấp cao nhất trong thư mục này (ngoài không gian làm việc)",
"wantsToViewRecursiveOutsideWorkspace": "Roo muốn xem đệ quy tất cả các tệp trong thư mục này (ngoài không gian làm việc)",
"didViewRecursiveOutsideWorkspace": "Roo đã xem đệ quy tất cả các tệp trong thư mục này (ngoài không gian làm việc)",
- "wantsToViewMultipleDirectories": "Roo muốn xem nhiều thư mục",
- "didViewMultipleDirectories": "Roo đã xem nhiều thư mục"
+ "wantsToViewMultipleDirectories": "Roo muốn xem nhiều thư mục"
},
"commandOutput": "Kết quả lệnh",
"commandExecution": {
diff --git a/webview-ui/src/i18n/locales/zh-CN/chat.json b/webview-ui/src/i18n/locales/zh-CN/chat.json
index 83b3ec70fd..4e5002b33d 100644
--- a/webview-ui/src/i18n/locales/zh-CN/chat.json
+++ b/webview-ui/src/i18n/locales/zh-CN/chat.json
@@ -219,8 +219,7 @@
"didViewTopLevelOutsideWorkspace": "已查看目录文件列表(工作区外)",
"wantsToViewRecursiveOutsideWorkspace": "需要查看目录所有文件(工作区外)",
"didViewRecursiveOutsideWorkspace": "已查看目录所有文件(工作区外)",
- "wantsToViewMultipleDirectories": "Roo 想要查看多个目录",
- "didViewMultipleDirectories": "Roo 查看了多个目录"
+ "wantsToViewMultipleDirectories": "Roo 想要查看多个目录"
},
"commandOutput": "命令输出",
"commandExecution": {
diff --git a/webview-ui/src/i18n/locales/zh-TW/chat.json b/webview-ui/src/i18n/locales/zh-TW/chat.json
index 3510b433b2..49b459d54b 100644
--- a/webview-ui/src/i18n/locales/zh-TW/chat.json
+++ b/webview-ui/src/i18n/locales/zh-TW/chat.json
@@ -242,8 +242,7 @@
"didSearch": "Roo 已在此目錄中搜尋 {{regex}}",
"wantsToSearchOutsideWorkspace": "Roo 想要在此目錄(工作區外)中搜尋 {{regex}}",
"didSearchOutsideWorkspace": "Roo 已在此目錄(工作區外)中搜尋 {{regex}}",
- "wantsToViewMultipleDirectories": "Roo 想要查看多個目錄",
- "didViewMultipleDirectories": "Roo 查看了多個目錄"
+ "wantsToViewMultipleDirectories": "Roo 想要查看多個目錄"
},
"codebaseSearch": {
"wantsToSearch": "Roo 想要在程式碼庫中搜尋 {{query}}",
diff --git a/webview-ui/src/utils/__tests__/batchConsecutive.spec.ts b/webview-ui/src/utils/__tests__/batchConsecutive.spec.ts
new file mode 100644
index 0000000000..3bb5a69d2f
--- /dev/null
+++ b/webview-ui/src/utils/__tests__/batchConsecutive.spec.ts
@@ -0,0 +1,74 @@
+import type { ClineMessage } from "@roo-code/types"
+
+import { batchConsecutive } from "../batchConsecutive"
+
+/** Helper: create a minimal ClineMessage with an identifiable text field. */
+function msg(text: string, type: ClineMessage["type"] = "say"): ClineMessage {
+ return { ts: Date.now(), type, text }
+}
+
+/** Predicate: matches messages whose text starts with "match". */
+const isMatch = (m: ClineMessage) => !!m.text?.startsWith("match")
+
+/** Synthesize: merges a batch into a single message with a "BATCH:" marker. */
+const synthesizeBatch = (batch: ClineMessage[]): ClineMessage => ({
+ ...batch[0],
+ text: `BATCH:${batch.map((m) => m.text).join(",")}`,
+})
+
+describe("batchConsecutive", () => {
+ test("empty input returns empty output", () => {
+ expect(batchConsecutive([], isMatch, synthesizeBatch)).toEqual([])
+ })
+
+ test("no matches returns passthrough", () => {
+ const messages = [msg("a"), msg("b"), msg("c")]
+ const result = batchConsecutive(messages, isMatch, synthesizeBatch)
+ expect(result).toEqual(messages)
+ })
+
+ test("single match is passed through without batching", () => {
+ const messages = [msg("a"), msg("match-1"), msg("b")]
+ const result = batchConsecutive(messages, isMatch, synthesizeBatch)
+ expect(result).toHaveLength(3)
+ expect(result[1].text).toBe("match-1")
+ })
+
+ test("two consecutive matches produce one synthetic message", () => {
+ const messages = [msg("a"), msg("match-1"), msg("match-2"), msg("b")]
+ const result = batchConsecutive(messages, isMatch, synthesizeBatch)
+ expect(result).toHaveLength(3)
+ expect(result[0].text).toBe("a")
+ expect(result[1].text).toBe("BATCH:match-1,match-2")
+ expect(result[2].text).toBe("b")
+ })
+
+ test("non-consecutive matches are not batched", () => {
+ const messages = [msg("match-1"), msg("other"), msg("match-2")]
+ const result = batchConsecutive(messages, isMatch, synthesizeBatch)
+ expect(result).toHaveLength(3)
+ expect(result[0].text).toBe("match-1")
+ expect(result[1].text).toBe("other")
+ expect(result[2].text).toBe("match-2")
+ })
+
+ test("mixed sequences are correctly interleaved", () => {
+ const messages = [
+ msg("match-1"),
+ msg("match-2"),
+ msg("match-3"),
+ msg("other-1"),
+ msg("match-4"),
+ msg("other-2"),
+ msg("match-5"),
+ msg("match-6"),
+ ]
+ const result = batchConsecutive(messages, isMatch, synthesizeBatch)
+ expect(result).toHaveLength(5)
+ expect(result[0].text).toBe("BATCH:match-1,match-2,match-3")
+ expect(result[1].text).toBe("other-1")
+ expect(result[2].text).toBe("match-4") // single — not batched
+ expect(result[3].text).toBe("other-2")
+ expect(result[4].text).toBe("BATCH:match-5,match-6")
+ })
+})
diff --git a/webview-ui/src/utils/batchConsecutive.ts b/webview-ui/src/utils/batchConsecutive.ts
new file mode 100644
index 0000000000..e030ad0c70
--- /dev/null
+++ b/webview-ui/src/utils/batchConsecutive.ts
@@ -0,0 +1,44 @@
+import type { ClineMessage } from "@roo-code/types"
+
+/**
+ * Walk a message array and batch runs of consecutive messages that match
+ * `predicate` into synthetic messages produced by `synthesize`.
+ *
+ * - Runs of length 1 are passed through unchanged.
+ * - Runs of length >= 2 are replaced by a single synthetic message.
+ * - Non-matching messages are preserved in-order.
+ */
+export function batchConsecutive(
+ messages: ClineMessage[],
+ predicate: (msg: ClineMessage) => boolean,
+ synthesize: (batch: ClineMessage[]) => ClineMessage,
+): ClineMessage[] {
+ const result: ClineMessage[] = []
+ let i = 0
+
+ while (i < messages.length) {
+ if (predicate(messages[i])) {
+ // Collect consecutive matches into a batch
+ const batch: ClineMessage[] = [messages[i]]
+ let j = i + 1
+
+ while (j < messages.length && predicate(messages[j])) {
+ batch.push(messages[j])
+ j++
+ }
+
+ if (batch.length > 1) {
+ result.push(synthesize(batch))
+ } else {
+ result.push(batch[0])
+ }
+
+ i = j
+ } else {
+ result.push(messages[i])
+ i++
+ }
+ }
+
+ return result
+}