Skip to content

Commit d78db01

Browse files
KJ7LNWmehmetsunkur
authored andcommitted
fix: webview terminal output processing error (RooCodeInc#3028)
1 parent d84bb7d commit d78db01

File tree

2 files changed

+38
-50
lines changed

2 files changed

+38
-50
lines changed

src/core/tools/executeCommandTool.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,9 +149,12 @@ export async function executeCommand(
149149
const terminalProvider = terminalShellIntegrationDisabled ? "execa" : "vscode"
150150
const clineProvider = await cline.providerRef.deref()
151151

152+
let accumulatedOutput = ""
152153
const callbacks: RooTerminalCallbacks = {
153-
onLine: async (output: string, process: RooTerminalProcess) => {
154-
const status: CommandExecutionStatus = { executionId, status: "output", output }
154+
onLine: async (lines: string, process: RooTerminalProcess) => {
155+
accumulatedOutput += lines
156+
const compressedOutput = Terminal.compressTerminalOutput(accumulatedOutput, terminalOutputLineLimit)
157+
const status: CommandExecutionStatus = { executionId, status: "output", output: compressedOutput }
155158
clineProvider?.postMessageToWebview({ type: "commandExecutionStatus", text: JSON.stringify(status) })
156159

157160
if (runInBackground) {

webview-ui/src/components/chat/CommandExecution.tsx

Lines changed: 33 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import { HTMLAttributes, useCallback, useEffect, useMemo, useState } from "react"
1+
import { useCallback, useState, memo } from "react"
22
import { useEvent } from "react-use"
3-
import { Virtuoso } from "react-virtuoso"
43
import { ChevronDown, Skull } from "lucide-react"
54

65
import { CommandExecutionStatus, commandExecutionStatusSchema } from "@roo/schemas"
@@ -19,6 +18,17 @@ interface CommandExecutionProps {
1918
text?: string
2019
}
2120

21+
const parseCommandAndOutput = (text: string) => {
22+
const index = text.indexOf(COMMAND_OUTPUT_STRING)
23+
if (index === -1) {
24+
return { command: text, output: "" }
25+
}
26+
return {
27+
command: text.slice(0, index),
28+
output: text.slice(index + COMMAND_OUTPUT_STRING.length),
29+
}
30+
}
31+
2232
export const CommandExecution = ({ executionId, text }: CommandExecutionProps) => {
2333
const { terminalShellIntegrationDisabled = false } = useExtensionState()
2434

@@ -27,13 +37,11 @@ export const CommandExecution = ({ executionId, text }: CommandExecutionProps) =
2737
const [isExpanded, setIsExpanded] = useState(terminalShellIntegrationDisabled)
2838

2939
const [status, setStatus] = useState<CommandExecutionStatus | null>(null)
30-
const [output, setOutput] = useState("")
31-
const [command, setCommand] = useState(text)
32-
33-
const lines = useMemo(
34-
() => [`$ ${command}`, ...output.split("\n").filter((line) => line.trim() !== "")],
35-
[output, command],
36-
)
40+
const { command: initialCommand, output: initialOutput } = text
41+
? parseCommandAndOutput(text)
42+
: { command: "", output: "" }
43+
const [output, setOutput] = useState(initialOutput)
44+
const [command, setCommand] = useState(initialCommand)
3745

3846
const onMessage = useCallback(
3947
(event: MessageEvent) => {
@@ -55,7 +63,7 @@ export const CommandExecution = ({ executionId, text }: CommandExecutionProps) =
5563
setStatus(data)
5664
break
5765
case "output":
58-
setOutput((output) => output + data.output)
66+
setOutput(data.output)
5967
break
6068
case "fallback":
6169
setIsExpanded(true)
@@ -72,22 +80,9 @@ export const CommandExecution = ({ executionId, text }: CommandExecutionProps) =
7280

7381
useEvent("message", onMessage)
7482

75-
useEffect(() => {
76-
if (!status && text) {
77-
const index = text.indexOf(COMMAND_OUTPUT_STRING)
78-
79-
if (index === -1) {
80-
setCommand(text)
81-
} else {
82-
setCommand(text.slice(0, index))
83-
setOutput(text.slice(index + COMMAND_OUTPUT_STRING.length))
84-
}
85-
}
86-
}, [status, text])
87-
8883
return (
8984
<div className="w-full bg-vscode-editor-background border border-vscode-border rounded-xs p-2">
90-
<CodeBlock source={command} language="shell" />
85+
<CodeBlock source={text ? parseCommandAndOutput(text).command : command} language="shell" />
9186
<div className="flex flex-row items-center justify-between gap-2 px-1">
9287
<div className="flex flex-row items-center gap-1">
9388
{status?.status === "started" && (
@@ -116,7 +111,7 @@ export const CommandExecution = ({ executionId, text }: CommandExecutionProps) =
116111
<div className="whitespace-nowrap">Exited ({status.exitCode})</div>
117112
</div>
118113
)}
119-
{lines.length > 0 && (
114+
{output.length > 0 && (
120115
<Button variant="ghost" size="icon" onClick={() => setIsExpanded(!isExpanded)}>
121116
<ChevronDown
122117
className={cn("size-4 transition-transform duration-300", {
@@ -127,31 +122,21 @@ export const CommandExecution = ({ executionId, text }: CommandExecutionProps) =
127122
)}
128123
</div>
129124
</div>
130-
<div
131-
className={cn("mt-1 pt-1 border-t border-border/25", { hidden: !isExpanded })}
132-
style={{ height: Math.min((lines.length + 1) * 16, 200) }}>
133-
{lines.length > 0 && (
134-
<Virtuoso
135-
className="h-full"
136-
totalCount={lines.length}
137-
itemContent={(i) => <Line className="text-sm">{lines[i]}</Line>}
138-
followOutput="auto"
139-
/>
140-
)}
141-
</div>
125+
<MemoizedOutputContainer isExpanded={isExpanded} output={output} />
142126
</div>
143127
)
144128
}
145129

146-
type LineProps = HTMLAttributes<HTMLDivElement>
147-
148-
const Line = ({ className, ...props }: LineProps) => {
149-
return (
150-
<div
151-
className={cn("font-mono text-vscode-editor-foreground whitespace-pre-wrap break-words", className)}
152-
{...props}
153-
/>
154-
)
155-
}
156-
157130
CommandExecution.displayName = "CommandExecution"
131+
132+
const OutputContainer = ({ isExpanded, output }: { isExpanded: boolean; output: string }) => (
133+
<div
134+
className={cn("mt-1 pt-1 border-t border-border/25 overflow-hidden transition-[max-height] duration-300", {
135+
"max-h-0": !isExpanded,
136+
"max-h-[100%]": isExpanded,
137+
})}>
138+
{output.length > 0 && <CodeBlock source={output} language="log" />}
139+
</div>
140+
)
141+
142+
const MemoizedOutputContainer = memo(OutputContainer)

0 commit comments

Comments
 (0)