Skip to content

Commit 0eb60e0

Browse files
author
Eric Wheeler
committed
feat: accumulate and compress terminal output in backend
- Accumulate output lines in executeCommandTool before compression - Apply compression using configured terminalOutputLineLimit - Update UI to display compressed output directly - Switch to CodeBlock component for syntax highlighting Signed-off-by: Eric Wheeler <[email protected]>
1 parent ce4ce17 commit 0eb60e0

File tree

2 files changed

+21
-34
lines changed

2 files changed

+21
-34
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: 16 additions & 32 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, useEffect, 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"
@@ -30,11 +29,6 @@ export const CommandExecution = ({ executionId, text }: CommandExecutionProps) =
3029
const [output, setOutput] = useState("")
3130
const [command, setCommand] = useState(text)
3231

33-
const lines = useMemo(
34-
() => [`$ ${command}`, ...output.split("\n").filter((line) => line.trim() !== "")],
35-
[output, command],
36-
)
37-
3832
const onMessage = useCallback(
3933
(event: MessageEvent) => {
4034
const message: ExtensionMessage = event.data
@@ -55,7 +49,7 @@ export const CommandExecution = ({ executionId, text }: CommandExecutionProps) =
5549
setStatus(data)
5650
break
5751
case "output":
58-
setOutput((output) => output + data.output)
52+
setOutput(data.output)
5953
break
6054
case "fallback":
6155
setIsExpanded(true)
@@ -116,7 +110,7 @@ export const CommandExecution = ({ executionId, text }: CommandExecutionProps) =
116110
<div className="whitespace-nowrap">Exited ({status.exitCode})</div>
117111
</div>
118112
)}
119-
{lines.length > 0 && (
113+
{output.length > 0 && (
120114
<Button variant="ghost" size="icon" onClick={() => setIsExpanded(!isExpanded)}>
121115
<ChevronDown
122116
className={cn("size-4 transition-transform duration-300", {
@@ -127,31 +121,21 @@ export const CommandExecution = ({ executionId, text }: CommandExecutionProps) =
127121
)}
128122
</div>
129123
</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>
124+
<MemoizedOutputContainer isExpanded={isExpanded} output={output} />
142125
</div>
143126
)
144127
}
145128

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-
157129
CommandExecution.displayName = "CommandExecution"
130+
131+
const OutputContainer = ({ isExpanded, output }: { isExpanded: boolean; output: string }) => (
132+
<div
133+
className={cn("mt-1 pt-1 border-t border-border/25 overflow-hidden transition-[max-height] duration-300", {
134+
"max-h-0": !isExpanded,
135+
"max-h-[100%]": isExpanded,
136+
})}>
137+
{output.length > 0 && <CodeBlock source={output} language="log" />}
138+
</div>
139+
)
140+
141+
const MemoizedOutputContainer = memo(OutputContainer)

0 commit comments

Comments
 (0)