Skip to content

Commit 0dd8890

Browse files
committed
Command chat row improvements
1 parent 4f38409 commit 0dd8890

File tree

21 files changed

+200
-194
lines changed

21 files changed

+200
-194
lines changed

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ import {
5959
FileCode2,
6060
PocketKnife,
6161
FolderTree,
62+
TerminalSquare,
6263
} from "lucide-react"
6364
import { cn } from "@/lib/utils"
6465

@@ -229,11 +230,9 @@ export const ChatRowContent = ({
229230
isCommandExecuting ? (
230231
<ProgressIndicator />
231232
) : (
232-
<span
233-
className="codicon codicon-terminal"
234-
style={{ color: normalColor, marginBottom: "-1.5px" }}></span>
233+
<TerminalSquare className="size-4" aria-label="Terminal icon" />
235234
),
236-
<span style={{ color: normalColor, fontWeight: "bold" }}>{t("chat:runCommand.title")}:</span>,
235+
<span style={{ color: normalColor, fontWeight: "bold" }}>{t("chat:runCommand.title")}</span>,
237236
]
238237
case "use_mcp_server":
239238
const mcpServerUse = safeJsonParse<ClineAskUseMcpServer>(message.text)

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

Lines changed: 36 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useCallback, useState, memo, useMemo } from "react"
22
import { useEvent } from "react-use"
3-
import { ChevronDown, Skull } from "lucide-react"
3+
import { ChevronDown, OctagonX } from "lucide-react"
44

55
import { CommandExecutionStatus, commandExecutionStatusSchema } from "@roo-code/types"
66

@@ -12,11 +12,12 @@ import { COMMAND_OUTPUT_STRING } from "@roo/combineCommandSequences"
1212
import { vscode } from "@src/utils/vscode"
1313
import { useExtensionState } from "@src/context/ExtensionStateContext"
1414
import { cn } from "@src/lib/utils"
15-
import { Button } from "@src/components/ui"
15+
import { Button, StandardTooltip } from "@src/components/ui"
1616
import CodeBlock from "../common/CodeBlock"
1717
import { CommandPatternSelector } from "./CommandPatternSelector"
1818
import { parseCommand } from "../../utils/command-validation"
1919
import { extractPatternsFromCommand } from "../../utils/command-parser"
20+
import { t } from "i18next"
2021

2122
interface CommandPattern {
2223
pattern: string
@@ -140,52 +141,58 @@ export const CommandExecution = ({ executionId, text, icon, title }: CommandExec
140141
return (
141142
<>
142143
<div className="flex flex-row items-center justify-between gap-2 mb-1">
143-
<div className="flex flex-row items-center gap-1">
144+
<div className="flex flex-row items-center gap-2">
144145
{icon}
145146
{title}
147+
{status?.status === "exited" && (
148+
<div className="flex flex-row items-center gap-2 font-mono text-xs">
149+
<StandardTooltip
150+
content={t("chat.commandExecution.exitStatus", { exitStatus: status.exitCode })}>
151+
<div
152+
className={cn(
153+
"rounded-full size-2",
154+
status.exitCode === 0 ? "bg-green-600" : "bg-red-600",
155+
)}
156+
/>
157+
</StandardTooltip>
158+
</div>
159+
)}
146160
</div>
147-
<div className="flex flex-row items-center justify-between gap-2 px-1">
161+
<div className=" flex flex-row items-center justify-between gap-2 px-1">
148162
<div className="flex flex-row items-center gap-1">
149163
{status?.status === "started" && (
150164
<div className="flex flex-row items-center gap-2 font-mono text-xs">
151-
<div className="rounded-full size-1.5 bg-lime-400" />
152-
<div>Running</div>
153165
{status.pid && <div className="whitespace-nowrap">(PID: {status.pid})</div>}
154-
<Button
155-
variant="ghost"
156-
size="icon"
157-
onClick={() =>
158-
vscode.postMessage({ type: "terminalOperation", terminalOperation: "abort" })
159-
}>
160-
<Skull />
161-
</Button>
162-
</div>
163-
)}
164-
{status?.status === "exited" && (
165-
<div className="flex flex-row items-center gap-2 font-mono text-xs">
166-
<div
167-
className={cn(
168-
"rounded-full size-1.5",
169-
status.exitCode === 0 ? "bg-lime-400" : "bg-red-400",
170-
)}
171-
/>
172-
<div className="whitespace-nowrap">Exited ({status.exitCode})</div>
166+
<StandardTooltip content={t("chat:commandExecution.abort")}>
167+
<Button
168+
variant="ghost"
169+
size="icon"
170+
onClick={() =>
171+
vscode.postMessage({
172+
type: "terminalOperation",
173+
terminalOperation: "abort",
174+
})
175+
}>
176+
<OctagonX className="size-4" />
177+
</Button>
178+
</StandardTooltip>
173179
</div>
174180
)}
175181
{output.length > 0 && (
176182
<Button variant="ghost" size="icon" onClick={() => setIsExpanded(!isExpanded)}>
177183
<ChevronDown
178-
className={cn("size-4 transition-transform duration-300", {
179-
"rotate-180": isExpanded,
180-
})}
184+
className={cn(
185+
"size-4 transition-transform duration-300",
186+
isExpanded && "rotate-180",
187+
)}
181188
/>
182189
</Button>
183190
)}
184191
</div>
185192
</div>
186193
</div>
187194

188-
<div className="w-full bg-vscode-editor-background border border-vscode-border rounded-xs">
195+
<div className="bg-vscode-editor-background border border-vscode-border rounded-xs ml-6 mt-2">
189196
<div className="p-2">
190197
<CodeBlock source={command} language="shell" />
191198
<OutputContainer isExpanded={isExpanded} output={output} />

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

Lines changed: 54 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import React, { useState, useMemo } from "react"
2-
import { Check, ChevronDown, Info, X } from "lucide-react"
2+
import { Check, CheckCheck, ChevronUp, X } from "lucide-react"
33
import { cn } from "../../lib/utils"
4-
import { useTranslation, Trans } from "react-i18next"
5-
import { VSCodeLink } from "@vscode/webview-ui-toolkit/react"
4+
import { useTranslation } from "react-i18next"
65
import { StandardTooltip } from "../ui/standard-tooltip"
76

87
interface CommandPattern {
@@ -29,10 +28,6 @@ export const CommandPatternSelector: React.FC<CommandPatternSelectorProps> = ({
2928
const [isExpanded, setIsExpanded] = useState(false)
3029
const [editingStates, setEditingStates] = useState<Record<string, { isEditing: boolean; value: string }>>({})
3130

32-
const handleOpenSettings = () => {
33-
window.postMessage({ type: "action", action: "settingsButtonClicked", values: { section: "autoApprove" } })
34-
}
35-
3631
// Create a combined list with full command first, then patterns
3732
const allPatterns = useMemo(() => {
3833
// Create a set to track unique patterns we've already seen
@@ -68,50 +63,36 @@ export const CommandPatternSelector: React.FC<CommandPatternSelectorProps> = ({
6863
}
6964

7065
return (
71-
<div className="border-t border-vscode-panel-border bg-vscode-sideBar-background/30">
66+
<div className="border-t border-vscode-panel-border/50 bg-vscode-sideBar-background/30">
7267
<button
7368
onClick={() => setIsExpanded(!isExpanded)}
7469
className="w-full px-3 py-2 flex items-center justify-between hover:bg-vscode-list-hoverBackground transition-colors">
75-
<div className="flex items-center gap-2">
76-
<ChevronDown
77-
className={cn("size-4 transition-transform", {
78-
"-rotate-90": !isExpanded,
79-
})}
70+
<div className="group flex items-center gap-2 cursor-pointer w-full text-left">
71+
<span
72+
className={cn(
73+
"text-sm flex-1 group-hover:opacity-100",
74+
isExpanded ? "opacity-100" : "opacity-40",
75+
)}>
76+
<CheckCheck className="size-3 inline-block mr-2" />
77+
{t("chat:commandExecution.manageCommands")}
78+
</span>
79+
<ChevronUp
80+
className={cn(
81+
"group-hover:opacity-100 size-4 transition-transform",
82+
isExpanded ? "opacity-100" : "opacity-40 -rotate-180",
83+
)}
8084
/>
81-
<span className="text-sm font-medium">{t("chat:commandExecution.manageCommands")}</span>
82-
<StandardTooltip
83-
content={
84-
<div className="max-w-xs">
85-
<Trans
86-
i18nKey="chat:commandExecution.commandManagementDescription"
87-
components={{
88-
settingsLink: (
89-
<VSCodeLink
90-
href="#"
91-
onClick={(e) => {
92-
e.preventDefault()
93-
handleOpenSettings()
94-
}}
95-
className="text-vscode-textLink-foreground hover:text-vscode-textLink-activeForeground"
96-
/>
97-
),
98-
}}
99-
/>
100-
</div>
101-
}>
102-
<Info className="size-3.5 text-vscode-descriptionForeground" />
103-
</StandardTooltip>
10485
</div>
10586
</button>
10687

10788
{isExpanded && (
108-
<div className="px-3 pb-3 space-y-2">
89+
<div className="pl-6 pr-2 pt-1 pb-2 space-y-2">
10990
{allPatterns.map((item) => {
11091
const editState = getEditState(item.pattern)
11192
const status = getPatternStatus(editState.value)
11293

11394
return (
114-
<div key={item.pattern} className="ml-5 flex items-center gap-2">
95+
<div key={item.pattern} className="flex items-center gap-2">
11596
<div className="flex-1">
11697
{editState.isEditing ? (
11798
<input
@@ -146,35 +127,49 @@ export const CommandPatternSelector: React.FC<CommandPatternSelectorProps> = ({
146127
)}
147128
</div>
148129
<div className="flex items-center gap-1">
149-
<button
150-
className={cn("p-1 rounded transition-all", {
151-
"bg-green-500/20 text-green-500 hover:bg-green-500/30":
152-
status === "allowed",
153-
"text-vscode-descriptionForeground hover:text-green-500 hover:bg-green-500/10":
154-
status !== "allowed",
155-
})}
156-
onClick={() => onAllowPatternChange(editState.value)}
157-
aria-label={t(
130+
<StandardTooltip
131+
content={t(
158132
status === "allowed"
159133
? "chat:commandExecution.removeFromAllowed"
160134
: "chat:commandExecution.addToAllowed",
161135
)}>
162-
<Check className="size-3.5" />
163-
</button>
164-
<button
165-
className={cn("p-1 rounded transition-all", {
166-
"bg-red-500/20 text-red-500 hover:bg-red-500/30": status === "denied",
167-
"text-vscode-descriptionForeground hover:text-red-500 hover:bg-red-500/10":
168-
status !== "denied",
169-
})}
170-
onClick={() => onDenyPatternChange(editState.value)}
171-
aria-label={t(
136+
<button
137+
className={cn("p-1 rounded transition-all cursor-pointer", {
138+
"bg-green-500/20 text-green-500 hover:bg-green-500/30":
139+
status === "allowed",
140+
"text-vscode-descriptionForeground hover:text-green-500 hover:bg-green-500/10":
141+
status !== "allowed",
142+
})}
143+
onClick={() => onAllowPatternChange(editState.value)}
144+
aria-label={t(
145+
status === "allowed"
146+
? "chat:commandExecution.removeFromAllowed"
147+
: "chat:commandExecution.addToAllowed",
148+
)}>
149+
<Check className="size-3.5" />
150+
</button>
151+
</StandardTooltip>
152+
<StandardTooltip
153+
content={t(
172154
status === "denied"
173155
? "chat:commandExecution.removeFromDenied"
174156
: "chat:commandExecution.addToDenied",
175157
)}>
176-
<X className="size-3.5" />
177-
</button>
158+
<button
159+
className={cn("p-1 rounded transition-all cursor-pointer", {
160+
"bg-red-500/20 text-red-500 hover:bg-red-500/30": status === "denied",
161+
"text-vscode-descriptionForeground hover:text-red-500 hover:bg-red-500/10":
162+
status !== "denied",
163+
})}
164+
onClick={() => onDenyPatternChange(editState.value)}
165+
aria-label={t(
166+
status === "denied"
167+
? "chat:commandExecution.removeFromDenied"
168+
: "chat:commandExecution.addToDenied",
169+
)}>
170+
<X className="size-3.5" />
171+
</button>
172+
</StandardTooltip>
178173
</div>
179174
</div>
180175
)

webview-ui/src/i18n/locales/ca/chat.json

Lines changed: 13 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

webview-ui/src/i18n/locales/de/chat.json

Lines changed: 10 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

webview-ui/src/i18n/locales/en/chat.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
}
7070
},
7171
"runCommand": {
72-
"title": "Run Command",
72+
"title": "Command",
7373
"tooltip": "Execute this command"
7474
},
7575
"proceedWhileRunning": {
@@ -223,10 +223,10 @@
223223
"commandOutput": "Command Output",
224224
"commandExecution": {
225225
"running": "Running",
226+
"abort": "Abort",
226227
"pid": "PID: {{pid}}",
227-
"exited": "Exited ({{exitCode}})",
228-
"manageCommands": "Manage Command Permissions",
229-
"commandManagementDescription": "Manage command permissions: Click ✓ to allow auto-execution, ✗ to deny execution. Patterns can be toggled on/off or removed from lists. <settingsLink>View all settings</settingsLink>",
228+
"exitStatus": "Exited with status {{exitCode}}",
229+
"manageCommands": "Auto-approved commands",
230230
"addToAllowed": "Add to allowed list",
231231
"removeFromAllowed": "Remove from allowed list",
232232
"addToDenied": "Add to denied list",

0 commit comments

Comments
 (0)