mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-07 08:26:51 +00:00
refactor: extract batchConsecutive utility, fix batch UI issues
- Extract generic batchConsecutive() utility from 3 identical while-loops - Fix React key collisions in BatchListFilesPermission, BatchFilePermission, BatchDiffApproval - Normalize language prop to "shellsession" (was "shell-session" for top-level) - Remove unused _batchedMessages property from synthetic messages - Remove dead didViewMultipleDirectories i18n key from all 18 locale files - Add batch button text for listFilesTopLevel/listFilesRecursive - Add batchConsecutive utility tests (6 cases)
This commit is contained in:
parent
2e02d04d7d
commit
3034b55f1d
25 changed files with 214 additions and 200 deletions
|
|
@ -35,12 +35,12 @@ export const BatchDiffApproval = memo(({ files = [], ts }: BatchDiffApprovalProp
|
|||
return (
|
||||
<div className="pt-[5px]">
|
||||
<div className="flex flex-col gap-0 border border-border rounded-md p-1">
|
||||
{files.map((file) => {
|
||||
{files.map((file, index) => {
|
||||
// Use backend-provided unified diff only. Stats also provided by backend.
|
||||
const unified = file.content || ""
|
||||
|
||||
return (
|
||||
<div key={`${file.path}-${ts}`}>
|
||||
<div key={`${file.path}-${index}-${ts}`}>
|
||||
<CodeAccordian
|
||||
path={file.path}
|
||||
code={unified}
|
||||
|
|
|
|||
|
|
@ -29,9 +29,9 @@ export const BatchFilePermission = memo(({ files = [], onPermissionResponse, ts
|
|||
<div className="pt-[5px]">
|
||||
{/* Individual files */}
|
||||
<div className="flex flex-col gap-0 border border-border rounded-md p-1">
|
||||
{files.map((file) => {
|
||||
{files.map((file, index) => {
|
||||
return (
|
||||
<div key={`${file.path}-${ts}`} className="flex items-center gap-2">
|
||||
<div key={`${file.path}-${index}-${ts}`} className="flex items-center gap-2">
|
||||
<ToolUseBlock className="flex-1">
|
||||
<ToolUseBlockHeader
|
||||
onClick={() => vscode.postMessage({ type: "openFile", text: file.content })}>
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@ export const BatchListFilesPermission = memo(({ dirs = [], ts }: BatchListFilesP
|
|||
return (
|
||||
<div className="pt-[5px]">
|
||||
<div className="flex flex-col gap-0 border border-border rounded-md p-1">
|
||||
{dirs.map((dir) => {
|
||||
{dirs.map((dir, index) => {
|
||||
return (
|
||||
<div key={`${dir.path}-${ts}`} className="flex items-center gap-2">
|
||||
<div key={`${dir.path}-${index}-${ts}`} className="flex items-center gap-2">
|
||||
<ToolUseBlock className="flex-1">
|
||||
<ToolUseBlockHeader>
|
||||
<PathTooltip content={dir.path}>
|
||||
|
|
|
|||
|
|
@ -789,7 +789,7 @@ export const ChatRowContent = ({
|
|||
<CodeAccordian
|
||||
path={tool.path}
|
||||
code={tool.content}
|
||||
language={isRecursive ? "shellsession" : "shell-session"}
|
||||
language="shellsession"
|
||||
isExpanded={isExpanded}
|
||||
onToggleExpand={handleToggleExpand}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { Trans } from "react-i18next"
|
|||
import { useDebounceEffect } from "@src/utils/useDebounceEffect"
|
||||
import { appendImages } from "@src/utils/imageUtils"
|
||||
import { getCostBreakdownIfNeeded } from "@src/utils/costFormatting"
|
||||
import { batchConsecutive } from "@src/utils/batchConsecutive"
|
||||
|
||||
import type { ClineAsk, ClineSayTool, ClineMessage, ExtensionMessage, AudioType } from "@roo-code/types"
|
||||
|
||||
|
|
@ -328,6 +329,16 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
|
|||
setSecondaryButtonText(t("chat:reject.title"))
|
||||
}
|
||||
break
|
||||
case "listFilesTopLevel":
|
||||
case "listFilesRecursive":
|
||||
if (tool.batchDirs && Array.isArray(tool.batchDirs)) {
|
||||
setPrimaryButtonText(t("chat:read-batch.approve.title"))
|
||||
setSecondaryButtonText(t("chat:read-batch.deny.title"))
|
||||
} else {
|
||||
setPrimaryButtonText(t("chat:approve.title"))
|
||||
setSecondaryButtonText(t("chat:reject.title"))
|
||||
}
|
||||
break
|
||||
default:
|
||||
setPrimaryButtonText(t("chat:approve.title"))
|
||||
setSecondaryButtonText(t("chat:reject.title"))
|
||||
|
|
@ -1187,179 +1198,82 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
|
|||
}
|
||||
}
|
||||
|
||||
// Consolidate consecutive read_file ask messages into batches
|
||||
const readFileBatched: ClineMessage[] = []
|
||||
let i = 0
|
||||
while (i < filtered.length) {
|
||||
const msg = filtered[i]
|
||||
|
||||
// Check if this starts a sequence of read_file asks
|
||||
if (isReadFileAsk(msg)) {
|
||||
// Collect all consecutive read_file asks
|
||||
const batch: ClineMessage[] = [msg]
|
||||
let j = i + 1
|
||||
while (j < filtered.length && isReadFileAsk(filtered[j])) {
|
||||
batch.push(filtered[j])
|
||||
j++
|
||||
// Synthesize a batch of consecutive read_file asks into a single message
|
||||
const synthesizeReadFileBatch = (batch: ClineMessage[]): ClineMessage => {
|
||||
const batchFiles = batch.map((batchMsg) => {
|
||||
try {
|
||||
const tool = JSON.parse(batchMsg.text || "{}")
|
||||
return {
|
||||
path: tool.path || "",
|
||||
lineSnippet: tool.reason || "",
|
||||
isOutsideWorkspace: tool.isOutsideWorkspace || false,
|
||||
key: `${tool.path}${tool.reason ? ` (${tool.reason})` : ""}`,
|
||||
content: tool.content || "",
|
||||
}
|
||||
} catch {
|
||||
return { path: "", lineSnippet: "", key: "", content: "" }
|
||||
}
|
||||
})
|
||||
|
||||
if (batch.length > 1) {
|
||||
// Create a synthetic batch message
|
||||
const batchFiles = batch.map((batchMsg) => {
|
||||
try {
|
||||
const tool = JSON.parse(batchMsg.text || "{}")
|
||||
return {
|
||||
path: tool.path || "",
|
||||
lineSnippet: tool.reason || "",
|
||||
isOutsideWorkspace: tool.isOutsideWorkspace || false,
|
||||
key: `${tool.path}${tool.reason ? ` (${tool.reason})` : ""}`,
|
||||
content: tool.content || "",
|
||||
}
|
||||
} catch {
|
||||
return { path: "", lineSnippet: "", key: "", content: "" }
|
||||
}
|
||||
})
|
||||
|
||||
// Use the first message as the base, but add batchFiles
|
||||
const firstTool = JSON.parse(msg.text || "{}")
|
||||
const syntheticMessage: ClineMessage = {
|
||||
...msg,
|
||||
text: JSON.stringify({
|
||||
...firstTool,
|
||||
batchFiles,
|
||||
}),
|
||||
// Store original messages for response handling
|
||||
_batchedMessages: batch,
|
||||
} as ClineMessage & { _batchedMessages: ClineMessage[] }
|
||||
|
||||
readFileBatched.push(syntheticMessage)
|
||||
i = j // Skip past all batched messages
|
||||
} else {
|
||||
// Single read_file ask, keep as-is
|
||||
readFileBatched.push(msg)
|
||||
i++
|
||||
}
|
||||
} else {
|
||||
readFileBatched.push(msg)
|
||||
i++
|
||||
const firstTool = JSON.parse(batch[0].text || "{}")
|
||||
return {
|
||||
...batch[0],
|
||||
text: JSON.stringify({ ...firstTool, batchFiles }),
|
||||
}
|
||||
}
|
||||
|
||||
// Consolidate consecutive list_files ask messages into batches
|
||||
const listFilesBatched: ClineMessage[] = []
|
||||
i = 0
|
||||
while (i < readFileBatched.length) {
|
||||
const msg = readFileBatched[i]
|
||||
|
||||
// Check if this starts a sequence of list_files asks
|
||||
if (isListFilesAsk(msg)) {
|
||||
// Collect all consecutive list_files asks
|
||||
const batch: ClineMessage[] = [msg]
|
||||
let j = i + 1
|
||||
while (j < readFileBatched.length && isListFilesAsk(readFileBatched[j])) {
|
||||
batch.push(readFileBatched[j])
|
||||
j++
|
||||
// Synthesize a batch of consecutive list_files asks into a single message
|
||||
const synthesizeListFilesBatch = (batch: ClineMessage[]): ClineMessage => {
|
||||
const batchDirs = batch.map((batchMsg) => {
|
||||
try {
|
||||
const tool = JSON.parse(batchMsg.text || "{}")
|
||||
return {
|
||||
path: tool.path || "",
|
||||
recursive: tool.tool === "listFilesRecursive",
|
||||
isOutsideWorkspace: tool.isOutsideWorkspace || false,
|
||||
key: tool.path || "",
|
||||
}
|
||||
} catch {
|
||||
return { path: "", recursive: false, key: "" }
|
||||
}
|
||||
})
|
||||
|
||||
if (batch.length > 1) {
|
||||
// Create a synthetic batch message
|
||||
const batchDirs = batch.map((batchMsg) => {
|
||||
try {
|
||||
const tool = JSON.parse(batchMsg.text || "{}")
|
||||
return {
|
||||
path: tool.path || "",
|
||||
recursive: tool.tool === "listFilesRecursive",
|
||||
isOutsideWorkspace: tool.isOutsideWorkspace || false,
|
||||
key: tool.path || "",
|
||||
}
|
||||
} catch {
|
||||
return { path: "", recursive: false, key: "" }
|
||||
}
|
||||
})
|
||||
|
||||
// Use the first message as the base, but add batchDirs
|
||||
const firstTool = JSON.parse(msg.text || "{}")
|
||||
const syntheticMessage: ClineMessage = {
|
||||
...msg,
|
||||
text: JSON.stringify({
|
||||
...firstTool,
|
||||
batchDirs,
|
||||
}),
|
||||
// Store original messages for response handling
|
||||
_batchedMessages: batch,
|
||||
} as ClineMessage & { _batchedMessages: ClineMessage[] }
|
||||
|
||||
listFilesBatched.push(syntheticMessage)
|
||||
i = j // Skip past all batched messages
|
||||
} else {
|
||||
// Single list_files ask, keep as-is
|
||||
listFilesBatched.push(msg)
|
||||
i++
|
||||
}
|
||||
} else {
|
||||
listFilesBatched.push(msg)
|
||||
i++
|
||||
const firstTool = JSON.parse(batch[0].text || "{}")
|
||||
return {
|
||||
...batch[0],
|
||||
text: JSON.stringify({ ...firstTool, batchDirs }),
|
||||
}
|
||||
}
|
||||
|
||||
// Consolidate consecutive file-edit ask messages into batches
|
||||
const result: ClineMessage[] = []
|
||||
i = 0
|
||||
while (i < listFilesBatched.length) {
|
||||
const msg = listFilesBatched[i]
|
||||
|
||||
// Check if this starts a sequence of file-edit asks
|
||||
if (isEditFileAsk(msg)) {
|
||||
// Collect all consecutive file-edit asks
|
||||
const batch: ClineMessage[] = [msg]
|
||||
let j = i + 1
|
||||
while (j < listFilesBatched.length && isEditFileAsk(listFilesBatched[j])) {
|
||||
batch.push(listFilesBatched[j])
|
||||
j++
|
||||
// Synthesize a batch of consecutive file-edit asks into a single message
|
||||
const synthesizeEditFileBatch = (batch: ClineMessage[]): ClineMessage => {
|
||||
const batchDiffs = batch.map((batchMsg) => {
|
||||
try {
|
||||
const tool = JSON.parse(batchMsg.text || "{}")
|
||||
return {
|
||||
path: tool.path || "",
|
||||
changeCount: 1,
|
||||
key: tool.path || "",
|
||||
content: tool.content || tool.diff || "",
|
||||
diffStats: tool.diffStats,
|
||||
}
|
||||
} catch {
|
||||
return { path: "", changeCount: 0, key: "", content: "" }
|
||||
}
|
||||
})
|
||||
|
||||
if (batch.length > 1) {
|
||||
// Create a synthetic batch message with batchDiffs
|
||||
const batchDiffs = batch.map((batchMsg) => {
|
||||
try {
|
||||
const tool = JSON.parse(batchMsg.text || "{}")
|
||||
return {
|
||||
path: tool.path || "",
|
||||
changeCount: 1,
|
||||
key: tool.path || "",
|
||||
content: tool.content || tool.diff || "",
|
||||
diffStats: tool.diffStats,
|
||||
}
|
||||
} catch {
|
||||
return { path: "", changeCount: 0, key: "", content: "" }
|
||||
}
|
||||
})
|
||||
|
||||
// Use the first message as the base, but add batchDiffs
|
||||
const firstTool = JSON.parse(msg.text || "{}")
|
||||
const syntheticMessage: ClineMessage = {
|
||||
...msg,
|
||||
text: JSON.stringify({
|
||||
...firstTool,
|
||||
batchDiffs,
|
||||
}),
|
||||
// Store original messages for response handling
|
||||
_batchedMessages: batch,
|
||||
} as ClineMessage & { _batchedMessages: ClineMessage[] }
|
||||
|
||||
result.push(syntheticMessage)
|
||||
i = j // Skip past all batched messages
|
||||
} else {
|
||||
// Single file-edit ask, keep as-is
|
||||
result.push(msg)
|
||||
i++
|
||||
}
|
||||
} else {
|
||||
result.push(msg)
|
||||
i++
|
||||
const firstTool = JSON.parse(batch[0].text || "{}")
|
||||
return {
|
||||
...batch[0],
|
||||
text: JSON.stringify({ ...firstTool, batchDiffs }),
|
||||
}
|
||||
}
|
||||
|
||||
// Consolidate consecutive ask messages into batches
|
||||
const readFileBatched = batchConsecutive(filtered, isReadFileAsk, synthesizeReadFileBatch)
|
||||
const listFilesBatched = batchConsecutive(readFileBatched, isListFilesAsk, synthesizeListFilesBatch)
|
||||
const result = batchConsecutive(listFilesBatched, isEditFileAsk, synthesizeEditFileBatch)
|
||||
|
||||
if (isCondensing) {
|
||||
result.push({
|
||||
type: "say",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/ca/chat.json
generated
3
webview-ui/src/i18n/locales/ca/chat.json
generated
|
|
@ -219,8 +219,7 @@
|
|||
"didViewTopLevelOutsideWorkspace": "Roo ha vist els fitxers de nivell superior en aquest directori (fora de l'espai de treball)",
|
||||
"wantsToViewRecursiveOutsideWorkspace": "Roo vol veure recursivament tots els fitxers en aquest directori (fora de l'espai de treball)",
|
||||
"didViewRecursiveOutsideWorkspace": "Roo ha vist recursivament tots els fitxers en aquest directori (fora de l'espai de treball)",
|
||||
"wantsToViewMultipleDirectories": "Roo vol veure diversos directoris",
|
||||
"didViewMultipleDirectories": "Roo ha vist diversos directoris"
|
||||
"wantsToViewMultipleDirectories": "Roo vol veure diversos directoris"
|
||||
},
|
||||
"commandOutput": "Sortida de la comanda",
|
||||
"commandExecution": {
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/de/chat.json
generated
3
webview-ui/src/i18n/locales/de/chat.json
generated
|
|
@ -219,8 +219,7 @@
|
|||
"didViewTopLevelOutsideWorkspace": "Roo hat die Dateien auf oberster Ebene in diesem Verzeichnis (außerhalb des Arbeitsbereichs) angezeigt",
|
||||
"wantsToViewRecursiveOutsideWorkspace": "Roo möchte rekursiv alle Dateien in diesem Verzeichnis (außerhalb des Arbeitsbereichs) anzeigen",
|
||||
"didViewRecursiveOutsideWorkspace": "Roo hat rekursiv alle Dateien in diesem Verzeichnis (außerhalb des Arbeitsbereichs) angezeigt",
|
||||
"wantsToViewMultipleDirectories": "Roo möchte mehrere Verzeichnisse anzeigen",
|
||||
"didViewMultipleDirectories": "Roo hat mehrere Verzeichnisse angezeigt"
|
||||
"wantsToViewMultipleDirectories": "Roo möchte mehrere Verzeichnisse anzeigen"
|
||||
},
|
||||
"commandOutput": "Befehlsausgabe",
|
||||
"commandExecution": {
|
||||
|
|
|
|||
|
|
@ -236,7 +236,6 @@
|
|||
"wantsToViewRecursiveOutsideWorkspace": "Roo wants to recursively view all files in this directory (outside workspace)",
|
||||
"didViewRecursiveOutsideWorkspace": "Roo recursively viewed all files in this directory (outside workspace)",
|
||||
"wantsToViewMultipleDirectories": "Roo wants to view multiple directories",
|
||||
"didViewMultipleDirectories": "Roo viewed multiple directories",
|
||||
"wantsToSearch": "Roo wants to search this directory for <code>{{regex}}</code>",
|
||||
"didSearch": "Roo searched this directory for <code>{{regex}}</code>",
|
||||
"wantsToSearchOutsideWorkspace": "Roo wants to search this directory (outside workspace) for <code>{{regex}}</code>",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/es/chat.json
generated
3
webview-ui/src/i18n/locales/es/chat.json
generated
|
|
@ -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": {
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/fr/chat.json
generated
3
webview-ui/src/i18n/locales/fr/chat.json
generated
|
|
@ -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": {
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/hi/chat.json
generated
3
webview-ui/src/i18n/locales/hi/chat.json
generated
|
|
@ -219,8 +219,7 @@
|
|||
"didViewTopLevelOutsideWorkspace": "Roo ने इस निर्देशिका (कार्यक्षेत्र के बाहर) में शीर्ष स्तर की फ़ाइलें देखीं",
|
||||
"wantsToViewRecursiveOutsideWorkspace": "Roo इस निर्देशिका (कार्यक्षेत्र के बाहर) में सभी फ़ाइलों को पुनरावर्ती रूप से देखना चाहता है",
|
||||
"didViewRecursiveOutsideWorkspace": "Roo ने इस निर्देशिका (कार्यक्षेत्र के बाहर) में सभी फ़ाइलों को पुनरावर्ती रूप से देखा",
|
||||
"wantsToViewMultipleDirectories": "Roo कई डायरेक्ट्रीज़ देखना चाहता है",
|
||||
"didViewMultipleDirectories": "Roo ने कई डायरेक्ट्रीज़ देखीं"
|
||||
"wantsToViewMultipleDirectories": "Roo कई डायरेक्ट्रीज़ देखना चाहता है"
|
||||
},
|
||||
"commandOutput": "कमांड आउटपुट",
|
||||
"commandExecution": {
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/id/chat.json
generated
3
webview-ui/src/i18n/locales/id/chat.json
generated
|
|
@ -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 <code>{{query}}</code>",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/it/chat.json
generated
3
webview-ui/src/i18n/locales/it/chat.json
generated
|
|
@ -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": {
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/ja/chat.json
generated
3
webview-ui/src/i18n/locales/ja/chat.json
generated
|
|
@ -219,8 +219,7 @@
|
|||
"didViewTopLevelOutsideWorkspace": "Rooはこのディレクトリ(ワークスペース外)のトップレベルファイルを表示しました",
|
||||
"wantsToViewRecursiveOutsideWorkspace": "Rooはこのディレクトリ(ワークスペース外)のすべてのファイルを再帰的に表示したい",
|
||||
"didViewRecursiveOutsideWorkspace": "Rooはこのディレクトリ(ワークスペース外)のすべてのファイルを再帰的に表示しました",
|
||||
"wantsToViewMultipleDirectories": "Roo は複数のディレクトリを表示したい",
|
||||
"didViewMultipleDirectories": "Roo は複数のディレクトリを表示しました"
|
||||
"wantsToViewMultipleDirectories": "Roo は複数のディレクトリを表示したい"
|
||||
},
|
||||
"commandOutput": "コマンド出力",
|
||||
"commandExecution": {
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/ko/chat.json
generated
3
webview-ui/src/i18n/locales/ko/chat.json
generated
|
|
@ -219,8 +219,7 @@
|
|||
"didViewTopLevelOutsideWorkspace": "Roo가 이 디렉토리(워크스페이스 외부)의 최상위 파일을 보았습니다",
|
||||
"wantsToViewRecursiveOutsideWorkspace": "Roo가 이 디렉토리(워크스페이스 외부)의 모든 파일을 재귀적으로 보고 싶어합니다",
|
||||
"didViewRecursiveOutsideWorkspace": "Roo가 이 디렉토리(워크스페이스 외부)의 모든 파일을 재귀적으로 보았습니다",
|
||||
"wantsToViewMultipleDirectories": "Roo가 여러 디렉토리를 보려고 합니다",
|
||||
"didViewMultipleDirectories": "Roo가 여러 디렉토리를 확인했습니다"
|
||||
"wantsToViewMultipleDirectories": "Roo가 여러 디렉토리를 보려고 합니다"
|
||||
},
|
||||
"commandOutput": "명령 출력",
|
||||
"commandExecution": {
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/nl/chat.json
generated
3
webview-ui/src/i18n/locales/nl/chat.json
generated
|
|
@ -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": {
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/pl/chat.json
generated
3
webview-ui/src/i18n/locales/pl/chat.json
generated
|
|
@ -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": {
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/pt-BR/chat.json
generated
3
webview-ui/src/i18n/locales/pt-BR/chat.json
generated
|
|
@ -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": {
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/ru/chat.json
generated
3
webview-ui/src/i18n/locales/ru/chat.json
generated
|
|
@ -214,8 +214,7 @@
|
|||
"didViewTopLevelOutsideWorkspace": "Roo просмотрел файлы верхнего уровня в этой директории (вне рабочего пространства)",
|
||||
"wantsToViewRecursiveOutsideWorkspace": "Roo хочет рекурсивно просмотреть все файлы в этой директории (вне рабочего пространства)",
|
||||
"didViewRecursiveOutsideWorkspace": "Roo рекурсивно просмотрел все файлы в этой директории (вне рабочего пространства)",
|
||||
"wantsToViewMultipleDirectories": "Roo хочет просмотреть несколько директорий",
|
||||
"didViewMultipleDirectories": "Roo просмотрел несколько директорий"
|
||||
"wantsToViewMultipleDirectories": "Roo хочет просмотреть несколько директорий"
|
||||
},
|
||||
"commandOutput": "Вывод команды",
|
||||
"commandExecution": {
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/tr/chat.json
generated
3
webview-ui/src/i18n/locales/tr/chat.json
generated
|
|
@ -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": {
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/vi/chat.json
generated
3
webview-ui/src/i18n/locales/vi/chat.json
generated
|
|
@ -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": {
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/zh-CN/chat.json
generated
3
webview-ui/src/i18n/locales/zh-CN/chat.json
generated
|
|
@ -219,8 +219,7 @@
|
|||
"didViewTopLevelOutsideWorkspace": "已查看目录文件列表(工作区外)",
|
||||
"wantsToViewRecursiveOutsideWorkspace": "需要查看目录所有文件(工作区外)",
|
||||
"didViewRecursiveOutsideWorkspace": "已查看目录所有文件(工作区外)",
|
||||
"wantsToViewMultipleDirectories": "Roo 想要查看多个目录",
|
||||
"didViewMultipleDirectories": "Roo 查看了多个目录"
|
||||
"wantsToViewMultipleDirectories": "Roo 想要查看多个目录"
|
||||
},
|
||||
"commandOutput": "命令输出",
|
||||
"commandExecution": {
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/zh-TW/chat.json
generated
3
webview-ui/src/i18n/locales/zh-TW/chat.json
generated
|
|
@ -242,8 +242,7 @@
|
|||
"didSearch": "Roo 已在此目錄中搜尋 <code>{{regex}}</code>",
|
||||
"wantsToSearchOutsideWorkspace": "Roo 想要在此目錄(工作區外)中搜尋 <code>{{regex}}</code>",
|
||||
"didSearchOutsideWorkspace": "Roo 已在此目錄(工作區外)中搜尋 <code>{{regex}}</code>",
|
||||
"wantsToViewMultipleDirectories": "Roo 想要查看多個目錄",
|
||||
"didViewMultipleDirectories": "Roo 查看了多個目錄"
|
||||
"wantsToViewMultipleDirectories": "Roo 想要查看多個目錄"
|
||||
},
|
||||
"codebaseSearch": {
|
||||
"wantsToSearch": "Roo 想要在程式碼庫中搜尋 <code>{{query}}</code>",
|
||||
|
|
|
|||
74
webview-ui/src/utils/__tests__/batchConsecutive.spec.ts
Normal file
74
webview-ui/src/utils/__tests__/batchConsecutive.spec.ts
Normal file
|
|
@ -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")
|
||||
})
|
||||
})
|
||||
44
webview-ui/src/utils/batchConsecutive.ts
Normal file
44
webview-ui/src/utils/batchConsecutive.ts
Normal file
|
|
@ -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
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue